@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,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StayEasy Design System - Border Radius Tokens
|
|
3
|
+
*
|
|
4
|
+
* Philosophy: Rounded but not bubbly.
|
|
5
|
+
* Friendly corners that feel premium, not childish.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* borderRadius: theme.radii.md // 12
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// =============================================================================
|
|
12
|
+
// BORDER RADIUS SCALE
|
|
13
|
+
// =============================================================================
|
|
14
|
+
|
|
15
|
+
export const radii = {
|
|
16
|
+
/** 0 - Sharp corners */
|
|
17
|
+
none: 0,
|
|
18
|
+
|
|
19
|
+
/** 4 - Subtle rounding */
|
|
20
|
+
xs: 4,
|
|
21
|
+
|
|
22
|
+
/** 8 - Light rounding */
|
|
23
|
+
sm: 8,
|
|
24
|
+
|
|
25
|
+
/** 12 - Standard rounding (most common) */
|
|
26
|
+
md: 12,
|
|
27
|
+
|
|
28
|
+
/** 16 - Generous rounding */
|
|
29
|
+
lg: 16,
|
|
30
|
+
|
|
31
|
+
/** 20 - Large rounding */
|
|
32
|
+
xl: 20,
|
|
33
|
+
|
|
34
|
+
/** 24 - Extra large rounding */
|
|
35
|
+
'2xl': 24,
|
|
36
|
+
|
|
37
|
+
/** 32 - Very large rounding */
|
|
38
|
+
'3xl': 32,
|
|
39
|
+
|
|
40
|
+
/** 9999 - Full/pill rounding */
|
|
41
|
+
full: 9999,
|
|
42
|
+
} as const;
|
|
43
|
+
|
|
44
|
+
// =============================================================================
|
|
45
|
+
// SEMANTIC RADII
|
|
46
|
+
// =============================================================================
|
|
47
|
+
|
|
48
|
+
export const semanticRadii = {
|
|
49
|
+
/** Buttons - friendly but not too soft */
|
|
50
|
+
button: radii.md, // 12
|
|
51
|
+
|
|
52
|
+
/** Input fields - matches buttons */
|
|
53
|
+
input: radii.md, // 12
|
|
54
|
+
|
|
55
|
+
/** Cards - generous for premium feel */
|
|
56
|
+
card: radii.lg, // 16
|
|
57
|
+
|
|
58
|
+
/** Modals - prominent rounding */
|
|
59
|
+
modal: radii.xl, // 20
|
|
60
|
+
|
|
61
|
+
/** Bottom sheets - very rounded top */
|
|
62
|
+
sheet: radii['2xl'], // 24
|
|
63
|
+
|
|
64
|
+
/** Chips/badges - pill-like */
|
|
65
|
+
chip: radii.full, // 9999
|
|
66
|
+
|
|
67
|
+
/** Avatars - circular */
|
|
68
|
+
avatar: radii.full, // 9999
|
|
69
|
+
|
|
70
|
+
/** Image thumbnails */
|
|
71
|
+
thumbnail: radii.md, // 12
|
|
72
|
+
|
|
73
|
+
/** Toast notifications */
|
|
74
|
+
toast: radii.md, // 12
|
|
75
|
+
} as const;
|
|
76
|
+
|
|
77
|
+
// =============================================================================
|
|
78
|
+
// TYPE EXPORTS
|
|
79
|
+
// =============================================================================
|
|
80
|
+
|
|
81
|
+
export type Radii = typeof radii;
|
|
82
|
+
export type RadiiKey = keyof typeof radii;
|
|
@@ -0,0 +1,588 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* El Sendero Design System - Scale Tokens
|
|
3
|
+
*
|
|
4
|
+
* Defines mathematical relationships between component sizes.
|
|
5
|
+
* All ratios are explicitly documented to prevent magic numbers.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* import { ICON_BUTTON_ICON_SIZE, calculateHitSlop } from '@/design-system/tokens/scales';
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// =============================================================================
|
|
12
|
+
// ICON RATIO CONSTANTS
|
|
13
|
+
// =============================================================================
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Icon-to-container ratios for different component types.
|
|
17
|
+
* These ensure visual consistency across the design system.
|
|
18
|
+
*
|
|
19
|
+
* Design Rationale:
|
|
20
|
+
* - Circular buttons need breathing room for touch feedback ripples
|
|
21
|
+
* - Toggle controls need maximum icon visibility within compact containers
|
|
22
|
+
* - Text-adjacent icons should match the text visual weight
|
|
23
|
+
*/
|
|
24
|
+
export const ICON_RATIOS = {
|
|
25
|
+
/**
|
|
26
|
+
* Circular button ratio (~45%)
|
|
27
|
+
* Used by: IconButton
|
|
28
|
+
* Provides breathing room for touch feedback ripples
|
|
29
|
+
*/
|
|
30
|
+
circularButton: 0.45,
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Toggle control ratio (~75%)
|
|
34
|
+
* Used by: Checkbox, Radio
|
|
35
|
+
* Maximizes icon visibility within compact container
|
|
36
|
+
*/
|
|
37
|
+
toggleControl: 0.75,
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Text-adjacent ratio (100%)
|
|
41
|
+
* Used by: Button with icon, TextInput icons
|
|
42
|
+
* Icon matches the text visual weight
|
|
43
|
+
*/
|
|
44
|
+
textAdjacent: 1.0,
|
|
45
|
+
} as const;
|
|
46
|
+
|
|
47
|
+
// =============================================================================
|
|
48
|
+
// COMPONENT SIZE SCALES
|
|
49
|
+
// =============================================================================
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Unified container size scale (in pixels)
|
|
53
|
+
* All interactive button components use this scale.
|
|
54
|
+
*
|
|
55
|
+
* | Size | Pixels | Use Case |
|
|
56
|
+
* |------|--------|----------|
|
|
57
|
+
* | sm | 36 | Dense UIs, secondary actions |
|
|
58
|
+
* | md | 44 | Default (meets Apple HIG 44px touch target) |
|
|
59
|
+
* | lg | 56 | Tablets, high-emphasis actions |
|
|
60
|
+
*/
|
|
61
|
+
export const CONTAINER_SIZES = {
|
|
62
|
+
sm: 36,
|
|
63
|
+
md: 44,
|
|
64
|
+
lg: 56,
|
|
65
|
+
} as const;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Button heights (slightly different scale for text buttons)
|
|
69
|
+
*
|
|
70
|
+
* | Size | Pixels | Use Case |
|
|
71
|
+
* |------|--------|----------|
|
|
72
|
+
* | sm | 36 | Dense forms, secondary actions |
|
|
73
|
+
* | md | 44 | Default mobile buttons |
|
|
74
|
+
* | lg | 52 | Primary CTAs, prominent actions |
|
|
75
|
+
*/
|
|
76
|
+
export const BUTTON_HEIGHTS = {
|
|
77
|
+
sm: 36,
|
|
78
|
+
md: 44,
|
|
79
|
+
lg: 52,
|
|
80
|
+
} as const;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Toggle control container sizes (compact scale for form controls)
|
|
84
|
+
*
|
|
85
|
+
* | Size | Pixels | Use Case |
|
|
86
|
+
* |------|--------|----------|
|
|
87
|
+
* | sm | 20 | Dense forms, compact UI |
|
|
88
|
+
* | md | 24 | Default mobile checkboxes |
|
|
89
|
+
* | lg | 32 | Tablets, accessibility focus |
|
|
90
|
+
*/
|
|
91
|
+
export const TOGGLE_CONTAINER_SIZES = {
|
|
92
|
+
sm: 20,
|
|
93
|
+
md: 24,
|
|
94
|
+
lg: 32,
|
|
95
|
+
} as const;
|
|
96
|
+
|
|
97
|
+
// =============================================================================
|
|
98
|
+
// ICON SIZE VALUES (in pixels)
|
|
99
|
+
// =============================================================================
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Icon size scale in pixels.
|
|
103
|
+
* Mirrors the iconSizes theme values for calculations.
|
|
104
|
+
*/
|
|
105
|
+
export const ICON_PIXEL_SIZES = {
|
|
106
|
+
xs: 12,
|
|
107
|
+
sm: 16,
|
|
108
|
+
md: 20,
|
|
109
|
+
lg: 24,
|
|
110
|
+
xl: 32,
|
|
111
|
+
"2xl": 40,
|
|
112
|
+
"3xl": 48,
|
|
113
|
+
} as const;
|
|
114
|
+
|
|
115
|
+
// =============================================================================
|
|
116
|
+
// DERIVED ICON SIZE MAPPINGS
|
|
117
|
+
// =============================================================================
|
|
118
|
+
|
|
119
|
+
type ComponentSize = "sm" | "md" | "lg";
|
|
120
|
+
type IconSizeKey = "xs" | "sm" | "md" | "lg" | "xl" | "2xl" | "3xl";
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* IconButton: Container -> Icon mapping
|
|
124
|
+
*
|
|
125
|
+
* Uses ~45% ratio (ICON_RATIOS.circularButton)
|
|
126
|
+
*
|
|
127
|
+
* | Size | Container | Icon | Actual Ratio |
|
|
128
|
+
* |------|-----------|--------|--------------|
|
|
129
|
+
* | sm | 36px | 16px | 44% |
|
|
130
|
+
* | md | 44px | 20px | 45% |
|
|
131
|
+
* | lg | 56px | 24px | 43% |
|
|
132
|
+
*/
|
|
133
|
+
export const ICON_BUTTON_ICON_SIZE: Record<ComponentSize, IconSizeKey> = {
|
|
134
|
+
sm: "sm", // 16px / 36px = 44%
|
|
135
|
+
md: "md", // 20px / 44px = 45%
|
|
136
|
+
lg: "lg", // 24px / 56px = 43%
|
|
137
|
+
} as const;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Checkbox: Container -> Icon mapping
|
|
141
|
+
*
|
|
142
|
+
* Uses ~75% ratio (ICON_RATIOS.toggleControl)
|
|
143
|
+
*
|
|
144
|
+
* | Size | Container | Icon | Actual Ratio |
|
|
145
|
+
* |------|-----------|--------|--------------|
|
|
146
|
+
* | sm | 20px | 16px | 80% |
|
|
147
|
+
* | md | 24px | 20px | 83% |
|
|
148
|
+
* | lg | 32px | 24px | 75% |
|
|
149
|
+
*/
|
|
150
|
+
export const CHECKBOX_ICON_SIZE: Record<ComponentSize, IconSizeKey> = {
|
|
151
|
+
sm: "sm", // 16px / 20px = 80%
|
|
152
|
+
md: "md", // 20px / 24px = 83%
|
|
153
|
+
lg: "lg", // 24px / 32px = 75%
|
|
154
|
+
} as const;
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Button: Size -> Icon mapping (text-adjacent)
|
|
158
|
+
*
|
|
159
|
+
* Icons match label visual weight for optical balance.
|
|
160
|
+
*
|
|
161
|
+
* | Size | Height | Icon | Text Variant |
|
|
162
|
+
* |------|--------|--------|--------------|
|
|
163
|
+
* | sm | 36px | 16px | labelSmall |
|
|
164
|
+
* | md | 44px | 20px | labelMedium |
|
|
165
|
+
* | lg | 52px | 24px | labelLarge |
|
|
166
|
+
*/
|
|
167
|
+
export const BUTTON_ICON_SIZE: Record<ComponentSize, IconSizeKey> = {
|
|
168
|
+
sm: "sm", // 16px
|
|
169
|
+
md: "md", // 20px
|
|
170
|
+
lg: "lg", // 24px
|
|
171
|
+
} as const;
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* TextInput: Size -> Icon mapping
|
|
175
|
+
*
|
|
176
|
+
* Icons match input text size for visual harmony.
|
|
177
|
+
*
|
|
178
|
+
* | Size | Icon |
|
|
179
|
+
* |------|--------|
|
|
180
|
+
* | sm | 16px |
|
|
181
|
+
* | md | 20px |
|
|
182
|
+
* | lg | 24px |
|
|
183
|
+
*/
|
|
184
|
+
export const TEXT_INPUT_ICON_SIZE: Record<ComponentSize, IconSizeKey> = {
|
|
185
|
+
sm: "sm",
|
|
186
|
+
md: "md",
|
|
187
|
+
lg: "lg",
|
|
188
|
+
} as const;
|
|
189
|
+
|
|
190
|
+
// =============================================================================
|
|
191
|
+
// SWITCH SPECIFIC SCALES
|
|
192
|
+
// =============================================================================
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Switch thumb inset in pixels.
|
|
196
|
+
* This is the padding between the track edge and the thumb.
|
|
197
|
+
* Total thumb padding = inset * 2 (both sides)
|
|
198
|
+
*
|
|
199
|
+
* Derived from the 2xs spacing token (2px) for consistency.
|
|
200
|
+
*/
|
|
201
|
+
export const SWITCH_THUMB_INSET = 2;
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Switch dimension definitions.
|
|
205
|
+
*
|
|
206
|
+
* Track width:height ratio is maintained at approximately 1.7:1
|
|
207
|
+
* Thumb size is calculated as: trackHeight - (SWITCH_THUMB_INSET * 2)
|
|
208
|
+
*
|
|
209
|
+
* | Size | Track W | Track H | Thumb | W:H Ratio |
|
|
210
|
+
* |------|---------|---------|-------|-----------|
|
|
211
|
+
* | sm | 40px | 24px | 20px | 1.67:1 |
|
|
212
|
+
* | md | 48px | 28px | 24px | 1.71:1 |
|
|
213
|
+
* | lg | 56px | 32px | 28px | 1.75:1 |
|
|
214
|
+
*/
|
|
215
|
+
export const SWITCH_DIMENSIONS: Record<
|
|
216
|
+
ComponentSize,
|
|
217
|
+
{ trackWidth: number; trackHeight: number }
|
|
218
|
+
> = {
|
|
219
|
+
sm: { trackWidth: 40, trackHeight: 24 },
|
|
220
|
+
md: { trackWidth: 48, trackHeight: 28 },
|
|
221
|
+
lg: { trackWidth: 56, trackHeight: 32 },
|
|
222
|
+
} as const;
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Calculate switch thumb size from track height.
|
|
226
|
+
* Uses SWITCH_THUMB_INSET to maintain consistent padding.
|
|
227
|
+
*
|
|
228
|
+
* @param trackHeight - The track height in pixels
|
|
229
|
+
* @returns The thumb diameter in pixels
|
|
230
|
+
*/
|
|
231
|
+
export function calculateSwitchThumbSize(trackHeight: number): number {
|
|
232
|
+
return trackHeight - SWITCH_THUMB_INSET * 2;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// =============================================================================
|
|
236
|
+
// TOUCH TARGET UTILITIES
|
|
237
|
+
// =============================================================================
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Minimum touch target size per Apple Human Interface Guidelines.
|
|
241
|
+
* All interactive elements should have at least 44x44px touch area.
|
|
242
|
+
*/
|
|
243
|
+
export const MIN_TOUCH_TARGET = 44;
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Calculate hitSlop needed to reach minimum touch target.
|
|
247
|
+
*
|
|
248
|
+
* This function ensures all interactive elements meet the 44px
|
|
249
|
+
* minimum touch target requirement, regardless of their visual size.
|
|
250
|
+
*
|
|
251
|
+
* @param visualSize - The visual size of the element in pixels
|
|
252
|
+
* @returns Insets object to expand the touch target
|
|
253
|
+
*
|
|
254
|
+
* @example
|
|
255
|
+
* // 36px button needs 4px padding each side to reach 44px
|
|
256
|
+
* calculateHitSlop(36) // { top: 4, bottom: 4, left: 4, right: 4 }
|
|
257
|
+
*
|
|
258
|
+
* // 44px button already meets target
|
|
259
|
+
* calculateHitSlop(44) // { top: 0, bottom: 0, left: 0, right: 0 }
|
|
260
|
+
*/
|
|
261
|
+
export function calculateHitSlop(visualSize: number): {
|
|
262
|
+
top: number;
|
|
263
|
+
bottom: number;
|
|
264
|
+
left: number;
|
|
265
|
+
right: number;
|
|
266
|
+
} {
|
|
267
|
+
if (visualSize >= MIN_TOUCH_TARGET) {
|
|
268
|
+
return { top: 0, bottom: 0, left: 0, right: 0 };
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const padding = Math.ceil((MIN_TOUCH_TARGET - visualSize) / 2);
|
|
272
|
+
return { top: padding, bottom: padding, left: padding, right: padding };
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Pre-calculated hitSlop for IconButton container sizes.
|
|
277
|
+
* Uses calculateHitSlop internally.
|
|
278
|
+
*
|
|
279
|
+
* | Size | Container | hitSlop | Total Touch Area |
|
|
280
|
+
* |------|-----------|---------|------------------|
|
|
281
|
+
* | sm | 36px | 4px | 44px |
|
|
282
|
+
* | md | 44px | 0px | 44px |
|
|
283
|
+
* | lg | 56px | 0px | 56px |
|
|
284
|
+
*/
|
|
285
|
+
export const ICON_BUTTON_HIT_SLOP: Record<
|
|
286
|
+
ComponentSize,
|
|
287
|
+
{ top: number; bottom: number; left: number; right: number }
|
|
288
|
+
> = {
|
|
289
|
+
sm: calculateHitSlop(CONTAINER_SIZES.sm),
|
|
290
|
+
md: calculateHitSlop(CONTAINER_SIZES.md),
|
|
291
|
+
lg: calculateHitSlop(CONTAINER_SIZES.lg),
|
|
292
|
+
} as const;
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Pre-calculated hitSlop for Checkbox container sizes.
|
|
296
|
+
*
|
|
297
|
+
* | Size | Container | hitSlop | Total Touch Area |
|
|
298
|
+
* |------|-----------|---------|------------------|
|
|
299
|
+
* | sm | 20px | 12px | 44px |
|
|
300
|
+
* | md | 24px | 10px | 44px |
|
|
301
|
+
* | lg | 32px | 6px | 44px |
|
|
302
|
+
*/
|
|
303
|
+
export const CHECKBOX_HIT_SLOP: Record<
|
|
304
|
+
ComponentSize,
|
|
305
|
+
{ top: number; bottom: number; left: number; right: number }
|
|
306
|
+
> = {
|
|
307
|
+
sm: calculateHitSlop(TOGGLE_CONTAINER_SIZES.sm),
|
|
308
|
+
md: calculateHitSlop(TOGGLE_CONTAINER_SIZES.md),
|
|
309
|
+
lg: calculateHitSlop(TOGGLE_CONTAINER_SIZES.lg),
|
|
310
|
+
} as const;
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* RadioButton inner dot size mapping.
|
|
314
|
+
*
|
|
315
|
+
* Uses ~40% ratio of container for the inner filled dot.
|
|
316
|
+
*
|
|
317
|
+
* | Size | Container | Inner Dot | Actual Ratio |
|
|
318
|
+
* |------|-----------|-----------|--------------|
|
|
319
|
+
* | sm | 20px | 8px | 40% |
|
|
320
|
+
* | md | 24px | 10px | 42% |
|
|
321
|
+
* | lg | 32px | 14px | 44% |
|
|
322
|
+
*/
|
|
323
|
+
export const RADIO_BUTTON_INNER_SIZE: Record<ComponentSize, number> = {
|
|
324
|
+
sm: 8,
|
|
325
|
+
md: 10,
|
|
326
|
+
lg: 14,
|
|
327
|
+
} as const;
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Pre-calculated hitSlop for RadioButton container sizes.
|
|
331
|
+
* Same as Checkbox (uses TOGGLE_CONTAINER_SIZES).
|
|
332
|
+
*
|
|
333
|
+
* | Size | Container | hitSlop | Total Touch Area |
|
|
334
|
+
* |------|-----------|---------|------------------|
|
|
335
|
+
* | sm | 20px | 12px | 44px |
|
|
336
|
+
* | md | 24px | 10px | 44px |
|
|
337
|
+
* | lg | 32px | 6px | 44px |
|
|
338
|
+
*/
|
|
339
|
+
export const RADIO_BUTTON_HIT_SLOP: Record<
|
|
340
|
+
ComponentSize,
|
|
341
|
+
{ top: number; bottom: number; left: number; right: number }
|
|
342
|
+
> = {
|
|
343
|
+
sm: calculateHitSlop(TOGGLE_CONTAINER_SIZES.sm),
|
|
344
|
+
md: calculateHitSlop(TOGGLE_CONTAINER_SIZES.md),
|
|
345
|
+
lg: calculateHitSlop(TOGGLE_CONTAINER_SIZES.lg),
|
|
346
|
+
} as const;
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Pre-calculated hitSlop for Switch track (uses height for calculation).
|
|
350
|
+
*
|
|
351
|
+
* | Size | Track H | hitSlop | Total Touch Area |
|
|
352
|
+
* |------|---------|---------|------------------|
|
|
353
|
+
* | sm | 24px | 10px | 44px |
|
|
354
|
+
* | md | 28px | 8px | 44px |
|
|
355
|
+
* | lg | 32px | 6px | 44px |
|
|
356
|
+
*/
|
|
357
|
+
export const SWITCH_HIT_SLOP: Record<
|
|
358
|
+
ComponentSize,
|
|
359
|
+
{ top: number; bottom: number; left: number; right: number }
|
|
360
|
+
> = {
|
|
361
|
+
sm: calculateHitSlop(SWITCH_DIMENSIONS.sm.trackHeight),
|
|
362
|
+
md: calculateHitSlop(SWITCH_DIMENSIONS.md.trackHeight),
|
|
363
|
+
lg: calculateHitSlop(SWITCH_DIMENSIONS.lg.trackHeight),
|
|
364
|
+
} as const;
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* hitSlop for ghost IconButton variant.
|
|
368
|
+
* Since ghost has no container, we use the icon visual size.
|
|
369
|
+
*
|
|
370
|
+
* | Size | Icon | hitSlop | Total Touch Area |
|
|
371
|
+
* |------|--------|---------|------------------|
|
|
372
|
+
* | sm | 16px | 14px | 44px |
|
|
373
|
+
* | md | 20px | 12px | 44px |
|
|
374
|
+
* | lg | 24px | 10px | 44px |
|
|
375
|
+
*/
|
|
376
|
+
export const GHOST_ICON_BUTTON_HIT_SLOP: Record<
|
|
377
|
+
ComponentSize,
|
|
378
|
+
{ top: number; bottom: number; left: number; right: number }
|
|
379
|
+
> = {
|
|
380
|
+
sm: calculateHitSlop(ICON_PIXEL_SIZES.sm),
|
|
381
|
+
md: calculateHitSlop(ICON_PIXEL_SIZES.md),
|
|
382
|
+
lg: calculateHitSlop(ICON_PIXEL_SIZES.lg),
|
|
383
|
+
} as const;
|
|
384
|
+
|
|
385
|
+
// =============================================================================
|
|
386
|
+
// SPINNER SIZE MAPPING
|
|
387
|
+
// =============================================================================
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Spinner size mapping - consistent across all components.
|
|
391
|
+
*
|
|
392
|
+
* | Component Size | Spinner Size |
|
|
393
|
+
* |----------------|--------------|
|
|
394
|
+
* | sm | sm |
|
|
395
|
+
* | md | sm |
|
|
396
|
+
* | lg | lg |
|
|
397
|
+
*/
|
|
398
|
+
export const SPINNER_SIZE: Record<ComponentSize, "sm" | "lg"> = {
|
|
399
|
+
sm: "sm",
|
|
400
|
+
md: "sm",
|
|
401
|
+
lg: "lg",
|
|
402
|
+
} as const;
|
|
403
|
+
|
|
404
|
+
// =============================================================================
|
|
405
|
+
// TEXT VARIANT MAPPINGS
|
|
406
|
+
// =============================================================================
|
|
407
|
+
|
|
408
|
+
type TextVariantKey =
|
|
409
|
+
| "labelSmall"
|
|
410
|
+
| "labelMedium"
|
|
411
|
+
| "labelLarge"
|
|
412
|
+
| "bodySmall"
|
|
413
|
+
| "bodyMedium"
|
|
414
|
+
| "bodyLarge";
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Button text variant mapping.
|
|
418
|
+
*
|
|
419
|
+
* | Button Size | Text Variant |
|
|
420
|
+
* |-------------|--------------|
|
|
421
|
+
* | sm | labelSmall |
|
|
422
|
+
* | md | labelMedium |
|
|
423
|
+
* | lg | labelLarge |
|
|
424
|
+
*/
|
|
425
|
+
export const BUTTON_TEXT_VARIANT: Record<ComponentSize, TextVariantKey> = {
|
|
426
|
+
sm: "labelSmall",
|
|
427
|
+
md: "labelMedium",
|
|
428
|
+
lg: "labelLarge",
|
|
429
|
+
} as const;
|
|
430
|
+
|
|
431
|
+
// =============================================================================
|
|
432
|
+
// PROGRESS BAR SIZE MAPPING
|
|
433
|
+
// =============================================================================
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Progress bar height mapping.
|
|
437
|
+
*
|
|
438
|
+
* | Size | Height |
|
|
439
|
+
* |------|--------|
|
|
440
|
+
* | sm | 4px |
|
|
441
|
+
* | md | 8px |
|
|
442
|
+
* | lg | 12px |
|
|
443
|
+
*/
|
|
444
|
+
export const PROGRESS_BAR_HEIGHTS: Record<ComponentSize, number> = {
|
|
445
|
+
sm: 4,
|
|
446
|
+
md: 8,
|
|
447
|
+
lg: 12,
|
|
448
|
+
} as const;
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Progress bar border radius mapping.
|
|
452
|
+
* Uses half of height for pill shape.
|
|
453
|
+
*
|
|
454
|
+
* | Size | Radius |
|
|
455
|
+
* |------|--------|
|
|
456
|
+
* | sm | 2px |
|
|
457
|
+
* | md | 4px |
|
|
458
|
+
* | lg | 6px |
|
|
459
|
+
*/
|
|
460
|
+
export const PROGRESS_BAR_BORDER_RADIUS: Record<ComponentSize, number> = {
|
|
461
|
+
sm: 2,
|
|
462
|
+
md: 4,
|
|
463
|
+
lg: 6,
|
|
464
|
+
} as const;
|
|
465
|
+
|
|
466
|
+
// =============================================================================
|
|
467
|
+
// ANIMATION TOKENS
|
|
468
|
+
// =============================================================================
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Animation spring configuration tokens.
|
|
472
|
+
* Used by animated components for consistent motion.
|
|
473
|
+
*/
|
|
474
|
+
export const ANIMATION_TOKENS = {
|
|
475
|
+
/**
|
|
476
|
+
* Switch toggle animation config.
|
|
477
|
+
* Uses spring physics for natural feel.
|
|
478
|
+
*/
|
|
479
|
+
switch: {
|
|
480
|
+
/** Main toggle animation */
|
|
481
|
+
toggle: { friction: 8, tension: 100 },
|
|
482
|
+
/** Thumb scale feedback animation */
|
|
483
|
+
thumbScale: { friction: 6, tension: 200 },
|
|
484
|
+
},
|
|
485
|
+
} as const;
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Shadow tokens for elevated components.
|
|
489
|
+
*/
|
|
490
|
+
export const SHADOW_TOKENS = {
|
|
491
|
+
/**
|
|
492
|
+
* Switch thumb shadow.
|
|
493
|
+
* Provides depth and separation from track.
|
|
494
|
+
*/
|
|
495
|
+
switchThumb: {
|
|
496
|
+
shadowColor: "#000",
|
|
497
|
+
shadowOffset: { width: 0, height: 2 },
|
|
498
|
+
shadowOpacity: 0.2,
|
|
499
|
+
shadowRadius: 2,
|
|
500
|
+
elevation: 2,
|
|
501
|
+
},
|
|
502
|
+
} as const;
|
|
503
|
+
|
|
504
|
+
// =============================================================================
|
|
505
|
+
// SELECT SHEET DIMENSIONS
|
|
506
|
+
// =============================================================================
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* SelectSheet industry-standard dimensions based on Material Design 3 and iOS HIG.
|
|
510
|
+
*
|
|
511
|
+
* Design Rationale:
|
|
512
|
+
* - Center dialog: max-width 400px (mobile), 560px (tablet) for readability
|
|
513
|
+
* - Bottom sheet: full width on mobile, max-width on tablet
|
|
514
|
+
* - Header height: 56px (Material Design standard)
|
|
515
|
+
* - Option row height: 52px (between Material 48px and iOS 44px minimum)
|
|
516
|
+
* - Corner radius: 16px (modern rounded aesthetic)
|
|
517
|
+
* - Overlay opacity: 50% (balanced backdrop)
|
|
518
|
+
*
|
|
519
|
+
* Responsive Breakpoints:
|
|
520
|
+
* - Phone: < 768px (compact layout)
|
|
521
|
+
* - Tablet: >= 768px (expanded layout with max-width constraints)
|
|
522
|
+
*/
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* SelectSheet size scale for different use cases.
|
|
526
|
+
*
|
|
527
|
+
* | Size | Max Width | List Height | Use Case |
|
|
528
|
+
* |------|-----------|-------------|----------|
|
|
529
|
+
* | sm | 320px | 200px | Few options, compact dialogs |
|
|
530
|
+
* | md | 400px | 300px | Default, most use cases |
|
|
531
|
+
* | lg | 560px | 400px | Tablets, many options |
|
|
532
|
+
*/
|
|
533
|
+
/**
|
|
534
|
+
* SelectSheet layout constants.
|
|
535
|
+
*
|
|
536
|
+
* Simplified to match Modal pattern:
|
|
537
|
+
* - Phone: Full width minus horizontalMargin (16px)
|
|
538
|
+
* - Tablet: Capped at tabletMaxWidth (480px) for phone-like experience
|
|
539
|
+
*/
|
|
540
|
+
export const SELECT_SHEET_LAYOUT = {
|
|
541
|
+
/** Horizontal margin from screen edges */
|
|
542
|
+
horizontalMargin: 16,
|
|
543
|
+
/** Maximum width on tablets (phone-like cap) */
|
|
544
|
+
tabletMaxWidth: 480,
|
|
545
|
+
/** Option row height (touch target minimum) */
|
|
546
|
+
optionRowHeight: 52,
|
|
547
|
+
/** Header height */
|
|
548
|
+
headerHeight: 56,
|
|
549
|
+
/** Footer height (Done button area) */
|
|
550
|
+
footerHeight: 56,
|
|
551
|
+
/** Overlay background opacity */
|
|
552
|
+
overlayOpacity: 0.5,
|
|
553
|
+
/** Default list height - good UX for most cases */
|
|
554
|
+
defaultListHeight: 300,
|
|
555
|
+
/** Tablet breakpoint in pixels */
|
|
556
|
+
tabletBreakpoint: 768,
|
|
557
|
+
} as const;
|
|
558
|
+
|
|
559
|
+
// =============================================================================
|
|
560
|
+
// MODAL COMPONENT SCALES
|
|
561
|
+
// =============================================================================
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Modal spacing and layout constants.
|
|
565
|
+
*
|
|
566
|
+
* Width behavior:
|
|
567
|
+
* - Phone: Full width minus horizontalMargin (16px = spacing.lg) on each side
|
|
568
|
+
* - Tablet (≥768px): Capped at tabletMaxWidth (480px) for phone-like experience
|
|
569
|
+
*/
|
|
570
|
+
export const MODAL_LAYOUT = {
|
|
571
|
+
/** Horizontal margin from screen edges (matches spacing.lg = 16px) */
|
|
572
|
+
horizontalMargin: 16,
|
|
573
|
+
/** Max width on tablets — prevents the modal from stretching too wide */
|
|
574
|
+
tabletMaxWidth: 480,
|
|
575
|
+
/** Corner radius for the modal container */
|
|
576
|
+
borderRadius: 16,
|
|
577
|
+
/** Overlay background opacity */
|
|
578
|
+
overlayOpacity: 0.5,
|
|
579
|
+
/** Tablet breakpoint in pixels */
|
|
580
|
+
tabletBreakpoint: 768,
|
|
581
|
+
} as const;
|
|
582
|
+
|
|
583
|
+
// =============================================================================
|
|
584
|
+
// TYPE EXPORTS
|
|
585
|
+
// =============================================================================
|
|
586
|
+
|
|
587
|
+
export type ComponentSizeType = ComponentSize;
|
|
588
|
+
export type IconSizeType = IconSizeKey;
|