import * as React from 'react' import type { CellEditorParams, EditorConfirmPayload, GridRowData, } from '../../types' import { Portal } from '@planview/pv-uikit' import type { ComboboxCloseReason, ComboboxOption, ComboboxProps, } from '@planview/pv-uikit' import { useFloating, autoUpdate, hide, offset, shift, } from '@floating-ui/react-dom' import { useGridColumn, useGridSelector } from '../../context/grid-context' import { useGrid } from '../../hooks' import { useMergeRefs, isTestEnvironment } from '@planview/pv-utilities' import { cleanPropForSpread } from './utils' import { AnchoredContainer, AnchoredContainerPortal, StyledCombobox, } from './styles' export type GridEditorComboboxMultiProps = Omit< CellEditorParams, 'value' | 'onConfirm' > & Pick< ComboboxProps, | 'className' | 'clearable' | 'creatable' | 'disabled' | 'error' | 'icon' | 'inputMode' | 'inputProps' | 'label' | 'loading' | 'onRemove' | 'onCreate' | 'onInputChange' | 'onSelect' | 'options' | 'placeholder' | 'virtualized' > & { /** value */ value?: ComboboxOption[] /** * confirm callback */ onConfirm: ( value?: ComboboxOption[], payload?: EditorConfirmPayload ) => void } const AnchoredPortal = React.forwardRef< HTMLDivElement, { children: React.ReactNode; minWidth: number; onHidden: () => void } >(function AnchoredPortal({ children, minWidth, onHidden }, containerRef) { const { refs: { setFloating, setReference }, x, y, strategy, middlewareData, } = useFloating({ placement: 'left-start', middleware: [ offset(({ rects }) => ({ mainAxis: -rects.floating.width, })), shift({ crossAxis: true, padding: 8 }), isTestEnvironment ? null : hide(), ], whileElementsMounted: autoUpdate, }) const floatingRef = useMergeRefs([containerRef, setFloating]) const hidden = !!middlewareData.hide?.referenceHidden React.useEffect(() => { if (hidden) { onHidden() } }, [hidden, onHidden]) return ( ) }) export const GridEditorComboboxMulti = ({ value, onCancel, onConfirm, options, label = '', ...rest }: GridEditorComboboxMultiProps) => { const wrapperRef = React.useRef(null) const valueRef = React.useRef(value) const column = useGridColumn(rest.columnId) React.useEffect(() => { valueRef.current = value }, [value]) const handleChange = React.useCallback( (opt: ComboboxOption[]) => { valueRef.current = opt onConfirm(opt, { continueEditing: 'current' }) }, [onConfirm] ) const handleHidden = React.useCallback(() => { onCancel() }, [onCancel]) const handleInputKeyDown = React.useCallback( (ev: KeyboardEvent) => { if (ev.key === 'Tab') { ev.stopPropagation() ev.preventDefault() onConfirm(valueRef.current, { continueEditing: ev.shiftKey ? 'previous' : 'next', }) } }, [onConfirm] ) const handleClose = React.useCallback( (reason: ComboboxCloseReason) => { if (reason === 'confirm_click') { onConfirm(valueRef.current) } else if (!reason.includes('tab')) { onCancel() } }, [onCancel, onConfirm] ) React.useEffect(() => { const el = wrapperRef.current if (el) { el?.addEventListener('keydown', handleInputKeyDown) return () => { el?.removeEventListener('keydown', handleInputKeyDown) } } }, [handleInputKeyDown]) React.useEffect(() => { setTimeout(() => { const el = wrapperRef.current if (el) { el.querySelector('input')?.focus() } }, 100) }, []) const { selectors } = useGrid() const height = useGridSelector(selectors.selectRowHeight) return ( ) }