@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,95 @@
1
+ import { BoxProps, ResponsiveValue, VariantProps } from "@shopify/restyle";
2
+ import { ReactNode } from "react";
3
+ import { AccessibilityState, GestureResponderEvent, PressableProps, StyleProp, ViewStyle } from "react-native";
4
+ import { RestyleTheme } from "../../../core/restyle";
5
+
6
+ /**
7
+ * Visual variant for the Card component.
8
+ *
9
+ * - `outlined`: Card with border and background
10
+ * - `flat`: Card with background only, no border
11
+ */
12
+ export type CardVariant = Exclude<
13
+ keyof RestyleTheme["cardVariants"],
14
+ "defaults"
15
+ >;
16
+
17
+ export type BaseCardProps = VariantProps<RestyleTheme, "cardVariants", "variant"> &
18
+ BoxProps<RestyleTheme> &
19
+ PressableProps;
20
+
21
+ /**
22
+ * Props for the Card component.
23
+ *
24
+ * @example
25
+ * // Basic usage
26
+ * <Card>
27
+ * <Text>Card content</Text>
28
+ * </Card>
29
+ *
30
+ * @example
31
+ * // Pressable card
32
+ * <Card
33
+ * variant="outlined"
34
+ * onPress={handlePress}
35
+ * accessibilityLabel="Product card"
36
+ * >
37
+ * <Text>Tap to view details</Text>
38
+ * </Card>
39
+ */
40
+ export interface CardProps extends Omit<BaseCardProps, "variant"> {
41
+ children?: ReactNode;
42
+ /** Visual variant of the card (supports responsive values) */
43
+ variant?: ResponsiveValue<CardVariant, RestyleTheme["breakpoints"]>;
44
+ /** Whether the card is disabled (only applies when onPress is provided) */
45
+ disabled?: boolean;
46
+ /** Container style overrides */
47
+ style?: StyleProp<ViewStyle>;
48
+ /** Test ID for testing */
49
+ testID?: string;
50
+ /** Accessibility label */
51
+ accessibilityLabel?: string;
52
+ /** Accessibility hint */
53
+ accessibilityHint?: string;
54
+ }
55
+
56
+ /**
57
+ * Parameters for generating card accessibility props.
58
+ */
59
+ export interface CardA11yParams {
60
+ /** Label for screen readers (required for pressable cards) */
61
+ accessibilityLabel?: string;
62
+ /** Whether the card is disabled */
63
+ disabled: boolean;
64
+ /** Press handler (presence determines if card is pressable) */
65
+ onPress?: ((event: GestureResponderEvent) => void) | null;
66
+ }
67
+
68
+ /**
69
+ * Accessibility props returned by getCardA11y.
70
+ */
71
+ export interface CardA11yProps {
72
+ accessibilityRole?: "button" | "none";
73
+ accessibilityLabel?: string;
74
+ accessibilityState?: AccessibilityState;
75
+ }
76
+
77
+ /**
78
+ * Parameters for the useCardLogic hook.
79
+ */
80
+ export interface UseCardLogicParams {
81
+ disabled?: boolean;
82
+ onPress?: ((event: GestureResponderEvent) => void) | null;
83
+ accessibilityLabel?: string;
84
+ }
85
+
86
+ /**
87
+ * Return value from the useCardLogic hook.
88
+ */
89
+ export interface UseCardLogicReturn {
90
+ isDisabled: boolean;
91
+ isPressable: boolean;
92
+ opacity: number;
93
+ handlePress: (event: GestureResponderEvent) => void;
94
+ a11yProps: CardA11yProps;
95
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Card Module
3
+ *
4
+ * Molecule component for content grouping with optional press behavior.
5
+ *
6
+ * ## Variants
7
+ * - `outlined`: Border with background (default)
8
+ * - `flat`: Background only, no border
9
+ *
10
+ * @example
11
+ * import { Card } from "@/design-system/primitives/content/Card";
12
+ *
13
+ * <Card padding="lg" onPress={handlePress} accessibilityLabel="View details">
14
+ * <Text>Card content</Text>
15
+ * </Card>
16
+ */
17
+
18
+ export { Card } from "./Card";
19
+ export type { CardProps, CardVariant } from "./Card.types";
20
+ export * from "./Card.a11y";
@@ -0,0 +1,48 @@
1
+ import { useCallback, useMemo } from "react";
2
+ import { GestureResponderEvent } from "react-native";
3
+ import { getCardA11y } from "./Card.a11y";
4
+ import { UseCardLogicParams, UseCardLogicReturn } from "./Card.types";
5
+
6
+ /**
7
+ * Orchestrates Card rendering logic and state management.
8
+ *
9
+ * Handles:
10
+ * - Pressable state determination
11
+ * - Disabled state management
12
+ * - Opacity calculation for disabled state
13
+ * - Press handler with disabled check
14
+ * - Accessibility props generation
15
+ *
16
+ * @param params - Configuration object for card behavior
17
+ * @returns Computed values for rendering Card
18
+ */
19
+ export function useCardLogic({
20
+ disabled = false,
21
+ onPress,
22
+ accessibilityLabel,
23
+ }: UseCardLogicParams): UseCardLogicReturn {
24
+ const isPressable = !!onPress;
25
+ const isDisabled = isPressable && disabled;
26
+ const opacity = isDisabled ? 0.5 : 1;
27
+
28
+ const handlePress = useCallback(
29
+ (event: GestureResponderEvent) => {
30
+ if (isDisabled || !onPress) return;
31
+ onPress(event);
32
+ },
33
+ [isDisabled, onPress],
34
+ );
35
+
36
+ const a11yProps = useMemo(
37
+ () => getCardA11y({ accessibilityLabel, disabled: isDisabled, onPress }),
38
+ [accessibilityLabel, isDisabled, onPress],
39
+ );
40
+
41
+ return {
42
+ isDisabled,
43
+ isPressable,
44
+ opacity,
45
+ handlePress,
46
+ a11yProps,
47
+ };
48
+ }
@@ -0,0 +1,304 @@
1
+ /**
2
+ * Chip Component Helpers
3
+ *
4
+ * Pure functions for computing Chip styles, sizes, and colors.
5
+ * All calculations are derived from the centralized scales.ts token file.
6
+ *
7
+ * ## Size Scale
8
+ * | Size | Height | Icon | Text Variant | Padding |
9
+ * |------|--------|-------|--------------|---------|
10
+ * | sm | 24px | xs | captionSmall | xs (4px)|
11
+ * | md | 32px | sm | labelSmall | sm (8px)|
12
+ * | lg | 40px | md | labelMedium | md (12px)|
13
+ *
14
+ * ## Color Support
15
+ * Accepts any theme color token. Preset configurations exist for semantic colors:
16
+ * - accentPrimary, accentSecondary
17
+ * - feedbackSuccess, feedbackWarning, feedbackError, feedbackInfo
18
+ *
19
+ * Any other theme color will use sensible defaults for filled/outlined variants.
20
+ */
21
+
22
+ import { RestyleColor } from "../../../types";
23
+ import { IconSize } from "../Icon";
24
+ import {
25
+ ChipColorTokens,
26
+ ChipSize,
27
+ ChipStyleResult,
28
+ ChipTextVariant,
29
+ ChipVariant,
30
+ ResolveChipStyleParams,
31
+ } from "./Chip.types";
32
+
33
+ // =============================================================================
34
+ // THEME VARIANT KEY MAPPING
35
+ // =============================================================================
36
+
37
+ /**
38
+ * Theme variant key mapping.
39
+ * Maps component variant names to theme variant keys.
40
+ */
41
+ type ThemeVariantKey = "filled" | "outlined";
42
+
43
+ const VARIANT_TO_THEME_KEY: Record<ChipVariant, ThemeVariantKey> = {
44
+ filled: "filled",
45
+ outlined: "outlined",
46
+ } as const;
47
+
48
+ /**
49
+ * Get theme variant key from component variant.
50
+ *
51
+ * @param variant - The component variant
52
+ * @returns The theme variant key
53
+ */
54
+ export function getThemeVariantKey(variant: ChipVariant): ThemeVariantKey {
55
+ return VARIANT_TO_THEME_KEY[variant];
56
+ }
57
+
58
+ // =============================================================================
59
+ // SIZE RESOLUTION
60
+ // =============================================================================
61
+
62
+ /**
63
+ * Chip icon size mapping.
64
+ *
65
+ * | Chip Size | Icon Size |
66
+ * |-----------|-----------|
67
+ * | sm | xs (12px) |
68
+ * | md | sm (16px) |
69
+ * | lg | md (20px) |
70
+ */
71
+ export const CHIP_ICON_SIZE: Record<ChipSize, IconSize> = {
72
+ sm: "xs",
73
+ md: "sm",
74
+ lg: "md",
75
+ } as const;
76
+
77
+ /**
78
+ * Chip text variant mapping.
79
+ *
80
+ * | Chip Size | Text Variant |
81
+ * |-----------|--------------|
82
+ * | sm | captionSmall |
83
+ * | md | labelSmall |
84
+ * | lg | labelMedium |
85
+ */
86
+ export const CHIP_TEXT_VARIANT: Record<ChipSize, ChipTextVariant> = {
87
+ sm: "captionSmall",
88
+ md: "labelSmall",
89
+ lg: "labelMedium",
90
+ } as const;
91
+
92
+ /**
93
+ * Get icon size based on chip size.
94
+ *
95
+ * @param size - The chip size
96
+ * @returns The icon size key
97
+ */
98
+ export function getChipIconSize(size: ChipSize): IconSize {
99
+ return CHIP_ICON_SIZE[size];
100
+ }
101
+
102
+ /**
103
+ * Get text variant based on chip size.
104
+ *
105
+ * @param size - The chip size
106
+ * @returns The text variant for Text
107
+ */
108
+ export function getChipTextVariant(size: ChipSize): ChipTextVariant {
109
+ return CHIP_TEXT_VARIANT[size];
110
+ }
111
+
112
+ // =============================================================================
113
+ // COLOR RESOLUTION
114
+ // =============================================================================
115
+
116
+ /**
117
+ * Preset color configurations for common semantic colors.
118
+ * These provide optimized backgrounds and text colors.
119
+ */
120
+ const PRESET_COLORS: Record<string, ChipColorTokens> = {
121
+ accentPrimary: {
122
+ filled: "accentPrimary",
123
+ accent: "accentPrimary",
124
+ filledText: "textInverse",
125
+ accentText: "accentPrimary",
126
+ },
127
+ accentSecondary: {
128
+ filled: "accentSecondary",
129
+ accent: "accentSecondary",
130
+ filledText: "textInverse",
131
+ accentText: "accentSecondary",
132
+ },
133
+ feedbackSuccess: {
134
+ filled: "feedbackSuccess",
135
+ accent: "feedbackSuccess",
136
+ filledText: "textInverse",
137
+ accentText: "feedbackSuccessDark",
138
+ },
139
+ feedbackWarning: {
140
+ filled: "feedbackWarning",
141
+ accent: "feedbackWarning",
142
+ filledText: "textPrimary",
143
+ accentText: "feedbackWarningDark",
144
+ },
145
+ feedbackError: {
146
+ filled: "feedbackError",
147
+ accent: "feedbackError",
148
+ filledText: "textInverse",
149
+ accentText: "feedbackErrorDark",
150
+ },
151
+ feedbackInfo: {
152
+ filled: "feedbackInfo",
153
+ accent: "feedbackInfo",
154
+ filledText: "textInverse",
155
+ accentText: "feedbackInfoDark",
156
+ },
157
+ } as const;
158
+
159
+ /**
160
+ * Get color tokens for a chip color.
161
+ * Uses preset configurations for semantic colors, or generates tokens for any theme color.
162
+ *
163
+ * @param color - Any theme color token
164
+ * @returns Color tokens for the chip
165
+ */
166
+ export function getChipColorTokens(color: RestyleColor): ChipColorTokens {
167
+ // Check if we have a preset for this color
168
+ if (color in PRESET_COLORS) {
169
+ return PRESET_COLORS[color];
170
+ }
171
+
172
+ // For any other color, use it directly with sensible defaults
173
+ return {
174
+ filled: color,
175
+ accent: color,
176
+ filledText: "textInverse",
177
+ accentText: color,
178
+ };
179
+ }
180
+
181
+ /**
182
+ * Get text color based on variant, color, and state.
183
+ *
184
+ * | Variant | Normal | Selected | Disabled |
185
+ * |----------|--------------|--------------|--------------|
186
+ * | filled | textInverse | textInverse | textDisabled |
187
+ * | outlined | accent color | textInverse | textDisabled |
188
+ *
189
+ * @param variant - Visual variant
190
+ * @param color - Any theme color token
191
+ * @param selected - Whether chip is selected
192
+ * @param disabled - Whether chip is disabled
193
+ * @returns Computed text color token
194
+ */
195
+ export function getChipTextColor(
196
+ variant: ChipVariant,
197
+ color: RestyleColor,
198
+ selected: boolean,
199
+ disabled: boolean
200
+ ): RestyleColor {
201
+ if (disabled) {
202
+ return "textDisabled";
203
+ }
204
+
205
+ const tokens = getChipColorTokens(color);
206
+
207
+ // When selected (for outlined), use filled text color
208
+ if (selected && variant !== "filled") {
209
+ return tokens.filledText;
210
+ }
211
+
212
+ if (variant === "filled") {
213
+ return tokens.filledText;
214
+ }
215
+
216
+ return tokens.accentText;
217
+ }
218
+
219
+ /**
220
+ * Get icon color based on variant, color, and state.
221
+ * Follows the same logic as text color for visual consistency.
222
+ *
223
+ * @param variant - Visual variant
224
+ * @param color - Any theme color token
225
+ * @param selected - Whether chip is selected
226
+ * @param disabled - Whether chip is disabled
227
+ * @returns Computed icon color token
228
+ */
229
+ export function getChipIconColor(
230
+ variant: ChipVariant,
231
+ color: RestyleColor,
232
+ selected: boolean,
233
+ disabled: boolean
234
+ ): RestyleColor {
235
+ return getChipTextColor(variant, color, selected, disabled);
236
+ }
237
+
238
+ // =============================================================================
239
+ // STYLE RESOLUTION
240
+ // =============================================================================
241
+
242
+ /**
243
+ * Resolve dynamic style properties for the Chip container.
244
+ *
245
+ * Static styles (borderRadius, padding) come from theme variants.
246
+ * This function handles dynamic state-based values:
247
+ * - backgroundColor based on variant, color, and selected state
248
+ * - borderColor based on variant and color
249
+ * - opacity based on disabled state
250
+ *
251
+ * Returns token keys instead of resolved hex values to let Restyle handle resolution.
252
+ *
253
+ * @param params - Resolution parameters
254
+ * @returns Computed style object with token keys
255
+ */
256
+ export function resolveChipStyle(
257
+ params: Omit<ResolveChipStyleParams, "theme">
258
+ ): ChipStyleResult {
259
+ const { variant, color, selected, disabled } = params;
260
+
261
+ const tokens = getChipColorTokens(color);
262
+
263
+ let backgroundColor: RestyleColor | "transparent";
264
+ let borderColor: RestyleColor | "transparent";
265
+ let borderWidth: number;
266
+
267
+ if (disabled) {
268
+ switch (variant) {
269
+ case "filled":
270
+ backgroundColor = "interactiveDisabled";
271
+ borderColor = "transparent";
272
+ borderWidth = 0;
273
+ break;
274
+ case "outlined":
275
+ backgroundColor = selected ? "interactiveDisabled" : "transparent";
276
+ borderColor = "borderDefault";
277
+ borderWidth = 1;
278
+ break;
279
+ default:
280
+ backgroundColor = "interactiveDisabled";
281
+ borderColor = "transparent";
282
+ borderWidth = 0;
283
+ }
284
+ } else {
285
+ switch (variant) {
286
+ case "filled":
287
+ backgroundColor = tokens.filled;
288
+ borderColor = "transparent";
289
+ borderWidth = 0;
290
+ break;
291
+ case "outlined":
292
+ backgroundColor = selected ? tokens.filled : "transparent";
293
+ borderColor = tokens.accent;
294
+ borderWidth = 1;
295
+ break;
296
+ default:
297
+ backgroundColor = tokens.filled;
298
+ borderColor = "transparent";
299
+ borderWidth = 0;
300
+ }
301
+ }
302
+
303
+ return { backgroundColor, borderColor, borderWidth, opacity: 1 };
304
+ }
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Chip Component
3
+ *
4
+ * @description Compact element for tags, filters, and selections - Atom
5
+ *
6
+ * Chip displays compact elements used for tags, categories, filters,
7
+ * and selection groups. Supports interactive (selectable/dismissible)
8
+ * and static display modes.
9
+ *
10
+ * ## Size Scale
11
+ * | Size | Height | Icon | Text Variant | Padding |
12
+ * |------|--------|-------|--------------|---------|
13
+ * | sm | 24px | xs | captionSmall | xs (4px)|
14
+ * | md | 32px | sm | labelSmall | sm (8px)|
15
+ * | lg | 40px | md | labelMedium | md (12px)|
16
+ *
17
+ * ## Variants
18
+ * - `filled`: Solid background (default)
19
+ * - `outlined`: Border only, transparent background
20
+ *
21
+ * ## Features
22
+ * - Responsive size prop (phone/tablet breakpoints)
23
+ * - Optional leading icon
24
+ * - Selectable with visual feedback
25
+ * - Dismissible with close button
26
+ * - Five color options (primary, secondary, success, warning, error)
27
+ * - Full accessibility support
28
+ * - Memoized for performance
29
+ *
30
+ * @see Chip.types.ts - Type definitions
31
+ * @see Chip.helpers.ts - Pure calculation functions
32
+ * @see Chip.a11y.ts - Accessibility prop generation
33
+ *
34
+ * @example
35
+ * // Basic tag
36
+ * <Chip label="JavaScript" />
37
+ *
38
+ * @example
39
+ * // Selectable chip
40
+ * <Chip
41
+ * label="Option A"
42
+ * onPress={() => setSelected(!selected)}
43
+ * selected={selected}
44
+ * variant="outlined"
45
+ * />
46
+ *
47
+ * @example
48
+ * // Dismissible filter chip
49
+ * <Chip
50
+ * label="Category: Tech"
51
+ * iconName="filter-list"
52
+ * onDelete={() => removeFilter()}
53
+ * color="primary"
54
+ * />
55
+ *
56
+ * @example
57
+ * // Status chip
58
+ * <Chip
59
+ * label="Active"
60
+ * variant="outlined"
61
+ * color="success"
62
+ * size="sm"
63
+ * />
64
+ */
65
+
66
+ import {
67
+ backgroundColor,
68
+ border,
69
+ createRestyleComponent,
70
+ createVariant,
71
+ layout,
72
+ spacing,
73
+ spacingShorthand,
74
+ } from "@shopify/restyle";
75
+ import React, { memo, useCallback } from "react";
76
+ import { Pressable } from "react-native";
77
+ import { RestyleTheme } from "../../../core/restyle";
78
+ import { BaseThemeColor, RestyleColor } from "../../../types";
79
+ import { IconButton } from "../../actions/IconButton";
80
+ import { Text } from "../../typography";
81
+ import { Icon } from "../Icon";
82
+ import { BaseChipProps, ChipProps } from "./Chip.types";
83
+ import { useChipLogic } from "./useChipLogic";
84
+
85
+ /** Narrow extended color tokens for Restyle interop (safe at runtime). */
86
+ const asBase = (c: RestyleColor | "transparent") => c as BaseThemeColor;
87
+
88
+ /**
89
+ * Base container with Restyle variant support.
90
+ * @internal
91
+ */
92
+ const BaseChipContainer = createRestyleComponent<BaseChipProps, RestyleTheme>(
93
+ [
94
+ createVariant({ themeKey: "chipVariants" }),
95
+ createVariant({ themeKey: "chipSizes", property: "size" }),
96
+ backgroundColor,
97
+ border,
98
+ layout,
99
+ spacing,
100
+ spacingShorthand,
101
+ ],
102
+ Pressable,
103
+ );
104
+
105
+ function ChipComponent({
106
+ label,
107
+ onPress,
108
+ onDelete,
109
+ variant = "filled",
110
+ size = "md",
111
+ color = "accentPrimary",
112
+ selected = false,
113
+ disabled = false,
114
+ iconName,
115
+ testID,
116
+ accessibilityHint,
117
+ accessibilityLabel,
118
+ style,
119
+ ...rest
120
+ }: ChipProps) {
121
+ const {
122
+ themeVariantKey,
123
+ textVariant,
124
+ textColor,
125
+ iconSize,
126
+ iconColor,
127
+ containerStyle,
128
+ a11yProps,
129
+ isInteractive,
130
+ showDeleteIcon,
131
+ deleteIconColor,
132
+ deleteHint,
133
+ } = useChipLogic({
134
+ label,
135
+ variant,
136
+ size,
137
+ color,
138
+ selected,
139
+ disabled,
140
+ onPress,
141
+ onDelete,
142
+ accessibilityLabel,
143
+ accessibilityHint,
144
+ });
145
+
146
+ const handlePress = useCallback(() => {
147
+ if (!disabled && onPress) {
148
+ onPress();
149
+ }
150
+ }, [disabled, onPress]);
151
+
152
+ const handleDelete = useCallback(() => {
153
+ if (!disabled && onDelete) {
154
+ onDelete();
155
+ }
156
+ }, [disabled, onDelete]);
157
+
158
+ return (
159
+ <BaseChipContainer
160
+ variant={themeVariantKey}
161
+ size={size}
162
+ onPress={isInteractive ? handlePress : undefined}
163
+ disabled={disabled}
164
+ testID={testID}
165
+ flexDirection="row"
166
+ alignItems="center"
167
+ justifyContent="center"
168
+ gap="xs"
169
+ backgroundColor={asBase(containerStyle.backgroundColor)}
170
+ borderColor={asBase(containerStyle.borderColor)}
171
+ borderWidth={containerStyle.borderWidth}
172
+ opacity={containerStyle.opacity}
173
+ style={style}
174
+ {...a11yProps}
175
+ {...rest}
176
+ >
177
+ {iconName && (
178
+ <Icon
179
+ name={iconName}
180
+ size={iconSize}
181
+ color={asBase(iconColor)}
182
+ accessibilityLabel={iconName}
183
+ />
184
+ )}
185
+ <Text variant={textVariant} color={asBase(textColor)}>
186
+ {label}
187
+ </Text>
188
+ {showDeleteIcon && (
189
+ <IconButton
190
+ iconName="close"
191
+ onPress={handleDelete}
192
+ variant="ghost"
193
+ size="sm"
194
+ disabled={disabled}
195
+ color={asBase(deleteIconColor)}
196
+ accessibilityLabel={`Remove ${label}`}
197
+ accessibilityHint={deleteHint}
198
+ />
199
+ )}
200
+ </BaseChipContainer>
201
+ );
202
+ }
203
+
204
+ export const Chip = memo(ChipComponent);
205
+ Chip.displayName = "Chip";