From e3c982227ad4b6a64052374261e04db1ad136602 Mon Sep 17 00:00:00 2001 From: adids1221 Date: Mon, 23 Dec 2024 09:47:25 +0200 Subject: [PATCH 1/8] Picker types - customTopElement, onItemSelection --- src/components/picker/types.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/components/picker/types.ts b/src/components/picker/types.ts index d768c450a5..59b4da116b 100644 --- a/src/components/picker/types.ts +++ b/src/components/picker/types.ts @@ -5,6 +5,17 @@ import {ModalTopBarProps} from '../modal/TopBar'; import {TextFieldMethods, TextFieldProps} from '../textField'; import {TouchableOpacityProps} from '../touchableOpacity'; +export interface CustomTopElementProps { + /** + * The current multi draft value + */ + value: PickerMultiValue; + /* + * Set the new multi draft value + */ + setValue: (value: PickerMultiValue) => void; +} + // Note: enum values are uppercase due to legacy export enum PickerModes { SINGLE = 'SINGLE', @@ -180,6 +191,10 @@ export type PickerBaseProps = Omit & * Callback for when picker value change */ onChange?: (value: PickerValue) => void; + /** + * Callback for when picker item is selected (only for multi mode) + */ + onItemSelection?: (value: PickerMultiValue) => void; /** * SINGLE or MULTI selection mode */ @@ -237,12 +252,20 @@ export type PickerBaseProps = Omit & }; export type PickerPropsWithSingle = PickerBaseProps & { + /** + * Custom top element + */ + customTopElement?: () => React.ReactElement; mode?: PickerModes.SINGLE; value?: PickerSingleValue; onChange?: (value: PickerSingleValue) => void; }; export type PickerPropsWithMulti = PickerBaseProps & { + /** + * Custom top element, props (vale, setValue) are for multi picker only + */ + customTopElement?: (props: CustomTopElementProps) => React.ReactElement; mode?: PickerModes.MULTI; value?: PickerMultiValue; onChange?: (value: PickerMultiValue) => void; @@ -305,6 +328,7 @@ export interface PickerContextProps isMultiMode: boolean; onSelectedLayout: (event: any) => any; selectionLimit: PickerProps['selectionLimit']; + setValue?: React.Dispatch> | undefined; } export type PickerItemsListProps = Pick< @@ -315,6 +339,7 @@ export type PickerItemsListProps = Pick< | 'useSafeArea' | 'showLoader' | 'customLoaderElement' + | 'customTopElement' | 'showSearch' | 'searchStyle' | 'searchPlaceholder' From 9a3d3ddedad32869fe3176920f4312eee7c4d820 Mon Sep 17 00:00:00 2001 From: adids1221 Date: Mon, 23 Dec 2024 09:51:16 +0200 Subject: [PATCH 2/8] add customTopElement support in Picker component --- src/components/picker/PickerItemsList.tsx | 17 ++++++++++++- .../picker/helpers/usePickerSelection.tsx | 24 +++++++++++++++++-- src/components/picker/index.tsx | 7 +++++- 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/components/picker/PickerItemsList.tsx b/src/components/picker/PickerItemsList.tsx index d798445867..4d045a7b77 100644 --- a/src/components/picker/PickerItemsList.tsx +++ b/src/components/picker/PickerItemsList.tsx @@ -34,7 +34,8 @@ const PickerItemsList = (props: PickerItemsListProps) => { mode, testID, showLoader, - customLoaderElement + customLoaderElement, + customTopElement } = props; const context = useContext(PickerContext); @@ -161,12 +162,26 @@ const PickerItemsList = (props: PickerItemsListProps) => { ); }; + const renderCustomTopElement = () => { + const {isMultiMode, value, setValue} = context; + if (customTopElement) { + if (isMultiMode) { + console.log(`renderCustomTopElement, isMultiMode!, value:`, value); + //@ts-expect-error - PickerWithMultiValue props need to pass the correct props + return customTopElement({value, setValue}); + } + //@ts-expect-error - PickerWithSingleValue props + return customTopElement(); + } + }; + const renderContent = () => { return useWheelPicker ? ( renderWheel() ) : ( <> {renderSearchInput()} + {renderCustomTopElement()} {renderList()} ); diff --git a/src/components/picker/helpers/usePickerSelection.tsx b/src/components/picker/helpers/usePickerSelection.tsx index 7ab850c086..98f79169ce 100644 --- a/src/components/picker/helpers/usePickerSelection.tsx +++ b/src/components/picker/helpers/usePickerSelection.tsx @@ -3,16 +3,35 @@ import _ from 'lodash'; import {PickerProps, PickerValue, PickerSingleValue, PickerMultiValue, PickerModes} from '../types'; interface UsePickerSelectionProps - extends Pick { + extends Pick< + PickerProps, + 'migrate' | 'value' | 'onChange' | 'onItemSelection' | 'getItemValue' | 'topBarProps' | 'mode' + > { pickerExpandableRef: RefObject; setSearchValue: (searchValue: string) => void; } const usePickerSelection = (props: UsePickerSelectionProps) => { - const {migrate, value, onChange, topBarProps, pickerExpandableRef, getItemValue, setSearchValue, mode} = props; + const { + migrate, + value, + onChange, + onItemSelection, + topBarProps, + pickerExpandableRef, + getItemValue, + setSearchValue, + mode + } = props; const [multiDraftValue, setMultiDraftValue] = useState(value as PickerMultiValue); const [multiFinalValue, setMultiFinalValue] = useState(value as PickerMultiValue); + useEffect(() => { + if (mode === PickerModes.MULTI && multiFinalValue !== multiDraftValue) { + onItemSelection?.(multiDraftValue); + } + }, [multiDraftValue]); + useEffect(() => { if (mode === PickerModes.MULTI && multiFinalValue !== value) { setMultiDraftValue(value as PickerMultiValue); @@ -50,6 +69,7 @@ const usePickerSelection = (props: UsePickerSelectionProps) => { return { multiDraftValue, + setMultiDraftValue, onDoneSelecting, toggleItemSelection, cancelSelect diff --git a/src/components/picker/index.tsx b/src/components/picker/index.tsx index 5feeb57783..51792a2db6 100644 --- a/src/components/picker/index.tsx +++ b/src/components/picker/index.tsx @@ -60,6 +60,7 @@ const Picker = React.forwardRef((props: PickerProps, ref) => { labelStyle, testID, onChange, + onItemSelection, onPress, onSearchChange, topBarProps, @@ -78,6 +79,7 @@ const Picker = React.forwardRef((props: PickerProps, ref) => { items: propItems, showLoader, customLoaderElement, + customTopElement, ...others } = themeProps; const {preset, placeholder, style, trailingAccessory, label: propsLabel} = others; @@ -101,10 +103,11 @@ const Picker = React.forwardRef((props: PickerProps, ref) => { setSearchValue, onSearchChange: _onSearchChange } = usePickerSearch({showSearch, onSearchChange, getItemLabel, children, items}); - const {multiDraftValue, onDoneSelecting, toggleItemSelection, cancelSelect} = usePickerSelection({ + const {multiDraftValue, setMultiDraftValue, onDoneSelecting, toggleItemSelection, cancelSelect} = usePickerSelection({ migrate, value, onChange, + onItemSelection, pickerExpandableRef: pickerExpandable, getItemValue, topBarProps, @@ -145,6 +148,7 @@ const Picker = React.forwardRef((props: PickerProps, ref) => { return { migrate, value: mode === PickerModes.MULTI ? multiDraftValue : pickerValue, + setValue: mode === PickerModes.MULTI ? setMultiDraftValue : undefined, onPress: mode === PickerModes.MULTI ? toggleItemSelection : onDoneSelecting, isMultiMode: mode === PickerModes.MULTI, getItemValue, @@ -245,6 +249,7 @@ const Picker = React.forwardRef((props: PickerProps, ref) => { useSafeArea={useSafeArea} showLoader={showLoader} customLoaderElement={customLoaderElement} + customTopElement={customTopElement} > {filteredItems} From e1b31371c4daf9e7861b09e8222259f7440d3ab7 Mon Sep 17 00:00:00 2001 From: adids1221 Date: Mon, 23 Dec 2024 10:32:13 +0200 Subject: [PATCH 3/8] PickerScreen example --- .../screens/componentScreens/PickerScreen.tsx | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/demo/src/screens/componentScreens/PickerScreen.tsx b/demo/src/screens/componentScreens/PickerScreen.tsx index 4a2c20fa0d..c2c828bf11 100644 --- a/demo/src/screens/componentScreens/PickerScreen.tsx +++ b/demo/src/screens/componentScreens/PickerScreen.tsx @@ -83,6 +83,15 @@ const dialogOptions = [ {label: 'Option 8', value: 6} ]; +const statusOptions = [ + {label: 'Overview', value: 'overview'}, + {label: 'In Progress', value: 'inProgress'}, + {label: 'Completed', value: 'completed'}, + {label: 'Pending Review', value: 'pendingReview'}, + {label: 'Approved', value: 'approved'}, + {label: 'Rejected', value: 'rejected'} +]; + export default class PickerScreen extends Component { picker = React.createRef(); state = { @@ -96,6 +105,7 @@ export default class PickerScreen extends Component { dialogPickerValue: 'java', customModalValues: [], filter: undefined, + statOption: [], scheme: undefined, contact: 0 }; @@ -122,6 +132,15 @@ export default class PickerScreen extends Component { ); }; + handleTopElementPress = (allOptionsSelected: boolean, setValue: any) => { + if (allOptionsSelected) { + setValue([]); + } else { + const allValues = statusOptions.map(option => option.value); + setValue(allValues); + } + }; + render() { return ( @@ -195,7 +214,32 @@ export default class PickerScreen extends Component { searchPlaceholder={'Search a language'} items={dialogOptions} /> - + + + Custom Top Element: + + this.setState({statOption: items})} + topBarProps={{title: 'Status'}} + mode={Picker.modes.MULTI} + items={statusOptions} + customTopElement={({value, setValue}) => { + const allOptionsSelected = Array.isArray(value) && value.length === statusOptions.length; + return ( + +