From 3454c8a03a915e1f8830c574e7ea6a2a375a3632 Mon Sep 17 00:00:00 2001 From: Markus Thielker Date: Sat, 16 Mar 2024 20:37:37 +0100 Subject: [PATCH] N-FIN-30: add new auto-complete component --- src/components/ui/auto-complete-input.tsx | 100 ++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 src/components/ui/auto-complete-input.tsx diff --git a/src/components/ui/auto-complete-input.tsx b/src/components/ui/auto-complete-input.tsx new file mode 100644 index 0000000..29363e6 --- /dev/null +++ b/src/components/ui/auto-complete-input.tsx @@ -0,0 +1,100 @@ +'use client'; + +import * as React from 'react'; +import { useEffect, useState } from 'react'; +import { cn } from '@/lib/utils'; + +export interface AutoCompleteInputProps + extends React.InputHTMLAttributes { + items: { label: string, value: any }[]; + next?: React.RefObject; +} + +const AutoCompleteInput = React.forwardRef( + ({className, type, ...props}, ref) => { + + const [value, setValue] = useState(getInitialValue()); + const [open, setOpen] = useState(false); + const [lastKey, setLastKey] = useState(''); + const [filteredItems, setFilteredItems] = useState(props.items); + + function getInitialValue() { + + if (!props.items) { + return ''; + } + + const item = props.items?.find(item => item.value === props.value); + return item?.label || ''; + } + + function handleChange(e: React.ChangeEvent) { + + props.onChange?.(undefined as any); + const value = e.target.value; + + setFilteredItems(props?.items?.filter((item) => { + return item.label.toLowerCase().includes(value.toLowerCase()); + })); + + setValue(value); + setOpen(value.length > 0); + } + + useEffect(() => { + if (filteredItems.length === 1 && /^[a-zA-Z0-9]$/.test(lastKey)) { + setValue(filteredItems[0].label); + setOpen(false); + props.onChange?.({target: {value: filteredItems[0].value}} as any); + props.next && props.next.current?.focus(); + } + }, [filteredItems]); + + return ( +
+ { + if (e.metaKey || e.ctrlKey || e.altKey) { + props.onKeyDown?.(e); + return; + } + setLastKey(e.key); + props.onKeyDown?.(e); + }} + /> + { + open && ( +
+ {filteredItems?.map((item) => +
{ + props.onChange?.({target: {value: item.value}} as any); + props.next && props.next.current?.focus(); + setValue(item.label); + setOpen(false); + }} + key={item.value}> + {item.label} +
, + )} +
+ ) + } +
+ ); + }, +); +AutoCompleteInput.displayName = 'Input'; + +export { AutoCompleteInput };