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
@@ -20,68 +20,58 @@ import {
20
20
  DocsH3,
21
21
  DocsP,
22
22
  ExampleGrid,
23
- ExampleSection,
23
+ ShowcaseExample,
24
24
  Showcase,
25
25
  ShowcaseDocs
26
26
  } from "../../dev/components/showcase";
27
27
  import { type Size } from "../../lib/types";
28
28
 
29
- // ──────────────────────────────────────────────────────────
30
- // SECTION 1: Macro Content
31
- // ──────────────────────────────────────────────────────────
32
29
  function TextareaMacroShowcase({ globalSize }: { globalSize: Size }) {
33
30
  const [bio, setBio] = useState("");
34
31
  const maxLength = 280;
35
32
 
36
33
  return (
37
- <div className="space-y-10 mt-6">
34
+ <div className="space-y-10">
38
35
  <ExampleGrid>
39
- <ExampleSection
40
- label="Tiêu chuẩn"
41
- description="Trường văn bản nhiều dòng với nhãn và mô tả."
42
- codeString={`<TextareaPreset
36
+ <ShowcaseExample title="Tiêu chuẩn" description="Trường văn bản nhiều dòng với nhãn và mô tả." code={`<TextareaPreset
43
37
  label="Phản hồi (Feedback)"
44
38
  description="Đóng góp ý kiến của bạn về sản phẩm."
45
39
  placeholder="Viết phản hồi..."
46
40
  className="w-full"
47
- />`}
48
- >
49
- <TextareaPreset
50
- label="Phản hồi (Feedback)"
51
- description="Đóng góp ý kiến của bạn về sản phẩm."
52
- placeholder="Viết phản hồi..."
53
- size={globalSize}
54
- className="w-full"
55
- />
56
- </ExampleSection>
41
+ />`} preview={
42
+ <>
43
+ <TextareaPreset
44
+ label="Phản hồi (Feedback)"
45
+ description="Đóng góp ý kiến của bạn về sản phẩm."
46
+ placeholder="Viết phản hồi..."
47
+ size={globalSize}
48
+ className="w-full"
49
+ />
50
+ </>
51
+ } />
57
52
 
58
- <ExampleSection
59
- label="Lỗi xác thực (Validation Error)"
60
- description="Hiển thị thông báo lỗi khi đầu vào không hợp lệ."
61
- codeString={`<TextareaPreset
53
+ <ShowcaseExample title="Lỗi xác thực (Validation Error)" description="Hiển thị thông báo lỗi khi đầu vào không hợp lệ." code={`<TextareaPreset
62
54
  label="Khiếu nại"
63
55
  placeholder="Mô tả vấn đề..."
64
56
  errorMessage="Vui lòng cung cấp ít nhất 20 ký tự để chúng tôi hỗ trợ tốt hơn."
65
57
  className="w-full"
66
58
  rows={3}
67
- />`}
68
- >
69
- <TextareaPreset
70
- label="Khiếu nại"
71
- placeholder="Mô tả vấn đề..."
72
- errorMessage="Vui lòng cung cấp ít nhất 20 ký tự để chúng tôi hỗ trợ tốt hơn."
73
- size={globalSize}
74
- className="w-full"
75
- rows={3}
76
- />
77
- </ExampleSection>
59
+ />`} preview={
60
+ <>
61
+ <TextareaPreset
62
+ label="Khiếu nại"
63
+ placeholder="Mô tả vấn đề..."
64
+ errorMessage="Vui lòng cung cấp ít nhất 20 ký tự để chúng tôi hỗ trợ tốt hơn."
65
+ size={globalSize}
66
+ className="w-full"
67
+ rows={3}
68
+ />
69
+ </>
70
+ } />
78
71
  </ExampleGrid>
79
72
 
80
73
  <ExampleGrid>
81
- <ExampleSection
82
- label="Giới hạn ký tự (Character Count)"
83
- description="Sử dụng React state và slot description để đếm ngược số ký tự."
84
- codeString={`const [bio, setBio] = useState(""); const maxLength = 280;
74
+ <ShowcaseExample title="Giới hạn ký tự (Character Count)" description="Sử dụng React state và slot description để đếm ngược số ký tự." code={`const [bio, setBio] = useState(""); const maxLength = 280;
85
75
  return (
86
76
  <TextareaPreset
87
77
  label="Tiểu sử"
@@ -110,65 +100,60 @@ function TextareaMacroShowcase({ globalSize }: { globalSize: Size }) {
110
100
  }
111
101
  rows={4}
112
102
  />
113
- );`}
114
- >
115
- <TextareaPreset
116
- label="Tiểu sử"
117
- description={
118
- <span className="flex justify-between">
119
- <span>Mô tả ngắn gọn về bản thân bạn.</span>
120
- <span
121
- data-invalid={bio.length > maxLength ? "" : undefined}
122
- className="data-invalid:text-destructive data-invalid:font-medium"
123
- >
124
- {bio.length}/{maxLength}
125
- </span>
126
- </span>
127
- }
128
- placeholder="I'm a developer who loves..."
129
- value={bio}
130
- onChange={(e) => setBio(e.target.value)}
131
- maxLength={maxLength}
132
- errorMessage={
133
- bio.length > maxLength
134
- ? `Vượt quá ${bio.length - maxLength} ký tự.`
135
- : undefined
136
- }
137
- size={globalSize}
138
- className="w-full"
139
- rows={4}
140
- />
141
- </ExampleSection>
103
+ );`} preview={
104
+ <>
105
+ <TextareaPreset
106
+ label="Tiểu sử"
107
+ description={
108
+ <span className="flex justify-between">
109
+ <span>Mô tả ngắn gọn về bản thân bạn.</span>
110
+ <span
111
+ data-invalid={bio.length > maxLength ? "" : undefined}
112
+ className="data-invalid:text-destructive data-invalid:font-medium"
113
+ >
114
+ {bio.length}/{maxLength}
115
+ </span>
116
+ </span>
117
+ }
118
+ placeholder="I'm a developer who loves..."
119
+ value={bio}
120
+ onChange={(e) => setBio(e.target.value)}
121
+ maxLength={maxLength}
122
+ errorMessage={
123
+ bio.length > maxLength
124
+ ? `Vượt quá ${bio.length - maxLength} ký tự.`
125
+ : undefined
126
+ }
127
+ size={globalSize}
128
+ className="w-full"
129
+ rows={4}
130
+ />
131
+ </>
132
+ } />
142
133
 
143
- <ExampleSection
144
- label="Khóa / Bất hoạt (Disabled)"
145
- description="Trường văn bản không thể tương tác."
146
- codeString={`<TextareaPreset
134
+ <ShowcaseExample title="Khóa / Bất hoạt (Disabled)" description="Trường văn bản không thể tương tác." code={`<TextareaPreset
147
135
  label="Ghi chú lưu trữ"
148
136
  description="Những ghi chú này ở dạng chỉ đọc."
149
137
  defaultValue="Dự án này đã hoàn thành vào năm 2023. Không thể thay đổi nội dung."
150
138
  disabled
151
139
  className="w-full"
152
140
  rows={4}
153
- />`}
154
- >
155
- <TextareaPreset
156
- label="Ghi chú lưu trữ"
157
- description="Những ghi chú này ở dạng chỉ đọc."
158
- defaultValue="Dự án này đã hoàn thành vào năm 2023. Không thể thay đổi nội dung."
159
- disabled
160
- size={globalSize}
161
- className="w-full"
162
- rows={4}
163
- />
164
- </ExampleSection>
141
+ />`} preview={
142
+ <>
143
+ <TextareaPreset
144
+ label="Ghi chú lưu trữ"
145
+ description="Những ghi chú này ở dạng chỉ đọc."
146
+ defaultValue="Dự án này đã hoàn thành vào năm 2023. Không thể thay đổi nội dung."
147
+ disabled
148
+ size={globalSize}
149
+ className="w-full"
150
+ rows={4}
151
+ />
152
+ </>
153
+ } />
165
154
  </ExampleGrid>
166
155
 
167
- <ExampleSection
168
- label="Thực tế sử dụng (Real-world Form)"
169
- description="Bố cục thực tế trong một form hoàn chỉnh."
170
- fullWidth
171
- codeString={`<div className="w-full max-w-lg rounded-xl border border-border bg-card p-5 shadow-sm">
156
+ <ShowcaseExample title="Thực tế sử dụng (Real-world Form)" description="Bố cục thực tế trong một form hoàn chỉnh." code={`<div className="w-full max-w-lg rounded-xl border border-border bg-card p-5 shadow-sm">
172
157
  <h3 className="mb-4 font-semibold">
173
158
  Tạo Ticket Hỗ Trợ
174
159
  </h3>
@@ -179,20 +164,21 @@ function TextareaMacroShowcase({ globalSize }: { globalSize: Size }) {
179
164
  className="w-full"
180
165
  rows={6}
181
166
  />
182
- </div>`}
183
- >
184
- <div className="w-full max-w-lg rounded-xl border border-border bg-card p-5 shadow-sm">
185
- <h3 className="mb-4 font-semibold">Tạo Ticket Hỗ Trợ</h3>
186
- <TextareaPreset
187
- label="Mô tả sự cố"
188
- description="Vui lòng cung cấp chi tiết lỗi, các bước tái hiện và cấu hình môi trường."
189
- placeholder="Ví dụ: Khi tôi click vào nút Submit, hệ thống báo lỗi 500..."
190
- size={globalSize}
191
- className="w-full"
192
- rows={6}
193
- />
194
- </div>
195
- </ExampleSection>
167
+ </div>`} preview={
168
+ <>
169
+ <div className="w-full max-w-lg rounded-xl border border-border bg-card p-5 shadow-sm">
170
+ <h3 className="mb-4 font-semibold">Tạo Ticket Hỗ Trợ</h3>
171
+ <TextareaPreset
172
+ label="Mô tả sự cố"
173
+ description="Vui lòng cung cấp chi tiết lỗi, các bước tái hiện và cấu hình môi trường."
174
+ placeholder="Ví dụ: Khi tôi click vào nút Submit, hệ thống báo lỗi 500..."
175
+ size={globalSize}
176
+ className="w-full"
177
+ rows={6}
178
+ />
179
+ </div>
180
+ </>
181
+ } />
196
182
  </div>
197
183
  );
198
184
  }
@@ -202,49 +188,42 @@ function TextareaMacroShowcase({ globalSize }: { globalSize: Size }) {
202
188
  // ──────────────────────────────────────────────────────────
203
189
  function TextareaMicroShowcase({ globalSize }: { globalSize: Size }) {
204
190
  return (
205
- <div className="space-y-10 mt-6">
191
+ <div className="space-y-10">
206
192
  <ExampleGrid>
207
- <ExampleSection
208
- label="Cơ bản (Primitive)"
209
- description="Khung nhập văn bản nhiều dòng không có nhãn."
210
- codeString={`<Textarea
193
+ <ShowcaseExample title="Cơ bản (Primitive)" description="Khung nhập văn bản nhiều dòng không có nhãn." code={`<Textarea
211
194
  rows={4}
212
195
  placeholder="Viết một đoạn văn dài ở đây..."
213
196
  className="w-full"
214
- />`}
215
- >
216
- <Textarea
217
- size={globalSize}
218
- rows={4}
219
- placeholder="Viết một đoạn văn dài ở đây..."
220
- className="w-full"
221
- />
222
- </ExampleSection>
197
+ />`} preview={
198
+ <>
199
+ <Textarea
200
+ size={globalSize}
201
+ rows={4}
202
+ placeholder="Viết một đoạn văn dài ở đây..."
203
+ className="w-full"
204
+ />
205
+ </>
206
+ } />
223
207
 
224
- <ExampleSection
225
- label="Auto Resize (CSS content)"
226
- description="Giãn chiều cao tự động nhờ class field-sizing-content (nếu trình duyệt hỗ trợ)."
227
- codeString={`<Textarea
208
+ <ShowcaseExample title="Auto Resize (CSS content)" description="Giãn chiều cao tự động nhờ class field-sizing-content (nếu trình duyệt hỗ trợ)." code={`<Textarea
228
209
  placeholder="Gõ nhiều dòng để xem nó tự giãn..."
229
210
  defaultValue={"Dòng 1\nDòng 2\nDòng 3\nDòng 4"}
230
211
  className="w-full"
231
212
  />
232
- `}
233
- >
234
- <Textarea
235
- size={globalSize}
236
- placeholder="Gõ nhiều dòng để xem nó tự giãn..."
237
- defaultValue={"Dòng 1\nDòng 2\nDòng 3\nDòng 4"}
238
- className="w-full"
239
- />
240
- </ExampleSection>
213
+ `} preview={
214
+ <>
215
+ <Textarea
216
+ size={globalSize}
217
+ placeholder="Gõ nhiều dòng để xem nó tự giãn..."
218
+ defaultValue={"Dòng 1\nDòng 2\nDòng 3\nDòng 4"}
219
+ className="w-full"
220
+ />
221
+ </>
222
+ } />
241
223
  </ExampleGrid>
242
224
 
243
225
  <ExampleGrid>
244
- <ExampleSection
245
- label="InputGroup + Textarea"
246
- description="Textarea bên trong InputGroup với Addon. Sử dụng InputGroupTextarea."
247
- codeString={`<Field className="w-full">
226
+ <ShowcaseExample title="InputGroup + Textarea" description="Textarea bên trong InputGroup với Addon. Sử dụng InputGroupTextarea." code={`<Field className="w-full">
248
227
  <FieldLabel htmlFor="tf-group-textarea">
249
228
  Bình luận
250
229
  </FieldLabel>
@@ -260,29 +239,27 @@ function TextareaMicroShowcase({ globalSize }: { globalSize: Size }) {
260
239
  />
261
240
  </InputGroup>
262
241
  </FieldContent>
263
- </Field>`}
264
- >
265
- <Field size={globalSize} className="w-full">
266
- <FieldLabel htmlFor="tf-group-textarea">Bình luận</FieldLabel>
267
- <FieldContent>
268
- <InputGroup size={globalSize} className="items-start">
269
- <InputGroupAddon align="start" className="mt-2">
270
- <InputGroupText>💬</InputGroupText>
271
- </InputGroupAddon>
272
- <InputGroupTextarea
273
- id="tf-group-textarea"
274
- rows={3}
275
- placeholder="Để lại bình luận của bạn..."
276
- />
277
- </InputGroup>
278
- </FieldContent>
279
- </Field>
280
- </ExampleSection>
242
+ </Field>`} preview={
243
+ <>
244
+ <Field size={globalSize} className="w-full">
245
+ <FieldLabel htmlFor="tf-group-textarea">Bình luận</FieldLabel>
246
+ <FieldContent>
247
+ <InputGroup size={globalSize} className="items-start">
248
+ <InputGroupAddon align="start" className="mt-2">
249
+ <InputGroupText>💬</InputGroupText>
250
+ </InputGroupAddon>
251
+ <InputGroupTextarea
252
+ id="tf-group-textarea"
253
+ rows={3}
254
+ placeholder="Để lại bình luận của bạn..."
255
+ />
256
+ </InputGroup>
257
+ </FieldContent>
258
+ </Field>
259
+ </>
260
+ } />
281
261
 
282
- <ExampleSection
283
- label="Trạng thái Lỗi thủ công"
284
- description="Tự gắn aria-invalid vào Textarea và dùng FieldError."
285
- codeString={`<Field className="w-full">
262
+ <ShowcaseExample title="Trạng thái Lỗi thủ công" description="Tự gắn aria-invalid vào Textarea và dùng FieldError." code={`<Field className="w-full">
286
263
  <FieldLabel htmlFor="tf-invalid">
287
264
  Mô tả hệ thống
288
265
  </FieldLabel>
@@ -298,31 +275,27 @@ function TextareaMicroShowcase({ globalSize }: { globalSize: Size }) {
298
275
  <FieldError>
299
276
  Mô tả không đạt đủ độ dài tối thiểu.
300
277
  </FieldError>
301
- </Field>`}
302
- >
303
- <Field size={globalSize} className="w-full">
304
- <FieldLabel htmlFor="tf-invalid">Mô tả hệ thống</FieldLabel>
305
- <FieldContent>
306
- <Textarea
307
- id="tf-invalid"
308
- size={globalSize}
309
- rows={3}
310
- aria-invalid
311
- placeholder="Mô tả..."
312
- defaultValue="Quá ngắn"
313
- />
314
- </FieldContent>
315
- <FieldError>Mô tả không đạt đủ độ dài tối thiểu.</FieldError>
316
- </Field>
317
- </ExampleSection>
278
+ </Field>`} preview={
279
+ <>
280
+ <Field size={globalSize} className="w-full">
281
+ <FieldLabel htmlFor="tf-invalid">Mô tả hệ thống</FieldLabel>
282
+ <FieldContent>
283
+ <Textarea
284
+ id="tf-invalid"
285
+ size={globalSize}
286
+ rows={3}
287
+ aria-invalid
288
+ placeholder="Mô tả..."
289
+ defaultValue="Quá ngắn"
290
+ />
291
+ </FieldContent>
292
+ <FieldError>Mô tả không đạt đủ độ dài tối thiểu.</FieldError>
293
+ </Field>
294
+ </>
295
+ } />
318
296
  </ExampleGrid>
319
297
 
320
- <ExampleSection
321
- label="🧭 Use Case Comparison"
322
- description="So sánh nhanh khi nào dùng Micro và Macro."
323
- fullWidth
324
- codeString={`<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
325
- {/* Story 1: Macro wins */}
298
+ <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">
326
299
  <div className="rounded-xl border border-border bg-card p-5 flex flex-col gap-4">
327
300
  <div className="flex items-start gap-3">
328
301
  <span className="mt-0.5 shrink-0 rounded-full bg-green-500/10 p-1.5 text-green-600">
@@ -364,7 +337,6 @@ function TextareaMicroShowcase({ globalSize }: { globalSize: Size }) {
364
337
  </div>
365
338
  </div>
366
339
 
367
- {/* Story 2: Micro wins */}
368
340
  <div className="rounded-xl border border-border bg-card p-5 flex flex-col gap-4">
369
341
  <div className="flex items-start gap-3">
370
342
  <span className="mt-0.5 shrink-0 rounded-full bg-blue-500/10 p-1.5 text-blue-600">
@@ -408,92 +380,93 @@ function TextareaMicroShowcase({ globalSize }: { globalSize: Size }) {
408
380
  </InputGroup>
409
381
  </div>
410
382
  </div>
411
- </div>`}
412
- >
413
- <div className="grid grid-cols-1 gap-6 md:grid-cols-2">
414
- {/* Story 1: Macro wins */}
415
- <div className="rounded-xl border border-border bg-card p-5 flex flex-col gap-4">
416
- <div className="flex items-start gap-3">
417
- <span className="mt-0.5 shrink-0 rounded-full bg-green-500/10 p-1.5 text-green-600">
418
- <svg
419
- xmlns="http://www.w3.org/2000/svg"
420
- className="size-4"
421
- viewBox="0 0 24 24"
422
- fill="none"
423
- stroke="currentColor"
424
- strokeWidth="2"
425
- strokeLinecap="round"
426
- strokeLinejoin="round"
427
- aria-hidden="true"
428
- >
429
- <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
430
- </svg>
431
- </span>
432
- <div>
433
- <p className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">
434
- Story 1 · Dùng Macro
435
- </p>
436
- <h3 className="mt-0.5 text-sm font-semibold text-foreground">
437
- Form lấy thông tin thông thường
438
- </h3>
439
- </div>
440
- </div>
441
- <p className="text-xs text-muted-foreground leading-relaxed">
442
- Bạn cần ô nhập có tên trường rõ ràng, có gợi ý nhập liệu. Chỉ việc
443
- truyền props vào <DocsCode>TextareaPreset</DocsCode> là xong.
444
- </p>
445
- <div className="rounded-lg bg-muted/50 p-3">
446
- <TextareaPreset
447
- size="sm"
448
- label="Mục tiêu nghề nghiệp"
449
- description="Tối đa 500 từ."
450
- className="w-full"
451
- />
452
- </div>
453
- </div>
383
+ </div>`} preview={
384
+ <>
385
+ <div className="grid grid-cols-1 gap-6 md:grid-cols-2">
386
+ {/* Story 1: Macro wins */}
387
+ <div className="rounded-xl border border-border bg-card p-5 flex flex-col gap-4">
388
+ <div className="flex items-start gap-3">
389
+ <span className="mt-0.5 shrink-0 rounded-full bg-green-500/10 p-1.5 text-green-600">
390
+ <svg
391
+ xmlns="http://www.w3.org/2000/svg"
392
+ className="size-4"
393
+ viewBox="0 0 24 24"
394
+ fill="none"
395
+ stroke="currentColor"
396
+ strokeWidth="2"
397
+ strokeLinecap="round"
398
+ strokeLinejoin="round"
399
+ aria-hidden="true"
400
+ >
401
+ <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
402
+ </svg>
403
+ </span>
404
+ <div>
405
+ <p className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">
406
+ Story 1 · Dùng Macro
407
+ </p>
408
+ <h3 className="mt-0.5 text-sm font-semibold text-foreground">
409
+ Form lấy thông tin thông thường
410
+ </h3>
411
+ </div>
412
+ </div>
413
+ <p className="text-xs text-muted-foreground leading-relaxed">
414
+ Bạn cần ô nhập có tên trường rõ ràng, có gợi ý nhập liệu. Chỉ việc
415
+ truyền props vào <DocsCode>TextareaPreset</DocsCode> là xong.
416
+ </p>
417
+ <div className="rounded-lg bg-muted/50 p-3">
418
+ <TextareaPreset
419
+ size="sm"
420
+ label="Mục tiêu nghề nghiệp"
421
+ description="Tối đa 500 từ."
422
+ className="w-full"
423
+ />
424
+ </div>
425
+ </div>
454
426
 
455
- {/* Story 2: Micro wins */}
456
- <div className="rounded-xl border border-border bg-card p-5 flex flex-col gap-4">
457
- <div className="flex items-start gap-3">
458
- <span className="mt-0.5 shrink-0 rounded-full bg-blue-500/10 p-1.5 text-blue-600">
459
- <svg
460
- xmlns="http://www.w3.org/2000/svg"
461
- className="size-4"
462
- viewBox="0 0 24 24"
463
- fill="none"
464
- stroke="currentColor"
465
- strokeWidth="2"
466
- strokeLinecap="round"
467
- strokeLinejoin="round"
468
- aria-hidden="true"
469
- >
470
- <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" />
471
- </svg>
472
- </span>
473
- <div>
474
- <p className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">
475
- Story 2 · Dùng Micro
476
- </p>
477
- <h3 className="mt-0.5 text-sm font-semibold text-foreground">
478
- Layout kết hợp Addon đặc thù
479
- </h3>
480
- </div>
481
- </div>
482
- <p className="text-xs text-muted-foreground leading-relaxed">
483
- Khi bạn cần nối một icon vào ngay bên cạnh khung textarea giống
484
- như giao diện chat. Macro không hỗ trợ layout này cho Textarea.
485
- </p>
486
- <div className="rounded-lg bg-muted/50 p-3">
487
- <InputGroup size="sm" className="items-end w-full">
488
- <InputGroupTextarea rows={2} placeholder="Tin nhắn..." />
489
- <button className="h-full px-3 shrink-0 rounded-r-md border-l border-input bg-muted/50 text-xs font-semibold text-primary transition-colors hover:bg-muted">
490
- GỬI
491
- </button>
492
- </InputGroup>
493
- </div>
494
- </div>
495
- </div>
496
- </ExampleSection>
427
+ {/* Story 2: Micro wins */}
428
+ <div className="rounded-xl border border-border bg-card p-5 flex flex-col gap-4">
429
+ <div className="flex items-start gap-3">
430
+ <span className="mt-0.5 shrink-0 rounded-full bg-blue-500/10 p-1.5 text-blue-600">
431
+ <svg
432
+ xmlns="http://www.w3.org/2000/svg"
433
+ className="size-4"
434
+ viewBox="0 0 24 24"
435
+ fill="none"
436
+ stroke="currentColor"
437
+ strokeWidth="2"
438
+ strokeLinecap="round"
439
+ strokeLinejoin="round"
440
+ aria-hidden="true"
441
+ >
442
+ <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" />
443
+ </svg>
444
+ </span>
445
+ <div>
446
+ <p className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">
447
+ Story 2 · Dùng Micro
448
+ </p>
449
+ <h3 className="mt-0.5 text-sm font-semibold text-foreground">
450
+ Layout kết hợp Addon đặc thù
451
+ </h3>
452
+ </div>
453
+ </div>
454
+ <p className="text-xs text-muted-foreground leading-relaxed">
455
+ Khi bạn cần nối một icon vào ngay bên cạnh khung textarea giống
456
+ như giao diện chat. Macro không hỗ trợ layout này cho Textarea.
457
+ </p>
458
+ <div className="rounded-lg bg-muted/50 p-3">
459
+ <InputGroup size="sm" className="items-end w-full">
460
+ <InputGroupTextarea rows={2} placeholder="Tin nhắn..." />
461
+ <button className="h-full px-3 shrink-0 rounded-r-md border-l border-input bg-muted/50 text-xs font-semibold text-primary transition-colors hover:bg-muted">
462
+ GỬI
463
+ </button>
464
+ </InputGroup>
465
+ </div>
466
+ </div>
467
+ </div>
468
+ </>
469
+ } />
497
470
  </div>
498
471
  );
499
472
  }
@@ -507,36 +480,10 @@ export default function TextareaShowcase() {
507
480
  <Showcase
508
481
  title="Textarea"
509
482
  description="Trường văn bản nhiều dòng dành cho nội dung dài, phản hồi hoặc mô tả."
510
- generalConcept={
511
- <div className="space-y-4">
512
- <ShowcaseDocs>
513
- <DocsH3>Khi nào nên dùng Macro</DocsH3>
514
- <DocsP>
515
- <DocsCode>TextareaPreset</DocsCode> là một component hoàn chỉnh,
516
- tích hợp sẵn Label, Description và Error Message. Sử dụng nó cho
517
- đa số các trường hợp để giữ code ngắn gọn và đảm bảo tính nhất
518
- quán (Accessibility).
519
- </DocsP>
520
- </ShowcaseDocs>
521
- <ShowcaseDocs>
522
- <DocsH3>Khi nào nên dùng Micro</DocsH3>
523
- <DocsP>
524
- <DocsCode>Textarea</DocsCode> chỉ là một ô nhập liệu độc lập. Dùng
525
- nó khi bạn tự quản lý <DocsCode>Field</DocsCode> thủ công hoặc khi
526
- chèn Textarea vào bên trong một <DocsCode>InputGroup</DocsCode>.
527
- </DocsP>
528
- </ShowcaseDocs>
529
- </div>
530
- }
483
+
531
484
 
532
- tabs={[
533
- {
534
- label: "Micro (Primitive)",
535
- content: <TextareaMicroShowcase globalSize={globalSize} /> },
536
- {
537
- label: "Macro (Preset)",
538
- content: <TextareaMacroShowcase globalSize={globalSize} /> },
539
- ]}
485
+ micro={{ content: <TextareaMicroShowcase globalSize={globalSize} /> }}
486
+ macro={{ content: <TextareaMacroShowcase globalSize={globalSize} /> }}
540
487
  />
541
488
  );
542
489
  }