@cdx-ui/primitives 0.0.1-alpha.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 (260) hide show
  1. package/README.md +17 -0
  2. package/lib/commonjs/button/context.js +11 -0
  3. package/lib/commonjs/button/context.js.map +1 -0
  4. package/lib/commonjs/button/createButtonGroup.js +80 -0
  5. package/lib/commonjs/button/createButtonGroup.js.map +1 -0
  6. package/lib/commonjs/button/createButtonIcon.js +18 -0
  7. package/lib/commonjs/button/createButtonIcon.js.map +1 -0
  8. package/lib/commonjs/button/createButtonRoot.js +81 -0
  9. package/lib/commonjs/button/createButtonRoot.js.map +1 -0
  10. package/lib/commonjs/button/createButtonSpinner.js +34 -0
  11. package/lib/commonjs/button/createButtonSpinner.js.map +1 -0
  12. package/lib/commonjs/button/createButtonText.js +38 -0
  13. package/lib/commonjs/button/createButtonText.js.map +1 -0
  14. package/lib/commonjs/button/index.js +31 -0
  15. package/lib/commonjs/button/index.js.map +1 -0
  16. package/lib/commonjs/button/types.js +6 -0
  17. package/lib/commonjs/button/types.js.map +1 -0
  18. package/lib/commonjs/index.js +52 -0
  19. package/lib/commonjs/index.js.map +1 -0
  20. package/lib/commonjs/input/context.js +11 -0
  21. package/lib/commonjs/input/context.js.map +1 -0
  22. package/lib/commonjs/input/createInputField.js +90 -0
  23. package/lib/commonjs/input/createInputField.js.map +1 -0
  24. package/lib/commonjs/input/createInputIcon.js +20 -0
  25. package/lib/commonjs/input/createInputIcon.js.map +1 -0
  26. package/lib/commonjs/input/createInputRoot.js +82 -0
  27. package/lib/commonjs/input/createInputRoot.js.map +1 -0
  28. package/lib/commonjs/input/createInputSlot.js +44 -0
  29. package/lib/commonjs/input/createInputSlot.js.map +1 -0
  30. package/lib/commonjs/input/index.js +28 -0
  31. package/lib/commonjs/input/index.js.map +1 -0
  32. package/lib/commonjs/input/types.js +6 -0
  33. package/lib/commonjs/input/types.js.map +1 -0
  34. package/lib/commonjs/overlay/OverlayContainer.js +67 -0
  35. package/lib/commonjs/overlay/OverlayContainer.js.map +1 -0
  36. package/lib/commonjs/overlay/index.js +40 -0
  37. package/lib/commonjs/overlay/index.js.map +1 -0
  38. package/lib/commonjs/overlay/useAnchorPosition.js +68 -0
  39. package/lib/commonjs/overlay/useAnchorPosition.js.map +1 -0
  40. package/lib/commonjs/overlay/useDismissOverlay.js +14 -0
  41. package/lib/commonjs/overlay/useDismissOverlay.js.map +1 -0
  42. package/lib/commonjs/overlay/useDismissOverlay.web.js +46 -0
  43. package/lib/commonjs/overlay/useDismissOverlay.web.js.map +1 -0
  44. package/lib/commonjs/overlay/useOverlayPosition.js +93 -0
  45. package/lib/commonjs/overlay/useOverlayPosition.js.map +1 -0
  46. package/lib/commonjs/package.json +1 -0
  47. package/lib/commonjs/select/context.js +63 -0
  48. package/lib/commonjs/select/context.js.map +1 -0
  49. package/lib/commonjs/select/createSelectContent.js +102 -0
  50. package/lib/commonjs/select/createSelectContent.js.map +1 -0
  51. package/lib/commonjs/select/createSelectIcon.js +41 -0
  52. package/lib/commonjs/select/createSelectIcon.js.map +1 -0
  53. package/lib/commonjs/select/createSelectItem.js +101 -0
  54. package/lib/commonjs/select/createSelectItem.js.map +1 -0
  55. package/lib/commonjs/select/createSelectItemLabel.js +41 -0
  56. package/lib/commonjs/select/createSelectItemLabel.js.map +1 -0
  57. package/lib/commonjs/select/createSelectRoot.js +99 -0
  58. package/lib/commonjs/select/createSelectRoot.js.map +1 -0
  59. package/lib/commonjs/select/createSelectTrigger.js +138 -0
  60. package/lib/commonjs/select/createSelectTrigger.js.map +1 -0
  61. package/lib/commonjs/select/createSelectValue.js +53 -0
  62. package/lib/commonjs/select/createSelectValue.js.map +1 -0
  63. package/lib/commonjs/select/index.js +38 -0
  64. package/lib/commonjs/select/index.js.map +1 -0
  65. package/lib/commonjs/select/types.js +6 -0
  66. package/lib/commonjs/select/types.js.map +1 -0
  67. package/lib/commonjs/select/useContentFocus.js +47 -0
  68. package/lib/commonjs/select/useContentFocus.js.map +1 -0
  69. package/lib/commonjs/select/useListboxNavigation.js +83 -0
  70. package/lib/commonjs/select/useListboxNavigation.js.map +1 -0
  71. package/lib/commonjs/uniwind.d.js +2 -0
  72. package/lib/commonjs/uniwind.d.js.map +1 -0
  73. package/lib/commonjs/utils/dataAttributes.js +32 -0
  74. package/lib/commonjs/utils/dataAttributes.js.map +1 -0
  75. package/lib/commonjs/utils/dataAttributes.web.js +31 -0
  76. package/lib/commonjs/utils/dataAttributes.web.js.map +1 -0
  77. package/lib/module/button/context.js +5 -0
  78. package/lib/module/button/context.js.map +1 -0
  79. package/lib/module/button/createButtonGroup.js +74 -0
  80. package/lib/module/button/createButtonGroup.js.map +1 -0
  81. package/lib/module/button/createButtonIcon.js +13 -0
  82. package/lib/module/button/createButtonIcon.js.map +1 -0
  83. package/lib/module/button/createButtonRoot.js +76 -0
  84. package/lib/module/button/createButtonRoot.js.map +1 -0
  85. package/lib/module/button/createButtonSpinner.js +29 -0
  86. package/lib/module/button/createButtonSpinner.js.map +1 -0
  87. package/lib/module/button/createButtonText.js +33 -0
  88. package/lib/module/button/createButtonText.js.map +1 -0
  89. package/lib/module/button/index.js +27 -0
  90. package/lib/module/button/index.js.map +1 -0
  91. package/lib/module/button/types.js +4 -0
  92. package/lib/module/button/types.js.map +1 -0
  93. package/lib/module/index.js +7 -0
  94. package/lib/module/index.js.map +1 -0
  95. package/lib/module/input/context.js +5 -0
  96. package/lib/module/input/context.js.map +1 -0
  97. package/lib/module/input/createInputField.js +85 -0
  98. package/lib/module/input/createInputField.js.map +1 -0
  99. package/lib/module/input/createInputIcon.js +15 -0
  100. package/lib/module/input/createInputIcon.js.map +1 -0
  101. package/lib/module/input/createInputRoot.js +76 -0
  102. package/lib/module/input/createInputRoot.js.map +1 -0
  103. package/lib/module/input/createInputSlot.js +39 -0
  104. package/lib/module/input/createInputSlot.js.map +1 -0
  105. package/lib/module/input/index.js +23 -0
  106. package/lib/module/input/index.js.map +1 -0
  107. package/lib/module/input/types.js +4 -0
  108. package/lib/module/input/types.js.map +1 -0
  109. package/lib/module/overlay/OverlayContainer.js +62 -0
  110. package/lib/module/overlay/OverlayContainer.js.map +1 -0
  111. package/lib/module/overlay/index.js +7 -0
  112. package/lib/module/overlay/index.js.map +1 -0
  113. package/lib/module/overlay/useAnchorPosition.js +64 -0
  114. package/lib/module/overlay/useAnchorPosition.js.map +1 -0
  115. package/lib/module/overlay/useDismissOverlay.js +10 -0
  116. package/lib/module/overlay/useDismissOverlay.js.map +1 -0
  117. package/lib/module/overlay/useDismissOverlay.web.js +43 -0
  118. package/lib/module/overlay/useDismissOverlay.web.js.map +1 -0
  119. package/lib/module/overlay/useOverlayPosition.js +88 -0
  120. package/lib/module/overlay/useOverlayPosition.js.map +1 -0
  121. package/lib/module/select/context.js +56 -0
  122. package/lib/module/select/context.js.map +1 -0
  123. package/lib/module/select/createSelectContent.js +97 -0
  124. package/lib/module/select/createSelectContent.js.map +1 -0
  125. package/lib/module/select/createSelectIcon.js +36 -0
  126. package/lib/module/select/createSelectIcon.js.map +1 -0
  127. package/lib/module/select/createSelectItem.js +96 -0
  128. package/lib/module/select/createSelectItem.js.map +1 -0
  129. package/lib/module/select/createSelectItemLabel.js +36 -0
  130. package/lib/module/select/createSelectItemLabel.js.map +1 -0
  131. package/lib/module/select/createSelectRoot.js +94 -0
  132. package/lib/module/select/createSelectRoot.js.map +1 -0
  133. package/lib/module/select/createSelectTrigger.js +133 -0
  134. package/lib/module/select/createSelectTrigger.js.map +1 -0
  135. package/lib/module/select/createSelectValue.js +48 -0
  136. package/lib/module/select/createSelectValue.js.map +1 -0
  137. package/lib/module/select/index.js +34 -0
  138. package/lib/module/select/index.js.map +1 -0
  139. package/lib/module/select/types.js +4 -0
  140. package/lib/module/select/types.js.map +1 -0
  141. package/lib/module/select/useContentFocus.js +44 -0
  142. package/lib/module/select/useContentFocus.js.map +1 -0
  143. package/lib/module/select/useListboxNavigation.js +80 -0
  144. package/lib/module/select/useListboxNavigation.js.map +1 -0
  145. package/lib/module/uniwind.d.js +2 -0
  146. package/lib/module/uniwind.d.js.map +1 -0
  147. package/lib/module/utils/dataAttributes.js +28 -0
  148. package/lib/module/utils/dataAttributes.js.map +1 -0
  149. package/lib/module/utils/dataAttributes.web.js +27 -0
  150. package/lib/module/utils/dataAttributes.web.js.map +1 -0
  151. package/lib/typescript/button/context.d.ts +6 -0
  152. package/lib/typescript/button/context.d.ts.map +1 -0
  153. package/lib/typescript/button/createButtonGroup.d.ts +4 -0
  154. package/lib/typescript/button/createButtonGroup.d.ts.map +1 -0
  155. package/lib/typescript/button/createButtonIcon.d.ts +2 -0
  156. package/lib/typescript/button/createButtonIcon.d.ts.map +1 -0
  157. package/lib/typescript/button/createButtonRoot.d.ts +6 -0
  158. package/lib/typescript/button/createButtonRoot.d.ts.map +1 -0
  159. package/lib/typescript/button/createButtonSpinner.d.ts +4 -0
  160. package/lib/typescript/button/createButtonSpinner.d.ts.map +1 -0
  161. package/lib/typescript/button/createButtonText.d.ts +4 -0
  162. package/lib/typescript/button/createButtonText.d.ts.map +1 -0
  163. package/lib/typescript/button/index.d.ts +11 -0
  164. package/lib/typescript/button/index.d.ts.map +1 -0
  165. package/lib/typescript/button/types.d.ts +65 -0
  166. package/lib/typescript/button/types.d.ts.map +1 -0
  167. package/lib/typescript/index.d.ts +5 -0
  168. package/lib/typescript/index.d.ts.map +1 -0
  169. package/lib/typescript/input/context.d.ts +6 -0
  170. package/lib/typescript/input/context.d.ts.map +1 -0
  171. package/lib/typescript/input/createInputField.d.ts +4 -0
  172. package/lib/typescript/input/createInputField.d.ts.map +1 -0
  173. package/lib/typescript/input/createInputIcon.d.ts +4 -0
  174. package/lib/typescript/input/createInputIcon.d.ts.map +1 -0
  175. package/lib/typescript/input/createInputRoot.d.ts +4 -0
  176. package/lib/typescript/input/createInputRoot.d.ts.map +1 -0
  177. package/lib/typescript/input/createInputSlot.d.ts +4 -0
  178. package/lib/typescript/input/createInputSlot.d.ts.map +1 -0
  179. package/lib/typescript/input/index.d.ts +9 -0
  180. package/lib/typescript/input/index.d.ts.map +1 -0
  181. package/lib/typescript/input/types.d.ts +91 -0
  182. package/lib/typescript/input/types.d.ts.map +1 -0
  183. package/lib/typescript/overlay/OverlayContainer.d.ts +23 -0
  184. package/lib/typescript/overlay/OverlayContainer.d.ts.map +1 -0
  185. package/lib/typescript/overlay/index.d.ts +6 -0
  186. package/lib/typescript/overlay/index.d.ts.map +1 -0
  187. package/lib/typescript/overlay/useAnchorPosition.d.ts +15 -0
  188. package/lib/typescript/overlay/useAnchorPosition.d.ts.map +1 -0
  189. package/lib/typescript/overlay/useDismissOverlay.d.ts +7 -0
  190. package/lib/typescript/overlay/useDismissOverlay.d.ts.map +1 -0
  191. package/lib/typescript/overlay/useDismissOverlay.web.d.ts +8 -0
  192. package/lib/typescript/overlay/useDismissOverlay.web.d.ts.map +1 -0
  193. package/lib/typescript/overlay/useOverlayPosition.d.ts +45 -0
  194. package/lib/typescript/overlay/useOverlayPosition.d.ts.map +1 -0
  195. package/lib/typescript/select/context.d.ts +17 -0
  196. package/lib/typescript/select/context.d.ts.map +1 -0
  197. package/lib/typescript/select/createSelectContent.d.ts +4 -0
  198. package/lib/typescript/select/createSelectContent.d.ts.map +1 -0
  199. package/lib/typescript/select/createSelectIcon.d.ts +2 -0
  200. package/lib/typescript/select/createSelectIcon.d.ts.map +1 -0
  201. package/lib/typescript/select/createSelectItem.d.ts +6 -0
  202. package/lib/typescript/select/createSelectItem.d.ts.map +1 -0
  203. package/lib/typescript/select/createSelectItemLabel.d.ts +4 -0
  204. package/lib/typescript/select/createSelectItemLabel.d.ts.map +1 -0
  205. package/lib/typescript/select/createSelectRoot.d.ts +4 -0
  206. package/lib/typescript/select/createSelectRoot.d.ts.map +1 -0
  207. package/lib/typescript/select/createSelectTrigger.d.ts +12 -0
  208. package/lib/typescript/select/createSelectTrigger.d.ts.map +1 -0
  209. package/lib/typescript/select/createSelectValue.d.ts +4 -0
  210. package/lib/typescript/select/createSelectValue.d.ts.map +1 -0
  211. package/lib/typescript/select/index.d.ts +13 -0
  212. package/lib/typescript/select/index.d.ts.map +1 -0
  213. package/lib/typescript/select/types.d.ts +96 -0
  214. package/lib/typescript/select/types.d.ts.map +1 -0
  215. package/lib/typescript/select/useContentFocus.d.ts +19 -0
  216. package/lib/typescript/select/useContentFocus.d.ts.map +1 -0
  217. package/lib/typescript/select/useListboxNavigation.d.ts +13 -0
  218. package/lib/typescript/select/useListboxNavigation.d.ts.map +1 -0
  219. package/lib/typescript/utils/dataAttributes.d.ts +14 -0
  220. package/lib/typescript/utils/dataAttributes.d.ts.map +1 -0
  221. package/lib/typescript/utils/dataAttributes.web.d.ts +16 -0
  222. package/lib/typescript/utils/dataAttributes.web.d.ts.map +1 -0
  223. package/package.json +78 -0
  224. package/src/button/context.tsx +4 -0
  225. package/src/button/createButtonGroup.tsx +88 -0
  226. package/src/button/createButtonIcon.tsx +8 -0
  227. package/src/button/createButtonRoot.tsx +101 -0
  228. package/src/button/createButtonSpinner.tsx +20 -0
  229. package/src/button/createButtonText.tsx +22 -0
  230. package/src/button/index.tsx +53 -0
  231. package/src/button/types.ts +85 -0
  232. package/src/index.ts +4 -0
  233. package/src/input/context.tsx +4 -0
  234. package/src/input/createInputField.tsx +104 -0
  235. package/src/input/createInputIcon.tsx +12 -0
  236. package/src/input/createInputRoot.tsx +92 -0
  237. package/src/input/createInputSlot.tsx +39 -0
  238. package/src/input/index.tsx +51 -0
  239. package/src/input/types.ts +113 -0
  240. package/src/overlay/OverlayContainer.tsx +77 -0
  241. package/src/overlay/index.ts +10 -0
  242. package/src/overlay/useAnchorPosition.ts +72 -0
  243. package/src/overlay/useDismissOverlay.ts +14 -0
  244. package/src/overlay/useDismissOverlay.web.ts +51 -0
  245. package/src/overlay/useOverlayPosition.ts +96 -0
  246. package/src/select/context.tsx +56 -0
  247. package/src/select/createSelectContent.tsx +115 -0
  248. package/src/select/createSelectIcon.tsx +27 -0
  249. package/src/select/createSelectItem.tsx +121 -0
  250. package/src/select/createSelectItemLabel.tsx +30 -0
  251. package/src/select/createSelectRoot.tsx +130 -0
  252. package/src/select/createSelectTrigger.tsx +192 -0
  253. package/src/select/createSelectValue.tsx +38 -0
  254. package/src/select/index.tsx +73 -0
  255. package/src/select/types.ts +131 -0
  256. package/src/select/useContentFocus.ts +54 -0
  257. package/src/select/useListboxNavigation.ts +85 -0
  258. package/src/uniwind.d.ts +3 -0
  259. package/src/utils/dataAttributes.ts +28 -0
  260. package/src/utils/dataAttributes.web.ts +26 -0
@@ -0,0 +1,20 @@
1
+ import type React from 'react';
2
+ import { forwardRef } from 'react';
3
+ import { dataAttributes } from '../utils/dataAttributes';
4
+ import { useButtonContext } from './context';
5
+ import type { IButtonSpinnerProps } from './types';
6
+
7
+ export const createButtonSpinner = <T,>(BaseButtonSpinner: React.ComponentType<T>) =>
8
+ forwardRef<unknown, IButtonSpinnerProps>((props, ref) => {
9
+ const { hover, focus, active, disabled, focusVisible } = useButtonContext();
10
+
11
+ return (
12
+ <BaseButtonSpinner
13
+ {...(props as T)}
14
+ tabIndex={0}
15
+ aria-label="loading"
16
+ ref={ref}
17
+ {...dataAttributes({ hover, focus, active, disabled, focusVisible })}
18
+ />
19
+ );
20
+ });
@@ -0,0 +1,22 @@
1
+ import type React from 'react';
2
+ import { forwardRef } from 'react';
3
+ import { dataAttributes } from '../utils/dataAttributes';
4
+ import { useButtonContext } from './context';
5
+ import type { IButtonTextProps } from './types';
6
+
7
+ // TODO: Rename to ButtonLabel
8
+
9
+ export const createButtonText = <T,>(BaseButtonText: React.ComponentType<T>) =>
10
+ forwardRef<unknown, IButtonTextProps>(({ children, ...props }, ref) => {
11
+ const { hover, focus, active, disabled, focusVisible } = useButtonContext();
12
+
13
+ return (
14
+ <BaseButtonText
15
+ ref={ref}
16
+ {...(props as T)}
17
+ {...dataAttributes({ hover, focus, active, disabled, focusVisible })}
18
+ >
19
+ {children}
20
+ </BaseButtonText>
21
+ );
22
+ });
@@ -0,0 +1,53 @@
1
+ import type React from 'react';
2
+ import { createButtonRoot } from './createButtonRoot';
3
+ import { createButtonGroup } from './createButtonGroup';
4
+ import { createButtonIcon } from './createButtonIcon';
5
+ import { createButtonSpinner } from './createButtonSpinner';
6
+ import { createButtonText } from './createButtonText';
7
+ import type { IButtonComponentType } from './types';
8
+
9
+ export type { IButtonProps } from './types';
10
+
11
+ export function createButton<
12
+ ButtonProps,
13
+ TextProps,
14
+ GroupProps,
15
+ SpinnerProps,
16
+ IconProps,
17
+ ButtonRef = unknown,
18
+ TextRef = unknown,
19
+ >({
20
+ Root,
21
+ Text,
22
+ Group,
23
+ Spinner,
24
+ Icon,
25
+ }: {
26
+ Root: React.ComponentType<ButtonProps>;
27
+ Text: React.ComponentType<TextProps>;
28
+ Group: React.ComponentType<GroupProps>;
29
+ Spinner: React.ComponentType<SpinnerProps>;
30
+ Icon: React.ComponentType<IconProps>;
31
+ }) {
32
+ const Button = createButtonRoot(Root) as any;
33
+ Button.Text = createButtonText(Text);
34
+ Button.Group = createButtonGroup(Group);
35
+ Button.Spinner = createButtonSpinner(Spinner);
36
+ Button.Icon = createButtonIcon(Icon);
37
+
38
+ Button.displayName = 'Button';
39
+ Button.Text.displayName = 'Button.Text';
40
+ Button.Group.displayName = 'Button.Group';
41
+ Button.Spinner.displayName = 'Button.Spinner';
42
+ Button.Icon.displayName = 'Button.Icon';
43
+
44
+ return Button as IButtonComponentType<
45
+ ButtonProps,
46
+ GroupProps,
47
+ SpinnerProps,
48
+ TextProps,
49
+ IconProps,
50
+ ButtonRef,
51
+ TextRef
52
+ >;
53
+ }
@@ -0,0 +1,85 @@
1
+ import type { PropsWithoutRef, ReactElement, ReactNode, RefAttributes } from 'react';
2
+ import type { PressableProps, ViewProps } from 'react-native';
3
+
4
+ export interface InteractionState {
5
+ hover: boolean;
6
+ focus: boolean;
7
+ active: boolean;
8
+ disabled?: boolean;
9
+ focusVisible: boolean;
10
+ }
11
+
12
+ export interface InterfaceButtonProps extends PressableProps {
13
+ /**
14
+ * If true, the button will be in hovered state.
15
+ */
16
+ isHovered?: boolean;
17
+ /**
18
+ * If true, the button will be in pressed state.
19
+ */
20
+ isActive?: boolean;
21
+ /**
22
+ * If true, the button will be focused.
23
+ */
24
+ isFocused?: boolean;
25
+ /**
26
+ * If true, the button focus ring will be visible.
27
+ */
28
+ isFocusVisible?: boolean;
29
+ /**
30
+ * If true, the button will be disabled.
31
+ */
32
+ isDisabled?: boolean;
33
+ }
34
+
35
+ export interface IButtonGroupProps {
36
+ /**
37
+ * The direction of the Stack Items.
38
+ * @default row
39
+ */
40
+ flexDirection?: 'row' | 'column' | 'row-reverse' | 'column-reverse';
41
+ /**
42
+ *
43
+ */
44
+ children: ReactElement | ReactElement[];
45
+
46
+ /**
47
+ * If true, the button will be disabled.
48
+ */
49
+ isDisabled?: boolean;
50
+ /**
51
+ * If true, button will be atttached together.
52
+ */
53
+ isAttached?: boolean;
54
+ reversed?: boolean;
55
+ isReversed?: boolean;
56
+ }
57
+
58
+ export type IButtonComponentType<
59
+ ButtonProps,
60
+ GroupProps,
61
+ SpinnerProps,
62
+ TextProps,
63
+ IconProps,
64
+ ButtonRef = unknown,
65
+ TextRef = unknown,
66
+ > = React.ForwardRefExoticComponent<
67
+ PropsWithoutRef<ButtonProps & IButtonProps> & RefAttributes<ButtonRef>
68
+ > & {
69
+ Group: React.ForwardRefExoticComponent<
70
+ RefAttributes<unknown> & PropsWithoutRef<GroupProps & IButtonGroupProps>
71
+ >;
72
+ Spinner: React.ForwardRefExoticComponent<
73
+ PropsWithoutRef<SpinnerProps> & React.RefAttributes<unknown>
74
+ >;
75
+ Text: React.ForwardRefExoticComponent<React.RefAttributes<TextRef> & PropsWithoutRef<TextProps>>;
76
+ Icon: React.ForwardRefExoticComponent<React.RefAttributes<unknown> & PropsWithoutRef<IconProps>>;
77
+ };
78
+
79
+ export interface IButtonTextProps extends ViewProps {
80
+ children?: ReactNode;
81
+ }
82
+
83
+ export interface IButtonSpinnerProps extends ViewProps {}
84
+
85
+ export type IButtonProps = InterfaceButtonProps;
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from './button';
2
+ export * from './input';
3
+ export * from './select';
4
+ export { OverlayInsetsProvider, type EdgeInsets } from './overlay';
@@ -0,0 +1,4 @@
1
+ import { createContext } from '@cdx-ui/utils';
2
+ import type { InputContext } from './types';
3
+
4
+ export const [InputProvider, useInputContext] = createContext<InputContext>('InputContext');
@@ -0,0 +1,104 @@
1
+ import type React from 'react';
2
+ import { forwardRef, useMemo } from 'react';
3
+ import {
4
+ type BlurEvent,
5
+ type FocusEvent,
6
+ Platform,
7
+ type TextInputKeyPressEvent,
8
+ } from 'react-native';
9
+ import { mergeRefs, useFormControl } from '@cdx-ui/utils';
10
+ import { dataAttributes } from '../utils/dataAttributes';
11
+ import { useInputContext } from './context';
12
+ import type { IInputProps } from './types';
13
+
14
+ export const createInputField = <T,>(BaseInputField: React.ComponentType<T>) =>
15
+ forwardRef(
16
+ (
17
+ {
18
+ children,
19
+ onKeyPress,
20
+ type = 'text',
21
+ 'aria-label': ariaLabel = 'Input Field',
22
+ secureTextEntry,
23
+ editable,
24
+ disabled,
25
+ ...props
26
+ }: IInputProps,
27
+ ref?: any,
28
+ ) => {
29
+ const {
30
+ isDisabled,
31
+ isReadOnly,
32
+ isFocused,
33
+ isInvalid,
34
+ setIsFocused,
35
+ isHovered,
36
+ isFocusVisible,
37
+ inputFieldRef,
38
+ isRequired,
39
+ } = useInputContext();
40
+
41
+ const inputProps = useFormControl({
42
+ isDisabled: props.isDisabled || disabled,
43
+ isInvalid: props.isInvalid,
44
+ isReadOnly: props.isReadOnly,
45
+ isRequired: props.isRequired,
46
+ id: props.id,
47
+ });
48
+
49
+ const handleFocus = (focusState: boolean, callback: any) => {
50
+ setIsFocused(focusState);
51
+ callback();
52
+ };
53
+
54
+ const mergedRef = mergeRefs(ref, inputFieldRef);
55
+
56
+ const isEffectivelyDisabled = isDisabled || inputProps.disabled;
57
+
58
+ const editableProp = useMemo(() => {
59
+ if (editable !== undefined) {
60
+ return editable;
61
+ }
62
+ return !(isDisabled || inputProps.disabled || isReadOnly);
63
+ }, [isDisabled, inputProps.disabled, isReadOnly, editable]);
64
+
65
+ return (
66
+ <BaseInputField
67
+ {...(props as T)}
68
+ type={type}
69
+ {...dataAttributes({
70
+ focus: isFocused,
71
+ invalid: isInvalid,
72
+ readonly: isReadOnly,
73
+ required: isRequired,
74
+ hover: isHovered,
75
+ focusVisible: isFocusVisible,
76
+ disabled: isEffectivelyDisabled,
77
+ })}
78
+ disabled={isEffectivelyDisabled}
79
+ secureTextEntry={secureTextEntry || type === 'password'}
80
+ accessible
81
+ aria-label={ariaLabel}
82
+ aria-required={isRequired || inputProps.required}
83
+ aria-invalid={isInvalid || inputProps['aria-invalid']}
84
+ aria-disabled={isEffectivelyDisabled}
85
+ aria-selected={Platform.OS !== 'web' ? isFocused : undefined}
86
+ accessibilityElementsHidden={isEffectivelyDisabled}
87
+ readOnly={!editableProp}
88
+ onKeyPress={(e: TextInputKeyPressEvent) => {
89
+ e.persist();
90
+ onKeyPress?.(e);
91
+ }}
92
+ onFocus={(e: FocusEvent) => {
93
+ handleFocus(true, () => props?.onFocus?.(e));
94
+ }}
95
+ onBlur={(e: BlurEvent) => {
96
+ handleFocus(false, () => props?.onBlur?.(e));
97
+ }}
98
+ ref={mergedRef}
99
+ >
100
+ {children}
101
+ </BaseInputField>
102
+ );
103
+ },
104
+ );
@@ -0,0 +1,12 @@
1
+ import type React from 'react';
2
+ import { forwardRef } from 'react';
3
+ import type { IInputIconProps } from './types';
4
+
5
+ export const createInputIcon = <T,>(BaseInputIcon: React.ComponentType<T>) =>
6
+ forwardRef<unknown, IInputIconProps>(({ children, ...props }, ref) => {
7
+ return (
8
+ <BaseInputIcon ref={ref} {...(props as T)}>
9
+ {children}
10
+ </BaseInputIcon>
11
+ );
12
+ });
@@ -0,0 +1,92 @@
1
+ import React, { forwardRef, useCallback, useMemo } from 'react';
2
+ import { mergeRefs, useFormControlContext } from '@cdx-ui/utils';
3
+ import { useFocusRing } from '@react-native-aria/focus';
4
+ import { useHover } from '@react-native-aria/interactions';
5
+ import { dataAttributes } from '../utils/dataAttributes';
6
+ import { InputProvider } from './context';
7
+ import type { IInputRootProps } from './types';
8
+
9
+ // TODO: Does this need useControllableState?
10
+
11
+ export const createInputRoot = <T,>(BaseInput: React.ComponentType<T>) =>
12
+ forwardRef(
13
+ (
14
+ {
15
+ children,
16
+ isReadOnly,
17
+ isDisabled,
18
+ isInvalid,
19
+ isRequired,
20
+ isHovered: isHoveredProp,
21
+ isFocused: isFocusedProp,
22
+ isFocusVisible: isFocusVisibleProp,
23
+ ...props
24
+ }: IInputRootProps,
25
+ ref?: React.Ref<T>,
26
+ ) => {
27
+ const inputRef = React.useRef(null);
28
+ const inputFieldRef = React.useRef(null);
29
+
30
+ const [isFocused, setIsFocused] = React.useState(false);
31
+ const handleFocus = useCallback((focusState: boolean, callback: any) => {
32
+ setIsFocused(focusState);
33
+ callback();
34
+ }, []);
35
+
36
+ const inputProps = useFormControlContext();
37
+ const { isHovered } = useHover({}, inputRef);
38
+ const { isFocusVisible } = useFocusRing();
39
+
40
+ const resolvedDisabled = isDisabled || inputProps.isDisabled;
41
+ const resolvedInvalid = isInvalid || inputProps.isInvalid;
42
+ const resolvedReadOnly = isReadOnly || inputProps.isReadOnly;
43
+ const resolvedRequired = isRequired || inputProps.isRequired;
44
+ const resolvedHovered = isHoveredProp || isHovered;
45
+ const resolvedFocused = isFocusedProp || isFocused;
46
+ const resolvedFocusVisible = isFocusVisibleProp || isFocusVisible;
47
+
48
+ const contextValue = useMemo(
49
+ () => ({
50
+ isDisabled: resolvedDisabled,
51
+ isInvalid: resolvedInvalid,
52
+ isHovered: resolvedHovered,
53
+ isFocused: resolvedFocused,
54
+ isFocusVisible: resolvedFocusVisible,
55
+ isReadOnly: resolvedReadOnly,
56
+ isRequired: resolvedRequired,
57
+ inputRef,
58
+ handleFocus,
59
+ setIsFocused,
60
+ inputFieldRef,
61
+ }),
62
+ [
63
+ resolvedDisabled,
64
+ resolvedInvalid,
65
+ resolvedHovered,
66
+ resolvedFocused,
67
+ resolvedFocusVisible,
68
+ resolvedReadOnly,
69
+ resolvedRequired,
70
+ handleFocus,
71
+ ],
72
+ );
73
+
74
+ return (
75
+ <BaseInput
76
+ {...dataAttributes({
77
+ hover: resolvedHovered,
78
+ focus: resolvedFocused,
79
+ disabled: resolvedDisabled,
80
+ invalid: resolvedInvalid,
81
+ readonly: resolvedReadOnly,
82
+ required: resolvedRequired,
83
+ focusVisible: resolvedFocusVisible,
84
+ })}
85
+ {...(props as T)}
86
+ ref={mergeRefs(inputRef, ref as any)} // TODO: Somehow utils and primitives package are resolving to different versions of react (@types/react?)
87
+ >
88
+ <InputProvider value={contextValue}>{children}</InputProvider>
89
+ </BaseInput>
90
+ );
91
+ },
92
+ );
@@ -0,0 +1,39 @@
1
+ import type React from 'react';
2
+ import { forwardRef } from 'react';
3
+ import { useFormControl } from '@cdx-ui/utils';
4
+ import { dataAttributes } from '../utils/dataAttributes';
5
+ import { useInputContext } from './context';
6
+ import type { IInputSlotProps } from './types';
7
+
8
+ export const createInputSlot = <T,>(BaseInputSlot: React.ComponentType<T>) =>
9
+ forwardRef<unknown, IInputSlotProps>(
10
+ ({ children, onPress, focusOnPress = true, ...props }, ref) => {
11
+ const { inputFieldRef, isDisabled } = useInputContext();
12
+
13
+ const handleFocus = () => {
14
+ focusOnPress && inputFieldRef.current?.focus();
15
+ };
16
+
17
+ const inputProps = useFormControl({
18
+ isDisabled: props.isDisabled,
19
+ });
20
+
21
+ return (
22
+ <BaseInputSlot
23
+ {...dataAttributes({
24
+ disabled: isDisabled || inputProps.disabled,
25
+ })}
26
+ onPress={() => {
27
+ onPress?.();
28
+ handleFocus();
29
+ }}
30
+ accessibilityElementsHidden={true}
31
+ tabIndex={-1}
32
+ ref={ref}
33
+ {...(props as T)}
34
+ >
35
+ {children}
36
+ </BaseInputSlot>
37
+ );
38
+ },
39
+ );
@@ -0,0 +1,51 @@
1
+ import { createInputField } from './createInputField';
2
+ import { createInputIcon } from './createInputIcon';
3
+ import { createInputRoot } from './createInputRoot';
4
+ import { createInputSlot } from './createInputSlot';
5
+ import type { IInputComponentType } from './types';
6
+
7
+ export type {
8
+ IInputFieldProps,
9
+ IInputIconProps,
10
+ IInputProps,
11
+ IInputRootProps,
12
+ IInputSlotProps,
13
+ } from './types';
14
+
15
+ export const createInput = <
16
+ RootProps,
17
+ IconProps,
18
+ SlotProps,
19
+ FieldProps,
20
+ RootRef = unknown,
21
+ FieldRef = unknown,
22
+ >({
23
+ Root,
24
+ Icon,
25
+ Slot,
26
+ Field,
27
+ }: {
28
+ Root: React.ComponentType<RootProps>;
29
+ Icon: React.ComponentType<IconProps>;
30
+ Slot: React.ComponentType<SlotProps>;
31
+ Field: React.ComponentType<FieldProps>;
32
+ }) => {
33
+ const Input = createInputRoot(Root) as any;
34
+ Input.Icon = createInputIcon(Icon);
35
+ Input.Slot = createInputSlot(Slot);
36
+ Input.Field = createInputField(Field);
37
+
38
+ Input.displayName = 'Input';
39
+ Input.Icon.displayName = 'Input.Icon';
40
+ Input.Slot.displayName = 'Input.Slot';
41
+ Input.Field.displayName = 'Input.Field';
42
+
43
+ return Input as IInputComponentType<
44
+ RootProps,
45
+ IconProps,
46
+ SlotProps,
47
+ FieldProps,
48
+ RootRef,
49
+ FieldRef
50
+ >;
51
+ };
@@ -0,0 +1,113 @@
1
+ import type { TextInputProps, ViewProps } from 'react-native';
2
+
3
+ export interface InputContext {
4
+ isDisabled?: boolean;
5
+ isInvalid?: boolean;
6
+ isReadOnly?: boolean;
7
+ isRequired?: boolean;
8
+ isHovered?: boolean;
9
+ isFocused?: boolean;
10
+ handleFocus?: any;
11
+ isFocusVisible?: boolean;
12
+ isFullWidth?: boolean;
13
+ inputRef?: any;
14
+ setIsFocused?: any;
15
+ inputFieldRef?: any;
16
+ }
17
+
18
+ export interface IInputFieldProps {
19
+ /**
20
+ * If true, the input will indicate an error.
21
+ */
22
+ isInvalid?: boolean;
23
+ /**
24
+ * If true, the input will be disabled.
25
+ */
26
+ isDisabled?: boolean;
27
+ /**
28
+ * If true, the input will be hovered.
29
+ */
30
+ isHovered?: boolean;
31
+
32
+ /**
33
+ * If true, the input will be focused.
34
+ */
35
+ isFocused?: boolean;
36
+ /**
37
+ * This will set aria-required="true" on web when passed in formcontrol.
38
+ */
39
+ isRequired?: boolean;
40
+ /**
41
+ * If true, prevents the value of the input from being edited.
42
+ */
43
+ isReadOnly?: boolean;
44
+ /**
45
+ * If true, the input element will span the full width of its parent
46
+ */
47
+ isFullWidth?: boolean;
48
+ }
49
+ export interface IInputRootProps extends IInputFieldProps {
50
+ children?: React.ReactNode;
51
+ isFocusVisible?: boolean;
52
+ }
53
+
54
+ interface IInputBaseProps extends TextInputProps {
55
+ disabled?: boolean;
56
+ isDisabled?: boolean;
57
+ isInvalid?: boolean;
58
+ isReadOnly?: boolean;
59
+ isRequired?: boolean;
60
+ }
61
+
62
+ interface IInputTextProps extends IInputBaseProps {
63
+ type?: 'text' | 'password';
64
+ }
65
+
66
+ interface IInputFileProps extends IInputBaseProps {
67
+ type: 'file';
68
+ /** @platform web — Accepted file types, e.g. "image/*", ".pdf,.doc" */
69
+ accept?: string;
70
+ /** @platform web — Allow selecting multiple files */
71
+ multiple?: boolean;
72
+ /** @platform web — Preferred capture source on mobile web: "user" (front) or "environment" (rear) */
73
+ capture?: 'user' | 'environment';
74
+ /** @platform web — Called when the user selects files */
75
+ onFiles?: (files: File[]) => void;
76
+ }
77
+
78
+ export type IInputProps = IInputTextProps | IInputFileProps;
79
+ export interface IInputIconProps extends ViewProps {
80
+ children?: React.ReactNode;
81
+ }
82
+
83
+ export interface IInputSlotProps extends ViewProps {
84
+ children?: React.ReactNode;
85
+ onPress?: () => void;
86
+ isDisabled?: boolean;
87
+ /**
88
+ * If true, the input will be focused on press.
89
+ * @default true
90
+ */
91
+ focusOnPress?: boolean;
92
+ }
93
+
94
+ export type IInputComponentType<
95
+ RootProps,
96
+ IconProps,
97
+ SlotProps,
98
+ FieldProps,
99
+ RootRef = unknown,
100
+ FieldRef = unknown,
101
+ > = React.ForwardRefExoticComponent<
102
+ React.PropsWithoutRef<RootProps & IInputFieldProps> & React.RefAttributes<RootRef>
103
+ > & {
104
+ Icon: React.ForwardRefExoticComponent<
105
+ React.PropsWithoutRef<IconProps> & React.RefAttributes<unknown>
106
+ >;
107
+ Slot: React.ForwardRefExoticComponent<
108
+ React.PropsWithoutRef<SlotProps & IInputSlotProps> & React.RefAttributes<unknown>
109
+ >;
110
+ Field: React.ForwardRefExoticComponent<
111
+ React.PropsWithoutRef<FieldProps & IInputProps> & React.RefAttributes<FieldRef>
112
+ >;
113
+ };
@@ -0,0 +1,77 @@
1
+ import type React from 'react';
2
+ import {
3
+ Modal,
4
+ Platform,
5
+ Pressable,
6
+ type StyleProp,
7
+ StyleSheet,
8
+ type ViewStyle,
9
+ } from 'react-native';
10
+ import Animated, { type EntryOrExitLayoutType } from 'react-native-reanimated';
11
+
12
+ let createPortalFn:
13
+ | ((children: React.ReactNode, container: Element) => React.ReactPortal)
14
+ | undefined;
15
+ if (Platform.OS === 'web') {
16
+ try {
17
+ createPortalFn = require('react-dom').createPortal;
18
+ } catch {}
19
+ }
20
+
21
+ export interface OverlayContainerProps {
22
+ onDismiss: () => void;
23
+ entering?: EntryOrExitLayoutType;
24
+ exiting?: EntryOrExitLayoutType;
25
+ style?: StyleProp<ViewStyle>;
26
+ children: React.ReactNode;
27
+ }
28
+
29
+ /**
30
+ * Cross-platform overlay container.
31
+ *
32
+ * - Web: renders children into a `createPortal` at `document.body` with
33
+ * `position: fixed` and `zIndex: 50`.
34
+ * - Native: renders children inside a transparent `<Modal>` with a
35
+ * backdrop `<Pressable>` that calls `onDismiss` on press.
36
+ *
37
+ * The consumer is responsible for not mounting this component when the
38
+ * overlay should be hidden.
39
+ */
40
+ export function OverlayContainer({
41
+ onDismiss,
42
+ entering,
43
+ exiting,
44
+ style,
45
+ children,
46
+ }: Readonly<OverlayContainerProps>) {
47
+ if (Platform.OS === 'web' && createPortalFn) {
48
+ return createPortalFn(
49
+ <Animated.View
50
+ entering={entering}
51
+ exiting={exiting}
52
+ style={[{ position: 'fixed' as any, zIndex: 50 }, style]}
53
+ >
54
+ {children}
55
+ </Animated.View>,
56
+ document.body,
57
+ );
58
+ }
59
+
60
+ return (
61
+ <Modal visible transparent animationType="none" statusBarTranslucent onRequestClose={onDismiss}>
62
+ <Pressable
63
+ style={StyleSheet.absoluteFill}
64
+ onPress={onDismiss}
65
+ accessible={false}
66
+ importantForAccessibility="no-hide-descendants"
67
+ />
68
+ <Animated.View
69
+ entering={entering}
70
+ exiting={exiting}
71
+ style={[{ position: 'absolute' }, style]}
72
+ >
73
+ {children}
74
+ </Animated.View>
75
+ </Modal>
76
+ );
77
+ }
@@ -0,0 +1,10 @@
1
+ export { OverlayContainer } from './OverlayContainer';
2
+ export type { OverlayContainerProps } from './OverlayContainer';
3
+ export { useAnchorPosition, type AnchorLayout } from './useAnchorPosition';
4
+ export { useDismissOverlay } from './useDismissOverlay';
5
+ export {
6
+ useOverlayPosition,
7
+ type OverlayPosition,
8
+ OverlayInsetsProvider,
9
+ type EdgeInsets,
10
+ } from './useOverlayPosition';