@economic/taco 12.0.0-react18-compat.0 → 13.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"useCombobox.cjs","sources":["../../../src/components/Combobox/useCombobox.tsx"],"sourcesContent":["import debounce from 'lodash/debounce';\nimport { nanoid } from 'nanoid';\nimport * as React from 'react';\n\nimport { useMergedRef } from '../../hooks/useMergedRef';\nimport { isElementInsideTable3OrReport } from '../../utils/dom';\nimport { createCustomKeyboardEvent } from '../../utils/input';\nimport type { InputProps } from '../Input/Input';\nimport type { ScrollableListItem, ScrollableListItemValue, ScrollableListPropsWithRef } from '../Listbox/ScrollableList';\nimport { getId } from '../Listbox/ScrollableList';\nimport {\n setInputValueByRef,\n getIndexFromValue,\n findByValue,\n useFlattenedData,\n sanitizeItem,\n getOptionParents,\n filterData,\n} from '../Listbox/util';\n\nimport type { ComboboxProps } from './Combobox';\n\nconst debouncer = debounce(f => f(), 200);\n\nconst defaultConvertToInputValue = (value: ScrollableListItemValue | undefined) => String(value ?? '');\n\ntype useCombobox = Omit<React.HTMLAttributes<HTMLDivElement>, 'popover'> & {\n combobox: React.HTMLAttributes<HTMLSpanElement>;\n input: Omit<InputProps, 'defaultValue'> & { ref: React.RefObject<HTMLInputElement> };\n list: ScrollableListPropsWithRef;\n button: { ref: any };\n popover: { open: boolean; onOpenChange: (open: boolean) => void };\n};\n\nexport const useCombobox = (props: Omit<ComboboxProps, 'dialog'>, ref: React.Ref<HTMLInputElement>): useCombobox => {\n const {\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n data: unfilteredData = [],\n defaultValue,\n disabled,\n id: _nativeId,\n inline,\n loading: __,\n onChange,\n onClick,\n onKeyDown,\n onSearch,\n readOnly,\n value,\n valueRenderer,\n ...otherProps\n } = props;\n const inputRef = useMergedRef<HTMLInputElement>(ref);\n const buttonRef = React.useRef<HTMLButtonElement>(null);\n const listRef = React.useRef<HTMLUListElement>(null);\n const [open, setOpen] = React.useState(false);\n const listId = React.useMemo(() => nanoid(), []);\n const flattenedData = useFlattenedData(unfilteredData);\n\n const convertToInputValue = React.useCallback(\n (val: ScrollableListItemValue | undefined): string => {\n const option = findByValue(flattenedData, val);\n if (typeof valueRenderer === 'function' && option) {\n return valueRenderer(option);\n }\n return defaultConvertToInputValue(val);\n },\n [valueRenderer, flattenedData]\n );\n\n const [inputValue, setInputValue] = React.useState<string>(convertToInputValue(value));\n const shouldFilterData = !onSearch && (!inline || (inline && inputValue !== convertToInputValue(value)));\n const data = React.useMemo(\n () => (shouldFilterData ? filterData(flattenedData, inputValue) : flattenedData),\n [shouldFilterData, inputValue, flattenedData]\n );\n // listbox/select change value _with_ the index, but combobox changes on select of an index (click/enter), so we need state\n const [currentIndex, setCurrentIndex] = React.useState<number | undefined>(\n inputValue === undefined ? undefined : getIndexFromValue(data, inputValue)\n );\n\n const setInputValueByIndex = (index: number | undefined, event?: string): void => {\n if (index !== undefined) {\n const option = data[index];\n\n if (option && !option.disabled) {\n const eventType = event ?? 'focusout';\n setInputValueByRef(inputRef.current, option.value, eventType);\n }\n }\n };\n\n const setCurrentValue = (index: number | undefined, event?: string) => {\n if (index === undefined) {\n return;\n }\n\n const option = data[index];\n\n if (option?.value === value) {\n // if the selected option is already selected, refill input with its value\n setInputValue(convertToInputValue(value));\n } else {\n // if the selected option is not already selected, trigger blur event\n setInputValueByIndex(index, event);\n }\n };\n\n // ensure the external value is synced with the internal value when mounting, e.g. incase a default value was set\n React.useEffect(() => {\n if (defaultValue && !value) {\n setInputValueByIndex(getIndexFromValue(data, defaultValue));\n }\n }, [data]);\n\n // Sync the input's text when `value` changes 'externally' (selecting an item, a modal, a form reset, etc.).\n // This is done DURING RENDER (React's \"adjust state when a prop changes\" pattern) rather than in an effect,\n // so the corrected inputValue is in place *before* the \"show listbox based on input value\" effect below runs.\n // Doing it in a post-commit effect left a one-render window where that effect saw the *stale* inputValue (the\n // previous value's text) together with a fresh `data` reference and spuriously opened the popover after a reset.\n // React 19's separate commits expose this; React 18 batched the stale render away.\n const [prevValue, setPrevValue] = React.useState(value);\n if (value !== prevValue) {\n setPrevValue(value);\n\n if (value !== undefined) {\n // Compare against the rendered value to handle the valueRenderer case\n // (e.g., value is \"1000\" but the rendered text is \"1000 - Account Name\").\n const renderedValue = convertToInputValue(value);\n if (renderedValue !== inputValue) {\n setInputValue(renderedValue);\n }\n } else if (prevValue !== undefined && inputValue === convertToInputValue(prevValue)) {\n // `value` was cleared to `undefined` externally while the input still shows the *previous* value's\n // rendered text — i.e. the user did not type this — so clear it. We intentionally do NOT clear when the\n // user edited to a non-matching value (their text never equals the previous value's render), so they\n // can keep typing / backspace back to a valid one.\n if (inputValue !== '') {\n setInputValue('');\n }\n }\n }\n\n React.useEffect(() => {\n if (onSearch) {\n debouncer(() => {\n onSearch(inputValue);\n });\n }\n }, [inputValue]);\n\n // show listbox based on input value\n React.useEffect(() => {\n // don't show the popover if the internal (input) value already is the current value\n // this prevents the popover showing after selecting a value or pressing escape\n const isCurrentValue = value !== undefined && value !== null && inputValue === convertToInputValue(value);\n\n if (inputValue && data.length && !isCurrentValue) {\n setCurrentIndex(getIndexFromValue(data, inputValue) || 0);\n\n if (!open) {\n setOpen(true);\n }\n } else {\n setOpen(false);\n }\n }, [inputValue, data]);\n\n React.useEffect(() => {\n if (open) {\n setCurrentIndex(getIndexFromValue(data, inputValue) || 0);\n } else {\n setCurrentIndex(undefined);\n }\n }, [open]);\n\n const findOption = React.useCallback(\n (inputVal: string): ScrollableListItem | undefined => {\n // First try to find by raw value\n const itemByValue = findByValue(flattenedData, inputVal);\n if (itemByValue) {\n return itemByValue;\n }\n\n // If valueRenderer is provided, also try to match against rendered values\n if (valueRenderer) {\n return flattenedData.find(option => valueRenderer(option) === inputVal);\n }\n\n return undefined;\n },\n [flattenedData, valueRenderer]\n );\n\n // event handlers\n const handleInputBlur = (event: React.FocusEvent<HTMLInputElement>): void => {\n if (listRef.current && event.relatedTarget === listRef.current) {\n event.preventDefault();\n return;\n }\n\n const item = findOption(event.target.value);\n\n // If the found item's value equals the current value, just update the display to rendered format\n // This handles the case where user types the raw value but valueRenderer shows something different\n // (e.g., user types \"1000\" but display should be \"1000 - Account Name\")\n if (item && item.value === value) {\n setInputValue(convertToInputValue(value));\n if (props.onBlur) {\n props.onBlur(event);\n }\n return;\n }\n\n // event.target.value is always a string so it is important to cast value to a string (convertToInputValue) before checking the equality\n if (onChange && event.target.value !== convertToInputValue(value)) {\n (event as any).detail = sanitizeItem(item);\n\n const parents = getOptionParents(flattenedData, item?.path);\n\n if (parents !== null && parents.length > 0) {\n (event as any).detail.parents = parents;\n }\n\n onChange(event);\n }\n\n if (props.onBlur) {\n props.onBlur(event);\n }\n };\n\n const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>): void => {\n setInputValue(event.target.value);\n };\n\n const handleInputClick = (event: React.MouseEvent<HTMLInputElement>): void => {\n if (inline || (!open && inputValue && data.length)) {\n setOpen(true);\n }\n\n if (onClick) {\n onClick(event);\n }\n };\n\n const handleInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>): void => {\n // Explicitly stop propagation for arrow keys when Combobox is in open state and inside Table3\n // This ensures Table3 won't react to these keys when using the Combobox\n if (\n isElementInsideTable3OrReport(event.currentTarget) &&\n (event.key === 'ArrowUp' || event.key === 'ArrowDown') &&\n open\n ) {\n event.stopPropagation();\n }\n\n if (!event.ctrlKey && !event.metaKey) {\n switch (event.key) {\n case 'Backspace': {\n return;\n }\n\n case 'Escape': {\n event.preventDefault();\n if (open) {\n // as focus is on input, need to prevent propagation to not close dialog.\n event.stopPropagation();\n }\n setInputValue(convertToInputValue(value));\n setOpen(false);\n return;\n }\n\n case 'Tab': {\n // default focusout will trigger onBlur and we don't want to trigger it twice\n setCurrentValue(currentIndex, 'change');\n setOpen(false);\n return;\n }\n\n case 'Enter': {\n event.preventDefault();\n\n if (isElementInsideTable3OrReport(event.currentTarget)) {\n if (inline && !open) {\n setOpen(true);\n } else if (buttonRef.current && !open) {\n buttonRef.current.click();\n }\n }\n\n if (open) {\n // we need to focusout to trigger onBlur as we are not actually blurring the input\n setCurrentValue(currentIndex, 'focusout');\n setOpen(false);\n }\n\n return;\n }\n\n case 'ArrowDown':\n if (open) {\n event.preventDefault();\n } else if (!inline && buttonRef.current && !isElementInsideTable3OrReport(event.currentTarget)) {\n buttonRef.current.click();\n }\n break;\n\n case 'ArrowUp':\n case 'Home':\n case 'End': {\n if (open) {\n event.preventDefault();\n }\n break;\n }\n\n default:\n }\n\n // we aren't focused on the list, so manually forward the keydown event to it\n if (listRef.current) {\n listRef.current.dispatchEvent(createCustomKeyboardEvent(event));\n }\n\n if (\n inline &&\n !open &&\n (event.key === 'ArrowUp' || event.key === 'ArrowDown') &&\n !isElementInsideTable3OrReport(event.currentTarget)\n ) {\n event.preventDefault();\n const initialIndex = event.key === 'ArrowUp' ? data.length - 1 : 0;\n setCurrentIndex(currentIndex === undefined ? initialIndex : currentIndex);\n setOpen(true);\n }\n }\n\n if (!event.isDefaultPrevented() && onKeyDown) {\n onKeyDown(event);\n }\n };\n\n const handleListboxChange = (index: number): void => {\n setCurrentIndex(index);\n };\n\n const handleListboxClick = (event: React.MouseEvent<HTMLLIElement>, index: number): void => {\n event.preventDefault();\n setCurrentValue(index);\n setOpen(false);\n };\n\n const combobox = {\n 'aria-expanded': open,\n 'aria-owns': listId,\n 'aria-haspopup': 'listbox' as const,\n role: 'combobox',\n };\n\n const input = {\n ...otherProps,\n 'aria-controls': listId,\n // Indicates that the autocomplete behavior of the text input is to suggest a list of possible values in a popup and that the suggestions\n // are related to the string that is present in the textbox\n 'aria-autocomplete': 'list' as const,\n // Enables assistive technologies to know which element the application regards as focused while DOM focus remains on the input element\n 'aria-activedescendant':\n currentIndex !== undefined && data[currentIndex] ? getId(listId, String(data[currentIndex].value)) : undefined,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n disabled,\n onBlur: !disabled && !readOnly ? handleInputBlur : undefined,\n onChange: !disabled && !readOnly ? handleInputChange : undefined,\n onClick: !disabled && !readOnly ? handleInputClick : undefined,\n onKeyDownCapture: !disabled && !readOnly ? handleInputKeyDown : undefined,\n readOnly,\n ref: inputRef,\n type: 'text',\n value: inputValue ?? '',\n };\n\n const list: ScrollableListPropsWithRef = {\n 'aria-labelledby': ariaLabelledBy,\n data,\n disabled,\n id: listId,\n onChange: handleListboxChange,\n onClick: handleListboxClick,\n ref: listRef,\n scrollOnFocus: false,\n tabIndex: -1,\n value: currentIndex,\n };\n\n const button = {\n ref: buttonRef,\n };\n\n return {\n combobox,\n input,\n list,\n button,\n popover: {\n open,\n onOpenChange: setOpen,\n //visible: !data.length ? false : open,\n },\n };\n};\n"],"names":["useMergedRef","React","nanoid","useFlattenedData","findByValue","filterData","getIndexFromValue","setInputValueByRef","sanitizeItem","getOptionParents","isElementInsideTable3OrReport","createCustomKeyboardEvent","input","getId"],"mappings":"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"}
1
+ {"version":3,"file":"useCombobox.cjs","sources":["../../../src/components/Combobox/useCombobox.tsx"],"sourcesContent":["import debounce from 'lodash/debounce';\nimport { nanoid } from 'nanoid';\nimport * as React from 'react';\n\nimport { useMergedRef } from '../../hooks/useMergedRef';\nimport { isElementInsideTable3OrReport } from '../../utils/dom';\nimport { createCustomKeyboardEvent } from '../../utils/input';\nimport type { InputProps } from '../Input/Input';\nimport type { ScrollableListItem, ScrollableListItemValue, ScrollableListPropsWithRef } from '../Listbox/ScrollableList';\nimport { getId } from '../Listbox/ScrollableList';\nimport {\n setInputValueByRef,\n getIndexFromValue,\n findByValue,\n useFlattenedData,\n sanitizeItem,\n getOptionParents,\n filterData,\n} from '../Listbox/util';\n\nimport type { ComboboxProps } from './Combobox';\n\nconst debouncer = debounce(f => f(), 200);\n\nconst defaultConvertToInputValue = (value: ScrollableListItemValue | undefined) => String(value ?? '');\n\ntype useCombobox = Omit<React.HTMLAttributes<HTMLDivElement>, 'popover'> & {\n combobox: React.HTMLAttributes<HTMLSpanElement>;\n input: Omit<InputProps, 'defaultValue'> & { ref: React.RefObject<HTMLInputElement> };\n list: ScrollableListPropsWithRef;\n button: { ref: any };\n popover: { open: boolean; onOpenChange: (open: boolean) => void };\n};\n\nexport const useCombobox = (props: Omit<ComboboxProps, 'dialog'>, ref: React.Ref<HTMLInputElement>): useCombobox => {\n const {\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n data: unfilteredData = [],\n defaultValue,\n disabled,\n id: _nativeId,\n inline,\n loading: __,\n onChange,\n onClick,\n onKeyDown,\n onSearch,\n readOnly,\n value,\n valueRenderer,\n ...otherProps\n } = props;\n const inputRef = useMergedRef<HTMLInputElement>(ref);\n const buttonRef = React.useRef<HTMLButtonElement>(null);\n const listRef = React.useRef<HTMLUListElement>(null);\n const [open, setOpen] = React.useState(false);\n const listId = React.useMemo(() => nanoid(), []);\n const flattenedData = useFlattenedData(unfilteredData);\n\n const convertToInputValue = React.useCallback(\n (val: ScrollableListItemValue | undefined): string => {\n const option = findByValue(flattenedData, val);\n if (typeof valueRenderer === 'function' && option) {\n return valueRenderer(option);\n }\n return defaultConvertToInputValue(val);\n },\n [valueRenderer, flattenedData]\n );\n\n const [inputValue, setInputValue] = React.useState<string>(convertToInputValue(value));\n const shouldFilterData = !onSearch && (!inline || (inline && inputValue !== convertToInputValue(value)));\n const data = React.useMemo(\n () => (shouldFilterData ? filterData(flattenedData, inputValue) : flattenedData),\n [shouldFilterData, inputValue, flattenedData]\n );\n // listbox/select change value _with_ the index, but combobox changes on select of an index (click/enter), so we need state\n const [currentIndex, setCurrentIndex] = React.useState<number | undefined>(\n inputValue === undefined ? undefined : getIndexFromValue(data, inputValue)\n );\n\n const setInputValueByIndex = (index: number | undefined, event?: string): void => {\n if (index !== undefined) {\n const option = data[index];\n\n if (option && !option.disabled) {\n const eventType = event ?? 'focusout';\n setInputValueByRef(inputRef.current, option.value, eventType);\n }\n }\n };\n\n const setCurrentValue = (index: number | undefined, event?: string) => {\n if (index === undefined) {\n return;\n }\n\n const option = data[index];\n\n if (option?.value === value) {\n // if the selected option is already selected, refill input with its value\n setInputValue(convertToInputValue(value));\n } else {\n // if the selected option is not already selected, trigger blur event\n setInputValueByIndex(index, event);\n }\n };\n\n // ensure the external value is synced with the internal value when mounting, e.g. incase a default value was set\n React.useEffect(() => {\n if (defaultValue && !value) {\n setInputValueByIndex(getIndexFromValue(data, defaultValue));\n }\n }, [data]);\n\n // Sync input text when `value` changes externally, during render (not in an effect) so it's correct before the\n // \"show listbox\" effect below — otherwise a one-render stale-text window can open the popover on reset.\n const [prevValue, setPrevValue] = React.useState(value);\n if (value !== prevValue) {\n setPrevValue(value);\n\n if (value !== undefined) {\n // Compare rendered values to handle valueRenderer (e.g. \"1000\" renders as \"1000 - Account Name\").\n const renderedValue = convertToInputValue(value);\n if (renderedValue !== inputValue) {\n setInputValue(renderedValue);\n }\n } else if (prevValue !== undefined && inputValue === convertToInputValue(prevValue)) {\n // `value` was cleared externally and the input still shows its previous rendered text (not user-typed),\n // so clear it. Text the user typed never matches the previous render, so it's preserved.\n if (inputValue !== '') {\n setInputValue('');\n }\n }\n }\n\n React.useEffect(() => {\n if (onSearch) {\n debouncer(() => {\n onSearch(inputValue);\n });\n }\n }, [inputValue]);\n\n // show listbox based on input value\n React.useEffect(() => {\n // don't show the popover if the internal (input) value already is the current value\n // this prevents the popover showing after selecting a value or pressing escape\n const isCurrentValue = value !== undefined && value !== null && inputValue === convertToInputValue(value);\n\n if (inputValue && data.length && !isCurrentValue) {\n setCurrentIndex(getIndexFromValue(data, inputValue) || 0);\n\n if (!open) {\n setOpen(true);\n }\n } else {\n setOpen(false);\n }\n }, [inputValue, data]);\n\n React.useEffect(() => {\n if (open) {\n setCurrentIndex(getIndexFromValue(data, inputValue) || 0);\n } else {\n setCurrentIndex(undefined);\n }\n }, [open]);\n\n const findOption = React.useCallback(\n (inputVal: string): ScrollableListItem | undefined => {\n // First try to find by raw value\n const itemByValue = findByValue(flattenedData, inputVal);\n if (itemByValue) {\n return itemByValue;\n }\n\n // If valueRenderer is provided, also try to match against rendered values\n if (valueRenderer) {\n return flattenedData.find(option => valueRenderer(option) === inputVal);\n }\n\n return undefined;\n },\n [flattenedData, valueRenderer]\n );\n\n // event handlers\n const handleInputBlur = (event: React.FocusEvent<HTMLInputElement>): void => {\n if (listRef.current && event.relatedTarget === listRef.current) {\n event.preventDefault();\n return;\n }\n\n const item = findOption(event.target.value);\n\n // If the found item's value equals the current value, just update the display to rendered format\n // This handles the case where user types the raw value but valueRenderer shows something different\n // (e.g., user types \"1000\" but display should be \"1000 - Account Name\")\n if (item && item.value === value) {\n setInputValue(convertToInputValue(value));\n if (props.onBlur) {\n props.onBlur(event);\n }\n return;\n }\n\n // event.target.value is always a string so it is important to cast value to a string (convertToInputValue) before checking the equality\n if (onChange && event.target.value !== convertToInputValue(value)) {\n (event as any).detail = sanitizeItem(item);\n\n const parents = getOptionParents(flattenedData, item?.path);\n\n if (parents !== null && parents.length > 0) {\n (event as any).detail.parents = parents;\n }\n\n onChange(event);\n }\n\n if (props.onBlur) {\n props.onBlur(event);\n }\n };\n\n const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>): void => {\n setInputValue(event.target.value);\n };\n\n const handleInputClick = (event: React.MouseEvent<HTMLInputElement>): void => {\n if (inline || (!open && inputValue && data.length)) {\n setOpen(true);\n }\n\n if (onClick) {\n onClick(event);\n }\n };\n\n const handleInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>): void => {\n // Explicitly stop propagation for arrow keys when Combobox is in open state and inside Table3\n // This ensures Table3 won't react to these keys when using the Combobox\n if (\n isElementInsideTable3OrReport(event.currentTarget) &&\n (event.key === 'ArrowUp' || event.key === 'ArrowDown') &&\n open\n ) {\n event.stopPropagation();\n }\n\n if (!event.ctrlKey && !event.metaKey) {\n switch (event.key) {\n case 'Backspace': {\n return;\n }\n\n case 'Escape': {\n event.preventDefault();\n if (open) {\n // as focus is on input, need to prevent propagation to not close dialog.\n event.stopPropagation();\n }\n setInputValue(convertToInputValue(value));\n setOpen(false);\n return;\n }\n\n case 'Tab': {\n // default focusout will trigger onBlur and we don't want to trigger it twice\n setCurrentValue(currentIndex, 'change');\n setOpen(false);\n return;\n }\n\n case 'Enter': {\n event.preventDefault();\n\n if (isElementInsideTable3OrReport(event.currentTarget)) {\n if (inline && !open) {\n setOpen(true);\n } else if (buttonRef.current && !open) {\n buttonRef.current.click();\n }\n }\n\n if (open) {\n // we need to focusout to trigger onBlur as we are not actually blurring the input\n setCurrentValue(currentIndex, 'focusout');\n setOpen(false);\n }\n\n return;\n }\n\n case 'ArrowDown':\n if (open) {\n event.preventDefault();\n } else if (!inline && buttonRef.current && !isElementInsideTable3OrReport(event.currentTarget)) {\n buttonRef.current.click();\n }\n break;\n\n case 'ArrowUp':\n case 'Home':\n case 'End': {\n if (open) {\n event.preventDefault();\n }\n break;\n }\n\n default:\n }\n\n // we aren't focused on the list, so manually forward the keydown event to it\n if (listRef.current) {\n listRef.current.dispatchEvent(createCustomKeyboardEvent(event));\n }\n\n if (\n inline &&\n !open &&\n (event.key === 'ArrowUp' || event.key === 'ArrowDown') &&\n !isElementInsideTable3OrReport(event.currentTarget)\n ) {\n event.preventDefault();\n const initialIndex = event.key === 'ArrowUp' ? data.length - 1 : 0;\n setCurrentIndex(currentIndex === undefined ? initialIndex : currentIndex);\n setOpen(true);\n }\n }\n\n if (!event.isDefaultPrevented() && onKeyDown) {\n onKeyDown(event);\n }\n };\n\n const handleListboxChange = (index: number): void => {\n setCurrentIndex(index);\n };\n\n const handleListboxClick = (event: React.MouseEvent<HTMLLIElement>, index: number): void => {\n event.preventDefault();\n setCurrentValue(index);\n setOpen(false);\n };\n\n const combobox = {\n 'aria-expanded': open,\n 'aria-owns': listId,\n 'aria-haspopup': 'listbox' as const,\n role: 'combobox',\n };\n\n const input = {\n ...otherProps,\n 'aria-controls': listId,\n // Indicates that the autocomplete behavior of the text input is to suggest a list of possible values in a popup and that the suggestions\n // are related to the string that is present in the textbox\n 'aria-autocomplete': 'list' as const,\n // Enables assistive technologies to know which element the application regards as focused while DOM focus remains on the input element\n 'aria-activedescendant':\n currentIndex !== undefined && data[currentIndex] ? getId(listId, String(data[currentIndex].value)) : undefined,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n disabled,\n onBlur: !disabled && !readOnly ? handleInputBlur : undefined,\n onChange: !disabled && !readOnly ? handleInputChange : undefined,\n onClick: !disabled && !readOnly ? handleInputClick : undefined,\n onKeyDownCapture: !disabled && !readOnly ? handleInputKeyDown : undefined,\n readOnly,\n ref: inputRef,\n type: 'text',\n value: inputValue ?? '',\n };\n\n const list: ScrollableListPropsWithRef = {\n 'aria-labelledby': ariaLabelledBy,\n data,\n disabled,\n id: listId,\n onChange: handleListboxChange,\n onClick: handleListboxClick,\n ref: listRef,\n scrollOnFocus: false,\n tabIndex: -1,\n value: currentIndex,\n };\n\n const button = {\n ref: buttonRef,\n };\n\n return {\n combobox,\n input,\n list,\n button,\n popover: {\n open,\n onOpenChange: setOpen,\n //visible: !data.length ? false : open,\n },\n };\n};\n"],"names":["useMergedRef","React","nanoid","useFlattenedData","findByValue","filterData","getIndexFromValue","setInputValueByRef","sanitizeItem","getOptionParents","isElementInsideTable3OrReport","createCustomKeyboardEvent","input","getId"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useCombobox.js","sources":["../../../src/components/Combobox/useCombobox.tsx"],"sourcesContent":["import debounce from 'lodash/debounce';\nimport { nanoid } from 'nanoid';\nimport * as React from 'react';\n\nimport { useMergedRef } from '../../hooks/useMergedRef';\nimport { isElementInsideTable3OrReport } from '../../utils/dom';\nimport { createCustomKeyboardEvent } from '../../utils/input';\nimport type { InputProps } from '../Input/Input';\nimport type { ScrollableListItem, ScrollableListItemValue, ScrollableListPropsWithRef } from '../Listbox/ScrollableList';\nimport { getId } from '../Listbox/ScrollableList';\nimport {\n setInputValueByRef,\n getIndexFromValue,\n findByValue,\n useFlattenedData,\n sanitizeItem,\n getOptionParents,\n filterData,\n} from '../Listbox/util';\n\nimport type { ComboboxProps } from './Combobox';\n\nconst debouncer = debounce(f => f(), 200);\n\nconst defaultConvertToInputValue = (value: ScrollableListItemValue | undefined) => String(value ?? '');\n\ntype useCombobox = Omit<React.HTMLAttributes<HTMLDivElement>, 'popover'> & {\n combobox: React.HTMLAttributes<HTMLSpanElement>;\n input: Omit<InputProps, 'defaultValue'> & { ref: React.RefObject<HTMLInputElement> };\n list: ScrollableListPropsWithRef;\n button: { ref: any };\n popover: { open: boolean; onOpenChange: (open: boolean) => void };\n};\n\nexport const useCombobox = (props: Omit<ComboboxProps, 'dialog'>, ref: React.Ref<HTMLInputElement>): useCombobox => {\n const {\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n data: unfilteredData = [],\n defaultValue,\n disabled,\n id: _nativeId,\n inline,\n loading: __,\n onChange,\n onClick,\n onKeyDown,\n onSearch,\n readOnly,\n value,\n valueRenderer,\n ...otherProps\n } = props;\n const inputRef = useMergedRef<HTMLInputElement>(ref);\n const buttonRef = React.useRef<HTMLButtonElement>(null);\n const listRef = React.useRef<HTMLUListElement>(null);\n const [open, setOpen] = React.useState(false);\n const listId = React.useMemo(() => nanoid(), []);\n const flattenedData = useFlattenedData(unfilteredData);\n\n const convertToInputValue = React.useCallback(\n (val: ScrollableListItemValue | undefined): string => {\n const option = findByValue(flattenedData, val);\n if (typeof valueRenderer === 'function' && option) {\n return valueRenderer(option);\n }\n return defaultConvertToInputValue(val);\n },\n [valueRenderer, flattenedData]\n );\n\n const [inputValue, setInputValue] = React.useState<string>(convertToInputValue(value));\n const shouldFilterData = !onSearch && (!inline || (inline && inputValue !== convertToInputValue(value)));\n const data = React.useMemo(\n () => (shouldFilterData ? filterData(flattenedData, inputValue) : flattenedData),\n [shouldFilterData, inputValue, flattenedData]\n );\n // listbox/select change value _with_ the index, but combobox changes on select of an index (click/enter), so we need state\n const [currentIndex, setCurrentIndex] = React.useState<number | undefined>(\n inputValue === undefined ? undefined : getIndexFromValue(data, inputValue)\n );\n\n const setInputValueByIndex = (index: number | undefined, event?: string): void => {\n if (index !== undefined) {\n const option = data[index];\n\n if (option && !option.disabled) {\n const eventType = event ?? 'focusout';\n setInputValueByRef(inputRef.current, option.value, eventType);\n }\n }\n };\n\n const setCurrentValue = (index: number | undefined, event?: string) => {\n if (index === undefined) {\n return;\n }\n\n const option = data[index];\n\n if (option?.value === value) {\n // if the selected option is already selected, refill input with its value\n setInputValue(convertToInputValue(value));\n } else {\n // if the selected option is not already selected, trigger blur event\n setInputValueByIndex(index, event);\n }\n };\n\n // ensure the external value is synced with the internal value when mounting, e.g. incase a default value was set\n React.useEffect(() => {\n if (defaultValue && !value) {\n setInputValueByIndex(getIndexFromValue(data, defaultValue));\n }\n }, [data]);\n\n // Sync the input's text when `value` changes 'externally' (selecting an item, a modal, a form reset, etc.).\n // This is done DURING RENDER (React's \"adjust state when a prop changes\" pattern) rather than in an effect,\n // so the corrected inputValue is in place *before* the \"show listbox based on input value\" effect below runs.\n // Doing it in a post-commit effect left a one-render window where that effect saw the *stale* inputValue (the\n // previous value's text) together with a fresh `data` reference and spuriously opened the popover after a reset.\n // React 19's separate commits expose this; React 18 batched the stale render away.\n const [prevValue, setPrevValue] = React.useState(value);\n if (value !== prevValue) {\n setPrevValue(value);\n\n if (value !== undefined) {\n // Compare against the rendered value to handle the valueRenderer case\n // (e.g., value is \"1000\" but the rendered text is \"1000 - Account Name\").\n const renderedValue = convertToInputValue(value);\n if (renderedValue !== inputValue) {\n setInputValue(renderedValue);\n }\n } else if (prevValue !== undefined && inputValue === convertToInputValue(prevValue)) {\n // `value` was cleared to `undefined` externally while the input still shows the *previous* value's\n // rendered text — i.e. the user did not type this — so clear it. We intentionally do NOT clear when the\n // user edited to a non-matching value (their text never equals the previous value's render), so they\n // can keep typing / backspace back to a valid one.\n if (inputValue !== '') {\n setInputValue('');\n }\n }\n }\n\n React.useEffect(() => {\n if (onSearch) {\n debouncer(() => {\n onSearch(inputValue);\n });\n }\n }, [inputValue]);\n\n // show listbox based on input value\n React.useEffect(() => {\n // don't show the popover if the internal (input) value already is the current value\n // this prevents the popover showing after selecting a value or pressing escape\n const isCurrentValue = value !== undefined && value !== null && inputValue === convertToInputValue(value);\n\n if (inputValue && data.length && !isCurrentValue) {\n setCurrentIndex(getIndexFromValue(data, inputValue) || 0);\n\n if (!open) {\n setOpen(true);\n }\n } else {\n setOpen(false);\n }\n }, [inputValue, data]);\n\n React.useEffect(() => {\n if (open) {\n setCurrentIndex(getIndexFromValue(data, inputValue) || 0);\n } else {\n setCurrentIndex(undefined);\n }\n }, [open]);\n\n const findOption = React.useCallback(\n (inputVal: string): ScrollableListItem | undefined => {\n // First try to find by raw value\n const itemByValue = findByValue(flattenedData, inputVal);\n if (itemByValue) {\n return itemByValue;\n }\n\n // If valueRenderer is provided, also try to match against rendered values\n if (valueRenderer) {\n return flattenedData.find(option => valueRenderer(option) === inputVal);\n }\n\n return undefined;\n },\n [flattenedData, valueRenderer]\n );\n\n // event handlers\n const handleInputBlur = (event: React.FocusEvent<HTMLInputElement>): void => {\n if (listRef.current && event.relatedTarget === listRef.current) {\n event.preventDefault();\n return;\n }\n\n const item = findOption(event.target.value);\n\n // If the found item's value equals the current value, just update the display to rendered format\n // This handles the case where user types the raw value but valueRenderer shows something different\n // (e.g., user types \"1000\" but display should be \"1000 - Account Name\")\n if (item && item.value === value) {\n setInputValue(convertToInputValue(value));\n if (props.onBlur) {\n props.onBlur(event);\n }\n return;\n }\n\n // event.target.value is always a string so it is important to cast value to a string (convertToInputValue) before checking the equality\n if (onChange && event.target.value !== convertToInputValue(value)) {\n (event as any).detail = sanitizeItem(item);\n\n const parents = getOptionParents(flattenedData, item?.path);\n\n if (parents !== null && parents.length > 0) {\n (event as any).detail.parents = parents;\n }\n\n onChange(event);\n }\n\n if (props.onBlur) {\n props.onBlur(event);\n }\n };\n\n const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>): void => {\n setInputValue(event.target.value);\n };\n\n const handleInputClick = (event: React.MouseEvent<HTMLInputElement>): void => {\n if (inline || (!open && inputValue && data.length)) {\n setOpen(true);\n }\n\n if (onClick) {\n onClick(event);\n }\n };\n\n const handleInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>): void => {\n // Explicitly stop propagation for arrow keys when Combobox is in open state and inside Table3\n // This ensures Table3 won't react to these keys when using the Combobox\n if (\n isElementInsideTable3OrReport(event.currentTarget) &&\n (event.key === 'ArrowUp' || event.key === 'ArrowDown') &&\n open\n ) {\n event.stopPropagation();\n }\n\n if (!event.ctrlKey && !event.metaKey) {\n switch (event.key) {\n case 'Backspace': {\n return;\n }\n\n case 'Escape': {\n event.preventDefault();\n if (open) {\n // as focus is on input, need to prevent propagation to not close dialog.\n event.stopPropagation();\n }\n setInputValue(convertToInputValue(value));\n setOpen(false);\n return;\n }\n\n case 'Tab': {\n // default focusout will trigger onBlur and we don't want to trigger it twice\n setCurrentValue(currentIndex, 'change');\n setOpen(false);\n return;\n }\n\n case 'Enter': {\n event.preventDefault();\n\n if (isElementInsideTable3OrReport(event.currentTarget)) {\n if (inline && !open) {\n setOpen(true);\n } else if (buttonRef.current && !open) {\n buttonRef.current.click();\n }\n }\n\n if (open) {\n // we need to focusout to trigger onBlur as we are not actually blurring the input\n setCurrentValue(currentIndex, 'focusout');\n setOpen(false);\n }\n\n return;\n }\n\n case 'ArrowDown':\n if (open) {\n event.preventDefault();\n } else if (!inline && buttonRef.current && !isElementInsideTable3OrReport(event.currentTarget)) {\n buttonRef.current.click();\n }\n break;\n\n case 'ArrowUp':\n case 'Home':\n case 'End': {\n if (open) {\n event.preventDefault();\n }\n break;\n }\n\n default:\n }\n\n // we aren't focused on the list, so manually forward the keydown event to it\n if (listRef.current) {\n listRef.current.dispatchEvent(createCustomKeyboardEvent(event));\n }\n\n if (\n inline &&\n !open &&\n (event.key === 'ArrowUp' || event.key === 'ArrowDown') &&\n !isElementInsideTable3OrReport(event.currentTarget)\n ) {\n event.preventDefault();\n const initialIndex = event.key === 'ArrowUp' ? data.length - 1 : 0;\n setCurrentIndex(currentIndex === undefined ? initialIndex : currentIndex);\n setOpen(true);\n }\n }\n\n if (!event.isDefaultPrevented() && onKeyDown) {\n onKeyDown(event);\n }\n };\n\n const handleListboxChange = (index: number): void => {\n setCurrentIndex(index);\n };\n\n const handleListboxClick = (event: React.MouseEvent<HTMLLIElement>, index: number): void => {\n event.preventDefault();\n setCurrentValue(index);\n setOpen(false);\n };\n\n const combobox = {\n 'aria-expanded': open,\n 'aria-owns': listId,\n 'aria-haspopup': 'listbox' as const,\n role: 'combobox',\n };\n\n const input = {\n ...otherProps,\n 'aria-controls': listId,\n // Indicates that the autocomplete behavior of the text input is to suggest a list of possible values in a popup and that the suggestions\n // are related to the string that is present in the textbox\n 'aria-autocomplete': 'list' as const,\n // Enables assistive technologies to know which element the application regards as focused while DOM focus remains on the input element\n 'aria-activedescendant':\n currentIndex !== undefined && data[currentIndex] ? getId(listId, String(data[currentIndex].value)) : undefined,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n disabled,\n onBlur: !disabled && !readOnly ? handleInputBlur : undefined,\n onChange: !disabled && !readOnly ? handleInputChange : undefined,\n onClick: !disabled && !readOnly ? handleInputClick : undefined,\n onKeyDownCapture: !disabled && !readOnly ? handleInputKeyDown : undefined,\n readOnly,\n ref: inputRef,\n type: 'text',\n value: inputValue ?? '',\n };\n\n const list: ScrollableListPropsWithRef = {\n 'aria-labelledby': ariaLabelledBy,\n data,\n disabled,\n id: listId,\n onChange: handleListboxChange,\n onClick: handleListboxClick,\n ref: listRef,\n scrollOnFocus: false,\n tabIndex: -1,\n value: currentIndex,\n };\n\n const button = {\n ref: buttonRef,\n };\n\n return {\n combobox,\n input,\n list,\n button,\n popover: {\n open,\n onOpenChange: setOpen,\n //visible: !data.length ? false : open,\n },\n };\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useCombobox.js","sources":["../../../src/components/Combobox/useCombobox.tsx"],"sourcesContent":["import debounce from 'lodash/debounce';\nimport { nanoid } from 'nanoid';\nimport * as React from 'react';\n\nimport { useMergedRef } from '../../hooks/useMergedRef';\nimport { isElementInsideTable3OrReport } from '../../utils/dom';\nimport { createCustomKeyboardEvent } from '../../utils/input';\nimport type { InputProps } from '../Input/Input';\nimport type { ScrollableListItem, ScrollableListItemValue, ScrollableListPropsWithRef } from '../Listbox/ScrollableList';\nimport { getId } from '../Listbox/ScrollableList';\nimport {\n setInputValueByRef,\n getIndexFromValue,\n findByValue,\n useFlattenedData,\n sanitizeItem,\n getOptionParents,\n filterData,\n} from '../Listbox/util';\n\nimport type { ComboboxProps } from './Combobox';\n\nconst debouncer = debounce(f => f(), 200);\n\nconst defaultConvertToInputValue = (value: ScrollableListItemValue | undefined) => String(value ?? '');\n\ntype useCombobox = Omit<React.HTMLAttributes<HTMLDivElement>, 'popover'> & {\n combobox: React.HTMLAttributes<HTMLSpanElement>;\n input: Omit<InputProps, 'defaultValue'> & { ref: React.RefObject<HTMLInputElement> };\n list: ScrollableListPropsWithRef;\n button: { ref: any };\n popover: { open: boolean; onOpenChange: (open: boolean) => void };\n};\n\nexport const useCombobox = (props: Omit<ComboboxProps, 'dialog'>, ref: React.Ref<HTMLInputElement>): useCombobox => {\n const {\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n data: unfilteredData = [],\n defaultValue,\n disabled,\n id: _nativeId,\n inline,\n loading: __,\n onChange,\n onClick,\n onKeyDown,\n onSearch,\n readOnly,\n value,\n valueRenderer,\n ...otherProps\n } = props;\n const inputRef = useMergedRef<HTMLInputElement>(ref);\n const buttonRef = React.useRef<HTMLButtonElement>(null);\n const listRef = React.useRef<HTMLUListElement>(null);\n const [open, setOpen] = React.useState(false);\n const listId = React.useMemo(() => nanoid(), []);\n const flattenedData = useFlattenedData(unfilteredData);\n\n const convertToInputValue = React.useCallback(\n (val: ScrollableListItemValue | undefined): string => {\n const option = findByValue(flattenedData, val);\n if (typeof valueRenderer === 'function' && option) {\n return valueRenderer(option);\n }\n return defaultConvertToInputValue(val);\n },\n [valueRenderer, flattenedData]\n );\n\n const [inputValue, setInputValue] = React.useState<string>(convertToInputValue(value));\n const shouldFilterData = !onSearch && (!inline || (inline && inputValue !== convertToInputValue(value)));\n const data = React.useMemo(\n () => (shouldFilterData ? filterData(flattenedData, inputValue) : flattenedData),\n [shouldFilterData, inputValue, flattenedData]\n );\n // listbox/select change value _with_ the index, but combobox changes on select of an index (click/enter), so we need state\n const [currentIndex, setCurrentIndex] = React.useState<number | undefined>(\n inputValue === undefined ? undefined : getIndexFromValue(data, inputValue)\n );\n\n const setInputValueByIndex = (index: number | undefined, event?: string): void => {\n if (index !== undefined) {\n const option = data[index];\n\n if (option && !option.disabled) {\n const eventType = event ?? 'focusout';\n setInputValueByRef(inputRef.current, option.value, eventType);\n }\n }\n };\n\n const setCurrentValue = (index: number | undefined, event?: string) => {\n if (index === undefined) {\n return;\n }\n\n const option = data[index];\n\n if (option?.value === value) {\n // if the selected option is already selected, refill input with its value\n setInputValue(convertToInputValue(value));\n } else {\n // if the selected option is not already selected, trigger blur event\n setInputValueByIndex(index, event);\n }\n };\n\n // ensure the external value is synced with the internal value when mounting, e.g. incase a default value was set\n React.useEffect(() => {\n if (defaultValue && !value) {\n setInputValueByIndex(getIndexFromValue(data, defaultValue));\n }\n }, [data]);\n\n // Sync input text when `value` changes externally, during render (not in an effect) so it's correct before the\n // \"show listbox\" effect below — otherwise a one-render stale-text window can open the popover on reset.\n const [prevValue, setPrevValue] = React.useState(value);\n if (value !== prevValue) {\n setPrevValue(value);\n\n if (value !== undefined) {\n // Compare rendered values to handle valueRenderer (e.g. \"1000\" renders as \"1000 - Account Name\").\n const renderedValue = convertToInputValue(value);\n if (renderedValue !== inputValue) {\n setInputValue(renderedValue);\n }\n } else if (prevValue !== undefined && inputValue === convertToInputValue(prevValue)) {\n // `value` was cleared externally and the input still shows its previous rendered text (not user-typed),\n // so clear it. Text the user typed never matches the previous render, so it's preserved.\n if (inputValue !== '') {\n setInputValue('');\n }\n }\n }\n\n React.useEffect(() => {\n if (onSearch) {\n debouncer(() => {\n onSearch(inputValue);\n });\n }\n }, [inputValue]);\n\n // show listbox based on input value\n React.useEffect(() => {\n // don't show the popover if the internal (input) value already is the current value\n // this prevents the popover showing after selecting a value or pressing escape\n const isCurrentValue = value !== undefined && value !== null && inputValue === convertToInputValue(value);\n\n if (inputValue && data.length && !isCurrentValue) {\n setCurrentIndex(getIndexFromValue(data, inputValue) || 0);\n\n if (!open) {\n setOpen(true);\n }\n } else {\n setOpen(false);\n }\n }, [inputValue, data]);\n\n React.useEffect(() => {\n if (open) {\n setCurrentIndex(getIndexFromValue(data, inputValue) || 0);\n } else {\n setCurrentIndex(undefined);\n }\n }, [open]);\n\n const findOption = React.useCallback(\n (inputVal: string): ScrollableListItem | undefined => {\n // First try to find by raw value\n const itemByValue = findByValue(flattenedData, inputVal);\n if (itemByValue) {\n return itemByValue;\n }\n\n // If valueRenderer is provided, also try to match against rendered values\n if (valueRenderer) {\n return flattenedData.find(option => valueRenderer(option) === inputVal);\n }\n\n return undefined;\n },\n [flattenedData, valueRenderer]\n );\n\n // event handlers\n const handleInputBlur = (event: React.FocusEvent<HTMLInputElement>): void => {\n if (listRef.current && event.relatedTarget === listRef.current) {\n event.preventDefault();\n return;\n }\n\n const item = findOption(event.target.value);\n\n // If the found item's value equals the current value, just update the display to rendered format\n // This handles the case where user types the raw value but valueRenderer shows something different\n // (e.g., user types \"1000\" but display should be \"1000 - Account Name\")\n if (item && item.value === value) {\n setInputValue(convertToInputValue(value));\n if (props.onBlur) {\n props.onBlur(event);\n }\n return;\n }\n\n // event.target.value is always a string so it is important to cast value to a string (convertToInputValue) before checking the equality\n if (onChange && event.target.value !== convertToInputValue(value)) {\n (event as any).detail = sanitizeItem(item);\n\n const parents = getOptionParents(flattenedData, item?.path);\n\n if (parents !== null && parents.length > 0) {\n (event as any).detail.parents = parents;\n }\n\n onChange(event);\n }\n\n if (props.onBlur) {\n props.onBlur(event);\n }\n };\n\n const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>): void => {\n setInputValue(event.target.value);\n };\n\n const handleInputClick = (event: React.MouseEvent<HTMLInputElement>): void => {\n if (inline || (!open && inputValue && data.length)) {\n setOpen(true);\n }\n\n if (onClick) {\n onClick(event);\n }\n };\n\n const handleInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>): void => {\n // Explicitly stop propagation for arrow keys when Combobox is in open state and inside Table3\n // This ensures Table3 won't react to these keys when using the Combobox\n if (\n isElementInsideTable3OrReport(event.currentTarget) &&\n (event.key === 'ArrowUp' || event.key === 'ArrowDown') &&\n open\n ) {\n event.stopPropagation();\n }\n\n if (!event.ctrlKey && !event.metaKey) {\n switch (event.key) {\n case 'Backspace': {\n return;\n }\n\n case 'Escape': {\n event.preventDefault();\n if (open) {\n // as focus is on input, need to prevent propagation to not close dialog.\n event.stopPropagation();\n }\n setInputValue(convertToInputValue(value));\n setOpen(false);\n return;\n }\n\n case 'Tab': {\n // default focusout will trigger onBlur and we don't want to trigger it twice\n setCurrentValue(currentIndex, 'change');\n setOpen(false);\n return;\n }\n\n case 'Enter': {\n event.preventDefault();\n\n if (isElementInsideTable3OrReport(event.currentTarget)) {\n if (inline && !open) {\n setOpen(true);\n } else if (buttonRef.current && !open) {\n buttonRef.current.click();\n }\n }\n\n if (open) {\n // we need to focusout to trigger onBlur as we are not actually blurring the input\n setCurrentValue(currentIndex, 'focusout');\n setOpen(false);\n }\n\n return;\n }\n\n case 'ArrowDown':\n if (open) {\n event.preventDefault();\n } else if (!inline && buttonRef.current && !isElementInsideTable3OrReport(event.currentTarget)) {\n buttonRef.current.click();\n }\n break;\n\n case 'ArrowUp':\n case 'Home':\n case 'End': {\n if (open) {\n event.preventDefault();\n }\n break;\n }\n\n default:\n }\n\n // we aren't focused on the list, so manually forward the keydown event to it\n if (listRef.current) {\n listRef.current.dispatchEvent(createCustomKeyboardEvent(event));\n }\n\n if (\n inline &&\n !open &&\n (event.key === 'ArrowUp' || event.key === 'ArrowDown') &&\n !isElementInsideTable3OrReport(event.currentTarget)\n ) {\n event.preventDefault();\n const initialIndex = event.key === 'ArrowUp' ? data.length - 1 : 0;\n setCurrentIndex(currentIndex === undefined ? initialIndex : currentIndex);\n setOpen(true);\n }\n }\n\n if (!event.isDefaultPrevented() && onKeyDown) {\n onKeyDown(event);\n }\n };\n\n const handleListboxChange = (index: number): void => {\n setCurrentIndex(index);\n };\n\n const handleListboxClick = (event: React.MouseEvent<HTMLLIElement>, index: number): void => {\n event.preventDefault();\n setCurrentValue(index);\n setOpen(false);\n };\n\n const combobox = {\n 'aria-expanded': open,\n 'aria-owns': listId,\n 'aria-haspopup': 'listbox' as const,\n role: 'combobox',\n };\n\n const input = {\n ...otherProps,\n 'aria-controls': listId,\n // Indicates that the autocomplete behavior of the text input is to suggest a list of possible values in a popup and that the suggestions\n // are related to the string that is present in the textbox\n 'aria-autocomplete': 'list' as const,\n // Enables assistive technologies to know which element the application regards as focused while DOM focus remains on the input element\n 'aria-activedescendant':\n currentIndex !== undefined && data[currentIndex] ? getId(listId, String(data[currentIndex].value)) : undefined,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n disabled,\n onBlur: !disabled && !readOnly ? handleInputBlur : undefined,\n onChange: !disabled && !readOnly ? handleInputChange : undefined,\n onClick: !disabled && !readOnly ? handleInputClick : undefined,\n onKeyDownCapture: !disabled && !readOnly ? handleInputKeyDown : undefined,\n readOnly,\n ref: inputRef,\n type: 'text',\n value: inputValue ?? '',\n };\n\n const list: ScrollableListPropsWithRef = {\n 'aria-labelledby': ariaLabelledBy,\n data,\n disabled,\n id: listId,\n onChange: handleListboxChange,\n onClick: handleListboxClick,\n ref: listRef,\n scrollOnFocus: false,\n tabIndex: -1,\n value: currentIndex,\n };\n\n const button = {\n ref: buttonRef,\n };\n\n return {\n combobox,\n input,\n list,\n button,\n popover: {\n open,\n onOpenChange: setOpen,\n //visible: !data.length ? false : open,\n },\n };\n};\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useSelect.cjs","sources":["../../../src/components/Select/useSelect.tsx"],"sourcesContent":["import type { Popover as PopoverPrimitive } from '@base-ui/react/popover';\nimport cn from 'clsx';\nimport { nanoid } from 'nanoid';\nimport * as React from 'react';\n\nimport { useMergedRef } from '../../hooks/useMergedRef';\nimport { createCustomEvent } from '../../utils/input';\nimport type { ListboxProps } from '../Listbox/Listbox';\nimport type { ScrollableListItemValue } from '../Listbox/ScrollableList';\nimport { setInputValueByRef, findByValue, useFlattenedData, sanitizeItem } from '../Listbox/util';\nimport { useLocalization } from '../Provider/Localization';\n\nimport type { SelectProps } from './Select';\n\ntype InternalButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement>;\ntype InternalListboxProps = ListboxProps & { ref: React.ForwardedRef<HTMLInputElement> };\ntype InternalInputProps = React.InputHTMLAttributes<HTMLInputElement> & { ref: React.ForwardedRef<HTMLInputElement> };\n\ntype useSelect = Omit<React.HtmlHTMLAttributes<HTMLDivElement>, 'popover'> & {\n button: InternalButtonProps;\n listbox: InternalListboxProps;\n input: InternalInputProps;\n popover: Pick<PopoverPrimitive.Root.Props, 'onOpenChange' | 'open'>;\n text: string | React.JSX.Element;\n more?: number;\n};\n\nexport const useSelect = (\n {\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n data = [],\n defaultValue,\n disabled,\n emptyValue,\n id: nativeId,\n multiselect,\n onBlur,\n onClick: _1,\n onChange,\n readOnly,\n tabIndex,\n value,\n ...otherProps\n }: SelectProps,\n ref: React.Ref<HTMLInputElement>\n): useSelect => {\n const { texts } = useLocalization();\n const flattenedData = useFlattenedData(data);\n const listboxInputRef = useMergedRef<HTMLInputElement>(ref);\n const [open, setOpen] = React.useState(false);\n const id = React.useMemo(() => nativeId || `select_${nanoid()}`, [nativeId]);\n const inputRef = React.useRef<HTMLInputElement>(null);\n const buttonId = `${id}-button`;\n // support 'escape' resetting to the value that was set when the listbox opened\n const [lastValue, setLastValue] = React.useState<ScrollableListItemValue | undefined>(value);\n\n React.useEffect(() => {\n setLastValue(value);\n }, [open]);\n\n React.useEffect(() => {\n if (value === undefined) {\n if (defaultValue !== undefined && findByValue(flattenedData, defaultValue)) {\n setInputValueByRef(inputRef.current, defaultValue);\n } else if (emptyValue !== undefined) {\n setInputValueByRef(inputRef.current, emptyValue);\n } else if (data.length > 0) {\n setInputValueByRef(inputRef.current, data[0]?.value);\n }\n } else if (!multiselect && !findByValue(flattenedData, value)) {\n if (emptyValue !== undefined) {\n setInputValueByRef(inputRef.current, emptyValue);\n } else if (data.length > 0) {\n setInputValueByRef(inputRef.current, data[0]?.value);\n }\n }\n }, []);\n\n // event handlers\n const handleButtonKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>): void => {\n if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {\n event.preventDefault();\n setOpen(true);\n }\n };\n\n const handleListboxKeyDown = (event: React.KeyboardEvent<HTMLElement>): void => {\n switch (event.key) {\n case 'Escape': {\n event.preventDefault();\n if (lastValue !== undefined) {\n setInputValueByRef(listboxInputRef.current, lastValue);\n }\n\n setOpen(false);\n break;\n }\n\n case 'Tab':\n case 'Enter': {\n if (event.key !== 'Tab') {\n event.preventDefault();\n }\n setOpen(false);\n break;\n }\n\n default:\n }\n\n if (otherProps.onKeyDown) {\n otherProps.onKeyDown(event);\n }\n };\n\n const handleListboxClick = (event: React.MouseEvent<HTMLLIElement>): void => {\n event.preventDefault();\n if (!multiselect) {\n setOpen(false);\n if (onBlur) {\n onBlur(createCustomEvent('blur') as unknown as React.FocusEvent<HTMLElement, Element>);\n }\n }\n };\n\n let text: string | React.JSX.Element = '';\n let more = 0;\n\n if (value !== undefined) {\n if (multiselect) {\n const selectedValues = value?.toString().split(',') || [];\n\n if (selectedValues.length === flattenedData.filter(item => !item.disabled).length) {\n text = texts.select.allOptionsSelected;\n } else {\n text = findByValue(flattenedData, selectedValues[0])?.text ?? '';\n more = selectedValues.length > 1 ? selectedValues.length - 1 : 0;\n }\n } else {\n const item = findByValue(flattenedData, value);\n\n if (item) {\n text = item.icon ? (\n <>\n {React.cloneElement(item.icon, {\n className: cn(item.icon.props.className, '-mt-px mr-1'),\n })}\n {item.text}\n </>\n ) : (\n item.text\n );\n }\n }\n }\n\n const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>): void => {\n event.persist();\n\n if (onChange) {\n const item = findByValue(flattenedData, event.target.value);\n (event as any).detail = sanitizeItem(item);\n\n const indexes = item?.path?.split('.') ?? [];\n\n if (indexes.length > 1) {\n // we don't want to map the current item\n indexes.pop();\n // we need to rebuild the path as we map\n let lastPath: string;\n\n (event as any).detail.parents = indexes.map(i => {\n lastPath = lastPath ? [lastPath, i].join('.') : i;\n return sanitizeItem(flattenedData.find(i => i.path === lastPath));\n });\n }\n\n onChange(event);\n }\n };\n\n const handleButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {\n if (readOnly) {\n event.preventDefault();\n }\n };\n\n // select the value text if the select is readonly\n const handleButtonFocus = (event: React.FocusEvent<HTMLButtonElement>) => {\n if (readOnly) {\n const value = event.currentTarget.querySelector(':first-child');\n\n if (value) {\n window.getSelection()?.selectAllChildren?.(value);\n }\n }\n };\n\n const button: InternalButtonProps = {\n 'aria-haspopup': 'listbox' as const,\n 'aria-label': ariaLabel ? `${ariaLabel} ${text}` : undefined,\n 'aria-labelledby': ariaLabelledBy ? `${ariaLabelledBy} ${buttonId}` : undefined,\n 'aria-readonly': readOnly ? 'true' : undefined,\n disabled: disabled,\n id: buttonId,\n onClick: handleButtonClick,\n onFocus: handleButtonFocus,\n onKeyDown: !disabled && !readOnly ? handleButtonKeyDown : undefined,\n tabIndex,\n type: 'button',\n };\n\n const listbox: InternalListboxProps = {\n ...otherProps,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n data,\n disabled,\n emptyValue,\n onBlur,\n onClick: handleListboxClick,\n onChange: event => {\n setInputValueByRef(inputRef.current, event.target.value);\n },\n onKeyDown: handleListboxKeyDown,\n ref: listboxInputRef,\n value,\n };\n\n const input: InternalInputProps = {\n onChange: handleInputChange,\n ref: inputRef,\n value: String(value ?? ''),\n };\n\n return {\n button,\n listbox,\n input,\n popover: {\n open,\n onOpenChange: setOpen,\n },\n text,\n more,\n };\n};\n"],"names":["useLocalization","useFlattenedData","useMergedRef","React","nanoid","findByValue","setInputValueByRef","createCustomEvent","sanitizeItem","i","value","input"],"mappings":"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"}
1
+ {"version":3,"file":"useSelect.cjs","sources":["../../../src/components/Select/useSelect.tsx"],"sourcesContent":["import type { Popover as PopoverPrimitive } from '@base-ui/react/popover';\nimport cn from 'clsx';\nimport { nanoid } from 'nanoid';\nimport * as React from 'react';\n\nimport { useMergedRef } from '../../hooks/useMergedRef';\nimport { createCustomEvent } from '../../utils/input';\nimport type { ListboxProps } from '../Listbox/Listbox';\nimport type { ScrollableListItemValue } from '../Listbox/ScrollableList';\nimport { setInputValueByRef, findByValue, useFlattenedData, sanitizeItem } from '../Listbox/util';\nimport { useLocalization } from '../Provider/Localization';\n\nimport type { SelectProps } from './Select';\n\ntype InternalButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement>;\ntype InternalListboxProps = ListboxProps & { ref: React.ForwardedRef<HTMLInputElement> };\ntype InternalInputProps = React.InputHTMLAttributes<HTMLInputElement> & { ref: React.ForwardedRef<HTMLInputElement> };\n\ntype useSelect = Omit<React.HTMLAttributes<HTMLDivElement>, 'popover'> & {\n button: InternalButtonProps;\n listbox: InternalListboxProps;\n input: InternalInputProps;\n popover: Pick<PopoverPrimitive.Root.Props, 'onOpenChange' | 'open'>;\n text: string | React.JSX.Element;\n more?: number;\n};\n\nexport const useSelect = (\n {\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n data = [],\n defaultValue,\n disabled,\n emptyValue,\n id: nativeId,\n multiselect,\n onBlur,\n onClick: _1,\n onChange,\n readOnly,\n tabIndex,\n value,\n ...otherProps\n }: SelectProps,\n ref: React.Ref<HTMLInputElement>\n): useSelect => {\n const { texts } = useLocalization();\n const flattenedData = useFlattenedData(data);\n const listboxInputRef = useMergedRef<HTMLInputElement>(ref);\n const [open, setOpen] = React.useState(false);\n const id = React.useMemo(() => nativeId || `select_${nanoid()}`, [nativeId]);\n const inputRef = React.useRef<HTMLInputElement>(null);\n const buttonId = `${id}-button`;\n // support 'escape' resetting to the value that was set when the listbox opened\n const [lastValue, setLastValue] = React.useState<ScrollableListItemValue | undefined>(value);\n\n React.useEffect(() => {\n setLastValue(value);\n }, [open]);\n\n React.useEffect(() => {\n if (value === undefined) {\n if (defaultValue !== undefined && findByValue(flattenedData, defaultValue)) {\n setInputValueByRef(inputRef.current, defaultValue);\n } else if (emptyValue !== undefined) {\n setInputValueByRef(inputRef.current, emptyValue);\n } else if (data.length > 0) {\n setInputValueByRef(inputRef.current, data[0]?.value);\n }\n } else if (!multiselect && !findByValue(flattenedData, value)) {\n if (emptyValue !== undefined) {\n setInputValueByRef(inputRef.current, emptyValue);\n } else if (data.length > 0) {\n setInputValueByRef(inputRef.current, data[0]?.value);\n }\n }\n }, []);\n\n // event handlers\n const handleButtonKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>): void => {\n if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {\n event.preventDefault();\n setOpen(true);\n }\n };\n\n const handleListboxKeyDown = (event: React.KeyboardEvent<HTMLElement>): void => {\n switch (event.key) {\n case 'Escape': {\n event.preventDefault();\n if (lastValue !== undefined) {\n setInputValueByRef(listboxInputRef.current, lastValue);\n }\n\n setOpen(false);\n break;\n }\n\n case 'Tab':\n case 'Enter': {\n if (event.key !== 'Tab') {\n event.preventDefault();\n }\n setOpen(false);\n break;\n }\n\n default:\n }\n\n if (otherProps.onKeyDown) {\n otherProps.onKeyDown(event);\n }\n };\n\n const handleListboxClick = (event: React.MouseEvent<HTMLLIElement>): void => {\n event.preventDefault();\n if (!multiselect) {\n setOpen(false);\n if (onBlur) {\n onBlur(createCustomEvent('blur') as unknown as React.FocusEvent<HTMLElement, Element>);\n }\n }\n };\n\n let text: string | React.JSX.Element = '';\n let more = 0;\n\n if (value !== undefined) {\n if (multiselect) {\n const selectedValues = value?.toString().split(',') || [];\n\n if (selectedValues.length === flattenedData.filter(item => !item.disabled).length) {\n text = texts.select.allOptionsSelected;\n } else {\n text = findByValue(flattenedData, selectedValues[0])?.text ?? '';\n more = selectedValues.length > 1 ? selectedValues.length - 1 : 0;\n }\n } else {\n const item = findByValue(flattenedData, value);\n\n if (item) {\n text = item.icon ? (\n <>\n {React.cloneElement(item.icon, {\n className: cn(item.icon.props.className, '-mt-px mr-1'),\n })}\n {item.text}\n </>\n ) : (\n item.text\n );\n }\n }\n }\n\n const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>): void => {\n event.persist();\n\n if (onChange) {\n const item = findByValue(flattenedData, event.target.value);\n (event as any).detail = sanitizeItem(item);\n\n const indexes = item?.path?.split('.') ?? [];\n\n if (indexes.length > 1) {\n // we don't want to map the current item\n indexes.pop();\n // we need to rebuild the path as we map\n let lastPath: string;\n\n (event as any).detail.parents = indexes.map(i => {\n lastPath = lastPath ? [lastPath, i].join('.') : i;\n return sanitizeItem(flattenedData.find(i => i.path === lastPath));\n });\n }\n\n onChange(event);\n }\n };\n\n const handleButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {\n if (readOnly) {\n event.preventDefault();\n }\n };\n\n // select the value text if the select is readonly\n const handleButtonFocus = (event: React.FocusEvent<HTMLButtonElement>) => {\n if (readOnly) {\n const value = event.currentTarget.querySelector(':first-child');\n\n if (value) {\n window.getSelection()?.selectAllChildren?.(value);\n }\n }\n };\n\n const button: InternalButtonProps = {\n 'aria-haspopup': 'listbox' as const,\n 'aria-label': ariaLabel ? `${ariaLabel} ${text}` : undefined,\n 'aria-labelledby': ariaLabelledBy ? `${ariaLabelledBy} ${buttonId}` : undefined,\n 'aria-readonly': readOnly ? 'true' : undefined,\n disabled: disabled,\n id: buttonId,\n onClick: handleButtonClick,\n onFocus: handleButtonFocus,\n onKeyDown: !disabled && !readOnly ? handleButtonKeyDown : undefined,\n tabIndex,\n type: 'button',\n };\n\n const listbox: InternalListboxProps = {\n ...otherProps,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n data,\n disabled,\n emptyValue,\n onBlur,\n onClick: handleListboxClick,\n onChange: event => {\n setInputValueByRef(inputRef.current, event.target.value);\n },\n onKeyDown: handleListboxKeyDown,\n ref: listboxInputRef,\n value,\n };\n\n const input: InternalInputProps = {\n onChange: handleInputChange,\n ref: inputRef,\n value: String(value ?? ''),\n };\n\n return {\n button,\n listbox,\n input,\n popover: {\n open,\n onOpenChange: setOpen,\n },\n text,\n more,\n };\n};\n"],"names":["useLocalization","useFlattenedData","useMergedRef","React","nanoid","findByValue","setInputValueByRef","createCustomEvent","sanitizeItem","i","value","input"],"mappings":"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"}
@@ -9,7 +9,7 @@ type InternalListboxProps = ListboxProps & {
9
9
  type InternalInputProps = React.InputHTMLAttributes<HTMLInputElement> & {
10
10
  ref: React.ForwardedRef<HTMLInputElement>;
11
11
  };
12
- type useSelect = Omit<React.HtmlHTMLAttributes<HTMLDivElement>, 'popover'> & {
12
+ type useSelect = Omit<React.HTMLAttributes<HTMLDivElement>, 'popover'> & {
13
13
  button: InternalButtonProps;
14
14
  listbox: InternalListboxProps;
15
15
  input: InternalInputProps;
@@ -1 +1 @@
1
- {"version":3,"file":"useSelect.js","sources":["../../../src/components/Select/useSelect.tsx"],"sourcesContent":["import type { Popover as PopoverPrimitive } from '@base-ui/react/popover';\nimport cn from 'clsx';\nimport { nanoid } from 'nanoid';\nimport * as React from 'react';\n\nimport { useMergedRef } from '../../hooks/useMergedRef';\nimport { createCustomEvent } from '../../utils/input';\nimport type { ListboxProps } from '../Listbox/Listbox';\nimport type { ScrollableListItemValue } from '../Listbox/ScrollableList';\nimport { setInputValueByRef, findByValue, useFlattenedData, sanitizeItem } from '../Listbox/util';\nimport { useLocalization } from '../Provider/Localization';\n\nimport type { SelectProps } from './Select';\n\ntype InternalButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement>;\ntype InternalListboxProps = ListboxProps & { ref: React.ForwardedRef<HTMLInputElement> };\ntype InternalInputProps = React.InputHTMLAttributes<HTMLInputElement> & { ref: React.ForwardedRef<HTMLInputElement> };\n\ntype useSelect = Omit<React.HtmlHTMLAttributes<HTMLDivElement>, 'popover'> & {\n button: InternalButtonProps;\n listbox: InternalListboxProps;\n input: InternalInputProps;\n popover: Pick<PopoverPrimitive.Root.Props, 'onOpenChange' | 'open'>;\n text: string | React.JSX.Element;\n more?: number;\n};\n\nexport const useSelect = (\n {\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n data = [],\n defaultValue,\n disabled,\n emptyValue,\n id: nativeId,\n multiselect,\n onBlur,\n onClick: _1,\n onChange,\n readOnly,\n tabIndex,\n value,\n ...otherProps\n }: SelectProps,\n ref: React.Ref<HTMLInputElement>\n): useSelect => {\n const { texts } = useLocalization();\n const flattenedData = useFlattenedData(data);\n const listboxInputRef = useMergedRef<HTMLInputElement>(ref);\n const [open, setOpen] = React.useState(false);\n const id = React.useMemo(() => nativeId || `select_${nanoid()}`, [nativeId]);\n const inputRef = React.useRef<HTMLInputElement>(null);\n const buttonId = `${id}-button`;\n // support 'escape' resetting to the value that was set when the listbox opened\n const [lastValue, setLastValue] = React.useState<ScrollableListItemValue | undefined>(value);\n\n React.useEffect(() => {\n setLastValue(value);\n }, [open]);\n\n React.useEffect(() => {\n if (value === undefined) {\n if (defaultValue !== undefined && findByValue(flattenedData, defaultValue)) {\n setInputValueByRef(inputRef.current, defaultValue);\n } else if (emptyValue !== undefined) {\n setInputValueByRef(inputRef.current, emptyValue);\n } else if (data.length > 0) {\n setInputValueByRef(inputRef.current, data[0]?.value);\n }\n } else if (!multiselect && !findByValue(flattenedData, value)) {\n if (emptyValue !== undefined) {\n setInputValueByRef(inputRef.current, emptyValue);\n } else if (data.length > 0) {\n setInputValueByRef(inputRef.current, data[0]?.value);\n }\n }\n }, []);\n\n // event handlers\n const handleButtonKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>): void => {\n if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {\n event.preventDefault();\n setOpen(true);\n }\n };\n\n const handleListboxKeyDown = (event: React.KeyboardEvent<HTMLElement>): void => {\n switch (event.key) {\n case 'Escape': {\n event.preventDefault();\n if (lastValue !== undefined) {\n setInputValueByRef(listboxInputRef.current, lastValue);\n }\n\n setOpen(false);\n break;\n }\n\n case 'Tab':\n case 'Enter': {\n if (event.key !== 'Tab') {\n event.preventDefault();\n }\n setOpen(false);\n break;\n }\n\n default:\n }\n\n if (otherProps.onKeyDown) {\n otherProps.onKeyDown(event);\n }\n };\n\n const handleListboxClick = (event: React.MouseEvent<HTMLLIElement>): void => {\n event.preventDefault();\n if (!multiselect) {\n setOpen(false);\n if (onBlur) {\n onBlur(createCustomEvent('blur') as unknown as React.FocusEvent<HTMLElement, Element>);\n }\n }\n };\n\n let text: string | React.JSX.Element = '';\n let more = 0;\n\n if (value !== undefined) {\n if (multiselect) {\n const selectedValues = value?.toString().split(',') || [];\n\n if (selectedValues.length === flattenedData.filter(item => !item.disabled).length) {\n text = texts.select.allOptionsSelected;\n } else {\n text = findByValue(flattenedData, selectedValues[0])?.text ?? '';\n more = selectedValues.length > 1 ? selectedValues.length - 1 : 0;\n }\n } else {\n const item = findByValue(flattenedData, value);\n\n if (item) {\n text = item.icon ? (\n <>\n {React.cloneElement(item.icon, {\n className: cn(item.icon.props.className, '-mt-px mr-1'),\n })}\n {item.text}\n </>\n ) : (\n item.text\n );\n }\n }\n }\n\n const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>): void => {\n event.persist();\n\n if (onChange) {\n const item = findByValue(flattenedData, event.target.value);\n (event as any).detail = sanitizeItem(item);\n\n const indexes = item?.path?.split('.') ?? [];\n\n if (indexes.length > 1) {\n // we don't want to map the current item\n indexes.pop();\n // we need to rebuild the path as we map\n let lastPath: string;\n\n (event as any).detail.parents = indexes.map(i => {\n lastPath = lastPath ? [lastPath, i].join('.') : i;\n return sanitizeItem(flattenedData.find(i => i.path === lastPath));\n });\n }\n\n onChange(event);\n }\n };\n\n const handleButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {\n if (readOnly) {\n event.preventDefault();\n }\n };\n\n // select the value text if the select is readonly\n const handleButtonFocus = (event: React.FocusEvent<HTMLButtonElement>) => {\n if (readOnly) {\n const value = event.currentTarget.querySelector(':first-child');\n\n if (value) {\n window.getSelection()?.selectAllChildren?.(value);\n }\n }\n };\n\n const button: InternalButtonProps = {\n 'aria-haspopup': 'listbox' as const,\n 'aria-label': ariaLabel ? `${ariaLabel} ${text}` : undefined,\n 'aria-labelledby': ariaLabelledBy ? `${ariaLabelledBy} ${buttonId}` : undefined,\n 'aria-readonly': readOnly ? 'true' : undefined,\n disabled: disabled,\n id: buttonId,\n onClick: handleButtonClick,\n onFocus: handleButtonFocus,\n onKeyDown: !disabled && !readOnly ? handleButtonKeyDown : undefined,\n tabIndex,\n type: 'button',\n };\n\n const listbox: InternalListboxProps = {\n ...otherProps,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n data,\n disabled,\n emptyValue,\n onBlur,\n onClick: handleListboxClick,\n onChange: event => {\n setInputValueByRef(inputRef.current, event.target.value);\n },\n onKeyDown: handleListboxKeyDown,\n ref: listboxInputRef,\n value,\n };\n\n const input: InternalInputProps = {\n onChange: handleInputChange,\n ref: inputRef,\n value: String(value ?? ''),\n };\n\n return {\n button,\n listbox,\n input,\n popover: {\n open,\n onOpenChange: setOpen,\n },\n text,\n more,\n };\n};\n"],"names":["i","value"],"mappings":"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"}
1
+ {"version":3,"file":"useSelect.js","sources":["../../../src/components/Select/useSelect.tsx"],"sourcesContent":["import type { Popover as PopoverPrimitive } from '@base-ui/react/popover';\nimport cn from 'clsx';\nimport { nanoid } from 'nanoid';\nimport * as React from 'react';\n\nimport { useMergedRef } from '../../hooks/useMergedRef';\nimport { createCustomEvent } from '../../utils/input';\nimport type { ListboxProps } from '../Listbox/Listbox';\nimport type { ScrollableListItemValue } from '../Listbox/ScrollableList';\nimport { setInputValueByRef, findByValue, useFlattenedData, sanitizeItem } from '../Listbox/util';\nimport { useLocalization } from '../Provider/Localization';\n\nimport type { SelectProps } from './Select';\n\ntype InternalButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement>;\ntype InternalListboxProps = ListboxProps & { ref: React.ForwardedRef<HTMLInputElement> };\ntype InternalInputProps = React.InputHTMLAttributes<HTMLInputElement> & { ref: React.ForwardedRef<HTMLInputElement> };\n\ntype useSelect = Omit<React.HTMLAttributes<HTMLDivElement>, 'popover'> & {\n button: InternalButtonProps;\n listbox: InternalListboxProps;\n input: InternalInputProps;\n popover: Pick<PopoverPrimitive.Root.Props, 'onOpenChange' | 'open'>;\n text: string | React.JSX.Element;\n more?: number;\n};\n\nexport const useSelect = (\n {\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n data = [],\n defaultValue,\n disabled,\n emptyValue,\n id: nativeId,\n multiselect,\n onBlur,\n onClick: _1,\n onChange,\n readOnly,\n tabIndex,\n value,\n ...otherProps\n }: SelectProps,\n ref: React.Ref<HTMLInputElement>\n): useSelect => {\n const { texts } = useLocalization();\n const flattenedData = useFlattenedData(data);\n const listboxInputRef = useMergedRef<HTMLInputElement>(ref);\n const [open, setOpen] = React.useState(false);\n const id = React.useMemo(() => nativeId || `select_${nanoid()}`, [nativeId]);\n const inputRef = React.useRef<HTMLInputElement>(null);\n const buttonId = `${id}-button`;\n // support 'escape' resetting to the value that was set when the listbox opened\n const [lastValue, setLastValue] = React.useState<ScrollableListItemValue | undefined>(value);\n\n React.useEffect(() => {\n setLastValue(value);\n }, [open]);\n\n React.useEffect(() => {\n if (value === undefined) {\n if (defaultValue !== undefined && findByValue(flattenedData, defaultValue)) {\n setInputValueByRef(inputRef.current, defaultValue);\n } else if (emptyValue !== undefined) {\n setInputValueByRef(inputRef.current, emptyValue);\n } else if (data.length > 0) {\n setInputValueByRef(inputRef.current, data[0]?.value);\n }\n } else if (!multiselect && !findByValue(flattenedData, value)) {\n if (emptyValue !== undefined) {\n setInputValueByRef(inputRef.current, emptyValue);\n } else if (data.length > 0) {\n setInputValueByRef(inputRef.current, data[0]?.value);\n }\n }\n }, []);\n\n // event handlers\n const handleButtonKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>): void => {\n if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {\n event.preventDefault();\n setOpen(true);\n }\n };\n\n const handleListboxKeyDown = (event: React.KeyboardEvent<HTMLElement>): void => {\n switch (event.key) {\n case 'Escape': {\n event.preventDefault();\n if (lastValue !== undefined) {\n setInputValueByRef(listboxInputRef.current, lastValue);\n }\n\n setOpen(false);\n break;\n }\n\n case 'Tab':\n case 'Enter': {\n if (event.key !== 'Tab') {\n event.preventDefault();\n }\n setOpen(false);\n break;\n }\n\n default:\n }\n\n if (otherProps.onKeyDown) {\n otherProps.onKeyDown(event);\n }\n };\n\n const handleListboxClick = (event: React.MouseEvent<HTMLLIElement>): void => {\n event.preventDefault();\n if (!multiselect) {\n setOpen(false);\n if (onBlur) {\n onBlur(createCustomEvent('blur') as unknown as React.FocusEvent<HTMLElement, Element>);\n }\n }\n };\n\n let text: string | React.JSX.Element = '';\n let more = 0;\n\n if (value !== undefined) {\n if (multiselect) {\n const selectedValues = value?.toString().split(',') || [];\n\n if (selectedValues.length === flattenedData.filter(item => !item.disabled).length) {\n text = texts.select.allOptionsSelected;\n } else {\n text = findByValue(flattenedData, selectedValues[0])?.text ?? '';\n more = selectedValues.length > 1 ? selectedValues.length - 1 : 0;\n }\n } else {\n const item = findByValue(flattenedData, value);\n\n if (item) {\n text = item.icon ? (\n <>\n {React.cloneElement(item.icon, {\n className: cn(item.icon.props.className, '-mt-px mr-1'),\n })}\n {item.text}\n </>\n ) : (\n item.text\n );\n }\n }\n }\n\n const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>): void => {\n event.persist();\n\n if (onChange) {\n const item = findByValue(flattenedData, event.target.value);\n (event as any).detail = sanitizeItem(item);\n\n const indexes = item?.path?.split('.') ?? [];\n\n if (indexes.length > 1) {\n // we don't want to map the current item\n indexes.pop();\n // we need to rebuild the path as we map\n let lastPath: string;\n\n (event as any).detail.parents = indexes.map(i => {\n lastPath = lastPath ? [lastPath, i].join('.') : i;\n return sanitizeItem(flattenedData.find(i => i.path === lastPath));\n });\n }\n\n onChange(event);\n }\n };\n\n const handleButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {\n if (readOnly) {\n event.preventDefault();\n }\n };\n\n // select the value text if the select is readonly\n const handleButtonFocus = (event: React.FocusEvent<HTMLButtonElement>) => {\n if (readOnly) {\n const value = event.currentTarget.querySelector(':first-child');\n\n if (value) {\n window.getSelection()?.selectAllChildren?.(value);\n }\n }\n };\n\n const button: InternalButtonProps = {\n 'aria-haspopup': 'listbox' as const,\n 'aria-label': ariaLabel ? `${ariaLabel} ${text}` : undefined,\n 'aria-labelledby': ariaLabelledBy ? `${ariaLabelledBy} ${buttonId}` : undefined,\n 'aria-readonly': readOnly ? 'true' : undefined,\n disabled: disabled,\n id: buttonId,\n onClick: handleButtonClick,\n onFocus: handleButtonFocus,\n onKeyDown: !disabled && !readOnly ? handleButtonKeyDown : undefined,\n tabIndex,\n type: 'button',\n };\n\n const listbox: InternalListboxProps = {\n ...otherProps,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n data,\n disabled,\n emptyValue,\n onBlur,\n onClick: handleListboxClick,\n onChange: event => {\n setInputValueByRef(inputRef.current, event.target.value);\n },\n onKeyDown: handleListboxKeyDown,\n ref: listboxInputRef,\n value,\n };\n\n const input: InternalInputProps = {\n onChange: handleInputChange,\n ref: inputRef,\n value: String(value ?? ''),\n };\n\n return {\n button,\n listbox,\n input,\n popover: {\n open,\n onOpenChange: setOpen,\n },\n text,\n more,\n };\n};\n"],"names":["i","value"],"mappings":"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"}
@@ -66,8 +66,9 @@ function Alert(props) {
66
66
  } catch {
67
67
  }
68
68
  let label = "";
69
- if (rowIdentityColumn) {
70
- label = String(row?.[rowIdentityColumn.id] ?? "");
69
+ const rowIdentity = rowIdentityColumn ? row?.[rowIdentityColumn.id] : void 0;
70
+ if (rowIdentity !== null && rowIdentity !== void 0) {
71
+ label = String(rowIdentity);
71
72
  } else if (pendingChangeWithError.index !== void 0) {
72
73
  label = String(pendingChangeWithError.index + 1);
73
74
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Alert.cjs","sources":["../../../../../src/components/Table3/components/Editing/Alert.tsx"],"sourcesContent":["import type { Table as ReactTable, TableMeta as ReactTableMeta } from '@tanstack/react-table';\nimport type { ScrollToOptions as ReactVirtualScrollToOptions } from '@tanstack/react-virtual';\nimport React from 'react';\n\nimport type { TableRef } from '../../../../primitives/Table/types';\nimport { Button } from '../../../Actions/Button/Button';\nimport type { AlertProps as BaseAlertProps } from '../../../Alert/Alert';\nimport { Alert as BaseAlert } from '../../../Alert/Alert';\nimport type { DialogProps } from '../../../Dialog/Dialog';\nimport { Dialog } from '../../../Dialog/Dialog';\nimport { Inline } from '../../../Inline/Inline';\nimport { useLocalization } from '../../../Provider/Localization';\nimport { Tooltip } from '../../../Tooltip/Tooltip';\n\ntype AlertProps<TType = unknown> = Omit<BaseAlertProps, 'children'> & {\n scrollToIndex: (index: number, options: ReactVirtualScrollToOptions) => void;\n table: ReactTable<TType>;\n tableRef: React.RefObject<TableRef>;\n};\n\nexport function Alert<TType = unknown>(props: AlertProps<TType>) {\n const { scrollToIndex, table, tableRef, ...attributes } = props;\n const { texts } = useLocalization();\n const validationTexts = texts.table3.editing.validation;\n const tableMeta = table.options.meta as ReactTableMeta<TType>;\n const [showFilterResetDialog, setShowFilterResetDialog] = React.useState<string | false>(false);\n // row to scroll to and focus once the filters have been reset and it becomes visible again\n const [pendingFocusRowId, setPendingFocusRowId] = React.useState<string | null>(null);\n const pendingChangesWithErrors = tableMeta.editing.getErrorsShownInAlert<TType>();\n\n const visibleRows = table.getRowModel().rows;\n\n function getVisibleRowIndex(rowId: string) {\n return visibleRows.findIndex(row => row.id === rowId);\n }\n\n function scrollToRow(rowIndex: number) {\n tableMeta.rowActive.setRowActiveIndex(rowIndex);\n scrollToIndex(rowIndex, { align: 'center' });\n\n requestAnimationFrame(() => {\n const cell = tableRef.current?.querySelector(\n 'tbody > tr[data-row-active=\"true\"] > td[data-cell-editing-invalid=\"true\"]'\n );\n\n if (cell) {\n (cell as HTMLElement).focus?.();\n }\n });\n }\n\n // generate the \"N unsaved entries\" title\n const title = (\n pendingChangesWithErrors.length === 1 ? validationTexts.alert.titleOne : validationTexts.alert.titlePlural\n ).replace('[COUNT]', String(pendingChangesWithErrors.length));\n\n // generate links to each invalid row, to go into the error message\n const links: React.ReactNode[] = [];\n const visibleColumns = table.getVisibleFlatColumns().map(c => c.id);\n const rowIdentityColumn =\n tableMeta.rowIdentityAccessor && visibleColumns.includes(String(tableMeta.rowIdentityAccessor))\n ? // table.getColumn(columName) throw error in strict dev mode. Related thread: https://github.com/TanStack/table/discussions/5505\n table.getAllColumns().find(x => x.id === String(tableMeta.rowIdentityAccessor))\n : undefined;\n\n pendingChangesWithErrors.forEach((pendingChangeWithError, index) => {\n // if appropriate, concatenate the item with the text \"and\"\n if (pendingChangesWithErrors.length > 1 && index === pendingChangesWithErrors.length - 1) {\n // Add space before and after `messageAnd` text\n links.push(` ${validationTexts.alert.messageAnd} `);\n }\n\n const handleClick = () => {\n const visibleIndex = getVisibleRowIndex(pendingChangeWithError.rowId);\n\n if (visibleIndex > -1) {\n scrollToRow(visibleIndex);\n } else {\n setShowFilterResetDialog(pendingChangeWithError.rowId);\n }\n };\n\n let tooltip;\n\n if (pendingChangeWithError.errors.row) {\n tooltip = pendingChangeWithError.errors.row;\n } else {\n const firstCellErrorColumnId = Object.keys(pendingChangeWithError.errors.cells)[0];\n const columnName = table.getAllColumns().find(column => column.id === firstCellErrorColumnId)?.columnDef.meta?.header;\n\n if (columnName && firstCellErrorColumnId) {\n tooltip = `${columnName}: ${pendingChangeWithError.errors.cells[firstCellErrorColumnId]}`;\n }\n }\n\n let row;\n try {\n row = table.getRow(pendingChangeWithError.rowId).original;\n } catch {\n // because of server loading, some rows may not be accessible\n }\n\n // the row identity when there is one, otherwise the 1-based position, which server loading can leave unknown\n let label = '';\n\n if (rowIdentityColumn) {\n label = String(row?.[rowIdentityColumn.id as keyof TType] ?? '');\n } else if (pendingChangeWithError.index !== undefined) {\n label = String(pendingChangeWithError.index + 1);\n }\n\n links.push(\n <Tooltip key={pendingChangeWithError.rowId} title={tooltip ?? ''}>\n <span className=\"text-blue\" onClick={handleClick} role=\"button\">\n {label}\n </span>\n </Tooltip>\n );\n\n // if appropriate, concatenate the item with the text \",\"\n if (pendingChangesWithErrors.length > 2 && index < pendingChangesWithErrors.length - 2) {\n links.push(', ');\n }\n });\n\n // generate the \"Row N is incomplete and hasn't been saved\" error message\n const message = (links.length === 1 ? validationTexts.alert.messageOne : validationTexts.alert.messagePlural)\n .split(/(\\[\\w+\\])/)\n .map(part => {\n if (part === '[COLUMN]') {\n return rowIdentityColumn?.columnDef.meta?.header ?? validationTexts.alert.messageRow;\n }\n\n if (part === '[ROW]') {\n return links;\n }\n\n return part;\n });\n\n const handleResetFilters = () => {\n const rowId = showFilterResetDialog;\n\n table.resetGlobalFilter();\n table.resetColumnFilters();\n\n // defer focusing until the row model has re-rendered with the filters cleared,\n // otherwise the row isn't in the model/DOM yet\n if (typeof rowId === 'string') {\n setPendingFocusRowId(rowId);\n }\n\n setShowFilterResetDialog(false);\n };\n\n // once the filters are reset and the previously hidden row is visible again, scroll to and focus it\n React.useEffect(() => {\n if (pendingFocusRowId === null) {\n return;\n }\n\n const visibleIndex = getVisibleRowIndex(pendingFocusRowId);\n\n if (visibleIndex > -1) {\n scrollToRow(visibleIndex);\n setPendingFocusRowId(null);\n }\n }, [pendingFocusRowId, visibleRows]);\n\n return (\n <>\n <BaseAlert {...attributes} state=\"error\">\n <span className=\"font-bold\">{title}</span>&nbsp;{message}\n </BaseAlert>\n <FilterResetDialog\n open={showFilterResetDialog !== false}\n onChange={() => setShowFilterResetDialog(false)}\n onSubmit={handleResetFilters}\n />\n </>\n );\n}\n\ntype FilterResetDialogProps = Omit<DialogProps, 'children'> & {\n onSubmit: () => void;\n};\n\nfunction FilterResetDialog(props: FilterResetDialogProps) {\n const { onSubmit: handleSubmit, ...dialogProps } = props;\n const { texts } = useLocalization();\n\n return (\n <Dialog {...dialogProps} size=\"xs\">\n <Dialog.Content aria-label={texts.table3.editing.validation.resetFiltersDialog.title}>\n <Dialog.Title>{texts.table3.editing.validation.resetFiltersDialog.title}</Dialog.Title>\n <p>{texts.table3.editing.validation.resetFiltersDialog.description}</p>\n <Dialog.Footer>\n <Inline spacing={8} alignInline=\"end\">\n <Dialog.Close>\n <Button>{texts.table3.editing.validation.resetFiltersDialog.cancel}</Button>\n </Dialog.Close>\n <Button variant=\"primary\" onClick={handleSubmit}>\n {texts.table3.editing.validation.resetFiltersDialog.confirm}\n </Button>\n </Inline>\n </Dialog.Footer>\n </Dialog.Content>\n </Dialog>\n );\n}\n"],"names":["useLocalization","Tooltip","BaseAlert","Dialog","Inline","Button"],"mappings":"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"}
1
+ {"version":3,"file":"Alert.cjs","sources":["../../../../../src/components/Table3/components/Editing/Alert.tsx"],"sourcesContent":["import type { Table as ReactTable, TableMeta as ReactTableMeta } from '@tanstack/react-table';\nimport type { ScrollToOptions as ReactVirtualScrollToOptions } from '@tanstack/react-virtual';\nimport React from 'react';\n\nimport type { TableRef } from '../../../../primitives/Table/types';\nimport { Button } from '../../../Actions/Button/Button';\nimport type { AlertProps as BaseAlertProps } from '../../../Alert/Alert';\nimport { Alert as BaseAlert } from '../../../Alert/Alert';\nimport type { DialogProps } from '../../../Dialog/Dialog';\nimport { Dialog } from '../../../Dialog/Dialog';\nimport { Inline } from '../../../Inline/Inline';\nimport { useLocalization } from '../../../Provider/Localization';\nimport { Tooltip } from '../../../Tooltip/Tooltip';\n\ntype AlertProps<TType = unknown> = Omit<BaseAlertProps, 'children'> & {\n scrollToIndex: (index: number, options: ReactVirtualScrollToOptions) => void;\n table: ReactTable<TType>;\n tableRef: React.RefObject<TableRef>;\n};\n\nexport function Alert<TType = unknown>(props: AlertProps<TType>) {\n const { scrollToIndex, table, tableRef, ...attributes } = props;\n const { texts } = useLocalization();\n const validationTexts = texts.table3.editing.validation;\n const tableMeta = table.options.meta as ReactTableMeta<TType>;\n const [showFilterResetDialog, setShowFilterResetDialog] = React.useState<string | false>(false);\n // row to scroll to and focus once the filters have been reset and it becomes visible again\n const [pendingFocusRowId, setPendingFocusRowId] = React.useState<string | null>(null);\n const pendingChangesWithErrors = tableMeta.editing.getErrorsShownInAlert<TType>();\n\n const visibleRows = table.getRowModel().rows;\n\n function getVisibleRowIndex(rowId: string) {\n return visibleRows.findIndex(row => row.id === rowId);\n }\n\n function scrollToRow(rowIndex: number) {\n tableMeta.rowActive.setRowActiveIndex(rowIndex);\n scrollToIndex(rowIndex, { align: 'center' });\n\n requestAnimationFrame(() => {\n const cell = tableRef.current?.querySelector(\n 'tbody > tr[data-row-active=\"true\"] > td[data-cell-editing-invalid=\"true\"]'\n );\n\n if (cell) {\n (cell as HTMLElement).focus?.();\n }\n });\n }\n\n // generate the \"N unsaved entries\" title\n const title = (\n pendingChangesWithErrors.length === 1 ? validationTexts.alert.titleOne : validationTexts.alert.titlePlural\n ).replace('[COUNT]', String(pendingChangesWithErrors.length));\n\n // generate links to each invalid row, to go into the error message\n const links: React.ReactNode[] = [];\n const visibleColumns = table.getVisibleFlatColumns().map(c => c.id);\n const rowIdentityColumn =\n tableMeta.rowIdentityAccessor && visibleColumns.includes(String(tableMeta.rowIdentityAccessor))\n ? // table.getColumn(columName) throw error in strict dev mode. Related thread: https://github.com/TanStack/table/discussions/5505\n table.getAllColumns().find(x => x.id === String(tableMeta.rowIdentityAccessor))\n : undefined;\n\n pendingChangesWithErrors.forEach((pendingChangeWithError, index) => {\n // if appropriate, concatenate the item with the text \"and\"\n if (pendingChangesWithErrors.length > 1 && index === pendingChangesWithErrors.length - 1) {\n // Add space before and after `messageAnd` text\n links.push(` ${validationTexts.alert.messageAnd} `);\n }\n\n const handleClick = () => {\n const visibleIndex = getVisibleRowIndex(pendingChangeWithError.rowId);\n\n if (visibleIndex > -1) {\n scrollToRow(visibleIndex);\n } else {\n setShowFilterResetDialog(pendingChangeWithError.rowId);\n }\n };\n\n let tooltip;\n\n if (pendingChangeWithError.errors.row) {\n tooltip = pendingChangeWithError.errors.row;\n } else {\n const firstCellErrorColumnId = Object.keys(pendingChangeWithError.errors.cells)[0];\n const columnName = table.getAllColumns().find(column => column.id === firstCellErrorColumnId)?.columnDef.meta?.header;\n\n if (columnName && firstCellErrorColumnId) {\n tooltip = `${columnName}: ${pendingChangeWithError.errors.cells[firstCellErrorColumnId]}`;\n }\n }\n\n let row;\n try {\n row = table.getRow(pendingChangeWithError.rowId).original;\n } catch {\n // because of server loading, some rows may not be accessible\n }\n\n // the row identity when there is one, otherwise the 1-based position, which server loading can leave unknown\n let label = '';\n\n const rowIdentity = rowIdentityColumn ? row?.[rowIdentityColumn.id as keyof TType] : undefined;\n\n if (rowIdentity !== null && rowIdentity !== undefined) {\n label = String(rowIdentity);\n } else if (pendingChangeWithError.index !== undefined) {\n label = String(pendingChangeWithError.index + 1);\n }\n\n links.push(\n <Tooltip key={pendingChangeWithError.rowId} title={tooltip ?? ''}>\n <span className=\"text-blue\" onClick={handleClick} role=\"button\">\n {label}\n </span>\n </Tooltip>\n );\n\n // if appropriate, concatenate the item with the text \",\"\n if (pendingChangesWithErrors.length > 2 && index < pendingChangesWithErrors.length - 2) {\n links.push(', ');\n }\n });\n\n // generate the \"Row N is incomplete and hasn't been saved\" error message\n const message = (links.length === 1 ? validationTexts.alert.messageOne : validationTexts.alert.messagePlural)\n .split(/(\\[\\w+\\])/)\n .map(part => {\n if (part === '[COLUMN]') {\n return rowIdentityColumn?.columnDef.meta?.header ?? validationTexts.alert.messageRow;\n }\n\n if (part === '[ROW]') {\n return links;\n }\n\n return part;\n });\n\n const handleResetFilters = () => {\n const rowId = showFilterResetDialog;\n\n table.resetGlobalFilter();\n table.resetColumnFilters();\n\n // defer focusing until the row model has re-rendered with the filters cleared,\n // otherwise the row isn't in the model/DOM yet\n if (typeof rowId === 'string') {\n setPendingFocusRowId(rowId);\n }\n\n setShowFilterResetDialog(false);\n };\n\n // once the filters are reset and the previously hidden row is visible again, scroll to and focus it\n React.useEffect(() => {\n if (pendingFocusRowId === null) {\n return;\n }\n\n const visibleIndex = getVisibleRowIndex(pendingFocusRowId);\n\n if (visibleIndex > -1) {\n scrollToRow(visibleIndex);\n setPendingFocusRowId(null);\n }\n }, [pendingFocusRowId, visibleRows]);\n\n return (\n <>\n <BaseAlert {...attributes} state=\"error\">\n <span className=\"font-bold\">{title}</span>&nbsp;{message}\n </BaseAlert>\n <FilterResetDialog\n open={showFilterResetDialog !== false}\n onChange={() => setShowFilterResetDialog(false)}\n onSubmit={handleResetFilters}\n />\n </>\n );\n}\n\ntype FilterResetDialogProps = Omit<DialogProps, 'children'> & {\n onSubmit: () => void;\n};\n\nfunction FilterResetDialog(props: FilterResetDialogProps) {\n const { onSubmit: handleSubmit, ...dialogProps } = props;\n const { texts } = useLocalization();\n\n return (\n <Dialog {...dialogProps} size=\"xs\">\n <Dialog.Content aria-label={texts.table3.editing.validation.resetFiltersDialog.title}>\n <Dialog.Title>{texts.table3.editing.validation.resetFiltersDialog.title}</Dialog.Title>\n <p>{texts.table3.editing.validation.resetFiltersDialog.description}</p>\n <Dialog.Footer>\n <Inline spacing={8} alignInline=\"end\">\n <Dialog.Close>\n <Button>{texts.table3.editing.validation.resetFiltersDialog.cancel}</Button>\n </Dialog.Close>\n <Button variant=\"primary\" onClick={handleSubmit}>\n {texts.table3.editing.validation.resetFiltersDialog.confirm}\n </Button>\n </Inline>\n </Dialog.Footer>\n </Dialog.Content>\n </Dialog>\n );\n}\n"],"names":["useLocalization","Tooltip","BaseAlert","Dialog","Inline","Button"],"mappings":"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"}
@@ -64,8 +64,9 @@ function Alert(props) {
64
64
  } catch {
65
65
  }
66
66
  let label = "";
67
- if (rowIdentityColumn) {
68
- label = String(row?.[rowIdentityColumn.id] ?? "");
67
+ const rowIdentity = rowIdentityColumn ? row?.[rowIdentityColumn.id] : void 0;
68
+ if (rowIdentity !== null && rowIdentity !== void 0) {
69
+ label = String(rowIdentity);
69
70
  } else if (pendingChangeWithError.index !== void 0) {
70
71
  label = String(pendingChangeWithError.index + 1);
71
72
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Alert.js","sources":["../../../../../src/components/Table3/components/Editing/Alert.tsx"],"sourcesContent":["import type { Table as ReactTable, TableMeta as ReactTableMeta } from '@tanstack/react-table';\nimport type { ScrollToOptions as ReactVirtualScrollToOptions } from '@tanstack/react-virtual';\nimport React from 'react';\n\nimport type { TableRef } from '../../../../primitives/Table/types';\nimport { Button } from '../../../Actions/Button/Button';\nimport type { AlertProps as BaseAlertProps } from '../../../Alert/Alert';\nimport { Alert as BaseAlert } from '../../../Alert/Alert';\nimport type { DialogProps } from '../../../Dialog/Dialog';\nimport { Dialog } from '../../../Dialog/Dialog';\nimport { Inline } from '../../../Inline/Inline';\nimport { useLocalization } from '../../../Provider/Localization';\nimport { Tooltip } from '../../../Tooltip/Tooltip';\n\ntype AlertProps<TType = unknown> = Omit<BaseAlertProps, 'children'> & {\n scrollToIndex: (index: number, options: ReactVirtualScrollToOptions) => void;\n table: ReactTable<TType>;\n tableRef: React.RefObject<TableRef>;\n};\n\nexport function Alert<TType = unknown>(props: AlertProps<TType>) {\n const { scrollToIndex, table, tableRef, ...attributes } = props;\n const { texts } = useLocalization();\n const validationTexts = texts.table3.editing.validation;\n const tableMeta = table.options.meta as ReactTableMeta<TType>;\n const [showFilterResetDialog, setShowFilterResetDialog] = React.useState<string | false>(false);\n // row to scroll to and focus once the filters have been reset and it becomes visible again\n const [pendingFocusRowId, setPendingFocusRowId] = React.useState<string | null>(null);\n const pendingChangesWithErrors = tableMeta.editing.getErrorsShownInAlert<TType>();\n\n const visibleRows = table.getRowModel().rows;\n\n function getVisibleRowIndex(rowId: string) {\n return visibleRows.findIndex(row => row.id === rowId);\n }\n\n function scrollToRow(rowIndex: number) {\n tableMeta.rowActive.setRowActiveIndex(rowIndex);\n scrollToIndex(rowIndex, { align: 'center' });\n\n requestAnimationFrame(() => {\n const cell = tableRef.current?.querySelector(\n 'tbody > tr[data-row-active=\"true\"] > td[data-cell-editing-invalid=\"true\"]'\n );\n\n if (cell) {\n (cell as HTMLElement).focus?.();\n }\n });\n }\n\n // generate the \"N unsaved entries\" title\n const title = (\n pendingChangesWithErrors.length === 1 ? validationTexts.alert.titleOne : validationTexts.alert.titlePlural\n ).replace('[COUNT]', String(pendingChangesWithErrors.length));\n\n // generate links to each invalid row, to go into the error message\n const links: React.ReactNode[] = [];\n const visibleColumns = table.getVisibleFlatColumns().map(c => c.id);\n const rowIdentityColumn =\n tableMeta.rowIdentityAccessor && visibleColumns.includes(String(tableMeta.rowIdentityAccessor))\n ? // table.getColumn(columName) throw error in strict dev mode. Related thread: https://github.com/TanStack/table/discussions/5505\n table.getAllColumns().find(x => x.id === String(tableMeta.rowIdentityAccessor))\n : undefined;\n\n pendingChangesWithErrors.forEach((pendingChangeWithError, index) => {\n // if appropriate, concatenate the item with the text \"and\"\n if (pendingChangesWithErrors.length > 1 && index === pendingChangesWithErrors.length - 1) {\n // Add space before and after `messageAnd` text\n links.push(` ${validationTexts.alert.messageAnd} `);\n }\n\n const handleClick = () => {\n const visibleIndex = getVisibleRowIndex(pendingChangeWithError.rowId);\n\n if (visibleIndex > -1) {\n scrollToRow(visibleIndex);\n } else {\n setShowFilterResetDialog(pendingChangeWithError.rowId);\n }\n };\n\n let tooltip;\n\n if (pendingChangeWithError.errors.row) {\n tooltip = pendingChangeWithError.errors.row;\n } else {\n const firstCellErrorColumnId = Object.keys(pendingChangeWithError.errors.cells)[0];\n const columnName = table.getAllColumns().find(column => column.id === firstCellErrorColumnId)?.columnDef.meta?.header;\n\n if (columnName && firstCellErrorColumnId) {\n tooltip = `${columnName}: ${pendingChangeWithError.errors.cells[firstCellErrorColumnId]}`;\n }\n }\n\n let row;\n try {\n row = table.getRow(pendingChangeWithError.rowId).original;\n } catch {\n // because of server loading, some rows may not be accessible\n }\n\n // the row identity when there is one, otherwise the 1-based position, which server loading can leave unknown\n let label = '';\n\n if (rowIdentityColumn) {\n label = String(row?.[rowIdentityColumn.id as keyof TType] ?? '');\n } else if (pendingChangeWithError.index !== undefined) {\n label = String(pendingChangeWithError.index + 1);\n }\n\n links.push(\n <Tooltip key={pendingChangeWithError.rowId} title={tooltip ?? ''}>\n <span className=\"text-blue\" onClick={handleClick} role=\"button\">\n {label}\n </span>\n </Tooltip>\n );\n\n // if appropriate, concatenate the item with the text \",\"\n if (pendingChangesWithErrors.length > 2 && index < pendingChangesWithErrors.length - 2) {\n links.push(', ');\n }\n });\n\n // generate the \"Row N is incomplete and hasn't been saved\" error message\n const message = (links.length === 1 ? validationTexts.alert.messageOne : validationTexts.alert.messagePlural)\n .split(/(\\[\\w+\\])/)\n .map(part => {\n if (part === '[COLUMN]') {\n return rowIdentityColumn?.columnDef.meta?.header ?? validationTexts.alert.messageRow;\n }\n\n if (part === '[ROW]') {\n return links;\n }\n\n return part;\n });\n\n const handleResetFilters = () => {\n const rowId = showFilterResetDialog;\n\n table.resetGlobalFilter();\n table.resetColumnFilters();\n\n // defer focusing until the row model has re-rendered with the filters cleared,\n // otherwise the row isn't in the model/DOM yet\n if (typeof rowId === 'string') {\n setPendingFocusRowId(rowId);\n }\n\n setShowFilterResetDialog(false);\n };\n\n // once the filters are reset and the previously hidden row is visible again, scroll to and focus it\n React.useEffect(() => {\n if (pendingFocusRowId === null) {\n return;\n }\n\n const visibleIndex = getVisibleRowIndex(pendingFocusRowId);\n\n if (visibleIndex > -1) {\n scrollToRow(visibleIndex);\n setPendingFocusRowId(null);\n }\n }, [pendingFocusRowId, visibleRows]);\n\n return (\n <>\n <BaseAlert {...attributes} state=\"error\">\n <span className=\"font-bold\">{title}</span>&nbsp;{message}\n </BaseAlert>\n <FilterResetDialog\n open={showFilterResetDialog !== false}\n onChange={() => setShowFilterResetDialog(false)}\n onSubmit={handleResetFilters}\n />\n </>\n );\n}\n\ntype FilterResetDialogProps = Omit<DialogProps, 'children'> & {\n onSubmit: () => void;\n};\n\nfunction FilterResetDialog(props: FilterResetDialogProps) {\n const { onSubmit: handleSubmit, ...dialogProps } = props;\n const { texts } = useLocalization();\n\n return (\n <Dialog {...dialogProps} size=\"xs\">\n <Dialog.Content aria-label={texts.table3.editing.validation.resetFiltersDialog.title}>\n <Dialog.Title>{texts.table3.editing.validation.resetFiltersDialog.title}</Dialog.Title>\n <p>{texts.table3.editing.validation.resetFiltersDialog.description}</p>\n <Dialog.Footer>\n <Inline spacing={8} alignInline=\"end\">\n <Dialog.Close>\n <Button>{texts.table3.editing.validation.resetFiltersDialog.cancel}</Button>\n </Dialog.Close>\n <Button variant=\"primary\" onClick={handleSubmit}>\n {texts.table3.editing.validation.resetFiltersDialog.confirm}\n </Button>\n </Inline>\n </Dialog.Footer>\n </Dialog.Content>\n </Dialog>\n );\n}\n"],"names":["React","BaseAlert"],"mappings":"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"}
1
+ {"version":3,"file":"Alert.js","sources":["../../../../../src/components/Table3/components/Editing/Alert.tsx"],"sourcesContent":["import type { Table as ReactTable, TableMeta as ReactTableMeta } from '@tanstack/react-table';\nimport type { ScrollToOptions as ReactVirtualScrollToOptions } from '@tanstack/react-virtual';\nimport React from 'react';\n\nimport type { TableRef } from '../../../../primitives/Table/types';\nimport { Button } from '../../../Actions/Button/Button';\nimport type { AlertProps as BaseAlertProps } from '../../../Alert/Alert';\nimport { Alert as BaseAlert } from '../../../Alert/Alert';\nimport type { DialogProps } from '../../../Dialog/Dialog';\nimport { Dialog } from '../../../Dialog/Dialog';\nimport { Inline } from '../../../Inline/Inline';\nimport { useLocalization } from '../../../Provider/Localization';\nimport { Tooltip } from '../../../Tooltip/Tooltip';\n\ntype AlertProps<TType = unknown> = Omit<BaseAlertProps, 'children'> & {\n scrollToIndex: (index: number, options: ReactVirtualScrollToOptions) => void;\n table: ReactTable<TType>;\n tableRef: React.RefObject<TableRef>;\n};\n\nexport function Alert<TType = unknown>(props: AlertProps<TType>) {\n const { scrollToIndex, table, tableRef, ...attributes } = props;\n const { texts } = useLocalization();\n const validationTexts = texts.table3.editing.validation;\n const tableMeta = table.options.meta as ReactTableMeta<TType>;\n const [showFilterResetDialog, setShowFilterResetDialog] = React.useState<string | false>(false);\n // row to scroll to and focus once the filters have been reset and it becomes visible again\n const [pendingFocusRowId, setPendingFocusRowId] = React.useState<string | null>(null);\n const pendingChangesWithErrors = tableMeta.editing.getErrorsShownInAlert<TType>();\n\n const visibleRows = table.getRowModel().rows;\n\n function getVisibleRowIndex(rowId: string) {\n return visibleRows.findIndex(row => row.id === rowId);\n }\n\n function scrollToRow(rowIndex: number) {\n tableMeta.rowActive.setRowActiveIndex(rowIndex);\n scrollToIndex(rowIndex, { align: 'center' });\n\n requestAnimationFrame(() => {\n const cell = tableRef.current?.querySelector(\n 'tbody > tr[data-row-active=\"true\"] > td[data-cell-editing-invalid=\"true\"]'\n );\n\n if (cell) {\n (cell as HTMLElement).focus?.();\n }\n });\n }\n\n // generate the \"N unsaved entries\" title\n const title = (\n pendingChangesWithErrors.length === 1 ? validationTexts.alert.titleOne : validationTexts.alert.titlePlural\n ).replace('[COUNT]', String(pendingChangesWithErrors.length));\n\n // generate links to each invalid row, to go into the error message\n const links: React.ReactNode[] = [];\n const visibleColumns = table.getVisibleFlatColumns().map(c => c.id);\n const rowIdentityColumn =\n tableMeta.rowIdentityAccessor && visibleColumns.includes(String(tableMeta.rowIdentityAccessor))\n ? // table.getColumn(columName) throw error in strict dev mode. Related thread: https://github.com/TanStack/table/discussions/5505\n table.getAllColumns().find(x => x.id === String(tableMeta.rowIdentityAccessor))\n : undefined;\n\n pendingChangesWithErrors.forEach((pendingChangeWithError, index) => {\n // if appropriate, concatenate the item with the text \"and\"\n if (pendingChangesWithErrors.length > 1 && index === pendingChangesWithErrors.length - 1) {\n // Add space before and after `messageAnd` text\n links.push(` ${validationTexts.alert.messageAnd} `);\n }\n\n const handleClick = () => {\n const visibleIndex = getVisibleRowIndex(pendingChangeWithError.rowId);\n\n if (visibleIndex > -1) {\n scrollToRow(visibleIndex);\n } else {\n setShowFilterResetDialog(pendingChangeWithError.rowId);\n }\n };\n\n let tooltip;\n\n if (pendingChangeWithError.errors.row) {\n tooltip = pendingChangeWithError.errors.row;\n } else {\n const firstCellErrorColumnId = Object.keys(pendingChangeWithError.errors.cells)[0];\n const columnName = table.getAllColumns().find(column => column.id === firstCellErrorColumnId)?.columnDef.meta?.header;\n\n if (columnName && firstCellErrorColumnId) {\n tooltip = `${columnName}: ${pendingChangeWithError.errors.cells[firstCellErrorColumnId]}`;\n }\n }\n\n let row;\n try {\n row = table.getRow(pendingChangeWithError.rowId).original;\n } catch {\n // because of server loading, some rows may not be accessible\n }\n\n // the row identity when there is one, otherwise the 1-based position, which server loading can leave unknown\n let label = '';\n\n const rowIdentity = rowIdentityColumn ? row?.[rowIdentityColumn.id as keyof TType] : undefined;\n\n if (rowIdentity !== null && rowIdentity !== undefined) {\n label = String(rowIdentity);\n } else if (pendingChangeWithError.index !== undefined) {\n label = String(pendingChangeWithError.index + 1);\n }\n\n links.push(\n <Tooltip key={pendingChangeWithError.rowId} title={tooltip ?? ''}>\n <span className=\"text-blue\" onClick={handleClick} role=\"button\">\n {label}\n </span>\n </Tooltip>\n );\n\n // if appropriate, concatenate the item with the text \",\"\n if (pendingChangesWithErrors.length > 2 && index < pendingChangesWithErrors.length - 2) {\n links.push(', ');\n }\n });\n\n // generate the \"Row N is incomplete and hasn't been saved\" error message\n const message = (links.length === 1 ? validationTexts.alert.messageOne : validationTexts.alert.messagePlural)\n .split(/(\\[\\w+\\])/)\n .map(part => {\n if (part === '[COLUMN]') {\n return rowIdentityColumn?.columnDef.meta?.header ?? validationTexts.alert.messageRow;\n }\n\n if (part === '[ROW]') {\n return links;\n }\n\n return part;\n });\n\n const handleResetFilters = () => {\n const rowId = showFilterResetDialog;\n\n table.resetGlobalFilter();\n table.resetColumnFilters();\n\n // defer focusing until the row model has re-rendered with the filters cleared,\n // otherwise the row isn't in the model/DOM yet\n if (typeof rowId === 'string') {\n setPendingFocusRowId(rowId);\n }\n\n setShowFilterResetDialog(false);\n };\n\n // once the filters are reset and the previously hidden row is visible again, scroll to and focus it\n React.useEffect(() => {\n if (pendingFocusRowId === null) {\n return;\n }\n\n const visibleIndex = getVisibleRowIndex(pendingFocusRowId);\n\n if (visibleIndex > -1) {\n scrollToRow(visibleIndex);\n setPendingFocusRowId(null);\n }\n }, [pendingFocusRowId, visibleRows]);\n\n return (\n <>\n <BaseAlert {...attributes} state=\"error\">\n <span className=\"font-bold\">{title}</span>&nbsp;{message}\n </BaseAlert>\n <FilterResetDialog\n open={showFilterResetDialog !== false}\n onChange={() => setShowFilterResetDialog(false)}\n onSubmit={handleResetFilters}\n />\n </>\n );\n}\n\ntype FilterResetDialogProps = Omit<DialogProps, 'children'> & {\n onSubmit: () => void;\n};\n\nfunction FilterResetDialog(props: FilterResetDialogProps) {\n const { onSubmit: handleSubmit, ...dialogProps } = props;\n const { texts } = useLocalization();\n\n return (\n <Dialog {...dialogProps} size=\"xs\">\n <Dialog.Content aria-label={texts.table3.editing.validation.resetFiltersDialog.title}>\n <Dialog.Title>{texts.table3.editing.validation.resetFiltersDialog.title}</Dialog.Title>\n <p>{texts.table3.editing.validation.resetFiltersDialog.description}</p>\n <Dialog.Footer>\n <Inline spacing={8} alignInline=\"end\">\n <Dialog.Close>\n <Button>{texts.table3.editing.validation.resetFiltersDialog.cancel}</Button>\n </Dialog.Close>\n <Button variant=\"primary\" onClick={handleSubmit}>\n {texts.table3.editing.validation.resetFiltersDialog.confirm}\n </Button>\n </Inline>\n </Dialog.Footer>\n </Dialog.Content>\n </Dialog>\n );\n}\n"],"names":["React","BaseAlert"],"mappings":"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"}
@@ -357,20 +357,13 @@ function usePendingChangesState(handleSave, handleChange, handleDiscard, rowIden
357
357
  if (!rowsWithErrors.length) {
358
358
  return [];
359
359
  }
360
- return rowsWithErrors.filter(hasRowErrorsShownInAlert).flatMap((rowId) => {
361
- const errors = state.changes.errors[rowId];
362
- if (!errors) {
363
- return [];
364
- }
365
- return [
366
- {
367
- rowId,
368
- index: state.indexes[rowId],
369
- changes: state.changes.rows[rowId],
370
- errors
371
- }
372
- ];
373
- });
360
+ return rowsWithErrors.filter(hasRowErrorsShownInAlert).map((rowId) => ({
361
+ rowId,
362
+ index: state.indexes[rowId],
363
+ changes: state.changes.rows[rowId],
364
+ // hasRowErrorsShownInAlert already guarantees errors is defined; the cast narrows the type
365
+ errors: state.changes.errors[rowId]
366
+ }));
374
367
  }
375
368
  function hasSaved() {
376
369
  return !!Object.values(state.statuses).filter((status) => status === "saved").length;
@@ -1 +1 @@
1
- {"version":3,"file":"useEditingState.cjs","sources":["../../../../src/components/Table3/features/useEditingState.ts"],"sourcesContent":["import type { Table as ReactTable, Cell as ReactTableCell, TableMeta as ReactTableMeta } from '@tanstack/react-table';\nimport omit from 'lodash/omit';\nimport setWith from 'lodash/setWith';\nimport { nanoid } from 'nanoid';\nimport React from 'react';\n\nimport { useLocalization } from '../../Provider/Localization';\nimport type {\n Table3EditingChangeHandler,\n Table3EditingDiscardHandler,\n Table3EditingSaveHandler,\n Table3EditingValidatorFn,\n} from '../types';\nimport { isTemporaryRow, TEMPORARY_ROW_ID_PREFIX, willRowMove } from '../util/editing';\n\nconst DELAY_BEFORE_REMOVING_SAVE_STATUS = 3000;\n\ntype EditingError = {\n row: string | undefined;\n cells: Record<string, string>;\n shouldShowErrorAlert: boolean;\n};\ntype EditingRowMoveReason = Record<string, 'search' | 'sorting' | 'filter'>;\ntype EditingSaveState = 'saving' | 'saved' | 'errored';\ntype EditingChangeset<TType = unknown> = Partial<TType>;\ntype EditingState<TType = unknown> = {\n changes: {\n rows: Record<string, EditingChangeset<TType>>;\n errors: Record<string, EditingError>;\n moveReasons: Record<string, EditingRowMoveReason>;\n originals: Record<string, TType>;\n dirty: Record<string, boolean>;\n };\n // we store row indexes because due to server loading, we don't always have all data available\n // so we can't determine an index from a row id\n // note: if the row click button in the error Alert is removed, indexes can be removed here\n indexes: Record<string, number>;\n temporaryRows: TType[];\n // unified state for all row statuses\n statuses: Record<string, EditingSaveState>;\n};\ntype EditingStateAction =\n | {\n type:\n | 'setCellValue'\n | 'removeCellValue'\n | 'updateRow'\n | 'mergeRowValue'\n | 'removeRow'\n | 'complete'\n | 'setRowStatus'\n | 'setRowErrors'\n | 'insertTemporaryRow';\n rowId: string;\n payload?: any;\n }\n | {\n type: 'forceValidate';\n payload?: any;\n };\n\nfunction reducer<TType = unknown>(state: EditingState<TType>, action: EditingStateAction) {\n const { type, payload } = action;\n\n switch (type) {\n case 'setCellValue': {\n const { columnId, index, row, value } = payload;\n return {\n ...state,\n changes: {\n ...state.changes,\n rows: setWith(state.changes.rows, `${action.rowId}.${columnId}`, value, Object),\n originals: setWith(state.changes.originals, action.rowId, row, Object),\n dirty: setWith(state.changes.dirty, action.rowId, true, Object),\n },\n indexes: setWith(state.indexes, action.rowId, index, Object),\n };\n }\n\n case 'removeCellValue': {\n const { columnId, rowIdentityAccessor } = payload;\n const changes = omit(state.changes.rows, `${action.rowId}.${columnId}`);\n\n const rowChanges = changes[action.rowId];\n\n // if there are no changes left, remove the row\n if (rowChanges && !Object.keys(rowChanges).length) {\n return reducer(state, { type: 'removeRow', rowId: action.rowId, payload: { rowIdentityAccessor } });\n }\n\n return {\n ...state,\n changes: {\n ...state.changes,\n rows: omit(state.changes.rows, `${action.rowId}.${columnId}`),\n errors: omit(state.changes.errors, `${action.rowId}.cells.${columnId}`),\n moveReasons: omit(state.changes.moveReasons, `${action.rowId}.${columnId}`),\n },\n };\n }\n\n case 'updateRow': {\n const { cellErrors, index, moveReasons, original, value } = payload;\n\n return {\n ...state,\n changes: {\n ...state.changes,\n rows: setWith(state.changes.rows, action.rowId, value, Object),\n errors: setWith(\n state.changes.errors,\n `${action.rowId}.cells`,\n cellErrors ?? state.changes.errors[action.rowId]?.cells,\n Object\n ),\n originals: setWith(\n state.changes.originals,\n action.rowId,\n original ?? state.changes.originals[action.rowId],\n Object\n ),\n moveReasons: setWith(\n state.changes.moveReasons,\n action.rowId,\n moveReasons ?? state.changes.moveReasons[action.rowId],\n Object\n ),\n },\n indexes: setWith(state.indexes, action.rowId, index, Object),\n statuses: omit(state.statuses, action.rowId),\n };\n }\n\n case 'removeRow': {\n const { rowIdentityAccessor } = payload;\n\n return {\n ...state,\n changes: {\n ...state.changes,\n rows: omit(state.changes.rows, action.rowId),\n errors: omit(state.changes.errors, action.rowId),\n moveReasons: omit(state.changes.moveReasons, action.rowId),\n originals: omit(state.changes.originals, action.rowId),\n dirty: omit(state.changes.dirty, action.rowId),\n },\n indexes: omit(state.indexes, action.rowId),\n temporaryRows: state.temporaryRows.filter(row => row[rowIdentityAccessor as keyof TType] !== action.rowId),\n };\n }\n\n case 'complete': {\n const { changes, handleDiscard } = payload;\n\n if (JSON.stringify(changes) !== JSON.stringify(state.changes.rows[action.rowId])) {\n return {\n ...state,\n changes: {\n ...state.changes,\n originals: setWith(\n state.changes.originals,\n action.rowId,\n { ...state.changes.originals[action.rowId], ...changes },\n Object\n ),\n },\n };\n }\n\n // bit hacky for now, but editing needs a rewrite\n if (typeof handleDiscard === 'function') {\n handleDiscard();\n }\n\n return state;\n }\n\n case 'setRowStatus': {\n const { status } = payload;\n\n if (status) {\n return {\n ...state,\n statuses: setWith(state.statuses, action.rowId, status, Object),\n };\n }\n // Remove status\n return {\n ...state,\n statuses: omit(state.statuses, action.rowId),\n };\n }\n\n case 'setRowErrors': {\n const { ...errors } = payload;\n return {\n ...state,\n changes: {\n ...state.changes,\n errors: setWith(state.changes.errors, action.rowId, errors, Object),\n },\n };\n }\n\n case 'insertTemporaryRow': {\n const { index, value } = payload;\n return {\n ...state,\n temporaryRows: state.temporaryRows.concat(value),\n changes: {\n ...state.changes,\n rows: setWith(state.changes.rows, action.rowId, value, Object),\n originals: setWith(state.changes.originals, action.rowId, value, Object),\n },\n indexes: setWith(state.indexes, action.rowId, index, Object),\n };\n }\n\n case 'forceValidate': {\n const { newState } = payload;\n\n return {\n ...state,\n ...newState,\n };\n }\n\n default:\n return state;\n }\n}\n\nexport function usePendingChangesState<TType = unknown>(\n handleSave: Table3EditingSaveHandler<TType> | undefined,\n handleChange: Table3EditingChangeHandler<TType> | undefined,\n handleDiscard: Table3EditingDiscardHandler | undefined,\n rowIdentityAccessor: keyof TType,\n validator: Table3EditingValidatorFn<TType> | undefined\n) {\n const localization = useLocalization();\n const [state, dispatch] = React.useReducer<EditingState<TType>, [EditingStateAction]>(reducer, {\n changes: { rows: {}, errors: {}, moveReasons: {}, originals: {}, dirty: {} },\n indexes: {},\n temporaryRows: [],\n statuses: {},\n });\n\n // rows\n function getRowValue(rowId: string) {\n return state.changes.rows?.[rowId] ?? undefined;\n }\n\n function getRowMoveReason(rowId: string) {\n return Object.values(state.changes.moveReasons?.[rowId] ?? {})[0] ?? undefined;\n }\n\n function hasRowErrors(rowId: string) {\n return !!state.changes.errors[rowId]?.row || !!Object.keys(state.changes.errors[rowId]?.cells ?? {}).length;\n }\n\n function hasRowErrorsShownInAlert(rowId: string) {\n return hasRowErrors(rowId) && !!state.changes.errors[rowId]?.shouldShowErrorAlert;\n }\n\n function hasTemporaryRowErrors<T extends TType>() {\n const newRow = state.temporaryRows?.[0] as T | undefined;\n\n if (!newRow) {\n return false;\n }\n\n const rowId = newRow[rowIdentityAccessor as keyof TType] as string;\n return !!state.changes.errors[rowId]?.row || !!Object.keys(state.changes.errors[rowId]?.cells ?? {}).length;\n }\n\n function getRowStatus(rowId: string): EditingSaveState | undefined {\n return state.statuses[rowId];\n }\n\n function setRowStatus(rowId: string, status: EditingSaveState | undefined) {\n dispatch({\n type: 'setRowStatus',\n rowId,\n payload: {\n status,\n },\n });\n }\n\n function showSavedStatus(rowId: string) {\n setRowStatus(rowId, 'saved');\n\n setTimeout(() => {\n setRowStatus(rowId, undefined);\n }, DELAY_BEFORE_REMOVING_SAVE_STATUS);\n }\n\n function insertTemporaryRow<T extends TType>(data: Partial<T>, rowIndex: number) {\n const newRowId = `${TEMPORARY_ROW_ID_PREFIX}${nanoid()}`;\n const value: Partial<TType> = {\n ...data,\n [rowIdentityAccessor]: newRowId,\n };\n\n dispatch({\n type: 'insertTemporaryRow',\n rowId: newRowId,\n payload: { index: rowIndex, value },\n });\n\n return newRowId;\n }\n\n // cells\n function setCellValue<T extends TType>(cell: ReactTableCell<T, unknown>, rowIndex: number, value: unknown) {\n const rowId = cell.row.id;\n const columnId = cell.column.id;\n\n // update if the change is different to the original value\n if (value !== cell.row.original[columnId as keyof TType]) {\n dispatch({\n type: 'setCellValue',\n rowId,\n payload: {\n columnId,\n index: rowIndex,\n row: cell.row.original,\n value,\n },\n });\n }\n // otherwise remove any previous change - no point saving the same value\n else if (cell.row.id in state.changes.rows) {\n dispatch({\n type: 'removeCellValue',\n rowId,\n payload: {\n columnId,\n rowIdentityAccessor,\n },\n });\n }\n }\n\n function getCellValue<T extends TType>(cell: ReactTableCell<T, unknown>): unknown | undefined {\n return state.changes.rows?.[cell.row.id]?.[cell.column.id as keyof TType];\n }\n\n function getCellError<T extends TType>(cell: ReactTableCell<T, unknown>): string | undefined {\n return state.changes.errors?.[cell.row.id]?.cells?.[cell.column.id];\n }\n\n async function onCellChanged<T extends TType>(\n cell: ReactTableCell<T, unknown>,\n rowIndex: number,\n nextValue: any,\n shouldRunUpdaters = true,\n shouldRunValidation = true\n ) {\n const tableMeta = cell.getContext().table.options.meta as ReactTableMeta<TType>;\n const state = tableMeta.editing.getState();\n\n // keyed by runtime column id, so a string-keyed record rather than Partial<TType>\n const changes: Record<string, unknown> =\n nextValue === undefined\n ? { ...state.changes.rows[cell.row.id] }\n : { ...state.changes.rows[cell.row.id], [cell.column.id]: nextValue };\n\n const original = cell.row.original;\n\n if (!Object.keys(changes).length) {\n return;\n }\n\n let updatesForOtherCells: Partial<T> = {};\n\n // run the updater handler if there is one, to see if there are any other cells to update\n if (typeof handleChange === 'function' && shouldRunUpdaters) {\n const previousRowValue = {\n ...original,\n //...getRowChangeset(updatersRef.current[cell.row.id], rowIdentityAccessor),\n };\n const nextRowValue = {\n ...original,\n ...changes,\n };\n updatesForOtherCells = ((await handleChange(\n cell.column.id,\n changes[cell.column.id],\n nextRowValue,\n previousRowValue\n )) ?? {}) as Partial<T>;\n }\n\n // create a projection of the next state, so we can act against it\n const nextChanges = { ...changes, ...updatesForOtherCells };\n const nextMoveReasons: EditingRowMoveReason = { ...state.changes.moveReasons[cell.row.id] };\n const nextCellErrors: Record<string, string> = { ...state.changes.errors[cell.row.id]?.cells };\n\n // run validation - if there are changes, and if there is an original stored\n let validationErrors: Record<string, string> = {};\n\n if (validator && Object.keys(nextChanges).length && original && shouldRunValidation) {\n // merge with the original so we get a full row\n const nextRowValue = { ...original, ...changes, ...updatesForOtherCells };\n validationErrors = ((await validator(nextRowValue)) ?? {}) as Record<string, string>;\n }\n\n // only set errors and move reasons for the cells we're currently acting on\n // why? because the UX is not good if we set them for cells the user hasn't touched yet\n const cellsToActOn = [cell.column.id, ...Object.keys(updatesForOtherCells)];\n const allCells = cell.row._getAllCellsByColumnId();\n\n cellsToActOn.forEach(accessor => {\n if (validationErrors[accessor]) {\n nextCellErrors[accessor] = validationErrors[accessor];\n // don't show move indicator for cells with errors, they aren't valid and can't be saved\n delete nextMoveReasons[accessor];\n } else {\n // there isn't any error in this run, remove any error set in state\n delete nextCellErrors[accessor];\n\n if (allCells[accessor]?.column.getIsSorted()) {\n // run row move determination\n const reason = willRowMove(cell, nextChanges[accessor], rowIndex, localization);\n\n // if the row will move based on this change save why, otherwise delete any existing state\n if (reason) {\n nextMoveReasons[accessor] = reason;\n } else {\n delete nextMoveReasons[accessor];\n }\n }\n }\n });\n\n dispatch({\n type: 'updateRow',\n rowId: cell.row.id,\n payload: {\n cellErrors: nextCellErrors,\n index: rowIndex,\n moveReasons: nextMoveReasons,\n value: nextChanges,\n original,\n },\n });\n }\n\n async function forceValidate<T extends TType>(table: ReactTable<T>) {\n if (validator && table.getRowModel().rows.length) {\n const newState: EditingState<TType> = { ...state };\n const returnIds: string[] = [];\n\n await Promise.all(\n table.getRowModel().rows.map(async (row, index) => {\n const rowWithChanges = { ...row.original, ...state.changes.rows[row.id] };\n\n if (rowWithChanges) {\n const cellErrors = await validator(rowWithChanges);\n\n if (cellErrors) {\n // prep row (it might be new or it might have changes already)\n newState.changes.dirty[row.id] = true;\n newState.changes.originals[row.id] = row.original;\n newState.changes.moveReasons[row.id] = { ...newState.changes.moveReasons[row.id] };\n newState.changes.rows[row.id] = { ...newState.changes.rows[row.id] };\n newState.statuses[row.id] = 'errored';\n newState.indexes[row.id] = index;\n\n newState.changes.errors[row.id] = {\n cells: cellErrors,\n row: newState.changes.errors[row.id]?.row ?? undefined,\n shouldShowErrorAlert: newState.changes.errors[row.id]?.shouldShowErrorAlert ?? false,\n } as EditingError;\n\n const rowChanges = newState.changes.rows[row.id];\n\n // ensure each errored cell actually has a change\n Object.keys(cellErrors).forEach(columnId => {\n if (rowChanges !== undefined) {\n rowChanges[columnId as keyof TType] = rowWithChanges[columnId as keyof TType];\n }\n });\n\n returnIds.push(row.id);\n }\n }\n })\n );\n\n dispatch({\n type: 'forceValidate',\n payload: {\n newState,\n },\n });\n\n return returnIds;\n }\n\n return [];\n }\n\n // general\n function getErrors() {\n return Object.keys(state.changes.errors);\n }\n\n function getErrorsShownInAlert<T extends TType>() {\n const rowsWithErrors = getErrors();\n\n if (!rowsWithErrors.length) {\n return [];\n }\n\n return rowsWithErrors.filter(hasRowErrorsShownInAlert).flatMap(rowId => {\n const errors = state.changes.errors[rowId];\n\n // hasRowErrorsShownInAlert already guarantees this, the check is here to narrow the type\n if (!errors) {\n return [];\n }\n\n return [\n {\n rowId,\n index: state.indexes[rowId],\n changes: state.changes.rows[rowId] as EditingChangeset<T>,\n errors,\n },\n ];\n });\n }\n\n function hasSaved() {\n return !!Object.values(state.statuses).filter(status => status === 'saved').length;\n }\n\n function hasChanges(rowId?: string) {\n return rowId ? !!state.changes.rows[rowId] : !!Object.keys(state.changes.rows).length;\n }\n\n async function saveChanges<T extends TType>(table: ReactTable<T>, rowId: string | undefined = undefined): Promise<boolean> {\n const tableMeta = table.options.meta as ReactTableMeta<TType>;\n const state = tableMeta.editing.getState();\n\n if (!handleSave) {\n // oxlint-disable-next-line no-console\n console.warn('Tried to save, but Table has no onEditingSave handler');\n return false;\n }\n\n // sometimes we only want to save one row\n const changes = rowId ? { [rowId]: state.changes.rows[rowId] } : state.changes.rows;\n\n let completed = true;\n\n for (const rowId of Object.keys(changes)) {\n if (!state.changes.dirty[rowId]) {\n discardChanges(rowId, table);\n continue;\n }\n\n const status = getRowStatus(rowId);\n\n try {\n const rowChanges = JSON.parse(JSON.stringify(changes[rowId] ?? {}));\n\n // don't try to save if - already saving, or there are known errors\n if (!Object.keys(rowChanges).length || status === 'saving' || status === 'errored') {\n continue;\n }\n\n setRowStatus(rowId, 'saving');\n\n const changeSet = { ...(state.changes.originals[rowId] as object), ...rowChanges };\n\n // if we had to create a temporary id, delete it first - it's our data, not theirs\n if (isTemporaryRow(changeSet[rowIdentityAccessor as string])) {\n delete changeSet[rowIdentityAccessor];\n }\n\n // re-run validation, maybe a cell is already invalid but has never been blurred\n if (validator) {\n // oxlint-disable-next-line no-await-in-loop\n const errors = await validator(changeSet);\n\n if (errors && Object.keys(errors).length) {\n throw errors;\n }\n }\n\n // send new data to the server\n // oxlint-disable-next-line no-await-in-loop\n await handleSave(changeSet);\n\n complete(rowId, table, rowChanges);\n showSavedStatus(rowId);\n } catch (error) {\n if (error instanceof ReferenceError || error instanceof TypeError || (error as any)?.response?.status >= 500) {\n // oxlint-disable-next-line no-console\n console.error(error);\n }\n\n // the onEditingSave handler should throw errors when something fails, e.g. validation, network errors etc\n // this code handles those errors and maps them either to row errors or cell specific errors\n let rowError: string | undefined;\n let cellErrors;\n\n if (typeof error === 'string') {\n rowError = error;\n } else if (error instanceof Error) {\n rowError = error.message;\n\n // most of our apis return error objects within this shape\n if (typeof (error as any).response?.data === 'object') {\n cellErrors = (error as any).response?.data;\n }\n } else if (typeof error === 'object') {\n cellErrors = error;\n }\n\n if (rowError || cellErrors) {\n dispatch({\n type: 'setRowErrors',\n rowId,\n payload: {\n row: rowError,\n cells: cellErrors,\n shouldShowErrorAlert: true,\n },\n });\n }\n\n setRowStatus(rowId, 'errored');\n completed = false;\n }\n }\n\n return completed;\n }\n\n async function discardChanges<T extends TType>(rowId: string, table: ReactTable<T>) {\n // remove any new rows from pinned state before discarding them\n if (rowId.startsWith(TEMPORARY_ROW_ID_PREFIX)) {\n table.resetRowPinning(true);\n }\n\n dispatch({\n type: 'removeRow',\n rowId,\n payload: { rowIdentityAccessor },\n });\n\n if (typeof handleDiscard === 'function') {\n await handleDiscard();\n }\n }\n\n function complete<T extends TType>(rowId: string, table: ReactTable<T>, changes: Partial<T>) {\n const handleDiscard = async () => {\n await discardChanges(rowId, table);\n };\n\n dispatch({\n type: 'complete',\n rowId,\n payload: { rowIdentityAccessor, changes, handleDiscard },\n });\n }\n\n function getState() {\n return state;\n }\n\n return {\n getState,\n // row\n getRowValue,\n getRowMoveReason,\n hasRowErrors,\n hasRowErrorsShownInAlert,\n hasTemporaryRowErrors,\n getRowStatus,\n setRowStatus,\n forceValidate,\n // cells\n setCellValue,\n getCellValue,\n getCellError,\n onCellChanged,\n // general\n getErrors,\n getErrorsShownInAlert,\n hasChanges,\n saveChanges,\n discardChanges,\n hasSaved,\n // new rows\n insertTemporaryRow,\n temporaryRows: state.temporaryRows,\n isDirty: hasChanges() || state.temporaryRows.length > 0,\n };\n}\n"],"names":["useLocalization","TEMPORARY_ROW_ID_PREFIX","nanoid","state","willRowMove","rowId","isTemporaryRow","handleDiscard"],"mappings":"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"}
1
+ {"version":3,"file":"useEditingState.cjs","sources":["../../../../src/components/Table3/features/useEditingState.ts"],"sourcesContent":["import type { Table as ReactTable, Cell as ReactTableCell, TableMeta as ReactTableMeta } from '@tanstack/react-table';\nimport omit from 'lodash/omit';\nimport setWith from 'lodash/setWith';\nimport { nanoid } from 'nanoid';\nimport React from 'react';\n\nimport { useLocalization } from '../../Provider/Localization';\nimport type {\n Table3EditingChangeHandler,\n Table3EditingDiscardHandler,\n Table3EditingSaveHandler,\n Table3EditingValidatorFn,\n} from '../types';\nimport { isTemporaryRow, TEMPORARY_ROW_ID_PREFIX, willRowMove } from '../util/editing';\n\nconst DELAY_BEFORE_REMOVING_SAVE_STATUS = 3000;\n\ntype EditingError = {\n row: string | undefined;\n cells: Record<string, string>;\n shouldShowErrorAlert: boolean;\n};\ntype EditingRowMoveReason = Record<string, 'search' | 'sorting' | 'filter'>;\ntype EditingSaveState = 'saving' | 'saved' | 'errored';\ntype EditingChangeset<TType = unknown> = Partial<TType>;\ntype EditingState<TType = unknown> = {\n changes: {\n rows: Record<string, EditingChangeset<TType>>;\n errors: Record<string, EditingError>;\n moveReasons: Record<string, EditingRowMoveReason>;\n originals: Record<string, TType>;\n dirty: Record<string, boolean>;\n };\n // we store row indexes because due to server loading, we don't always have all data available\n // so we can't determine an index from a row id\n // note: if the row click button in the error Alert is removed, indexes can be removed here\n indexes: Record<string, number>;\n temporaryRows: TType[];\n // unified state for all row statuses\n statuses: Record<string, EditingSaveState>;\n};\ntype EditingStateAction =\n | {\n type:\n | 'setCellValue'\n | 'removeCellValue'\n | 'updateRow'\n | 'mergeRowValue'\n | 'removeRow'\n | 'complete'\n | 'setRowStatus'\n | 'setRowErrors'\n | 'insertTemporaryRow';\n rowId: string;\n payload?: any;\n }\n | {\n type: 'forceValidate';\n payload?: any;\n };\n\nfunction reducer<TType = unknown>(state: EditingState<TType>, action: EditingStateAction): EditingState<TType> {\n const { type, payload } = action;\n\n switch (type) {\n case 'setCellValue': {\n const { columnId, index, row, value } = payload;\n return {\n ...state,\n changes: {\n ...state.changes,\n rows: setWith(state.changes.rows, `${action.rowId}.${columnId}`, value, Object),\n originals: setWith(state.changes.originals, action.rowId, row, Object),\n dirty: setWith(state.changes.dirty, action.rowId, true, Object),\n },\n indexes: setWith(state.indexes, action.rowId, index, Object),\n };\n }\n\n case 'removeCellValue': {\n const { columnId, rowIdentityAccessor } = payload;\n const changes = omit(state.changes.rows, `${action.rowId}.${columnId}`);\n\n const rowChanges = changes[action.rowId];\n\n // if there are no changes left, remove the row\n if (rowChanges && !Object.keys(rowChanges).length) {\n return reducer(state, { type: 'removeRow', rowId: action.rowId, payload: { rowIdentityAccessor } });\n }\n\n return {\n ...state,\n changes: {\n ...state.changes,\n rows: omit(state.changes.rows, `${action.rowId}.${columnId}`),\n errors: omit(state.changes.errors, `${action.rowId}.cells.${columnId}`),\n moveReasons: omit(state.changes.moveReasons, `${action.rowId}.${columnId}`),\n },\n };\n }\n\n case 'updateRow': {\n const { cellErrors, index, moveReasons, original, value } = payload;\n\n return {\n ...state,\n changes: {\n ...state.changes,\n rows: setWith(state.changes.rows, action.rowId, value, Object),\n errors: setWith(\n state.changes.errors,\n `${action.rowId}.cells`,\n cellErrors ?? state.changes.errors[action.rowId]?.cells,\n Object\n ),\n originals: setWith(\n state.changes.originals,\n action.rowId,\n original ?? state.changes.originals[action.rowId],\n Object\n ),\n moveReasons: setWith(\n state.changes.moveReasons,\n action.rowId,\n moveReasons ?? state.changes.moveReasons[action.rowId],\n Object\n ),\n },\n indexes: setWith(state.indexes, action.rowId, index, Object),\n statuses: omit(state.statuses, action.rowId),\n };\n }\n\n case 'removeRow': {\n const { rowIdentityAccessor } = payload;\n\n return {\n ...state,\n changes: {\n ...state.changes,\n rows: omit(state.changes.rows, action.rowId),\n errors: omit(state.changes.errors, action.rowId),\n moveReasons: omit(state.changes.moveReasons, action.rowId),\n originals: omit(state.changes.originals, action.rowId),\n dirty: omit(state.changes.dirty, action.rowId),\n },\n indexes: omit(state.indexes, action.rowId),\n temporaryRows: state.temporaryRows.filter(row => row[rowIdentityAccessor as keyof TType] !== action.rowId),\n };\n }\n\n case 'complete': {\n const { changes, handleDiscard } = payload;\n\n if (JSON.stringify(changes) !== JSON.stringify(state.changes.rows[action.rowId])) {\n return {\n ...state,\n changes: {\n ...state.changes,\n originals: setWith(\n state.changes.originals,\n action.rowId,\n { ...state.changes.originals[action.rowId], ...changes },\n Object\n ),\n },\n };\n }\n\n // bit hacky for now, but editing needs a rewrite\n if (typeof handleDiscard === 'function') {\n handleDiscard();\n }\n\n return state;\n }\n\n case 'setRowStatus': {\n const { status } = payload;\n\n if (status) {\n return {\n ...state,\n statuses: setWith(state.statuses, action.rowId, status, Object),\n };\n }\n // Remove status\n return {\n ...state,\n statuses: omit(state.statuses, action.rowId),\n };\n }\n\n case 'setRowErrors': {\n const { ...errors } = payload;\n return {\n ...state,\n changes: {\n ...state.changes,\n errors: setWith(state.changes.errors, action.rowId, errors, Object),\n },\n };\n }\n\n case 'insertTemporaryRow': {\n const { index, value } = payload;\n return {\n ...state,\n temporaryRows: state.temporaryRows.concat(value),\n changes: {\n ...state.changes,\n rows: setWith(state.changes.rows, action.rowId, value, Object),\n originals: setWith(state.changes.originals, action.rowId, value, Object),\n },\n indexes: setWith(state.indexes, action.rowId, index, Object),\n };\n }\n\n case 'forceValidate': {\n const { newState } = payload;\n\n return {\n ...state,\n ...newState,\n };\n }\n\n default:\n return state;\n }\n}\n\nexport function usePendingChangesState<TType = unknown>(\n handleSave: Table3EditingSaveHandler<TType> | undefined,\n handleChange: Table3EditingChangeHandler<TType> | undefined,\n handleDiscard: Table3EditingDiscardHandler | undefined,\n rowIdentityAccessor: keyof TType,\n validator: Table3EditingValidatorFn<TType> | undefined\n) {\n const localization = useLocalization();\n const [state, dispatch] = React.useReducer(reducer<TType>, {\n changes: { rows: {}, errors: {}, moveReasons: {}, originals: {}, dirty: {} },\n indexes: {},\n temporaryRows: [],\n statuses: {},\n });\n\n // rows\n function getRowValue(rowId: string) {\n return state.changes.rows?.[rowId] ?? undefined;\n }\n\n function getRowMoveReason(rowId: string) {\n return Object.values(state.changes.moveReasons?.[rowId] ?? {})[0] ?? undefined;\n }\n\n function hasRowErrors(rowId: string) {\n return !!state.changes.errors[rowId]?.row || !!Object.keys(state.changes.errors[rowId]?.cells ?? {}).length;\n }\n\n function hasRowErrorsShownInAlert(rowId: string) {\n return hasRowErrors(rowId) && !!state.changes.errors[rowId]?.shouldShowErrorAlert;\n }\n\n function hasTemporaryRowErrors<T extends TType>() {\n const newRow = state.temporaryRows?.[0] as T | undefined;\n\n if (!newRow) {\n return false;\n }\n\n const rowId = newRow[rowIdentityAccessor as keyof TType] as string;\n return !!state.changes.errors[rowId]?.row || !!Object.keys(state.changes.errors[rowId]?.cells ?? {}).length;\n }\n\n function getRowStatus(rowId: string): EditingSaveState | undefined {\n return state.statuses[rowId];\n }\n\n function setRowStatus(rowId: string, status: EditingSaveState | undefined) {\n dispatch({\n type: 'setRowStatus',\n rowId,\n payload: {\n status,\n },\n });\n }\n\n function showSavedStatus(rowId: string) {\n setRowStatus(rowId, 'saved');\n\n setTimeout(() => {\n setRowStatus(rowId, undefined);\n }, DELAY_BEFORE_REMOVING_SAVE_STATUS);\n }\n\n function insertTemporaryRow<T extends TType>(data: Partial<T>, rowIndex: number) {\n const newRowId = `${TEMPORARY_ROW_ID_PREFIX}${nanoid()}`;\n const value: Partial<TType> = {\n ...data,\n [rowIdentityAccessor]: newRowId,\n };\n\n dispatch({\n type: 'insertTemporaryRow',\n rowId: newRowId,\n payload: { index: rowIndex, value },\n });\n\n return newRowId;\n }\n\n // cells\n function setCellValue<T extends TType>(cell: ReactTableCell<T, unknown>, rowIndex: number, value: unknown) {\n const rowId = cell.row.id;\n const columnId = cell.column.id;\n\n // update if the change is different to the original value\n if (value !== cell.row.original[columnId as keyof TType]) {\n dispatch({\n type: 'setCellValue',\n rowId,\n payload: {\n columnId,\n index: rowIndex,\n row: cell.row.original,\n value,\n },\n });\n }\n // otherwise remove any previous change - no point saving the same value\n else if (cell.row.id in state.changes.rows) {\n dispatch({\n type: 'removeCellValue',\n rowId,\n payload: {\n columnId,\n rowIdentityAccessor,\n },\n });\n }\n }\n\n function getCellValue<T extends TType>(cell: ReactTableCell<T, unknown>): unknown | undefined {\n return state.changes.rows?.[cell.row.id]?.[cell.column.id as keyof TType];\n }\n\n function getCellError<T extends TType>(cell: ReactTableCell<T, unknown>): string | undefined {\n return state.changes.errors?.[cell.row.id]?.cells?.[cell.column.id];\n }\n\n async function onCellChanged<T extends TType>(\n cell: ReactTableCell<T, unknown>,\n rowIndex: number,\n nextValue: any,\n shouldRunUpdaters = true,\n shouldRunValidation = true\n ) {\n const tableMeta = cell.getContext().table.options.meta as ReactTableMeta<TType>;\n const state = tableMeta.editing.getState();\n\n // keyed by runtime column id, so a string-keyed record rather than Partial<TType>\n const changes: Record<string, unknown> =\n nextValue === undefined\n ? { ...state.changes.rows[cell.row.id] }\n : { ...state.changes.rows[cell.row.id], [cell.column.id]: nextValue };\n\n const original = cell.row.original;\n\n if (!Object.keys(changes).length) {\n return;\n }\n\n let updatesForOtherCells: Partial<T> = {};\n\n // run the updater handler if there is one, to see if there are any other cells to update\n if (typeof handleChange === 'function' && shouldRunUpdaters) {\n const previousRowValue = {\n ...original,\n //...getRowChangeset(updatersRef.current[cell.row.id], rowIdentityAccessor),\n };\n const nextRowValue = {\n ...original,\n ...changes,\n };\n updatesForOtherCells = ((await handleChange(\n cell.column.id,\n changes[cell.column.id],\n nextRowValue,\n previousRowValue\n )) ?? {}) as Partial<T>;\n }\n\n // create a projection of the next state, so we can act against it\n const nextChanges = { ...changes, ...updatesForOtherCells };\n const nextMoveReasons: EditingRowMoveReason = { ...state.changes.moveReasons[cell.row.id] };\n const nextCellErrors: Record<string, string> = { ...state.changes.errors[cell.row.id]?.cells };\n\n // run validation - if there are changes, and if there is an original stored\n let validationErrors: Record<string, string> = {};\n\n if (validator && Object.keys(nextChanges).length && original && shouldRunValidation) {\n // merge with the original so we get a full row\n const nextRowValue = { ...original, ...changes, ...updatesForOtherCells };\n validationErrors = ((await validator(nextRowValue)) ?? {}) as Record<string, string>;\n }\n\n // only set errors and move reasons for the cells we're currently acting on\n // why? because the UX is not good if we set them for cells the user hasn't touched yet\n const cellsToActOn = [cell.column.id, ...Object.keys(updatesForOtherCells)];\n const allCells = cell.row._getAllCellsByColumnId();\n\n cellsToActOn.forEach(accessor => {\n if (validationErrors[accessor]) {\n nextCellErrors[accessor] = validationErrors[accessor];\n // don't show move indicator for cells with errors, they aren't valid and can't be saved\n delete nextMoveReasons[accessor];\n } else {\n // there isn't any error in this run, remove any error set in state\n delete nextCellErrors[accessor];\n\n if (allCells[accessor]?.column.getIsSorted()) {\n // run row move determination\n const reason = willRowMove(cell, nextChanges[accessor], rowIndex, localization);\n\n // if the row will move based on this change save why, otherwise delete any existing state\n if (reason) {\n nextMoveReasons[accessor] = reason;\n } else {\n delete nextMoveReasons[accessor];\n }\n }\n }\n });\n\n dispatch({\n type: 'updateRow',\n rowId: cell.row.id,\n payload: {\n cellErrors: nextCellErrors,\n index: rowIndex,\n moveReasons: nextMoveReasons,\n value: nextChanges,\n original,\n },\n });\n }\n\n async function forceValidate<T extends TType>(table: ReactTable<T>) {\n if (validator && table.getRowModel().rows.length) {\n const newState: EditingState<TType> = { ...state };\n const returnIds: string[] = [];\n\n await Promise.all(\n table.getRowModel().rows.map(async (row, index) => {\n const rowWithChanges = { ...row.original, ...state.changes.rows[row.id] };\n\n if (rowWithChanges) {\n const cellErrors = await validator(rowWithChanges);\n\n if (cellErrors) {\n // prep row (it might be new or it might have changes already)\n newState.changes.dirty[row.id] = true;\n newState.changes.originals[row.id] = row.original;\n newState.changes.moveReasons[row.id] = { ...newState.changes.moveReasons[row.id] };\n newState.changes.rows[row.id] = { ...newState.changes.rows[row.id] };\n newState.statuses[row.id] = 'errored';\n newState.indexes[row.id] = index;\n\n newState.changes.errors[row.id] = {\n cells: cellErrors,\n row: newState.changes.errors[row.id]?.row ?? undefined,\n shouldShowErrorAlert: newState.changes.errors[row.id]?.shouldShowErrorAlert ?? false,\n } as EditingError;\n\n const rowChanges = newState.changes.rows[row.id];\n\n // ensure each errored cell actually has a change\n Object.keys(cellErrors).forEach(columnId => {\n if (rowChanges !== undefined) {\n rowChanges[columnId as keyof TType] = rowWithChanges[columnId as keyof TType];\n }\n });\n\n returnIds.push(row.id);\n }\n }\n })\n );\n\n dispatch({\n type: 'forceValidate',\n payload: {\n newState,\n },\n });\n\n return returnIds;\n }\n\n return [];\n }\n\n // general\n function getErrors() {\n return Object.keys(state.changes.errors);\n }\n\n function getErrorsShownInAlert<T extends TType>() {\n const rowsWithErrors = getErrors();\n\n if (!rowsWithErrors.length) {\n return [];\n }\n\n return rowsWithErrors.filter(hasRowErrorsShownInAlert).map(rowId => ({\n rowId,\n index: state.indexes[rowId],\n changes: state.changes.rows[rowId] as EditingChangeset<T>,\n // hasRowErrorsShownInAlert already guarantees errors is defined; the cast narrows the type\n errors: state.changes.errors[rowId] as EditingError,\n }));\n }\n\n function hasSaved() {\n return !!Object.values(state.statuses).filter(status => status === 'saved').length;\n }\n\n function hasChanges(rowId?: string) {\n return rowId ? !!state.changes.rows[rowId] : !!Object.keys(state.changes.rows).length;\n }\n\n async function saveChanges<T extends TType>(table: ReactTable<T>, rowId: string | undefined = undefined): Promise<boolean> {\n const tableMeta = table.options.meta as ReactTableMeta<TType>;\n const state = tableMeta.editing.getState();\n\n if (!handleSave) {\n // oxlint-disable-next-line no-console\n console.warn('Tried to save, but Table has no onEditingSave handler');\n return false;\n }\n\n // sometimes we only want to save one row\n const changes = rowId ? { [rowId]: state.changes.rows[rowId] } : state.changes.rows;\n\n let completed = true;\n\n for (const rowId of Object.keys(changes)) {\n if (!state.changes.dirty[rowId]) {\n discardChanges(rowId, table);\n continue;\n }\n\n const status = getRowStatus(rowId);\n\n try {\n const rowChanges = JSON.parse(JSON.stringify(changes[rowId] ?? {}));\n\n // don't try to save if - already saving, or there are known errors\n if (!Object.keys(rowChanges).length || status === 'saving' || status === 'errored') {\n continue;\n }\n\n setRowStatus(rowId, 'saving');\n\n const changeSet = { ...(state.changes.originals[rowId] as object), ...rowChanges };\n\n // if we had to create a temporary id, delete it first - it's our data, not theirs\n if (isTemporaryRow(changeSet[rowIdentityAccessor as string])) {\n delete changeSet[rowIdentityAccessor];\n }\n\n // re-run validation, maybe a cell is already invalid but has never been blurred\n if (validator) {\n // oxlint-disable-next-line no-await-in-loop\n const errors = await validator(changeSet);\n\n if (errors && Object.keys(errors).length) {\n throw errors;\n }\n }\n\n // send new data to the server\n // oxlint-disable-next-line no-await-in-loop\n await handleSave(changeSet);\n\n complete(rowId, table, rowChanges);\n showSavedStatus(rowId);\n } catch (error) {\n if (error instanceof ReferenceError || error instanceof TypeError || (error as any)?.response?.status >= 500) {\n // oxlint-disable-next-line no-console\n console.error(error);\n }\n\n // the onEditingSave handler should throw errors when something fails, e.g. validation, network errors etc\n // this code handles those errors and maps them either to row errors or cell specific errors\n let rowError: string | undefined;\n let cellErrors;\n\n if (typeof error === 'string') {\n rowError = error;\n } else if (error instanceof Error) {\n rowError = error.message;\n\n // most of our apis return error objects within this shape\n if (typeof (error as any).response?.data === 'object') {\n cellErrors = (error as any).response?.data;\n }\n } else if (typeof error === 'object') {\n cellErrors = error;\n }\n\n if (rowError || cellErrors) {\n dispatch({\n type: 'setRowErrors',\n rowId,\n payload: {\n row: rowError,\n cells: cellErrors,\n shouldShowErrorAlert: true,\n },\n });\n }\n\n setRowStatus(rowId, 'errored');\n completed = false;\n }\n }\n\n return completed;\n }\n\n async function discardChanges<T extends TType>(rowId: string, table: ReactTable<T>) {\n // remove any new rows from pinned state before discarding them\n if (rowId.startsWith(TEMPORARY_ROW_ID_PREFIX)) {\n table.resetRowPinning(true);\n }\n\n dispatch({\n type: 'removeRow',\n rowId,\n payload: { rowIdentityAccessor },\n });\n\n if (typeof handleDiscard === 'function') {\n await handleDiscard();\n }\n }\n\n function complete<T extends TType>(rowId: string, table: ReactTable<T>, changes: Partial<T>) {\n const handleDiscard = async () => {\n await discardChanges(rowId, table);\n };\n\n dispatch({\n type: 'complete',\n rowId,\n payload: { rowIdentityAccessor, changes, handleDiscard },\n });\n }\n\n function getState() {\n return state;\n }\n\n return {\n getState,\n // row\n getRowValue,\n getRowMoveReason,\n hasRowErrors,\n hasRowErrorsShownInAlert,\n hasTemporaryRowErrors,\n getRowStatus,\n setRowStatus,\n forceValidate,\n // cells\n setCellValue,\n getCellValue,\n getCellError,\n onCellChanged,\n // general\n getErrors,\n getErrorsShownInAlert,\n hasChanges,\n saveChanges,\n discardChanges,\n hasSaved,\n // new rows\n insertTemporaryRow,\n temporaryRows: state.temporaryRows,\n isDirty: hasChanges() || state.temporaryRows.length > 0,\n };\n}\n"],"names":["useLocalization","TEMPORARY_ROW_ID_PREFIX","nanoid","state","willRowMove","rowId","isTemporaryRow","handleDiscard"],"mappings":"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"}
@@ -355,20 +355,13 @@ function usePendingChangesState(handleSave, handleChange, handleDiscard, rowIden
355
355
  if (!rowsWithErrors.length) {
356
356
  return [];
357
357
  }
358
- return rowsWithErrors.filter(hasRowErrorsShownInAlert).flatMap((rowId) => {
359
- const errors = state.changes.errors[rowId];
360
- if (!errors) {
361
- return [];
362
- }
363
- return [
364
- {
365
- rowId,
366
- index: state.indexes[rowId],
367
- changes: state.changes.rows[rowId],
368
- errors
369
- }
370
- ];
371
- });
358
+ return rowsWithErrors.filter(hasRowErrorsShownInAlert).map((rowId) => ({
359
+ rowId,
360
+ index: state.indexes[rowId],
361
+ changes: state.changes.rows[rowId],
362
+ // hasRowErrorsShownInAlert already guarantees errors is defined; the cast narrows the type
363
+ errors: state.changes.errors[rowId]
364
+ }));
372
365
  }
373
366
  function hasSaved() {
374
367
  return !!Object.values(state.statuses).filter((status) => status === "saved").length;
@@ -1 +1 @@
1
- {"version":3,"file":"useEditingState.js","sources":["../../../../src/components/Table3/features/useEditingState.ts"],"sourcesContent":["import type { Table as ReactTable, Cell as ReactTableCell, TableMeta as ReactTableMeta } from '@tanstack/react-table';\nimport omit from 'lodash/omit';\nimport setWith from 'lodash/setWith';\nimport { nanoid } from 'nanoid';\nimport React from 'react';\n\nimport { useLocalization } from '../../Provider/Localization';\nimport type {\n Table3EditingChangeHandler,\n Table3EditingDiscardHandler,\n Table3EditingSaveHandler,\n Table3EditingValidatorFn,\n} from '../types';\nimport { isTemporaryRow, TEMPORARY_ROW_ID_PREFIX, willRowMove } from '../util/editing';\n\nconst DELAY_BEFORE_REMOVING_SAVE_STATUS = 3000;\n\ntype EditingError = {\n row: string | undefined;\n cells: Record<string, string>;\n shouldShowErrorAlert: boolean;\n};\ntype EditingRowMoveReason = Record<string, 'search' | 'sorting' | 'filter'>;\ntype EditingSaveState = 'saving' | 'saved' | 'errored';\ntype EditingChangeset<TType = unknown> = Partial<TType>;\ntype EditingState<TType = unknown> = {\n changes: {\n rows: Record<string, EditingChangeset<TType>>;\n errors: Record<string, EditingError>;\n moveReasons: Record<string, EditingRowMoveReason>;\n originals: Record<string, TType>;\n dirty: Record<string, boolean>;\n };\n // we store row indexes because due to server loading, we don't always have all data available\n // so we can't determine an index from a row id\n // note: if the row click button in the error Alert is removed, indexes can be removed here\n indexes: Record<string, number>;\n temporaryRows: TType[];\n // unified state for all row statuses\n statuses: Record<string, EditingSaveState>;\n};\ntype EditingStateAction =\n | {\n type:\n | 'setCellValue'\n | 'removeCellValue'\n | 'updateRow'\n | 'mergeRowValue'\n | 'removeRow'\n | 'complete'\n | 'setRowStatus'\n | 'setRowErrors'\n | 'insertTemporaryRow';\n rowId: string;\n payload?: any;\n }\n | {\n type: 'forceValidate';\n payload?: any;\n };\n\nfunction reducer<TType = unknown>(state: EditingState<TType>, action: EditingStateAction) {\n const { type, payload } = action;\n\n switch (type) {\n case 'setCellValue': {\n const { columnId, index, row, value } = payload;\n return {\n ...state,\n changes: {\n ...state.changes,\n rows: setWith(state.changes.rows, `${action.rowId}.${columnId}`, value, Object),\n originals: setWith(state.changes.originals, action.rowId, row, Object),\n dirty: setWith(state.changes.dirty, action.rowId, true, Object),\n },\n indexes: setWith(state.indexes, action.rowId, index, Object),\n };\n }\n\n case 'removeCellValue': {\n const { columnId, rowIdentityAccessor } = payload;\n const changes = omit(state.changes.rows, `${action.rowId}.${columnId}`);\n\n const rowChanges = changes[action.rowId];\n\n // if there are no changes left, remove the row\n if (rowChanges && !Object.keys(rowChanges).length) {\n return reducer(state, { type: 'removeRow', rowId: action.rowId, payload: { rowIdentityAccessor } });\n }\n\n return {\n ...state,\n changes: {\n ...state.changes,\n rows: omit(state.changes.rows, `${action.rowId}.${columnId}`),\n errors: omit(state.changes.errors, `${action.rowId}.cells.${columnId}`),\n moveReasons: omit(state.changes.moveReasons, `${action.rowId}.${columnId}`),\n },\n };\n }\n\n case 'updateRow': {\n const { cellErrors, index, moveReasons, original, value } = payload;\n\n return {\n ...state,\n changes: {\n ...state.changes,\n rows: setWith(state.changes.rows, action.rowId, value, Object),\n errors: setWith(\n state.changes.errors,\n `${action.rowId}.cells`,\n cellErrors ?? state.changes.errors[action.rowId]?.cells,\n Object\n ),\n originals: setWith(\n state.changes.originals,\n action.rowId,\n original ?? state.changes.originals[action.rowId],\n Object\n ),\n moveReasons: setWith(\n state.changes.moveReasons,\n action.rowId,\n moveReasons ?? state.changes.moveReasons[action.rowId],\n Object\n ),\n },\n indexes: setWith(state.indexes, action.rowId, index, Object),\n statuses: omit(state.statuses, action.rowId),\n };\n }\n\n case 'removeRow': {\n const { rowIdentityAccessor } = payload;\n\n return {\n ...state,\n changes: {\n ...state.changes,\n rows: omit(state.changes.rows, action.rowId),\n errors: omit(state.changes.errors, action.rowId),\n moveReasons: omit(state.changes.moveReasons, action.rowId),\n originals: omit(state.changes.originals, action.rowId),\n dirty: omit(state.changes.dirty, action.rowId),\n },\n indexes: omit(state.indexes, action.rowId),\n temporaryRows: state.temporaryRows.filter(row => row[rowIdentityAccessor as keyof TType] !== action.rowId),\n };\n }\n\n case 'complete': {\n const { changes, handleDiscard } = payload;\n\n if (JSON.stringify(changes) !== JSON.stringify(state.changes.rows[action.rowId])) {\n return {\n ...state,\n changes: {\n ...state.changes,\n originals: setWith(\n state.changes.originals,\n action.rowId,\n { ...state.changes.originals[action.rowId], ...changes },\n Object\n ),\n },\n };\n }\n\n // bit hacky for now, but editing needs a rewrite\n if (typeof handleDiscard === 'function') {\n handleDiscard();\n }\n\n return state;\n }\n\n case 'setRowStatus': {\n const { status } = payload;\n\n if (status) {\n return {\n ...state,\n statuses: setWith(state.statuses, action.rowId, status, Object),\n };\n }\n // Remove status\n return {\n ...state,\n statuses: omit(state.statuses, action.rowId),\n };\n }\n\n case 'setRowErrors': {\n const { ...errors } = payload;\n return {\n ...state,\n changes: {\n ...state.changes,\n errors: setWith(state.changes.errors, action.rowId, errors, Object),\n },\n };\n }\n\n case 'insertTemporaryRow': {\n const { index, value } = payload;\n return {\n ...state,\n temporaryRows: state.temporaryRows.concat(value),\n changes: {\n ...state.changes,\n rows: setWith(state.changes.rows, action.rowId, value, Object),\n originals: setWith(state.changes.originals, action.rowId, value, Object),\n },\n indexes: setWith(state.indexes, action.rowId, index, Object),\n };\n }\n\n case 'forceValidate': {\n const { newState } = payload;\n\n return {\n ...state,\n ...newState,\n };\n }\n\n default:\n return state;\n }\n}\n\nexport function usePendingChangesState<TType = unknown>(\n handleSave: Table3EditingSaveHandler<TType> | undefined,\n handleChange: Table3EditingChangeHandler<TType> | undefined,\n handleDiscard: Table3EditingDiscardHandler | undefined,\n rowIdentityAccessor: keyof TType,\n validator: Table3EditingValidatorFn<TType> | undefined\n) {\n const localization = useLocalization();\n const [state, dispatch] = React.useReducer<EditingState<TType>, [EditingStateAction]>(reducer, {\n changes: { rows: {}, errors: {}, moveReasons: {}, originals: {}, dirty: {} },\n indexes: {},\n temporaryRows: [],\n statuses: {},\n });\n\n // rows\n function getRowValue(rowId: string) {\n return state.changes.rows?.[rowId] ?? undefined;\n }\n\n function getRowMoveReason(rowId: string) {\n return Object.values(state.changes.moveReasons?.[rowId] ?? {})[0] ?? undefined;\n }\n\n function hasRowErrors(rowId: string) {\n return !!state.changes.errors[rowId]?.row || !!Object.keys(state.changes.errors[rowId]?.cells ?? {}).length;\n }\n\n function hasRowErrorsShownInAlert(rowId: string) {\n return hasRowErrors(rowId) && !!state.changes.errors[rowId]?.shouldShowErrorAlert;\n }\n\n function hasTemporaryRowErrors<T extends TType>() {\n const newRow = state.temporaryRows?.[0] as T | undefined;\n\n if (!newRow) {\n return false;\n }\n\n const rowId = newRow[rowIdentityAccessor as keyof TType] as string;\n return !!state.changes.errors[rowId]?.row || !!Object.keys(state.changes.errors[rowId]?.cells ?? {}).length;\n }\n\n function getRowStatus(rowId: string): EditingSaveState | undefined {\n return state.statuses[rowId];\n }\n\n function setRowStatus(rowId: string, status: EditingSaveState | undefined) {\n dispatch({\n type: 'setRowStatus',\n rowId,\n payload: {\n status,\n },\n });\n }\n\n function showSavedStatus(rowId: string) {\n setRowStatus(rowId, 'saved');\n\n setTimeout(() => {\n setRowStatus(rowId, undefined);\n }, DELAY_BEFORE_REMOVING_SAVE_STATUS);\n }\n\n function insertTemporaryRow<T extends TType>(data: Partial<T>, rowIndex: number) {\n const newRowId = `${TEMPORARY_ROW_ID_PREFIX}${nanoid()}`;\n const value: Partial<TType> = {\n ...data,\n [rowIdentityAccessor]: newRowId,\n };\n\n dispatch({\n type: 'insertTemporaryRow',\n rowId: newRowId,\n payload: { index: rowIndex, value },\n });\n\n return newRowId;\n }\n\n // cells\n function setCellValue<T extends TType>(cell: ReactTableCell<T, unknown>, rowIndex: number, value: unknown) {\n const rowId = cell.row.id;\n const columnId = cell.column.id;\n\n // update if the change is different to the original value\n if (value !== cell.row.original[columnId as keyof TType]) {\n dispatch({\n type: 'setCellValue',\n rowId,\n payload: {\n columnId,\n index: rowIndex,\n row: cell.row.original,\n value,\n },\n });\n }\n // otherwise remove any previous change - no point saving the same value\n else if (cell.row.id in state.changes.rows) {\n dispatch({\n type: 'removeCellValue',\n rowId,\n payload: {\n columnId,\n rowIdentityAccessor,\n },\n });\n }\n }\n\n function getCellValue<T extends TType>(cell: ReactTableCell<T, unknown>): unknown | undefined {\n return state.changes.rows?.[cell.row.id]?.[cell.column.id as keyof TType];\n }\n\n function getCellError<T extends TType>(cell: ReactTableCell<T, unknown>): string | undefined {\n return state.changes.errors?.[cell.row.id]?.cells?.[cell.column.id];\n }\n\n async function onCellChanged<T extends TType>(\n cell: ReactTableCell<T, unknown>,\n rowIndex: number,\n nextValue: any,\n shouldRunUpdaters = true,\n shouldRunValidation = true\n ) {\n const tableMeta = cell.getContext().table.options.meta as ReactTableMeta<TType>;\n const state = tableMeta.editing.getState();\n\n // keyed by runtime column id, so a string-keyed record rather than Partial<TType>\n const changes: Record<string, unknown> =\n nextValue === undefined\n ? { ...state.changes.rows[cell.row.id] }\n : { ...state.changes.rows[cell.row.id], [cell.column.id]: nextValue };\n\n const original = cell.row.original;\n\n if (!Object.keys(changes).length) {\n return;\n }\n\n let updatesForOtherCells: Partial<T> = {};\n\n // run the updater handler if there is one, to see if there are any other cells to update\n if (typeof handleChange === 'function' && shouldRunUpdaters) {\n const previousRowValue = {\n ...original,\n //...getRowChangeset(updatersRef.current[cell.row.id], rowIdentityAccessor),\n };\n const nextRowValue = {\n ...original,\n ...changes,\n };\n updatesForOtherCells = ((await handleChange(\n cell.column.id,\n changes[cell.column.id],\n nextRowValue,\n previousRowValue\n )) ?? {}) as Partial<T>;\n }\n\n // create a projection of the next state, so we can act against it\n const nextChanges = { ...changes, ...updatesForOtherCells };\n const nextMoveReasons: EditingRowMoveReason = { ...state.changes.moveReasons[cell.row.id] };\n const nextCellErrors: Record<string, string> = { ...state.changes.errors[cell.row.id]?.cells };\n\n // run validation - if there are changes, and if there is an original stored\n let validationErrors: Record<string, string> = {};\n\n if (validator && Object.keys(nextChanges).length && original && shouldRunValidation) {\n // merge with the original so we get a full row\n const nextRowValue = { ...original, ...changes, ...updatesForOtherCells };\n validationErrors = ((await validator(nextRowValue)) ?? {}) as Record<string, string>;\n }\n\n // only set errors and move reasons for the cells we're currently acting on\n // why? because the UX is not good if we set them for cells the user hasn't touched yet\n const cellsToActOn = [cell.column.id, ...Object.keys(updatesForOtherCells)];\n const allCells = cell.row._getAllCellsByColumnId();\n\n cellsToActOn.forEach(accessor => {\n if (validationErrors[accessor]) {\n nextCellErrors[accessor] = validationErrors[accessor];\n // don't show move indicator for cells with errors, they aren't valid and can't be saved\n delete nextMoveReasons[accessor];\n } else {\n // there isn't any error in this run, remove any error set in state\n delete nextCellErrors[accessor];\n\n if (allCells[accessor]?.column.getIsSorted()) {\n // run row move determination\n const reason = willRowMove(cell, nextChanges[accessor], rowIndex, localization);\n\n // if the row will move based on this change save why, otherwise delete any existing state\n if (reason) {\n nextMoveReasons[accessor] = reason;\n } else {\n delete nextMoveReasons[accessor];\n }\n }\n }\n });\n\n dispatch({\n type: 'updateRow',\n rowId: cell.row.id,\n payload: {\n cellErrors: nextCellErrors,\n index: rowIndex,\n moveReasons: nextMoveReasons,\n value: nextChanges,\n original,\n },\n });\n }\n\n async function forceValidate<T extends TType>(table: ReactTable<T>) {\n if (validator && table.getRowModel().rows.length) {\n const newState: EditingState<TType> = { ...state };\n const returnIds: string[] = [];\n\n await Promise.all(\n table.getRowModel().rows.map(async (row, index) => {\n const rowWithChanges = { ...row.original, ...state.changes.rows[row.id] };\n\n if (rowWithChanges) {\n const cellErrors = await validator(rowWithChanges);\n\n if (cellErrors) {\n // prep row (it might be new or it might have changes already)\n newState.changes.dirty[row.id] = true;\n newState.changes.originals[row.id] = row.original;\n newState.changes.moveReasons[row.id] = { ...newState.changes.moveReasons[row.id] };\n newState.changes.rows[row.id] = { ...newState.changes.rows[row.id] };\n newState.statuses[row.id] = 'errored';\n newState.indexes[row.id] = index;\n\n newState.changes.errors[row.id] = {\n cells: cellErrors,\n row: newState.changes.errors[row.id]?.row ?? undefined,\n shouldShowErrorAlert: newState.changes.errors[row.id]?.shouldShowErrorAlert ?? false,\n } as EditingError;\n\n const rowChanges = newState.changes.rows[row.id];\n\n // ensure each errored cell actually has a change\n Object.keys(cellErrors).forEach(columnId => {\n if (rowChanges !== undefined) {\n rowChanges[columnId as keyof TType] = rowWithChanges[columnId as keyof TType];\n }\n });\n\n returnIds.push(row.id);\n }\n }\n })\n );\n\n dispatch({\n type: 'forceValidate',\n payload: {\n newState,\n },\n });\n\n return returnIds;\n }\n\n return [];\n }\n\n // general\n function getErrors() {\n return Object.keys(state.changes.errors);\n }\n\n function getErrorsShownInAlert<T extends TType>() {\n const rowsWithErrors = getErrors();\n\n if (!rowsWithErrors.length) {\n return [];\n }\n\n return rowsWithErrors.filter(hasRowErrorsShownInAlert).flatMap(rowId => {\n const errors = state.changes.errors[rowId];\n\n // hasRowErrorsShownInAlert already guarantees this, the check is here to narrow the type\n if (!errors) {\n return [];\n }\n\n return [\n {\n rowId,\n index: state.indexes[rowId],\n changes: state.changes.rows[rowId] as EditingChangeset<T>,\n errors,\n },\n ];\n });\n }\n\n function hasSaved() {\n return !!Object.values(state.statuses).filter(status => status === 'saved').length;\n }\n\n function hasChanges(rowId?: string) {\n return rowId ? !!state.changes.rows[rowId] : !!Object.keys(state.changes.rows).length;\n }\n\n async function saveChanges<T extends TType>(table: ReactTable<T>, rowId: string | undefined = undefined): Promise<boolean> {\n const tableMeta = table.options.meta as ReactTableMeta<TType>;\n const state = tableMeta.editing.getState();\n\n if (!handleSave) {\n // oxlint-disable-next-line no-console\n console.warn('Tried to save, but Table has no onEditingSave handler');\n return false;\n }\n\n // sometimes we only want to save one row\n const changes = rowId ? { [rowId]: state.changes.rows[rowId] } : state.changes.rows;\n\n let completed = true;\n\n for (const rowId of Object.keys(changes)) {\n if (!state.changes.dirty[rowId]) {\n discardChanges(rowId, table);\n continue;\n }\n\n const status = getRowStatus(rowId);\n\n try {\n const rowChanges = JSON.parse(JSON.stringify(changes[rowId] ?? {}));\n\n // don't try to save if - already saving, or there are known errors\n if (!Object.keys(rowChanges).length || status === 'saving' || status === 'errored') {\n continue;\n }\n\n setRowStatus(rowId, 'saving');\n\n const changeSet = { ...(state.changes.originals[rowId] as object), ...rowChanges };\n\n // if we had to create a temporary id, delete it first - it's our data, not theirs\n if (isTemporaryRow(changeSet[rowIdentityAccessor as string])) {\n delete changeSet[rowIdentityAccessor];\n }\n\n // re-run validation, maybe a cell is already invalid but has never been blurred\n if (validator) {\n // oxlint-disable-next-line no-await-in-loop\n const errors = await validator(changeSet);\n\n if (errors && Object.keys(errors).length) {\n throw errors;\n }\n }\n\n // send new data to the server\n // oxlint-disable-next-line no-await-in-loop\n await handleSave(changeSet);\n\n complete(rowId, table, rowChanges);\n showSavedStatus(rowId);\n } catch (error) {\n if (error instanceof ReferenceError || error instanceof TypeError || (error as any)?.response?.status >= 500) {\n // oxlint-disable-next-line no-console\n console.error(error);\n }\n\n // the onEditingSave handler should throw errors when something fails, e.g. validation, network errors etc\n // this code handles those errors and maps them either to row errors or cell specific errors\n let rowError: string | undefined;\n let cellErrors;\n\n if (typeof error === 'string') {\n rowError = error;\n } else if (error instanceof Error) {\n rowError = error.message;\n\n // most of our apis return error objects within this shape\n if (typeof (error as any).response?.data === 'object') {\n cellErrors = (error as any).response?.data;\n }\n } else if (typeof error === 'object') {\n cellErrors = error;\n }\n\n if (rowError || cellErrors) {\n dispatch({\n type: 'setRowErrors',\n rowId,\n payload: {\n row: rowError,\n cells: cellErrors,\n shouldShowErrorAlert: true,\n },\n });\n }\n\n setRowStatus(rowId, 'errored');\n completed = false;\n }\n }\n\n return completed;\n }\n\n async function discardChanges<T extends TType>(rowId: string, table: ReactTable<T>) {\n // remove any new rows from pinned state before discarding them\n if (rowId.startsWith(TEMPORARY_ROW_ID_PREFIX)) {\n table.resetRowPinning(true);\n }\n\n dispatch({\n type: 'removeRow',\n rowId,\n payload: { rowIdentityAccessor },\n });\n\n if (typeof handleDiscard === 'function') {\n await handleDiscard();\n }\n }\n\n function complete<T extends TType>(rowId: string, table: ReactTable<T>, changes: Partial<T>) {\n const handleDiscard = async () => {\n await discardChanges(rowId, table);\n };\n\n dispatch({\n type: 'complete',\n rowId,\n payload: { rowIdentityAccessor, changes, handleDiscard },\n });\n }\n\n function getState() {\n return state;\n }\n\n return {\n getState,\n // row\n getRowValue,\n getRowMoveReason,\n hasRowErrors,\n hasRowErrorsShownInAlert,\n hasTemporaryRowErrors,\n getRowStatus,\n setRowStatus,\n forceValidate,\n // cells\n setCellValue,\n getCellValue,\n getCellError,\n onCellChanged,\n // general\n getErrors,\n getErrorsShownInAlert,\n hasChanges,\n saveChanges,\n discardChanges,\n hasSaved,\n // new rows\n insertTemporaryRow,\n temporaryRows: state.temporaryRows,\n isDirty: hasChanges() || state.temporaryRows.length > 0,\n };\n}\n"],"names":["React","state","rowId","handleDiscard"],"mappings":"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"}
1
+ {"version":3,"file":"useEditingState.js","sources":["../../../../src/components/Table3/features/useEditingState.ts"],"sourcesContent":["import type { Table as ReactTable, Cell as ReactTableCell, TableMeta as ReactTableMeta } from '@tanstack/react-table';\nimport omit from 'lodash/omit';\nimport setWith from 'lodash/setWith';\nimport { nanoid } from 'nanoid';\nimport React from 'react';\n\nimport { useLocalization } from '../../Provider/Localization';\nimport type {\n Table3EditingChangeHandler,\n Table3EditingDiscardHandler,\n Table3EditingSaveHandler,\n Table3EditingValidatorFn,\n} from '../types';\nimport { isTemporaryRow, TEMPORARY_ROW_ID_PREFIX, willRowMove } from '../util/editing';\n\nconst DELAY_BEFORE_REMOVING_SAVE_STATUS = 3000;\n\ntype EditingError = {\n row: string | undefined;\n cells: Record<string, string>;\n shouldShowErrorAlert: boolean;\n};\ntype EditingRowMoveReason = Record<string, 'search' | 'sorting' | 'filter'>;\ntype EditingSaveState = 'saving' | 'saved' | 'errored';\ntype EditingChangeset<TType = unknown> = Partial<TType>;\ntype EditingState<TType = unknown> = {\n changes: {\n rows: Record<string, EditingChangeset<TType>>;\n errors: Record<string, EditingError>;\n moveReasons: Record<string, EditingRowMoveReason>;\n originals: Record<string, TType>;\n dirty: Record<string, boolean>;\n };\n // we store row indexes because due to server loading, we don't always have all data available\n // so we can't determine an index from a row id\n // note: if the row click button in the error Alert is removed, indexes can be removed here\n indexes: Record<string, number>;\n temporaryRows: TType[];\n // unified state for all row statuses\n statuses: Record<string, EditingSaveState>;\n};\ntype EditingStateAction =\n | {\n type:\n | 'setCellValue'\n | 'removeCellValue'\n | 'updateRow'\n | 'mergeRowValue'\n | 'removeRow'\n | 'complete'\n | 'setRowStatus'\n | 'setRowErrors'\n | 'insertTemporaryRow';\n rowId: string;\n payload?: any;\n }\n | {\n type: 'forceValidate';\n payload?: any;\n };\n\nfunction reducer<TType = unknown>(state: EditingState<TType>, action: EditingStateAction): EditingState<TType> {\n const { type, payload } = action;\n\n switch (type) {\n case 'setCellValue': {\n const { columnId, index, row, value } = payload;\n return {\n ...state,\n changes: {\n ...state.changes,\n rows: setWith(state.changes.rows, `${action.rowId}.${columnId}`, value, Object),\n originals: setWith(state.changes.originals, action.rowId, row, Object),\n dirty: setWith(state.changes.dirty, action.rowId, true, Object),\n },\n indexes: setWith(state.indexes, action.rowId, index, Object),\n };\n }\n\n case 'removeCellValue': {\n const { columnId, rowIdentityAccessor } = payload;\n const changes = omit(state.changes.rows, `${action.rowId}.${columnId}`);\n\n const rowChanges = changes[action.rowId];\n\n // if there are no changes left, remove the row\n if (rowChanges && !Object.keys(rowChanges).length) {\n return reducer(state, { type: 'removeRow', rowId: action.rowId, payload: { rowIdentityAccessor } });\n }\n\n return {\n ...state,\n changes: {\n ...state.changes,\n rows: omit(state.changes.rows, `${action.rowId}.${columnId}`),\n errors: omit(state.changes.errors, `${action.rowId}.cells.${columnId}`),\n moveReasons: omit(state.changes.moveReasons, `${action.rowId}.${columnId}`),\n },\n };\n }\n\n case 'updateRow': {\n const { cellErrors, index, moveReasons, original, value } = payload;\n\n return {\n ...state,\n changes: {\n ...state.changes,\n rows: setWith(state.changes.rows, action.rowId, value, Object),\n errors: setWith(\n state.changes.errors,\n `${action.rowId}.cells`,\n cellErrors ?? state.changes.errors[action.rowId]?.cells,\n Object\n ),\n originals: setWith(\n state.changes.originals,\n action.rowId,\n original ?? state.changes.originals[action.rowId],\n Object\n ),\n moveReasons: setWith(\n state.changes.moveReasons,\n action.rowId,\n moveReasons ?? state.changes.moveReasons[action.rowId],\n Object\n ),\n },\n indexes: setWith(state.indexes, action.rowId, index, Object),\n statuses: omit(state.statuses, action.rowId),\n };\n }\n\n case 'removeRow': {\n const { rowIdentityAccessor } = payload;\n\n return {\n ...state,\n changes: {\n ...state.changes,\n rows: omit(state.changes.rows, action.rowId),\n errors: omit(state.changes.errors, action.rowId),\n moveReasons: omit(state.changes.moveReasons, action.rowId),\n originals: omit(state.changes.originals, action.rowId),\n dirty: omit(state.changes.dirty, action.rowId),\n },\n indexes: omit(state.indexes, action.rowId),\n temporaryRows: state.temporaryRows.filter(row => row[rowIdentityAccessor as keyof TType] !== action.rowId),\n };\n }\n\n case 'complete': {\n const { changes, handleDiscard } = payload;\n\n if (JSON.stringify(changes) !== JSON.stringify(state.changes.rows[action.rowId])) {\n return {\n ...state,\n changes: {\n ...state.changes,\n originals: setWith(\n state.changes.originals,\n action.rowId,\n { ...state.changes.originals[action.rowId], ...changes },\n Object\n ),\n },\n };\n }\n\n // bit hacky for now, but editing needs a rewrite\n if (typeof handleDiscard === 'function') {\n handleDiscard();\n }\n\n return state;\n }\n\n case 'setRowStatus': {\n const { status } = payload;\n\n if (status) {\n return {\n ...state,\n statuses: setWith(state.statuses, action.rowId, status, Object),\n };\n }\n // Remove status\n return {\n ...state,\n statuses: omit(state.statuses, action.rowId),\n };\n }\n\n case 'setRowErrors': {\n const { ...errors } = payload;\n return {\n ...state,\n changes: {\n ...state.changes,\n errors: setWith(state.changes.errors, action.rowId, errors, Object),\n },\n };\n }\n\n case 'insertTemporaryRow': {\n const { index, value } = payload;\n return {\n ...state,\n temporaryRows: state.temporaryRows.concat(value),\n changes: {\n ...state.changes,\n rows: setWith(state.changes.rows, action.rowId, value, Object),\n originals: setWith(state.changes.originals, action.rowId, value, Object),\n },\n indexes: setWith(state.indexes, action.rowId, index, Object),\n };\n }\n\n case 'forceValidate': {\n const { newState } = payload;\n\n return {\n ...state,\n ...newState,\n };\n }\n\n default:\n return state;\n }\n}\n\nexport function usePendingChangesState<TType = unknown>(\n handleSave: Table3EditingSaveHandler<TType> | undefined,\n handleChange: Table3EditingChangeHandler<TType> | undefined,\n handleDiscard: Table3EditingDiscardHandler | undefined,\n rowIdentityAccessor: keyof TType,\n validator: Table3EditingValidatorFn<TType> | undefined\n) {\n const localization = useLocalization();\n const [state, dispatch] = React.useReducer(reducer<TType>, {\n changes: { rows: {}, errors: {}, moveReasons: {}, originals: {}, dirty: {} },\n indexes: {},\n temporaryRows: [],\n statuses: {},\n });\n\n // rows\n function getRowValue(rowId: string) {\n return state.changes.rows?.[rowId] ?? undefined;\n }\n\n function getRowMoveReason(rowId: string) {\n return Object.values(state.changes.moveReasons?.[rowId] ?? {})[0] ?? undefined;\n }\n\n function hasRowErrors(rowId: string) {\n return !!state.changes.errors[rowId]?.row || !!Object.keys(state.changes.errors[rowId]?.cells ?? {}).length;\n }\n\n function hasRowErrorsShownInAlert(rowId: string) {\n return hasRowErrors(rowId) && !!state.changes.errors[rowId]?.shouldShowErrorAlert;\n }\n\n function hasTemporaryRowErrors<T extends TType>() {\n const newRow = state.temporaryRows?.[0] as T | undefined;\n\n if (!newRow) {\n return false;\n }\n\n const rowId = newRow[rowIdentityAccessor as keyof TType] as string;\n return !!state.changes.errors[rowId]?.row || !!Object.keys(state.changes.errors[rowId]?.cells ?? {}).length;\n }\n\n function getRowStatus(rowId: string): EditingSaveState | undefined {\n return state.statuses[rowId];\n }\n\n function setRowStatus(rowId: string, status: EditingSaveState | undefined) {\n dispatch({\n type: 'setRowStatus',\n rowId,\n payload: {\n status,\n },\n });\n }\n\n function showSavedStatus(rowId: string) {\n setRowStatus(rowId, 'saved');\n\n setTimeout(() => {\n setRowStatus(rowId, undefined);\n }, DELAY_BEFORE_REMOVING_SAVE_STATUS);\n }\n\n function insertTemporaryRow<T extends TType>(data: Partial<T>, rowIndex: number) {\n const newRowId = `${TEMPORARY_ROW_ID_PREFIX}${nanoid()}`;\n const value: Partial<TType> = {\n ...data,\n [rowIdentityAccessor]: newRowId,\n };\n\n dispatch({\n type: 'insertTemporaryRow',\n rowId: newRowId,\n payload: { index: rowIndex, value },\n });\n\n return newRowId;\n }\n\n // cells\n function setCellValue<T extends TType>(cell: ReactTableCell<T, unknown>, rowIndex: number, value: unknown) {\n const rowId = cell.row.id;\n const columnId = cell.column.id;\n\n // update if the change is different to the original value\n if (value !== cell.row.original[columnId as keyof TType]) {\n dispatch({\n type: 'setCellValue',\n rowId,\n payload: {\n columnId,\n index: rowIndex,\n row: cell.row.original,\n value,\n },\n });\n }\n // otherwise remove any previous change - no point saving the same value\n else if (cell.row.id in state.changes.rows) {\n dispatch({\n type: 'removeCellValue',\n rowId,\n payload: {\n columnId,\n rowIdentityAccessor,\n },\n });\n }\n }\n\n function getCellValue<T extends TType>(cell: ReactTableCell<T, unknown>): unknown | undefined {\n return state.changes.rows?.[cell.row.id]?.[cell.column.id as keyof TType];\n }\n\n function getCellError<T extends TType>(cell: ReactTableCell<T, unknown>): string | undefined {\n return state.changes.errors?.[cell.row.id]?.cells?.[cell.column.id];\n }\n\n async function onCellChanged<T extends TType>(\n cell: ReactTableCell<T, unknown>,\n rowIndex: number,\n nextValue: any,\n shouldRunUpdaters = true,\n shouldRunValidation = true\n ) {\n const tableMeta = cell.getContext().table.options.meta as ReactTableMeta<TType>;\n const state = tableMeta.editing.getState();\n\n // keyed by runtime column id, so a string-keyed record rather than Partial<TType>\n const changes: Record<string, unknown> =\n nextValue === undefined\n ? { ...state.changes.rows[cell.row.id] }\n : { ...state.changes.rows[cell.row.id], [cell.column.id]: nextValue };\n\n const original = cell.row.original;\n\n if (!Object.keys(changes).length) {\n return;\n }\n\n let updatesForOtherCells: Partial<T> = {};\n\n // run the updater handler if there is one, to see if there are any other cells to update\n if (typeof handleChange === 'function' && shouldRunUpdaters) {\n const previousRowValue = {\n ...original,\n //...getRowChangeset(updatersRef.current[cell.row.id], rowIdentityAccessor),\n };\n const nextRowValue = {\n ...original,\n ...changes,\n };\n updatesForOtherCells = ((await handleChange(\n cell.column.id,\n changes[cell.column.id],\n nextRowValue,\n previousRowValue\n )) ?? {}) as Partial<T>;\n }\n\n // create a projection of the next state, so we can act against it\n const nextChanges = { ...changes, ...updatesForOtherCells };\n const nextMoveReasons: EditingRowMoveReason = { ...state.changes.moveReasons[cell.row.id] };\n const nextCellErrors: Record<string, string> = { ...state.changes.errors[cell.row.id]?.cells };\n\n // run validation - if there are changes, and if there is an original stored\n let validationErrors: Record<string, string> = {};\n\n if (validator && Object.keys(nextChanges).length && original && shouldRunValidation) {\n // merge with the original so we get a full row\n const nextRowValue = { ...original, ...changes, ...updatesForOtherCells };\n validationErrors = ((await validator(nextRowValue)) ?? {}) as Record<string, string>;\n }\n\n // only set errors and move reasons for the cells we're currently acting on\n // why? because the UX is not good if we set them for cells the user hasn't touched yet\n const cellsToActOn = [cell.column.id, ...Object.keys(updatesForOtherCells)];\n const allCells = cell.row._getAllCellsByColumnId();\n\n cellsToActOn.forEach(accessor => {\n if (validationErrors[accessor]) {\n nextCellErrors[accessor] = validationErrors[accessor];\n // don't show move indicator for cells with errors, they aren't valid and can't be saved\n delete nextMoveReasons[accessor];\n } else {\n // there isn't any error in this run, remove any error set in state\n delete nextCellErrors[accessor];\n\n if (allCells[accessor]?.column.getIsSorted()) {\n // run row move determination\n const reason = willRowMove(cell, nextChanges[accessor], rowIndex, localization);\n\n // if the row will move based on this change save why, otherwise delete any existing state\n if (reason) {\n nextMoveReasons[accessor] = reason;\n } else {\n delete nextMoveReasons[accessor];\n }\n }\n }\n });\n\n dispatch({\n type: 'updateRow',\n rowId: cell.row.id,\n payload: {\n cellErrors: nextCellErrors,\n index: rowIndex,\n moveReasons: nextMoveReasons,\n value: nextChanges,\n original,\n },\n });\n }\n\n async function forceValidate<T extends TType>(table: ReactTable<T>) {\n if (validator && table.getRowModel().rows.length) {\n const newState: EditingState<TType> = { ...state };\n const returnIds: string[] = [];\n\n await Promise.all(\n table.getRowModel().rows.map(async (row, index) => {\n const rowWithChanges = { ...row.original, ...state.changes.rows[row.id] };\n\n if (rowWithChanges) {\n const cellErrors = await validator(rowWithChanges);\n\n if (cellErrors) {\n // prep row (it might be new or it might have changes already)\n newState.changes.dirty[row.id] = true;\n newState.changes.originals[row.id] = row.original;\n newState.changes.moveReasons[row.id] = { ...newState.changes.moveReasons[row.id] };\n newState.changes.rows[row.id] = { ...newState.changes.rows[row.id] };\n newState.statuses[row.id] = 'errored';\n newState.indexes[row.id] = index;\n\n newState.changes.errors[row.id] = {\n cells: cellErrors,\n row: newState.changes.errors[row.id]?.row ?? undefined,\n shouldShowErrorAlert: newState.changes.errors[row.id]?.shouldShowErrorAlert ?? false,\n } as EditingError;\n\n const rowChanges = newState.changes.rows[row.id];\n\n // ensure each errored cell actually has a change\n Object.keys(cellErrors).forEach(columnId => {\n if (rowChanges !== undefined) {\n rowChanges[columnId as keyof TType] = rowWithChanges[columnId as keyof TType];\n }\n });\n\n returnIds.push(row.id);\n }\n }\n })\n );\n\n dispatch({\n type: 'forceValidate',\n payload: {\n newState,\n },\n });\n\n return returnIds;\n }\n\n return [];\n }\n\n // general\n function getErrors() {\n return Object.keys(state.changes.errors);\n }\n\n function getErrorsShownInAlert<T extends TType>() {\n const rowsWithErrors = getErrors();\n\n if (!rowsWithErrors.length) {\n return [];\n }\n\n return rowsWithErrors.filter(hasRowErrorsShownInAlert).map(rowId => ({\n rowId,\n index: state.indexes[rowId],\n changes: state.changes.rows[rowId] as EditingChangeset<T>,\n // hasRowErrorsShownInAlert already guarantees errors is defined; the cast narrows the type\n errors: state.changes.errors[rowId] as EditingError,\n }));\n }\n\n function hasSaved() {\n return !!Object.values(state.statuses).filter(status => status === 'saved').length;\n }\n\n function hasChanges(rowId?: string) {\n return rowId ? !!state.changes.rows[rowId] : !!Object.keys(state.changes.rows).length;\n }\n\n async function saveChanges<T extends TType>(table: ReactTable<T>, rowId: string | undefined = undefined): Promise<boolean> {\n const tableMeta = table.options.meta as ReactTableMeta<TType>;\n const state = tableMeta.editing.getState();\n\n if (!handleSave) {\n // oxlint-disable-next-line no-console\n console.warn('Tried to save, but Table has no onEditingSave handler');\n return false;\n }\n\n // sometimes we only want to save one row\n const changes = rowId ? { [rowId]: state.changes.rows[rowId] } : state.changes.rows;\n\n let completed = true;\n\n for (const rowId of Object.keys(changes)) {\n if (!state.changes.dirty[rowId]) {\n discardChanges(rowId, table);\n continue;\n }\n\n const status = getRowStatus(rowId);\n\n try {\n const rowChanges = JSON.parse(JSON.stringify(changes[rowId] ?? {}));\n\n // don't try to save if - already saving, or there are known errors\n if (!Object.keys(rowChanges).length || status === 'saving' || status === 'errored') {\n continue;\n }\n\n setRowStatus(rowId, 'saving');\n\n const changeSet = { ...(state.changes.originals[rowId] as object), ...rowChanges };\n\n // if we had to create a temporary id, delete it first - it's our data, not theirs\n if (isTemporaryRow(changeSet[rowIdentityAccessor as string])) {\n delete changeSet[rowIdentityAccessor];\n }\n\n // re-run validation, maybe a cell is already invalid but has never been blurred\n if (validator) {\n // oxlint-disable-next-line no-await-in-loop\n const errors = await validator(changeSet);\n\n if (errors && Object.keys(errors).length) {\n throw errors;\n }\n }\n\n // send new data to the server\n // oxlint-disable-next-line no-await-in-loop\n await handleSave(changeSet);\n\n complete(rowId, table, rowChanges);\n showSavedStatus(rowId);\n } catch (error) {\n if (error instanceof ReferenceError || error instanceof TypeError || (error as any)?.response?.status >= 500) {\n // oxlint-disable-next-line no-console\n console.error(error);\n }\n\n // the onEditingSave handler should throw errors when something fails, e.g. validation, network errors etc\n // this code handles those errors and maps them either to row errors or cell specific errors\n let rowError: string | undefined;\n let cellErrors;\n\n if (typeof error === 'string') {\n rowError = error;\n } else if (error instanceof Error) {\n rowError = error.message;\n\n // most of our apis return error objects within this shape\n if (typeof (error as any).response?.data === 'object') {\n cellErrors = (error as any).response?.data;\n }\n } else if (typeof error === 'object') {\n cellErrors = error;\n }\n\n if (rowError || cellErrors) {\n dispatch({\n type: 'setRowErrors',\n rowId,\n payload: {\n row: rowError,\n cells: cellErrors,\n shouldShowErrorAlert: true,\n },\n });\n }\n\n setRowStatus(rowId, 'errored');\n completed = false;\n }\n }\n\n return completed;\n }\n\n async function discardChanges<T extends TType>(rowId: string, table: ReactTable<T>) {\n // remove any new rows from pinned state before discarding them\n if (rowId.startsWith(TEMPORARY_ROW_ID_PREFIX)) {\n table.resetRowPinning(true);\n }\n\n dispatch({\n type: 'removeRow',\n rowId,\n payload: { rowIdentityAccessor },\n });\n\n if (typeof handleDiscard === 'function') {\n await handleDiscard();\n }\n }\n\n function complete<T extends TType>(rowId: string, table: ReactTable<T>, changes: Partial<T>) {\n const handleDiscard = async () => {\n await discardChanges(rowId, table);\n };\n\n dispatch({\n type: 'complete',\n rowId,\n payload: { rowIdentityAccessor, changes, handleDiscard },\n });\n }\n\n function getState() {\n return state;\n }\n\n return {\n getState,\n // row\n getRowValue,\n getRowMoveReason,\n hasRowErrors,\n hasRowErrorsShownInAlert,\n hasTemporaryRowErrors,\n getRowStatus,\n setRowStatus,\n forceValidate,\n // cells\n setCellValue,\n getCellValue,\n getCellError,\n onCellChanged,\n // general\n getErrors,\n getErrorsShownInAlert,\n hasChanges,\n saveChanges,\n discardChanges,\n hasSaved,\n // new rows\n insertTemporaryRow,\n temporaryRows: state.temporaryRows,\n isDirty: hasChanges() || state.temporaryRows.length > 0,\n };\n}\n"],"names":["React","state","rowId","handleDiscard"],"mappings":";;;;;;AAeA,MAAM,oCAAoC;AA8C1C,SAAS,QAAyB,OAA4B,QAAiD;AAC3G,QAAM,EAAE,MAAM,QAAA,IAAY;AAE1B,UAAQ,MAAA;AAAA,IACJ,KAAK,gBAAgB;AACjB,YAAM,EAAE,UAAU,OAAO,KAAK,UAAU;AACxC,aAAO;AAAA,QACH,GAAG;AAAA,QACH,SAAS;AAAA,UACL,GAAG,MAAM;AAAA,UACT,MAAM,QAAQ,MAAM,QAAQ,MAAM,GAAG,OAAO,KAAK,IAAI,QAAQ,IAAI,OAAO,MAAM;AAAA,UAC9E,WAAW,QAAQ,MAAM,QAAQ,WAAW,OAAO,OAAO,KAAK,MAAM;AAAA,UACrE,OAAO,QAAQ,MAAM,QAAQ,OAAO,OAAO,OAAO,MAAM,MAAM;AAAA,QAAA;AAAA,QAElE,SAAS,QAAQ,MAAM,SAAS,OAAO,OAAO,OAAO,MAAM;AAAA,MAAA;AAAA,IAEnE;AAAA,IAEA,KAAK,mBAAmB;AACpB,YAAM,EAAE,UAAU,oBAAA,IAAwB;AAC1C,YAAM,UAAU,KAAK,MAAM,QAAQ,MAAM,GAAG,OAAO,KAAK,IAAI,QAAQ,EAAE;AAEtE,YAAM,aAAa,QAAQ,OAAO,KAAK;AAGvC,UAAI,cAAc,CAAC,OAAO,KAAK,UAAU,EAAE,QAAQ;AAC/C,eAAO,QAAQ,OAAO,EAAE,MAAM,aAAa,OAAO,OAAO,OAAO,SAAS,EAAE,oBAAA,EAAoB,CAAG;AAAA,MACtG;AAEA,aAAO;AAAA,QACH,GAAG;AAAA,QACH,SAAS;AAAA,UACL,GAAG,MAAM;AAAA,UACT,MAAM,KAAK,MAAM,QAAQ,MAAM,GAAG,OAAO,KAAK,IAAI,QAAQ,EAAE;AAAA,UAC5D,QAAQ,KAAK,MAAM,QAAQ,QAAQ,GAAG,OAAO,KAAK,UAAU,QAAQ,EAAE;AAAA,UACtE,aAAa,KAAK,MAAM,QAAQ,aAAa,GAAG,OAAO,KAAK,IAAI,QAAQ,EAAE;AAAA,QAAA;AAAA,MAC9E;AAAA,IAER;AAAA,IAEA,KAAK,aAAa;AACd,YAAM,EAAE,YAAY,OAAO,aAAa,UAAU,UAAU;AAE5D,aAAO;AAAA,QACH,GAAG;AAAA,QACH,SAAS;AAAA,UACL,GAAG,MAAM;AAAA,UACT,MAAM,QAAQ,MAAM,QAAQ,MAAM,OAAO,OAAO,OAAO,MAAM;AAAA,UAC7D,QAAQ;AAAA,YACJ,MAAM,QAAQ;AAAA,YACd,GAAG,OAAO,KAAK;AAAA,YACf,cAAc,MAAM,QAAQ,OAAO,OAAO,KAAK,GAAG;AAAA,YAClD;AAAA,UAAA;AAAA,UAEJ,WAAW;AAAA,YACP,MAAM,QAAQ;AAAA,YACd,OAAO;AAAA,YACP,YAAY,MAAM,QAAQ,UAAU,OAAO,KAAK;AAAA,YAChD;AAAA,UAAA;AAAA,UAEJ,aAAa;AAAA,YACT,MAAM,QAAQ;AAAA,YACd,OAAO;AAAA,YACP,eAAe,MAAM,QAAQ,YAAY,OAAO,KAAK;AAAA,YACrD;AAAA,UAAA;AAAA,QACJ;AAAA,QAEJ,SAAS,QAAQ,MAAM,SAAS,OAAO,OAAO,OAAO,MAAM;AAAA,QAC3D,UAAU,KAAK,MAAM,UAAU,OAAO,KAAK;AAAA,MAAA;AAAA,IAEnD;AAAA,IAEA,KAAK,aAAa;AACd,YAAM,EAAE,wBAAwB;AAEhC,aAAO;AAAA,QACH,GAAG;AAAA,QACH,SAAS;AAAA,UACL,GAAG,MAAM;AAAA,UACT,MAAM,KAAK,MAAM,QAAQ,MAAM,OAAO,KAAK;AAAA,UAC3C,QAAQ,KAAK,MAAM,QAAQ,QAAQ,OAAO,KAAK;AAAA,UAC/C,aAAa,KAAK,MAAM,QAAQ,aAAa,OAAO,KAAK;AAAA,UACzD,WAAW,KAAK,MAAM,QAAQ,WAAW,OAAO,KAAK;AAAA,UACrD,OAAO,KAAK,MAAM,QAAQ,OAAO,OAAO,KAAK;AAAA,QAAA;AAAA,QAEjD,SAAS,KAAK,MAAM,SAAS,OAAO,KAAK;AAAA,QACzC,eAAe,MAAM,cAAc,OAAO,SAAO,IAAI,mBAAkC,MAAM,OAAO,KAAK;AAAA,MAAA;AAAA,IAEjH;AAAA,IAEA,KAAK,YAAY;AACb,YAAM,EAAE,SAAS,cAAA,IAAkB;AAEnC,UAAI,KAAK,UAAU,OAAO,MAAM,KAAK,UAAU,MAAM,QAAQ,KAAK,OAAO,KAAK,CAAC,GAAG;AAC9E,eAAO;AAAA,UACH,GAAG;AAAA,UACH,SAAS;AAAA,YACL,GAAG,MAAM;AAAA,YACT,WAAW;AAAA,cACP,MAAM,QAAQ;AAAA,cACd,OAAO;AAAA,cACP,EAAE,GAAG,MAAM,QAAQ,UAAU,OAAO,KAAK,GAAG,GAAG,QAAA;AAAA,cAC/C;AAAA,YAAA;AAAA,UACJ;AAAA,QACJ;AAAA,MAER;AAGA,UAAI,OAAO,kBAAkB,YAAY;AACrC,sBAAA;AAAA,MACJ;AAEA,aAAO;AAAA,IACX;AAAA,IAEA,KAAK,gBAAgB;AACjB,YAAM,EAAE,WAAW;AAEnB,UAAI,QAAQ;AACR,eAAO;AAAA,UACH,GAAG;AAAA,UACH,UAAU,QAAQ,MAAM,UAAU,OAAO,OAAO,QAAQ,MAAM;AAAA,QAAA;AAAA,MAEtE;AAEA,aAAO;AAAA,QACH,GAAG;AAAA,QACH,UAAU,KAAK,MAAM,UAAU,OAAO,KAAK;AAAA,MAAA;AAAA,IAEnD;AAAA,IAEA,KAAK,gBAAgB;AACjB,YAAM,EAAE,GAAG,OAAA,IAAW;AACtB,aAAO;AAAA,QACH,GAAG;AAAA,QACH,SAAS;AAAA,UACL,GAAG,MAAM;AAAA,UACT,QAAQ,QAAQ,MAAM,QAAQ,QAAQ,OAAO,OAAO,QAAQ,MAAM;AAAA,QAAA;AAAA,MACtE;AAAA,IAER;AAAA,IAEA,KAAK,sBAAsB;AACvB,YAAM,EAAE,OAAO,MAAA,IAAU;AACzB,aAAO;AAAA,QACH,GAAG;AAAA,QACH,eAAe,MAAM,cAAc,OAAO,KAAK;AAAA,QAC/C,SAAS;AAAA,UACL,GAAG,MAAM;AAAA,UACT,MAAM,QAAQ,MAAM,QAAQ,MAAM,OAAO,OAAO,OAAO,MAAM;AAAA,UAC7D,WAAW,QAAQ,MAAM,QAAQ,WAAW,OAAO,OAAO,OAAO,MAAM;AAAA,QAAA;AAAA,QAE3E,SAAS,QAAQ,MAAM,SAAS,OAAO,OAAO,OAAO,MAAM;AAAA,MAAA;AAAA,IAEnE;AAAA,IAEA,KAAK,iBAAiB;AAClB,YAAM,EAAE,aAAa;AAErB,aAAO;AAAA,QACH,GAAG;AAAA,QACH,GAAG;AAAA,MAAA;AAAA,IAEX;AAAA,IAEA;AACI,aAAO;AAAA,EAAA;AAEnB;AAEO,SAAS,uBACZ,YACA,cACA,eACA,qBACA,WACF;AACE,QAAM,eAAe,gBAAA;AACrB,QAAM,CAAC,OAAO,QAAQ,IAAIA,eAAM,WAAW,SAAgB;AAAA,IACvD,SAAS,EAAE,MAAM,CAAA,GAAI,QAAQ,CAAA,GAAI,aAAa,CAAA,GAAI,WAAW,IAAI,OAAO,CAAA,EAAC;AAAA,IACzE,SAAS,CAAA;AAAA,IACT,eAAe,CAAA;AAAA,IACf,UAAU,CAAA;AAAA,EAAC,CACd;AAGD,WAAS,YAAY,OAAe;AAChC,WAAO,MAAM,QAAQ,OAAO,KAAK,KAAK;AAAA,EAC1C;AAEA,WAAS,iBAAiB,OAAe;AACrC,WAAO,OAAO,OAAO,MAAM,QAAQ,cAAc,KAAK,KAAK,CAAA,CAAE,EAAE,CAAC,KAAK;AAAA,EACzE;AAEA,WAAS,aAAa,OAAe;AACjC,WAAO,CAAC,CAAC,MAAM,QAAQ,OAAO,KAAK,GAAG,OAAO,CAAC,CAAC,OAAO,KAAK,MAAM,QAAQ,OAAO,KAAK,GAAG,SAAS,CAAA,CAAE,EAAE;AAAA,EACzG;AAEA,WAAS,yBAAyB,OAAe;AAC7C,WAAO,aAAa,KAAK,KAAK,CAAC,CAAC,MAAM,QAAQ,OAAO,KAAK,GAAG;AAAA,EACjE;AAEA,WAAS,wBAAyC;AAC9C,UAAM,SAAS,MAAM,gBAAgB,CAAC;AAEtC,QAAI,CAAC,QAAQ;AACT,aAAO;AAAA,IACX;AAEA,UAAM,QAAQ,OAAO,mBAAkC;AACvD,WAAO,CAAC,CAAC,MAAM,QAAQ,OAAO,KAAK,GAAG,OAAO,CAAC,CAAC,OAAO,KAAK,MAAM,QAAQ,OAAO,KAAK,GAAG,SAAS,CAAA,CAAE,EAAE;AAAA,EACzG;AAEA,WAAS,aAAa,OAA6C;AAC/D,WAAO,MAAM,SAAS,KAAK;AAAA,EAC/B;AAEA,WAAS,aAAa,OAAe,QAAsC;AACvE,aAAS;AAAA,MACL,MAAM;AAAA,MACN;AAAA,MACA,SAAS;AAAA,QACL;AAAA,MAAA;AAAA,IACJ,CACH;AAAA,EACL;AAEA,WAAS,gBAAgB,OAAe;AACpC,iBAAa,OAAO,OAAO;AAE3B,eAAW,MAAM;AACb,mBAAa,OAAO,MAAS;AAAA,IACjC,GAAG,iCAAiC;AAAA,EACxC;AAEA,WAAS,mBAAoC,MAAkB,UAAkB;AAC7E,UAAM,WAAW,GAAG,uBAAuB,GAAG,QAAQ;AACtD,UAAM,QAAwB;AAAA,MAC1B,GAAG;AAAA,MACH,CAAC,mBAAmB,GAAG;AAAA,IAAA;AAG3B,aAAS;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP,SAAS,EAAE,OAAO,UAAU,MAAA;AAAA,IAAM,CACrC;AAED,WAAO;AAAA,EACX;AAGA,WAAS,aAA8B,MAAkC,UAAkB,OAAgB;AACvG,UAAM,QAAQ,KAAK,IAAI;AACvB,UAAM,WAAW,KAAK,OAAO;AAG7B,QAAI,UAAU,KAAK,IAAI,SAAS,QAAuB,GAAG;AACtD,eAAS;AAAA,QACL,MAAM;AAAA,QACN;AAAA,QACA,SAAS;AAAA,UACL;AAAA,UACA,OAAO;AAAA,UACP,KAAK,KAAK,IAAI;AAAA,UACd;AAAA,QAAA;AAAA,MACJ,CACH;AAAA,IACL,WAES,KAAK,IAAI,MAAM,MAAM,QAAQ,MAAM;AACxC,eAAS;AAAA,QACL,MAAM;AAAA,QACN;AAAA,QACA,SAAS;AAAA,UACL;AAAA,UACA;AAAA,QAAA;AAAA,MACJ,CACH;AAAA,IACL;AAAA,EACJ;AAEA,WAAS,aAA8B,MAAuD;AAC1F,WAAO,MAAM,QAAQ,OAAO,KAAK,IAAI,EAAE,IAAI,KAAK,OAAO,EAAiB;AAAA,EAC5E;AAEA,WAAS,aAA8B,MAAsD;AACzF,WAAO,MAAM,QAAQ,SAAS,KAAK,IAAI,EAAE,GAAG,QAAQ,KAAK,OAAO,EAAE;AAAA,EACtE;AAEA,iBAAe,cACX,MACA,UACA,WACA,oBAAoB,MACpB,sBAAsB,MACxB;AACE,UAAM,YAAY,KAAK,WAAA,EAAa,MAAM,QAAQ;AAClD,UAAMC,SAAQ,UAAU,QAAQ,SAAA;AAGhC,UAAM,UACF,cAAc,SACR,EAAE,GAAGA,OAAM,QAAQ,KAAK,KAAK,IAAI,EAAE,EAAA,IACnC,EAAE,GAAGA,OAAM,QAAQ,KAAK,KAAK,IAAI,EAAE,GAAG,CAAC,KAAK,OAAO,EAAE,GAAG,UAAA;AAElE,UAAM,WAAW,KAAK,IAAI;AAE1B,QAAI,CAAC,OAAO,KAAK,OAAO,EAAE,QAAQ;AAC9B;AAAA,IACJ;AAEA,QAAI,uBAAmC,CAAA;AAGvC,QAAI,OAAO,iBAAiB,cAAc,mBAAmB;AACzD,YAAM,mBAAmB;AAAA,QACrB,GAAG;AAAA;AAAA,MAAA;AAGP,YAAM,eAAe;AAAA,QACjB,GAAG;AAAA,QACH,GAAG;AAAA,MAAA;AAEP,6BAAyB,MAAM;AAAA,QAC3B,KAAK,OAAO;AAAA,QACZ,QAAQ,KAAK,OAAO,EAAE;AAAA,QACtB;AAAA,QACA;AAAA,MAAA,KACE,CAAA;AAAA,IACV;AAGA,UAAM,cAAc,EAAE,GAAG,SAAS,GAAG,qBAAA;AACrC,UAAM,kBAAwC,EAAE,GAAGA,OAAM,QAAQ,YAAY,KAAK,IAAI,EAAE,EAAA;AACxF,UAAM,iBAAyC,EAAE,GAAGA,OAAM,QAAQ,OAAO,KAAK,IAAI,EAAE,GAAG,MAAA;AAGvF,QAAI,mBAA2C,CAAA;AAE/C,QAAI,aAAa,OAAO,KAAK,WAAW,EAAE,UAAU,YAAY,qBAAqB;AAEjF,YAAM,eAAe,EAAE,GAAG,UAAU,GAAG,SAAS,GAAG,qBAAA;AACnD,yBAAqB,MAAM,UAAU,YAAY,KAAM,CAAA;AAAA,IAC3D;AAIA,UAAM,eAAe,CAAC,KAAK,OAAO,IAAI,GAAG,OAAO,KAAK,oBAAoB,CAAC;AAC1E,UAAM,WAAW,KAAK,IAAI,uBAAA;AAE1B,iBAAa,QAAQ,CAAA,aAAY;AAC7B,UAAI,iBAAiB,QAAQ,GAAG;AAC5B,uBAAe,QAAQ,IAAI,iBAAiB,QAAQ;AAEpD,eAAO,gBAAgB,QAAQ;AAAA,MACnC,OAAO;AAEH,eAAO,eAAe,QAAQ;AAE9B,YAAI,SAAS,QAAQ,GAAG,OAAO,eAAe;AAE1C,gBAAM,SAAS,YAAY,MAAM,YAAY,QAAQ,GAAG,UAAU,YAAY;AAG9E,cAAI,QAAQ;AACR,4BAAgB,QAAQ,IAAI;AAAA,UAChC,OAAO;AACH,mBAAO,gBAAgB,QAAQ;AAAA,UACnC;AAAA,QACJ;AAAA,MACJ;AAAA,IACJ,CAAC;AAED,aAAS;AAAA,MACL,MAAM;AAAA,MACN,OAAO,KAAK,IAAI;AAAA,MAChB,SAAS;AAAA,QACL,YAAY;AAAA,QACZ,OAAO;AAAA,QACP,aAAa;AAAA,QACb,OAAO;AAAA,QACP;AAAA,MAAA;AAAA,IACJ,CACH;AAAA,EACL;AAEA,iBAAe,cAA+B,OAAsB;AAChE,QAAI,aAAa,MAAM,YAAA,EAAc,KAAK,QAAQ;AAC9C,YAAM,WAAgC,EAAE,GAAG,MAAA;AAC3C,YAAM,YAAsB,CAAA;AAE5B,YAAM,QAAQ;AAAA,QACV,MAAM,YAAA,EAAc,KAAK,IAAI,OAAO,KAAK,UAAU;AAC/C,gBAAM,iBAAiB,EAAE,GAAG,IAAI,UAAU,GAAG,MAAM,QAAQ,KAAK,IAAI,EAAE,EAAA;AAEtE,cAAI,gBAAgB;AAChB,kBAAM,aAAa,MAAM,UAAU,cAAc;AAEjD,gBAAI,YAAY;AAEZ,uBAAS,QAAQ,MAAM,IAAI,EAAE,IAAI;AACjC,uBAAS,QAAQ,UAAU,IAAI,EAAE,IAAI,IAAI;AACzC,uBAAS,QAAQ,YAAY,IAAI,EAAE,IAAI,EAAE,GAAG,SAAS,QAAQ,YAAY,IAAI,EAAE,EAAA;AAC/E,uBAAS,QAAQ,KAAK,IAAI,EAAE,IAAI,EAAE,GAAG,SAAS,QAAQ,KAAK,IAAI,EAAE,EAAA;AACjE,uBAAS,SAAS,IAAI,EAAE,IAAI;AAC5B,uBAAS,QAAQ,IAAI,EAAE,IAAI;AAE3B,uBAAS,QAAQ,OAAO,IAAI,EAAE,IAAI;AAAA,gBAC9B,OAAO;AAAA,gBACP,KAAK,SAAS,QAAQ,OAAO,IAAI,EAAE,GAAG,OAAO;AAAA,gBAC7C,sBAAsB,SAAS,QAAQ,OAAO,IAAI,EAAE,GAAG,wBAAwB;AAAA,cAAA;AAGnF,oBAAM,aAAa,SAAS,QAAQ,KAAK,IAAI,EAAE;AAG/C,qBAAO,KAAK,UAAU,EAAE,QAAQ,CAAA,aAAY;AACxC,oBAAI,eAAe,QAAW;AAC1B,6BAAW,QAAuB,IAAI,eAAe,QAAuB;AAAA,gBAChF;AAAA,cACJ,CAAC;AAED,wBAAU,KAAK,IAAI,EAAE;AAAA,YACzB;AAAA,UACJ;AAAA,QACJ,CAAC;AAAA,MAAA;AAGL,eAAS;AAAA,QACL,MAAM;AAAA,QACN,SAAS;AAAA,UACL;AAAA,QAAA;AAAA,MACJ,CACH;AAED,aAAO;AAAA,IACX;AAEA,WAAO,CAAA;AAAA,EACX;AAGA,WAAS,YAAY;AACjB,WAAO,OAAO,KAAK,MAAM,QAAQ,MAAM;AAAA,EAC3C;AAEA,WAAS,wBAAyC;AAC9C,UAAM,iBAAiB,UAAA;AAEvB,QAAI,CAAC,eAAe,QAAQ;AACxB,aAAO,CAAA;AAAA,IACX;AAEA,WAAO,eAAe,OAAO,wBAAwB,EAAE,IAAI,CAAA,WAAU;AAAA,MACjE;AAAA,MACA,OAAO,MAAM,QAAQ,KAAK;AAAA,MAC1B,SAAS,MAAM,QAAQ,KAAK,KAAK;AAAA;AAAA,MAEjC,QAAQ,MAAM,QAAQ,OAAO,KAAK;AAAA,IAAA,EACpC;AAAA,EACN;AAEA,WAAS,WAAW;AAChB,WAAO,CAAC,CAAC,OAAO,OAAO,MAAM,QAAQ,EAAE,OAAO,CAAA,WAAU,WAAW,OAAO,EAAE;AAAA,EAChF;AAEA,WAAS,WAAW,OAAgB;AAChC,WAAO,QAAQ,CAAC,CAAC,MAAM,QAAQ,KAAK,KAAK,IAAI,CAAC,CAAC,OAAO,KAAK,MAAM,QAAQ,IAAI,EAAE;AAAA,EACnF;AAEA,iBAAe,YAA6B,OAAsB,QAA4B,QAA6B;AACvH,UAAM,YAAY,MAAM,QAAQ;AAChC,UAAMA,SAAQ,UAAU,QAAQ,SAAA;AAEhC,QAAI,CAAC,YAAY;AAEb,cAAQ,KAAK,uDAAuD;AACpE,aAAO;AAAA,IACX;AAGA,UAAM,UAAU,QAAQ,EAAE,CAAC,KAAK,GAAGA,OAAM,QAAQ,KAAK,KAAK,EAAA,IAAMA,OAAM,QAAQ;AAE/E,QAAI,YAAY;AAEhB,eAAWC,UAAS,OAAO,KAAK,OAAO,GAAG;AACtC,UAAI,CAACD,OAAM,QAAQ,MAAMC,MAAK,GAAG;AAC7B,uBAAeA,QAAO,KAAK;AAC3B;AAAA,MACJ;AAEA,YAAM,SAAS,aAAaA,MAAK;AAEjC,UAAI;AACA,cAAM,aAAa,KAAK,MAAM,KAAK,UAAU,QAAQA,MAAK,KAAK,CAAA,CAAE,CAAC;AAGlE,YAAI,CAAC,OAAO,KAAK,UAAU,EAAE,UAAU,WAAW,YAAY,WAAW,WAAW;AAChF;AAAA,QACJ;AAEA,qBAAaA,QAAO,QAAQ;AAE5B,cAAM,YAAY,EAAE,GAAID,OAAM,QAAQ,UAAUC,MAAK,GAAc,GAAG,WAAA;AAGtE,YAAI,eAAe,UAAU,mBAA6B,CAAC,GAAG;AAC1D,iBAAO,UAAU,mBAAmB;AAAA,QACxC;AAGA,YAAI,WAAW;AAEX,gBAAM,SAAS,MAAM,UAAU,SAAS;AAExC,cAAI,UAAU,OAAO,KAAK,MAAM,EAAE,QAAQ;AACtC,kBAAM;AAAA,UACV;AAAA,QACJ;AAIA,cAAM,WAAW,SAAS;AAE1B,iBAASA,QAAO,OAAO,UAAU;AACjC,wBAAgBA,MAAK;AAAA,MACzB,SAAS,OAAO;AACZ,YAAI,iBAAiB,kBAAkB,iBAAiB,aAAc,OAAe,UAAU,UAAU,KAAK;AAE1G,kBAAQ,MAAM,KAAK;AAAA,QACvB;AAIA,YAAI;AACJ,YAAI;AAEJ,YAAI,OAAO,UAAU,UAAU;AAC3B,qBAAW;AAAA,QACf,WAAW,iBAAiB,OAAO;AAC/B,qBAAW,MAAM;AAGjB,cAAI,OAAQ,MAAc,UAAU,SAAS,UAAU;AACnD,yBAAc,MAAc,UAAU;AAAA,UAC1C;AAAA,QACJ,WAAW,OAAO,UAAU,UAAU;AAClC,uBAAa;AAAA,QACjB;AAEA,YAAI,YAAY,YAAY;AACxB,mBAAS;AAAA,YACL,MAAM;AAAA,YACN,OAAAA;AAAAA,YACA,SAAS;AAAA,cACL,KAAK;AAAA,cACL,OAAO;AAAA,cACP,sBAAsB;AAAA,YAAA;AAAA,UAC1B,CACH;AAAA,QACL;AAEA,qBAAaA,QAAO,SAAS;AAC7B,oBAAY;AAAA,MAChB;AAAA,IACJ;AAEA,WAAO;AAAA,EACX;AAEA,iBAAe,eAAgC,OAAe,OAAsB;AAEhF,QAAI,MAAM,WAAW,uBAAuB,GAAG;AAC3C,YAAM,gBAAgB,IAAI;AAAA,IAC9B;AAEA,aAAS;AAAA,MACL,MAAM;AAAA,MACN;AAAA,MACA,SAAS,EAAE,oBAAA;AAAA,IAAoB,CAClC;AAED,QAAI,OAAO,kBAAkB,YAAY;AACrC,YAAM,cAAA;AAAA,IACV;AAAA,EACJ;AAEA,WAAS,SAA0B,OAAe,OAAsB,SAAqB;AACzF,UAAMC,iBAAgB,YAAY;AAC9B,YAAM,eAAe,OAAO,KAAK;AAAA,IACrC;AAEA,aAAS;AAAA,MACL,MAAM;AAAA,MACN;AAAA,MACA,SAAS,EAAE,qBAAqB,SAAS,eAAAA,eAAAA;AAAAA,IAAc,CAC1D;AAAA,EACL;AAEA,WAAS,WAAW;AAChB,WAAO;AAAA,EACX;AAEA,SAAO;AAAA,IACH;AAAA;AAAA,IAEA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,IACA,eAAe,MAAM;AAAA,IACrB,SAAS,WAAA,KAAgB,MAAM,cAAc,SAAS;AAAA,EAAA;AAE9D;"}
package/dist/taco.css CHANGED
@@ -903,8 +903,10 @@ table[data-taco^='table'][data-table-font-size='large'] tr[data-row-id] {
903
903
  }
904
904
 
905
905
  /* small font size */
906
- table[data-taco^='table'][data-table-font-size='small'] tr[data-row-id] td [data-taco='legacy-button'],
907
- table[data-taco^='table'][data-table-font-size='small'] tr[data-row-id] td [data-taco='legacy-icon-button'] {
906
+ table[data-taco^='table'][data-table-font-size='small']
907
+ tr[data-row-id]
908
+ td
909
+ :where([data-taco='legacy-button'], [data-taco='legacy-icon-button'], [data-taco='button'], [data-taco='linkbutton']) {
908
910
  @apply !-my-2;
909
911
  }
910
912
 
@@ -1080,8 +1082,10 @@ table[data-taco^='table'][data-table-font-size='medium'] tr[data-row-id] td [dat
1080
1082
  @apply !h-5 !w-5;
1081
1083
  }
1082
1084
 
1083
- table[data-taco^='table'][data-table-font-size='medium'] tr[data-row-id] td [data-taco='legacy-button'],
1084
- table[data-taco^='table'][data-table-font-size='medium'] tr[data-row-id] td [data-taco='legacy-icon-button'] {
1085
+ table[data-taco^='table'][data-table-font-size='medium']
1086
+ tr[data-row-id]
1087
+ td
1088
+ :where([data-taco='legacy-button'], [data-taco='legacy-icon-button'], [data-taco='button'], [data-taco='linkbutton']) {
1085
1089
  @apply !-my-1.5;
1086
1090
  }
1087
1091
 
@@ -1194,8 +1198,10 @@ table[data-taco^='table'][data-table-font-size='large'] tr[data-row-id] td [data
1194
1198
  @apply !h-6 !w-6;
1195
1199
  }
1196
1200
 
1197
- table[data-taco^='table'][data-table-font-size='large'] tr[data-row-id] td [data-taco='legacy-button'],
1198
- table[data-taco^='table'][data-table-font-size='large'] tr[data-row-id] td [data-taco='legacy-icon-button'] {
1201
+ table[data-taco^='table'][data-table-font-size='large']
1202
+ tr[data-row-id]
1203
+ td
1204
+ :where([data-taco='legacy-button'], [data-taco='legacy-icon-button'], [data-taco='button'], [data-taco='linkbutton']) {
1199
1205
  @apply !-my-1;
1200
1206
  }
1201
1207
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@economic/taco",
3
- "version": "12.0.0-react18-compat.0",
3
+ "version": "13.0.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -46,7 +46,7 @@
46
46
  "@dnd-kit/modifiers": "^9.0.0",
47
47
  "@dnd-kit/sortable": "^10.0.0",
48
48
  "@dnd-kit/utilities": "^3.2.2",
49
- "@economic/taco-tokens": "3.0.2-react18-compat.0",
49
+ "@economic/taco-tokens": "^3.0.2",
50
50
  "@radix-ui/react-scroll-area": "1.2.10",
51
51
  "@react-aria/focus": "^3.19.0",
52
52
  "@react-aria/interactions": "^3.12.0",
@@ -68,12 +68,12 @@
68
68
  "tailwindcss": ">=3.4.19"
69
69
  },
70
70
  "devDependencies": {
71
- "@economic/taco-rules": "1.0.2-react18-compat.0",
71
+ "@economic/taco-rules": "^1.0.2",
72
72
  "@oxlint/plugins": "^1.75.0",
73
73
  "@svgr/cli": "^8.1.0",
74
74
  "@testing-library/jest-dom": "^6.9.1",
75
75
  "@testing-library/react": "^16.3.2",
76
- "@testing-library/user-event": "^14.6.3",
76
+ "@testing-library/user-event": "^14.6.6",
77
77
  "@types/lodash": "^4.17.25",
78
78
  "@types/node": "^22.20.1",
79
79
  "@types/react": "19.2.18",
@@ -91,10 +91,10 @@
91
91
  "vite": "^7.3.6",
92
92
  "vite-plugin-dts": "^5.0.3",
93
93
  "vite-plugin-externalize-deps": "^0.10.0",
94
- "vitest": "^4.1.10"
94
+ "vitest": "^4.1.11"
95
95
  },
96
96
  "optionalDependencies": {
97
- "@rollup/rollup-linux-x64-gnu": "^4.62.4"
97
+ "@rollup/rollup-linux-x64-gnu": "^4.62.5"
98
98
  },
99
- "gitHead": "102ac8458611e9a5c37fcfc69711d90df6ebb0b9"
99
+ "gitHead": "2c4e5842687ac7921789d706e33d570348b45c44"
100
100
  }