@praxiis/ui 0.0.1
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/dist/index.d.mts +52556 -0
- package/dist/index.d.ts +52556 -0
- package/dist/index.js +8753 -0
- package/dist/index.mjs +8777 -0
- package/package.json +70 -0
- package/src/__test-utils__/index.tsx +39 -0
- package/src/components/CalendarStrip/CalendarStrip.helpers.ts +106 -0
- package/src/components/CalendarStrip/CalendarStrip.tsx +83 -0
- package/src/components/CalendarStrip/CalendarStrip.types.ts +133 -0
- package/src/components/CalendarStrip/DayCard/DayCard.helpers.ts +44 -0
- package/src/components/CalendarStrip/DayCard/DayCard.tsx +71 -0
- package/src/components/CalendarStrip/DayCard/DayCard.types.ts +134 -0
- package/src/components/CalendarStrip/DayCard/index.ts +2 -0
- package/src/components/CalendarStrip/DayCard/useDayCardLogic.ts +45 -0
- package/src/components/CalendarStrip/index.ts +9 -0
- package/src/components/CalendarStrip/useCalendarStripLogic.ts +53 -0
- package/src/components/EmptyState/EmptyState.helpers.ts +104 -0
- package/src/components/EmptyState/EmptyState.tsx +205 -0
- package/src/components/EmptyState/EmptyState.types.ts +213 -0
- package/src/components/EmptyState/index.ts +44 -0
- package/src/components/EmptyState/useEmptyStateLogic.ts +131 -0
- package/src/components/Header/Header.helpers.ts +93 -0
- package/src/components/Header/Header.tsx +185 -0
- package/src/components/Header/Header.types.ts +153 -0
- package/src/components/Header/index.ts +44 -0
- package/src/components/Header/useHeaderLogic.ts +146 -0
- package/src/components/ScheduleItem/ScheduleItem/ScheduleItem.helpers.ts +50 -0
- package/src/components/ScheduleItem/ScheduleItem/ScheduleItem.tsx +78 -0
- package/src/components/ScheduleItem/ScheduleItem/ScheduleItem.types.ts +99 -0
- package/src/components/ScheduleItem/ScheduleItem/index.ts +16 -0
- package/src/components/ScheduleItem/ScheduleItem/useScheduleItemLogic.ts +31 -0
- package/src/components/ScheduleItem/index.ts +15 -0
- package/src/components/index.ts +40 -0
- package/src/core/index.ts +34 -0
- package/src/core/restyle/RestyleThemeProviderWrapper.tsx +31 -0
- package/src/core/restyle/index.ts +38 -0
- package/src/core/restyle/restylePresetRegistry.ts +195 -0
- package/src/core/restyle/restyleTheme.ts +1352 -0
- package/src/core/restyle/restyleTypes.ts +8 -0
- package/src/core/restyle/useRestyleTheme.ts +10 -0
- package/src/hooks/animations/index.ts +3 -0
- package/src/hooks/animations/useAnimatedValue.ts +10 -0
- package/src/hooks/animations/useEntranceAnimation.ts +106 -0
- package/src/hooks/animations/usePulseAnimation.ts +63 -0
- package/src/hooks/index.ts +30 -0
- package/src/hooks/useReducedMotion.ts +60 -0
- package/src/i18n/index.ts +2 -0
- package/src/i18n/labels/en.ts +120 -0
- package/src/i18n/labels/es.ts +120 -0
- package/src/i18n/labels/index.ts +6 -0
- package/src/i18n/labels/types.ts +165 -0
- package/src/index.tsx +215 -0
- package/src/primitives/actions/Button/Button.helpers.ts +243 -0
- package/src/primitives/actions/Button/Button.tsx +198 -0
- package/src/primitives/actions/Button/Button.types.ts +207 -0
- package/src/primitives/actions/Button/index.ts +41 -0
- package/src/primitives/actions/Button/useButtonLogic.ts +160 -0
- package/src/primitives/actions/IconButton/IconButton.helpers.ts +235 -0
- package/src/primitives/actions/IconButton/IconButton.tsx +177 -0
- package/src/primitives/actions/IconButton/IconButton.types.ts +273 -0
- package/src/primitives/actions/IconButton/index.ts +30 -0
- package/src/primitives/actions/IconButton/useIconButtonLogic.ts +172 -0
- package/src/primitives/actions/index.ts +20 -0
- package/src/primitives/content/Avatar/Avatar.helpers.ts +177 -0
- package/src/primitives/content/Avatar/Avatar.tsx +199 -0
- package/src/primitives/content/Avatar/Avatar.types.ts +222 -0
- package/src/primitives/content/Avatar/index.ts +46 -0
- package/src/primitives/content/Avatar/useAvatarLogic.ts +149 -0
- package/src/primitives/content/Badge/Badge.helpers.ts +175 -0
- package/src/primitives/content/Badge/Badge.tsx +174 -0
- package/src/primitives/content/Badge/Badge.types.ts +223 -0
- package/src/primitives/content/Badge/index.ts +40 -0
- package/src/primitives/content/Badge/useBadgeLogic.ts +128 -0
- package/src/primitives/content/Card/Card.helpers.ts +27 -0
- package/src/primitives/content/Card/Card.tsx +123 -0
- package/src/primitives/content/Card/Card.types.ts +95 -0
- package/src/primitives/content/Card/index.ts +20 -0
- package/src/primitives/content/Card/useCardLogic.ts +48 -0
- package/src/primitives/content/Chip/Chip.helpers.ts +304 -0
- package/src/primitives/content/Chip/Chip.tsx +205 -0
- package/src/primitives/content/Chip/Chip.types.ts +234 -0
- package/src/primitives/content/Chip/index.ts +47 -0
- package/src/primitives/content/Chip/useChipLogic.ts +167 -0
- package/src/primitives/content/Icon/Icon.helpers.ts +54 -0
- package/src/primitives/content/Icon/Icon.tsx +110 -0
- package/src/primitives/content/Icon/Icon.types.ts +95 -0
- package/src/primitives/content/Icon/index.ts +20 -0
- package/src/primitives/content/Icon/useIconLogic.ts +73 -0
- package/src/primitives/content/index.ts +45 -0
- package/src/primitives/feedback/ProgressBar/ProgressBar.helpers.ts +122 -0
- package/src/primitives/feedback/ProgressBar/ProgressBar.tsx +154 -0
- package/src/primitives/feedback/ProgressBar/ProgressBar.types.ts +178 -0
- package/src/primitives/feedback/ProgressBar/index.ts +17 -0
- package/src/primitives/feedback/ProgressBar/useProgressBarLogic.ts +120 -0
- package/src/primitives/feedback/Skeleton/Skeleton.helpers.ts +145 -0
- package/src/primitives/feedback/Skeleton/Skeleton.tsx +155 -0
- package/src/primitives/feedback/Skeleton/Skeleton.types.ts +223 -0
- package/src/primitives/feedback/Skeleton/index.ts +44 -0
- package/src/primitives/feedback/Skeleton/useSkeletonLogic.ts +125 -0
- package/src/primitives/feedback/Spinner/Spinner.helpers.ts +40 -0
- package/src/primitives/feedback/Spinner/Spinner.tsx +105 -0
- package/src/primitives/feedback/Spinner/Spinner.types.ts +114 -0
- package/src/primitives/feedback/Spinner/index.ts +18 -0
- package/src/primitives/feedback/Spinner/useSpinnerLogic.ts +84 -0
- package/src/primitives/feedback/Toast/Toast.helpers.ts +163 -0
- package/src/primitives/feedback/Toast/Toast.tsx +190 -0
- package/src/primitives/feedback/Toast/Toast.types.ts +270 -0
- package/src/primitives/feedback/Toast/ToastContext.tsx +96 -0
- package/src/primitives/feedback/Toast/ToastProvider.tsx +241 -0
- package/src/primitives/feedback/Toast/index.ts +59 -0
- package/src/primitives/feedback/Toast/useToastLogic.ts +112 -0
- package/src/primitives/feedback/index.ts +45 -0
- package/src/primitives/index.ts +158 -0
- package/src/primitives/inputs/Checkbox/Checkbox.helpers.ts +132 -0
- package/src/primitives/inputs/Checkbox/Checkbox.tsx +150 -0
- package/src/primitives/inputs/Checkbox/Checkbox.types.ts +106 -0
- package/src/primitives/inputs/Checkbox/index.ts +30 -0
- package/src/primitives/inputs/Checkbox/useCheckboxLogic.ts +121 -0
- package/src/primitives/inputs/RadioButton/RadioButton.helpers.ts +123 -0
- package/src/primitives/inputs/RadioButton/RadioButton.tsx +159 -0
- package/src/primitives/inputs/RadioButton/RadioButton.types.ts +106 -0
- package/src/primitives/inputs/RadioButton/index.ts +25 -0
- package/src/primitives/inputs/RadioButton/useRadioButtonLogic.ts +117 -0
- package/src/primitives/inputs/SegmentedControl/SegmentedControl.helpers.ts +174 -0
- package/src/primitives/inputs/SegmentedControl/SegmentedControl.tsx +224 -0
- package/src/primitives/inputs/SegmentedControl/SegmentedControl.types.ts +187 -0
- package/src/primitives/inputs/SegmentedControl/index.ts +39 -0
- package/src/primitives/inputs/SegmentedControl/useSegmentedControlLogic.ts +151 -0
- package/src/primitives/inputs/SelectSheet/SelectSheet.helpers.ts +147 -0
- package/src/primitives/inputs/SelectSheet/SelectSheet.tsx +247 -0
- package/src/primitives/inputs/SelectSheet/SelectSheet.types.ts +196 -0
- package/src/primitives/inputs/SelectSheet/SelectSheetOption.tsx +177 -0
- package/src/primitives/inputs/SelectSheet/index.ts +48 -0
- package/src/primitives/inputs/SelectSheet/useSelectSheetLogic.ts +309 -0
- package/src/primitives/inputs/Switch/Switch.helpers.ts +109 -0
- package/src/primitives/inputs/Switch/Switch.tsx +191 -0
- package/src/primitives/inputs/Switch/Switch.types.ts +154 -0
- package/src/primitives/inputs/Switch/index.ts +40 -0
- package/src/primitives/inputs/Switch/useSwitchLogic.ts +192 -0
- package/src/primitives/inputs/TextInput/TextInput.helpers.ts +206 -0
- package/src/primitives/inputs/TextInput/TextInput.tsx +392 -0
- package/src/primitives/inputs/TextInput/TextInput.types.ts +216 -0
- package/src/primitives/inputs/TextInput/index.ts +37 -0
- package/src/primitives/inputs/TextInput/useTextInputLogic.ts +195 -0
- package/src/primitives/inputs/index.ts +52 -0
- package/src/primitives/layout/AnimatedBox.tsx +44 -0
- package/src/primitives/layout/Box.tsx +71 -0
- package/src/primitives/layout/Divider/Divider.helpers.ts +115 -0
- package/src/primitives/layout/Divider/Divider.tsx +139 -0
- package/src/primitives/layout/Divider/Divider.types.ts +178 -0
- package/src/primitives/layout/Divider/index.ts +24 -0
- package/src/primitives/layout/Divider/useDividerLogic.ts +109 -0
- package/src/primitives/layout/FlatList.tsx +66 -0
- package/src/primitives/layout/Pressable.tsx +74 -0
- package/src/primitives/layout/ScrollView.tsx +63 -0
- package/src/primitives/layout/Stack.tsx +69 -0
- package/src/primitives/layout/index.ts +40 -0
- package/src/primitives/navigation/index.ts +6 -0
- package/src/primitives/overlays/Modal/Modal.helpers.ts +31 -0
- package/src/primitives/overlays/Modal/Modal.tsx +264 -0
- package/src/primitives/overlays/Modal/Modal.types.ts +193 -0
- package/src/primitives/overlays/Modal/index.ts +43 -0
- package/src/primitives/overlays/Modal/useModalLogic.ts +103 -0
- package/src/primitives/overlays/index.ts +12 -0
- package/src/primitives/typography/Text.tsx +51 -0
- package/src/primitives/typography/index.ts +1 -0
- package/src/provider/DesignSystemContext.ts +22 -0
- package/src/provider/DesignSystemProvider.tsx +121 -0
- package/src/provider/index.ts +7 -0
- package/src/providers/ThemeProvider/createTheme.ts +304 -0
- package/src/providers/ThemeProvider/defaultTheme.ts +70 -0
- package/src/providers/ThemeProvider/index.ts +34 -0
- package/src/providers/ThemeProvider/types.ts +249 -0
- package/src/providers/index.ts +29 -0
- package/src/tokens/colors.ts +371 -0
- package/src/tokens/index.ts +145 -0
- package/src/tokens/motion.ts +176 -0
- package/src/tokens/radii.ts +82 -0
- package/src/tokens/scales.ts +588 -0
- package/src/tokens/shadows.ts +190 -0
- package/src/tokens/spacing.ts +140 -0
- package/src/tokens/tokens.json +207 -0
- package/src/tokens/typography.ts +251 -0
- package/src/types.ts +50 -0
- package/src/utils/accessibility.ts +169 -0
- package/src/utils/index.ts +25 -0
- package/src/utils/platform.ts +72 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Switch Component
|
|
3
|
+
*
|
|
4
|
+
* @description Toggle switch for boolean settings - Atom
|
|
5
|
+
*
|
|
6
|
+
* Switch provides a sliding toggle control for enabling/disabling features.
|
|
7
|
+
* Uses smooth spring animations for thumb position and track color.
|
|
8
|
+
*
|
|
9
|
+
* ## Geometry (W:H ≈ 1.7:1)
|
|
10
|
+
* - Thumb = TrackHeight - (SWITCH_THUMB_INSET × 2)
|
|
11
|
+
* - SWITCH_THUMB_INSET = 2px (padding between thumb and track edge)
|
|
12
|
+
*
|
|
13
|
+
* ## Size Scale
|
|
14
|
+
* | Size | Track W | Track H | Thumb | W:H Ratio | hitSlop | Touch |
|
|
15
|
+
* |------|---------|---------|-------|-----------|---------|-------|
|
|
16
|
+
* | sm | 40px | 24px | 20px | 1.67:1 | 10px | 44px |
|
|
17
|
+
* | md | 48px | 28px | 24px | 1.71:1 | 8px | 44px |
|
|
18
|
+
* | lg | 56px | 32px | 28px | 1.75:1 | 6px | 44px |
|
|
19
|
+
*
|
|
20
|
+
* ## Animation
|
|
21
|
+
* - Uses spring physics for natural feel
|
|
22
|
+
* - Track color interpolates between off (gray) and on (trackColor)
|
|
23
|
+
* - Thumb scales slightly during transition for tactile feedback
|
|
24
|
+
*
|
|
25
|
+
* ## Features
|
|
26
|
+
* - Responsive size prop (phone/tablet breakpoints)
|
|
27
|
+
* - Customizable track and thumb colors
|
|
28
|
+
* - Smooth animated transitions
|
|
29
|
+
* - Full accessibility support (switch role)
|
|
30
|
+
*
|
|
31
|
+
* @performance
|
|
32
|
+
* - Wrapped with React.memo() to prevent unnecessary re-renders
|
|
33
|
+
* - Uses React Native's native driver for smooth 60fps animations
|
|
34
|
+
* - Spring animations with configurable friction/tension for natural feel
|
|
35
|
+
* - Color interpolation cached in Animated.Value for efficient transitions
|
|
36
|
+
* - Respects reduced motion preferences for accessibility
|
|
37
|
+
*
|
|
38
|
+
* @see Switch.types.ts - Type definitions
|
|
39
|
+
* @see Switch.helpers.ts - Dimension and style functions
|
|
40
|
+
* @see Switch.a11y.ts - Accessibility prop generation
|
|
41
|
+
* @see tokens/scales.ts - SWITCH_DIMENSIONS, SWITCH_THUMB_INSET
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* // Basic usage
|
|
45
|
+
* <Switch value={isEnabled} onValueChange={setIsEnabled} />
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* // With label and accessibility
|
|
49
|
+
* <Switch
|
|
50
|
+
* value={isEnabled}
|
|
51
|
+
* onValueChange={setIsEnabled}
|
|
52
|
+
* size={{ phone: "sm", tablet: "lg" }}
|
|
53
|
+
* trackColor="accentPrimary"
|
|
54
|
+
* label="Enable notifications"
|
|
55
|
+
* accessibilityLabel="Enable push notifications"
|
|
56
|
+
* accessibilityHint="Toggle to receive push notifications"
|
|
57
|
+
* />
|
|
58
|
+
*/
|
|
59
|
+
|
|
60
|
+
import {
|
|
61
|
+
createRestyleComponent,
|
|
62
|
+
createVariant,
|
|
63
|
+
layout,
|
|
64
|
+
spacing,
|
|
65
|
+
spacingShorthand,
|
|
66
|
+
} from "@shopify/restyle";
|
|
67
|
+
import React, { forwardRef, memo, useMemo } from "react";
|
|
68
|
+
import { Pressable, View } from "react-native";
|
|
69
|
+
import { RestyleTheme } from "../../../core/restyle";
|
|
70
|
+
import { SHADOW_TOKENS } from "../../../tokens/scales";
|
|
71
|
+
import { AnimatedBox } from "../../layout";
|
|
72
|
+
import { BaseSwitchProps, SwitchProps } from "./Switch.types";
|
|
73
|
+
import { useSwitchLogic } from "./useSwitchLogic";
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Base Switch container with Restyle variant support.
|
|
77
|
+
* @internal
|
|
78
|
+
*/
|
|
79
|
+
const BaseSwitch = createRestyleComponent<BaseSwitchProps, RestyleTheme>(
|
|
80
|
+
[
|
|
81
|
+
createVariant({ themeKey: "switchSizes", property: "size" }),
|
|
82
|
+
layout,
|
|
83
|
+
spacing,
|
|
84
|
+
spacingShorthand,
|
|
85
|
+
],
|
|
86
|
+
Pressable,
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const SwitchComponent = forwardRef<View, SwitchProps>(function Switch(
|
|
90
|
+
{
|
|
91
|
+
value,
|
|
92
|
+
onValueChange,
|
|
93
|
+
size = "md",
|
|
94
|
+
disabled = false,
|
|
95
|
+
trackColor = "accentPrimary",
|
|
96
|
+
thumbColor = "white",
|
|
97
|
+
label,
|
|
98
|
+
accessibilityLabel,
|
|
99
|
+
accessibilityHint,
|
|
100
|
+
testID,
|
|
101
|
+
...rest
|
|
102
|
+
},
|
|
103
|
+
ref,
|
|
104
|
+
) {
|
|
105
|
+
const {
|
|
106
|
+
trackWidth,
|
|
107
|
+
trackHeight,
|
|
108
|
+
thumbSize,
|
|
109
|
+
thumbTranslateX,
|
|
110
|
+
trackBackgroundColor,
|
|
111
|
+
thumbScale,
|
|
112
|
+
finalThumbColor,
|
|
113
|
+
handlePress,
|
|
114
|
+
containerStyle,
|
|
115
|
+
hitSlop,
|
|
116
|
+
a11yProps,
|
|
117
|
+
} = useSwitchLogic({
|
|
118
|
+
value,
|
|
119
|
+
disabled,
|
|
120
|
+
size,
|
|
121
|
+
trackColor,
|
|
122
|
+
thumbColor,
|
|
123
|
+
onValueChange,
|
|
124
|
+
label,
|
|
125
|
+
accessibilityLabel,
|
|
126
|
+
accessibilityHint,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
const thumbPadding = 2;
|
|
130
|
+
const maxTranslateX = trackWidth - thumbSize - thumbPadding * 2;
|
|
131
|
+
const borderRadius = trackHeight / 2;
|
|
132
|
+
|
|
133
|
+
const trackStyle = useMemo(
|
|
134
|
+
() => ({
|
|
135
|
+
borderRadius,
|
|
136
|
+
backgroundColor: trackBackgroundColor,
|
|
137
|
+
}),
|
|
138
|
+
[borderRadius, trackBackgroundColor],
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const thumbStyle = useMemo(
|
|
142
|
+
() => ({
|
|
143
|
+
...SHADOW_TOKENS.switchThumb,
|
|
144
|
+
borderRadius: thumbSize / 2,
|
|
145
|
+
backgroundColor: finalThumbColor,
|
|
146
|
+
transform: [
|
|
147
|
+
{
|
|
148
|
+
translateX: thumbTranslateX.interpolate({
|
|
149
|
+
inputRange: [0, 1],
|
|
150
|
+
outputRange: [thumbPadding, maxTranslateX],
|
|
151
|
+
}),
|
|
152
|
+
},
|
|
153
|
+
{ scale: thumbScale },
|
|
154
|
+
],
|
|
155
|
+
}),
|
|
156
|
+
[
|
|
157
|
+
thumbSize,
|
|
158
|
+
finalThumbColor,
|
|
159
|
+
thumbTranslateX,
|
|
160
|
+
thumbPadding,
|
|
161
|
+
maxTranslateX,
|
|
162
|
+
thumbScale,
|
|
163
|
+
],
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
return (
|
|
167
|
+
<BaseSwitch
|
|
168
|
+
ref={ref}
|
|
169
|
+
size={size}
|
|
170
|
+
onPress={handlePress}
|
|
171
|
+
disabled={disabled}
|
|
172
|
+
hitSlop={hitSlop}
|
|
173
|
+
testID={testID}
|
|
174
|
+
style={containerStyle}
|
|
175
|
+
{...a11yProps}
|
|
176
|
+
{...rest}
|
|
177
|
+
>
|
|
178
|
+
<AnimatedBox
|
|
179
|
+
width={trackWidth}
|
|
180
|
+
height={trackHeight}
|
|
181
|
+
justifyContent="center"
|
|
182
|
+
style={trackStyle}
|
|
183
|
+
>
|
|
184
|
+
<AnimatedBox width={thumbSize} height={thumbSize} style={thumbStyle} />
|
|
185
|
+
</AnimatedBox>
|
|
186
|
+
</BaseSwitch>
|
|
187
|
+
);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
export const Switch = memo(SwitchComponent);
|
|
191
|
+
Switch.displayName = "Switch";
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { BoxProps, ResponsiveValue, VariantProps } from "@shopify/restyle";
|
|
2
|
+
import { Animated, Insets, PressableProps, View, ViewStyle } from "react-native";
|
|
3
|
+
import { RestyleTheme } from "../../../core/restyle";
|
|
4
|
+
import { RestyleColor } from "../../../types";
|
|
5
|
+
|
|
6
|
+
type SwitchSizeVariantProps = VariantProps<RestyleTheme, "switchSizes", "size">;
|
|
7
|
+
type SwitchRestyleProps = SwitchSizeVariantProps & BoxProps<RestyleTheme>;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Size variant for the Switch component.
|
|
11
|
+
*
|
|
12
|
+
* - `sm`: 40×24px track - Compact UIs, dense lists
|
|
13
|
+
* - `md`: 48×28px track - Default mobile size
|
|
14
|
+
* - `lg`: 56×32px track - Tablets, emphasized settings
|
|
15
|
+
*/
|
|
16
|
+
export type SwitchSize = Exclude<keyof RestyleTheme["switchSizes"], "defaults">;
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
/** Ref type for the Switch component — the underlying React Native View instance. */
|
|
20
|
+
export type SwitchRef = View;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Base props for the Switch container component.
|
|
24
|
+
*/
|
|
25
|
+
export type BaseSwitchProps = SwitchRestyleProps & PressableProps;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Props for the Switch component.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* // Basic usage
|
|
32
|
+
* <Switch value={isEnabled} onValueChange={setIsEnabled} />
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* // With label and accessibility
|
|
36
|
+
* <Switch
|
|
37
|
+
* value={isEnabled}
|
|
38
|
+
* onValueChange={setIsEnabled}
|
|
39
|
+
* size={{ phone: "sm", tablet: "lg" }}
|
|
40
|
+
* trackColor="accentPrimary"
|
|
41
|
+
* label="Enable notifications"
|
|
42
|
+
* accessibilityLabel="Enable push notifications"
|
|
43
|
+
* accessibilityHint="Toggle to receive push notifications"
|
|
44
|
+
* />
|
|
45
|
+
*/
|
|
46
|
+
export interface SwitchProps extends SwitchRestyleProps {
|
|
47
|
+
/** Current value of the switch */
|
|
48
|
+
value: boolean;
|
|
49
|
+
/** Callback when the value changes */
|
|
50
|
+
onValueChange: (value: boolean) => void;
|
|
51
|
+
/** Size of the switch (supports responsive values) */
|
|
52
|
+
size?: ResponsiveValue<SwitchSize, RestyleTheme["breakpoints"]>;
|
|
53
|
+
/** Whether the switch is disabled */
|
|
54
|
+
disabled?: boolean;
|
|
55
|
+
/** Color for the track when enabled */
|
|
56
|
+
trackColor?: RestyleColor;
|
|
57
|
+
/** Color for the thumb */
|
|
58
|
+
thumbColor?: RestyleColor;
|
|
59
|
+
/** Label text for the switch */
|
|
60
|
+
label?: string;
|
|
61
|
+
/** Accessibility label (required for screen readers) */
|
|
62
|
+
accessibilityLabel?: string;
|
|
63
|
+
/** Accessibility hint for additional context */
|
|
64
|
+
accessibilityHint?: string;
|
|
65
|
+
/** Test ID for testing */
|
|
66
|
+
testID?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Parameters for the useSwitchLogic hook.
|
|
71
|
+
*/
|
|
72
|
+
export interface UseSwitchLogicParams {
|
|
73
|
+
value: boolean;
|
|
74
|
+
disabled: boolean;
|
|
75
|
+
size: ResponsiveValue<SwitchSize, RestyleTheme["breakpoints"]>;
|
|
76
|
+
trackColor: RestyleColor;
|
|
77
|
+
thumbColor: RestyleColor;
|
|
78
|
+
onValueChange: (value: boolean) => void;
|
|
79
|
+
label?: string;
|
|
80
|
+
accessibilityLabel?: string;
|
|
81
|
+
accessibilityHint?: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Return value from the useSwitchLogic hook.
|
|
86
|
+
*/
|
|
87
|
+
export interface UseSwitchLogicReturn {
|
|
88
|
+
/** Track width based on size */
|
|
89
|
+
trackWidth: number;
|
|
90
|
+
/** Track height based on size */
|
|
91
|
+
trackHeight: number;
|
|
92
|
+
/** Thumb size based on track height */
|
|
93
|
+
thumbSize: number;
|
|
94
|
+
/** Animated value for thumb position */
|
|
95
|
+
thumbTranslateX: Animated.Value;
|
|
96
|
+
/** Animated interpolation for track background color */
|
|
97
|
+
trackBackgroundColor: Animated.AnimatedInterpolation<string | number> | string;
|
|
98
|
+
/** Animated value for thumb scale */
|
|
99
|
+
thumbScale: Animated.Value;
|
|
100
|
+
/** Final thumb color based on disabled state */
|
|
101
|
+
finalThumbColor: string;
|
|
102
|
+
/** Press handler callback */
|
|
103
|
+
handlePress: () => void;
|
|
104
|
+
/** Container style with opacity */
|
|
105
|
+
containerStyle: ViewStyle;
|
|
106
|
+
/** Hit slop for touch target */
|
|
107
|
+
hitSlop: Insets;
|
|
108
|
+
/** Accessibility props */
|
|
109
|
+
a11yProps: SwitchA11yProps;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Parameters for the getSwitchA11y function.
|
|
114
|
+
*/
|
|
115
|
+
export interface SwitchA11yParams {
|
|
116
|
+
label?: string;
|
|
117
|
+
accessibilityLabel?: string;
|
|
118
|
+
accessibilityHint?: string;
|
|
119
|
+
value: boolean;
|
|
120
|
+
disabled: boolean;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Accessibility props returned by getSwitchA11y.
|
|
125
|
+
*/
|
|
126
|
+
export interface SwitchA11yProps {
|
|
127
|
+
accessibilityLabel: string;
|
|
128
|
+
accessibilityHint?: string;
|
|
129
|
+
accessibilityRole: "switch";
|
|
130
|
+
accessibilityState: { checked: boolean; disabled: boolean };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Parameters for resolveSwitchStyle function.
|
|
135
|
+
*/
|
|
136
|
+
export interface ResolveSwitchStyleParams {
|
|
137
|
+
disabled: boolean;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Result from resolveSwitchStyle function.
|
|
142
|
+
*/
|
|
143
|
+
export interface SwitchStyleResult {
|
|
144
|
+
opacity: number;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Dimensions for the switch track and thumb.
|
|
149
|
+
*/
|
|
150
|
+
export interface SwitchDimensions {
|
|
151
|
+
trackWidth: number;
|
|
152
|
+
trackHeight: number;
|
|
153
|
+
thumbSize: number;
|
|
154
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Switch Component Module
|
|
3
|
+
*
|
|
4
|
+
* A toggle switch component for boolean values.
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - Three sizes: sm, md, lg (supports responsive values)
|
|
8
|
+
* - Customizable track and thumb colors
|
|
9
|
+
* - Smooth animated transitions
|
|
10
|
+
* - Full accessibility support
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* import { Switch } from '@/src/design-system/primitives/inputs/Switch';
|
|
14
|
+
*
|
|
15
|
+
* // Basic usage
|
|
16
|
+
* <Switch value={isEnabled} onValueChange={setIsEnabled} />
|
|
17
|
+
*
|
|
18
|
+
* // With label and accessibility
|
|
19
|
+
* <Switch
|
|
20
|
+
* value={isEnabled}
|
|
21
|
+
* onValueChange={setIsEnabled}
|
|
22
|
+
* size={{ phone: "sm", tablet: "lg" }}
|
|
23
|
+
* trackColor="accentPrimary"
|
|
24
|
+
* label="Enable notifications"
|
|
25
|
+
* accessibilityLabel="Enable push notifications"
|
|
26
|
+
* accessibilityHint="Toggle to receive push notifications"
|
|
27
|
+
* />
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
export { Switch } from "./Switch";
|
|
31
|
+
|
|
32
|
+
export type {
|
|
33
|
+
SwitchProps,
|
|
34
|
+
SwitchRef,
|
|
35
|
+
SwitchSize,
|
|
36
|
+
SwitchA11yParams,
|
|
37
|
+
SwitchA11yProps,
|
|
38
|
+
} from "./Switch.types";
|
|
39
|
+
|
|
40
|
+
export { getSwitchA11y } from "./Switch.a11y";
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { useResponsiveProp } from "@shopify/restyle";
|
|
2
|
+
import { useCallback, useEffect, useMemo } from "react";
|
|
3
|
+
import { Animated, ViewStyle } from "react-native";
|
|
4
|
+
import { useRestyleTheme } from "../../../core/restyle";
|
|
5
|
+
import { BaseThemeColor } from "../../../types";
|
|
6
|
+
import { useReducedMotion } from "../../../hooks/useReducedMotion";
|
|
7
|
+
import { useAnimatedValue } from "../../../hooks/animations";
|
|
8
|
+
import { ANIMATION_TOKENS } from "../../../tokens/scales";
|
|
9
|
+
import { getSwitchA11y } from "./Switch.a11y";
|
|
10
|
+
import {
|
|
11
|
+
getSwitchDimensions,
|
|
12
|
+
getSwitchHitSlop,
|
|
13
|
+
resolveSwitchStyle,
|
|
14
|
+
} from "./Switch.helpers";
|
|
15
|
+
import { SwitchSize, UseSwitchLogicParams, UseSwitchLogicReturn } from "./Switch.types";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Orchestrates Switch rendering logic and state management.
|
|
19
|
+
*
|
|
20
|
+
* Handles:
|
|
21
|
+
* - Responsive size resolution
|
|
22
|
+
* - Dimension calculations (track width, height, thumb size)
|
|
23
|
+
* - Animated transitions for thumb position and scale
|
|
24
|
+
* - Reduced motion support (instant state changes)
|
|
25
|
+
* - Track and thumb color interpolation
|
|
26
|
+
* - Hit slop calculation for 44px minimum touch target
|
|
27
|
+
* - Press handler with disabled state check
|
|
28
|
+
* - Accessibility props generation with on/off state
|
|
29
|
+
*
|
|
30
|
+
* @param params - Configuration object for switch behavior
|
|
31
|
+
* @param params.value - Current switch state (on/off)
|
|
32
|
+
* @param params.disabled - Whether switch is disabled
|
|
33
|
+
* @param params.size - Size variant ('sm' | 'md' | 'lg')
|
|
34
|
+
* @param params.trackColor - Theme color token for track (when on)
|
|
35
|
+
* @param params.thumbColor - Theme color token for thumb
|
|
36
|
+
* @param params.onValueChange - Callback when value changes
|
|
37
|
+
* @param params.label - Associated label text
|
|
38
|
+
* @param params.accessibilityLabel - Custom a11y label
|
|
39
|
+
* @param params.accessibilityHint - Describes action result
|
|
40
|
+
*
|
|
41
|
+
* @returns Computed values for rendering Switch
|
|
42
|
+
* @returns {number} trackWidth - Track width in pixels
|
|
43
|
+
* @returns {number} trackHeight - Track height in pixels
|
|
44
|
+
* @returns {number} thumbSize - Thumb size in pixels
|
|
45
|
+
* @returns {Animated.Value} thumbTranslateX - Animated thumb position (0-1)
|
|
46
|
+
* @returns {Animated.Value} trackBackgroundColor - Animated track color
|
|
47
|
+
* @returns {Animated.Value} thumbScale - Animated thumb scale (0.9-1)
|
|
48
|
+
* @returns {string} finalThumbColor - Thumb color token
|
|
49
|
+
* @returns {function} handlePress - Press handler
|
|
50
|
+
* @returns {ViewStyle} containerStyle - Dynamic container styles
|
|
51
|
+
* @returns {Insets} hitSlop - Touch target expansion
|
|
52
|
+
* @returns {object} a11yProps - Accessibility props with on/off state
|
|
53
|
+
*
|
|
54
|
+
* @performance
|
|
55
|
+
* - Uses React Native's native driver for smooth 60fps animations
|
|
56
|
+
* - Spring animations with configurable friction/tension
|
|
57
|
+
* - Respects reduced motion preferences for accessibility
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* const {
|
|
61
|
+
* trackWidth,
|
|
62
|
+
* thumbTranslateX,
|
|
63
|
+
* trackBackgroundColor,
|
|
64
|
+
* handlePress,
|
|
65
|
+
* a11yProps
|
|
66
|
+
* } = useSwitchLogic({
|
|
67
|
+
* value: true,
|
|
68
|
+
* size: "md",
|
|
69
|
+
* trackColor: "accentPrimary",
|
|
70
|
+
* thumbColor: "white",
|
|
71
|
+
* onValueChange: setValue,
|
|
72
|
+
* label: "Enable notifications",
|
|
73
|
+
* });
|
|
74
|
+
*/
|
|
75
|
+
export function useSwitchLogic(
|
|
76
|
+
params: UseSwitchLogicParams
|
|
77
|
+
): UseSwitchLogicReturn {
|
|
78
|
+
const {
|
|
79
|
+
value,
|
|
80
|
+
disabled,
|
|
81
|
+
size,
|
|
82
|
+
trackColor,
|
|
83
|
+
thumbColor,
|
|
84
|
+
onValueChange,
|
|
85
|
+
label,
|
|
86
|
+
accessibilityLabel,
|
|
87
|
+
accessibilityHint,
|
|
88
|
+
} = params;
|
|
89
|
+
|
|
90
|
+
const theme = useRestyleTheme();
|
|
91
|
+
const resolvedSize = (useResponsiveProp(size) ?? "md") as SwitchSize;
|
|
92
|
+
const prefersReducedMotion = useReducedMotion();
|
|
93
|
+
|
|
94
|
+
// Get dimensions based on resolved size
|
|
95
|
+
const { trackWidth, trackHeight, thumbSize } =
|
|
96
|
+
getSwitchDimensions(resolvedSize);
|
|
97
|
+
|
|
98
|
+
// Get hit slop based on resolved size
|
|
99
|
+
const hitSlop = getSwitchHitSlop(resolvedSize);
|
|
100
|
+
|
|
101
|
+
// Animated values for smooth transitions
|
|
102
|
+
const thumbTranslateX = useAnimatedValue(value ? 1 : 0);
|
|
103
|
+
const thumbScale = useAnimatedValue(1);
|
|
104
|
+
|
|
105
|
+
// Animate on value change
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
if (prefersReducedMotion) {
|
|
108
|
+
// Set immediate state without animation for reduced motion
|
|
109
|
+
thumbTranslateX.setValue(value ? 1 : 0);
|
|
110
|
+
thumbScale.setValue(1);
|
|
111
|
+
} else {
|
|
112
|
+
// Use animated spring transitions
|
|
113
|
+
Animated.parallel([
|
|
114
|
+
Animated.spring(thumbTranslateX, {
|
|
115
|
+
toValue: value ? 1 : 0,
|
|
116
|
+
useNativeDriver: false,
|
|
117
|
+
friction: ANIMATION_TOKENS.switch.toggle.friction,
|
|
118
|
+
tension: ANIMATION_TOKENS.switch.toggle.tension,
|
|
119
|
+
}),
|
|
120
|
+
Animated.sequence([
|
|
121
|
+
Animated.spring(thumbScale, {
|
|
122
|
+
toValue: 0.9,
|
|
123
|
+
useNativeDriver: false,
|
|
124
|
+
friction: ANIMATION_TOKENS.switch.thumbScale.friction,
|
|
125
|
+
tension: ANIMATION_TOKENS.switch.thumbScale.tension,
|
|
126
|
+
}),
|
|
127
|
+
Animated.spring(thumbScale, {
|
|
128
|
+
toValue: 1,
|
|
129
|
+
useNativeDriver: false,
|
|
130
|
+
friction: ANIMATION_TOKENS.switch.thumbScale.friction,
|
|
131
|
+
tension: ANIMATION_TOKENS.switch.thumbScale.tension,
|
|
132
|
+
}),
|
|
133
|
+
]),
|
|
134
|
+
]).start();
|
|
135
|
+
}
|
|
136
|
+
}, [value, thumbTranslateX, thumbScale, prefersReducedMotion]);
|
|
137
|
+
|
|
138
|
+
// Animated track background color
|
|
139
|
+
const trackBackgroundColor = disabled
|
|
140
|
+
? theme.colors.interactiveDisabled
|
|
141
|
+
: thumbTranslateX.interpolate({
|
|
142
|
+
inputRange: [0, 1],
|
|
143
|
+
outputRange: [
|
|
144
|
+
theme.colors.borderDefault,
|
|
145
|
+
theme.colors[trackColor as BaseThemeColor] ?? theme.colors.accentPrimary,
|
|
146
|
+
],
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// Final thumb color
|
|
150
|
+
const finalThumbColor = disabled
|
|
151
|
+
? theme.colors.textDisabled
|
|
152
|
+
: theme.colors[thumbColor as BaseThemeColor] ?? theme.colors.white;
|
|
153
|
+
|
|
154
|
+
// Press handler
|
|
155
|
+
const handlePress = useCallback(() => {
|
|
156
|
+
if (!disabled) {
|
|
157
|
+
onValueChange(!value);
|
|
158
|
+
}
|
|
159
|
+
}, [disabled, value, onValueChange]);
|
|
160
|
+
|
|
161
|
+
// Container style with opacity
|
|
162
|
+
const containerStyle: ViewStyle = {
|
|
163
|
+
opacity: resolveSwitchStyle({ disabled }).opacity,
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// Accessibility props
|
|
167
|
+
const a11yProps = useMemo(
|
|
168
|
+
() =>
|
|
169
|
+
getSwitchA11y({
|
|
170
|
+
label,
|
|
171
|
+
accessibilityLabel,
|
|
172
|
+
accessibilityHint,
|
|
173
|
+
value,
|
|
174
|
+
disabled,
|
|
175
|
+
}),
|
|
176
|
+
[label, accessibilityLabel, accessibilityHint, value, disabled]
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
trackWidth,
|
|
181
|
+
trackHeight,
|
|
182
|
+
thumbSize,
|
|
183
|
+
thumbTranslateX,
|
|
184
|
+
trackBackgroundColor,
|
|
185
|
+
thumbScale,
|
|
186
|
+
finalThumbColor,
|
|
187
|
+
handlePress,
|
|
188
|
+
containerStyle,
|
|
189
|
+
hitSlop,
|
|
190
|
+
a11yProps,
|
|
191
|
+
};
|
|
192
|
+
}
|