@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.
Files changed (187) hide show
  1. package/dist/index.d.mts +52556 -0
  2. package/dist/index.d.ts +52556 -0
  3. package/dist/index.js +8753 -0
  4. package/dist/index.mjs +8777 -0
  5. package/package.json +70 -0
  6. package/src/__test-utils__/index.tsx +39 -0
  7. package/src/components/CalendarStrip/CalendarStrip.helpers.ts +106 -0
  8. package/src/components/CalendarStrip/CalendarStrip.tsx +83 -0
  9. package/src/components/CalendarStrip/CalendarStrip.types.ts +133 -0
  10. package/src/components/CalendarStrip/DayCard/DayCard.helpers.ts +44 -0
  11. package/src/components/CalendarStrip/DayCard/DayCard.tsx +71 -0
  12. package/src/components/CalendarStrip/DayCard/DayCard.types.ts +134 -0
  13. package/src/components/CalendarStrip/DayCard/index.ts +2 -0
  14. package/src/components/CalendarStrip/DayCard/useDayCardLogic.ts +45 -0
  15. package/src/components/CalendarStrip/index.ts +9 -0
  16. package/src/components/CalendarStrip/useCalendarStripLogic.ts +53 -0
  17. package/src/components/EmptyState/EmptyState.helpers.ts +104 -0
  18. package/src/components/EmptyState/EmptyState.tsx +205 -0
  19. package/src/components/EmptyState/EmptyState.types.ts +213 -0
  20. package/src/components/EmptyState/index.ts +44 -0
  21. package/src/components/EmptyState/useEmptyStateLogic.ts +131 -0
  22. package/src/components/Header/Header.helpers.ts +93 -0
  23. package/src/components/Header/Header.tsx +185 -0
  24. package/src/components/Header/Header.types.ts +153 -0
  25. package/src/components/Header/index.ts +44 -0
  26. package/src/components/Header/useHeaderLogic.ts +146 -0
  27. package/src/components/ScheduleItem/ScheduleItem/ScheduleItem.helpers.ts +50 -0
  28. package/src/components/ScheduleItem/ScheduleItem/ScheduleItem.tsx +78 -0
  29. package/src/components/ScheduleItem/ScheduleItem/ScheduleItem.types.ts +99 -0
  30. package/src/components/ScheduleItem/ScheduleItem/index.ts +16 -0
  31. package/src/components/ScheduleItem/ScheduleItem/useScheduleItemLogic.ts +31 -0
  32. package/src/components/ScheduleItem/index.ts +15 -0
  33. package/src/components/index.ts +40 -0
  34. package/src/core/index.ts +34 -0
  35. package/src/core/restyle/RestyleThemeProviderWrapper.tsx +31 -0
  36. package/src/core/restyle/index.ts +38 -0
  37. package/src/core/restyle/restylePresetRegistry.ts +195 -0
  38. package/src/core/restyle/restyleTheme.ts +1352 -0
  39. package/src/core/restyle/restyleTypes.ts +8 -0
  40. package/src/core/restyle/useRestyleTheme.ts +10 -0
  41. package/src/hooks/animations/index.ts +3 -0
  42. package/src/hooks/animations/useAnimatedValue.ts +10 -0
  43. package/src/hooks/animations/useEntranceAnimation.ts +106 -0
  44. package/src/hooks/animations/usePulseAnimation.ts +63 -0
  45. package/src/hooks/index.ts +30 -0
  46. package/src/hooks/useReducedMotion.ts +60 -0
  47. package/src/i18n/index.ts +2 -0
  48. package/src/i18n/labels/en.ts +120 -0
  49. package/src/i18n/labels/es.ts +120 -0
  50. package/src/i18n/labels/index.ts +6 -0
  51. package/src/i18n/labels/types.ts +165 -0
  52. package/src/index.tsx +215 -0
  53. package/src/primitives/actions/Button/Button.helpers.ts +243 -0
  54. package/src/primitives/actions/Button/Button.tsx +198 -0
  55. package/src/primitives/actions/Button/Button.types.ts +207 -0
  56. package/src/primitives/actions/Button/index.ts +41 -0
  57. package/src/primitives/actions/Button/useButtonLogic.ts +160 -0
  58. package/src/primitives/actions/IconButton/IconButton.helpers.ts +235 -0
  59. package/src/primitives/actions/IconButton/IconButton.tsx +177 -0
  60. package/src/primitives/actions/IconButton/IconButton.types.ts +273 -0
  61. package/src/primitives/actions/IconButton/index.ts +30 -0
  62. package/src/primitives/actions/IconButton/useIconButtonLogic.ts +172 -0
  63. package/src/primitives/actions/index.ts +20 -0
  64. package/src/primitives/content/Avatar/Avatar.helpers.ts +177 -0
  65. package/src/primitives/content/Avatar/Avatar.tsx +199 -0
  66. package/src/primitives/content/Avatar/Avatar.types.ts +222 -0
  67. package/src/primitives/content/Avatar/index.ts +46 -0
  68. package/src/primitives/content/Avatar/useAvatarLogic.ts +149 -0
  69. package/src/primitives/content/Badge/Badge.helpers.ts +175 -0
  70. package/src/primitives/content/Badge/Badge.tsx +174 -0
  71. package/src/primitives/content/Badge/Badge.types.ts +223 -0
  72. package/src/primitives/content/Badge/index.ts +40 -0
  73. package/src/primitives/content/Badge/useBadgeLogic.ts +128 -0
  74. package/src/primitives/content/Card/Card.helpers.ts +27 -0
  75. package/src/primitives/content/Card/Card.tsx +123 -0
  76. package/src/primitives/content/Card/Card.types.ts +95 -0
  77. package/src/primitives/content/Card/index.ts +20 -0
  78. package/src/primitives/content/Card/useCardLogic.ts +48 -0
  79. package/src/primitives/content/Chip/Chip.helpers.ts +304 -0
  80. package/src/primitives/content/Chip/Chip.tsx +205 -0
  81. package/src/primitives/content/Chip/Chip.types.ts +234 -0
  82. package/src/primitives/content/Chip/index.ts +47 -0
  83. package/src/primitives/content/Chip/useChipLogic.ts +167 -0
  84. package/src/primitives/content/Icon/Icon.helpers.ts +54 -0
  85. package/src/primitives/content/Icon/Icon.tsx +110 -0
  86. package/src/primitives/content/Icon/Icon.types.ts +95 -0
  87. package/src/primitives/content/Icon/index.ts +20 -0
  88. package/src/primitives/content/Icon/useIconLogic.ts +73 -0
  89. package/src/primitives/content/index.ts +45 -0
  90. package/src/primitives/feedback/ProgressBar/ProgressBar.helpers.ts +122 -0
  91. package/src/primitives/feedback/ProgressBar/ProgressBar.tsx +154 -0
  92. package/src/primitives/feedback/ProgressBar/ProgressBar.types.ts +178 -0
  93. package/src/primitives/feedback/ProgressBar/index.ts +17 -0
  94. package/src/primitives/feedback/ProgressBar/useProgressBarLogic.ts +120 -0
  95. package/src/primitives/feedback/Skeleton/Skeleton.helpers.ts +145 -0
  96. package/src/primitives/feedback/Skeleton/Skeleton.tsx +155 -0
  97. package/src/primitives/feedback/Skeleton/Skeleton.types.ts +223 -0
  98. package/src/primitives/feedback/Skeleton/index.ts +44 -0
  99. package/src/primitives/feedback/Skeleton/useSkeletonLogic.ts +125 -0
  100. package/src/primitives/feedback/Spinner/Spinner.helpers.ts +40 -0
  101. package/src/primitives/feedback/Spinner/Spinner.tsx +105 -0
  102. package/src/primitives/feedback/Spinner/Spinner.types.ts +114 -0
  103. package/src/primitives/feedback/Spinner/index.ts +18 -0
  104. package/src/primitives/feedback/Spinner/useSpinnerLogic.ts +84 -0
  105. package/src/primitives/feedback/Toast/Toast.helpers.ts +163 -0
  106. package/src/primitives/feedback/Toast/Toast.tsx +190 -0
  107. package/src/primitives/feedback/Toast/Toast.types.ts +270 -0
  108. package/src/primitives/feedback/Toast/ToastContext.tsx +96 -0
  109. package/src/primitives/feedback/Toast/ToastProvider.tsx +241 -0
  110. package/src/primitives/feedback/Toast/index.ts +59 -0
  111. package/src/primitives/feedback/Toast/useToastLogic.ts +112 -0
  112. package/src/primitives/feedback/index.ts +45 -0
  113. package/src/primitives/index.ts +158 -0
  114. package/src/primitives/inputs/Checkbox/Checkbox.helpers.ts +132 -0
  115. package/src/primitives/inputs/Checkbox/Checkbox.tsx +150 -0
  116. package/src/primitives/inputs/Checkbox/Checkbox.types.ts +106 -0
  117. package/src/primitives/inputs/Checkbox/index.ts +30 -0
  118. package/src/primitives/inputs/Checkbox/useCheckboxLogic.ts +121 -0
  119. package/src/primitives/inputs/RadioButton/RadioButton.helpers.ts +123 -0
  120. package/src/primitives/inputs/RadioButton/RadioButton.tsx +159 -0
  121. package/src/primitives/inputs/RadioButton/RadioButton.types.ts +106 -0
  122. package/src/primitives/inputs/RadioButton/index.ts +25 -0
  123. package/src/primitives/inputs/RadioButton/useRadioButtonLogic.ts +117 -0
  124. package/src/primitives/inputs/SegmentedControl/SegmentedControl.helpers.ts +174 -0
  125. package/src/primitives/inputs/SegmentedControl/SegmentedControl.tsx +224 -0
  126. package/src/primitives/inputs/SegmentedControl/SegmentedControl.types.ts +187 -0
  127. package/src/primitives/inputs/SegmentedControl/index.ts +39 -0
  128. package/src/primitives/inputs/SegmentedControl/useSegmentedControlLogic.ts +151 -0
  129. package/src/primitives/inputs/SelectSheet/SelectSheet.helpers.ts +147 -0
  130. package/src/primitives/inputs/SelectSheet/SelectSheet.tsx +247 -0
  131. package/src/primitives/inputs/SelectSheet/SelectSheet.types.ts +196 -0
  132. package/src/primitives/inputs/SelectSheet/SelectSheetOption.tsx +177 -0
  133. package/src/primitives/inputs/SelectSheet/index.ts +48 -0
  134. package/src/primitives/inputs/SelectSheet/useSelectSheetLogic.ts +309 -0
  135. package/src/primitives/inputs/Switch/Switch.helpers.ts +109 -0
  136. package/src/primitives/inputs/Switch/Switch.tsx +191 -0
  137. package/src/primitives/inputs/Switch/Switch.types.ts +154 -0
  138. package/src/primitives/inputs/Switch/index.ts +40 -0
  139. package/src/primitives/inputs/Switch/useSwitchLogic.ts +192 -0
  140. package/src/primitives/inputs/TextInput/TextInput.helpers.ts +206 -0
  141. package/src/primitives/inputs/TextInput/TextInput.tsx +392 -0
  142. package/src/primitives/inputs/TextInput/TextInput.types.ts +216 -0
  143. package/src/primitives/inputs/TextInput/index.ts +37 -0
  144. package/src/primitives/inputs/TextInput/useTextInputLogic.ts +195 -0
  145. package/src/primitives/inputs/index.ts +52 -0
  146. package/src/primitives/layout/AnimatedBox.tsx +44 -0
  147. package/src/primitives/layout/Box.tsx +71 -0
  148. package/src/primitives/layout/Divider/Divider.helpers.ts +115 -0
  149. package/src/primitives/layout/Divider/Divider.tsx +139 -0
  150. package/src/primitives/layout/Divider/Divider.types.ts +178 -0
  151. package/src/primitives/layout/Divider/index.ts +24 -0
  152. package/src/primitives/layout/Divider/useDividerLogic.ts +109 -0
  153. package/src/primitives/layout/FlatList.tsx +66 -0
  154. package/src/primitives/layout/Pressable.tsx +74 -0
  155. package/src/primitives/layout/ScrollView.tsx +63 -0
  156. package/src/primitives/layout/Stack.tsx +69 -0
  157. package/src/primitives/layout/index.ts +40 -0
  158. package/src/primitives/navigation/index.ts +6 -0
  159. package/src/primitives/overlays/Modal/Modal.helpers.ts +31 -0
  160. package/src/primitives/overlays/Modal/Modal.tsx +264 -0
  161. package/src/primitives/overlays/Modal/Modal.types.ts +193 -0
  162. package/src/primitives/overlays/Modal/index.ts +43 -0
  163. package/src/primitives/overlays/Modal/useModalLogic.ts +103 -0
  164. package/src/primitives/overlays/index.ts +12 -0
  165. package/src/primitives/typography/Text.tsx +51 -0
  166. package/src/primitives/typography/index.ts +1 -0
  167. package/src/provider/DesignSystemContext.ts +22 -0
  168. package/src/provider/DesignSystemProvider.tsx +121 -0
  169. package/src/provider/index.ts +7 -0
  170. package/src/providers/ThemeProvider/createTheme.ts +304 -0
  171. package/src/providers/ThemeProvider/defaultTheme.ts +70 -0
  172. package/src/providers/ThemeProvider/index.ts +34 -0
  173. package/src/providers/ThemeProvider/types.ts +249 -0
  174. package/src/providers/index.ts +29 -0
  175. package/src/tokens/colors.ts +371 -0
  176. package/src/tokens/index.ts +145 -0
  177. package/src/tokens/motion.ts +176 -0
  178. package/src/tokens/radii.ts +82 -0
  179. package/src/tokens/scales.ts +588 -0
  180. package/src/tokens/shadows.ts +190 -0
  181. package/src/tokens/spacing.ts +140 -0
  182. package/src/tokens/tokens.json +207 -0
  183. package/src/tokens/typography.ts +251 -0
  184. package/src/types.ts +50 -0
  185. package/src/utils/accessibility.ts +169 -0
  186. package/src/utils/index.ts +25 -0
  187. package/src/utils/platform.ts +72 -0
@@ -0,0 +1,224 @@
1
+ /**
2
+ * SegmentedControl Component
3
+ *
4
+ * @description Toggle control for switching between multiple options - Atom
5
+ *
6
+ * SegmentedControl provides a horizontal row of mutually exclusive options.
7
+ * Ideal for filtering, view switching, and binary choices.
8
+ *
9
+ * ## Size Scale
10
+ * | Size | Height | Segment H | Text Variant | Use Case |
11
+ * |------|--------|-----------|--------------|----------|
12
+ * | sm | 36px | 28px | labelSmall | Compact filters |
13
+ * | md | 44px | 36px | labelMedium | Standard (default) |
14
+ * | lg | 52px | 44px | labelLarge | Prominent controls |
15
+ *
16
+ * ## Variants
17
+ * - `default`: Neutral background, white selected segment
18
+ * - `filled`: Subtle background, primary color selected segment
19
+ * - `outline`: Bordered container, primary color selected segment
20
+ *
21
+ * ## Features
22
+ * - Responsive size prop (phone/tablet breakpoints)
23
+ * - Three visual variants
24
+ * - Individual segment disable support
25
+ * - Theme-aware colors via Restyle
26
+ * - Full accessibility support (radiogroup pattern)
27
+ * - Memoized for performance
28
+ * - Spreads rest props for composability
29
+ *
30
+ * @see SegmentedControl.types.ts - Type definitions
31
+ * @see SegmentedControl.helpers.ts - Style calculation functions
32
+ * @see SegmentedControl.a11y.ts - Accessibility props
33
+ *
34
+ * @example
35
+ * // Basic usage
36
+ * <SegmentedControl
37
+ * options={[
38
+ * { value: "day", label: "Day" },
39
+ * { value: "week", label: "Week" },
40
+ * { value: "month", label: "Month" },
41
+ * ]}
42
+ * selectedValue={view}
43
+ * onValueChange={setView}
44
+ * />
45
+ *
46
+ * @example
47
+ * // Different sizes and variants
48
+ * <SegmentedControl
49
+ * options={options}
50
+ * selectedValue={value}
51
+ * onValueChange={setValue}
52
+ * size="lg"
53
+ * variant="filled"
54
+ * />
55
+ *
56
+ * @example
57
+ * // With margin (composable via rest props)
58
+ * <SegmentedControl
59
+ * options={options}
60
+ * selectedValue={value}
61
+ * onValueChange={setValue}
62
+ * marginBottom="lg"
63
+ * />
64
+ */
65
+
66
+ import {
67
+ backgroundColor,
68
+ border,
69
+ createRestyleComponent,
70
+ createVariant,
71
+ layout,
72
+ spacing,
73
+ spacingShorthand,
74
+ } from "@shopify/restyle";
75
+ import React, { memo, useCallback } from "react";
76
+ import { Pressable } from "react-native";
77
+ import { RestyleTheme } from "../../../core/restyle";
78
+ import { Box } from "../../layout";
79
+ import { Text } from "../../typography";
80
+ import { SEGMENT_TEXT_VARIANT } from "./SegmentedControl.helpers";
81
+ import {
82
+ BaseSegmentedControlProps,
83
+ SegmentedControlProps,
84
+ SegmentedControlSize,
85
+ } from "./SegmentedControl.types";
86
+ import { useSegmentedControlLogic } from "./useSegmentedControlLogic";
87
+
88
+ /**
89
+ * Base SegmentedControl container with Restyle variant support.
90
+ * @internal
91
+ */
92
+ const BaseSegmentedControlContainer = createRestyleComponent<
93
+ BaseSegmentedControlProps,
94
+ RestyleTheme
95
+ >(
96
+ [
97
+ createVariant({ themeKey: "segmentedControlVariants" }),
98
+ createVariant({ themeKey: "segmentedControlSizes", property: "size" }),
99
+ backgroundColor,
100
+ border,
101
+ layout,
102
+ spacing,
103
+ spacingShorthand,
104
+ ],
105
+ Box,
106
+ );
107
+
108
+ /**
109
+ * Base Segment container with Restyle variant support.
110
+ * @internal
111
+ */
112
+ const BaseSegmentContainer = createRestyleComponent<
113
+ BaseSegmentedControlProps,
114
+ RestyleTheme
115
+ >(
116
+ [
117
+ createVariant({
118
+ themeKey: "segmentedControlSegmentSizes",
119
+ property: "size",
120
+ }),
121
+ backgroundColor,
122
+ border,
123
+ layout,
124
+ spacing,
125
+ spacingShorthand,
126
+ ],
127
+ Box,
128
+ );
129
+
130
+ /**
131
+ * Base Pressable segment with Restyle support.
132
+ * @internal
133
+ */
134
+ const BaseSegmentPressable = createRestyleComponent<
135
+ BaseSegmentedControlProps & { onPress?: () => void; disabled?: boolean },
136
+ RestyleTheme
137
+ >([layout, spacing, spacingShorthand], Pressable);
138
+
139
+ function SegmentedControlComponent({
140
+ options,
141
+ selectedValue,
142
+ onValueChange,
143
+ size = "md",
144
+ variant = "default",
145
+ disabled = false,
146
+ testID,
147
+ style,
148
+ ...rest
149
+ }: SegmentedControlProps) {
150
+ const {
151
+ containerStyles,
152
+ getSegmentStyles,
153
+ getTextColor,
154
+ handlePress,
155
+ containerA11y,
156
+ getSegmentA11y,
157
+ } = useSegmentedControlLogic({
158
+ options,
159
+ selectedValue,
160
+ onValueChange,
161
+ size,
162
+ variant,
163
+ disabled,
164
+ });
165
+
166
+ const handleSegmentPress = useCallback(
167
+ (value: string, segmentDisabled?: boolean) => {
168
+ handlePress(value, segmentDisabled);
169
+ },
170
+ [handlePress],
171
+ );
172
+
173
+ return (
174
+ <BaseSegmentedControlContainer
175
+ variant={variant}
176
+ size={size}
177
+ backgroundColor={containerStyles.backgroundColor}
178
+ opacity={disabled ? 0.5 : 1}
179
+ testID={testID}
180
+ style={style}
181
+ {...containerA11y}
182
+ {...rest}
183
+ >
184
+ {options.map((option, index) => {
185
+ const isSelected = option.value === selectedValue;
186
+ const isDisabled = disabled || option.disabled === true;
187
+ const segmentStyles = getSegmentStyles(isSelected, isDisabled);
188
+ const textColor = getTextColor(isSelected, isDisabled);
189
+ const segmentA11yProps = getSegmentA11y(option, index, isSelected);
190
+
191
+ return (
192
+ <BaseSegmentPressable
193
+ key={option.value}
194
+ flex={1}
195
+ onPress={() => handleSegmentPress(option.value, option.disabled)}
196
+ disabled={isDisabled}
197
+ testID={testID ? `${testID}-segment-${option.value}` : undefined}
198
+ {...segmentA11yProps}
199
+ >
200
+ <BaseSegmentContainer
201
+ size={size}
202
+ backgroundColor={segmentStyles.backgroundColor}
203
+ borderRadius={segmentStyles.borderRadius}
204
+ >
205
+ <Text
206
+ variant={
207
+ SEGMENT_TEXT_VARIANT[size as SegmentedControlSize] ??
208
+ "labelMedium"
209
+ }
210
+ color={textColor}
211
+ numberOfLines={1}
212
+ >
213
+ {option.label}
214
+ </Text>
215
+ </BaseSegmentContainer>
216
+ </BaseSegmentPressable>
217
+ );
218
+ })}
219
+ </BaseSegmentedControlContainer>
220
+ );
221
+ }
222
+
223
+ export const SegmentedControl = memo(SegmentedControlComponent);
224
+ SegmentedControl.displayName = "SegmentedControl";
@@ -0,0 +1,187 @@
1
+ /**
2
+ * SegmentedControl Component Types
3
+ *
4
+ * Type definitions for the SegmentedControl component and its related hooks/helpers.
5
+ * All types are explicitly exported for external consumption.
6
+ *
7
+ * @see SegmentedControl.tsx - Component implementation
8
+ */
9
+
10
+ import {
11
+ BoxProps,
12
+ ResponsiveValue,
13
+ VariantProps,
14
+ } from "@shopify/restyle";
15
+ import { StyleProp, ViewProps, ViewStyle } from "react-native";
16
+ import { RestyleTheme } from "../../../core/restyle";
17
+
18
+ // =============================================================================
19
+ // VARIANT TYPES
20
+ // =============================================================================
21
+
22
+ /**
23
+ * Segment option configuration.
24
+ */
25
+ export interface SegmentOption {
26
+ /** Unique identifier for the segment */
27
+ value: string;
28
+ /** Display label for the segment */
29
+ label: string;
30
+ /** Optional icon name to display */
31
+ icon?: string;
32
+ /** Whether this segment is disabled */
33
+ disabled?: boolean;
34
+ }
35
+
36
+ /**
37
+ * Size preset for the SegmentedControl component.
38
+ *
39
+ * - `sm`: 36px height - Compact filters, dense UIs
40
+ * - `md`: 44px height - Standard control (default)
41
+ * - `lg`: 52px height - Prominent controls, tablets
42
+ */
43
+ export type SegmentedControlSize = Exclude<
44
+ keyof RestyleTheme["segmentedControlSizes"],
45
+ "defaults"
46
+ >;
47
+
48
+ /**
49
+ * Visual variant for the SegmentedControl component.
50
+ *
51
+ * - `default`: Neutral background, white selected segment
52
+ * - `outline`: Bordered container, primary color selected
53
+ */
54
+ export type SegmentedControlVariant = Exclude<
55
+ keyof RestyleTheme["segmentedControlVariants"],
56
+ "defaults"
57
+ >;
58
+
59
+ // =============================================================================
60
+ // RESTYLE PROP TYPES
61
+ // =============================================================================
62
+
63
+ type SegmentedControlVariantProps = VariantProps<
64
+ RestyleTheme,
65
+ "segmentedControlVariants"
66
+ >;
67
+ type SegmentedControlSizeProps = VariantProps<
68
+ RestyleTheme,
69
+ "segmentedControlSizes",
70
+ "size"
71
+ >;
72
+
73
+ /** Base props combining Restyle Box props with ViewProps and variant props */
74
+ export type BaseSegmentedControlProps = SegmentedControlVariantProps &
75
+ SegmentedControlSizeProps &
76
+ BoxProps<RestyleTheme> &
77
+ ViewProps;
78
+
79
+ // =============================================================================
80
+ // COMPONENT PROPS
81
+ // =============================================================================
82
+
83
+ /**
84
+ * Props for SegmentedControl component.
85
+ *
86
+ * @example
87
+ * // Basic usage
88
+ * <SegmentedControl
89
+ * options={[
90
+ * { value: "day", label: "Day" },
91
+ * { value: "week", label: "Week" },
92
+ * ]}
93
+ * selectedValue={view}
94
+ * onValueChange={setView}
95
+ * />
96
+ *
97
+ * @example
98
+ * // With different size and variant
99
+ * <SegmentedControl
100
+ * options={options}
101
+ * selectedValue={value}
102
+ * onValueChange={setValue}
103
+ * size="lg"
104
+ * variant="outline"
105
+ * />
106
+ *
107
+ * @example
108
+ * // With spacing props (composable)
109
+ * <SegmentedControl
110
+ * options={options}
111
+ * selectedValue={value}
112
+ * onValueChange={setValue}
113
+ * marginBottom="lg"
114
+ * />
115
+ */
116
+ export interface SegmentedControlProps
117
+ extends Omit<BaseSegmentedControlProps, "variant"> {
118
+ /** Array of segment options */
119
+ options: SegmentOption[];
120
+ /** Currently selected value */
121
+ selectedValue: string;
122
+ /** Callback when selection changes */
123
+ onValueChange: (value: string) => void;
124
+ /** Size variant (supports responsive values) */
125
+ size?: ResponsiveValue<SegmentedControlSize, RestyleTheme["breakpoints"]>;
126
+ /** Visual variant (supports responsive values) */
127
+ variant?: ResponsiveValue<SegmentedControlVariant, RestyleTheme["breakpoints"]>;
128
+ /** Whether the entire control is disabled */
129
+ disabled?: boolean;
130
+ /** Test ID for testing */
131
+ testID?: string;
132
+ /** Custom style overrides */
133
+ style?: StyleProp<ViewStyle>;
134
+ }
135
+
136
+ // =============================================================================
137
+ // HOOK TYPES
138
+ // =============================================================================
139
+
140
+ /**
141
+ * Props passed to the logic hook.
142
+ */
143
+ export interface UseSegmentedControlLogicProps {
144
+ options: SegmentOption[];
145
+ selectedValue: string;
146
+ onValueChange: (value: string) => void;
147
+ size: ResponsiveValue<SegmentedControlSize, RestyleTheme["breakpoints"]>;
148
+ variant: ResponsiveValue<SegmentedControlVariant, RestyleTheme["breakpoints"]>;
149
+ disabled: boolean;
150
+ }
151
+
152
+ /**
153
+ * Return type for useSegmentedControlLogic hook.
154
+ */
155
+ export interface UseSegmentedControlLogicReturn {
156
+ /** Computed container styles */
157
+ containerStyles: {
158
+ backgroundColor: keyof RestyleTheme["colors"];
159
+ borderRadius: keyof RestyleTheme["borderRadii"];
160
+ padding: keyof RestyleTheme["spacing"];
161
+ };
162
+ /** Computed segment styles based on selection state */
163
+ getSegmentStyles: (
164
+ isSelected: boolean,
165
+ isDisabled: boolean
166
+ ) => {
167
+ backgroundColor: keyof RestyleTheme["colors"];
168
+ borderRadius: keyof RestyleTheme["borderRadii"];
169
+ paddingVertical: keyof RestyleTheme["spacing"];
170
+ paddingHorizontal: keyof RestyleTheme["spacing"];
171
+ };
172
+ /** Computed text color based on selection state */
173
+ getTextColor: (
174
+ isSelected: boolean,
175
+ isDisabled: boolean
176
+ ) => keyof RestyleTheme["colors"];
177
+ /** Handle segment press */
178
+ handlePress: (value: string, disabled?: boolean) => void;
179
+ /** Accessibility props for container */
180
+ containerA11y: Record<string, unknown>;
181
+ /** Get accessibility props for a segment */
182
+ getSegmentA11y: (
183
+ option: SegmentOption,
184
+ index: number,
185
+ isSelected: boolean
186
+ ) => Record<string, unknown>;
187
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * SegmentedControl Component
3
+ *
4
+ * @description Toggle control for switching between multiple options
5
+ *
6
+ * @example
7
+ * import { SegmentedControl } from "@/design-system";
8
+ *
9
+ * <SegmentedControl
10
+ * options={[
11
+ * { value: "day", label: "Day" },
12
+ * { value: "week", label: "Week" },
13
+ * ]}
14
+ * selectedValue={view}
15
+ * onValueChange={setView}
16
+ * />
17
+ */
18
+
19
+ export { SegmentedControl } from "./SegmentedControl";
20
+ export type {
21
+ BaseSegmentedControlProps,
22
+ SegmentedControlProps,
23
+ SegmentedControlSize,
24
+ SegmentedControlVariant,
25
+ SegmentOption,
26
+ UseSegmentedControlLogicProps,
27
+ UseSegmentedControlLogicReturn,
28
+ } from "./SegmentedControl.types";
29
+ export { useSegmentedControlLogic } from "./useSegmentedControlLogic";
30
+ export {
31
+ getContainerStyles,
32
+ getSegmentStyles,
33
+ getTextColor,
34
+ validateOptions,
35
+ } from "./SegmentedControl.helpers";
36
+ export {
37
+ getSegmentA11y,
38
+ getSegmentedControlContainerA11y,
39
+ } from "./SegmentedControl.a11y";
@@ -0,0 +1,151 @@
1
+ import { useDesignSystem } from "../../../provider";
2
+ import { useResponsiveProp } from "@shopify/restyle";
3
+ import { useCallback, useMemo } from "react";
4
+ import {
5
+ getSegmentA11y,
6
+ getSegmentedControlContainerA11y,
7
+ } from "./SegmentedControl.a11y";
8
+ import {
9
+ getContainerStyles,
10
+ getSegmentStyles as getSegmentStylesHelper,
11
+ getTextColor as getTextColorHelper,
12
+ } from "./SegmentedControl.helpers";
13
+ import type {
14
+ SegmentedControlSize,
15
+ SegmentedControlVariant,
16
+ SegmentOption,
17
+ UseSegmentedControlLogicProps,
18
+ UseSegmentedControlLogicReturn,
19
+ } from "./SegmentedControl.types";
20
+
21
+ /**
22
+ * Orchestrates SegmentedControl rendering logic and state management.
23
+ *
24
+ * Handles:
25
+ * - Responsive size and variant resolution
26
+ * - Container style calculation
27
+ * - Segment style computation based on selection state
28
+ * - Text color determination per segment
29
+ * - Press handling with disabled state check
30
+ * - Accessibility props for container and individual segments
31
+ *
32
+ * @param props - Configuration object for segmented control behavior
33
+ * @param props.options - Array of segment options with values and labels
34
+ * @param props.selectedValue - Currently selected segment value
35
+ * @param props.onValueChange - Callback when selection changes
36
+ * @param props.size - Size variant ('sm' | 'md' | 'lg')
37
+ * @param props.variant - Visual variant ('default' | 'emphasized')
38
+ * @param props.disabled - Whether entire control is disabled
39
+ *
40
+ * @returns Computed values for rendering SegmentedControl
41
+ * @returns {object} containerStyles - Container style configuration
42
+ * @returns {function} getSegmentStyles - Returns styles for a segment
43
+ * @returns {function} getTextColor - Returns text color for a segment
44
+ * @returns {function} handlePress - Press handler for segments
45
+ * @returns {object} containerA11y - Container accessibility props
46
+ * @returns {function} getSegmentA11y - Returns a11y props for a segment
47
+ *
48
+ * @performance This hook uses useMemo() and useCallback() for optimization
49
+ *
50
+ * @example
51
+ * const {
52
+ * containerStyles,
53
+ * getSegmentStyles,
54
+ * getTextColor,
55
+ * handlePress,
56
+ * getSegmentA11y
57
+ * } = useSegmentedControlLogic({
58
+ * options: [
59
+ * { value: "day", label: "Day" },
60
+ * { value: "week", label: "Week" },
61
+ * { value: "month", label: "Month" }
62
+ * ],
63
+ * selectedValue: "week",
64
+ * onValueChange: setValue,
65
+ * size: "md",
66
+ * });
67
+ */
68
+ export function useSegmentedControlLogic({
69
+ options,
70
+ selectedValue,
71
+ onValueChange,
72
+ size,
73
+ variant,
74
+ disabled,
75
+ }: UseSegmentedControlLogicProps): UseSegmentedControlLogicReturn {
76
+ const { labels: t } = useDesignSystem();
77
+ const resolvedSize = (useResponsiveProp(size) ?? "md") as SegmentedControlSize;
78
+ const resolvedVariant = (useResponsiveProp(variant) ?? "default") as SegmentedControlVariant;
79
+
80
+ // Memoize container styles
81
+ const containerStyles = getContainerStyles(resolvedSize, resolvedVariant);
82
+
83
+ // Memoize i18n params for container a11y
84
+ const containerI18n = useMemo(
85
+ () => ({
86
+ containerLabel: t.designSystem.segmentedControl.containerLabel,
87
+ optionsLabel: t.designSystem.segmentedControl.options,
88
+ }),
89
+ [t],
90
+ );
91
+
92
+ // Memoize container accessibility
93
+ const containerA11y = getSegmentedControlContainerA11y(options.length, containerI18n);
94
+
95
+ // Memoize i18n params for segment a11y
96
+ const segmentI18n = useMemo(
97
+ () => ({
98
+ selectedLabel: t.designSystem.segmentedControl.selected,
99
+ currentlySelectedHint: t.designSystem.segmentedControl.currentlySelected,
100
+ doubleTapToSelectHint: t.designSystem.segmentedControl.doubleTapToSelect,
101
+ ofLabel: t.designSystem.segmentedControl.of,
102
+ }),
103
+ [t],
104
+ );
105
+
106
+ // Get segment styles based on selection state
107
+ const getSegmentStyles = useCallback(
108
+ (isSelected: boolean, isDisabled: boolean) =>
109
+ getSegmentStylesHelper(
110
+ resolvedSize,
111
+ resolvedVariant,
112
+ isSelected,
113
+ isDisabled,
114
+ ),
115
+ [resolvedSize, resolvedVariant],
116
+ );
117
+
118
+ // Get text color based on selection state
119
+ const getTextColor = useCallback(
120
+ (isSelected: boolean, isDisabled: boolean) =>
121
+ getTextColorHelper(resolvedVariant, isSelected, isDisabled),
122
+ [resolvedVariant],
123
+ );
124
+
125
+ // Handle segment press
126
+ const handlePress = useCallback(
127
+ (value: string, segmentDisabled?: boolean) => {
128
+ if (disabled || segmentDisabled || value === selectedValue) {
129
+ return;
130
+ }
131
+ onValueChange(value);
132
+ },
133
+ [disabled, selectedValue, onValueChange],
134
+ );
135
+
136
+ // Get accessibility props for a segment
137
+ const getSegmentA11yProps = useCallback(
138
+ (option: SegmentOption, index: number, isSelected: boolean) =>
139
+ getSegmentA11y(option, index, options.length, isSelected, segmentI18n),
140
+ [options.length, segmentI18n],
141
+ );
142
+
143
+ return {
144
+ containerStyles,
145
+ getSegmentStyles,
146
+ getTextColor,
147
+ handlePress,
148
+ containerA11y,
149
+ getSegmentA11y: getSegmentA11yProps,
150
+ };
151
+ }