@datametria/vue-components 1.2.0 → 2.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.
Files changed (97) hide show
  1. package/README.md +548 -657
  2. package/dist/index.es.js +2353 -1364
  3. package/dist/index.umd.js +10 -10
  4. package/dist/vue-components.css +1 -1
  5. package/package.json +102 -98
  6. package/src/components/DatametriaAlert.vue +137 -137
  7. package/src/components/DatametriaAutocomplete.vue +184 -138
  8. package/src/components/DatametriaAvatar.vue +177 -33
  9. package/src/components/DatametriaBadge.vue +98 -98
  10. package/src/components/DatametriaBreadcrumb.vue +21 -21
  11. package/src/components/DatametriaButton.vue +177 -165
  12. package/src/components/DatametriaCard.vue +12 -12
  13. package/src/components/DatametriaCheckbox.vue +8 -8
  14. package/src/components/DatametriaChip.vue +145 -149
  15. package/src/components/DatametriaContainer.vue +4 -4
  16. package/src/components/DatametriaDatePicker.vue +686 -68
  17. package/src/components/DatametriaDivider.vue +13 -13
  18. package/src/components/DatametriaFileUpload.vue +272 -140
  19. package/src/components/DatametriaGrid.vue +3 -3
  20. package/src/components/DatametriaInput.vue +15 -15
  21. package/src/components/DatametriaMenu.vue +604 -619
  22. package/src/components/DatametriaModal.vue +16 -16
  23. package/src/components/DatametriaNavbar.vue +230 -252
  24. package/src/components/DatametriaPasswordInput.vue +430 -0
  25. package/src/components/DatametriaProgress.vue +18 -18
  26. package/src/components/DatametriaRadio.vue +20 -20
  27. package/src/components/DatametriaSelect.vue +15 -15
  28. package/src/components/DatametriaSkeleton.vue +243 -239
  29. package/src/components/DatametriaSlider.vue +395 -407
  30. package/src/components/DatametriaSortableTable.vue +585 -0
  31. package/src/components/DatametriaSpinner.vue +7 -7
  32. package/src/components/DatametriaSwitch.vue +16 -16
  33. package/src/components/DatametriaTable.vue +14 -14
  34. package/src/components/DatametriaTextarea.vue +28 -28
  35. package/src/components/DatametriaTimePicker.vue +285 -285
  36. package/src/components/DatametriaToast.vue +176 -176
  37. package/src/components/DatametriaTooltip.vue +408 -408
  38. package/src/components/__tests__/DatametriaAlert.test.js +35 -35
  39. package/src/components/__tests__/DatametriaAlert.test.ts +190 -0
  40. package/src/components/__tests__/DatametriaAutocomplete.test.ts +180 -0
  41. package/src/components/__tests__/DatametriaAvatar.test.ts +152 -0
  42. package/src/components/__tests__/DatametriaBadge.test.js +29 -29
  43. package/src/components/__tests__/DatametriaBadge.test.ts +167 -0
  44. package/src/components/__tests__/DatametriaBreadcrumb.test.ts +75 -0
  45. package/src/components/__tests__/DatametriaButton.test.js +30 -30
  46. package/src/components/__tests__/DatametriaButton.test.ts +283 -0
  47. package/src/components/__tests__/DatametriaCard.test.ts +201 -0
  48. package/src/components/__tests__/DatametriaCheckbox.test.ts +47 -0
  49. package/src/components/__tests__/DatametriaChip.test.js +38 -38
  50. package/src/components/__tests__/DatametriaContainer.test.ts +52 -0
  51. package/src/components/__tests__/DatametriaDatePicker.test.ts +234 -0
  52. package/src/components/__tests__/DatametriaDivider.test.ts +54 -0
  53. package/src/components/__tests__/DatametriaFileUpload.test.ts +291 -0
  54. package/src/components/__tests__/DatametriaGrid.test.ts +31 -0
  55. package/src/components/__tests__/DatametriaInput.test.ts +72 -0
  56. package/src/components/__tests__/DatametriaMenu.test.ts +366 -0
  57. package/src/components/__tests__/DatametriaModal.test.ts +86 -0
  58. package/src/components/__tests__/DatametriaNavbar.test.js +48 -48
  59. package/src/components/__tests__/DatametriaNavbar.test.ts +203 -0
  60. package/src/components/__tests__/DatametriaPasswordInput.test.js +305 -0
  61. package/src/components/__tests__/DatametriaProgress.test.ts +90 -0
  62. package/src/components/__tests__/DatametriaRadio.test.ts +77 -0
  63. package/src/components/__tests__/DatametriaSelect.test.ts +77 -0
  64. package/src/components/__tests__/DatametriaSlider.test.ts +261 -0
  65. package/src/components/__tests__/DatametriaSortableTable.test.js +168 -0
  66. package/src/components/__tests__/DatametriaSpinner.test.ts +156 -0
  67. package/src/components/__tests__/DatametriaSwitch.test.ts +64 -0
  68. package/src/components/__tests__/DatametriaTable.test.ts +97 -0
  69. package/src/components/__tests__/DatametriaTextarea.test.ts +66 -0
  70. package/src/components/__tests__/DatametriaToast.test.js +48 -48
  71. package/src/components/__tests__/DatametriaToast.test.ts +99 -0
  72. package/src/composables/useAccessibilityScale.ts +94 -94
  73. package/src/composables/useBreakpoints.ts +82 -82
  74. package/src/composables/useHapticFeedback.ts +439 -439
  75. package/src/composables/useRipple.ts +218 -218
  76. package/src/index.ts +68 -61
  77. package/src/stories/Variants.stories.js +95 -95
  78. package/src/styles/design-tokens.css +623 -623
  79. package/src/theme/ThemeProvider.vue +96 -0
  80. package/src/theme/__tests__/ThemeProvider.test.ts +208 -0
  81. package/src/theme/__tests__/constants.test.ts +31 -0
  82. package/src/theme/__tests__/presets.test.ts +166 -0
  83. package/src/theme/__tests__/tokens.test.ts +155 -0
  84. package/src/theme/__tests__/types.test.ts +153 -0
  85. package/src/theme/__tests__/useTheme.test.ts +146 -0
  86. package/src/theme/constants.ts +14 -0
  87. package/src/theme/index.ts +12 -0
  88. package/src/theme/presets/datametria.ts +94 -0
  89. package/src/theme/presets/default.ts +94 -0
  90. package/src/theme/presets/index.ts +8 -0
  91. package/src/theme/tokens/colors.ts +28 -0
  92. package/src/theme/tokens/index.ts +47 -0
  93. package/src/theme/tokens/spacing.ts +21 -0
  94. package/src/theme/tokens/typography.ts +35 -0
  95. package/src/theme/types.ts +111 -0
  96. package/src/theme/useTheme.ts +28 -0
  97. package/src/types/index.ts +19 -0
@@ -0,0 +1,585 @@
1
+ <template>
2
+ <div class="datametria-sortable-table">
3
+ <!-- Search Bar -->
4
+ <div v-if="searchable" class="datametria-sortable-table__search">
5
+ <input
6
+ v-model="searchQuery"
7
+ type="text"
8
+ placeholder="Buscar..."
9
+ class="datametria-sortable-table__search-input"
10
+ aria-label="Buscar na tabela"
11
+ />
12
+ </div>
13
+
14
+ <!-- Table -->
15
+ <div class="datametria-sortable-table__wrapper">
16
+ <table class="datametria-sortable-table__table" role="table">
17
+ <thead class="datametria-sortable-table__thead">
18
+ <tr role="row">
19
+ <!-- Selection Column -->
20
+ <th
21
+ v-if="selectable"
22
+ class="datametria-sortable-table__th datametria-sortable-table__th--checkbox"
23
+ role="columnheader"
24
+ >
25
+ <input
26
+ type="checkbox"
27
+ :checked="isAllSelected"
28
+ :indeterminate="isIndeterminate"
29
+ aria-label="Selecionar todas as linhas"
30
+ @change="toggleSelectAll"
31
+ />
32
+ </th>
33
+
34
+ <!-- Data Columns -->
35
+ <th
36
+ v-for="column in columns"
37
+ :key="column.key"
38
+ class="datametria-sortable-table__th"
39
+ :class="{ 'datametria-sortable-table__th--sortable': column.sortable !== false }"
40
+ :style="{ width: column.width }"
41
+ role="columnheader"
42
+ :aria-sort="sortKey === column.key ? (sortOrder === 'asc' ? 'ascending' : 'descending') : 'none'"
43
+ @click="column.sortable !== false && toggleSort(column.key)"
44
+ @keydown.enter="column.sortable !== false && toggleSort(column.key)"
45
+ @keydown.space.prevent="column.sortable !== false && toggleSort(column.key)"
46
+ :tabindex="column.sortable !== false ? 0 : undefined"
47
+ >
48
+ <div class="datametria-sortable-table__th-content">
49
+ <span>{{ column.label }}</span>
50
+ <span v-if="column.sortable !== false" class="datametria-sortable-table__sort-icon" aria-hidden="true">
51
+ <span v-if="sortKey === column.key">
52
+ {{ sortOrder === 'asc' ? '↑' : '↓' }}
53
+ </span>
54
+ <span v-else class="datametria-sortable-table__sort-icon--inactive">↕</span>
55
+ </span>
56
+ </div>
57
+
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
+ />
68
+ </th>
69
+ </tr>
70
+ </thead>
71
+
72
+ <tbody class="datametria-sortable-table__tbody">
73
+ <tr
74
+ v-for="(row, index) in paginatedData"
75
+ :key="row.id || index"
76
+ class="datametria-sortable-table__tr"
77
+ :class="{ 'datametria-sortable-table__tr--selected': selectedRows.has(row.id || index) }"
78
+ role="row"
79
+ >
80
+ <!-- Selection Column -->
81
+ <td
82
+ v-if="selectable"
83
+ class="datametria-sortable-table__td datametria-sortable-table__td--checkbox"
84
+ role="cell"
85
+ >
86
+ <input
87
+ type="checkbox"
88
+ :checked="selectedRows.has(row.id || index)"
89
+ :aria-label="`Selecionar linha ${index + 1}`"
90
+ @change="toggleSelectRow(row.id || index)"
91
+ />
92
+ </td>
93
+
94
+ <!-- Data Columns -->
95
+ <td
96
+ v-for="column in columns"
97
+ :key="column.key"
98
+ class="datametria-sortable-table__td"
99
+ role="cell"
100
+ >
101
+ <slot :name="`cell-${column.key}`" :row="row" :value="row[column.key]">
102
+ {{ row[column.key] }}
103
+ </slot>
104
+ </td>
105
+ </tr>
106
+ </tbody>
107
+ </table>
108
+
109
+ <!-- Empty State -->
110
+ <div v-if="filteredData.length === 0" class="datametria-sortable-table__empty" role="status">
111
+ <slot name="empty">Nenhum dado encontrado</slot>
112
+ </div>
113
+ </div>
114
+
115
+ <!-- Pagination -->
116
+ <div
117
+ v-if="paginated && filteredData.length > 0"
118
+ class="datametria-sortable-table__pagination"
119
+ role="navigation"
120
+ aria-label="Paginação da tabela"
121
+ >
122
+ <div class="datametria-sortable-table__pagination-info" aria-live="polite">
123
+ Mostrando {{ startIndex + 1 }} - {{ endIndex }} de {{ filteredData.length }} registros
124
+ <span v-if="selectedRows.size > 0">({{ selectedRows.size }} selecionados)</span>
125
+ </div>
126
+
127
+ <div class="datametria-sortable-table__pagination-controls">
128
+ <select
129
+ v-model.number="currentPageSize"
130
+ class="datametria-sortable-table__page-size"
131
+ aria-label="Itens por página"
132
+ >
133
+ <option v-for="size in pageSizeOptions" :key="size" :value="size">
134
+ {{ size }} por página
135
+ </option>
136
+ </select>
137
+
138
+ <button
139
+ class="datametria-sortable-table__pagination-btn"
140
+ :disabled="currentPage === 1"
141
+ aria-label="Primeira página"
142
+ @click="currentPage = 1"
143
+ >
144
+ ««
145
+ </button>
146
+ <button
147
+ class="datametria-sortable-table__pagination-btn"
148
+ :disabled="currentPage === 1"
149
+ aria-label="Página anterior"
150
+ @click="currentPage--"
151
+ >
152
+
153
+ </button>
154
+
155
+ <span class="datametria-sortable-table__pagination-pages" aria-current="page">
156
+ Página {{ currentPage }} de {{ totalPages }}
157
+ </span>
158
+
159
+ <button
160
+ class="datametria-sortable-table__pagination-btn"
161
+ :disabled="currentPage === totalPages"
162
+ aria-label="Próxima página"
163
+ @click="currentPage++"
164
+ >
165
+
166
+ </button>
167
+ <button
168
+ class="datametria-sortable-table__pagination-btn"
169
+ :disabled="currentPage === totalPages"
170
+ aria-label="Última página"
171
+ @click="currentPage = totalPages"
172
+ >
173
+ »»
174
+ </button>
175
+ </div>
176
+ </div>
177
+ </div>
178
+ </template>
179
+
180
+ <script setup lang="ts">
181
+ import { ref, computed, watch } from 'vue'
182
+ import type { SortableTableProps } from '../types'
183
+
184
+ const props = withDefaults(defineProps<SortableTableProps>(), {
185
+ selectable: false,
186
+ searchable: true,
187
+ filterable: true,
188
+ paginated: true,
189
+ pageSize: 10,
190
+ pageSizeOptions: () => [5, 10, 25, 50, 100]
191
+ })
192
+
193
+ // Validação em desenvolvimento
194
+ if (process.env.NODE_ENV === 'development') {
195
+ if (!props.columns || props.columns.length === 0) {
196
+ console.warn('[DatametriaSortableTable] No columns provided')
197
+ }
198
+ if (!props.data) {
199
+ console.warn('[DatametriaSortableTable] No data provided')
200
+ }
201
+ }
202
+
203
+ const emit = defineEmits<{
204
+ 'selection-change': [selectedIds: (string | number)[]]
205
+ }>()
206
+
207
+ // Search
208
+ const searchQuery = ref('')
209
+
210
+ // Sorting
211
+ const sortKey = ref<string>('')
212
+ const sortOrder = ref<'asc' | 'desc'>('asc')
213
+
214
+ // Filtering
215
+ const columnFilters = ref<Record<string, string>>({})
216
+
217
+ // Selection
218
+ const selectedRows = ref<Set<string | number>>(new Set())
219
+
220
+ // Pagination
221
+ const currentPage = ref(1)
222
+ const currentPageSize = ref(props.pageSize)
223
+
224
+ // Computed: Filtered Data
225
+ const filteredData = computed(() => {
226
+ let result = [...props.data]
227
+
228
+ // Global search
229
+ if (searchQuery.value) {
230
+ const query = searchQuery.value.toLowerCase()
231
+ result = result.filter(row =>
232
+ Object.values(row).some(value =>
233
+ String(value).toLowerCase().includes(query)
234
+ )
235
+ )
236
+ }
237
+
238
+ // Column filters
239
+ Object.entries(columnFilters.value).forEach(([key, filterValue]) => {
240
+ if (filterValue) {
241
+ const query = filterValue.toLowerCase()
242
+ result = result.filter(row =>
243
+ String(row[key]).toLowerCase().includes(query)
244
+ )
245
+ }
246
+ })
247
+
248
+ // Sorting
249
+ if (sortKey.value) {
250
+ 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
257
+ return sortOrder.value === 'asc' ? comparison : -comparison
258
+ })
259
+ }
260
+
261
+ return result
262
+ })
263
+
264
+ // Computed: Pagination
265
+ const totalPages = computed(() =>
266
+ Math.ceil(filteredData.value.length / currentPageSize.value)
267
+ )
268
+
269
+ const startIndex = computed(() =>
270
+ (currentPage.value - 1) * currentPageSize.value
271
+ )
272
+
273
+ const endIndex = computed(() =>
274
+ Math.min(startIndex.value + currentPageSize.value, filteredData.value.length)
275
+ )
276
+
277
+ const paginatedData = computed(() => {
278
+ if (!props.paginated) return filteredData.value
279
+ return filteredData.value.slice(startIndex.value, endIndex.value)
280
+ })
281
+
282
+ // Computed: Selection
283
+ const isAllSelected = computed(() =>
284
+ paginatedData.value.length > 0 &&
285
+ paginatedData.value.every(row => selectedRows.value.has(row.id || props.data.indexOf(row)))
286
+ )
287
+
288
+ const isIndeterminate = computed(() =>
289
+ selectedRows.value.size > 0 && !isAllSelected.value
290
+ )
291
+
292
+ // Methods: Sorting
293
+ const toggleSort = (key: string) => {
294
+ if (sortKey.value === key) {
295
+ sortOrder.value = sortOrder.value === 'asc' ? 'desc' : 'asc'
296
+ } else {
297
+ sortKey.value = key
298
+ sortOrder.value = 'asc'
299
+ }
300
+ }
301
+
302
+ // Methods: Selection
303
+ const toggleSelectRow = (id: string | number) => {
304
+ if (selectedRows.value.has(id)) {
305
+ selectedRows.value.delete(id)
306
+ } else {
307
+ selectedRows.value.add(id)
308
+ }
309
+ emit('selection-change', Array.from(selectedRows.value))
310
+ }
311
+
312
+ const toggleSelectAll = () => {
313
+ if (isAllSelected.value) {
314
+ paginatedData.value.forEach(row => {
315
+ selectedRows.value.delete(row.id || props.data.indexOf(row))
316
+ })
317
+ } else {
318
+ paginatedData.value.forEach(row => {
319
+ selectedRows.value.add(row.id || props.data.indexOf(row))
320
+ })
321
+ }
322
+ emit('selection-change', Array.from(selectedRows.value))
323
+ }
324
+
325
+ // Watch: Reset page on filter change
326
+ watch([searchQuery, columnFilters], () => {
327
+ currentPage.value = 1
328
+ }, { deep: true })
329
+
330
+ // Watch: Reset page if current page exceeds total pages
331
+ watch(totalPages, (newTotal) => {
332
+ if (currentPage.value > newTotal && newTotal > 0) {
333
+ currentPage.value = newTotal
334
+ }
335
+ })
336
+ </script>
337
+
338
+ <style scoped>
339
+ .datametria-sortable-table {
340
+ width: 100%;
341
+ background: var(--dm-neutral-50, #ffffff);
342
+ border-radius: var(--dm-radius-md, 0.375rem);
343
+ box-shadow: var(--dm-shadow-sm, 0 1px 3px rgba(0, 0, 0, 0.1));
344
+ }
345
+
346
+ .datametria-sortable-table__search {
347
+ padding: var(--dm-spacing-4, 1rem);
348
+ border-bottom: 1px solid var(--dm-neutral-100, #f3f4f6);
349
+ }
350
+
351
+ .datametria-sortable-table__search-input {
352
+ width: 100%;
353
+ padding: var(--dm-spacing-2, 0.5rem) var(--dm-spacing-4, 1rem);
354
+ border: 1px solid var(--dm-neutral-200, #e5e7eb);
355
+ border-radius: var(--dm-radius-md, 0.375rem);
356
+ font-size: var(--dm-font-size-sm, 0.875rem);
357
+ transition: all 0.2s ease;
358
+ }
359
+
360
+ .datametria-sortable-table__search-input:focus {
361
+ outline: none;
362
+ border-color: var(--dm-primary, #0072CE);
363
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--dm-primary, #0072CE) 20%, transparent);
364
+ }
365
+
366
+ .datametria-sortable-table__wrapper {
367
+ overflow-x: auto;
368
+ }
369
+
370
+ .datametria-sortable-table__table {
371
+ width: 100%;
372
+ border-collapse: collapse;
373
+ }
374
+
375
+ .datametria-sortable-table__thead {
376
+ background: var(--dm-neutral-50, #f9fafb);
377
+ border-bottom: 2px solid var(--dm-neutral-200, #e5e7eb);
378
+ }
379
+
380
+ .datametria-sortable-table__th {
381
+ padding: var(--dm-spacing-3, 0.75rem) var(--dm-spacing-4, 1rem);
382
+ text-align: left;
383
+ font-size: var(--dm-font-size-sm, 0.875rem);
384
+ font-weight: var(--dm-font-weight-semibold, 600);
385
+ color: var(--dm-neutral-700, #374151);
386
+ position: relative;
387
+ }
388
+
389
+ .datametria-sortable-table__th--sortable {
390
+ cursor: pointer;
391
+ user-select: none;
392
+ }
393
+
394
+ .datametria-sortable-table__th--sortable:hover {
395
+ background: var(--dm-neutral-100, #f3f4f6);
396
+ }
397
+
398
+ .datametria-sortable-table__th--sortable:focus-visible {
399
+ outline: none;
400
+ box-shadow: inset 0 0 0 2px var(--dm-primary, #0072CE);
401
+ }
402
+
403
+ .datametria-sortable-table__th--checkbox {
404
+ width: 40px;
405
+ padding: var(--dm-spacing-3, 0.75rem) var(--dm-spacing-2, 0.5rem);
406
+ }
407
+
408
+ .datametria-sortable-table__th-content {
409
+ display: flex;
410
+ align-items: center;
411
+ gap: var(--dm-spacing-2, 0.5rem);
412
+ }
413
+
414
+ .datametria-sortable-table__sort-icon {
415
+ font-size: var(--dm-font-size-sm, 0.875rem);
416
+ color: var(--dm-primary, #0072CE);
417
+ }
418
+
419
+ .datametria-sortable-table__sort-icon--inactive {
420
+ color: var(--dm-neutral-400, #9ca3af);
421
+ opacity: 0.5;
422
+ }
423
+
424
+ .datametria-sortable-table__filter-input {
425
+ width: 100%;
426
+ margin-top: var(--dm-spacing-2, 0.5rem);
427
+ padding: var(--dm-spacing-2, 0.5rem);
428
+ border: 1px solid var(--dm-neutral-200, #e5e7eb);
429
+ border-radius: var(--dm-radius-md, 0.375rem);
430
+ font-size: var(--dm-font-size-sm, 0.875rem);
431
+ font-weight: 400;
432
+ transition: all 0.2s ease;
433
+ }
434
+
435
+ .datametria-sortable-table__filter-input:focus {
436
+ outline: none;
437
+ border-color: var(--dm-primary, #0072CE);
438
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--dm-primary, #0072CE) 20%, transparent);
439
+ }
440
+
441
+ .datametria-sortable-table__tbody {
442
+ background: var(--dm-neutral-50, #ffffff);
443
+ }
444
+
445
+ .datametria-sortable-table__tr {
446
+ border-bottom: 1px solid var(--dm-neutral-100, #f3f4f6);
447
+ transition: background-color 0.2s ease;
448
+ }
449
+
450
+ .datametria-sortable-table__tr:hover {
451
+ background: var(--dm-neutral-50, #f9fafb);
452
+ }
453
+
454
+ .datametria-sortable-table__tr--selected {
455
+ background: color-mix(in srgb, var(--dm-primary, #0072CE) 10%, transparent);
456
+ }
457
+
458
+ .datametria-sortable-table__td {
459
+ padding: var(--dm-spacing-3, 0.75rem) var(--dm-spacing-4, 1rem);
460
+ font-size: var(--dm-font-size-sm, 0.875rem);
461
+ color: var(--dm-neutral-900, #111827);
462
+ }
463
+
464
+ .datametria-sortable-table__td--checkbox {
465
+ width: 40px;
466
+ padding: var(--dm-spacing-3, 0.75rem) var(--dm-spacing-2, 0.5rem);
467
+ }
468
+
469
+ .datametria-sortable-table__empty {
470
+ padding: var(--dm-spacing-4, 1rem);
471
+ text-align: center;
472
+ color: var(--dm-neutral-500, #6b7280);
473
+ font-size: var(--dm-font-size-sm, 0.875rem);
474
+ }
475
+
476
+ .datametria-sortable-table__pagination {
477
+ display: flex;
478
+ justify-content: space-between;
479
+ align-items: center;
480
+ padding: var(--dm-spacing-4, 1rem);
481
+ border-top: 1px solid var(--dm-neutral-200, #e5e7eb);
482
+ flex-wrap: wrap;
483
+ gap: var(--dm-spacing-4, 1rem);
484
+ }
485
+
486
+ .datametria-sortable-table__pagination-info {
487
+ font-size: var(--dm-font-size-sm, 0.875rem);
488
+ color: var(--dm-neutral-600, #4b5563);
489
+ }
490
+
491
+ .datametria-sortable-table__pagination-controls {
492
+ display: flex;
493
+ align-items: center;
494
+ gap: var(--dm-spacing-2, 0.5rem);
495
+ }
496
+
497
+ .datametria-sortable-table__page-size {
498
+ padding: var(--dm-spacing-2, 0.5rem);
499
+ border: 1px solid var(--dm-neutral-200, #e5e7eb);
500
+ border-radius: var(--dm-radius-md, 0.375rem);
501
+ font-size: var(--dm-font-size-sm, 0.875rem);
502
+ cursor: pointer;
503
+ transition: all 0.2s ease;
504
+ background: var(--dm-neutral-50, #ffffff);
505
+ color: var(--dm-neutral-900, #111827);
506
+ }
507
+
508
+ .datametria-sortable-table__page-size:focus {
509
+ outline: none;
510
+ border-color: var(--dm-primary, #0072CE);
511
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--dm-primary, #0072CE) 20%, transparent);
512
+ }
513
+
514
+ .datametria-sortable-table__pagination-btn {
515
+ padding: var(--dm-spacing-2, 0.5rem) var(--dm-spacing-3, 0.75rem);
516
+ border: 1px solid var(--dm-neutral-200, #e5e7eb);
517
+ border-radius: var(--dm-radius-md, 0.375rem);
518
+ background: var(--dm-neutral-50, #ffffff);
519
+ color: var(--dm-neutral-900, #111827);
520
+ font-size: var(--dm-font-size-sm, 0.875rem);
521
+ cursor: pointer;
522
+ transition: all 0.2s ease;
523
+ }
524
+
525
+ .datametria-sortable-table__pagination-btn:hover:not(:disabled) {
526
+ background: var(--dm-neutral-100, #f3f4f6);
527
+ border-color: var(--dm-primary, #0072CE);
528
+ }
529
+
530
+ .datametria-sortable-table__pagination-btn:focus-visible {
531
+ outline: none;
532
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--dm-primary, #0072CE) 20%, transparent);
533
+ }
534
+
535
+ .datametria-sortable-table__pagination-btn:disabled {
536
+ opacity: 0.5;
537
+ cursor: not-allowed;
538
+ }
539
+
540
+ .datametria-sortable-table__pagination-pages {
541
+ padding: 0 var(--dm-spacing-2, 0.5rem);
542
+ font-size: var(--dm-font-size-sm, 0.875rem);
543
+ color: var(--dm-neutral-600, #4b5563);
544
+ }
545
+
546
+ input[type="checkbox"] {
547
+ width: 16px;
548
+ height: 16px;
549
+ cursor: pointer;
550
+ accent-color: var(--dm-primary, #0072CE);
551
+ }
552
+
553
+ input[type="checkbox"]:focus-visible {
554
+ outline: 2px solid var(--dm-primary, #0072CE);
555
+ outline-offset: 2px;
556
+ }
557
+
558
+ /* Mobile Responsiveness */
559
+ @media (max-width: 640px) {
560
+ .datametria-sortable-table__pagination {
561
+ flex-direction: column;
562
+ align-items: stretch;
563
+ }
564
+
565
+ .datametria-sortable-table__pagination-controls {
566
+ justify-content: center;
567
+ }
568
+
569
+ .datametria-sortable-table__th,
570
+ .datametria-sortable-table__td {
571
+ padding: var(--dm-spacing-2, 0.5rem) var(--dm-spacing-3, 0.75rem);
572
+ font-size: calc(var(--dm-font-size-sm, 0.875rem) * 0.9);
573
+ }
574
+ }
575
+
576
+ /* Reduced Motion */
577
+ @media (prefers-reduced-motion: reduce) {
578
+ .datametria-sortable-table__tr,
579
+ .datametria-sortable-table__pagination-btn,
580
+ .datametria-sortable-table__search-input,
581
+ .datametria-sortable-table__filter-input {
582
+ transition: none;
583
+ }
584
+ }
585
+ </style>
@@ -39,7 +39,7 @@ withDefaults(defineProps<Props>(), {
39
39
  display: inline-flex;
40
40
  flex-direction: column;
41
41
  align-items: center;
42
- gap: var(--dm-space-2);
42
+ gap: var(--dm-spacing-2, 0.5rem);
43
43
  }
44
44
 
45
45
  .dm-spinner__svg {
@@ -67,20 +67,20 @@ withDefaults(defineProps<Props>(), {
67
67
  }
68
68
 
69
69
  .dm-spinner--primary .dm-spinner__circle {
70
- stroke: var(--dm-primary);
70
+ stroke: var(--dm-primary, #0072CE);
71
71
  }
72
72
 
73
73
  .dm-spinner--secondary .dm-spinner__circle {
74
- stroke: var(--dm-secondary);
74
+ stroke: var(--dm-secondary, #4B0078);
75
75
  }
76
76
 
77
77
  .dm-spinner--white .dm-spinner__circle {
78
- stroke: var(--dm-white);
78
+ stroke: var(--dm-neutral-50, #ffffff);
79
79
  }
80
80
 
81
81
  .dm-spinner__label {
82
- font-size: var(--dm-text-sm);
83
- color: var(--dm-text-secondary);
82
+ font-size: var(--dm-font-size-sm, 0.875rem);
83
+ color: var(--dm-neutral-600, #4b5563);
84
84
  }
85
85
 
86
86
  @keyframes dm-spinner-rotate {
@@ -106,7 +106,7 @@ withDefaults(defineProps<Props>(), {
106
106
 
107
107
  @media (prefers-color-scheme: dark) {
108
108
  .dm-spinner__label {
109
- color: var(--dm-gray-400);
109
+ color: var(--dm-neutral-400, #9ca3af);
110
110
  }
111
111
  }
112
112
  </style>