react-native-molecules 0.5.0-beta.0 → 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/AccordionItem.tsx +3 -2
- package/components/Accordion/AccordionItemContent.tsx +1 -0
- package/components/Accordion/AccordionItemHeader.tsx +6 -5
- package/components/Accordion/index.tsx +8 -14
- 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 -212
- 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 +2 -6
- 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 +3 -7
- 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 +3 -14
- 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 +7 -9
- 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/NavigationRailItem.tsx +2 -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 +5 -10
- package/components/RadioButton/utils.ts +9 -8
- package/components/Rating/RatingItem.tsx +2 -1
- 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/Tabs.tsx +14 -13
- 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 +5 -9
- 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/index.tsx +0 -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/shortcuts-manager/ShortcutsManager/utils.tsx +3 -2
- package/shortcuts-manager/useShortcut/index.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/extractPropertiesFromStyles.ts +25 -0
- package/utils/lodash.ts +77 -6
- package/utils/repository.ts +2 -52
- package/utils/tokenStylesParser.ts +3 -1
- package/hooks/useBreakpoints.tsx +0 -7
|
@@ -5,15 +5,13 @@ import {
|
|
|
5
5
|
type StyleProp,
|
|
6
6
|
type TextStyle,
|
|
7
7
|
type ViewProps,
|
|
8
|
-
type ViewStyle,
|
|
9
8
|
} from 'react-native';
|
|
10
9
|
|
|
11
10
|
import { useActionState } from '../../hooks/useActionState';
|
|
12
11
|
import { resolveStateVariant } from '../../utils';
|
|
13
|
-
import { Icon, type IconType } from '../Icon';
|
|
12
|
+
import { Icon, type IconProps, type IconType } from '../Icon';
|
|
14
13
|
import CrossFadeIcon from '../Icon/CrossFadeIcon';
|
|
15
14
|
import { StateLayer } from '../StateLayer';
|
|
16
|
-
import { Surface } from '../Surface';
|
|
17
15
|
import { TouchableRipple, type TouchableRippleProps } from '../TouchableRipple';
|
|
18
16
|
import type { IconButtonVariant } from './types';
|
|
19
17
|
import { defaultStyles, iconButtonSizeToIconSizeMap } from './utils';
|
|
@@ -63,14 +61,11 @@ export type Props = Omit<TouchableRippleProps, 'children' | 'style'> & {
|
|
|
63
61
|
*/
|
|
64
62
|
style?: StyleProp<TextStyle>;
|
|
65
63
|
iconStyle?: StyleProp<TextStyle>;
|
|
64
|
+
iconProps?: Omit<IconProps, 'name' | 'type' | 'style' | 'color' | 'size'>;
|
|
66
65
|
/**
|
|
67
66
|
* color of the icon
|
|
68
67
|
*/
|
|
69
68
|
color?: string;
|
|
70
|
-
/**
|
|
71
|
-
* Style of the innerContainer
|
|
72
|
-
*/
|
|
73
|
-
innerContainerStyle?: ViewStyle;
|
|
74
69
|
/**
|
|
75
70
|
* Props for the state layer
|
|
76
71
|
* */
|
|
@@ -92,10 +87,10 @@ const IconButton = (
|
|
|
92
87
|
animated = false,
|
|
93
88
|
variant = 'default',
|
|
94
89
|
style,
|
|
95
|
-
innerContainerStyle: innerContainerStyleProp = emptyObject,
|
|
96
90
|
testID,
|
|
97
91
|
stateLayerProps = emptyObject,
|
|
98
|
-
iconStyle,
|
|
92
|
+
iconStyle: iconStyleProp,
|
|
93
|
+
iconProps,
|
|
99
94
|
...rest
|
|
100
95
|
}: Props,
|
|
101
96
|
ref: any,
|
|
@@ -113,7 +108,9 @@ const IconButton = (
|
|
|
113
108
|
});
|
|
114
109
|
|
|
115
110
|
defaultStyles.useVariants({
|
|
116
|
-
|
|
111
|
+
// @ts-ignore // TODO - fix this
|
|
112
|
+
variant: variant as any,
|
|
113
|
+
// @ts-ignore // TODO - fix this
|
|
117
114
|
state,
|
|
118
115
|
size: typeof size === 'string' && size ? size : undefined,
|
|
119
116
|
});
|
|
@@ -124,9 +121,9 @@ const IconButton = (
|
|
|
124
121
|
rippleColor,
|
|
125
122
|
containerStyle,
|
|
126
123
|
accessibilityState,
|
|
127
|
-
innerContainerStyle,
|
|
128
124
|
// accessibilityTraits,
|
|
129
125
|
stateLayerStyle,
|
|
126
|
+
iconStyle,
|
|
130
127
|
} = useMemo(() => {
|
|
131
128
|
const iconSizeInNum =
|
|
132
129
|
iconButtonSizeToIconSizeMap[size as keyof typeof iconButtonSizeToIconSizeMap] ??
|
|
@@ -144,7 +141,6 @@ const IconButton = (
|
|
|
144
141
|
iconColor: _iconColor,
|
|
145
142
|
iconSize: iconSizeInNum,
|
|
146
143
|
rippleColor: _rippleColor,
|
|
147
|
-
innerContainerStyle: [defaultStyles.innerContainer, innerContainerStyleProp],
|
|
148
144
|
containerStyle: [
|
|
149
145
|
iconSizeInNum
|
|
150
146
|
? {
|
|
@@ -155,60 +151,51 @@ const IconButton = (
|
|
|
155
151
|
defaultStyles.root,
|
|
156
152
|
style,
|
|
157
153
|
],
|
|
154
|
+
iconStyle: [defaultStyles.icon, iconStyleProp],
|
|
158
155
|
// accessibilityTraits: disabled ? ['button', 'disabled'] : 'button',
|
|
159
156
|
accessibilityState: { disabled },
|
|
160
157
|
stateLayerStyle: [defaultStyles.stateLayer, stateLayerProps?.style],
|
|
161
158
|
};
|
|
162
159
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
163
|
-
}, [
|
|
164
|
-
_iconColor,
|
|
165
|
-
disabled,
|
|
166
|
-
innerContainerStyleProp,
|
|
167
|
-
size,
|
|
168
|
-
stateLayerProps?.style,
|
|
169
|
-
style,
|
|
170
|
-
state,
|
|
171
|
-
variant,
|
|
172
|
-
]);
|
|
160
|
+
}, [_iconColor, disabled, size, stateLayerProps?.style, style, state, variant]);
|
|
173
161
|
|
|
174
162
|
return (
|
|
175
|
-
<
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
hitSlop={
|
|
189
|
-
// @ts-ignore
|
|
190
|
-
TouchableRipple?.supported ? rippleSupportedHitSlop : rippleUnsupportedHitSlop
|
|
191
|
-
}
|
|
163
|
+
<TouchableRipple
|
|
164
|
+
borderless
|
|
165
|
+
centered
|
|
166
|
+
onPress={onPress}
|
|
167
|
+
rippleColor={rippleColor}
|
|
168
|
+
accessibilityLabel={accessibilityLabel}
|
|
169
|
+
style={containerStyle}
|
|
170
|
+
// accessibilityTraits={accessibilityTraits}
|
|
171
|
+
// accessibilityComponentType="button"
|
|
172
|
+
accessibilityRole="button"
|
|
173
|
+
accessibilityState={accessibilityState}
|
|
174
|
+
disabled={disabled}
|
|
175
|
+
hitSlop={
|
|
192
176
|
// @ts-ignore
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
177
|
+
TouchableRipple?.supported ? rippleSupportedHitSlop : rippleUnsupportedHitSlop
|
|
178
|
+
}
|
|
179
|
+
// @ts-ignore
|
|
180
|
+
ref={actionsRef}
|
|
181
|
+
testID={testID}
|
|
182
|
+
{...rest}>
|
|
183
|
+
<>
|
|
184
|
+
<IconComponent
|
|
185
|
+
color={iconColor}
|
|
186
|
+
name={name}
|
|
187
|
+
size={iconSize}
|
|
188
|
+
type={type}
|
|
189
|
+
style={iconStyle}
|
|
190
|
+
{...iconProps}
|
|
191
|
+
/>
|
|
192
|
+
<StateLayer
|
|
193
|
+
testID={testID ? `${testID}-stateLayer` : ''}
|
|
194
|
+
{...stateLayerProps}
|
|
195
|
+
style={stateLayerStyle}
|
|
196
|
+
/>
|
|
197
|
+
</>
|
|
198
|
+
</TouchableRipple>
|
|
212
199
|
);
|
|
213
200
|
};
|
|
214
201
|
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
import { getRegisteredComponentWithFallback
|
|
1
|
+
import { getRegisteredComponentWithFallback } from '../../core';
|
|
2
2
|
import IconButtonDefault from './IconButton';
|
|
3
3
|
|
|
4
|
-
registerMoleculesComponents({
|
|
5
|
-
IconButton: IconButtonDefault,
|
|
6
|
-
});
|
|
7
|
-
|
|
8
4
|
export const IconButton = getRegisteredComponentWithFallback('IconButton', IconButtonDefault);
|
|
9
5
|
|
|
10
6
|
export type { Props as IconButtonProps } from './IconButton';
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { StyleSheet } from 'react-native-unistyles';
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
} from '../../core';
|
|
8
|
-
import { getRegisteredComponentUtils } from './../../core/componentsRegistry';
|
|
4
|
+
getRegisteredComponentStylesWithFallback,
|
|
5
|
+
getRegisteredComponentUtilsWithFallback,
|
|
6
|
+
} from './../../core/componentsRegistry';
|
|
9
7
|
|
|
10
8
|
export type States =
|
|
11
9
|
| 'selectedAndDisabled'
|
|
@@ -29,6 +27,8 @@ const iconButtonStylesDefault = StyleSheet.create(theme => ({
|
|
|
29
27
|
overflow: 'hidden',
|
|
30
28
|
borderWidth: 0,
|
|
31
29
|
backgroundColor: 'transparent',
|
|
30
|
+
justifyContent: 'center',
|
|
31
|
+
alignItems: 'center',
|
|
32
32
|
|
|
33
33
|
variants: {
|
|
34
34
|
size: {
|
|
@@ -215,11 +215,7 @@ const iconButtonStylesDefault = StyleSheet.create(theme => ({
|
|
|
215
215
|
{
|
|
216
216
|
variant: 'outlined',
|
|
217
217
|
state: 'hovered',
|
|
218
|
-
styles: {
|
|
219
|
-
backgroundColor: theme.colors.inverseSurface,
|
|
220
|
-
color: theme.colors.inverseOnSurface,
|
|
221
|
-
borderWidth: 0,
|
|
222
|
-
},
|
|
218
|
+
styles: {},
|
|
223
219
|
},
|
|
224
220
|
],
|
|
225
221
|
},
|
|
@@ -304,22 +300,15 @@ const iconButtonStylesDefault = StyleSheet.create(theme => ({
|
|
|
304
300
|
],
|
|
305
301
|
},
|
|
306
302
|
|
|
307
|
-
|
|
308
|
-
flexGrow: 1,
|
|
309
|
-
justifyContent: 'center',
|
|
310
|
-
alignItems: 'center',
|
|
311
|
-
},
|
|
303
|
+
icon: {},
|
|
312
304
|
}));
|
|
313
305
|
|
|
314
|
-
|
|
315
|
-
IconButton
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
306
|
+
export const defaultStyles = getRegisteredComponentStylesWithFallback(
|
|
307
|
+
'IconButton',
|
|
308
|
+
iconButtonStylesDefault,
|
|
309
|
+
);
|
|
310
|
+
export const iconButtonSizeToIconSizeMap = getRegisteredComponentUtilsWithFallback(
|
|
311
|
+
'IconButton',
|
|
319
312
|
iconButtonSizeToIconSizeMapDefault,
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
export const defaultStyles = getRegisteredMoleculesComponentStyles('IconButton');
|
|
323
|
-
export const iconButtonSizeToIconSizeMap =
|
|
324
|
-
getRegisteredComponentUtils('IconButton')?.iconButtonSizeToIconSizeMapDefault ||
|
|
325
|
-
iconButtonSizeToIconSizeMapDefault;
|
|
313
|
+
'iconButtonSizeToIconSizeMap',
|
|
314
|
+
);
|
package/components/If/index.tsx
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import type { PropsWithChildren } from 'react';
|
|
2
2
|
|
|
3
|
-
import { getRegisteredComponentWithFallback
|
|
3
|
+
import { getRegisteredComponentWithFallback } from '../../core';
|
|
4
4
|
|
|
5
5
|
const IfDefault = (props: PropsWithChildren<{ shouldRender?: boolean }>) => {
|
|
6
6
|
return <>{!!props.shouldRender && props.children}</>;
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
-
registerMoleculesComponents({
|
|
10
|
-
If: IfDefault,
|
|
11
|
-
});
|
|
12
|
-
|
|
13
9
|
export const If = getRegisteredComponentWithFallback('If', IfDefault);
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
import { getRegisteredComponentWithFallback
|
|
1
|
+
import { getRegisteredComponentWithFallback } from '../../core';
|
|
2
2
|
import InputAddonDefault from './InputAddon';
|
|
3
3
|
|
|
4
|
-
registerMoleculesComponents({
|
|
5
|
-
InputAddon: InputAddonDefault,
|
|
6
|
-
});
|
|
7
|
-
|
|
8
4
|
export const InputAddon = getRegisteredComponentWithFallback('InputAddon', InputAddonDefault);
|
|
9
5
|
|
|
10
6
|
export type { Props as InputAddonProps } from './InputAddon';
|
|
@@ -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 inputAddonStylesDefault = StyleSheet.create(theme => ({
|
|
6
6
|
root: {
|
|
@@ -26,8 +26,7 @@ const inputAddonStylesDefault = StyleSheet.create(theme => ({
|
|
|
26
26
|
},
|
|
27
27
|
}));
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
InputAddon
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
export const inputAddonStyles = getRegisteredMoleculesComponentStyles('InputAddon');
|
|
29
|
+
export const inputAddonStyles = getRegisteredComponentStylesWithFallback(
|
|
30
|
+
'InputAddon',
|
|
31
|
+
inputAddonStylesDefault,
|
|
32
|
+
);
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
import { getRegisteredComponentWithFallback
|
|
1
|
+
import { getRegisteredComponentWithFallback } from '../../core';
|
|
2
2
|
import LinkDefault from './Link';
|
|
3
3
|
|
|
4
|
-
registerMoleculesComponents({
|
|
5
|
-
Link: LinkDefault,
|
|
6
|
-
});
|
|
7
|
-
|
|
8
4
|
export const Link = getRegisteredComponentWithFallback('Link', LinkDefault);
|
|
9
5
|
|
|
10
6
|
export type { Props as LinkProps } from './Link';
|
package/components/Link/utils.ts
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
import { StyleSheet } from 'react-native-unistyles';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { getCursorStyle } from '../../utils';
|
|
3
|
+
import { getRegisteredComponentStylesWithFallback } from '../../core';
|
|
5
4
|
|
|
6
5
|
// type States = 'disabled' | 'hovered';
|
|
7
6
|
|
|
8
7
|
const linkStylesDefault = StyleSheet.create(theme => ({
|
|
9
8
|
root: {
|
|
10
|
-
...getCursorStyle('pointer'),
|
|
11
9
|
color: theme.colors.primary,
|
|
12
10
|
...theme.typescale.labelLarge,
|
|
13
11
|
|
|
14
12
|
_web: {
|
|
13
|
+
cursor: 'pointer',
|
|
15
14
|
_hover: {
|
|
16
15
|
textDecorationLine: 'underline',
|
|
17
16
|
},
|
|
@@ -22,7 +21,10 @@ const linkStylesDefault = StyleSheet.create(theme => ({
|
|
|
22
21
|
disabled: {
|
|
23
22
|
color: theme.colors.onSurfaceDisabled,
|
|
24
23
|
opacity: 0.38,
|
|
25
|
-
|
|
24
|
+
|
|
25
|
+
_web: {
|
|
26
|
+
cursor: 'not-allowed',
|
|
27
|
+
},
|
|
26
28
|
},
|
|
27
29
|
default: {},
|
|
28
30
|
},
|
|
@@ -30,8 +32,4 @@ const linkStylesDefault = StyleSheet.create(theme => ({
|
|
|
30
32
|
},
|
|
31
33
|
}));
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
Link: linkStylesDefault,
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
export const linkStyles = getRegisteredMoleculesComponentStyles('Link');
|
|
35
|
+
export const linkStyles = getRegisteredComponentStylesWithFallback('Link', linkStylesDefault);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getRegisteredComponentWithFallback
|
|
1
|
+
import { getRegisteredComponentWithFallback } from '../../core';
|
|
2
2
|
import ListItemComponent from './ListItem';
|
|
3
3
|
import ListItemDescription from './ListItemDescription';
|
|
4
4
|
import ListItemTitle from './ListItemTitle';
|
|
@@ -8,10 +8,6 @@ const ListItemDefault = Object.assign(ListItemComponent, {
|
|
|
8
8
|
Description: ListItemDescription,
|
|
9
9
|
});
|
|
10
10
|
|
|
11
|
-
registerMoleculesComponents({
|
|
12
|
-
ListItem: ListItemDefault,
|
|
13
|
-
});
|
|
14
|
-
|
|
15
11
|
export const ListItem = getRegisteredComponentWithFallback('ListItem', ListItemDefault);
|
|
16
12
|
|
|
17
13
|
export type { Props as ListItemProps } from './ListItem';
|
|
@@ -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 listItemStylesDefault = StyleSheet.create(theme => ({
|
|
6
6
|
root: {
|
|
@@ -101,13 +101,15 @@ const listItemDescriptionStylesDefault = StyleSheet.create(theme => ({
|
|
|
101
101
|
},
|
|
102
102
|
}));
|
|
103
103
|
|
|
104
|
-
|
|
105
|
-
ListItem
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
export const listItemDescriptionStyles =
|
|
113
|
-
|
|
104
|
+
export const listItemStyles = getRegisteredComponentStylesWithFallback(
|
|
105
|
+
'ListItem',
|
|
106
|
+
listItemStylesDefault,
|
|
107
|
+
);
|
|
108
|
+
export const listItemTitleStyles = getRegisteredComponentStylesWithFallback(
|
|
109
|
+
'ListItem_Title',
|
|
110
|
+
listItemTitleStylesDefault,
|
|
111
|
+
);
|
|
112
|
+
export const listItemDescriptionStyles = getRegisteredComponentStylesWithFallback(
|
|
113
|
+
'ListItem_Description',
|
|
114
|
+
listItemDescriptionStylesDefault,
|
|
115
|
+
);
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { interpolate as flubberInterpolate } from 'flubber';
|
|
2
|
+
import { memo, useCallback, useEffect, useState } from 'react';
|
|
3
|
+
import { View } from 'react-native';
|
|
4
|
+
import Animated, {
|
|
5
|
+
cancelAnimation,
|
|
6
|
+
Easing,
|
|
7
|
+
useAnimatedStyle,
|
|
8
|
+
useDerivedValue,
|
|
9
|
+
useFrameCallback,
|
|
10
|
+
useSharedValue,
|
|
11
|
+
withRepeat,
|
|
12
|
+
withTiming,
|
|
13
|
+
} from 'react-native-reanimated';
|
|
14
|
+
import Svg, { Path } from 'react-native-svg';
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
cookie4Path,
|
|
18
|
+
cookie9Path,
|
|
19
|
+
loadingIndicatorStyles as componentStyles,
|
|
20
|
+
ovalPath,
|
|
21
|
+
pentagonPath,
|
|
22
|
+
pillPath,
|
|
23
|
+
type Props,
|
|
24
|
+
softBurstPath,
|
|
25
|
+
sunnyPath,
|
|
26
|
+
useProcessProps,
|
|
27
|
+
} from './utils';
|
|
28
|
+
|
|
29
|
+
// Animation constants matching the web version
|
|
30
|
+
const ANIMATION_DURATION = 4550; // 4.55 seconds total cycle
|
|
31
|
+
const PULSE_DURATION = 2275; // 2.275 seconds for pulse
|
|
32
|
+
|
|
33
|
+
const SHAPE_PATHS = [
|
|
34
|
+
softBurstPath,
|
|
35
|
+
cookie9Path,
|
|
36
|
+
pentagonPath,
|
|
37
|
+
pillPath,
|
|
38
|
+
sunnyPath,
|
|
39
|
+
cookie4Path,
|
|
40
|
+
ovalPath,
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
// Number of pre-computed frames per transition for smooth animation
|
|
44
|
+
const FRAMES_PER_TRANSITION = 30;
|
|
45
|
+
const TOTAL_FRAMES = SHAPE_PATHS.length * FRAMES_PER_TRANSITION;
|
|
46
|
+
|
|
47
|
+
// Material 3 Express Fast Spatial easing function
|
|
48
|
+
// Original: cubic-bezier(0.42, 1.67, 0.21, 0.90)
|
|
49
|
+
const expressFastSpatialEase = (t: number): number => {
|
|
50
|
+
'worklet';
|
|
51
|
+
// Approximate cubic-bezier(0.42, 1.67, 0.21, 0.90) using a simplified function
|
|
52
|
+
// This creates the characteristic overshoot and fast settle
|
|
53
|
+
const p1x = 0.42,
|
|
54
|
+
p1y = 1.67,
|
|
55
|
+
p2x = 0.21,
|
|
56
|
+
p2y = 0.9;
|
|
57
|
+
|
|
58
|
+
// Simple cubic bezier approximation
|
|
59
|
+
const cx = 3.0 * p1x;
|
|
60
|
+
const bx = 3.0 * (p2x - p1x) - cx;
|
|
61
|
+
const ax = 1.0 - cx - bx;
|
|
62
|
+
|
|
63
|
+
const cy = 3.0 * p1y;
|
|
64
|
+
const by = 3.0 * (p2y - p1y) - cy;
|
|
65
|
+
const ay = 1.0 - cy - by;
|
|
66
|
+
|
|
67
|
+
// Sample the bezier curve
|
|
68
|
+
const sampleX = (x: number) => ((ax * x + bx) * x + cx) * x;
|
|
69
|
+
const sampleY = (x: number) => ((ay * x + by) * x + cy) * x;
|
|
70
|
+
|
|
71
|
+
// Newton-Raphson to find t for given x
|
|
72
|
+
let guess = t;
|
|
73
|
+
for (let i = 0; i < 4; i++) {
|
|
74
|
+
const currentX = sampleX(guess) - t;
|
|
75
|
+
const currentSlope = (3.0 * ax * guess + 2.0 * bx) * guess + cx;
|
|
76
|
+
if (Math.abs(currentSlope) < 1e-6) break;
|
|
77
|
+
guess -= currentX / currentSlope;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return sampleY(guess);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Pre-compute all animation frames at initialization for smooth playback.
|
|
85
|
+
* This avoids runtime flubber calls which can cause jank.
|
|
86
|
+
*/
|
|
87
|
+
const precomputeFrames = (): string[] => {
|
|
88
|
+
const frames: string[] = [];
|
|
89
|
+
|
|
90
|
+
for (let i = 0; i < SHAPE_PATHS.length; i++) {
|
|
91
|
+
const fromPath = SHAPE_PATHS[i];
|
|
92
|
+
const toPath = SHAPE_PATHS[(i + 1) % SHAPE_PATHS.length];
|
|
93
|
+
|
|
94
|
+
// Create interpolator with higher precision
|
|
95
|
+
const interpolator = flubberInterpolate(fromPath, toPath, {
|
|
96
|
+
maxSegmentLength: 1, // Higher precision for smoother morphing
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
for (let j = 0; j < FRAMES_PER_TRANSITION; j++) {
|
|
100
|
+
const t = j / FRAMES_PER_TRANSITION;
|
|
101
|
+
// Apply easing to match CSS animation
|
|
102
|
+
const easedT = expressFastSpatialEase(t);
|
|
103
|
+
frames.push(interpolator(Math.max(0, Math.min(1, easedT))));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return frames;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const frames = precomputeFrames();
|
|
111
|
+
|
|
112
|
+
const LoadingIndicator = ({
|
|
113
|
+
animating = true,
|
|
114
|
+
color: colorProp,
|
|
115
|
+
size: sizeProp = 'md',
|
|
116
|
+
style,
|
|
117
|
+
variant = 'default',
|
|
118
|
+
innerContainerProps,
|
|
119
|
+
...rest
|
|
120
|
+
}: Props) => {
|
|
121
|
+
const [currentPath, setCurrentPath] = useState(frames[0]);
|
|
122
|
+
|
|
123
|
+
const progress = useSharedValue(0);
|
|
124
|
+
const pulseScale = useSharedValue(1);
|
|
125
|
+
// Track last frame to avoid redundant updates
|
|
126
|
+
const lastFrameRef = useSharedValue(-1);
|
|
127
|
+
|
|
128
|
+
componentStyles.useVariants({
|
|
129
|
+
variant: variant as 'contained',
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const { size, strokeColor } = useProcessProps({
|
|
133
|
+
variant,
|
|
134
|
+
size: sizeProp,
|
|
135
|
+
color: colorProp,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const updatePathFromFrame = useCallback((frameIndex: number) => {
|
|
139
|
+
const safeIndex = Math.max(0, Math.min(frameIndex, frames.length - 1));
|
|
140
|
+
setCurrentPath(prevPath => {
|
|
141
|
+
const newPath = frames[safeIndex];
|
|
142
|
+
return prevPath === newPath ? prevPath : newPath;
|
|
143
|
+
});
|
|
144
|
+
}, []);
|
|
145
|
+
|
|
146
|
+
useFrameCallback(() => {
|
|
147
|
+
'worklet';
|
|
148
|
+
const frameIndex = Math.floor(progress.value * TOTAL_FRAMES) % TOTAL_FRAMES;
|
|
149
|
+
if (frameIndex !== lastFrameRef.value) {
|
|
150
|
+
lastFrameRef.value = frameIndex;
|
|
151
|
+
updatePathFromFrame(frameIndex);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
useEffect(() => {
|
|
156
|
+
if (animating) {
|
|
157
|
+
// Main morphing and rotation animation
|
|
158
|
+
progress.value = 0;
|
|
159
|
+
progress.value = withRepeat(
|
|
160
|
+
withTiming(1, {
|
|
161
|
+
duration: ANIMATION_DURATION,
|
|
162
|
+
easing: Easing.linear,
|
|
163
|
+
}),
|
|
164
|
+
-1, // Infinite repeat
|
|
165
|
+
false, // Don't reverse
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
// Pulse animation
|
|
169
|
+
pulseScale.value = 1;
|
|
170
|
+
pulseScale.value = withRepeat(
|
|
171
|
+
withTiming(1.1, {
|
|
172
|
+
duration: PULSE_DURATION,
|
|
173
|
+
easing: Easing.inOut(Easing.ease),
|
|
174
|
+
}),
|
|
175
|
+
-1, // Infinite repeat
|
|
176
|
+
true, // Reverse (ping-pong effect)
|
|
177
|
+
);
|
|
178
|
+
} else {
|
|
179
|
+
cancelAnimation(progress);
|
|
180
|
+
cancelAnimation(pulseScale);
|
|
181
|
+
progress.value = 0;
|
|
182
|
+
pulseScale.value = 1;
|
|
183
|
+
updatePathFromFrame(0);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return () => {
|
|
187
|
+
cancelAnimation(progress);
|
|
188
|
+
cancelAnimation(pulseScale);
|
|
189
|
+
};
|
|
190
|
+
}, [animating, progress, pulseScale, updatePathFromFrame]);
|
|
191
|
+
|
|
192
|
+
// Derived value for rotation with per-segment easing (matches CSS animation-timing-function per keyframe)
|
|
193
|
+
const rotation = useDerivedValue(() => {
|
|
194
|
+
'worklet';
|
|
195
|
+
const p = progress.value;
|
|
196
|
+
const segmentCount = SHAPE_PATHS.length;
|
|
197
|
+
|
|
198
|
+
// Determine which segment we're in
|
|
199
|
+
const scaledProgress = p * segmentCount;
|
|
200
|
+
const segmentIndex = Math.min(Math.floor(scaledProgress), segmentCount - 1);
|
|
201
|
+
const segmentProgress = scaledProgress - segmentIndex;
|
|
202
|
+
|
|
203
|
+
// Apply easing to this segment's progress
|
|
204
|
+
const easedSegmentProgress = expressFastSpatialEase(segmentProgress);
|
|
205
|
+
|
|
206
|
+
// Calculate rotation: base rotation for completed segments + eased progress within current segment
|
|
207
|
+
const rotationPerSegment = 1080 / segmentCount; // ~154.29 degrees per segment
|
|
208
|
+
const baseRotation = segmentIndex * rotationPerSegment;
|
|
209
|
+
const segmentRotation = easedSegmentProgress * rotationPerSegment;
|
|
210
|
+
|
|
211
|
+
return baseRotation + segmentRotation;
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
const animatedStyle = useAnimatedStyle(() => {
|
|
215
|
+
return {
|
|
216
|
+
transform: [{ scale: pulseScale.value }, { rotate: `${rotation.value}deg` }],
|
|
217
|
+
};
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
if (!animating) return null;
|
|
221
|
+
|
|
222
|
+
return (
|
|
223
|
+
<View
|
|
224
|
+
style={[
|
|
225
|
+
componentStyles.container,
|
|
226
|
+
{
|
|
227
|
+
width: Math.floor((10 / 48) * size * 2) + size,
|
|
228
|
+
height: Math.floor((10 / 48) * size * 2) + size,
|
|
229
|
+
},
|
|
230
|
+
style,
|
|
231
|
+
]}
|
|
232
|
+
accessible
|
|
233
|
+
accessibilityLabel="Loading"
|
|
234
|
+
accessibilityRole="progressbar"
|
|
235
|
+
accessibilityState={{ busy: animating }}
|
|
236
|
+
{...rest}>
|
|
237
|
+
<Animated.View
|
|
238
|
+
{...innerContainerProps}
|
|
239
|
+
style={[
|
|
240
|
+
{ width: size, height: size },
|
|
241
|
+
componentStyles.innerContainer,
|
|
242
|
+
innerContainerProps?.style,
|
|
243
|
+
animatedStyle,
|
|
244
|
+
]}>
|
|
245
|
+
<Svg width={size} height={size} viewBox="0 0 38 38">
|
|
246
|
+
<Path d={currentPath} fill={strokeColor} />
|
|
247
|
+
</Svg>
|
|
248
|
+
</Animated.View>
|
|
249
|
+
</View>
|
|
250
|
+
);
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
export default memo(LoadingIndicator);
|