From 0d5475c2be829005049814e20c840054f2028039 Mon Sep 17 00:00:00 2001 From: Ashley McEntee Date: Tue, 2 Jul 2024 15:35:15 -0400 Subject: [PATCH] Update depricated PF multiselect --- frontend/src/components/MultiSelection.tsx | 209 ++++++++++++++++++--- 1 file changed, 178 insertions(+), 31 deletions(-) diff --git a/frontend/src/components/MultiSelection.tsx b/frontend/src/components/MultiSelection.tsx index 123c38652e..94e0df1bad 100644 --- a/frontend/src/components/MultiSelection.tsx +++ b/frontend/src/components/MultiSelection.tsx @@ -1,6 +1,20 @@ import * as React from 'react'; -import { HelperText, HelperTextItem } from '@patternfly/react-core'; -import { Select, SelectOption, SelectVariant } from '@patternfly/react-core/deprecated'; +import { + Select, + SelectOption, + SelectList, + MenuToggle, + MenuToggleElement, + TextInputGroup, + TextInputGroupMain, + TextInputGroupUtilities, + ChipGroup, + Chip, + Button, + HelperText, + HelperTextItem, +} from '@patternfly/react-core'; +import { TimesIcon } from '@patternfly/react-icons/dist/esm/icons/times-icon'; import { MenuItemStatus } from '~/pages/groupSettings/groupTypes'; type MultiSelectionProps = { @@ -9,46 +23,179 @@ type MultiSelectionProps = { ariaLabel: string; }; -export const MultiSelection: React.FC = ({ value, setValue, ariaLabel }) => { - const [showMenu, setShowMenu] = React.useState(false); +export const MultiSelection: React.FC = ({ value, setValue }) => { + const [isOpen, setIsOpen] = React.useState(false); + const [inputValue, setInputValue] = React.useState(''); + const [focusedItemIndex, setFocusedItemIndex] = React.useState(null); + const [activeItem, setActiveItem] = React.useState(null); + const textInputRef = React.useRef(); + const selected = React.useMemo(() => value.filter((v) => v.enabled), [value]); + const selectOptions = React.useMemo( + () => + value.filter((v) => !inputValue || v.name.toLowerCase().includes(inputValue.toLowerCase())), + [inputValue, value], + ); + + React.useEffect(() => { + if (inputValue) { + setIsOpen(true); + } + setFocusedItemIndex(null); + setActiveItem(null); + }, [inputValue]); + + const handleMenuArrowKeys = (key: string) => { + let indexToFocus; + if (isOpen) { + if (key === 'ArrowUp') { + if (focusedItemIndex === null || focusedItemIndex === 0) { + indexToFocus = selectOptions.length - 1; + } else { + indexToFocus = focusedItemIndex - 1; + } + } + + if (key === 'ArrowDown') { + if (focusedItemIndex === null || focusedItemIndex === selectOptions.length - 1) { + indexToFocus = 0; + } else { + indexToFocus = focusedItemIndex + 1; + } + } + + if (indexToFocus != null) { + setFocusedItemIndex(indexToFocus); + const focusedItem = selectOptions[indexToFocus]; + setActiveItem(`select-multi-typeahead-${focusedItem.name.replace(' ', '-')}`); + } + } + }; - const toggleMenu = (isOpen: React.SetStateAction) => { - setShowMenu(isOpen); + const onInputKeyDown = (event: React.KeyboardEvent) => { + const focusedItem = focusedItemIndex !== null ? selectOptions[focusedItemIndex] : null; + switch (event.key) { + case 'Enter': + if (isOpen && focusedItem) { + onSelect(focusedItem); + } + if (!isOpen) { + setIsOpen(true); + } + break; + case 'Tab': + case 'Escape': + setIsOpen(false); + setActiveItem(null); + break; + case 'ArrowUp': + case 'ArrowDown': + event.preventDefault(); + handleMenuArrowKeys(event.key); + break; + } }; - const clearSelection = () => { - const newState = value.map((element) => ({ ...element, enabled: false })); - setValue(newState); + const onToggleClick = () => { + setIsOpen(!isOpen); + setTimeout(() => textInputRef.current?.focus(), 100); + }; + const onTextInputChange = (_event: React.FormEvent, valueOfInput: string) => { + setInputValue(valueOfInput); + }; + const onSelect = (menuItem?: MenuItemStatus) => { + if (menuItem) { + setValue( + selected.includes(menuItem) + ? value.map((option) => (option === menuItem ? { ...option, enabled: false } : option)) + : value.map((option) => (option === menuItem ? { ...option, enabled: true } : option)), + ); + } + textInputRef.current?.focus(); }; const noSelectedItems = value.filter((option) => option.enabled).length === 0; + const toggle = (toggleRef: React.Ref) => ( + + + + + {selected.map((selection, index) => ( + { + ev.stopPropagation(); + onSelect(selection); + }} + > + {selection.name} + + ))} + + + + {selected.length > 0 && ( + + )} + + + + ); + return ( <> {noSelectedItems && (