Skip to content

Commit

Permalink
Move traversal logic into core feature (#46)
Browse files Browse the repository at this point in the history
  • Loading branch information
szhsin authored Jul 19, 2024
1 parent 5eb14ce commit 2d9b7c0
Show file tree
Hide file tree
Showing 29 changed files with 166 additions and 301 deletions.
85 changes: 28 additions & 57 deletions dist/cjs/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,8 @@ const useIdShim = () => {
const useId = React.useId || useIdShim;

const useAutocomplete = ({
value,
onChange,
feature: useFeature,
traversal: useTraversal,
isItemSelected,
...passthrough
}) => {
Expand All @@ -39,18 +37,13 @@ const useAutocomplete = ({
open,
setOpen
};
const contextual = {
const featureYield = useFeature({
id: useId(),
tmpValue,
setTmpValue,
value,
onChange: newValue => value != newValue && (onChange == null ? void 0 : onChange(newValue)),
onChange: newValue => passthrough.value != newValue && (onChange == null ? void 0 : onChange(newValue)),
...passthrough,
...state
};
const featureYield = useFeature({
...contextual,
...useTraversal(contextual)
});
return {
...state,
Expand Down Expand Up @@ -191,7 +184,6 @@ const autocompleteLite = ({
isItemDisabled,
isItemAction,
onAction,
traverse,
value,
onChange,
tmpValue,
Expand Down Expand Up @@ -226,6 +218,25 @@ const autocompleteLite = ({
if (select) onChange();
}
};
const traverse = isForward => {
const baseIndex = rovingText ? -1 : 0;
let newItem,
nextIndex = items.findIndex(item => isEqual(focusItem, item)),
itemCounter = 0;
const itemLength = items.length;
for (;;) {
if (isForward) {
if (++nextIndex >= itemLength) nextIndex = baseIndex;
} else {
if (--nextIndex < baseIndex) nextIndex = itemLength - 1;
}
newItem = items[nextIndex];
if (!newItem || !(isItemDisabled != null && isItemDisabled(newItem))) break;
if (++itemCounter >= itemLength) return;
}
setFocusItem(newItem);
if (rovingText) setTmpValue(getItemValue(newItem));
};
const listId = getId(id, 'l');
let ariaActivedescendant;
if (focusItem) {
Expand Down Expand Up @@ -299,8 +310,7 @@ const autocompleteLite = ({
case 'ArrowDown':
e.preventDefault();
if (open) {
const nextItem = traverse(e.key != 'ArrowUp');
if (rovingText) setTmpValue(getItemValue(nextItem));
traverse(e.key != 'ArrowUp');
} else {
setOpen(true);
}
Expand Down Expand Up @@ -564,59 +574,20 @@ const supercomplete = props => mergeModules(autocomplete({
rovingText: true
}), autoInline(props));

const linearTraversal = ({
traverseInput,
items
}) => ({
focusItem,
setFocusItem,
isItemDisabled,
isEqual
}) => {
return {
items,
traverse: isForward => {
const baseIndex = traverseInput ? -1 : 0;
let newItem,
nextIndex = items.findIndex(item => isEqual(focusItem, item)),
itemCounter = 0;
const itemLength = items.length;
for (;;) {
if (isForward) {
if (++nextIndex >= itemLength) nextIndex = baseIndex;
} else {
if (--nextIndex < baseIndex) nextIndex = itemLength - 1;
}
newItem = items[nextIndex];
if (!newItem || !(isItemDisabled != null && isItemDisabled(newItem))) break;
if (++itemCounter >= itemLength) return focusItem;
}
setFocusItem(newItem);
return newItem;
}
};
};

const isArray = Array.isArray;
const groupedTraversal = ({
groupedItems,
getItemsInGroup,
...restProps
const getGroupedItems = ({
groups,
getItemsInGroup
}) => {
const groups = isArray(groupedItems) ? groupedItems : Object.values(groupedItems);
const items = groups.reduce((accu, group) => accu.concat(isArray(group) ? group : getItemsInGroup ? getItemsInGroup(group) : []), []);
return linearTraversal({
...restProps,
items
});
const groupArray = isArray(groups) ? groups : Object.values(groups);
return groupArray.reduce((accu, group) => accu.concat(isArray(group) ? group : getItemsInGroup ? getItemsInGroup(group) : []), []);
};

exports.autoFocus = autoFocus;
exports.autocomplete = autocomplete;
exports.autocompleteLite = autocompleteLite;
exports.dropdown = dropdown;
exports.groupedTraversal = groupedTraversal;
exports.linearTraversal = linearTraversal;
exports.getGroupedItems = getGroupedItems;
exports.mergeModules = mergeModules;
exports.multiSelect = multiSelect;
exports.multiSelectDropdown = multiSelectDropdown;
Expand Down
23 changes: 20 additions & 3 deletions dist/esm/features/atom/autocompleteLite.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ const autocompleteLite = ({
isItemDisabled,
isItemAction,
onAction,
traverse,
value,
onChange,
tmpValue,
Expand Down Expand Up @@ -55,6 +54,25 @@ const autocompleteLite = ({
if (select) onChange();
}
};
const traverse = isForward => {
const baseIndex = rovingText ? -1 : 0;
let newItem,
nextIndex = items.findIndex(item => isEqual(focusItem, item)),
itemCounter = 0;
const itemLength = items.length;
for (;;) {
if (isForward) {
if (++nextIndex >= itemLength) nextIndex = baseIndex;
} else {
if (--nextIndex < baseIndex) nextIndex = itemLength - 1;
}
newItem = items[nextIndex];
if (!newItem || !(isItemDisabled != null && isItemDisabled(newItem))) break;
if (++itemCounter >= itemLength) return;
}
setFocusItem(newItem);
if (rovingText) setTmpValue(getItemValue(newItem));
};
const listId = getId(id, 'l');
let ariaActivedescendant;
if (focusItem) {
Expand Down Expand Up @@ -128,8 +146,7 @@ const autocompleteLite = ({
case 'ArrowDown':
e.preventDefault();
if (open) {
const nextItem = traverse(e.key != 'ArrowUp');
if (rovingText) setTmpValue(getItemValue(nextItem));
traverse(e.key != 'ArrowUp');
} else {
setOpen(true);
}
Expand Down
11 changes: 2 additions & 9 deletions dist/esm/hooks/useAutocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@ import { useRef, useState } from 'react';
import { useId } from './useId.js';

const useAutocomplete = ({
value,
onChange,
feature: useFeature,
traversal: useTraversal,
isItemSelected,
...passthrough
}) => {
Expand All @@ -21,18 +19,13 @@ const useAutocomplete = ({
open,
setOpen
};
const contextual = {
const featureYield = useFeature({
id: useId(),
tmpValue,
setTmpValue,
value,
onChange: newValue => value != newValue && (onChange == null ? void 0 : onChange(newValue)),
onChange: newValue => passthrough.value != newValue && (onChange == null ? void 0 : onChange(newValue)),
...passthrough,
...state
};
const featureYield = useFeature({
...contextual,
...useTraversal(contextual)
});
return {
...state,
Expand Down
3 changes: 1 addition & 2 deletions dist/esm/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,5 @@ export { dropdown } from './features/molecule/dropdown.js';
export { multiSelect } from './features/molecule/multiSelect.js';
export { multiSelectDropdown } from './features/molecule/multiSelectDropdown.js';
export { supercomplete } from './features/molecule/supercomplete.js';
export { linearTraversal } from './traversals/linearTraversal.js';
export { groupedTraversal } from './traversals/groupedTraversal.js';
export { getGroupedItems } from './utils/getGroupedItems.js';
export { mergeModules } from './utils/mergeModules.js';
17 changes: 0 additions & 17 deletions dist/esm/traversals/groupedTraversal.js

This file was deleted.

34 changes: 0 additions & 34 deletions dist/esm/traversals/linearTraversal.js

This file was deleted.

10 changes: 10 additions & 0 deletions dist/esm/utils/getGroupedItems.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const isArray = Array.isArray;
const getGroupedItems = ({
groups,
getItemsInGroup
}) => {
const groupArray = isArray(groups) ? groups : Object.values(groups);
return groupArray.reduce((accu, group) => accu.concat(isArray(group) ? group : getItemsInGroup ? getItemsInGroup(group) : []), []);
};

export { getGroupedItems };
2 changes: 1 addition & 1 deletion examples/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 7 additions & 8 deletions examples/pages/dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
import {
useCombobox,
dropdown,
groupedTraversal,
getGroupedItems,
useAutoHeight
} from '@szhsin/react-autocomplete';
import styles from '@/styles/Home.module.css';
Expand All @@ -12,7 +12,7 @@ type Item = { name: string; abbr: string };
const getItemValue = (item: Item) => item.name;
const isItemDisabled = ({ abbr }: Item) => abbr.startsWith('CO');

const getGroupedItems = (value: string = '') =>
const filterGroupedItems = (value: string = '') =>
LIST_GROUP.map((group) => ({
...group,
states: group.states.filter((item) =>
Expand All @@ -27,7 +27,7 @@ export default function Dropdown() {
const [value, setValue] = useState<string | undefined>('');
const [selectedItem, setSelectedItem] = useState<Item | undefined>();

const groupedItems = getGroupedItems(value);
const groupedItems = filterGroupedItems(value);

const {
getInputProps,
Expand Down Expand Up @@ -56,17 +56,16 @@ export default function Dropdown() {
// item.name.toLowerCase().startsWith(value.toLowerCase())
// ).find((item) => !isItemDisabled(item));
// // setItems(items);
// const item = getGroupedItems(value)[0]?.states.find((item) => !isItemDisabled(item));
// const item = filterGroupedItems(value)[0]?.states.find((item) => !isItemDisabled(item));
// item && inlineComplete({ item });
},
selected: selectedItem,
onSelectChange: setSelectedItem,
// feature: autocomplete({ constricted, rovingText }),
feature: dropdown({ rovingText, selectOnBlur, closeOnSelect }),
traversal: groupedTraversal({
traverseInput: true,
groupedItems,
getItemsInGroup: (gp) => gp.states
items: getGroupedItems({
groups: groupedItems,
getItemsInGroup: (group) => group.states
})
});

Expand Down
Loading

0 comments on commit 2d9b7c0

Please sign in to comment.