react-native-molecules 0.5.0-beta.1 → 0.5.0-beta.11
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/README.md +87 -0
- package/components/Accordion/index.tsx +1 -6
- package/components/Accordion/utils.ts +17 -14
- package/components/ActivityIndicator/ActivityIndicator.tsx +12 -20
- package/components/ActivityIndicator/index.tsx +1 -5
- package/components/Appbar/index.tsx +1 -4
- package/components/Appbar/utils.ts +33 -21
- package/components/Avatar/index.tsx +1 -5
- package/components/Avatar/utils.ts +2 -6
- package/components/Backdrop/Backdrop.tsx +2 -2
- package/components/Backdrop/index.tsx +1 -5
- package/components/Backdrop/utils.ts +5 -6
- package/components/Badge/index.tsx +1 -5
- package/components/Badge/utils.ts +2 -6
- package/components/Button/Button.tsx +211 -264
- package/components/Button/index.tsx +9 -7
- package/components/Button/types.ts +16 -2
- package/components/Button/utils.ts +231 -210
- package/components/Card/Card.tsx +8 -4
- package/components/Card/CardContent.tsx +5 -4
- package/components/Card/CardHeader.tsx +5 -3
- package/components/Card/CardMedia.tsx +5 -3
- package/components/Card/CardTypography.tsx +5 -3
- package/components/Card/index.tsx +1 -5
- package/components/Card/utils.ts +5 -6
- package/components/Checkbox/Checkbox.tsx +1 -0
- package/components/Checkbox/CheckboxBase.ios.tsx +1 -0
- package/components/Checkbox/CheckboxBase.tsx +24 -128
- package/components/Checkbox/index.tsx +1 -5
- package/components/Checkbox/utils.ts +6 -31
- package/components/Chip/Chip.tsx +40 -52
- package/components/Chip/index.tsx +1 -5
- package/components/Chip/utils.ts +5 -13
- package/components/DatePickerDocked/index.tsx +1 -5
- package/components/DatePickerDocked/utils.ts +21 -19
- package/components/DatePickerInline/index.tsx +1 -5
- package/components/DatePickerInline/utils.ts +41 -28
- package/components/DatePickerInput/DatePickerInput.tsx +4 -2
- package/components/DatePickerInput/DatePickerInputWithoutModal.tsx +0 -4
- package/components/DatePickerInput/index.tsx +1 -5
- package/components/DatePickerInput/types.ts +1 -3
- package/components/DatePickerInput/utils.ts +5 -6
- package/components/DatePickerModal/CalendarEdit.tsx +10 -9
- package/components/DatePickerModal/DatePickerModalHeader.tsx +1 -1
- package/components/DatePickerModal/index.tsx +1 -5
- package/components/DatePickerModal/utils.ts +17 -16
- package/components/DateTimePicker/index.tsx +1 -5
- package/components/DateTimePicker/utils.ts +5 -6
- package/components/Dialog/index.tsx +1 -5
- package/components/Dialog/utils.ts +22 -16
- package/components/Drawer/Collapsible/utils.ts +13 -13
- package/components/Drawer/Drawer.tsx +2 -3
- package/components/Drawer/DrawerContent.tsx +5 -3
- package/components/Drawer/DrawerFooter.tsx +5 -4
- package/components/Drawer/DrawerHeader.tsx +5 -4
- package/components/Drawer/DrawerItem.tsx +5 -3
- package/components/Drawer/DrawerItemGroup.tsx +5 -4
- package/components/Drawer/index.tsx +1 -5
- package/components/Drawer/utils.ts +7 -7
- package/components/ElementGroup/ElementGroup.tsx +16 -14
- package/components/ElementGroup/index.tsx +1 -5
- package/components/ElementGroup/utils.ts +5 -6
- package/components/FAB/index.tsx +1 -5
- package/components/FAB/utils.ts +2 -6
- package/components/FilePicker/FilePicker.tsx +47 -76
- package/components/FilePicker/index.tsx +1 -5
- package/components/FilePicker/utils.ts +5 -6
- package/components/HelperText/HelperText.tsx +0 -35
- package/components/HelperText/index.tsx +1 -5
- package/components/HelperText/utils.ts +5 -7
- package/components/HorizontalDivider/HorizontalDivider.tsx +5 -3
- package/components/HorizontalDivider/index.tsx +1 -5
- package/components/Icon/CrossFadeIcon.tsx +3 -5
- package/components/Icon/Icon.tsx +2 -4
- package/components/Icon/iconFactory.tsx +3 -3
- package/components/Icon/index.tsx +2 -6
- package/components/Icon/types.ts +17 -6
- package/components/IconButton/IconButton.tsx +45 -58
- package/components/IconButton/index.tsx +1 -5
- package/components/IconButton/utils.ts +153 -49
- package/components/If/index.tsx +1 -5
- package/components/InputAddon/index.tsx +1 -5
- package/components/InputAddon/utils.ts +5 -6
- package/components/Link/index.tsx +1 -5
- package/components/Link/utils.ts +2 -6
- package/components/ListItem/index.tsx +1 -5
- package/components/ListItem/utils.ts +13 -11
- 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/index.tsx +1 -5
- package/components/Menu/utils.ts +6 -8
- package/components/Modal/index.tsx +1 -5
- package/components/Modal/utils.ts +2 -6
- package/components/NavigationRail/NavigationRailHeader.tsx +1 -1
- package/components/NavigationRail/index.tsx +1 -5
- package/components/NavigationRail/utils.ts +21 -17
- package/components/NavigationStack/index.tsx +1 -5
- package/components/NavigationStack/utils.tsx +7 -1
- package/components/Portal/index.tsx +1 -5
- package/components/RadioButton/index.ts +1 -5
- package/components/RadioButton/utils.ts +9 -8
- package/components/Rating/index.tsx +1 -5
- package/components/Rating/utils.ts +6 -8
- package/components/Select/Select.tsx +369 -507
- package/components/Select/index.ts +7 -14
- package/components/Select/types.ts +2 -4
- package/components/Select/utils.ts +215 -0
- package/components/Slot/Slot.tsx +244 -0
- package/components/Slot/compose-refs.tsx +60 -0
- package/components/Slot/index.tsx +8 -0
- package/components/StateLayer/index.tsx +1 -5
- package/components/StateLayer/utils.ts +5 -6
- package/components/Surface/Surface.android.tsx +34 -8
- package/components/Surface/Surface.ios.tsx +36 -29
- package/components/Surface/Surface.tsx +31 -4
- package/components/Surface/index.tsx +1 -5
- package/components/Surface/utils.ts +49 -36
- package/components/Switch/Switch.tsx +8 -2
- package/components/Switch/index.tsx +1 -5
- package/components/Switch/utils.ts +2 -6
- package/components/Tabs/TabItem.tsx +35 -58
- package/components/Tabs/TabLabel.tsx +5 -9
- package/components/Tabs/Tabs.tsx +154 -149
- package/components/Tabs/index.tsx +1 -5
- package/components/Tabs/utils.ts +25 -12
- package/components/Text/Text.tsx +2 -8
- package/components/TextInput/TextInput.tsx +655 -571
- package/components/TextInput/index.tsx +19 -7
- package/components/TextInput/types.ts +76 -27
- package/components/TextInput/utils.ts +232 -159
- package/components/TextInputWithMask/index.tsx +1 -5
- package/components/TimePicker/AmPmSwitcher.tsx +1 -1
- package/components/TimePicker/index.tsx +1 -5
- package/components/TimePicker/utils.ts +29 -21
- package/components/TimePickerField/TimePickerField.tsx +7 -5
- package/components/TimePickerField/index.tsx +1 -5
- package/components/TimePickerField/utils.ts +5 -6
- package/components/TimePickerModal/TimePickerModal.tsx +6 -2
- package/components/TimePickerModal/index.tsx +1 -5
- package/components/TimePickerModal/utils.ts +5 -6
- package/components/Tooltip/TooltipTrigger.tsx +25 -16
- package/components/Tooltip/index.tsx +1 -5
- package/components/Tooltip/utils.ts +5 -6
- package/components/TouchableRipple/TouchableRipple.native.tsx +50 -14
- package/components/TouchableRipple/TouchableRipple.tsx +137 -47
- package/components/TouchableRipple/index.tsx +1 -5
- package/components/TouchableRipple/utils.ts +5 -6
- package/components/VerticalDivider/VerticalDivider.tsx +9 -8
- package/components/VerticalDivider/index.tsx +1 -5
- package/core/componentsRegistry.ts +31 -19
- package/hocs/withPortal.tsx +1 -1
- package/hooks/index.tsx +0 -5
- package/hooks/useControlledValue.tsx +20 -4
- package/hooks/useSubcomponents.tsx +63 -31
- package/hooks/useWhatHasUpdated.tsx +48 -0
- package/package.json +12 -15
- package/shortcuts-manager/ShortcutsManager/ShortcutsManager.tsx +5 -2
- package/styles/shadow.ts +2 -1
- package/styles/themes/LightTheme.tsx +1 -1
- package/utils/extractPropertiesFromStyles.ts +25 -0
- package/utils/lodash.ts +77 -6
- package/utils/repository.ts +2 -52
- package/hooks/useSearchable.tsx +0 -74
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { forwardRef, memo, type ReactNode, useCallback, useMemo, useRef } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
type GestureResponderEvent,
|
|
4
4
|
Pressable,
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
} from 'react-native';
|
|
10
10
|
import { StyleSheet } from 'react-native-unistyles';
|
|
11
11
|
|
|
12
|
+
import { Slot } from '../Slot';
|
|
12
13
|
import { touchableRippleStyles } from './utils';
|
|
13
14
|
|
|
14
15
|
export type Props = PressableProps & {
|
|
@@ -50,6 +51,28 @@ export type Props = PressableProps & {
|
|
|
50
51
|
*/
|
|
51
52
|
children: ReactNode;
|
|
52
53
|
style?: StyleProp<ViewStyle>;
|
|
54
|
+
/**
|
|
55
|
+
* When `true`, the component will not render a wrapper element. Instead, it will
|
|
56
|
+
* merge its props (styles, event handlers, ref) onto its immediate child element.
|
|
57
|
+
* This follows the Radix UI "Slot" pattern for flexible component composition.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```tsx
|
|
61
|
+
* // Without asChild - renders a Pressable wrapper
|
|
62
|
+
* <TouchableRipple onPress={handlePress}>
|
|
63
|
+
* <View><Text>Click me</Text></View>
|
|
64
|
+
* </TouchableRipple>
|
|
65
|
+
*
|
|
66
|
+
* // With asChild - merges props onto the child
|
|
67
|
+
* <TouchableRipple asChild onPress={handlePress}>
|
|
68
|
+
* <Link href="/page"><Text>Navigate</Text></Link>
|
|
69
|
+
* </TouchableRipple>
|
|
70
|
+
* ```
|
|
71
|
+
*
|
|
72
|
+
* @note When `asChild` is `true`, only a single child element is allowed.
|
|
73
|
+
* @default false
|
|
74
|
+
*/
|
|
75
|
+
asChild?: boolean;
|
|
53
76
|
};
|
|
54
77
|
|
|
55
78
|
/**
|
|
@@ -96,12 +119,13 @@ const TouchableRipple = (
|
|
|
96
119
|
onPressIn: onPressInProp,
|
|
97
120
|
onPressOut: onPressOutProp,
|
|
98
121
|
centered,
|
|
122
|
+
asChild = false,
|
|
99
123
|
...rest
|
|
100
124
|
}: Props,
|
|
101
125
|
ref: any,
|
|
102
126
|
) => {
|
|
103
127
|
// TODO - enable ripple onLongPress, need to check for mobile as well
|
|
104
|
-
const disabled = disabledProp
|
|
128
|
+
const disabled = disabledProp;
|
|
105
129
|
|
|
106
130
|
const componentStyles = touchableRippleStyles;
|
|
107
131
|
|
|
@@ -121,29 +145,52 @@ const TouchableRipple = (
|
|
|
121
145
|
};
|
|
122
146
|
}, [borderless, componentStyles.root, rippleColorProp, style]);
|
|
123
147
|
|
|
124
|
-
|
|
148
|
+
// Track whether pointer is currently down for handling pointer leave
|
|
149
|
+
const isPointerDownRef = useRef(false);
|
|
150
|
+
// Store current target element to clean up ripples on pointer up/leave
|
|
151
|
+
const currentTargetRef = useRef<HTMLElement | null>(null);
|
|
152
|
+
|
|
153
|
+
// Using 'any' for event types to support both React DOM PointerEvent and React Native events
|
|
154
|
+
// This is a web-only file, so we primarily handle DOM pointer events
|
|
155
|
+
const handlePointerDown = useCallback(
|
|
125
156
|
(e: any) => {
|
|
126
|
-
onPressInProp?.(e);
|
|
157
|
+
onPressInProp?.(e as GestureResponderEvent);
|
|
127
158
|
|
|
128
159
|
if (disabled) return;
|
|
129
160
|
|
|
130
|
-
|
|
161
|
+
isPointerDownRef.current = true;
|
|
162
|
+
|
|
163
|
+
const button = e.currentTarget as HTMLElement;
|
|
164
|
+
currentTargetRef.current = button;
|
|
131
165
|
const computedStyle = window.getComputedStyle(button);
|
|
132
166
|
const dimensions = button.getBoundingClientRect();
|
|
133
167
|
|
|
134
|
-
let touchX;
|
|
135
|
-
let touchY;
|
|
136
|
-
|
|
137
|
-
const { changedTouches, touches } = e.nativeEvent;
|
|
138
|
-
const touch = touches?.[0] ?? changedTouches?.[0];
|
|
168
|
+
let touchX: number;
|
|
169
|
+
let touchY: number;
|
|
139
170
|
|
|
140
|
-
|
|
141
|
-
|
|
171
|
+
if (centered) {
|
|
172
|
+
// If centered, always position ripple at center
|
|
142
173
|
touchX = dimensions.width / 2;
|
|
143
174
|
touchY = dimensions.height / 2;
|
|
175
|
+
} else if ('clientX' in e && 'clientY' in e) {
|
|
176
|
+
// Web pointer event - calculate position relative to element
|
|
177
|
+
touchX = e.clientX - dimensions.left;
|
|
178
|
+
touchY = e.clientY - dimensions.top;
|
|
179
|
+
} else if (e.nativeEvent) {
|
|
180
|
+
// React Native gesture event
|
|
181
|
+
const { changedTouches, touches } = e.nativeEvent;
|
|
182
|
+
const touch = touches?.[0] ?? changedTouches?.[0];
|
|
183
|
+
if (touch) {
|
|
184
|
+
touchX = touch.locationX ?? dimensions.width / 2;
|
|
185
|
+
touchY = touch.locationY ?? dimensions.height / 2;
|
|
186
|
+
} else {
|
|
187
|
+
touchX = dimensions.width / 2;
|
|
188
|
+
touchY = dimensions.height / 2;
|
|
189
|
+
}
|
|
144
190
|
} else {
|
|
145
|
-
|
|
146
|
-
|
|
191
|
+
// Fallback to center (keyboard activation)
|
|
192
|
+
touchX = dimensions.width / 2;
|
|
193
|
+
touchY = dimensions.height / 2;
|
|
147
194
|
}
|
|
148
195
|
|
|
149
196
|
// Get the size of the button to determine how big the ripple should be
|
|
@@ -156,7 +203,7 @@ const TouchableRipple = (
|
|
|
156
203
|
// Create a container for our ripple effect so we don't need to change the parent's style
|
|
157
204
|
const container = document.createElement('span');
|
|
158
205
|
|
|
159
|
-
container.setAttribute('data-
|
|
206
|
+
container.setAttribute('data-molecules-ripple', '');
|
|
160
207
|
|
|
161
208
|
Object.assign(container.style, {
|
|
162
209
|
position: 'absolute',
|
|
@@ -217,42 +264,86 @@ const TouchableRipple = (
|
|
|
217
264
|
[onPressInProp, disabled, centered, rippleColor],
|
|
218
265
|
);
|
|
219
266
|
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
if (disabled) return;
|
|
225
|
-
|
|
226
|
-
const containers = e.currentTarget.querySelectorAll(
|
|
227
|
-
'[data-paper-ripple]',
|
|
228
|
-
) as HTMLElement[];
|
|
267
|
+
const fadeOutRipples = useCallback((target: HTMLElement) => {
|
|
268
|
+
const containers = target.querySelectorAll(
|
|
269
|
+
'[data-molecules-ripple]',
|
|
270
|
+
) as NodeListOf<HTMLElement>;
|
|
229
271
|
|
|
272
|
+
requestAnimationFrame(() => {
|
|
230
273
|
requestAnimationFrame(() => {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
opacity: 0,
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
// Finally remove the span after the transition
|
|
241
|
-
setTimeout(() => {
|
|
242
|
-
const { parentNode } = container;
|
|
243
|
-
|
|
244
|
-
if (parentNode) {
|
|
245
|
-
parentNode.removeChild(container);
|
|
246
|
-
}
|
|
247
|
-
}, 500);
|
|
274
|
+
containers.forEach(container => {
|
|
275
|
+
const ripple = container.firstChild as HTMLSpanElement;
|
|
276
|
+
|
|
277
|
+
Object.assign(ripple.style, {
|
|
278
|
+
transitionDuration: '250ms',
|
|
279
|
+
opacity: 0,
|
|
248
280
|
});
|
|
281
|
+
|
|
282
|
+
// Finally remove the span after the transition
|
|
283
|
+
setTimeout(() => {
|
|
284
|
+
const { parentNode } = container;
|
|
285
|
+
|
|
286
|
+
if (parentNode) {
|
|
287
|
+
parentNode.removeChild(container);
|
|
288
|
+
}
|
|
289
|
+
}, 500);
|
|
249
290
|
});
|
|
250
291
|
});
|
|
292
|
+
});
|
|
293
|
+
}, []);
|
|
294
|
+
|
|
295
|
+
const handlePointerUp = useCallback(
|
|
296
|
+
(e: any) => {
|
|
297
|
+
onPressOutProp?.(e as GestureResponderEvent);
|
|
298
|
+
|
|
299
|
+
if (disabled || !isPointerDownRef.current) return;
|
|
300
|
+
|
|
301
|
+
isPointerDownRef.current = false;
|
|
302
|
+
currentTargetRef.current = null;
|
|
303
|
+
|
|
304
|
+
const target = e.currentTarget as HTMLElement;
|
|
305
|
+
fadeOutRipples(target);
|
|
251
306
|
},
|
|
252
|
-
[onPressOutProp, disabled],
|
|
307
|
+
[onPressOutProp, disabled, fadeOutRipples],
|
|
253
308
|
);
|
|
254
309
|
|
|
255
|
-
const
|
|
310
|
+
const handlePointerLeave = useCallback(
|
|
311
|
+
(e: any) => {
|
|
312
|
+
// Only fade out if pointer was down (dragging out of element)
|
|
313
|
+
if (disabled || !isPointerDownRef.current) return;
|
|
314
|
+
|
|
315
|
+
isPointerDownRef.current = false;
|
|
316
|
+
currentTargetRef.current = null;
|
|
317
|
+
|
|
318
|
+
const target = e.currentTarget as HTMLElement;
|
|
319
|
+
fadeOutRipples(target);
|
|
320
|
+
},
|
|
321
|
+
[disabled, fadeOutRipples],
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
const handlePointerCancel = useCallback(
|
|
325
|
+
(e: any) => {
|
|
326
|
+
if (disabled || !isPointerDownRef.current) return;
|
|
327
|
+
|
|
328
|
+
isPointerDownRef.current = false;
|
|
329
|
+
currentTargetRef.current = null;
|
|
330
|
+
|
|
331
|
+
const target = e.currentTarget as HTMLElement;
|
|
332
|
+
fadeOutRipples(target);
|
|
333
|
+
},
|
|
334
|
+
[disabled, fadeOutRipples],
|
|
335
|
+
);
|
|
336
|
+
|
|
337
|
+
const Component = asChild ? Slot : onPress ? Pressable : View;
|
|
338
|
+
|
|
339
|
+
// Use pointer events for universal compatibility (works on any HTML element)
|
|
340
|
+
// These events work with mouse, touch, and stylus inputs
|
|
341
|
+
const pointerEventProps = {
|
|
342
|
+
onPointerDown: handlePointerDown,
|
|
343
|
+
onPointerUp: handlePointerUp,
|
|
344
|
+
onPointerLeave: handlePointerLeave,
|
|
345
|
+
onPointerCancel: handlePointerCancel,
|
|
346
|
+
};
|
|
256
347
|
|
|
257
348
|
return (
|
|
258
349
|
<Component
|
|
@@ -261,10 +352,9 @@ const TouchableRipple = (
|
|
|
261
352
|
style={containerStyle}
|
|
262
353
|
ref={ref}
|
|
263
354
|
onPress={onPress}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
{Children.only(children)}
|
|
355
|
+
disabled={disabled}
|
|
356
|
+
{...pointerEventProps}>
|
|
357
|
+
{children}
|
|
268
358
|
</Component>
|
|
269
359
|
);
|
|
270
360
|
};
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
import { getRegisteredComponentWithFallback
|
|
1
|
+
import { getRegisteredComponentWithFallback } from '../../core';
|
|
2
2
|
import TouchableRippleDefault from './TouchableRipple';
|
|
3
3
|
|
|
4
|
-
registerMoleculesComponents({
|
|
5
|
-
TouchableRipple: TouchableRippleDefault,
|
|
6
|
-
});
|
|
7
|
-
|
|
8
4
|
export const TouchableRipple = getRegisteredComponentWithFallback(
|
|
9
5
|
'TouchableRipple',
|
|
10
6
|
TouchableRippleDefault,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { StyleSheet } from 'react-native-unistyles';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { getRegisteredComponentStylesWithFallback } from '../../core';
|
|
4
4
|
|
|
5
5
|
const touchableRippleStylesDefault = StyleSheet.create(theme => ({
|
|
6
6
|
root: {
|
|
@@ -8,8 +8,7 @@ const touchableRippleStylesDefault = StyleSheet.create(theme => ({
|
|
|
8
8
|
} as any,
|
|
9
9
|
}));
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
TouchableRipple
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
export const touchableRippleStyles = getRegisteredMoleculesComponentStyles('TouchableRipple');
|
|
11
|
+
export const touchableRippleStyles = getRegisteredComponentStylesWithFallback(
|
|
12
|
+
'TouchableRipple',
|
|
13
|
+
touchableRippleStylesDefault,
|
|
14
|
+
);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { memo } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { View, type ViewProps } from 'react-native';
|
|
3
3
|
import { StyleSheet } from 'react-native-unistyles';
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { getRegisteredComponentStylesWithFallback } from '../../core';
|
|
6
6
|
|
|
7
7
|
export type Props = Omit<ViewProps, 'children'> & {
|
|
8
8
|
/**
|
|
@@ -21,7 +21,6 @@ export type Props = Omit<ViewProps, 'children'> & {
|
|
|
21
21
|
* Horizontal spacing of the Divider
|
|
22
22
|
*/
|
|
23
23
|
spacing?: number;
|
|
24
|
-
style?: StyleProp<ViewStyle>;
|
|
25
24
|
};
|
|
26
25
|
|
|
27
26
|
/**
|
|
@@ -70,9 +69,9 @@ const VerticalDivider = ({
|
|
|
70
69
|
style={[
|
|
71
70
|
verticalDividerStyles.root,
|
|
72
71
|
style,
|
|
73
|
-
topInset
|
|
74
|
-
bottomInset
|
|
75
|
-
spacing
|
|
72
|
+
topInset ? { marginTop: topInset } : undefined,
|
|
73
|
+
bottomInset ? { marginBottom: bottomInset } : undefined,
|
|
74
|
+
spacing ? { marginHorizontal: spacing } : undefined,
|
|
76
75
|
]}
|
|
77
76
|
/>
|
|
78
77
|
);
|
|
@@ -93,7 +92,9 @@ export const verticalDividerStylesDefault = StyleSheet.create(theme => ({
|
|
|
93
92
|
},
|
|
94
93
|
}));
|
|
95
94
|
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
export const verticalDividerStyles = getRegisteredComponentStylesWithFallback(
|
|
96
|
+
'VerticalDivider',
|
|
97
|
+
verticalDividerStylesDefault,
|
|
98
|
+
);
|
|
98
99
|
|
|
99
100
|
export default memo(VerticalDivider);
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
import { getRegisteredComponentWithFallback
|
|
1
|
+
import { getRegisteredComponentWithFallback } from '../../core';
|
|
2
2
|
import VerticalDividerDefault from './VerticalDivider';
|
|
3
3
|
|
|
4
|
-
registerMoleculesComponents({
|
|
5
|
-
VerticalDivider: VerticalDividerDefault,
|
|
6
|
-
});
|
|
7
|
-
|
|
8
4
|
export const VerticalDivider = getRegisteredComponentWithFallback(
|
|
9
5
|
'VerticalDivider',
|
|
10
6
|
VerticalDividerDefault,
|
|
@@ -1,18 +1,13 @@
|
|
|
1
|
-
import EventEmitter, {
|
|
2
|
-
type ConstructorOptions,
|
|
3
|
-
type event as Event,
|
|
4
|
-
type eventNS,
|
|
5
|
-
} from 'eventemitter2';
|
|
6
1
|
import type { ComponentType } from 'react';
|
|
7
2
|
|
|
8
|
-
interface RepositoryConstructor<T>
|
|
3
|
+
interface RepositoryConstructor<T> {
|
|
9
4
|
onRegister?: (arg: T, name: string, registery: Record<string, T>) => T;
|
|
10
5
|
name?: string;
|
|
11
6
|
}
|
|
12
7
|
|
|
13
8
|
let id = Date.now();
|
|
14
9
|
|
|
15
|
-
export class Repository<T>
|
|
10
|
+
export class Repository<T> {
|
|
16
11
|
private registry: Record<string, T> = {};
|
|
17
12
|
readonly #name!: string;
|
|
18
13
|
|
|
@@ -29,9 +24,7 @@ export class Repository<T> extends EventEmitter {
|
|
|
29
24
|
constructor({
|
|
30
25
|
onRegister = arg => arg,
|
|
31
26
|
name = Repository.uniqueId,
|
|
32
|
-
...options
|
|
33
27
|
}: RepositoryConstructor<T> = {}) {
|
|
34
|
-
super(options);
|
|
35
28
|
this.#onRegister = onRegister;
|
|
36
29
|
this.#name = name;
|
|
37
30
|
}
|
|
@@ -40,11 +33,6 @@ export class Repository<T> extends EventEmitter {
|
|
|
40
33
|
return !!this.registry[itemName];
|
|
41
34
|
};
|
|
42
35
|
|
|
43
|
-
emit(event: eventNS | Event, ...values: any[]) {
|
|
44
|
-
event = typeof event === 'string' ? `${this.#name}::event` : event;
|
|
45
|
-
return super.emit(event, ...values);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
36
|
/**
|
|
49
37
|
* Register a item with the src.
|
|
50
38
|
*/
|
|
@@ -58,8 +46,6 @@ export class Repository<T> extends EventEmitter {
|
|
|
58
46
|
...this.registry,
|
|
59
47
|
[itemName]: updatedItem,
|
|
60
48
|
};
|
|
61
|
-
|
|
62
|
-
this.emit('item_registered', itemName);
|
|
63
49
|
};
|
|
64
50
|
|
|
65
51
|
/**
|
|
@@ -92,17 +78,14 @@ export class Repository<T> extends EventEmitter {
|
|
|
92
78
|
|
|
93
79
|
export const componentsRepository = new Repository<Record<string, any>>({
|
|
94
80
|
name: 'Components_Repository',
|
|
95
|
-
maxListeners: 0,
|
|
96
81
|
});
|
|
97
82
|
|
|
98
83
|
export const componentsStylesRepository = new Repository<Record<string, any>>({
|
|
99
84
|
name: 'Components_Styles_Repository',
|
|
100
|
-
maxListeners: 0,
|
|
101
85
|
});
|
|
102
86
|
|
|
103
87
|
export const componentsUtilsRepository = new Repository<Record<string, any>>({
|
|
104
88
|
name: 'Components_Utils_Repository',
|
|
105
|
-
maxListeners: 0,
|
|
106
89
|
});
|
|
107
90
|
|
|
108
91
|
export const registerMoleculesComponent = componentsRepository.registerOne;
|
|
@@ -162,3 +145,32 @@ export function getRegisteredComponentWithFallback<T extends ComponentType<any>>
|
|
|
162
145
|
): T {
|
|
163
146
|
return (getRegisteredMoleculesComponent(name) ?? defaultComponent) as T;
|
|
164
147
|
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Gets a registered component with a fallback to the default component
|
|
151
|
+
* @param name The name of the component to retrieve
|
|
152
|
+
* @param defaultStyles The default styles to use as fallback
|
|
153
|
+
* @returns The registered styles or the default styles
|
|
154
|
+
*/
|
|
155
|
+
export function getRegisteredComponentStylesWithFallback<T extends unknown>(
|
|
156
|
+
name: string,
|
|
157
|
+
defaultStyles: T,
|
|
158
|
+
): T {
|
|
159
|
+
return (getRegisteredMoleculesComponentStyles(name) ?? defaultStyles) as T;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Gets a registered component with a fallback to the default component
|
|
164
|
+
* @param name The name of the component to retrieve
|
|
165
|
+
* @param defaultUtils The default utils to use as fallback
|
|
166
|
+
* @returns The registered utils or the default utils
|
|
167
|
+
*/
|
|
168
|
+
export function getRegisteredComponentUtilsWithFallback<T extends unknown>(
|
|
169
|
+
name: string,
|
|
170
|
+
defaultUtils: T,
|
|
171
|
+
key?: string,
|
|
172
|
+
): T {
|
|
173
|
+
return key
|
|
174
|
+
? (((getRegisteredComponentUtils(name) as Record<string, any>)?.[key] ?? defaultUtils) as T)
|
|
175
|
+
: ((getRegisteredComponentUtils(name) ?? defaultUtils) as T);
|
|
176
|
+
}
|
package/hocs/withPortal.tsx
CHANGED
package/hooks/index.tsx
CHANGED
|
@@ -18,11 +18,6 @@ export { useMediaQuery } from './useMediaQuery';
|
|
|
18
18
|
export { useMergedRefs } from './useMergedRefs';
|
|
19
19
|
export { default as usePrevious } from './usePrevious';
|
|
20
20
|
export * from './useQueryFilter';
|
|
21
|
-
export {
|
|
22
|
-
default as useSearchable,
|
|
23
|
-
type UseSearchableProps,
|
|
24
|
-
useSearchInputProps,
|
|
25
|
-
} from './useSearchable';
|
|
26
21
|
export { default as useSubcomponents, type UseSubcomponentsProps } from './useSubcomponents';
|
|
27
22
|
export * from './useTheme';
|
|
28
23
|
export { default as useToggle } from './useToggle';
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
2
|
|
|
3
|
+
import useLatest from './useLatest';
|
|
4
|
+
|
|
3
5
|
type ReturnType<T> = [T, (value: T, ...args: any[]) => void];
|
|
4
6
|
|
|
5
7
|
type Args<T> = {
|
|
@@ -31,21 +33,35 @@ const useControlledValue = <T,>({
|
|
|
31
33
|
|
|
32
34
|
const isUncontrolled = useRef(valueProp).current === undefined;
|
|
33
35
|
const [uncontrolledValue, setValue] = useState(value);
|
|
36
|
+
const valuePropRef = useLatest(valueProp);
|
|
37
|
+
const onChangeRef = useLatest(onChange);
|
|
38
|
+
const manipulateValueRef = useLatest(manipulateValue);
|
|
39
|
+
const uncontrolledValueRef = useLatest(uncontrolledValue);
|
|
34
40
|
|
|
35
41
|
const updateValue = useCallback(
|
|
36
42
|
(val: T, ...rest: any[]) => {
|
|
37
43
|
if (disabled) return;
|
|
38
44
|
|
|
39
45
|
if (isUncontrolled) {
|
|
40
|
-
setValue(
|
|
46
|
+
setValue(manipulateValueRef.current(val, uncontrolledValueRef.current));
|
|
41
47
|
}
|
|
42
48
|
|
|
43
|
-
|
|
44
|
-
|
|
49
|
+
onChangeRef.current?.(
|
|
50
|
+
manipulateValueRef.current(
|
|
51
|
+
val,
|
|
52
|
+
isUncontrolled ? uncontrolledValueRef.current : valuePropRef.current,
|
|
53
|
+
),
|
|
45
54
|
...rest,
|
|
46
55
|
);
|
|
47
56
|
},
|
|
48
|
-
[
|
|
57
|
+
[
|
|
58
|
+
disabled,
|
|
59
|
+
isUncontrolled,
|
|
60
|
+
manipulateValueRef,
|
|
61
|
+
onChangeRef,
|
|
62
|
+
uncontrolledValueRef,
|
|
63
|
+
valuePropRef,
|
|
64
|
+
],
|
|
49
65
|
);
|
|
50
66
|
|
|
51
67
|
useEffect(() => {
|
|
@@ -1,14 +1,23 @@
|
|
|
1
|
-
import type { ReactElement } from 'react';
|
|
1
|
+
import type { ReactElement, ReactNode } from 'react';
|
|
2
2
|
import { Children, type FC, isValidElement, useMemo } from 'react';
|
|
3
3
|
|
|
4
4
|
export type UseSubcomponentsProps<T extends string> = {
|
|
5
|
-
children:
|
|
5
|
+
children: ReactNode;
|
|
6
6
|
/**
|
|
7
7
|
* array of displayName as string
|
|
8
8
|
* */
|
|
9
|
-
allowedChildren: T[];
|
|
9
|
+
allowedChildren: (T | { name: T; allowMultiple?: boolean })[];
|
|
10
|
+
/**
|
|
11
|
+
* If true, also returns the remaining children that don't match any of the allowedChildren
|
|
12
|
+
* in a `rest` property
|
|
13
|
+
*/
|
|
14
|
+
includeRest?: boolean;
|
|
10
15
|
};
|
|
11
16
|
|
|
17
|
+
export type UseSubcomponentsResult<T extends string, IncludeRest extends boolean = false> = {
|
|
18
|
+
[key in T]: ReactElement[];
|
|
19
|
+
} & (IncludeRest extends true ? { rest: ReactNode[] } : {});
|
|
20
|
+
|
|
12
21
|
/**
|
|
13
22
|
* This will return an object with the displayNames as the property names
|
|
14
23
|
* eg. allowedChildren: ['Drawer_Header', 'Drawer_Content', 'Drawer_Footer', 'DrawerItem'];
|
|
@@ -19,41 +28,64 @@ export type UseSubcomponentsProps<T extends string> = {
|
|
|
19
28
|
* Drawer_Footer: [],
|
|
20
29
|
* DrawerItem: [],
|
|
21
30
|
* }
|
|
31
|
+
*
|
|
32
|
+
* If includeRest is true, also returns:
|
|
33
|
+
* {
|
|
34
|
+
* ...above,
|
|
35
|
+
* rest: [remaining children that don't match allowedChildren]
|
|
36
|
+
* }
|
|
22
37
|
* */
|
|
23
|
-
|
|
38
|
+
function useSubcomponents<T extends string = string, IncludeRest extends boolean = false>({
|
|
24
39
|
children,
|
|
25
40
|
allowedChildren,
|
|
26
|
-
|
|
41
|
+
includeRest,
|
|
42
|
+
}: UseSubcomponentsProps<T> & { includeRest?: IncludeRest }): UseSubcomponentsResult<
|
|
43
|
+
T,
|
|
44
|
+
IncludeRest
|
|
45
|
+
> {
|
|
27
46
|
return useMemo(() => {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
) {
|
|
44
|
-
|
|
47
|
+
const configs = allowedChildren.map(entry =>
|
|
48
|
+
typeof entry === 'string'
|
|
49
|
+
? { name: entry, allowMultiple: true as boolean }
|
|
50
|
+
: { name: entry.name, allowMultiple: entry.allowMultiple ?? true },
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const nameSet = new Set(configs.map(c => c.name));
|
|
54
|
+
const allowMultipleMap = new Map(configs.map(c => [c.name, c.allowMultiple]));
|
|
55
|
+
|
|
56
|
+
const result = configs.reduce((acc, { name }) => {
|
|
57
|
+
(acc as any)[name] = [];
|
|
58
|
+
return acc;
|
|
59
|
+
}, (includeRest ? { rest: [] } : {}) as UseSubcomponentsResult<T, IncludeRest>);
|
|
60
|
+
|
|
61
|
+
Children.forEach(children, child => {
|
|
62
|
+
if (!isValidElement(child)) {
|
|
63
|
+
if (includeRest) {
|
|
64
|
+
(result as any).rest.push(child);
|
|
65
|
+
}
|
|
66
|
+
return;
|
|
45
67
|
}
|
|
46
68
|
|
|
47
|
-
const
|
|
69
|
+
const displayName = (child.type as FC)?.displayName as T | undefined;
|
|
48
70
|
|
|
49
|
-
if (
|
|
71
|
+
if (displayName && nameSet.has(displayName)) {
|
|
72
|
+
if (allowMultipleMap.get(displayName)) {
|
|
73
|
+
(result as any)[displayName].push(child);
|
|
74
|
+
} else {
|
|
75
|
+
// Only keep the last matching child
|
|
76
|
+
(result as any)[displayName] = [child];
|
|
77
|
+
}
|
|
50
78
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
};
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (includeRest) {
|
|
83
|
+
(result as any).rest.push(child);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
return result;
|
|
88
|
+
}, [allowedChildren, children, includeRest]);
|
|
89
|
+
}
|
|
58
90
|
|
|
59
91
|
export default useSubcomponents;
|