From 57a4c17d0f9cb68a3934ea46d40c54035d5f0369 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 3 Mar 2024 12:44:57 -0600 Subject: [PATCH] Allow pasting of multiple tags --- src/components/TagInput.tsx | 42 ++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/src/components/TagInput.tsx b/src/components/TagInput.tsx index 92cfc680..934820eb 100644 --- a/src/components/TagInput.tsx +++ b/src/components/TagInput.tsx @@ -67,18 +67,22 @@ export default function TagInput({ } }, []); - const addOption = useCallback((option?: string) => { - if (option && !selectedOptions.includes(option)) { + const addOptions = useCallback((options: (string | undefined)[]) => { + const optionsToAdd = (options + .filter(Boolean) as string[]) + .map(option => option.startsWith(CREATE_LABEL) + ? option.match(new RegExp(`^${CREATE_LABEL} "(.+)"$`))?.[1] ?? option + : option) + .map(option => parameterize(option)) + .filter(option => !selectedOptions.includes(option)); + + if (optionsToAdd.length > 0) { onChange?.([ ...selectedOptions, - option.startsWith(CREATE_LABEL) - ? option.match(new RegExp(`^${CREATE_LABEL} "(.+)"$`))?.[1] ?? option - : option, - ] - .filter(Boolean) - .map(item => parameterize(item)) - .join(',')); + ...optionsToAdd, + ].join(',')); } + setSelectedOptionIndex(undefined); inputRef.current?.focus(); }, [onChange, selectedOptions]); @@ -93,9 +97,14 @@ export default function TagInput({ // Show options when input text changes useEffect(() => { if (inputText) { - setShouldShowMenu(true); + if (inputText.includes(',')) { + addOptions(inputText.split(',')); + setInputText(''); + } else { + setShouldShowMenu(true); + } } - }, [inputText]); + }, [inputText, addOptions, selectedOptions]); // Focus option in the DOM when selected index changes useEffect(() => { @@ -113,7 +122,6 @@ export default function TagInput({ const listener = (e: KeyboardEvent) => { // Keys which always trap focus switch (e.key) { - case ',': case 'ArrowDown': case 'ArrowUp': case 'Escape': @@ -127,14 +135,10 @@ export default function TagInput({ if (shouldShowMenu && optionsFiltered.length > 0) { e.stopImmediatePropagation(); e.preventDefault(); - addOption(optionsFiltered[selectedOptionIndex ?? 0].value); + addOptions([optionsFiltered[selectedOptionIndex ?? 0].value]); setInputText(''); } break; - case ',': - addOption(inputText); - setInputText(''); - break; case 'ArrowDown': if (shouldShowMenu) { setSelectedOptionIndex(i => { @@ -187,7 +191,7 @@ export default function TagInput({ selectedOptions, selectedOptionIndex, optionsFiltered, - addOption, + addOptions, shouldShowMenu, ]); @@ -304,7 +308,7 @@ export default function TagInput({ 'outline-none', )} onClick={() => { - addOption(value); + addOptions([value]); setInputText(''); }} onFocus={() => setSelectedOptionIndex(index)}