From 8e763ee6a7548037dec6cc9ea1b043d44a01633d Mon Sep 17 00:00:00 2001 From: Sujan Sundareswaran Date: Mon, 30 Dec 2024 17:21:19 +0530 Subject: [PATCH] v1.11.9-alpha-0 --- package.json | 2 +- src/components/OptionCard/OptionCard.tsx | 205 ++++++++++++----------- 2 files changed, 110 insertions(+), 97 deletions(-) diff --git a/package.json b/package.json index b3463715..1c025a90 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fictoan-react", - "version": "1.11.8", + "version": "1.11.9-alpha.0", "private": false, "description": "A full-featured, designer-friendly, yet performant framework with plain-English props and focus on rapid iteration.", "repository": { diff --git a/src/components/OptionCard/OptionCard.tsx b/src/components/OptionCard/OptionCard.tsx index ee8d4242..547f589d 100644 --- a/src/components/OptionCard/OptionCard.tsx +++ b/src/components/OptionCard/OptionCard.tsx @@ -32,7 +32,7 @@ export interface OptionCardsProviderProps { showTickIcon ? : boolean; tickPosition ? : TickPosition; onSelectionChange ? : (selectedIds: Set) => void; - selectionLimit ? : number; + selectionLimit ? : number; } export interface OptionCardProps extends CardProps { @@ -41,6 +41,11 @@ export interface OptionCardProps extends CardProps { disabled ? : boolean; } +export interface OptionCardsGroupRef { + selectAllOptions: () => void; + clearAllOptions: () => void; +} + interface OptionCardsContextType { isSelected : (id: string) => boolean; toggleSelection : (id: string) => void; @@ -64,103 +69,111 @@ const OptionCardsContext = createContext({ }); // COMPONENT /////////////////////////////////////////////////////////////////////////////////////////////////////////// -export const OptionCardsGroup: React.FC = ( - { - children, - allowMultipleSelections = false, - showTickIcon, - onSelectionChange, - tickPosition = "top-right", - selectionLimit, - ...props - }, -) => { - const [selectedIds, setSelectedIds] = useState>(new Set()); - const availableOptionsRef = useRef>(new Map()); // id -> disabled - - const registerOption = useCallback((id: string, disabled: boolean) => { - availableOptionsRef.current.set(id, disabled); - }, []); - - const unregisterOption = useCallback((id: string) => { - availableOptionsRef.current.delete(id); - }, []); - - const toggleSelection = useCallback((id: string) => { - setSelectedIds(prevSelectedIds => { - const newSelectedIds = new Set(prevSelectedIds); - if (allowMultipleSelections) { - if (newSelectedIds.has(id)) { - newSelectedIds.delete(id); - } else { - if (selectionLimit && newSelectedIds.size >= selectionLimit) { - return prevSelectedIds; +export const OptionCardsGroup = React.forwardRef( + ( + { + children, + allowMultipleSelections = false, + showTickIcon, + onSelectionChange, + tickPosition = "top-right", + selectionLimit, + ...props + }, + ref + ) => { + const [selectedIds, setSelectedIds] = useState>(new Set()); + const availableOptionsRef = useRef>(new Map()); // id -> disabled + + const registerOption = useCallback((id: string, disabled: boolean) => { + availableOptionsRef.current.set(id, disabled); + }, []); + + const unregisterOption = useCallback((id: string) => { + availableOptionsRef.current.delete(id); + }, []); + + const toggleSelection = useCallback((id: string) => { + setSelectedIds(prevSelectedIds => { + const newSelectedIds = new Set(prevSelectedIds); + if (allowMultipleSelections) { + if (newSelectedIds.has(id)) { + newSelectedIds.delete(id); + } else { + if (selectionLimit && newSelectedIds.size >= selectionLimit) { + return prevSelectedIds; + } + newSelectedIds.add(id); } - newSelectedIds.add(id); - } - } else { - if (newSelectedIds.has(id) && prevSelectedIds.size === 1) { - newSelectedIds.clear(); } else { - newSelectedIds.clear(); - newSelectedIds.add(id); + if (newSelectedIds.has(id) && prevSelectedIds.size === 1) { + newSelectedIds.clear(); + } else { + newSelectedIds.clear(); + newSelectedIds.add(id); + } } - } - onSelectionChange?.(newSelectedIds); - return newSelectedIds; - }); - }, [allowMultipleSelections, onSelectionChange, selectionLimit]); - - const selectAllOptions = useCallback(() => { - if (!allowMultipleSelections) return; - - setSelectedIds(prevSelectedIds => { - const newSelectedIds = new Set(prevSelectedIds); - - // Get all enabled options - const enabledOptions = Array.from(availableOptionsRef.current.entries()) - .filter(([_, disabled]) => !disabled) - .map(([id]) => id); - - // Respect selection limit if set - const optionsToAdd = selectionLimit - ? enabledOptions.slice(0, selectionLimit) - : enabledOptions; - - optionsToAdd.forEach(id => newSelectedIds.add(id)); - onSelectionChange?.(newSelectedIds); - return newSelectedIds; - }); - }, [allowMultipleSelections, selectionLimit, onSelectionChange]); - - const clearAllOptions = useCallback(() => { - setSelectedIds(new Set()); - onSelectionChange?.(new Set()); - }, [onSelectionChange]); - - const isSelected = useCallback((id: string) => { - return selectedIds.has(id); - }, [selectedIds]); - - const contextValue = { - isSelected, - toggleSelection, - showTickIcon, - tickPosition, - selectAllOptions, - clearAllOptions, - registerOption, - unregisterOption, - }; - - return ( - -
- {children} -
-
- ); -}; + onSelectionChange?.(newSelectedIds); + return newSelectedIds; + }); + }, [allowMultipleSelections, onSelectionChange, selectionLimit]); + + const selectAllOptions = useCallback(() => { + if (!allowMultipleSelections) return; + + setSelectedIds(prevSelectedIds => { + const newSelectedIds = new Set(prevSelectedIds); + + // Get all enabled options + const enabledOptions = Array.from(availableOptionsRef.current.entries()) + .filter(([_, disabled]) => !disabled) + .map(([id]) => id); + + // Respect selection limit if set + const optionsToAdd = selectionLimit + ? enabledOptions.slice(0, selectionLimit) + : enabledOptions; + + optionsToAdd.forEach(id => newSelectedIds.add(id)); + onSelectionChange?.(newSelectedIds); + return newSelectedIds; + }); + }, [allowMultipleSelections, selectionLimit, onSelectionChange]); + + const clearAllOptions = useCallback(() => { + setSelectedIds(new Set()); + onSelectionChange?.(new Set()); + }, [onSelectionChange]); + + const isSelected = useCallback((id: string) => { + return selectedIds.has(id); + }, [selectedIds]); + + React.useImperativeHandle(ref, () => ({ + selectAllOptions, + clearAllOptions + })); + + const contextValue = { + isSelected, + toggleSelection, + showTickIcon, + tickPosition, + selectAllOptions, + clearAllOptions, + registerOption, + unregisterOption, + }; + + return ( + +
+ {children} +
+
+ ); + } +); export const useOptionCard = (id: string) => { const context = useContext(OptionCardsContext); @@ -173,7 +186,7 @@ export const useOptionCard = (id: string) => { export const useOptionCards = () => { const { selectAllOptions, clearAllOptions } = useContext(OptionCardsContext); - return { selectAllOptions, clearAllOptions }; // Shorter version, same functionality + return { selectAllOptions, clearAllOptions }; }; export const OptionCard: React.FC = ({ id, children, disabled = false, ...props }) => {