diff --git a/src/components/select-modal.tsx b/src/components/select-modal.tsx index 9125140..fdfa7fe 100644 --- a/src/components/select-modal.tsx +++ b/src/components/select-modal.tsx @@ -7,6 +7,8 @@ import { SearchIcon } from '@nextui-org/shared-icons'; import { AutoSizer, CellMeasurer, CellMeasurerCache, List } from 'react-virtualized'; import { useDebounceCallback } from 'usehooks-ts'; import { CellMeasurerChildProps } from 'react-virtualized/dist/es/CellMeasurer'; +import { useRouter } from 'next/navigation'; +import { useWindowListener } from '@/helpers/use-window-listener'; @@ -19,14 +21,16 @@ export type SelectModalProps (ReactNode | ((props: Pick) => ReactNode)), - gap?: number + gap?: number, + onSelectionChanged?: (item: D) => void, + footer?: ReactNode } & (T extends 'grid' ? { colSize: number } : { colSize?: never }); -const SelectModal = ({ gap, selectedItem, renderItem, displayMode, items, isOpen, onSelected, modalSize, colSize, rowSize }: SelectModalProps) => { +const SelectModal = ({ footer, onSelectionChanged, gap, selectedItem, renderItem, displayMode, items, isOpen, onSelected, modalSize, colSize, rowSize }: SelectModalProps) => { const measurementCache = useMemo(() => { return new CellMeasurerCache({ defaultHeight: rowSize, @@ -40,6 +44,7 @@ const SelectModal = (null); useEffect(() => { // reset filtered and displayed selected item on open @@ -49,6 +54,10 @@ const SelectModal = { + listRef.current?.recomputeRowHeights(); + }, [colSize, rowSize]); + const filter = useDebounceCallback((query: string) => { const lowerQuery = query.toLowerCase(); setFilteredItems(items.filter(({ name }) => name?.toLowerCase().includes(lowerQuery))); @@ -101,7 +110,7 @@ const SelectModal = setSelected(filteredItems[index])}> + variant="flat" onPress={() => { setSelected(filteredItems[index]); onSelectionChanged?.(filteredItems[index]) }}> {typeof child === 'function' ? child({ measure }) : child} ) @@ -122,7 +131,7 @@ const SelectModal = { const itemsPerRow = Math.max(1, Math.floor(width / colSize!)); const rowCount = Math.ceil(filteredItems.length / itemsPerRow); - setGridRowCount(rowCount); + setTimeout(() => setGridRowCount(rowCount)); return (
@@ -140,13 +149,15 @@ const SelectModal = setSelected(item)}> + onPress={() => { setSelected(item); onSelectionChanged?.(item) }}> { typeof res === 'function' ? res({ measure }) : res } ) }); return
{children} + { index === rowCount - 1 ? [...new Array(itemsPerRow - children.length)].map((_, i) => +
) : null }
; }} )} />)} @@ -154,10 +165,10 @@ const SelectModal = ); })}
) - }, [displayMode, filteredItems, colSize, rowSize, selected, isOpen, gridRowCount, gap]); + }, [displayMode, filteredItems, colSize, rowSize, selected, isOpen, gridRowCount, gap, onSelectionChanged]); return ( { - onSelected(selected); + onSelected(outputSelected.current); }} isOpen={isOpen} className={`!rounded-2xl !max-h-[90dvh] sm:!max-h-[85dvh] ${modalSize === 'full' ? 'md:max-w-[90dvw]' : ''}`}> @@ -168,12 +179,19 @@ const SelectModal = setFilteredItems(items)} /> {renderedContent} - - - + + { footer } +
+ + +
}
@@ -181,18 +199,36 @@ const SelectModal = = Omit & - Pick, 'modalSize' | 'displayMode' | 'colSize' | 'rowSize' | 'items' | 'renderItem' | 'selectedItem' | 'onSelected' | 'gap'> + Pick, 'modalSize' | 'displayMode' | 'colSize' | 'rowSize' | 'items' | 'renderItem' | 'selectedItem' | 'onSelected' | 'gap' | 'onSelectionChanged' | 'footer'> & + { modalId: string }; export const SelectModalButton = (props: SelectModalButtonProps) => { + const router = useRouter(); const [isOpen, setOpen] = useState(false); - const { gap, onSelected, selectedItem, renderItem, items, colSize, rowSize, displayMode, modalSize } = props; + const { modalId, footer, onSelectionChanged, gap, onSelected, selectedItem, renderItem, items, colSize, rowSize, displayMode, modalSize } = props; + + useWindowListener('hashchange', () => { + if (window.location.hash !== `#modal-${modalId}` && isOpen) { + setOpen(false); + onSelected(null); + } + }, [isOpen, modalId]); + + useEffect(() => { + if (window.location.hash === `#modal-${modalId}`) + setOpen(true); + }, []); return (<> - { setOpen(false); onSelected(item); + router.back(); }} /> -