react-native-molecules 0.5.0-beta.7 → 0.5.0-beta.9
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/ActivityIndicator/ActivityIndicator.tsx +6 -15
- package/components/Button/Button.tsx +206 -246
- package/components/Button/index.tsx +9 -3
- package/components/Button/types.ts +16 -2
- package/components/Button/utils.ts +230 -207
- package/components/DatePickerModal/DatePickerModalHeader.tsx +1 -1
- package/components/IconButton/utils.ts +1 -5
- 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/TimePickerModal/TimePickerModal.tsx +6 -2
- package/hooks/useSubcomponents.tsx +56 -22
- package/package.json +9 -2
- package/styles/themes/LightTheme.tsx +1 -1
|
@@ -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);
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { memo, useId } from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
cookie4Path,
|
|
6
|
+
cookie9Path,
|
|
7
|
+
expressFastSpatial,
|
|
8
|
+
loadingIndicatorStyles as componentStyles,
|
|
9
|
+
ovalPath,
|
|
10
|
+
pentagonPath,
|
|
11
|
+
pillPath,
|
|
12
|
+
type Props,
|
|
13
|
+
softBurstPath,
|
|
14
|
+
sunnyPath,
|
|
15
|
+
useProcessProps,
|
|
16
|
+
} from './utils';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Material 3 Expressive Loading Indicator for Web.
|
|
20
|
+
* Uses CSS 'd' attribute transitions for true shape morphing.
|
|
21
|
+
*/
|
|
22
|
+
const LoadingIndicator = ({
|
|
23
|
+
animating = true,
|
|
24
|
+
color: colorProp,
|
|
25
|
+
size: sizeProp = 'md',
|
|
26
|
+
style,
|
|
27
|
+
variant = 'default',
|
|
28
|
+
innerContainerProps,
|
|
29
|
+
}: Props) => {
|
|
30
|
+
const id = useId();
|
|
31
|
+
componentStyles.useVariants({
|
|
32
|
+
variant: variant as 'contained',
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const { size, strokeColor } = useProcessProps({
|
|
36
|
+
variant,
|
|
37
|
+
size: sizeProp,
|
|
38
|
+
color: colorProp,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
if (!animating) return null;
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<View
|
|
45
|
+
style={[
|
|
46
|
+
componentStyles.container,
|
|
47
|
+
{
|
|
48
|
+
width: Math.floor((10 / 48) * size * 2) + size,
|
|
49
|
+
height: Math.floor((10 / 48) * size * 2) + size,
|
|
50
|
+
},
|
|
51
|
+
style,
|
|
52
|
+
]}
|
|
53
|
+
accessible
|
|
54
|
+
accessibilityLabel="Loading"
|
|
55
|
+
accessibilityRole="progressbar"
|
|
56
|
+
accessibilityState={{ busy: animating }}>
|
|
57
|
+
<View
|
|
58
|
+
{...innerContainerProps}
|
|
59
|
+
style={[
|
|
60
|
+
{ width: size, height: size },
|
|
61
|
+
componentStyles.innerContainer,
|
|
62
|
+
innerContainerProps?.style,
|
|
63
|
+
]}>
|
|
64
|
+
<svg
|
|
65
|
+
className={`m3-expressive-svg-${id}`}
|
|
66
|
+
width={size}
|
|
67
|
+
height={size}
|
|
68
|
+
viewBox="0 0 38 38">
|
|
69
|
+
<path className={`m3-expressive-path-${id}`} fill={strokeColor} />
|
|
70
|
+
</svg>
|
|
71
|
+
</View>
|
|
72
|
+
<style>
|
|
73
|
+
{`
|
|
74
|
+
.m3-expressive-svg-${id} {
|
|
75
|
+
transform-origin: center;
|
|
76
|
+
display: block;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.m3-expressive-path-${id} {
|
|
80
|
+
animation: m3-expressive-combined 4.55s linear infinite;
|
|
81
|
+
transform-origin: center;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@keyframes m3-expressive-pulse-${id} {
|
|
85
|
+
0%, 100% { transform: scale(1); }
|
|
86
|
+
50% { transform: scale(1.1); }
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@keyframes m3-expressive-combined {
|
|
90
|
+
0% {
|
|
91
|
+
animation-timing-function: ${expressFastSpatial};
|
|
92
|
+
transform: rotate(0deg);
|
|
93
|
+
d: path('${softBurstPath}');
|
|
94
|
+
}
|
|
95
|
+
14.28% {
|
|
96
|
+
animation-timing-function: ${expressFastSpatial};
|
|
97
|
+
transform: rotate(154.29deg);
|
|
98
|
+
d: path('${cookie9Path}');
|
|
99
|
+
}
|
|
100
|
+
28.57% {
|
|
101
|
+
animation-timing-function: ${expressFastSpatial};
|
|
102
|
+
transform: rotate(308.57deg);
|
|
103
|
+
d: path('${pentagonPath}');
|
|
104
|
+
}
|
|
105
|
+
42.85% {
|
|
106
|
+
animation-timing-function: ${expressFastSpatial};
|
|
107
|
+
transform: rotate(462.86deg);
|
|
108
|
+
d: path('${pillPath}');
|
|
109
|
+
}
|
|
110
|
+
57.14% {
|
|
111
|
+
animation-timing-function: ${expressFastSpatial};
|
|
112
|
+
transform: rotate(617.14deg);
|
|
113
|
+
d: path('${sunnyPath}');
|
|
114
|
+
}
|
|
115
|
+
71.42% {
|
|
116
|
+
animation-timing-function: ${expressFastSpatial};
|
|
117
|
+
transform: rotate(771.43deg);
|
|
118
|
+
d: path('${cookie4Path}');
|
|
119
|
+
}
|
|
120
|
+
85.71% {
|
|
121
|
+
animation-timing-function: ${expressFastSpatial};
|
|
122
|
+
transform: rotate(925.71deg);
|
|
123
|
+
d: path('${ovalPath}');
|
|
124
|
+
}
|
|
125
|
+
100% {
|
|
126
|
+
transform: rotate(1080deg);
|
|
127
|
+
d: path('${softBurstPath}');
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
`}
|
|
131
|
+
</style>
|
|
132
|
+
</View>
|
|
133
|
+
);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export default memo(LoadingIndicator);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { getRegisteredComponentWithFallback } from '../../core';
|
|
2
|
+
import LoadingIndicatorDefault from './LoadingIndicator';
|
|
3
|
+
|
|
4
|
+
export const LoadingIndicator = getRegisteredComponentWithFallback(
|
|
5
|
+
'LoadingIndicator',
|
|
6
|
+
LoadingIndicatorDefault,
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
type LoadingIndicatorProps,
|
|
11
|
+
loadingIndicatorStyles,
|
|
12
|
+
loadingIndicatorStylesDefault,
|
|
13
|
+
} from './utils';
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type { ViewProps } from 'react-native';
|
|
2
|
+
import { StyleSheet } from 'react-native-unistyles';
|
|
3
|
+
|
|
4
|
+
import { getRegisteredComponentStylesWithFallback } from '../../core';
|
|
5
|
+
import { tokenStylesParser } from '../../utils/tokenStylesParser';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Props for the LoadingIndicator component.
|
|
9
|
+
*/
|
|
10
|
+
export type Props = ViewProps & {
|
|
11
|
+
/**
|
|
12
|
+
* Whether the indicator is animating. When false, the indicator is hidden.
|
|
13
|
+
* @default true
|
|
14
|
+
*/
|
|
15
|
+
animating?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Color of the indicator. Accepts theme color tokens (e.g., 'primary', 'secondary', 'error') or CSS color values.
|
|
18
|
+
* @default 'primary' for default variant, 'onPrimaryContainer' for contained variant
|
|
19
|
+
*/
|
|
20
|
+
color?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Size of the indicator. 'sm' = 24px, 'md' = 38px, or a custom number.
|
|
23
|
+
* @default 'md'
|
|
24
|
+
*/
|
|
25
|
+
size?: number | 'sm' | 'md';
|
|
26
|
+
/**
|
|
27
|
+
* Visual variant. 'contained' adds a circular background using primaryContainer color.
|
|
28
|
+
* @default 'default'
|
|
29
|
+
*/
|
|
30
|
+
variant?: 'default' | 'contained';
|
|
31
|
+
/**
|
|
32
|
+
* Props passed to the inner animated container.
|
|
33
|
+
*/
|
|
34
|
+
innerContainerProps?: ViewProps;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/* Soft Burst */
|
|
38
|
+
export const softBurstPath =
|
|
39
|
+
'M17.4683 2.83978C18.1732 1.72008 19.8268 1.72007 20.5317 2.83977L22.2214 5.52373C22.6848 6.25987 23.6197 6.55915 24.4338 6.23198L27.4021 5.0391C28.6404 4.54145 29.9781 5.49904 29.8804 6.8131L29.6461 9.96297C29.5819 10.8269 30.1597 11.6104 31.0135 11.8172L34.1265 12.571C35.4252 12.8855 35.9362 14.4349 35.0731 15.4414L33.0044 17.854C32.437 18.5158 32.437 19.4842 33.0044 20.146L35.0731 22.5586C35.9362 23.5651 35.4252 25.1145 34.1265 25.429L31.0135 26.1828C30.1597 26.3896 29.5819 27.1731 29.6461 28.037L29.8804 31.1869C29.9781 32.501 28.6404 33.4585 27.4021 32.9609L24.4338 31.768C23.6197 31.4408 22.6848 31.7401 22.2214 32.4763L20.5317 35.1602C19.8268 36.2799 18.1732 36.2799 17.4683 35.1602L15.7786 32.4763C15.3152 31.7401 14.3803 31.4408 13.5662 31.768L10.5979 32.9609C9.35964 33.4585 8.02187 32.501 8.1196 31.1869L8.35388 28.037C8.41814 27.1731 7.84033 26.3896 6.98651 26.1828L3.87348 25.429C2.57479 25.1145 2.0638 23.5651 2.92684 22.5586L4.99559 20.146C5.56299 19.4842 5.56299 18.5158 4.99559 17.854L2.92685 15.4414C2.0638 14.4349 2.57478 12.8855 3.87348 12.571L6.98651 11.8172C7.84033 11.6104 8.41814 10.8269 8.35388 9.96297L8.1196 6.81311C8.02187 5.49904 9.35964 4.54145 10.5979 5.0391L13.5662 6.23198C14.3803 6.55915 15.3152 6.25987 15.7786 5.52373L17.4683 2.83978Z';
|
|
40
|
+
|
|
41
|
+
/* 9-sided Cookie */
|
|
42
|
+
export const cookie9Path =
|
|
43
|
+
'M15.3091 3.60363C15.4924 3.454 15.584 3.37919 15.6677 3.31603C17.6389 1.82799 20.3611 1.82799 22.3323 3.31603C22.416 3.37919 22.5076 3.454 22.6909 3.60363C22.7727 3.67042 22.8136 3.70381 22.8541 3.7356C23.7818 4.46445 24.9191 4.87748 26.0993 4.91409C26.1508 4.91569 26.2037 4.91634 26.3094 4.91765C26.5462 4.92059 26.6646 4.92206 26.7694 4.92733C29.2381 5.05162 31.3234 6.79743 31.8748 9.20154C31.8982 9.30358 31.9202 9.41966 31.9642 9.65183C31.9838 9.75547 31.9937 9.80729 32.0042 9.85759C32.2452 11.0109 32.8504 12.0567 33.7309 12.8416C33.7693 12.8759 33.8094 12.9103 33.8895 12.9791C34.069 13.1332 34.1588 13.2102 34.2357 13.2815C36.0467 14.96 36.5194 17.6347 35.393 19.8299C35.3451 19.9231 35.2872 20.0262 35.1714 20.2322C35.1196 20.3242 35.0938 20.3702 35.0694 20.4155C34.5111 21.4536 34.3009 22.6429 34.4697 23.8088C34.4771 23.8597 34.4856 23.9117 34.5027 24.0158C34.5409 24.249 34.56 24.3656 34.573 24.4695C34.879 26.9168 33.5179 29.2689 31.2407 30.2281C31.1441 30.2688 31.0333 30.3106 30.8118 30.3942C30.7129 30.4315 30.6635 30.4501 30.6156 30.4692C29.5192 30.9063 28.592 31.6826 27.9701 32.684C27.943 32.7277 27.916 32.7731 27.862 32.8637C27.741 33.0669 27.6806 33.1685 27.6236 33.2564C26.2814 35.3273 23.7234 36.2563 21.3609 35.5306C21.2606 35.4998 21.1489 35.4608 20.9253 35.3827C20.8256 35.3479 20.7757 35.3305 20.7268 35.3144C19.6052 34.9461 18.3948 34.9461 17.2732 35.3144C17.2243 35.3305 17.1744 35.3479 17.0747 35.3827C16.8511 35.4608 16.7394 35.4998 16.6391 35.5306C14.2767 36.2563 11.7186 35.3273 10.3764 33.2564C10.3194 33.1685 10.259 33.0669 10.138 32.8637C10.084 32.7731 10.057 32.7277 10.0299 32.684C9.40803 31.6826 8.48083 30.9063 7.38436 30.4692C7.33654 30.4501 7.28709 30.4315 7.18821 30.3942C6.96669 30.3106 6.85593 30.2688 6.75928 30.2281C4.48205 29.2689 3.12097 26.9168 3.42698 24.4695C3.43997 24.3656 3.45908 24.249 3.4973 24.0158C3.51436 23.9117 3.52289 23.8597 3.53026 23.8088C3.69906 22.6429 3.48889 21.4536 2.93056 20.4155C2.90621 20.3702 2.88035 20.3242 2.82863 20.2322C2.71278 20.0262 2.65485 19.9231 2.60704 19.8299C1.48057 17.6347 1.95327 14.96 3.76433 13.2815C3.8412 13.2102 3.93096 13.1332 4.11047 12.9791C4.19061 12.9103 4.23068 12.8759 4.26908 12.8416C5.14958 12.0567 5.75476 11.0109 5.99583 9.8576C6.00634 9.80729 6.01617 9.75547 6.03582 9.65183C6.07984 9.41966 6.10185 9.30358 6.12525 9.20154C6.67662 6.79743 8.76192 5.05162 11.2306 4.92733C11.3354 4.92206 11.4538 4.92059 11.6906 4.91765C11.7963 4.91634 11.8492 4.91569 11.9007 4.91409C13.0809 4.87748 14.2182 4.46445 15.1459 3.7356C15.1864 3.70381 15.2273 3.67042 15.3091 3.60363Z';
|
|
44
|
+
|
|
45
|
+
/* Pentagon */
|
|
46
|
+
export const pentagonPath =
|
|
47
|
+
'M13.6481 4.93735C15.3067 3.72903 16.136 3.12486 17.022 2.82514C18.3035 2.39162 19.6965 2.39162 20.978 2.82514C21.864 3.12486 22.6933 3.72903 24.3519 4.93735L27.9794 7.58012L31.6049 10.0564C33.3249 11.2312 34.1849 11.8185 34.7598 12.5573C35.5914 13.6258 36.0282 14.9391 35.9986 16.2824C35.9781 17.211 35.6363 18.1817 34.9526 20.1232L33.529 24.1657L32.215 28.308C31.5983 30.2519 31.29 31.2239 30.7509 31.98C29.9712 33.0736 28.838 33.878 27.5341 34.2634C26.6327 34.5299 25.5936 34.5144 23.5154 34.4835L19 34.4162L14.4846 34.4835C12.4064 34.5144 11.3673 34.5299 10.4659 34.2634C9.16204 33.878 8.02882 33.0736 7.24909 31.98C6.71002 31.2239 6.40169 30.2519 5.78503 28.308L4.47101 24.1657L3.04741 20.1232C2.36372 18.1817 2.02187 17.211 2.00141 16.2824C1.97182 14.9391 2.40855 13.6258 3.24017 12.5573C3.8151 11.8185 4.67508 11.2312 6.39505 10.0564L10.0206 7.58012L13.6481 4.93735Z';
|
|
48
|
+
|
|
49
|
+
/* Pill */
|
|
50
|
+
export const pillPath =
|
|
51
|
+
'M11.9857 7.77706C17.0217 2.74098 25.1869 2.74098 30.2229 7.77706C35.259 12.8131 35.259 20.9783 30.2229 26.0143L26.0143 30.2229C20.9783 35.259 12.8131 35.259 7.77706 30.2229C2.74098 25.1869 2.74098 17.0217 7.77706 11.9857L11.9857 7.77706Z';
|
|
52
|
+
|
|
53
|
+
/* Sunny */
|
|
54
|
+
export const sunnyPath =
|
|
55
|
+
'M28.1856 6.12375C29.2431 6.19561 29.7718 6.23154 30.1991 6.41844C30.8175 6.68887 31.3111 7.18252 31.5816 7.80088C31.7685 8.22823 31.8044 8.75694 31.8762 9.81438L32.0402 12.2275C32.0693 12.6552 32.0838 12.869 32.1303 13.0733C32.1975 13.3684 32.3142 13.6501 32.4754 13.9063C32.5869 14.0836 32.7279 14.2451 33.0097 14.5681L34.6001 16.3903C35.297 17.1889 35.6455 17.5881 35.8155 18.0225C36.0615 18.6509 36.0615 19.3491 35.8155 19.9775C35.6455 20.4119 35.297 20.8111 34.6001 21.6097L33.0097 23.4319C32.7279 23.7549 32.5869 23.9164 32.4754 24.0937C32.3142 24.3499 32.1975 24.6316 32.1303 24.9267C32.0838 25.131 32.0693 25.3448 32.0402 25.7725L31.8762 28.1856C31.8044 29.2431 31.7685 29.7718 31.5816 30.1991C31.3111 30.8175 30.8175 31.3111 30.1991 31.5816C29.7718 31.7685 29.2431 31.8044 28.1856 31.8762L25.7725 32.0402C25.3448 32.0693 25.131 32.0838 24.9267 32.1303C24.6316 32.1975 24.3499 32.3142 24.0937 32.4754C23.9164 32.5869 23.7549 32.7279 23.4319 33.0097L21.6097 34.6001C20.8111 35.297 20.4119 35.6455 19.9775 35.8155C19.3491 36.0615 18.6509 36.0615 18.0225 35.8155C17.5881 35.6455 17.1889 35.297 16.3903 34.6001L14.5681 33.0097C14.2451 32.7279 14.0836 32.5869 13.9063 32.4754C13.6501 32.3142 13.3684 32.1975 13.0733 32.1303C12.869 32.0838 12.6552 32.0693 12.2275 32.0402L9.81438 31.8762C8.75694 31.8044 8.22822 31.7685 7.80088 31.5816C7.18252 31.3111 6.68887 30.8175 6.41844 30.1991C6.23154 29.7718 6.19561 29.2431 6.12375 28.1856L5.95977 25.7725C5.93071 25.3448 5.91618 25.131 5.86969 24.9267C5.80251 24.6316 5.68584 24.3499 5.52463 24.0937C5.41306 23.9164 5.27213 23.7549 4.99027 23.4319L3.3999 21.6097C2.703 20.8111 2.35454 20.4119 2.18452 19.9775C1.93849 19.3491 1.93849 18.6509 2.18452 18.0225C2.35454 17.5881 2.703 17.1889 3.3999 16.3903L4.99027 14.5681C5.27213 14.2451 5.41306 14.0836 5.52463 13.9063C5.68584 13.6501 5.80251 13.3684 5.86969 13.0733C5.91618 12.869 5.93071 12.6552 5.95977 12.2275L6.12375 9.81438C6.19561 8.75694 6.23154 8.22823 6.41844 7.80088C6.68887 7.18252 7.18252 6.68887 7.80088 6.41844C8.22823 6.23154 8.75694 6.19561 9.81438 6.12375L12.2275 5.95977C12.6552 5.93071 12.869 5.91618 13.0733 5.86969C13.3684 5.80251 13.6501 5.68584 13.9063 5.52463C14.0836 5.41306 14.2451 5.27213 14.5681 4.99027L16.3903 3.3999C17.1889 2.703 17.5881 2.35454 18.0225 2.18452C18.6509 1.93849 19.3491 1.93849 19.9775 2.18452C20.4119 2.35454 20.8111 2.703 21.6097 3.3999L23.4319 4.99027C23.7549 5.27213 23.9164 5.41306 24.0937 5.52463C24.3499 5.68584 24.6316 5.80251 24.9267 5.86969C25.131 5.91618 25.3448 5.93071 25.7725 5.95977L28.1856 6.12375Z';
|
|
56
|
+
|
|
57
|
+
/* 4-sided Cookie */
|
|
58
|
+
export const cookie4Path =
|
|
59
|
+
'M22.8729 5.6207C28.8872 3.00862 34.9914 9.11282 32.3793 15.1271L31.9474 16.1214C31.1499 17.9576 31.1499 20.0424 31.9474 21.8786L32.3793 22.8729C34.9914 28.8872 28.8872 34.9914 22.8729 32.3793L21.8786 31.9474C20.0424 31.1499 17.9576 31.1499 16.1214 31.9474L15.1271 32.3793C9.11281 34.9914 3.00862 28.8872 5.6207 22.8729L6.05257 21.8786C6.85006 20.0424 6.85005 17.9576 6.05257 16.1214L5.6207 15.1271C3.00862 9.11281 9.11282 3.00862 15.1271 5.6207L16.1214 6.05257C17.9576 6.85005 20.0424 6.85005 21.8786 6.05257L22.8729 5.6207Z';
|
|
60
|
+
|
|
61
|
+
/* Oval */
|
|
62
|
+
export const ovalPath =
|
|
63
|
+
'M27.1309 27.1309C20.1705 34.0913 10.8877 36.0935 6.39707 31.6029C1.90648 27.1123 3.90867 17.8295 10.8691 10.8691C17.8295 3.90867 27.1123 1.90648 31.6029 6.39707C36.0935 10.8877 34.0913 20.1705 27.1309 27.1309Z';
|
|
64
|
+
|
|
65
|
+
export const expressFastSpatial = 'cubic-bezier(0.42, 1.67, 0.21, 0.90)';
|
|
66
|
+
|
|
67
|
+
export const useProcessProps = ({
|
|
68
|
+
variant,
|
|
69
|
+
size: sizeProp,
|
|
70
|
+
color: colorProp,
|
|
71
|
+
}: Pick<Props, 'variant' | 'size' | 'color'>) => {
|
|
72
|
+
const size = typeof sizeProp === 'string' ? (sizeProp === 'sm' ? 24 : 38) : sizeProp || 38;
|
|
73
|
+
|
|
74
|
+
const color = colorProp || (variant === 'contained' ? 'onPrimaryContainer' : 'primary');
|
|
75
|
+
const parsedColor = tokenStylesParser.getColor(color, 'color') as { color?: string };
|
|
76
|
+
const strokeColor = parsedColor?.color;
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
size,
|
|
80
|
+
strokeColor,
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export const loadingIndicatorStylesDefault = StyleSheet.create(theme => ({
|
|
85
|
+
container: {
|
|
86
|
+
justifyContent: 'center',
|
|
87
|
+
alignItems: 'center',
|
|
88
|
+
borderRadius: theme.shapes.corner.full,
|
|
89
|
+
_web: {
|
|
90
|
+
width: 'fit-content',
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
variants: {
|
|
94
|
+
variant: {
|
|
95
|
+
default: {},
|
|
96
|
+
contained: {
|
|
97
|
+
backgroundColor: theme.colors.primaryContainer,
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
innerContainer: {
|
|
103
|
+
display: 'flex',
|
|
104
|
+
alignItems: 'center',
|
|
105
|
+
justifyContent: 'center',
|
|
106
|
+
transformOrigin: 'center',
|
|
107
|
+
|
|
108
|
+
_web: {
|
|
109
|
+
animation: 'm3-expressive-pulse 2.275s ease-in-out infinite',
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
}));
|
|
113
|
+
|
|
114
|
+
export const loadingIndicatorStyles = getRegisteredComponentStylesWithFallback(
|
|
115
|
+
'LoadingIndicator',
|
|
116
|
+
loadingIndicatorStylesDefault,
|
|
117
|
+
);
|
|
@@ -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,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
9
|
allowedChildren: T[];
|
|
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,66 @@ 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
47
|
// this will create properties with default empty array values even if they don't exist in the children
|
|
29
|
-
const defaultContext = allowedChildren.reduce(
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
48
|
+
const defaultContext = allowedChildren.reduce(
|
|
49
|
+
(context, childName) => {
|
|
50
|
+
return {
|
|
51
|
+
...context,
|
|
52
|
+
[childName]: [],
|
|
53
|
+
};
|
|
54
|
+
},
|
|
55
|
+
includeRest ? { rest: [] as ReactNode[] } : {},
|
|
56
|
+
) as UseSubcomponentsResult<T, IncludeRest>;
|
|
37
57
|
|
|
38
|
-
|
|
39
|
-
if (!isValidElement(child)) return context;
|
|
58
|
+
const childArray = Children.toArray(children);
|
|
40
59
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
60
|
+
return childArray.reduce((context, child) => {
|
|
61
|
+
if (!isValidElement(child)) {
|
|
62
|
+
// Non-element children go to rest if includeRest is enabled
|
|
63
|
+
if (includeRest) {
|
|
64
|
+
return {
|
|
65
|
+
...context,
|
|
66
|
+
rest: [...(context as any).rest, child],
|
|
67
|
+
};
|
|
68
|
+
}
|
|
44
69
|
return context;
|
|
45
70
|
}
|
|
46
71
|
|
|
47
|
-
const
|
|
72
|
+
const displayName = (child.type as FC)?.displayName as string | undefined;
|
|
48
73
|
|
|
49
|
-
if (!
|
|
74
|
+
if (!displayName || !allowedChildren.includes(displayName as T)) {
|
|
75
|
+
// Unmatched elements go to rest if includeRest is enabled
|
|
76
|
+
if (includeRest) {
|
|
77
|
+
return {
|
|
78
|
+
...context,
|
|
79
|
+
rest: [...(context as any).rest, child],
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return context;
|
|
83
|
+
}
|
|
50
84
|
|
|
51
85
|
return {
|
|
52
86
|
...context,
|
|
53
|
-
[
|
|
87
|
+
[displayName]: [...(context as any)[displayName], child],
|
|
54
88
|
};
|
|
55
89
|
}, defaultContext);
|
|
56
|
-
}, [allowedChildren, children]);
|
|
57
|
-
}
|
|
90
|
+
}, [allowedChildren, children, includeRest]);
|
|
91
|
+
}
|
|
58
92
|
|
|
59
93
|
export default useSubcomponents;
|