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.
Files changed (134) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -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
@@ -19,122 +19,105 @@ import {
19
19
  DocsH3,
20
20
  DocsP,
21
21
  ExampleGrid,
22
- ExampleSection,
22
+ ShowcaseExample,
23
23
  Showcase,
24
24
  ShowcaseDocs
25
25
  } from "../../dev/components/showcase";
26
26
  import { type Size } from "../../lib/types";
27
27
 
28
- // ──────────────────────────────────────────────────────────
29
- // SECTION 1: Macro Content
30
- // ──────────────────────────────────────────────────────────
31
28
  function InputOTPMacroShowcase({ globalSize }: { globalSize: Size }) {
32
29
  return (
33
- <div className="space-y-10 mt-6">
30
+ <div className="space-y-10">
34
31
  <ExampleGrid>
35
- <ExampleSection
36
- label="Tiêu chuẩn (2 nhóm)"
37
- description="Chia mã 6 số thành 2 nhóm (mỗi nhóm 3 số) cách nhau bởi dấu gạch ngang."
38
- codeString={`<InputOTPPreset
32
+ <ShowcaseExample title="Tiêu chuẩn (2 nhóm)" description="Chia mã 6 số thành 2 nhóm (mỗi nhóm 3 số) cách nhau bởi dấu gạch ngang." code={`<InputOTPPreset
39
33
  groups={[3, 3]}
40
34
  size="md"
41
35
  label="Mã xác thực"
42
36
  description="Vui lòng nhập mã 6 số được gửi tới điện thoại của bạn."
43
- />`}
44
- >
45
- <InputOTPPreset
46
- className="w-full"
47
- groups={[3, 3]}
48
- size={globalSize}
49
- label="Mã xác thực"
50
- description="Vui lòng nhập mã 6 số được gửi tới điện thoại của bạn."
51
- />
52
- </ExampleSection>
37
+ />`} preview={
38
+ <>
39
+ <InputOTPPreset
40
+ className="w-full"
41
+ groups={[3, 3]}
42
+ size={globalSize}
43
+ label="Mã xác thực"
44
+ description="Vui lòng nhập mã 6 số được gửi tới điện thoại của bạn."
45
+ />
46
+ </>
47
+ } />
53
48
 
54
- <ExampleSection
55
- label="Trạng thái Lỗi (Error)"
56
- description="Hiển thị viền đỏ và thông báo lỗi khi nhập sai mã."
57
- codeString={`<InputOTPPreset
49
+ <ShowcaseExample title="Trạng thái Lỗi (Error)" description="Hiển thị viền đỏ và thông báo lỗi khi nhập sai mã." code={`<InputOTPPreset
58
50
  groups={[3, 3]}
59
51
  size="md"
60
52
  label="Mã bảo mật"
61
53
  errorMessage="Mã xác nhận không chính xác. Vui lòng thử lại."
62
- />`}
63
- >
64
- <InputOTPPreset
65
- className="w-full"
66
- groups={[3, 3]}
67
- size={globalSize}
68
- label="Mã bảo mật (Lỗi)"
69
- errorMessage="Mã xác nhận không chính xác. Vui lòng thử lại."
70
- />
71
- </ExampleSection>
54
+ />`} preview={
55
+ <>
56
+ <InputOTPPreset
57
+ className="w-full"
58
+ groups={[3, 3]}
59
+ size={globalSize}
60
+ label="Mã bảo mật (Lỗi)"
61
+ errorMessage="Mã xác nhận không chính xác. Vui lòng thử lại."
62
+ />
63
+ </>
64
+ } />
72
65
  </ExampleGrid>
73
66
 
74
67
  <ExampleGrid>
75
- <ExampleSection
76
- label="Khóa / Bất hoạt (Disabled)"
77
- description="Người dùng không thể nhập mã khi ở trạng thái này."
78
- codeString={`<InputOTPPreset
68
+ <ShowcaseExample title="Khóa / Bất hoạt (Disabled)" description="Người dùng không thể nhập mã khi ở trạng thái này." code={`<InputOTPPreset
79
69
  groups={[4]}
80
70
  size="md"
81
71
  label="Mã khôi phục"
82
72
  description="Tài khoản của bạn đang bị khóa tạm thời."
83
73
  disabled
84
- />`}
85
- >
86
- <InputOTPPreset
87
- className="w-full"
88
- groups={[4]}
89
- size={globalSize}
90
- label="Mã khôi phục"
91
- description="Tài khoản của bạn đang bị khóa tạm thời."
92
- disabled
93
- />
94
- </ExampleSection>
74
+ />`} preview={
75
+ <>
76
+ <InputOTPPreset
77
+ className="w-full"
78
+ groups={[4]}
79
+ size={globalSize}
80
+ label="Mã khôi phục"
81
+ description="Tài khoản của bạn đang bị khóa tạm thời."
82
+ disabled
83
+ />
84
+ </>
85
+ } />
95
86
 
96
- <ExampleSection
97
- label="Tuỳ biến Nhóm & Dấu phân cách"
98
- description="Dùng mảng groups tuỳ ý và đổi icon phân cách."
99
- codeString={`<InputOTPPreset
87
+ <ShowcaseExample title="Tuỳ biến Nhóm & Dấu phân cách" description="Dùng mảng groups tuỳ ý và đổi icon phân cách." code={`<InputOTPPreset
100
88
  groups={[2, 2, 2]}
101
89
  separator={
102
90
  <span className="text-muted-foreground/50">/</span>
103
91
  }
104
92
  size="md"
105
93
  label="Ngày sinh (DD/MM/YY)"
106
- />`}
107
- >
108
- <div className="w-full flex flex-col gap-6">
109
- <InputOTPPreset
110
- groups={[2, 2, 2]}
111
- separator={
112
- <span className="text-muted-foreground/50 text-sm font-semibold">
113
- /
114
- </span>
115
- }
116
- size={globalSize}
117
- label="Định dạng tuỳ chỉnh"
118
- description="Chia thành 3 nhóm, phân cách bằng dấu gạch chéo."
119
- />
120
- </div>
121
- </ExampleSection>
94
+ />`} preview={
95
+ <>
96
+ <div className="w-full flex flex-col gap-6">
97
+ <InputOTPPreset
98
+ groups={[2, 2, 2]}
99
+ separator={
100
+ <span className="text-muted-foreground/50 text-sm font-semibold">
101
+ /
102
+ </span>
103
+ }
104
+ size={globalSize}
105
+ label="Định dạng tuỳ chỉnh"
106
+ description="Chia thành 3 nhóm, phân cách bằng dấu gạch chéo."
107
+ />
108
+ </div>
109
+ </>
110
+ } />
122
111
  </ExampleGrid>
123
112
  </div>
124
113
  );
125
114
  }
126
115
 
127
- // ──────────────────────────────────────────────────────────
128
- // SECTION 2: Micro Content
129
- // ──────────────────────────────────────────────────────────
130
116
  function InputOTPMicroShowcase({ globalSize }: { globalSize: Size }) {
131
117
  return (
132
- <div className="space-y-10 mt-6">
118
+ <div className="space-y-10">
133
119
  <ExampleGrid>
134
- <ExampleSection
135
- label="Cơ bản (Primitive)"
136
- description="Mã 6 chữ số liền mạch không có dấu phân cách."
137
- codeString={`<Field size="md">
120
+ <ShowcaseExample title="Cơ bản (Primitive)" description="Mã 6 chữ số liền mạch không có dấu phân cách." code={`<Field size="md">
138
121
  <FieldLabel htmlFor="otp-basic">Mã PIN</FieldLabel>
139
122
  <FieldContent>
140
123
  <InputOTP id="otp-basic" size="md" maxLength={6}>
@@ -145,29 +128,27 @@ function InputOTPMicroShowcase({ globalSize }: { globalSize: Size }) {
145
128
  </InputOTPGroup>
146
129
  </InputOTP>
147
130
  </FieldContent>
148
- </Field>`}
149
- >
150
- <Field className="w-full" size={globalSize}>
151
- <FieldLabel htmlFor="tf-otp-0">Mã PIN</FieldLabel>
152
- <FieldContent>
153
- <InputOTP id="tf-otp-0" size={globalSize} maxLength={6}>
154
- <InputOTPGroup>
155
- {Array.from({ length: 6 }).map((_, index) => (
156
- <InputOTPSlot
157
- key={`tf-otp-0-slot-${index}`}
158
- index={index}
159
- />
160
- ))}
161
- </InputOTPGroup>
162
- </InputOTP>
163
- </FieldContent>
164
- </Field>
165
- </ExampleSection>
131
+ </Field>`} preview={
132
+ <>
133
+ <Field className="w-full" size={globalSize}>
134
+ <FieldLabel htmlFor="tf-otp-0">Mã PIN</FieldLabel>
135
+ <FieldContent>
136
+ <InputOTP id="tf-otp-0" size={globalSize} maxLength={6}>
137
+ <InputOTPGroup>
138
+ {Array.from({ length: 6 }).map((_, index) => (
139
+ <InputOTPSlot
140
+ key={`tf-otp-0-slot-${index}`}
141
+ index={index}
142
+ />
143
+ ))}
144
+ </InputOTPGroup>
145
+ </InputOTP>
146
+ </FieldContent>
147
+ </Field>
148
+ </>
149
+ } />
166
150
 
167
- <ExampleSection
168
- label="Phân tách thủ công"
169
- description="Tự chèn InputOTPSeparator vào giữa các nhóm."
170
- codeString={`<Field size="md">
151
+ <ShowcaseExample title="Phân tách thủ công" description="Tự chèn InputOTPSeparator vào giữa các nhóm." code={`<Field size="md">
171
152
  <FieldLabel htmlFor="otp-sep">Mã bảo mật</FieldLabel>
172
153
  <FieldContent>
173
154
  <InputOTP id="otp-sep" size="md" maxLength={6}>
@@ -184,40 +165,38 @@ function InputOTPMicroShowcase({ globalSize }: { globalSize: Size }) {
184
165
  </InputOTPGroup>
185
166
  </InputOTP>
186
167
  </FieldContent>
187
- </Field>`}
188
- >
189
- <Field className="w-full" size={globalSize}>
190
- <FieldLabel htmlFor="tf-otp-1">Mã bảo mật</FieldLabel>
191
- <FieldContent>
192
- <InputOTP id="tf-otp-1" size={globalSize} maxLength={6}>
193
- <InputOTPGroup>
194
- {Array.from({ length: 3 }).map((_, index) => (
195
- <InputOTPSlot
196
- key={`tf-otp-1-slot-a-${index}`}
197
- index={index}
198
- />
199
- ))}
200
- </InputOTPGroup>
201
- <InputOTPSeparator />
202
- <InputOTPGroup>
203
- {Array.from({ length: 3 }).map((_, index) => (
204
- <InputOTPSlot
205
- key={`tf-otp-1-slot-b-${index + 3}`}
206
- index={index + 3}
207
- />
208
- ))}
209
- </InputOTPGroup>
210
- </InputOTP>
211
- </FieldContent>
212
- </Field>
213
- </ExampleSection>
168
+ </Field>`} preview={
169
+ <>
170
+ <Field className="w-full" size={globalSize}>
171
+ <FieldLabel htmlFor="tf-otp-1">Mã bảo mật</FieldLabel>
172
+ <FieldContent>
173
+ <InputOTP id="tf-otp-1" size={globalSize} maxLength={6}>
174
+ <InputOTPGroup>
175
+ {Array.from({ length: 3 }).map((_, index) => (
176
+ <InputOTPSlot
177
+ key={`tf-otp-1-slot-a-${index}`}
178
+ index={index}
179
+ />
180
+ ))}
181
+ </InputOTPGroup>
182
+ <InputOTPSeparator />
183
+ <InputOTPGroup>
184
+ {Array.from({ length: 3 }).map((_, index) => (
185
+ <InputOTPSlot
186
+ key={`tf-otp-1-slot-b-${index + 3}`}
187
+ index={index + 3}
188
+ />
189
+ ))}
190
+ </InputOTPGroup>
191
+ </InputOTP>
192
+ </FieldContent>
193
+ </Field>
194
+ </>
195
+ } />
214
196
  </ExampleGrid>
215
197
 
216
198
  <ExampleGrid>
217
- <ExampleSection
218
- label="Trạng thái Lỗi thủ công"
219
- description="Gắn data-invalid vào Field để thay đổi màu sắc."
220
- codeString={`<Field size="md" data-invalid={true}>
199
+ <ShowcaseExample title="Trạng thái Lỗi thủ công" description="Gắn data-invalid vào Field để thay đổi màu sắc." code={`<Field size="md" data-invalid={true}>
221
200
  <FieldLabel htmlFor="otp-err">Mã xác minh</FieldLabel>
222
201
  <FieldContent>
223
202
  <InputOTP id="otp-err" size="md" maxLength={4}>
@@ -229,30 +208,28 @@ function InputOTPMicroShowcase({ globalSize }: { globalSize: Size }) {
229
208
  </InputOTP>
230
209
  </FieldContent>
231
210
  <FieldError>Mã không hợp lệ.</FieldError>
232
- </Field>`}
233
- >
234
- <Field className="w-full" size={globalSize} data-invalid={true}>
235
- <FieldLabel htmlFor="tf-otp-err">Mã xác minh</FieldLabel>
236
- <FieldContent>
237
- <InputOTP id="tf-otp-err" size={globalSize} maxLength={4}>
238
- <InputOTPGroup>
239
- {Array.from({ length: 4 }).map((_, index) => (
240
- <InputOTPSlot
241
- key={`tf-otp-err-slot-${index}`}
242
- index={index}
243
- />
244
- ))}
245
- </InputOTPGroup>
246
- </InputOTP>
247
- </FieldContent>
248
- <FieldError>Mã không hợp lệ.</FieldError>
249
- </Field>
250
- </ExampleSection>
211
+ </Field>`} preview={
212
+ <>
213
+ <Field className="w-full" size={globalSize} data-invalid={true}>
214
+ <FieldLabel htmlFor="tf-otp-err">Mã xác minh</FieldLabel>
215
+ <FieldContent>
216
+ <InputOTP id="tf-otp-err" size={globalSize} maxLength={4}>
217
+ <InputOTPGroup>
218
+ {Array.from({ length: 4 }).map((_, index) => (
219
+ <InputOTPSlot
220
+ key={`tf-otp-err-slot-${index}`}
221
+ index={index}
222
+ />
223
+ ))}
224
+ </InputOTPGroup>
225
+ </InputOTP>
226
+ </FieldContent>
227
+ <FieldError>Mã không hợp lệ.</FieldError>
228
+ </Field>
229
+ </>
230
+ } />
251
231
 
252
- <ExampleSection
253
- label="Disabled thủ công"
254
- description="Truyền thuộc tính disabled."
255
- codeString={`<Field size="md">
232
+ <ShowcaseExample title="Disabled thủ công" description="Truyền thuộc tính disabled." code={`<Field size="md">
256
233
  <FieldLabel htmlFor="otp-disabled">Mã OTP</FieldLabel>
257
234
  <FieldContent>
258
235
  <InputOTP
@@ -268,38 +245,34 @@ function InputOTPMicroShowcase({ globalSize }: { globalSize: Size }) {
268
245
  </InputOTPGroup>
269
246
  </InputOTP>
270
247
  </FieldContent>
271
- </Field>`}
272
- >
273
- <Field className="w-full" size={globalSize}>
274
- <FieldLabel htmlFor="tf-otp-d">Mã OTP</FieldLabel>
275
- <FieldContent>
276
- <InputOTP id="tf-otp-d" size={globalSize} maxLength={4} disabled>
277
- <InputOTPGroup>
278
- {Array.from({ length: 4 }).map((_, index) => (
279
- <InputOTPSlot
280
- key={`tf-otp-d-slot-${index}`}
281
- index={index}
282
- />
283
- ))}
284
- </InputOTPGroup>
285
- </InputOTP>
286
- </FieldContent>
287
- </Field>
288
- </ExampleSection>
248
+ </Field>`} preview={
249
+ <>
250
+ <Field className="w-full" size={globalSize}>
251
+ <FieldLabel htmlFor="tf-otp-d">Mã OTP</FieldLabel>
252
+ <FieldContent>
253
+ <InputOTP id="tf-otp-d" size={globalSize} maxLength={4} disabled>
254
+ <InputOTPGroup>
255
+ {Array.from({ length: 4 }).map((_, index) => (
256
+ <InputOTPSlot
257
+ key={`tf-otp-d-slot-${index}`}
258
+ index={index}
259
+ />
260
+ ))}
261
+ </InputOTPGroup>
262
+ </InputOTP>
263
+ </FieldContent>
264
+ </Field>
265
+ </>
266
+ } />
289
267
  </ExampleGrid>
290
268
 
291
- <ExampleSection
292
- label="🧭 Use Case Comparison"
293
- description="So sánh nhanh khi nào dùng Micro và Macro."
294
- fullWidth
295
- codeString={`{/* Macro — đơn giản, 1 dòng khai báo */}
269
+ <ShowcaseExample title="🧭 So sánh Use Case" description="So sánh nhanh khi nào dùng Micro và Macro." code={`{}
296
270
  <InputOTPPreset
297
271
  groups={[3, 3]}
298
272
  size="md"
299
273
  label="Mã xác thực"
300
274
  />
301
275
 
302
- {/* Micro — khi cần custom từng slot */}
303
276
  <InputOTP size="md" maxLength={6}>
304
277
  <InputOTPGroup>
305
278
  {Array.from({ length: 3 }).map((_, i) => (
@@ -312,56 +285,57 @@ function InputOTPMicroShowcase({ globalSize }: { globalSize: Size }) {
312
285
  <InputOTPSlot key={i + 3} index={i + 3} />
313
286
  ))}
314
287
  </InputOTPGroup>
315
- </InputOTP>`}
316
- >
317
- <div className="grid grid-cols-1 gap-6 md:grid-cols-2">
318
- {/* Story 1: Macro wins */}
319
- <div className="rounded-xl border border-border bg-card p-5 flex flex-col gap-4">
320
- <div className="flex items-start gap-3">
321
- <span className="mt-0.5 shrink-0 rounded-full bg-green-500/10 p-1.5 text-green-600">
322
- <ShieldIcon className="size-4" aria-hidden="true" />
323
- </span>
324
- <div>
325
- <p className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">
326
- Story 1 · Dùng Macro
327
- </p>
328
- <h3 className="mt-0.5 text-sm font-semibold text-foreground">
329
- Form xác thực SMS thông thường
330
- </h3>
331
- </div>
332
- </div>
333
- <p className="text-xs text-muted-foreground leading-relaxed">
334
- Thay vì phải dùng vòng lặp <DocsCode>.map()</DocsCode> dài dòng
335
- cho từng nhóm số, dùng Preset{" "}
336
- <DocsCode>groups={`{[3, 3]}`}</DocsCode> để code sinh ra y chang
337
- chỉ với 1 dòng khai báo. Cực kỳ sạch.
338
- </p>
339
- </div>
288
+ </InputOTP>`} preview={
289
+ <>
290
+ <div className="grid grid-cols-1 gap-6 md:grid-cols-2">
291
+ {/* Story 1: Macro wins */}
292
+ <div className="rounded-xl border border-border bg-card p-5 flex flex-col gap-4">
293
+ <div className="flex items-start gap-3">
294
+ <span className="mt-0.5 shrink-0 rounded-full bg-green-500/10 p-1.5 text-green-600">
295
+ <ShieldIcon className="size-4" aria-hidden="true" />
296
+ </span>
297
+ <div>
298
+ <p className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">
299
+ Story 1 · Dùng Macro
300
+ </p>
301
+ <h3 className="mt-0.5 text-sm font-semibold text-foreground">
302
+ Form xác thực SMS thông thường
303
+ </h3>
304
+ </div>
305
+ </div>
306
+ <p className="text-xs text-muted-foreground leading-relaxed">
307
+ Thay vì phải dùng vòng lặp <DocsCode>.map()</DocsCode> dài dòng
308
+ cho từng nhóm số, dùng Preset{" "}
309
+ <DocsCode>groups={`{[3, 3]}`}</DocsCode> để code sinh ra y chang
310
+ chỉ với 1 dòng khai báo. Cực kỳ sạch.
311
+ </p>
312
+ </div>
340
313
 
341
- {/* Story 2: Micro wins */}
342
- <div className="rounded-xl border border-border bg-card p-5 flex flex-col gap-4">
343
- <div className="flex items-start gap-3">
344
- <span className="mt-0.5 shrink-0 rounded-full bg-blue-500/10 p-1.5 text-blue-600">
345
- <WrenchIcon className="size-4" aria-hidden="true" />
346
- </span>
347
- <div>
348
- <p className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">
349
- Story 2 · Dùng Micro
350
- </p>
351
- <h3 className="mt-0.5 text-sm font-semibold text-foreground">
352
- Custom Slot Design
353
- </h3>
354
- </div>
355
- </div>
356
- <p className="text-xs text-muted-foreground leading-relaxed">
357
- Bạn cần can thiệp trực tiếp vào từng ô số (ví dụ: style lại các ô
358
- đầu là số, ô cuối là chữ cái đặc biệt, hoặc xen kẽ Icon vào giữa
359
- thay vì dấu gạch ngang). Lúc này bạn buộc phải viết vòng lặp{" "}
360
- <DocsCode>.map()</DocsCode> và dùng Micro.
361
- </p>
362
- </div>
363
- </div>
364
- </ExampleSection>
314
+ {/* Story 2: Micro wins */}
315
+ <div className="rounded-xl border border-border bg-card p-5 flex flex-col gap-4">
316
+ <div className="flex items-start gap-3">
317
+ <span className="mt-0.5 shrink-0 rounded-full bg-blue-500/10 p-1.5 text-blue-600">
318
+ <WrenchIcon className="size-4" aria-hidden="true" />
319
+ </span>
320
+ <div>
321
+ <p className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">
322
+ Story 2 · Dùng Micro
323
+ </p>
324
+ <h3 className="mt-0.5 text-sm font-semibold text-foreground">
325
+ Custom Slot Design
326
+ </h3>
327
+ </div>
328
+ </div>
329
+ <p className="text-xs text-muted-foreground leading-relaxed">
330
+ Bạn cần can thiệp trực tiếp vào từng ô số (ví dụ: style lại các ô
331
+ đầu là số, ô cuối là chữ cái đặc biệt, hoặc xen kẽ Icon vào giữa
332
+ thay vì dấu gạch ngang). Lúc này bạn buộc phải viết vòng lặp{" "}
333
+ <DocsCode>.map()</DocsCode> và dùng Micro.
334
+ </p>
335
+ </div>
336
+ </div>
337
+ </>
338
+ } />
365
339
  </div>
366
340
  );
367
341
  }
@@ -375,38 +349,18 @@ export default function InputOTPShowcase() {
375
349
  <Showcase
376
350
  title="Input OTP"
377
351
  description="Trường nhập mật khẩu một lần (One-Time Password) có khả năng tự động tách chữ số và paste."
378
- generalConcept={
352
+ guideline={
379
353
  <ShowcaseDocs>
380
354
  <DocsP>
381
355
  Dùng để nhập các đoạn mã có chiều dài cố định như OTP, PIN code,
382
356
  2FA.
383
357
  </DocsP>
384
- <DocsH3>Khi nào nên dùng Macro</DocsH3>
385
- <DocsP>
386
- <DocsCode>InputOTPPreset</DocsCode> tự động sinh ra các nhóm số
387
- (`InputOTPGroup`) và dấu phân cách (`InputOTPSeparator`) dựa vào
388
- prop <DocsCode>groups</DocsCode> (ví dụ: `groups={[3, 3]}`). Dùng
389
- Preset này cho 99% các trường hợp form nhập mã xác thực (OTP) vì nó
390
- đã tích hợp sẵn Label và Error Message.
391
- </DocsP>
392
- <DocsH3>Khi nào nên dùng Micro</DocsH3>
393
- <DocsP>
394
- Dùng <DocsCode>InputOTP</DocsCode> và{" "}
395
- <DocsCode>InputOTPGroup</DocsCode> thủ công khi bạn cần một giao
396
- diện nhập mã đặc thù không theo chuẩn (ví dụ: các ô số nằm rải rác
397
- trong một thiết kế đồ hoạ) hoặc khi chèn trực tiếp vào một luồng
398
- không có Label đi kèm.
399
- </DocsP>
358
+
359
+
400
360
  </ShowcaseDocs>
401
361
  }
402
- tabs={[
403
- {
404
- label: "Micro (Primitive)",
405
- content: <InputOTPMicroShowcase globalSize={globalSize} /> },
406
- {
407
- label: "Macro (Preset)",
408
- content: <InputOTPMacroShowcase globalSize={globalSize} /> },
409
- ]}
362
+ micro={{ content: <InputOTPMicroShowcase globalSize={globalSize} /> }}
363
+ macro={{ content: <InputOTPMacroShowcase globalSize={globalSize} /> }}
410
364
  />
411
365
  );
412
366
  }