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
@@ -30,15 +30,12 @@ import {
30
30
  DocsH3,
31
31
  DocsP,
32
32
  ExampleGrid,
33
- ExampleSection,
33
+ ShowcaseExample,
34
34
  Showcase,
35
35
  ShowcaseDocs
36
36
  } from "../../dev/components/showcase";
37
37
  import { type Size } from "../../lib/types";
38
38
 
39
- // ──────────────────────────────────────────────────────────
40
- // RHF Form Demo (Macro)
41
- // ──────────────────────────────────────────────────────────
42
39
  const formSchema = z.object({
43
40
  language: z.string().min(1, "Vui lòng chọn một ngôn ngữ.") });
44
41
  type FormValues = z.infer<typeof formSchema>;
@@ -112,17 +109,11 @@ function SelectForm({ size }: { size: Size }) {
112
109
  );
113
110
  }`;
114
111
 
115
- // ──────────────────────────────────────────────────────────
116
- // SECTION 1: Macro Content
117
- // ──────────────────────────────────────────────────────────
118
112
  function SelectMacroShowcase({ globalSize }: { globalSize: Size }) {
119
113
  return (
120
- <div className="space-y-10 mt-6">
114
+ <div className="space-y-10">
121
115
  <ExampleGrid>
122
- <ExampleSection
123
- label="Tiêu chuẩn"
124
- description="Trình thả xuống cơ bản với mảng options."
125
- codeString={`<SelectPreset
116
+ <ShowcaseExample title="Tiêu chuẩn" description="Trình thả xuống cơ bản với mảng options." code={`<SelectPreset
126
117
  className="w-full"
127
118
  label="Trái cây yêu thích"
128
119
  description="Chọn một loại trái cây bạn thích nhất."
@@ -132,26 +123,24 @@ function SelectMacroShowcase({ globalSize }: { globalSize: Size }) {
132
123
  { value: "banana", label: "Chuối (Banana)" },
133
124
  { value: "cherry", label: "Anh đào (Cherry)" },
134
125
  ]}
135
- />`}
136
- >
137
- <SelectPreset
138
- className="w-full"
139
- size={globalSize}
140
- label="Trái cây yêu thích"
141
- description="Chọn một loại trái cây bạn thích nhất."
142
- placeholder="Chọn trái cây..."
143
- options={[
144
- { value: "apple", label: "Táo (Apple)" },
145
- { value: "banana", label: "Chuối (Banana)" },
146
- { value: "cherry", label: "Anh đào (Cherry)" },
147
- ]}
148
- />
149
- </ExampleSection>
126
+ />`} preview={
127
+ <>
128
+ <SelectPreset
129
+ className="w-full"
130
+ size={globalSize}
131
+ label="Trái cây yêu thích"
132
+ description="Chọn một loại trái cây bạn thích nhất."
133
+ placeholder="Chọn trái cây..."
134
+ options={[
135
+ { value: "apple", label: "Táo (Apple)" },
136
+ { value: "banana", label: "Chuối (Banana)" },
137
+ { value: "cherry", label: "Anh đào (Cherry)" },
138
+ ]}
139
+ />
140
+ </>
141
+ } />
150
142
 
151
- <ExampleSection
152
- label="Trạng thái Lỗi (Error)"
153
- description="Báo lỗi khi form submit mà user chưa chọn."
154
- codeString={`<div className="w-full flex flex-col gap-6">
143
+ <ShowcaseExample title="Trạng thái Lỗi (Error)" description="Báo lỗi khi form submit mà user chưa chọn." code={`<div className="w-full flex flex-col gap-6">
155
144
  <SelectPreset
156
145
  label="Framework (Lỗi)"
157
146
  placeholder="Chọn một framework..."
@@ -161,28 +150,26 @@ function SelectMacroShowcase({ globalSize }: { globalSize: Size }) {
161
150
  ]}
162
151
  errorMessage="Vui lòng chọn một framework để tiếp tục."
163
152
  />
164
- </div>`}
165
- >
166
- <div className="w-full flex flex-col gap-6">
167
- <SelectPreset
168
- size={globalSize}
169
- label="Framework (Lỗi)"
170
- placeholder="Chọn một framework..."
171
- options={[
172
- { value: "react", label: "React" },
173
- { value: "vue", label: "Vue" },
174
- ]}
175
- errorMessage="Vui lòng chọn một framework để tiếp tục."
176
- />
177
- </div>
178
- </ExampleSection>
153
+ </div>`} preview={
154
+ <>
155
+ <div className="w-full flex flex-col gap-6">
156
+ <SelectPreset
157
+ size={globalSize}
158
+ label="Framework (Lỗi)"
159
+ placeholder="Chọn một framework..."
160
+ options={[
161
+ { value: "react", label: "React" },
162
+ { value: "vue", label: "Vue" },
163
+ ]}
164
+ errorMessage="Vui lòng chọn một framework để tiếp tục."
165
+ />
166
+ </div>
167
+ </>
168
+ } />
179
169
  </ExampleGrid>
180
170
 
181
171
  <ExampleGrid>
182
- <ExampleSection
183
- label="Khóa / Bất hoạt (Disabled)"
184
- description="Người dùng không thể mở danh sách."
185
- codeString={`<SelectPreset
172
+ <ShowcaseExample title="Khóa / Bất hoạt (Disabled)" description="Người dùng không thể mở danh sách." code={`<SelectPreset
186
173
  className="w-full"
187
174
  label="Gói tài khoản"
188
175
  description="Bạn không thể thay đổi gói cước khi chưa thanh toán nợ."
@@ -193,27 +180,25 @@ function SelectMacroShowcase({ globalSize }: { globalSize: Size }) {
193
180
  { value: "pro", label: "Chuyên nghiệp (Pro)" },
194
181
  ]}
195
182
  disabled
196
- />`}
197
- >
198
- <SelectPreset
199
- className="w-full"
200
- size={globalSize}
201
- label="Gói tài khoản"
202
- description="Bạn không thể thay đổi gói cước khi chưa thanh toán nợ."
203
- placeholder="Chọn gói..."
204
- value="pro"
205
- options={[
206
- { value: "basic", label: "Cơ bản (Free)" },
207
- { value: "pro", label: "Chuyên nghiệp (Pro)" },
208
- ]}
209
- disabled
210
- />
211
- </ExampleSection>
183
+ />`} preview={
184
+ <>
185
+ <SelectPreset
186
+ className="w-full"
187
+ size={globalSize}
188
+ label="Gói tài khoản"
189
+ description="Bạn không thể thay đổi gói cước khi chưa thanh toán nợ."
190
+ placeholder="Chọn gói..."
191
+ value="pro"
192
+ options={[
193
+ { value: "basic", label: "Cơ bản (Free)" },
194
+ { value: "pro", label: "Chuyên nghiệp (Pro)" },
195
+ ]}
196
+ disabled
197
+ />
198
+ </>
199
+ } />
212
200
 
213
- <ExampleSection
214
- label="Nhãn phức tạp (Custom Content)"
215
- description="Truyền React Node vào Label để tạo giao diện phong phú."
216
- codeString={`<SelectPreset
201
+ <ShowcaseExample title="Nhãn phức tạp (Custom Content)" description="Truyền React Node vào Label để tạo giao diện phong phú." code={`<SelectPreset
217
202
  label={
218
203
  <span className="flex items-center gap-2">
219
204
  Quốc gia cư trú
@@ -227,50 +212,43 @@ function SelectMacroShowcase({ globalSize }: { globalSize: Size }) {
227
212
  { value: "vn", label: "Việt Nam" },
228
213
  ]}
229
214
  />
230
- `}
231
- >
232
- <SelectPreset
233
- size={globalSize}
234
- label={
235
- <span className="flex items-center gap-2">
236
- Quốc gia cư trú
237
- <span className="text-destructive">*</span>
238
- </span>
239
- }
240
- description="Thông tin này được sử dụng cho mục đích tính thuế, phải khớp với địa chỉ thanh toán."
241
- placeholder="Chọn quốc gia..."
242
- options={[
243
- { value: "us", label: "United States" },
244
- { value: "vn", label: "Việt Nam" },
245
- ]}
246
- />
247
- </ExampleSection>
215
+ `} preview={
216
+ <>
217
+ <SelectPreset
218
+ size={globalSize}
219
+ label={
220
+ <span className="flex items-center gap-2">
221
+ Quốc gia cư trú
222
+ <span className="text-destructive">*</span>
223
+ </span>
224
+ }
225
+ description="Thông tin này được sử dụng cho mục đích tính thuế, phải khớp với địa chỉ thanh toán."
226
+ placeholder="Chọn quốc gia..."
227
+ options={[
228
+ { value: "us", label: "United States" },
229
+ { value: "vn", label: "Việt Nam" },
230
+ ]}
231
+ />
232
+ </>
233
+ } />
248
234
  </ExampleGrid>
249
235
 
250
236
  <ExampleGrid>
251
- <ExampleSection
252
- label="Tích hợp React Hook Form"
253
- description="Sử dụng Controller để bắt giá trị."
254
- codeString={rhfCode}
255
- >
256
- <SelectForm size={globalSize} />
257
- </ExampleSection>
237
+ <ShowcaseExample title="Tích hợp React Hook Form" description="Sử dụng Controller để bắt giá trị." code={rhfCode} preview={
238
+ <>
239
+ <SelectForm size={globalSize} />
240
+ </>
241
+ } />
258
242
  </ExampleGrid>
259
243
  </div>
260
244
  );
261
245
  }
262
246
 
263
- // ──────────────────────────────────────────────────────────
264
- // SECTION 2: Micro Content
265
- // ──────────────────────────────────────────────────────────
266
247
  function SelectMicroShowcase({ globalSize }: { globalSize: Size }) {
267
248
  return (
268
- <div className="space-y-10 mt-6">
249
+ <div className="space-y-10">
269
250
  <ExampleGrid>
270
- <ExampleSection
271
- label="Cơ bản (Primitive)"
272
- description="Ghép nối thủ công bằng cách bọc Field."
273
- codeString={`<Field className="w-full">
251
+ <ShowcaseExample title="Cơ bản (Primitive)" description="Ghép nối thủ công bằng cách bọc Field." code={`<Field className="w-full">
274
252
  <FieldLabel>Framework</FieldLabel>
275
253
  <FieldContent>
276
254
  <Select
@@ -296,36 +274,34 @@ function SelectMicroShowcase({ globalSize }: { globalSize: Size }) {
296
274
  <FieldDescription>
297
275
  Chọn công nghệ bạn muốn sử dụng.
298
276
  </FieldDescription>
299
- </Field>`}
300
- >
301
- <Field size={globalSize} className="w-full">
302
- <FieldLabel>Framework</FieldLabel>
303
- <FieldContent>
304
- <Select items={{ next: "Next.js", vite: "Vite", remix: "Remix" }}>
305
- <SelectTrigger size={globalSize}>
306
- <SelectValue placeholder="Chọn framework..." />
307
- </SelectTrigger>
277
+ </Field>`} preview={
278
+ <>
279
+ <Field size={globalSize} className="w-full">
280
+ <FieldLabel>Framework</FieldLabel>
281
+ <FieldContent>
282
+ <Select items={{ next: "Next.js", vite: "Vite", remix: "Remix" }}>
283
+ <SelectTrigger size={globalSize}>
284
+ <SelectValue placeholder="Chọn framework..." />
285
+ </SelectTrigger>
308
286
 
309
- <SelectContent>
310
- <SelectScrollUpButton />
311
- <SelectItem value="next">Next.js</SelectItem>
312
- <SelectItem value="vite">Vite</SelectItem>
313
- <SelectItem value="remix">Remix</SelectItem>
314
- <SelectScrollDownButton />
315
- </SelectContent>
316
-
317
- </Select>
318
- </FieldContent>
319
- <FieldDescription>
320
- Chọn công nghệ bạn muốn sử dụng.
321
- </FieldDescription>
322
- </Field>
323
- </ExampleSection>
287
+ <SelectContent>
288
+ <SelectScrollUpButton />
289
+ <SelectItem value="next">Next.js</SelectItem>
290
+ <SelectItem value="vite">Vite</SelectItem>
291
+ <SelectItem value="remix">Remix</SelectItem>
292
+ <SelectScrollDownButton />
293
+ </SelectContent>
294
+
295
+ </Select>
296
+ </FieldContent>
297
+ <FieldDescription>
298
+ Chọn công nghệ bạn muốn sử dụng.
299
+ </FieldDescription>
300
+ </Field>
301
+ </>
302
+ } />
324
303
 
325
- <ExampleSection
326
- label="Phân nhóm (Grouped)"
327
- description="Sử dụng SelectGroup, SelectLabel và SelectSeparator."
328
- codeString={`<Field className="w-full">
304
+ <ShowcaseExample title="Phân nhóm (Grouped)" description="Sử dụng SelectGroup, SelectLabel và SelectSeparator." code={`<Field className="w-full">
329
305
  <FieldLabel>Trái cây</FieldLabel>
330
306
  <FieldContent>
331
307
  <Select
@@ -333,7 +309,7 @@ function SelectMicroShowcase({ globalSize }: { globalSize: Size }) {
333
309
  orange: "Orange",
334
310
  lemon: "Lemon",
335
311
  strawberry: "Strawberry",
336
- blueberry: "Blueberry" }}
312
+ blueberry: "Việt quất" }}
337
313
  >
338
314
  <SelectTrigger>
339
315
  <SelectValue placeholder="Chọn loại quả..." />
@@ -361,47 +337,45 @@ function SelectMicroShowcase({ globalSize }: { globalSize: Size }) {
361
337
 
362
338
  </Select>
363
339
  </FieldContent>
364
- </Field>`}
365
- >
366
- <Field size={globalSize} className="w-full">
367
- <FieldLabel>Trái cây</FieldLabel>
368
- <FieldContent>
369
- <Select
370
- items={{
371
- orange: "Orange",
372
- lemon: "Lemon",
373
- strawberry: "Strawberry",
374
- blueberry: "Blueberry" }}
375
- >
376
- <SelectTrigger size={globalSize}>
377
- <SelectValue placeholder="Chọn loại quả..." />
378
- </SelectTrigger>
379
-
380
- <SelectContent>
381
- <SelectGroup>
382
- <SelectLabel>Họ cam chanh (Citrus)</SelectLabel>
383
- <SelectItem value="orange">Orange</SelectItem>
384
- <SelectItem value="lemon">Lemon</SelectItem>
385
- </SelectGroup>
386
- <SelectSeparator />
387
- <SelectGroup>
388
- <SelectLabel>Họ dâu (Berry)</SelectLabel>
389
- <SelectItem value="strawberry">Strawberry</SelectItem>
390
- <SelectItem value="blueberry">Blueberry</SelectItem>
391
- </SelectGroup>
392
- </SelectContent>
393
-
394
- </Select>
395
- </FieldContent>
396
- </Field>
397
- </ExampleSection>
340
+ </Field>`} preview={
341
+ <>
342
+ <Field size={globalSize} className="w-full">
343
+ <FieldLabel>Trái cây</FieldLabel>
344
+ <FieldContent>
345
+ <Select
346
+ items={{
347
+ orange: "Orange",
348
+ lemon: "Lemon",
349
+ strawberry: "Strawberry",
350
+ blueberry: "Việt quất" }}
351
+ >
352
+ <SelectTrigger size={globalSize}>
353
+ <SelectValue placeholder="Chọn loại quả..." />
354
+ </SelectTrigger>
355
+
356
+ <SelectContent>
357
+ <SelectGroup>
358
+ <SelectLabel>Họ cam chanh (Citrus)</SelectLabel>
359
+ <SelectItem value="orange">Orange</SelectItem>
360
+ <SelectItem value="lemon">Lemon</SelectItem>
361
+ </SelectGroup>
362
+ <SelectSeparator />
363
+ <SelectGroup>
364
+ <SelectLabel>Họ dâu (Berry)</SelectLabel>
365
+ <SelectItem value="strawberry">Strawberry</SelectItem>
366
+ <SelectItem value="blueberry">Blueberry</SelectItem>
367
+ </SelectGroup>
368
+ </SelectContent>
369
+
370
+ </Select>
371
+ </FieldContent>
372
+ </Field>
373
+ </>
374
+ } />
398
375
  </ExampleGrid>
399
376
 
400
377
  <ExampleGrid>
401
- <ExampleSection
402
- label="Trạng thái Lỗi thủ công"
403
- description="Tự gắn aria-invalid vào Select."
404
- codeString={`<Field data-invalid={true} className="w-full">
378
+ <ShowcaseExample title="Trạng thái Lỗi thủ công" description="Tự gắn aria-invalid vào Select." code={`<Field data-invalid={true} className="w-full">
405
379
  <FieldLabel>Dự án</FieldLabel>
406
380
  <FieldContent>
407
381
  <Select items={{ p1: "Project 1" }}>
@@ -416,30 +390,28 @@ function SelectMicroShowcase({ globalSize }: { globalSize: Size }) {
416
390
  </Select>
417
391
  </FieldContent>
418
392
  <FieldError>Vui lòng chọn một dự án hợp lệ.</FieldError>
419
- </Field>`}
420
- >
421
- <Field size={globalSize} data-invalid={true} className="w-full">
422
- <FieldLabel>Dự án</FieldLabel>
423
- <FieldContent>
424
- <Select items={{ p1: "Project 1" }}>
425
- <SelectTrigger size={globalSize} aria-invalid={true}>
426
- <SelectValue placeholder="Chọn dự án..." />
427
- </SelectTrigger>
428
-
429
- <SelectContent>
430
- <SelectItem value="p1">Project 1</SelectItem>
431
- </SelectContent>
432
-
433
- </Select>
434
- </FieldContent>
435
- <FieldError>Vui lòng chọn một dự án hợp lệ.</FieldError>
436
- </Field>
437
- </ExampleSection>
393
+ </Field>`} preview={
394
+ <>
395
+ <Field size={globalSize} data-invalid={true} className="w-full">
396
+ <FieldLabel>Dự án</FieldLabel>
397
+ <FieldContent>
398
+ <Select items={{ p1: "Project 1" }}>
399
+ <SelectTrigger size={globalSize} aria-invalid={true}>
400
+ <SelectValue placeholder="Chọn dự án..." />
401
+ </SelectTrigger>
402
+
403
+ <SelectContent>
404
+ <SelectItem value="p1">Project 1</SelectItem>
405
+ </SelectContent>
406
+
407
+ </Select>
408
+ </FieldContent>
409
+ <FieldError>Vui lòng chọn một dự án hợp lệ.</FieldError>
410
+ </Field>
411
+ </>
412
+ } />
438
413
 
439
- <ExampleSection
440
- label="Disabled thủ công"
441
- description="Khóa SelectTrigger."
442
- codeString={`<Field className="w-full">
414
+ <ShowcaseExample title="Disabled thủ công" description="Khóa SelectTrigger." code={`<Field className="w-full">
443
415
  <FieldLabel>Múi giờ</FieldLabel>
444
416
  <FieldContent>
445
417
  <Select items={{ gmt: "GMT+7" }}>
@@ -458,34 +430,31 @@ function SelectMicroShowcase({ globalSize }: { globalSize: Size }) {
458
430
  <FieldDescription>
459
431
  Múi giờ tự động lấy theo hệ thống.
460
432
  </FieldDescription>
461
- </Field>`}
462
- >
463
- <Field size={globalSize} className="w-full">
464
- <FieldLabel>Múi giờ</FieldLabel>
465
- <FieldContent>
466
- <Select items={{ gmt: "GMT+7" }}>
467
- <SelectTrigger size={globalSize} disabled>
468
- <SelectValue placeholder="Chọn múi giờ..." />
469
- </SelectTrigger>
470
-
471
- <SelectContent>
472
- <SelectItem value="gmt">GMT+7 (Indochina Time)</SelectItem>
473
- </SelectContent>
474
-
475
- </Select>
476
- </FieldContent>
477
- <FieldDescription>
478
- Múi giờ tự động lấy theo hệ thống.
479
- </FieldDescription>
480
- </Field>
481
- </ExampleSection>
433
+ </Field>`} preview={
434
+ <>
435
+ <Field size={globalSize} className="w-full">
436
+ <FieldLabel>Múi giờ</FieldLabel>
437
+ <FieldContent>
438
+ <Select items={{ gmt: "GMT+7" }}>
439
+ <SelectTrigger size={globalSize} disabled>
440
+ <SelectValue placeholder="Chọn múi giờ..." />
441
+ </SelectTrigger>
442
+
443
+ <SelectContent>
444
+ <SelectItem value="gmt">GMT+7 (Indochina Time)</SelectItem>
445
+ </SelectContent>
446
+
447
+ </Select>
448
+ </FieldContent>
449
+ <FieldDescription>
450
+ Múi giờ tự động lấy theo hệ thống.
451
+ </FieldDescription>
452
+ </Field>
453
+ </>
454
+ } />
482
455
  </ExampleGrid>
483
456
 
484
- <ExampleSection
485
- label="🧭 Use Case Comparison"
486
- description="So sánh nhanh khi nào dùng Micro và Macro."
487
- fullWidth
488
- codeString={`<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
457
+ <ShowcaseExample title="🧭 So sánh Use Case" description="So sánh nhanh khi nào dùng Micro và Macro." code={`<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
489
458
  {/* Story 1: Macro wins */}
490
459
  <div className="rounded-xl border border-border bg-card p-5 flex flex-col gap-4">
491
460
  <div className="flex items-start gap-3">
@@ -556,120 +525,91 @@ function SelectMicroShowcase({ globalSize }: { globalSize: Size }) {
556
525
  <DocsCode>SelectItem</DocsCode>.
557
526
  </p>
558
527
  </div>
559
- </div>`}
560
- >
561
- <div className="grid grid-cols-1 gap-6 md:grid-cols-2">
562
- {/* Story 1: Macro wins */}
563
- <div className="rounded-xl border border-border bg-card p-5 flex flex-col gap-4">
564
- <div className="flex items-start gap-3">
565
- <span className="mt-0.5 shrink-0 rounded-full bg-green-500/10 p-1.5 text-green-600">
566
- <svg
567
- xmlns="http://www.w3.org/2000/svg"
568
- className="size-4"
569
- viewBox="0 0 24 24"
570
- fill="none"
571
- stroke="currentColor"
572
- strokeWidth="2"
573
- strokeLinecap="round"
574
- strokeLinejoin="round"
575
- aria-hidden="true"
576
- >
577
- <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
578
- </svg>
579
- </span>
580
- <div>
581
- <p className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">
582
- Story 1 · Dùng Macro
583
- </p>
584
- <h3 className="mt-0.5 text-sm font-semibold text-foreground">
585
- Form nhập Quốc tịch
586
- </h3>
587
- </div>
588
- </div>
589
- <p className="text-xs text-muted-foreground leading-relaxed">
590
- Bạn có 195 quốc gia để chọn, tất cả đều chỉ là chữ. Thay vì viết
591
- 195 thẻ <DocsCode>SelectItem</DocsCode>, bạn vứt mảng JSON 195
592
- phần tử đó vào thuộc tính <DocsCode>options</DocsCode> của Macro.
593
- </p>
594
- </div>
528
+ </div>`} preview={
529
+ <>
530
+ <div className="grid grid-cols-1 gap-6 md:grid-cols-2">
531
+ <div className="rounded-xl border border-border bg-card p-5 flex flex-col gap-4">
532
+ <div className="flex items-start gap-3">
533
+ <span className="mt-0.5 shrink-0 rounded-full bg-green-500/10 p-1.5 text-green-600">
534
+ <svg
535
+ xmlns="http://www.w3.org/2000/svg"
536
+ className="size-4"
537
+ viewBox="0 0 24 24"
538
+ fill="none"
539
+ stroke="currentColor"
540
+ strokeWidth="2"
541
+ strokeLinecap="round"
542
+ strokeLinejoin="round"
543
+ aria-hidden="true"
544
+ >
545
+ <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
546
+ </svg>
547
+ </span>
548
+ <div>
549
+ <p className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">
550
+ Story 1 · Dùng Macro
551
+ </p>
552
+ <h3 className="mt-0.5 text-sm font-semibold text-foreground">
553
+ Form nhập Quốc tịch
554
+ </h3>
555
+ </div>
556
+ </div>
557
+ <p className="text-xs text-muted-foreground leading-relaxed">
558
+ Bạn 195 quốc gia để chọn, tất cả đều chỉ là chữ. Thay vì viết
559
+ 195 thẻ <DocsCode>SelectItem</DocsCode>, bạn vứt mảng JSON 195
560
+ phần tử đó vào thuộc tính <DocsCode>options</DocsCode> của Macro.
561
+ </p>
562
+ </div>
595
563
 
596
- {/* Story 2: Micro wins */}
597
- <div className="rounded-xl border border-border bg-card p-5 flex flex-col gap-4">
598
- <div className="flex items-start gap-3">
599
- <span className="mt-0.5 shrink-0 rounded-full bg-blue-500/10 p-1.5 text-blue-600">
600
- <svg
601
- xmlns="http://www.w3.org/2000/svg"
602
- className="size-4"
603
- viewBox="0 0 24 24"
604
- fill="none"
605
- stroke="currentColor"
606
- strokeWidth="2"
607
- strokeLinecap="round"
608
- strokeLinejoin="round"
609
- aria-hidden="true"
610
- >
611
- <path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z" />
612
- </svg>
613
- </span>
614
- <div>
615
- <p className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">
616
- Story 2 · Dùng Micro
617
- </p>
618
- <h3 className="mt-0.5 text-sm font-semibold text-foreground">
619
- Menu Chọn Tài khoản
620
- </h3>
621
- </div>
622
- </div>
623
- <p className="text-xs text-muted-foreground leading-relaxed">
624
- Bạn muốn trong danh sách thả xuống, mỗi dòng không chỉ Tên,
625
- còn cả Avatar (ảnh tròn) Email (chữ mờ dưới). Bạn bắt buộc
626
- phải dùng Micro để tự thiết kế nội dung bên trong{" "}
627
- <DocsCode>SelectItem</DocsCode>.
628
- </p>
629
- </div>
630
- </div>
631
- </ExampleSection>
564
+ <div className="rounded-xl border border-border bg-card p-5 flex flex-col gap-4">
565
+ <div className="flex items-start gap-3">
566
+ <span className="mt-0.5 shrink-0 rounded-full bg-blue-500/10 p-1.5 text-blue-600">
567
+ <svg
568
+ xmlns="http://www.w3.org/2000/svg"
569
+ className="size-4"
570
+ viewBox="0 0 24 24"
571
+ fill="none"
572
+ stroke="currentColor"
573
+ strokeWidth="2"
574
+ strokeLinecap="round"
575
+ strokeLinejoin="round"
576
+ aria-hidden="true"
577
+ >
578
+ <path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z" />
579
+ </svg>
580
+ </span>
581
+ <div>
582
+ <p className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">
583
+ Story 2 · Dùng Micro
584
+ </p>
585
+ <h3 className="mt-0.5 text-sm font-semibold text-foreground">
586
+ Menu Chọn Tài khoản
587
+ </h3>
588
+ </div>
589
+ </div>
590
+ <p className="text-xs text-muted-foreground leading-relaxed">
591
+ Bạn muốn trong danh sách thả xuống, mỗi dòng không chỉ là Tên, mà
592
+ còn cả Avatar (ảnh tròn) Email (chữ mờ dưới). Bạn bắt buộc
593
+ phải dùng Micro để tự thiết kế nội dung bên trong{" "}
594
+ <DocsCode>SelectItem</DocsCode>.
595
+ </p>
596
+ </div>
597
+ </div>
598
+ </>
599
+ } />
632
600
  </div>
633
601
  );
634
602
  }
635
603
 
636
- // ──────────────────────────────────────────────────────────
637
- // SECTION 3: Entry point
638
- // ──────────────────────────────────────────────────────────
639
604
  export default function SelectShowcase() {
640
605
  const { size: globalSize } = useDevContext();
641
606
  return (
642
607
  <Showcase
643
608
  title="Select"
644
609
  description="Thành phần điều khiển hiển thị danh sách các tùy chọn thả xuống để người dùng chọn."
645
- generalConcept={
646
- <ShowcaseDocs>
647
- <DocsH3>Khi nào nên dùng Macro</DocsH3>
648
- <DocsP>
649
- <DocsCode>SelectPreset</DocsCode> đóng gói sẵn cấu trúc 8 thẻ của
650
- Select nguyên thuỷ thành 1 Component duy nhất. Nó dùng prop{" "}
651
- <DocsCode>options</DocsCode> để render danh sách thả xuống. Dùng
652
- Macro cho 90% trường hợp form nhập liệu chọn 1 giá trị thông thường.
653
- </DocsP>
654
- <DocsH3>Khi nào nên dùng Micro</DocsH3>
655
- <DocsP>
656
- Dùng <DocsCode>Select</DocsCode> và các thành phần con nguyên thuỷ
657
- khi bạn cần một danh sách thả xuống có phân nhóm (
658
- <DocsCode>SelectGroup</DocsCode>, <DocsCode>SelectLabel</DocsCode>)
659
- hoặc khi mỗi tuỳ chọn (<DocsCode>SelectItem</DocsCode>) có thiết kế
660
- phức tạp (chứa icon, ảnh đại diện, subtext) thay vì chỉ là text đơn
661
- thuần.
662
- </DocsP>
663
- </ShowcaseDocs>
664
- }
665
- tabs={[
666
- {
667
- label: "Micro (Primitive)",
668
- content: <SelectMicroShowcase globalSize={globalSize} /> },
669
- {
670
- label: "Macro (Preset)",
671
- content: <SelectMacroShowcase globalSize={globalSize} /> },
672
- ]}
610
+
611
+ micro={{ content: <SelectMicroShowcase globalSize={globalSize} /> }}
612
+ macro={{ content: <SelectMacroShowcase globalSize={globalSize} /> }}
673
613
  />
674
614
  );
675
615
  }