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
|
@@ -1,17 +1,35 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import {
|
|
2
|
+
cloneElement,
|
|
3
|
+
Fragment,
|
|
4
|
+
memo,
|
|
5
|
+
type ReactElement,
|
|
6
|
+
type ReactNode,
|
|
7
|
+
type Ref,
|
|
8
|
+
useContext,
|
|
9
|
+
useMemo,
|
|
10
|
+
useRef,
|
|
11
|
+
} from 'react';
|
|
12
|
+
import { Pressable, type PressableProps, type StyleProp, View, type ViewStyle } from 'react-native';
|
|
13
|
+
import { ScopedTheme, UnistylesRuntime } from 'react-native-unistyles';
|
|
14
|
+
|
|
15
|
+
import { mergeRefs } from '../../utils';
|
|
5
16
|
import { Portal } from '../Portal';
|
|
6
17
|
import {
|
|
7
18
|
DEFAULT_ARROW_SIZE,
|
|
8
|
-
|
|
19
|
+
PopoverContext,
|
|
20
|
+
PopoverPanelContext,
|
|
21
|
+
type PopoverPanelContextValue,
|
|
9
22
|
type PopoverProps,
|
|
10
23
|
useArrowStyles,
|
|
11
24
|
usePopover,
|
|
12
25
|
} from './common';
|
|
26
|
+
import { createPopoverRoot } from './PopoverRoot';
|
|
27
|
+
import { usePlatformMeasure } from './usePlatformMeasure';
|
|
28
|
+
import { popoverStyles } from './utils';
|
|
29
|
+
|
|
30
|
+
type PopoverPanelProps = PopoverProps & { backdrop?: ReactNode };
|
|
13
31
|
|
|
14
|
-
const
|
|
32
|
+
const PopoverPanel = ({
|
|
15
33
|
triggerRef,
|
|
16
34
|
children,
|
|
17
35
|
isOpen,
|
|
@@ -19,17 +37,15 @@ const Popover = ({
|
|
|
19
37
|
position = 'bottom',
|
|
20
38
|
align = 'center',
|
|
21
39
|
style,
|
|
22
|
-
showArrow = false,
|
|
23
|
-
arrowSize = DEFAULT_ARROW_SIZE,
|
|
24
40
|
inverted = false,
|
|
25
41
|
// @ts-ignore
|
|
26
42
|
dataSet,
|
|
27
|
-
withBackdropDismiss = false,
|
|
28
43
|
offset = 8,
|
|
29
|
-
|
|
44
|
+
horizontalOffset = 0,
|
|
30
45
|
triggerDimensions,
|
|
46
|
+
backdrop,
|
|
31
47
|
...rest
|
|
32
|
-
}:
|
|
48
|
+
}: PopoverPanelProps) => {
|
|
33
49
|
const {
|
|
34
50
|
popoverLayoutRef,
|
|
35
51
|
targetLayoutRef,
|
|
@@ -37,111 +53,33 @@ const Popover = ({
|
|
|
37
53
|
calculatedPosition,
|
|
38
54
|
calculateAndSetPosition,
|
|
39
55
|
handlePopoverLayout,
|
|
40
|
-
} = usePopover({
|
|
41
|
-
isOpen,
|
|
42
|
-
position,
|
|
43
|
-
align,
|
|
44
|
-
showArrow,
|
|
45
|
-
arrowSize,
|
|
46
|
-
offset,
|
|
47
|
-
});
|
|
56
|
+
} = usePopover({ isOpen, position, align, offset, horizontalOffset });
|
|
48
57
|
|
|
49
58
|
const popoverRef = useRef<View>(null);
|
|
59
|
+
const hasBackdrop = !!backdrop;
|
|
50
60
|
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const newLayout = { x, y, width, height };
|
|
57
|
-
const changed =
|
|
58
|
-
!targetLayoutRef.current ||
|
|
59
|
-
targetLayoutRef.current.x !== newLayout.x ||
|
|
60
|
-
targetLayoutRef.current.y !== newLayout.y ||
|
|
61
|
-
targetLayoutRef.current.width !== newLayout.width ||
|
|
62
|
-
targetLayoutRef.current.height !== newLayout.height;
|
|
63
|
-
|
|
64
|
-
if (changed) {
|
|
65
|
-
targetLayoutRef.current = newLayout;
|
|
66
|
-
calculateAndSetPosition();
|
|
67
|
-
}
|
|
68
|
-
} else {
|
|
69
|
-
targetLayoutRef.current = null;
|
|
70
|
-
calculateAndSetPosition();
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
);
|
|
74
|
-
} else {
|
|
75
|
-
targetLayoutRef.current = null;
|
|
76
|
-
calculateAndSetPosition();
|
|
77
|
-
}
|
|
78
|
-
}, [triggerRef, calculateAndSetPosition, targetLayoutRef]);
|
|
79
|
-
|
|
80
|
-
useLayoutEffect(() => {
|
|
81
|
-
if (isOpen) {
|
|
82
|
-
const timeoutId = setTimeout(measureTarget, 0);
|
|
83
|
-
return () => clearTimeout(timeoutId);
|
|
84
|
-
}
|
|
85
|
-
return;
|
|
86
|
-
}, [isOpen, measureTarget, triggerDimensions]);
|
|
87
|
-
|
|
88
|
-
useLayoutEffect(() => {
|
|
89
|
-
if (!isOpen) return;
|
|
90
|
-
const handleResize = () => {
|
|
91
|
-
if (triggerRef.current && isOpen) {
|
|
92
|
-
window.requestAnimationFrame(measureTarget);
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
window.addEventListener('resize', handleResize);
|
|
96
|
-
window.addEventListener('scroll', handleResize, true);
|
|
97
|
-
return () => {
|
|
98
|
-
window.removeEventListener('resize', handleResize);
|
|
99
|
-
window.removeEventListener('scroll', handleResize, true);
|
|
100
|
-
};
|
|
101
|
-
}, [isOpen, measureTarget, triggerRef]);
|
|
102
|
-
|
|
103
|
-
useEffect(() => {
|
|
104
|
-
if (!isOpen || !onClose || withBackdropDismiss) return;
|
|
105
|
-
const handleClickOutside = (event: MouseEvent) => {
|
|
106
|
-
const popoverElement = popoverRef.current as any as HTMLElement;
|
|
107
|
-
const targetElement = triggerRef.current as any as HTMLElement;
|
|
108
|
-
if (
|
|
109
|
-
popoverElement &&
|
|
110
|
-
!popoverElement.contains(event.target as Node) &&
|
|
111
|
-
targetElement &&
|
|
112
|
-
!targetElement.contains(event.target as Node)
|
|
113
|
-
) {
|
|
114
|
-
onClose();
|
|
115
|
-
}
|
|
116
|
-
};
|
|
117
|
-
document.addEventListener('mousedown', handleClickOutside, { capture: true });
|
|
118
|
-
return () => {
|
|
119
|
-
document.removeEventListener('mousedown', handleClickOutside, { capture: true });
|
|
120
|
-
};
|
|
121
|
-
}, [isOpen, onClose, popoverRef, triggerRef, withBackdropDismiss]);
|
|
122
|
-
|
|
123
|
-
const arrowStyles = useArrowStyles({
|
|
124
|
-
showArrow,
|
|
125
|
-
arrowSize,
|
|
126
|
-
style,
|
|
61
|
+
const { popoverStyle } = usePlatformMeasure({
|
|
62
|
+
triggerRef,
|
|
63
|
+
isOpen,
|
|
64
|
+
onClose,
|
|
65
|
+
dismissOnClickOutside: !hasBackdrop,
|
|
127
66
|
calculatedPosition,
|
|
67
|
+
calculateAndSetPosition,
|
|
128
68
|
targetLayoutRef,
|
|
129
|
-
|
|
130
|
-
|
|
69
|
+
popoverRef,
|
|
70
|
+
triggerDimensions,
|
|
131
71
|
});
|
|
132
72
|
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
};
|
|
144
|
-
}, [calculatedPosition]);
|
|
73
|
+
const panelContextValue = useMemo<PopoverPanelContextValue>(
|
|
74
|
+
() => ({
|
|
75
|
+
calculatedPosition,
|
|
76
|
+
targetLayoutRef,
|
|
77
|
+
popoverLayoutRef,
|
|
78
|
+
actualPositionRef,
|
|
79
|
+
containerStyle: style,
|
|
80
|
+
}),
|
|
81
|
+
[calculatedPosition, targetLayoutRef, popoverLayoutRef, actualPositionRef, style],
|
|
82
|
+
);
|
|
145
83
|
|
|
146
84
|
const Wrapper = inverted ? ScopedTheme : Fragment;
|
|
147
85
|
const WrapperProps = inverted
|
|
@@ -154,45 +92,81 @@ const Popover = ({
|
|
|
154
92
|
|
|
155
93
|
return (
|
|
156
94
|
<Portal>
|
|
157
|
-
<
|
|
158
|
-
{
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
</Wrapper>
|
|
95
|
+
<PopoverPanelContext value={panelContextValue}>
|
|
96
|
+
{backdrop}
|
|
97
|
+
<Wrapper {...(WrapperProps as any)}>
|
|
98
|
+
<View
|
|
99
|
+
onLayout={handlePopoverLayout}
|
|
100
|
+
style={[popoverStyles.popoverContainer, style, popoverStyle]}
|
|
101
|
+
{...{ dataSet }}
|
|
102
|
+
{...rest}
|
|
103
|
+
ref={popoverRef}>
|
|
104
|
+
{children}
|
|
105
|
+
</View>
|
|
106
|
+
</Wrapper>
|
|
107
|
+
</PopoverPanelContext>
|
|
171
108
|
</Portal>
|
|
172
109
|
);
|
|
173
110
|
};
|
|
174
111
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
left: 0,
|
|
190
|
-
right: 0,
|
|
191
|
-
bottom: 0,
|
|
192
|
-
_web: {
|
|
193
|
-
cursor: 'default',
|
|
194
|
-
},
|
|
112
|
+
type PopoverTriggerProps = {
|
|
113
|
+
children: ReactElement;
|
|
114
|
+
ref?: Ref<any>;
|
|
115
|
+
triggerRef?: Ref<any>;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export const PopoverTrigger = memo(
|
|
119
|
+
({ children, ref: refProp, triggerRef: triggerRefProp }: PopoverTriggerProps) => {
|
|
120
|
+
const { triggerRef } = useContext(PopoverContext);
|
|
121
|
+
const mergedRef = useMemo(
|
|
122
|
+
() => mergeRefs([triggerRef, refProp, triggerRefProp]),
|
|
123
|
+
[triggerRef, refProp, triggerRefProp],
|
|
124
|
+
);
|
|
125
|
+
return cloneElement(children as ReactElement<{ ref?: unknown }>, { ref: mergedRef });
|
|
195
126
|
},
|
|
196
|
-
|
|
127
|
+
);
|
|
128
|
+
PopoverTrigger.displayName = 'Popover_Trigger';
|
|
129
|
+
|
|
130
|
+
export const PopoverBackdrop = memo(({ style, onPress, ...rest }: PressableProps) => {
|
|
131
|
+
const { isOpen, onClose } = useContext(PopoverContext);
|
|
132
|
+
if (!isOpen) return null;
|
|
133
|
+
return (
|
|
134
|
+
<Pressable
|
|
135
|
+
{...rest}
|
|
136
|
+
onPress={onPress ?? onClose}
|
|
137
|
+
style={[popoverStyles.overlay, style as StyleProp<ViewStyle>]}
|
|
138
|
+
/>
|
|
139
|
+
);
|
|
140
|
+
});
|
|
141
|
+
PopoverBackdrop.displayName = 'Popover_Backdrop';
|
|
142
|
+
|
|
143
|
+
type PopoverArrowProps = {
|
|
144
|
+
size?: number;
|
|
145
|
+
style?: StyleProp<ViewStyle>;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
export const PopoverArrow = memo(({ size = DEFAULT_ARROW_SIZE, style }: PopoverArrowProps) => {
|
|
149
|
+
const {
|
|
150
|
+
calculatedPosition,
|
|
151
|
+
targetLayoutRef,
|
|
152
|
+
popoverLayoutRef,
|
|
153
|
+
actualPositionRef,
|
|
154
|
+
containerStyle,
|
|
155
|
+
} = useContext(PopoverPanelContext);
|
|
156
|
+
|
|
157
|
+
const arrowStyles = useArrowStyles({
|
|
158
|
+
arrowSize: size,
|
|
159
|
+
containerStyle,
|
|
160
|
+
calculatedPosition,
|
|
161
|
+
targetLayoutRef,
|
|
162
|
+
popoverLayoutRef,
|
|
163
|
+
actualPositionRef,
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
if (!arrowStyles || Object.keys(arrowStyles).length === 0) return null;
|
|
167
|
+
return <View style={[arrowStyles, style]} />;
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
PopoverArrow.displayName = 'Popover_Arrow';
|
|
197
171
|
|
|
198
|
-
export default memo(
|
|
172
|
+
export default memo(createPopoverRoot(PopoverPanel));
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { type ComponentType, memo, type ReactNode, useMemo, useRef } from 'react';
|
|
2
|
+
import { type View } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import { extractSubcomponents } from '../../utils/extractSubcomponents';
|
|
5
|
+
import { PopoverContext, type PopoverProps } from './common';
|
|
6
|
+
|
|
7
|
+
type PopoverPanelInternalProps = PopoverProps & { backdrop?: ReactNode };
|
|
8
|
+
|
|
9
|
+
export const createPopoverRoot = (PopoverPanel: ComponentType<PopoverPanelInternalProps>) => {
|
|
10
|
+
const PopoverRoot = ({
|
|
11
|
+
triggerRef: triggerRefProp,
|
|
12
|
+
isOpen,
|
|
13
|
+
onClose,
|
|
14
|
+
children,
|
|
15
|
+
...rest
|
|
16
|
+
}: PopoverProps) => {
|
|
17
|
+
const internalTriggerRef = useRef<View>(null);
|
|
18
|
+
|
|
19
|
+
const {
|
|
20
|
+
Popover_Trigger,
|
|
21
|
+
Popover_Backdrop,
|
|
22
|
+
rest: restChildren,
|
|
23
|
+
} = extractSubcomponents({
|
|
24
|
+
children,
|
|
25
|
+
allowedChildren: [
|
|
26
|
+
{ name: 'Popover_Trigger', allowMultiple: false },
|
|
27
|
+
{ name: 'Popover_Backdrop', allowMultiple: false },
|
|
28
|
+
] as const,
|
|
29
|
+
includeRest: true,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const hasTrigger = Popover_Trigger.length > 0;
|
|
33
|
+
const resolvedTriggerRef = triggerRefProp ?? (hasTrigger ? internalTriggerRef : undefined);
|
|
34
|
+
|
|
35
|
+
const contextValue = useMemo(
|
|
36
|
+
() => ({
|
|
37
|
+
triggerRef: resolvedTriggerRef ?? internalTriggerRef,
|
|
38
|
+
isOpen,
|
|
39
|
+
onClose,
|
|
40
|
+
}),
|
|
41
|
+
[resolvedTriggerRef, isOpen, onClose],
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<PopoverContext value={contextValue}>
|
|
46
|
+
{hasTrigger && Popover_Trigger[0]}
|
|
47
|
+
<PopoverPanel
|
|
48
|
+
triggerRef={resolvedTriggerRef}
|
|
49
|
+
isOpen={isOpen}
|
|
50
|
+
onClose={onClose}
|
|
51
|
+
backdrop={Popover_Backdrop[0]}
|
|
52
|
+
{...rest}>
|
|
53
|
+
{restChildren}
|
|
54
|
+
</PopoverPanel>
|
|
55
|
+
</PopoverContext>
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
return memo(PopoverRoot);
|
|
60
|
+
};
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import type { ReactNode, RefObject } from 'react';
|
|
2
|
+
import { createContext } from 'react';
|
|
2
3
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
4
|
import type { LayoutRectangle, StyleProp, View, ViewStyle } from 'react-native';
|
|
4
5
|
import { Dimensions, StyleSheet } from 'react-native';
|
|
5
6
|
|
|
7
|
+
import { registerPortalContext } from '../Portal';
|
|
8
|
+
|
|
6
9
|
export type Position = 'top' | 'left' | 'right' | 'bottom';
|
|
7
10
|
export type Align = 'start' | 'center' | 'end';
|
|
8
11
|
|
|
@@ -15,10 +18,38 @@ export const popoverDefaultStyles = {
|
|
|
15
18
|
opacity: 0,
|
|
16
19
|
};
|
|
17
20
|
|
|
21
|
+
export type PopoverContextValue = {
|
|
22
|
+
triggerRef: RefObject<View | any>;
|
|
23
|
+
isOpen: boolean;
|
|
24
|
+
onClose?: () => void;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const PopoverContext = createContext<PopoverContextValue>({
|
|
28
|
+
isOpen: false,
|
|
29
|
+
triggerRef: { current: null },
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
registerPortalContext(PopoverContext);
|
|
33
|
+
|
|
34
|
+
export type PopoverPanelContextValue = {
|
|
35
|
+
calculatedPosition: ViewStyle | null;
|
|
36
|
+
targetLayoutRef: RefObject<LayoutRectangle | null>;
|
|
37
|
+
popoverLayoutRef: RefObject<LayoutRectangle | null>;
|
|
38
|
+
actualPositionRef: RefObject<Position | undefined>;
|
|
39
|
+
containerStyle?: StyleProp<ViewStyle>;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const PopoverPanelContext = createContext<PopoverPanelContextValue>({
|
|
43
|
+
calculatedPosition: null,
|
|
44
|
+
targetLayoutRef: { current: null },
|
|
45
|
+
popoverLayoutRef: { current: null },
|
|
46
|
+
actualPositionRef: { current: undefined },
|
|
47
|
+
});
|
|
48
|
+
|
|
18
49
|
export type PopoverProps = {
|
|
19
50
|
inverted?: boolean;
|
|
20
51
|
/** Reference to the element the popover should anchor to */
|
|
21
|
-
triggerRef
|
|
52
|
+
triggerRef?: RefObject<View | any>;
|
|
22
53
|
/** Content to display inside the popover */
|
|
23
54
|
children: ReactNode;
|
|
24
55
|
/** Whether the popover is visible */
|
|
@@ -31,13 +62,10 @@ export type PopoverProps = {
|
|
|
31
62
|
align?: Align;
|
|
32
63
|
/** Optional style for the popover container */
|
|
33
64
|
style?: StyleProp<ViewStyle>;
|
|
34
|
-
/**
|
|
35
|
-
showArrow?: boolean;
|
|
36
|
-
/** Size of the arrow */
|
|
37
|
-
arrowSize?: number;
|
|
38
|
-
withBackdropDismiss?: boolean;
|
|
65
|
+
/** Gap between the popover and the trigger along the main axis */
|
|
39
66
|
offset?: number;
|
|
40
|
-
|
|
67
|
+
/** Additional horizontal shift applied to the popover (positive = right) */
|
|
68
|
+
horizontalOffset?: number;
|
|
41
69
|
/** Optional trigger dimensions to trigger re-measurement when changed */
|
|
42
70
|
triggerDimensions?: { width: number; height: number } | null;
|
|
43
71
|
};
|
|
@@ -165,13 +193,12 @@ export const adjustPositionForBoundaries = (
|
|
|
165
193
|
// --- Arrow Style Hook ---
|
|
166
194
|
|
|
167
195
|
interface UseArrowStylesProps {
|
|
168
|
-
showArrow?: boolean;
|
|
169
196
|
arrowSize: number;
|
|
170
|
-
|
|
197
|
+
containerStyle?: StyleProp<ViewStyle>;
|
|
171
198
|
calculatedPosition: ViewStyle | null;
|
|
172
199
|
targetLayoutRef: RefObject<LayoutRectangle | null>;
|
|
173
200
|
popoverLayoutRef: RefObject<LayoutRectangle | null>;
|
|
174
|
-
actualPositionRef: RefObject<Position | undefined>;
|
|
201
|
+
actualPositionRef: RefObject<Position | undefined>;
|
|
175
202
|
}
|
|
176
203
|
|
|
177
204
|
// Define a base style for the popover container to extract default background
|
|
@@ -183,9 +210,8 @@ const basePopoverStyle = StyleSheet.create({
|
|
|
183
210
|
});
|
|
184
211
|
|
|
185
212
|
export const useArrowStyles = ({
|
|
186
|
-
showArrow,
|
|
187
213
|
arrowSize,
|
|
188
|
-
|
|
214
|
+
containerStyle,
|
|
189
215
|
calculatedPosition,
|
|
190
216
|
targetLayoutRef,
|
|
191
217
|
popoverLayoutRef,
|
|
@@ -193,7 +219,6 @@ export const useArrowStyles = ({
|
|
|
193
219
|
}: UseArrowStylesProps): ViewStyle => {
|
|
194
220
|
return useMemo(() => {
|
|
195
221
|
if (
|
|
196
|
-
!showArrow ||
|
|
197
222
|
!targetLayoutRef.current ||
|
|
198
223
|
!popoverLayoutRef.current ||
|
|
199
224
|
!calculatedPosition ||
|
|
@@ -213,7 +238,7 @@ export const useArrowStyles = ({
|
|
|
213
238
|
} = targetLayoutRef.current;
|
|
214
239
|
|
|
215
240
|
const arrowHalfSize = arrowSize / 2;
|
|
216
|
-
const popoverStyleFlat = StyleSheet.flatten(
|
|
241
|
+
const popoverStyleFlat = StyleSheet.flatten(containerStyle || {});
|
|
217
242
|
const containerStyleFlat = StyleSheet.flatten(basePopoverStyle.container);
|
|
218
243
|
|
|
219
244
|
const backgroundColor =
|
|
@@ -320,14 +345,11 @@ export const useArrowStyles = ({
|
|
|
320
345
|
default:
|
|
321
346
|
return {};
|
|
322
347
|
}
|
|
323
|
-
// Use refs directly in dependency array for useMemo
|
|
324
|
-
// React checks ref.current internally when deciding memoization
|
|
325
348
|
}, [
|
|
326
|
-
showArrow,
|
|
327
349
|
arrowSize,
|
|
328
|
-
|
|
350
|
+
containerStyle,
|
|
329
351
|
calculatedPosition,
|
|
330
|
-
targetLayoutRef,
|
|
352
|
+
targetLayoutRef,
|
|
331
353
|
popoverLayoutRef,
|
|
332
354
|
actualPositionRef,
|
|
333
355
|
]);
|
|
@@ -339,18 +361,16 @@ interface UsePopoverProps {
|
|
|
339
361
|
isOpen: boolean;
|
|
340
362
|
position: Position | undefined;
|
|
341
363
|
align: Align | undefined;
|
|
342
|
-
showArrow: boolean | undefined;
|
|
343
|
-
arrowSize: number;
|
|
344
364
|
offset?: number;
|
|
365
|
+
horizontalOffset?: number;
|
|
345
366
|
}
|
|
346
367
|
|
|
347
368
|
export const usePopover = ({
|
|
348
369
|
isOpen,
|
|
349
370
|
position = 'bottom',
|
|
350
371
|
align = 'center',
|
|
351
|
-
showArrow = true,
|
|
352
|
-
arrowSize = DEFAULT_ARROW_SIZE,
|
|
353
372
|
offset = 0,
|
|
373
|
+
horizontalOffset = 0,
|
|
354
374
|
}: UsePopoverProps) => {
|
|
355
375
|
const popoverLayoutRef = useRef<LayoutRectangle | null>(null);
|
|
356
376
|
const targetLayoutRef = useRef<LayoutRectangle | null>(null);
|
|
@@ -359,22 +379,20 @@ export const usePopover = ({
|
|
|
359
379
|
|
|
360
380
|
const calculateAndSetPosition = useCallback(() => {
|
|
361
381
|
if (!targetLayoutRef.current || !popoverLayoutRef.current) {
|
|
362
|
-
setCalculatedPosition(popoverDefaultStyles);
|
|
382
|
+
setCalculatedPosition(popoverDefaultStyles);
|
|
363
383
|
return;
|
|
364
384
|
}
|
|
365
385
|
|
|
366
386
|
const { width: screenWidth, height: screenHeight } = Dimensions.get('window');
|
|
367
|
-
const effectiveArrowSize = showArrow ? arrowSize : 0;
|
|
368
387
|
|
|
369
388
|
let { top: initialTop, left: initialLeft } = getInitialPosition(
|
|
370
389
|
position,
|
|
371
390
|
align,
|
|
372
391
|
targetLayoutRef.current,
|
|
373
392
|
popoverLayoutRef.current,
|
|
374
|
-
|
|
393
|
+
0,
|
|
375
394
|
);
|
|
376
395
|
|
|
377
|
-
// Apply offset based on the initial intended position
|
|
378
396
|
switch (position) {
|
|
379
397
|
case 'top':
|
|
380
398
|
initialTop -= offset;
|
|
@@ -390,18 +408,20 @@ export const usePopover = ({
|
|
|
390
408
|
break;
|
|
391
409
|
}
|
|
392
410
|
|
|
411
|
+
initialLeft += horizontalOffset;
|
|
412
|
+
|
|
393
413
|
const { finalTop, finalLeft, finalPosition } = adjustPositionForBoundaries(
|
|
394
|
-
position,
|
|
395
|
-
initialTop,
|
|
396
|
-
initialLeft,
|
|
414
|
+
position,
|
|
415
|
+
initialTop,
|
|
416
|
+
initialLeft,
|
|
397
417
|
targetLayoutRef.current,
|
|
398
418
|
popoverLayoutRef.current,
|
|
399
419
|
screenHeight,
|
|
400
420
|
screenWidth,
|
|
401
|
-
|
|
421
|
+
0,
|
|
402
422
|
);
|
|
403
423
|
|
|
404
|
-
actualPositionRef.current = finalPosition;
|
|
424
|
+
actualPositionRef.current = finalPosition;
|
|
405
425
|
|
|
406
426
|
setCalculatedPosition({
|
|
407
427
|
position: 'absolute',
|
|
@@ -409,7 +429,7 @@ export const usePopover = ({
|
|
|
409
429
|
left: finalLeft,
|
|
410
430
|
opacity: 1,
|
|
411
431
|
});
|
|
412
|
-
}, [position, align,
|
|
432
|
+
}, [position, align, offset, horizontalOffset]);
|
|
413
433
|
|
|
414
434
|
const handlePopoverLayout = useCallback(
|
|
415
435
|
(event: { nativeEvent: { layout: LayoutRectangle } }) => {
|
|
@@ -435,7 +455,7 @@ export const usePopover = ({
|
|
|
435
455
|
calculateAndSetPosition();
|
|
436
456
|
}
|
|
437
457
|
// This effect specifically handles prop changes
|
|
438
|
-
}, [isOpen, position, align,
|
|
458
|
+
}, [isOpen, position, align, calculateAndSetPosition]);
|
|
439
459
|
|
|
440
460
|
// Effect to reset layout refs when popover is closed
|
|
441
461
|
useEffect(() => {
|
|
@@ -1,2 +1,13 @@
|
|
|
1
|
+
import { getRegisteredComponentWithFallback } from '../../core';
|
|
2
|
+
import PopoverDefault, { PopoverArrow, PopoverBackdrop, PopoverTrigger } from './Popover';
|
|
3
|
+
|
|
4
|
+
const PopoverBase = getRegisteredComponentWithFallback('Popover', PopoverDefault);
|
|
5
|
+
|
|
6
|
+
export const Popover = Object.assign(PopoverBase, {
|
|
7
|
+
Trigger: PopoverTrigger,
|
|
8
|
+
Arrow: PopoverArrow,
|
|
9
|
+
Backdrop: PopoverBackdrop,
|
|
10
|
+
});
|
|
11
|
+
|
|
1
12
|
export type { Align, PopoverProps, Position } from './common';
|
|
2
|
-
export {
|
|
13
|
+
export { PopoverContext, PopoverPanelContext } from './common';
|