@thangnqdev/ui 0.1.2 → 0.2.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 (233) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +54 -206
  3. package/bin/cli.js +125 -0
  4. package/dist/bento/index.cjs.map +1 -1
  5. package/dist/bento/index.js +2 -2
  6. package/dist/bento/styles.css +2 -46
  7. package/dist/buttons/index.cjs +111 -18
  8. package/dist/buttons/index.cjs.map +1 -1
  9. package/dist/buttons/index.d.cts +1 -1
  10. package/dist/buttons/index.d.ts +1 -1
  11. package/dist/buttons/index.js +3 -3
  12. package/dist/buttons/styles.css +2 -6
  13. package/dist/{chunk-4S6L5V7X.js → chunk-2YJBW4E2.js} +2 -2
  14. package/dist/chunk-2YJBW4E2.js.map +1 -0
  15. package/dist/{chunk-F5Q7BTMH.js → chunk-4CRFNALF.js} +1011 -556
  16. package/dist/chunk-4CRFNALF.js.map +1 -0
  17. package/dist/{chunk-LWFRUJRU.js → chunk-4MS6ZHFQ.js} +225 -41
  18. package/dist/chunk-4MS6ZHFQ.js.map +1 -0
  19. package/dist/{chunk-2OXZK6IP.js → chunk-77KTYOMQ.js} +3 -3
  20. package/dist/chunk-77KTYOMQ.js.map +1 -0
  21. package/dist/{chunk-IABPUCAQ.js → chunk-ADO3SVUA.js} +2 -2
  22. package/dist/chunk-ADO3SVUA.js.map +1 -0
  23. package/dist/{chunk-GXI2H5XA.js → chunk-D7FB3TCA.js} +3 -3
  24. package/dist/chunk-D7FB3TCA.js.map +1 -0
  25. package/dist/chunk-DSEL7ELZ.js +314 -0
  26. package/dist/chunk-DSEL7ELZ.js.map +1 -0
  27. package/dist/{chunk-KJRBRTQE.js → chunk-EXFR2BFJ.js} +37 -28
  28. package/dist/chunk-EXFR2BFJ.js.map +1 -0
  29. package/dist/{chunk-PF4ZHGVW.js → chunk-GXWB6BSY.js} +116 -23
  30. package/dist/chunk-GXWB6BSY.js.map +1 -0
  31. package/dist/{chunk-66VWUJEI.js → chunk-I2FQBMVW.js} +3 -3
  32. package/dist/chunk-I2FQBMVW.js.map +1 -0
  33. package/dist/{chunk-EU2LPUYP.js → chunk-I5O2WCGF.js} +3 -3
  34. package/dist/chunk-I5O2WCGF.js.map +1 -0
  35. package/dist/{chunk-ATYGDTMK.js → chunk-ME4XILG5.js} +5 -5
  36. package/dist/chunk-ME4XILG5.js.map +1 -0
  37. package/dist/{chunk-5FZ6ZFII.js → chunk-PC5SZFZC.js} +373 -150
  38. package/dist/chunk-PC5SZFZC.js.map +1 -0
  39. package/dist/{chunk-C4SUXMI3.js → chunk-S6XJMYME.js} +3 -3
  40. package/dist/chunk-S6XJMYME.js.map +1 -0
  41. package/dist/{chunk-A7P3O5Z5.js → chunk-V52JOSDF.js} +3 -3
  42. package/dist/chunk-V52JOSDF.js.map +1 -0
  43. package/dist/{chunk-NFVONJUK.js → chunk-W5WNWTQC.js} +455 -211
  44. package/dist/chunk-W5WNWTQC.js.map +1 -0
  45. package/dist/{chunk-WAFHC4DQ.js → chunk-YJ64RI7J.js} +3 -3
  46. package/dist/chunk-YJ64RI7J.js.map +1 -0
  47. package/dist/chunk-YOCGBORV.js +155 -0
  48. package/dist/chunk-YOCGBORV.js.map +1 -0
  49. package/dist/chunk-ZEDXARXW.js +96 -0
  50. package/dist/chunk-ZEDXARXW.js.map +1 -0
  51. package/dist/components.css +2 -0
  52. package/dist/core/index.cjs +33 -24
  53. package/dist/core/index.cjs.map +1 -1
  54. package/dist/core/index.js +2 -2
  55. package/dist/core/styles.css +2 -134
  56. package/dist/data-display/index.cjs +770 -247
  57. package/dist/data-display/index.cjs.map +1 -1
  58. package/dist/data-display/index.d.cts +1 -1
  59. package/dist/data-display/index.d.ts +1 -1
  60. package/dist/data-display/index.js +5 -4
  61. package/dist/data-display/styles.css +2 -4
  62. package/dist/feedback/index.cjs.map +1 -1
  63. package/dist/feedback/index.js +2 -2
  64. package/dist/feedback/styles.css +2 -4
  65. package/dist/forms/index.cjs +1211 -459
  66. package/dist/forms/index.cjs.map +1 -1
  67. package/dist/forms/index.d.cts +1 -1
  68. package/dist/forms/index.d.ts +1 -1
  69. package/dist/forms/index.js +8 -8
  70. package/dist/forms/styles.css +2 -6
  71. package/dist/glass/index.cjs.map +1 -1
  72. package/dist/glass/index.js +2 -2
  73. package/dist/glass/styles.css +2 -81
  74. package/dist/{index-D6fE7w-b.d.cts → index-BGt_hecO.d.cts} +1 -0
  75. package/dist/{index-D6fE7w-b.d.ts → index-BGt_hecO.d.ts} +1 -0
  76. package/dist/{index-DzcWh1Hs.d.cts → index-BtDvpZCf.d.cts} +7 -0
  77. package/dist/{index-DzcWh1Hs.d.ts → index-BtDvpZCf.d.ts} +7 -0
  78. package/dist/{index-Cle6QzQl.d.ts → index-COX1sBUN.d.ts} +27 -3
  79. package/dist/{index-BkRqGNG1.d.ts → index-Cg8MuP7u.d.cts} +40 -13
  80. package/dist/{index-BkRqGNG1.d.cts → index-Cg8MuP7u.d.ts} +40 -13
  81. package/dist/{index-CenuF_P2.d.ts → index-DHkPRTvE.d.cts} +39 -8
  82. package/dist/{index-CenuF_P2.d.cts → index-DHkPRTvE.d.ts} +39 -8
  83. package/dist/{index-TyK-Ldpy.d.cts → index-Du_oht9k.d.cts} +27 -3
  84. package/dist/index.cjs +2481 -917
  85. package/dist/index.cjs.map +1 -1
  86. package/dist/index.d.cts +5 -5
  87. package/dist/index.d.ts +5 -5
  88. package/dist/index.js +19 -19
  89. package/dist/layout/index.cjs.map +1 -1
  90. package/dist/layout/index.js +2 -2
  91. package/dist/layout/styles.css +2 -8
  92. package/dist/navigation/index.cjs +288 -48
  93. package/dist/navigation/index.cjs.map +1 -1
  94. package/dist/navigation/index.d.cts +1 -1
  95. package/dist/navigation/index.d.ts +1 -1
  96. package/dist/navigation/index.js +6 -6
  97. package/dist/navigation/styles.css +2 -4
  98. package/dist/overlays/index.cjs +566 -229
  99. package/dist/overlays/index.cjs.map +1 -1
  100. package/dist/overlays/index.d.cts +1 -1
  101. package/dist/overlays/index.d.ts +1 -1
  102. package/dist/overlays/index.js +7 -7
  103. package/dist/overlays/styles.css +2 -8
  104. package/dist/reset.css +45 -0
  105. package/dist/styles.css +1 -1
  106. package/dist/tokens.css +11 -0
  107. package/dist/typography/index.cjs.map +1 -1
  108. package/dist/typography/index.js +4 -4
  109. package/dist/typography/styles.css +2 -5
  110. package/llms.txt +211 -113
  111. package/package.json +167 -116
  112. package/scripts/postinstall.js +125 -0
  113. package/src/bento/BentoCard.tsx +140 -140
  114. package/src/bento/BentoGrid.tsx +50 -50
  115. package/src/bento/index.ts +2 -2
  116. package/src/bento/tokens.css +46 -46
  117. package/src/buttons/Button.tsx +104 -104
  118. package/src/buttons/ButtonGroup.tsx +43 -43
  119. package/src/buttons/IconButton.tsx +79 -79
  120. package/src/buttons/SplitButton.tsx +186 -86
  121. package/src/buttons/ToggleButton.tsx +157 -157
  122. package/src/buttons/index.ts +5 -5
  123. package/src/buttons/tokens.css +6 -6
  124. package/src/core/ThemeProvider.tsx +135 -123
  125. package/src/core/index.ts +2 -2
  126. package/src/core/tokens.css +134 -134
  127. package/src/data-display/Accordion.tsx +147 -147
  128. package/src/data-display/Avatar.tsx +134 -134
  129. package/src/data-display/Card.tsx +112 -112
  130. package/src/data-display/Chip.tsx +74 -74
  131. package/src/data-display/DataTablePro.tsx +299 -121
  132. package/src/data-display/StatCard.tsx +60 -60
  133. package/src/data-display/Table.tsx +74 -74
  134. package/src/data-display/Timeline.tsx +62 -62
  135. package/src/data-display/TreeView.tsx +243 -104
  136. package/src/data-display/tokens.css +4 -4
  137. package/src/feedback/Alert.tsx +80 -80
  138. package/src/feedback/EmptyState.tsx +45 -45
  139. package/src/feedback/Skeleton.tsx +66 -66
  140. package/src/feedback/Spinner.tsx +65 -65
  141. package/src/feedback/index.ts +5 -5
  142. package/src/feedback/tokens.css +4 -4
  143. package/src/forms/Checkbox.tsx +149 -69
  144. package/src/forms/CheckboxGroup.tsx +76 -29
  145. package/src/forms/Combobox.tsx +172 -28
  146. package/src/forms/DatePicker.tsx +385 -81
  147. package/src/forms/FileUpload.tsx +51 -7
  148. package/src/forms/FormField.tsx +115 -115
  149. package/src/forms/Input.tsx +151 -151
  150. package/src/forms/NumberInput.tsx +34 -20
  151. package/src/forms/PasswordInput.tsx +35 -35
  152. package/src/forms/PinInput.tsx +139 -125
  153. package/src/forms/Radio.tsx +142 -142
  154. package/src/forms/RangeSlider.tsx +237 -111
  155. package/src/forms/SearchInput.tsx +34 -34
  156. package/src/forms/Select.tsx +103 -103
  157. package/src/forms/Slider.tsx +85 -85
  158. package/src/forms/Textarea.tsx +114 -114
  159. package/src/forms/tokens.css +6 -6
  160. package/src/glass/GlassButton.tsx +59 -59
  161. package/src/glass/GlassCard.tsx +71 -71
  162. package/src/glass/index.ts +3 -3
  163. package/src/glass/tokens.css +81 -81
  164. package/src/index.ts +26 -26
  165. package/src/layout/AspectRatio.tsx +39 -39
  166. package/src/layout/Container.tsx +53 -53
  167. package/src/layout/Divider.tsx +61 -61
  168. package/src/layout/Flex.tsx +79 -79
  169. package/src/layout/Grid.tsx +124 -124
  170. package/src/layout/PageLayout.tsx +59 -59
  171. package/src/layout/Section.tsx +47 -47
  172. package/src/layout/Spacer.tsx +30 -30
  173. package/src/layout/Stack.tsx +88 -88
  174. package/src/layout/tokens.css +8 -8
  175. package/src/navigation/Breadcrumb.tsx +73 -73
  176. package/src/navigation/CommandPalette.tsx +142 -142
  177. package/src/navigation/DropdownMenu.tsx +244 -160
  178. package/src/navigation/Pagination.tsx +114 -114
  179. package/src/navigation/Stepper.tsx +93 -93
  180. package/src/navigation/Tabs.tsx +265 -133
  181. package/src/navigation/index.ts +6 -6
  182. package/src/navigation/tokens.css +4 -4
  183. package/src/overlays/AlertDialog.tsx +63 -54
  184. package/src/overlays/BottomSheet.tsx +30 -10
  185. package/src/overlays/ContextMenu.tsx +51 -9
  186. package/src/overlays/Drawer.tsx +160 -145
  187. package/src/overlays/FocusTrap.tsx +122 -49
  188. package/src/overlays/Modal.tsx +296 -219
  189. package/src/overlays/Popover.tsx +55 -25
  190. package/src/overlays/Portal.tsx +23 -23
  191. package/src/overlays/Toast.tsx +175 -137
  192. package/src/overlays/Tooltip.tsx +125 -100
  193. package/src/overlays/tokens.css +8 -8
  194. package/src/registry/components.ts +1368 -0
  195. package/src/styles/components.css +39 -0
  196. package/src/styles/index.css +2 -93
  197. package/src/styles/reset.css +45 -0
  198. package/src/styles/tokens.css +11 -2
  199. package/src/typography/Blockquote.tsx +32 -32
  200. package/src/typography/Code.tsx +89 -89
  201. package/src/typography/Heading.tsx +56 -56
  202. package/src/typography/Kbd.tsx +25 -25
  203. package/src/typography/Label.tsx +47 -47
  204. package/src/typography/Link.tsx +56 -56
  205. package/src/typography/Text.tsx +62 -62
  206. package/src/typography/index.ts +7 -7
  207. package/src/typography/tokens.css +5 -5
  208. package/src/utils/cn.ts +6 -6
  209. package/src/utils/scroll-lock.ts +19 -0
  210. package/src/utils/useControllableState.ts +54 -0
  211. package/ui-contract.json +192 -35
  212. package/dist/chunk-2OXZK6IP.js.map +0 -1
  213. package/dist/chunk-4S6L5V7X.js.map +0 -1
  214. package/dist/chunk-5FZ6ZFII.js.map +0 -1
  215. package/dist/chunk-66VWUJEI.js.map +0 -1
  216. package/dist/chunk-A7P3O5Z5.js.map +0 -1
  217. package/dist/chunk-ATYGDTMK.js.map +0 -1
  218. package/dist/chunk-C4SUXMI3.js.map +0 -1
  219. package/dist/chunk-EU2LPUYP.js.map +0 -1
  220. package/dist/chunk-F5Q7BTMH.js.map +0 -1
  221. package/dist/chunk-GXI2H5XA.js.map +0 -1
  222. package/dist/chunk-H3LCNPMC.js +0 -101
  223. package/dist/chunk-H3LCNPMC.js.map +0 -1
  224. package/dist/chunk-IABPUCAQ.js.map +0 -1
  225. package/dist/chunk-KJRBRTQE.js.map +0 -1
  226. package/dist/chunk-LBUBXUYQ.js +0 -40
  227. package/dist/chunk-LBUBXUYQ.js.map +0 -1
  228. package/dist/chunk-LWFRUJRU.js.map +0 -1
  229. package/dist/chunk-NFVONJUK.js.map +0 -1
  230. package/dist/chunk-PF4ZHGVW.js.map +0 -1
  231. package/dist/chunk-WAFHC4DQ.js.map +0 -1
  232. package/dist/chunk-X76KBH4Y.js +0 -71
  233. package/dist/chunk-X76KBH4Y.js.map +0 -1
@@ -1,49 +1,122 @@
1
- import React, { useEffect, useRef } from 'react';
2
-
3
- export interface FocusTrapProps {
4
- children: React.ReactNode;
5
- active?: boolean;
6
- }
7
-
8
- const FOCUSABLE_SELECTOR =
9
- 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';
10
-
11
- export function FocusTrap({ children, active = true }: FocusTrapProps) {
12
- const rootRef = useRef<HTMLDivElement>(null);
13
-
14
- useEffect(() => {
15
- if (!active) return;
16
-
17
- const root = rootRef.current;
18
- if (!root) return;
19
-
20
- const focusable = root.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR);
21
- const first = focusable[0];
22
- const last = focusable[focusable.length - 1];
23
-
24
- if (first) {
25
- first.focus();
26
- }
27
-
28
- const handleKeyDown = (e: KeyboardEvent) => {
29
- if (e.key !== 'Tab') return;
30
-
31
- if (e.shiftKey) {
32
- if (document.activeElement === first) {
33
- e.preventDefault();
34
- last?.focus();
35
- }
36
- } else {
37
- if (document.activeElement === last) {
38
- e.preventDefault();
39
- first?.focus();
40
- }
41
- }
42
- };
43
-
44
- window.addEventListener('keydown', handleKeyDown);
45
- return () => window.removeEventListener('keydown', handleKeyDown);
46
- }, [active]);
47
-
48
- return <div ref={rootRef} className="contents">{children}</div>;
49
- }
1
+ import React, { useEffect, useRef } from 'react';
2
+
3
+ export interface FocusTrapProps {
4
+ children: React.ReactNode;
5
+ active?: boolean;
6
+ autoFocus?: boolean;
7
+ restoreFocus?: boolean;
8
+ }
9
+
10
+ const FOCUSABLE_SELECTOR = [
11
+ 'a[href]',
12
+ 'button:not([disabled])',
13
+ 'textarea:not([disabled])',
14
+ 'input:not([disabled])',
15
+ 'select:not([disabled])',
16
+ '[tabindex]:not([tabindex="-1"])',
17
+ '[contenteditable="true"]',
18
+ ].join(', ');
19
+
20
+ // Global trap stack to handle nested dialogs/popovers correctly
21
+ const trapStack: HTMLElement[] = [];
22
+
23
+ export function FocusTrap({
24
+ children,
25
+ active = true,
26
+ autoFocus = true,
27
+ restoreFocus = true,
28
+ }: FocusTrapProps) {
29
+ const rootRef = useRef<HTMLDivElement>(null);
30
+ const previousActiveElementRef = useRef<HTMLElement | null>(null);
31
+
32
+ useEffect(() => {
33
+ if (!active) return;
34
+
35
+ if (typeof document !== 'undefined') {
36
+ previousActiveElementRef.current = document.activeElement as HTMLElement | null;
37
+ }
38
+
39
+ const root = rootRef.current;
40
+ if (!root) return;
41
+
42
+ trapStack.push(root);
43
+
44
+ if (autoFocus) {
45
+ const timer = setTimeout(() => {
46
+ const focusable = root.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR);
47
+ if (focusable.length > 0) {
48
+ focusable[0].focus();
49
+ } else if (typeof root.focus === 'function') {
50
+ root.focus();
51
+ }
52
+ }, 0);
53
+ return () => clearTimeout(timer);
54
+ }
55
+
56
+ return () => {
57
+ const idx = trapStack.indexOf(root);
58
+ if (idx !== -1) trapStack.splice(idx, 1);
59
+ };
60
+ }, [active, autoFocus]);
61
+
62
+ useEffect(() => {
63
+ if (!active) return;
64
+
65
+ const handleKeyDown = (e: KeyboardEvent) => {
66
+ if (e.key !== 'Tab') return;
67
+
68
+ const root = rootRef.current;
69
+ if (!root) return;
70
+
71
+ // Only topmost trap in the stack handles tab navigation
72
+ if (trapStack.length > 0 && trapStack[trapStack.length - 1] !== root) {
73
+ return;
74
+ }
75
+
76
+ const focusable = Array.from(root.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)).filter(
77
+ (el) => el.offsetParent !== null || el.getClientRects().length > 0
78
+ );
79
+
80
+ if (focusable.length === 0) {
81
+ e.preventDefault();
82
+ root.focus();
83
+ return;
84
+ }
85
+
86
+ const first = focusable[0];
87
+ const last = focusable[focusable.length - 1];
88
+
89
+ if (e.shiftKey) {
90
+ if (document.activeElement === first || !root.contains(document.activeElement)) {
91
+ e.preventDefault();
92
+ last.focus();
93
+ }
94
+ } else {
95
+ if (document.activeElement === last || !root.contains(document.activeElement)) {
96
+ e.preventDefault();
97
+ first.focus();
98
+ }
99
+ }
100
+ };
101
+
102
+ window.addEventListener('keydown', handleKeyDown, true);
103
+
104
+ return () => {
105
+ window.removeEventListener('keydown', handleKeyDown, true);
106
+ const root = rootRef.current;
107
+ if (root) {
108
+ const idx = trapStack.indexOf(root);
109
+ if (idx !== -1) trapStack.splice(idx, 1);
110
+ }
111
+ if (restoreFocus && previousActiveElementRef.current && typeof previousActiveElementRef.current.focus === 'function') {
112
+ previousActiveElementRef.current.focus();
113
+ }
114
+ };
115
+ }, [active, restoreFocus]);
116
+
117
+ return (
118
+ <div ref={rootRef} tabIndex={-1} className="contents" style={{ outline: 'none' }}>
119
+ {children}
120
+ </div>
121
+ );
122
+ }