react-native-molecules 0.5.0-beta.22 → 0.5.0-beta.24
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 +1 -1
- package/components/Accordion/AccordionItem.tsx +1 -1
- package/components/Button/Button.tsx +3 -1
- package/components/Checkbox/Checkbox.tsx +2 -1
- package/components/DateField/useDateFieldState.ts +2 -2
- package/components/DatePicker/DatePickerProvider.tsx +1 -1
- package/components/DatePicker/utils.ts +2 -0
- package/components/DatePickerInline/DatePickerInline.tsx +1 -1
- package/components/DatePickerInline/DatePickerInlineBase.tsx +1 -1
- package/components/DatePickerInline/Day.tsx +1 -1
- package/components/DatePickerInline/MonthPicker.tsx +24 -40
- package/components/DatePickerInline/Swiper.tsx +1 -1
- package/components/DatePickerInline/SwiperUtils.ts +1 -1
- package/components/DatePickerInline/YearPicker.tsx +44 -79
- package/components/DatePickerInline/dateUtils.tsx +1 -1
- package/components/DatePickerInline/store.tsx +2 -1
- package/components/Divider/index.tsx +2 -3
- package/components/ElementGroup/ElementGroup.tsx +1 -1
- package/components/FilePicker/FilePicker.tsx +1 -1
- package/components/Icon/iconFactory.tsx +2 -1
- package/components/IconButton/IconButton.tsx +39 -13
- package/components/IconButton/index.tsx +1 -0
- package/components/IconButton/types.ts +2 -0
- package/components/List/List.tsx +156 -387
- package/components/List/context.tsx +4 -5
- package/components/List/index.ts +0 -1
- package/components/List/types.ts +77 -109
- package/components/List/utils.ts +4 -37
- package/components/Menu/Menu.tsx +13 -30
- package/components/Menu/index.tsx +0 -2
- package/components/Popover/Popover.tsx +7 -10
- package/components/Popover/PopoverRoot.tsx +6 -20
- package/components/Popover/common.ts +4 -0
- package/components/Popover/index.ts +2 -8
- package/components/Popover/usePlatformMeasure.ts +4 -2
- package/components/Portal/Portal.tsx +1 -2
- package/components/RadioButton/RadioButtonGroup.tsx +1 -2
- package/components/Rating/Rating.tsx +1 -1
- package/components/Select/Select.tsx +304 -71
- package/components/Select/context.tsx +30 -3
- package/components/Select/index.ts +20 -2
- package/components/Select/types.ts +43 -25
- package/components/Select/utils.ts +18 -4
- package/components/Switch/Switch.ios.tsx +1 -1
- package/components/Switch/Switch.tsx +2 -1
- package/components/Tabs/Tabs.tsx +2 -2
- package/components/TextInput/TextInput.tsx +4 -3
- package/components/TimePicker/AnalogClock.tsx +1 -1
- package/components/TimePicker/TimeInputs.tsx +1 -1
- package/components/TimePicker/TimePicker.tsx +1 -1
- package/components/TimePicker/TimePickerModal.tsx +1 -1
- package/components/Tooltip/Tooltip.tsx +1 -1
- package/components/TouchableRipple/TouchableRipple.tsx +76 -152
- package/hocs/index.tsx +1 -1
- package/hocs/withKeyboardAccessibility.tsx +2 -3
- package/hooks/index.tsx +2 -6
- package/hooks/useContrastColor.ts +1 -2
- package/hooks/useFilePicker.tsx +1 -1
- package/hooks/useHandleNumberFormat.tsx +2 -2
- package/hooks/useMediaQuery.tsx +1 -2
- package/package.json +5 -28
- package/shortcuts-manager/ShortcutsManager/ShortcutsManager.tsx +1 -1
- 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/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/context-bridge/index.tsx +0 -87
- package/fast-context/index.tsx +0 -190
- package/hocs/typedMemo.tsx +0 -5
- package/hooks/useControlledValue.tsx +0 -84
- package/hooks/useLatest.tsx +0 -9
- package/hooks/useMergedRefs.ts +0 -14
- package/hooks/usePrevious.ts +0 -13
- package/hooks/useToggle.tsx +0 -24
- package/hooks/useWhatHasUpdated.tsx +0 -48
- package/utils/color.ts +0 -22
- package/utils/compare/index.ts +0 -54
- package/utils/lodash.ts +0 -121
- package/utils/repository.ts +0 -53
package/components/List/types.ts
CHANGED
|
@@ -1,149 +1,117 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ReactNode, RefObject } from 'react';
|
|
2
2
|
import {
|
|
3
|
+
type AccessibilityRole,
|
|
3
4
|
type GestureResponderEvent,
|
|
4
|
-
|
|
5
|
-
type
|
|
6
|
-
type
|
|
5
|
+
type ScrollViewProps,
|
|
6
|
+
type StyleProp,
|
|
7
|
+
type ViewStyle,
|
|
7
8
|
} from 'react-native';
|
|
8
9
|
|
|
9
|
-
import type {
|
|
10
|
+
import type { TouchableRippleProps } from '../TouchableRipple';
|
|
11
|
+
|
|
12
|
+
export type ListItemId = string | number;
|
|
10
13
|
|
|
11
14
|
export type DefaultListItemT = {
|
|
12
|
-
id
|
|
15
|
+
id?: ListItemId;
|
|
13
16
|
label?: string;
|
|
14
17
|
selectable?: boolean;
|
|
15
|
-
[key: string]:
|
|
18
|
+
[key: string]: unknown;
|
|
16
19
|
};
|
|
17
20
|
|
|
18
|
-
export type ListValue<
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
export type ListValue<Multiple extends boolean = false> = Multiple extends true
|
|
22
|
+
? ListItemId[]
|
|
23
|
+
: ListItemId | null;
|
|
24
|
+
|
|
25
|
+
export type ListEmptyStateRender = (ctx: {
|
|
26
|
+
/** True when `items` (the raw input) has at least one entry. */
|
|
27
|
+
hasItems: boolean;
|
|
28
|
+
}) => ReactNode;
|
|
22
29
|
|
|
23
|
-
export type ListContextValue<Option extends
|
|
24
|
-
value:
|
|
30
|
+
export type ListContextValue<Option extends object = DefaultListItemT> = {
|
|
31
|
+
value: ListItemId | ListItemId[] | null;
|
|
25
32
|
multiple: boolean;
|
|
26
33
|
onAdd: (item: Option) => void;
|
|
27
34
|
onRemove: (item: Option) => void;
|
|
35
|
+
isSelectedId: (id: ListItemId) => boolean;
|
|
28
36
|
disabled?: boolean;
|
|
29
37
|
error: boolean;
|
|
30
|
-
|
|
31
|
-
searchQuery: string;
|
|
32
|
-
setSearchQuery: (query: string) => void;
|
|
33
|
-
filteredItems: Option[];
|
|
38
|
+
allowDeselect: boolean;
|
|
34
39
|
};
|
|
35
40
|
|
|
36
|
-
type ListPropsBase
|
|
41
|
+
type ListPropsBase = {
|
|
37
42
|
children: ReactNode;
|
|
38
|
-
items: Option[];
|
|
39
43
|
disabled?: boolean;
|
|
40
44
|
error?: boolean;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Whether re-clicking the currently-selected row should remove it. Defaults
|
|
47
|
+
* to `true` for multiple, `false` for single (re-clicking the picked row
|
|
48
|
+
* in a "pick one and close" flow shouldn't clear the value).
|
|
49
|
+
*/
|
|
50
|
+
allowDeselect?: boolean;
|
|
44
51
|
};
|
|
45
52
|
|
|
46
|
-
type SingleListProps<Option extends
|
|
53
|
+
type SingleListProps<Option extends object = DefaultListItemT> = {
|
|
47
54
|
multiple?: false | undefined;
|
|
48
|
-
value?: ListValue<
|
|
49
|
-
defaultValue?: ListValue<
|
|
50
|
-
onChange?: (
|
|
51
|
-
value: ListValue<Option, false>,
|
|
52
|
-
item: Option,
|
|
53
|
-
event?: GestureResponderEvent,
|
|
54
|
-
) => void;
|
|
55
|
+
value?: ListValue<false>;
|
|
56
|
+
defaultValue?: ListValue<false>;
|
|
57
|
+
onChange?: (value: ListValue<false>, item: Option, event?: GestureResponderEvent) => void;
|
|
55
58
|
};
|
|
56
59
|
|
|
57
|
-
type MultipleListProps<Option extends
|
|
60
|
+
type MultipleListProps<Option extends object = DefaultListItemT> = {
|
|
58
61
|
multiple: true;
|
|
59
|
-
value?: ListValue<
|
|
60
|
-
defaultValue?: ListValue<
|
|
61
|
-
onChange?: (
|
|
62
|
-
value: ListValue<Option, true>,
|
|
63
|
-
item: Option,
|
|
64
|
-
event?: GestureResponderEvent,
|
|
65
|
-
) => void;
|
|
62
|
+
value?: ListValue<true>;
|
|
63
|
+
defaultValue?: ListValue<true>;
|
|
64
|
+
onChange?: (value: ListValue<true>, item: Option, event?: GestureResponderEvent) => void;
|
|
66
65
|
};
|
|
67
66
|
|
|
68
|
-
export type ListProps<Option extends
|
|
67
|
+
export type ListProps<Option extends object = DefaultListItemT> = ListPropsBase &
|
|
69
68
|
(SingleListProps<Option> | MultipleListProps<Option>);
|
|
70
69
|
|
|
70
|
+
export type ListContentProps = Omit<ScrollViewProps, 'children'> & {
|
|
71
|
+
children?: ReactNode;
|
|
72
|
+
};
|
|
73
|
+
|
|
71
74
|
/**
|
|
72
|
-
*
|
|
75
|
+
* Props for `<List.Item>`. When `value` is provided, the item participates in the
|
|
76
|
+
* surrounding `<List>` context — it derives its `selected` state from the context's
|
|
77
|
+
* value and toggles selection on press (unless `shouldToggleOnPress` is false).
|
|
73
78
|
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
79
|
+
* Without `value`, the item is a plain styled row (use it for menu-style entries
|
|
80
|
+
* that don't represent a selectable option).
|
|
81
|
+
*
|
|
82
|
+
* Note: when `value` is set, both `onPress` and the selection toggle fire on press,
|
|
83
|
+
* in that order. For most cases that's fine — pass `onPress` for side effects
|
|
84
|
+
* (e.g. closing a menu) and let the toggle drive `onChange`. Pass
|
|
85
|
+
* `onBeforeToggle` for side effects that should only run when the built-in
|
|
86
|
+
* toggle will happen. Set `shouldToggleOnPress={false}` to suppress the toggle entirely.
|
|
87
|
+
*
|
|
88
|
+
* Deselection: by default, single-select rows do **not** deselect on re-click
|
|
89
|
+
* (use `<List allowDeselect>` to opt in or out at the list level).
|
|
77
90
|
*/
|
|
78
|
-
export type
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
> = {
|
|
82
|
-
/** The user-provided props on `<List.Content>` minus `children`/`ref`. */
|
|
83
|
-
props: ContainerProps;
|
|
84
|
-
/** The current `filteredItems` from the List context. */
|
|
85
|
-
items: Option[];
|
|
86
|
-
/** True when there are no items to render after filtering. */
|
|
87
|
-
isEmpty: boolean;
|
|
88
|
-
/** Resolved empty state node (caller-provided or the default). */
|
|
89
|
-
emptyState: ReactNode;
|
|
90
|
-
/** Returns whether the given item is currently selected. */
|
|
91
|
-
isSelected: (item: Option) => boolean;
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
type ListContentPropsShared<C extends ComponentType<any> = typeof ScrollView> = Partial<
|
|
95
|
-
Omit<ComponentProps<C>, 'children' | 'ref'>
|
|
96
|
-
> & {
|
|
97
|
-
/**
|
|
98
|
-
* The component used to render the scrollable container. Defaults to ScrollView.
|
|
99
|
-
* The rest of the props on `<List.Content>` are inferred from this component's props.
|
|
100
|
-
*
|
|
101
|
-
* Required props (e.g. `FlatList`'s `data`/`renderItem`) can be supplied
|
|
102
|
-
* either directly or via `processProps`.
|
|
103
|
-
*/
|
|
104
|
-
ContainerComponent?: C;
|
|
105
|
-
emptyState?: ReactNode;
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
export type ListContentProps<
|
|
109
|
-
Option extends DefaultListItemT = DefaultListItemT,
|
|
110
|
-
C extends ComponentType<any> = typeof ScrollView,
|
|
111
|
-
> = ListContentPropsShared<C> &
|
|
112
|
-
(
|
|
113
|
-
| {
|
|
114
|
-
/**
|
|
115
|
-
* Optional when `processProps` renders rows/items itself (e.g. chunked grid rows).
|
|
116
|
-
*/
|
|
117
|
-
processProps: (
|
|
118
|
-
args: ListContentProcessPropsArgs<
|
|
119
|
-
Option,
|
|
120
|
-
Omit<ComponentProps<C>, 'children' | 'ref'>
|
|
121
|
-
>,
|
|
122
|
-
) => ComponentProps<C>;
|
|
123
|
-
children?: (item: Option, isSelected: boolean) => ReactNode;
|
|
124
|
-
}
|
|
125
|
-
| {
|
|
126
|
-
processProps?: undefined;
|
|
127
|
-
children: (item: Option, isSelected: boolean) => ReactNode;
|
|
128
|
-
}
|
|
129
|
-
);
|
|
130
|
-
|
|
131
|
-
export type ListGroupProps = ViewProps & {
|
|
132
|
-
children: ReactNode;
|
|
133
|
-
label?: string;
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
export type ListItemOptionProps<Option extends DefaultListItemT = DefaultListItemT> = Omit<
|
|
137
|
-
ListItemProps,
|
|
138
|
-
'children' | 'selected' | 'disabled' | 'onPress'
|
|
91
|
+
export type ListItemProps<Option extends object = DefaultListItemT> = Omit<
|
|
92
|
+
TouchableRippleProps,
|
|
93
|
+
'children' | 'onPress'
|
|
139
94
|
> & {
|
|
140
|
-
|
|
141
|
-
children
|
|
142
|
-
|
|
95
|
+
ref?: RefObject<unknown>;
|
|
96
|
+
children?: ReactNode;
|
|
97
|
+
value?: ListItemId;
|
|
98
|
+
style?: StyleProp<ViewStyle>;
|
|
99
|
+
variant?: 'default' | 'menuItem';
|
|
100
|
+
selected?: boolean;
|
|
143
101
|
disabled?: boolean;
|
|
102
|
+
hovered?: boolean;
|
|
103
|
+
hoverable?: boolean;
|
|
144
104
|
shouldToggleOnPress?: boolean;
|
|
145
|
-
|
|
105
|
+
/** Runs after `onPress`, before the built-in selection toggle. */
|
|
106
|
+
onBeforeToggle?: (event: GestureResponderEvent) => void;
|
|
107
|
+
onPress?: (event: GestureResponderEvent) => void;
|
|
108
|
+
accessibilityRole?: AccessibilityRole;
|
|
146
109
|
accessibilityState?: Record<string, unknown>;
|
|
110
|
+
/** Reserved for generic item shape; not consumed directly. */
|
|
111
|
+
__optionType?: Option;
|
|
147
112
|
};
|
|
148
113
|
|
|
149
|
-
export type
|
|
114
|
+
export type ListItemElementProps = {
|
|
115
|
+
children?: ReactNode;
|
|
116
|
+
style?: StyleProp<ViewStyle>;
|
|
117
|
+
};
|
package/components/List/utils.ts
CHANGED
|
@@ -3,19 +3,6 @@ import { StyleSheet } from 'react-native-unistyles';
|
|
|
3
3
|
import { getRegisteredComponentStylesWithFallback } from '../../core';
|
|
4
4
|
|
|
5
5
|
const defaultStyles = StyleSheet.create(theme => ({
|
|
6
|
-
groupLabel: {
|
|
7
|
-
paddingHorizontal: theme.spacings['4'],
|
|
8
|
-
paddingVertical: theme.spacings['2'],
|
|
9
|
-
fontWeight: '600',
|
|
10
|
-
color: theme.colors.onSurface,
|
|
11
|
-
},
|
|
12
|
-
searchInput: {
|
|
13
|
-
marginHorizontal: theme.spacings['2'],
|
|
14
|
-
marginVertical: theme.spacings['3'],
|
|
15
|
-
},
|
|
16
|
-
searchInputInput: {
|
|
17
|
-
height: 42,
|
|
18
|
-
},
|
|
19
6
|
emptyState: {
|
|
20
7
|
paddingHorizontal: theme.spacings['4'],
|
|
21
8
|
paddingVertical: theme.spacings['6'],
|
|
@@ -33,6 +20,9 @@ export const listStyles = getRegisteredComponentStylesWithFallback('List', defau
|
|
|
33
20
|
const listItemStylesDefault = StyleSheet.create(theme => ({
|
|
34
21
|
root: {
|
|
35
22
|
backgroundColor: theme.colors.surface,
|
|
23
|
+
flexDirection: 'row',
|
|
24
|
+
alignItems: 'center',
|
|
25
|
+
gap: theme.spacings['4'],
|
|
36
26
|
|
|
37
27
|
_web: {
|
|
38
28
|
outlineStyle: 'none',
|
|
@@ -58,37 +48,14 @@ const listItemStylesDefault = StyleSheet.create(theme => ({
|
|
|
58
48
|
paddingLeft: theme.spacings['4'],
|
|
59
49
|
paddingRight: theme.spacings['6'],
|
|
60
50
|
minHeight: 56,
|
|
61
|
-
justifyContent: 'center',
|
|
62
51
|
},
|
|
63
52
|
menuItem: {
|
|
64
53
|
paddingHorizontal: theme.spacings['3'],
|
|
65
|
-
minHeight:
|
|
66
|
-
justifyContent: 'center',
|
|
54
|
+
minHeight: 40,
|
|
67
55
|
},
|
|
68
56
|
},
|
|
69
57
|
},
|
|
70
58
|
},
|
|
71
|
-
|
|
72
|
-
innerContainer: {
|
|
73
|
-
flexDirection: 'row',
|
|
74
|
-
alignItems: 'center',
|
|
75
|
-
minHeight: 40,
|
|
76
|
-
},
|
|
77
|
-
|
|
78
|
-
content: {
|
|
79
|
-
flex: 1,
|
|
80
|
-
justifyContent: 'center',
|
|
81
|
-
minHeight: 40,
|
|
82
|
-
},
|
|
83
|
-
|
|
84
|
-
leftElement: {
|
|
85
|
-
marginRight: theme.spacings['4'],
|
|
86
|
-
marginLeft: theme.spacings._1,
|
|
87
|
-
},
|
|
88
|
-
rightElement: {
|
|
89
|
-
marginRight: theme.spacings._1,
|
|
90
|
-
marginLeft: theme.spacings['4'],
|
|
91
|
-
},
|
|
92
59
|
stateLayer: {
|
|
93
60
|
variants: {
|
|
94
61
|
state: {
|
package/components/Menu/Menu.tsx
CHANGED
|
@@ -29,20 +29,16 @@ type MenuBaseProps = Omit<
|
|
|
29
29
|
style?: ViewStyle;
|
|
30
30
|
closeOnSelect?: boolean;
|
|
31
31
|
children: ReactElement | ReactElement[];
|
|
32
|
-
backdropStyles?: ViewStyle;
|
|
33
|
-
items?: DefaultListItemT[];
|
|
34
32
|
disabled?: boolean;
|
|
35
|
-
|
|
36
|
-
onSearchChange?: (query: string) => void;
|
|
37
|
-
hideSelected?: boolean;
|
|
33
|
+
allowDeselect?: boolean;
|
|
38
34
|
};
|
|
39
35
|
|
|
40
36
|
type SingleMenuProps = {
|
|
41
37
|
multiple?: false | undefined;
|
|
42
|
-
value?: ListValue<
|
|
43
|
-
defaultValue?: ListValue<
|
|
38
|
+
value?: ListValue<false>;
|
|
39
|
+
defaultValue?: ListValue<false>;
|
|
44
40
|
onChange?: (
|
|
45
|
-
value: ListValue<
|
|
41
|
+
value: ListValue<false>,
|
|
46
42
|
item: DefaultListItemT,
|
|
47
43
|
event?: GestureResponderEvent,
|
|
48
44
|
) => void;
|
|
@@ -50,10 +46,10 @@ type SingleMenuProps = {
|
|
|
50
46
|
|
|
51
47
|
type MultipleMenuProps = {
|
|
52
48
|
multiple: true;
|
|
53
|
-
value?: ListValue<
|
|
54
|
-
defaultValue?: ListValue<
|
|
49
|
+
value?: ListValue<true>;
|
|
50
|
+
defaultValue?: ListValue<true>;
|
|
55
51
|
onChange?: (
|
|
56
|
-
value: ListValue<
|
|
52
|
+
value: ListValue<true>,
|
|
57
53
|
item: DefaultListItemT,
|
|
58
54
|
event?: GestureResponderEvent,
|
|
59
55
|
) => void;
|
|
@@ -61,34 +57,25 @@ type MultipleMenuProps = {
|
|
|
61
57
|
|
|
62
58
|
export type Props = MenuBaseProps & (SingleMenuProps | MultipleMenuProps);
|
|
63
59
|
|
|
64
|
-
const emptyObj = {} as ViewStyle;
|
|
65
|
-
|
|
66
|
-
const emptyArr = [] as DefaultListItemT[];
|
|
67
|
-
|
|
68
60
|
const Menu = ({
|
|
69
61
|
children,
|
|
70
62
|
style: styleProp,
|
|
71
|
-
backdropStyles = emptyObj,
|
|
72
63
|
closeOnSelect = true,
|
|
73
|
-
items,
|
|
74
64
|
value,
|
|
75
65
|
defaultValue,
|
|
76
66
|
onChange,
|
|
77
67
|
multiple,
|
|
78
68
|
disabled,
|
|
79
|
-
|
|
80
|
-
onSearchChange,
|
|
81
|
-
hideSelected,
|
|
69
|
+
allowDeselect,
|
|
82
70
|
...rest
|
|
83
71
|
}: Props) => {
|
|
84
72
|
const { isOpen, onClose, triggerRef } = useContext(MenuRootContext);
|
|
85
73
|
|
|
86
|
-
const {
|
|
74
|
+
const { style } = useMemo(() => {
|
|
87
75
|
return {
|
|
88
|
-
backdropStyle: [menuStyles.backdrop, backdropStyles] as unknown as ViewStyle,
|
|
89
76
|
style: [menuStyles.root, styleProp] as unknown as ViewStyle,
|
|
90
77
|
};
|
|
91
|
-
}, [
|
|
78
|
+
}, [styleProp]);
|
|
92
79
|
|
|
93
80
|
const contextValue = useMemo(
|
|
94
81
|
() => ({
|
|
@@ -99,20 +86,16 @@ const Menu = ({
|
|
|
99
86
|
);
|
|
100
87
|
|
|
101
88
|
const listProps = {
|
|
102
|
-
items: items ?? emptyArr,
|
|
103
89
|
multiple,
|
|
104
90
|
value,
|
|
105
91
|
defaultValue,
|
|
106
92
|
onChange,
|
|
107
93
|
disabled,
|
|
108
|
-
|
|
109
|
-
onSearchChange,
|
|
110
|
-
hideSelected,
|
|
94
|
+
allowDeselect,
|
|
111
95
|
} as ListProps<DefaultListItemT>;
|
|
112
96
|
|
|
113
97
|
return (
|
|
114
98
|
<Popover isOpen={isOpen} onClose={onClose} style={style} triggerRef={triggerRef} {...rest}>
|
|
115
|
-
<Popover.Overlay style={backdropStyle} />
|
|
116
99
|
<List {...listProps}>
|
|
117
100
|
<MenuContext.Provider value={contextValue}>{children}</MenuContext.Provider>
|
|
118
101
|
</List>
|
|
@@ -187,9 +170,9 @@ export const MenuItem = memo(({ onPress, children, ...rest }: MenuItemProps) =>
|
|
|
187
170
|
);
|
|
188
171
|
|
|
189
172
|
return (
|
|
190
|
-
<List.
|
|
173
|
+
<List.Item variant="menuItem" {...rest} onPress={handlePress}>
|
|
191
174
|
{children}
|
|
192
|
-
</List.
|
|
175
|
+
</List.Item>
|
|
193
176
|
);
|
|
194
177
|
});
|
|
195
178
|
|
|
@@ -27,9 +27,7 @@ import { createPopoverRoot } from './PopoverRoot';
|
|
|
27
27
|
import { usePlatformMeasure } from './usePlatformMeasure';
|
|
28
28
|
import { popoverStyles } from './utils';
|
|
29
29
|
|
|
30
|
-
type PopoverPanelProps = PopoverProps & {
|
|
31
|
-
overlay?: ReactNode;
|
|
32
|
-
};
|
|
30
|
+
type PopoverPanelProps = PopoverProps & { backdrop?: ReactNode };
|
|
33
31
|
|
|
34
32
|
const PopoverPanel = ({
|
|
35
33
|
triggerRef,
|
|
@@ -45,7 +43,7 @@ const PopoverPanel = ({
|
|
|
45
43
|
offset = 8,
|
|
46
44
|
horizontalOffset = 0,
|
|
47
45
|
triggerDimensions,
|
|
48
|
-
|
|
46
|
+
backdrop,
|
|
49
47
|
...rest
|
|
50
48
|
}: PopoverPanelProps) => {
|
|
51
49
|
const {
|
|
@@ -58,11 +56,13 @@ const PopoverPanel = ({
|
|
|
58
56
|
} = usePopover({ isOpen, position, align, offset, horizontalOffset });
|
|
59
57
|
|
|
60
58
|
const popoverRef = useRef<View>(null);
|
|
59
|
+
const hasBackdrop = !!backdrop;
|
|
61
60
|
|
|
62
61
|
const { popoverStyle } = usePlatformMeasure({
|
|
63
62
|
triggerRef,
|
|
64
63
|
isOpen,
|
|
65
64
|
onClose,
|
|
65
|
+
dismissOnClickOutside: !hasBackdrop,
|
|
66
66
|
calculatedPosition,
|
|
67
67
|
calculateAndSetPosition,
|
|
68
68
|
targetLayoutRef,
|
|
@@ -93,8 +93,8 @@ const PopoverPanel = ({
|
|
|
93
93
|
return (
|
|
94
94
|
<Portal>
|
|
95
95
|
<PopoverPanelContext value={panelContextValue}>
|
|
96
|
+
{backdrop}
|
|
96
97
|
<Wrapper {...(WrapperProps as any)}>
|
|
97
|
-
{overlay}
|
|
98
98
|
<View
|
|
99
99
|
onLayout={handlePopoverLayout}
|
|
100
100
|
style={[popoverStyles.popoverContainer, style, popoverStyle]}
|
|
@@ -127,10 +127,7 @@ export const PopoverTrigger = memo(
|
|
|
127
127
|
);
|
|
128
128
|
PopoverTrigger.displayName = 'Popover_Trigger';
|
|
129
129
|
|
|
130
|
-
export const
|
|
131
|
-
PopoverContent.displayName = 'Popover_Content';
|
|
132
|
-
|
|
133
|
-
export const PopoverOverlay = memo(({ style, onPress, ...rest }: PressableProps) => {
|
|
130
|
+
export const PopoverBackdrop = memo(({ style, onPress, ...rest }: PressableProps) => {
|
|
134
131
|
const { isOpen, onClose } = useContext(PopoverContext);
|
|
135
132
|
if (!isOpen) return null;
|
|
136
133
|
return (
|
|
@@ -141,7 +138,7 @@ export const PopoverOverlay = memo(({ style, onPress, ...rest }: PressableProps)
|
|
|
141
138
|
/>
|
|
142
139
|
);
|
|
143
140
|
});
|
|
144
|
-
|
|
141
|
+
PopoverBackdrop.displayName = 'Popover_Backdrop';
|
|
145
142
|
|
|
146
143
|
type PopoverArrowProps = {
|
|
147
144
|
size?: number;
|
|
@@ -1,17 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type ComponentType,
|
|
3
|
-
memo,
|
|
4
|
-
type ReactElement,
|
|
5
|
-
type ReactNode,
|
|
6
|
-
useMemo,
|
|
7
|
-
useRef,
|
|
8
|
-
} from 'react';
|
|
1
|
+
import { type ComponentType, memo, type ReactNode, useMemo, useRef } from 'react';
|
|
9
2
|
import { type View } from 'react-native';
|
|
10
3
|
|
|
11
4
|
import { extractSubcomponents } from '../../utils/extractSubcomponents';
|
|
12
5
|
import { PopoverContext, type PopoverProps } from './common';
|
|
13
6
|
|
|
14
|
-
type PopoverPanelInternalProps = PopoverProps & {
|
|
7
|
+
type PopoverPanelInternalProps = PopoverProps & { backdrop?: ReactNode };
|
|
15
8
|
|
|
16
9
|
export const createPopoverRoot = (PopoverPanel: ComponentType<PopoverPanelInternalProps>) => {
|
|
17
10
|
const PopoverRoot = ({
|
|
@@ -25,15 +18,13 @@ export const createPopoverRoot = (PopoverPanel: ComponentType<PopoverPanelIntern
|
|
|
25
18
|
|
|
26
19
|
const {
|
|
27
20
|
Popover_Trigger,
|
|
28
|
-
|
|
29
|
-
Popover_Overlay,
|
|
21
|
+
Popover_Backdrop,
|
|
30
22
|
rest: restChildren,
|
|
31
23
|
} = extractSubcomponents({
|
|
32
24
|
children,
|
|
33
25
|
allowedChildren: [
|
|
34
26
|
{ name: 'Popover_Trigger', allowMultiple: false },
|
|
35
|
-
{ name: '
|
|
36
|
-
{ name: 'Popover_Overlay', allowMultiple: false },
|
|
27
|
+
{ name: 'Popover_Backdrop', allowMultiple: false },
|
|
37
28
|
] as const,
|
|
38
29
|
includeRest: true,
|
|
39
30
|
});
|
|
@@ -41,11 +32,6 @@ export const createPopoverRoot = (PopoverPanel: ComponentType<PopoverPanelIntern
|
|
|
41
32
|
const hasTrigger = Popover_Trigger.length > 0;
|
|
42
33
|
const resolvedTriggerRef = triggerRefProp ?? (hasTrigger ? internalTriggerRef : undefined);
|
|
43
34
|
|
|
44
|
-
const panelContent =
|
|
45
|
-
Popover_Content.length > 0
|
|
46
|
-
? (Popover_Content[0] as ReactElement<{ children?: ReactNode }>).props.children
|
|
47
|
-
: restChildren;
|
|
48
|
-
|
|
49
35
|
const contextValue = useMemo(
|
|
50
36
|
() => ({
|
|
51
37
|
triggerRef: resolvedTriggerRef ?? internalTriggerRef,
|
|
@@ -62,9 +48,9 @@ export const createPopoverRoot = (PopoverPanel: ComponentType<PopoverPanelIntern
|
|
|
62
48
|
triggerRef={resolvedTriggerRef}
|
|
63
49
|
isOpen={isOpen}
|
|
64
50
|
onClose={onClose}
|
|
65
|
-
|
|
51
|
+
backdrop={Popover_Backdrop[0]}
|
|
66
52
|
{...rest}>
|
|
67
|
-
{
|
|
53
|
+
{restChildren}
|
|
68
54
|
</PopoverPanel>
|
|
69
55
|
</PopoverContext>
|
|
70
56
|
);
|
|
@@ -4,6 +4,8 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
|
4
4
|
import type { LayoutRectangle, StyleProp, View, ViewStyle } from 'react-native';
|
|
5
5
|
import { Dimensions, StyleSheet } from 'react-native';
|
|
6
6
|
|
|
7
|
+
import { registerPortalContext } from '../Portal';
|
|
8
|
+
|
|
7
9
|
export type Position = 'top' | 'left' | 'right' | 'bottom';
|
|
8
10
|
export type Align = 'start' | 'center' | 'end';
|
|
9
11
|
|
|
@@ -27,6 +29,8 @@ export const PopoverContext = createContext<PopoverContextValue>({
|
|
|
27
29
|
triggerRef: { current: null },
|
|
28
30
|
});
|
|
29
31
|
|
|
32
|
+
registerPortalContext(PopoverContext);
|
|
33
|
+
|
|
30
34
|
export type PopoverPanelContextValue = {
|
|
31
35
|
calculatedPosition: ViewStyle | null;
|
|
32
36
|
targetLayoutRef: RefObject<LayoutRectangle | null>;
|
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
import { getRegisteredComponentWithFallback } from '../../core';
|
|
2
|
-
import PopoverDefault, {
|
|
3
|
-
PopoverArrow,
|
|
4
|
-
PopoverContent,
|
|
5
|
-
PopoverOverlay,
|
|
6
|
-
PopoverTrigger,
|
|
7
|
-
} from './Popover';
|
|
2
|
+
import PopoverDefault, { PopoverArrow, PopoverBackdrop, PopoverTrigger } from './Popover';
|
|
8
3
|
|
|
9
4
|
const PopoverBase = getRegisteredComponentWithFallback('Popover', PopoverDefault);
|
|
10
5
|
|
|
11
6
|
export const Popover = Object.assign(PopoverBase, {
|
|
12
7
|
Trigger: PopoverTrigger,
|
|
13
|
-
Content: PopoverContent,
|
|
14
8
|
Arrow: PopoverArrow,
|
|
15
|
-
|
|
9
|
+
Backdrop: PopoverBackdrop,
|
|
16
10
|
});
|
|
17
11
|
|
|
18
12
|
export type { Align, PopoverProps, Position } from './common';
|
|
@@ -7,6 +7,7 @@ export type UsePlatformMeasureArgs = {
|
|
|
7
7
|
triggerRef: RefObject<View | any> | undefined;
|
|
8
8
|
isOpen: boolean;
|
|
9
9
|
onClose?: () => void;
|
|
10
|
+
dismissOnClickOutside?: boolean;
|
|
10
11
|
calculatedPosition: ViewStyle | null;
|
|
11
12
|
calculateAndSetPosition: () => void;
|
|
12
13
|
targetLayoutRef: RefObject<LayoutRectangle | null>;
|
|
@@ -23,6 +24,7 @@ export const usePlatformMeasure = ({
|
|
|
23
24
|
triggerRef,
|
|
24
25
|
isOpen,
|
|
25
26
|
onClose,
|
|
27
|
+
dismissOnClickOutside = true,
|
|
26
28
|
calculatedPosition,
|
|
27
29
|
calculateAndSetPosition,
|
|
28
30
|
targetLayoutRef,
|
|
@@ -82,7 +84,7 @@ export const usePlatformMeasure = ({
|
|
|
82
84
|
}, [isOpen, measureTarget, triggerRef]);
|
|
83
85
|
|
|
84
86
|
useEffect(() => {
|
|
85
|
-
if (!isOpen || !onClose) return;
|
|
87
|
+
if (!isOpen || !onClose || !dismissOnClickOutside) return;
|
|
86
88
|
const handleClickOutside = (event: MouseEvent) => {
|
|
87
89
|
const popoverElement = popoverRef.current as any as HTMLElement;
|
|
88
90
|
const targetElement = triggerRef?.current as any as HTMLElement;
|
|
@@ -99,7 +101,7 @@ export const usePlatformMeasure = ({
|
|
|
99
101
|
return () => {
|
|
100
102
|
document.removeEventListener('mousedown', handleClickOutside, { capture: true });
|
|
101
103
|
};
|
|
102
|
-
}, [isOpen, onClose, popoverRef, triggerRef]);
|
|
104
|
+
}, [dismissOnClickOutside, isOpen, onClose, popoverRef, triggerRef]);
|
|
103
105
|
|
|
104
106
|
const popoverStyle = useMemo(() => {
|
|
105
107
|
if (!calculatedPosition) return popoverDefaultStyles;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { Portal as GorhomPortal } from '@gorhom/portal';
|
|
2
|
+
import { createContextBridge } from '@react-native-molecules/utils/context-bridge';
|
|
2
3
|
import { type ComponentType, type ReactNode } from 'react';
|
|
3
4
|
|
|
4
|
-
import { createContextBridge } from '../../context-bridge';
|
|
5
|
-
|
|
6
5
|
const { BridgedComponent: Portal, registerContextToBridge: registerPortalContext } =
|
|
7
6
|
createContextBridge<Omit<any, 'children'> & { children: ReactNode }>(
|
|
8
7
|
'portal-context',
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
+
import { useControlledValue } from '@react-native-molecules/utils/hooks';
|
|
1
2
|
import { createContext, memo, type ReactNode, useMemo } from 'react';
|
|
2
3
|
import { View, type ViewProps } from 'react-native';
|
|
3
4
|
|
|
4
|
-
import { useControlledValue } from '../../hooks';
|
|
5
|
-
|
|
6
5
|
export type Props = ViewProps & {
|
|
7
6
|
/**
|
|
8
7
|
* Function to execute on selection change.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { useControlledValue } from '@react-native-molecules/utils/hooks';
|
|
1
2
|
import { forwardRef, memo, useMemo } from 'react';
|
|
2
3
|
import { View, type ViewProps, type ViewStyle } from 'react-native';
|
|
3
4
|
|
|
4
|
-
import { useControlledValue } from '../../hooks';
|
|
5
5
|
import type { IconProps, IconType } from '../Icon';
|
|
6
6
|
import type { TooltipProps } from '../Tooltip';
|
|
7
7
|
import RatingItem from './RatingItem';
|