react-native-molecules 0.5.0-beta.3 → 0.5.0-beta.31
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.
- package/components/Accordion/Accordion.tsx +2 -6
- package/components/Accordion/AccordionItem.tsx +16 -12
- package/components/Accordion/AccordionItemContent.tsx +6 -1
- package/components/Accordion/AccordionItemHeader.tsx +1 -1
- package/components/Accordion/utils.ts +6 -0
- package/components/ActivityIndicator/ActivityIndicator.tsx +6 -15
- package/components/Appbar/AppbarBase.tsx +18 -13
- package/components/Button/Button.tsx +211 -264
- package/components/Button/index.tsx +9 -3
- package/components/Button/types.ts +16 -2
- package/components/Button/utils.ts +230 -208
- package/components/Card/Card.tsx +1 -1
- package/components/Checkbox/Checkbox.tsx +125 -88
- package/components/Checkbox/CheckboxBase.ios.tsx +14 -23
- package/components/Checkbox/CheckboxBase.tsx +21 -137
- package/components/Checkbox/context.tsx +14 -0
- package/components/Checkbox/index.tsx +11 -4
- package/components/Checkbox/types.ts +63 -29
- package/components/Checkbox/utils.ts +25 -108
- package/components/Chip/Chip.tsx +41 -52
- package/components/Chip/utils.ts +3 -7
- package/components/DateField/DateField.tsx +111 -0
- package/components/DateField/index.tsx +6 -0
- package/components/{DatePickerInput/inputUtils.ts → DateField/useDateFieldState.ts} +19 -51
- package/components/DatePicker/DateCalendar.tsx +83 -0
- package/components/DatePicker/DatePickerActions.tsx +73 -0
- package/components/DatePicker/DatePickerModal.tsx +246 -0
- package/components/DatePicker/DatePickerPopover.tsx +79 -0
- package/components/DatePicker/DatePickerProvider.tsx +158 -0
- package/components/DatePicker/DatePickerTrigger.tsx +23 -0
- package/components/DatePicker/context.tsx +83 -0
- package/components/DatePicker/index.tsx +45 -0
- package/components/DatePicker/utils.ts +295 -0
- package/components/DatePickerInline/DatePickerDockedHeader.tsx +117 -0
- package/components/DatePickerInline/DatePickerInline.tsx +17 -16
- package/components/DatePickerInline/DatePickerInlineBase.tsx +11 -5
- package/components/DatePickerInline/DatePickerInlineHeader.tsx +50 -20
- package/components/DatePickerInline/Day.tsx +25 -1
- package/components/DatePickerInline/DayNames.tsx +13 -10
- package/components/DatePickerInline/DayRange.tsx +2 -4
- package/components/DatePickerInline/HeaderItem.tsx +44 -29
- package/components/DatePickerInline/Month.tsx +48 -67
- package/components/DatePickerInline/MonthPicker.tsx +80 -92
- package/components/DatePickerInline/Swiper.native.tsx +21 -4
- package/components/DatePickerInline/Swiper.tsx +169 -14
- package/components/DatePickerInline/SwiperUtils.ts +1 -1
- package/components/DatePickerInline/Week.tsx +6 -1
- package/components/DatePickerInline/YearPicker.tsx +220 -78
- package/components/DatePickerInline/dateUtils.tsx +18 -13
- package/components/DatePickerInline/store.tsx +27 -0
- package/components/DatePickerInline/types.ts +6 -2
- package/components/DatePickerInline/utils.ts +66 -29
- package/components/Divider/Divider.tsx +192 -0
- package/components/Divider/index.tsx +10 -0
- package/components/Drawer/Drawer.tsx +17 -6
- package/components/Drawer/DrawerItemGroup.tsx +3 -7
- package/components/ElementGroup/ElementGroup.tsx +1 -1
- package/components/FilePicker/FilePicker.tsx +48 -78
- package/components/FilePicker/index.tsx +2 -1
- package/components/FilePicker/utils.ts +9 -0
- package/components/HelperText/HelperText.tsx +0 -35
- package/components/Icon/iconFactory.tsx +5 -4
- package/components/Icon/index.tsx +1 -1
- package/components/Icon/types.ts +17 -6
- package/components/IconButton/IconButton.tsx +84 -84
- package/components/IconButton/index.tsx +1 -0
- package/components/IconButton/types.ts +10 -0
- package/components/IconButton/utils.ts +167 -33
- package/components/List/List.tsx +276 -0
- package/components/List/context.tsx +27 -0
- package/components/List/index.ts +8 -0
- package/components/List/types.ts +117 -0
- package/components/List/utils.ts +79 -0
- package/components/LoadingIndicator/LoadingIndicator.tsx +253 -0
- package/components/LoadingIndicator/LoadingIndicator.web.tsx +136 -0
- package/components/LoadingIndicator/index.tsx +13 -0
- package/components/LoadingIndicator/utils.ts +117 -0
- package/components/Menu/Menu.tsx +162 -39
- package/components/Menu/index.tsx +10 -7
- package/components/Menu/utils.ts +21 -70
- package/components/NavigationRail/NavigationRail.tsx +15 -9
- package/components/Popover/Popover.tsx +119 -145
- package/components/Popover/PopoverRoot.tsx +60 -0
- package/components/Popover/common.ts +54 -34
- package/components/Popover/index.ts +12 -1
- package/components/Popover/usePlatformMeasure.native.ts +90 -0
- package/components/Popover/usePlatformMeasure.ts +120 -0
- package/components/Popover/utils.ts +34 -0
- package/components/Portal/Portal.tsx +1 -2
- package/components/Radio/Radio.tsx +188 -0
- package/components/Radio/RadioBase.ios.tsx +69 -0
- package/components/Radio/RadioBase.tsx +136 -0
- package/components/Radio/context.tsx +23 -0
- package/components/Radio/index.tsx +20 -0
- package/components/Radio/types.ts +101 -0
- package/components/Radio/utils.ts +115 -0
- package/components/Rating/Rating.tsx +1 -1
- package/components/Select/Select.tsx +521 -785
- package/components/Select/context.tsx +81 -0
- package/components/Select/index.ts +26 -14
- package/components/Select/types.ts +65 -58
- package/components/Select/utils.ts +126 -0
- package/components/Slot/Slot.tsx +224 -0
- package/components/Slot/compose-refs.tsx +62 -0
- package/components/Slot/index.tsx +8 -0
- package/components/Surface/Surface.android.tsx +32 -7
- package/components/Surface/Surface.ios.tsx +34 -29
- package/components/Surface/Surface.tsx +31 -4
- package/components/Surface/utils.ts +44 -6
- package/components/Switch/Switch.ios.tsx +1 -1
- package/components/Switch/Switch.tsx +10 -3
- package/components/Tabs/TabItem.tsx +35 -58
- package/components/Tabs/TabLabel.tsx +5 -9
- package/components/Tabs/Tabs.tsx +156 -150
- package/components/Tabs/utils.ts +15 -2
- package/components/Text/textFactory.tsx +17 -5
- package/components/TextInput/TextInput.tsx +663 -579
- package/components/TextInput/index.tsx +19 -3
- package/components/TextInput/types.ts +77 -28
- package/components/TextInput/utils.ts +235 -145
- package/components/TimeField/TimeField.tsx +75 -0
- package/components/TimeField/index.tsx +6 -0
- package/components/TimeField/useTimeFieldState.ts +70 -0
- package/components/{TimePickerField/sanitizeTime.ts → TimeField/utils.ts} +77 -10
- package/components/TimePicker/AnalogClock.tsx +1 -1
- package/components/TimePicker/TimeInput.tsx +87 -42
- package/components/TimePicker/TimeInputs.tsx +138 -50
- package/components/TimePicker/TimePicker.tsx +74 -11
- package/components/TimePicker/TimePickerModal.tsx +186 -0
- package/components/TimePicker/context.tsx +17 -0
- package/components/TimePicker/index.tsx +15 -3
- package/components/TimePicker/utils.ts +93 -4
- package/components/Tooltip/Tooltip.tsx +42 -67
- package/components/Tooltip/TooltipContent.tsx +32 -5
- package/components/Tooltip/TooltipTrigger.tsx +21 -24
- package/components/Tooltip/index.tsx +1 -1
- package/components/TouchableRipple/TouchableRipple.native.tsx +83 -16
- package/components/TouchableRipple/TouchableRipple.tsx +150 -102
- package/components/TouchableRipple/rippleFromForegroundColor.ts +21 -0
- package/hocs/index.tsx +1 -1
- package/hocs/withKeyboardAccessibility.tsx +2 -3
- package/hocs/withPortal.tsx +1 -1
- package/hooks/index.tsx +2 -12
- package/hooks/useActionState.tsx +19 -8
- package/hooks/useContrastColor.ts +1 -2
- package/hooks/useFilePicker.tsx +7 -17
- package/hooks/useHandleNumberFormat.tsx +2 -2
- package/hooks/useMediaQuery.tsx +1 -2
- package/package.json +95 -111
- package/shortcuts-manager/ShortcutsManager/ShortcutsManager.tsx +6 -3
- package/shortcuts-manager/ShortcutsManager/utils.tsx +1 -1
- package/shortcuts-manager/useSetScopes/useSetScopes.tsx +1 -1
- package/shortcuts-manager/useShortcut/useShortcut.tsx +1 -1
- package/styles/shadow.ts +2 -1
- package/styles/themes/LightTheme.tsx +1 -1
- package/utils/DocumentPicker/documentPicker.ts +78 -27
- package/utils/DocumentPicker/types.ts +0 -1
- package/utils/extractSubcomponents.ts +89 -0
- package/utils/extractTextStyles.ts +1 -2
- package/utils/formatNumberWithMask/formatNumberWithMask.ts +2 -1
- package/utils/index.ts +0 -3
- package/utils/normalizeToNumberString/normalizeToNumberString.ts +1 -1
- package/components/DatePickerDocked/DatePickerDocked.tsx +0 -30
- package/components/DatePickerDocked/DatePickerDockedHeader.tsx +0 -129
- package/components/DatePickerDocked/index.tsx +0 -17
- package/components/DatePickerDocked/types.ts +0 -11
- package/components/DatePickerDocked/utils.ts +0 -157
- package/components/DatePickerInline/DatePickerContext.tsx +0 -21
- package/components/DatePickerInput/DatePickerInput.tsx +0 -139
- package/components/DatePickerInput/DatePickerInputModal.tsx +0 -48
- package/components/DatePickerInput/DatePickerInputWithoutModal.tsx +0 -77
- package/components/DatePickerInput/DateRangeInput.tsx +0 -88
- package/components/DatePickerInput/index.tsx +0 -10
- package/components/DatePickerInput/types.ts +0 -28
- package/components/DatePickerInput/utils.ts +0 -15
- package/components/DatePickerModal/AnimatedCrossView.tsx +0 -94
- package/components/DatePickerModal/CalendarEdit.tsx +0 -139
- package/components/DatePickerModal/DatePickerModal.tsx +0 -85
- package/components/DatePickerModal/DatePickerModalContent.tsx +0 -155
- package/components/DatePickerModal/DatePickerModalContentHeader.tsx +0 -213
- package/components/DatePickerModal/DatePickerModalHeader.tsx +0 -74
- package/components/DatePickerModal/DatePickerModalHeaderBackground.tsx +0 -13
- package/components/DatePickerModal/index.tsx +0 -16
- package/components/DatePickerModal/types.ts +0 -92
- package/components/DatePickerModal/utils.ts +0 -122
- package/components/DateTimePicker/DateTimePicker.tsx +0 -172
- package/components/DateTimePicker/index.tsx +0 -10
- package/components/DateTimePicker/utils.ts +0 -12
- package/components/HorizontalDivider/HorizontalDivider.tsx +0 -103
- package/components/HorizontalDivider/index.tsx +0 -9
- package/components/ListItem/ListItem.tsx +0 -136
- package/components/ListItem/ListItemDescription.tsx +0 -25
- package/components/ListItem/ListItemTitle.tsx +0 -25
- package/components/ListItem/index.tsx +0 -14
- package/components/ListItem/utils.ts +0 -115
- package/components/Menu/MenuDivider.tsx +0 -13
- package/components/Menu/MenuItem.tsx +0 -128
- package/components/Popover/Popover.native.tsx +0 -185
- package/components/RadioButton/RadioButton.tsx +0 -138
- package/components/RadioButton/RadioButtonAndroid.tsx +0 -188
- package/components/RadioButton/RadioButtonGroup.tsx +0 -98
- package/components/RadioButton/RadioButtonIOS.tsx +0 -106
- package/components/RadioButton/RadioButtonItem.tsx +0 -232
- package/components/RadioButton/index.ts +0 -22
- package/components/RadioButton/utils.ts +0 -165
- package/components/TimePickerField/TimePickerField.tsx +0 -152
- package/components/TimePickerField/index.tsx +0 -10
- package/components/TimePickerField/utils.ts +0 -94
- package/components/TimePickerModal/TimePickerModal.tsx +0 -115
- package/components/TimePickerModal/index.tsx +0 -10
- package/components/TimePickerModal/utils.ts +0 -47
- package/components/VerticalDivider/VerticalDivider.tsx +0 -100
- package/components/VerticalDivider/index.tsx +0 -9
- package/context-bridge/index.tsx +0 -87
- package/fast-context/index.tsx +0 -190
- package/hocs/typedMemo.tsx +0 -5
- package/hooks/useControlledValue.tsx +0 -68
- package/hooks/useLatest.tsx +0 -9
- package/hooks/useMergedRefs.ts +0 -14
- package/hooks/usePrevious.ts +0 -13
- package/hooks/useSearchable.tsx +0 -74
- package/hooks/useSubcomponents.tsx +0 -59
- package/hooks/useToggle.tsx +0 -24
- package/utils/color.ts +0 -22
- package/utils/compare/index.ts +0 -54
- package/utils/lodash.ts +0 -49
- package/utils/repository.ts +0 -53
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { createFastContext } from '@react-native-molecules/utils/fast-context';
|
|
2
|
+
import type { View } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
ListContext,
|
|
6
|
+
ListContextProvider,
|
|
7
|
+
useListContext,
|
|
8
|
+
useListContextValue,
|
|
9
|
+
useListStoreRef,
|
|
10
|
+
} from '../List';
|
|
11
|
+
import { registerPortalContext } from '../Portal';
|
|
12
|
+
import type { DefaultItemT, SelectDropdownContextValue, SelectSearchContextValue } from './types';
|
|
13
|
+
|
|
14
|
+
export {
|
|
15
|
+
ListContext as SelectContext,
|
|
16
|
+
ListContextProvider as SelectContextProvider,
|
|
17
|
+
useListContext as useSelectContext,
|
|
18
|
+
useListContextValue as useSelectContextValue,
|
|
19
|
+
useListStoreRef as useSelectStoreRef,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// SelectDropdownContext - holds isOpen, onClose, triggerRef with fast-context
|
|
23
|
+
export type SelectDropdownContextType = SelectDropdownContextValue & {
|
|
24
|
+
triggerRef: React.RefObject<View> | null;
|
|
25
|
+
triggerLayout: { width: number; height: number } | null;
|
|
26
|
+
setTriggerLayout: (layout: { width: number; height: number }) => void;
|
|
27
|
+
triggerHovered?: boolean;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const selectDropdownContextDefaultValue: SelectDropdownContextType = {
|
|
31
|
+
isOpen: false,
|
|
32
|
+
onClose: () => {},
|
|
33
|
+
onOpen: () => {},
|
|
34
|
+
triggerRef: null,
|
|
35
|
+
triggerLayout: null,
|
|
36
|
+
setTriggerLayout: () => {},
|
|
37
|
+
triggerHovered: false,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const {
|
|
41
|
+
useStoreRef: useSelectDropdownStoreRef,
|
|
42
|
+
Provider: SelectDropdownContextProvider,
|
|
43
|
+
useContext: useSelectDropdownContext,
|
|
44
|
+
useContextValue: useSelectDropdownContextValue,
|
|
45
|
+
Context: SelectDropdownContext,
|
|
46
|
+
} = createFastContext<SelectDropdownContextType>(selectDropdownContextDefaultValue, true);
|
|
47
|
+
|
|
48
|
+
const selectSearchContextDefaultValue: SelectSearchContextValue<DefaultItemT> = {
|
|
49
|
+
searchQuery: '',
|
|
50
|
+
setSearchQuery: () => {},
|
|
51
|
+
allOptions: [],
|
|
52
|
+
options: [],
|
|
53
|
+
optionById: new Map(),
|
|
54
|
+
getOptionId: item => item.id,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const {
|
|
58
|
+
useStoreRef: useSelectSearchStoreRef,
|
|
59
|
+
Provider: SelectSearchContextProvider,
|
|
60
|
+
useContext: useSelectSearchContext,
|
|
61
|
+
useContextValue: useSelectSearchContextValue,
|
|
62
|
+
Context: SelectSearchContext,
|
|
63
|
+
} = createFastContext<SelectSearchContextValue<DefaultItemT>>(
|
|
64
|
+
selectSearchContextDefaultValue,
|
|
65
|
+
true,
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
export {
|
|
69
|
+
SelectDropdownContext,
|
|
70
|
+
SelectDropdownContextProvider,
|
|
71
|
+
SelectSearchContext,
|
|
72
|
+
SelectSearchContextProvider,
|
|
73
|
+
useSelectDropdownContext,
|
|
74
|
+
useSelectDropdownContextValue,
|
|
75
|
+
useSelectDropdownStoreRef,
|
|
76
|
+
useSelectSearchContext,
|
|
77
|
+
useSelectSearchContextValue,
|
|
78
|
+
useSelectSearchStoreRef,
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
registerPortalContext([SelectDropdownContext, SelectSearchContext]);
|
|
@@ -1,14 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
import { getRegisteredComponentWithFallback } from '../../core';
|
|
2
|
+
import SelectRoot, {
|
|
3
|
+
SelectContent,
|
|
4
|
+
SelectDropdown,
|
|
5
|
+
SelectOption,
|
|
6
|
+
SelectSearchInput,
|
|
7
|
+
SelectTrigger,
|
|
8
|
+
SelectTriggerOutline,
|
|
9
|
+
SelectValue,
|
|
10
|
+
} from './Select';
|
|
11
|
+
|
|
12
|
+
const SelectWithSubcomponents = Object.assign(SelectRoot, {
|
|
13
|
+
Trigger: SelectTrigger,
|
|
14
|
+
TriggerOutline: SelectTriggerOutline,
|
|
15
|
+
Value: SelectValue,
|
|
16
|
+
Dropdown: SelectDropdown,
|
|
17
|
+
Content: SelectContent,
|
|
18
|
+
Option: SelectOption,
|
|
19
|
+
SearchInput: SelectSearchInput,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export const Select = getRegisteredComponentWithFallback('Select', SelectWithSubcomponents);
|
|
23
|
+
|
|
24
|
+
export * from './context';
|
|
25
|
+
export type * from './types';
|
|
26
|
+
export * from './utils';
|
|
@@ -1,33 +1,16 @@
|
|
|
1
1
|
import type { ComponentType, ReactNode } from 'react';
|
|
2
|
-
import type {
|
|
3
|
-
GestureResponderEvent,
|
|
4
|
-
ScrollViewProps,
|
|
5
|
-
TextInputProps,
|
|
6
|
-
ViewProps,
|
|
7
|
-
} from 'react-native';
|
|
2
|
+
import type { GestureResponderEvent, TextInputProps, ViewProps } from 'react-native';
|
|
8
3
|
|
|
4
|
+
import type { ListContentProps, ListItemId, ListValue } from '../List';
|
|
9
5
|
import type { PopoverProps } from '../Popover';
|
|
10
6
|
|
|
7
|
+
export type { ListContextValue as SelectContextValue } from '../List';
|
|
8
|
+
|
|
11
9
|
export type DefaultItemT = {
|
|
12
10
|
id: string | number;
|
|
13
11
|
label?: string;
|
|
14
12
|
selectable?: boolean;
|
|
15
|
-
[key: string]:
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
// SelectContext types
|
|
19
|
-
export type SelectContextValue<Option extends DefaultItemT = DefaultItemT> = {
|
|
20
|
-
value: Option | Option[] | null;
|
|
21
|
-
multiple: boolean;
|
|
22
|
-
onAdd: (item: Option) => void;
|
|
23
|
-
onRemove: (item: Option) => void;
|
|
24
|
-
disabled?: boolean;
|
|
25
|
-
error?: boolean;
|
|
26
|
-
labelKey?: string;
|
|
27
|
-
options: Option[];
|
|
28
|
-
searchQuery: string;
|
|
29
|
-
setSearchQuery: (query: string) => void;
|
|
30
|
-
filteredOptions: Option[];
|
|
13
|
+
[key: string]: unknown;
|
|
31
14
|
};
|
|
32
15
|
|
|
33
16
|
// SelectDropdownContext types
|
|
@@ -37,24 +20,61 @@ export type SelectDropdownContextValue = {
|
|
|
37
20
|
onOpen: () => void;
|
|
38
21
|
};
|
|
39
22
|
|
|
23
|
+
export type SelectSearchMode = 'client' | 'external';
|
|
24
|
+
|
|
25
|
+
export type SelectSearchKey<Option extends object = DefaultItemT> =
|
|
26
|
+
| string
|
|
27
|
+
| string[]
|
|
28
|
+
| ((item: Option, query: string) => boolean);
|
|
29
|
+
|
|
30
|
+
export type SelectSearchContextValue<Option extends DefaultItemT = DefaultItemT> = {
|
|
31
|
+
searchQuery: string;
|
|
32
|
+
setSearchQuery: (query: string) => void;
|
|
33
|
+
allOptions: Option[];
|
|
34
|
+
options: Option[];
|
|
35
|
+
optionById: Map<ListItemId, Option>;
|
|
36
|
+
getOptionId: (item: Option) => ListItemId;
|
|
37
|
+
};
|
|
38
|
+
|
|
40
39
|
// SelectProvider props
|
|
41
|
-
|
|
40
|
+
type SelectPropsBase<Option extends DefaultItemT = DefaultItemT> = {
|
|
42
41
|
children: ReactNode;
|
|
43
|
-
value?: Option['id'] | Option['id'][] | null;
|
|
44
|
-
defaultValue?: Option['id'] | Option['id'][] | null;
|
|
45
|
-
onChange?: (
|
|
46
|
-
value: Option['id'] | Option['id'][] | null,
|
|
47
|
-
item: Option,
|
|
48
|
-
event?: GestureResponderEvent,
|
|
49
|
-
) => void;
|
|
50
|
-
multiple?: boolean;
|
|
51
42
|
disabled?: boolean;
|
|
52
43
|
error?: boolean;
|
|
53
|
-
labelKey?: string;
|
|
54
44
|
options: Option[];
|
|
55
|
-
searchKey?:
|
|
45
|
+
searchKey?: SelectSearchKey<Option>;
|
|
46
|
+
searchQuery?: string;
|
|
47
|
+
defaultSearchQuery?: string;
|
|
56
48
|
onSearchChange?: (query: string) => void;
|
|
57
|
-
|
|
49
|
+
searchMode?: SelectSearchMode;
|
|
50
|
+
allowDeselect?: boolean;
|
|
51
|
+
getItemId?: (item: Option) => ListItemId;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export type SelectSearchInputProps = Omit<TextInputProps, 'value' | 'onChangeText'>;
|
|
55
|
+
|
|
56
|
+
type SingleSelectProps<Option extends DefaultItemT = DefaultItemT> = {
|
|
57
|
+
multiple?: false | undefined;
|
|
58
|
+
value?: ListValue<false>;
|
|
59
|
+
defaultValue?: ListValue<false>;
|
|
60
|
+
onChange?: (value: ListValue<false>, item: Option, event?: GestureResponderEvent) => void;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
type MultipleSelectProps<Option extends DefaultItemT = DefaultItemT> = {
|
|
64
|
+
multiple: true;
|
|
65
|
+
value?: ListValue<true>;
|
|
66
|
+
defaultValue?: ListValue<true>;
|
|
67
|
+
onChange?: (value: ListValue<true>, item: Option, event?: GestureResponderEvent) => void;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export type SelectProps<Option extends DefaultItemT = DefaultItemT> = SelectPropsBase<Option> &
|
|
71
|
+
(SingleSelectProps<Option> | MultipleSelectProps<Option>);
|
|
72
|
+
|
|
73
|
+
export type SelectContentProps<Option extends DefaultItemT = DefaultItemT> = Omit<
|
|
74
|
+
ListContentProps,
|
|
75
|
+
'children'
|
|
76
|
+
> & {
|
|
77
|
+
children?: ReactNode | ((item: Option, isSelected: boolean) => ReactNode);
|
|
58
78
|
};
|
|
59
79
|
|
|
60
80
|
// Select.Trigger props
|
|
@@ -62,9 +82,12 @@ export type SelectTriggerProps = ViewProps & {
|
|
|
62
82
|
children?: ReactNode;
|
|
63
83
|
};
|
|
64
84
|
|
|
85
|
+
export type SelectTriggerOutlineProps = ViewProps;
|
|
86
|
+
|
|
65
87
|
// Select.Value props
|
|
66
88
|
export type SelectValueProps = ViewProps & {
|
|
67
89
|
placeholder?: string;
|
|
90
|
+
labelKey?: string;
|
|
68
91
|
renderValue?: (value: DefaultItemT | DefaultItemT[] | null) => ReactNode;
|
|
69
92
|
};
|
|
70
93
|
|
|
@@ -78,38 +101,22 @@ export type SelectDropdownProps = Omit<
|
|
|
78
101
|
wrapperComponentProps?: Record<string, any>;
|
|
79
102
|
};
|
|
80
103
|
|
|
81
|
-
// Select.Content props
|
|
82
|
-
export type SelectContentProps<Option extends DefaultItemT = DefaultItemT> = Omit<
|
|
83
|
-
ScrollViewProps,
|
|
84
|
-
'children'
|
|
85
|
-
> & {
|
|
86
|
-
children: (item: Option, isSelected: boolean) => ReactNode;
|
|
87
|
-
ContainerComponent?: ComponentType<any>;
|
|
88
|
-
emptyState?: ReactNode;
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
// Select.Group props
|
|
92
|
-
export type SelectGroupProps = ViewProps & {
|
|
93
|
-
children: ReactNode;
|
|
94
|
-
label?: string;
|
|
95
|
-
};
|
|
96
|
-
|
|
97
104
|
// Select.Option props
|
|
98
|
-
|
|
105
|
+
// `accessibilityRole` and `role` are intentionally omitted: Select.Option forces them to
|
|
106
|
+
// "option" on web so the dropdown's keyboard navigator (which queries [role="option"]) can
|
|
107
|
+
// always find the rows. Allowing callers to override would silently break keyboard nav.
|
|
108
|
+
export type SelectOptionProps<Option extends DefaultItemT = DefaultItemT> = Omit<
|
|
109
|
+
ViewProps,
|
|
110
|
+
'accessibilityRole' | 'role'
|
|
111
|
+
> & {
|
|
99
112
|
/**
|
|
100
113
|
* Unique id for the option
|
|
101
114
|
*/
|
|
102
115
|
value: Option['id'];
|
|
103
|
-
children
|
|
104
|
-
renderItem?: (item: Option, isSelected: boolean) => ReactNode;
|
|
116
|
+
children: ReactNode;
|
|
105
117
|
onPress?: (item: Option, event: GestureResponderEvent) => void;
|
|
106
118
|
/**
|
|
107
119
|
* When true, the option can't be selected (similar to HTML option disabled)
|
|
108
120
|
*/
|
|
109
121
|
disabled?: boolean;
|
|
110
122
|
};
|
|
111
|
-
|
|
112
|
-
// Select.SearchInput props
|
|
113
|
-
export type SelectSearchInputProps = TextInputProps & {
|
|
114
|
-
onQueryChange?: (query: string) => void;
|
|
115
|
-
};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { StyleSheet } from 'react-native-unistyles';
|
|
2
|
+
|
|
3
|
+
import { getRegisteredComponentStylesWithFallback } from '../../core';
|
|
4
|
+
|
|
5
|
+
/** Web-only marker on Select.Option roots so keyboard nav does not depend on role/accessibilityRole overrides. */
|
|
6
|
+
export const SELECT_OPTION_DATA_ATTR = 'data-molecules-select-option';
|
|
7
|
+
|
|
8
|
+
const SELECT_OPTION_SELECTOR = `[${SELECT_OPTION_DATA_ATTR}], [data-option-id], [role="option"]`;
|
|
9
|
+
|
|
10
|
+
export function collectWebSelectKeyboardOptionElements(container: ParentNode): HTMLElement[] {
|
|
11
|
+
return Array.from(container.querySelectorAll(SELECT_OPTION_SELECTOR)).filter(
|
|
12
|
+
(el): el is HTMLElement => {
|
|
13
|
+
if (!(el instanceof HTMLElement)) return false;
|
|
14
|
+
if (el.getAttribute('aria-disabled') === 'true') return false;
|
|
15
|
+
return true;
|
|
16
|
+
},
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const triggerDefaultStyles = StyleSheet.create(theme => ({
|
|
21
|
+
trigger: {
|
|
22
|
+
borderRadius: theme.shapes.corner.extraSmall,
|
|
23
|
+
paddingHorizontal: theme.spacings['3'],
|
|
24
|
+
paddingVertical: theme.spacings['2'],
|
|
25
|
+
minHeight: 48,
|
|
26
|
+
flexDirection: 'row',
|
|
27
|
+
alignItems: 'center',
|
|
28
|
+
justifyContent: 'space-between',
|
|
29
|
+
width: '100%',
|
|
30
|
+
variants: {
|
|
31
|
+
state: {
|
|
32
|
+
disabled: {
|
|
33
|
+
opacity: 0.38,
|
|
34
|
+
backgroundColor: theme.colors.surfaceVariant,
|
|
35
|
+
},
|
|
36
|
+
errorDisabled: {
|
|
37
|
+
opacity: 0.38,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
triggerIcon: {
|
|
43
|
+
marginLeft: theme.spacings['2'],
|
|
44
|
+
color: theme.colors.onSurfaceVariant,
|
|
45
|
+
},
|
|
46
|
+
}));
|
|
47
|
+
|
|
48
|
+
const outlineDefaultStyles = StyleSheet.create(theme => ({
|
|
49
|
+
outline: {
|
|
50
|
+
position: 'absolute',
|
|
51
|
+
top: 0,
|
|
52
|
+
left: 0,
|
|
53
|
+
right: 0,
|
|
54
|
+
bottom: 0,
|
|
55
|
+
borderRadius: theme.shapes.corner.extraSmall,
|
|
56
|
+
borderWidth: 1,
|
|
57
|
+
borderColor: theme.colors.outline,
|
|
58
|
+
pointerEvents: 'none',
|
|
59
|
+
variants: {
|
|
60
|
+
state: {
|
|
61
|
+
focused: {
|
|
62
|
+
borderWidth: 2,
|
|
63
|
+
borderColor: theme.colors.primary,
|
|
64
|
+
},
|
|
65
|
+
hovered: {
|
|
66
|
+
borderColor: theme.colors.onSurface,
|
|
67
|
+
},
|
|
68
|
+
hoveredAndFocused: {
|
|
69
|
+
borderWidth: 2,
|
|
70
|
+
borderColor: theme.colors.primary,
|
|
71
|
+
},
|
|
72
|
+
disabled: {
|
|
73
|
+
borderColor: theme.colors.onSurface,
|
|
74
|
+
},
|
|
75
|
+
error: {
|
|
76
|
+
borderColor: theme.colors.error,
|
|
77
|
+
},
|
|
78
|
+
errorFocused: {
|
|
79
|
+
borderWidth: 2,
|
|
80
|
+
borderColor: theme.colors.error,
|
|
81
|
+
},
|
|
82
|
+
errorHovered: {
|
|
83
|
+
borderColor: theme.colors.onErrorContainer,
|
|
84
|
+
},
|
|
85
|
+
errorFocusedAndHovered: {
|
|
86
|
+
borderWidth: 2,
|
|
87
|
+
borderColor: theme.colors.error,
|
|
88
|
+
},
|
|
89
|
+
errorDisabled: {
|
|
90
|
+
borderColor: theme.colors.error,
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
}));
|
|
96
|
+
|
|
97
|
+
export const defaultStyles = StyleSheet.create(theme => ({
|
|
98
|
+
valueText: {
|
|
99
|
+
flex: 1,
|
|
100
|
+
},
|
|
101
|
+
chipContainer: {
|
|
102
|
+
flexDirection: 'row',
|
|
103
|
+
flexWrap: 'wrap',
|
|
104
|
+
gap: 6,
|
|
105
|
+
maxWidth: '90%',
|
|
106
|
+
flex: 1,
|
|
107
|
+
},
|
|
108
|
+
searchInput: {
|
|
109
|
+
marginHorizontal: theme.spacings['2'],
|
|
110
|
+
marginVertical: theme.spacings['3'],
|
|
111
|
+
},
|
|
112
|
+
searchInputInput: {
|
|
113
|
+
height: 42,
|
|
114
|
+
},
|
|
115
|
+
}));
|
|
116
|
+
|
|
117
|
+
export const triggerStyles = getRegisteredComponentStylesWithFallback(
|
|
118
|
+
'Select_Trigger',
|
|
119
|
+
triggerDefaultStyles,
|
|
120
|
+
);
|
|
121
|
+
export const selectOutlineStyles = getRegisteredComponentStylesWithFallback(
|
|
122
|
+
'SelectOutline',
|
|
123
|
+
outlineDefaultStyles,
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
export const styles = getRegisteredComponentStylesWithFallback('Select', defaultStyles);
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { composeRefs } from '@radix-ui/react-compose-refs';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { type PressableProps, type StyleProp, type ViewProps, type ViewStyle } from 'react-native';
|
|
4
|
+
|
|
5
|
+
declare module 'react' {
|
|
6
|
+
interface ReactElement {
|
|
7
|
+
$$typeof?: symbol | string;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const REACT_LAZY_TYPE = Symbol.for('react.lazy');
|
|
12
|
+
|
|
13
|
+
interface LazyReactElement extends React.ReactElement {
|
|
14
|
+
$$typeof: typeof REACT_LAZY_TYPE;
|
|
15
|
+
_payload: PromiseLike<Exclude<React.ReactNode, PromiseLike<any>>>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/* -------------------------------------------------------------------------------------------------
|
|
19
|
+
* Slot
|
|
20
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
21
|
+
|
|
22
|
+
export type Usable<T> = PromiseLike<T> | React.Context<T>;
|
|
23
|
+
const use: typeof React.use | undefined = (React as any)[' use '.trim().toString()];
|
|
24
|
+
|
|
25
|
+
interface SlotProps
|
|
26
|
+
extends Omit<ViewProps, 'children'>,
|
|
27
|
+
Partial<
|
|
28
|
+
Pick<
|
|
29
|
+
PressableProps,
|
|
30
|
+
'onPress' | 'onPressIn' | 'onPressOut' | 'onLongPress' | 'disabled'
|
|
31
|
+
>
|
|
32
|
+
> {
|
|
33
|
+
children?: React.ReactNode;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function isPromiseLike(value: unknown): value is PromiseLike<unknown> {
|
|
37
|
+
return typeof value === 'object' && value !== null && 'then' in value;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function isLazyComponent(element: React.ReactNode): element is LazyReactElement {
|
|
41
|
+
return (
|
|
42
|
+
element != null &&
|
|
43
|
+
typeof element === 'object' &&
|
|
44
|
+
'$$typeof' in element &&
|
|
45
|
+
element.$$typeof === REACT_LAZY_TYPE &&
|
|
46
|
+
'_payload' in element &&
|
|
47
|
+
isPromiseLike(element._payload)
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* @__NO_SIDE_EFFECTS__ */ export function createSlot(ownerName: string) {
|
|
52
|
+
const SlotClone = createSlotClone(ownerName);
|
|
53
|
+
const Slot = React.forwardRef<any, SlotProps>((props, forwardedRef) => {
|
|
54
|
+
const { children: childrenProp, ...slotProps } = props;
|
|
55
|
+
let children = childrenProp;
|
|
56
|
+
if (isLazyComponent(children) && typeof use === 'function') {
|
|
57
|
+
children = use(children._payload);
|
|
58
|
+
}
|
|
59
|
+
const childrenArray = React.Children.toArray(children);
|
|
60
|
+
const slottable = childrenArray.find(isSlottable);
|
|
61
|
+
|
|
62
|
+
if (slottable) {
|
|
63
|
+
// the new element to render is the one passed as a child of `Slottable`
|
|
64
|
+
const newElement = slottable.props.children;
|
|
65
|
+
|
|
66
|
+
const newChildren = childrenArray.map(child => {
|
|
67
|
+
if (child === slottable) {
|
|
68
|
+
// because the new element will be the one rendered, we are only interested
|
|
69
|
+
// in grabbing its children (`newElement.props.children`)
|
|
70
|
+
if (React.Children.count(newElement) > 1) return React.Children.only(null);
|
|
71
|
+
return React.isValidElement(newElement)
|
|
72
|
+
? (newElement.props as { children: React.ReactNode }).children
|
|
73
|
+
: null;
|
|
74
|
+
} else {
|
|
75
|
+
return child;
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<SlotClone {...slotProps} ref={forwardedRef}>
|
|
81
|
+
{React.isValidElement(newElement)
|
|
82
|
+
? React.cloneElement(newElement, undefined, newChildren)
|
|
83
|
+
: null}
|
|
84
|
+
</SlotClone>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<SlotClone {...slotProps} ref={forwardedRef}>
|
|
90
|
+
{children}
|
|
91
|
+
</SlotClone>
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
Slot.displayName = `${ownerName}.Slot`;
|
|
96
|
+
return Slot;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const Slot = createSlot('Slot');
|
|
100
|
+
|
|
101
|
+
/* -------------------------------------------------------------------------------------------------
|
|
102
|
+
* SlotClone
|
|
103
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
104
|
+
|
|
105
|
+
interface SlotCloneProps {
|
|
106
|
+
children: React.ReactNode;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* @__NO_SIDE_EFFECTS__ */ function createSlotClone(ownerName: string) {
|
|
110
|
+
const SlotClone = React.forwardRef<any, SlotCloneProps>((props, forwardedRef) => {
|
|
111
|
+
const { children: childrenProp, ...slotProps } = props;
|
|
112
|
+
let children = childrenProp;
|
|
113
|
+
if (isLazyComponent(children) && typeof use === 'function') {
|
|
114
|
+
children = use(children._payload);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (React.isValidElement(children)) {
|
|
118
|
+
const childrenRef = getElementRef(children);
|
|
119
|
+
const mergedProps = mergeProps(slotProps, children.props as AnyProps);
|
|
120
|
+
// do not pass ref to React.Fragment for React 19 compatibility
|
|
121
|
+
if (children.type !== React.Fragment) {
|
|
122
|
+
mergedProps.ref = forwardedRef
|
|
123
|
+
? composeRefs(forwardedRef, childrenRef)
|
|
124
|
+
: childrenRef;
|
|
125
|
+
}
|
|
126
|
+
return React.cloneElement(children, mergedProps);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return React.Children.count(children) > 1 ? React.Children.only(null) : null;
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
SlotClone.displayName = `${ownerName}.SlotClone`;
|
|
133
|
+
return SlotClone;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/* -------------------------------------------------------------------------------------------------
|
|
137
|
+
* Slottable
|
|
138
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
139
|
+
|
|
140
|
+
const SLOTTABLE_IDENTIFIER = Symbol('radix.slottable');
|
|
141
|
+
|
|
142
|
+
interface SlottableProps {
|
|
143
|
+
children: React.ReactNode;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export interface SlottableComponent extends React.FC<SlottableProps> {
|
|
147
|
+
__radixId: symbol;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/* @__NO_SIDE_EFFECTS__ */ export function createSlottable(ownerName: string) {
|
|
151
|
+
const Slottable: SlottableComponent = ({ children }) => {
|
|
152
|
+
return <>{children}</>;
|
|
153
|
+
};
|
|
154
|
+
Slottable.displayName = `${ownerName}.Slottable`;
|
|
155
|
+
Slottable.__radixId = SLOTTABLE_IDENTIFIER;
|
|
156
|
+
return Slottable;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const Slottable = createSlottable('Slottable');
|
|
160
|
+
|
|
161
|
+
/* ---------------------------------------------------------------------------------------------- */
|
|
162
|
+
|
|
163
|
+
type AnyProps = Record<string, any>;
|
|
164
|
+
|
|
165
|
+
function isSlottable(
|
|
166
|
+
child: React.ReactNode,
|
|
167
|
+
): child is React.ReactElement<SlottableProps, typeof Slottable> {
|
|
168
|
+
return (
|
|
169
|
+
React.isValidElement(child) &&
|
|
170
|
+
typeof child.type === 'function' &&
|
|
171
|
+
'__radixId' in child.type &&
|
|
172
|
+
child.type.__radixId === SLOTTABLE_IDENTIFIER
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function mergeProps(slotProps: AnyProps, childProps: AnyProps) {
|
|
177
|
+
// all child props should override
|
|
178
|
+
const overrideProps = { ...childProps };
|
|
179
|
+
|
|
180
|
+
for (const propName in childProps) {
|
|
181
|
+
const slotPropValue = slotProps[propName];
|
|
182
|
+
const childPropValue = childProps[propName];
|
|
183
|
+
|
|
184
|
+
const isHandler = /^on[A-Z]/.test(propName);
|
|
185
|
+
if (isHandler) {
|
|
186
|
+
// if the handler exists on both, we compose them
|
|
187
|
+
if (slotPropValue && childPropValue) {
|
|
188
|
+
overrideProps[propName] = (...args: unknown[]) => {
|
|
189
|
+
const result = childPropValue(...args);
|
|
190
|
+
slotPropValue(...args);
|
|
191
|
+
return result;
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
// but if it exists only on the slot, we use only this one
|
|
195
|
+
else if (slotPropValue) {
|
|
196
|
+
overrideProps[propName] = slotPropValue;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// if it's `style`, we merge them (React Native styles can be arrays)
|
|
200
|
+
else if (propName === 'style') {
|
|
201
|
+
const slotStyle = slotPropValue as StyleProp<ViewStyle> | undefined;
|
|
202
|
+
const childStyle = childPropValue as StyleProp<ViewStyle> | undefined;
|
|
203
|
+
if (slotStyle || childStyle) {
|
|
204
|
+
overrideProps[propName] = [slotStyle, childStyle].filter(
|
|
205
|
+
Boolean,
|
|
206
|
+
) as StyleProp<ViewStyle>;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return { ...slotProps, ...overrideProps };
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function getElementRef(element: React.ReactElement) {
|
|
215
|
+
return (element.props as { ref?: React.Ref<unknown> }).ref;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export {
|
|
219
|
+
//
|
|
220
|
+
Slot as Root,
|
|
221
|
+
Slot,
|
|
222
|
+
Slottable,
|
|
223
|
+
};
|
|
224
|
+
export type { SlotProps };
|