@thangnqdev/ui 0.2.0 → 0.2.2

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 (190) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +144 -142
  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/buttons/index.cjs.map +1 -1
  7. package/dist/buttons/index.js +3 -3
  8. package/dist/{chunk-77KTYOMQ.js → chunk-2OXZK6IP.js} +3 -3
  9. package/dist/chunk-2OXZK6IP.js.map +1 -0
  10. package/dist/{chunk-4CRFNALF.js → chunk-4GNFT2ZJ.js} +7 -7
  11. package/dist/chunk-4GNFT2ZJ.js.map +1 -0
  12. package/dist/{chunk-2YJBW4E2.js → chunk-4S6L5V7X.js} +2 -2
  13. package/dist/chunk-4S6L5V7X.js.map +1 -0
  14. package/dist/{chunk-I2FQBMVW.js → chunk-66VWUJEI.js} +3 -3
  15. package/dist/chunk-66VWUJEI.js.map +1 -0
  16. package/dist/{chunk-YOCGBORV.js → chunk-7NMB2Y64.js} +4 -4
  17. package/dist/chunk-7NMB2Y64.js.map +1 -0
  18. package/dist/{chunk-V52JOSDF.js → chunk-A7P3O5Z5.js} +3 -3
  19. package/dist/chunk-A7P3O5Z5.js.map +1 -0
  20. package/dist/{chunk-ME4XILG5.js → chunk-ATYGDTMK.js} +5 -5
  21. package/dist/chunk-ATYGDTMK.js.map +1 -0
  22. package/dist/{chunk-S6XJMYME.js → chunk-C4SUXMI3.js} +3 -3
  23. package/dist/chunk-C4SUXMI3.js.map +1 -0
  24. package/dist/{chunk-EXFR2BFJ.js → chunk-DUPPPGXY.js} +7 -7
  25. package/dist/chunk-DUPPPGXY.js.map +1 -0
  26. package/dist/{chunk-I5O2WCGF.js → chunk-EU2LPUYP.js} +3 -3
  27. package/dist/chunk-EU2LPUYP.js.map +1 -0
  28. package/dist/{chunk-D7FB3TCA.js → chunk-GXI2H5XA.js} +3 -3
  29. package/dist/chunk-GXI2H5XA.js.map +1 -0
  30. package/dist/{chunk-ADO3SVUA.js → chunk-IABPUCAQ.js} +2 -2
  31. package/dist/chunk-IABPUCAQ.js.map +1 -0
  32. package/dist/{chunk-PC5SZFZC.js → chunk-JOKEKOP3.js} +8 -8
  33. package/dist/chunk-JOKEKOP3.js.map +1 -0
  34. package/dist/{chunk-GXWB6BSY.js → chunk-KUQWUTZA.js} +4 -4
  35. package/dist/chunk-KUQWUTZA.js.map +1 -0
  36. package/dist/{chunk-ZEDXARXW.js → chunk-U3J4UPV3.js} +2 -2
  37. package/dist/chunk-U3J4UPV3.js.map +1 -0
  38. package/dist/{chunk-DSEL7ELZ.js → chunk-VE2PNSDQ.js} +4 -4
  39. package/dist/chunk-VE2PNSDQ.js.map +1 -0
  40. package/dist/{chunk-YJ64RI7J.js → chunk-WAFHC4DQ.js} +3 -3
  41. package/dist/chunk-WAFHC4DQ.js.map +1 -0
  42. package/dist/{chunk-W5WNWTQC.js → chunk-YJ67WYVW.js} +5 -5
  43. package/dist/chunk-YJ67WYVW.js.map +1 -0
  44. package/dist/{chunk-4MS6ZHFQ.js → chunk-Z427NPMU.js} +7 -7
  45. package/dist/chunk-Z427NPMU.js.map +1 -0
  46. package/dist/core/index.cjs +4 -4
  47. package/dist/core/index.cjs.map +1 -1
  48. package/dist/core/index.js +2 -2
  49. package/dist/data-display/index.cjs.map +1 -1
  50. package/dist/data-display/index.js +5 -5
  51. package/dist/feedback/index.cjs.map +1 -1
  52. package/dist/feedback/index.js +2 -2
  53. package/dist/forms/index.cjs.map +1 -1
  54. package/dist/forms/index.js +8 -8
  55. package/dist/glass/index.cjs.map +1 -1
  56. package/dist/glass/index.js +2 -2
  57. package/dist/index.cjs +4 -4
  58. package/dist/index.cjs.map +1 -1
  59. package/dist/index.js +19 -19
  60. package/dist/layout/index.cjs.map +1 -1
  61. package/dist/layout/index.js +2 -2
  62. package/dist/navigation/index.cjs.map +1 -1
  63. package/dist/navigation/index.js +6 -6
  64. package/dist/overlays/index.cjs.map +1 -1
  65. package/dist/overlays/index.js +7 -7
  66. package/dist/reset.css +45 -45
  67. package/dist/tokens.css +11 -11
  68. package/dist/typography/index.cjs.map +1 -1
  69. package/dist/typography/index.js +4 -4
  70. package/llms.txt +3 -3
  71. package/package.json +166 -159
  72. package/scripts/postinstall.js +125 -0
  73. package/src/bento/BentoCard.tsx +140 -140
  74. package/src/bento/BentoGrid.tsx +50 -50
  75. package/src/bento/index.ts +2 -2
  76. package/src/bento/tokens.css +46 -46
  77. package/src/buttons/Button.tsx +104 -104
  78. package/src/buttons/ButtonGroup.tsx +43 -43
  79. package/src/buttons/IconButton.tsx +79 -79
  80. package/src/buttons/SplitButton.tsx +186 -186
  81. package/src/buttons/ToggleButton.tsx +157 -157
  82. package/src/buttons/index.ts +5 -5
  83. package/src/buttons/tokens.css +6 -6
  84. package/src/core/ThemeProvider.tsx +133 -133
  85. package/src/core/index.ts +2 -2
  86. package/src/core/tokens.css +134 -134
  87. package/src/data-display/Accordion.tsx +147 -147
  88. package/src/data-display/Avatar.tsx +134 -134
  89. package/src/data-display/Card.tsx +112 -112
  90. package/src/data-display/Chip.tsx +74 -74
  91. package/src/data-display/DataTablePro.tsx +534 -534
  92. package/src/data-display/StatCard.tsx +60 -60
  93. package/src/data-display/Table.tsx +74 -74
  94. package/src/data-display/Timeline.tsx +62 -62
  95. package/src/data-display/TreeView.tsx +279 -279
  96. package/src/data-display/tokens.css +4 -4
  97. package/src/feedback/Alert.tsx +80 -80
  98. package/src/feedback/EmptyState.tsx +45 -45
  99. package/src/feedback/Skeleton.tsx +66 -66
  100. package/src/feedback/Spinner.tsx +65 -65
  101. package/src/feedback/index.ts +5 -5
  102. package/src/feedback/tokens.css +4 -4
  103. package/src/forms/Checkbox.tsx +149 -149
  104. package/src/forms/CheckboxGroup.tsx +76 -76
  105. package/src/forms/Combobox.tsx +512 -512
  106. package/src/forms/DatePicker.tsx +784 -784
  107. package/src/forms/FileUpload.tsx +266 -266
  108. package/src/forms/FormField.tsx +115 -115
  109. package/src/forms/Input.tsx +151 -151
  110. package/src/forms/NumberInput.tsx +262 -262
  111. package/src/forms/PasswordInput.tsx +35 -35
  112. package/src/forms/PinInput.tsx +139 -139
  113. package/src/forms/Radio.tsx +142 -142
  114. package/src/forms/RangeSlider.tsx +237 -237
  115. package/src/forms/SearchInput.tsx +34 -34
  116. package/src/forms/Select.tsx +103 -103
  117. package/src/forms/Slider.tsx +85 -85
  118. package/src/forms/Textarea.tsx +114 -114
  119. package/src/forms/tokens.css +6 -6
  120. package/src/glass/GlassButton.tsx +59 -59
  121. package/src/glass/GlassCard.tsx +71 -71
  122. package/src/glass/index.ts +3 -3
  123. package/src/glass/tokens.css +81 -81
  124. package/src/index.ts +26 -26
  125. package/src/layout/AspectRatio.tsx +39 -39
  126. package/src/layout/Container.tsx +53 -53
  127. package/src/layout/Divider.tsx +61 -61
  128. package/src/layout/Flex.tsx +79 -79
  129. package/src/layout/Grid.tsx +124 -124
  130. package/src/layout/PageLayout.tsx +59 -59
  131. package/src/layout/Section.tsx +47 -47
  132. package/src/layout/Spacer.tsx +30 -30
  133. package/src/layout/Stack.tsx +88 -88
  134. package/src/layout/tokens.css +8 -8
  135. package/src/navigation/Breadcrumb.tsx +73 -73
  136. package/src/navigation/CommandPalette.tsx +142 -142
  137. package/src/navigation/DropdownMenu.tsx +244 -244
  138. package/src/navigation/Pagination.tsx +114 -114
  139. package/src/navigation/Stepper.tsx +93 -93
  140. package/src/navigation/Tabs.tsx +265 -265
  141. package/src/navigation/index.ts +6 -6
  142. package/src/navigation/tokens.css +4 -4
  143. package/src/overlays/AlertDialog.tsx +63 -63
  144. package/src/overlays/BottomSheet.tsx +143 -143
  145. package/src/overlays/ContextMenu.tsx +161 -161
  146. package/src/overlays/Drawer.tsx +160 -160
  147. package/src/overlays/FocusTrap.tsx +122 -122
  148. package/src/overlays/Modal.tsx +296 -296
  149. package/src/overlays/Popover.tsx +156 -156
  150. package/src/overlays/Portal.tsx +23 -23
  151. package/src/overlays/Toast.tsx +175 -175
  152. package/src/overlays/Tooltip.tsx +125 -125
  153. package/src/overlays/tokens.css +8 -8
  154. package/src/registry/components.ts +1368 -1368
  155. package/src/styles/components.css +39 -39
  156. package/src/styles/index.css +5 -5
  157. package/src/styles/reset.css +45 -45
  158. package/src/styles/tokens.css +11 -11
  159. package/src/typography/Blockquote.tsx +32 -32
  160. package/src/typography/Code.tsx +89 -89
  161. package/src/typography/Heading.tsx +56 -56
  162. package/src/typography/Kbd.tsx +25 -25
  163. package/src/typography/Label.tsx +47 -47
  164. package/src/typography/Link.tsx +56 -56
  165. package/src/typography/Text.tsx +62 -62
  166. package/src/typography/index.ts +7 -7
  167. package/src/typography/tokens.css +5 -5
  168. package/src/utils/cn.ts +6 -6
  169. package/src/utils/scroll-lock.ts +19 -19
  170. package/src/utils/useControllableState.ts +54 -54
  171. package/ui-contract.json +1 -1
  172. package/dist/chunk-2YJBW4E2.js.map +0 -1
  173. package/dist/chunk-4CRFNALF.js.map +0 -1
  174. package/dist/chunk-4MS6ZHFQ.js.map +0 -1
  175. package/dist/chunk-77KTYOMQ.js.map +0 -1
  176. package/dist/chunk-ADO3SVUA.js.map +0 -1
  177. package/dist/chunk-D7FB3TCA.js.map +0 -1
  178. package/dist/chunk-DSEL7ELZ.js.map +0 -1
  179. package/dist/chunk-EXFR2BFJ.js.map +0 -1
  180. package/dist/chunk-GXWB6BSY.js.map +0 -1
  181. package/dist/chunk-I2FQBMVW.js.map +0 -1
  182. package/dist/chunk-I5O2WCGF.js.map +0 -1
  183. package/dist/chunk-ME4XILG5.js.map +0 -1
  184. package/dist/chunk-PC5SZFZC.js.map +0 -1
  185. package/dist/chunk-S6XJMYME.js.map +0 -1
  186. package/dist/chunk-V52JOSDF.js.map +0 -1
  187. package/dist/chunk-W5WNWTQC.js.map +0 -1
  188. package/dist/chunk-YJ64RI7J.js.map +0 -1
  189. package/dist/chunk-YOCGBORV.js.map +0 -1
  190. package/dist/chunk-ZEDXARXW.js.map +0 -1
@@ -1,156 +1,156 @@
1
- import React, { useRef, useEffect, useState, useCallback } from 'react';
2
- import { Portal } from './Portal';
3
- import { cn } from '../utils/cn';
4
- import { useControllableState } from '../utils/useControllableState';
5
-
6
- export interface PopoverProps {
7
- trigger: React.ReactNode;
8
- children: React.ReactNode;
9
- open?: boolean;
10
- isOpen?: boolean; // Legacy alias
11
- defaultOpen?: boolean;
12
- onOpenChange?: (open: boolean) => void;
13
- onClose?: () => void;
14
- placement?: 'top' | 'bottom' | 'left' | 'right';
15
- className?: string;
16
- }
17
-
18
- export const Popover = ({
19
- trigger,
20
- children,
21
- open: controlledOpen,
22
- isOpen: legacyIsOpen,
23
- defaultOpen = false,
24
- onOpenChange,
25
- onClose,
26
- placement = 'bottom',
27
- className,
28
- }: PopoverProps) => {
29
- // Resolve controlled open state with legacy support
30
- const activeControlled = controlledOpen !== undefined ? controlledOpen : legacyIsOpen;
31
-
32
- const handleStateChange = useCallback(
33
- (nextOpen: boolean) => {
34
- onOpenChange?.(nextOpen);
35
- if (!nextOpen) {
36
- onClose?.();
37
- }
38
- },
39
- [onOpenChange, onClose]
40
- );
41
-
42
- const [isOpen, setIsOpen] = useControllableState<boolean>({
43
- value: activeControlled,
44
- defaultValue: defaultOpen,
45
- onChange: handleStateChange,
46
- name: 'Popover',
47
- });
48
-
49
- const [coords, setCoords] = useState<{ top: number; left: number }>({ top: 0, left: 0 });
50
- const triggerRef = useRef<HTMLDivElement | null>(null);
51
- const popoverRef = useRef<HTMLDivElement | null>(null);
52
-
53
- const calculatePosition = useCallback(() => {
54
- if (triggerRef.current) {
55
- const rect = triggerRef.current.getBoundingClientRect();
56
- let top = 0;
57
- let left = 0;
58
-
59
- if (placement === 'bottom') {
60
- top = rect.bottom + window.scrollY + 8;
61
- left = rect.left + window.scrollX;
62
- } else if (placement === 'top') {
63
- top = rect.top + window.scrollY - 8;
64
- left = rect.left + window.scrollX;
65
- } else if (placement === 'right') {
66
- top = rect.top + window.scrollY;
67
- left = rect.right + window.scrollX + 8;
68
- } else if (placement === 'left') {
69
- top = rect.top + window.scrollY;
70
- left = rect.left + window.scrollX - 8;
71
- }
72
-
73
- setCoords({ top, left });
74
- }
75
- }, [placement]);
76
-
77
- const handleToggle = () => {
78
- if (!isOpen) {
79
- calculatePosition();
80
- }
81
- setIsOpen(!isOpen);
82
- };
83
-
84
- useEffect(() => {
85
- if (isOpen) {
86
- calculatePosition();
87
- }
88
- }, [isOpen, calculatePosition]);
89
-
90
- useEffect(() => {
91
- if (!isOpen) return;
92
-
93
- const handleOutside = (e: MouseEvent) => {
94
- if (
95
- popoverRef.current &&
96
- !popoverRef.current.contains(e.target as Node) &&
97
- triggerRef.current &&
98
- !triggerRef.current.contains(e.target as Node)
99
- ) {
100
- setIsOpen(false);
101
- }
102
- };
103
-
104
- const handleKeyDown = (e: KeyboardEvent) => {
105
- if (e.key === 'Escape') {
106
- setIsOpen(false);
107
- }
108
- };
109
-
110
- document.addEventListener('mousedown', handleOutside);
111
- document.addEventListener('keydown', handleKeyDown);
112
- window.addEventListener('resize', calculatePosition);
113
- window.addEventListener('scroll', calculatePosition);
114
-
115
- return () => {
116
- document.removeEventListener('mousedown', handleOutside);
117
- document.removeEventListener('keydown', handleKeyDown);
118
- window.removeEventListener('resize', calculatePosition);
119
- window.removeEventListener('scroll', calculatePosition);
120
- };
121
- }, [isOpen, setIsOpen, calculatePosition]);
122
-
123
- return (
124
- <>
125
- <div
126
- ref={triggerRef}
127
- onClick={handleToggle}
128
- className="w-full inline-block cursor-pointer"
129
- aria-expanded={isOpen}
130
- >
131
- {trigger}
132
- </div>
133
-
134
- {isOpen && (
135
- <Portal>
136
- <div
137
- ref={popoverRef}
138
- style={{
139
- top: `${coords.top}px`,
140
- left: `${coords.left}px`,
141
- position: 'absolute',
142
- }}
143
- className={cn(
144
- 'z-50 min-w-[12rem] rounded-[var(--ui-radius-lg)] bg-[var(--ui-bg-surface)] border border-[var(--ui-border-default)] p-4 text-[var(--ui-text-primary)] shadow-xl animate-in fade-in zoom-in-95',
145
- placement === 'top' && '-translate-y-full',
146
- placement === 'left' && '-translate-x-full',
147
- className
148
- )}
149
- >
150
- {children}
151
- </div>
152
- </Portal>
153
- )}
154
- </>
155
- );
156
- };
1
+ import React, { useRef, useEffect, useState, useCallback } from 'react';
2
+ import { Portal } from './Portal';
3
+ import { cn } from '../utils/cn';
4
+ import { useControllableState } from '../utils/useControllableState';
5
+
6
+ export interface PopoverProps {
7
+ trigger: React.ReactNode;
8
+ children: React.ReactNode;
9
+ open?: boolean;
10
+ isOpen?: boolean; // Legacy alias
11
+ defaultOpen?: boolean;
12
+ onOpenChange?: (open: boolean) => void;
13
+ onClose?: () => void;
14
+ placement?: 'top' | 'bottom' | 'left' | 'right';
15
+ className?: string;
16
+ }
17
+
18
+ export const Popover = ({
19
+ trigger,
20
+ children,
21
+ open: controlledOpen,
22
+ isOpen: legacyIsOpen,
23
+ defaultOpen = false,
24
+ onOpenChange,
25
+ onClose,
26
+ placement = 'bottom',
27
+ className,
28
+ }: PopoverProps) => {
29
+ // Resolve controlled open state with legacy support
30
+ const activeControlled = controlledOpen !== undefined ? controlledOpen : legacyIsOpen;
31
+
32
+ const handleStateChange = useCallback(
33
+ (nextOpen: boolean) => {
34
+ onOpenChange?.(nextOpen);
35
+ if (!nextOpen) {
36
+ onClose?.();
37
+ }
38
+ },
39
+ [onOpenChange, onClose]
40
+ );
41
+
42
+ const [isOpen, setIsOpen] = useControllableState<boolean>({
43
+ value: activeControlled,
44
+ defaultValue: defaultOpen,
45
+ onChange: handleStateChange,
46
+ name: 'Popover',
47
+ });
48
+
49
+ const [coords, setCoords] = useState<{ top: number; left: number }>({ top: 0, left: 0 });
50
+ const triggerRef = useRef<HTMLDivElement | null>(null);
51
+ const popoverRef = useRef<HTMLDivElement | null>(null);
52
+
53
+ const calculatePosition = useCallback(() => {
54
+ if (triggerRef.current) {
55
+ const rect = triggerRef.current.getBoundingClientRect();
56
+ let top = 0;
57
+ let left = 0;
58
+
59
+ if (placement === 'bottom') {
60
+ top = rect.bottom + window.scrollY + 8;
61
+ left = rect.left + window.scrollX;
62
+ } else if (placement === 'top') {
63
+ top = rect.top + window.scrollY - 8;
64
+ left = rect.left + window.scrollX;
65
+ } else if (placement === 'right') {
66
+ top = rect.top + window.scrollY;
67
+ left = rect.right + window.scrollX + 8;
68
+ } else if (placement === 'left') {
69
+ top = rect.top + window.scrollY;
70
+ left = rect.left + window.scrollX - 8;
71
+ }
72
+
73
+ setCoords({ top, left });
74
+ }
75
+ }, [placement]);
76
+
77
+ const handleToggle = () => {
78
+ if (!isOpen) {
79
+ calculatePosition();
80
+ }
81
+ setIsOpen(!isOpen);
82
+ };
83
+
84
+ useEffect(() => {
85
+ if (isOpen) {
86
+ calculatePosition();
87
+ }
88
+ }, [isOpen, calculatePosition]);
89
+
90
+ useEffect(() => {
91
+ if (!isOpen) return;
92
+
93
+ const handleOutside = (e: MouseEvent) => {
94
+ if (
95
+ popoverRef.current &&
96
+ !popoverRef.current.contains(e.target as Node) &&
97
+ triggerRef.current &&
98
+ !triggerRef.current.contains(e.target as Node)
99
+ ) {
100
+ setIsOpen(false);
101
+ }
102
+ };
103
+
104
+ const handleKeyDown = (e: KeyboardEvent) => {
105
+ if (e.key === 'Escape') {
106
+ setIsOpen(false);
107
+ }
108
+ };
109
+
110
+ document.addEventListener('mousedown', handleOutside);
111
+ document.addEventListener('keydown', handleKeyDown);
112
+ window.addEventListener('resize', calculatePosition);
113
+ window.addEventListener('scroll', calculatePosition);
114
+
115
+ return () => {
116
+ document.removeEventListener('mousedown', handleOutside);
117
+ document.removeEventListener('keydown', handleKeyDown);
118
+ window.removeEventListener('resize', calculatePosition);
119
+ window.removeEventListener('scroll', calculatePosition);
120
+ };
121
+ }, [isOpen, setIsOpen, calculatePosition]);
122
+
123
+ return (
124
+ <>
125
+ <div
126
+ ref={triggerRef}
127
+ onClick={handleToggle}
128
+ className="w-full inline-block cursor-pointer"
129
+ aria-expanded={isOpen}
130
+ >
131
+ {trigger}
132
+ </div>
133
+
134
+ {isOpen && (
135
+ <Portal>
136
+ <div
137
+ ref={popoverRef}
138
+ style={{
139
+ top: `${coords.top}px`,
140
+ left: `${coords.left}px`,
141
+ position: 'absolute',
142
+ }}
143
+ className={cn(
144
+ 'z-50 min-w-[12rem] rounded-[var(--ui-radius-lg)] bg-[var(--ui-bg-surface)] border border-[var(--ui-border-default)] p-4 text-[var(--ui-text-primary)] shadow-xl animate-in fade-in zoom-in-95',
145
+ placement === 'top' && '-translate-y-full',
146
+ placement === 'left' && '-translate-x-full',
147
+ className
148
+ )}
149
+ >
150
+ {children}
151
+ </div>
152
+ </Portal>
153
+ )}
154
+ </>
155
+ );
156
+ };
@@ -1,23 +1,23 @@
1
- import React, { useEffect, useState } from 'react';
2
- import { createPortal } from 'react-dom';
3
-
4
- export interface PortalProps {
5
- children: React.ReactNode;
6
- container?: Element | null;
7
- }
8
-
9
- export function Portal({ children, container }: PortalProps) {
10
- const [mounted, setMounted] = useState(false);
11
-
12
- useEffect(() => {
13
- setMounted(true);
14
- return () => setMounted(false);
15
- }, []);
16
-
17
- if (!mounted) return null;
18
-
19
- const target = container || (typeof document !== 'undefined' ? document.body : null);
20
- if (!target) return null;
21
-
22
- return createPortal(children, target);
23
- }
1
+ import React, { useEffect, useState } from 'react';
2
+ import { createPortal } from 'react-dom';
3
+
4
+ export interface PortalProps {
5
+ children: React.ReactNode;
6
+ container?: Element | null;
7
+ }
8
+
9
+ export function Portal({ children, container }: PortalProps) {
10
+ const [mounted, setMounted] = useState(false);
11
+
12
+ useEffect(() => {
13
+ setMounted(true);
14
+ return () => setMounted(false);
15
+ }, []);
16
+
17
+ if (!mounted) return null;
18
+
19
+ const target = container || (typeof document !== 'undefined' ? document.body : null);
20
+ if (!target) return null;
21
+
22
+ return createPortal(children, target);
23
+ }