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
@@ -40,7 +40,7 @@ import {
40
40
  DocsCode,
41
41
  DocsP,
42
42
  ExampleGrid,
43
- ExampleSection,
43
+ ShowcaseExample,
44
44
  Showcase,
45
45
  ShowcaseDocs
46
46
  } from "../../dev/components/showcase";
@@ -52,9 +52,6 @@ const allFrameworks = [...frontendFrameworks, ...backendFrameworks];
52
52
  const tagItems = ["bug", "feature", "enhancement", "docs"];
53
53
  const engineItems = ["v8", "spidermonkey"];
54
54
 
55
- // ──────────────────────────────────────────────────────────
56
- // RHF Form Demo (Macro)
57
- // ──────────────────────────────────────────────────────────
58
55
  const formSchema = z.object({
59
56
  framework: z.string().min(1, "Vui lòng chọn một framework.") });
60
57
  type FormValues = z.infer<typeof formSchema>;
@@ -133,17 +130,11 @@ function ComboboxForm({ size }: { size: Size }) {
133
130
  );
134
131
  }`;
135
132
 
136
- // ──────────────────────────────────────────────────────────
137
- // SECTION 1: Macro Content
138
- // ──────────────────────────────────────────────────────────
139
133
  function ComboboxMacroShowcase({ globalSize }: { globalSize: Size }) {
140
134
  return (
141
- <div className="space-y-10 mt-6">
135
+ <div className="space-y-10">
142
136
  <ExampleGrid>
143
- <ExampleSection
144
- label="Tiêu chuẩn"
145
- description="Tương tự Select nhưng có thể gõ để tìm kiếm."
146
- codeString={`<ComboboxPreset
137
+ <ShowcaseExample title="Tiêu chuẩn" description="Tương tự Select nhưng có thể gõ để tìm kiếm." code={`<ComboboxPreset
147
138
  className="w-full"
148
139
  label="Framework"
149
140
  description="Hỗ trợ hàng ngàn bản ghi mà không lag."
@@ -156,29 +147,27 @@ function ComboboxMacroShowcase({ globalSize }: { globalSize: Size }) {
156
147
  { value: "angular", label: "Angular" },
157
148
  { value: "svelte", label: "Svelte" },
158
149
  ]}
159
- />`}
160
- >
161
- <ComboboxPreset
162
- className="w-full"
163
- size={globalSize}
164
- label="Framework"
165
- description="Hỗ trợ hàng ngàn bản ghi mà không lag."
166
- placeholder="Chọn framework..."
167
- searchPlaceholder="Tìm kiếm framework..."
168
- emptyText="Không tìm thấy framework nào."
169
- options={[
170
- { value: "react", label: "React" },
171
- { value: "vue", label: "Vue" },
172
- { value: "angular", label: "Angular" },
173
- { value: "svelte", label: "Svelte" },
174
- ]}
175
- />
176
- </ExampleSection>
150
+ />`} preview={
151
+ <>
152
+ <ComboboxPreset
153
+ className="w-full"
154
+ size={globalSize}
155
+ label="Framework"
156
+ description="Hỗ trợ hàng ngàn bản ghi mà không lag."
157
+ placeholder="Chọn framework..."
158
+ searchPlaceholder="Tìm kiếm framework..."
159
+ emptyText="Không tìm thấy framework nào."
160
+ options={[
161
+ { value: "react", label: "React" },
162
+ { value: "vue", label: "Vue" },
163
+ { value: "angular", label: "Angular" },
164
+ { value: "svelte", label: "Svelte" },
165
+ ]}
166
+ />
167
+ </>
168
+ } />
177
169
 
178
- <ExampleSection
179
- label="Trạng thái Lỗi (Error)"
180
- description="Báo lỗi khi bỏ trống form."
181
- codeString={`<div className="w-full flex flex-col gap-6">
170
+ <ShowcaseExample title="Trạng thái Lỗi (Error)" description="Báo lỗi khi bỏ trống form." code={`<div className="w-full flex flex-col gap-6">
182
171
  <ComboboxPreset
183
172
  label="Khu vực (Lỗi)"
184
173
  placeholder="Chọn khu vực..."
@@ -187,25 +176,23 @@ function ComboboxMacroShowcase({ globalSize }: { globalSize: Size }) {
187
176
  ]}
188
177
  errorMessage="Khu vực là bắt buộc."
189
178
  />
190
- </div>`}
191
- >
192
- <div className="w-full flex flex-col gap-6">
193
- <ComboboxPreset
194
- size={globalSize}
195
- label="Khu vực (Lỗi)"
196
- placeholder="Chọn khu vực..."
197
- options={[{ value: "ap", label: "Châu Á Thái Bình Dương" }]}
198
- errorMessage="Khu vực là bắt buộc."
199
- />
200
- </div>
201
- </ExampleSection>
179
+ </div>`} preview={
180
+ <>
181
+ <div className="w-full flex flex-col gap-6">
182
+ <ComboboxPreset
183
+ size={globalSize}
184
+ label="Khu vực (Lỗi)"
185
+ placeholder="Chọn khu vực..."
186
+ options={[{ value: "ap", label: "Châu Á Thái Bình Dương" }]}
187
+ errorMessage="Khu vực là bắt buộc."
188
+ />
189
+ </div>
190
+ </>
191
+ } />
202
192
  </ExampleGrid>
203
193
 
204
194
  <ExampleGrid>
205
- <ExampleSection
206
- label="Khóa / Bất hoạt (Disabled)"
207
- description="Người dùng không thể click hay gõ vào ô."
208
- codeString={`<ComboboxPreset
195
+ <ShowcaseExample title="Khóa / Bất hoạt (Disabled)" description="Người dùng không thể click hay gõ vào ô." code={`<ComboboxPreset
209
196
  className="w-full"
210
197
  label="Đội nhóm (Team)"
211
198
  description="Bạn không có quyền thay đổi đội trong dự án này."
@@ -216,27 +203,25 @@ function ComboboxMacroShowcase({ globalSize }: { globalSize: Size }) {
216
203
  ]}
217
204
  value="engineering"
218
205
  disabled
219
- />`}
220
- >
221
- <ComboboxPreset
222
- className="w-full"
223
- size={globalSize}
224
- label="Đội nhóm (Team)"
225
- description="Bạn không có quyền thay đổi đội trong dự án này."
226
- placeholder="Chọn đội..."
227
- options={[
228
- { value: "engineering", label: "Engineering" },
229
- { value: "design", label: "Design" },
230
- ]}
231
- value="engineering"
232
- disabled
233
- />
234
- </ExampleSection>
206
+ />`} preview={
207
+ <>
208
+ <ComboboxPreset
209
+ className="w-full"
210
+ size={globalSize}
211
+ label="Đội nhóm (Team)"
212
+ description="Bạn không có quyền thay đổi đội trong dự án này."
213
+ placeholder="Chọn đội..."
214
+ options={[
215
+ { value: "engineering", label: "Engineering" },
216
+ { value: "design", label: "Design" },
217
+ ]}
218
+ value="engineering"
219
+ disabled
220
+ />
221
+ </>
222
+ } />
235
223
 
236
- <ExampleSection
237
- label="Cuộn danh sách dài (Long List)"
238
- description="Trải nghiệm mượt mà với danh sách ảo hoá."
239
- codeString={`<ComboboxPreset
224
+ <ShowcaseExample title="Cuộn danh sách dài (Long List)" description="Trải nghiệm mượt mà với danh sách ảo hoá." code={`<ComboboxPreset
240
225
  className="w-full"
241
226
 
242
227
  label="Múi giờ (Timezone)"
@@ -246,30 +231,29 @@ function ComboboxMacroShowcase({ globalSize }: { globalSize: Size }) {
246
231
  options={Array.from({ length: 50 }).map((_, i) => ({
247
232
  value: \`utc\${i - 12}\`,
248
233
  label: \`UTC \${i - 12 > 0 ? "+" : ""}\${i - 12}:00\` }))}
249
- />`}
250
- >
251
- <ComboboxPreset
252
- className="w-full"
253
- size={globalSize}
254
- label="Múi giờ (Timezone)"
255
- placeholder="Chọn múi giờ..."
256
- searchPlaceholder="Tìm kiếm múi giờ..."
257
- emptyText="Không tìm thấy múi giờ."
258
- options={Array.from({ length: 50 }).map((_, i) => ({
259
- value: `utc${i - 12}`,
260
- label: `UTC ${i - 12 > 0 ? "+" : ""}${i - 12}:00` }))}
261
- />
262
- </ExampleSection>
234
+ />`} preview={
235
+ <>
236
+ <ComboboxPreset
237
+ className="w-full"
238
+ size={globalSize}
239
+ label="Múi giờ (Timezone)"
240
+ placeholder="Chọn múi giờ..."
241
+ searchPlaceholder="Tìm kiếm múi giờ..."
242
+ emptyText="Không tìm thấy múi giờ."
243
+ options={Array.from({ length: 50 }).map((_, i) => ({
244
+ value: `utc${i - 12}`,
245
+ label: `UTC ${i - 12 > 0 ? "+" : ""}${i - 12}:00` }))}
246
+ />
247
+ </>
248
+ } />
263
249
  </ExampleGrid>
264
250
 
265
251
  <ExampleGrid>
266
- <ExampleSection
267
- label="Tích hợp React Hook Form"
268
- description="Sử dụng Controller để bắt giá trị."
269
- codeString={rhfCode}
270
- >
271
- <ComboboxForm size={globalSize} />
272
- </ExampleSection>
252
+ <ShowcaseExample title="Tích hợp React Hook Form" description="Sử dụng Controller để bắt giá trị." code={rhfCode} preview={
253
+ <>
254
+ <ComboboxForm size={globalSize} />
255
+ </>
256
+ } />
273
257
  </ExampleGrid>
274
258
  </div>
275
259
  );
@@ -280,12 +264,9 @@ function ComboboxMacroShowcase({ globalSize }: { globalSize: Size }) {
280
264
  // ──────────────────────────────────────────────────────────
281
265
  function ComboboxMicroShowcase({ globalSize }: { globalSize: Size }) {
282
266
  return (
283
- <div className="space-y-10 mt-6">
267
+ <div className="space-y-10">
284
268
  <ExampleGrid>
285
- <ExampleSection
286
- label="Cơ bản & Phân nhóm"
287
- description="Sử dụng InputGroup, ComboboxAnchor, ComboboxGroup."
288
- codeString={`<Field className="w-full">
269
+ <ShowcaseExample title="Cơ bản & Phân nhóm" description="Sử dụng InputGroup, ComboboxAnchor, ComboboxGroup." code={`<Field className="w-full">
289
270
  <FieldLabel>Tìm kiếm Framework</FieldLabel>
290
271
  <FieldContent>
291
272
  <Combobox items={allFrameworks}>
@@ -330,52 +311,50 @@ function ComboboxMicroShowcase({ globalSize }: { globalSize: Size }) {
330
311
 
331
312
  </Combobox>
332
313
  </FieldContent>
333
- </Field>`}
334
- >
335
- <Field size={globalSize} className="w-full">
336
- <FieldLabel>Tìm kiếm Framework</FieldLabel>
337
- <FieldContent>
338
- <Combobox items={allFrameworks}>
339
- <ComboboxAnchor className="w-full">
340
- <InputGroup size={globalSize} className="w-full">
341
- <ComboboxInput
342
- placeholder="Tìm framework..."
343
- render={<InputGroupInput />}
344
- />
345
- <InputGroupAddon align="end">
346
- <ComboboxTrigger className="flex h-full cursor-default items-center justify-center px-2.5 outline-none group-has-data-[slot=combobox-clear]/input-group:hidden disabled:cursor-not-allowed disabled:opacity-50" />
347
- <ComboboxClear />
348
- </InputGroupAddon>
349
- </InputGroup>
350
- </ComboboxAnchor>
314
+ </Field>`} preview={
315
+ <>
316
+ <Field size={globalSize} className="w-full">
317
+ <FieldLabel>Tìm kiếm Framework</FieldLabel>
318
+ <FieldContent>
319
+ <Combobox items={allFrameworks}>
320
+ <ComboboxAnchor className="w-full">
321
+ <InputGroup size={globalSize} className="w-full">
322
+ <ComboboxInput
323
+ placeholder="Tìm framework..."
324
+ render={<InputGroupInput />}
325
+ />
326
+ <InputGroupAddon align="end">
327
+ <ComboboxTrigger className="flex h-full cursor-default items-center justify-center px-2.5 outline-none group-has-data-[slot=combobox-clear]/input-group:hidden disabled:cursor-not-allowed disabled:opacity-50" />
328
+ <ComboboxClear />
329
+ </InputGroupAddon>
330
+ </InputGroup>
331
+ </ComboboxAnchor>
351
332
 
352
- <ComboboxContent>
353
- <ComboboxEmpty>Không tìm thấy.</ComboboxEmpty>
354
- <ComboboxList>
355
- <ComboboxGroup>
356
- <ComboboxLabel>Frontend</ComboboxLabel>
357
- <ComboboxItem value="react">React</ComboboxItem>
358
- <ComboboxItem value="vue">Vue</ComboboxItem>
359
- <ComboboxItem value="svelte">Svelte</ComboboxItem>
360
- </ComboboxGroup>
361
- <ComboboxSeparator />
362
- <ComboboxGroup>
363
- <ComboboxLabel>Backend</ComboboxLabel>
364
- <ComboboxItem value="express">Express</ComboboxItem>
365
- <ComboboxItem value="nest">NestJS</ComboboxItem>
366
- </ComboboxGroup>
367
- </ComboboxList>
368
- </ComboboxContent>
369
-
370
- </Combobox>
371
- </FieldContent>
372
- </Field>
373
- </ExampleSection>
333
+ <ComboboxContent>
334
+ <ComboboxEmpty>Không tìm thấy.</ComboboxEmpty>
335
+ <ComboboxList>
336
+ <ComboboxGroup>
337
+ <ComboboxLabel>Frontend</ComboboxLabel>
338
+ <ComboboxItem value="react">React</ComboboxItem>
339
+ <ComboboxItem value="vue">Vue</ComboboxItem>
340
+ <ComboboxItem value="svelte">Svelte</ComboboxItem>
341
+ </ComboboxGroup>
342
+ <ComboboxSeparator />
343
+ <ComboboxGroup>
344
+ <ComboboxLabel>Backend</ComboboxLabel>
345
+ <ComboboxItem value="express">Express</ComboboxItem>
346
+ <ComboboxItem value="nest">NestJS</ComboboxItem>
347
+ </ComboboxGroup>
348
+ </ComboboxList>
349
+ </ComboboxContent>
350
+
351
+ </Combobox>
352
+ </FieldContent>
353
+ </Field>
354
+ </>
355
+ } />
374
356
 
375
- <ExampleSection
376
- label="Chọn nhiều (Multi-select Chips)"
377
- description="Sử dụng ComboboxChips và ComboboxChip thay cho Input."
378
- codeString={`<Field className="w-full">
357
+ <ShowcaseExample title="Chọn nhiều (Multi-select Chips)" description="Sử dụng ComboboxChips và ComboboxChip thay cho Input." code={`<Field className="w-full">
379
358
  <FieldLabel>Gán thẻ (Tags)</FieldLabel>
380
359
  <FieldContent>
381
360
  <Combobox items={tagItems} multiple>
@@ -412,46 +391,44 @@ function ComboboxMicroShowcase({ globalSize }: { globalSize: Size }) {
412
391
  <FieldDescription>
413
392
  Bạn có thể chọn nhiều thẻ cùng lúc.
414
393
  </FieldDescription>
415
- </Field>`}
416
- >
417
- <Field size={globalSize} className="w-full">
418
- <FieldLabel>Gán thẻ (Tags)</FieldLabel>
419
- <FieldContent>
420
- <Combobox items={tagItems} multiple>
421
- <ComboboxChips size={globalSize}>
422
- <ComboboxChip value="bug">Lỗi (Bug)</ComboboxChip>
423
- <ComboboxChip value="feature">
424
- Tính năng (Feature)
425
- </ComboboxChip>
426
- <ComboboxChipsInput placeholder="Thêm thẻ..." />
427
- </ComboboxChips>
394
+ </Field>`} preview={
395
+ <>
396
+ <Field size={globalSize} className="w-full">
397
+ <FieldLabel>Gán thẻ (Tags)</FieldLabel>
398
+ <FieldContent>
399
+ <Combobox items={tagItems} multiple>
400
+ <ComboboxChips size={globalSize}>
401
+ <ComboboxChip value="bug">Lỗi (Bug)</ComboboxChip>
402
+ <ComboboxChip value="feature">
403
+ Tính năng (Feature)
404
+ </ComboboxChip>
405
+ <ComboboxChipsInput placeholder="Thêm thẻ..." />
406
+ </ComboboxChips>
428
407
 
429
- <ComboboxContent>
430
- <ComboboxEmpty>Không tìm thấy thẻ.</ComboboxEmpty>
431
- <ComboboxList>
432
- <ComboboxItem value="bug">Lỗi (Bug)</ComboboxItem>
433
- <ComboboxItem value="feature">
434
- Tính năng (Feature)
435
- </ComboboxItem>
436
- <ComboboxItem value="enhancement">Cải thiện</ComboboxItem>
437
- <ComboboxItem value="docs">Tài liệu</ComboboxItem>
438
- </ComboboxList>
439
- </ComboboxContent>
440
-
441
- </Combobox>
442
- </FieldContent>
443
- <FieldDescription>
444
- Bạn có thể chọn nhiều thẻ cùng lúc.
445
- </FieldDescription>
446
- </Field>
447
- </ExampleSection>
408
+ <ComboboxContent>
409
+ <ComboboxEmpty>Không tìm thấy thẻ.</ComboboxEmpty>
410
+ <ComboboxList>
411
+ <ComboboxItem value="bug">Lỗi (Bug)</ComboboxItem>
412
+ <ComboboxItem value="feature">
413
+ Tính năng (Feature)
414
+ </ComboboxItem>
415
+ <ComboboxItem value="enhancement">Cải thiện</ComboboxItem>
416
+ <ComboboxItem value="docs">Tài liệu</ComboboxItem>
417
+ </ComboboxList>
418
+ </ComboboxContent>
419
+
420
+ </Combobox>
421
+ </FieldContent>
422
+ <FieldDescription>
423
+ Bạn có thể chọn nhiều thẻ cùng lúc.
424
+ </FieldDescription>
425
+ </Field>
426
+ </>
427
+ } />
448
428
  </ExampleGrid>
449
429
 
450
430
  <ExampleGrid>
451
- <ExampleSection
452
- label="Disabled thủ công"
453
- description="Truyền disabled vào InputGroupInput và ComboboxTrigger."
454
- codeString={`<Field className="w-full">
431
+ <ShowcaseExample title="Disabled thủ công" description="Truyền disabled vào InputGroupInput và ComboboxTrigger." code={`<Field className="w-full">
455
432
  <FieldLabel>Tìm kiếm (Disabled)</FieldLabel>
456
433
  <FieldContent>
457
434
  <Combobox items={["react"]}>
@@ -481,43 +458,41 @@ function ComboboxMicroShowcase({ globalSize }: { globalSize: Size }) {
481
458
 
482
459
  </Combobox>
483
460
  </FieldContent>
484
- </Field>`}
485
- >
486
- <Field size={globalSize} className="w-full">
487
- <FieldLabel>Tìm kiếm (Disabled)</FieldLabel>
488
- <FieldContent>
489
- <Combobox items={["react"]}>
490
- <ComboboxAnchor className="w-full">
491
- <InputGroup size={globalSize} className="w-full">
492
- <ComboboxInput
493
- placeholder="Tìm kiếm..."
494
- disabled
495
- render={<InputGroupInput disabled />}
496
- />
497
- <InputGroupAddon align="end">
498
- <ComboboxTrigger
499
- className="flex h-full cursor-default items-center justify-center px-2.5 outline-none group-has-data-[slot=combobox-clear]/input-group:hidden disabled:cursor-not-allowed disabled:opacity-50"
500
- disabled
501
- />
502
- </InputGroupAddon>
503
- </InputGroup>
504
- </ComboboxAnchor>
461
+ </Field>`} preview={
462
+ <>
463
+ <Field size={globalSize} className="w-full">
464
+ <FieldLabel>Tìm kiếm (Disabled)</FieldLabel>
465
+ <FieldContent>
466
+ <Combobox items={["react"]}>
467
+ <ComboboxAnchor className="w-full">
468
+ <InputGroup size={globalSize} className="w-full">
469
+ <ComboboxInput
470
+ placeholder="Tìm kiếm..."
471
+ disabled
472
+ render={<InputGroupInput disabled />}
473
+ />
474
+ <InputGroupAddon align="end">
475
+ <ComboboxTrigger
476
+ className="flex h-full cursor-default items-center justify-center px-2.5 outline-none group-has-data-[slot=combobox-clear]/input-group:hidden disabled:cursor-not-allowed disabled:opacity-50"
477
+ disabled
478
+ />
479
+ </InputGroupAddon>
480
+ </InputGroup>
481
+ </ComboboxAnchor>
505
482
 
506
- <ComboboxContent>
507
- <ComboboxList>
508
- <ComboboxItem value="react">React</ComboboxItem>
509
- </ComboboxList>
510
- </ComboboxContent>
511
-
512
- </Combobox>
513
- </FieldContent>
514
- </Field>
515
- </ExampleSection>
483
+ <ComboboxContent>
484
+ <ComboboxList>
485
+ <ComboboxItem value="react">React</ComboboxItem>
486
+ </ComboboxList>
487
+ </ComboboxContent>
488
+
489
+ </Combobox>
490
+ </FieldContent>
491
+ </Field>
492
+ </>
493
+ } />
516
494
 
517
- <ExampleSection
518
- label="Trạng thái Lỗi (Invalid)"
519
- description="Gắn aria-invalid vào ô nhập liệu."
520
- codeString={`<Field data-invalid={true} className="w-full">
495
+ <ShowcaseExample title="Trạng thái Lỗi (Invalid)" description="Gắn aria-invalid vào ô nhập liệu." code={`<Field data-invalid={true} className="w-full">
521
496
  <FieldLabel>Mã giảm giá</FieldLabel>
522
497
  <FieldContent>
523
498
  <Combobox items={["sale20", "sale50"]}>
@@ -552,46 +527,44 @@ function ComboboxMicroShowcase({ globalSize }: { globalSize: Size }) {
552
527
  </Combobox>
553
528
  </FieldContent>
554
529
  <FieldError>Mã này đã hết hạn.</FieldError>
555
- </Field>`}
556
- >
557
- <Field size={globalSize} data-invalid={true} className="w-full">
558
- <FieldLabel>Mã giảm giá</FieldLabel>
559
- <FieldContent>
560
- <Combobox items={["sale20", "sale50"]}>
561
- <ComboboxAnchor className="w-full">
562
- <InputGroup size={globalSize} className="w-full">
563
- <ComboboxInput
564
- placeholder="Nhập mã..."
565
- aria-invalid={true}
566
- render={<InputGroupInput />}
567
- />
568
- <InputGroupAddon align="end">
569
- <ComboboxTrigger className="flex h-full cursor-default items-center justify-center px-2.5 outline-none group-has-data-[slot=combobox-clear]/input-group:hidden disabled:cursor-not-allowed disabled:opacity-50" />
570
- <ComboboxClear />
571
- </InputGroupAddon>
572
- </InputGroup>
573
- </ComboboxAnchor>
530
+ </Field>`} preview={
531
+ <>
532
+ <Field size={globalSize} data-invalid={true} className="w-full">
533
+ <FieldLabel>Mã giảm giá</FieldLabel>
534
+ <FieldContent>
535
+ <Combobox items={["sale20", "sale50"]}>
536
+ <ComboboxAnchor className="w-full">
537
+ <InputGroup size={globalSize} className="w-full">
538
+ <ComboboxInput
539
+ placeholder="Nhập mã..."
540
+ aria-invalid={true}
541
+ render={<InputGroupInput />}
542
+ />
543
+ <InputGroupAddon align="end">
544
+ <ComboboxTrigger className="flex h-full cursor-default items-center justify-center px-2.5 outline-none group-has-data-[slot=combobox-clear]/input-group:hidden disabled:cursor-not-allowed disabled:opacity-50" />
545
+ <ComboboxClear />
546
+ </InputGroupAddon>
547
+ </InputGroup>
548
+ </ComboboxAnchor>
574
549
 
575
- <ComboboxContent>
576
- <ComboboxEmpty>Không tìm thấy mã.</ComboboxEmpty>
577
- <ComboboxList>
578
- <ComboboxItem value="sale20">Giảm 20%</ComboboxItem>
579
- <ComboboxItem value="sale50">Giảm 50%</ComboboxItem>
580
- </ComboboxList>
581
- </ComboboxContent>
582
-
583
- </Combobox>
584
- </FieldContent>
585
- <FieldError>Mã này đã hết hạn.</FieldError>
586
- </Field>
587
- </ExampleSection>
550
+ <ComboboxContent>
551
+ <ComboboxEmpty>Không tìm thấy mã.</ComboboxEmpty>
552
+ <ComboboxList>
553
+ <ComboboxItem value="sale20">Giảm 20%</ComboboxItem>
554
+ <ComboboxItem value="sale50">Giảm 50%</ComboboxItem>
555
+ </ComboboxList>
556
+ </ComboboxContent>
557
+
558
+ </Combobox>
559
+ </FieldContent>
560
+ <FieldError>Mã này đã hết hạn.</FieldError>
561
+ </Field>
562
+ </>
563
+ } />
588
564
  </ExampleGrid>
589
565
 
590
566
  <ExampleGrid>
591
- <ExampleSection
592
- label="Giao diện Nút (With Trigger)"
593
- description="Combobox dùng nút bấm (giống Select) thay vì ô nhập chữ (Input)."
594
- codeString={`<Field className="w-full">
567
+ <ShowcaseExample title="Giao diện Nút (With Trigger)" description="Combobox dùng nút bấm (giống Select) thay vì ô nhập chữ (Input)." code={`<Field className="w-full">
595
568
  <FieldLabel>Chọn Engine</FieldLabel>
596
569
  <FieldContent>
597
570
  <Combobox items={engineItems}>
@@ -621,46 +594,42 @@ function ComboboxMicroShowcase({ globalSize }: { globalSize: Size }) {
621
594
 
622
595
  </Combobox>
623
596
  </FieldContent>
624
- </Field>`}
625
- >
626
- <Field size={globalSize} className="w-full">
627
- <FieldLabel>Chọn Engine</FieldLabel>
628
- <FieldContent>
629
- <Combobox items={engineItems}>
630
- <ComboboxTrigger className="w-full justify-between flex items-center border border-border rounded-md p-2 hover:bg-accent focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring">
631
- <ComboboxValue placeholder="Chọn một engine" />
632
- </ComboboxTrigger>
597
+ </Field>`} preview={
598
+ <>
599
+ <Field size={globalSize} className="w-full">
600
+ <FieldLabel>Chọn Engine</FieldLabel>
601
+ <FieldContent>
602
+ <Combobox items={engineItems}>
603
+ <ComboboxTrigger className="w-full justify-between flex items-center border border-border rounded-md p-2 hover:bg-accent focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring">
604
+ <ComboboxValue placeholder="Chọn một engine" />
605
+ </ComboboxTrigger>
633
606
 
634
- <ComboboxContent>
635
- <div className="p-1">
636
- <InputGroup size={globalSize} className="w-full">
637
- <ComboboxInput
638
- placeholder="Tìm engine..."
639
- render={<InputGroupInput />}
640
- />
641
- </InputGroup>
642
- </div>
643
- <ComboboxEmpty>Không tìm thấy.</ComboboxEmpty>
644
- <ComboboxList>
645
- <ComboboxItem value="v8">V8 (Chrome)</ComboboxItem>
646
- <ComboboxItem value="spidermonkey">
647
- SpiderMonkey (Firefox)
648
- </ComboboxItem>
649
- </ComboboxList>
650
- </ComboboxContent>
651
-
652
- </Combobox>
653
- </FieldContent>
654
- </Field>
655
- </ExampleSection>
607
+ <ComboboxContent>
608
+ <div className="p-1">
609
+ <InputGroup size={globalSize} className="w-full">
610
+ <ComboboxInput
611
+ placeholder="Tìm engine..."
612
+ render={<InputGroupInput />}
613
+ />
614
+ </InputGroup>
615
+ </div>
616
+ <ComboboxEmpty>Không tìm thấy.</ComboboxEmpty>
617
+ <ComboboxList>
618
+ <ComboboxItem value="v8">V8 (Chrome)</ComboboxItem>
619
+ <ComboboxItem value="spidermonkey">
620
+ SpiderMonkey (Firefox)
621
+ </ComboboxItem>
622
+ </ComboboxList>
623
+ </ComboboxContent>
624
+
625
+ </Combobox>
626
+ </FieldContent>
627
+ </Field>
628
+ </>
629
+ } />
656
630
  </ExampleGrid>
657
631
 
658
- <ExampleSection
659
- label="🧭 Use Case Comparison"
660
- description="So sánh nhanh khi nào dùng Micro và Macro."
661
- fullWidth
662
- codeString={`<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
663
- {/* Story 1: Macro wins */}
632
+ <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">
664
633
  <div className="rounded-xl border border-border bg-card p-5 flex flex-col gap-4">
665
634
  <div className="flex items-start gap-3">
666
635
  <span className="mt-0.5 shrink-0 rounded-full bg-green-500/10 p-1.5 text-green-600">
@@ -696,7 +665,6 @@ function ComboboxMicroShowcase({ globalSize }: { globalSize: Size }) {
696
665
  </p>
697
666
  </div>
698
667
 
699
- {/* Story 2: Micro wins */}
700
668
  <div className="rounded-xl border border-border bg-card p-5 flex flex-col gap-4">
701
669
  <div className="flex items-start gap-3">
702
670
  <span className="mt-0.5 shrink-0 rounded-full bg-blue-500/10 p-1.5 text-blue-600">
@@ -730,93 +698,89 @@ function ComboboxMicroShowcase({ globalSize }: { globalSize: Size }) {
730
698
  cụm <DocsCode>{\`<ComboboxChips />\`}</DocsCode>.
731
699
  </p>
732
700
  </div>
733
- </div>`}
734
- >
735
- <div className="grid grid-cols-1 gap-6 md:grid-cols-2">
736
- {/* Story 1: Macro wins */}
737
- <div className="rounded-xl border border-border bg-card p-5 flex flex-col gap-4">
738
- <div className="flex items-start gap-3">
739
- <span className="mt-0.5 shrink-0 rounded-full bg-green-500/10 p-1.5 text-green-600">
740
- <svg
741
- xmlns="http://www.w3.org/2000/svg"
742
- className="size-4"
743
- viewBox="0 0 24 24"
744
- fill="none"
745
- stroke="currentColor"
746
- strokeWidth="2"
747
- strokeLinecap="round"
748
- strokeLinejoin="round"
749
- aria-hidden="true"
750
- >
751
- <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
752
- </svg>
753
- </span>
754
- <div>
755
- <p className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">
756
- Story 1 · Dùng Macro
757
- </p>
758
- <h3 className="mt-0.5 text-sm font-semibold text-foreground">
759
- Single Select tự động hoàn thành
760
- </h3>
761
- </div>
762
- </div>
763
- <p className="text-xs text-muted-foreground leading-relaxed">
764
- Bạn cần một form chọn Thành phố. Số lượng thành phố quá lớn nên
765
- không dùng Select được, phải dùng Combobox để gõ tìm.{" "}
766
- <DocsCode>ComboboxPreset</DocsCode> sinh ra để giải quyết đúng bài
767
- toán chọn 1 giá trị từ tập lớn này.
768
- </p>
769
- </div>
701
+ </div>`} preview={
702
+ <>
703
+ <div className="grid grid-cols-1 gap-6 md:grid-cols-2">
704
+ <div className="rounded-xl border border-border bg-card p-5 flex flex-col gap-4">
705
+ <div className="flex items-start gap-3">
706
+ <span className="mt-0.5 shrink-0 rounded-full bg-green-500/10 p-1.5 text-green-600">
707
+ <svg
708
+ xmlns="http://www.w3.org/2000/svg"
709
+ className="size-4"
710
+ viewBox="0 0 24 24"
711
+ fill="none"
712
+ stroke="currentColor"
713
+ strokeWidth="2"
714
+ strokeLinecap="round"
715
+ strokeLinejoin="round"
716
+ aria-hidden="true"
717
+ >
718
+ <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
719
+ </svg>
720
+ </span>
721
+ <div>
722
+ <p className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">
723
+ Story 1 · Dùng Macro
724
+ </p>
725
+ <h3 className="mt-0.5 text-sm font-semibold text-foreground">
726
+ Single Select tự động hoàn thành
727
+ </h3>
728
+ </div>
729
+ </div>
730
+ <p className="text-xs text-muted-foreground leading-relaxed">
731
+ Bạn cần một form chọn Thành phố. Số lượng thành phố quá lớn nên
732
+ không dùng Select được, phải dùng Combobox để tìm.{" "}
733
+ <DocsCode>ComboboxPreset</DocsCode> sinh ra để giải quyết đúng bài
734
+ toán chọn 1 giá trị từ tập lớn này.
735
+ </p>
736
+ </div>
770
737
 
771
- {/* Story 2: Micro wins */}
772
- <div className="rounded-xl border border-border bg-card p-5 flex flex-col gap-4">
773
- <div className="flex items-start gap-3">
774
- <span className="mt-0.5 shrink-0 rounded-full bg-blue-500/10 p-1.5 text-blue-600">
775
- <svg
776
- xmlns="http://www.w3.org/2000/svg"
777
- className="size-4"
778
- viewBox="0 0 24 24"
779
- fill="none"
780
- stroke="currentColor"
781
- strokeWidth="2"
782
- strokeLinecap="round"
783
- strokeLinejoin="round"
784
- aria-hidden="true"
785
- >
786
- <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" />
787
- </svg>
788
- </span>
789
- <div>
790
- <p className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">
791
- Story 2 · Dùng Micro
792
- </p>
793
- <h3 className="mt-0.5 text-sm font-semibold text-foreground">
794
- Gán thẻ (Multi-select)
795
- </h3>
796
- </div>
797
- </div>
798
- <p className="text-xs text-muted-foreground leading-relaxed">
799
- Bạn làm form tạo bài viết mới cần gán nhiều Thẻ (Tags) cho bài
800
- viết. Macro không hỗ trợ Multi-select. Bạn buộc phải dùng Micro
801
- kết hợp với cụm <DocsCode>{`<ComboboxChips />`}</DocsCode>.
802
- </p>
803
- </div>
804
- </div>
805
- </ExampleSection>
738
+ <div className="rounded-xl border border-border bg-card p-5 flex flex-col gap-4">
739
+ <div className="flex items-start gap-3">
740
+ <span className="mt-0.5 shrink-0 rounded-full bg-blue-500/10 p-1.5 text-blue-600">
741
+ <svg
742
+ xmlns="http://www.w3.org/2000/svg"
743
+ className="size-4"
744
+ viewBox="0 0 24 24"
745
+ fill="none"
746
+ stroke="currentColor"
747
+ strokeWidth="2"
748
+ strokeLinecap="round"
749
+ strokeLinejoin="round"
750
+ aria-hidden="true"
751
+ >
752
+ <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" />
753
+ </svg>
754
+ </span>
755
+ <div>
756
+ <p className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">
757
+ Story 2 · Dùng Micro
758
+ </p>
759
+ <h3 className="mt-0.5 text-sm font-semibold text-foreground">
760
+ Gán thẻ (Multi-select)
761
+ </h3>
762
+ </div>
763
+ </div>
764
+ <p className="text-xs text-muted-foreground leading-relaxed">
765
+ Bạn làm form tạo bài viết mới và cần gán nhiều Thẻ (Tags) cho bài
766
+ viết. Macro không hỗ trợ Multi-select. Bạn buộc phải dùng Micro
767
+ kết hợp với cụm <DocsCode>{`<ComboboxChips />`}</DocsCode>.
768
+ </p>
769
+ </div>
770
+ </div>
771
+ </>
772
+ } />
806
773
  </div>
807
774
  );
808
775
  }
809
776
 
810
- // ──────────────────────────────────────────────────────────
811
- // SECTION 3: Entry point
812
- // ──────────────────────────────────────────────────────────
813
777
  export default function ComboboxShowcase() {
814
778
  const { size: globalSize } = useDevContext();
815
779
  return (
816
780
  <Showcase
817
781
  title="Combobox"
818
782
  description="Thành phần mở rộng của Select, cho phép người dùng gõ phím để tìm kiếm và lọc qua các tập dữ liệu lớn."
819
- generalConcept={
783
+ guideline={
820
784
  <ShowcaseDocs>
821
785
  <DocsP>
822
786
  Dùng để chọn một hoặc nhiều giá trị từ một danh sách lớn. Khác với
@@ -825,14 +789,8 @@ export default function ComboboxShowcase() {
825
789
  </DocsP>
826
790
  </ShowcaseDocs>
827
791
  }
828
- tabs={[
829
- {
830
- label: "Micro (Primitive)",
831
- content: <ComboboxMicroShowcase globalSize={globalSize} /> },
832
- {
833
- label: "Macro (Preset)",
834
- content: <ComboboxMacroShowcase globalSize={globalSize} /> },
835
- ]}
792
+ micro={{ content: <ComboboxMicroShowcase globalSize={globalSize} /> }}
793
+ macro={{ content: <ComboboxMacroShowcase globalSize={globalSize} /> }}
836
794
  />
837
795
  );
838
796
  }