From 7b9195ea3550f21882b7adc00cae5d907b3c735f Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Fri, 13 Jun 2025 12:40:43 -0500 Subject: [PATCH] Match tag input to other menus --- src/components/MaskedScroll.tsx | 9 ++- src/components/TagInput.tsx | 123 +++++++++++++++++--------------- 2 files changed, 71 insertions(+), 61 deletions(-) diff --git a/src/components/MaskedScroll.tsx b/src/components/MaskedScroll.tsx index 45dba06a..36f9272d 100644 --- a/src/components/MaskedScroll.tsx +++ b/src/components/MaskedScroll.tsx @@ -1,7 +1,8 @@ -import { HTMLAttributes, useRef } from 'react'; +import { HTMLAttributes, RefObject, useRef } from 'react'; import useMaskedScroll from './useMaskedScroll'; export default function MaskedScroll({ + ref: refProp, direction, fadeSize, animationDuration, @@ -13,8 +14,10 @@ export default function MaskedScroll({ children, ...props }: HTMLAttributes & -Omit[0], 'ref'>) { - const ref = useRef(null); +Omit[0], 'ref'> & +{ ref?: RefObject }) { + const refInternal = useRef(null); + const ref = refProp ?? refInternal; const { styleMask } = useMaskedScroll({ ref, diff --git a/src/components/TagInput.tsx b/src/components/TagInput.tsx index 6e8e2bc0..02881295 100644 --- a/src/components/TagInput.tsx +++ b/src/components/TagInput.tsx @@ -9,6 +9,8 @@ import { useRef, useState, } from 'react'; +import MaskedScroll from './MaskedScroll'; + const KEY_KEYDOWN = 'keydown'; const CREATE_LABEL = 'Create'; @@ -46,7 +48,7 @@ export default function TagInput({ const containerRef = useRef(null); const inputRef = useRef(null); - const optionsRef = useRef(null); + const optionsRef = useRef(null); const [shouldShowMenu, setShouldShowMenu] = useState(false); const [inputText, setInputText] = useState(''); @@ -357,68 +359,73 @@ export default function TagInput({ /> - {shouldShowMenu && optionsFiltered.length > 0 && -
+
+ {shouldShowMenu && optionsFiltered.length > 0 &&
- {/* Menu Options */} - {optionsFiltered.map(({ - value, - annotation, - annotationAria, - }, index) => -
{ - if (!hasReachedLimit) { - addOptions([value]); + + {/* Menu Options */} + {optionsFiltered.map(({ + value, + annotation, + annotationAria, + }, index) => +
setSelectedOptionIndex(index)} - > - - {renderTag(value)} - - {annotation && - - - {annotation} - - } -
)} -
-
} + tabIndex={0} + className={clsx( + 'group flex items-center gap-2', + 'px-1.5 py-1 rounded-sm', + 'text-base select-none', + hasReachedLimit ? 'cursor-not-allowed' : 'cursor-pointer', + 'hover:bg-gray-100 dark:hover:bg-gray-800', + !hasReachedLimit && + 'active:bg-gray-50 dark:active:bg-gray-900', + 'focus:bg-gray-100 dark:focus:bg-gray-800', + index === 0 && selectedOptionIndex === undefined && + 'bg-gray-100 dark:bg-gray-800', + 'outline-hidden', + )} + onClick={() => { + if (!hasReachedLimit) { + addOptions([value]); + } + }} + onFocus={() => setSelectedOptionIndex(index)} + > + + {renderTag(value)} + + {annotation && + + + {annotation} + + } +
)} + +
} + ); }