Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions packages/autocomplete-core/src/createStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,23 @@ export function createStore<TItem>(
reducer: Reducer,
props: InternalAutocompleteOptions<TItem>
): AutocompleteStore<TItem> {
let state = props.initialState;

return {
state: props.initialState,
getState() {
return this.state;
return state;
},
send(action, payload) {
this.state = withCompletion(
reducer({ type: action, value: payload }, this.state, props),
state = withCompletion(
reducer(state, {
type: action,
props,
payload,
}),
props
);

props.onStateChange({ state: this.state });
props.onStateChange({ state });
},
};
}
Expand Down
2 changes: 1 addition & 1 deletion packages/autocomplete-core/src/onKeyDown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export function onKeyDown<TItem>({
// Arrow down.
event.preventDefault();

store.send(event.key, { shiftKey: event.shiftKey });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was this never used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope. I initially supported Shift to jump items 5 by 5 but then decided that it was gadget. I forgot to remove this parameter.

store.send(event.key, null);

const nodeItem = props.environment.document.getElementById(
`${props.id}-item-${store.getState().highlightedIndex}`
Expand Down
34 changes: 18 additions & 16 deletions packages/autocomplete-core/src/stateReducer.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,40 @@
import { Reducer } from './types';
import { getItemsCount, getNextHighlightedIndex } from './utils';

export const stateReducer: Reducer = (action, state, props) => {
export const stateReducer: Reducer = (state, action) => {
switch (action.type) {
case 'setHighlightedIndex': {
return {
...state,
highlightedIndex: action.value,
highlightedIndex: action.payload,
};
}

case 'setQuery': {
return {
...state,
query: action.value,
query: action.payload,
};
}

case 'setSuggestions': {
return {
...state,
suggestions: action.value,
suggestions: action.payload,
};
}

case 'setIsOpen': {
return {
...state,
isOpen: action.value,
isOpen: action.payload,
};
}

case 'setStatus': {
return {
...state,
status: action.value,
status: action.payload,
};
}

Expand All @@ -43,7 +43,7 @@ export const stateReducer: Reducer = (action, state, props) => {
...state,
context: {
...state.context,
...action.value,
...action.payload,
},
};
}
Expand All @@ -55,7 +55,7 @@ export const stateReducer: Reducer = (action, state, props) => {
1,
state.highlightedIndex,
getItemsCount(state),
props.defaultHighlightedIndex
action.props.defaultHighlightedIndex
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a reason these variables are nested in action.props, and not top-level in action, for an easier type signature for action?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI defaultHighlightedIndex is an Autocomplete option.

Did you mean to not retrieve this value from props but rather from payload.defaultHighlightedIndex?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't get that props meant options, it makes sense in that case then!

),
};
}
Expand All @@ -67,7 +67,7 @@ export const stateReducer: Reducer = (action, state, props) => {
-1,
state.highlightedIndex,
getItemsCount(state),
props.defaultHighlightedIndex
action.props.defaultHighlightedIndex
),
};
}
Expand Down Expand Up @@ -108,8 +108,10 @@ export const stateReducer: Reducer = (action, state, props) => {

// Since we close the menu when openOnFocus=false
// we lose track of the highlighted index. (Query-suggestions use-case)
props.openOnFocus === true ? props.defaultHighlightedIndex : null,
isOpen: props.openOnFocus, // @TODO: Check with UX team if we want to close the menu on reset.
action.props.openOnFocus === true
? action.props.defaultHighlightedIndex
: null,
isOpen: action.props.openOnFocus, // @TODO: Check with UX team if we want to close the menu on reset.
status: 'idle',
statusContext: {},
query: '',
Expand All @@ -119,13 +121,13 @@ export const stateReducer: Reducer = (action, state, props) => {
case 'focus': {
return {
...state,
highlightedIndex: props.defaultHighlightedIndex,
isOpen: props.openOnFocus || state.query.length > 0,
highlightedIndex: action.props.defaultHighlightedIndex,
isOpen: action.props.openOnFocus || state.query.length > 0,
};
}

case 'blur': {
if (props.debug) {
if (action.props.debug) {
return state;
}

Expand All @@ -139,14 +141,14 @@ export const stateReducer: Reducer = (action, state, props) => {
case 'mousemove': {
return {
...state,
highlightedIndex: action.value,
highlightedIndex: action.payload,
};
}

case 'mouseleave': {
return {
...state,
highlightedIndex: props.defaultHighlightedIndex,
highlightedIndex: action.props.defaultHighlightedIndex,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eg. this could maybe just be the payload?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my mind it's still not totally clear what should be passed in our payload when thinking of it as an API. Can you explain the reasoning behind your suggestion?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really use the reducer pattern so often, but saw action.type as the identifier, and then in the cases itself, I usually only saw payload being used, so when writing the dispatch, you don't have to think whether to use payload or props or something else.

Props of course doesn't make sense to think about, since those are the "global arguments", which makes sense as its own thing indeed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, merging as is then!

};
}

Expand Down
9 changes: 4 additions & 5 deletions packages/autocomplete-core/src/types/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,19 @@ import { InternalAutocompleteOptions } from './api';
import { AutocompleteState } from './state';

export interface AutocompleteStore<TItem> {
state: AutocompleteState<TItem>;
getState(): AutocompleteState<TItem>;
send(action: ActionType, payload: any): void;
}

export type Reducer = <TItem>(
action: Action,
state: AutocompleteState<TItem>,
props: InternalAutocompleteOptions<TItem>
action: Action<TItem, any>
) => AutocompleteState<TItem>;

type Action = {
type Action<TItem, TPayload> = {
type: ActionType;
value: any;
props: InternalAutocompleteOptions<TItem>;
payload: TPayload;
};

type ActionType =
Expand Down