@datametria/vue-components 2.1.1 → 2.3.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 (80) hide show
  1. package/README.md +34 -8
  2. package/dist/index.es.js +3520 -2211
  3. package/dist/index.umd.js +10 -10
  4. package/dist/src/components/DatametriaAutocomplete.vue.d.ts +14 -17
  5. package/dist/src/components/DatametriaBreadcrumb.vue.d.ts +39 -7
  6. package/dist/src/components/DatametriaCheckbox.vue.d.ts +35 -6
  7. package/dist/src/components/DatametriaCheckboxGroup.vue.d.ts +30 -0
  8. package/dist/src/components/DatametriaDataTable.vue.d.ts +64 -0
  9. package/dist/src/components/DatametriaDatePicker.vue.d.ts +15 -37
  10. package/dist/src/components/DatametriaDialog.vue.d.ts +71 -0
  11. package/dist/src/components/DatametriaEmpty.vue.d.ts +30 -0
  12. package/dist/src/components/DatametriaFloatingBar.vue.d.ts +2 -2
  13. package/dist/src/components/DatametriaForm.vue.d.ts +40 -0
  14. package/dist/src/components/DatametriaFormItem.vue.d.ts +28 -0
  15. package/dist/src/components/DatametriaGrid.vue.d.ts +1 -1
  16. package/dist/src/components/DatametriaInput.vue.d.ts +69 -10
  17. package/dist/src/components/DatametriaMenu.vue.d.ts +3 -3
  18. package/dist/src/components/DatametriaNavbar.vue.d.ts +2 -2
  19. package/dist/src/components/DatametriaPagination.vue.d.ts +29 -0
  20. package/dist/src/components/DatametriaPopconfirm.vue.d.ts +43 -0
  21. package/dist/src/components/DatametriaProgress.vue.d.ts +33 -8
  22. package/dist/src/components/DatametriaRadio.vue.d.ts +25 -6
  23. package/dist/src/components/DatametriaRadioGroup.vue.d.ts +29 -0
  24. package/dist/src/components/DatametriaResult.vue.d.ts +30 -0
  25. package/dist/src/components/DatametriaSelect.vue.d.ts +16 -11
  26. package/dist/src/components/DatametriaSidebar.vue.d.ts +3 -3
  27. package/dist/src/components/DatametriaSlider.vue.d.ts +3 -3
  28. package/dist/src/components/DatametriaSortableTable.vue.d.ts +1 -1
  29. package/dist/src/components/DatametriaSteps.vue.d.ts +45 -0
  30. package/dist/src/components/DatametriaSwitch.vue.d.ts +9 -4
  31. package/dist/src/components/DatametriaTabPane.vue.d.ts +28 -0
  32. package/dist/src/components/DatametriaTextarea.vue.d.ts +27 -8
  33. package/dist/src/components/DatametriaTimePicker.vue.d.ts +17 -25
  34. package/dist/src/components/DatametriaToast.vue.d.ts +1 -1
  35. package/dist/src/components/DatametriaTooltip.vue.d.ts +1 -1
  36. package/dist/src/components/DatametriaTree.vue.d.ts +31 -0
  37. package/dist/src/components/DatametriaTreeNode.vue.d.ts +17 -0
  38. package/dist/src/components/DatametriaUpload.vue.d.ts +64 -0
  39. package/dist/src/index.d.ts +14 -0
  40. package/dist/src/types/index.d.ts +5 -0
  41. package/dist/vue-components.css +1 -1
  42. package/package.json +4 -3
  43. package/src/components/DatametriaAutocomplete.vue +155 -260
  44. package/src/components/DatametriaBreadcrumb.vue +66 -80
  45. package/src/components/DatametriaCheckbox.vue +150 -37
  46. package/src/components/DatametriaCheckboxGroup.vue +43 -0
  47. package/src/components/DatametriaDataTable.vue +304 -0
  48. package/src/components/DatametriaDatePicker.vue +238 -614
  49. package/src/components/DatametriaDialog.vue +295 -0
  50. package/src/components/DatametriaDropdown.vue +352 -0
  51. package/src/components/DatametriaEmpty.vue +153 -0
  52. package/src/components/DatametriaForm.vue +160 -0
  53. package/src/components/DatametriaFormItem.vue +181 -0
  54. package/src/components/DatametriaInput.vue +226 -63
  55. package/src/components/DatametriaPagination.vue +373 -0
  56. package/src/components/DatametriaPopconfirm.vue +236 -0
  57. package/src/components/DatametriaProgress.vue +176 -63
  58. package/src/components/DatametriaRadio.vue +83 -72
  59. package/src/components/DatametriaRadioGroup.vue +42 -0
  60. package/src/components/DatametriaResult.vue +133 -0
  61. package/src/components/DatametriaSelect.vue +172 -67
  62. package/src/components/DatametriaSortableTable.vue +241 -20
  63. package/src/components/DatametriaSteps.vue +314 -0
  64. package/src/components/DatametriaSwitch.vue +86 -80
  65. package/src/components/DatametriaTabPane.vue +82 -0
  66. package/src/components/DatametriaTextarea.vue +140 -100
  67. package/src/components/DatametriaTimePicker.vue +231 -214
  68. package/src/components/DatametriaTree.vue +124 -0
  69. package/src/components/DatametriaTreeNode.vue +174 -0
  70. package/src/components/DatametriaUpload.vue +365 -0
  71. package/src/index.ts +25 -11
  72. package/src/types/index.ts +2 -0
  73. package/src/components/__tests__/DatametriaAutocomplete.test.ts +0 -180
  74. package/src/components/__tests__/DatametriaBreadcrumb.test.ts +0 -75
  75. package/src/components/__tests__/DatametriaCheckbox.test.ts +0 -47
  76. package/src/components/__tests__/DatametriaDatePicker.test.ts +0 -234
  77. package/src/components/__tests__/DatametriaProgress.test.ts +0 -90
  78. package/src/components/__tests__/DatametriaRadio.test.ts +0 -77
  79. package/src/components/__tests__/DatametriaSwitch.test.ts +0 -64
  80. package/src/components/__tests__/DatametriaTextarea.test.ts +0 -66
@@ -56,15 +56,68 @@
56
56
  </div>
57
57
 
58
58
  <!-- Column Filter -->
59
- <input
60
- v-if="filterable && column.filterable !== false"
61
- v-model="columnFilters[column.key]"
62
- type="text"
63
- :placeholder="`Filtrar ${column.label}...`"
64
- :aria-label="`Filtrar por ${column.label}`"
65
- class="datametria-sortable-table__filter-input"
66
- @click.stop
67
- />
59
+ <div v-if="filterable && column.filterable !== false" @click.stop>
60
+ <!-- Select Filter -->
61
+ <select
62
+ v-if="column.filterType === 'select'"
63
+ v-model="columnFilters[column.key]"
64
+ :aria-label="`Filtrar por ${column.label}`"
65
+ class="datametria-sortable-table__filter-input"
66
+ @keydown.stop
67
+ >
68
+ <option value="">Todos</option>
69
+ <option
70
+ v-for="option in getFilterOptions(column)"
71
+ :key="option.value"
72
+ :value="option.value"
73
+ >
74
+ {{ option.label }}
75
+ </option>
76
+ </select>
77
+
78
+ <!-- Multi-Select Filter -->
79
+ <div
80
+ v-else-if="column.filterType === 'multiselect'"
81
+ class="datametria-sortable-table__multiselect"
82
+ >
83
+ <button
84
+ type="button"
85
+ class="datametria-sortable-table__multiselect-trigger"
86
+ @click="toggleMultiselect(column.key)"
87
+ >
88
+ {{ getMultiselectLabel(column) }}
89
+ </button>
90
+ <div
91
+ v-if="activeMultiselect === column.key"
92
+ class="datametria-sortable-table__multiselect-dropdown"
93
+ >
94
+ <label
95
+ v-for="option in getFilterOptions(column)"
96
+ :key="option.value"
97
+ class="datametria-sortable-table__multiselect-option"
98
+ >
99
+ <input
100
+ type="checkbox"
101
+ :value="option.value"
102
+ :checked="isMultiselectChecked(column.key, option.value)"
103
+ @change="toggleMultiselectOption(column.key, option.value)"
104
+ />
105
+ {{ option.label }}
106
+ </label>
107
+ </div>
108
+ </div>
109
+
110
+ <!-- Text/Date Filter -->
111
+ <input
112
+ v-else
113
+ v-model="columnFilters[column.key]"
114
+ :type="column.filterType || getFilterInputType(column)"
115
+ :placeholder="`Filtrar ${column.label}...`"
116
+ :aria-label="`Filtrar por ${column.label}`"
117
+ class="datametria-sortable-table__filter-input"
118
+ @keydown.stop
119
+ />
120
+ </div>
68
121
  </th>
69
122
  </tr>
70
123
  </thead>
@@ -204,6 +257,72 @@ const emit = defineEmits<{
204
257
  'selection-change': [selectedIds: (string | number)[]]
205
258
  }>()
206
259
 
260
+ // Detect filter input type based on column data
261
+ const getFilterInputType = (column: any) => {
262
+ if (!props.data || props.data.length === 0) return 'text'
263
+
264
+ const sampleValue = props.data[0][column.key]
265
+
266
+ // Check if it's a date
267
+ if (sampleValue instanceof Date) return 'date'
268
+ if (typeof sampleValue === 'string' && !isNaN(Date.parse(sampleValue))) {
269
+ // Check if it looks like a date string (YYYY-MM-DD, DD/MM/YYYY, etc)
270
+ const datePattern = /^\d{4}-\d{2}-\d{2}|\d{2}\/\d{2}\/\d{4}/
271
+ if (datePattern.test(sampleValue)) return 'date'
272
+ }
273
+
274
+ return 'text'
275
+ }
276
+
277
+ // Get filter options (auto-generate if 'auto')
278
+ const getFilterOptions = (column: any) => {
279
+ if (column.filterOptions === 'auto') {
280
+ const uniqueValues = new Set<string>()
281
+ props.data.forEach(row => {
282
+ const value = row[column.key]
283
+ if (value !== null && value !== undefined && value !== '') {
284
+ uniqueValues.add(String(value))
285
+ }
286
+ })
287
+ return Array.from(uniqueValues)
288
+ .sort()
289
+ .map(value => ({ value, label: value }))
290
+ }
291
+ return column.filterOptions || []
292
+ }
293
+
294
+ // Multi-select methods
295
+ const toggleMultiselect = (key: string) => {
296
+ activeMultiselect.value = activeMultiselect.value === key ? null : key
297
+ }
298
+
299
+ const getMultiselectLabel = (column: any) => {
300
+ const selected = columnFilters.value[column.key] as string[] || []
301
+ if (selected.length === 0) return 'Todos'
302
+ if (selected.length === 1) {
303
+ const options = getFilterOptions(column)
304
+ const option = options.find((o: any) => o.value === selected[0])
305
+ return option?.label || selected[0]
306
+ }
307
+ return `${selected.length} selecionados`
308
+ }
309
+
310
+ const isMultiselectChecked = (key: string, value: string) => {
311
+ const selected = columnFilters.value[key] as string[] || []
312
+ return selected.includes(value)
313
+ }
314
+
315
+ const toggleMultiselectOption = (key: string, value: string) => {
316
+ const selected = (columnFilters.value[key] as string[]) || []
317
+ const index = selected.indexOf(value)
318
+
319
+ if (index > -1) {
320
+ columnFilters.value[key] = selected.filter(v => v !== value)
321
+ } else {
322
+ columnFilters.value[key] = [...selected, value]
323
+ }
324
+ }
325
+
207
326
  // Search
208
327
  const searchQuery = ref('')
209
328
 
@@ -212,7 +331,8 @@ const sortKey = ref<string>('')
212
331
  const sortOrder = ref<'asc' | 'desc'>('asc')
213
332
 
214
333
  // Filtering
215
- const columnFilters = ref<Record<string, string>>({})
334
+ const columnFilters = ref<Record<string, string | string[]>>({})
335
+ const activeMultiselect = ref<string | null>(null)
216
336
 
217
337
  // Selection
218
338
  const selectedRows = ref<Set<string | number>>(new Set())
@@ -238,22 +358,62 @@ const filteredData = computed(() => {
238
358
  // Column filters
239
359
  Object.entries(columnFilters.value).forEach(([key, filterValue]) => {
240
360
  if (filterValue) {
241
- const query = filterValue.toLowerCase()
242
- result = result.filter(row =>
243
- String(row[key]).toLowerCase().includes(query)
244
- )
361
+ // Multi-select filter
362
+ if (Array.isArray(filterValue) && filterValue.length > 0) {
363
+ result = result.filter(row =>
364
+ filterValue.includes(String(row[key]))
365
+ )
366
+ }
367
+ // Text/Date/Select filter
368
+ else if (typeof filterValue === 'string') {
369
+ const query = filterValue.toLowerCase()
370
+ result = result.filter(row =>
371
+ String(row[key]).toLowerCase().includes(query)
372
+ )
373
+ }
245
374
  }
246
375
  })
247
376
 
248
377
  // Sorting
249
378
  if (sortKey.value) {
250
379
  result.sort((a, b) => {
251
- const aVal = a[sortKey.value]
252
- const bVal = b[sortKey.value]
253
-
254
- if (aVal === bVal) return 0
255
-
256
- const comparison = aVal > bVal ? 1 : -1
380
+ let aVal = a[sortKey.value]
381
+ let bVal = b[sortKey.value]
382
+
383
+ // Handle null/undefined
384
+ if (aVal == null && bVal == null) return 0
385
+ if (aVal == null) return 1
386
+ if (bVal == null) return -1
387
+
388
+ // Convert dates to timestamps for proper comparison
389
+ if (aVal instanceof Date) aVal = aVal.getTime()
390
+ if (bVal instanceof Date) bVal = bVal.getTime()
391
+
392
+ // Try to parse as date if string looks like a date
393
+ if (typeof aVal === 'string' && !isNaN(Date.parse(aVal))) {
394
+ const datePattern = /^\d{4}-\d{2}-\d{2}|\d{2}\/\d{2}\/\d{4}/
395
+ if (datePattern.test(aVal)) {
396
+ aVal = new Date(aVal).getTime()
397
+ }
398
+ }
399
+ if (typeof bVal === 'string' && !isNaN(Date.parse(bVal))) {
400
+ const datePattern = /^\d{4}-\d{2}-\d{2}|\d{2}\/\d{2}\/\d{4}/
401
+ if (datePattern.test(bVal)) {
402
+ bVal = new Date(bVal).getTime()
403
+ }
404
+ }
405
+
406
+ // Numeric comparison
407
+ if (typeof aVal === 'number' && typeof bVal === 'number') {
408
+ return sortOrder.value === 'asc' ? aVal - bVal : bVal - aVal
409
+ }
410
+
411
+ // String comparison (case-insensitive)
412
+ const aStr = String(aVal).toLowerCase()
413
+ const bStr = String(bVal).toLowerCase()
414
+
415
+ if (aStr === bStr) return 0
416
+ const comparison = aStr > bStr ? 1 : -1
257
417
  return sortOrder.value === 'asc' ? comparison : -comparison
258
418
  })
259
419
  }
@@ -438,6 +598,67 @@ watch(totalPages, (newTotal) => {
438
598
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--dm-primary, #0072CE) 20%, transparent);
439
599
  }
440
600
 
601
+ .datametria-sortable-table__multiselect {
602
+ position: relative;
603
+ margin-top: var(--dm-spacing-2, 0.5rem);
604
+ }
605
+
606
+ .datametria-sortable-table__multiselect-trigger {
607
+ width: 100%;
608
+ padding: var(--dm-spacing-2, 0.5rem);
609
+ border: 1px solid var(--dm-neutral-200, #e5e7eb);
610
+ border-radius: var(--dm-radius-md, 0.375rem);
611
+ font-size: var(--dm-font-size-sm, 0.875rem);
612
+ font-weight: 400;
613
+ text-align: left;
614
+ background: var(--dm-neutral-50, #ffffff);
615
+ cursor: pointer;
616
+ transition: all 0.2s ease;
617
+ }
618
+
619
+ .datametria-sortable-table__multiselect-trigger:hover {
620
+ border-color: var(--dm-primary, #0072CE);
621
+ }
622
+
623
+ .datametria-sortable-table__multiselect-trigger:focus {
624
+ outline: none;
625
+ border-color: var(--dm-primary, #0072CE);
626
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--dm-primary, #0072CE) 20%, transparent);
627
+ }
628
+
629
+ .datametria-sortable-table__multiselect-dropdown {
630
+ position: absolute;
631
+ top: 100%;
632
+ left: 0;
633
+ right: 0;
634
+ margin-top: var(--dm-spacing-1, 0.25rem);
635
+ max-height: 200px;
636
+ overflow-y: auto;
637
+ background: var(--dm-neutral-50, #ffffff);
638
+ border: 1px solid var(--dm-neutral-200, #e5e7eb);
639
+ border-radius: var(--dm-radius-md, 0.375rem);
640
+ box-shadow: var(--dm-shadow-lg, 0 10px 15px rgba(0, 0, 0, 0.1));
641
+ z-index: 10;
642
+ }
643
+
644
+ .datametria-sortable-table__multiselect-option {
645
+ display: flex;
646
+ align-items: center;
647
+ gap: var(--dm-spacing-2, 0.5rem);
648
+ padding: var(--dm-spacing-2, 0.5rem) var(--dm-spacing-3, 0.75rem);
649
+ font-size: var(--dm-font-size-sm, 0.875rem);
650
+ cursor: pointer;
651
+ transition: background-color 0.2s ease;
652
+ }
653
+
654
+ .datametria-sortable-table__multiselect-option:hover {
655
+ background: var(--dm-neutral-100, #f3f4f6);
656
+ }
657
+
658
+ .datametria-sortable-table__multiselect-option input[type="checkbox"] {
659
+ margin: 0;
660
+ }
661
+
441
662
  .datametria-sortable-table__tbody {
442
663
  background: var(--dm-neutral-50, #ffffff);
443
664
  }
@@ -0,0 +1,314 @@
1
+ <template>
2
+ <div
3
+ class="datametria-steps"
4
+ :class="[
5
+ `datametria-steps--${direction}`,
6
+ { 'datametria-steps--simple': simple }
7
+ ]"
8
+ >
9
+ <div
10
+ v-for="(item, index) in items"
11
+ :key="index"
12
+ class="datametria-steps__item"
13
+ :class="[
14
+ `is-${getStepStatus(index)}`,
15
+ { 'is-last': index === items.length - 1 }
16
+ ]"
17
+ >
18
+ <div class="datametria-steps__head">
19
+ <div class="datametria-steps__line" v-if="index > 0">
20
+ <div class="datametria-steps__line-inner"></div>
21
+ </div>
22
+ <div class="datametria-steps__icon">
23
+ <span v-if="getStepStatus(index) === 'finish'" class="icon-check">✓</span>
24
+ <span v-else-if="getStepStatus(index) === 'error'" class="icon-error">✕</span>
25
+ <span v-else class="icon-number">{{ index + 1 }}</span>
26
+ </div>
27
+ </div>
28
+ <div class="datametria-steps__main">
29
+ <div class="datametria-steps__title">{{ item.title }}</div>
30
+ <div v-if="item.description" class="datametria-steps__description">
31
+ {{ item.description }}
32
+ </div>
33
+ </div>
34
+ </div>
35
+ </div>
36
+ </template>
37
+
38
+ <script setup lang="ts">
39
+ import { computed } from 'vue'
40
+
41
+ /**
42
+ * DatametriaSteps - Componente de passos/wizard
43
+ *
44
+ * @component
45
+ * @example
46
+ * <DatametriaSteps
47
+ * :active="1"
48
+ * :items="[
49
+ * { title: 'Passo 1', description: 'Descrição' },
50
+ * { title: 'Passo 2' },
51
+ * { title: 'Passo 3' }
52
+ * ]"
53
+ * direction="horizontal"
54
+ * />
55
+ */
56
+
57
+ export interface StepItem {
58
+ /** Título do passo */
59
+ title: string
60
+ /** Descrição do passo (opcional) */
61
+ description?: string
62
+ /** Status customizado do passo */
63
+ status?: 'wait' | 'process' | 'finish' | 'error'
64
+ }
65
+
66
+ interface Props {
67
+ /** Índice do passo ativo (0-based) */
68
+ active?: number
69
+ /** Array de itens dos passos */
70
+ items: StepItem[]
71
+ /** Direção dos passos */
72
+ direction?: 'horizontal' | 'vertical'
73
+ /** Modo simples (sem descrição) */
74
+ simple?: boolean
75
+ /** Status global (sobrescreve status individual) */
76
+ status?: 'wait' | 'process' | 'finish' | 'error'
77
+ }
78
+
79
+ const props = withDefaults(defineProps<Props>(), {
80
+ active: 0,
81
+ direction: 'horizontal',
82
+ simple: false
83
+ })
84
+
85
+ const emit = defineEmits<{
86
+ change: [index: number]
87
+ }>()
88
+
89
+ /**
90
+ * Determina o status de um passo baseado no índice ativo
91
+ */
92
+ const getStepStatus = (index: number): 'wait' | 'process' | 'finish' | 'error' => {
93
+ // Status customizado do item tem prioridade
94
+ if (props.items[index]?.status) {
95
+ return props.items[index].status!
96
+ }
97
+
98
+ // Status global tem segunda prioridade
99
+ if (props.status) {
100
+ return props.status
101
+ }
102
+
103
+ // Status baseado no índice ativo
104
+ if (index < props.active) {
105
+ return 'finish'
106
+ } else if (index === props.active) {
107
+ return 'process'
108
+ } else {
109
+ return 'wait'
110
+ }
111
+ }
112
+ </script>
113
+
114
+ <style scoped>
115
+ .datametria-steps {
116
+ display: flex;
117
+ font-size: 14px;
118
+ }
119
+
120
+ .datametria-steps--horizontal {
121
+ flex-direction: row;
122
+ }
123
+
124
+ .datametria-steps--vertical {
125
+ flex-direction: column;
126
+ }
127
+
128
+ .datametria-steps__item {
129
+ position: relative;
130
+ flex: 1;
131
+ display: flex;
132
+ overflow: hidden;
133
+ }
134
+
135
+ .datametria-steps--horizontal .datametria-steps__item {
136
+ flex-direction: column;
137
+ }
138
+
139
+ .datametria-steps--vertical .datametria-steps__item {
140
+ flex-direction: row;
141
+ padding-bottom: 24px;
142
+ }
143
+
144
+ .datametria-steps--vertical .datametria-steps__item.is-last {
145
+ padding-bottom: 0;
146
+ }
147
+
148
+ .datametria-steps__head {
149
+ position: relative;
150
+ display: flex;
151
+ align-items: center;
152
+ }
153
+
154
+ .datametria-steps--horizontal .datametria-steps__head {
155
+ flex-direction: column;
156
+ margin-bottom: 8px;
157
+ }
158
+
159
+ .datametria-steps--vertical .datametria-steps__head {
160
+ flex-direction: column;
161
+ margin-right: 16px;
162
+ flex-shrink: 0;
163
+ }
164
+
165
+ .datametria-steps__icon {
166
+ width: 32px;
167
+ height: 32px;
168
+ border-radius: 50%;
169
+ display: flex;
170
+ align-items: center;
171
+ justify-content: center;
172
+ font-size: 16px;
173
+ font-weight: 500;
174
+ border: 2px solid var(--datametria-border, #dcdfe6);
175
+ background: var(--datametria-bg, #fff);
176
+ color: var(--datametria-text-secondary, #909399);
177
+ transition: all 0.3s;
178
+ z-index: 1;
179
+ }
180
+
181
+ .datametria-steps__item.is-process .datametria-steps__icon {
182
+ border-color: var(--datametria-primary, #0072ce);
183
+ background: var(--datametria-primary, #0072ce);
184
+ color: #fff;
185
+ }
186
+
187
+ .datametria-steps__item.is-finish .datametria-steps__icon {
188
+ border-color: var(--datametria-success, #67c23a);
189
+ background: var(--datametria-success, #67c23a);
190
+ color: #fff;
191
+ }
192
+
193
+ .datametria-steps__item.is-error .datametria-steps__icon {
194
+ border-color: var(--datametria-danger, #f56c6c);
195
+ background: var(--datametria-danger, #f56c6c);
196
+ color: #fff;
197
+ }
198
+
199
+ .datametria-steps__line {
200
+ position: absolute;
201
+ background: var(--datametria-border, #dcdfe6);
202
+ }
203
+
204
+ .datametria-steps--horizontal .datametria-steps__line {
205
+ height: 2px;
206
+ top: 15px;
207
+ left: -50%;
208
+ right: 50%;
209
+ }
210
+
211
+ .datametria-steps--vertical .datametria-steps__line {
212
+ width: 2px;
213
+ top: 32px;
214
+ bottom: 0;
215
+ left: 15px;
216
+ }
217
+
218
+ .datametria-steps__line-inner {
219
+ width: 0;
220
+ height: 100%;
221
+ background: var(--datametria-primary, #0072ce);
222
+ transition: width 0.3s, height 0.3s;
223
+ }
224
+
225
+ .datametria-steps__item.is-finish .datametria-steps__line-inner,
226
+ .datametria-steps__item.is-process .datametria-steps__line-inner {
227
+ width: 100%;
228
+ height: 100%;
229
+ background: var(--datametria-success, #67c23a);
230
+ }
231
+
232
+ .datametria-steps__item.is-error .datametria-steps__line-inner {
233
+ width: 100%;
234
+ height: 100%;
235
+ background: var(--datametria-danger, #f56c6c);
236
+ }
237
+
238
+ .datametria-steps__main {
239
+ text-align: center;
240
+ }
241
+
242
+ .datametria-steps--vertical .datametria-steps__main {
243
+ text-align: left;
244
+ flex: 1;
245
+ }
246
+
247
+ .datametria-steps__title {
248
+ font-size: 14px;
249
+ font-weight: 500;
250
+ color: var(--datametria-text-primary, #303133);
251
+ line-height: 1.5;
252
+ }
253
+
254
+ .datametria-steps__item.is-wait .datametria-steps__title {
255
+ color: var(--datametria-text-secondary, #909399);
256
+ }
257
+
258
+ .datametria-steps__item.is-process .datametria-steps__title {
259
+ color: var(--datametria-primary, #0072ce);
260
+ }
261
+
262
+ .datametria-steps__item.is-finish .datametria-steps__title {
263
+ color: var(--datametria-success, #67c23a);
264
+ }
265
+
266
+ .datametria-steps__item.is-error .datametria-steps__title {
267
+ color: var(--datametria-danger, #f56c6c);
268
+ }
269
+
270
+ .datametria-steps__description {
271
+ font-size: 12px;
272
+ color: var(--datametria-text-secondary, #909399);
273
+ margin-top: 4px;
274
+ line-height: 1.5;
275
+ }
276
+
277
+ .datametria-steps--simple .datametria-steps__description {
278
+ display: none;
279
+ }
280
+
281
+ /* Dark mode */
282
+ @media (prefers-color-scheme: dark) {
283
+ .datametria-steps__icon {
284
+ border-color: var(--datametria-border-dark, #4c4d4f);
285
+ background: var(--datametria-bg-dark, #1d1e1f);
286
+ color: var(--datametria-text-secondary-dark, #909399);
287
+ }
288
+
289
+ .datametria-steps__item.is-process .datametria-steps__icon {
290
+ border-color: var(--datametria-primary-dark, #409eff);
291
+ background: var(--datametria-primary-dark, #409eff);
292
+ }
293
+
294
+ .datametria-steps__line {
295
+ background: var(--datametria-border-dark, #4c4d4f);
296
+ }
297
+
298
+ .datametria-steps__title {
299
+ color: var(--datametria-text-primary-dark, #e5eaf3);
300
+ }
301
+
302
+ .datametria-steps__item.is-wait .datametria-steps__title {
303
+ color: var(--datametria-text-secondary-dark, #909399);
304
+ }
305
+
306
+ .datametria-steps__item.is-process .datametria-steps__title {
307
+ color: var(--datametria-primary-dark, #409eff);
308
+ }
309
+
310
+ .datametria-steps__description {
311
+ color: var(--datametria-text-secondary-dark, #909399);
312
+ }
313
+ }
314
+ </style>