gladvn 0.2.35 → 0.2.37

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 (134) hide show
  1. package/README.md +1 -1
  2. package/package.json +2 -1
  3. package/scripts/strip-comments.ts +79 -0
  4. package/src/blocks/auth-form.tsx +34 -30
  5. package/src/blocks/auth-recovery.tsx +9 -13
  6. package/src/blocks/auth-split.tsx +46 -36
  7. package/src/blocks/settings.tsx +84 -117
  8. package/src/components/macro/alert-preset.tsx +3 -1
  9. package/src/components/macro/carousel-preset.tsx +0 -1
  10. package/src/components/macro/checkbox-preset.test.tsx +2 -2
  11. package/src/components/macro/checkbox-preset.tsx +1 -2
  12. package/src/components/macro/command-preset.tsx +1 -1
  13. package/src/components/macro/date-picker.tsx +5 -18
  14. package/src/components/macro/dialog-preset.tsx +5 -3
  15. package/src/components/macro/input-preset.tsx +0 -1
  16. package/src/components/macro/select-preset.tsx +1 -1
  17. package/src/components/macro/switch-preset.test.tsx +1 -1
  18. package/src/components/micro/accordion.test.tsx +6 -12
  19. package/src/components/micro/alert-dialog.tsx +0 -2
  20. package/src/components/micro/alert.test.tsx +5 -8
  21. package/src/components/micro/alert.tsx +0 -2
  22. package/src/components/micro/badge.test.tsx +5 -7
  23. package/src/components/micro/badge.tsx +1 -3
  24. package/src/components/micro/button.test.tsx +19 -26
  25. package/src/components/micro/button.tsx +3 -11
  26. package/src/components/micro/calendar.tsx +12 -13
  27. package/src/components/micro/card.test.tsx +3 -5
  28. package/src/components/micro/carousel.tsx +0 -1
  29. package/src/components/micro/chart.tsx +3 -2
  30. package/src/components/micro/checkbox.test.tsx +9 -18
  31. package/src/components/micro/combobox.tsx +1 -3
  32. package/src/components/micro/command.tsx +23 -11
  33. package/src/components/micro/context-menu.tsx +0 -2
  34. package/src/components/micro/dialog.test.tsx +6 -14
  35. package/src/components/micro/dialog.tsx +0 -2
  36. package/src/components/micro/drawer.tsx +0 -3
  37. package/src/components/micro/dropdown-menu.test.tsx +8 -14
  38. package/src/components/micro/dropdown-menu.tsx +0 -2
  39. package/src/components/micro/field.tsx +3 -3
  40. package/src/components/micro/hover-card.tsx +0 -2
  41. package/src/components/micro/input-group.tsx +9 -42
  42. package/src/components/micro/input.test.tsx +15 -24
  43. package/src/components/micro/navigation-menu.tsx +0 -2
  44. package/src/components/micro/popover.test.tsx +5 -9
  45. package/src/components/micro/progress.tsx +5 -1
  46. package/src/components/micro/select.test.tsx +5 -18
  47. package/src/components/micro/select.tsx +3 -5
  48. package/src/components/micro/separator.tsx +1 -1
  49. package/src/components/micro/sheet.test.tsx +6 -14
  50. package/src/components/micro/sheet.tsx +0 -2
  51. package/src/components/micro/sidebar.tsx +5 -6
  52. package/src/components/micro/sonner.tsx +0 -1
  53. package/src/components/micro/switch.test.tsx +9 -18
  54. package/src/components/micro/switch.tsx +1 -1
  55. package/src/components/micro/tabs.test.tsx +5 -11
  56. package/src/components/micro/theme-provider.tsx +0 -12
  57. package/src/components/micro/toggle-group.tsx +1 -12
  58. package/src/components/micro/tooltip.test.tsx +7 -12
  59. package/src/dev/App.tsx +6 -6
  60. package/src/dev/components/BlockViewer.tsx +15 -4
  61. package/src/dev/components/Container.tsx +20 -0
  62. package/src/dev/components/GladvnLogo.tsx +3 -9
  63. package/src/dev/components/code-highlighter.tsx +5 -3
  64. package/src/dev/components/showcase.tsx +116 -168
  65. package/src/dev/sections/buttons.tsx +4 -9
  66. package/src/dev/sections/display.tsx +68 -72
  67. package/src/dev/sections/feedback.tsx +304 -314
  68. package/src/dev/sections/forms.tsx +10 -14
  69. package/src/dev/sections/interactive.tsx +32 -32
  70. package/src/dev/sections/overview.tsx +2 -10
  71. package/src/dev/showcase/accordion.tsx +512 -554
  72. package/src/dev/showcase/alert-dialog.tsx +525 -565
  73. package/src/dev/showcase/alert.tsx +352 -402
  74. package/src/dev/showcase/aspect-ratio.tsx +167 -193
  75. package/src/dev/showcase/avatar.tsx +231 -269
  76. package/src/dev/showcase/badge.tsx +92 -140
  77. package/src/dev/showcase/breadcrumb.tsx +113 -135
  78. package/src/dev/showcase/button.tsx +140 -173
  79. package/src/dev/showcase/calendar.tsx +187 -237
  80. package/src/dev/showcase/card.tsx +229 -261
  81. package/src/dev/showcase/carousel.tsx +288 -315
  82. package/src/dev/showcase/chart.tsx +44 -50
  83. package/src/dev/showcase/checkbox.tsx +286 -330
  84. package/src/dev/showcase/collapsible.tsx +143 -160
  85. package/src/dev/showcase/combobox.tsx +327 -369
  86. package/src/dev/showcase/command.tsx +106 -120
  87. package/src/dev/showcase/context-menu.tsx +93 -107
  88. package/src/dev/showcase/dialog.tsx +585 -630
  89. package/src/dev/showcase/drawer.tsx +176 -196
  90. package/src/dev/showcase/dropdown-menu.tsx +183 -201
  91. package/src/dev/showcase/empty.tsx +232 -274
  92. package/src/dev/showcase/hover-card.tsx +153 -180
  93. package/src/dev/showcase/input-group.tsx +221 -253
  94. package/src/dev/showcase/input-otp.tsx +204 -250
  95. package/src/dev/showcase/input.tsx +246 -325
  96. package/src/dev/showcase/item.tsx +134 -152
  97. package/src/dev/showcase/kbd.tsx +149 -172
  98. package/src/dev/showcase/label.tsx +50 -68
  99. package/src/dev/showcase/menubar.tsx +194 -213
  100. package/src/dev/showcase/navigation-menu.tsx +119 -133
  101. package/src/dev/showcase/overview-components/ArchitectureSection.tsx +126 -0
  102. package/src/dev/showcase/overview-components/CodeBlock.tsx +50 -0
  103. package/src/dev/showcase/overview-components/ColorPaletteSection.tsx +44 -0
  104. package/src/dev/showcase/overview-components/DashboardPreview.tsx +159 -0
  105. package/src/dev/showcase/overview-components/FeatureGridSection.tsx +535 -0
  106. package/src/dev/showcase/overview-components/FooterCTA.tsx +137 -0
  107. package/src/dev/showcase/overview-components/HeroSection.tsx +65 -0
  108. package/src/dev/showcase/overview-components/PrinciplesSection.tsx +128 -0
  109. package/src/dev/showcase/overview.tsx +21 -1163
  110. package/src/dev/showcase/pagination.tsx +144 -190
  111. package/src/dev/showcase/popover.tsx +181 -196
  112. package/src/dev/showcase/progress.tsx +141 -189
  113. package/src/dev/showcase/radio-group.tsx +272 -332
  114. package/src/dev/showcase/resizable.tsx +119 -141
  115. package/src/dev/showcase/scroll-area.tsx +21 -36
  116. package/src/dev/showcase/select.tsx +262 -322
  117. package/src/dev/showcase/separator.tsx +75 -73
  118. package/src/dev/showcase/sheet.tsx +333 -361
  119. package/src/dev/showcase/sidebar.tsx +53 -59
  120. package/src/dev/showcase/skeleton.tsx +93 -112
  121. package/src/dev/showcase/slider.tsx +244 -296
  122. package/src/dev/showcase/sonner.tsx +251 -258
  123. package/src/dev/showcase/spinner.tsx +46 -67
  124. package/src/dev/showcase/switch.tsx +298 -362
  125. package/src/dev/showcase/table.tsx +76 -80
  126. package/src/dev/showcase/tabs.tsx +302 -355
  127. package/src/dev/showcase/textarea.tsx +240 -293
  128. package/src/dev/showcase/toast.tsx +48 -54
  129. package/src/dev/showcase/toggle-group.tsx +104 -129
  130. package/src/dev/showcase/toggle.tsx +82 -102
  131. package/src/dev/showcase/tooltip.tsx +173 -192
  132. package/src/dev/showcase/why.tsx +6 -12
  133. package/src/hooks/use-mobile.test.ts +3 -2
  134. package/src/lib/utils.test.ts +5 -4
@@ -14,21 +14,19 @@ import { COLORS, COLOR_INFO } from "../../dev/data";
14
14
  import { type Size } from "../../lib/types";
15
15
  import { cn } from "../../lib/utils";
16
16
 
17
- /* ─────────────────────────────────────────────────────────────────
18
- SectionHeader – page‐level title bar
19
- Clean typography + subtle separator. No blobs, no gradients.
20
- ────────────────────────────────────────────────────────────── */
21
17
  export function SectionHeader({
22
18
  title,
23
19
  description,
24
20
  children,
21
+ className,
25
22
  }: {
26
23
  title: string;
27
24
  description?: string;
28
25
  children?: React.ReactNode;
26
+ className?: string;
29
27
  }) {
30
28
  return (
31
- <div className="mb-8 flex flex-col gap-1">
29
+ <div className={cn("flex flex-col gap-1", className)}>
32
30
  <div className="flex items-end justify-between gap-4">
33
31
  <h2 className="text-3xl font-bold tracking-tight bg-gradient-to-br from-foreground to-foreground/60 bg-clip-text text-transparent">
34
32
  {title}
@@ -42,16 +40,13 @@ export function SectionHeader({
42
40
  );
43
41
  }
44
42
 
45
- /* ─────────────────────────────────────────────────────────────────
46
- ShowcaseDocs – Documentation block
47
- ────────────────────────────────────────────────────────────── */
48
- export function ShowcaseDocs({ children }: { children: React.ReactNode }) {
43
+ export function ShowcaseDocs({ children, className }: { children: React.ReactNode; className?: string }) {
49
44
  return (
50
- <div className="mb-12 overflow-hidden rounded-2xl border border-border border-amber-500/20 bg-amber-500/5 shadow-sm">
45
+ <div className={cn("overflow-hidden rounded-2xl border border-amber-500/20 bg-amber-500/5 shadow-sm", className)}>
51
46
  <div className="flex items-center gap-2 border-b border-b-border border-amber-500/20 bg-amber-500/10 px-6 py-3.5">
52
47
  <BookOpenIcon className="size-4 text-amber-700 dark:text-amber-500" />
53
48
  <h3 className="text-sm font-semibold text-amber-900 dark:text-amber-200">
54
- Usage Guidelines
49
+ Hướng dẫn sử dụng
55
50
  </h3>
56
51
  </div>
57
52
  <div className="px-6 py-5 text-[15px] leading-relaxed text-muted-foreground">
@@ -61,78 +56,59 @@ export function ShowcaseDocs({ children }: { children: React.ReactNode }) {
61
56
  );
62
57
  }
63
58
 
64
- export function DocsH3({ children }: { children: React.ReactNode }) {
59
+ export function DocsH3({ children, className }: { children: React.ReactNode; className?: string }) {
65
60
  return (
66
- <h3 className="mt-8 first:mt-1 mb-3 text-base font-semibold text-foreground">
61
+ <h3 className={cn("text-base font-semibold text-foreground", className)}>
67
62
  {children}
68
63
  </h3>
69
64
  );
70
65
  }
71
66
 
72
- export function DocsP({ children }: { children: React.ReactNode }) {
73
- return <p className="mb-6 last:mb-1">{children}</p>;
67
+ export function DocsP({ children, className }: { children: React.ReactNode; className?: string }) {
68
+ return <p className={cn(className)}>{children}</p>;
74
69
  }
75
70
 
76
- export function DocsUl({ children }: { children: React.ReactNode }) {
77
- return <ul className="mb-6 list-inside list-disc space-y-2">{children}</ul>;
71
+ export function DocsUl({ children, className }: { children: React.ReactNode; className?: string }) {
72
+ return <ul className={cn("list-inside list-disc space-y-2", className)}>{children}</ul>;
78
73
  }
79
74
 
80
- export function DocsLi({ children }: { children: React.ReactNode }) {
81
- return <li>{children}</li>;
75
+ export function DocsLi({ children, className }: { children: React.ReactNode; className?: string }) {
76
+ return <li className={className}>{children}</li>;
82
77
  }
83
78
 
84
- export function DocsCode({ children }: { children: React.ReactNode }) {
79
+ export function DocsCode({ children, className }: { children: React.ReactNode; className?: string }) {
85
80
  return (
86
- <code className="rounded-md border border-border border-amber-500/20 bg-amber-500/10 px-1.5 py-0.5 font-mono text-[13px] text-amber-900 dark:text-amber-200">
81
+ <code className={cn("rounded-md border border-amber-500/20 bg-amber-500/10 px-1.5 py-0.5 font-mono text-[13px] text-amber-900 dark:text-amber-200", className)}>
87
82
  {children}
88
83
  </code>
89
84
  );
90
85
  }
91
86
 
92
- /* ─────────────────────────────────────────────────────────────────
93
- ExampleSection – a single demo block (replaces ComponentPreview)
94
-
95
- Inspired by shadcn docs:
96
- ┌─ label ──────────────────────────────────────────────┐
97
- │ │
98
- │ live component preview │
99
- │ │
100
- └───────────────────────────────────────────────────────┘
101
-
102
- • Label sits *above* the card as a small muted caption
103
- • The card itself is a clean bordered rectangle
104
- • Component sits centered with generous padding
105
- • NO icon badges, NO dot‑grid, NO gradients inside
106
- ────────────────────────────────────────────────────────── */
107
- export function ExampleSection({
108
- label,
87
+ export function ShowcaseExample({
88
+ title,
109
89
  description,
110
- children,
90
+ preview,
111
91
  className,
112
- codeString: customCodeString,
113
- hideCode = false,
92
+ code,
114
93
  }: {
115
- label?: string;
94
+ title?: string;
116
95
  description?: string;
117
- children: React.ReactNode;
96
+ preview: React.ReactNode;
118
97
  className?: string;
119
- fullWidth?: boolean;
120
- codeString?: string;
121
- hideCode?: boolean;
98
+ code?: string;
122
99
  }) {
123
100
  const [copied, setCopied] = useState(false);
124
101
  const [activeTab, setActiveTab] = useState("preview");
125
102
 
126
- const shouldHideCode = hideCode || label?.includes("Use Case Comparison");
127
- const codeString = customCodeString || "// Please provide a codeString prop";
103
+ const hasCode = code !== undefined;
128
104
 
129
105
  const handleTabChange = (value: string) => {
130
106
  setActiveTab(value);
131
107
  };
132
108
 
133
109
  const copyToClipboard = () => {
134
- if (codeString) {
135
- navigator.clipboard.writeText(codeString);
110
+ if (code) {
111
+ navigator.clipboard.writeText(code);
136
112
  setCopied(true);
137
113
  setTimeout(() => setCopied(false), 2000);
138
114
  }
@@ -146,8 +122,8 @@ export function ExampleSection({
146
122
  >
147
123
  <div className="flex items-start justify-between gap-4">
148
124
  <div className="flex flex-col gap-0.5">
149
- {label && (
150
- <h3 className="text-sm font-semibold text-foreground">{label}</h3>
125
+ {title && (
126
+ <h3 className="text-sm font-semibold text-foreground">{title}</h3>
151
127
  )}
152
128
  {description && (
153
129
  <p className="text-[13px] text-muted-foreground leading-relaxed pr-4">
@@ -155,7 +131,7 @@ export function ExampleSection({
155
131
  </p>
156
132
  )}
157
133
  </div>
158
- {!shouldHideCode && (
134
+ {hasCode && (
159
135
  <TabsList className="h-8 shrink-0">
160
136
  <TabsTrigger value="preview" className="text-xs px-3 py-1">
161
137
  Preview
@@ -171,20 +147,19 @@ export function ExampleSection({
171
147
  <TabsContent value="preview" className="mt-0 outline-none">
172
148
  <div
173
149
  className={cn(
174
- "relative rounded-2xl border border-border/80 bg-background/50 backdrop-blur-sm p-8 shadow-sm transition-all duration-300 hover:shadow-md hover:border-ring/30",
175
- "min-h-[120px]",
150
+ "relative min-h-[120px] w-full rounded-2xl bg-muted/30 dark:bg-muted/20 p-4 sm:p-6",
176
151
  className,
177
152
  )}
178
153
  >
179
- <div className="absolute inset-0 -z-10 opacity-[0.03] dark:opacity-[0.05] [background-size:24px_24px] [background-image:radial-gradient(circle_at_center,var(--color-foreground)_1.5px,transparent_1.5px)]" />
180
- <div className="relative z-10 w-full">{children}</div>
154
+
155
+ <div className="relative z-10 w-full">{preview}</div>
181
156
  </div>
182
157
  </TabsContent>
183
158
 
184
- {!shouldHideCode && (
159
+ {hasCode && (
185
160
  <TabsContent value="code" className="mt-0 outline-none">
186
- <div className="relative rounded-2xl border border-border/80 bg-muted/50 p-4 text-foreground shadow-sm overflow-clip group/code">
187
- <CodeHighlighter code={codeString || "// Loading..."} />
161
+ <div className="relative rounded-2xl bg-muted/30 dark:bg-muted/20 p-4 text-foreground overflow-clip group/code">
162
+ <CodeHighlighter code={code} />
188
163
  <Button
189
164
  size="sm"
190
165
  iconOnly
@@ -206,9 +181,6 @@ export function ExampleSection({
206
181
  );
207
182
  }
208
183
 
209
- /* ─────────────────────────────────────────────────────────────────
210
- ExampleGrid – responsive columns for multiple examples
211
- ────────────────────────────────────────────────────────── */
212
184
  export function ExampleGrid({
213
185
  children,
214
186
  className,
@@ -221,50 +193,7 @@ export function ExampleGrid({
221
193
  );
222
194
  }
223
195
 
224
- /* ─────────────────────────────────────────────────────────────────
225
- Legacy compat – keep old names working during migration
226
- ────────────────────────────────────────────────────────── */
227
- export const ComponentGrid = ExampleGrid;
228
- export function ComponentPreview({
229
- title,
230
- description,
231
- children,
232
- className,
233
- }: {
234
- title?: string;
235
- description?: string;
236
- icon?: React.ElementType;
237
- color?: string;
238
- children: React.ReactNode;
239
- className?: string;
240
- }) {
241
- return (
242
- <ExampleSection
243
- label={title}
244
- description={description}
245
- className={className}
246
- >
247
- {children}
248
- </ExampleSection>
249
- );
250
- }
251
-
252
- /* ─────────────────────────────────────────────────────────────────
253
- ShowcaseBlock – legacy wrapper (still used by some pages)
254
- ────────────────────────────────────────────────────────── */
255
- export function ShowcaseBlock({
256
- title,
257
- children,
258
- }: {
259
- title: string;
260
- children: React.ReactNode;
261
- }) {
262
- return <ExampleSection label={title}>{children}</ExampleSection>;
263
- }
264
196
 
265
- /* ─────────────────────────────────────────────────────────────────
266
- ColorSwatch – design‑token preview
267
- ────────────────────────────────────────────────────────── */
268
197
  const bgColorMap: Record<(typeof COLORS)[number], string> = {
269
198
  primary: "bg-primary",
270
199
  secondary: "bg-secondary",
@@ -275,10 +204,10 @@ const bgColorMap: Record<(typeof COLORS)[number], string> = {
275
204
  tertiary: "bg-tertiary",
276
205
  };
277
206
 
278
- export function ColorSwatch({ color }: { color: (typeof COLORS)[number] }) {
207
+ export function ColorSwatch({ color, className }: { color: (typeof COLORS)[number]; className?: string }) {
279
208
  const info = COLOR_INFO[color];
280
209
  return (
281
- <div className="flex flex-col items-center gap-3 p-3.5 sm:p-5 rounded-[2rem] bg-background/40 border border-border/50 shadow-sm backdrop-blur-xl transition-all duration-500 hover:-translate-y-2 hover:shadow-2xl hover:shadow-primary/20 hover:border-primary/30 group">
210
+ <div className={cn("flex flex-col items-center gap-3 p-3.5 sm:p-5 rounded-[2rem] bg-background/40 border border-border/50 shadow-sm backdrop-blur-xl transition-all duration-500 hover:-translate-y-2 hover:shadow-2xl hover:shadow-primary/20 hover:border-primary/30 group", className)}>
282
211
  <div
283
212
  className={cn(
284
213
  "h-12 w-12 sm:h-16 sm:w-16 rounded-2xl border border-border shadow-inner transition-all duration-500 group-hover:scale-110 group-hover:rotate-3 group-hover:rounded-[2rem]",
@@ -294,36 +223,50 @@ export function ColorSwatch({ color }: { color: (typeof COLORS)[number] }) {
294
223
  );
295
224
  }
296
225
 
297
- /* ─────────────────────────────────────────────────────────────────
298
- Showcase – The unified showcase layout wrapper
299
- ────────────────────────────────────────────────────────────── */
300
-
301
- export interface ShowcaseTab {
302
- label: string;
303
- content: React.ReactNode;
304
- }
305
-
306
226
  export interface ShowcaseProps {
307
227
  title: React.ReactNode;
308
228
  description?: React.ReactNode;
309
- generalConcept?: React.ReactNode;
229
+ guideline?: React.ReactNode;
310
230
  actions?: React.ReactNode;
311
- tabs: ShowcaseTab[];
231
+ micro?: {
232
+ content: React.ReactNode;
233
+ useCaseComparison?: React.ReactNode;
234
+ };
235
+ macro?: {
236
+ content: React.ReactNode;
237
+ useCaseComparison?: React.ReactNode;
238
+ };
239
+ className?: string;
312
240
  }
313
241
 
314
242
  export function Showcase({
315
243
  title,
316
244
  description,
317
- generalConcept,
245
+ guideline,
318
246
  actions,
319
- tabs,
247
+ micro,
248
+ macro,
249
+ className,
320
250
  }: ShowcaseProps) {
321
- const hasTabs = tabs.length >= 2;
251
+ const hasTabs = !!micro && !!macro;
252
+
253
+ const renderTabContent = (tab?: { content: React.ReactNode; useCaseComparison?: React.ReactNode }) => {
254
+ if (!tab) return null;
255
+ return (
256
+ <div className="space-y-10">
257
+ {tab.content}
258
+ {tab.useCaseComparison && (
259
+ <div className="mt-16">
260
+ {tab.useCaseComparison}
261
+ </div>
262
+ )}
263
+ </div>
264
+ );
265
+ };
322
266
 
323
267
  const content = (
324
- <>
325
- {/* 1 & 2: Title and Description */}
326
- <div className="flex flex-col gap-2 mb-6">
268
+ <div className={cn("flex flex-col gap-8", className)}>
269
+ <div className="flex flex-col gap-2">
327
270
  <div className="flex items-center gap-4">
328
271
  <h2 className="text-3xl font-bold tracking-tight bg-gradient-to-br from-foreground to-foreground/60 bg-clip-text text-transparent">
329
272
  {title}
@@ -335,73 +278,78 @@ export function Showcase({
335
278
  )}
336
279
  </div>
337
280
 
338
- {/* 3: General Concept */}
339
- {generalConcept && <div className="mb-8">{generalConcept}</div>}
281
+ {guideline}
340
282
 
341
- {/* 4 & 5: TabsList and Active Content */}
342
- <div className="mt-2">
283
+ <div className="flex flex-col gap-6">
343
284
  {hasTabs && (
344
- <div className="mb-6 flex">
345
- <TabsList>
346
- {tabs.map((tab) => (
347
- <TabsTrigger
348
- key={tab.label}
349
- value={tab.label}
350
- className="px-4 py-1.5"
351
- >
352
- {tab.label}
353
- </TabsTrigger>
354
- ))}
355
- </TabsList>
356
- </div>
285
+ <TabsList>
286
+ {micro && (
287
+ <TabsTrigger value="Micro (Primitive)" className="px-4 py-1.5">
288
+ Micro (Primitive)
289
+ </TabsTrigger>
290
+ )}
291
+ {macro && (
292
+ <TabsTrigger value="Macro (Preset)" className="px-4 py-1.5">
293
+ Macro (Preset)
294
+ </TabsTrigger>
295
+ )}
296
+ </TabsList>
297
+ )}
298
+
299
+ {micro && (
300
+ hasTabs ? (
301
+ <TabsContent value="Micro (Primitive)" className="mt-0 focus-visible:outline-none">
302
+ {renderTabContent(micro)}
303
+ </TabsContent>
304
+ ) : (
305
+ renderTabContent(micro)
306
+ )
307
+ )}
308
+
309
+ {macro && (
310
+ hasTabs ? (
311
+ <TabsContent value="Macro (Preset)" className="mt-0 focus-visible:outline-none">
312
+ {renderTabContent(macro)}
313
+ </TabsContent>
314
+ ) : (
315
+ !micro ? renderTabContent(macro) : null
316
+ )
357
317
  )}
358
- {hasTabs
359
- ? tabs.map((tab) => (
360
- <TabsContent
361
- key={tab.label}
362
- value={tab.label}
363
- className="mt-0 focus-visible:outline-none"
364
- >
365
- {tab.content}
366
- </TabsContent>
367
- ))
368
- : tabs[0]?.content}
369
318
  </div>
370
- </>
319
+ </div>
371
320
  );
372
321
 
373
- return (
374
- <div className="w-full flex flex-col gap-8">
375
- {hasTabs ? (
376
- <Tabs defaultValue={tabs[0]!.label} className="w-full">
377
- {content}
378
- </Tabs>
379
- ) : (
380
- content
381
- )}
382
- </div>
322
+ return hasTabs ? (
323
+ <Tabs defaultValue="Micro (Primitive)" className="w-full">
324
+ {content}
325
+ </Tabs>
326
+ ) : (
327
+ content
383
328
  );
384
329
  }
385
330
 
386
331
  export function SizeToggle({
387
332
  value,
388
333
  onValueChange,
334
+ className,
389
335
  }: {
390
336
  value: Size;
391
337
  onValueChange: (size: Size) => void;
338
+ className?: string;
392
339
  }) {
393
340
  const sizes: Size[] = ["sm", "md", "lg"];
394
341
  return (
395
- <div className="flex h-8 items-center rounded-md border border-border bg-muted/50 p-1">
342
+ <div className={cn("flex h-8 items-center rounded-md border border-border bg-muted/50 p-1", className)}>
396
343
  {sizes.map((size) => (
397
344
  <button
398
345
  key={size}
399
346
  onClick={() => onValueChange(size)}
400
347
  className={cn(
401
348
  "flex items-center justify-center rounded-sm px-2.5 text-xs font-mono font-medium transition-all",
402
- value === size
403
- ? "bg-background text-foreground shadow-sm"
404
- : "text-muted-foreground hover:bg-muted hover:text-foreground",
349
+ {
350
+ "bg-background text-foreground shadow-sm": value === size,
351
+ "text-muted-foreground hover:bg-muted hover:text-foreground": value !== size,
352
+ }
405
353
  )}
406
354
  >
407
355
  {size}
@@ -57,9 +57,7 @@ export default function ButtonsSection() {
57
57
  />
58
58
  </SectionHeader>
59
59
 
60
- {/* Real-world Contexts Grid */}
61
60
  <div className="grid grid-cols-1 md:grid-cols-3 gap-4 md:gap-6">
62
- {/* Context 1: Danger Zone */}
63
61
  <div className="rounded-2xl border border-border bg-card/40 p-6 shadow-sm transition-all hover:shadow-md">
64
62
  <div className="flex items-center gap-3 mb-4">
65
63
  <div className="flex size-10 items-center justify-center rounded-xl bg-destructive/10 text-destructive">
@@ -91,8 +89,8 @@ export default function ButtonsSection() {
91
89
  >
92
90
  {isDeleting && <Spinner />}
93
91
  {!isDeleting && <Trash2Icon />}
94
- {isDeleting && "Deleting..."}
95
- {!isDeleting && "Delete Permanently"}
92
+ {isDeleting && "Đang xoá..."}
93
+ {!isDeleting && "Xoá vĩnh viễn"}
96
94
  </Button>
97
95
  }
98
96
  />
@@ -103,7 +101,6 @@ export default function ButtonsSection() {
103
101
  </div>
104
102
  </div>
105
103
 
106
- {/* Context 2: Form Actions */}
107
104
  <div className="rounded-2xl border border-border bg-card/40 p-6 shadow-sm transition-all hover:shadow-md">
108
105
  <div className="flex items-center gap-3 mb-4">
109
106
  <div className="flex size-10 items-center justify-center rounded-xl bg-primary/10 text-primary">
@@ -144,14 +141,13 @@ export default function ButtonsSection() {
144
141
  >
145
142
  {isSending && <Spinner />}
146
143
  {!isSending && <CheckCircle2Icon />}
147
- {isSending && "Saving"}
148
- {!isSending && "Save Changes"}
144
+ {isSending && "Đang lưu..."}
145
+ {!isSending && "Lưu thay đổi"}
149
146
  </Button>
150
147
  </div>
151
148
  </div>
152
149
  </div>
153
150
 
154
- {/* Context 3: Quick Actions */}
155
151
  <div className="rounded-2xl border border-border bg-card/40 p-6 shadow-sm transition-all hover:shadow-md">
156
152
  <div className="flex items-center gap-3 mb-4">
157
153
  <div className="flex size-10 items-center justify-center rounded-xl bg-info/10 text-info">
@@ -181,7 +177,6 @@ export default function ButtonsSection() {
181
177
  </div>
182
178
  </div>
183
179
 
184
- {/* The Matrix (Variants x Colors) */}
185
180
  <div className="rounded-2xl border border-border bg-card/40 overflow-hidden shadow-sm">
186
181
  <div className="border-b border-b-border bg-muted/30 px-6 py-4">
187
182
  <h3 className="font-semibold text-lg flex items-center gap-2">