@cdx-ui/components 0.0.1-beta.65 → 0.0.1-beta.67

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 (242) hide show
  1. package/lib/commonjs/components/AlertDialog/index.js +12 -10
  2. package/lib/commonjs/components/AlertDialog/index.js.map +1 -1
  3. package/lib/commonjs/components/Avatar/index.js +125 -25
  4. package/lib/commonjs/components/Avatar/index.js.map +1 -1
  5. package/lib/commonjs/components/Button/index.js +127 -28
  6. package/lib/commonjs/components/Button/index.js.map +1 -1
  7. package/lib/commonjs/components/Button/styles.js +9 -3
  8. package/lib/commonjs/components/Button/styles.js.map +1 -1
  9. package/lib/commonjs/components/CLAUDE.md +115 -11
  10. package/lib/commonjs/components/Checkbox/index.js +69 -22
  11. package/lib/commonjs/components/Checkbox/index.js.map +1 -1
  12. package/lib/commonjs/components/Checkbox/styles.js +1 -1
  13. package/lib/commonjs/components/Checkbox/styles.js.map +1 -1
  14. package/lib/commonjs/components/Chip/index.js +44 -36
  15. package/lib/commonjs/components/Chip/index.js.map +1 -1
  16. package/lib/commonjs/components/Chip/styles.js +6 -35
  17. package/lib/commonjs/components/Chip/styles.js.map +1 -1
  18. package/lib/commonjs/components/Dialog/index.js +92 -72
  19. package/lib/commonjs/components/Dialog/index.js.map +1 -1
  20. package/lib/commonjs/components/Field/index.js +70 -26
  21. package/lib/commonjs/components/Field/index.js.map +1 -1
  22. package/lib/commonjs/components/Form/index.js +5 -12
  23. package/lib/commonjs/components/Form/index.js.map +1 -1
  24. package/lib/commonjs/components/IconButton/index.js +5 -33
  25. package/lib/commonjs/components/IconButton/index.js.map +1 -1
  26. package/lib/commonjs/components/Input/index.js +50 -20
  27. package/lib/commonjs/components/Input/index.js.map +1 -1
  28. package/lib/commonjs/components/Link/index.js +14 -7
  29. package/lib/commonjs/components/Link/index.js.map +1 -1
  30. package/lib/commonjs/components/ListItem/index.js +129 -54
  31. package/lib/commonjs/components/ListItem/index.js.map +1 -1
  32. package/lib/commonjs/components/OtpInput/index.js +94 -28
  33. package/lib/commonjs/components/OtpInput/index.js.map +1 -1
  34. package/lib/commonjs/components/ProgressBar/index.js +8 -13
  35. package/lib/commonjs/components/ProgressBar/index.js.map +1 -1
  36. package/lib/commonjs/components/Radio/index.js +51 -53
  37. package/lib/commonjs/components/Radio/index.js.map +1 -1
  38. package/lib/commonjs/components/Select/index.js +112 -46
  39. package/lib/commonjs/components/Select/index.js.map +1 -1
  40. package/lib/commonjs/components/Select/styles.js +2 -2
  41. package/lib/commonjs/components/Select/styles.js.map +1 -1
  42. package/lib/commonjs/components/Switch/BaseSwitch.js +23 -13
  43. package/lib/commonjs/components/Switch/BaseSwitch.js.map +1 -1
  44. package/lib/commonjs/components/Switch/BaseSwitch.web.js +25 -21
  45. package/lib/commonjs/components/Switch/BaseSwitch.web.js.map +1 -1
  46. package/lib/commonjs/components/Switch/index.js +34 -3
  47. package/lib/commonjs/components/Switch/index.js.map +1 -1
  48. package/lib/commonjs/components/Tile/index.js +150 -77
  49. package/lib/commonjs/components/Tile/index.js.map +1 -1
  50. package/lib/commonjs/figma/Chip.figma.js +2 -2
  51. package/lib/commonjs/figma/Chip.figma.js.map +1 -1
  52. package/lib/commonjs/utils/positionToDataIcon.js +41 -0
  53. package/lib/commonjs/utils/positionToDataIcon.js.map +1 -0
  54. package/lib/module/components/AlertDialog/index.js +13 -11
  55. package/lib/module/components/AlertDialog/index.js.map +1 -1
  56. package/lib/module/components/Avatar/index.js +128 -28
  57. package/lib/module/components/Avatar/index.js.map +1 -1
  58. package/lib/module/components/Button/index.js +130 -32
  59. package/lib/module/components/Button/index.js.map +1 -1
  60. package/lib/module/components/Button/styles.js +8 -2
  61. package/lib/module/components/Button/styles.js.map +1 -1
  62. package/lib/module/components/CLAUDE.md +115 -11
  63. package/lib/module/components/Checkbox/index.js +72 -25
  64. package/lib/module/components/Checkbox/index.js.map +1 -1
  65. package/lib/module/components/Checkbox/styles.js +1 -1
  66. package/lib/module/components/Checkbox/styles.js.map +1 -1
  67. package/lib/module/components/Chip/index.js +47 -39
  68. package/lib/module/components/Chip/index.js.map +1 -1
  69. package/lib/module/components/Chip/styles.js +6 -35
  70. package/lib/module/components/Chip/styles.js.map +1 -1
  71. package/lib/module/components/Dialog/index.js +96 -76
  72. package/lib/module/components/Dialog/index.js.map +1 -1
  73. package/lib/module/components/Field/index.js +74 -31
  74. package/lib/module/components/Field/index.js.map +1 -1
  75. package/lib/module/components/Form/index.js +7 -14
  76. package/lib/module/components/Form/index.js.map +1 -1
  77. package/lib/module/components/IconButton/index.js +6 -34
  78. package/lib/module/components/IconButton/index.js.map +1 -1
  79. package/lib/module/components/Input/index.js +53 -23
  80. package/lib/module/components/Input/index.js.map +1 -1
  81. package/lib/module/components/Link/index.js +16 -8
  82. package/lib/module/components/Link/index.js.map +1 -1
  83. package/lib/module/components/ListItem/index.js +134 -59
  84. package/lib/module/components/ListItem/index.js.map +1 -1
  85. package/lib/module/components/OtpInput/index.js +94 -28
  86. package/lib/module/components/OtpInput/index.js.map +1 -1
  87. package/lib/module/components/ProgressBar/index.js +9 -14
  88. package/lib/module/components/ProgressBar/index.js.map +1 -1
  89. package/lib/module/components/Radio/index.js +54 -57
  90. package/lib/module/components/Radio/index.js.map +1 -1
  91. package/lib/module/components/Select/index.js +118 -52
  92. package/lib/module/components/Select/index.js.map +1 -1
  93. package/lib/module/components/Select/styles.js +2 -2
  94. package/lib/module/components/Select/styles.js.map +1 -1
  95. package/lib/module/components/Switch/BaseSwitch.js +24 -14
  96. package/lib/module/components/Switch/BaseSwitch.js.map +1 -1
  97. package/lib/module/components/Switch/BaseSwitch.web.js +26 -22
  98. package/lib/module/components/Switch/BaseSwitch.web.js.map +1 -1
  99. package/lib/module/components/Switch/index.js +35 -4
  100. package/lib/module/components/Switch/index.js.map +1 -1
  101. package/lib/module/components/Tile/index.js +155 -83
  102. package/lib/module/components/Tile/index.js.map +1 -1
  103. package/lib/module/figma/Chip.figma.js +2 -2
  104. package/lib/module/figma/Chip.figma.js.map +1 -1
  105. package/lib/module/utils/positionToDataIcon.js +38 -0
  106. package/lib/module/utils/positionToDataIcon.js.map +1 -0
  107. package/lib/typescript/components/AlertDialog/index.d.ts.map +1 -1
  108. package/lib/typescript/components/Avatar/index.d.ts +6 -2
  109. package/lib/typescript/components/Avatar/index.d.ts.map +1 -1
  110. package/lib/typescript/components/Button/index.d.ts +31 -8
  111. package/lib/typescript/components/Button/index.d.ts.map +1 -1
  112. package/lib/typescript/components/Button/styles.d.ts +2 -1
  113. package/lib/typescript/components/Button/styles.d.ts.map +1 -1
  114. package/lib/typescript/components/Checkbox/index.d.ts +3 -1
  115. package/lib/typescript/components/Checkbox/index.d.ts.map +1 -1
  116. package/lib/typescript/components/Chip/index.d.ts +19 -9
  117. package/lib/typescript/components/Chip/index.d.ts.map +1 -1
  118. package/lib/typescript/components/Chip/styles.d.ts +0 -1
  119. package/lib/typescript/components/Chip/styles.d.ts.map +1 -1
  120. package/lib/typescript/components/Dialog/index.d.ts +12 -6
  121. package/lib/typescript/components/Dialog/index.d.ts.map +1 -1
  122. package/lib/typescript/components/Dialog/styles.d.ts +1 -1
  123. package/lib/typescript/components/Field/index.d.ts +14 -7
  124. package/lib/typescript/components/Field/index.d.ts.map +1 -1
  125. package/lib/typescript/components/Form/index.d.ts +4 -4
  126. package/lib/typescript/components/Form/index.d.ts.map +1 -1
  127. package/lib/typescript/components/IconButton/index.d.ts.map +1 -1
  128. package/lib/typescript/components/Input/index.d.ts +11 -3
  129. package/lib/typescript/components/Input/index.d.ts.map +1 -1
  130. package/lib/typescript/components/Link/index.d.ts +2 -0
  131. package/lib/typescript/components/Link/index.d.ts.map +1 -1
  132. package/lib/typescript/components/ListItem/index.d.ts +28 -6
  133. package/lib/typescript/components/ListItem/index.d.ts.map +1 -1
  134. package/lib/typescript/components/OtpInput/index.d.ts +1 -1
  135. package/lib/typescript/components/OtpInput/index.d.ts.map +1 -1
  136. package/lib/typescript/components/ProgressBar/index.d.ts +4 -4
  137. package/lib/typescript/components/ProgressBar/index.d.ts.map +1 -1
  138. package/lib/typescript/components/Radio/index.d.ts +4 -0
  139. package/lib/typescript/components/Radio/index.d.ts.map +1 -1
  140. package/lib/typescript/components/Select/index.d.ts +35 -2
  141. package/lib/typescript/components/Select/index.d.ts.map +1 -1
  142. package/lib/typescript/components/Select/styles.d.ts.map +1 -1
  143. package/lib/typescript/components/Switch/BaseSwitch.d.ts +12 -5
  144. package/lib/typescript/components/Switch/BaseSwitch.d.ts.map +1 -1
  145. package/lib/typescript/components/Switch/BaseSwitch.web.d.ts +11 -5
  146. package/lib/typescript/components/Switch/BaseSwitch.web.d.ts.map +1 -1
  147. package/lib/typescript/components/Switch/index.d.ts +5 -1
  148. package/lib/typescript/components/Switch/index.d.ts.map +1 -1
  149. package/lib/typescript/components/Tile/index.d.ts +8 -2
  150. package/lib/typescript/components/Tile/index.d.ts.map +1 -1
  151. package/lib/typescript/utils/positionToDataIcon.d.ts +29 -0
  152. package/lib/typescript/utils/positionToDataIcon.d.ts.map +1 -0
  153. package/package.json +5 -5
  154. package/src/components/AlertDialog/index.tsx +13 -14
  155. package/src/components/Avatar/index.tsx +134 -44
  156. package/src/components/Button/index.tsx +216 -44
  157. package/src/components/Button/styles.ts +50 -38
  158. package/src/components/CLAUDE.md +115 -11
  159. package/src/components/Checkbox/index.tsx +93 -32
  160. package/src/components/Checkbox/styles.ts +1 -1
  161. package/src/components/Chip/index.tsx +88 -61
  162. package/src/components/Chip/styles.ts +6 -18
  163. package/src/components/Dialog/index.tsx +139 -99
  164. package/src/components/Field/index.tsx +113 -49
  165. package/src/components/Form/index.tsx +18 -14
  166. package/src/components/IconButton/index.tsx +7 -31
  167. package/src/components/Input/index.tsx +75 -32
  168. package/src/components/Link/index.tsx +38 -12
  169. package/src/components/ListItem/index.tsx +158 -73
  170. package/src/components/OtpInput/index.tsx +126 -43
  171. package/src/components/ProgressBar/index.tsx +11 -16
  172. package/src/components/Radio/index.tsx +76 -54
  173. package/src/components/Select/index.tsx +174 -59
  174. package/src/components/Select/styles.ts +1 -2
  175. package/src/components/Switch/BaseSwitch.tsx +38 -16
  176. package/src/components/Switch/BaseSwitch.web.tsx +28 -35
  177. package/src/components/Switch/index.tsx +51 -4
  178. package/src/components/Tile/index.tsx +145 -94
  179. package/src/utils/positionToDataIcon.ts +34 -0
  180. package/lib/commonjs/components/Field/FieldLabel.js +0 -33
  181. package/lib/commonjs/components/Field/FieldLabel.js.map +0 -1
  182. package/lib/commonjs/components/Field/FieldLabel.web.js +0 -22
  183. package/lib/commonjs/components/Field/FieldLabel.web.js.map +0 -1
  184. package/lib/commonjs/components/Form/FormRoot.js +0 -25
  185. package/lib/commonjs/components/Form/FormRoot.js.map +0 -1
  186. package/lib/commonjs/components/Form/FormRoot.web.js +0 -17
  187. package/lib/commonjs/components/Form/FormRoot.web.js.map +0 -1
  188. package/lib/commonjs/components/Input/BaseInput.android.js +0 -20
  189. package/lib/commonjs/components/Input/BaseInput.android.js.map +0 -1
  190. package/lib/commonjs/components/Input/BaseInput.js +0 -13
  191. package/lib/commonjs/components/Input/BaseInput.js.map +0 -1
  192. package/lib/commonjs/components/Input/BaseInput.web.js +0 -50
  193. package/lib/commonjs/components/Input/BaseInput.web.js.map +0 -1
  194. package/lib/commonjs/components/Select/SelectTriggerHost.js +0 -10
  195. package/lib/commonjs/components/Select/SelectTriggerHost.js.map +0 -1
  196. package/lib/commonjs/components/Select/SelectTriggerHost.web.js +0 -102
  197. package/lib/commonjs/components/Select/SelectTriggerHost.web.js.map +0 -1
  198. package/lib/module/components/Field/FieldLabel.js +0 -29
  199. package/lib/module/components/Field/FieldLabel.js.map +0 -1
  200. package/lib/module/components/Field/FieldLabel.web.js +0 -17
  201. package/lib/module/components/Field/FieldLabel.web.js.map +0 -1
  202. package/lib/module/components/Form/FormRoot.js +0 -21
  203. package/lib/module/components/Form/FormRoot.js.map +0 -1
  204. package/lib/module/components/Form/FormRoot.web.js +0 -12
  205. package/lib/module/components/Form/FormRoot.web.js.map +0 -1
  206. package/lib/module/components/Input/BaseInput.android.js +0 -16
  207. package/lib/module/components/Input/BaseInput.android.js.map +0 -1
  208. package/lib/module/components/Input/BaseInput.js +0 -4
  209. package/lib/module/components/Input/BaseInput.js.map +0 -1
  210. package/lib/module/components/Input/BaseInput.web.js +0 -45
  211. package/lib/module/components/Input/BaseInput.web.js.map +0 -1
  212. package/lib/module/components/Select/SelectTriggerHost.js +0 -7
  213. package/lib/module/components/Select/SelectTriggerHost.js.map +0 -1
  214. package/lib/module/components/Select/SelectTriggerHost.web.js +0 -97
  215. package/lib/module/components/Select/SelectTriggerHost.web.js.map +0 -1
  216. package/lib/typescript/components/Field/FieldLabel.d.ts +0 -18
  217. package/lib/typescript/components/Field/FieldLabel.d.ts.map +0 -1
  218. package/lib/typescript/components/Field/FieldLabel.web.d.ts +0 -16
  219. package/lib/typescript/components/Field/FieldLabel.web.d.ts.map +0 -1
  220. package/lib/typescript/components/Form/FormRoot.d.ts +0 -14
  221. package/lib/typescript/components/Form/FormRoot.d.ts.map +0 -1
  222. package/lib/typescript/components/Form/FormRoot.web.d.ts +0 -10
  223. package/lib/typescript/components/Form/FormRoot.web.d.ts.map +0 -1
  224. package/lib/typescript/components/Input/BaseInput.android.d.ts +0 -3
  225. package/lib/typescript/components/Input/BaseInput.android.d.ts.map +0 -1
  226. package/lib/typescript/components/Input/BaseInput.d.ts +0 -2
  227. package/lib/typescript/components/Input/BaseInput.d.ts.map +0 -1
  228. package/lib/typescript/components/Input/BaseInput.web.d.ts +0 -3
  229. package/lib/typescript/components/Input/BaseInput.web.d.ts.map +0 -1
  230. package/lib/typescript/components/Select/SelectTriggerHost.d.ts +0 -3
  231. package/lib/typescript/components/Select/SelectTriggerHost.d.ts.map +0 -1
  232. package/lib/typescript/components/Select/SelectTriggerHost.web.d.ts +0 -16
  233. package/lib/typescript/components/Select/SelectTriggerHost.web.d.ts.map +0 -1
  234. package/src/components/Field/FieldLabel.tsx +0 -33
  235. package/src/components/Field/FieldLabel.web.tsx +0 -25
  236. package/src/components/Form/FormRoot.tsx +0 -20
  237. package/src/components/Form/FormRoot.web.tsx +0 -12
  238. package/src/components/Input/BaseInput.android.tsx +0 -13
  239. package/src/components/Input/BaseInput.tsx +0 -1
  240. package/src/components/Input/BaseInput.web.tsx +0 -41
  241. package/src/components/Select/SelectTriggerHost.tsx +0 -4
  242. package/src/components/Select/SelectTriggerHost.web.tsx +0 -143
@@ -1,6 +1,14 @@
1
1
  import { forwardRef, type ReactNode } from 'react';
2
- import { Pressable, Text, type TextProps, View, type ViewProps } from 'react-native';
3
- import { createRadio, dataAttributes, type IRadioProps } from '@cdx-ui/primitives';
2
+ import { Text, type TextProps, View, type ViewProps } from 'react-native';
3
+ import {
4
+ RadioRoot as RadioRootPrimitive,
5
+ RadioGroup as RadioGroupPrimitive,
6
+ Slot,
7
+ dataAttributes,
8
+ mergeDataAttributes,
9
+ useRadioContext,
10
+ type IRadioProps,
11
+ } from '@cdx-ui/primitives';
4
12
  import { cn } from '@cdx-ui/utils';
5
13
  import {
6
14
  radioGroupVariants,
@@ -10,44 +18,6 @@ import {
10
18
  radioRootVariants,
11
19
  } from './styles';
12
20
 
13
- // =============================================================================
14
- // STYLED INDICATOR BASE
15
- // =============================================================================
16
-
17
- const RadioIndicatorBase = forwardRef<View, ViewProps & { className?: string }>(
18
- ({ className, children, style, ...props }, ref) => {
19
- // On web, data attributes arrive as `dataSet: { checked: true }`.
20
- // On native, they arrive as individual `data-checked: "true"` props.
21
- const ds = (props as any).dataSet;
22
- const isChecked = ds ? ds.checked === true : (props as any)['data-checked'] === 'true';
23
- const isInvalid = ds ? ds.invalid === true : (props as any)['data-invalid'] === 'true';
24
- const isHovered = ds ? ds.hover === true : (props as any)['data-hover'] === 'true';
25
-
26
- return (
27
- <View ref={ref} className={cn(radioIndicatorVariants(), className)} style={style} {...props}>
28
- <View
29
- className={cn(radioInnerDotVariants())}
30
- {...dataAttributes({ checked: isChecked, invalid: isInvalid, hover: isHovered })}
31
- />
32
- {children}
33
- </View>
34
- );
35
- },
36
- );
37
-
38
- RadioIndicatorBase.displayName = 'Radio.IndicatorBase';
39
-
40
- // =============================================================================
41
- // PRIMITIVE ASSEMBLY
42
- // =============================================================================
43
-
44
- const RadioPrimitive = createRadio({
45
- Root: Pressable,
46
- Indicator: RadioIndicatorBase,
47
- Label: Text,
48
- Group: View,
49
- });
50
-
51
21
  // =============================================================================
52
22
  // RADIO ROOT
53
23
  // =============================================================================
@@ -61,29 +31,69 @@ const RadioRoot = forwardRef<View, RadioProps>(({ className, children, style, ..
61
31
  const computedClassName = cn(radioRootVariants(), className);
62
32
 
63
33
  return (
64
- <RadioPrimitive ref={ref as any} className={computedClassName} style={style} {...props}>
34
+ <RadioRootPrimitive
35
+ ref={ref}
36
+ className={computedClassName}
37
+ style={style}
38
+ {...props}
39
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'radio' }))}
40
+ >
41
+ <RadioIndicator />
65
42
  {children}
66
- </RadioPrimitive>
43
+ </RadioRootPrimitive>
67
44
  );
68
45
  });
69
46
 
70
47
  RadioRoot.displayName = 'Radio';
71
48
 
72
49
  // =============================================================================
73
- // RADIO INDICATOR
50
+ // RADIO INDICATOR (styled-only, reads context for data-* styling)
74
51
  // =============================================================================
75
52
 
76
53
  export interface RadioIndicatorProps extends ViewProps {
54
+ asChild?: boolean;
77
55
  className?: string;
78
56
  children?: ReactNode;
79
57
  }
80
58
 
81
59
  const RadioIndicator = forwardRef<View, RadioIndicatorProps>(
82
- ({ className, children, style, ...props }, ref) => {
60
+ ({ asChild, className, children, style, ...props }, ref) => {
61
+ const { isChecked, isDisabled, isHovered, isInvalid, isReadOnly, isPressed, isFocusVisible } =
62
+ useRadioContext();
63
+
64
+ const computedClassName = cn(radioIndicatorVariants(), className);
65
+ const Comp = asChild ? Slot : View;
66
+
83
67
  return (
84
- <RadioPrimitive.Indicator ref={ref as any} className={className} style={style} {...props}>
68
+ <Comp
69
+ ref={ref}
70
+ className={computedClassName}
71
+ style={style}
72
+ {...props}
73
+ {...mergeDataAttributes(
74
+ props,
75
+ dataAttributes({
76
+ slot: 'radio-indicator',
77
+ hover: isHovered,
78
+ checked: isChecked,
79
+ disabled: isDisabled,
80
+ focusVisible: isFocusVisible,
81
+ invalid: isInvalid,
82
+ readonly: isReadOnly,
83
+ active: isPressed,
84
+ }),
85
+ )}
86
+ >
87
+ <View
88
+ className={cn(radioInnerDotVariants())}
89
+ {...dataAttributes({
90
+ checked: isChecked,
91
+ invalid: isInvalid,
92
+ hover: isHovered,
93
+ })}
94
+ />
85
95
  {children}
86
- </RadioPrimitive.Indicator>
96
+ </Comp>
87
97
  );
88
98
  },
89
99
  );
@@ -91,22 +101,30 @@ const RadioIndicator = forwardRef<View, RadioIndicatorProps>(
91
101
  RadioIndicator.displayName = 'Radio.Indicator';
92
102
 
93
103
  // =============================================================================
94
- // RADIO LABEL
104
+ // RADIO LABEL (styled-only)
95
105
  // =============================================================================
96
106
 
97
107
  export interface RadioLabelProps extends TextProps {
108
+ asChild?: boolean;
98
109
  className?: string;
99
110
  children?: ReactNode;
100
111
  }
101
112
 
102
113
  const RadioLabel = forwardRef<Text, RadioLabelProps>(
103
- ({ className, children, style, ...props }, ref) => {
114
+ ({ asChild, className, children, style, ...props }, ref) => {
104
115
  const computedClassName = cn(radioLabelVariants(), className);
116
+ const Comp = asChild ? Slot : Text;
105
117
 
106
118
  return (
107
- <RadioPrimitive.Label ref={ref as any} className={computedClassName} style={style} {...props}>
119
+ <Comp
120
+ ref={ref}
121
+ className={computedClassName}
122
+ style={style}
123
+ {...props}
124
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'radio-label' }))}
125
+ >
108
126
  {children}
109
- </RadioPrimitive.Label>
127
+ </Comp>
110
128
  );
111
129
  },
112
130
  );
@@ -114,7 +132,7 @@ const RadioLabel = forwardRef<Text, RadioLabelProps>(
114
132
  RadioLabel.displayName = 'Radio.Label';
115
133
 
116
134
  // =============================================================================
117
- // RADIO GROUP
135
+ // RADIO GROUP (wraps the primitive)
118
136
  // =============================================================================
119
137
 
120
138
  export interface RadioGroupProps extends ViewProps {
@@ -129,6 +147,8 @@ export interface RadioGroupProps extends ViewProps {
129
147
  isReadOnly?: boolean;
130
148
  direction?: 'column' | 'row';
131
149
  name?: string;
150
+ 'aria-label'?: string;
151
+ 'aria-labelledby'?: string;
132
152
  }
133
153
 
134
154
  const RadioGroup = forwardRef<View, RadioGroupProps>(
@@ -153,8 +173,8 @@ const RadioGroup = forwardRef<View, RadioGroupProps>(
153
173
  const computedClassName = cn(radioGroupVariants({ direction }), className);
154
174
 
155
175
  return (
156
- <RadioPrimitive.Group
157
- ref={ref as any}
176
+ <RadioGroupPrimitive
177
+ ref={ref}
158
178
  className={computedClassName}
159
179
  style={style}
160
180
  value={value}
@@ -165,10 +185,12 @@ const RadioGroup = forwardRef<View, RadioGroupProps>(
165
185
  isRequired={isRequired}
166
186
  isReadOnly={isReadOnly}
167
187
  name={name}
188
+ direction={direction}
168
189
  {...viewProps}
190
+ {...mergeDataAttributes(viewProps, dataAttributes({ slot: 'radio-group' }))}
169
191
  >
170
192
  {children}
171
- </RadioPrimitive.Group>
193
+ </RadioGroupPrimitive>
172
194
  );
173
195
  },
174
196
  );
@@ -1,8 +1,16 @@
1
- import { forwardRef, type ReactNode } from 'react';
2
- import { Pressable, Text, type TextProps, View, type ViewProps } from 'react-native';
3
- import { KeyboardArrowDown } from '@cdx-ui/icons';
1
+ import { forwardRef, useMemo, type ReactNode } from 'react';
2
+ import { Text, type TextProps, View, type ViewProps } from 'react-native';
3
+ import { KeyboardArrowDown, KeyboardArrowUp } from '@cdx-ui/icons';
4
4
  import {
5
- createSelect,
5
+ SelectContent as SelectContentPrimitive,
6
+ SelectItem as SelectItemPrimitive,
7
+ SelectRoot as SelectRootPrimitive,
8
+ SelectTrigger as SelectTriggerPrimitive,
9
+ Slot,
10
+ dataAttributes,
11
+ mergeDataAttributes,
12
+ useSelectContext,
13
+ useSelectItemContext,
6
14
  type ISelectContentProps,
7
15
  type ISelectItemLabelProps,
8
16
  type ISelectItemProps,
@@ -10,9 +18,9 @@ import {
10
18
  type ISelectTriggerProps,
11
19
  type ISelectValueProps,
12
20
  } from '@cdx-ui/primitives';
13
- import { cn, useStyleContext, withStyleContext } from '@cdx-ui/utils';
21
+ import { cn, ParentContext, useParentContext, useStyleContext } from '@cdx-ui/utils';
14
22
  import { Icon, IconProps } from '../Icon';
15
- import { SelectTriggerHost } from './SelectTriggerHost';
23
+ import { positionToDataIcon, type IconSlotPosition } from '../../utils/positionToDataIcon';
16
24
  import {
17
25
  type SelectVariantProps,
18
26
  selectContentVariants,
@@ -25,22 +33,10 @@ import {
25
33
 
26
34
  const SCOPE = 'SELECT';
27
35
 
28
- const Root = withStyleContext(View, SCOPE);
29
-
30
36
  const useSelectStyleContext = () => useStyleContext(SCOPE) as SelectVariantProps;
31
37
 
32
- const SelectPrimitive = createSelect({
33
- Root,
34
- Trigger: SelectTriggerHost,
35
- Value: Text,
36
- Icon: View,
37
- Content: View,
38
- Item: Pressable,
39
- ItemLabel: Text,
40
- });
41
-
42
38
  // =============================================================================
43
- // STYLED ROOT COMPONENT
39
+ // STYLED ROOT
44
40
  // =============================================================================
45
41
 
46
42
  export interface SelectProps extends ViewProps, ISelectProps, SelectVariantProps {
@@ -49,16 +45,21 @@ export interface SelectProps extends ViewProps, ISelectProps, SelectVariantProps
49
45
 
50
46
  const SelectRoot = forwardRef<View, SelectProps>(
51
47
  ({ variant = 'outline', size = 'default', className, children, style, ...props }, ref) => {
48
+ const parent = useParentContext();
49
+ const ctx = useMemo(() => ({ ...parent, [SCOPE]: { variant, size } }), [parent, variant, size]);
50
+
52
51
  return (
53
- <SelectPrimitive
54
- ref={ref}
55
- className={className}
56
- style={style}
57
- context={{ variant, size }}
58
- {...props}
59
- >
60
- {children}
61
- </SelectPrimitive>
52
+ <ParentContext.Provider value={ctx}>
53
+ <SelectRootPrimitive
54
+ ref={ref}
55
+ className={className}
56
+ style={style}
57
+ {...props}
58
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'select' }))}
59
+ >
60
+ {children}
61
+ </SelectRootPrimitive>
62
+ </ParentContext.Provider>
62
63
  );
63
64
  },
64
65
  );
@@ -66,25 +67,57 @@ const SelectRoot = forwardRef<View, SelectProps>(
66
67
  SelectRoot.displayName = 'Select';
67
68
 
68
69
  // =============================================================================
69
- // STYLED TRIGGER COMPONENT
70
+ // STYLED TRIGGER
70
71
  // =============================================================================
71
72
 
72
73
  export interface SelectTriggerProps extends ISelectTriggerProps {
74
+ /**
75
+ * Substitute the rendered trigger element with the single child. The child must
76
+ * be another Forge primitive (IconButton, Chip, Button) — anything whose
77
+ * underlying Pressable accepts Pressable-shaped handlers. When set, the
78
+ * auto-injected arrow indicator is skipped and `selectTriggerVariants` is not
79
+ * applied so the substituted component's own appearance wins.
80
+ *
81
+ * On web, `<label htmlFor>` association is preserved via a `focus()` bridge
82
+ * registered by the primitive.
83
+ *
84
+ * NOTE — `data-slot` overlap: the substituted component's own `data-slot`
85
+ * (e.g. `button`, `icon-button`, `chip`) may win on the merged element.
86
+ * Selectors that need guaranteed `select-trigger` access should wrap the
87
+ * trigger instead of relying on `data-slot`.
88
+ */
89
+ asChild?: boolean;
73
90
  className?: string;
74
91
  children?: ReactNode;
75
92
  }
76
93
 
77
94
  const SelectTrigger = forwardRef<View, SelectTriggerProps>(
78
- ({ className, children, style, ...props }, ref) => {
95
+ ({ asChild, className, children, style, ...props }, ref) => {
96
+ const { open } = useSelectContext();
79
97
  const { variant, size } = useSelectStyleContext();
80
98
 
81
- const computedClassName = cn(selectTriggerVariants({ variant, size }), className);
99
+ const computedClassName = asChild
100
+ ? className
101
+ : cn(selectTriggerVariants({ variant, size }), className);
82
102
 
83
103
  return (
84
- <SelectPrimitive.Trigger ref={ref} className={computedClassName} style={style} {...props}>
85
- {children}
86
- <SelectIcon as={KeyboardArrowDown} />
87
- </SelectPrimitive.Trigger>
104
+ <SelectTriggerPrimitive
105
+ ref={ref}
106
+ asChild={asChild}
107
+ className={computedClassName}
108
+ style={style}
109
+ {...props}
110
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'select-trigger' }))}
111
+ >
112
+ {asChild ? (
113
+ children
114
+ ) : (
115
+ <>
116
+ {children}
117
+ <SelectIcon as={open ? KeyboardArrowUp : KeyboardArrowDown} />
118
+ </>
119
+ )}
120
+ </SelectTriggerPrimitive>
88
121
  );
89
122
  },
90
123
  );
@@ -92,58 +125,115 @@ const SelectTrigger = forwardRef<View, SelectTriggerProps>(
92
125
  SelectTrigger.displayName = 'Select.Trigger';
93
126
 
94
127
  // =============================================================================
95
- // STYLED VALUE COMPONENT
128
+ // STYLED VALUE (styled-only)
96
129
  // =============================================================================
97
130
 
98
131
  export interface SelectValueProps extends TextProps, ISelectValueProps {
132
+ asChild?: boolean;
99
133
  className?: string;
100
134
  }
101
135
 
102
- const SelectValue = forwardRef<Text, SelectValueProps>(({ className, style, ...props }, ref) => {
103
- const { size } = useSelectStyleContext();
136
+ const SelectValue = forwardRef<Text, SelectValueProps>(
137
+ ({ asChild, className, style, placeholder, children, ...props }, ref) => {
138
+ const { size } = useSelectStyleContext();
139
+ const { value, items, invalid } = useSelectContext();
104
140
 
105
- const computedClassName = cn(selectValueVariants({ size }), className);
141
+ const hasValue = value !== undefined && value !== '';
142
+ const showPlaceholder = !hasValue;
143
+ const displayText = hasValue ? items.find((item) => item.value === value)?.label : placeholder;
106
144
 
107
- return <SelectPrimitive.Value ref={ref} className={computedClassName} style={style} {...props} />;
108
- });
145
+ const computedClassName = cn(selectValueVariants({ size }), className);
146
+ const Comp = asChild ? Slot : Text;
147
+
148
+ return (
149
+ <Comp
150
+ ref={ref}
151
+ className={computedClassName}
152
+ style={style}
153
+ {...props}
154
+ {...mergeDataAttributes(
155
+ props,
156
+ dataAttributes({
157
+ slot: 'select-value',
158
+ placeholder: showPlaceholder,
159
+ invalid,
160
+ }),
161
+ )}
162
+ >
163
+ {children ?? displayText}
164
+ </Comp>
165
+ );
166
+ },
167
+ );
109
168
 
110
169
  SelectValue.displayName = 'Select.Value';
111
170
 
112
171
  // =============================================================================
113
- // STYLED ICON COMPONENT
172
+ // STYLED ICON (styled-only, reads context for state=open / invalid)
114
173
  // =============================================================================
115
174
 
116
- export interface SelectIconProps extends Omit<IconProps, 'children'> {}
175
+ export interface SelectIconProps extends Omit<IconProps, 'children' | 'as'> {
176
+ asChild?: boolean;
177
+ /**
178
+ * Forge icon component to render. Required unless `asChild` is set, in which
179
+ * case the consumer's substituted element is rendered instead.
180
+ */
181
+ as?: IconProps['as'];
182
+ /**
183
+ * Position hint that emits `data-icon="inline-start"` or `data-icon="inline-end"`.
184
+ * CVA uses this attribute to apply leading/trailing padding logically (RTL-aware).
185
+ * Omit to apply the symmetric baseline gap.
186
+ */
187
+ position?: IconSlotPosition;
188
+ }
117
189
 
118
- const SelectIcon = ({ className, style, as: IconComponent, ...props }: SelectIconProps) => {
190
+ const SelectIcon = ({ asChild, className, style, as, position, ...props }: SelectIconProps) => {
119
191
  const { size } = useSelectStyleContext();
192
+ const { invalid } = useSelectContext();
120
193
 
121
194
  const computedClassName = cn(selectIconVariants({ size }), className);
122
- const iconSizeClass = size === 'small' ? 'size-4' : 'size-5';
123
195
 
124
- return (
125
- <SelectPrimitive.Icon className={computedClassName} style={style}>
126
- <Icon as={IconComponent} className={iconSizeClass} {...props} />
127
- </SelectPrimitive.Icon>
196
+ const slotData = mergeDataAttributes(
197
+ props,
198
+ dataAttributes({ slot: 'select-icon', invalid }),
199
+ positionToDataIcon(position),
128
200
  );
201
+
202
+ if (asChild) {
203
+ return <Slot className={computedClassName} style={style} {...props} {...slotData} />;
204
+ }
205
+
206
+ if (!as) {
207
+ return null;
208
+ }
209
+
210
+ return <Icon as={as} className={computedClassName} style={style} {...props} {...slotData} />;
129
211
  };
130
212
 
131
213
  SelectIcon.displayName = 'Select.Icon';
132
214
 
133
215
  // =============================================================================
134
- // STYLED CONTENT COMPONENT
216
+ // STYLED CONTENT
135
217
  // =============================================================================
136
218
 
137
219
  export interface SelectContentProps extends ViewProps, ISelectContentProps {
220
+ asChild?: boolean;
138
221
  className?: string;
139
222
  }
140
223
 
141
224
  const SelectContent = forwardRef<View, SelectContentProps>(
142
- ({ className, style, ...props }, ref) => {
225
+ ({ asChild, className, style, ...props }, ref) => {
143
226
  const computedClassName = cn(selectContentVariants(), className);
144
227
 
145
228
  return (
146
- <SelectPrimitive.Content ref={ref} className={computedClassName} style={style} {...props} />
229
+ <SelectContentPrimitive
230
+ ref={ref}
231
+ asChild={asChild}
232
+ className={computedClassName}
233
+ style={style}
234
+ {...props}
235
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'select-content' }))}
236
+ />
147
237
  );
148
238
  },
149
239
  );
@@ -151,24 +241,32 @@ const SelectContent = forwardRef<View, SelectContentProps>(
151
241
  SelectContent.displayName = 'Select.Content';
152
242
 
153
243
  // =============================================================================
154
- // STYLED ITEM COMPONENT
244
+ // STYLED ITEM
155
245
  // =============================================================================
156
246
 
157
247
  export interface SelectItemProps extends ISelectItemProps {
248
+ asChild?: boolean;
158
249
  className?: string;
159
250
  children?: ReactNode;
160
251
  }
161
252
 
162
253
  const SelectItem = forwardRef<View, SelectItemProps>(
163
- ({ className, children, style, ...props }, ref) => {
254
+ ({ asChild, className, children, style, ...props }, ref) => {
164
255
  const { size } = useSelectStyleContext();
165
256
 
166
257
  const computedClassName = cn(selectItemVariants({ size }), className);
167
258
 
168
259
  return (
169
- <SelectPrimitive.Item ref={ref} className={computedClassName} style={style} {...props}>
260
+ <SelectItemPrimitive
261
+ ref={ref}
262
+ asChild={asChild}
263
+ className={computedClassName}
264
+ style={style}
265
+ {...props}
266
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'select-item' }))}
267
+ >
170
268
  {children}
171
- </SelectPrimitive.Item>
269
+ </SelectItemPrimitive>
172
270
  );
173
271
  },
174
272
  );
@@ -176,21 +274,38 @@ const SelectItem = forwardRef<View, SelectItemProps>(
176
274
  SelectItem.displayName = 'Select.Item';
177
275
 
178
276
  // =============================================================================
179
- // STYLED ITEM LABEL COMPONENT
277
+ // STYLED ITEM LABEL (styled-only, reads item context for checked state)
180
278
  // =============================================================================
181
279
 
182
280
  export interface SelectItemLabelProps extends TextProps, ISelectItemLabelProps {
281
+ asChild?: boolean;
183
282
  className?: string;
184
283
  }
185
284
 
186
285
  const SelectItemLabel = forwardRef<Text, SelectItemLabelProps>(
187
- ({ className, style, ...props }, ref) => {
286
+ ({ asChild, className, style, children, ...props }, ref) => {
188
287
  const { size } = useSelectStyleContext();
288
+ const { checked } = useSelectItemContext();
189
289
 
190
290
  const computedClassName = cn(selectItemLabelVariants({ size }), className);
291
+ const Comp = asChild ? Slot : Text;
191
292
 
192
293
  return (
193
- <SelectPrimitive.ItemLabel ref={ref} className={computedClassName} style={style} {...props} />
294
+ <Comp
295
+ ref={ref}
296
+ className={computedClassName}
297
+ style={style}
298
+ {...props}
299
+ {...mergeDataAttributes(
300
+ props,
301
+ dataAttributes({
302
+ slot: 'select-item-label',
303
+ state: checked ? 'checked' : 'unchecked',
304
+ }),
305
+ )}
306
+ >
307
+ {children}
308
+ </Comp>
194
309
  );
195
310
  },
196
311
  );
@@ -16,7 +16,7 @@ export const selectTriggerVariants = cva(
16
16
  DISABLED_CURSOR,
17
17
  'data-[readonly=true]:cursor-default',
18
18
  'data-[readonly=true]:bg-surface-secondary',
19
- 'web:data-[hover=true]:data-[focus=false]:data-[invalid=false]:border-stroke-hover',
19
+ 'web:hover:data-[focus=false]:data-[invalid=false]:border-stroke-hover',
20
20
  'data-[focus=true]:border-[length:var(--border-width-focused)]',
21
21
  'data-[focus=true]:data-[invalid=false]:border-stroke-action',
22
22
  'data-[focus=true]:data-[invalid=true]:border-stroke-danger',
@@ -83,7 +83,6 @@ export const selectIconVariants = cva(
83
83
  'shrink-0 items-center justify-center self-center',
84
84
  'text-content-tertiary',
85
85
  'data-[invalid=true]:text-content-danger',
86
- 'data-[state=open]:rotate-180',
87
86
  ],
88
87
  {
89
88
  variants: {
@@ -1,6 +1,7 @@
1
1
  import { forwardRef } from 'react';
2
- import { Switch as RNSwitch, type SwitchProps } from 'react-native';
3
- import { cn } from '@cdx-ui/utils';
2
+ import { Switch as RNSwitch, type SwitchProps as RNSwitchProps } from 'react-native';
3
+ import { mergeDataAttributes, type IUseSwitchReturn } from '@cdx-ui/primitives';
4
+ import { cn, composeEventHandlers } from '@cdx-ui/utils';
4
5
  import {
5
6
  switchIosBgVariants,
6
7
  switchThumbColorVariants,
@@ -8,29 +9,50 @@ import {
8
9
  switchTrackOnVariants,
9
10
  } from './styles';
10
11
 
11
- export interface BaseSwitchProps extends Omit<SwitchProps, 'className'> {
12
- states?: Record<string, boolean | undefined>;
13
- dataSet?: Record<string, string>;
12
+ export interface BaseSwitchProps extends Omit<
13
+ RNSwitchProps,
14
+ | 'value'
15
+ | 'disabled'
16
+ | 'onValueChange'
17
+ | 'thumbColor'
18
+ | 'trackColor'
19
+ | 'ios_backgroundColor'
20
+ | 'className'
21
+ > {
22
+ switchState: IUseSwitchReturn;
14
23
  className?: string;
15
- size?: string;
24
+ size?: string | null;
16
25
  }
17
26
 
27
+ /**
28
+ * Native host for `Switch`. Renders `RNSwitch` with Uniwind className passthrough
29
+ * derived from the resolved interaction state in `switchState`.
30
+ *
31
+ * All state/a11y/interaction machinery lives in the `useSwitch` hook; this file
32
+ * is pure rendering (Standard 3: primitives must not carry styling machinery).
33
+ */
18
34
  export const BaseSwitch = forwardRef<RNSwitch, BaseSwitchProps>(
19
- ({ states, className, disabled, value, ...props }, ref) => {
20
- const isChecked = value ?? states?.checked ?? false;
21
- const isDisabled = disabled ?? states?.disabled ?? false;
22
- const isInvalid = states?.invalid ?? false;
23
-
35
+ ({ switchState, className, size: _size, onFocus, onBlur, ...props }, ref) => {
24
36
  const uniwindProps: Record<string, string | undefined> = {
25
37
  className,
26
- thumbColorClassName: cn(switchThumbColorVariants({ isDisabled })),
27
- trackColorOnClassName: cn(switchTrackOnVariants({ isInvalid })),
28
- trackColorOffClassName: cn(switchTrackOffVariants({ isInvalid })),
29
- ios_backgroundColorClassName: cn(switchIosBgVariants({ isInvalid })),
38
+ thumbColorClassName: cn(switchThumbColorVariants({ isDisabled: switchState.isDisabled })),
39
+ trackColorOnClassName: cn(switchTrackOnVariants({ isInvalid: switchState.isInvalid })),
40
+ trackColorOffClassName: cn(switchTrackOffVariants({ isInvalid: switchState.isInvalid })),
41
+ ios_backgroundColorClassName: cn(switchIosBgVariants({ isInvalid: switchState.isInvalid })),
30
42
  };
31
43
 
32
44
  return (
33
- <RNSwitch ref={ref} value={isChecked} disabled={isDisabled} {...uniwindProps} {...props} />
45
+ <RNSwitch
46
+ ref={ref}
47
+ value={switchState.checked}
48
+ disabled={switchState.isDisabled}
49
+ onValueChange={switchState.setChecked}
50
+ onFocus={composeEventHandlers(onFocus, switchState.focusProps.onFocus)}
51
+ onBlur={composeEventHandlers(onBlur, switchState.focusProps.onBlur)}
52
+ {...uniwindProps}
53
+ {...props}
54
+ {...mergeDataAttributes(props, switchState.dataAttrs)}
55
+ />
34
56
  );
35
57
  },
36
58
  );