@geoit/ui 0.0.2 → 0.0.4

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 (38) hide show
  1. package/README.md +148 -12
  2. package/ai/AGENTS.md +5 -4
  3. package/ai/README.md +2 -2
  4. package/ai/catalog.json +3064 -424
  5. package/ai/components/geo-alert.md +75 -21
  6. package/ai/components/geo-back-button.md +64 -18
  7. package/ai/components/geo-breadcrumb.md +71 -12
  8. package/ai/components/geo-button.md +90 -27
  9. package/ai/components/geo-checkbox.md +69 -20
  10. package/ai/components/geo-column-picker.md +91 -18
  11. package/ai/components/geo-confirm-modal.md +93 -27
  12. package/ai/components/geo-date-picker.md +103 -29
  13. package/ai/components/geo-date-range-picker.md +99 -28
  14. package/ai/components/geo-file-picker.md +97 -26
  15. package/ai/components/geo-form-modal.md +112 -44
  16. package/ai/components/geo-icon.md +70 -16
  17. package/ai/components/geo-input.md +92 -29
  18. package/ai/components/geo-page-title.md +62 -16
  19. package/ai/components/geo-pagination.md +79 -24
  20. package/ai/components/geo-radio.md +108 -27
  21. package/ai/components/geo-row-actions.md +87 -19
  22. package/ai/components/geo-select.md +106 -28
  23. package/ai/components/geo-slider.md +72 -21
  24. package/ai/components/geo-status-tag.md +67 -15
  25. package/ai/components/geo-step-form-layout.md +122 -38
  26. package/ai/components/geo-stepper.md +82 -19
  27. package/ai/components/geo-table-filter.md +91 -29
  28. package/ai/components/geo-table-pagination.md +73 -23
  29. package/ai/components/geo-table-toolbar.md +84 -29
  30. package/ai/components/geo-table.md +194 -59
  31. package/ai/components/geo-tabs.md +101 -27
  32. package/ai/components/geo-textarea.md +82 -23
  33. package/ai/components/geo-toast.md +138 -27
  34. package/ai/components/geo-tree.md +96 -22
  35. package/ai/components/geo-typography.md +70 -19
  36. package/ai/components/geo-upload.md +110 -39
  37. package/ai/recipes.md +76 -4
  38. package/package.json +1 -1
package/ai/catalog.json CHANGED
@@ -1,16 +1,22 @@
1
1
  {
2
2
  "package": "@geoit/ui",
3
3
  "import": "@geoit/ui",
4
- "version": "0.0.1",
5
- "generatedAt": "2026-07-27T01:27:04.549Z",
4
+ "version": "0.0.4",
5
+ "generatedAt": "2026-07-27T01:57:41.109Z",
6
6
  "styles": [
7
- "projects/geo-ui/src/lib/styles/design-tokens.scss",
8
- "projects/geo-ui/src/lib/styles/theme.scss"
7
+ "@geoit/ui/src/lib/styles/design-tokens.scss",
8
+ "@geoit/ui/src/lib/styles/theme.scss"
9
9
  ],
10
10
  "tokens": {
11
11
  "radius": "var(--geo-radius) /* 6px */",
12
12
  "primary": "var(--geo-color-primary-default)",
13
- "docs": "projects/geo-ui/src/lib/styles/design-tokens.scss"
13
+ "docs": "node_modules/@geoit/ui/src/lib/styles/design-tokens.scss"
14
+ },
15
+ "docs": {
16
+ "agents": "ai/AGENTS.md",
17
+ "recipes": "ai/recipes.md",
18
+ "index": "ai/README.md",
19
+ "componentsDir": "ai/components/"
14
20
  },
15
21
  "rules": [
16
22
  "Always use geo-* components from @geoit/ui for UI.",
@@ -27,20 +33,78 @@
27
33
  "summary": "Inline alert banner (success/error/warning/info).",
28
34
  "whenToUse": "Form-level or page-level status messages that stay in layout.",
29
35
  "doNotUseWhen": "Transient notifications — use geo-toast / GeoToastService.",
36
+ "howTo": "Hiển thị banner trạng thái cố định trong layout (form/page). Dùng type success|error|warning|info; đóng qua closable + (closed).",
37
+ "tips": [
38
+ "Toast tạm thời → GeoToastService / geo-toast; alert là inline persistent.",
39
+ "Gắn [visible] nếu cần ẩn/hiện từ component cha.",
40
+ "title + message dùng tiếng Việt ngắn gọn."
41
+ ],
42
+ "binding": "none",
43
+ "cva": false,
30
44
  "path": "projects/geo-ui/src/lib/figma/geo-alert/geo-alert.component.ts",
31
45
  "doc": "ai/components/geo-alert.md",
32
46
  "inputs": [
33
- "type",
34
- "title",
35
- "message",
36
- "closable",
37
- "visible",
38
- "showIcon"
47
+ {
48
+ "name": "type",
49
+ "type": "'success' | 'error' | 'warning' | 'info'",
50
+ "default": "'info'",
51
+ "note": ""
52
+ },
53
+ {
54
+ "name": "title",
55
+ "type": "string",
56
+ "default": "''",
57
+ "note": ""
58
+ },
59
+ {
60
+ "name": "message",
61
+ "type": "string",
62
+ "default": "''",
63
+ "note": ""
64
+ },
65
+ {
66
+ "name": "closable",
67
+ "type": "boolean",
68
+ "default": "true",
69
+ "note": ""
70
+ },
71
+ {
72
+ "name": "visible",
73
+ "type": "boolean",
74
+ "default": "true",
75
+ "note": ""
76
+ },
77
+ {
78
+ "name": "showIcon",
79
+ "type": "boolean",
80
+ "default": "true",
81
+ "note": ""
82
+ }
39
83
  ],
40
84
  "outputs": [
41
- "closed"
85
+ {
86
+ "name": "closed",
87
+ "type": "EventEmitter<void>",
88
+ "note": ""
89
+ }
90
+ ],
91
+ "examples": [
92
+ {
93
+ "title": "Basic",
94
+ "lang": "html",
95
+ "code": "<geo-alert type=\"success\" title=\"Thành công\" message=\"Đã lưu hồ sơ.\"></geo-alert>"
96
+ },
97
+ {
98
+ "title": "Full options",
99
+ "lang": "html",
100
+ "code": "<geo-alert\n type=\"warning\"\n title=\"Cảnh báo\"\n message=\"Vui lòng kiểm tra lại thông tin trước khi gửi.\"\n [closable]=\"true\"\n [visible]=\"showAlert\"\n [showIcon]=\"true\"\n (closed)=\"showAlert = false\"\n></geo-alert>"
101
+ }
42
102
  ],
43
- "example": "<geo-alert type=\"success\" title=\"Thành công\" message=\"Đã lưu.\"></geo-alert>"
103
+ "example": "<geo-alert type=\"success\" title=\"Thành công\" message=\"Đã lưu hồ sơ.\"></geo-alert>",
104
+ "related": [
105
+ "geo-toast",
106
+ "geo-icon"
107
+ ]
44
108
  },
45
109
  {
46
110
  "name": "GeoBackButtonComponent",
@@ -49,18 +113,67 @@
49
113
  "summary": "Back navigation control (history or custom click).",
50
114
  "whenToUse": "Detail/edit pages that return to a list.",
51
115
  "doNotUseWhen": "Primary CTA — use geo-button.",
116
+ "howTo": "Nút quay lại trên trang chi tiết/sửa. Mặc định historyBack=true; hoặc lắng nghe (clicked) để điều hướng tùy chỉnh.",
117
+ "tips": [
118
+ "CTA chính → geo-button, không dùng back-button.",
119
+ "Đặt title/ariaLabel tiếng Việt (mặc định 'Quay lại').",
120
+ "Tắt historyBack và xử lý (clicked) khi dùng router.navigate."
121
+ ],
122
+ "binding": "none",
123
+ "cva": false,
52
124
  "path": "projects/geo-ui/src/lib/figma/geo-back-button/geo-back-button.component.ts",
53
125
  "doc": "ai/components/geo-back-button.md",
54
126
  "inputs": [
55
- "ariaLabel",
56
- "title",
57
- "disabled",
58
- "historyBack"
127
+ {
128
+ "name": "ariaLabel",
129
+ "type": "string",
130
+ "default": "'Quay lại'",
131
+ "note": ""
132
+ },
133
+ {
134
+ "name": "title",
135
+ "type": "string",
136
+ "default": "'Quay lại'",
137
+ "note": ""
138
+ },
139
+ {
140
+ "name": "disabled",
141
+ "type": "boolean",
142
+ "default": "false",
143
+ "note": ""
144
+ },
145
+ {
146
+ "name": "historyBack",
147
+ "type": "boolean",
148
+ "default": "true",
149
+ "note": "`true` (mặc định): `Location.back()` — về trang trước trong history."
150
+ }
59
151
  ],
60
152
  "outputs": [
61
- "clicked"
153
+ {
154
+ "name": "clicked",
155
+ "type": "EventEmitter<MouseEvent>",
156
+ "note": ""
157
+ }
158
+ ],
159
+ "examples": [
160
+ {
161
+ "title": "Basic",
162
+ "lang": "html",
163
+ "code": "<geo-back-button (clicked)=\"goBack()\"></geo-back-button>"
164
+ },
165
+ {
166
+ "title": "Full options",
167
+ "lang": "html",
168
+ "code": "<geo-back-button\n title=\"Quay lại danh sách\"\n ariaLabel=\"Quay lại danh sách\"\n [historyBack]=\"false\"\n [disabled]=\"saving\"\n (clicked)=\"router.navigate(['/danh-sach'])\"\n></geo-back-button>"
169
+ }
62
170
  ],
63
- "example": "<geo-back-button (clicked)=\"goBack()\"></geo-back-button>"
171
+ "example": "<geo-back-button (clicked)=\"goBack()\"></geo-back-button>",
172
+ "related": [
173
+ "geo-button",
174
+ "geo-page-title",
175
+ "geo-breadcrumb"
176
+ ]
64
177
  },
65
178
  {
66
179
  "name": "GeoBreadcrumbComponent",
@@ -69,13 +182,42 @@
69
182
  "summary": "Breadcrumb trail; last item active (primary).",
70
183
  "whenToUse": "Page hierarchy under page title.",
71
184
  "doNotUseWhen": "Primary navigation / sidebar menus.",
185
+ "howTo": "Đường dẫn phân cấp trang. Truyền [items] dạng { label, link? }; mục cuối là active (không cần link).",
186
+ "tips": [
187
+ "Thường đặt trên geo-page-title.",
188
+ "Không dùng cho menu sidebar.",
189
+ "label tiếng Việt; link là route path."
190
+ ],
191
+ "binding": "none",
192
+ "cva": false,
72
193
  "path": "projects/geo-ui/src/lib/figma/geo-breadcrumb/geo-breadcrumb.component.ts",
73
194
  "doc": "ai/components/geo-breadcrumb.md",
74
195
  "inputs": [
75
- "items"
196
+ {
197
+ "name": "items",
198
+ "type": "GeoBreadcrumbItem[]",
199
+ "default": "[]",
200
+ "note": ""
201
+ }
76
202
  ],
77
203
  "outputs": [],
78
- "example": "<geo-breadcrumb [items]=\"[\n { label: 'Trang chủ', link: '/' },\n { label: 'Chi tiết' }\n]\"></geo-breadcrumb>"
204
+ "examples": [
205
+ {
206
+ "title": "Basic",
207
+ "lang": "html",
208
+ "code": "<geo-breadcrumb [items]=\"[\n { label: 'Trang chủ', link: '/' },\n { label: 'Chi tiết' }\n]\"></geo-breadcrumb>"
209
+ },
210
+ {
211
+ "title": "Full options",
212
+ "lang": "html",
213
+ "code": "<geo-breadcrumb [items]=\"[\n { label: 'Trang chủ', link: '/' },\n { label: 'Tra cứu hồ sơ', link: '/tra-cuu' },\n { label: 'Xem chi tiết hồ sơ' }\n]\"></geo-breadcrumb>"
214
+ }
215
+ ],
216
+ "example": "<geo-breadcrumb [items]=\"[\n { label: 'Trang chủ', link: '/' },\n { label: 'Chi tiết' }\n]\"></geo-breadcrumb>",
217
+ "related": [
218
+ "geo-page-title",
219
+ "geo-back-button"
220
+ ]
79
221
  },
80
222
  {
81
223
  "name": "GeoButtonComponent",
@@ -84,46 +226,183 @@
84
226
  "summary": "Primary action button with brand/semantic colors and icons.",
85
227
  "whenToUse": "All CTAs, form submit/cancel, toolbar actions.",
86
228
  "doNotUseWhen": "Do not use raw ant-btn or native button for branded UI.",
229
+ "howTo": "CTA chính của GEO UI. appearance primary|secondary|tertiary; color brand|danger|warning|success; icon Ant Design qua [icon].",
230
+ "tips": [
231
+ "Không dùng ant-btn / <button> raw cho UI brand.",
232
+ "iconPlacement start|end; iconOnly cho toolbar icon.",
233
+ "Dùng tooltip thay title native khi cần hover text."
234
+ ],
235
+ "binding": "none",
236
+ "cva": false,
87
237
  "path": "projects/geo-ui/src/lib/figma/geo-button/geo-button.component.ts",
88
238
  "doc": "ai/components/geo-button.md",
89
239
  "inputs": [
90
- "appearance",
91
- "color",
92
- "icon",
93
- "iconTheme",
94
- "iconPlacement",
95
- "iconOnly",
96
- "type",
97
- "disabled",
98
- "tooltip",
99
- "tooltipPlacement",
100
- "active",
101
- "title"
240
+ {
241
+ "name": "appearance",
242
+ "type": "'primary' | 'secondary' | 'tertiary'",
243
+ "default": "'primary'",
244
+ "note": ""
245
+ },
246
+ {
247
+ "name": "color",
248
+ "type": "'brand' | 'danger' | 'warning' | 'success'",
249
+ "default": "'brand'",
250
+ "note": ""
251
+ },
252
+ {
253
+ "name": "icon",
254
+ "type": "string",
255
+ "default": "",
256
+ "note": ""
257
+ },
258
+ {
259
+ "name": "iconTheme",
260
+ "type": "'outline' | 'fill' | 'twotone'",
261
+ "default": "'outline'",
262
+ "note": ""
263
+ },
264
+ {
265
+ "name": "iconPlacement",
266
+ "type": "'start' | 'end'",
267
+ "default": "'start'",
268
+ "note": "Vị trí icon: `start` (trái) hoặc `end` (phải)."
269
+ },
270
+ {
271
+ "name": "iconOnly",
272
+ "type": "boolean",
273
+ "default": "false",
274
+ "note": ""
275
+ },
276
+ {
277
+ "name": "type",
278
+ "type": "'button' | 'submit'",
279
+ "default": "'button'",
280
+ "note": ""
281
+ },
282
+ {
283
+ "name": "disabled",
284
+ "type": "boolean",
285
+ "default": "false",
286
+ "note": ""
287
+ },
288
+ {
289
+ "name": "tooltip",
290
+ "type": "string",
291
+ "default": "''",
292
+ "note": "Tooltip Ant Design (`nz-tooltip`). Ưu tiên hơn `title` native."
293
+ },
294
+ {
295
+ "name": "tooltipPlacement",
296
+ "type": "'top' | 'left' | 'right' | 'bottom' | 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight' | 'le…",
297
+ "default": "'top'",
298
+ "note": ""
299
+ },
300
+ {
301
+ "name": "active",
302
+ "type": "boolean",
303
+ "default": "false",
304
+ "note": "Trạng thái active (vd. toolbar toggle đang bật)."
305
+ },
306
+ {
307
+ "name": "title",
308
+ "type": "string",
309
+ "default": "''",
310
+ "note": "Native `title` — fallback khi không set `tooltip`."
311
+ }
102
312
  ],
103
313
  "outputs": [
104
- "clicked"
314
+ {
315
+ "name": "clicked",
316
+ "type": "EventEmitter<MouseEvent>",
317
+ "note": ""
318
+ }
319
+ ],
320
+ "examples": [
321
+ {
322
+ "title": "Basic",
323
+ "lang": "html",
324
+ "code": "<geo-button appearance=\"primary\" color=\"brand\" icon=\"plus\">Thêm mới</geo-button>"
325
+ },
326
+ {
327
+ "title": "Full options",
328
+ "lang": "html",
329
+ "code": "<geo-button\n appearance=\"primary\"\n color=\"brand\"\n icon=\"right\"\n iconTheme=\"outline\"\n iconPlacement=\"end\"\n type=\"submit\"\n tooltip=\"Lưu và tiếp tục\"\n tooltipPlacement=\"top\"\n [disabled]=\"saving\"\n (clicked)=\"onSave()\"\n>Tiếp theo</geo-button>\n\n<geo-button appearance=\"secondary\" color=\"danger\" icon=\"delete\" [iconOnly]=\"true\" tooltip=\"Xóa\"></geo-button>"
330
+ }
105
331
  ],
106
- "example": "<geo-button appearance=\"primary\" color=\"brand\" icon=\"plus\">Thêm mới</geo-button>\n<geo-button appearance=\"primary\" icon=\"right\" iconPlacement=\"end\">Tiếp theo</geo-button>"
332
+ "example": "<geo-button appearance=\"primary\" color=\"brand\" icon=\"plus\">Thêm mới</geo-button>",
333
+ "related": [
334
+ "geo-icon",
335
+ "geo-form-modal",
336
+ "geo-confirm-modal"
337
+ ]
107
338
  },
108
339
  {
109
340
  "name": "GeoCheckboxComponent",
110
341
  "selector": "geo-checkbox",
111
342
  "category": "forms",
112
- "summary": "Checkbox with label; supports CVA / ngModel.",
343
+ "summary": "Checkbox with label; bind with [(checked)] (not CVA/ngModel).",
113
344
  "whenToUse": "Boolean / multi-select options in forms.",
114
345
  "doNotUseWhen": "Single exclusive choice — use geo-radio.",
346
+ "howTo": "Checkbox có label. Two-way bằng [(checked)] / (checkedChange) — không phải CVA ngModel.",
347
+ "tips": [
348
+ "Độc quyền 1 lựa chọn → geo-radio.",
349
+ "indeterminate cho trạng thái chọn một phần (select-all).",
350
+ "Luôn dùng [(checked)], không [(ngModel)]."
351
+ ],
352
+ "binding": "checked",
353
+ "cva": false,
115
354
  "path": "projects/geo-ui/src/lib/figma/geo-checkbox/geo-checkbox.component.ts",
116
355
  "doc": "ai/components/geo-checkbox.md",
117
356
  "inputs": [
118
- "label",
119
- "checked",
120
- "disabled",
121
- "indeterminate"
357
+ {
358
+ "name": "label",
359
+ "type": "string",
360
+ "default": "''",
361
+ "note": ""
362
+ },
363
+ {
364
+ "name": "checked",
365
+ "type": "boolean",
366
+ "default": "false",
367
+ "note": ""
368
+ },
369
+ {
370
+ "name": "disabled",
371
+ "type": "boolean",
372
+ "default": "false",
373
+ "note": ""
374
+ },
375
+ {
376
+ "name": "indeterminate",
377
+ "type": "boolean",
378
+ "default": "false",
379
+ "note": ""
380
+ }
122
381
  ],
123
382
  "outputs": [
124
- "checkedChange"
383
+ {
384
+ "name": "checkedChange",
385
+ "type": "EventEmitter<boolean>",
386
+ "note": ""
387
+ }
388
+ ],
389
+ "examples": [
390
+ {
391
+ "title": "Basic",
392
+ "lang": "html",
393
+ "code": "<geo-checkbox label=\"Tôi đồng ý điều khoản\" [(checked)]=\"agreed\"></geo-checkbox>"
394
+ },
395
+ {
396
+ "title": "Full options",
397
+ "lang": "html",
398
+ "code": "<geo-checkbox\n label=\"Chọn tất cả\"\n [(checked)]=\"allChecked\"\n [indeterminate]=\"indeterminate\"\n [disabled]=\"loading\"\n (checkedChange)=\"onToggleAll($event)\"\n></geo-checkbox>"
399
+ }
125
400
  ],
126
- "example": "<geo-checkbox label=\"Tôi đồng ý\" [(ngModel)]=\"agreed\"></geo-checkbox>"
401
+ "example": "<geo-checkbox label=\"Tôi đồng ý điều khoản\" [(checked)]=\"agreed\"></geo-checkbox>",
402
+ "related": [
403
+ "geo-radio",
404
+ "geo-button"
405
+ ]
127
406
  },
128
407
  {
129
408
  "name": "GeoColumnPickerComponent",
@@ -132,18 +411,66 @@
132
411
  "summary": "Column visibility picker for tables (optional persistence).",
133
412
  "whenToUse": "Let users show/hide table columns.",
134
413
  "doNotUseWhen": "Static tables with fixed columns.",
414
+ "howTo": "Popup ẩn/hiện cột bảng. [items] GeoColumnPickerItem[]; (apply) nhận payload cột đã chọn; tùy chọn storageKey để persist.",
415
+ "tips": [
416
+ "Thường gắn qua geo-table-toolbar [columnItems].",
417
+ "storageKey ổn định theo màn hình (vd. 'hs-columns').",
418
+ "triggerLabel/hint tiếng Việt mặc định đã có."
419
+ ],
420
+ "binding": "none",
421
+ "cva": false,
135
422
  "path": "projects/geo-ui/src/lib/figma/geo-table/geo-column-picker/geo-column-picker.component.ts",
136
423
  "doc": "ai/components/geo-column-picker.md",
137
424
  "inputs": [
138
- "triggerLabel",
139
- "hint",
140
- "items",
141
- "storageKey"
425
+ {
426
+ "name": "triggerLabel",
427
+ "type": "string",
428
+ "default": "'Ẩn hiện cột'",
429
+ "note": ""
430
+ },
431
+ {
432
+ "name": "hint",
433
+ "type": "string",
434
+ "default": "'Chọn cột hiển thị trên bảng.'",
435
+ "note": ""
436
+ },
437
+ {
438
+ "name": "items",
439
+ "type": "GeoColumnPickerItem[]",
440
+ "default": "[]",
441
+ "note": ""
442
+ },
443
+ {
444
+ "name": "storageKey",
445
+ "type": "string",
446
+ "default": "",
447
+ "note": "Lưu trạng thái ẩn/hiện cột theo session khi người dùng áp dụng"
448
+ }
142
449
  ],
143
450
  "outputs": [
144
- "apply"
451
+ {
452
+ "name": "apply",
453
+ "type": "EventEmitter<GeoColumnPickerApplyPayload>",
454
+ "note": ""
455
+ }
456
+ ],
457
+ "examples": [
458
+ {
459
+ "title": "Basic",
460
+ "lang": "html",
461
+ "code": "<geo-column-picker [items]=\"cols\" (apply)=\"onCols($event)\"></geo-column-picker>"
462
+ },
463
+ {
464
+ "title": "Full options",
465
+ "lang": "html",
466
+ "code": "<geo-column-picker\n triggerLabel=\"Ẩn hiện cột\"\n hint=\"Chọn cột hiển thị trên bảng.\"\n [items]=\"[\n { key: 'code', label: 'Mã', visible: true },\n { key: 'name', label: 'Tên', visible: true },\n { key: 'status', label: 'Trạng thái', visible: false }\n ]\"\n storageKey=\"ho-so-columns\"\n (apply)=\"onColumnApply($event)\"\n></geo-column-picker>"
467
+ }
145
468
  ],
146
- "example": "<geo-column-picker [items]=\"cols\" (apply)=\"onCols($event)\"></geo-column-picker>"
469
+ "example": "<geo-column-picker [items]=\"cols\" (apply)=\"onCols($event)\"></geo-column-picker>",
470
+ "related": [
471
+ "geo-table",
472
+ "geo-table-toolbar"
473
+ ]
147
474
  },
148
475
  {
149
476
  "name": "GeoConfirmModalComponent",
@@ -152,27 +479,119 @@
152
479
  "summary": "Confirm / danger dialog (destroy actions).",
153
480
  "whenToUse": "Delete, irreversible actions.",
154
481
  "doNotUseWhen": "Multi-field forms — use geo-form-modal.",
482
+ "howTo": "Dialog xác nhận hành động phá hủy (xóa). Điều khiển bằng [visible] + (visibleChange); (confirm)/(cancel).",
483
+ "tips": [
484
+ "Form nhiều field → geo-form-modal.",
485
+ "highlight để nhấn mạnh tên đối tượng bị xóa.",
486
+ "confirmLoading khi đang gọi API."
487
+ ],
488
+ "binding": "none",
489
+ "cva": false,
155
490
  "path": "projects/geo-ui/src/lib/figma/geo-confirm-modal/geo-confirm-modal.component.ts",
156
491
  "doc": "ai/components/geo-confirm-modal.md",
157
492
  "inputs": [
158
- "visible",
159
- "title",
160
- "message",
161
- "highlight",
162
- "description",
163
- "cancelText",
164
- "confirmText",
165
- "confirmLoading",
166
- "width",
167
- "maskClosable",
168
- "closable"
493
+ {
494
+ "name": "visible",
495
+ "type": "boolean",
496
+ "default": "false",
497
+ "note": ""
498
+ },
499
+ {
500
+ "name": "title",
501
+ "type": "string",
502
+ "default": "'Xác nhận xoá'",
503
+ "note": ""
504
+ },
505
+ {
506
+ "name": "message",
507
+ "type": "string",
508
+ "default": "''",
509
+ "note": ""
510
+ },
511
+ {
512
+ "name": "highlight",
513
+ "type": "string",
514
+ "default": "''",
515
+ "note": ""
516
+ },
517
+ {
518
+ "name": "description",
519
+ "type": "string",
520
+ "default": "'Hành động không thể hoàn tác, bạn chắc chắn muốn thực hiện?'",
521
+ "note": ""
522
+ },
523
+ {
524
+ "name": "cancelText",
525
+ "type": "string",
526
+ "default": "'Hủy'",
527
+ "note": ""
528
+ },
529
+ {
530
+ "name": "confirmText",
531
+ "type": "string",
532
+ "default": "'Xác nhận'",
533
+ "note": ""
534
+ },
535
+ {
536
+ "name": "confirmLoading",
537
+ "type": "boolean",
538
+ "default": "false",
539
+ "note": ""
540
+ },
541
+ {
542
+ "name": "width",
543
+ "type": "number | string",
544
+ "default": "445",
545
+ "note": ""
546
+ },
547
+ {
548
+ "name": "maskClosable",
549
+ "type": "boolean",
550
+ "default": "false",
551
+ "note": ""
552
+ },
553
+ {
554
+ "name": "closable",
555
+ "type": "boolean",
556
+ "default": "true",
557
+ "note": ""
558
+ }
169
559
  ],
170
560
  "outputs": [
171
- "visibleChange",
172
- "confirm",
173
- "cancel"
561
+ {
562
+ "name": "visibleChange",
563
+ "type": "EventEmitter<boolean>",
564
+ "note": ""
565
+ },
566
+ {
567
+ "name": "confirm",
568
+ "type": "EventEmitter<void>",
569
+ "note": ""
570
+ },
571
+ {
572
+ "name": "cancel",
573
+ "type": "EventEmitter<void>",
574
+ "note": ""
575
+ }
576
+ ],
577
+ "examples": [
578
+ {
579
+ "title": "Basic",
580
+ "lang": "html",
581
+ "code": "<geo-confirm-modal\n [visible]=\"open\"\n title=\"Xóa hồ sơ?\"\n message=\"Hành động không thể hoàn tác.\"\n confirmText=\"Xóa\"\n (confirm)=\"onDelete()\"\n (visibleChange)=\"open = $event\"\n></geo-confirm-modal>"
582
+ },
583
+ {
584
+ "title": "Full options",
585
+ "lang": "html",
586
+ "code": "<geo-confirm-modal\n [visible]=\"confirmOpen\"\n title=\"Xác nhận xoá\"\n message=\"Bạn sắp xóa hồ sơ\"\n highlight=\"HS-2024-001\"\n description=\"Hành động không thể hoàn tác, bạn chắc chắn muốn thực hiện?\"\n cancelText=\"Hủy\"\n confirmText=\"Xóa\"\n [confirmLoading]=\"deleting\"\n [width]=\"445\"\n [maskClosable]=\"false\"\n [closable]=\"true\"\n (confirm)=\"onDelete()\"\n (cancel)=\"confirmOpen = false\"\n (visibleChange)=\"confirmOpen = $event\"\n></geo-confirm-modal>"
587
+ }
174
588
  ],
175
- "example": "<geo-confirm-modal\n [visible]=\"open\"\n title=\"Xóa hồ sơ?\"\n message=\"Hành động không thể hoàn tác.\"\n confirmText=\"Xóa\"\n (confirm)=\"onDelete()\"\n (visibleChange)=\"open = $event\"\n></geo-confirm-modal>"
589
+ "example": "<geo-confirm-modal\n [visible]=\"open\"\n title=\"Xóa hồ sơ?\"\n message=\"Hành động không thể hoàn tác.\"\n confirmText=\"Xóa\"\n (confirm)=\"onDelete()\"\n (visibleChange)=\"open = $event\"\n></geo-confirm-modal>",
590
+ "related": [
591
+ "geo-form-modal",
592
+ "geo-button",
593
+ "geo-toast"
594
+ ]
176
595
  },
177
596
  {
178
597
  "name": "GeoDatePickerComponent",
@@ -181,29 +600,133 @@
181
600
  "summary": "Single date (optional time) picker.",
182
601
  "whenToUse": "One date field in forms.",
183
602
  "doNotUseWhen": "Date range — use geo-date-range-picker.",
603
+ "howTo": "Chọn một ngày (CVA). [(ngModel)] hoặc formControlName; format mặc định dd/MM/yyyy; bật showTime khi cần giờ.",
604
+ "tips": [
605
+ "Khoảng ngày → geo-date-range-picker.",
606
+ "[block]=\"true\" trong form modal/grid.",
607
+ "disabledDate để chặn ngày không hợp lệ."
608
+ ],
609
+ "binding": "cva",
610
+ "cva": true,
184
611
  "path": "projects/geo-ui/src/lib/figma/geo-date-picker/geo-date-picker.component.ts",
185
612
  "doc": "ai/components/geo-date-picker.md",
186
613
  "inputs": [
187
- "label",
188
- "placeholder",
189
- "format",
190
- "mode",
191
- "size",
192
- "disabled",
193
- "required",
194
- "error",
195
- "errorMessage",
196
- "allowClear",
197
- "showToday",
198
- "showTime",
199
- "block",
200
- "width",
201
- "disabledDate"
614
+ {
615
+ "name": "label",
616
+ "type": "string",
617
+ "default": "''",
618
+ "note": ""
619
+ },
620
+ {
621
+ "name": "placeholder",
622
+ "type": "string",
623
+ "default": "'Chọn ngày'",
624
+ "note": ""
625
+ },
626
+ {
627
+ "name": "format",
628
+ "type": "string",
629
+ "default": "'dd/MM/yyyy'",
630
+ "note": ""
631
+ },
632
+ {
633
+ "name": "mode",
634
+ "type": "'date' | 'week' | 'month' | 'year'",
635
+ "default": "'date'",
636
+ "note": ""
637
+ },
638
+ {
639
+ "name": "size",
640
+ "type": "'default' | 'small'",
641
+ "default": "'default'",
642
+ "note": ""
643
+ },
644
+ {
645
+ "name": "disabled",
646
+ "type": "boolean",
647
+ "default": "false",
648
+ "note": ""
649
+ },
650
+ {
651
+ "name": "required",
652
+ "type": "boolean",
653
+ "default": "false",
654
+ "note": ""
655
+ },
656
+ {
657
+ "name": "error",
658
+ "type": "boolean",
659
+ "default": "false",
660
+ "note": ""
661
+ },
662
+ {
663
+ "name": "errorMessage",
664
+ "type": "string",
665
+ "default": "''",
666
+ "note": ""
667
+ },
668
+ {
669
+ "name": "allowClear",
670
+ "type": "boolean",
671
+ "default": "true",
672
+ "note": ""
673
+ },
674
+ {
675
+ "name": "showToday",
676
+ "type": "boolean",
677
+ "default": "true",
678
+ "note": ""
679
+ },
680
+ {
681
+ "name": "showTime",
682
+ "type": "boolean",
683
+ "default": "false",
684
+ "note": ""
685
+ },
686
+ {
687
+ "name": "block",
688
+ "type": "boolean",
689
+ "default": "false",
690
+ "note": ""
691
+ },
692
+ {
693
+ "name": "width",
694
+ "type": "number | string",
695
+ "default": "",
696
+ "note": ""
697
+ },
698
+ {
699
+ "name": "disabledDate",
700
+ "type": "(current: Date)",
701
+ "default": "> boolean",
702
+ "note": ""
703
+ }
202
704
  ],
203
705
  "outputs": [
204
- "valueChange"
706
+ {
707
+ "name": "valueChange",
708
+ "type": "EventEmitter<Date | null>",
709
+ "note": ""
710
+ }
711
+ ],
712
+ "examples": [
713
+ {
714
+ "title": "Basic",
715
+ "lang": "html",
716
+ "code": "<geo-date-picker label=\"Ngày tạo\" [required]=\"true\" [(ngModel)]=\"date\" [block]=\"true\"></geo-date-picker>"
717
+ },
718
+ {
719
+ "title": "Full options",
720
+ "lang": "html",
721
+ "code": "<geo-date-picker\n label=\"Ngày hiệu lực\"\n placeholder=\"Chọn ngày\"\n format=\"dd/MM/yyyy\"\n mode=\"date\"\n size=\"default\"\n [required]=\"true\"\n [allowClear]=\"true\"\n [showToday]=\"true\"\n [showTime]=\"false\"\n [block]=\"true\"\n [error]=\"hasError\"\n errorMessage=\"Vui lòng chọn ngày\"\n [disabledDate]=\"disablePast\"\n [(ngModel)]=\"effectiveDate\"\n (valueChange)=\"onDate($event)\"\n></geo-date-picker>"
722
+ }
205
723
  ],
206
- "example": "<geo-date-picker label=\"Ngày tạo\" [required]=\"true\" [(ngModel)]=\"date\" [block]=\"true\"></geo-date-picker>"
724
+ "example": "<geo-date-picker label=\"Ngày tạo\" [required]=\"true\" [(ngModel)]=\"date\" [block]=\"true\"></geo-date-picker>",
725
+ "related": [
726
+ "geo-date-range-picker",
727
+ "geo-input",
728
+ "geo-form-modal"
729
+ ]
207
730
  },
208
731
  {
209
732
  "name": "GeoDateRangePickerComponent",
@@ -212,28 +735,126 @@
212
735
  "summary": "Start–end date range picker.",
213
736
  "whenToUse": "Filters and period selection.",
214
737
  "doNotUseWhen": "Single date — use geo-date-picker.",
738
+ "howTo": "Chọn khoảng từ–đến (CVA). [(ngModel)] nhận [Date, Date] | null; placeholder mặc định ['Từ ngày','Đến ngày'].",
739
+ "tips": [
740
+ "Một ngày đơn → geo-date-picker.",
741
+ "Dùng trong bộ lọc bảng / báo cáo.",
742
+ "separator mặc định '→'."
743
+ ],
744
+ "binding": "cva",
745
+ "cva": true,
215
746
  "path": "projects/geo-ui/src/lib/figma/geo-date-range-picker/geo-date-range-picker.component.ts",
216
747
  "doc": "ai/components/geo-date-range-picker.md",
217
748
  "inputs": [
218
- "label",
219
- "placeholder",
220
- "format",
221
- "size",
222
- "disabled",
223
- "required",
224
- "error",
225
- "errorMessage",
226
- "allowClear",
227
- "showTime",
228
- "block",
229
- "width",
230
- "disabledDate",
231
- "separator"
749
+ {
750
+ "name": "label",
751
+ "type": "string",
752
+ "default": "''",
753
+ "note": ""
754
+ },
755
+ {
756
+ "name": "placeholder",
757
+ "type": "[string, string]",
758
+ "default": "['Từ ngày', 'Đến ngày']",
759
+ "note": ""
760
+ },
761
+ {
762
+ "name": "format",
763
+ "type": "string",
764
+ "default": "'dd/MM/yyyy'",
765
+ "note": ""
766
+ },
767
+ {
768
+ "name": "size",
769
+ "type": "'default' | 'small'",
770
+ "default": "'default'",
771
+ "note": ""
772
+ },
773
+ {
774
+ "name": "disabled",
775
+ "type": "boolean",
776
+ "default": "false",
777
+ "note": ""
778
+ },
779
+ {
780
+ "name": "required",
781
+ "type": "boolean",
782
+ "default": "false",
783
+ "note": ""
784
+ },
785
+ {
786
+ "name": "error",
787
+ "type": "boolean",
788
+ "default": "false",
789
+ "note": ""
790
+ },
791
+ {
792
+ "name": "errorMessage",
793
+ "type": "string",
794
+ "default": "''",
795
+ "note": ""
796
+ },
797
+ {
798
+ "name": "allowClear",
799
+ "type": "boolean",
800
+ "default": "true",
801
+ "note": ""
802
+ },
803
+ {
804
+ "name": "showTime",
805
+ "type": "boolean",
806
+ "default": "false",
807
+ "note": ""
808
+ },
809
+ {
810
+ "name": "block",
811
+ "type": "boolean",
812
+ "default": "false",
813
+ "note": ""
814
+ },
815
+ {
816
+ "name": "width",
817
+ "type": "number | string",
818
+ "default": "",
819
+ "note": ""
820
+ },
821
+ {
822
+ "name": "disabledDate",
823
+ "type": "(current: Date)",
824
+ "default": "> boolean",
825
+ "note": ""
826
+ },
827
+ {
828
+ "name": "separator",
829
+ "type": "string",
830
+ "default": "'→'",
831
+ "note": ""
832
+ }
232
833
  ],
233
834
  "outputs": [
234
- "valueChange"
835
+ {
836
+ "name": "valueChange",
837
+ "type": "EventEmitter<[Date, Date] | null>",
838
+ "note": ""
839
+ }
840
+ ],
841
+ "examples": [
842
+ {
843
+ "title": "Basic",
844
+ "lang": "html",
845
+ "code": "<geo-date-range-picker label=\"Khoảng thời gian\" [(ngModel)]=\"range\" [block]=\"true\"></geo-date-range-picker>"
846
+ },
847
+ {
848
+ "title": "Full options",
849
+ "lang": "html",
850
+ "code": "<geo-date-range-picker\n label=\"Thời gian lọc\"\n [placeholder]=\"['Từ ngày', 'Đến ngày']\"\n format=\"dd/MM/yyyy\"\n size=\"default\"\n separator=\"→\"\n [allowClear]=\"true\"\n [showTime]=\"false\"\n [block]=\"true\"\n [required]=\"true\"\n [error]=\"rangeError\"\n errorMessage=\"Vui lòng chọn khoảng thời gian\"\n [(ngModel)]=\"dateRange\"\n (valueChange)=\"onRange($event)\"\n></geo-date-range-picker>"
851
+ }
235
852
  ],
236
- "example": "<geo-date-range-picker label=\"Khoảng thời gian\" [(ngModel)]=\"range\" [block]=\"true\"></geo-date-range-picker>"
853
+ "example": "<geo-date-range-picker label=\"Khoảng thời gian\" [(ngModel)]=\"range\" [block]=\"true\"></geo-date-range-picker>",
854
+ "related": [
855
+ "geo-date-picker",
856
+ "geo-table-filter"
857
+ ]
237
858
  },
238
859
  {
239
860
  "name": "GeoFilePickerComponent",
@@ -242,26 +863,115 @@
242
863
  "summary": "Compact file input for forms (button + filename).",
243
864
  "whenToUse": "Single/small file field inside a form row.",
244
865
  "doNotUseWhen": "Drag-drop multi upload with progress — use geo-upload.",
866
+ "howTo": "Ô chọn file gọn trong form (nút + tên file). CVA; (fileChange) nhận File | FileList; accept để giới hạn loại.",
867
+ "tips": [
868
+ "Upload kéo-thả nhiều file + progress → geo-upload.",
869
+ "selectedName hiển thị tên đã chọn khi không bind CVA file object.",
870
+ "[block]=\"true\" trong modal form."
871
+ ],
872
+ "binding": "cva",
873
+ "cva": true,
245
874
  "path": "projects/geo-ui/src/lib/figma/geo-file-picker/geo-file-picker.component.ts",
246
875
  "doc": "ai/components/geo-file-picker.md",
247
876
  "inputs": [
248
- "label",
249
- "placeholder",
250
- "buttonText",
251
- "required",
252
- "disabled",
253
- "accept",
254
- "multiple",
255
- "error",
256
- "errorMessage",
257
- "block",
258
- "width",
259
- "selectedName"
877
+ {
878
+ "name": "label",
879
+ "type": "string",
880
+ "default": "'Tải file'",
881
+ "note": ""
882
+ },
883
+ {
884
+ "name": "placeholder",
885
+ "type": "string",
886
+ "default": "'Chọn giá trị'",
887
+ "note": ""
888
+ },
889
+ {
890
+ "name": "buttonText",
891
+ "type": "string",
892
+ "default": "'Chọn file'",
893
+ "note": ""
894
+ },
895
+ {
896
+ "name": "required",
897
+ "type": "boolean",
898
+ "default": "false",
899
+ "note": ""
900
+ },
901
+ {
902
+ "name": "disabled",
903
+ "type": "boolean",
904
+ "default": "false",
905
+ "note": ""
906
+ },
907
+ {
908
+ "name": "accept",
909
+ "type": "string",
910
+ "default": "''",
911
+ "note": ""
912
+ },
913
+ {
914
+ "name": "multiple",
915
+ "type": "boolean",
916
+ "default": "false",
917
+ "note": ""
918
+ },
919
+ {
920
+ "name": "error",
921
+ "type": "boolean",
922
+ "default": "false",
923
+ "note": ""
924
+ },
925
+ {
926
+ "name": "errorMessage",
927
+ "type": "string",
928
+ "default": "''",
929
+ "note": ""
930
+ },
931
+ {
932
+ "name": "block",
933
+ "type": "boolean",
934
+ "default": "false",
935
+ "note": ""
936
+ },
937
+ {
938
+ "name": "width",
939
+ "type": "number | string",
940
+ "default": "",
941
+ "note": ""
942
+ },
943
+ {
944
+ "name": "selectedName",
945
+ "type": "string",
946
+ "default": "''",
947
+ "note": "Tên file đã upload (server) — hiện khi chưa có File local trong CVA. Dùng cho flow: chọn file → upload → giữ tên từ API."
948
+ }
260
949
  ],
261
950
  "outputs": [
262
- "fileChange"
951
+ {
952
+ "name": "fileChange",
953
+ "type": "EventEmitter<File | File[] | null>",
954
+ "note": ""
955
+ }
263
956
  ],
264
- "example": "<geo-file-picker label=\"Đính kèm\" accept=\".pdf,.png\" (fileChange)=\"onFile($event)\" [block]=\"true\"></geo-file-picker>"
957
+ "examples": [
958
+ {
959
+ "title": "Basic",
960
+ "lang": "html",
961
+ "code": "<geo-file-picker label=\"Đính kèm\" accept=\".pdf,.png\" (fileChange)=\"onFile($event)\" [block]=\"true\"></geo-file-picker>"
962
+ },
963
+ {
964
+ "title": "Full options",
965
+ "lang": "html",
966
+ "code": "<geo-file-picker\n label=\"Tải file\"\n placeholder=\"Chọn giá trị\"\n buttonText=\"Chọn file\"\n accept=\".pdf,.doc,.docx\"\n [multiple]=\"false\"\n [required]=\"true\"\n [block]=\"true\"\n [error]=\"fileError\"\n errorMessage=\"Vui lòng chọn file\"\n [(ngModel)]=\"file\"\n (fileChange)=\"onFile($event)\"\n></geo-file-picker>"
967
+ }
968
+ ],
969
+ "example": "<geo-file-picker label=\"Đính kèm\" accept=\".pdf,.png\" (fileChange)=\"onFile($event)\" [block]=\"true\"></geo-file-picker>",
970
+ "related": [
971
+ "geo-upload",
972
+ "geo-form-modal",
973
+ "geo-input"
974
+ ]
265
975
  },
266
976
  {
267
977
  "name": "GeoFormModalComponent",
@@ -270,43 +980,214 @@
270
980
  "summary": "Form modal: fixed header/footer, scrollable body via ng-content.",
271
981
  "whenToUse": "Create/edit dialogs; compose geo-stepper inside body for multi-step.",
272
982
  "doNotUseWhen": "Full-page wizards — use geo-step-form-layout.",
983
+ "howTo": "Modal form: header/footer cố định, body scroll qua ng-content. [open] + (openChange); (ok)/(cancel). Ghép geo-stepper trong body cho multi-step dialog.",
984
+ "tips": [
985
+ "Wizard full-page → geo-step-form-layout.",
986
+ "okIcon/cancelIcon + placement cho nút Tiếp theo / Quay lại.",
987
+ "bodyMaxHeight mặc định 60vh; readonly ẩn nút OK."
988
+ ],
989
+ "binding": "none",
990
+ "cva": false,
273
991
  "path": "projects/geo-ui/src/lib/figma/geo-form-modal/geo-form-modal.component.ts",
274
992
  "doc": "ai/components/geo-form-modal.md",
275
993
  "inputs": [
276
- "open",
277
- "title",
278
- "titleUppercase",
279
- "okText",
280
- "cancelText",
281
- "cancelIcon",
282
- "cancelIconPlacement",
283
- "okIcon",
284
- "okIconPlacement",
285
- "secondaryOkText",
286
- "confirmLoading",
287
- "okDisabled",
288
- "readonly",
289
- "width",
290
- "closable",
291
- "mask",
292
- "maskClosable",
293
- "keyboard",
294
- "centered",
295
- "zIndex",
296
- "className",
297
- "wrapClassName",
298
- "bodyScroll",
299
- "bodyMaxHeight"
994
+ {
995
+ "name": "open",
996
+ "type": "boolean",
997
+ "default": "false",
998
+ "note": "Tương đương `open` (antd) / `nzVisible` (ng-zorro)."
999
+ },
1000
+ {
1001
+ "name": "title",
1002
+ "type": "string",
1003
+ "default": "''",
1004
+ "note": ""
1005
+ },
1006
+ {
1007
+ "name": "titleUppercase",
1008
+ "type": "boolean",
1009
+ "default": "true",
1010
+ "note": "Title chữ in hoa. Default `true` theo design."
1011
+ },
1012
+ {
1013
+ "name": "okText",
1014
+ "type": "string",
1015
+ "default": "'Lưu'",
1016
+ "note": ""
1017
+ },
1018
+ {
1019
+ "name": "cancelText",
1020
+ "type": "string",
1021
+ "default": "'Hủy'",
1022
+ "note": ""
1023
+ },
1024
+ {
1025
+ "name": "cancelIcon",
1026
+ "type": "string",
1027
+ "default": "",
1028
+ "note": "Icon nút cancel (vd. `left` cho Quay lại)."
1029
+ },
1030
+ {
1031
+ "name": "cancelIconPlacement",
1032
+ "type": "'start' | 'end'",
1033
+ "default": "'start'",
1034
+ "note": ""
1035
+ },
1036
+ {
1037
+ "name": "okIcon",
1038
+ "type": "string",
1039
+ "default": "",
1040
+ "note": "Icon nút OK (vd. `right` cho Tiếp theo)."
1041
+ },
1042
+ {
1043
+ "name": "okIconPlacement",
1044
+ "type": "'start' | 'end'",
1045
+ "default": "'end'",
1046
+ "note": ""
1047
+ },
1048
+ {
1049
+ "name": "secondaryOkText",
1050
+ "type": "string",
1051
+ "default": "''",
1052
+ "note": "Nút phụ (vd. Lưu và khởi tạo lại). Chỉ hiện khi có text và không readonly."
1053
+ },
1054
+ {
1055
+ "name": "confirmLoading",
1056
+ "type": "boolean",
1057
+ "default": "false",
1058
+ "note": "Loading trên nút OK — giống antd `confirmLoading` / nz `nzOkLoading`."
1059
+ },
1060
+ {
1061
+ "name": "okDisabled",
1062
+ "type": "boolean",
1063
+ "default": "false",
1064
+ "note": "Disable nút OK — giống antd `okButtonProps.disabled` / nz `nzOkDisabled`."
1065
+ },
1066
+ {
1067
+ "name": "readonly",
1068
+ "type": "boolean",
1069
+ "default": "false",
1070
+ "note": "Ẩn nút OK (xem chi tiết)."
1071
+ },
1072
+ {
1073
+ "name": "width",
1074
+ "type": "number | string",
1075
+ "default": "720",
1076
+ "note": ""
1077
+ },
1078
+ {
1079
+ "name": "closable",
1080
+ "type": "boolean",
1081
+ "default": "true",
1082
+ "note": ""
1083
+ },
1084
+ {
1085
+ "name": "mask",
1086
+ "type": "boolean",
1087
+ "default": "true",
1088
+ "note": ""
1089
+ },
1090
+ {
1091
+ "name": "maskClosable",
1092
+ "type": "boolean",
1093
+ "default": "false",
1094
+ "note": ""
1095
+ },
1096
+ {
1097
+ "name": "keyboard",
1098
+ "type": "boolean",
1099
+ "default": "true",
1100
+ "note": ""
1101
+ },
1102
+ {
1103
+ "name": "centered",
1104
+ "type": "boolean",
1105
+ "default": "true",
1106
+ "note": ""
1107
+ },
1108
+ {
1109
+ "name": "zIndex",
1110
+ "type": "number",
1111
+ "default": "1000",
1112
+ "note": ""
1113
+ },
1114
+ {
1115
+ "name": "className",
1116
+ "type": "string",
1117
+ "default": "''",
1118
+ "note": ""
1119
+ },
1120
+ {
1121
+ "name": "wrapClassName",
1122
+ "type": "string",
1123
+ "default": "''",
1124
+ "note": ""
1125
+ },
1126
+ {
1127
+ "name": "bodyScroll",
1128
+ "type": "boolean",
1129
+ "default": "true",
1130
+ "note": "Body cuộn, header/footer cố định."
1131
+ },
1132
+ {
1133
+ "name": "bodyMaxHeight",
1134
+ "type": "string | number",
1135
+ "default": "'60vh'",
1136
+ "note": "Max-height của vùng body khi `bodyScroll` (vd `'60vh'`, `400`)."
1137
+ }
300
1138
  ],
301
1139
  "outputs": [
302
- "openChange",
303
- "ok",
304
- "secondaryOk",
305
- "cancel",
306
- "afterOpen",
307
- "afterClose"
308
- ],
309
- "example": "<geo-form-modal\n [open]=\"open\"\n title=\"Thêm mới\"\n cancelText=\"Hủy bỏ\"\n okText=\"Lưu\"\n (openChange)=\"open = $event\"\n (ok)=\"save()\"\n>\n <geo-input label=\"Tên\" [required]=\"true\" [block]=\"true\" [(ngModel)]=\"name\"></geo-input>\n</geo-form-modal>"
1140
+ {
1141
+ "name": "openChange",
1142
+ "type": "EventEmitter<boolean>",
1143
+ "note": ""
1144
+ },
1145
+ {
1146
+ "name": "ok",
1147
+ "type": "EventEmitter<void>",
1148
+ "note": ""
1149
+ },
1150
+ {
1151
+ "name": "secondaryOk",
1152
+ "type": "EventEmitter<void>",
1153
+ "note": ""
1154
+ },
1155
+ {
1156
+ "name": "cancel",
1157
+ "type": "EventEmitter<void>",
1158
+ "note": ""
1159
+ },
1160
+ {
1161
+ "name": "afterOpen",
1162
+ "type": "EventEmitter<void>",
1163
+ "note": ""
1164
+ },
1165
+ {
1166
+ "name": "afterClose",
1167
+ "type": "EventEmitter<void>",
1168
+ "note": ""
1169
+ }
1170
+ ],
1171
+ "examples": [
1172
+ {
1173
+ "title": "Basic",
1174
+ "lang": "html",
1175
+ "code": "<geo-form-modal\n [open]=\"open\"\n title=\"Thêm mới\"\n cancelText=\"Hủy bỏ\"\n okText=\"Lưu\"\n (openChange)=\"open = $event\"\n (ok)=\"save()\"\n>\n <geo-input label=\"Tên\" [required]=\"true\" [block]=\"true\" [(ngModel)]=\"name\"></geo-input>\n</geo-form-modal>"
1176
+ },
1177
+ {
1178
+ "title": "Full options",
1179
+ "lang": "html",
1180
+ "code": "<geo-form-modal\n [open]=\"open\"\n title=\"Lập phiếu đăng ký\"\n [titleUppercase]=\"true\"\n cancelText=\"Hủy bỏ\"\n okText=\"Lưu\"\n okIcon=\"right\"\n okIconPlacement=\"end\"\n [confirmLoading]=\"saving\"\n [okDisabled]=\"!valid\"\n [width]=\"720\"\n [closable]=\"true\"\n [mask]=\"true\"\n [maskClosable]=\"false\"\n [centered]=\"true\"\n [bodyScroll]=\"true\"\n bodyMaxHeight=\"60vh\"\n (openChange)=\"open = $event\"\n (ok)=\"onSave()\"\n (cancel)=\"open = false\"\n>\n <div style=\"display:grid;grid-template-columns:1fr 1fr;gap:16px;\">\n <geo-input label=\"Mã\" [required]=\"true\" [block]=\"true\" [(ngModel)]=\"code\"></geo-input>\n <geo-input label=\"Tên\" [required]=\"true\" [block]=\"true\" [(ngModel)]=\"name\"></geo-input>\n </div>\n</geo-form-modal>"
1181
+ }
1182
+ ],
1183
+ "example": "<geo-form-modal\n [open]=\"open\"\n title=\"Thêm mới\"\n cancelText=\"Hủy bỏ\"\n okText=\"Lưu\"\n (openChange)=\"open = $event\"\n (ok)=\"save()\"\n>\n <geo-input label=\"Tên\" [required]=\"true\" [block]=\"true\" [(ngModel)]=\"name\"></geo-input>\n</geo-form-modal>",
1184
+ "related": [
1185
+ "geo-stepper",
1186
+ "geo-input",
1187
+ "geo-select",
1188
+ "geo-confirm-modal",
1189
+ "geo-step-form-layout"
1190
+ ]
310
1191
  },
311
1192
  {
312
1193
  "name": "GeoIconComponent",
@@ -315,17 +1196,66 @@
315
1196
  "summary": "Ant Design icon wrapper with GEO size/color tokens.",
316
1197
  "whenToUse": "Standalone icons; buttons usually use geo-button [icon].",
317
1198
  "doNotUseWhen": "Do not hardcode icon hex colors — use color token names.",
1199
+ "howTo": "Wrapper icon Ant Design. Bắt buộc [type]; size xs|sm|md|lg|xl hoặc px; color dùng token (vd. primary / var(--geo-color-*)).",
1200
+ "tips": [
1201
+ "Trong nút ưu tiên geo-button [icon].",
1202
+ "Không hardcode hex — dùng color token.",
1203
+ "theme outline|fill|twotone; spin cho loading."
1204
+ ],
1205
+ "binding": "none",
1206
+ "cva": false,
318
1207
  "path": "projects/geo-ui/src/lib/figma/geo-icon/geo-icon.component.ts",
319
1208
  "doc": "ai/components/geo-icon.md",
320
1209
  "inputs": [
321
- "type",
322
- "theme",
323
- "spin",
324
- "size",
325
- "color"
1210
+ {
1211
+ "name": "type",
1212
+ "type": "",
1213
+ "default": "",
1214
+ "note": "Tên icon Ant Design (vd. `close`, `search`, `appstore`)."
1215
+ },
1216
+ {
1217
+ "name": "theme",
1218
+ "type": "'outline' | 'fill' | 'twotone'",
1219
+ "default": "'outline'",
1220
+ "note": ""
1221
+ },
1222
+ {
1223
+ "name": "spin",
1224
+ "type": "boolean",
1225
+ "default": "false",
1226
+ "note": ""
1227
+ },
1228
+ {
1229
+ "name": "size",
1230
+ "type": "'xs' | 'sm' | 'md' | 'lg' | 'xl' | number | string",
1231
+ "default": "'md'",
1232
+ "note": "xs | sm | md | lg | xl | number(px) | CSS size string"
1233
+ },
1234
+ {
1235
+ "name": "color",
1236
+ "type": "string",
1237
+ "default": "",
1238
+ "note": "CSS color — ưu tiên design token, vd. `var(--geo-color-primary-default)`"
1239
+ }
326
1240
  ],
327
1241
  "outputs": [],
328
- "example": "<geo-icon type=\"search\" size=\"md\" color=\"primary\"></geo-icon>"
1242
+ "examples": [
1243
+ {
1244
+ "title": "Basic",
1245
+ "lang": "html",
1246
+ "code": "<geo-icon type=\"search\" size=\"md\" color=\"primary\"></geo-icon>"
1247
+ },
1248
+ {
1249
+ "title": "Full options",
1250
+ "lang": "html",
1251
+ "code": "<geo-icon\n type=\"loading\"\n theme=\"outline\"\n size=\"lg\"\n color=\"var(--geo-color-primary-default)\"\n [spin]=\"true\"\n></geo-icon>"
1252
+ }
1253
+ ],
1254
+ "example": "<geo-icon type=\"search\" size=\"md\" color=\"primary\"></geo-icon>",
1255
+ "related": [
1256
+ "geo-button",
1257
+ "geo-status-tag"
1258
+ ]
329
1259
  },
330
1260
  {
331
1261
  "name": "GeoInputComponent",
@@ -334,29 +1264,132 @@
334
1264
  "summary": "Text input with label, validation, icons; CVA/ngModel.",
335
1265
  "whenToUse": "Default text fields in forms.",
336
1266
  "doNotUseWhen": "Long text — use geo-textarea; select options — use geo-select.",
1267
+ "howTo": "Ô text chuẩn form (CVA). label + [(ngModel)]; type text|number|password|email; prefixIcon/suffixIcon; [block] full width.",
1268
+ "tips": [
1269
+ "Văn bản dài → geo-textarea; chọn danh sách → geo-select.",
1270
+ "error + errorMessage khi validate fail.",
1271
+ "(enterPressed) cho tìm kiếm nhanh."
1272
+ ],
1273
+ "binding": "cva",
1274
+ "cva": true,
337
1275
  "path": "projects/geo-ui/src/lib/figma/geo-input/geo-input.component.ts",
338
1276
  "doc": "ai/components/geo-input.md",
339
1277
  "inputs": [
340
- "label",
341
- "placeholder",
342
- "type",
343
- "disabled",
344
- "readonly",
345
- "required",
346
- "error",
347
- "errorMessage",
348
- "maxlength",
349
- "prefixIcon",
350
- "suffixIcon",
351
- "allowClear",
352
- "block",
353
- "width"
1278
+ {
1279
+ "name": "label",
1280
+ "type": "string",
1281
+ "default": "''",
1282
+ "note": ""
1283
+ },
1284
+ {
1285
+ "name": "placeholder",
1286
+ "type": "string",
1287
+ "default": "''",
1288
+ "note": ""
1289
+ },
1290
+ {
1291
+ "name": "type",
1292
+ "type": "'text' | 'number' | 'password' | 'email'",
1293
+ "default": "'text'",
1294
+ "note": ""
1295
+ },
1296
+ {
1297
+ "name": "disabled",
1298
+ "type": "boolean",
1299
+ "default": "false",
1300
+ "note": ""
1301
+ },
1302
+ {
1303
+ "name": "readonly",
1304
+ "type": "boolean",
1305
+ "default": "false",
1306
+ "note": ""
1307
+ },
1308
+ {
1309
+ "name": "required",
1310
+ "type": "boolean",
1311
+ "default": "false",
1312
+ "note": ""
1313
+ },
1314
+ {
1315
+ "name": "error",
1316
+ "type": "boolean",
1317
+ "default": "false",
1318
+ "note": ""
1319
+ },
1320
+ {
1321
+ "name": "errorMessage",
1322
+ "type": "string",
1323
+ "default": "''",
1324
+ "note": ""
1325
+ },
1326
+ {
1327
+ "name": "maxlength",
1328
+ "type": "number",
1329
+ "default": "",
1330
+ "note": ""
1331
+ },
1332
+ {
1333
+ "name": "prefixIcon",
1334
+ "type": "string",
1335
+ "default": "",
1336
+ "note": ""
1337
+ },
1338
+ {
1339
+ "name": "suffixIcon",
1340
+ "type": "string",
1341
+ "default": "",
1342
+ "note": ""
1343
+ },
1344
+ {
1345
+ "name": "allowClear",
1346
+ "type": "boolean",
1347
+ "default": "false",
1348
+ "note": ""
1349
+ },
1350
+ {
1351
+ "name": "block",
1352
+ "type": "boolean",
1353
+ "default": "false",
1354
+ "note": ""
1355
+ },
1356
+ {
1357
+ "name": "width",
1358
+ "type": "number | string",
1359
+ "default": "",
1360
+ "note": ""
1361
+ }
354
1362
  ],
355
1363
  "outputs": [
356
- "valueChange",
357
- "enterPressed"
1364
+ {
1365
+ "name": "valueChange",
1366
+ "type": "EventEmitter<string>",
1367
+ "note": ""
1368
+ },
1369
+ {
1370
+ "name": "enterPressed",
1371
+ "type": "EventEmitter<void>",
1372
+ "note": ""
1373
+ }
1374
+ ],
1375
+ "examples": [
1376
+ {
1377
+ "title": "Basic",
1378
+ "lang": "html",
1379
+ "code": "<geo-input label=\"Họ tên\" [required]=\"true\" [block]=\"true\" [(ngModel)]=\"name\"></geo-input>"
1380
+ },
1381
+ {
1382
+ "title": "Full options",
1383
+ "lang": "html",
1384
+ "code": "<geo-input\n label=\"Mã hồ sơ\"\n placeholder=\"Nhập mã hồ sơ\"\n type=\"text\"\n prefixIcon=\"search\"\n [allowClear]=\"true\"\n [required]=\"true\"\n [block]=\"true\"\n [maxlength]=\"50\"\n [error]=\"codeError\"\n errorMessage=\"Mã không được để trống\"\n [(ngModel)]=\"code\"\n (enterPressed)=\"onSearch()\"\n (valueChange)=\"onCode($event)\"\n></geo-input>"
1385
+ }
358
1386
  ],
359
- "example": "<geo-input label=\"Họ tên\" [required]=\"true\" [block]=\"true\" [(ngModel)]=\"name\"></geo-input>"
1387
+ "example": "<geo-input label=\"Họ tên\" [required]=\"true\" [block]=\"true\" [(ngModel)]=\"name\"></geo-input>",
1388
+ "related": [
1389
+ "geo-textarea",
1390
+ "geo-select",
1391
+ "geo-form-modal"
1392
+ ]
360
1393
  },
361
1394
  {
362
1395
  "name": "GeoPageTitleComponent",
@@ -365,17 +1398,67 @@
365
1398
  "summary": "Page title (uppercase) with short primary accent underline; uses geo-typography.",
366
1399
  "whenToUse": "Top of detail/list pages, often under breadcrumb.",
367
1400
  "doNotUseWhen": "Modal titles — geo-form-modal has its own title.",
1401
+ "howTo": "Tiêu đề trang chữ hoa + gạch nhấn primary. title + level 1–4; accentWidth/accentHeight tùy chỉnh gạch.",
1402
+ "tips": [
1403
+ "Thường dưới geo-breadcrumb.",
1404
+ "Tiêu đề modal → geo-form-modal title, không dùng page-title.",
1405
+ "uppercase mặc định true."
1406
+ ],
1407
+ "binding": "none",
1408
+ "cva": false,
368
1409
  "path": "projects/geo-ui/src/lib/figma/geo-page-title/geo-page-title.component.ts",
369
1410
  "doc": "ai/components/geo-page-title.md",
370
1411
  "inputs": [
371
- "title",
372
- "uppercase",
373
- "accentWidth",
374
- "accentHeight",
375
- "level"
1412
+ {
1413
+ "name": "title",
1414
+ "type": "string",
1415
+ "default": "''",
1416
+ "note": "Nội dung tiêu đề."
1417
+ },
1418
+ {
1419
+ "name": "uppercase",
1420
+ "type": "boolean",
1421
+ "default": "true",
1422
+ "note": "Tự chuyển uppercase. Default `true`."
1423
+ },
1424
+ {
1425
+ "name": "accentWidth",
1426
+ "type": "number | string",
1427
+ "default": "56",
1428
+ "note": "Chiều rộng gạch chân (px hoặc CSS size). Default `56px`."
1429
+ },
1430
+ {
1431
+ "name": "accentHeight",
1432
+ "type": "number | string",
1433
+ "default": "4",
1434
+ "note": "Độ dày gạch chân. Default `4px`."
1435
+ },
1436
+ {
1437
+ "name": "level",
1438
+ "type": "1 | 2 | 3 | 4",
1439
+ "default": "3",
1440
+ "note": "Heading level → geo-typography variant."
1441
+ }
376
1442
  ],
377
1443
  "outputs": [],
378
- "example": "<geo-page-title title=\"Thông tin chi tiết hồ sơ\" [level]=\"3\"></geo-page-title>"
1444
+ "examples": [
1445
+ {
1446
+ "title": "Basic",
1447
+ "lang": "html",
1448
+ "code": "<geo-page-title title=\"Thông tin chi tiết hồ sơ\" [level]=\"3\"></geo-page-title>"
1449
+ },
1450
+ {
1451
+ "title": "Full options",
1452
+ "lang": "html",
1453
+ "code": "<geo-page-title\n title=\"Danh sách hồ sơ khai thác\"\n [uppercase]=\"true\"\n [level]=\"3\"\n [accentWidth]=\"56\"\n [accentHeight]=\"4\"\n></geo-page-title>"
1454
+ }
1455
+ ],
1456
+ "example": "<geo-page-title title=\"Thông tin chi tiết hồ sơ\" [level]=\"3\"></geo-page-title>",
1457
+ "related": [
1458
+ "geo-breadcrumb",
1459
+ "geo-typography",
1460
+ "geo-back-button"
1461
+ ]
379
1462
  },
380
1463
  {
381
1464
  "name": "GeoPaginationComponent",
@@ -384,23 +1467,95 @@
384
1467
  "summary": "Standalone pagination control.",
385
1468
  "whenToUse": "Paging outside geo-table.",
386
1469
  "doNotUseWhen": "Inside tables prefer geo-table / geo-table-pagination.",
1470
+ "howTo": "Phân trang độc lập ngoài bảng. [pageIndex]/[pageSize]/[total]; (pageIndexChange)/(pageSizeChange).",
1471
+ "tips": [
1472
+ "Trong geo-table ưu tiên phân trang tích hợp / geo-table-pagination.",
1473
+ "showSizeChanger + pageSizeOptions khi cần đổi số dòng.",
1474
+ "hideOnSinglePage khi chỉ 1 trang."
1475
+ ],
1476
+ "binding": "none",
1477
+ "cva": false,
387
1478
  "path": "projects/geo-ui/src/lib/figma/geo-pagination/geo-pagination.component.ts",
388
1479
  "doc": "ai/components/geo-pagination.md",
389
1480
  "inputs": [
390
- "pageIndex",
391
- "pageSize",
392
- "total",
393
- "size",
394
- "simple",
395
- "hideOnSinglePage",
396
- "showSizeChanger",
397
- "pageSizeOptions"
1481
+ {
1482
+ "name": "pageIndex",
1483
+ "type": "number",
1484
+ "default": "1",
1485
+ "note": ""
1486
+ },
1487
+ {
1488
+ "name": "pageSize",
1489
+ "type": "number",
1490
+ "default": "10",
1491
+ "note": ""
1492
+ },
1493
+ {
1494
+ "name": "total",
1495
+ "type": "number",
1496
+ "default": "0",
1497
+ "note": ""
1498
+ },
1499
+ {
1500
+ "name": "size",
1501
+ "type": "'default' | 'small'",
1502
+ "default": "'default'",
1503
+ "note": ""
1504
+ },
1505
+ {
1506
+ "name": "simple",
1507
+ "type": "boolean",
1508
+ "default": "false",
1509
+ "note": ""
1510
+ },
1511
+ {
1512
+ "name": "hideOnSinglePage",
1513
+ "type": "boolean",
1514
+ "default": "false",
1515
+ "note": ""
1516
+ },
1517
+ {
1518
+ "name": "showSizeChanger",
1519
+ "type": "boolean",
1520
+ "default": "false",
1521
+ "note": ""
1522
+ },
1523
+ {
1524
+ "name": "pageSizeOptions",
1525
+ "type": "number[]",
1526
+ "default": "[5, 10, 20, 50]",
1527
+ "note": ""
1528
+ }
398
1529
  ],
399
1530
  "outputs": [
400
- "pageIndexChange",
401
- "pageSizeChange"
1531
+ {
1532
+ "name": "pageIndexChange",
1533
+ "type": "EventEmitter<number>",
1534
+ "note": ""
1535
+ },
1536
+ {
1537
+ "name": "pageSizeChange",
1538
+ "type": "EventEmitter<number>",
1539
+ "note": ""
1540
+ }
1541
+ ],
1542
+ "examples": [
1543
+ {
1544
+ "title": "Basic",
1545
+ "lang": "html",
1546
+ "code": "<geo-pagination\n [pageIndex]=\"1\"\n [pageSize]=\"10\"\n [total]=\"100\"\n (pageIndexChange)=\"onPage($event)\"\n></geo-pagination>"
1547
+ },
1548
+ {
1549
+ "title": "Full options",
1550
+ "lang": "html",
1551
+ "code": "<geo-pagination\n [pageIndex]=\"pageIndex\"\n [pageSize]=\"pageSize\"\n [total]=\"total\"\n size=\"default\"\n [simple]=\"false\"\n [hideOnSinglePage]=\"false\"\n [showSizeChanger]=\"true\"\n [pageSizeOptions]=\"[5, 10, 20, 50]\"\n (pageIndexChange)=\"pageIndex = $event; load()\"\n (pageSizeChange)=\"pageSize = $event; load()\"\n></geo-pagination>"
1552
+ }
402
1553
  ],
403
- "example": "<geo-pagination [pageIndex]=\"1\" [pageSize]=\"10\" [total]=\"100\" (pageIndexChange)=\"onPage($event)\"></geo-pagination>"
1554
+ "example": "<geo-pagination\n [pageIndex]=\"1\"\n [pageSize]=\"10\"\n [total]=\"100\"\n (pageIndexChange)=\"onPage($event)\"\n></geo-pagination>",
1555
+ "related": [
1556
+ "geo-table",
1557
+ "geo-table-pagination"
1558
+ ]
404
1559
  },
405
1560
  {
406
1561
  "name": "GeoRadioComponent",
@@ -409,25 +1564,108 @@
409
1564
  "summary": "Radio group (horizontal/vertical or button style).",
410
1565
  "whenToUse": "Exclusive choice among few options.",
411
1566
  "doNotUseWhen": "Many options — use geo-select.",
1567
+ "howTo": "Nhóm radio độc quyền (CVA). [options] {label,value}[]; layout horizontal|vertical; buttonStyle cho dạng nút.",
1568
+ "tips": [
1569
+ "Nhiều option → geo-select.",
1570
+ "buttonAppearance outline|solid khi buttonStyle=true.",
1571
+ "[block] + label cho form field."
1572
+ ],
1573
+ "binding": "cva",
1574
+ "cva": true,
412
1575
  "path": "projects/geo-ui/src/lib/figma/geo-radio/geo-radio.component.ts",
413
1576
  "doc": "ai/components/geo-radio.md",
414
1577
  "inputs": [
415
- "options",
416
- "label",
417
- "size",
418
- "layout",
419
- "buttonStyle",
420
- "buttonAppearance",
421
- "disabled",
422
- "required",
423
- "error",
424
- "errorMessage",
425
- "block"
1578
+ {
1579
+ "name": "options",
1580
+ "type": "GeoRadioOption[]",
1581
+ "default": "[]",
1582
+ "note": ""
1583
+ },
1584
+ {
1585
+ "name": "label",
1586
+ "type": "string",
1587
+ "default": "''",
1588
+ "note": ""
1589
+ },
1590
+ {
1591
+ "name": "size",
1592
+ "type": "'default' | 'small' | 'large'",
1593
+ "default": "'default'",
1594
+ "note": ""
1595
+ },
1596
+ {
1597
+ "name": "layout",
1598
+ "type": "'horizontal' | 'vertical'",
1599
+ "default": "'horizontal'",
1600
+ "note": ""
1601
+ },
1602
+ {
1603
+ "name": "buttonStyle",
1604
+ "type": "boolean",
1605
+ "default": "false",
1606
+ "note": "`true` → dùng `nz-radio-button` thay vì `nz-radio`"
1607
+ },
1608
+ {
1609
+ "name": "buttonAppearance",
1610
+ "type": "'outline' | 'solid'",
1611
+ "default": "'outline'",
1612
+ "note": "Chỉ áp dụng khi `buttonStyle: true`"
1613
+ },
1614
+ {
1615
+ "name": "disabled",
1616
+ "type": "boolean",
1617
+ "default": "false",
1618
+ "note": ""
1619
+ },
1620
+ {
1621
+ "name": "required",
1622
+ "type": "boolean",
1623
+ "default": "false",
1624
+ "note": ""
1625
+ },
1626
+ {
1627
+ "name": "error",
1628
+ "type": "boolean",
1629
+ "default": "false",
1630
+ "note": ""
1631
+ },
1632
+ {
1633
+ "name": "errorMessage",
1634
+ "type": "string",
1635
+ "default": "''",
1636
+ "note": ""
1637
+ },
1638
+ {
1639
+ "name": "block",
1640
+ "type": "boolean",
1641
+ "default": "false",
1642
+ "note": ""
1643
+ }
426
1644
  ],
427
1645
  "outputs": [
428
- "valueChange"
1646
+ {
1647
+ "name": "valueChange",
1648
+ "type": "EventEmitter<string | number | null>",
1649
+ "note": ""
1650
+ }
1651
+ ],
1652
+ "examples": [
1653
+ {
1654
+ "title": "Basic",
1655
+ "lang": "html",
1656
+ "code": "<geo-radio\n label=\"Loại yêu cầu\"\n layout=\"vertical\"\n [options]=\"[{ label: 'Cấp mới', value: 'new' }, { label: 'Gia hạn', value: 'renew' }]\"\n [(ngModel)]=\"requestType\"\n [block]=\"true\"\n></geo-radio>"
1657
+ },
1658
+ {
1659
+ "title": "Full options",
1660
+ "lang": "html",
1661
+ "code": "<geo-radio\n label=\"Trạng thái\"\n [options]=\"[\n { label: 'Hoạt động', value: 1 },\n { label: 'Ngưng', value: 0 }\n ]\"\n size=\"default\"\n layout=\"horizontal\"\n [buttonStyle]=\"true\"\n buttonAppearance=\"outline\"\n [required]=\"true\"\n [block]=\"true\"\n [error]=\"statusError\"\n errorMessage=\"Vui lòng chọn trạng thái\"\n [(ngModel)]=\"status\"\n (valueChange)=\"onStatus($event)\"\n></geo-radio>"
1662
+ }
429
1663
  ],
430
- "example": "<geo-radio\n label=\"Loại yêu cầu\"\n layout=\"vertical\"\n [options]=\"[{ label: 'A', value: 'a' }, { label: 'B', value: 'b' }]\"\n [(ngModel)]=\"type\"\n [block]=\"true\"\n></geo-radio>"
1664
+ "example": "<geo-radio\n label=\"Loại yêu cầu\"\n layout=\"vertical\"\n [options]=\"[{ label: 'Cấp mới', value: 'new' }, { label: 'Gia hạn', value: 'renew' }]\"\n [(ngModel)]=\"requestType\"\n [block]=\"true\"\n></geo-radio>",
1665
+ "related": [
1666
+ "geo-checkbox",
1667
+ "geo-select"
1668
+ ]
431
1669
  },
432
1670
  {
433
1671
  "name": "GeoRowActionsComponent",
@@ -436,18 +1674,60 @@
436
1674
  "summary": "Row action icons/menu (edit, more, custom).",
437
1675
  "whenToUse": "Actions column inside geo-table.",
438
1676
  "doNotUseWhen": "Page-level CTAs — use geo-button.",
1677
+ "howTo": "Cụm thao tác dòng bảng. [actions] GeoRowAction[]; (actionClick) theo key. Thường qua cột renderAs: 'actions' của geo-table.",
1678
+ "tips": [
1679
+ "≤2 actions hiện hết; >2: primary + danger, còn lại menu.",
1680
+ "primaryActionKey mặc định 'edit'.",
1681
+ "CTA trang → geo-button, không row-actions."
1682
+ ],
1683
+ "binding": "none",
1684
+ "cva": false,
439
1685
  "path": "projects/geo-ui/src/lib/figma/geo-table/geo-row-actions/geo-row-actions.component.ts",
440
1686
  "doc": "ai/components/geo-row-actions.md",
441
1687
  "inputs": [
442
- "actions",
443
- "primaryActionKey"
1688
+ {
1689
+ "name": "actions",
1690
+ "type": "GeoRowAction[] | null",
1691
+ "default": "null",
1692
+ "note": ""
1693
+ },
1694
+ {
1695
+ "name": "primaryActionKey",
1696
+ "type": "string",
1697
+ "default": "'edit'",
1698
+ "note": "Action ưu tiên hiển thị khi có > 2 thao tác (mặc định: edit)"
1699
+ }
444
1700
  ],
445
1701
  "outputs": [
446
- "actionClick",
447
- "edit",
448
- "more"
1702
+ {
1703
+ "name": "edit",
1704
+ "type": "EventEmitter<void>",
1705
+ "note": "Action ưu tiên hiển thị khi có > 2 thao tác (mặc định: edit) */ @Input() primaryActionKey = 'edit'; @Output() actionClick = new EventEmitter<string>(); /** @deprecated Dùng `actionClick` với key `edit`"
1706
+ },
1707
+ {
1708
+ "name": "more",
1709
+ "type": "EventEmitter<void>",
1710
+ "note": "@deprecated Dùng `actionClick` với key `more`"
1711
+ }
1712
+ ],
1713
+ "examples": [
1714
+ {
1715
+ "title": "Basic",
1716
+ "lang": "html",
1717
+ "code": "<geo-row-actions\n [actions]=\"[\n { key: 'edit', icon: 'edit', title: 'Chỉnh sửa' },\n { key: 'more', icon: 'more', title: 'Thêm thao tác' }\n ]\"\n (actionClick)=\"onAction($event)\"\n></geo-row-actions>"
1718
+ },
1719
+ {
1720
+ "title": "Full options",
1721
+ "lang": "html",
1722
+ "code": "<geo-row-actions\n primaryActionKey=\"edit\"\n [actions]=\"[\n { key: 'edit', icon: 'edit', title: 'Chỉnh sửa' },\n { key: 'view', icon: 'eye', title: 'Xem' },\n { key: 'delete', icon: 'delete', title: 'Xóa', danger: true }\n ]\"\n (actionClick)=\"onRowAction($event)\"\n></geo-row-actions>"
1723
+ }
449
1724
  ],
450
- "example": "<geo-row-actions [actions]=\"actions\" (actionClick)=\"onAction($event)\"></geo-row-actions>"
1725
+ "example": "<geo-row-actions\n [actions]=\"[\n { key: 'edit', icon: 'edit', title: 'Chỉnh sửa' },\n { key: 'more', icon: 'more', title: 'Thêm thao tác' }\n ]\"\n (actionClick)=\"onAction($event)\"\n></geo-row-actions>",
1726
+ "related": [
1727
+ "geo-table",
1728
+ "geo-button",
1729
+ "geo-icon"
1730
+ ]
451
1731
  },
452
1732
  {
453
1733
  "name": "GeoSelectComponent",
@@ -456,27 +1736,121 @@
456
1736
  "summary": "Select / multi-select dropdown.",
457
1737
  "whenToUse": "Choosing from a list of options.",
458
1738
  "doNotUseWhen": "2–3 exclusive options may use geo-radio instead.",
1739
+ "howTo": "Select danh sách (CVA). [options] {label,value}[]; mode default|multiple|tags; showSearch khi danh sách dài.",
1740
+ "tips": [
1741
+ "2–3 lựa chọn độc quyền có thể dùng geo-radio.",
1742
+ "allowClear mặc định true.",
1743
+ "[block]=\"true\" trong form."
1744
+ ],
1745
+ "binding": "cva",
1746
+ "cva": true,
459
1747
  "path": "projects/geo-ui/src/lib/figma/geo-select/geo-select.component.ts",
460
1748
  "doc": "ai/components/geo-select.md",
461
1749
  "inputs": [
462
- "options",
463
- "label",
464
- "placeholder",
465
- "size",
466
- "mode",
467
- "disabled",
468
- "required",
469
- "error",
470
- "errorMessage",
471
- "allowClear",
472
- "showSearch",
473
- "block",
474
- "width"
1750
+ {
1751
+ "name": "options",
1752
+ "type": "GeoSelectOption[]",
1753
+ "default": "[]",
1754
+ "note": ""
1755
+ },
1756
+ {
1757
+ "name": "label",
1758
+ "type": "string",
1759
+ "default": "''",
1760
+ "note": ""
1761
+ },
1762
+ {
1763
+ "name": "placeholder",
1764
+ "type": "string",
1765
+ "default": "'Chọn giá trị'",
1766
+ "note": ""
1767
+ },
1768
+ {
1769
+ "name": "size",
1770
+ "type": "'default' | 'small'",
1771
+ "default": "'default'",
1772
+ "note": ""
1773
+ },
1774
+ {
1775
+ "name": "mode",
1776
+ "type": "'default' | 'multiple' | 'tags'",
1777
+ "default": "'default'",
1778
+ "note": ""
1779
+ },
1780
+ {
1781
+ "name": "disabled",
1782
+ "type": "boolean",
1783
+ "default": "false",
1784
+ "note": ""
1785
+ },
1786
+ {
1787
+ "name": "required",
1788
+ "type": "boolean",
1789
+ "default": "false",
1790
+ "note": ""
1791
+ },
1792
+ {
1793
+ "name": "error",
1794
+ "type": "boolean",
1795
+ "default": "false",
1796
+ "note": ""
1797
+ },
1798
+ {
1799
+ "name": "errorMessage",
1800
+ "type": "string",
1801
+ "default": "''",
1802
+ "note": ""
1803
+ },
1804
+ {
1805
+ "name": "allowClear",
1806
+ "type": "boolean",
1807
+ "default": "true",
1808
+ "note": ""
1809
+ },
1810
+ {
1811
+ "name": "showSearch",
1812
+ "type": "boolean",
1813
+ "default": "false",
1814
+ "note": ""
1815
+ },
1816
+ {
1817
+ "name": "block",
1818
+ "type": "boolean",
1819
+ "default": "false",
1820
+ "note": ""
1821
+ },
1822
+ {
1823
+ "name": "width",
1824
+ "type": "number | string",
1825
+ "default": "",
1826
+ "note": ""
1827
+ }
475
1828
  ],
476
1829
  "outputs": [
477
- "valueChange"
1830
+ {
1831
+ "name": "valueChange",
1832
+ "type": "EventEmitter<string | number | Array<string | number> | null>",
1833
+ "note": ""
1834
+ }
478
1835
  ],
479
- "example": "<geo-select\n label=\"Trạng thái\"\n [options]=\"[{ label: 'Active', value: 1 }, { label: 'Inactive', value: 0 }]\"\n [(ngModel)]=\"status\"\n [block]=\"true\"\n></geo-select>"
1836
+ "examples": [
1837
+ {
1838
+ "title": "Basic",
1839
+ "lang": "html",
1840
+ "code": "<geo-select\n label=\"Trạng thái\"\n [options]=\"[{ label: 'Hoạt động', value: 1 }, { label: 'Ngưng', value: 0 }]\"\n [(ngModel)]=\"status\"\n [block]=\"true\"\n></geo-select>"
1841
+ },
1842
+ {
1843
+ "title": "Full options",
1844
+ "lang": "html",
1845
+ "code": "<geo-select\n label=\"Đơn vị\"\n placeholder=\"Chọn giá trị\"\n mode=\"default\"\n size=\"default\"\n [options]=\"unitOptions\"\n [showSearch]=\"true\"\n [allowClear]=\"true\"\n [required]=\"true\"\n [block]=\"true\"\n [error]=\"unitError\"\n errorMessage=\"Vui lòng chọn đơn vị\"\n [(ngModel)]=\"unitId\"\n (valueChange)=\"onUnit($event)\"\n></geo-select>"
1846
+ }
1847
+ ],
1848
+ "example": "<geo-select\n label=\"Trạng thái\"\n [options]=\"[{ label: 'Hoạt động', value: 1 }, { label: 'Ngưng', value: 0 }]\"\n [(ngModel)]=\"status\"\n [block]=\"true\"\n></geo-select>",
1849
+ "related": [
1850
+ "geo-radio",
1851
+ "geo-input",
1852
+ "geo-form-modal"
1853
+ ]
480
1854
  },
481
1855
  {
482
1856
  "name": "GeoSliderComponent",
@@ -485,20 +1859,78 @@
485
1859
  "summary": "Numeric range slider.",
486
1860
  "whenToUse": "Continuous numeric value in a range.",
487
1861
  "doNotUseWhen": "Discrete small sets — use select/radio.",
1862
+ "howTo": "Thanh trượt số liên tục. Two-way [(value)] / (valueChange); min/max/step; tipFormatter tùy chỉnh tooltip.",
1863
+ "tips": [
1864
+ "Tập rời rạc nhỏ → select/radio.",
1865
+ "Không phải CVA — dùng [(value)].",
1866
+ "tipFormatter nhận number → string hiển thị."
1867
+ ],
1868
+ "binding": "value",
1869
+ "cva": false,
488
1870
  "path": "projects/geo-ui/src/lib/figma/geo-slider/geo-slider.component.ts",
489
1871
  "doc": "ai/components/geo-slider.md",
490
1872
  "inputs": [
491
- "value",
492
- "min",
493
- "max",
494
- "step",
495
- "disabled",
496
- "tipFormatter"
1873
+ {
1874
+ "name": "value",
1875
+ "type": "number",
1876
+ "default": "0",
1877
+ "note": ""
1878
+ },
1879
+ {
1880
+ "name": "min",
1881
+ "type": "number",
1882
+ "default": "0",
1883
+ "note": ""
1884
+ },
1885
+ {
1886
+ "name": "max",
1887
+ "type": "number",
1888
+ "default": "100",
1889
+ "note": ""
1890
+ },
1891
+ {
1892
+ "name": "step",
1893
+ "type": "number",
1894
+ "default": "1",
1895
+ "note": ""
1896
+ },
1897
+ {
1898
+ "name": "disabled",
1899
+ "type": "boolean",
1900
+ "default": "false",
1901
+ "note": ""
1902
+ },
1903
+ {
1904
+ "name": "tipFormatter",
1905
+ "type": "(value: number)",
1906
+ "default": "> string",
1907
+ "note": ""
1908
+ }
497
1909
  ],
498
1910
  "outputs": [
499
- "valueChange"
1911
+ {
1912
+ "name": "valueChange",
1913
+ "type": "EventEmitter<number>",
1914
+ "note": ""
1915
+ }
500
1916
  ],
501
- "example": "<geo-slider [min]=\"0\" [max]=\"100\" [(ngModel)]=\"percent\"></geo-slider>"
1917
+ "examples": [
1918
+ {
1919
+ "title": "Basic",
1920
+ "lang": "html",
1921
+ "code": "<geo-slider [(value)]=\"percent\" [min]=\"0\" [max]=\"100\"></geo-slider>"
1922
+ },
1923
+ {
1924
+ "title": "Full options",
1925
+ "lang": "html",
1926
+ "code": "<geo-slider\n [(value)]=\"zoom\"\n [min]=\"0\"\n [max]=\"100\"\n [step]=\"5\"\n [disabled]=\"false\"\n [tipFormatter]=\"formatZoom\"\n (valueChange)=\"onZoom($event)\"\n></geo-slider>"
1927
+ }
1928
+ ],
1929
+ "example": "<geo-slider [(value)]=\"percent\" [min]=\"0\" [max]=\"100\"></geo-slider>",
1930
+ "related": [
1931
+ "geo-input",
1932
+ "geo-select"
1933
+ ]
502
1934
  },
503
1935
  {
504
1936
  "name": "GeoStatusTagComponent",
@@ -507,15 +1939,54 @@
507
1939
  "summary": "Pill status tag with optional dot (success/error/warning/info/teal/…).",
508
1940
  "whenToUse": "Status in tables, cards, lists.",
509
1941
  "doNotUseWhen": "Clickable filters — use button/tabs.",
1942
+ "howTo": "Tag trạng thái (dot + label). status preset success|primary|error|warning|info|teal|neutral (hoặc legacy completed|cancelled|…); [label] ghi đè text.",
1943
+ "tips": [
1944
+ "Trong bảng dùng cột renderAs: 'status'.",
1945
+ "Không dùng làm filter clickable.",
1946
+ "showDot mặc định true."
1947
+ ],
1948
+ "binding": "none",
1949
+ "cva": false,
510
1950
  "path": "projects/geo-ui/src/lib/figma/geo-status-tag/geo-status-tag.component.ts",
511
1951
  "doc": "ai/components/geo-status-tag.md",
512
1952
  "inputs": [
513
- "status",
514
- "label",
515
- "showDot"
1953
+ {
1954
+ "name": "status",
1955
+ "type": "GeoStatusPreset | string",
1956
+ "default": "'success'",
1957
+ "note": "Status preset or free-form string. Semantic: success | primary | error | warning | info | teal | neutral Legacy: completed | cancelled | in_progress | adjusting"
1958
+ },
1959
+ {
1960
+ "name": "label",
1961
+ "type": "string",
1962
+ "default": "",
1963
+ "note": "Override label text."
1964
+ },
1965
+ {
1966
+ "name": "showDot",
1967
+ "type": "boolean",
1968
+ "default": "true",
1969
+ "note": "Show leading status dot (design default)."
1970
+ }
516
1971
  ],
517
1972
  "outputs": [],
518
- "example": "<geo-status-tag status=\"success\" label=\"Hoạt động\"></geo-status-tag>"
1973
+ "examples": [
1974
+ {
1975
+ "title": "Basic",
1976
+ "lang": "html",
1977
+ "code": "<geo-status-tag status=\"success\" label=\"Đã duyệt\"></geo-status-tag>"
1978
+ },
1979
+ {
1980
+ "title": "Full options",
1981
+ "lang": "html",
1982
+ "code": "<geo-status-tag status=\"warning\" label=\"Chờ xử lý\" [showDot]=\"true\"></geo-status-tag>\n<geo-status-tag status=\"error\" label=\"Từ chối\"></geo-status-tag>\n<geo-status-tag status=\"in_progress\" label=\"Đang xử lý\"></geo-status-tag>"
1983
+ }
1984
+ ],
1985
+ "example": "<geo-status-tag status=\"success\" label=\"Đã duyệt\"></geo-status-tag>",
1986
+ "related": [
1987
+ "geo-table",
1988
+ "geo-typography"
1989
+ ]
519
1990
  },
520
1991
  {
521
1992
  "name": "GeoStepFormLayoutComponent",
@@ -524,36 +1995,170 @@
524
1995
  "summary": "Full-page multi-step form: vertical stepper + content card + footer actions.",
525
1996
  "whenToUse": "Long registration / create wizards as a page.",
526
1997
  "doNotUseWhen": "Dialogs — use geo-form-modal + geo-stepper.",
1998
+ "howTo": "Wizard full-page: stepper dọc + card nội dung + footer Hủy/Trở lại/Lưu/Tiếp theo. [steps] + [activeIndex]; (next)/(previous)/(save)/(cancel).",
1999
+ "tips": [
2000
+ "Dialog multi-step → geo-form-modal + geo-stepper.",
2001
+ "finishLabel hiện khi bước cuối (thay nextLabel).",
2002
+ "hideDefaultFooter nếu tự render footer."
2003
+ ],
2004
+ "binding": "none",
2005
+ "cva": false,
527
2006
  "path": "projects/geo-ui/src/lib/figma/geo-step-form-layout/geo-step-form-layout.component.ts",
528
2007
  "doc": "ai/components/geo-step-form-layout.md",
529
2008
  "inputs": [
530
- "steps",
531
- "activeIndex",
532
- "title",
533
- "clickableSteps",
534
- "showScrollButtons",
535
- "showCancel",
536
- "showPrevious",
537
- "showSave",
538
- "showNext",
539
- "cancelLabel",
540
- "previousLabel",
541
- "saveLabel",
542
- "nextLabel",
543
- "finishLabel",
544
- "saveLoading",
545
- "nextLoading",
546
- "hideDefaultFooter"
2009
+ {
2010
+ "name": "steps",
2011
+ "type": "GeoStepperStep[]",
2012
+ "default": "[]",
2013
+ "note": ""
2014
+ },
2015
+ {
2016
+ "name": "activeIndex",
2017
+ "type": "number",
2018
+ "default": "0",
2019
+ "note": ""
2020
+ },
2021
+ {
2022
+ "name": "title",
2023
+ "type": "string",
2024
+ "default": "''",
2025
+ "note": ""
2026
+ },
2027
+ {
2028
+ "name": "clickableSteps",
2029
+ "type": "boolean",
2030
+ "default": "true",
2031
+ "note": ""
2032
+ },
2033
+ {
2034
+ "name": "showScrollButtons",
2035
+ "type": "boolean",
2036
+ "default": "true",
2037
+ "note": "Hiện nút ↑/↓ góc phải card (chuyển bước). Mặc định `true`."
2038
+ },
2039
+ {
2040
+ "name": "showCancel",
2041
+ "type": "boolean",
2042
+ "default": "true",
2043
+ "note": ""
2044
+ },
2045
+ {
2046
+ "name": "showPrevious",
2047
+ "type": "boolean",
2048
+ "default": "true",
2049
+ "note": ""
2050
+ },
2051
+ {
2052
+ "name": "showSave",
2053
+ "type": "boolean",
2054
+ "default": "true",
2055
+ "note": ""
2056
+ },
2057
+ {
2058
+ "name": "showNext",
2059
+ "type": "boolean",
2060
+ "default": "true",
2061
+ "note": ""
2062
+ },
2063
+ {
2064
+ "name": "cancelLabel",
2065
+ "type": "string",
2066
+ "default": "'Hủy'",
2067
+ "note": ""
2068
+ },
2069
+ {
2070
+ "name": "previousLabel",
2071
+ "type": "string",
2072
+ "default": "'Trở lại'",
2073
+ "note": ""
2074
+ },
2075
+ {
2076
+ "name": "saveLabel",
2077
+ "type": "string",
2078
+ "default": "'Lưu'",
2079
+ "note": ""
2080
+ },
2081
+ {
2082
+ "name": "nextLabel",
2083
+ "type": "string",
2084
+ "default": "'Tiếp theo'",
2085
+ "note": ""
2086
+ },
2087
+ {
2088
+ "name": "finishLabel",
2089
+ "type": "string",
2090
+ "default": "'Hoàn thành'",
2091
+ "note": ""
2092
+ },
2093
+ {
2094
+ "name": "saveLoading",
2095
+ "type": "boolean",
2096
+ "default": "false",
2097
+ "note": ""
2098
+ },
2099
+ {
2100
+ "name": "nextLoading",
2101
+ "type": "boolean",
2102
+ "default": "false",
2103
+ "note": ""
2104
+ },
2105
+ {
2106
+ "name": "hideDefaultFooter",
2107
+ "type": "boolean",
2108
+ "default": "false",
2109
+ "note": "Ẩn footer actions (dùng slot `[footer]` hoặc không cần nút)."
2110
+ }
547
2111
  ],
548
2112
  "outputs": [
549
- "activeIndexChange",
550
- "stepSelect",
551
- "cancel",
552
- "previous",
553
- "save",
554
- "next"
555
- ],
556
- "example": "<geo-step-form-layout\n title=\"Thêm mới\"\n [steps]=\"steps\"\n [activeIndex]=\"i\"\n (next)=\"goNext()\"\n (previous)=\"goPrev()\"\n (cancel)=\"cancel()\"\n>\n <!-- step content -->\n</geo-step-form-layout>"
2113
+ {
2114
+ "name": "activeIndexChange",
2115
+ "type": "EventEmitter<number>",
2116
+ "note": ""
2117
+ },
2118
+ {
2119
+ "name": "stepSelect",
2120
+ "type": "EventEmitter<{ index: number; step: GeoStepperStep }>",
2121
+ "note": ""
2122
+ },
2123
+ {
2124
+ "name": "cancel",
2125
+ "type": "EventEmitter<void>",
2126
+ "note": ""
2127
+ },
2128
+ {
2129
+ "name": "previous",
2130
+ "type": "EventEmitter<void>",
2131
+ "note": ""
2132
+ },
2133
+ {
2134
+ "name": "save",
2135
+ "type": "EventEmitter<void>",
2136
+ "note": ""
2137
+ },
2138
+ {
2139
+ "name": "next",
2140
+ "type": "EventEmitter<void>",
2141
+ "note": ""
2142
+ }
2143
+ ],
2144
+ "examples": [
2145
+ {
2146
+ "title": "Basic",
2147
+ "lang": "html",
2148
+ "code": "<geo-step-form-layout\n title=\"Thêm mới\"\n [steps]=\"steps\"\n [activeIndex]=\"i\"\n (next)=\"goNext()\"\n (previous)=\"goPrev()\"\n (cancel)=\"cancel()\"\n>\n <!-- nội dung bước -->\n</geo-step-form-layout>"
2149
+ },
2150
+ {
2151
+ "title": "Full options",
2152
+ "lang": "html",
2153
+ "code": "<geo-step-form-layout\n title=\"Đăng ký khai thác\"\n [steps]=\"[\n { key: 'info', label: '1. Thông tin' },\n { key: 'files', label: '2. Tài liệu' },\n { key: 'confirm', label: '3. Xác nhận' }\n ]\"\n [activeIndex]=\"activeIndex\"\n [clickableSteps]=\"true\"\n [showScrollButtons]=\"true\"\n [showCancel]=\"true\"\n [showPrevious]=\"true\"\n [showSave]=\"true\"\n [showNext]=\"true\"\n cancelLabel=\"Hủy\"\n previousLabel=\"Trở lại\"\n saveLabel=\"Lưu\"\n nextLabel=\"Tiếp theo\"\n finishLabel=\"Hoàn thành\"\n [saveLoading]=\"saving\"\n [nextLoading]=\"validating\"\n (activeIndexChange)=\"activeIndex = $event\"\n (stepSelect)=\"onStep($event)\"\n (cancel)=\"onCancel()\"\n (previous)=\"goPrev()\"\n (save)=\"onSave()\"\n (next)=\"goNext()\"\n>\n @if (activeIndex === 0) {\n <geo-input label=\"Tên hồ sơ\" [block]=\"true\" [(ngModel)]=\"name\"></geo-input>\n }\n</geo-step-form-layout>"
2154
+ }
2155
+ ],
2156
+ "example": "<geo-step-form-layout\n title=\"Thêm mới\"\n [steps]=\"steps\"\n [activeIndex]=\"i\"\n (next)=\"goNext()\"\n (previous)=\"goPrev()\"\n (cancel)=\"cancel()\"\n>\n <!-- nội dung bước -->\n</geo-step-form-layout>",
2157
+ "related": [
2158
+ "geo-stepper",
2159
+ "geo-form-modal",
2160
+ "geo-button"
2161
+ ]
557
2162
  },
558
2163
  {
559
2164
  "name": "GeoStepperComponent",
@@ -562,19 +2167,72 @@
562
2167
  "summary": "Steps indicator (horizontal default). Active=primary, completed=success+check.",
563
2168
  "whenToUse": "Multi-step flows; place inside geo-form-modal body or pages.",
564
2169
  "doNotUseWhen": "Tabs for peer sections — use geo-tabs.",
2170
+ "howTo": "Chỉ báo bước. [steps] {key,label}[]; [activeIndex]; orientation horizontal|vertical. Active=primary, completed=success+check.",
2171
+ "tips": [
2172
+ "Trong dialog: đặt trong geo-form-modal body.",
2173
+ "Wizard trang: dùng geo-step-form-layout.",
2174
+ "clickable=false khi bắt buộc đi tuần tự."
2175
+ ],
2176
+ "binding": "none",
2177
+ "cva": false,
565
2178
  "path": "projects/geo-ui/src/lib/figma/geo-stepper/geo-stepper.component.ts",
566
2179
  "doc": "ai/components/geo-stepper.md",
567
2180
  "inputs": [
568
- "steps",
569
- "activeIndex",
570
- "orientation",
571
- "clickable"
2181
+ {
2182
+ "name": "steps",
2183
+ "type": "GeoStepperStep[]",
2184
+ "default": "[]",
2185
+ "note": ""
2186
+ },
2187
+ {
2188
+ "name": "activeIndex",
2189
+ "type": "number",
2190
+ "default": "0",
2191
+ "note": ""
2192
+ },
2193
+ {
2194
+ "name": "orientation",
2195
+ "type": "'vertical' | 'horizontal'",
2196
+ "default": "'horizontal'",
2197
+ "note": "Mặc định horizontal theo design form modal."
2198
+ },
2199
+ {
2200
+ "name": "clickable",
2201
+ "type": "boolean",
2202
+ "default": "true",
2203
+ "note": "Cho phép click để đổi bước."
2204
+ }
572
2205
  ],
573
2206
  "outputs": [
574
- "activeIndexChange",
575
- "stepSelect"
2207
+ {
2208
+ "name": "activeIndexChange",
2209
+ "type": "EventEmitter<number>",
2210
+ "note": ""
2211
+ },
2212
+ {
2213
+ "name": "stepSelect",
2214
+ "type": "EventEmitter<{ index: number; step: GeoStepperStep }>",
2215
+ "note": ""
2216
+ }
2217
+ ],
2218
+ "examples": [
2219
+ {
2220
+ "title": "Basic",
2221
+ "lang": "html",
2222
+ "code": "<geo-stepper\n [steps]=\"[\n { key: 'a', label: '1. Thông tin' },\n { key: 'b', label: '2. Xác nhận' }\n ]\"\n [activeIndex]=\"0\"\n orientation=\"horizontal\"\n [clickable]=\"false\"\n></geo-stepper>"
2223
+ },
2224
+ {
2225
+ "title": "Full options",
2226
+ "lang": "html",
2227
+ "code": "<geo-stepper\n [steps]=\"steps\"\n [activeIndex]=\"activeIndex\"\n orientation=\"vertical\"\n [clickable]=\"true\"\n (activeIndexChange)=\"activeIndex = $event\"\n (stepSelect)=\"onStepSelect($event)\"\n></geo-stepper>"
2228
+ }
576
2229
  ],
577
- "example": "<geo-stepper\n [steps]=\"[\n { key: 'a', label: '1. Thông tin' },\n { key: 'b', label: '2. Xác nhận' }\n ]\"\n [activeIndex]=\"0\"\n orientation=\"horizontal\"\n [clickable]=\"false\"\n></geo-stepper>"
2230
+ "example": "<geo-stepper\n [steps]=\"[\n { key: 'a', label: '1. Thông tin' },\n { key: 'b', label: '2. Xác nhận' }\n ]\"\n [activeIndex]=\"0\"\n orientation=\"horizontal\"\n [clickable]=\"false\"\n></geo-stepper>",
2231
+ "related": [
2232
+ "geo-form-modal",
2233
+ "geo-step-form-layout",
2234
+ "geo-tabs"
2235
+ ]
578
2236
  },
579
2237
  {
580
2238
  "name": "GeoTableComponent",
@@ -583,55 +2241,283 @@
583
2241
  "summary": "Data table with pagination, selection, row actions, mobile card mode.",
584
2242
  "whenToUse": "List/index screens with structured rows.",
585
2243
  "doNotUseWhen": "Simple key-value detail — use typography/grid.",
2244
+ "howTo": "Bảng dữ liệu: [columns] GeoTableColumn[] + [data]. Hỗ trợ renderAs index|status|actions, phân trang, selection, mobile card.",
2245
+ "tips": [
2246
+ "Khai báo columns TS với renderAs cho STT / trạng thái / thao tác.",
2247
+ "Ghép geo-table-toolbar + geo-table-filter phía trên.",
2248
+ "frontPagination=false khi phân trang server (set total)."
2249
+ ],
2250
+ "binding": "none",
2251
+ "cva": false,
586
2252
  "path": "projects/geo-ui/src/lib/figma/geo-table/geo-table.component.ts",
587
2253
  "doc": "ai/components/geo-table.md",
588
2254
  "inputs": [
589
- "columns",
590
- "data",
591
- "loading",
592
- "bordered",
593
- "outerBordered",
594
- "size",
595
- "striped",
596
- "clickable",
597
- "tableLayout",
598
- "showPagination",
599
- "frontPagination",
600
- "pageIndex",
601
- "pageSize",
602
- "total",
603
- "pageSizeOptions",
604
- "showSizeChanger",
605
- "showQuickJumper",
606
- "hideOnSinglePage",
607
- "simplePagination",
608
- "paginationPosition",
609
- "scroll",
610
- "scrollY",
611
- "autoScroll",
612
- "tableTitle",
613
- "footer",
614
- "emptyText",
615
- "showTotalRange",
616
- "selectable",
617
- "rowKey",
618
- "selectedKeys",
619
- "mobileTitleKey",
620
- "mobileCardView"
2255
+ {
2256
+ "name": "columns",
2257
+ "type": "GeoTableColumn[]",
2258
+ "default": "[]",
2259
+ "note": ""
2260
+ },
2261
+ {
2262
+ "name": "data",
2263
+ "type": "Record<string, unknown>[]",
2264
+ "default": "[]",
2265
+ "note": ""
2266
+ },
2267
+ {
2268
+ "name": "loading",
2269
+ "type": "boolean",
2270
+ "default": "false",
2271
+ "note": ""
2272
+ },
2273
+ {
2274
+ "name": "bordered",
2275
+ "type": "boolean",
2276
+ "default": "true",
2277
+ "note": ""
2278
+ },
2279
+ {
2280
+ "name": "outerBordered",
2281
+ "type": "boolean",
2282
+ "default": "false",
2283
+ "note": ""
2284
+ },
2285
+ {
2286
+ "name": "size",
2287
+ "type": "NzTableSize",
2288
+ "default": "'middle'",
2289
+ "note": ""
2290
+ },
2291
+ {
2292
+ "name": "striped",
2293
+ "type": "boolean",
2294
+ "default": "true",
2295
+ "note": ""
2296
+ },
2297
+ {
2298
+ "name": "clickable",
2299
+ "type": "boolean",
2300
+ "default": "true",
2301
+ "note": ""
2302
+ },
2303
+ {
2304
+ "name": "tableLayout",
2305
+ "type": "'auto' | 'fixed'",
2306
+ "default": "'auto'",
2307
+ "note": ""
2308
+ },
2309
+ {
2310
+ "name": "showPagination",
2311
+ "type": "boolean",
2312
+ "default": "true",
2313
+ "note": ""
2314
+ },
2315
+ {
2316
+ "name": "frontPagination",
2317
+ "type": "boolean",
2318
+ "default": "true",
2319
+ "note": ""
2320
+ },
2321
+ {
2322
+ "name": "pageIndex",
2323
+ "type": "number",
2324
+ "default": "1",
2325
+ "note": ""
2326
+ },
2327
+ {
2328
+ "name": "pageSize",
2329
+ "type": "number",
2330
+ "default": "10",
2331
+ "note": ""
2332
+ },
2333
+ {
2334
+ "name": "total",
2335
+ "type": "number",
2336
+ "default": "",
2337
+ "note": ""
2338
+ },
2339
+ {
2340
+ "name": "pageSizeOptions",
2341
+ "type": "number[]",
2342
+ "default": "DEFAULT_UI_TABLE_PAGE_SIZE_OPTIONS",
2343
+ "note": ""
2344
+ },
2345
+ {
2346
+ "name": "showSizeChanger",
2347
+ "type": "boolean",
2348
+ "default": "false",
2349
+ "note": ""
2350
+ },
2351
+ {
2352
+ "name": "showQuickJumper",
2353
+ "type": "boolean",
2354
+ "default": "false",
2355
+ "note": ""
2356
+ },
2357
+ {
2358
+ "name": "hideOnSinglePage",
2359
+ "type": "boolean",
2360
+ "default": "false",
2361
+ "note": ""
2362
+ },
2363
+ {
2364
+ "name": "simplePagination",
2365
+ "type": "boolean",
2366
+ "default": "false",
2367
+ "note": ""
2368
+ },
2369
+ {
2370
+ "name": "paginationPosition",
2371
+ "type": "'top' | 'bottom' | 'both'",
2372
+ "default": "'bottom'",
2373
+ "note": ""
2374
+ },
2375
+ {
2376
+ "name": "scroll",
2377
+ "type": "{ x?: string; y?: string }",
2378
+ "default": "{}",
2379
+ "note": ""
2380
+ },
2381
+ {
2382
+ "name": "scrollY",
2383
+ "type": "string",
2384
+ "default": "",
2385
+ "note": ""
2386
+ },
2387
+ {
2388
+ "name": "autoScroll",
2389
+ "type": "boolean",
2390
+ "default": "false",
2391
+ "note": "Tự bật scroll ngang theo tổng width cột. Khi có cột `fixed`, scroll.x luôn được tính (bắt buộc với nz-table fixed)."
2392
+ },
2393
+ {
2394
+ "name": "tableTitle",
2395
+ "type": "string",
2396
+ "default": "",
2397
+ "note": ""
2398
+ },
2399
+ {
2400
+ "name": "footer",
2401
+ "type": "string",
2402
+ "default": "",
2403
+ "note": ""
2404
+ },
2405
+ {
2406
+ "name": "emptyText",
2407
+ "type": "string",
2408
+ "default": "'Không có dữ liệu'",
2409
+ "note": ""
2410
+ },
2411
+ {
2412
+ "name": "showTotalRange",
2413
+ "type": "boolean",
2414
+ "default": "false",
2415
+ "note": ""
2416
+ },
2417
+ {
2418
+ "name": "selectable",
2419
+ "type": "boolean",
2420
+ "default": "false",
2421
+ "note": ""
2422
+ },
2423
+ {
2424
+ "name": "rowKey",
2425
+ "type": "string",
2426
+ "default": "'id'",
2427
+ "note": ""
2428
+ },
2429
+ {
2430
+ "name": "selectedKeys",
2431
+ "type": "string[]",
2432
+ "default": "[]",
2433
+ "note": ""
2434
+ },
2435
+ {
2436
+ "name": "mobileTitleKey",
2437
+ "type": "string",
2438
+ "default": "",
2439
+ "note": "Mobile card: key cột làm tiêu đề. Ưu tiên hơn `column.mobileTitle`; nếu không set thì lấy cột dữ liệu đầu tiên."
2440
+ },
2441
+ {
2442
+ "name": "mobileCardView",
2443
+ "type": "boolean",
2444
+ "default": "true",
2445
+ "note": "Bật/tắt chuyển card trên mobile (mặc định bật)."
2446
+ }
621
2447
  ],
622
2448
  "outputs": [
623
- "rowClick",
624
- "rowEdit",
625
- "rowMore",
626
- "rowAction",
627
- "pageIndexChange",
628
- "pageSizeChange",
629
- "queryParams",
630
- "currentPageDataChange",
631
- "selectedKeysChange",
632
- "selectionChange"
633
- ],
634
- "example": "<geo-table\n [columns]=\"columns\"\n [data]=\"rows\"\n [total]=\"total\"\n [pageIndex]=\"page\"\n [pageSize]=\"10\"\n (pageIndexChange)=\"onPage($event)\"\n></geo-table>"
2449
+ {
2450
+ "name": "rowClick",
2451
+ "type": "EventEmitter<Record<string, unknown>>",
2452
+ "note": ""
2453
+ },
2454
+ {
2455
+ "name": "rowEdit",
2456
+ "type": "EventEmitter<Record<string, unknown>>",
2457
+ "note": ""
2458
+ },
2459
+ {
2460
+ "name": "rowMore",
2461
+ "type": "EventEmitter<Record<string, unknown>>",
2462
+ "note": ""
2463
+ },
2464
+ {
2465
+ "name": "rowAction",
2466
+ "type": "EventEmitter<{ row: Record<string, unknown>; action: string }>",
2467
+ "note": ""
2468
+ },
2469
+ {
2470
+ "name": "pageIndexChange",
2471
+ "type": "EventEmitter<number>",
2472
+ "note": ""
2473
+ },
2474
+ {
2475
+ "name": "pageSizeChange",
2476
+ "type": "EventEmitter<number>",
2477
+ "note": ""
2478
+ },
2479
+ {
2480
+ "name": "queryParams",
2481
+ "type": "EventEmitter<NzTableQueryParams>",
2482
+ "note": ""
2483
+ },
2484
+ {
2485
+ "name": "currentPageDataChange",
2486
+ "type": "EventEmitter<readonly Record<string, unknown>[]>",
2487
+ "note": ""
2488
+ },
2489
+ {
2490
+ "name": "selectedKeysChange",
2491
+ "type": "EventEmitter<string[]>",
2492
+ "note": ""
2493
+ },
2494
+ {
2495
+ "name": "selectionChange",
2496
+ "type": "EventEmitter<Record<string, unknown>[]>",
2497
+ "note": ""
2498
+ }
2499
+ ],
2500
+ "examples": [
2501
+ {
2502
+ "title": "Columns TS",
2503
+ "lang": "ts",
2504
+ "code": "import { GeoTableColumn } from '@geoit/ui';\n\ncolumns: GeoTableColumn[] = [\n { key: 'index', title: 'STT', width: '72px', align: 'center', renderAs: 'index' },\n { key: 'code', title: 'Mã', width: '120px', dataType: 'text' },\n { key: 'name', title: 'Tên hồ sơ', dataType: 'text' },\n { key: 'status', title: 'Trạng thái', width: '150px', renderAs: 'status', statusLabelKey: 'statusLabel' },\n {\n key: 'actions',\n title: 'Thao tác',\n width: '110px',\n align: 'center',\n renderAs: 'actions',\n actions: [\n { key: 'edit', icon: 'edit', title: 'Chỉnh sửa' },\n { key: 'delete', icon: 'delete', title: 'Xóa', danger: true },\n ],\n },\n];\n\nrows = [\n { id: '1', code: 'HS-001', name: 'Hồ sơ A', status: 'success', statusLabel: 'Đã duyệt' },\n { id: '2', code: 'HS-002', name: 'Hồ sơ B', status: 'warning', statusLabel: 'Chờ xử lý' },\n];"
2505
+ },
2506
+ {
2507
+ "title": "Template",
2508
+ "lang": "html",
2509
+ "code": "<geo-table\n [columns]=\"columns\"\n [data]=\"rows\"\n [loading]=\"loading\"\n [bordered]=\"true\"\n [striped]=\"true\"\n [showPagination]=\"true\"\n [frontPagination]=\"false\"\n [pageIndex]=\"pageIndex\"\n [pageSize]=\"pageSize\"\n [total]=\"total\"\n [showSizeChanger]=\"true\"\n [selectable]=\"true\"\n rowKey=\"id\"\n [(selectedKeys)]=\"selectedKeys\"\n emptyText=\"Không có dữ liệu\"\n (pageIndexChange)=\"pageIndex = $event; load()\"\n (pageSizeChange)=\"pageSize = $event; load()\"\n (rowAction)=\"onRowAction($event)\"\n (rowClick)=\"onRowClick($event)\"\n></geo-table>"
2510
+ }
2511
+ ],
2512
+ "example": "import { GeoTableColumn } from '@geoit/ui';\n\ncolumns: GeoTableColumn[] = [\n { key: 'index', title: 'STT', width: '72px', align: 'center', renderAs: 'index' },\n { key: 'code', title: 'Mã', width: '120px', dataType: 'text' },\n { key: 'name', title: 'Tên hồ sơ', dataType: 'text' },\n { key: 'status', title: 'Trạng thái', width: '150px', renderAs: 'status', statusLabelKey: 'statusLabel' },\n {\n key: 'actions',\n title: 'Thao tác',\n width: '110px',\n align: 'center',\n renderAs: 'actions',\n actions: [\n { key: 'edit', icon: 'edit', title: 'Chỉnh sửa' },\n { key: 'delete', icon: 'delete', title: 'Xóa', danger: true },\n ],\n },\n];\n\nrows = [\n { id: '1', code: 'HS-001', name: 'Hồ sơ A', status: 'success', statusLabel: 'Đã duyệt' },\n { id: '2', code: 'HS-002', name: 'Hồ sơ B', status: 'warning', statusLabel: 'Chờ xử lý' },\n];",
2513
+ "related": [
2514
+ "geo-table-toolbar",
2515
+ "geo-table-filter",
2516
+ "geo-table-pagination",
2517
+ "geo-row-actions",
2518
+ "geo-status-tag",
2519
+ "geo-column-picker"
2520
+ ]
635
2521
  },
636
2522
  {
637
2523
  "name": "GeoTableFilterComponent",
@@ -640,29 +2526,133 @@
640
2526
  "summary": "Table filter bar with search + advanced filters.",
641
2527
  "whenToUse": "Above geo-table for query/filter UX.",
642
2528
  "doNotUseWhen": "Standalone forms unrelated to tables.",
2529
+ "howTo": "Thanh lọc bảng: ô tìm + bộ lọc nâng cao (ng-content). (search)/(reset); advancedExpanded + advancedFilterCount.",
2530
+ "tips": [
2531
+ "Đặt field lọc nâng cao trong ng-content khi expanded.",
2532
+ "responsiveColumns/collapse cho mobile.",
2533
+ "searchLoading khi đang gọi API."
2534
+ ],
2535
+ "binding": "none",
2536
+ "cva": false,
643
2537
  "path": "projects/geo-ui/src/lib/figma/geo-table/geo-table-filter/geo-table-filter.component.ts",
644
2538
  "doc": "ai/components/geo-table-filter.md",
645
2539
  "inputs": [
646
- "columns",
647
- "responsiveColumns",
648
- "responsiveCollapse",
649
- "mobileBreakpoint",
650
- "gap",
651
- "searchLabel",
652
- "searchLoading",
653
- "showAdvancedToggle",
654
- "showReset",
655
- "advancedExpanded",
656
- "advancedFilterCount",
657
- "filterToggleTitle",
658
- "resetTitle"
2540
+ {
2541
+ "name": "columns",
2542
+ "type": "number",
2543
+ "default": "4",
2544
+ "note": "Số cột field mỗi hàng (mặc định 4 → tổng 5 cột gồm actions). Ví dụ: 4 field + columns=2 → 2 hàng × 2 cột field (+ 1 cột actions)."
2545
+ },
2546
+ {
2547
+ "name": "responsiveColumns",
2548
+ "type": "boolean",
2549
+ "default": "true",
2550
+ "note": "Khi `true` (mặc định): số cột thực tế ≤ `columns` theo breakpoint. Khi `false`: luôn dùng đúng `columns`."
2551
+ },
2552
+ {
2553
+ "name": "responsiveCollapse",
2554
+ "type": "boolean",
2555
+ "default": "true",
2556
+ "note": "Đưa actions lên hàng đầu khi màn hẹp (stacked)."
2557
+ },
2558
+ {
2559
+ "name": "mobileBreakpoint",
2560
+ "type": "number",
2561
+ "default": "1024",
2562
+ "note": "Breakpoint chuyển layout stacked (px)."
2563
+ },
2564
+ {
2565
+ "name": "gap",
2566
+ "type": "number",
2567
+ "default": "15",
2568
+ "note": "Khoảng cách grid (px)."
2569
+ },
2570
+ {
2571
+ "name": "searchLabel",
2572
+ "type": "string",
2573
+ "default": "'Tìm kiếm'",
2574
+ "note": ""
2575
+ },
2576
+ {
2577
+ "name": "searchLoading",
2578
+ "type": "boolean",
2579
+ "default": "false",
2580
+ "note": ""
2581
+ },
2582
+ {
2583
+ "name": "showAdvancedToggle",
2584
+ "type": "boolean",
2585
+ "default": "true",
2586
+ "note": "Hiện nút mở/đóng slot `[advanced]`."
2587
+ },
2588
+ {
2589
+ "name": "showReset",
2590
+ "type": "boolean",
2591
+ "default": "true",
2592
+ "note": ""
2593
+ },
2594
+ {
2595
+ "name": "advancedExpanded",
2596
+ "type": "boolean",
2597
+ "default": "false",
2598
+ "note": "Mở panel field nâng cao (`[advanced]`)."
2599
+ },
2600
+ {
2601
+ "name": "advancedFilterCount",
2602
+ "type": "number",
2603
+ "default": "0",
2604
+ "note": "Số field advanced đang có giá trị — hiện badge trên nút expand. Parent tự tính (vd. `countFilledFilters(query, ADVANCED_KEYS)`)."
2605
+ },
2606
+ {
2607
+ "name": "filterToggleTitle",
2608
+ "type": "string",
2609
+ "default": "'Bộ lọc nâng cao'",
2610
+ "note": ""
2611
+ },
2612
+ {
2613
+ "name": "resetTitle",
2614
+ "type": "string",
2615
+ "default": "'Xóa bộ lọc'",
2616
+ "note": ""
2617
+ }
659
2618
  ],
660
2619
  "outputs": [
661
- "search",
662
- "reset",
663
- "advancedExpandedChange"
2620
+ {
2621
+ "name": "search",
2622
+ "type": "EventEmitter<void>",
2623
+ "note": ""
2624
+ },
2625
+ {
2626
+ "name": "reset",
2627
+ "type": "EventEmitter<void>",
2628
+ "note": ""
2629
+ },
2630
+ {
2631
+ "name": "advancedExpandedChange",
2632
+ "type": "EventEmitter<boolean>",
2633
+ "note": ""
2634
+ }
664
2635
  ],
665
- "example": "<geo-table-filter (search)=\"onSearch()\" (reset)=\"onReset()\"></geo-table-filter>"
2636
+ "examples": [
2637
+ {
2638
+ "title": "Basic",
2639
+ "lang": "html",
2640
+ "code": "<geo-table-filter (search)=\"onSearch()\" (reset)=\"onReset()\"></geo-table-filter>"
2641
+ },
2642
+ {
2643
+ "title": "Full options",
2644
+ "lang": "html",
2645
+ "code": "<geo-table-filter\n [columns]=\"4\"\n [responsiveColumns]=\"true\"\n [responsiveCollapse]=\"true\"\n [mobileBreakpoint]=\"1024\"\n [gap]=\"15\"\n searchLabel=\"Tìm kiếm\"\n [searchLoading]=\"searching\"\n [showAdvancedToggle]=\"true\"\n [showReset]=\"true\"\n [(advancedExpanded)]=\"advancedOpen\"\n [advancedFilterCount]=\"activeFilterCount\"\n filterToggleTitle=\"Bộ lọc nâng cao\"\n resetTitle=\"Xóa bộ lọc\"\n (search)=\"onSearch()\"\n (reset)=\"onReset()\"\n>\n <geo-select label=\"Trạng thái\" [options]=\"statusOptions\" [(ngModel)]=\"status\" [block]=\"true\"></geo-select>\n <geo-date-range-picker label=\"Thời gian\" [(ngModel)]=\"range\" [block]=\"true\"></geo-date-range-picker>\n</geo-table-filter>"
2646
+ }
2647
+ ],
2648
+ "example": "<geo-table-filter (search)=\"onSearch()\" (reset)=\"onReset()\"></geo-table-filter>",
2649
+ "related": [
2650
+ "geo-table",
2651
+ "geo-table-toolbar",
2652
+ "geo-input",
2653
+ "geo-select",
2654
+ "geo-date-range-picker"
2655
+ ]
666
2656
  },
667
2657
  {
668
2658
  "name": "GeoTablePaginationComponent",
@@ -671,23 +2661,95 @@
671
2661
  "summary": "Table-specific pagination footer (range label + size).",
672
2662
  "whenToUse": "Custom table footers; geo-table has built-in paging too.",
673
2663
  "doNotUseWhen": "Non-table paging — use geo-pagination.",
2664
+ "howTo": "Footer phân trang riêng cho bảng (nhãn khoảng + đổi page size). [pageIndex]/[pageSize]/[total]; showTotalRange mặc định true.",
2665
+ "tips": [
2666
+ "geo-table đã có pagination tích hợp — dùng component này khi custom layout.",
2667
+ "sizeLabel mặc định 'Hiển thị'.",
2668
+ "pageSizeOptions mặc định [10,20,50,100]."
2669
+ ],
2670
+ "binding": "none",
2671
+ "cva": false,
674
2672
  "path": "projects/geo-ui/src/lib/figma/geo-table/geo-table-pagination/geo-table-pagination.component.ts",
675
2673
  "doc": "ai/components/geo-table-pagination.md",
676
2674
  "inputs": [
677
- "pageIndex",
678
- "pageSize",
679
- "total",
680
- "pageSizeOptions",
681
- "showSizeChanger",
682
- "hideOnSinglePage",
683
- "showTotalRange",
684
- "sizeLabel"
2675
+ {
2676
+ "name": "pageIndex",
2677
+ "type": "number",
2678
+ "default": "1",
2679
+ "note": ""
2680
+ },
2681
+ {
2682
+ "name": "pageSize",
2683
+ "type": "number",
2684
+ "default": "10",
2685
+ "note": ""
2686
+ },
2687
+ {
2688
+ "name": "total",
2689
+ "type": "number",
2690
+ "default": "0",
2691
+ "note": ""
2692
+ },
2693
+ {
2694
+ "name": "pageSizeOptions",
2695
+ "type": "number[]",
2696
+ "default": "DEFAULT_UI_TABLE_PAGE_SIZE_OPTIONS",
2697
+ "note": ""
2698
+ },
2699
+ {
2700
+ "name": "showSizeChanger",
2701
+ "type": "boolean",
2702
+ "default": "true",
2703
+ "note": ""
2704
+ },
2705
+ {
2706
+ "name": "hideOnSinglePage",
2707
+ "type": "boolean",
2708
+ "default": "false",
2709
+ "note": ""
2710
+ },
2711
+ {
2712
+ "name": "showTotalRange",
2713
+ "type": "boolean",
2714
+ "default": "true",
2715
+ "note": ""
2716
+ },
2717
+ {
2718
+ "name": "sizeLabel",
2719
+ "type": "string",
2720
+ "default": "'Hiển thị'",
2721
+ "note": ""
2722
+ }
685
2723
  ],
686
2724
  "outputs": [
687
- "pageIndexChange",
688
- "pageSizeChange"
2725
+ {
2726
+ "name": "pageIndexChange",
2727
+ "type": "EventEmitter<number>",
2728
+ "note": ""
2729
+ },
2730
+ {
2731
+ "name": "pageSizeChange",
2732
+ "type": "EventEmitter<number>",
2733
+ "note": ""
2734
+ }
2735
+ ],
2736
+ "examples": [
2737
+ {
2738
+ "title": "Basic",
2739
+ "lang": "html",
2740
+ "code": "<geo-table-pagination [pageIndex]=\"1\" [pageSize]=\"10\" [total]=\"200\"></geo-table-pagination>"
2741
+ },
2742
+ {
2743
+ "title": "Full options",
2744
+ "lang": "html",
2745
+ "code": "<geo-table-pagination\n [pageIndex]=\"pageIndex\"\n [pageSize]=\"pageSize\"\n [total]=\"total\"\n [pageSizeOptions]=\"[10, 20, 50, 100]\"\n [showSizeChanger]=\"true\"\n [hideOnSinglePage]=\"false\"\n [showTotalRange]=\"true\"\n sizeLabel=\"Hiển thị\"\n (pageIndexChange)=\"pageIndex = $event\"\n (pageSizeChange)=\"pageSize = $event\"\n></geo-table-pagination>"
2746
+ }
689
2747
  ],
690
- "example": "<geo-table-pagination [pageIndex]=\"1\" [pageSize]=\"10\" [total]=\"200\"></geo-table-pagination>"
2748
+ "example": "<geo-table-pagination [pageIndex]=\"1\" [pageSize]=\"10\" [total]=\"200\"></geo-table-pagination>",
2749
+ "related": [
2750
+ "geo-table",
2751
+ "geo-pagination"
2752
+ ]
691
2753
  },
692
2754
  {
693
2755
  "name": "GeoTableToolbarComponent",
@@ -696,28 +2758,123 @@
696
2758
  "summary": "Table toolbar: result count, delete/export/add, column toggle.",
697
2759
  "whenToUse": "Above geo-table for bulk actions.",
698
2760
  "doNotUseWhen": "Page header actions unrelated to table.",
2761
+ "howTo": "Toolbar danh sách: số kết quả, xóa/xuất/thêm, ẩn hiện cột. (addClick)/(deleteClick)/(exportClick)/(columnApply).",
2762
+ "tips": [
2763
+ "selectedCount > 0 để bật xóa hàng loạt.",
2764
+ "columnItems + columnStorageKey cho column picker tích hợp.",
2765
+ "Đặt ngay trên geo-table."
2766
+ ],
2767
+ "binding": "none",
2768
+ "cva": false,
699
2769
  "path": "projects/geo-ui/src/lib/figma/geo-table/geo-table-toolbar/geo-table-toolbar.component.ts",
700
2770
  "doc": "ai/components/geo-table-toolbar.md",
701
2771
  "inputs": [
702
- "resultLabel",
703
- "resultCount",
704
- "selectedCount",
705
- "showDelete",
706
- "showExport",
707
- "showColumnToggle",
708
- "showAdd",
709
- "addLabel",
710
- "columnItems",
711
- "columnStorageKey"
2772
+ {
2773
+ "name": "resultLabel",
2774
+ "type": "string",
2775
+ "default": "''",
2776
+ "note": ""
2777
+ },
2778
+ {
2779
+ "name": "resultCount",
2780
+ "type": "number | null",
2781
+ "default": "null",
2782
+ "note": ""
2783
+ },
2784
+ {
2785
+ "name": "selectedCount",
2786
+ "type": "number",
2787
+ "default": "0",
2788
+ "note": ""
2789
+ },
2790
+ {
2791
+ "name": "showDelete",
2792
+ "type": "boolean",
2793
+ "default": "true",
2794
+ "note": ""
2795
+ },
2796
+ {
2797
+ "name": "showExport",
2798
+ "type": "boolean",
2799
+ "default": "true",
2800
+ "note": ""
2801
+ },
2802
+ {
2803
+ "name": "showColumnToggle",
2804
+ "type": "boolean",
2805
+ "default": "true",
2806
+ "note": ""
2807
+ },
2808
+ {
2809
+ "name": "showAdd",
2810
+ "type": "boolean",
2811
+ "default": "true",
2812
+ "note": ""
2813
+ },
2814
+ {
2815
+ "name": "addLabel",
2816
+ "type": "string",
2817
+ "default": "'Thêm mới'",
2818
+ "note": ""
2819
+ },
2820
+ {
2821
+ "name": "columnItems",
2822
+ "type": "GeoColumnPickerItem[]",
2823
+ "default": "[]",
2824
+ "note": ""
2825
+ },
2826
+ {
2827
+ "name": "columnStorageKey",
2828
+ "type": "string",
2829
+ "default": "",
2830
+ "note": "Key lưu trạng thái ẩn/hiện cột trong sessionStorage"
2831
+ }
712
2832
  ],
713
2833
  "outputs": [
714
- "deleteClick",
715
- "exportClick",
716
- "toggleColumns",
717
- "columnApply",
718
- "addClick"
2834
+ {
2835
+ "name": "deleteClick",
2836
+ "type": "EventEmitter<void>",
2837
+ "note": ""
2838
+ },
2839
+ {
2840
+ "name": "exportClick",
2841
+ "type": "EventEmitter<void>",
2842
+ "note": ""
2843
+ },
2844
+ {
2845
+ "name": "toggleColumns",
2846
+ "type": "EventEmitter<void>",
2847
+ "note": ""
2848
+ },
2849
+ {
2850
+ "name": "columnApply",
2851
+ "type": "EventEmitter<GeoColumnPickerApplyPayload>",
2852
+ "note": ""
2853
+ },
2854
+ {
2855
+ "name": "addClick",
2856
+ "type": "EventEmitter<void>",
2857
+ "note": ""
2858
+ }
2859
+ ],
2860
+ "examples": [
2861
+ {
2862
+ "title": "Basic",
2863
+ "lang": "html",
2864
+ "code": "<geo-table-toolbar [resultCount]=\"20\" [showAdd]=\"true\" (addClick)=\"add()\"></geo-table-toolbar>"
2865
+ },
2866
+ {
2867
+ "title": "Full options",
2868
+ "lang": "html",
2869
+ "code": "<geo-table-toolbar\n resultLabel=\"Kết quả\"\n [resultCount]=\"total\"\n [selectedCount]=\"selectedKeys.length\"\n [showDelete]=\"true\"\n [showExport]=\"true\"\n [showColumnToggle]=\"true\"\n [showAdd]=\"true\"\n addLabel=\"Thêm mới\"\n [columnItems]=\"columnPickerItems\"\n columnStorageKey=\"ho-so-columns\"\n (deleteClick)=\"onBulkDelete()\"\n (exportClick)=\"onExport()\"\n (columnApply)=\"onColumns($event)\"\n (addClick)=\"openCreate()\"\n></geo-table-toolbar>"
2870
+ }
719
2871
  ],
720
- "example": "<geo-table-toolbar [resultCount]=\"20\" [showAdd]=\"true\" (addClick)=\"add()\"></geo-table-toolbar>"
2872
+ "example": "<geo-table-toolbar [resultCount]=\"20\" [showAdd]=\"true\" (addClick)=\"add()\"></geo-table-toolbar>",
2873
+ "related": [
2874
+ "geo-table",
2875
+ "geo-column-picker",
2876
+ "geo-button"
2877
+ ]
721
2878
  },
722
2879
  {
723
2880
  "name": "GeoTabsComponent",
@@ -726,23 +2883,94 @@
726
2883
  "summary": "Tab set with brand ink bar; content via ng-template uiTab.",
727
2884
  "whenToUse": "Peer sections on one page (info / history / files).",
728
2885
  "doNotUseWhen": "Ordered wizard steps — use geo-stepper.",
2886
+ "howTo": "Tab ngang brand ink. [items] {key,title}[]; nội dung qua <ng-template uiTab=\"key\">. Import GeoTabsComponent + GeoTabDirective.",
2887
+ "tips": [
2888
+ "Wizard tuần tự → geo-stepper, không tabs.",
2889
+ "[(selectedIndex)] hoặc [(selectedKey)].",
2890
+ "Bắt buộc import GeoTabDirective cho uiTab."
2891
+ ],
2892
+ "binding": "none",
2893
+ "cva": false,
729
2894
  "path": "projects/geo-ui/src/lib/figma/geo-tabs/geo-tabs.component.ts",
730
2895
  "doc": "ai/components/geo-tabs.md",
731
2896
  "inputs": [
732
- "items",
733
- "type",
734
- "size",
735
- "selectedIndex",
736
- "selectedKey",
737
- "centered",
738
- "animated"
2897
+ {
2898
+ "name": "items",
2899
+ "type": "GeoTabItem[]",
2900
+ "default": "[]",
2901
+ "note": ""
2902
+ },
2903
+ {
2904
+ "name": "type",
2905
+ "type": "'line' | 'card'",
2906
+ "default": "'line'",
2907
+ "note": ""
2908
+ },
2909
+ {
2910
+ "name": "size",
2911
+ "type": "'default' | 'large' | 'small'",
2912
+ "default": "'default'",
2913
+ "note": ""
2914
+ },
2915
+ {
2916
+ "name": "selectedIndex",
2917
+ "type": "number",
2918
+ "default": "0",
2919
+ "note": ""
2920
+ },
2921
+ {
2922
+ "name": "selectedKey",
2923
+ "type": "string",
2924
+ "default": "",
2925
+ "note": ""
2926
+ },
2927
+ {
2928
+ "name": "centered",
2929
+ "type": "boolean",
2930
+ "default": "false",
2931
+ "note": ""
2932
+ },
2933
+ {
2934
+ "name": "animated",
2935
+ "type": "boolean",
2936
+ "default": "true",
2937
+ "note": ""
2938
+ }
739
2939
  ],
740
2940
  "outputs": [
741
- "selectedIndexChange",
742
- "selectedKeyChange",
743
- "tabChange"
2941
+ {
2942
+ "name": "selectedIndexChange",
2943
+ "type": "EventEmitter<number>",
2944
+ "note": ""
2945
+ },
2946
+ {
2947
+ "name": "selectedKeyChange",
2948
+ "type": "EventEmitter<string>",
2949
+ "note": ""
2950
+ },
2951
+ {
2952
+ "name": "tabChange",
2953
+ "type": "EventEmitter<{ index: number; key: string }>",
2954
+ "note": ""
2955
+ }
2956
+ ],
2957
+ "examples": [
2958
+ {
2959
+ "title": "Basic",
2960
+ "lang": "html",
2961
+ "code": "<geo-tabs\n [items]=\"[{ key: 'info', title: 'Thông tin' }, { key: 'files', title: 'Tệp đính kèm' }]\"\n [(selectedKey)]=\"tab\"\n>\n <ng-template uiTab=\"info\">Nội dung thông tin</ng-template>\n <ng-template uiTab=\"files\">Danh sách tệp</ng-template>\n</geo-tabs>"
2962
+ },
2963
+ {
2964
+ "title": "Full options",
2965
+ "lang": "html",
2966
+ "code": "<geo-tabs\n [items]=\"[\n { key: 'info', title: 'Thông tin' },\n { key: 'history', title: 'Lịch sử' },\n { key: 'files', title: 'Tệp đính kèm' }\n ]\"\n type=\"line\"\n size=\"default\"\n [selectedIndex]=\"0\"\n [centered]=\"false\"\n [animated]=\"true\"\n (selectedKeyChange)=\"onTab($event)\"\n (tabChange)=\"onTabChange($event)\"\n>\n <ng-template uiTab=\"info\">…</ng-template>\n <ng-template uiTab=\"history\">…</ng-template>\n <ng-template uiTab=\"files\">…</ng-template>\n</geo-tabs>"
2967
+ }
744
2968
  ],
745
- "example": "<geo-tabs [items]=\"[{ key: 'a', title: 'Tab A' }, { key: 'b', title: 'Tab B' }]\" [(selectedIndex)]=\"i\">\n <ng-template uiTab=\"a\">Content A</ng-template>\n <ng-template uiTab=\"b\">Content B</ng-template>\n</geo-tabs>"
2969
+ "example": "<geo-tabs\n [items]=\"[{ key: 'info', title: 'Thông tin' }, { key: 'files', title: 'Tệp đính kèm' }]\"\n [(selectedKey)]=\"tab\"\n>\n <ng-template uiTab=\"info\">Nội dung thông tin</ng-template>\n <ng-template uiTab=\"files\">Danh sách tệp</ng-template>\n</geo-tabs>",
2970
+ "related": [
2971
+ "geo-stepper",
2972
+ "geo-typography"
2973
+ ]
746
2974
  },
747
2975
  {
748
2976
  "name": "GeoTextareaComponent",
@@ -751,23 +2979,96 @@
751
2979
  "summary": "Multi-line text field with label/validation.",
752
2980
  "whenToUse": "Notes, descriptions, long text.",
753
2981
  "doNotUseWhen": "Single-line — use geo-input.",
2982
+ "howTo": "Ô đa dòng (CVA). label + [(ngModel)]; rows; [block] mặc định true; maxlength + errorMessage khi validate.",
2983
+ "tips": [
2984
+ "Một dòng → geo-input.",
2985
+ "Đặt [required] + error khi submit.",
2986
+ "block=true full width trong modal."
2987
+ ],
2988
+ "binding": "cva",
2989
+ "cva": true,
754
2990
  "path": "projects/geo-ui/src/lib/figma/geo-textarea/geo-textarea.component.ts",
755
2991
  "doc": "ai/components/geo-textarea.md",
756
2992
  "inputs": [
757
- "label",
758
- "placeholder",
759
- "rows",
760
- "disabled",
761
- "required",
762
- "error",
763
- "errorMessage",
764
- "maxlength",
765
- "block"
2993
+ {
2994
+ "name": "label",
2995
+ "type": "string",
2996
+ "default": "''",
2997
+ "note": ""
2998
+ },
2999
+ {
3000
+ "name": "placeholder",
3001
+ "type": "string",
3002
+ "default": "''",
3003
+ "note": ""
3004
+ },
3005
+ {
3006
+ "name": "rows",
3007
+ "type": "number",
3008
+ "default": "3",
3009
+ "note": ""
3010
+ },
3011
+ {
3012
+ "name": "disabled",
3013
+ "type": "boolean",
3014
+ "default": "false",
3015
+ "note": ""
3016
+ },
3017
+ {
3018
+ "name": "required",
3019
+ "type": "boolean",
3020
+ "default": "false",
3021
+ "note": ""
3022
+ },
3023
+ {
3024
+ "name": "error",
3025
+ "type": "boolean",
3026
+ "default": "false",
3027
+ "note": ""
3028
+ },
3029
+ {
3030
+ "name": "errorMessage",
3031
+ "type": "string",
3032
+ "default": "''",
3033
+ "note": ""
3034
+ },
3035
+ {
3036
+ "name": "maxlength",
3037
+ "type": "number",
3038
+ "default": "",
3039
+ "note": ""
3040
+ },
3041
+ {
3042
+ "name": "block",
3043
+ "type": "boolean",
3044
+ "default": "true",
3045
+ "note": ""
3046
+ }
766
3047
  ],
767
3048
  "outputs": [
768
- "valueChange"
3049
+ {
3050
+ "name": "valueChange",
3051
+ "type": "EventEmitter<string>",
3052
+ "note": ""
3053
+ }
3054
+ ],
3055
+ "examples": [
3056
+ {
3057
+ "title": "Basic",
3058
+ "lang": "html",
3059
+ "code": "<geo-textarea label=\"Ghi chú\" [rows]=\"4\" [block]=\"true\" [(ngModel)]=\"note\"></geo-textarea>"
3060
+ },
3061
+ {
3062
+ "title": "Full options",
3063
+ "lang": "html",
3064
+ "code": "<geo-textarea\n label=\"Mô tả\"\n placeholder=\"Nhập mô tả chi tiết\"\n [rows]=\"5\"\n [required]=\"true\"\n [maxlength]=\"500\"\n [block]=\"true\"\n [error]=\"descError\"\n errorMessage=\"Vui lòng nhập mô tả\"\n [(ngModel)]=\"description\"\n (valueChange)=\"onDesc($event)\"\n></geo-textarea>"
3065
+ }
769
3066
  ],
770
- "example": "<geo-textarea label=\"Ghi chú\" [rows]=\"4\" [block]=\"true\" [(ngModel)]=\"note\"></geo-textarea>"
3067
+ "example": "<geo-textarea label=\"Ghi chú\" [rows]=\"4\" [block]=\"true\" [(ngModel)]=\"note\"></geo-textarea>",
3068
+ "related": [
3069
+ "geo-input",
3070
+ "geo-form-modal"
3071
+ ]
771
3072
  },
772
3073
  {
773
3074
  "name": "GeoToastComponent",
@@ -776,21 +3077,84 @@
776
3077
  "summary": "Toast UI; prefer GeoToastService for app-wide notifications.",
777
3078
  "whenToUse": "Transient success/error after actions.",
778
3079
  "doNotUseWhen": "Persistent inline messages — use geo-alert.",
3080
+ "howTo": "Thông báo tạm thời. Ưu tiên inject GeoToastService (success/error/warning/info). Component geo-toast chỉ khi cần render tĩnh.",
3081
+ "tips": [
3082
+ "Inline persistent → geo-alert.",
3083
+ "duration: 0 để toast không tự đóng.",
3084
+ "Import GeoToastService từ @geoit/ui (providedIn root)."
3085
+ ],
3086
+ "binding": "none",
3087
+ "cva": false,
779
3088
  "path": "projects/geo-ui/src/lib/figma/geo-toast/geo-toast.component.ts",
780
3089
  "doc": "ai/components/geo-toast.md",
781
3090
  "inputs": [
782
- "type",
783
- "title",
784
- "message",
785
- "closable",
786
- "showIcon",
787
- "visible",
788
- "icon"
3091
+ {
3092
+ "name": "type",
3093
+ "type": "'success' | 'error' | 'warning' | 'info'",
3094
+ "default": "'info'",
3095
+ "note": ""
3096
+ },
3097
+ {
3098
+ "name": "title",
3099
+ "type": "string",
3100
+ "default": "''",
3101
+ "note": ""
3102
+ },
3103
+ {
3104
+ "name": "message",
3105
+ "type": "string",
3106
+ "default": "''",
3107
+ "note": ""
3108
+ },
3109
+ {
3110
+ "name": "closable",
3111
+ "type": "boolean",
3112
+ "default": "true",
3113
+ "note": ""
3114
+ },
3115
+ {
3116
+ "name": "showIcon",
3117
+ "type": "boolean",
3118
+ "default": "true",
3119
+ "note": ""
3120
+ },
3121
+ {
3122
+ "name": "visible",
3123
+ "type": "boolean",
3124
+ "default": "true",
3125
+ "note": ""
3126
+ },
3127
+ {
3128
+ "name": "icon",
3129
+ "type": "string",
3130
+ "default": "",
3131
+ "note": "Override Ant Design icon type."
3132
+ }
789
3133
  ],
790
3134
  "outputs": [
791
- "closed"
3135
+ {
3136
+ "name": "closed",
3137
+ "type": "EventEmitter<void>",
3138
+ "note": ""
3139
+ }
3140
+ ],
3141
+ "examples": [
3142
+ {
3143
+ "title": "Service (preferred)",
3144
+ "lang": "ts",
3145
+ "code": "import { GeoToastService } from '@geoit/ui';\n\nconstructor(private toast: GeoToastService) {}\n\nsave(): void {\n this.toast.success('Thành công!', 'Đã lưu hồ sơ.');\n}\n\nfail(): void {\n this.toast.error('Lỗi!', 'Không thể lưu.', { duration: 0 });\n}\n\nwarn(): void {\n this.toast.warning('Cảnh báo!', 'Dữ liệu chưa đầy đủ.');\n}\n\ninfo(): void {\n this.toast.info('Thông báo', 'Hệ thống sẽ bảo trì lúc 22:00.');\n}"
3146
+ },
3147
+ {
3148
+ "title": "Static template",
3149
+ "lang": "html",
3150
+ "code": "<geo-toast\n type=\"success\"\n title=\"Thành công!\"\n message=\"Đã lưu.\"\n [closable]=\"true\"\n [showIcon]=\"true\"\n [visible]=\"true\"\n></geo-toast>"
3151
+ }
792
3152
  ],
793
- "example": "// Preferred\nthis.toast.success('Thành công!', 'Đã lưu hồ sơ.');\n\n// Or static\n<geo-toast type=\"success\" title=\"Thành công!\" message=\"Đã lưu.\"></geo-toast>"
3153
+ "example": "import { GeoToastService } from '@geoit/ui';\n\nconstructor(private toast: GeoToastService) {}\n\nsave(): void {\n this.toast.success('Thành công!', 'Đã lưu hồ sơ.');\n}\n\nfail(): void {\n this.toast.error('Lỗi!', 'Không thể lưu.', { duration: 0 });\n}\n\nwarn(): void {\n this.toast.warning('Cảnh báo!', 'Dữ liệu chưa đầy đủ.');\n}\n\ninfo(): void {\n this.toast.info('Thông báo', 'Hệ thống sẽ bảo trì lúc 22:00.');\n}",
3154
+ "related": [
3155
+ "geo-alert",
3156
+ "geo-confirm-modal"
3157
+ ]
794
3158
  },
795
3159
  {
796
3160
  "name": "GeoTreeComponent",
@@ -799,22 +3163,88 @@
799
3163
  "summary": "Tree view with checkable / expandable nodes.",
800
3164
  "whenToUse": "Hierarchy selection (org, folder, category).",
801
3165
  "doNotUseWhen": "Flat lists — use geo-table or select.",
3166
+ "howTo": "Cây phân cấp. [nodes] GeoTreeNode[]; bật checkable + [(checkedKeys)]; [(expandedKeys)]; (nodeClick).",
3167
+ "tips": [
3168
+ "nodes: { title, key, children? }[].",
3169
+ "showLine / blockNode theo design cần thiết.",
3170
+ "Không dùng thay select đơn giản."
3171
+ ],
3172
+ "binding": "none",
3173
+ "cva": false,
802
3174
  "path": "projects/geo-ui/src/lib/figma/geo-tree/geo-tree.component.ts",
803
3175
  "doc": "ai/components/geo-tree.md",
804
3176
  "inputs": [
805
- "nodes",
806
- "checkable",
807
- "checkedKeys",
808
- "expandedKeys",
809
- "showLine",
810
- "blockNode"
3177
+ {
3178
+ "name": "nodes",
3179
+ "type": "GeoTreeNode[]",
3180
+ "default": "[]",
3181
+ "note": ""
3182
+ },
3183
+ {
3184
+ "name": "checkable",
3185
+ "type": "boolean",
3186
+ "default": "false",
3187
+ "note": ""
3188
+ },
3189
+ {
3190
+ "name": "checkedKeys",
3191
+ "type": "Array<string | number>",
3192
+ "default": "[]",
3193
+ "note": ""
3194
+ },
3195
+ {
3196
+ "name": "expandedKeys",
3197
+ "type": "Array<string | number>",
3198
+ "default": "[]",
3199
+ "note": ""
3200
+ },
3201
+ {
3202
+ "name": "showLine",
3203
+ "type": "boolean",
3204
+ "default": "false",
3205
+ "note": ""
3206
+ },
3207
+ {
3208
+ "name": "blockNode",
3209
+ "type": "boolean",
3210
+ "default": "false",
3211
+ "note": ""
3212
+ }
811
3213
  ],
812
3214
  "outputs": [
813
- "checkedKeysChange",
814
- "expandedKeysChange",
815
- "nodeClick"
3215
+ {
3216
+ "name": "checkedKeysChange",
3217
+ "type": "EventEmitter<Array<string | number>>",
3218
+ "note": ""
3219
+ },
3220
+ {
3221
+ "name": "expandedKeysChange",
3222
+ "type": "EventEmitter<Array<string | number>>",
3223
+ "note": ""
3224
+ },
3225
+ {
3226
+ "name": "nodeClick",
3227
+ "type": "EventEmitter<NzFormatEmitEvent>",
3228
+ "note": ""
3229
+ }
3230
+ ],
3231
+ "examples": [
3232
+ {
3233
+ "title": "Basic",
3234
+ "lang": "html",
3235
+ "code": "<geo-tree [nodes]=\"nodes\" [checkable]=\"true\" [(checkedKeys)]=\"checked\"></geo-tree>"
3236
+ },
3237
+ {
3238
+ "title": "Full options",
3239
+ "lang": "html",
3240
+ "code": "<geo-tree\n [nodes]=\"[\n {\n title: 'Đơn vị gốc',\n key: 'root',\n children: [\n { title: 'Phòng A', key: 'a' },\n { title: 'Phòng B', key: 'b' }\n ]\n }\n ]\"\n [checkable]=\"true\"\n [(checkedKeys)]=\"checkedKeys\"\n [(expandedKeys)]=\"expandedKeys\"\n [showLine]=\"true\"\n [blockNode]=\"true\"\n (nodeClick)=\"onNode($event)\"\n></geo-tree>"
3241
+ }
816
3242
  ],
817
- "example": "<geo-tree [nodes]=\"nodes\" [checkable]=\"true\" [(checkedKeys)]=\"checked\"></geo-tree>"
3243
+ "example": "<geo-tree [nodes]=\"nodes\" [checkable]=\"true\" [(checkedKeys)]=\"checked\"></geo-tree>",
3244
+ "related": [
3245
+ "geo-checkbox",
3246
+ "geo-select"
3247
+ ]
818
3248
  },
819
3249
  {
820
3250
  "name": "GeoTypographyComponent",
@@ -823,20 +3253,84 @@
823
3253
  "summary": "Text with GEO type scale (h1–tiny) and semantic colors.",
824
3254
  "whenToUse": "All readable text; prefer over raw h1/p with custom CSS.",
825
3255
  "doNotUseWhen": "Do not hardcode font-size/color — use variant/color props.",
3256
+ "howTo": "Text theo type scale GEO. variant h1–h4|subtitle|body|body-small|caption|…; color semantic; strong/underline/mark/code.",
3257
+ "tips": [
3258
+ "Tiêu đề trang có gạch nhấn → geo-page-title.",
3259
+ "color: default|secondary|primary|success|warning|danger|disabled.",
3260
+ "Không hardcode font-size — dùng variant."
3261
+ ],
3262
+ "binding": "none",
3263
+ "cva": false,
826
3264
  "path": "projects/geo-ui/src/lib/figma/geo-typography/geo-typography.component.ts",
827
3265
  "doc": "ai/components/geo-typography.md",
828
3266
  "inputs": [
829
- "variant",
830
- "color",
831
- "strong",
832
- "underline",
833
- "delete",
834
- "mark",
835
- "code",
836
- "disabled"
3267
+ {
3268
+ "name": "variant",
3269
+ "type": "'h1' | 'h2' | 'h3' | 'h4' | 'subtitle' | 'body' | 'body-small' | 'caption' | 'overline' | 'tiny'",
3270
+ "default": "'body'",
3271
+ "note": "Type scale variant from the design system."
3272
+ },
3273
+ {
3274
+ "name": "color",
3275
+ "type": "'default' | 'secondary' | 'primary' | 'success' | 'warning' | 'danger' | 'disabled'",
3276
+ "default": "'default'",
3277
+ "note": "Semantic text color."
3278
+ },
3279
+ {
3280
+ "name": "strong",
3281
+ "type": "boolean",
3282
+ "default": "false",
3283
+ "note": "Force bold weight (overrides variant weight when true)."
3284
+ },
3285
+ {
3286
+ "name": "underline",
3287
+ "type": "boolean",
3288
+ "default": "false",
3289
+ "note": "Underline text decoration."
3290
+ },
3291
+ {
3292
+ "name": "delete",
3293
+ "type": "boolean",
3294
+ "default": "false",
3295
+ "note": "Strikethrough text decoration."
3296
+ },
3297
+ {
3298
+ "name": "mark",
3299
+ "type": "boolean",
3300
+ "default": "false",
3301
+ "note": "Mark / highlight background."
3302
+ },
3303
+ {
3304
+ "name": "code",
3305
+ "type": "boolean",
3306
+ "default": "false",
3307
+ "note": "Monospace code appearance."
3308
+ },
3309
+ {
3310
+ "name": "disabled",
3311
+ "type": "boolean",
3312
+ "default": "false",
3313
+ "note": "Disabled appearance"
3314
+ }
837
3315
  ],
838
3316
  "outputs": [],
839
- "example": "<geo-typography variant=\"h3\">Tiêu đề</geo-typography>\n<geo-typography variant=\"body\" color=\"secondary\">Mô tả phụ</geo-typography>"
3317
+ "examples": [
3318
+ {
3319
+ "title": "Basic",
3320
+ "lang": "html",
3321
+ "code": "<geo-typography variant=\"h3\">Tiêu đề</geo-typography>\n<geo-typography variant=\"body\" color=\"secondary\">Mô tả phụ</geo-typography>"
3322
+ },
3323
+ {
3324
+ "title": "Full options",
3325
+ "lang": "html",
3326
+ "code": "<geo-typography variant=\"h4\" [strong]=\"true\">1. Thông tin cá nhân</geo-typography>\n<geo-typography variant=\"body\" color=\"primary\" [underline]=\"true\">Xem chi tiết</geo-typography>\n<geo-typography variant=\"caption\" color=\"danger\">Trường bắt buộc</geo-typography>\n<geo-typography variant=\"body-small\" [code]=\"true\">HS-2024-001</geo-typography>"
3327
+ }
3328
+ ],
3329
+ "example": "<geo-typography variant=\"h3\">Tiêu đề</geo-typography>\n<geo-typography variant=\"body\" color=\"secondary\">Mô tả phụ</geo-typography>",
3330
+ "related": [
3331
+ "geo-page-title",
3332
+ "geo-status-tag"
3333
+ ]
840
3334
  },
841
3335
  {
842
3336
  "name": "GeoUploadComponent",
@@ -845,38 +3339,184 @@
845
3339
  "summary": "Drag-drop upload zone with progress list.",
846
3340
  "whenToUse": "Multi-file upload with progress UI.",
847
3341
  "doNotUseWhen": "Simple one-file form field — use geo-file-picker.",
3342
+ "howTo": "Vùng kéo-thả upload + danh sách tiến độ. accept/maxSize; [(fileList)]; (change)/(remove)/(reject). Form gọn một file → geo-file-picker.",
3343
+ "tips": [
3344
+ "maxSize tính bằng byte (mặc định 1MB).",
3345
+ "hint/titlePrefix/selectText/titleSuffix tiếng Việt tùy chỉnh.",
3346
+ "customRequest / beforeUpload khi cần upload tùy biến."
3347
+ ],
3348
+ "binding": "none",
3349
+ "cva": false,
848
3350
  "path": "projects/geo-ui/src/lib/figma/geo-upload/geo-upload.component.ts",
849
3351
  "doc": "ai/components/geo-upload.md",
850
3352
  "inputs": [
851
- "action",
852
- "accept",
853
- "maxSize",
854
- "multiple",
855
- "disabled",
856
- "error",
857
- "errorMessage",
858
- "hint",
859
- "titlePrefix",
860
- "selectText",
861
- "titleSuffix",
862
- "listTitle",
863
- "showListTitle",
864
- "fileList",
865
- "headers",
866
- "data",
867
- "name",
868
- "withCredentials",
869
- "directory",
870
- "customRequest",
871
- "beforeUpload"
3353
+ {
3354
+ "name": "action",
3355
+ "type": "string | ((file: NzUploadFile) => string | Observable<string>)",
3356
+ "default": "''",
3357
+ "note": "Upload endpoint — bỏ trống nếu dùng `customRequest` / chỉ chọn file local."
3358
+ },
3359
+ {
3360
+ "name": "accept",
3361
+ "type": "string",
3362
+ "default": "'.png,.jpg,.jpeg,.svg'",
3363
+ "note": ""
3364
+ },
3365
+ {
3366
+ "name": "maxSize",
3367
+ "type": "",
3368
+ "default": "1024 * 1024",
3369
+ "note": "Max size (bytes). Default 1MB. `0` = không giới hạn."
3370
+ },
3371
+ {
3372
+ "name": "multiple",
3373
+ "type": "boolean",
3374
+ "default": "true",
3375
+ "note": ""
3376
+ },
3377
+ {
3378
+ "name": "disabled",
3379
+ "type": "boolean",
3380
+ "default": "false",
3381
+ "note": ""
3382
+ },
3383
+ {
3384
+ "name": "error",
3385
+ "type": "boolean",
3386
+ "default": "false",
3387
+ "note": ""
3388
+ },
3389
+ {
3390
+ "name": "errorMessage",
3391
+ "type": "string",
3392
+ "default": "''",
3393
+ "note": ""
3394
+ },
3395
+ {
3396
+ "name": "hint",
3397
+ "type": "string",
3398
+ "default": "'Chỉ cho phép file dạng .png, .jpg, .jpeg, .svg và dung lượng không quá 1Mb'",
3399
+ "note": ""
3400
+ },
3401
+ {
3402
+ "name": "titlePrefix",
3403
+ "type": "string",
3404
+ "default": "'Kéo thả ảnh hoặc'",
3405
+ "note": ""
3406
+ },
3407
+ {
3408
+ "name": "selectText",
3409
+ "type": "string",
3410
+ "default": "'Chọn file'",
3411
+ "note": ""
3412
+ },
3413
+ {
3414
+ "name": "titleSuffix",
3415
+ "type": "string",
3416
+ "default": "'để tải lên'",
3417
+ "note": ""
3418
+ },
3419
+ {
3420
+ "name": "listTitle",
3421
+ "type": "string",
3422
+ "default": "'Tập tin đính kèm'",
3423
+ "note": ""
3424
+ },
3425
+ {
3426
+ "name": "showListTitle",
3427
+ "type": "boolean",
3428
+ "default": "true",
3429
+ "note": ""
3430
+ },
3431
+ {
3432
+ "name": "fileList",
3433
+ "type": "GeoUploadFile[]",
3434
+ "default": "[]",
3435
+ "note": "Controlled file list."
3436
+ },
3437
+ {
3438
+ "name": "headers",
3439
+ "type": "{} | ((file: NzUploadFile) => {} | Observable<{}>)",
3440
+ "default": "",
3441
+ "note": ""
3442
+ },
3443
+ {
3444
+ "name": "data",
3445
+ "type": "{} | ((file: NzUploadFile) => {} | Observable<{}>)",
3446
+ "default": "",
3447
+ "note": ""
3448
+ },
3449
+ {
3450
+ "name": "name",
3451
+ "type": "string",
3452
+ "default": "'file'",
3453
+ "note": ""
3454
+ },
3455
+ {
3456
+ "name": "withCredentials",
3457
+ "type": "boolean",
3458
+ "default": "false",
3459
+ "note": ""
3460
+ },
3461
+ {
3462
+ "name": "directory",
3463
+ "type": "boolean",
3464
+ "default": "false",
3465
+ "note": ""
3466
+ },
3467
+ {
3468
+ "name": "customRequest",
3469
+ "type": "(item: NzUploadXHRArgs) => Subscription",
3470
+ "default": "",
3471
+ "note": "Custom XHR — mặc định simulate progress khi không có `action`."
3472
+ },
3473
+ {
3474
+ "name": "beforeUpload",
3475
+ "type": "( file: NzUploadFile, fileList: NzUploadFile[] )",
3476
+ "default": "> boolean | Observable<boolean>",
3477
+ "note": ""
3478
+ }
872
3479
  ],
873
3480
  "outputs": [
874
- "fileListChange",
875
- "change",
876
- "remove",
877
- "reject"
3481
+ {
3482
+ "name": "fileListChange",
3483
+ "type": "EventEmitter<GeoUploadFile[]>",
3484
+ "note": ""
3485
+ },
3486
+ {
3487
+ "name": "change",
3488
+ "type": "EventEmitter<NzUploadChangeParam>",
3489
+ "note": ""
3490
+ },
3491
+ {
3492
+ "name": "remove",
3493
+ "type": "EventEmitter<GeoUploadFile>",
3494
+ "note": ""
3495
+ },
3496
+ {
3497
+ "name": "reject",
3498
+ "type": "EventEmitter<{ file: File; reason: 'type' | 'size' }>",
3499
+ "note": ""
3500
+ }
3501
+ ],
3502
+ "examples": [
3503
+ {
3504
+ "title": "Basic",
3505
+ "lang": "html",
3506
+ "code": "<geo-upload\n accept=\".png,.jpg\"\n [maxSize]=\"1048576\"\n [(fileList)]=\"files\"\n></geo-upload>"
3507
+ },
3508
+ {
3509
+ "title": "Full options",
3510
+ "lang": "html",
3511
+ "code": "<geo-upload\n action=\"/api/upload\"\n accept=\".png,.jpg,.jpeg,.svg,.pdf\"\n [maxSize]=\"2097152\"\n [multiple]=\"true\"\n [disabled]=\"false\"\n hint=\"Chỉ cho phép .png, .jpg, .pdf và dung lượng không quá 2Mb\"\n titlePrefix=\"Kéo thả ảnh hoặc\"\n selectText=\"Chọn file\"\n titleSuffix=\"để tải lên\"\n listTitle=\"Tập tin đính kèm\"\n [showListTitle]=\"true\"\n [(fileList)]=\"files\"\n (fileListChange)=\"onFiles($event)\"\n (change)=\"onUploadChange($event)\"\n (remove)=\"onRemove($event)\"\n (reject)=\"onReject($event)\"\n></geo-upload>"
3512
+ }
878
3513
  ],
879
- "example": "<geo-upload\n accept=\".png,.jpg\"\n [maxSize]=\"1048576\"\n [(fileList)]=\"files\"\n></geo-upload>"
3514
+ "example": "<geo-upload\n accept=\".png,.jpg\"\n [maxSize]=\"1048576\"\n [(fileList)]=\"files\"\n></geo-upload>",
3515
+ "related": [
3516
+ "geo-file-picker",
3517
+ "geo-form-modal",
3518
+ "geo-button"
3519
+ ]
880
3520
  }
881
3521
  ]
882
3522
  }