react-native-molecules 0.5.0-beta.22 → 0.5.0-beta.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/Accordion/Accordion.tsx +1 -1
- package/components/Accordion/AccordionItem.tsx +1 -1
- package/components/Button/Button.tsx +3 -1
- package/components/Checkbox/Checkbox.tsx +2 -1
- package/components/DateField/useDateFieldState.ts +2 -2
- package/components/DatePicker/DatePickerProvider.tsx +1 -1
- package/components/DatePicker/utils.ts +2 -0
- package/components/DatePickerInline/DatePickerInline.tsx +1 -1
- package/components/DatePickerInline/DatePickerInlineBase.tsx +1 -1
- package/components/DatePickerInline/Day.tsx +1 -1
- package/components/DatePickerInline/MonthPicker.tsx +24 -40
- package/components/DatePickerInline/Swiper.tsx +1 -1
- package/components/DatePickerInline/SwiperUtils.ts +1 -1
- package/components/DatePickerInline/YearPicker.tsx +44 -79
- package/components/DatePickerInline/dateUtils.tsx +1 -1
- package/components/DatePickerInline/store.tsx +2 -1
- package/components/Divider/index.tsx +2 -3
- package/components/ElementGroup/ElementGroup.tsx +1 -1
- package/components/FilePicker/FilePicker.tsx +1 -1
- package/components/Icon/iconFactory.tsx +2 -1
- package/components/IconButton/IconButton.tsx +39 -13
- package/components/IconButton/index.tsx +1 -0
- package/components/IconButton/types.ts +2 -0
- package/components/List/List.tsx +156 -387
- package/components/List/context.tsx +4 -5
- package/components/List/index.ts +0 -1
- package/components/List/types.ts +77 -109
- package/components/List/utils.ts +4 -37
- package/components/Menu/Menu.tsx +13 -30
- package/components/Menu/index.tsx +0 -2
- package/components/Popover/Popover.tsx +7 -10
- package/components/Popover/PopoverRoot.tsx +6 -20
- package/components/Popover/common.ts +4 -0
- package/components/Popover/index.ts +2 -8
- package/components/Popover/usePlatformMeasure.ts +4 -2
- package/components/Portal/Portal.tsx +1 -2
- package/components/RadioButton/RadioButtonGroup.tsx +1 -2
- package/components/Rating/Rating.tsx +1 -1
- package/components/Select/Select.tsx +304 -71
- package/components/Select/context.tsx +30 -3
- package/components/Select/index.ts +20 -2
- package/components/Select/types.ts +43 -25
- package/components/Select/utils.ts +18 -4
- package/components/Switch/Switch.ios.tsx +1 -1
- package/components/Switch/Switch.tsx +2 -1
- package/components/Tabs/Tabs.tsx +2 -2
- package/components/TextInput/TextInput.tsx +4 -3
- package/components/TimePicker/AnalogClock.tsx +1 -1
- package/components/TimePicker/TimeInputs.tsx +1 -1
- package/components/TimePicker/TimePicker.tsx +1 -1
- package/components/TimePicker/TimePickerModal.tsx +1 -1
- package/components/Tooltip/Tooltip.tsx +1 -1
- package/components/TouchableRipple/TouchableRipple.tsx +76 -152
- package/hocs/index.tsx +1 -1
- package/hocs/withKeyboardAccessibility.tsx +2 -3
- package/hooks/index.tsx +2 -6
- package/hooks/useContrastColor.ts +1 -2
- package/hooks/useFilePicker.tsx +1 -1
- package/hooks/useHandleNumberFormat.tsx +2 -2
- package/hooks/useMediaQuery.tsx +1 -2
- package/package.json +5 -28
- package/shortcuts-manager/ShortcutsManager/ShortcutsManager.tsx +1 -1
- package/shortcuts-manager/ShortcutsManager/utils.tsx +1 -1
- package/shortcuts-manager/useSetScopes/useSetScopes.tsx +1 -1
- package/shortcuts-manager/useShortcut/useShortcut.tsx +1 -1
- package/utils/extractTextStyles.ts +1 -2
- package/utils/formatNumberWithMask/formatNumberWithMask.ts +2 -1
- package/utils/index.ts +0 -3
- package/utils/normalizeToNumberString/normalizeToNumberString.ts +1 -1
- package/context-bridge/index.tsx +0 -87
- package/fast-context/index.tsx +0 -190
- package/hocs/typedMemo.tsx +0 -5
- package/hooks/useControlledValue.tsx +0 -84
- package/hooks/useLatest.tsx +0 -9
- package/hooks/useMergedRefs.ts +0 -14
- package/hooks/usePrevious.ts +0 -13
- package/hooks/useToggle.tsx +0 -24
- package/hooks/useWhatHasUpdated.tsx +0 -48
- package/utils/color.ts +0 -22
- package/utils/compare/index.ts +0 -54
- package/utils/lodash.ts +0 -121
- package/utils/repository.ts +0 -53
|
@@ -1,21 +1,16 @@
|
|
|
1
1
|
import type { ComponentType, ReactNode } from 'react';
|
|
2
|
-
import type { GestureResponderEvent, ViewProps } from 'react-native';
|
|
2
|
+
import type { GestureResponderEvent, TextInputProps, ViewProps } from 'react-native';
|
|
3
3
|
|
|
4
|
-
import type { ListValue } from '../List';
|
|
4
|
+
import type { ListContentProps, ListItemId, ListValue } from '../List';
|
|
5
5
|
import type { PopoverProps } from '../Popover';
|
|
6
6
|
|
|
7
|
-
export type {
|
|
8
|
-
ListContentProps as SelectContentProps,
|
|
9
|
-
ListContextValue as SelectContextValue,
|
|
10
|
-
ListGroupProps as SelectGroupProps,
|
|
11
|
-
ListSearchInputProps as SelectSearchInputProps,
|
|
12
|
-
} from '../List';
|
|
7
|
+
export type { ListContextValue as SelectContextValue } from '../List';
|
|
13
8
|
|
|
14
9
|
export type DefaultItemT = {
|
|
15
10
|
id: string | number;
|
|
16
11
|
label?: string;
|
|
17
12
|
selectable?: boolean;
|
|
18
|
-
[key: string]:
|
|
13
|
+
[key: string]: unknown;
|
|
19
14
|
};
|
|
20
15
|
|
|
21
16
|
// SelectDropdownContext types
|
|
@@ -25,47 +20,70 @@ export type SelectDropdownContextValue = {
|
|
|
25
20
|
onOpen: () => void;
|
|
26
21
|
};
|
|
27
22
|
|
|
23
|
+
export type SelectSearchMode = 'client' | 'external';
|
|
24
|
+
|
|
25
|
+
export type SelectSearchKey<Option extends object = DefaultItemT> =
|
|
26
|
+
| string
|
|
27
|
+
| string[]
|
|
28
|
+
| ((item: Option, query: string) => boolean);
|
|
29
|
+
|
|
30
|
+
export type SelectSearchContextValue<Option extends DefaultItemT = DefaultItemT> = {
|
|
31
|
+
searchQuery: string;
|
|
32
|
+
setSearchQuery: (query: string) => void;
|
|
33
|
+
allOptions: Option[];
|
|
34
|
+
options: Option[];
|
|
35
|
+
optionById: Map<ListItemId, Option>;
|
|
36
|
+
getOptionId: (item: Option) => ListItemId;
|
|
37
|
+
};
|
|
38
|
+
|
|
28
39
|
// SelectProvider props
|
|
29
40
|
type SelectPropsBase<Option extends DefaultItemT = DefaultItemT> = {
|
|
30
41
|
children: ReactNode;
|
|
31
42
|
disabled?: boolean;
|
|
32
43
|
error?: boolean;
|
|
33
44
|
options: Option[];
|
|
34
|
-
searchKey?:
|
|
45
|
+
searchKey?: SelectSearchKey<Option>;
|
|
46
|
+
searchQuery?: string;
|
|
47
|
+
defaultSearchQuery?: string;
|
|
35
48
|
onSearchChange?: (query: string) => void;
|
|
36
|
-
|
|
49
|
+
searchMode?: SelectSearchMode;
|
|
50
|
+
allowDeselect?: boolean;
|
|
51
|
+
getItemId?: (item: Option) => ListItemId;
|
|
37
52
|
};
|
|
38
53
|
|
|
54
|
+
export type SelectSearchInputProps = Omit<TextInputProps, 'value' | 'onChangeText'>;
|
|
55
|
+
|
|
39
56
|
type SingleSelectProps<Option extends DefaultItemT = DefaultItemT> = {
|
|
40
57
|
multiple?: false | undefined;
|
|
41
|
-
value?: ListValue<
|
|
42
|
-
defaultValue?: ListValue<
|
|
43
|
-
onChange?: (
|
|
44
|
-
value: ListValue<Option, false>,
|
|
45
|
-
item: Option,
|
|
46
|
-
event?: GestureResponderEvent,
|
|
47
|
-
) => void;
|
|
58
|
+
value?: ListValue<false>;
|
|
59
|
+
defaultValue?: ListValue<false>;
|
|
60
|
+
onChange?: (value: ListValue<false>, item: Option, event?: GestureResponderEvent) => void;
|
|
48
61
|
};
|
|
49
62
|
|
|
50
63
|
type MultipleSelectProps<Option extends DefaultItemT = DefaultItemT> = {
|
|
51
64
|
multiple: true;
|
|
52
|
-
value?: ListValue<
|
|
53
|
-
defaultValue?: ListValue<
|
|
54
|
-
onChange?: (
|
|
55
|
-
value: ListValue<Option, true>,
|
|
56
|
-
item: Option,
|
|
57
|
-
event?: GestureResponderEvent,
|
|
58
|
-
) => void;
|
|
65
|
+
value?: ListValue<true>;
|
|
66
|
+
defaultValue?: ListValue<true>;
|
|
67
|
+
onChange?: (value: ListValue<true>, item: Option, event?: GestureResponderEvent) => void;
|
|
59
68
|
};
|
|
60
69
|
|
|
61
70
|
export type SelectProps<Option extends DefaultItemT = DefaultItemT> = SelectPropsBase<Option> &
|
|
62
71
|
(SingleSelectProps<Option> | MultipleSelectProps<Option>);
|
|
63
72
|
|
|
73
|
+
export type SelectContentProps<Option extends DefaultItemT = DefaultItemT> = Omit<
|
|
74
|
+
ListContentProps,
|
|
75
|
+
'children'
|
|
76
|
+
> & {
|
|
77
|
+
children?: ReactNode | ((item: Option, isSelected: boolean) => ReactNode);
|
|
78
|
+
};
|
|
79
|
+
|
|
64
80
|
// Select.Trigger props
|
|
65
81
|
export type SelectTriggerProps = ViewProps & {
|
|
66
82
|
children?: ReactNode;
|
|
67
83
|
};
|
|
68
84
|
|
|
85
|
+
export type SelectTriggerOutlineProps = ViewProps;
|
|
86
|
+
|
|
69
87
|
// Select.Value props
|
|
70
88
|
export type SelectValueProps = ViewProps & {
|
|
71
89
|
placeholder?: string;
|
|
@@ -39,6 +39,13 @@ const triggerDefaultStyles = StyleSheet.create(theme => ({
|
|
|
39
39
|
},
|
|
40
40
|
},
|
|
41
41
|
},
|
|
42
|
+
triggerIcon: {
|
|
43
|
+
marginLeft: theme.spacings['2'],
|
|
44
|
+
color: theme.colors.onSurfaceVariant,
|
|
45
|
+
},
|
|
46
|
+
}));
|
|
47
|
+
|
|
48
|
+
const outlineDefaultStyles = StyleSheet.create(theme => ({
|
|
42
49
|
outline: {
|
|
43
50
|
position: 'absolute',
|
|
44
51
|
top: 0,
|
|
@@ -85,28 +92,35 @@ const triggerDefaultStyles = StyleSheet.create(theme => ({
|
|
|
85
92
|
},
|
|
86
93
|
},
|
|
87
94
|
},
|
|
88
|
-
triggerIcon: {
|
|
89
|
-
marginLeft: theme.spacings['2'],
|
|
90
|
-
color: theme.colors.onSurfaceVariant,
|
|
91
|
-
},
|
|
92
95
|
}));
|
|
93
96
|
|
|
94
97
|
export const defaultStyles = StyleSheet.create(theme => ({
|
|
98
|
+
valueText: {
|
|
99
|
+
flex: 1,
|
|
100
|
+
},
|
|
95
101
|
chipContainer: {
|
|
96
102
|
flexDirection: 'row',
|
|
97
103
|
flexWrap: 'wrap',
|
|
98
104
|
gap: 6,
|
|
99
105
|
maxWidth: '90%',
|
|
106
|
+
flex: 1,
|
|
100
107
|
},
|
|
101
108
|
searchInput: {
|
|
102
109
|
marginHorizontal: theme.spacings['2'],
|
|
103
110
|
marginVertical: theme.spacings['3'],
|
|
104
111
|
},
|
|
112
|
+
searchInputInput: {
|
|
113
|
+
height: 42,
|
|
114
|
+
},
|
|
105
115
|
}));
|
|
106
116
|
|
|
107
117
|
export const triggerStyles = getRegisteredComponentStylesWithFallback(
|
|
108
118
|
'Select_Trigger',
|
|
109
119
|
triggerDefaultStyles,
|
|
110
120
|
);
|
|
121
|
+
export const selectOutlineStyles = getRegisteredComponentStylesWithFallback(
|
|
122
|
+
'SelectOutline',
|
|
123
|
+
outlineDefaultStyles,
|
|
124
|
+
);
|
|
111
125
|
|
|
112
126
|
export const styles = getRegisteredComponentStylesWithFallback('Select', defaultStyles);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useControlledValue } from '@react-native-molecules/utils/hooks';
|
|
1
2
|
import { forwardRef, memo } from 'react';
|
|
2
3
|
import {
|
|
3
4
|
Switch as NativeSwitch,
|
|
@@ -6,7 +7,6 @@ import {
|
|
|
6
7
|
type ViewStyle,
|
|
7
8
|
} from 'react-native';
|
|
8
9
|
|
|
9
|
-
import { useControlledValue } from '../../hooks';
|
|
10
10
|
import type { IconType } from '../Icon';
|
|
11
11
|
|
|
12
12
|
export type Props = SwitchProps & {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useControlledValue, useLatest } from '@react-native-molecules/utils/hooks';
|
|
1
2
|
import { forwardRef, memo, useCallback, useEffect, useMemo, useRef } from 'react';
|
|
2
3
|
import {
|
|
3
4
|
Animated,
|
|
@@ -9,7 +10,7 @@ import {
|
|
|
9
10
|
type ViewStyle,
|
|
10
11
|
} from 'react-native';
|
|
11
12
|
|
|
12
|
-
import { useActionState
|
|
13
|
+
import { useActionState } from '../../hooks';
|
|
13
14
|
import { resolveStateVariant } from '../../utils';
|
|
14
15
|
import { Icon, type IconType } from '../Icon';
|
|
15
16
|
import { switchStyles, useSwitchColors } from './utils';
|
package/components/Tabs/Tabs.tsx
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { noop } from '@react-native-molecules/utils/helpers/lodash';
|
|
2
|
+
import { useControlledValue } from '@react-native-molecules/utils/hooks';
|
|
1
3
|
import {
|
|
2
4
|
cloneElement,
|
|
3
5
|
type ComponentType,
|
|
@@ -22,9 +24,7 @@ import {
|
|
|
22
24
|
} from 'react-native';
|
|
23
25
|
|
|
24
26
|
import { typedMemo } from '../../hocs';
|
|
25
|
-
import { useControlledValue } from '../../hooks';
|
|
26
27
|
import { extractSubcomponents } from '../../utils/extractSubcomponents';
|
|
27
|
-
import { noop } from '../../utils/lodash';
|
|
28
28
|
import type { TabItemProps } from './TabItem';
|
|
29
29
|
import { tabsStyles } from './utils';
|
|
30
30
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useControlledValue, useLatest } from '@react-native-molecules/utils/hooks';
|
|
1
2
|
import React, {
|
|
2
3
|
memo,
|
|
3
4
|
type PropsWithoutRef,
|
|
@@ -31,9 +32,7 @@ import {
|
|
|
31
32
|
View,
|
|
32
33
|
} from 'react-native';
|
|
33
34
|
|
|
34
|
-
import { useActionState } from '../../hooks
|
|
35
|
-
import useControlledValue from '../../hooks/useControlledValue';
|
|
36
|
-
import useLatest from '../../hooks/useLatest';
|
|
35
|
+
import { useActionState } from '../../hooks';
|
|
37
36
|
import { createSyntheticEvent, resolveStateVariant } from '../../utils';
|
|
38
37
|
import { extractSubcomponents } from '../../utils/extractSubcomponents';
|
|
39
38
|
import { HelperText } from '../HelperText';
|
|
@@ -645,6 +644,7 @@ export const TextInputLeft = memo(
|
|
|
645
644
|
style={[textInputLeftStyles.leftElement, style]}
|
|
646
645
|
onLayout={handleLayout}
|
|
647
646
|
accessibilityRole="none"
|
|
647
|
+
tabIndex={-1}
|
|
648
648
|
{...rest}>
|
|
649
649
|
{children}
|
|
650
650
|
</Pressable>
|
|
@@ -683,6 +683,7 @@ export const TextInputRight = memo(
|
|
|
683
683
|
onPress={onPress}
|
|
684
684
|
style={[textInputRightStyles.rightElement, style]}
|
|
685
685
|
accessibilityRole="none"
|
|
686
|
+
tabIndex={-1}
|
|
686
687
|
{...rest}>
|
|
687
688
|
{children}
|
|
688
689
|
</Pressable>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useLatest } from '@react-native-molecules/utils/hooks';
|
|
1
2
|
import { memo, useCallback, useMemo, useRef } from 'react';
|
|
2
3
|
import {
|
|
3
4
|
type GestureResponderEvent,
|
|
@@ -7,7 +8,6 @@ import {
|
|
|
7
8
|
type ViewProps,
|
|
8
9
|
} from 'react-native';
|
|
9
10
|
|
|
10
|
-
import { useLatest } from '../../hooks';
|
|
11
11
|
import AnalogClockHours from './AnalogClockHours';
|
|
12
12
|
import AnalogClockMinutes from './AnalogClockMinutes';
|
|
13
13
|
import AnimatedClockSwitcher from './AnimatedClockSwitcher';
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// @typescript-eslint/no-unused-vars
|
|
2
2
|
// WORK IN PROGRESS
|
|
3
3
|
|
|
4
|
+
import { useLatest } from '@react-native-molecules/utils/hooks';
|
|
4
5
|
import { memo, useCallback, useEffect, useRef, useState } from 'react';
|
|
5
6
|
import { TextInput as TextInputNative, useWindowDimensions, View } from 'react-native';
|
|
6
7
|
|
|
7
|
-
import { useLatest } from '../../hooks';
|
|
8
8
|
import { resolveStateVariant } from '../../utils';
|
|
9
9
|
import { Text } from '../Text';
|
|
10
10
|
import AmPmSwitcher from './AmPmSwitcher';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { useControlledValue } from '@react-native-molecules/utils/hooks';
|
|
1
2
|
import { memo, useCallback, useMemo, useState } from 'react';
|
|
2
3
|
import { type StyleProp, View, type ViewStyle } from 'react-native';
|
|
3
4
|
|
|
4
5
|
import { getRegisteredComponentWithFallback } from '../../core';
|
|
5
|
-
import { useControlledValue } from '../../hooks';
|
|
6
6
|
import { format, parse } from '../../utils/date-fns';
|
|
7
7
|
import { useOptionalDatePickerContext } from '../DatePicker/context';
|
|
8
8
|
import AnalogClock from './AnalogClock';
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { useControlledValue } from '@react-native-molecules/utils/hooks';
|
|
1
2
|
import type { ReactNode } from 'react';
|
|
2
3
|
import { memo, useMemo } from 'react';
|
|
3
4
|
import { KeyboardAvoidingView, Platform, View } from 'react-native';
|
|
4
5
|
|
|
5
6
|
import { getRegisteredComponentWithFallback } from '../../core';
|
|
6
|
-
import { useControlledValue } from '../../hooks';
|
|
7
7
|
import { DatePickerActions, DatePickerProvider } from '../DatePicker';
|
|
8
8
|
import type {
|
|
9
9
|
DatePickerContextType,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useToggle } from '@react-native-molecules/utils/hooks';
|
|
1
2
|
import {
|
|
2
3
|
createContext,
|
|
3
4
|
memo,
|
|
@@ -9,7 +10,6 @@ import {
|
|
|
9
10
|
useRef,
|
|
10
11
|
} from 'react';
|
|
11
12
|
|
|
12
|
-
import { useToggle } from '../../hooks';
|
|
13
13
|
import { extractSubcomponents } from '../../utils/extractSubcomponents';
|
|
14
14
|
|
|
15
15
|
export type Props = {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { noop } from '@react-native-molecules/utils/helpers/lodash';
|
|
1
2
|
import { forwardRef, memo, type ReactNode, useCallback, useRef } from 'react';
|
|
2
3
|
import {
|
|
3
4
|
type GestureResponderEvent,
|
|
@@ -5,7 +6,6 @@ import {
|
|
|
5
6
|
Pressable,
|
|
6
7
|
type PressableProps,
|
|
7
8
|
type StyleProp,
|
|
8
|
-
View,
|
|
9
9
|
type ViewStyle,
|
|
10
10
|
} from 'react-native';
|
|
11
11
|
import { StyleSheet } from 'react-native-unistyles';
|
|
@@ -123,7 +123,7 @@ const TouchableRipple = (
|
|
|
123
123
|
rippleColor: rippleColorProp,
|
|
124
124
|
underlayColor: _underlayColor,
|
|
125
125
|
rippleAlpha = 0.24,
|
|
126
|
-
onPress,
|
|
126
|
+
onPress = noop,
|
|
127
127
|
children,
|
|
128
128
|
onPressIn: onPressInProp,
|
|
129
129
|
onPressOut: onPressOutProp,
|
|
@@ -157,25 +157,16 @@ const TouchableRipple = (
|
|
|
157
157
|
style,
|
|
158
158
|
];
|
|
159
159
|
|
|
160
|
-
//
|
|
161
|
-
|
|
162
|
-
//
|
|
163
|
-
|
|
160
|
+
// The active ripple is tracked so onPressOut can fade it. Driving the lifecycle
|
|
161
|
+
// off Pressable's press events (instead of raw pointer events) means a nested
|
|
162
|
+
// element that captures the gesture won't trigger an orphan ripple — Pressable
|
|
163
|
+
// only fires onPressIn when its own press is being handled.
|
|
164
|
+
const activeRippleRef = useRef<HTMLElement | null>(null);
|
|
164
165
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
onPressInProp?.(e as GestureResponderEvent);
|
|
170
|
-
|
|
171
|
-
if (disabled) return;
|
|
172
|
-
|
|
173
|
-
isPointerDownRef.current = true;
|
|
174
|
-
|
|
175
|
-
const button = e.currentTarget as HTMLElement;
|
|
176
|
-
currentTargetRef.current = button;
|
|
177
|
-
const computedStyle = window.getComputedStyle(button);
|
|
178
|
-
const dimensions = button.getBoundingClientRect();
|
|
166
|
+
const startRipple = useCallback(
|
|
167
|
+
(host: HTMLElement, x: number, y: number) => {
|
|
168
|
+
const computedStyle = window.getComputedStyle(host);
|
|
169
|
+
const dimensions = host.getBoundingClientRect();
|
|
179
170
|
|
|
180
171
|
const resolvedRippleColor =
|
|
181
172
|
rippleColorResolvedProp ??
|
|
@@ -187,46 +178,14 @@ const TouchableRipple = (
|
|
|
187
178
|
)
|
|
188
179
|
: String(themeRippleFallback));
|
|
189
180
|
|
|
190
|
-
let touchX: number;
|
|
191
|
-
let touchY: number;
|
|
192
|
-
|
|
193
|
-
if (centered) {
|
|
194
|
-
// If centered, always position ripple at center
|
|
195
|
-
touchX = dimensions.width / 2;
|
|
196
|
-
touchY = dimensions.height / 2;
|
|
197
|
-
} else if ('clientX' in e && 'clientY' in e) {
|
|
198
|
-
// Web pointer event - calculate position relative to element
|
|
199
|
-
touchX = e.clientX - dimensions.left;
|
|
200
|
-
touchY = e.clientY - dimensions.top;
|
|
201
|
-
} else if (e.nativeEvent) {
|
|
202
|
-
// React Native gesture event
|
|
203
|
-
const { changedTouches, touches } = e.nativeEvent;
|
|
204
|
-
const touch = touches?.[0] ?? changedTouches?.[0];
|
|
205
|
-
if (touch) {
|
|
206
|
-
touchX = touch.locationX ?? dimensions.width / 2;
|
|
207
|
-
touchY = touch.locationY ?? dimensions.height / 2;
|
|
208
|
-
} else {
|
|
209
|
-
touchX = dimensions.width / 2;
|
|
210
|
-
touchY = dimensions.height / 2;
|
|
211
|
-
}
|
|
212
|
-
} else {
|
|
213
|
-
// Fallback to center (keyboard activation)
|
|
214
|
-
touchX = dimensions.width / 2;
|
|
215
|
-
touchY = dimensions.height / 2;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// Get the size of the button to determine how big the ripple should be
|
|
219
181
|
const size = centered
|
|
220
|
-
?
|
|
221
|
-
|
|
222
|
-
: // Otherwise make it twice as big so clicking on one end spreads ripple to other
|
|
223
|
-
Math.max(dimensions.width, dimensions.height) * 2;
|
|
182
|
+
? Math.min(dimensions.width, dimensions.height) * 1.25
|
|
183
|
+
: Math.max(dimensions.width, dimensions.height) * 2;
|
|
224
184
|
|
|
225
|
-
|
|
226
|
-
const container = document.createElement('span');
|
|
185
|
+
const expandDuration = Math.min(size * 1.5, 350);
|
|
227
186
|
|
|
187
|
+
const container = document.createElement('span');
|
|
228
188
|
container.setAttribute('data-molecules-ripple', '');
|
|
229
|
-
|
|
230
189
|
Object.assign(container.style, {
|
|
231
190
|
position: 'absolute',
|
|
232
191
|
pointerEvents: 'none',
|
|
@@ -241,39 +200,28 @@ const TouchableRipple = (
|
|
|
241
200
|
overflow: centered ? 'visible' : 'hidden',
|
|
242
201
|
});
|
|
243
202
|
|
|
244
|
-
// Create span to show the ripple effect
|
|
245
203
|
const ripple = document.createElement('span');
|
|
246
|
-
|
|
247
204
|
Object.assign(ripple.style, {
|
|
248
205
|
position: 'absolute',
|
|
249
206
|
pointerEvents: 'none',
|
|
250
207
|
backgroundColor: resolvedRippleColor,
|
|
251
208
|
borderRadius: '50%',
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
transitionProperty: 'transform opacity',
|
|
255
|
-
transitionDuration: `${Math.min(size * 1.5, 350)}ms`,
|
|
209
|
+
transitionProperty: 'transform, opacity',
|
|
210
|
+
transitionDuration: `${expandDuration}ms`,
|
|
256
211
|
transitionTimingFunction: 'linear',
|
|
257
212
|
transformOrigin: 'center',
|
|
258
|
-
|
|
259
|
-
/* We'll animate these properties */
|
|
260
213
|
transform: 'translate3d(-50%, -50%, 0) scale3d(0.1, 0.1, 0.1)',
|
|
261
214
|
opacity: '0.5',
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
left: `${touchX}px`,
|
|
265
|
-
top: `${touchY}px`,
|
|
215
|
+
left: `${x}px`,
|
|
216
|
+
top: `${y}px`,
|
|
266
217
|
width: `${size}px`,
|
|
267
218
|
height: `${size}px`,
|
|
268
219
|
});
|
|
269
220
|
|
|
270
|
-
// Finally, append it to DOM
|
|
271
221
|
container.appendChild(ripple);
|
|
272
|
-
|
|
222
|
+
host.appendChild(container);
|
|
223
|
+
activeRippleRef.current = container;
|
|
273
224
|
|
|
274
|
-
// rAF runs in the same frame as the event handler
|
|
275
|
-
// Use double rAF to ensure the transition class is added in next frame
|
|
276
|
-
// This will make sure that the transition animation is triggered
|
|
277
225
|
requestAnimationFrame(() => {
|
|
278
226
|
requestAnimationFrame(() => {
|
|
279
227
|
Object.assign(ripple.style, {
|
|
@@ -283,96 +231,71 @@ const TouchableRipple = (
|
|
|
283
231
|
});
|
|
284
232
|
});
|
|
285
233
|
},
|
|
286
|
-
[
|
|
287
|
-
onPressInProp,
|
|
288
|
-
disabled,
|
|
289
|
-
centered,
|
|
290
|
-
rippleColorResolvedProp,
|
|
291
|
-
themeRippleFallback,
|
|
292
|
-
rippleAlpha,
|
|
293
|
-
],
|
|
234
|
+
[centered, rippleColorResolvedProp, themeRippleFallback, rippleAlpha],
|
|
294
235
|
);
|
|
295
236
|
|
|
296
|
-
const
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
)
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
if (parentNode) {
|
|
316
|
-
parentNode.removeChild(container);
|
|
317
|
-
}
|
|
318
|
-
}, 500);
|
|
319
|
-
});
|
|
320
|
-
});
|
|
237
|
+
const fadeRipple = useCallback((container: HTMLElement | null) => {
|
|
238
|
+
if (!container) return;
|
|
239
|
+
const ripple = container.firstChild as HTMLElement | null;
|
|
240
|
+
if (!ripple) {
|
|
241
|
+
container.parentNode?.removeChild(container);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const onTransitionEnd = (ev: TransitionEvent) => {
|
|
246
|
+
if (ev.propertyName !== 'opacity') return;
|
|
247
|
+
ripple.removeEventListener('transitionend', onTransitionEnd);
|
|
248
|
+
container.parentNode?.removeChild(container);
|
|
249
|
+
};
|
|
250
|
+
ripple.addEventListener('transitionend', onTransitionEnd);
|
|
251
|
+
|
|
252
|
+
Object.assign(ripple.style, {
|
|
253
|
+
transitionDuration: '250ms',
|
|
254
|
+
opacity: '0',
|
|
321
255
|
});
|
|
322
256
|
}, []);
|
|
323
257
|
|
|
324
|
-
const
|
|
325
|
-
(e:
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
if (disabled || !isPointerDownRef.current) return;
|
|
329
|
-
|
|
330
|
-
isPointerDownRef.current = false;
|
|
331
|
-
currentTargetRef.current = null;
|
|
332
|
-
|
|
333
|
-
const target = e.currentTarget as HTMLElement;
|
|
334
|
-
fadeOutRipples(target);
|
|
335
|
-
},
|
|
336
|
-
[onPressOutProp, disabled, fadeOutRipples],
|
|
337
|
-
);
|
|
338
|
-
|
|
339
|
-
const handlePointerLeave = useCallback(
|
|
340
|
-
(e: any) => {
|
|
341
|
-
// Only fade out if pointer was down (dragging out of element)
|
|
342
|
-
if (disabled || !isPointerDownRef.current) return;
|
|
258
|
+
const handlePressIn = useCallback(
|
|
259
|
+
(e: GestureResponderEvent) => {
|
|
260
|
+
onPressInProp?.(e);
|
|
261
|
+
if (disabled) return;
|
|
343
262
|
|
|
344
|
-
|
|
345
|
-
|
|
263
|
+
const host = e.currentTarget as unknown as HTMLElement | null;
|
|
264
|
+
if (!host || typeof host.appendChild !== 'function') return;
|
|
265
|
+
|
|
266
|
+
const rect = host.getBoundingClientRect();
|
|
267
|
+
let x = rect.width / 2;
|
|
268
|
+
let y = rect.height / 2;
|
|
269
|
+
|
|
270
|
+
if (!centered) {
|
|
271
|
+
const ne: any = e.nativeEvent;
|
|
272
|
+
if (ne) {
|
|
273
|
+
if (typeof ne.locationX === 'number' && typeof ne.locationY === 'number') {
|
|
274
|
+
x = ne.locationX;
|
|
275
|
+
y = ne.locationY;
|
|
276
|
+
} else if (typeof ne.clientX === 'number' && typeof ne.clientY === 'number') {
|
|
277
|
+
x = ne.clientX - rect.left;
|
|
278
|
+
y = ne.clientY - rect.top;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
346
282
|
|
|
347
|
-
|
|
348
|
-
fadeOutRipples(target);
|
|
283
|
+
startRipple(host, x, y);
|
|
349
284
|
},
|
|
350
|
-
[disabled,
|
|
285
|
+
[onPressInProp, disabled, centered, startRipple],
|
|
351
286
|
);
|
|
352
287
|
|
|
353
|
-
const
|
|
354
|
-
(e:
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
const target = e.currentTarget as HTMLElement;
|
|
361
|
-
fadeOutRipples(target);
|
|
288
|
+
const handlePressOut = useCallback(
|
|
289
|
+
(e: GestureResponderEvent) => {
|
|
290
|
+
onPressOutProp?.(e);
|
|
291
|
+
const container = activeRippleRef.current;
|
|
292
|
+
activeRippleRef.current = null;
|
|
293
|
+
fadeRipple(container);
|
|
362
294
|
},
|
|
363
|
-
[
|
|
295
|
+
[onPressOutProp, fadeRipple],
|
|
364
296
|
);
|
|
365
297
|
|
|
366
|
-
const Component = asChild ? Slot :
|
|
367
|
-
|
|
368
|
-
// Use pointer events for universal compatibility (works on any HTML element)
|
|
369
|
-
// These events work with mouse, touch, and stylus inputs
|
|
370
|
-
const pointerEventProps = {
|
|
371
|
-
onPointerDown: handlePointerDown,
|
|
372
|
-
onPointerUp: handlePointerUp,
|
|
373
|
-
onPointerLeave: handlePointerLeave,
|
|
374
|
-
onPointerCancel: handlePointerCancel,
|
|
375
|
-
};
|
|
298
|
+
const Component = asChild ? Slot : Pressable;
|
|
376
299
|
|
|
377
300
|
const accessibilityRoleProp = (rest as { accessibilityRole?: unknown }).accessibilityRole;
|
|
378
301
|
const roleProp = (rest as { role?: unknown }).role;
|
|
@@ -386,8 +309,9 @@ const TouchableRipple = (
|
|
|
386
309
|
style={containerStyle}
|
|
387
310
|
ref={ref}
|
|
388
311
|
onPress={onPress}
|
|
389
|
-
|
|
390
|
-
{
|
|
312
|
+
onPressIn={handlePressIn}
|
|
313
|
+
onPressOut={handlePressOut}
|
|
314
|
+
disabled={disabled}>
|
|
391
315
|
{children}
|
|
392
316
|
</Component>
|
|
393
317
|
);
|
package/hocs/index.tsx
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { createFastContext } from '@react-native-molecules/utils/fast-context';
|
|
2
|
+
import { useLatest } from '@react-native-molecules/utils/hooks';
|
|
1
3
|
import {
|
|
2
4
|
type ComponentType,
|
|
3
5
|
forwardRef,
|
|
@@ -15,9 +17,6 @@ import type { FlatList } from 'react-native';
|
|
|
15
17
|
import type { SectionList } from 'react-native';
|
|
16
18
|
import { Platform } from 'react-native';
|
|
17
19
|
|
|
18
|
-
import { createFastContext } from '../fast-context';
|
|
19
|
-
import useLatest from '../hooks/useLatest';
|
|
20
|
-
|
|
21
20
|
export type Store = {
|
|
22
21
|
currentIndex: number | null;
|
|
23
22
|
};
|
package/hooks/index.tsx
CHANGED
|
@@ -4,7 +4,6 @@ export * from './useActionState';
|
|
|
4
4
|
export * from './useActive';
|
|
5
5
|
export { default as useColorMode } from './useColorMode';
|
|
6
6
|
export { useContrastColor } from './useContrastColor';
|
|
7
|
-
export { default as useControlledValue } from './useControlledValue';
|
|
8
7
|
export * from './useFocus';
|
|
9
8
|
export {
|
|
10
9
|
type NumberMaskConfig,
|
|
@@ -13,10 +12,7 @@ export {
|
|
|
13
12
|
} from './useHandleNumberFormat';
|
|
14
13
|
export * from './useHover';
|
|
15
14
|
export * from './useKeyboardDismissable';
|
|
16
|
-
export
|
|
17
|
-
export { useMediaQuery } from './useMediaQuery';
|
|
18
|
-
export { useMergedRefs } from './useMergedRefs';
|
|
19
|
-
export { default as usePrevious } from './usePrevious';
|
|
15
|
+
export * from './useMediaQuery';
|
|
20
16
|
export * from './useQueryFilter';
|
|
21
17
|
export * from './useTheme';
|
|
22
|
-
export
|
|
18
|
+
export * from '@react-native-molecules/utils/hooks';
|