diff --git a/Form/Core/AutocompleteSelect.js b/Form/Core/AutocompleteSelect.js index 376a359..f12fd59 100644 --- a/Form/Core/AutocompleteSelect.js +++ b/Form/Core/AutocompleteSelect.js @@ -7,14 +7,7 @@ import tw, { styled } from "twin.macro"; import "styled-components/macro"; import { useField } from "@shared/nej-react-components/Parts/Input"; -/** - * @param {object} props - * @param {((query: string) => Promise) | Array} props.data - * @param {(val: any, state: { active: boolean, selected: boolean }) => React.ReactNode} [props.renderOption] - Custom renderer for each option. Receives the raw item and combobox state. - * @param {() => void} [props.onScrollEnd] - Called when the options list is scrolled to the bottom (for infinite scroll). - * @param {(query: string) => void} [props.onQueryChange] - Called when the search input changes. Use this with renderOption to drive server-side filtering. - */ -export function AutocompleteSelect({ title, titleProps = null, data, nullable = false, renderOption, onScrollEnd, onQueryChange, ...props }) { +export function AutocompleteSelect({ title, titleProps = null, data, nullable = false, renderOption = undefined, onScrollEnd = undefined, onQueryChange = undefined, ...props }) { const [field, meta, helpers] = useField(props); const [query, setQuery] = useState(field.value ?? ""); @@ -52,14 +45,14 @@ export function AutocompleteSelect({ title, titleProps = null, data, nullable = }) ); } - } else if(typeof data === "object") - { - setInternalData(Object.values(data).map((value) => { - return { - value: value, - label: value - }; - })); + } else if (typeof data === "object") { + // TypeScript enum objects — convert values to { value, label } then filter + const enumItems = Object.values(data).map((value) => ({ value, label: value })); + setInternalData( + !renderOption && query !== "" + ? enumItems.filter((item) => String(item.label).toLowerCase().includes(query.toLowerCase())) + : enumItems + ); } else { console.error("data is not an array or function :c"); diff --git a/Form/Core/NejDropzone.tsx b/Form/Core/NejDropzone.tsx index 3891f81..141a3f6 100644 --- a/Form/Core/NejDropzone.tsx +++ b/Form/Core/NejDropzone.tsx @@ -7,13 +7,13 @@ import { HTMLAttributes, useMemo } from "react"; * @param {Object} props - Component props. * @param {(files: T[], event: DropEvent) => void} props.onDrop - Callback function triggered when files are dropped. * @param {string[]} props.accept - Array of supported file extensions. - * @param {Array} props.value - The files to be uploaded. + * @param {Array | null} props.value - The files to be uploaded. * @param {boolean} props.single - Whether to allow only one file to be uploaded. * @param {(file: T) => FileError | FileError[] | null} props.validator - The validator function to be used. * @param {string} props.text - The text to be displayed in the dropzone. * @returns {JSX.Element} - NejDropzone component. */ -export default function NejDropzone({ value, accept, onDrop, validator, single, text, css, children, ...props }: HTMLAttributes & { value: File | File[], accept?: Accept | string, onDrop: (files: File[]) => void, validator?: (file: File) => FileError | FileError[] | null, single?: boolean, text?: string, css?: any, children?: any }) { +export default function NejDropzone({ value = null, accept, onDrop, validator, single, text, css, children, ...props }: HTMLAttributes & { value?: File | File[] | null, accept?: Accept | string, onDrop: (files: File[]) => void, validator?: (file: File) => FileError | FileError[] | null, single?: boolean, text?: string, css?: any, children?: any }) { const { getRootProps, getInputProps, isFocused, isDragAccept, isDragReject } = useDropzone({ //validator: validator,