@oxyhq/bloom 0.1.7 → 0.1.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/lib/commonjs/button/Button.js +53 -19
- package/lib/commonjs/button/Button.js.map +1 -1
- package/lib/commonjs/collapsible/Collapsible.js +42 -3
- package/lib/commonjs/collapsible/Collapsible.js.map +1 -1
- package/lib/commonjs/loading/Loading.js +2 -1
- package/lib/commonjs/loading/Loading.js.map +1 -1
- package/lib/commonjs/skeleton/index.js +31 -8
- package/lib/commonjs/skeleton/index.js.map +1 -1
- package/lib/commonjs/styles/tokens.js +31 -1
- package/lib/commonjs/styles/tokens.js.map +1 -1
- package/lib/commonjs/switch/Switch.js +10 -7
- package/lib/commonjs/switch/Switch.js.map +1 -1
- package/lib/module/button/Button.js +55 -21
- package/lib/module/button/Button.js.map +1 -1
- package/lib/module/collapsible/Collapsible.js +44 -5
- package/lib/module/collapsible/Collapsible.js.map +1 -1
- package/lib/module/loading/Loading.js +2 -1
- package/lib/module/loading/Loading.js.map +1 -1
- package/lib/module/skeleton/index.js +31 -8
- package/lib/module/skeleton/index.js.map +1 -1
- package/lib/module/styles/tokens.js +30 -0
- package/lib/module/styles/tokens.js.map +1 -1
- package/lib/module/switch/Switch.js +10 -7
- package/lib/module/switch/Switch.js.map +1 -1
- package/lib/typescript/commonjs/button/Button.d.ts.map +1 -1
- package/lib/typescript/commonjs/collapsible/Collapsible.d.ts.map +1 -1
- package/lib/typescript/commonjs/loading/Loading.d.ts.map +1 -1
- package/lib/typescript/commonjs/skeleton/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/styles/tokens.d.ts +29 -0
- package/lib/typescript/commonjs/styles/tokens.d.ts.map +1 -1
- package/lib/typescript/commonjs/switch/Switch.d.ts.map +1 -1
- package/lib/typescript/module/button/Button.d.ts.map +1 -1
- package/lib/typescript/module/collapsible/Collapsible.d.ts.map +1 -1
- package/lib/typescript/module/loading/Loading.d.ts.map +1 -1
- package/lib/typescript/module/skeleton/index.d.ts.map +1 -1
- package/lib/typescript/module/styles/tokens.d.ts +29 -0
- package/lib/typescript/module/styles/tokens.d.ts.map +1 -1
- package/lib/typescript/module/switch/Switch.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/button/Button.tsx +53 -20
- package/src/collapsible/Collapsible.tsx +40 -6
- package/src/loading/Loading.tsx +2 -1
- package/src/skeleton/index.tsx +42 -12
- package/src/styles/tokens.ts +21 -0
- package/src/switch/Switch.tsx +5 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Collapsible.d.ts","sourceRoot":"","sources":["../../../../src/collapsible/Collapsible.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"Collapsible.d.ts","sourceRoot":"","sources":["../../../../src/collapsible/Collapsible.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyD,MAAM,OAAO,CAAC;AAK9E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAyFhD,eAAO,MAAM,WAAW,8CAA6B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Loading.d.ts","sourceRoot":"","sources":["../../../../src/loading/Loading.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmC,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"Loading.d.ts","sourceRoot":"","sources":["../../../../src/loading/Loading.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmC,MAAM,OAAO,CAAC;AAMxD,OAAO,KAAK,EACV,YAAY,EAKb,MAAM,SAAS,CAAC;AAmNjB,eAAO,MAAM,OAAO,0CAAyB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/skeleton/index.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/skeleton/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4B,MAAM,OAAO,CAAC;AACjD,OAAO,EAAkB,KAAK,SAAS,EAAE,KAAK,SAAS,EAAc,MAAM,cAAc,CAAC;AAiC1F,wBAAgB,IAAI,CAAC,EACnB,KAAK,EACL,KAAK,GACN,EAAE;IACD,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;IAChC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,2CA0BA;AAED,wBAAgB,MAAM,CAAC,EACrB,QAAQ,EACR,IAAI,EACJ,KAAK,EACL,KAAK,GACN,EAAE;IACD,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;CACjC,2CAmBA;AAED,wBAAgB,IAAI,CAAC,EACnB,IAAI,EACJ,KAAK,EACL,KAAK,GACN,EAAE;IACD,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;CACjC,2CAkBA;AAED,wBAAgB,GAAG,CAAC,EAClB,QAAQ,EACR,KAAK,GACN,EAAE;IACD,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;CACjC,2CAEA;AAED,wBAAgB,GAAG,CAAC,EAClB,QAAQ,EACR,KAAK,GACN,EAAE;IACD,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;CACjC,2CAEA"}
|
|
@@ -51,6 +51,35 @@ export declare const borderRadius: {
|
|
|
51
51
|
readonly _2xl: 24;
|
|
52
52
|
readonly full: 999;
|
|
53
53
|
};
|
|
54
|
+
/**
|
|
55
|
+
* Gradient definitions for icon system.
|
|
56
|
+
*/
|
|
57
|
+
/**
|
|
58
|
+
* Animation timing and spring tokens.
|
|
59
|
+
* Centralized values ensure consistent motion across all components.
|
|
60
|
+
*/
|
|
61
|
+
export declare const animation: {
|
|
62
|
+
readonly duration: {
|
|
63
|
+
readonly instant: 100;
|
|
64
|
+
readonly fast: 150;
|
|
65
|
+
readonly normal: 200;
|
|
66
|
+
readonly slow: 300;
|
|
67
|
+
};
|
|
68
|
+
readonly spring: {
|
|
69
|
+
readonly snappy: {
|
|
70
|
+
readonly friction: 8;
|
|
71
|
+
readonly tension: 100;
|
|
72
|
+
};
|
|
73
|
+
readonly gentle: {
|
|
74
|
+
readonly friction: 8;
|
|
75
|
+
readonly tension: 60;
|
|
76
|
+
};
|
|
77
|
+
readonly bouncy: {
|
|
78
|
+
readonly friction: 6;
|
|
79
|
+
readonly tension: 120;
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
};
|
|
54
83
|
/**
|
|
55
84
|
* Gradient definitions for icon system.
|
|
56
85
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../../../src/styles/tokens.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,eAAO,MAAM,KAAK;;;;;;;;;;;CAWR,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;CAWX,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,UAAU;;;;;CAKb,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,YAAY;;;;;;;;;CASf,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,SAAS;;yBAOb,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;;;;yBAOvB,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;;;CAGtB,CAAC"}
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../../../src/styles/tokens.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,eAAO,MAAM,KAAK;;;;;;;;;;;CAWR,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;CAWX,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,UAAU;;;;;CAKb,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,YAAY;;;;;;;;;CASf,CAAC;AAEX;;GAEG;AACH;;;GAGG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;CAYZ,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,SAAS;;yBAOb,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;;;;yBAOvB,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;;;CAGtB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Switch.d.ts","sourceRoot":"","sources":["../../../../src/switch/Switch.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkC,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"Switch.d.ts","sourceRoot":"","sources":["../../../../src/switch/Switch.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkC,MAAM,OAAO,CAAC;AAKvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AA6G3C,eAAO,MAAM,MAAM,4FAAwB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxyhq/bloom",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Bloom UI — Oxy ecosystem component library for React Native + Expo + Web",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
}
|
|
47
47
|
},
|
|
48
48
|
"./portal": {
|
|
49
|
-
"react-native": "./src/portal/index.
|
|
49
|
+
"react-native": "./src/portal/index.tsx",
|
|
50
50
|
"import": {
|
|
51
51
|
"types": "./lib/typescript/module/portal/index.d.ts",
|
|
52
52
|
"default": "./lib/module/portal/index.js"
|
package/src/button/Button.tsx
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import React, { useMemo, memo } from 'react';
|
|
2
|
-
import {
|
|
1
|
+
import React, { useCallback, useMemo, useRef, memo } from 'react';
|
|
2
|
+
import { Pressable, Text, Platform, Animated, type ViewStyle, type TextStyle } from 'react-native';
|
|
3
3
|
|
|
4
4
|
import { useTheme } from '../theme/use-theme';
|
|
5
|
+
import { animation } from '../styles/tokens';
|
|
5
6
|
import type { ButtonProps } from './types';
|
|
6
7
|
|
|
7
8
|
export type { ButtonProps, ButtonVariant, ButtonSize } from './types';
|
|
@@ -29,6 +30,9 @@ const SIZE_CONFIG = {
|
|
|
29
30
|
|
|
30
31
|
const ICON_HIT_SLOP = { top: 10, bottom: 10, left: 10, right: 10 } as const;
|
|
31
32
|
|
|
33
|
+
const PRESS_SCALE = 0.97;
|
|
34
|
+
const SCALE_VARIANTS = new Set<string>(['primary', 'secondary']);
|
|
35
|
+
|
|
32
36
|
const ButtonComponent: React.FC<ButtonProps> = ({
|
|
33
37
|
onPress,
|
|
34
38
|
children,
|
|
@@ -46,6 +50,26 @@ const ButtonComponent: React.FC<ButtonProps> = ({
|
|
|
46
50
|
testID,
|
|
47
51
|
}) => {
|
|
48
52
|
const theme = useTheme();
|
|
53
|
+
const scaleAnim = useRef(new Animated.Value(1)).current;
|
|
54
|
+
const hasScaleFeedback = SCALE_VARIANTS.has(variant);
|
|
55
|
+
|
|
56
|
+
const onPressIn = useCallback(() => {
|
|
57
|
+
if (!hasScaleFeedback) return;
|
|
58
|
+
Animated.spring(scaleAnim, {
|
|
59
|
+
toValue: PRESS_SCALE,
|
|
60
|
+
useNativeDriver: true,
|
|
61
|
+
...animation.spring.snappy,
|
|
62
|
+
}).start();
|
|
63
|
+
}, [scaleAnim, hasScaleFeedback]);
|
|
64
|
+
|
|
65
|
+
const onPressOut = useCallback(() => {
|
|
66
|
+
if (!hasScaleFeedback) return;
|
|
67
|
+
Animated.spring(scaleAnim, {
|
|
68
|
+
toValue: 1,
|
|
69
|
+
useNativeDriver: true,
|
|
70
|
+
...animation.spring.gentle,
|
|
71
|
+
}).start();
|
|
72
|
+
}, [scaleAnim, hasScaleFeedback]);
|
|
49
73
|
|
|
50
74
|
const baseStyles = useMemo((): ViewStyle => {
|
|
51
75
|
const sizeConfig = SIZE_CONFIG[size];
|
|
@@ -120,26 +144,35 @@ const ButtonComponent: React.FC<ButtonProps> = ({
|
|
|
120
144
|
}, [variant, size, theme]);
|
|
121
145
|
|
|
122
146
|
const defaultHitSlop = variant === 'icon' ? ICON_HIT_SLOP : undefined;
|
|
147
|
+
const resolvedActiveOpacity = activeOpacity ?? (variant === 'icon' ? 0.7 : 0.8);
|
|
123
148
|
|
|
124
149
|
return (
|
|
125
|
-
<
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
150
|
+
<Animated.View style={hasScaleFeedback ? { transform: [{ scale: scaleAnim }] } : undefined}>
|
|
151
|
+
<Pressable
|
|
152
|
+
style={({ pressed }) => [
|
|
153
|
+
baseStyles,
|
|
154
|
+
disabled && { opacity: 0.5 },
|
|
155
|
+
pressed && !hasScaleFeedback && { opacity: resolvedActiveOpacity },
|
|
156
|
+
style,
|
|
157
|
+
]}
|
|
158
|
+
onPress={onPress}
|
|
159
|
+
onPressIn={onPressIn}
|
|
160
|
+
onPressOut={onPressOut}
|
|
161
|
+
disabled={disabled}
|
|
162
|
+
hitSlop={hitSlop ?? defaultHitSlop}
|
|
163
|
+
accessibilityLabel={accessibilityLabel}
|
|
164
|
+
accessibilityHint={accessibilityHint}
|
|
165
|
+
accessibilityRole="button"
|
|
166
|
+
accessibilityState={{ disabled }}
|
|
167
|
+
testID={testID}
|
|
168
|
+
>
|
|
169
|
+
{iconPosition === 'left' && icon}
|
|
170
|
+
{children != null && (
|
|
171
|
+
<Text style={[computedTextStyle, textStyle]}>{children}</Text>
|
|
172
|
+
)}
|
|
173
|
+
{iconPosition === 'right' && icon}
|
|
174
|
+
</Pressable>
|
|
175
|
+
</Animated.View>
|
|
143
176
|
);
|
|
144
177
|
};
|
|
145
178
|
|
|
@@ -1,9 +1,17 @@
|
|
|
1
|
-
import React, { memo, useState } from 'react';
|
|
2
|
-
import { View, Text, TouchableOpacity } from 'react-native';
|
|
1
|
+
import React, { memo, useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { View, Text, TouchableOpacity, Animated, LayoutAnimation, Platform, UIManager } from 'react-native';
|
|
3
3
|
|
|
4
4
|
import { useTheme } from '../theme/use-theme';
|
|
5
|
+
import { animation } from '../styles/tokens';
|
|
5
6
|
import type { CollapsibleProps } from './types';
|
|
6
7
|
|
|
8
|
+
if (Platform.OS === 'android' && UIManager.setLayoutAnimationEnabledExperimental) {
|
|
9
|
+
UIManager.setLayoutAnimationEnabledExperimental(true);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const CHEVRON_CLOSED = '0deg';
|
|
13
|
+
const CHEVRON_OPEN = '90deg';
|
|
14
|
+
|
|
7
15
|
const CollapsibleComponent: React.FC<CollapsibleProps> = ({
|
|
8
16
|
title,
|
|
9
17
|
children,
|
|
@@ -15,12 +23,38 @@ const CollapsibleComponent: React.FC<CollapsibleProps> = ({
|
|
|
15
23
|
}) => {
|
|
16
24
|
const [isOpen, setIsOpen] = useState(defaultOpen);
|
|
17
25
|
const theme = useTheme();
|
|
26
|
+
const chevronAnim = useRef(new Animated.Value(defaultOpen ? 1 : 0)).current;
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
Animated.spring(chevronAnim, {
|
|
30
|
+
toValue: isOpen ? 1 : 0,
|
|
31
|
+
useNativeDriver: true,
|
|
32
|
+
...animation.spring.gentle,
|
|
33
|
+
}).start();
|
|
34
|
+
}, [isOpen, chevronAnim]);
|
|
35
|
+
|
|
36
|
+
const handleToggle = useCallback(() => {
|
|
37
|
+
LayoutAnimation.configureNext({
|
|
38
|
+
duration: animation.duration.normal,
|
|
39
|
+
update: { type: LayoutAnimation.Types.easeInEaseOut },
|
|
40
|
+
create: { type: LayoutAnimation.Types.easeInEaseOut, property: LayoutAnimation.Properties.opacity },
|
|
41
|
+
delete: { type: LayoutAnimation.Types.easeInEaseOut, property: LayoutAnimation.Properties.opacity },
|
|
42
|
+
});
|
|
43
|
+
setIsOpen((prev) => !prev);
|
|
44
|
+
}, []);
|
|
45
|
+
|
|
46
|
+
const chevronRotation = chevronAnim.interpolate({
|
|
47
|
+
inputRange: [0, 1],
|
|
48
|
+
outputRange: [CHEVRON_CLOSED, CHEVRON_OPEN],
|
|
49
|
+
});
|
|
18
50
|
|
|
19
51
|
return (
|
|
20
52
|
<View style={style} testID={testID}>
|
|
21
53
|
<TouchableOpacity
|
|
22
|
-
onPress={
|
|
54
|
+
onPress={handleToggle}
|
|
23
55
|
activeOpacity={0.7}
|
|
56
|
+
accessibilityRole="button"
|
|
57
|
+
accessibilityState={{ expanded: isOpen }}
|
|
24
58
|
style={{
|
|
25
59
|
flexDirection: 'row',
|
|
26
60
|
alignItems: 'center',
|
|
@@ -29,15 +63,15 @@ const CollapsibleComponent: React.FC<CollapsibleProps> = ({
|
|
|
29
63
|
}}
|
|
30
64
|
>
|
|
31
65
|
{chevronIcon ?? (
|
|
32
|
-
<Text
|
|
66
|
+
<Animated.Text
|
|
33
67
|
style={{
|
|
34
68
|
fontSize: 16,
|
|
35
69
|
color: theme.colors.textSecondary,
|
|
36
|
-
transform: [{ rotate:
|
|
70
|
+
transform: [{ rotate: chevronRotation }],
|
|
37
71
|
}}
|
|
38
72
|
>
|
|
39
73
|
›
|
|
40
|
-
</Text>
|
|
74
|
+
</Animated.Text>
|
|
41
75
|
)}
|
|
42
76
|
<Text
|
|
43
77
|
style={[
|
package/src/loading/Loading.tsx
CHANGED
|
@@ -2,6 +2,7 @@ import React, { memo, useEffect, useMemo } from 'react';
|
|
|
2
2
|
import { View, Text, StyleSheet, type DimensionValue } from 'react-native';
|
|
3
3
|
|
|
4
4
|
import { useTheme } from '../theme/use-theme';
|
|
5
|
+
import { animation } from '../styles/tokens';
|
|
5
6
|
import { SpinnerIcon } from './SpinnerIcon';
|
|
6
7
|
import type {
|
|
7
8
|
LoadingProps,
|
|
@@ -108,7 +109,7 @@ const TopLoading: React.FC<TopLoadingProps> = ({
|
|
|
108
109
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
109
110
|
const translateY = useSharedValue(showLoading ? 0 : -targetHeight);
|
|
110
111
|
|
|
111
|
-
const timingConfig = { duration:
|
|
112
|
+
const timingConfig = { duration: animation.duration.slow, easing: Easing.out(Easing.cubic) };
|
|
112
113
|
|
|
113
114
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
114
115
|
useEffect(() => {
|
package/src/skeleton/index.tsx
CHANGED
|
@@ -1,11 +1,36 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { View, type ViewStyle, type TextStyle, StyleSheet } from 'react-native';
|
|
1
|
+
import React, { useEffect, useRef } from 'react';
|
|
2
|
+
import { Animated, View, type ViewStyle, type TextStyle, StyleSheet } from 'react-native';
|
|
3
3
|
|
|
4
4
|
import { useTheme } from '../theme/use-theme';
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
const SHIMMER_DURATION = 1500;
|
|
7
|
+
const SHIMMER_MIN_OPACITY = 0.4;
|
|
8
|
+
const SHIMMER_MAX_OPACITY = 1;
|
|
9
|
+
|
|
10
|
+
function useShimmer() {
|
|
11
|
+
const opacity = useRef(new Animated.Value(SHIMMER_MAX_OPACITY)).current;
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
const loop = Animated.loop(
|
|
15
|
+
Animated.sequence([
|
|
16
|
+
Animated.timing(opacity, {
|
|
17
|
+
toValue: SHIMMER_MIN_OPACITY,
|
|
18
|
+
duration: SHIMMER_DURATION / 2,
|
|
19
|
+
useNativeDriver: true,
|
|
20
|
+
}),
|
|
21
|
+
Animated.timing(opacity, {
|
|
22
|
+
toValue: SHIMMER_MAX_OPACITY,
|
|
23
|
+
duration: SHIMMER_DURATION / 2,
|
|
24
|
+
useNativeDriver: true,
|
|
25
|
+
}),
|
|
26
|
+
]),
|
|
27
|
+
);
|
|
28
|
+
loop.start();
|
|
29
|
+
return () => loop.stop();
|
|
30
|
+
}, [opacity]);
|
|
31
|
+
|
|
32
|
+
return opacity;
|
|
33
|
+
}
|
|
9
34
|
|
|
10
35
|
export function Text({
|
|
11
36
|
blend,
|
|
@@ -15,6 +40,7 @@ export function Text({
|
|
|
15
40
|
blend?: boolean;
|
|
16
41
|
}) {
|
|
17
42
|
const { colors } = useTheme();
|
|
43
|
+
const shimmer = useShimmer();
|
|
18
44
|
const flattened = StyleSheet.flatten(style);
|
|
19
45
|
const width = (flattened as ViewStyle)?.width;
|
|
20
46
|
const lineHeight = (flattened?.lineHeight as number) || 14;
|
|
@@ -26,13 +52,13 @@ export function Text({
|
|
|
26
52
|
{ maxWidth: width as number },
|
|
27
53
|
{ paddingVertical: lineHeight * 0.15 },
|
|
28
54
|
]}>
|
|
29
|
-
<View
|
|
55
|
+
<Animated.View
|
|
30
56
|
style={[
|
|
31
57
|
styles.textInner,
|
|
32
58
|
{
|
|
33
59
|
backgroundColor: colors.contrast50,
|
|
34
60
|
height: lineHeight * 0.7,
|
|
35
|
-
opacity: blend ? 0.6 : 1,
|
|
61
|
+
opacity: Animated.multiply(shimmer, blend ? 0.6 : 1),
|
|
36
62
|
},
|
|
37
63
|
]}
|
|
38
64
|
/>
|
|
@@ -52,20 +78,22 @@ export function Circle({
|
|
|
52
78
|
style?: ViewStyle | ViewStyle[];
|
|
53
79
|
}) {
|
|
54
80
|
const { colors } = useTheme();
|
|
81
|
+
const shimmer = useShimmer();
|
|
82
|
+
|
|
55
83
|
return (
|
|
56
|
-
<View
|
|
84
|
+
<Animated.View
|
|
57
85
|
style={[
|
|
58
86
|
styles.circle,
|
|
59
87
|
{
|
|
60
88
|
backgroundColor: colors.contrast50,
|
|
61
89
|
width: size,
|
|
62
90
|
height: size,
|
|
63
|
-
opacity: blend ? 0.6 : 1,
|
|
91
|
+
opacity: Animated.multiply(shimmer, blend ? 0.6 : 1),
|
|
64
92
|
},
|
|
65
93
|
style,
|
|
66
94
|
]}>
|
|
67
95
|
{children}
|
|
68
|
-
</View>
|
|
96
|
+
</Animated.View>
|
|
69
97
|
);
|
|
70
98
|
}
|
|
71
99
|
|
|
@@ -79,15 +107,17 @@ export function Pill({
|
|
|
79
107
|
style?: ViewStyle | ViewStyle[];
|
|
80
108
|
}) {
|
|
81
109
|
const { colors } = useTheme();
|
|
110
|
+
const shimmer = useShimmer();
|
|
111
|
+
|
|
82
112
|
return (
|
|
83
|
-
<View
|
|
113
|
+
<Animated.View
|
|
84
114
|
style={[
|
|
85
115
|
styles.pill,
|
|
86
116
|
{
|
|
87
117
|
backgroundColor: colors.contrast50,
|
|
88
118
|
width: size * 1.618,
|
|
89
119
|
height: size,
|
|
90
|
-
opacity: blend ? 0.6 : 1,
|
|
120
|
+
opacity: Animated.multiply(shimmer, blend ? 0.6 : 1),
|
|
91
121
|
},
|
|
92
122
|
style,
|
|
93
123
|
]}
|
package/src/styles/tokens.ts
CHANGED
|
@@ -57,6 +57,27 @@ export const borderRadius = {
|
|
|
57
57
|
full: 999,
|
|
58
58
|
} as const;
|
|
59
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Gradient definitions for icon system.
|
|
62
|
+
*/
|
|
63
|
+
/**
|
|
64
|
+
* Animation timing and spring tokens.
|
|
65
|
+
* Centralized values ensure consistent motion across all components.
|
|
66
|
+
*/
|
|
67
|
+
export const animation = {
|
|
68
|
+
duration: {
|
|
69
|
+
instant: 100,
|
|
70
|
+
fast: 150,
|
|
71
|
+
normal: 200,
|
|
72
|
+
slow: 300,
|
|
73
|
+
},
|
|
74
|
+
spring: {
|
|
75
|
+
snappy: { friction: 8, tension: 100 },
|
|
76
|
+
gentle: { friction: 8, tension: 60 },
|
|
77
|
+
bouncy: { friction: 6, tension: 120 },
|
|
78
|
+
},
|
|
79
|
+
} as const;
|
|
80
|
+
|
|
60
81
|
/**
|
|
61
82
|
* Gradient definitions for icon system.
|
|
62
83
|
*/
|
package/src/switch/Switch.tsx
CHANGED
|
@@ -2,6 +2,7 @@ import React, { memo, useEffect, useRef } from 'react';
|
|
|
2
2
|
import { Pressable, Animated } from 'react-native';
|
|
3
3
|
|
|
4
4
|
import { useTheme } from '../theme/use-theme';
|
|
5
|
+
import { animation } from '../styles/tokens';
|
|
5
6
|
import type { SwitchProps } from './types';
|
|
6
7
|
|
|
7
8
|
const TRACK = { default: { w: 44, h: 26 }, sm: { w: 36, h: 22 } } as const;
|
|
@@ -19,8 +20,7 @@ const SwitchComponent = React.forwardRef<React.ElementRef<typeof Pressable>, Swi
|
|
|
19
20
|
Animated.spring(anim, {
|
|
20
21
|
toValue: value ? 1 : 0,
|
|
21
22
|
useNativeDriver: false,
|
|
22
|
-
|
|
23
|
-
tension: 60,
|
|
23
|
+
...animation.spring.gentle,
|
|
24
24
|
}).start();
|
|
25
25
|
}, [value, anim]);
|
|
26
26
|
|
|
@@ -29,8 +29,7 @@ const SwitchComponent = React.forwardRef<React.ElementRef<typeof Pressable>, Swi
|
|
|
29
29
|
Animated.spring(pressAnim, {
|
|
30
30
|
toValue: 1,
|
|
31
31
|
useNativeDriver: false,
|
|
32
|
-
|
|
33
|
-
tension: 100,
|
|
32
|
+
...animation.spring.snappy,
|
|
34
33
|
}).start();
|
|
35
34
|
};
|
|
36
35
|
|
|
@@ -38,8 +37,7 @@ const SwitchComponent = React.forwardRef<React.ElementRef<typeof Pressable>, Swi
|
|
|
38
37
|
Animated.spring(pressAnim, {
|
|
39
38
|
toValue: 0,
|
|
40
39
|
useNativeDriver: false,
|
|
41
|
-
|
|
42
|
-
tension: 60,
|
|
40
|
+
...animation.spring.gentle,
|
|
43
41
|
}).start();
|
|
44
42
|
};
|
|
45
43
|
|
|
@@ -79,7 +77,7 @@ const SwitchComponent = React.forwardRef<React.ElementRef<typeof Pressable>, Swi
|
|
|
79
77
|
onPressIn={onPressIn}
|
|
80
78
|
onPressOut={onPressOut}
|
|
81
79
|
style={[{ opacity: disabled ? 0.4 : 1 }, style]}
|
|
82
|
-
hitSlop={
|
|
80
|
+
hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
|
|
83
81
|
testID={testID}
|
|
84
82
|
>
|
|
85
83
|
<Animated.View
|