@oxyhq/bloom 0.1.0 → 0.1.2
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/index.js +12 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/loading/Loading.js +9 -8
- package/lib/commonjs/loading/Loading.js.map +1 -1
- package/lib/commonjs/loading/SpinnerIcon.js +185 -0
- package/lib/commonjs/loading/SpinnerIcon.js.map +1 -0
- package/lib/commonjs/loading/index.js +7 -0
- package/lib/commonjs/loading/index.js.map +1 -1
- package/lib/commonjs/switch/Switch.js +134 -0
- package/lib/commonjs/switch/Switch.js.map +1 -0
- package/lib/commonjs/switch/index.js +13 -0
- package/lib/commonjs/switch/index.js.map +1 -0
- package/lib/commonjs/switch/types.js +6 -0
- package/lib/commonjs/switch/types.js.map +1 -0
- package/lib/commonjs/theme/index.js +14 -0
- package/lib/commonjs/theme/index.js.map +1 -1
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/loading/Loading.js +10 -9
- package/lib/module/loading/Loading.js.map +1 -1
- package/lib/module/loading/SpinnerIcon.js +179 -0
- package/lib/module/loading/SpinnerIcon.js.map +1 -0
- package/lib/module/loading/index.js +1 -0
- package/lib/module/loading/index.js.map +1 -1
- package/lib/module/switch/Switch.js +129 -0
- package/lib/module/switch/Switch.js.map +1 -0
- package/lib/module/switch/index.js +4 -0
- package/lib/module/switch/index.js.map +1 -0
- package/lib/module/switch/types.js +4 -0
- package/lib/module/switch/types.js.map +1 -0
- package/lib/module/theme/index.js +2 -0
- package/lib/module/theme/index.js.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +1 -0
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/loading/Loading.d.ts.map +1 -1
- package/lib/typescript/commonjs/loading/SpinnerIcon.d.ts +15 -0
- package/lib/typescript/commonjs/loading/SpinnerIcon.d.ts.map +1 -0
- package/lib/typescript/commonjs/loading/index.d.ts +1 -0
- package/lib/typescript/commonjs/loading/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/switch/Switch.d.ts +4 -0
- package/lib/typescript/commonjs/switch/Switch.d.ts.map +1 -0
- package/lib/typescript/commonjs/switch/index.d.ts +3 -0
- package/lib/typescript/commonjs/switch/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/switch/types.d.ts +15 -0
- package/lib/typescript/commonjs/switch/types.d.ts.map +1 -0
- package/lib/typescript/commonjs/theme/index.d.ts +2 -0
- package/lib/typescript/commonjs/theme/index.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +1 -0
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/loading/Loading.d.ts.map +1 -1
- package/lib/typescript/module/loading/SpinnerIcon.d.ts +15 -0
- package/lib/typescript/module/loading/SpinnerIcon.d.ts.map +1 -0
- package/lib/typescript/module/loading/index.d.ts +1 -0
- package/lib/typescript/module/loading/index.d.ts.map +1 -1
- package/lib/typescript/module/switch/Switch.d.ts +4 -0
- package/lib/typescript/module/switch/Switch.d.ts.map +1 -0
- package/lib/typescript/module/switch/index.d.ts +3 -0
- package/lib/typescript/module/switch/index.d.ts.map +1 -0
- package/lib/typescript/module/switch/types.d.ts +15 -0
- package/lib/typescript/module/switch/types.d.ts.map +1 -0
- package/lib/typescript/module/theme/index.d.ts +2 -0
- package/lib/typescript/module/theme/index.d.ts.map +1 -1
- package/package.json +12 -1
- package/src/index.ts +1 -0
- package/src/loading/Loading.tsx +6 -5
- package/src/loading/SpinnerIcon.tsx +115 -0
- package/src/loading/index.ts +1 -0
- package/src/switch/Switch.tsx +117 -0
- package/src/switch/index.ts +2 -0
- package/src/switch/types.ts +15 -0
- package/src/theme/index.ts +2 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Switch.d.ts","sourceRoot":"","sources":["../../../../src/switch/Switch.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkC,MAAM,OAAO,CAAC;AAIvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAgH3C,eAAO,MAAM,MAAM,4FAAwB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/switch/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { StyleProp, ViewStyle } from 'react-native';
|
|
2
|
+
export interface SwitchProps {
|
|
3
|
+
/** Current on/off state */
|
|
4
|
+
value: boolean;
|
|
5
|
+
/** Called when the user toggles the switch */
|
|
6
|
+
onValueChange: (value: boolean) => void;
|
|
7
|
+
/** Whether the switch is disabled */
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
/** Container style */
|
|
10
|
+
style?: StyleProp<ViewStyle>;
|
|
11
|
+
/** Size variant */
|
|
12
|
+
size?: 'default' | 'sm';
|
|
13
|
+
testID?: string;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/switch/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzD,MAAM,WAAW,WAAW;IAC1B,2BAA2B;IAC3B,KAAK,EAAE,OAAO,CAAC;IACf,8CAA8C;IAC9C,aAAa,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACxC,qCAAqC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sBAAsB;IACtB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,mBAAmB;IACnB,IAAI,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
|
|
@@ -4,4 +4,6 @@ export { useTheme, useThemeColor, useBloomTheme } from './use-theme';
|
|
|
4
4
|
export type { Theme, ThemeColors, ThemeMode } from './types';
|
|
5
5
|
export type { AppColorName, AppColorPreset } from './color-presets';
|
|
6
6
|
export { APP_COLOR_NAMES, APP_COLOR_PRESETS, HEX_TO_APP_COLOR, hexToAppColorName } from './color-presets';
|
|
7
|
+
export { applyDarkClass } from './apply-dark-class';
|
|
8
|
+
export { setColorSchemeSafe } from './set-color-scheme-safe';
|
|
7
9
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/theme/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,YAAY,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC5F,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACrE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC7D,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/theme/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,YAAY,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC5F,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACrE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC7D,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAC1G,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxyhq/bloom",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
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",
|
|
@@ -155,6 +155,17 @@
|
|
|
155
155
|
"default": "./lib/commonjs/loading/index.js"
|
|
156
156
|
}
|
|
157
157
|
},
|
|
158
|
+
"./switch": {
|
|
159
|
+
"react-native": "./src/switch/index.ts",
|
|
160
|
+
"import": {
|
|
161
|
+
"types": "./lib/typescript/module/switch/index.d.ts",
|
|
162
|
+
"default": "./lib/module/switch/index.js"
|
|
163
|
+
},
|
|
164
|
+
"require": {
|
|
165
|
+
"types": "./lib/typescript/commonjs/switch/index.d.ts",
|
|
166
|
+
"default": "./lib/commonjs/switch/index.js"
|
|
167
|
+
}
|
|
168
|
+
},
|
|
158
169
|
"./prompt-input": {
|
|
159
170
|
"react-native": "./src/prompt-input/index.ts",
|
|
160
171
|
"import": {
|
package/src/index.ts
CHANGED
package/src/loading/Loading.tsx
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import React, { memo, useEffect, useMemo } from 'react';
|
|
2
|
-
import { View, Text,
|
|
2
|
+
import { View, Text, StyleSheet, type DimensionValue } from 'react-native';
|
|
3
3
|
|
|
4
4
|
import { useTheme } from '../theme/use-theme';
|
|
5
|
+
import { SpinnerIcon } from './SpinnerIcon';
|
|
5
6
|
import type {
|
|
6
7
|
LoadingProps,
|
|
7
8
|
SpinnerLoadingProps,
|
|
@@ -52,7 +53,7 @@ const SpinnerLoading: React.FC<SpinnerLoadingProps> = ({
|
|
|
52
53
|
|
|
53
54
|
return (
|
|
54
55
|
<View style={[styles.container, style]} testID={testID}>
|
|
55
|
-
{spinnerIcon ?? <
|
|
56
|
+
{spinnerIcon ?? <SpinnerIcon size={effectiveIconSize} color={spinnerColor} />}
|
|
56
57
|
{showText && text && (
|
|
57
58
|
<Text
|
|
58
59
|
style={[
|
|
@@ -92,7 +93,7 @@ const TopLoading: React.FC<TopLoadingProps> = ({
|
|
|
92
93
|
return (
|
|
93
94
|
<View style={[styles.topContainer, { height: targetHeight }, style]} testID={testID}>
|
|
94
95
|
<View style={[styles.topLoadingView, { height: targetHeight }]}>
|
|
95
|
-
{spinnerIcon ?? <
|
|
96
|
+
{spinnerIcon ?? <SpinnerIcon size={effectiveIconSize} color={spinnerColor} />}
|
|
96
97
|
</View>
|
|
97
98
|
</View>
|
|
98
99
|
);
|
|
@@ -130,7 +131,7 @@ const TopLoading: React.FC<TopLoadingProps> = ({
|
|
|
130
131
|
return (
|
|
131
132
|
<Animated.View style={[styles.topContainer, containerAnimated]} testID={testID}>
|
|
132
133
|
<Animated.View style={[styles.topLoadingView, { height: targetHeight }, innerAnimated, style]}>
|
|
133
|
-
{spinnerIcon ?? <
|
|
134
|
+
{spinnerIcon ?? <SpinnerIcon size={effectiveIconSize} color={spinnerColor} />}
|
|
134
135
|
</Animated.View>
|
|
135
136
|
</Animated.View>
|
|
136
137
|
);
|
|
@@ -188,7 +189,7 @@ const InlineLoading: React.FC<InlineLoadingProps> = ({
|
|
|
188
189
|
|
|
189
190
|
return (
|
|
190
191
|
<View style={[styles.inlineContainer, style]} testID={testID}>
|
|
191
|
-
{spinnerIcon ?? <
|
|
192
|
+
{spinnerIcon ?? <SpinnerIcon size={SIZE_CONFIG.small.spinner} color={spinnerColor} />}
|
|
192
193
|
{text && (
|
|
193
194
|
<Text
|
|
194
195
|
style={[
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
|
+
import { ActivityIndicator, View, type ViewStyle } from 'react-native';
|
|
3
|
+
|
|
4
|
+
// Lazy-loaded dependencies for the SVG spinner.
|
|
5
|
+
// Falls back to ActivityIndicator if react-native-svg or react-native-reanimated are not installed.
|
|
6
|
+
type SvgModuleType = typeof import('react-native-svg');
|
|
7
|
+
type ReanimatedType = typeof import('react-native-reanimated');
|
|
8
|
+
|
|
9
|
+
let svgModule: SvgModuleType | null = null;
|
|
10
|
+
let svgModuleResolved = false;
|
|
11
|
+
let reanimatedModule: ReanimatedType | null = null;
|
|
12
|
+
let reanimatedResolved = false;
|
|
13
|
+
|
|
14
|
+
function getSvgModule(): SvgModuleType | null {
|
|
15
|
+
if (!svgModuleResolved) {
|
|
16
|
+
svgModuleResolved = true;
|
|
17
|
+
try {
|
|
18
|
+
svgModule = require('react-native-svg');
|
|
19
|
+
} catch {
|
|
20
|
+
svgModule = null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return svgModule;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function getReanimated(): ReanimatedType | null {
|
|
27
|
+
if (!reanimatedResolved) {
|
|
28
|
+
reanimatedResolved = true;
|
|
29
|
+
try {
|
|
30
|
+
reanimatedModule = require('react-native-reanimated');
|
|
31
|
+
} catch {
|
|
32
|
+
reanimatedModule = null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return reanimatedModule;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface SpinnerIconProps {
|
|
39
|
+
size?: number;
|
|
40
|
+
color?: string;
|
|
41
|
+
style?: ViewStyle;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* iOS-style SVG spinner with 8 rotating rectangles and an opacity gradient trail.
|
|
46
|
+
* Requires react-native-svg and react-native-reanimated as peer dependencies.
|
|
47
|
+
* Falls back to ActivityIndicator if either is missing.
|
|
48
|
+
*/
|
|
49
|
+
export const SpinnerIcon: React.FC<SpinnerIconProps> = ({
|
|
50
|
+
color = '#005c67',
|
|
51
|
+
size = 26,
|
|
52
|
+
style,
|
|
53
|
+
}) => {
|
|
54
|
+
const svg = getSvgModule();
|
|
55
|
+
const reanimated = getReanimated();
|
|
56
|
+
|
|
57
|
+
if (!svg || !reanimated) {
|
|
58
|
+
return <ActivityIndicator size={size > 30 ? 'large' : 'small'} color={color} />;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const { default: Svg, Rect } = svg;
|
|
62
|
+
const {
|
|
63
|
+
default: Animated,
|
|
64
|
+
useAnimatedStyle,
|
|
65
|
+
useSharedValue,
|
|
66
|
+
withRepeat,
|
|
67
|
+
withTiming,
|
|
68
|
+
Easing,
|
|
69
|
+
} = reanimated;
|
|
70
|
+
|
|
71
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
72
|
+
const rotation = useSharedValue(0);
|
|
73
|
+
|
|
74
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
rotation.value = withRepeat(
|
|
77
|
+
withTiming(360, { duration: 400, easing: Easing.linear }),
|
|
78
|
+
-1,
|
|
79
|
+
false
|
|
80
|
+
);
|
|
81
|
+
}, [rotation, withRepeat, withTiming, Easing]);
|
|
82
|
+
|
|
83
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
84
|
+
const animatedStyle = useAnimatedStyle(() => ({
|
|
85
|
+
transform: [{ rotate: `${rotation.value}deg` }],
|
|
86
|
+
}));
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<Animated.View
|
|
90
|
+
style={[
|
|
91
|
+
{
|
|
92
|
+
width: size,
|
|
93
|
+
height: size,
|
|
94
|
+
alignItems: 'center',
|
|
95
|
+
justifyContent: 'center',
|
|
96
|
+
},
|
|
97
|
+
animatedStyle,
|
|
98
|
+
style,
|
|
99
|
+
]}
|
|
100
|
+
>
|
|
101
|
+
<Svg viewBox="0 0 100 100" width={size} height={size}>
|
|
102
|
+
<Rect fill={color} height="10" opacity="0" rx="5" ry="5" transform="rotate(-90 50 50)" width="28" x="67" y="45" />
|
|
103
|
+
<Rect fill={color} height="10" opacity="0.125" rx="5" ry="5" transform="rotate(-45 50 50)" width="28" x="67" y="45" />
|
|
104
|
+
<Rect fill={color} height="10" opacity="0.25" rx="5" ry="5" transform="rotate(0 50 50)" width="28" x="67" y="45" />
|
|
105
|
+
<Rect fill={color} height="10" opacity="0.375" rx="5" ry="5" transform="rotate(45 50 50)" width="28" x="67" y="45" />
|
|
106
|
+
<Rect fill={color} height="10" opacity="0.5" rx="5" ry="5" transform="rotate(90 50 50)" width="28" x="67" y="45" />
|
|
107
|
+
<Rect fill={color} height="10" opacity="0.625" rx="5" ry="5" transform="rotate(135 50 50)" width="28" x="67" y="45" />
|
|
108
|
+
<Rect fill={color} height="10" opacity="0.75" rx="5" ry="5" transform="rotate(180 50 50)" width="28" x="67" y="45" />
|
|
109
|
+
<Rect fill={color} height="10" opacity="0.875" rx="5" ry="5" transform="rotate(225 50 50)" width="28" x="67" y="45" />
|
|
110
|
+
</Svg>
|
|
111
|
+
</Animated.View>
|
|
112
|
+
);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
SpinnerIcon.displayName = 'SpinnerIcon';
|
package/src/loading/index.ts
CHANGED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import React, { memo, useEffect, useRef } from 'react';
|
|
2
|
+
import { Pressable, Animated } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import { useTheme } from '../theme/use-theme';
|
|
5
|
+
import type { SwitchProps } from './types';
|
|
6
|
+
|
|
7
|
+
const TRACK = { default: { w: 44, h: 26 }, sm: { w: 36, h: 22 } } as const;
|
|
8
|
+
const THUMB = { default: 22, sm: 18 } as const;
|
|
9
|
+
const PADDING = 2;
|
|
10
|
+
const SQUEEZE_RATIO = 0.75; // thumb height shrinks to 75% when pressed
|
|
11
|
+
|
|
12
|
+
const SwitchComponent = React.forwardRef<React.ElementRef<typeof Pressable>, SwitchProps>(
|
|
13
|
+
({ value, onValueChange, disabled, style, size = 'default', testID }, ref) => {
|
|
14
|
+
const theme = useTheme();
|
|
15
|
+
const anim = useRef(new Animated.Value(value ? 1 : 0)).current;
|
|
16
|
+
const pressAnim = useRef(new Animated.Value(0)).current;
|
|
17
|
+
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
Animated.spring(anim, {
|
|
20
|
+
toValue: value ? 1 : 0,
|
|
21
|
+
useNativeDriver: false,
|
|
22
|
+
friction: 8,
|
|
23
|
+
tension: 60,
|
|
24
|
+
}).start();
|
|
25
|
+
}, [value, anim]);
|
|
26
|
+
|
|
27
|
+
const onPressIn = () => {
|
|
28
|
+
if (disabled) return;
|
|
29
|
+
Animated.spring(pressAnim, {
|
|
30
|
+
toValue: 1,
|
|
31
|
+
useNativeDriver: false,
|
|
32
|
+
friction: 8,
|
|
33
|
+
tension: 100,
|
|
34
|
+
}).start();
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const onPressOut = () => {
|
|
38
|
+
Animated.spring(pressAnim, {
|
|
39
|
+
toValue: 0,
|
|
40
|
+
useNativeDriver: false,
|
|
41
|
+
friction: 8,
|
|
42
|
+
tension: 60,
|
|
43
|
+
}).start();
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const track = TRACK[size];
|
|
47
|
+
const thumb = THUMB[size];
|
|
48
|
+
const travel = track.w - thumb - PADDING * 2;
|
|
49
|
+
|
|
50
|
+
const trackBg = anim.interpolate({
|
|
51
|
+
inputRange: [0, 1],
|
|
52
|
+
outputRange: [theme.colors.border, theme.colors.primary],
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const thumbX = anim.interpolate({
|
|
56
|
+
inputRange: [0, 1],
|
|
57
|
+
outputRange: [PADDING, PADDING + travel],
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const squeezedHeight = thumb * SQUEEZE_RATIO;
|
|
61
|
+
|
|
62
|
+
const thumbHeight = pressAnim.interpolate({
|
|
63
|
+
inputRange: [0, 1],
|
|
64
|
+
outputRange: [thumb, squeezedHeight],
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const thumbRadius = pressAnim.interpolate({
|
|
68
|
+
inputRange: [0, 1],
|
|
69
|
+
outputRange: [thumb / 2, squeezedHeight / 2],
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<Pressable
|
|
74
|
+
ref={ref}
|
|
75
|
+
role="switch"
|
|
76
|
+
aria-checked={value}
|
|
77
|
+
accessibilityState={{ checked: value, disabled }}
|
|
78
|
+
onPress={() => !disabled && onValueChange(!value)}
|
|
79
|
+
onPressIn={onPressIn}
|
|
80
|
+
onPressOut={onPressOut}
|
|
81
|
+
style={[{ opacity: disabled ? 0.4 : 1 }, style]}
|
|
82
|
+
hitSlop={4}
|
|
83
|
+
testID={testID}
|
|
84
|
+
>
|
|
85
|
+
<Animated.View
|
|
86
|
+
style={{
|
|
87
|
+
width: track.w,
|
|
88
|
+
height: track.h,
|
|
89
|
+
borderRadius: track.h / 2,
|
|
90
|
+
backgroundColor: trackBg,
|
|
91
|
+
justifyContent: 'center',
|
|
92
|
+
alignItems: 'flex-start',
|
|
93
|
+
}}
|
|
94
|
+
>
|
|
95
|
+
<Animated.View
|
|
96
|
+
style={{
|
|
97
|
+
width: thumb,
|
|
98
|
+
height: thumbHeight,
|
|
99
|
+
borderRadius: thumbRadius,
|
|
100
|
+
backgroundColor: '#fff',
|
|
101
|
+
transform: [{ translateX: thumbX }],
|
|
102
|
+
shadowColor: '#000',
|
|
103
|
+
shadowOffset: { width: 0, height: 2 },
|
|
104
|
+
shadowOpacity: 0.15,
|
|
105
|
+
shadowRadius: 3,
|
|
106
|
+
elevation: 3,
|
|
107
|
+
}}
|
|
108
|
+
/>
|
|
109
|
+
</Animated.View>
|
|
110
|
+
</Pressable>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
SwitchComponent.displayName = 'Switch';
|
|
116
|
+
|
|
117
|
+
export const Switch = memo(SwitchComponent);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { StyleProp, ViewStyle } from 'react-native';
|
|
2
|
+
|
|
3
|
+
export interface SwitchProps {
|
|
4
|
+
/** Current on/off state */
|
|
5
|
+
value: boolean;
|
|
6
|
+
/** Called when the user toggles the switch */
|
|
7
|
+
onValueChange: (value: boolean) => void;
|
|
8
|
+
/** Whether the switch is disabled */
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
/** Container style */
|
|
11
|
+
style?: StyleProp<ViewStyle>;
|
|
12
|
+
/** Size variant */
|
|
13
|
+
size?: 'default' | 'sm';
|
|
14
|
+
testID?: string;
|
|
15
|
+
}
|
package/src/theme/index.ts
CHANGED
|
@@ -4,3 +4,5 @@ export { useTheme, useThemeColor, useBloomTheme } from './use-theme';
|
|
|
4
4
|
export type { Theme, ThemeColors, ThemeMode } from './types';
|
|
5
5
|
export type { AppColorName, AppColorPreset } from './color-presets';
|
|
6
6
|
export { APP_COLOR_NAMES, APP_COLOR_PRESETS, HEX_TO_APP_COLOR, hexToAppColorName } from './color-presets';
|
|
7
|
+
export { applyDarkClass } from './apply-dark-class';
|
|
8
|
+
export { setColorSchemeSafe } from './set-color-scheme-safe';
|