-
Notifications
You must be signed in to change notification settings - Fork 142
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
chore: ComboBoxのuseOptionsのロジックを整理する #5332
Changes from all commits
485eebb
ff3c853
a4f8957
c515bea
e69c2d1
2f619c4
3838a06
0df395e
430d1bf
ffb3ad7
0a7be39
ca5e096
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,71 +9,86 @@ const defaultIsItemSelected = <T>( | |
selectedItems: Array<ComboBoxItem<T>>, | ||
) => selectedItems.some((item) => areComboBoxItemsEqual(item, targetItem)) | ||
|
||
export function useOptions<T>({ | ||
items, | ||
selected, | ||
creatable, | ||
inputValue = '', | ||
isFilteringDisabled = false, | ||
isItemSelected = defaultIsItemSelected, | ||
}: { | ||
type BaseUseOptionsProps<T> = { | ||
items: Array<ComboBoxItem<T>> | ||
selected: (ComboBoxItem<T> | null) | Array<ComboBoxItem<T>> | ||
creatable: boolean | ||
inputValue?: string | ||
isFilteringDisabled?: boolean | ||
} | ||
|
||
export const useSingleOptions = <T>({ | ||
selected, | ||
...rest | ||
}: BaseUseOptionsProps<T> & { | ||
selected: ComboBoxItem<T> | null | ||
}) => { | ||
const isSelected = useCallback( | ||
(item: ComboBoxItem<T>) => selected !== null && areComboBoxItemsEqual(selected, item), | ||
[selected], | ||
) | ||
|
||
return useOptions<T>(rest, isSelected) | ||
} | ||
|
||
export const useMultiOptions = <T>({ | ||
selected, | ||
isItemSelected = defaultIsItemSelected, | ||
...rest | ||
}: BaseUseOptionsProps<T> & { | ||
selected: Array<ComboBoxItem<T>> | ||
isItemSelected?: (targetItem: ComboBoxItem<T>, selectedItems: Array<ComboBoxItem<T>>) => boolean | ||
}) { | ||
const isInputValueAddable = useMemo( | ||
() => creatable && inputValue !== '' && !items.some((item) => item.label === inputValue), | ||
[creatable, inputValue, items], | ||
}) => { | ||
const isSelected = useCallback( | ||
(item: ComboBoxItem<T>) => isItemSelected(item, selected), | ||
[selected, isItemSelected], | ||
) | ||
|
||
return useOptions<T>(rest, isSelected) | ||
} | ||
|
||
function useOptions<T>( | ||
{ items, creatable, inputValue = '', isFilteringDisabled = false }: BaseUseOptionsProps<T>, | ||
isSelected: (item: ComboBoxItem<T>) => boolean, | ||
) { | ||
const newItemId = useId() | ||
const optionIdPrefix = useId() | ||
const getOptionId = useCallback( | ||
(optionIndex: number) => `${optionIdPrefix}-${optionIndex}`, | ||
[optionIdPrefix], | ||
) | ||
Comment on lines
-34
to
-37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. メソッド化する意味が薄かったため削除しています |
||
|
||
const isSelected = useCallback( | ||
(item: ComboBoxItem<T>) => { | ||
if (Array.isArray(selected)) { | ||
return isItemSelected(item, selected) | ||
} else { | ||
return selected !== null && areComboBoxItemsEqual(selected, item) | ||
} | ||
}, | ||
[isItemSelected, selected], | ||
const existedOptions: Array<ComboBoxOption<T>> = useMemo( | ||
() => | ||
items.map((item, i) => ({ | ||
id: `${optionIdPrefix}-${i}`, | ||
selected: isSelected(item), | ||
isNew: false, | ||
item, | ||
})), | ||
[isSelected, items, optionIdPrefix], | ||
) | ||
const addingOption: ComboBoxOption<T> | null = useMemo( | ||
() => | ||
creatable && inputValue && items.every((item) => item.label !== inputValue) | ||
? { | ||
id: newItemId, | ||
isNew: true, | ||
selected: false, | ||
item: { label: inputValue, value: inputValue }, | ||
} | ||
: null, | ||
[inputValue, items, creatable, newItemId], | ||
Comment on lines
+66
to
+76
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. creatableがtrueになる可能性はかなり低く、nullになる可能性が高くなります。 |
||
) | ||
|
||
const allOptions: Array<ComboBoxOption<T>> = useMemo(() => { | ||
const _options = items.map((item, i) => ({ | ||
id: getOptionId(i), | ||
selected: isSelected(item), | ||
isNew: false, | ||
item, | ||
})) | ||
if (isInputValueAddable) { | ||
const addingOption = { | ||
id: newItemId, | ||
isNew: true, | ||
selected: false, | ||
item: { label: inputValue, value: inputValue }, | ||
} | ||
return [addingOption, ..._options] | ||
} | ||
return _options | ||
}, [getOptionId, inputValue, isInputValueAddable, isSelected, items, newItemId]) | ||
const allOptions: Array<ComboBoxOption<T>> = useMemo( | ||
() => (addingOption ? [addingOption, ...existedOptions] : existedOptions), | ||
[existedOptions, addingOption], | ||
) | ||
|
||
const options = useMemo(() => { | ||
if (isFilteringDisabled) { | ||
if (isFilteringDisabled || !inputValue) { | ||
return allOptions | ||
} | ||
return allOptions.filter(({ item: { label } }) => { | ||
if (!inputValue) return true | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. filter内で処理する意味がないため、isFilteringDisabledと同時にチェックするようにしています |
||
return convertMatchableString(innerText(label)).includes(convertMatchableString(inputValue)) | ||
}) | ||
|
||
return allOptions.filter(({ item: { label } }) => | ||
convertMatchableString(innerText(label)).includes(convertMatchableString(inputValue)), | ||
) | ||
}, [allOptions, inputValue, isFilteringDisabled]) | ||
|
||
return { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Q: useSingleOptionsのほうのテストって追加しなくても大丈夫そうですか? (これまでなさそうな雰囲気は理解しつつ、追加してもいいんじゃないかなと思って聞いています)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
まー、内部処理に隠蔽されちゃってるところなんで、必要になったらでいい気がしてます