gladvn 0.2.36 → 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.
- package/README.md +1 -1
- package/package.json +1 -1
- package/scripts/strip-comments.ts +79 -0
- package/src/blocks/auth-form.tsx +34 -30
- package/src/blocks/auth-recovery.tsx +9 -13
- package/src/blocks/auth-split.tsx +46 -36
- package/src/blocks/settings.tsx +84 -117
- package/src/components/macro/alert-preset.tsx +3 -1
- package/src/components/macro/carousel-preset.tsx +0 -1
- package/src/components/macro/checkbox-preset.test.tsx +2 -2
- package/src/components/macro/checkbox-preset.tsx +1 -2
- package/src/components/macro/command-preset.tsx +1 -1
- package/src/components/macro/date-picker.tsx +5 -18
- package/src/components/macro/dialog-preset.tsx +5 -3
- package/src/components/macro/input-preset.tsx +0 -1
- package/src/components/macro/select-preset.tsx +1 -1
- package/src/components/macro/switch-preset.test.tsx +1 -1
- package/src/components/micro/accordion.test.tsx +6 -12
- package/src/components/micro/alert-dialog.tsx +0 -2
- package/src/components/micro/alert.test.tsx +5 -8
- package/src/components/micro/alert.tsx +0 -2
- package/src/components/micro/badge.test.tsx +5 -7
- package/src/components/micro/badge.tsx +1 -3
- package/src/components/micro/button.test.tsx +19 -26
- package/src/components/micro/button.tsx +3 -11
- package/src/components/micro/calendar.tsx +12 -13
- package/src/components/micro/card.test.tsx +3 -5
- package/src/components/micro/carousel.tsx +0 -1
- package/src/components/micro/chart.tsx +3 -2
- package/src/components/micro/checkbox.test.tsx +9 -18
- package/src/components/micro/combobox.tsx +1 -3
- package/src/components/micro/command.tsx +23 -11
- package/src/components/micro/context-menu.tsx +0 -2
- package/src/components/micro/dialog.test.tsx +6 -14
- package/src/components/micro/dialog.tsx +0 -2
- package/src/components/micro/drawer.tsx +0 -3
- package/src/components/micro/dropdown-menu.test.tsx +8 -14
- package/src/components/micro/dropdown-menu.tsx +0 -2
- package/src/components/micro/field.tsx +3 -3
- package/src/components/micro/hover-card.tsx +0 -2
- package/src/components/micro/input-group.tsx +9 -42
- package/src/components/micro/input.test.tsx +15 -24
- package/src/components/micro/navigation-menu.tsx +0 -2
- package/src/components/micro/popover.test.tsx +5 -9
- package/src/components/micro/progress.tsx +5 -1
- package/src/components/micro/select.test.tsx +5 -18
- package/src/components/micro/select.tsx +3 -5
- package/src/components/micro/separator.tsx +1 -1
- package/src/components/micro/sheet.test.tsx +6 -14
- package/src/components/micro/sheet.tsx +0 -2
- package/src/components/micro/sidebar.tsx +5 -6
- package/src/components/micro/sonner.tsx +0 -1
- package/src/components/micro/switch.test.tsx +9 -18
- package/src/components/micro/switch.tsx +1 -1
- package/src/components/micro/tabs.test.tsx +5 -11
- package/src/components/micro/theme-provider.tsx +0 -12
- package/src/components/micro/toggle-group.tsx +1 -12
- package/src/components/micro/tooltip.test.tsx +7 -12
- package/src/dev/App.tsx +6 -6
- package/src/dev/components/BlockViewer.tsx +15 -4
- package/src/dev/components/Container.tsx +20 -0
- package/src/dev/components/GladvnLogo.tsx +3 -9
- package/src/dev/components/code-highlighter.tsx +5 -3
- package/src/dev/components/showcase.tsx +116 -168
- package/src/dev/sections/buttons.tsx +4 -9
- package/src/dev/sections/display.tsx +68 -72
- package/src/dev/sections/feedback.tsx +304 -314
- package/src/dev/sections/forms.tsx +10 -14
- package/src/dev/sections/interactive.tsx +32 -32
- package/src/dev/sections/overview.tsx +2 -10
- package/src/dev/showcase/accordion.tsx +512 -554
- package/src/dev/showcase/alert-dialog.tsx +525 -565
- package/src/dev/showcase/alert.tsx +352 -402
- package/src/dev/showcase/aspect-ratio.tsx +167 -193
- package/src/dev/showcase/avatar.tsx +231 -269
- package/src/dev/showcase/badge.tsx +92 -140
- package/src/dev/showcase/breadcrumb.tsx +113 -135
- package/src/dev/showcase/button.tsx +140 -173
- package/src/dev/showcase/calendar.tsx +187 -237
- package/src/dev/showcase/card.tsx +229 -261
- package/src/dev/showcase/carousel.tsx +288 -315
- package/src/dev/showcase/chart.tsx +44 -50
- package/src/dev/showcase/checkbox.tsx +286 -330
- package/src/dev/showcase/collapsible.tsx +143 -160
- package/src/dev/showcase/combobox.tsx +327 -369
- package/src/dev/showcase/command.tsx +106 -120
- package/src/dev/showcase/context-menu.tsx +93 -107
- package/src/dev/showcase/dialog.tsx +585 -630
- package/src/dev/showcase/drawer.tsx +176 -196
- package/src/dev/showcase/dropdown-menu.tsx +183 -201
- package/src/dev/showcase/empty.tsx +232 -274
- package/src/dev/showcase/hover-card.tsx +153 -180
- package/src/dev/showcase/input-group.tsx +221 -253
- package/src/dev/showcase/input-otp.tsx +204 -250
- package/src/dev/showcase/input.tsx +246 -325
- package/src/dev/showcase/item.tsx +134 -152
- package/src/dev/showcase/kbd.tsx +149 -172
- package/src/dev/showcase/label.tsx +50 -68
- package/src/dev/showcase/menubar.tsx +194 -213
- package/src/dev/showcase/navigation-menu.tsx +119 -133
- package/src/dev/showcase/overview-components/ArchitectureSection.tsx +126 -0
- package/src/dev/showcase/overview-components/CodeBlock.tsx +50 -0
- package/src/dev/showcase/overview-components/ColorPaletteSection.tsx +44 -0
- package/src/dev/showcase/overview-components/DashboardPreview.tsx +159 -0
- package/src/dev/showcase/overview-components/FeatureGridSection.tsx +535 -0
- package/src/dev/showcase/overview-components/FooterCTA.tsx +137 -0
- package/src/dev/showcase/overview-components/HeroSection.tsx +65 -0
- package/src/dev/showcase/overview-components/PrinciplesSection.tsx +128 -0
- package/src/dev/showcase/overview.tsx +21 -1163
- package/src/dev/showcase/pagination.tsx +144 -190
- package/src/dev/showcase/popover.tsx +181 -196
- package/src/dev/showcase/progress.tsx +141 -189
- package/src/dev/showcase/radio-group.tsx +272 -332
- package/src/dev/showcase/resizable.tsx +119 -141
- package/src/dev/showcase/scroll-area.tsx +21 -36
- package/src/dev/showcase/select.tsx +262 -322
- package/src/dev/showcase/separator.tsx +75 -73
- package/src/dev/showcase/sheet.tsx +333 -361
- package/src/dev/showcase/sidebar.tsx +53 -59
- package/src/dev/showcase/skeleton.tsx +93 -112
- package/src/dev/showcase/slider.tsx +244 -296
- package/src/dev/showcase/sonner.tsx +251 -258
- package/src/dev/showcase/spinner.tsx +46 -67
- package/src/dev/showcase/switch.tsx +298 -362
- package/src/dev/showcase/table.tsx +76 -80
- package/src/dev/showcase/tabs.tsx +302 -355
- package/src/dev/showcase/textarea.tsx +240 -293
- package/src/dev/showcase/toast.tsx +48 -54
- package/src/dev/showcase/toggle-group.tsx +104 -129
- package/src/dev/showcase/toggle.tsx +82 -102
- package/src/dev/showcase/tooltip.tsx +173 -192
- package/src/dev/showcase/why.tsx +6 -12
- package/src/hooks/use-mobile.test.ts +3 -2
- 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="
|
|
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="
|
|
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
|
-
|
|
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="
|
|
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=
|
|
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="
|
|
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-
|
|
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
|
-
|
|
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
|
-
|
|
90
|
+
preview,
|
|
111
91
|
className,
|
|
112
|
-
|
|
113
|
-
hideCode = false,
|
|
92
|
+
code,
|
|
114
93
|
}: {
|
|
115
|
-
|
|
94
|
+
title?: string;
|
|
116
95
|
description?: string;
|
|
117
|
-
|
|
96
|
+
preview: React.ReactNode;
|
|
118
97
|
className?: string;
|
|
119
|
-
|
|
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
|
|
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 (
|
|
135
|
-
navigator.clipboard.writeText(
|
|
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
|
-
{
|
|
150
|
-
<h3 className="text-sm font-semibold text-foreground">{
|
|
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
|
-
{
|
|
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
|
|
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
|
-
|
|
180
|
-
<div className="relative z-10 w-full">{
|
|
154
|
+
|
|
155
|
+
<div className="relative z-10 w-full">{preview}</div>
|
|
181
156
|
</div>
|
|
182
157
|
</TabsContent>
|
|
183
158
|
|
|
184
|
-
{
|
|
159
|
+
{hasCode && (
|
|
185
160
|
<TabsContent value="code" className="mt-0 outline-none">
|
|
186
|
-
<div className="relative rounded-2xl
|
|
187
|
-
<CodeHighlighter code={
|
|
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
|
-
|
|
229
|
+
guideline?: React.ReactNode;
|
|
310
230
|
actions?: React.ReactNode;
|
|
311
|
-
|
|
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
|
-
|
|
245
|
+
guideline,
|
|
318
246
|
actions,
|
|
319
|
-
|
|
247
|
+
micro,
|
|
248
|
+
macro,
|
|
249
|
+
className,
|
|
320
250
|
}: ShowcaseProps) {
|
|
321
|
-
const hasTabs =
|
|
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
|
-
|
|
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
|
-
{
|
|
339
|
-
{generalConcept && <div className="mb-8">{generalConcept}</div>}
|
|
281
|
+
{guideline}
|
|
340
282
|
|
|
341
|
-
|
|
342
|
-
<div className="mt-2">
|
|
283
|
+
<div className="flex flex-col gap-6">
|
|
343
284
|
{hasTabs && (
|
|
344
|
-
<
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
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
|
-
<
|
|
375
|
-
{
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
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
|
-
|
|
403
|
-
|
|
404
|
-
|
|
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 && "
|
|
95
|
-
{!isDeleting && "
|
|
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 && "
|
|
148
|
-
{!isSending && "
|
|
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">
|