@datametria/vue-components 2.3.1 → 2.4.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.
Files changed (128) hide show
  1. package/README.md +625 -594
  2. package/dist/index.es.js +1962 -1887
  3. package/dist/index.umd.js +6 -6
  4. package/dist/src/components/DatametriaForm.vue.d.ts +1 -1
  5. package/dist/src/components/DatametriaInput.vue.d.ts +9 -1
  6. package/dist/src/components/DatametriaSelect.vue.d.ts +10 -1
  7. package/dist/vue-components.css +1 -1
  8. package/package.json +103 -102
  9. package/src/components/DatametriaAlert.vue +151 -133
  10. package/src/components/DatametriaAutocomplete.vue +250 -229
  11. package/src/components/DatametriaAvatar.vue +256 -238
  12. package/src/components/DatametriaBadge.vue +101 -96
  13. package/src/components/DatametriaBreadcrumb.vue +132 -128
  14. package/src/components/DatametriaButton.vue +191 -173
  15. package/src/components/DatametriaCard.vue +84 -66
  16. package/src/components/DatametriaCheckbox.vue +197 -193
  17. package/src/components/DatametriaCheckboxGroup.vue +56 -38
  18. package/src/components/DatametriaChip.vue +159 -141
  19. package/src/components/DatametriaContainer.vue +70 -52
  20. package/src/components/DatametriaDataTable.vue +318 -300
  21. package/src/components/DatametriaDatePicker.vue +396 -378
  22. package/src/components/DatametriaDialog.vue +297 -293
  23. package/src/components/DatametriaDivider.vue +105 -98
  24. package/src/components/DatametriaDropdown.vue +356 -350
  25. package/src/components/DatametriaEmpty.vue +155 -151
  26. package/src/components/DatametriaFileUpload.vue +413 -395
  27. package/src/components/DatametriaFloatingBar.vue +144 -126
  28. package/src/components/DatametriaForm.vue +174 -156
  29. package/src/components/DatametriaFormItem.vue +183 -179
  30. package/src/components/DatametriaGrid.vue +55 -37
  31. package/src/components/DatametriaInput.vue +314 -263
  32. package/src/components/DatametriaMenu.vue +618 -600
  33. package/src/components/DatametriaModal.vue +147 -129
  34. package/src/components/DatametriaNavbar.vue +277 -223
  35. package/src/components/DatametriaPagination.vue +375 -371
  36. package/src/components/DatametriaPasswordInput.vue +444 -426
  37. package/src/components/DatametriaPopconfirm.vue +240 -234
  38. package/src/components/DatametriaProgress.vue +228 -224
  39. package/src/components/DatametriaRadio.vue +151 -147
  40. package/src/components/DatametriaRadioGroup.vue +55 -37
  41. package/src/components/DatametriaResult.vue +135 -131
  42. package/src/components/DatametriaSelect.vue +311 -211
  43. package/src/components/DatametriaSidebar.vue +294 -222
  44. package/src/components/DatametriaSkeleton.vue +257 -234
  45. package/src/components/DatametriaSlider.vue +409 -391
  46. package/src/components/DatametriaSortableTable.vue +820 -802
  47. package/src/components/DatametriaSpinner.vue +114 -110
  48. package/src/components/DatametriaSteps.vue +318 -312
  49. package/src/components/DatametriaSwitch.vue +146 -142
  50. package/src/components/DatametriaTabPane.vue +94 -76
  51. package/src/components/DatametriaTable.vue +118 -100
  52. package/src/components/DatametriaTabs.vue +315 -297
  53. package/src/components/DatametriaTextarea.vue +213 -195
  54. package/src/components/DatametriaTimePicker.vue +317 -299
  55. package/src/components/DatametriaToast.vue +176 -176
  56. package/src/components/DatametriaTooltip.vue +421 -400
  57. package/src/components/DatametriaTree.vue +126 -122
  58. package/src/components/DatametriaTreeNode.vue +176 -172
  59. package/src/components/DatametriaUpload.vue +379 -361
  60. package/src/components/__tests__/DatametriaAlert.test.js +35 -35
  61. package/src/components/__tests__/DatametriaAlert.test.ts +190 -190
  62. package/src/components/__tests__/DatametriaAvatar.test.ts +151 -151
  63. package/src/components/__tests__/DatametriaBadge.test.js +29 -29
  64. package/src/components/__tests__/DatametriaBadge.test.ts +167 -167
  65. package/src/components/__tests__/DatametriaBreadcrumb.test.ts +187 -0
  66. package/src/components/__tests__/DatametriaButton.test.js +30 -30
  67. package/src/components/__tests__/DatametriaButton.test.ts +283 -283
  68. package/src/components/__tests__/DatametriaCard.test.ts +201 -201
  69. package/src/components/__tests__/DatametriaCheckbox.test.ts +204 -0
  70. package/src/components/__tests__/DatametriaChip.test.js +38 -38
  71. package/src/components/__tests__/DatametriaContainer.test.ts +52 -52
  72. package/src/components/__tests__/DatametriaDialog.test.ts +338 -0
  73. package/src/components/__tests__/DatametriaDivider.test.ts +54 -54
  74. package/src/components/__tests__/DatametriaDropdown.test.ts +357 -0
  75. package/src/components/__tests__/DatametriaEmpty.test.ts +261 -0
  76. package/src/components/__tests__/DatametriaFileUpload.test.ts +290 -290
  77. package/src/components/__tests__/DatametriaFloatingBar.test.ts +137 -137
  78. package/src/components/__tests__/DatametriaForm.test.ts +96 -0
  79. package/src/components/__tests__/DatametriaFormItem.test.ts +58 -0
  80. package/src/components/__tests__/DatametriaGrid.test.ts +31 -31
  81. package/src/components/__tests__/DatametriaInput.test.ts +72 -72
  82. package/src/components/__tests__/DatametriaMenu.test.ts +366 -366
  83. package/src/components/__tests__/DatametriaModal.test.ts +86 -86
  84. package/src/components/__tests__/DatametriaNavbar.test.js +48 -48
  85. package/src/components/__tests__/DatametriaNavbar.test.ts +203 -203
  86. package/src/components/__tests__/DatametriaPasswordInput.test.js +305 -305
  87. package/src/components/__tests__/DatametriaRadio.test.ts +195 -0
  88. package/src/components/__tests__/DatametriaSelect.test.ts +77 -77
  89. package/src/components/__tests__/DatametriaSidebar.test.ts +169 -169
  90. package/src/components/__tests__/DatametriaSlider.test.ts +261 -261
  91. package/src/components/__tests__/DatametriaSortableTable.test.js +168 -168
  92. package/src/components/__tests__/DatametriaSpinner.test.ts +156 -156
  93. package/src/components/__tests__/DatametriaSteps.test.ts +211 -0
  94. package/src/components/__tests__/DatametriaSwitch.test.ts +129 -0
  95. package/src/components/__tests__/DatametriaTabPane.test.ts +205 -0
  96. package/src/components/__tests__/DatametriaTable.test.ts +97 -97
  97. package/src/components/__tests__/DatametriaTabs.test.ts +232 -232
  98. package/src/components/__tests__/DatametriaToast.test.js +48 -48
  99. package/src/components/__tests__/DatametriaToast.test.ts +99 -99
  100. package/src/components/__tests__/DatametriaTree.test.ts +376 -0
  101. package/src/components/__tests__/index.test.ts +48 -0
  102. package/src/composables/useAccessibilityScale.ts +94 -94
  103. package/src/composables/useBreakpoints.ts +82 -82
  104. package/src/composables/useHapticFeedback.ts +439 -439
  105. package/src/composables/useRipple.ts +218 -218
  106. package/src/composables/useTheme.ts +5 -1
  107. package/src/index.ts +84 -84
  108. package/src/stories/Variants.stories.js +95 -95
  109. package/src/styles/design-tokens.css +623 -623
  110. package/src/theme/ThemeProvider.vue +96 -96
  111. package/src/theme/__tests__/ThemeProvider.test.ts +208 -208
  112. package/src/theme/__tests__/constants.test.ts +31 -31
  113. package/src/theme/__tests__/presets.test.ts +166 -166
  114. package/src/theme/__tests__/tokens.test.ts +155 -155
  115. package/src/theme/__tests__/types.test.ts +153 -153
  116. package/src/theme/__tests__/useTheme.test.ts +146 -146
  117. package/src/theme/constants.ts +14 -14
  118. package/src/theme/index.ts +12 -12
  119. package/src/theme/presets/datametria.ts +94 -94
  120. package/src/theme/presets/default.ts +94 -94
  121. package/src/theme/presets/index.ts +8 -8
  122. package/src/theme/tokens/colors.ts +28 -28
  123. package/src/theme/tokens/index.ts +47 -47
  124. package/src/theme/tokens/spacing.ts +21 -21
  125. package/src/theme/tokens/typography.ts +35 -35
  126. package/src/theme/types.ts +111 -111
  127. package/src/theme/useTheme.ts +28 -28
  128. package/src/types/index.ts +55 -55
@@ -1,301 +1,319 @@
1
- <template>
2
- <div :class="tabsClasses">
3
- <div class="dm-tabs__header" role="tablist" :aria-label="ariaLabel" :aria-orientation="orientation">
4
- <button
5
- v-for="(tab, index) in tabs"
6
- :key="index"
7
- :id="`tab-${index}`"
8
- class="dm-tabs__tab"
9
- :class="{ 'dm-tabs__tab--active': activeTab === index, 'dm-tabs__tab--disabled': typeof tab === 'object' && 'disabled' in tab && tab.disabled }"
10
- role="tab"
11
- :aria-selected="activeTab === index"
12
- :aria-controls="`panel-${index}`"
13
- :aria-disabled="typeof tab === 'object' && 'disabled' in tab ? tab.disabled : false"
14
- :tabindex="activeTab === index ? 0 : -1"
15
- :disabled="typeof tab === 'object' && 'disabled' in tab ? tab.disabled : false"
16
- @click="selectTab(index)"
17
- @keydown="handleKeydown($event, index)"
18
- >
19
- <span v-if="typeof tab === 'object' && 'icon' in tab && tab.icon" class="dm-tabs__icon">{{ tab.icon }}</span>
20
- <span class="dm-tabs__label">{{ typeof tab === 'string' ? tab : tab.label }}</span>
21
- <span v-if="typeof tab === 'object' && 'badge' in tab && tab.badge" class="dm-tabs__badge">{{ tab.badge }}</span>
22
- </button>
23
- <div
24
- v-if="showIndicator"
25
- class="dm-tabs__indicator"
26
- :style="indicatorStyle"
27
- ></div>
28
- </div>
29
- <div class="dm-tabs__panels">
30
- <div
31
- v-for="(_, index) in tabs"
32
- :key="index"
33
- :id="`panel-${index}`"
34
- class="dm-tabs__panel"
35
- :class="{ 'dm-tabs__panel--active': activeTab === index }"
36
- role="tabpanel"
37
- :aria-labelledby="`tab-${index}`"
38
- :hidden="activeTab !== index"
39
- >
40
- <slot :name="`panel-${index}`"></slot>
41
- </div>
42
- </div>
43
- </div>
44
- </template>
45
-
46
- <script setup lang="ts">
47
- import { ref, watch, computed } from 'vue'
48
-
49
- interface Tab {
50
- label: string
51
- icon?: string
52
- badge?: string | number
53
- disabled?: boolean
54
- }
55
-
56
- interface Props {
57
- tabs: (string | Tab)[]
58
- modelValue?: number
59
- variant?: 'default' | 'pills' | 'underline'
60
- orientation?: 'horizontal' | 'vertical'
61
- showIndicator?: boolean
62
- ariaLabel?: string
63
- }
64
-
65
- const props = withDefaults(defineProps<Props>(), {
66
- modelValue: 0,
67
- variant: 'default',
68
- orientation: 'horizontal',
69
- showIndicator: true,
70
- ariaLabel: 'Tabs'
71
- })
72
-
73
- const emit = defineEmits<{
74
- 'update:modelValue': [index: number]
75
- change: [index: number]
76
- }>()
77
-
78
- const activeTab = ref(props.modelValue)
79
-
80
- watch(() => props.modelValue, (newValue) => {
81
- activeTab.value = newValue
82
- })
83
-
84
- const tabsClasses = computed(() => [
85
- 'dm-tabs',
86
- `dm-tabs--${props.variant}`,
87
- `dm-tabs--${props.orientation}`
88
- ])
89
-
90
- const indicatorStyle = computed(() => {
91
- const count = props.tabs.length
92
- const position = props.orientation === 'horizontal'
93
- ? `translateX(${activeTab.value * 100}%)`
94
- : `translateY(${activeTab.value * 100}%)`
95
-
96
- return {
97
- transform: position,
98
- width: props.orientation === 'horizontal' ? `${100 / count}%` : '100%',
99
- height: props.orientation === 'vertical' ? `${100 / count}%` : '2px'
100
- }
101
- })
102
-
103
- const selectTab = (index: number) => {
104
- const tab = props.tabs[index]
105
- const isDisabled = typeof tab === 'object' && 'disabled' in tab && tab.disabled
106
-
107
- if (isDisabled) return
108
-
109
- activeTab.value = index
110
- emit('update:modelValue', index)
111
- emit('change', index)
112
- }
113
-
114
- const handleKeydown = (event: KeyboardEvent, index: number) => {
115
- let newIndex = index
116
-
117
- const isHorizontal = props.orientation === 'horizontal'
118
-
119
- switch (event.key) {
120
- case 'ArrowLeft':
121
- if (!isHorizontal) return
122
- event.preventDefault()
123
- newIndex = index > 0 ? index - 1 : props.tabs.length - 1
124
- break
125
- case 'ArrowRight':
126
- if (!isHorizontal) return
127
- event.preventDefault()
128
- newIndex = index < props.tabs.length - 1 ? index + 1 : 0
129
- break
130
- case 'ArrowUp':
131
- if (isHorizontal) return
132
- event.preventDefault()
133
- newIndex = index > 0 ? index - 1 : props.tabs.length - 1
134
- break
135
- case 'ArrowDown':
136
- if (isHorizontal) return
137
- event.preventDefault()
138
- newIndex = index < props.tabs.length - 1 ? index + 1 : 0
139
- break
140
- case 'Home':
141
- event.preventDefault()
142
- newIndex = 0
143
- break
144
- case 'End':
145
- event.preventDefault()
146
- newIndex = props.tabs.length - 1
147
- break
148
- default:
149
- return
1
+ <template>
2
+ <div :class="tabsClasses">
3
+ <div class="dm-tabs__header" role="tablist" :aria-label="ariaLabel" :aria-orientation="orientation">
4
+ <button
5
+ v-for="(tab, index) in tabs"
6
+ :key="index"
7
+ :id="`tab-${index}`"
8
+ class="dm-tabs__tab"
9
+ :class="{ 'dm-tabs__tab--active': activeTab === index, 'dm-tabs__tab--disabled': typeof tab === 'object' && 'disabled' in tab && tab.disabled }"
10
+ role="tab"
11
+ :aria-selected="activeTab === index"
12
+ :aria-controls="`panel-${index}`"
13
+ :aria-disabled="typeof tab === 'object' && 'disabled' in tab ? tab.disabled : false"
14
+ :tabindex="activeTab === index ? 0 : -1"
15
+ :disabled="typeof tab === 'object' && 'disabled' in tab ? tab.disabled : false"
16
+ @click="selectTab(index)"
17
+ @keydown="handleKeydown($event, index)"
18
+ >
19
+ <span v-if="typeof tab === 'object' && 'icon' in tab && tab.icon" class="dm-tabs__icon">{{ tab.icon }}</span>
20
+ <span class="dm-tabs__label">{{ typeof tab === 'string' ? tab : tab.label }}</span>
21
+ <span v-if="typeof tab === 'object' && 'badge' in tab && tab.badge" class="dm-tabs__badge">{{ tab.badge }}</span>
22
+ </button>
23
+ <div
24
+ v-if="showIndicator"
25
+ class="dm-tabs__indicator"
26
+ :style="indicatorStyle"
27
+ ></div>
28
+ </div>
29
+ <div class="dm-tabs__panels">
30
+ <div
31
+ v-for="(_, index) in tabs"
32
+ :key="index"
33
+ :id="`panel-${index}`"
34
+ class="dm-tabs__panel"
35
+ :class="{ 'dm-tabs__panel--active': activeTab === index }"
36
+ role="tabpanel"
37
+ :aria-labelledby="`tab-${index}`"
38
+ :hidden="activeTab !== index"
39
+ >
40
+ <slot :name="`panel-${index}`"></slot>
41
+ </div>
42
+ </div>
43
+ </div>
44
+ </template>
45
+
46
+ <script setup lang="ts">
47
+ import { ref, watch, computed } from 'vue'
48
+
49
+ interface Tab {
50
+ label: string
51
+ icon?: string
52
+ badge?: string | number
53
+ disabled?: boolean
54
+ }
55
+
56
+ interface Props {
57
+ tabs: (string | Tab)[]
58
+ modelValue?: number
59
+ variant?: 'default' | 'pills' | 'underline'
60
+ orientation?: 'horizontal' | 'vertical'
61
+ showIndicator?: boolean
62
+ ariaLabel?: string
63
+ }
64
+
65
+ const props = withDefaults(defineProps<Props>(), {
66
+ modelValue: 0,
67
+ variant: 'default',
68
+ orientation: 'horizontal',
69
+ showIndicator: true,
70
+ ariaLabel: 'Tabs'
71
+ })
72
+
73
+ const emit = defineEmits<{
74
+ 'update:modelValue': [index: number]
75
+ change: [index: number]
76
+ }>()
77
+
78
+ const activeTab = ref(props.modelValue)
79
+
80
+ watch(() => props.modelValue, (newValue) => {
81
+ activeTab.value = newValue
82
+ })
83
+
84
+ const tabsClasses = computed(() => [
85
+ 'dm-tabs',
86
+ `dm-tabs--${props.variant}`,
87
+ `dm-tabs--${props.orientation}`
88
+ ])
89
+
90
+ const indicatorStyle = computed(() => {
91
+ const count = props.tabs.length
92
+ const position = props.orientation === 'horizontal'
93
+ ? `translateX(${activeTab.value * 100}%)`
94
+ : `translateY(${activeTab.value * 100}%)`
95
+
96
+ return {
97
+ transform: position,
98
+ width: props.orientation === 'horizontal' ? `${100 / count}%` : '100%',
99
+ height: props.orientation === 'vertical' ? `${100 / count}%` : '2px'
100
+ }
101
+ })
102
+
103
+ const selectTab = (index: number) => {
104
+ const tab = props.tabs[index]
105
+ const isDisabled = typeof tab === 'object' && 'disabled' in tab && tab.disabled
106
+
107
+ if (isDisabled) return
108
+
109
+ activeTab.value = index
110
+ emit('update:modelValue', index)
111
+ emit('change', index)
112
+ }
113
+
114
+ const handleKeydown = (event: KeyboardEvent, index: number) => {
115
+ let newIndex = index
116
+
117
+ const isHorizontal = props.orientation === 'horizontal'
118
+
119
+ switch (event.key) {
120
+ case 'ArrowLeft':
121
+ if (!isHorizontal) return
122
+ event.preventDefault()
123
+ newIndex = index > 0 ? index - 1 : props.tabs.length - 1
124
+ break
125
+ case 'ArrowRight':
126
+ if (!isHorizontal) return
127
+ event.preventDefault()
128
+ newIndex = index < props.tabs.length - 1 ? index + 1 : 0
129
+ break
130
+ case 'ArrowUp':
131
+ if (isHorizontal) return
132
+ event.preventDefault()
133
+ newIndex = index > 0 ? index - 1 : props.tabs.length - 1
134
+ break
135
+ case 'ArrowDown':
136
+ if (isHorizontal) return
137
+ event.preventDefault()
138
+ newIndex = index < props.tabs.length - 1 ? index + 1 : 0
139
+ break
140
+ case 'Home':
141
+ event.preventDefault()
142
+ newIndex = 0
143
+ break
144
+ case 'End':
145
+ event.preventDefault()
146
+ newIndex = props.tabs.length - 1
147
+ break
148
+ default:
149
+ return
150
+ }
151
+
152
+ selectTab(newIndex)
153
+ document.getElementById(`tab-${newIndex}`)?.focus()
154
+ }
155
+ </script>
156
+
157
+ <style scoped>
158
+ .dm-tabs {
159
+ display: flex;
160
+ flex-direction: column;
161
+ }
162
+
163
+ .dm-tabs--vertical {
164
+ flex-direction: row;
165
+ }
166
+
167
+ .dm-tabs--vertical .dm-tabs__header {
168
+ flex-direction: column;
169
+ border-bottom: none;
170
+ border-right: 2px solid var(--dm-neutral-200, #e5e7eb);
171
+ }
172
+
173
+ .dm-tabs--vertical .dm-tabs__tab {
174
+ text-align: left;
175
+ }
176
+
177
+ .dm-tabs__header {
178
+ display: flex;
179
+ position: relative;
180
+ border-bottom: 2px solid var(--dm-neutral-200, #e5e7eb);
181
+ overflow-x: auto;
182
+ scrollbar-width: none;
183
+ }
184
+
185
+ .dm-tabs__header::-webkit-scrollbar {
186
+ display: none;
187
+ }
188
+
189
+ .dm-tabs__tab {
190
+ flex: 1;
191
+ min-width: max-content;
192
+ padding: var(--dm-spacing-3, 0.75rem) var(--dm-spacing-4, 1rem);
193
+ border: none;
194
+ background: transparent;
195
+ color: var(--dm-neutral-600, #6b7280);
196
+ font-size: 1rem;
197
+ font-weight: 500;
198
+ cursor: pointer;
199
+ transition: all 0.2s ease;
200
+ position: relative;
201
+ white-space: nowrap;
202
+ display: flex;
203
+ align-items: center;
204
+ gap: var(--dm-spacing-2, 0.5rem);
205
+ }
206
+
207
+ .dm-tabs__tab--disabled {
208
+ opacity: 0.5;
209
+ cursor: not-allowed;
210
+ }
211
+
212
+ .dm-tabs__icon {
213
+ font-size: 1.25rem;
214
+ }
215
+
216
+ .dm-tabs__badge {
217
+ display: inline-flex;
218
+ align-items: center;
219
+ justify-content: center;
220
+ min-width: 20px;
221
+ height: 20px;
222
+ padding: 0 6px;
223
+ background: var(--dm-primary, #0072CE);
224
+ color: white;
225
+ font-size: 0.75rem;
226
+ font-weight: 600;
227
+ border-radius: 10px;
228
+ }
229
+
230
+ .dm-tabs__tab:hover:not(:disabled) {
231
+ color: var(--dm-neutral-900, #111827);
232
+ }
233
+
234
+ .dm-tabs__tab:focus-visible {
235
+ outline: 2px solid var(--dm-primary, #0072CE);
236
+ outline-offset: -2px;
237
+ border-radius: 4px;
238
+ }
239
+
240
+ .dm-tabs__tab--active {
241
+ color: var(--dm-primary, #0072CE);
242
+ }
243
+
244
+ .dm-tabs__indicator {
245
+ position: absolute;
246
+ bottom: -2px;
247
+ left: 0;
248
+ background: var(--dm-primary, #0072CE);
249
+ transition: transform 0.3s ease;
250
+ }
251
+
252
+ .dm-tabs--vertical .dm-tabs__indicator {
253
+ bottom: auto;
254
+ left: auto;
255
+ right: -2px;
256
+ top: 0;
257
+ width: 2px;
258
+ }
259
+
260
+ .dm-tabs--pills .dm-tabs__header {
261
+ border-bottom: none;
262
+ gap: var(--dm-spacing-2, 0.5rem);
263
+ }
264
+
265
+ .dm-tabs--pills .dm-tabs__tab {
266
+ border-radius: var(--dm-radius-md, 0.375rem);
267
+ }
268
+
269
+ .dm-tabs--pills .dm-tabs__tab--active {
270
+ background: var(--dm-primary, #0072CE);
271
+ color: white;
272
+ }
273
+
274
+ .dm-tabs--pills .dm-tabs__indicator {
275
+ display: none;
276
+ }
277
+
278
+ .dm-tabs__panels {
279
+ padding: var(--dm-spacing-4, 1rem);
280
+ flex: 1;
281
+ }
282
+
283
+ .dm-tabs__panel {
284
+ display: none;
285
+ }
286
+
287
+ .dm-tabs__panel--active {
288
+ display: block;
289
+ }
290
+
291
+ @media (max-width: 768px) {
292
+ .dm-tabs__tab {
293
+ padding: var(--dm-spacing-2, 0.5rem) var(--dm-spacing-3, 0.75rem);
294
+ font-size: 0.875rem;
295
+ }
296
+
297
+ .dm-tabs__icon {
298
+ font-size: 1rem;
299
+ }
300
+ }
301
+
302
+ /* Dark Mode Support - Hybrid Approach */
303
+
304
+ /* Fallback automático (sem JS) */
305
+ @media (prefers-color-scheme: dark) {
306
+ .tabsClasses {
307
+ background: var(--dm-bg-color-dark, #1e1e1e);
308
+ color: var(--dm-text-primary-dark, #e0e0e0);
309
+ border-color: var(--dm-border-color-dark, #404040);
150
310
  }
151
-
152
- selectTab(newIndex)
153
- document.getElementById(`tab-${newIndex}`)?.focus()
154
- }
155
- </script>
156
-
157
- <style scoped>
158
- .dm-tabs {
159
- display: flex;
160
- flex-direction: column;
161
- }
162
-
163
- .dm-tabs--vertical {
164
- flex-direction: row;
165
- }
166
-
167
- .dm-tabs--vertical .dm-tabs__header {
168
- flex-direction: column;
169
- border-bottom: none;
170
- border-right: 2px solid var(--dm-neutral-200, #e5e7eb);
171
- }
172
-
173
- .dm-tabs--vertical .dm-tabs__tab {
174
- text-align: left;
175
- }
176
-
177
- .dm-tabs__header {
178
- display: flex;
179
- position: relative;
180
- border-bottom: 2px solid var(--dm-neutral-200, #e5e7eb);
181
- overflow-x: auto;
182
- scrollbar-width: none;
183
- }
184
-
185
- .dm-tabs__header::-webkit-scrollbar {
186
- display: none;
187
- }
188
-
189
- .dm-tabs__tab {
190
- flex: 1;
191
- min-width: max-content;
192
- padding: var(--dm-spacing-3, 0.75rem) var(--dm-spacing-4, 1rem);
193
- border: none;
194
- background: transparent;
195
- color: var(--dm-neutral-600, #6b7280);
196
- font-size: 1rem;
197
- font-weight: 500;
198
- cursor: pointer;
199
- transition: all 0.2s ease;
200
- position: relative;
201
- white-space: nowrap;
202
- display: flex;
203
- align-items: center;
204
- gap: var(--dm-spacing-2, 0.5rem);
205
311
  }
206
312
 
207
- .dm-tabs__tab--disabled {
208
- opacity: 0.5;
209
- cursor: not-allowed;
210
- }
211
-
212
- .dm-tabs__icon {
213
- font-size: 1.25rem;
214
- }
215
-
216
- .dm-tabs__badge {
217
- display: inline-flex;
218
- align-items: center;
219
- justify-content: center;
220
- min-width: 20px;
221
- height: 20px;
222
- padding: 0 6px;
223
- background: var(--dm-primary, #0072CE);
224
- color: white;
225
- font-size: 0.75rem;
226
- font-weight: 600;
227
- border-radius: 10px;
228
- }
229
-
230
- .dm-tabs__tab:hover:not(:disabled) {
231
- color: var(--dm-neutral-900, #111827);
232
- }
233
-
234
- .dm-tabs__tab:focus-visible {
235
- outline: 2px solid var(--dm-primary, #0072CE);
236
- outline-offset: -2px;
237
- border-radius: 4px;
238
- }
239
-
240
- .dm-tabs__tab--active {
241
- color: var(--dm-primary, #0072CE);
242
- }
243
-
244
- .dm-tabs__indicator {
245
- position: absolute;
246
- bottom: -2px;
247
- left: 0;
248
- background: var(--dm-primary, #0072CE);
249
- transition: transform 0.3s ease;
250
- }
251
-
252
- .dm-tabs--vertical .dm-tabs__indicator {
253
- bottom: auto;
254
- left: auto;
255
- right: -2px;
256
- top: 0;
257
- width: 2px;
258
- }
259
-
260
- .dm-tabs--pills .dm-tabs__header {
261
- border-bottom: none;
262
- gap: var(--dm-spacing-2, 0.5rem);
263
- }
264
-
265
- .dm-tabs--pills .dm-tabs__tab {
266
- border-radius: var(--dm-radius-md, 0.375rem);
267
- }
268
-
269
- .dm-tabs--pills .dm-tabs__tab--active {
270
- background: var(--dm-primary, #0072CE);
271
- color: white;
272
- }
273
-
274
- .dm-tabs--pills .dm-tabs__indicator {
275
- display: none;
276
- }
277
-
278
- .dm-tabs__panels {
279
- padding: var(--dm-spacing-4, 1rem);
280
- flex: 1;
281
- }
282
-
283
- .dm-tabs__panel {
284
- display: none;
285
- }
286
-
287
- .dm-tabs__panel--active {
288
- display: block;
289
- }
290
-
291
- @media (max-width: 768px) {
292
- .dm-tabs__tab {
293
- padding: var(--dm-spacing-2, 0.5rem) var(--dm-spacing-3, 0.75rem);
294
- font-size: 0.875rem;
295
- }
296
-
297
- .dm-tabs__icon {
298
- font-size: 1rem;
299
- }
313
+ /* Controle manual via useTheme() */
314
+ [data-theme="dark"] .tabsClasses {
315
+ background: var(--dm-bg-color-dark, #1e1e1e);
316
+ color: var(--dm-text-primary-dark, #e0e0e0);
317
+ border-color: var(--dm-border-color-dark, #404040);
300
318
  }
301
- </style>
319
+ </style>