react-native-molecules 0.5.0-beta.1 → 0.5.0-beta.10
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 +1 -0
- package/components/Checkbox/index.tsx +1 -5
- package/components/Checkbox/utils.ts +6 -6
- 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/index.tsx +1 -5
- package/components/DatePickerInput/utils.ts +5 -6
- 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/index.tsx +1 -5
- package/components/FilePicker/utils.ts +5 -6
- 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 +15 -26
- 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 +360 -501
- 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/index.tsx +1 -5
- package/components/Tabs/utils.ts +10 -10
- package/components/Text/Text.tsx +2 -8
- package/components/TextInput/TextInput.tsx +5 -4
- package/components/TextInput/index.tsx +1 -5
- package/components/TextInput/utils.ts +8 -15
- 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/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 +49 -13
- package/components/TouchableRipple/TouchableRipple.tsx +136 -46
- 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/useControlledValue.tsx +20 -4
- package/hooks/useSubcomponents.tsx +56 -22
- package/hooks/useWhatHasUpdated.tsx +48 -0
- package/package.json +10 -13
- 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
|
@@ -103,8 +103,12 @@ export function TimePickerModal({
|
|
|
103
103
|
accessibilityLabel="toggle keyboard"
|
|
104
104
|
/>
|
|
105
105
|
<View style={styles.fill} />
|
|
106
|
-
<Button onPress={onClose}>
|
|
107
|
-
|
|
106
|
+
<Button onPress={onClose}>
|
|
107
|
+
<Button.Text>{cancelLabel}</Button.Text>
|
|
108
|
+
</Button>
|
|
109
|
+
<Button onPress={onConfirm}>
|
|
110
|
+
<Button.Text>{confirmLabel}</Button.Text>
|
|
111
|
+
</Button>
|
|
108
112
|
</View>
|
|
109
113
|
</KeyboardAvoidingView>
|
|
110
114
|
</Modal>
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
import { getRegisteredComponentWithFallback
|
|
1
|
+
import { getRegisteredComponentWithFallback } from '../../core';
|
|
2
2
|
import TimePickerModalDefault from './TimePickerModal';
|
|
3
3
|
|
|
4
|
-
registerMoleculesComponents({
|
|
5
|
-
TimePickerModal: TimePickerModalDefault,
|
|
6
|
-
});
|
|
7
|
-
|
|
8
4
|
export const TimePickerModal = getRegisteredComponentWithFallback(
|
|
9
5
|
'TimePickerModal',
|
|
10
6
|
TimePickerModalDefault,
|
|
@@ -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 timepickerModalStylesDefault = StyleSheet.create(theme => ({
|
|
6
6
|
keyboardView: {
|
|
@@ -41,8 +41,7 @@ const timepickerModalStylesDefault = StyleSheet.create(theme => ({
|
|
|
41
41
|
fill: { flex: 1 },
|
|
42
42
|
}));
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
TimePickerModal
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
export const styles = getRegisteredMoleculesComponentStyles('TimePickerModal');
|
|
44
|
+
export const styles = getRegisteredComponentStylesWithFallback(
|
|
45
|
+
'TimePickerModal',
|
|
46
|
+
timepickerModalStylesDefault,
|
|
47
|
+
);
|
|
@@ -30,27 +30,36 @@ const TooltipTrigger = memo(({ children }: { children: ReactElement }) => {
|
|
|
30
30
|
() => triggerRef?.current,
|
|
31
31
|
);
|
|
32
32
|
|
|
33
|
-
const onPress = useCallback(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
const onPress = useCallback(
|
|
34
|
+
(e: unknown) => {
|
|
35
|
+
// @ts-ignore
|
|
36
|
+
children?.props?.onPress?.(e);
|
|
37
|
+
},
|
|
38
|
+
[children?.props],
|
|
39
|
+
);
|
|
37
40
|
|
|
38
|
-
const onLongPress = useCallback(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
const onLongPress = useCallback(
|
|
42
|
+
(e: unknown) => {
|
|
43
|
+
// @ts-ignore
|
|
44
|
+
children?.props?.onLongPress?.(e);
|
|
41
45
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
if (isWeb) return;
|
|
47
|
+
onOpen();
|
|
48
|
+
},
|
|
49
|
+
[children?.props, isWeb, onOpen],
|
|
50
|
+
);
|
|
45
51
|
|
|
46
|
-
const onPressOut = useCallback(
|
|
47
|
-
|
|
52
|
+
const onPressOut = useCallback(
|
|
53
|
+
(e: unknown) => {
|
|
54
|
+
// @ts-ignore
|
|
48
55
|
|
|
49
|
-
|
|
56
|
+
children?.props?.onPressOut?.(e);
|
|
50
57
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
58
|
+
if (isWeb) return;
|
|
59
|
+
onClose();
|
|
60
|
+
},
|
|
61
|
+
[children?.props, isWeb, onClose],
|
|
62
|
+
);
|
|
54
63
|
|
|
55
64
|
const onHoverIn = useCallback(() => {
|
|
56
65
|
// @ts-ignore
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getRegisteredComponentWithFallback
|
|
1
|
+
import { getRegisteredComponentWithFallback } from '../../core';
|
|
2
2
|
import TooltipComponent from './Tooltip';
|
|
3
3
|
import TooltipContent from './TooltipContent';
|
|
4
4
|
import TooltipTrigger from './TooltipTrigger';
|
|
@@ -8,10 +8,6 @@ export const TooltipDefault = Object.assign(TooltipComponent, {
|
|
|
8
8
|
Content: TooltipContent,
|
|
9
9
|
});
|
|
10
10
|
|
|
11
|
-
registerMoleculesComponents({
|
|
12
|
-
Tooltip: TooltipDefault,
|
|
13
|
-
});
|
|
14
|
-
|
|
15
11
|
export const Tooltip = getRegisteredComponentWithFallback('Tooltip', TooltipDefault);
|
|
16
12
|
|
|
17
13
|
export type { Props as TooltipProps } from './Tooltip';
|
|
@@ -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 tooltipStylesDefault = StyleSheet.create(theme => ({
|
|
6
6
|
content: {
|
|
@@ -14,8 +14,7 @@ const tooltipStylesDefault = StyleSheet.create(theme => ({
|
|
|
14
14
|
},
|
|
15
15
|
}));
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
Tooltip
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
export const tooltipStyles = getRegisteredMoleculesComponentStyles('Tooltip');
|
|
17
|
+
export const tooltipStyles = getRegisteredComponentStylesWithFallback(
|
|
18
|
+
'Tooltip',
|
|
19
|
+
tooltipStylesDefault,
|
|
20
|
+
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { type ComponentProps, forwardRef, memo, type ReactNode, useMemo } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
type BackgroundPropType,
|
|
4
4
|
Platform,
|
|
@@ -7,25 +7,44 @@ import {
|
|
|
7
7
|
StyleSheet,
|
|
8
8
|
TouchableNativeFeedback,
|
|
9
9
|
TouchableWithoutFeedback,
|
|
10
|
-
View,
|
|
11
10
|
type ViewStyle,
|
|
12
11
|
} from 'react-native';
|
|
13
|
-
import { withUnistyles } from 'react-native-unistyles';
|
|
14
12
|
|
|
13
|
+
import { extractPropertiesFromStyles } from '../../utils/extractPropertiesFromStyles';
|
|
14
|
+
import { Slot } from '../Slot';
|
|
15
15
|
import { touchableRippleStyles } from './utils';
|
|
16
16
|
|
|
17
17
|
const ANDROID_VERSION_LOLLIPOP = 21;
|
|
18
18
|
const ANDROID_VERSION_PIE = 28;
|
|
19
19
|
|
|
20
|
-
type Props =
|
|
20
|
+
type Props = ComponentProps<typeof TouchableWithoutFeedback> & {
|
|
21
21
|
borderless?: boolean;
|
|
22
22
|
background?: BackgroundPropType;
|
|
23
23
|
disabled?: boolean;
|
|
24
24
|
onPress?: () => void | null;
|
|
25
25
|
rippleColor?: string;
|
|
26
26
|
underlayColor?: string;
|
|
27
|
-
children:
|
|
27
|
+
children: ReactNode;
|
|
28
28
|
style?: StyleProp<ViewStyle>;
|
|
29
|
+
/**
|
|
30
|
+
* When `true`, the component will not render a wrapper element. Instead, it will
|
|
31
|
+
* merge its props (styles, event handlers, ref) onto its immediate child element.
|
|
32
|
+
* This follows the Radix UI "Slot" pattern for flexible component composition.
|
|
33
|
+
*
|
|
34
|
+
* @note On Android, the native ripple effect will NOT work when `asChild` is `true`
|
|
35
|
+
* because `TouchableNativeFeedback` requires a View wrapper. Only press events will work.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```tsx
|
|
39
|
+
* <TouchableRipple asChild onPress={handlePress}>
|
|
40
|
+
* <View><Text>Custom pressable</Text></View>
|
|
41
|
+
* </TouchableRipple>
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* @note When `asChild` is `true`, only a single child element is allowed.
|
|
45
|
+
* @default false
|
|
46
|
+
*/
|
|
47
|
+
asChild?: boolean;
|
|
29
48
|
};
|
|
30
49
|
|
|
31
50
|
const TouchableRipple = (
|
|
@@ -37,6 +56,7 @@ const TouchableRipple = (
|
|
|
37
56
|
rippleColor: rippleColorProp,
|
|
38
57
|
underlayColor: underlayColorProp,
|
|
39
58
|
children,
|
|
59
|
+
asChild = false,
|
|
40
60
|
...rest
|
|
41
61
|
}: Props,
|
|
42
62
|
ref: any,
|
|
@@ -46,8 +66,12 @@ const TouchableRipple = (
|
|
|
46
66
|
const componentStyles = touchableRippleStyles;
|
|
47
67
|
|
|
48
68
|
const { rippleColor, underlayColor, containerStyle } = useMemo(() => {
|
|
69
|
+
const { rippleColor: _rippleColor } = extractPropertiesFromStyles(
|
|
70
|
+
[componentStyles.root, style],
|
|
71
|
+
['rippleColor'],
|
|
72
|
+
);
|
|
49
73
|
return {
|
|
50
|
-
rippleColor: rippleColorProp,
|
|
74
|
+
rippleColor: rippleColorProp || _rippleColor,
|
|
51
75
|
underlayColor: underlayColorProp || rippleColorProp,
|
|
52
76
|
containerStyle: [borderless && styles.borderless, componentStyles.root, style],
|
|
53
77
|
};
|
|
@@ -58,6 +82,21 @@ const TouchableRipple = (
|
|
|
58
82
|
const useForeground =
|
|
59
83
|
Platform.OS === 'android' && Platform.Version >= ANDROID_VERSION_PIE && borderless;
|
|
60
84
|
|
|
85
|
+
if (asChild) {
|
|
86
|
+
// When asChild is true, use Slot to merge props with the child
|
|
87
|
+
// Note: TouchableNativeFeedback ripple won't work with asChild since it requires a View wrapper
|
|
88
|
+
return (
|
|
89
|
+
<Slot
|
|
90
|
+
{...rest}
|
|
91
|
+
style={containerStyle}
|
|
92
|
+
ref={ref}
|
|
93
|
+
onPress={rest.onPress}
|
|
94
|
+
disabled={disabled}>
|
|
95
|
+
{children}
|
|
96
|
+
</Slot>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
61
100
|
if (TouchableRipple.supported) {
|
|
62
101
|
return (
|
|
63
102
|
<TouchableNativeFeedback
|
|
@@ -65,12 +104,13 @@ const TouchableRipple = (
|
|
|
65
104
|
ref={ref}
|
|
66
105
|
disabled={disabled}
|
|
67
106
|
useForeground={useForeground}
|
|
107
|
+
style={containerStyle}
|
|
68
108
|
background={
|
|
69
109
|
background != null
|
|
70
110
|
? background
|
|
71
111
|
: TouchableNativeFeedback.Ripple(rippleColor!, borderless)
|
|
72
112
|
}>
|
|
73
|
-
|
|
113
|
+
<>{children}</>
|
|
74
114
|
</TouchableNativeFeedback>
|
|
75
115
|
);
|
|
76
116
|
}
|
|
@@ -84,7 +124,7 @@ const TouchableRipple = (
|
|
|
84
124
|
containerStyle,
|
|
85
125
|
pressed && { backgroundColor: underlayColor },
|
|
86
126
|
]}>
|
|
87
|
-
{
|
|
127
|
+
{children}
|
|
88
128
|
</Pressable>
|
|
89
129
|
);
|
|
90
130
|
};
|
|
@@ -98,8 +138,4 @@ const styles = StyleSheet.create({
|
|
|
98
138
|
TouchableRipple.supported =
|
|
99
139
|
Platform.OS === 'android' && Platform.Version >= ANDROID_VERSION_LOLLIPOP;
|
|
100
140
|
|
|
101
|
-
export default memo(
|
|
102
|
-
withUnistyles(forwardRef(TouchableRipple), theme => ({
|
|
103
|
-
rippleColor: theme.colors.onSurfaceRipple,
|
|
104
|
-
})),
|
|
105
|
-
);
|
|
141
|
+
export default memo(forwardRef(TouchableRipple));
|
|
@@ -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,6 +119,7 @@ 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,
|
|
@@ -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,
|