@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,132 @@
1
+ /**
2
+ * Checkbox Component Helpers
3
+ *
4
+ * Pure functions for computing Checkbox styles, sizes, and colors.
5
+ * All calculations are derived from the centralized scales.ts token file.
6
+ */
7
+
8
+ import { Insets } from "react-native";
9
+ import { CHECKBOX_HIT_SLOP, CHECKBOX_ICON_SIZE } from "../../../tokens/scales";
10
+ import { BaseThemeColor, RestyleColor } from "../../../types";
11
+ import { IconSize } from "../../content/Icon";
12
+ import {
13
+ CheckboxSize,
14
+ CheckboxStyleResult,
15
+ ResolveCheckboxStyleParams,
16
+ } from "./Checkbox.types";
17
+
18
+ // =============================================================================
19
+ // ICON SIZE RESOLUTION
20
+ // =============================================================================
21
+
22
+ /**
23
+ * Get icon size based on checkbox size.
24
+ *
25
+ * Uses CHECKBOX_ICON_SIZE from scales.ts for mathematical consistency.
26
+ * Target ratio: ~75% (ICON_RATIOS.toggleControl)
27
+ *
28
+ * | Size | Container | Icon | Ratio |
29
+ * |------|-----------|------|-------|
30
+ * | sm | 20px | 16px | 80% |
31
+ * | md | 24px | 20px | 83% |
32
+ * | lg | 32px | 24px | 75% |
33
+ *
34
+ * @param size - The checkbox size variant
35
+ * @returns The corresponding icon size
36
+ */
37
+ export function getCheckboxIconSize(size: CheckboxSize): IconSize {
38
+ return CHECKBOX_ICON_SIZE[size];
39
+ }
40
+
41
+ // =============================================================================
42
+ // TOUCH TARGET (hitSlop)
43
+ // =============================================================================
44
+
45
+ /**
46
+ * Get hitSlop based on checkbox size.
47
+ *
48
+ * Uses pre-calculated values from scales.ts to ensure
49
+ * all checkboxes meet the 44px minimum touch target.
50
+ *
51
+ * | Size | Container | hitSlop | Total |
52
+ * |------|-----------|---------|-------|
53
+ * | sm | 20px | 12px | 44px |
54
+ * | md | 24px | 10px | 44px |
55
+ * | lg | 32px | 6px | 44px |
56
+ *
57
+ * @param size - The checkbox size variant
58
+ * @returns Insets for touch target expansion
59
+ */
60
+ export function getCheckboxHitSlop(size: CheckboxSize): Insets {
61
+ return CHECKBOX_HIT_SLOP[size];
62
+ }
63
+
64
+ // =============================================================================
65
+ // COLOR RESOLUTION
66
+ // =============================================================================
67
+
68
+ /**
69
+ * Get check icon color based on disabled state.
70
+ *
71
+ * The check icon is always displayed on a filled background,
72
+ * so it uses inverse text color for contrast.
73
+ *
74
+ * | State | Color |
75
+ * |----------|--------------|
76
+ * | Normal | textInverse |
77
+ * | Disabled | textDisabled |
78
+ *
79
+ * @param disabled - Whether the checkbox is disabled
80
+ * @returns The computed icon color
81
+ */
82
+ export function getCheckIconColor(disabled: boolean): RestyleColor {
83
+ return disabled ? "textDisabled" : "textInverse";
84
+ }
85
+
86
+ // =============================================================================
87
+ // STYLE RESOLUTION
88
+ // =============================================================================
89
+
90
+ /**
91
+ * Resolve dynamic style properties for the Checkbox container.
92
+ *
93
+ * Static styles (borderRadius, borderWidth) come from theme variants.
94
+ * This function handles dynamic state-based values:
95
+ * - backgroundColor based on checked and disabled state
96
+ * - borderColor based on checked and disabled state
97
+ * - opacity based on disabled state
98
+ *
99
+ * @param params - Resolution parameters
100
+ * @returns Computed style object
101
+ */
102
+ export function resolveCheckboxStyle(
103
+ params: ResolveCheckboxStyleParams
104
+ ): CheckboxStyleResult {
105
+ const { checked, disabled, color, theme } = params;
106
+
107
+ // Background color logic
108
+ let backgroundColor: string;
109
+ if (!checked) {
110
+ backgroundColor = "transparent";
111
+ } else if (disabled) {
112
+ backgroundColor = theme.colors.interactiveDisabled;
113
+ } else {
114
+ backgroundColor = theme.colors[color as BaseThemeColor];
115
+ }
116
+
117
+ // Border color logic
118
+ let borderColor: string;
119
+ if (disabled) {
120
+ borderColor = theme.colors.borderDefault;
121
+ } else if (!checked) {
122
+ borderColor = theme.colors.borderDefault;
123
+ } else {
124
+ borderColor = theme.colors[color as BaseThemeColor];
125
+ }
126
+
127
+ return {
128
+ backgroundColor,
129
+ borderColor,
130
+ opacity: 1,
131
+ };
132
+ }
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Checkbox Component
3
+ *
4
+ * @description Toggle control for binary choices - Atom
5
+ *
6
+ * Checkbox provides a compact toggle for boolean form values.
7
+ * Uses a checkmark icon to indicate the checked state.
8
+ *
9
+ * ## Ratios & Constraints
10
+ * - Icon = Container × ~75% (ICON_RATIOS.toggleControl)
11
+ * - All sizes meet 44px minimum touch target via hitSlop
12
+ *
13
+ * ## Size Scale
14
+ * | Size | Container | Icon | hitSlop | Touch Target |
15
+ * |------|-----------|------|---------|--------------|
16
+ * | sm | 20px | 16px | 12px | 44px |
17
+ * | md | 24px | 20px | 10px | 44px |
18
+ * | lg | 32px | 24px | 6px | 44px |
19
+ *
20
+ * ## Visual States
21
+ * - Unchecked: Border only, transparent background
22
+ * - Checked: Filled background with checkmark icon
23
+ * - Disabled: Reduced opacity (0.5)
24
+ *
25
+ * ## Features
26
+ * - Responsive size prop (phone/tablet breakpoints)
27
+ * - Customizable color when checked
28
+ * - Full accessibility support (checkbox role)
29
+ *
30
+ * @performance
31
+ * - Wrapped with React.memo() to prevent unnecessary re-renders
32
+ * - Uses useMemo() for expensive calculations (style resolution, icon sizing)
33
+ * - Check icon calculated using ~75% container ratio for instant rendering
34
+ * - Hit slop pre-calculated in lookup tables for 44px touch targets
35
+ *
36
+ * @see Checkbox.types.ts - Type definitions
37
+ * @see Checkbox.helpers.ts - Pure calculation functions
38
+ * @see Checkbox.a11y.ts - Accessibility prop generation
39
+ * @see tokens/scales.ts - Mathematical ratios
40
+ *
41
+ * @example
42
+ * // Basic usage
43
+ * <Checkbox
44
+ * checked={isChecked}
45
+ * onPress={toggleChecked}
46
+ * accessibilityLabel="Accept terms"
47
+ * />
48
+ *
49
+ * @example
50
+ * // Responsive with label
51
+ * <Checkbox
52
+ * checked={isChecked}
53
+ * onPress={toggleChecked}
54
+ * size={{ phone: "md", tablet: "lg" }}
55
+ * color="feedbackSuccess"
56
+ * label="Enable notifications"
57
+ * />
58
+ */
59
+
60
+ import {
61
+ backgroundColor,
62
+ border,
63
+ createRestyleComponent,
64
+ createVariant,
65
+ layout,
66
+ spacing,
67
+ spacingShorthand,
68
+ } from "@shopify/restyle";
69
+ import React, { forwardRef, memo, useCallback } from "react";
70
+ import { Pressable, View } from "react-native";
71
+ import { RestyleTheme } from "../../../core/restyle";
72
+ import { Icon } from "../../content/Icon";
73
+ import {
74
+ BaseCheckboxInteractiveContainerProps,
75
+ CheckboxProps,
76
+ } from "./Checkbox.types";
77
+ import { useCheckboxLogic } from "./useCheckboxLogic";
78
+
79
+ /**
80
+ * Base container with Restyle variant support.
81
+ * @internal
82
+ */
83
+ const BaseCheckboxInteractiveContainer = createRestyleComponent<
84
+ BaseCheckboxInteractiveContainerProps,
85
+ RestyleTheme
86
+ >(
87
+ [
88
+ createVariant({ themeKey: "checkboxSizes", property: "size" }),
89
+ backgroundColor,
90
+ border,
91
+ layout,
92
+ spacing,
93
+ spacingShorthand,
94
+ ],
95
+ Pressable,
96
+ );
97
+
98
+ const CheckboxComponent = forwardRef<View, CheckboxProps>(function Checkbox(
99
+ {
100
+ checked,
101
+ onPress,
102
+ size = "md",
103
+ disabled = false,
104
+ color = "accentPrimary",
105
+ label,
106
+ accessibilityLabel,
107
+ accessibilityHint,
108
+ testID,
109
+ ...rest
110
+ },
111
+ ref,
112
+ ) {
113
+ const { iconSize, checkIconColor, containerStyle, hitSlop, a11yProps } =
114
+ useCheckboxLogic({
115
+ checked,
116
+ disabled,
117
+ size,
118
+ color,
119
+ label,
120
+ accessibilityLabel,
121
+ accessibilityHint,
122
+ });
123
+
124
+ const handlePress = useCallback(() => {
125
+ if (!disabled) {
126
+ onPress();
127
+ }
128
+ }, [disabled, onPress]);
129
+
130
+ return (
131
+ <BaseCheckboxInteractiveContainer
132
+ ref={ref}
133
+ size={size}
134
+ onPress={handlePress}
135
+ disabled={disabled}
136
+ hitSlop={hitSlop}
137
+ testID={testID}
138
+ alignItems="center"
139
+ justifyContent="center"
140
+ style={containerStyle}
141
+ {...a11yProps}
142
+ {...rest}
143
+ >
144
+ {checked && <Icon name="check" size={iconSize} color={checkIconColor} accessibilityLabel="check" />}
145
+ </BaseCheckboxInteractiveContainer>
146
+ );
147
+ });
148
+
149
+ export const Checkbox = memo(CheckboxComponent);
150
+ Checkbox.displayName = "Checkbox";
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Checkbox Component Types
3
+ *
4
+ * Type definitions for the Checkbox component and its related hooks/helpers.
5
+ * All types are explicitly exported for external consumption.
6
+ */
7
+
8
+ import { BoxProps, ResponsiveValue, VariantProps } from "@shopify/restyle";
9
+ import { Insets, PressableProps, View, ViewStyle } from "react-native";
10
+ import { RestyleTheme } from "../../../core/restyle";
11
+ import { RestyleColor } from "../../../types";
12
+ import { IconSize } from "../../content/Icon";
13
+
14
+ // =============================================================================
15
+ // VARIANT TYPES (Explicitly exported for external use)
16
+ // =============================================================================
17
+
18
+ /**
19
+ * Size variants derived from theme.
20
+ * Maps to checkboxSizes in restyleTheme.ts
21
+ *
22
+ * | Size | Container | Touch Target |
23
+ * |------|-----------|--------------|
24
+ * | sm | 20px | 44px (hitSlop) |
25
+ * | md | 24px | 44px (hitSlop) |
26
+ * | lg | 32px | 44px (hitSlop) |
27
+ */
28
+ export type CheckboxSize = Exclude<
29
+ keyof RestyleTheme["checkboxSizes"],
30
+ "defaults"
31
+ >;
32
+
33
+ /** Ref type for the Checkbox component — the underlying React Native View instance. */
34
+ export type CheckboxRef = View;
35
+
36
+ // =============================================================================
37
+ // RESTYLE PROP TYPES
38
+ // =============================================================================
39
+
40
+ type CheckboxSizeVariantProps = VariantProps<
41
+ RestyleTheme,
42
+ "checkboxSizes",
43
+ "size"
44
+ >;
45
+ type CheckboxRestyleProps = CheckboxSizeVariantProps & BoxProps<RestyleTheme>;
46
+
47
+ export type BaseCheckboxInteractiveContainerProps = CheckboxRestyleProps & PressableProps;
48
+
49
+ export interface CheckboxProps extends Omit<CheckboxRestyleProps, "size"> {
50
+ checked: boolean;
51
+ onPress: () => void;
52
+ /** Size of the checkbox (supports responsive values) */
53
+ size?: ResponsiveValue<CheckboxSize, RestyleTheme["breakpoints"]>;
54
+ disabled?: boolean;
55
+ color?: RestyleColor;
56
+ label?: string;
57
+ accessibilityLabel?: string;
58
+ accessibilityHint?: string;
59
+ testID?: string;
60
+ }
61
+
62
+ export interface UseCheckboxLogicParams {
63
+ checked: boolean;
64
+ disabled: boolean;
65
+ size: ResponsiveValue<CheckboxSize, RestyleTheme["breakpoints"]>;
66
+ color: RestyleColor;
67
+ label?: string;
68
+ accessibilityLabel?: string;
69
+ accessibilityHint?: string;
70
+ }
71
+
72
+ export interface UseCheckboxLogicReturn {
73
+ iconSize: IconSize;
74
+ checkIconColor: RestyleColor;
75
+ containerStyle: ViewStyle;
76
+ hitSlop: Insets;
77
+ a11yProps: CheckboxA11yProps;
78
+ }
79
+
80
+ export interface CheckboxA11yParams {
81
+ label?: string;
82
+ accessibilityLabel?: string;
83
+ accessibilityHint?: string;
84
+ checked: boolean;
85
+ disabled: boolean;
86
+ }
87
+
88
+ export interface CheckboxA11yProps {
89
+ accessibilityLabel: string;
90
+ accessibilityHint?: string;
91
+ accessibilityRole: "checkbox";
92
+ accessibilityState: { checked: boolean; disabled: boolean };
93
+ }
94
+
95
+ export interface ResolveCheckboxStyleParams {
96
+ checked: boolean;
97
+ disabled: boolean;
98
+ color: RestyleColor;
99
+ theme: RestyleTheme;
100
+ }
101
+
102
+ export interface CheckboxStyleResult {
103
+ backgroundColor: string;
104
+ borderColor: string;
105
+ opacity: number;
106
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Checkbox Module
3
+ *
4
+ * Atom component for binary toggle controls in forms.
5
+ *
6
+ * ## Mathematical Ratios
7
+ * - Icon = Container × ~75% (ICON_RATIOS.toggleControl)
8
+ * - All sizes meet 44px minimum touch target via hitSlop
9
+ *
10
+ * @example
11
+ * import { Checkbox } from "@/design-system/primitives/inputs/Checkbox";
12
+ *
13
+ * <Checkbox
14
+ * checked={isChecked}
15
+ * onPress={toggleChecked}
16
+ * accessibilityLabel="Accept terms"
17
+ * />
18
+ */
19
+
20
+ export { Checkbox } from "./Checkbox";
21
+
22
+ export type {
23
+ CheckboxProps,
24
+ CheckboxRef,
25
+ CheckboxSize,
26
+ CheckboxA11yParams,
27
+ CheckboxA11yProps,
28
+ } from "./Checkbox.types";
29
+
30
+ export { getCheckboxA11y } from "./Checkbox.a11y";
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Checkbox Logic Hook
3
+ *
4
+ * Extracts all computational logic from the Checkbox component.
5
+ * Returns memoized values for optimal render performance.
6
+ *
7
+ * Responsibilities:
8
+ * - Resolve responsive size prop to current breakpoint value
9
+ * - Compute icon size from container size (~75% ratio)
10
+ * - Determine icon color based on disabled state
11
+ * - Calculate container styles (background, border, opacity)
12
+ * - Compute hitSlop for proper touch targets
13
+ * - Generate accessibility props
14
+ *
15
+ * The component (Checkbox.tsx) is a "dumb view" that only renders
16
+ * the pre-computed values from this hook.
17
+ *
18
+ * @see Checkbox.tsx - Component consuming this hook
19
+ * @see Checkbox.helpers.ts - Pure calculation functions
20
+ * @see tokens/scales.ts - Mathematical ratios and constants
21
+ */
22
+
23
+ import { useResponsiveProp } from "@shopify/restyle";
24
+ import { useMemo } from "react";
25
+ import { ViewStyle } from "react-native";
26
+ import { useRestyleTheme } from "../../../core/restyle";
27
+ import { getCheckboxA11y } from "./Checkbox.a11y";
28
+ import {
29
+ getCheckboxHitSlop,
30
+ getCheckboxIconSize,
31
+ getCheckIconColor,
32
+ resolveCheckboxStyle,
33
+ } from "./Checkbox.helpers";
34
+ import { CheckboxSize, UseCheckboxLogicParams, UseCheckboxLogicReturn } from "./Checkbox.types";
35
+
36
+ /**
37
+ * Orchestrates Checkbox rendering logic and state management.
38
+ *
39
+ * Handles:
40
+ * - Responsive size resolution
41
+ * - Check icon size calculation (~75% of container ratio)
42
+ * - Check icon color determination
43
+ * - Container styles (background, border, opacity)
44
+ * - Hit slop calculation for 44px minimum touch target
45
+ * - Accessibility props generation with checked state
46
+ *
47
+ * @param params - Configuration object for checkbox behavior
48
+ * @param params.checked - Whether checkbox is checked
49
+ * @param params.disabled - Whether checkbox is disabled
50
+ * @param params.size - Size variant ('sm' | 'md' | 'lg')
51
+ * @param params.color - Theme color token for checked state
52
+ * @param params.label - Associated label text
53
+ * @param params.accessibilityLabel - Custom a11y label
54
+ * @param params.accessibilityHint - Describes action result
55
+ *
56
+ * @returns Computed values for rendering Checkbox
57
+ * @returns {number} iconSize - Check icon size in pixels
58
+ * @returns {string} checkIconColor - Check icon color token
59
+ * @returns {ViewStyle} containerStyle - Dynamic container styles
60
+ * @returns {Insets} hitSlop - Touch target expansion
61
+ * @returns {object} a11yProps - Accessibility props with checked state
62
+ *
63
+ * @performance This hook uses useMemo() for expensive calculations
64
+ *
65
+ * @example
66
+ * const { iconSize, checkIconColor, containerStyle, hitSlop, a11yProps } = useCheckboxLogic({
67
+ * checked: true,
68
+ * disabled: false,
69
+ * size: "md",
70
+ * color: "accentPrimary",
71
+ * label: "Accept terms and conditions",
72
+ * });
73
+ */
74
+ export function useCheckboxLogic(
75
+ params: UseCheckboxLogicParams,
76
+ ): UseCheckboxLogicReturn {
77
+ const {
78
+ checked,
79
+ disabled,
80
+ size,
81
+ color,
82
+ label,
83
+ accessibilityLabel,
84
+ accessibilityHint,
85
+ } = params;
86
+
87
+ const theme = useRestyleTheme();
88
+ const resolvedSize = (useResponsiveProp(size) ?? "md") as CheckboxSize;
89
+
90
+ const iconSize = getCheckboxIconSize(resolvedSize);
91
+ const checkIconColor = getCheckIconColor(disabled);
92
+ const hitSlop = getCheckboxHitSlop(resolvedSize);
93
+
94
+ const containerStyle = useMemo((): ViewStyle => {
95
+ const styleResult = resolveCheckboxStyle({
96
+ checked,
97
+ disabled,
98
+ color,
99
+ theme,
100
+ });
101
+
102
+ return {
103
+ backgroundColor: styleResult.backgroundColor,
104
+ borderColor: styleResult.borderColor,
105
+ opacity: styleResult.opacity,
106
+ };
107
+ }, [checked, disabled, color, theme]);
108
+
109
+ const a11yProps = useMemo(
110
+ () => getCheckboxA11y({ label, accessibilityLabel, accessibilityHint, checked, disabled }),
111
+ [label, accessibilityLabel, accessibilityHint, checked, disabled],
112
+ );
113
+
114
+ return {
115
+ iconSize,
116
+ checkIconColor,
117
+ containerStyle,
118
+ hitSlop,
119
+ a11yProps,
120
+ };
121
+ }
@@ -0,0 +1,123 @@
1
+ /**
2
+ * RadioButton Component Helpers
3
+ *
4
+ * Pure functions for computing RadioButton styles, sizes, and colors.
5
+ * All calculations are derived from the centralized scales.ts token file.
6
+ */
7
+
8
+ import { Insets } from "react-native";
9
+ import { RADIO_BUTTON_HIT_SLOP, RADIO_BUTTON_INNER_SIZE } from "../../../tokens/scales";
10
+ import { BaseThemeColor } from "../../../types";
11
+ import {
12
+ RadioButtonSize,
13
+ RadioButtonStyleResult,
14
+ ResolveRadioButtonStyleParams,
15
+ } from "./RadioButton.types";
16
+
17
+ // =============================================================================
18
+ // INNER DOT SIZE RESOLUTION
19
+ // =============================================================================
20
+
21
+ /**
22
+ * Get inner dot size based on radio button size.
23
+ *
24
+ * Uses RADIO_BUTTON_INNER_SIZE from scales.ts for mathematical consistency.
25
+ * Target ratio: ~40% of container
26
+ *
27
+ * | Size | Container | Inner Dot | Ratio |
28
+ * |------|-----------|-----------|-------|
29
+ * | sm | 20px | 8px | 40% |
30
+ * | md | 24px | 10px | 42% |
31
+ * | lg | 32px | 14px | 44% |
32
+ *
33
+ * @param size - The radio button size variant
34
+ * @returns The inner dot diameter in pixels
35
+ */
36
+ export function getRadioButtonInnerSize(size: RadioButtonSize): number {
37
+ return RADIO_BUTTON_INNER_SIZE[size];
38
+ }
39
+
40
+ // =============================================================================
41
+ // TOUCH TARGET (hitSlop)
42
+ // =============================================================================
43
+
44
+ /**
45
+ * Get hitSlop based on radio button size.
46
+ *
47
+ * Uses pre-calculated values from scales.ts to ensure
48
+ * all radio buttons meet the 44px minimum touch target.
49
+ *
50
+ * | Size | Container | hitSlop | Total |
51
+ * |------|-----------|---------|-------|
52
+ * | sm | 20px | 12px | 44px |
53
+ * | md | 24px | 10px | 44px |
54
+ * | lg | 32px | 6px | 44px |
55
+ *
56
+ * @param size - The radio button size variant
57
+ * @returns Insets for touch target expansion
58
+ */
59
+ export function getRadioButtonHitSlop(size: RadioButtonSize): Insets {
60
+ return RADIO_BUTTON_HIT_SLOP[size];
61
+ }
62
+
63
+ // =============================================================================
64
+ // STYLE RESOLUTION
65
+ // =============================================================================
66
+
67
+ /**
68
+ * Resolve dynamic style properties for the RadioButton container.
69
+ *
70
+ * Static styles (borderRadius, borderWidth) come from theme variants.
71
+ * This function handles dynamic state-based values:
72
+ * - backgroundColor based on checked and disabled state
73
+ * - borderColor based on checked and disabled state
74
+ * - opacity based on disabled state
75
+ *
76
+ * @param params - Resolution parameters
77
+ * @returns Computed style object
78
+ */
79
+ export function resolveRadioButtonStyle(
80
+ params: ResolveRadioButtonStyleParams
81
+ ): RadioButtonStyleResult {
82
+ const { checked, disabled, color, theme } = params;
83
+
84
+ // Background color logic
85
+ let backgroundColor: string;
86
+ if (!checked) {
87
+ backgroundColor = "transparent";
88
+ } else if (disabled) {
89
+ backgroundColor = theme.colors.interactiveDisabled;
90
+ } else {
91
+ backgroundColor = theme.colors[color as BaseThemeColor];
92
+ }
93
+
94
+ // Border color logic
95
+ let borderColor: string;
96
+ if (disabled) {
97
+ borderColor = theme.colors.borderDefault;
98
+ } else if (!checked) {
99
+ borderColor = theme.colors.borderDefault;
100
+ } else {
101
+ borderColor = theme.colors[color as BaseThemeColor];
102
+ }
103
+
104
+ return {
105
+ backgroundColor,
106
+ borderColor,
107
+ opacity: 1,
108
+ };
109
+ }
110
+
111
+ // =============================================================================
112
+ // VALIDATION
113
+ // =============================================================================
114
+
115
+ /**
116
+ * Validate if a value is a valid RadioButton size.
117
+ *
118
+ * @param value - Value to validate
119
+ * @returns True if valid size
120
+ */
121
+ export function isValidRadioButtonSize(value: unknown): value is RadioButtonSize {
122
+ return value === "sm" || value === "md" || value === "lg";
123
+ }