@ttdk-design-system/library-vue 0.0.0-stage → 1.0.0
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/llms.txt +6518 -0
- package/package.json +1096 -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.vue +432 -0
- package/src/web/accordion/accordion.vue.d.ts +227 -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.vue +290 -0
- package/src/web/alert/alert.vue.d.ts +154 -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.vue +217 -0
- package/src/web/avatar/avatar.vue.d.ts +135 -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.vue +135 -0
- package/src/web/badge/badge.vue.d.ts +108 -0
- package/src/web/badge/index.ts +3 -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.vue +303 -0
- package/src/web/banner/banner.vue.d.ts +162 -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.vue +313 -0
- package/src/web/breadcrumb/breadcrumb.vue.d.ts +139 -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.vue +283 -0
- package/src/web/button/button.vue.d.ts +147 -0
- package/src/web/button/index.ts +3 -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.vue +1536 -0
- package/src/web/calendar/calendar.vue.d.ts +261 -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.vue +423 -0
- package/src/web/card/card.vue.d.ts +232 -0
- package/src/web/card/index.ts +3 -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.vue +304 -0
- package/src/web/checkbox/checkbox.vue.d.ts +189 -0
- package/src/web/checkbox/index.ts +3 -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.vue +220 -0
- package/src/web/chip/chip.vue.d.ts +140 -0
- package/src/web/chip/index.ts +3 -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.vue +1257 -0
- package/src/web/dropdown/dropdown.vue.d.ts +381 -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.vue +94 -0
- package/src/web/flex/flex-item.vue.d.ts +84 -0
- package/src/web/flex/flex.css +1 -0
- package/src/web/flex/flex.vue +103 -0
- package/src/web/flex/flex.vue.d.ts +92 -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.vue +109 -0
- package/src/web/grid/grid-item.vue.d.ts +99 -0
- package/src/web/grid/grid.css +1 -0
- package/src/web/grid/grid.vue +94 -0
- package/src/web/grid/grid.vue.d.ts +84 -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.vue +521 -0
- package/src/web/input/input.vue.d.ts +274 -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.vue +1153 -0
- package/src/web/input-number/input-number.vue.d.ts +343 -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.vue +344 -0
- package/src/web/modal/modal.vue.d.ts +64 -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.vue +42 -0
- package/src/web/pagination/pagination-content.vue.d.ts +28 -0
- package/src/web/pagination/pagination-ellipsis.vue +55 -0
- package/src/web/pagination/pagination-ellipsis.vue.d.ts +28 -0
- package/src/web/pagination/pagination-first.vue +161 -0
- package/src/web/pagination/pagination-first.vue.d.ts +67 -0
- package/src/web/pagination/pagination-item.vue +42 -0
- package/src/web/pagination/pagination-item.vue.d.ts +28 -0
- package/src/web/pagination/pagination-last.vue +161 -0
- package/src/web/pagination/pagination-last.vue.d.ts +67 -0
- package/src/web/pagination/pagination-link.vue +118 -0
- package/src/web/pagination/pagination-link.vue.d.ts +61 -0
- package/src/web/pagination/pagination-next.vue +160 -0
- package/src/web/pagination/pagination-next.vue.d.ts +68 -0
- package/src/web/pagination/pagination-previous.vue +160 -0
- package/src/web/pagination/pagination-previous.vue.d.ts +68 -0
- package/src/web/pagination/pagination.css +1 -0
- package/src/web/pagination/pagination.vue +730 -0
- package/src/web/pagination/pagination.vue.d.ts +247 -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.vue +579 -0
- package/src/web/popover/popover.vue.d.ts +83 -0
- package/src/web/radio/AI_GUIDE.md +155 -0
- package/src/web/radio/index.ts +3 -0
- package/src/web/radio/manifest.json +294 -0
- package/src/web/radio/radio.css +1 -0
- package/src/web/radio/radio.vue +288 -0
- package/src/web/radio/radio.vue.d.ts +182 -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.vue +1024 -0
- package/src/web/selectbox/selectbox.vue.d.ts +300 -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.vue +180 -0
- package/src/web/sonner/sonner.vue.d.ts +113 -0
- package/src/web/table/AI_GUIDE.md +145 -0
- package/src/web/table/index.ts +34 -0
- package/src/web/table/manifest.json +168 -0
- package/src/web/table/table-body.vue +42 -0
- package/src/web/table/table-body.vue.d.ts +28 -0
- package/src/web/table/table-caption.vue +42 -0
- package/src/web/table/table-caption.vue.d.ts +28 -0
- package/src/web/table/table-cell.vue +71 -0
- package/src/web/table/table-cell.vue.d.ts +49 -0
- package/src/web/table/table-empty.vue +105 -0
- package/src/web/table/table-empty.vue.d.ts +57 -0
- package/src/web/table/table-footer.vue +42 -0
- package/src/web/table/table-footer.vue.d.ts +28 -0
- package/src/web/table/table-head.vue +78 -0
- package/src/web/table/table-head.vue.d.ts +50 -0
- package/src/web/table/table-header.vue +42 -0
- package/src/web/table/table-header.vue.d.ts +28 -0
- package/src/web/table/table-row.vue +70 -0
- package/src/web/table/table-row.vue.d.ts +44 -0
- package/src/web/table/table.css +1 -0
- package/src/web/table/table.vue +298 -0
- package/src/web/table/table.vue.d.ts +119 -0
- package/src/web/tabs/AI_GUIDE.md +150 -0
- package/src/web/tabs/index.ts +2 -0
- package/src/web/tabs/manifest.json +315 -0
- package/src/web/tabs/tabs.css +1 -0
- package/src/web/tabs/tabs.vue +164 -0
- package/src/web/tabs/tabs.vue.d.ts +60 -0
- package/src/web/text/AI_GUIDE.md +129 -0
- package/src/web/text/index.ts +2 -0
- package/src/web/text/manifest.json +195 -0
- package/src/web/text/text.css +1 -0
- package/src/web/text/text.vue +195 -0
- package/src/web/text/text.vue.d.ts +161 -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.vue +458 -0
- package/src/web/textarea/textarea.vue.d.ts +265 -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.vue +417 -0
- package/src/web/tooltip/tooltip.vue.d.ts +150 -0
- package/src/webview/bottomsheet/AI_GUIDE.md +141 -0
- package/src/webview/bottomsheet/bottomsheet.css +1 -0
- package/src/webview/bottomsheet/bottomsheet.vue +441 -0
- package/src/webview/bottomsheet/bottomsheet.vue.d.ts +178 -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.vue +138 -0
- package/src/webview/button/button-ios.vue.d.ts +102 -0
- package/src/webview/button/button-material.css +1 -0
- package/src/webview/button/button-material.vue +138 -0
- package/src/webview/button/button-material.vue.d.ts +102 -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
- package/README.md +0 -3
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# <Tabs /> — TTDK Component Reference
|
|
2
|
+
|
|
3
|
+
Komponen Tabs untuk navigasi multi-panel dengan orientasi Horizontal & Vertikal, varian Line, Pills, dan Enclosed.
|
|
4
|
+
|
|
5
|
+
## 1. Stylesheets & Theming (Pure CSS)
|
|
6
|
+
Import the corresponding stylesheet for the theme you wish to use:
|
|
7
|
+
- **COMMON Theme**: `import "@ttdk-design-system/library-<react|vue|svelte>/web/tabs/tabs.css";`
|
|
8
|
+
|
|
9
|
+
## 2. Subpath Component Import
|
|
10
|
+
```ts
|
|
11
|
+
// React
|
|
12
|
+
import Tabs from "@ttdk-design-system/library-react/web/tabs";
|
|
13
|
+
|
|
14
|
+
// Vue 3
|
|
15
|
+
import Tabs from "@ttdk-design-system/library-vue/web/tabs";
|
|
16
|
+
|
|
17
|
+
// Svelte
|
|
18
|
+
import Tabs from "@ttdk-design-system/library-svelte/web/tabs";
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 3. Props & API Reference
|
|
22
|
+
|
|
23
|
+
| Prop | Type | Default | Description |
|
|
24
|
+
| :--- | :--- | :--- | :--- |
|
|
25
|
+
| `items` | `TabItem[]` | `-` | Daftar tab items yang akan dirender. |
|
|
26
|
+
| `value?` | `string` | `-` | Nilai tab yang sedang aktif (v-model / controlled value). |
|
|
27
|
+
| `defaultValue?` | `string` | `-` | Nilai tab default saat pertama kali dirender (uncontrolled initial value). |
|
|
28
|
+
| `orientation?` | `'horizontal' | 'vertical'` | `horizontal` | 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. |
|
|
29
|
+
| `variant?` | `'line' | 'pills'` | `line` | 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. |
|
|
30
|
+
| `size?` | `'sm' | 'md' | 'lg'` | `md` | 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). |
|
|
31
|
+
| `fitted?` | `boolean` | `false` | Membuat lebar setiap tab mengisi rata seluruh ruang container (full width / equal width). |
|
|
32
|
+
| `rounded?` | `'none' | 'sm' | 'md' | 'lg' | 'full'` | `md` | Sudut kelengkungan (khusus untuk varian 'pills'). - `'none'`: Sudut 0px. - `'sm'`: Sudut 4px. - `'md'`: Sudut 6px. - `'lg'`: Sudut 8px. - `'full'`: Sudut 9999px. |
|
|
33
|
+
| `lazy?` | `boolean` | `false` | Hanya me-render konten panel tab saat tab tersebut aktif (lazy rendering). Berguna untuk menghemat performa saat tab memiliki konten berat. |
|
|
34
|
+
| `style?` | `any` | `-` | Inline CSS style object native. |
|
|
35
|
+
| `ui?` | `UiProps` | `-` | Kustomisasi visual granular root container (UiProps). |
|
|
36
|
+
| `listStyle?` | `UiProps` | `-` | Style kustom untuk container tab list (UiProps). |
|
|
37
|
+
| `listClass?` | `string` | `-` | Class CSS kustom untuk container tab list. |
|
|
38
|
+
| `tabStyle?` | `UiProps` | `-` | Style kustom untuk tombol tab item (UiProps). |
|
|
39
|
+
| `tabClass?` | `string` | `-` | Class CSS kustom untuk tombol tab item. |
|
|
40
|
+
| `activeTabStyle?` | `UiProps` | `-` | Style kustom untuk tombol tab item saat aktif (UiProps). |
|
|
41
|
+
| `activeTabClass?` | `string` | `-` | Class CSS kustom untuk tombol tab item saat aktif. |
|
|
42
|
+
| `indicatorStyle?` | `UiProps` | `-` | Style kustom untuk garis indikator tab (UiProps). |
|
|
43
|
+
| `indicatorClass?` | `string` | `-` | Class CSS kustom untuk garis indikator tab. |
|
|
44
|
+
| `badgeStyle?` | `UiProps` | `-` | Style kustom untuk badge tab (UiProps). |
|
|
45
|
+
| `badgeClass?` | `string` | `-` | Class CSS kustom untuk badge tab. |
|
|
46
|
+
| `panelStyle?` | `UiProps` | `-` | Style kustom untuk panel konten tab (UiProps). |
|
|
47
|
+
| `panelClass?` | `string` | `-` | Class CSS kustom untuk panel konten tab. |
|
|
48
|
+
| `onChange?` | `(value: string) => void` | `-` | Callback event saat pengguna mengganti tab aktif. |
|
|
49
|
+
| `className?` | `string` | `-` | Class CSS kustom. |
|
|
50
|
+
| `children?` | `any` | `-` | Konten kustom atau slot anak. |
|
|
51
|
+
|
|
52
|
+
### Sub-interface: `TabItem`
|
|
53
|
+
|
|
54
|
+
| Field | Type | Default | Description |
|
|
55
|
+
| :--- | :--- | :--- | :--- |
|
|
56
|
+
| `label` | `string` | `-` | Label teks yang ditampilkan pada tab item. |
|
|
57
|
+
| `value` | `string` | `-` | Identifier unik / nilai aktif untuk tab ini. |
|
|
58
|
+
| `icon?` | `any` | `-` | Ikon atau emoji opsional di samping label teks. |
|
|
59
|
+
| `badge?` | `string | number` | `-` | Badge teks atau counter angka opsional (misal: '12', 'Baru'). |
|
|
60
|
+
| `disabled?` | `boolean` | `false` | Menonaktifkan tab item dari interaksi klik. |
|
|
61
|
+
| `content?` | `any` | `-` | Konten tab opsional jika ingin menggunakan panel terintegrasi. |
|
|
62
|
+
|
|
63
|
+
## 4. Usage Examples
|
|
64
|
+
|
|
65
|
+
### React Example
|
|
66
|
+
```tsx
|
|
67
|
+
import { useState } from "react";
|
|
68
|
+
import Tabs from "@ttdk-design-system/library-react/web/tabs";
|
|
69
|
+
import "@ttdk-design-system/library-react/web/tabs/tabs.css";
|
|
70
|
+
|
|
71
|
+
export function Example() {
|
|
72
|
+
const [activeTab, setActiveTab] = useState("overview");
|
|
73
|
+
|
|
74
|
+
const tabItems = [
|
|
75
|
+
{ label: "Ringkasan", value: "overview", badge: 4 },
|
|
76
|
+
{ label: "Data Pasien", value: "patients" },
|
|
77
|
+
{ label: "Integrasi API", value: "api" },
|
|
78
|
+
{ label: "Pengaturan", value: "settings", disabled: true }
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>
|
|
83
|
+
<Tabs
|
|
84
|
+
items={tabItems}
|
|
85
|
+
value={activeTab}
|
|
86
|
+
variant="line"
|
|
87
|
+
onChange={(val) => setActiveTab(val)}
|
|
88
|
+
/>
|
|
89
|
+
<div>Konten aktif tab: {activeTab}</div>
|
|
90
|
+
</div>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Vue 3 Example
|
|
96
|
+
```vue
|
|
97
|
+
<script setup lang="ts">
|
|
98
|
+
import { ref } from "vue";
|
|
99
|
+
import Tabs from "@ttdk-design-system/library-vue/web/tabs";
|
|
100
|
+
import "@ttdk-design-system/library-vue/web/tabs/tabs.css";
|
|
101
|
+
|
|
102
|
+
const activeTab = ref("overview");
|
|
103
|
+
|
|
104
|
+
const tabItems = [
|
|
105
|
+
{ label: "Ringkasan", value: "overview", badge: 4 },
|
|
106
|
+
{ label: "Data Pasien", value: "patients" },
|
|
107
|
+
{ label: "Integrasi API", value: "api" },
|
|
108
|
+
{ label: "Pengaturan", value: "settings", disabled: true }
|
|
109
|
+
];
|
|
110
|
+
</script>
|
|
111
|
+
|
|
112
|
+
<template>
|
|
113
|
+
<div style="display: flex; flex-direction: column; gap: 16px;">
|
|
114
|
+
<Tabs
|
|
115
|
+
:items="tabItems"
|
|
116
|
+
:value="activeTab"
|
|
117
|
+
variant="line"
|
|
118
|
+
@change="(val: string) => (activeTab = val)"
|
|
119
|
+
/>
|
|
120
|
+
<div>Konten aktif tab: {{ activeTab }}</div>
|
|
121
|
+
</div>
|
|
122
|
+
</template>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Svelte Example
|
|
126
|
+
```svelte
|
|
127
|
+
<script lang="ts">
|
|
128
|
+
import Tabs from "@ttdk-design-system/library-svelte/web/tabs";
|
|
129
|
+
import "@ttdk-design-system/library-svelte/web/tabs/tabs.css";
|
|
130
|
+
|
|
131
|
+
let activeTab = "overview";
|
|
132
|
+
|
|
133
|
+
const tabItems = [
|
|
134
|
+
{ label: "Ringkasan", value: "overview", badge: 4 },
|
|
135
|
+
{ label: "Data Pasien", value: "patients" },
|
|
136
|
+
{ label: "Integrasi API", value: "api" },
|
|
137
|
+
{ label: "Pengaturan", value: "settings", disabled: true }
|
|
138
|
+
];
|
|
139
|
+
</script>
|
|
140
|
+
|
|
141
|
+
<div style="display: flex; flex-direction: column; gap: 16px;">
|
|
142
|
+
<Tabs
|
|
143
|
+
items={tabItems}
|
|
144
|
+
value={activeTab}
|
|
145
|
+
variant="line"
|
|
146
|
+
on:change={(e) => (activeTab = e.detail)}
|
|
147
|
+
/>
|
|
148
|
+
<div>Konten aktif tab: {activeTab}</div>
|
|
149
|
+
</div>
|
|
150
|
+
```
|
|
@@ -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,164 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
:class="['ttdk-tabs', className, $attrs.class]"
|
|
4
|
+
:data-orientation="orientation || 'horizontal'"
|
|
5
|
+
:data-variant="variant || 'line'"
|
|
6
|
+
:data-size="size || 'md'"
|
|
7
|
+
:data-fitted="fitted ? 'true' : 'false'"
|
|
8
|
+
:data-rounded="rounded || 'md'"
|
|
9
|
+
:style="{
|
|
10
|
+
...buildResponsiveVars(ui, {
|
|
11
|
+
activeColor: '--ttdk-ui-tabs-active-color',
|
|
12
|
+
indicatorColor: '--ttdk-ui-tabs-indicator-color',
|
|
13
|
+
inactiveColor: '--ttdk-ui-tabs-inactive-color',
|
|
14
|
+
bg: '--ttdk-ui-tabs-bg',
|
|
15
|
+
borderColor: '--ttdk-ui-tabs-border-color',
|
|
16
|
+
textSize: '--ttdk-ui-tabs-font-size',
|
|
17
|
+
fontSize: '--ttdk-ui-tabs-font-size',
|
|
18
|
+
padding: '--ttdk-ui-tabs-padding',
|
|
19
|
+
gap: '--ttdk-ui-tabs-gap',
|
|
20
|
+
}),
|
|
21
|
+
}"
|
|
22
|
+
>
|
|
23
|
+
<div
|
|
24
|
+
class="ttdk-tabs-list"
|
|
25
|
+
role="tablist"
|
|
26
|
+
:aria-orientation="orientation || 'horizontal'"
|
|
27
|
+
>
|
|
28
|
+
<template :key="item.value" v-for="item in items">
|
|
29
|
+
<button
|
|
30
|
+
type="button"
|
|
31
|
+
role="tab"
|
|
32
|
+
class="ttdk-tab-item"
|
|
33
|
+
:data-state="getCurrentActive() === item.value ? 'active' : 'inactive'"
|
|
34
|
+
:data-disabled="item.disabled ? 'true' : 'false'"
|
|
35
|
+
:disabled="item.disabled"
|
|
36
|
+
:aria-selected="getCurrentActive() === item.value"
|
|
37
|
+
@click="handleTabClick(item.value, item.disabled)"
|
|
38
|
+
>
|
|
39
|
+
<template v-if="item.icon">
|
|
40
|
+
<span class="ttdk-tab-icon">{{ item.icon }}</span>
|
|
41
|
+
</template>
|
|
42
|
+
|
|
43
|
+
<span class="ttdk-tab-label">{{ item.label }}</span>
|
|
44
|
+
<template v-if="item.badge !== undefined && item.badge !== null">
|
|
45
|
+
<span class="ttdk-tab-badge">{{ item.badge }}</span>
|
|
46
|
+
</template>
|
|
47
|
+
|
|
48
|
+
<template
|
|
49
|
+
v-if="
|
|
50
|
+
getCurrentActive() === item.value &&
|
|
51
|
+
(!variant || variant === 'line')
|
|
52
|
+
"
|
|
53
|
+
>
|
|
54
|
+
<span class="ttdk-tab-indicator"></span>
|
|
55
|
+
</template>
|
|
56
|
+
</button>
|
|
57
|
+
</template>
|
|
58
|
+
</div>
|
|
59
|
+
<div class="ttdk-tabs-panels">
|
|
60
|
+
<template v-if="getActiveItem() && getActiveItem()?.content">
|
|
61
|
+
<div class="ttdk-tab-panel" role="tabpanel">
|
|
62
|
+
{{ getActiveItem()?.content }}
|
|
63
|
+
</div>
|
|
64
|
+
</template>
|
|
65
|
+
|
|
66
|
+
<template v-if="$slots.default">
|
|
67
|
+
<div class="ttdk-tab-children"><slot /></div>
|
|
68
|
+
</template>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
</template>
|
|
72
|
+
|
|
73
|
+
<script setup lang="ts">
|
|
74
|
+
import { ref } from "vue";
|
|
75
|
+
import { buildResponsiveVars, type ResponsiveBreakpoints } from "../../core/types";
|
|
76
|
+
|
|
77
|
+
export interface TabItem {
|
|
78
|
+
label: string;
|
|
79
|
+
value: string;
|
|
80
|
+
icon?: any;
|
|
81
|
+
badge?: string | number;
|
|
82
|
+
disabled?: boolean;
|
|
83
|
+
content?: any;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface TabsUiBaseProps {
|
|
87
|
+
activeColor?: string;
|
|
88
|
+
indicatorColor?: string;
|
|
89
|
+
inactiveColor?: string;
|
|
90
|
+
bg?: string;
|
|
91
|
+
borderColor?: string;
|
|
92
|
+
textSize?: string;
|
|
93
|
+
fontSize?: string;
|
|
94
|
+
padding?: string;
|
|
95
|
+
gap?: string;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface TabsUiProps extends TabsUiBaseProps, ResponsiveBreakpoints<TabsUiBaseProps> {
|
|
99
|
+
sm?: Partial<TabsUiBaseProps>;
|
|
100
|
+
lt_sm?: Partial<TabsUiBaseProps>;
|
|
101
|
+
md?: Partial<TabsUiBaseProps>;
|
|
102
|
+
lt_md?: Partial<TabsUiBaseProps>;
|
|
103
|
+
lg?: Partial<TabsUiBaseProps>;
|
|
104
|
+
lt_lg?: Partial<TabsUiBaseProps>;
|
|
105
|
+
xl?: Partial<TabsUiBaseProps>;
|
|
106
|
+
lt_xl?: Partial<TabsUiBaseProps>;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export interface TabsProps {
|
|
110
|
+
items: TabItem[];
|
|
111
|
+
modelValue?: string;
|
|
112
|
+
value?: string;
|
|
113
|
+
defaultValue?: string;
|
|
114
|
+
orientation?: "horizontal" | "vertical";
|
|
115
|
+
variant?: "line" | "pills";
|
|
116
|
+
size?: "sm" | "md" | "lg";
|
|
117
|
+
fitted?: boolean;
|
|
118
|
+
rounded?: "none" | "sm" | "md" | "lg" | "full";
|
|
119
|
+
ui?: TabsUiProps;
|
|
120
|
+
className?: string;
|
|
121
|
+
class?: string;
|
|
122
|
+
onChange?: (value: string) => void;
|
|
123
|
+
children?: any;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const props = defineProps<TabsProps>();
|
|
127
|
+
const emit = defineEmits<{
|
|
128
|
+
(e: "update:modelValue", value: string): void;
|
|
129
|
+
(e: "update:value", value: string): void;
|
|
130
|
+
(e: "change", value: string): void;
|
|
131
|
+
}>();
|
|
132
|
+
|
|
133
|
+
const internalActive = ref(
|
|
134
|
+
props.modelValue ||
|
|
135
|
+
props.value ||
|
|
136
|
+
props.defaultValue ||
|
|
137
|
+
(props.items && props.items[0]?.value) ||
|
|
138
|
+
""
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
function getCurrentActive() {
|
|
142
|
+
if (props.modelValue !== undefined) return props.modelValue;
|
|
143
|
+
if (props.value !== undefined) return props.value;
|
|
144
|
+
return internalActive.value;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function handleTabClick(itemValue: string, disabled?: boolean) {
|
|
148
|
+
if (disabled) return;
|
|
149
|
+
internalActive.value = itemValue;
|
|
150
|
+
emit("update:modelValue", itemValue);
|
|
151
|
+
emit("update:value", itemValue);
|
|
152
|
+
emit("change", itemValue);
|
|
153
|
+
if (props.onChange) {
|
|
154
|
+
props.onChange(itemValue);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function getActiveItem() {
|
|
159
|
+
const current = getCurrentActive();
|
|
160
|
+
return props.items
|
|
161
|
+
? props.items.find((item: any) => item.value === current)
|
|
162
|
+
: null;
|
|
163
|
+
}
|
|
164
|
+
</script>
|