@spaced-out/ui-design-system
Version:
Sense UI components library
118 lines (107 loc) • 4 kB
Flow
// @flow strict
import type {GroupTitleOption} from '../useFilteredOptions';
import {useFilteredOptions} from '../useFilteredOptions';
export type ArbitraryOption = {
key: string,
label: string,
arbitrary?: boolean,
multiArbitrary?: boolean,
};
export function useArbitraryOptionAddition<V>({
searchTerm,
options = [],
excludedKeys = [],
allowArbitraryValues = true,
allowMultiArbitraryValues = false,
validateArbitraryValue = (value: string): boolean => value.trim() !== '',
// $FlowFixMe[incompatible-return] - Object literal is compatible with V due to type constraint
makeArbitraryValue = (searchTerm): V => ({
key: searchTerm,
label: searchTerm,
arbitrary: true,
}),
// $FlowFixMe[incompatible-return] - Object literal is compatible with V due to type constraint
makeMultiArbitraryValue = (searchTerm): V => {
const regex = /^(?=.*[,\n]).+$/s;
if (regex.test(searchTerm)) {
// $FlowFixMe - Object literal is compatible with V due to type constraint
return {key: searchTerm, label: searchTerm, multiArbitrary: true};
}
},
groupTitleOptions = [],
arbitraryGroup = {groupTitle: '', showLineDivider: false, options: []},
searchOptionsBy = (option: V, searchTerm: string): boolean => {
// $FlowFixMe
const {label, key}: {label: string, key: string} = option;
return (
key.toLowerCase().includes(searchTerm) ||
label.toLowerCase().includes(searchTerm)
);
},
}: {
searchTerm: string,
options?: V[],
excludedKeys?: string[],
allowArbitraryValues?: boolean,
allowMultiArbitraryValues?: boolean,
validateArbitraryValue?: (string) => boolean,
makeArbitraryValue?: (string) => V,
makeMultiArbitraryValue?: (string) => V,
groupTitleOptions?: GroupTitleOption<V>[],
arbitraryGroup?: GroupTitleOption<V>,
searchOptionsBy?: (option: V, searchTerm: string) => boolean,
}): {
optionsWithArbitrary: Array<V>,
groupTitleOptionsWithArbitrary: Array<GroupTitleOption<V>>,
} {
const trimmedSearchTerm = (searchTerm || '').trim().toLowerCase();
const {filteredOptions, filteredGroupTitleOptions} = useFilteredOptions({
searchTerm: trimmedSearchTerm,
options,
// $FlowFixMe[incompatible-call]
groupTitleOptions,
searchOptionsBy,
});
// Find if an arbitrary option should be added
const arbitraryOption =
allowArbitraryValues &&
trimmedSearchTerm &&
// $FlowFixMe - Array methods are valid here
!filteredOptions.some((option) => option.key === trimmedSearchTerm) &&
// $FlowFixMe - Array methods are valid here
!filteredGroupTitleOptions.some(
(group) =>
Array.isArray(group.options) &&
// $FlowFixMe- option has key property
group.options.some((option) => option.key === trimmedSearchTerm),
) &&
!excludedKeys.includes(trimmedSearchTerm) &&
validateArbitraryValue(trimmedSearchTerm) &&
makeArbitraryValue(trimmedSearchTerm);
// Find if a multi-arbitrary option should be added
const multiArbitraryOption =
allowMultiArbitraryValues &&
trimmedSearchTerm &&
makeMultiArbitraryValue(trimmedSearchTerm);
// Compose the results
let optionsWithArbitrary = filteredOptions;
let groupTitleOptionsWithArbitrary = groupTitleOptions;
if (arbitraryOption) {
// $FlowFixMe[incompatible-type] - Array spread is valid here
optionsWithArbitrary = [arbitraryOption, ...optionsWithArbitrary];
groupTitleOptionsWithArbitrary = [
{...arbitraryGroup, options: [arbitraryOption]},
...groupTitleOptionsWithArbitrary,
];
}
if (multiArbitraryOption) {
// $FlowFixMe[incompatible-type] - Array spread is valid here
optionsWithArbitrary = [multiArbitraryOption, ...optionsWithArbitrary];
groupTitleOptionsWithArbitrary = [
{...arbitraryGroup, options: [multiArbitraryOption]},
...groupTitleOptionsWithArbitrary,
];
}
// $FlowFixMe[incompatible-return]
return {optionsWithArbitrary, groupTitleOptionsWithArbitrary};
}