@ttdk-design-system/library-react 0.0.0-stage → 1.0.1
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.
- package/AI_GUIDE.md +6518 -0
- package/README.md +106 -2
- package/llms.txt +6518 -0
- package/package.json +1099 -4
- package/src/core/index.ts +2 -0
- package/src/core/tailwind-preset.cjs +62 -0
- package/src/core/tailwind-preset.ts +85 -0
- package/src/core/theme/tokens.css +663 -0
- package/src/core/types.ts +836 -0
- package/src/index.ts +17 -0
- package/src/tailwind-preset.cjs +62 -0
- package/src/tailwind-preset.ts +85 -0
- package/src/types.ts +1 -0
- package/src/web/accordion/AI_GUIDE.md +149 -0
- package/src/web/accordion/accordion.css +1 -0
- package/src/web/accordion/accordion.tsx +430 -0
- package/src/web/accordion/index.ts +3 -0
- package/src/web/accordion/manifest.json +351 -0
- package/src/web/alert/AI_GUIDE.md +139 -0
- package/src/web/alert/alert.css +1 -0
- package/src/web/alert/alert.tsx +318 -0
- package/src/web/alert/index.ts +3 -0
- package/src/web/alert/manifest.json +240 -0
- package/src/web/avatar/AI_GUIDE.md +118 -0
- package/src/web/avatar/avatar.css +1 -0
- package/src/web/avatar/avatar.tsx +233 -0
- package/src/web/avatar/index.ts +3 -0
- package/src/web/avatar/manifest.json +195 -0
- package/src/web/badge/AI_GUIDE.md +94 -0
- package/src/web/badge/badge.css +1 -0
- package/src/web/badge/badge.tsx +140 -0
- package/src/web/badge/index.ts +2 -0
- package/src/web/badge/manifest.json +141 -0
- package/src/web/banner/AI_GUIDE.md +130 -0
- package/src/web/banner/banner.css +1 -0
- package/src/web/banner/banner.tsx +334 -0
- package/src/web/banner/index.ts +3 -0
- package/src/web/banner/manifest.json +240 -0
- package/src/web/breadcrumb/AI_GUIDE.md +120 -0
- package/src/web/breadcrumb/breadcrumb.css +1 -0
- package/src/web/breadcrumb/breadcrumb.tsx +306 -0
- package/src/web/breadcrumb/index.ts +3 -0
- package/src/web/breadcrumb/manifest.json +225 -0
- package/src/web/button/AI_GUIDE.md +121 -0
- package/src/web/button/button.css +1 -0
- package/src/web/button/button.tsx +287 -0
- package/src/web/button/index.ts +2 -0
- package/src/web/button/manifest.json +177 -0
- package/src/web/calendar/AI_GUIDE.md +151 -0
- package/src/web/calendar/calendar.css +1 -0
- package/src/web/calendar/calendar.tsx +1611 -0
- package/src/web/calendar/index.ts +3 -0
- package/src/web/calendar/manifest.json +470 -0
- package/src/web/card/AI_GUIDE.md +133 -0
- package/src/web/card/card.css +1 -0
- package/src/web/card/card.tsx +408 -0
- package/src/web/card/index.ts +4 -0
- package/src/web/card/manifest.json +357 -0
- package/src/web/checkbox/AI_GUIDE.md +146 -0
- package/src/web/checkbox/checkbox.css +1 -0
- package/src/web/checkbox/checkbox.tsx +307 -0
- package/src/web/checkbox/index.ts +2 -0
- package/src/web/checkbox/manifest.json +294 -0
- package/src/web/chip/AI_GUIDE.md +186 -0
- package/src/web/chip/chip.css +1 -0
- package/src/web/chip/chip.tsx +230 -0
- package/src/web/chip/index.ts +2 -0
- package/src/web/chip/manifest.json +186 -0
- package/src/web/dropdown/AI_GUIDE.md +167 -0
- package/src/web/dropdown/dropdown.css +1 -0
- package/src/web/dropdown/dropdown.tsx +1274 -0
- package/src/web/dropdown/index.ts +3 -0
- package/src/web/dropdown/manifest.json +576 -0
- package/src/web/flex/AI_GUIDE.md +113 -0
- package/src/web/flex/flex-item.tsx +97 -0
- package/src/web/flex/flex.css +1 -0
- package/src/web/flex/flex.tsx +108 -0
- package/src/web/flex/index.ts +5 -0
- package/src/web/flex/manifest.json +162 -0
- package/src/web/form/index.ts +7 -0
- package/src/web/grid/AI_GUIDE.md +112 -0
- package/src/web/grid/grid-item.tsx +112 -0
- package/src/web/grid/grid.css +1 -0
- package/src/web/grid/grid.tsx +99 -0
- package/src/web/grid/index.ts +5 -0
- package/src/web/grid/manifest.json +153 -0
- package/src/web/index.ts +86 -0
- package/src/web/input/AI_GUIDE.md +159 -0
- package/src/web/input/index.ts +3 -0
- package/src/web/input/input.css +1 -0
- package/src/web/input/input.tsx +537 -0
- package/src/web/input/manifest.json +465 -0
- package/src/web/input-number/AI_GUIDE.md +164 -0
- package/src/web/input-number/index.ts +3 -0
- package/src/web/input-number/input-number.css +1 -0
- package/src/web/input-number/input-number.tsx +1193 -0
- package/src/web/input-number/manifest.json +575 -0
- package/src/web/modal/AI_GUIDE.md +171 -0
- package/src/web/modal/index.ts +3 -0
- package/src/web/modal/manifest.json +375 -0
- package/src/web/modal/modal.css +1 -0
- package/src/web/modal/modal.tsx +571 -0
- package/src/web/pagination/AI_GUIDE.md +148 -0
- package/src/web/pagination/index.ts +21 -0
- package/src/web/pagination/manifest.json +432 -0
- package/src/web/pagination/pagination-content.tsx +45 -0
- package/src/web/pagination/pagination-ellipsis.tsx +54 -0
- package/src/web/pagination/pagination-first.tsx +155 -0
- package/src/web/pagination/pagination-item.tsx +45 -0
- package/src/web/pagination/pagination-last.tsx +155 -0
- package/src/web/pagination/pagination-link.tsx +121 -0
- package/src/web/pagination/pagination-next.tsx +158 -0
- package/src/web/pagination/pagination-previous.tsx +158 -0
- package/src/web/pagination/pagination.css +1 -0
- package/src/web/pagination/pagination.tsx +687 -0
- package/src/web/popover/AI_GUIDE.md +163 -0
- package/src/web/popover/index.ts +3 -0
- package/src/web/popover/manifest.json +396 -0
- package/src/web/popover/popover.css +1 -0
- package/src/web/popover/popover.tsx +702 -0
- package/src/web/radio/AI_GUIDE.md +155 -0
- package/src/web/radio/index.ts +2 -0
- package/src/web/radio/manifest.json +294 -0
- package/src/web/radio/radio.css +1 -0
- package/src/web/radio/radio.tsx +290 -0
- package/src/web/selectbox/AI_GUIDE.md +216 -0
- package/src/web/selectbox/index.ts +3 -0
- package/src/web/selectbox/manifest.json +477 -0
- package/src/web/selectbox/selectbox.css +1 -0
- package/src/web/selectbox/selectbox.tsx +1048 -0
- package/src/web/sonner/AI_GUIDE.md +98 -0
- package/src/web/sonner/index.ts +114 -0
- package/src/web/sonner/manifest.json +132 -0
- package/src/web/sonner/sonner.css +1 -0
- package/src/web/sonner/sonner.tsx +177 -0
- package/src/web/table/AI_GUIDE.md +145 -0
- package/src/web/table/index.ts +35 -0
- package/src/web/table/manifest.json +168 -0
- package/src/web/table/table-body.tsx +45 -0
- package/src/web/table/table-caption.tsx +45 -0
- package/src/web/table/table-cell.tsx +74 -0
- package/src/web/table/table-empty.tsx +108 -0
- package/src/web/table/table-footer.tsx +45 -0
- package/src/web/table/table-head.tsx +80 -0
- package/src/web/table/table-header.tsx +45 -0
- package/src/web/table/table-row.tsx +69 -0
- package/src/web/table/table.css +1 -0
- package/src/web/table/table.tsx +305 -0
- package/src/web/tabs/AI_GUIDE.md +150 -0
- package/src/web/tabs/index.ts +3 -0
- package/src/web/tabs/manifest.json +315 -0
- package/src/web/tabs/tabs.css +1 -0
- package/src/web/tabs/tabs.tsx +332 -0
- package/src/web/text/AI_GUIDE.md +129 -0
- package/src/web/text/index.ts +3 -0
- package/src/web/text/manifest.json +195 -0
- package/src/web/text/text.css +1 -0
- package/src/web/text/text.tsx +689 -0
- package/src/web/textarea/AI_GUIDE.md +141 -0
- package/src/web/textarea/index.ts +3 -0
- package/src/web/textarea/manifest.json +438 -0
- package/src/web/textarea/textarea.css +1 -0
- package/src/web/textarea/textarea.tsx +467 -0
- package/src/web/tooltip/AI_GUIDE.md +110 -0
- package/src/web/tooltip/index.ts +3 -0
- package/src/web/tooltip/manifest.json +222 -0
- package/src/web/tooltip/tooltip.css +1 -0
- package/src/web/tooltip/tooltip.tsx +423 -0
- package/src/webview/bottomsheet/AI_GUIDE.md +141 -0
- package/src/webview/bottomsheet/bottomsheet.css +1 -0
- package/src/webview/bottomsheet/bottomsheet.tsx +603 -0
- package/src/webview/bottomsheet/index.ts +4 -0
- package/src/webview/bottomsheet/manifest.json +377 -0
- package/src/webview/button/AI_GUIDE.md +79 -0
- package/src/webview/button/button-ios.css +1 -0
- package/src/webview/button/button-ios.tsx +138 -0
- package/src/webview/button/button-material.css +1 -0
- package/src/webview/button/button-material.tsx +138 -0
- package/src/webview/button/index.ts +7 -0
- package/src/webview/button/manifest.json +148 -0
- package/src/webview/index.ts +7 -0
- package/tailwind-preset.cjs +62 -0
- package/tailwind-preset.js +62 -0
- package/ttdk-manifest.json +10188 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ttdk-design-system/tabs",
|
|
3
|
+
"component": "Tabs",
|
|
4
|
+
"folder": "tabs",
|
|
5
|
+
"description": "Komponen Tabs untuk navigasi multi-panel dengan orientasi Horizontal & Vertikal, varian Line, Pills, dan Enclosed.",
|
|
6
|
+
"stylesheets": [
|
|
7
|
+
{
|
|
8
|
+
"theme": "common",
|
|
9
|
+
"file": "tabs.css",
|
|
10
|
+
"importPath": "web/tabs/tabs.css"
|
|
11
|
+
}
|
|
12
|
+
],
|
|
13
|
+
"props": {
|
|
14
|
+
"items": {
|
|
15
|
+
"name": "items",
|
|
16
|
+
"type": "TabItem[]",
|
|
17
|
+
"optional": false,
|
|
18
|
+
"default": null,
|
|
19
|
+
"description": "Daftar tab items yang akan dirender.",
|
|
20
|
+
"example": null,
|
|
21
|
+
"param": null
|
|
22
|
+
},
|
|
23
|
+
"value": {
|
|
24
|
+
"name": "value",
|
|
25
|
+
"type": "string",
|
|
26
|
+
"optional": true,
|
|
27
|
+
"default": null,
|
|
28
|
+
"description": "Nilai tab yang sedang aktif (v-model / controlled value).",
|
|
29
|
+
"example": null,
|
|
30
|
+
"param": null
|
|
31
|
+
},
|
|
32
|
+
"defaultValue": {
|
|
33
|
+
"name": "defaultValue",
|
|
34
|
+
"type": "string",
|
|
35
|
+
"optional": true,
|
|
36
|
+
"default": null,
|
|
37
|
+
"description": "Nilai tab default saat pertama kali dirender (uncontrolled initial value).",
|
|
38
|
+
"example": null,
|
|
39
|
+
"param": null
|
|
40
|
+
},
|
|
41
|
+
"orientation": {
|
|
42
|
+
"name": "orientation",
|
|
43
|
+
"type": "'horizontal' | 'vertical'",
|
|
44
|
+
"optional": true,
|
|
45
|
+
"default": "horizontal",
|
|
46
|
+
"description": "Orientasi tata letak tab. - `'horizontal'`: Tab berjejer ke samping dengan garis indikator di bawah (standar). - `'vertical'`: Tab berjejer ke bawah dalam kolom dengan garis indikator di samping.",
|
|
47
|
+
"example": null,
|
|
48
|
+
"param": null
|
|
49
|
+
},
|
|
50
|
+
"variant": {
|
|
51
|
+
"name": "variant",
|
|
52
|
+
"type": "'line' | 'pills'",
|
|
53
|
+
"optional": true,
|
|
54
|
+
"default": "line",
|
|
55
|
+
"description": "Varian visual tampilan tab. - `'line'`: Garis indikator bawah/samping minimalis elegan (sesuai desain TTDK). - `'pills'`: Bentuk kapsul/tombol latar belakang penuh ala Shadcn / Nuxt UI.",
|
|
56
|
+
"example": null,
|
|
57
|
+
"param": null
|
|
58
|
+
},
|
|
59
|
+
"size": {
|
|
60
|
+
"name": "size",
|
|
61
|
+
"type": "'sm' | 'md' | 'lg'",
|
|
62
|
+
"optional": true,
|
|
63
|
+
"default": "md",
|
|
64
|
+
"description": "Ukuran proporsional tab (tinggi, padding, ukuran font). - `'sm'`: Kecil (12px font, compact padding). - `'md'`: Standar (14px font, regular padding). - `'lg'`: Besar (16px font, spacious padding).",
|
|
65
|
+
"example": null,
|
|
66
|
+
"param": null
|
|
67
|
+
},
|
|
68
|
+
"fitted": {
|
|
69
|
+
"name": "fitted",
|
|
70
|
+
"type": "boolean",
|
|
71
|
+
"optional": true,
|
|
72
|
+
"default": "false",
|
|
73
|
+
"description": "Membuat lebar setiap tab mengisi rata seluruh ruang container (full width / equal width).",
|
|
74
|
+
"example": null,
|
|
75
|
+
"param": null
|
|
76
|
+
},
|
|
77
|
+
"rounded": {
|
|
78
|
+
"name": "rounded",
|
|
79
|
+
"type": "'none' | 'sm' | 'md' | 'lg' | 'full'",
|
|
80
|
+
"optional": true,
|
|
81
|
+
"default": "md",
|
|
82
|
+
"description": "Sudut kelengkungan (khusus untuk varian 'pills'). - `'none'`: Sudut 0px. - `'sm'`: Sudut 4px. - `'md'`: Sudut 6px. - `'lg'`: Sudut 8px. - `'full'`: Sudut 9999px.",
|
|
83
|
+
"example": null,
|
|
84
|
+
"param": null
|
|
85
|
+
},
|
|
86
|
+
"lazy": {
|
|
87
|
+
"name": "lazy",
|
|
88
|
+
"type": "boolean",
|
|
89
|
+
"optional": true,
|
|
90
|
+
"default": "false",
|
|
91
|
+
"description": "Hanya me-render konten panel tab saat tab tersebut aktif (lazy rendering). Berguna untuk menghemat performa saat tab memiliki konten berat.",
|
|
92
|
+
"example": null,
|
|
93
|
+
"param": null
|
|
94
|
+
},
|
|
95
|
+
"style": {
|
|
96
|
+
"name": "style",
|
|
97
|
+
"type": "any",
|
|
98
|
+
"optional": true,
|
|
99
|
+
"default": null,
|
|
100
|
+
"description": "Inline CSS style object native.",
|
|
101
|
+
"example": null,
|
|
102
|
+
"param": null
|
|
103
|
+
},
|
|
104
|
+
"ui": {
|
|
105
|
+
"name": "ui",
|
|
106
|
+
"type": "UiProps",
|
|
107
|
+
"optional": true,
|
|
108
|
+
"default": null,
|
|
109
|
+
"description": "Kustomisasi visual granular root container (UiProps).",
|
|
110
|
+
"example": null,
|
|
111
|
+
"param": null
|
|
112
|
+
},
|
|
113
|
+
"listStyle": {
|
|
114
|
+
"name": "listStyle",
|
|
115
|
+
"type": "UiProps",
|
|
116
|
+
"optional": true,
|
|
117
|
+
"default": null,
|
|
118
|
+
"description": "Style kustom untuk container tab list (UiProps).",
|
|
119
|
+
"example": null,
|
|
120
|
+
"param": null
|
|
121
|
+
},
|
|
122
|
+
"listClass": {
|
|
123
|
+
"name": "listClass",
|
|
124
|
+
"type": "string",
|
|
125
|
+
"optional": true,
|
|
126
|
+
"default": null,
|
|
127
|
+
"description": "Class CSS kustom untuk container tab list.",
|
|
128
|
+
"example": null,
|
|
129
|
+
"param": null
|
|
130
|
+
},
|
|
131
|
+
"tabStyle": {
|
|
132
|
+
"name": "tabStyle",
|
|
133
|
+
"type": "UiProps",
|
|
134
|
+
"optional": true,
|
|
135
|
+
"default": null,
|
|
136
|
+
"description": "Style kustom untuk tombol tab item (UiProps).",
|
|
137
|
+
"example": null,
|
|
138
|
+
"param": null
|
|
139
|
+
},
|
|
140
|
+
"tabClass": {
|
|
141
|
+
"name": "tabClass",
|
|
142
|
+
"type": "string",
|
|
143
|
+
"optional": true,
|
|
144
|
+
"default": null,
|
|
145
|
+
"description": "Class CSS kustom untuk tombol tab item.",
|
|
146
|
+
"example": null,
|
|
147
|
+
"param": null
|
|
148
|
+
},
|
|
149
|
+
"activeTabStyle": {
|
|
150
|
+
"name": "activeTabStyle",
|
|
151
|
+
"type": "UiProps",
|
|
152
|
+
"optional": true,
|
|
153
|
+
"default": null,
|
|
154
|
+
"description": "Style kustom untuk tombol tab item saat aktif (UiProps).",
|
|
155
|
+
"example": null,
|
|
156
|
+
"param": null
|
|
157
|
+
},
|
|
158
|
+
"activeTabClass": {
|
|
159
|
+
"name": "activeTabClass",
|
|
160
|
+
"type": "string",
|
|
161
|
+
"optional": true,
|
|
162
|
+
"default": null,
|
|
163
|
+
"description": "Class CSS kustom untuk tombol tab item saat aktif.",
|
|
164
|
+
"example": null,
|
|
165
|
+
"param": null
|
|
166
|
+
},
|
|
167
|
+
"indicatorStyle": {
|
|
168
|
+
"name": "indicatorStyle",
|
|
169
|
+
"type": "UiProps",
|
|
170
|
+
"optional": true,
|
|
171
|
+
"default": null,
|
|
172
|
+
"description": "Style kustom untuk garis indikator tab (UiProps).",
|
|
173
|
+
"example": null,
|
|
174
|
+
"param": null
|
|
175
|
+
},
|
|
176
|
+
"indicatorClass": {
|
|
177
|
+
"name": "indicatorClass",
|
|
178
|
+
"type": "string",
|
|
179
|
+
"optional": true,
|
|
180
|
+
"default": null,
|
|
181
|
+
"description": "Class CSS kustom untuk garis indikator tab.",
|
|
182
|
+
"example": null,
|
|
183
|
+
"param": null
|
|
184
|
+
},
|
|
185
|
+
"badgeStyle": {
|
|
186
|
+
"name": "badgeStyle",
|
|
187
|
+
"type": "UiProps",
|
|
188
|
+
"optional": true,
|
|
189
|
+
"default": null,
|
|
190
|
+
"description": "Style kustom untuk badge tab (UiProps).",
|
|
191
|
+
"example": null,
|
|
192
|
+
"param": null
|
|
193
|
+
},
|
|
194
|
+
"badgeClass": {
|
|
195
|
+
"name": "badgeClass",
|
|
196
|
+
"type": "string",
|
|
197
|
+
"optional": true,
|
|
198
|
+
"default": null,
|
|
199
|
+
"description": "Class CSS kustom untuk badge tab.",
|
|
200
|
+
"example": null,
|
|
201
|
+
"param": null
|
|
202
|
+
},
|
|
203
|
+
"panelStyle": {
|
|
204
|
+
"name": "panelStyle",
|
|
205
|
+
"type": "UiProps",
|
|
206
|
+
"optional": true,
|
|
207
|
+
"default": null,
|
|
208
|
+
"description": "Style kustom untuk panel konten tab (UiProps).",
|
|
209
|
+
"example": null,
|
|
210
|
+
"param": null
|
|
211
|
+
},
|
|
212
|
+
"panelClass": {
|
|
213
|
+
"name": "panelClass",
|
|
214
|
+
"type": "string",
|
|
215
|
+
"optional": true,
|
|
216
|
+
"default": null,
|
|
217
|
+
"description": "Class CSS kustom untuk panel konten tab.",
|
|
218
|
+
"example": null,
|
|
219
|
+
"param": null
|
|
220
|
+
},
|
|
221
|
+
"onChange": {
|
|
222
|
+
"name": "onChange",
|
|
223
|
+
"type": "(value: string) => void",
|
|
224
|
+
"optional": true,
|
|
225
|
+
"default": null,
|
|
226
|
+
"description": "Callback event saat pengguna mengganti tab aktif.",
|
|
227
|
+
"example": null,
|
|
228
|
+
"param": "value Nilai/identifier tab yang dipilih."
|
|
229
|
+
},
|
|
230
|
+
"className": {
|
|
231
|
+
"name": "className",
|
|
232
|
+
"type": "string",
|
|
233
|
+
"optional": true,
|
|
234
|
+
"default": null,
|
|
235
|
+
"description": "Class CSS kustom.",
|
|
236
|
+
"example": null,
|
|
237
|
+
"param": null
|
|
238
|
+
},
|
|
239
|
+
"children": {
|
|
240
|
+
"name": "children",
|
|
241
|
+
"type": "any",
|
|
242
|
+
"optional": true,
|
|
243
|
+
"default": null,
|
|
244
|
+
"description": "Konten kustom atau slot anak.",
|
|
245
|
+
"example": null,
|
|
246
|
+
"param": null
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
"subInterfaces": {
|
|
250
|
+
"TabItem": {
|
|
251
|
+
"label": {
|
|
252
|
+
"name": "label",
|
|
253
|
+
"type": "string",
|
|
254
|
+
"optional": false,
|
|
255
|
+
"default": null,
|
|
256
|
+
"description": "Label teks yang ditampilkan pada tab item.",
|
|
257
|
+
"example": null,
|
|
258
|
+
"param": null
|
|
259
|
+
},
|
|
260
|
+
"value": {
|
|
261
|
+
"name": "value",
|
|
262
|
+
"type": "string",
|
|
263
|
+
"optional": false,
|
|
264
|
+
"default": null,
|
|
265
|
+
"description": "Identifier unik / nilai aktif untuk tab ini.",
|
|
266
|
+
"example": null,
|
|
267
|
+
"param": null
|
|
268
|
+
},
|
|
269
|
+
"icon": {
|
|
270
|
+
"name": "icon",
|
|
271
|
+
"type": "any",
|
|
272
|
+
"optional": true,
|
|
273
|
+
"default": null,
|
|
274
|
+
"description": "Ikon atau emoji opsional di samping label teks.",
|
|
275
|
+
"example": null,
|
|
276
|
+
"param": null
|
|
277
|
+
},
|
|
278
|
+
"badge": {
|
|
279
|
+
"name": "badge",
|
|
280
|
+
"type": "string | number",
|
|
281
|
+
"optional": true,
|
|
282
|
+
"default": null,
|
|
283
|
+
"description": "Badge teks atau counter angka opsional (misal: '12', 'Baru').",
|
|
284
|
+
"example": null,
|
|
285
|
+
"param": null
|
|
286
|
+
},
|
|
287
|
+
"disabled": {
|
|
288
|
+
"name": "disabled",
|
|
289
|
+
"type": "boolean",
|
|
290
|
+
"optional": true,
|
|
291
|
+
"default": "false",
|
|
292
|
+
"description": "Menonaktifkan tab item dari interaksi klik.",
|
|
293
|
+
"example": null,
|
|
294
|
+
"param": null
|
|
295
|
+
},
|
|
296
|
+
"content": {
|
|
297
|
+
"name": "content",
|
|
298
|
+
"type": "any",
|
|
299
|
+
"optional": true,
|
|
300
|
+
"default": null,
|
|
301
|
+
"description": "Konten tab opsional jika ingin menggunakan panel terintegrasi.",
|
|
302
|
+
"example": null,
|
|
303
|
+
"param": null
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
},
|
|
307
|
+
"supportedFrameworks": [
|
|
308
|
+
"react",
|
|
309
|
+
"vue",
|
|
310
|
+
"svelte"
|
|
311
|
+
],
|
|
312
|
+
"themes": [
|
|
313
|
+
"tw"
|
|
314
|
+
]
|
|
315
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.ttdk-tabs{--tab-radius:var(--ttdk-radius-input-md);box-sizing:border-box;font-family:var(--ttdk-font-sans);color:var(--ttdk-ui-color,var(--ttdk-text-icon-primary));width:100%;min-width:0;max-width:100%;display:flex;&[data-orientation=horizontal]{flex-direction:column;width:100%;min-width:0;max-width:100%;& .ttdk-tabs-list{box-sizing:border-box;scrollbar-width:none;-webkit-overflow-scrolling:touch;flex-direction:row;align-items:center;width:100%;min-width:0;max-width:100%;display:flex;overflow:auto hidden;&::-webkit-scrollbar{display:none}}}&[data-orientation=vertical]{align-items:stretch;gap:var(--ttdk-spacing-md);flex-direction:row;width:100%;min-width:0;max-width:100%;& .ttdk-tabs-list{flex-direction:column;flex-shrink:0;min-width:180px;max-width:260px;display:flex;@media (width<=640px){min-width:120px;max-width:160px}}& .ttdk-tabs-panels{flex:1;min-width:0}}&[data-fitted=true][data-orientation=horizontal]{& .ttdk-tabs-list{width:100%}& .ttdk-tab-item{text-align:center;flex:1;justify-content:center}}&[data-size=sm]{font-size:var(--ttdk-font-size-12);& .ttdk-tab-item{font-size:var(--ttdk-font-size-12);gap:6px}&[data-variant=line][data-orientation=horizontal] .ttdk-tab-item{gap:6px;padding:8px 2px}&[data-variant=line][data-orientation=vertical] .ttdk-tab-item{padding:8px 12px}&[data-variant=pills] .ttdk-tab-item{font-size:var(--ttdk-font-size-12);padding:5px 10px}& .ttdk-tab-icon{font-size:var(--ttdk-font-size-14)}}&[data-size=md]{font-size:var(--ttdk-font-size-14);& .ttdk-tab-item{font-size:var(--ttdk-font-size-14);gap:var(--ttdk-spacing-xs)}&[data-variant=line][data-orientation=horizontal] .ttdk-tab-item{padding:12px 4px}&[data-variant=line][data-orientation=vertical] .ttdk-tab-item{padding:10px 14px}&[data-variant=pills] .ttdk-tab-item{padding:6px 14px;font-size:13px}& .ttdk-tab-icon{font-size:var(--ttdk-font-size-16)}}&[data-size=lg]{font-size:var(--ttdk-font-size-16);& .ttdk-tab-item{font-size:var(--ttdk-font-size-16);gap:10px}&[data-variant=line][data-orientation=horizontal] .ttdk-tab-item{padding:14px 6px}&[data-variant=line][data-orientation=vertical] .ttdk-tab-item{padding:12px 16px}&[data-variant=pills] .ttdk-tab-item{font-size:var(--ttdk-font-size-14);padding:8px 18px}& .ttdk-tab-icon{font-size:var(--ttdk-font-size-18)}}&[data-rounded=none]{--tab-radius:var(--ttdk-radius-none)}&[data-rounded=sm]{--tab-radius:var(--ttdk-radius-input-sm)}&[data-rounded=md]{--tab-radius:var(--ttdk-radius-input-md)}&[data-rounded=lg]{--tab-radius:var(--ttdk-radius-input-lg)}&[data-rounded=full]{--tab-radius:var(--ttdk-radius-full)}}.ttdk-tab-item{font-family:inherit;font-weight:var(--ttdk-font-weight-medium);color:var(--ttdk-ui-color,var(--ttdk-text-icon-secondary));cursor:pointer;white-space:nowrap;user-select:none;background:0 0;border:none;outline:none;flex-shrink:0;justify-content:flex-start;align-items:center;transition:color .15s,background-color .15s,border-color .15s;display:inline-flex;position:relative;&:hover:not(:disabled){color:var(--ttdk-text-icon-primary)}&:focus-visible{box-shadow:0 0 0 2px color-mix(in oklab, var(--ttdk-brand-default) 40%, transparent);border-radius:var(--ttdk-radius-input-sm)}&[data-state=active]{color:var(--ttdk-brand-default);font-weight:var(--ttdk-font-weight-semibold)}&:disabled,&[data-disabled=true]{opacity:.45;cursor:not-allowed;pointer-events:none}}.ttdk-tab-icon{flex-shrink:0;justify-content:center;align-items:center;display:inline-flex}.ttdk-tab-label{display:inline-block}.ttdk-tab-badge{font-size:11px;font-weight:var(--ttdk-font-weight-semibold);border-radius:var(--ttdk-radius-full);background:var(--ttdk-bg-secondary-disabled);color:var(--ttdk-text-icon-secondary);justify-content:center;align-items:center;padding:1px 6px;line-height:1.2;transition:all .15s;display:inline-flex}.ttdk-tab-item[data-state=active] .ttdk-tab-badge{background:color-mix(in oklab, var(--ttdk-brand-default) 15%, transparent);color:var(--ttdk-brand-default)}.ttdk-tabs[data-variant=line]{&[data-orientation=horizontal]{& .ttdk-tabs-list{border-bottom:var(--ttdk-stroke-width-thin) solid var(--ttdk-stroke-subtle);gap:var(--ttdk-spacing-lg)}& .ttdk-tab-indicator{background-color:var(--ttdk-brand-default);border-radius:var(--ttdk-radius-input-xs) var(--ttdk-radius-input-xs) 0 0;height:2px;animation:.2s ease-in-out ttdkTabFadeIn;position:absolute;bottom:-1px;left:0;right:0}}&[data-orientation=vertical]{& .ttdk-tabs-list{border-right:var(--ttdk-stroke-width-thin) solid var(--ttdk-stroke-subtle);gap:var(--ttdk-spacing-xxs);padding-right:var(--ttdk-spacing-xxs)}& .ttdk-tab-item{border-radius:var(--tab-radius);&[data-state=active]{background:color-mix(in oklab, var(--ttdk-brand-default) 8%, transparent)}}& .ttdk-tab-indicator{background-color:var(--ttdk-brand-default);border-radius:var(--ttdk-radius-input-xs);width:2px;animation:.2s ease-in-out ttdkTabFadeIn;position:absolute;top:4px;bottom:4px;right:-5px}}}.ttdk-tabs[data-variant=pills]{width:100%;min-width:0;max-width:100%;& .ttdk-tabs-list{box-sizing:border-box;background:var(--ttdk-bg-secondary-disabled);width:100%;min-width:0;max-width:100%;padding:var(--ttdk-spacing-xxs);border-radius:var(--tab-radius);gap:var(--ttdk-spacing-xxs)}& .ttdk-tab-item{border-radius:var(--tab-radius);color:var(--ttdk-text-icon-secondary);&:hover:not(:disabled){color:var(--ttdk-text-icon-primary)}&[data-state=active]{background:var(--ttdk-bg-card);color:var(--ttdk-text-icon-primary);box-shadow:var(--ttdk-shadow-sm)}}}.ttdk-tabs-panels{width:100%}.ttdk-tab-panel{padding:var(--ttdk-spacing-md) 0;color:var(--ttdk-text-icon-primary);animation:.2s ease-in-out ttdkTabFadeIn}@keyframes ttdkTabFadeIn{0%{opacity:0}to{opacity:1}}
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
|
|
5
|
+
export interface TabItem {
|
|
6
|
+
/**
|
|
7
|
+
* Label teks yang ditampilkan pada tab item.
|
|
8
|
+
*/
|
|
9
|
+
label: string;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Identifier unik / nilai aktif untuk tab ini.
|
|
13
|
+
*/
|
|
14
|
+
value: string;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Ikon atau emoji opsional di samping label teks.
|
|
18
|
+
*/
|
|
19
|
+
icon?: any;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Badge teks atau counter angka opsional (misal: '12', 'Baru').
|
|
23
|
+
*/
|
|
24
|
+
badge?: string | number;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Menonaktifkan tab item dari interaksi klik.
|
|
28
|
+
* @default false
|
|
29
|
+
*/
|
|
30
|
+
disabled?: boolean;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Konten tab opsional jika ingin menggunakan panel terintegrasi.
|
|
34
|
+
*/
|
|
35
|
+
content?: any;
|
|
36
|
+
}
|
|
37
|
+
export type TabsUiProps = UiProps;
|
|
38
|
+
export interface TabsProps {
|
|
39
|
+
/**
|
|
40
|
+
* Daftar tab items yang akan dirender.
|
|
41
|
+
*/
|
|
42
|
+
items: TabItem[];
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Nilai tab yang sedang aktif (v-model / controlled value).
|
|
46
|
+
*/
|
|
47
|
+
value?: string;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Nilai tab default saat pertama kali dirender (uncontrolled initial value).
|
|
51
|
+
*/
|
|
52
|
+
defaultValue?: string;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Orientasi tata letak tab.
|
|
56
|
+
* - `'horizontal'`: Tab berjejer ke samping dengan garis indikator di bawah (standar).
|
|
57
|
+
* - `'vertical'`: Tab berjejer ke bawah dalam kolom dengan garis indikator di samping.
|
|
58
|
+
*
|
|
59
|
+
* @default 'horizontal'
|
|
60
|
+
*/
|
|
61
|
+
orientation?: "horizontal" | "vertical";
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Varian visual tampilan tab.
|
|
65
|
+
* - `'line'`: Garis indikator bawah/samping minimalis elegan (sesuai desain TTDK).
|
|
66
|
+
* - `'pills'`: Bentuk kapsul/tombol latar belakang penuh ala Shadcn / Nuxt UI.
|
|
67
|
+
*
|
|
68
|
+
* @default 'line'
|
|
69
|
+
*/
|
|
70
|
+
variant?: "line" | "pills";
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Ukuran proporsional tab (tinggi, padding, ukuran font).
|
|
74
|
+
* - `'sm'`: Kecil (12px font, compact padding).
|
|
75
|
+
* - `'md'`: Standar (14px font, regular padding).
|
|
76
|
+
* - `'lg'`: Besar (16px font, spacious padding).
|
|
77
|
+
*
|
|
78
|
+
* @default 'md'
|
|
79
|
+
*/
|
|
80
|
+
size?: "sm" | "md" | "lg";
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Membuat lebar setiap tab mengisi rata seluruh ruang container (full width / equal width).
|
|
84
|
+
*
|
|
85
|
+
* @default false
|
|
86
|
+
*/
|
|
87
|
+
fitted?: boolean;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Sudut kelengkungan (khusus untuk varian 'pills').
|
|
91
|
+
* - `'none'`: Sudut 0px.
|
|
92
|
+
* - `'sm'`: Sudut 4px.
|
|
93
|
+
* - `'md'`: Sudut 6px.
|
|
94
|
+
* - `'lg'`: Sudut 8px.
|
|
95
|
+
* - `'full'`: Sudut 9999px.
|
|
96
|
+
*
|
|
97
|
+
* @default 'md'
|
|
98
|
+
*/
|
|
99
|
+
rounded?: "none" | "sm" | "md" | "lg" | "full";
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Hanya me-render konten panel tab saat tab tersebut aktif (lazy rendering).
|
|
103
|
+
* Berguna untuk menghemat performa saat tab memiliki konten berat.
|
|
104
|
+
* @default false
|
|
105
|
+
*/
|
|
106
|
+
lazy?: boolean;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Inline CSS style object native.
|
|
110
|
+
*/
|
|
111
|
+
style?: React.CSSProperties;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Kustomisasi visual granular root container (UiProps).
|
|
115
|
+
*/
|
|
116
|
+
ui?: UiProps;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Style kustom untuk container tab list (UiProps).
|
|
120
|
+
*/
|
|
121
|
+
listStyle?: UiProps;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Class CSS kustom untuk container tab list.
|
|
125
|
+
*/
|
|
126
|
+
listClass?: string;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Style kustom untuk tombol tab item (UiProps).
|
|
130
|
+
*/
|
|
131
|
+
tabStyle?: UiProps;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Class CSS kustom untuk tombol tab item.
|
|
135
|
+
*/
|
|
136
|
+
tabClass?: string;
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Style kustom untuk tombol tab item saat aktif (UiProps).
|
|
140
|
+
*/
|
|
141
|
+
activeTabStyle?: UiProps;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Class CSS kustom untuk tombol tab item saat aktif.
|
|
145
|
+
*/
|
|
146
|
+
activeTabClass?: string;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Style kustom untuk garis indikator tab (UiProps).
|
|
150
|
+
*/
|
|
151
|
+
indicatorStyle?: UiProps;
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Class CSS kustom untuk garis indikator tab.
|
|
155
|
+
*/
|
|
156
|
+
indicatorClass?: string;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Style kustom untuk badge tab (UiProps).
|
|
160
|
+
*/
|
|
161
|
+
badgeStyle?: UiProps;
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Class CSS kustom untuk badge tab.
|
|
165
|
+
*/
|
|
166
|
+
badgeClass?: string;
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Style kustom untuk panel konten tab (UiProps).
|
|
170
|
+
*/
|
|
171
|
+
panelStyle?: UiProps;
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Class CSS kustom untuk panel konten tab.
|
|
175
|
+
*/
|
|
176
|
+
panelClass?: string;
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Callback event saat pengguna mengganti tab aktif.
|
|
180
|
+
*
|
|
181
|
+
* @param value Nilai/identifier tab yang dipilih.
|
|
182
|
+
*/
|
|
183
|
+
onChange?: (value: string) => void;
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Class CSS kustom.
|
|
187
|
+
*/
|
|
188
|
+
className?: string;
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Konten kustom atau slot anak.
|
|
192
|
+
*/
|
|
193
|
+
children?: any;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Komponen Tabs untuk navigasi multi-panel dengan orientasi Horizontal & Vertikal, varian Line, Pills, dan Enclosed.
|
|
198
|
+
*/
|
|
199
|
+
|
|
200
|
+
import { buildUiVars, UiProps } from "../../core/types";
|
|
201
|
+
|
|
202
|
+
function Tabs(props: TabsProps) {
|
|
203
|
+
const [activeVal, setActiveVal] = useState(
|
|
204
|
+
() =>
|
|
205
|
+
props.value ||
|
|
206
|
+
props.defaultValue ||
|
|
207
|
+
(props.items && props.items[0]?.value) ||
|
|
208
|
+
""
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
function getCurrentActive() {
|
|
212
|
+
return props.value !== undefined ? props.value : activeVal;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function handleTabClick(itemValue: string, disabled?: boolean) {
|
|
216
|
+
if (disabled) return;
|
|
217
|
+
setActiveVal(itemValue);
|
|
218
|
+
if (props.onChange) {
|
|
219
|
+
props.onChange(itemValue);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function getActiveItem() {
|
|
224
|
+
const current = getCurrentActive();
|
|
225
|
+
return props.items
|
|
226
|
+
? props.items.find((item: any) => item.value === current)
|
|
227
|
+
: null;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return (
|
|
231
|
+
<div
|
|
232
|
+
className={`ttdk-tabs ${props.className || ""}`.trim()}
|
|
233
|
+
data-orientation={props.orientation || "horizontal"}
|
|
234
|
+
data-variant={props.variant || "line"}
|
|
235
|
+
data-size={props.size || "md"}
|
|
236
|
+
data-fitted={props.fitted ? "true" : "false"}
|
|
237
|
+
data-rounded={props.rounded || "md"}
|
|
238
|
+
style={{
|
|
239
|
+
...buildUiVars(props.ui),
|
|
240
|
+
...((props as any)["style"] || {}),
|
|
241
|
+
...props.style,
|
|
242
|
+
}}
|
|
243
|
+
>
|
|
244
|
+
<div
|
|
245
|
+
role="tablist"
|
|
246
|
+
className={`ttdk-tabs-list ${props.listClass || ""}`.trim()}
|
|
247
|
+
aria-orientation={props.orientation || "horizontal"}
|
|
248
|
+
style={{
|
|
249
|
+
...buildUiVars(props.listStyle),
|
|
250
|
+
}}
|
|
251
|
+
>
|
|
252
|
+
{props.items?.map((item) => (
|
|
253
|
+
<button
|
|
254
|
+
type="button"
|
|
255
|
+
role="tab"
|
|
256
|
+
key={item.value}
|
|
257
|
+
className={`ttdk-tab-item ${props.tabClass || ""} ${
|
|
258
|
+
getCurrentActive() === item.value
|
|
259
|
+
? props.activeTabClass || ""
|
|
260
|
+
: ""
|
|
261
|
+
}`.trim()}
|
|
262
|
+
data-state={
|
|
263
|
+
getCurrentActive() === item.value ? "active" : "inactive"
|
|
264
|
+
}
|
|
265
|
+
data-disabled={item.disabled ? "true" : "false"}
|
|
266
|
+
disabled={item.disabled}
|
|
267
|
+
aria-selected={getCurrentActive() === item.value}
|
|
268
|
+
onClick={(event) => handleTabClick(item.value, item.disabled)}
|
|
269
|
+
style={{
|
|
270
|
+
...buildUiVars(props.tabStyle),
|
|
271
|
+
...(getCurrentActive() === item.value
|
|
272
|
+
? buildUiVars(props.activeTabStyle)
|
|
273
|
+
: {}),
|
|
274
|
+
}}
|
|
275
|
+
>
|
|
276
|
+
{item.icon ? (
|
|
277
|
+
<span className="ttdk-tab-icon">{item.icon}</span>
|
|
278
|
+
) : null}
|
|
279
|
+
<span className="ttdk-tab-label">{item.label}</span>
|
|
280
|
+
{item.badge !== undefined && item.badge !== null ? (
|
|
281
|
+
<span
|
|
282
|
+
className={`ttdk-tab-badge ${props.badgeClass || ""}`.trim()}
|
|
283
|
+
style={{
|
|
284
|
+
...buildUiVars(props.badgeStyle),
|
|
285
|
+
}}
|
|
286
|
+
>
|
|
287
|
+
{item.badge}
|
|
288
|
+
</span>
|
|
289
|
+
) : null}
|
|
290
|
+
{getCurrentActive() === item.value &&
|
|
291
|
+
(!props.variant || props.variant === "line") ? (
|
|
292
|
+
<span
|
|
293
|
+
className={`ttdk-tab-indicator ${
|
|
294
|
+
props.indicatorClass || ""
|
|
295
|
+
}`.trim()}
|
|
296
|
+
style={{
|
|
297
|
+
...buildUiVars(props.indicatorStyle),
|
|
298
|
+
}}
|
|
299
|
+
/>
|
|
300
|
+
) : null}
|
|
301
|
+
</button>
|
|
302
|
+
))}
|
|
303
|
+
</div>
|
|
304
|
+
<div className="ttdk-tabs-panels">
|
|
305
|
+
{getActiveItem() && getActiveItem()?.content ? (
|
|
306
|
+
<>
|
|
307
|
+
{!props.lazy || getCurrentActive() === getActiveItem()?.value ? (
|
|
308
|
+
<div
|
|
309
|
+
role="tabpanel"
|
|
310
|
+
className={`ttdk-tab-panel ${props.panelClass || ""}`.trim()}
|
|
311
|
+
style={{
|
|
312
|
+
...buildUiVars(props.panelStyle),
|
|
313
|
+
}}
|
|
314
|
+
>
|
|
315
|
+
{getActiveItem()?.content}
|
|
316
|
+
</div>
|
|
317
|
+
) : null}
|
|
318
|
+
</>
|
|
319
|
+
) : null}
|
|
320
|
+
{props.children ? (
|
|
321
|
+
<div className="ttdk-tab-children">
|
|
322
|
+
<>{props.children}</>
|
|
323
|
+
</div>
|
|
324
|
+
) : null}
|
|
325
|
+
</div>
|
|
326
|
+
</div>
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
export default Tabs;
|
|
331
|
+
|
|
332
|
+
export { Tabs };
|