@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,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
|
+
}
|