@innertia-solutions/innertia-nuxt 0.1.9 → 0.1.11

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.
@@ -16,24 +16,43 @@ const emit = defineEmits<{
16
16
 
17
17
  const selectRef = ref<HTMLSelectElement | null>(null)
18
18
 
19
- const reinitHsSelect = async () => {
19
+ /**
20
+ * Init Preline HSSelect una sola vez después del mount.
21
+ * No destruimos ni re-inicializamos en cambios de options — para forzar
22
+ * un re-mount limpio, el consumer puede cambiar el `:key` del componente.
23
+ *
24
+ * Si Preline aún no está disponible (plugin carga async), esperamos.
25
+ */
26
+ const initHsSelect = async () => {
20
27
  await nextTick()
21
28
  const el = selectRef.value
22
29
  if (!el) return
23
30
 
24
- const instance = (window as any).HSSelect?.getInstance?.(el)
25
- if (instance?.destroy) instance.destroy()
31
+ const tryInit = (attempts = 0) => {
32
+ const HSSelect = (window as any).HSSelect
33
+ if (!HSSelect) {
34
+ if (attempts < 20) {
35
+ // Re-intentar hasta 20 frames (~330ms) esperando que cargue el plugin
36
+ requestAnimationFrame(() => tryInit(attempts + 1))
37
+ }
38
+ return
39
+ }
26
40
 
27
- new (window as any).HSSelect(el)
28
- }
41
+ // Evitar doble init si ya hay una instancia registrada
42
+ if (HSSelect.getInstance?.(el)) return
43
+
44
+ try {
45
+ new HSSelect(el)
46
+ } catch (e) {
47
+ console.warn('[FormsSelect] HSSelect init falló:', e)
48
+ }
49
+ }
29
50
 
30
- // Re-initialize when options change (async load)
31
- watch(() => props.options, async () => {
32
- await reinitHsSelect()
33
- }, { deep: true })
51
+ tryInit()
52
+ }
34
53
 
35
54
  onMounted(() => {
36
- reinitHsSelect()
55
+ initHsSelect()
37
56
  })
38
57
 
39
58
  const handleChange = (e: Event) => {
@@ -53,7 +72,7 @@ const handleChange = (e: Event) => {
53
72
  <!-- Select (HSSelect) -->
54
73
  <ClientOnly>
55
74
  <template #fallback>
56
- <div class="h-[38px] bg-surface animate-pulse rounded-lg" />
75
+ <div class="innertia-field bg-surface animate-pulse" />
57
76
  </template>
58
77
 
59
78
  <div :class="['relative', error ? 'select-error' : '']">
@@ -66,9 +85,9 @@ const handleChange = (e: Event) => {
66
85
  data-hs-select='{
67
86
  "placeholder": "Seleccionar...",
68
87
  "toggleTag": "<button type=\"button\" aria-expanded=\"false\"></button>",
69
- "toggleClasses": "hs-select-disabled:pointer-events-none hs-select-disabled:opacity-50 relative py-2 ps-4 pe-9 flex gap-x-2 text-nowrap w-full cursor-pointer bg-card border border-card-line rounded-lg text-start text-sm focus:outline-hidden focus:ring-2 focus:ring-blue-500 dark:text-muted-foreground-1 dark:focus:outline-hidden dark:focus:ring-1 dark:focus:ring-blue-600",
70
- "dropdownClasses": "mt-1 z-50 w-full max-h-72 p-1 space-y-0.5 bg-dropdown border border-dropdown-line rounded-lg overflow-hidden overflow-y-auto shadow-lg",
71
- "optionClasses": "py-2 px-4 w-full text-sm text-foreground cursor-pointer hover:bg-muted-hover rounded-lg focus:outline-hidden focus:bg-muted-hover dark:bg-card",
88
+ "toggleClasses": "innertia-field hs-select-disabled:pointer-events-none hs-select-disabled:opacity-50 aria-expanded:!rounded-b-none aria-expanded:!border-b-transparent relative pe-9 flex gap-x-2 items-center text-nowrap cursor-pointer text-start",
89
+ "dropdownClasses": "-mt-px z-50 w-full max-h-72 py-1 bg-[color:var(--field-dropdown-bg)] border border-[color:var(--field-border)] rounded-b-control overflow-hidden overflow-y-auto shadow-lg",
90
+ "optionClasses": "py-2 px-3 w-full text-sm text-foreground cursor-pointer bg-transparent hover:bg-muted-hover focus:outline-hidden focus:bg-muted-hover",
72
91
  "optionTemplate": "<div class=\"flex justify-between items-center w-full\"><span data-title></span><span class=\"hidden hs-selected:block\"><svg class=\"shrink-0 size-3.5 text-blue-600 dark:text-blue-500\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"20 6 9 17 4 12\"/></svg></span></div>"
73
92
  }'
74
93
  >
@@ -84,17 +103,34 @@ const handleChange = (e: Event) => {
84
103
  </div>
85
104
  </ClientOnly>
86
105
 
87
- <!-- Error -->
88
- <p v-if="error" class="text-xs text-red-500 dark:text-red-400">{{ error }}</p>
106
+ <!-- Error message -->
107
+ <p v-if="error" class="text-xs text-red-500">{{ error }}</p>
89
108
 
90
109
  <!-- Hint -->
91
110
  <p v-else-if="hint" class="text-xs text-muted-foreground">{{ hint }}</p>
92
111
  </div>
93
112
  </template>
94
113
 
95
- <style scoped>
96
- /* Apply error border to the HSSelect toggle button when in error state */
97
- .select-error :deep(button[aria-expanded]) {
98
- border-color: rgb(248 113 113) !important; /* red-400 */
114
+ <style>
115
+ /*
116
+ * Preline HSSelect: cuando el dropdown está abierto, el trigger pierde
117
+ * sus esquinas inferiores y el borde inferior se vuelve invisible para
118
+ * fusionarse visualmente con el dropdown panel.
119
+ *
120
+ * Estilos globales (sin scoped) porque el HSSelect inyecta DOM fuera
121
+ * del componente Vue (button generado, no del template).
122
+ */
123
+ .hs-select [aria-expanded="true"].innertia-field {
124
+ border-bottom-left-radius: 0 !important;
125
+ border-bottom-right-radius: 0 !important;
126
+ border-bottom-color: transparent !important;
127
+ }
128
+
129
+ /* Pegar el dropdown al trigger eliminando cualquier offset/margen que
130
+ * Preline pueda inyectar. Target amplio: cualquier div con la clase
131
+ * `rounded-b-control` dentro del wrapper `.hs-select`. */
132
+ .hs-select div[class*="rounded-b-control"] {
133
+ top: 100% !important;
134
+ margin-top: -1px !important;
99
135
  }
100
136
  </style>
@@ -20,6 +20,8 @@ const props = defineProps({
20
20
  multiple: { type: Boolean, default: false },
21
21
  searchable: { type: Boolean, default: true },
22
22
  disabled: { type: Boolean, default: false },
23
+ loading: { type: Boolean, default: false },
24
+ name: { type: String, default: "" },
23
25
  clearable: { type: Boolean, default: false },
24
26
  searchPlaceholder: { type: String, default: "Buscar..." },
25
27
  minSearchLength: { type: Number, default: 0 },
@@ -336,23 +338,22 @@ const validationClasses = computed(() => {
336
338
 
337
339
  // Combined select classes
338
340
  const selectClasses = computed(() => {
339
- let base, focus, disabled;
340
-
341
- base =
342
- "relative w-full rounded-lg border bg-card transition-colors cursor-pointer text-foreground";
343
-
344
- if (props.multiple && props.tagsMode) {
345
- base += " px-3 pe-8 min-h-[2.375rem] flex items-center flex-wrap text-nowrap";
346
- focus = "focus:outline-none focus:border-gray-400";
347
- } else {
348
- focus = "focus:outline-none focus:ring-0 focus:border-gray-400";
349
- }
350
-
351
- disabled = props.disabled || props.loading ? "opacity-50 cursor-not-allowed" : "";
352
-
353
- const sizeClass = props.multiple && props.tagsMode ? "" : sizeClasses.value;
354
-
355
- return `${base} ${sizeClass} ${validationClasses.value} ${focus} ${disabled} ${props.class}`;
341
+ // Apariencia unificada con el resto de los fields del DS.
342
+ // En modo tags-multiple desactivamos la altura fija porque el control crece
343
+ // según la cantidad de chips seleccionados.
344
+ const base = props.multiple && props.tagsMode
345
+ ? "innertia-field relative cursor-pointer text-start !ps-3.5 !pe-9 flex items-center flex-wrap gap-2 text-nowrap !h-auto min-h-[var(--field-height)] py-2.5"
346
+ : "innertia-field relative cursor-pointer text-start pe-9 flex items-center gap-x-2";
347
+
348
+ // Cuando el dropdown está abierto, el trigger se "funde" visualmente con
349
+ // el panel: deja de redondear sus esquinas inferiores y queda como una sola pieza.
350
+ const openStyles = isOpen.value
351
+ ? "!rounded-b-none border-b-transparent"
352
+ : "";
353
+
354
+ const disabled = props.disabled || props.loading ? "opacity-50 cursor-not-allowed" : "";
355
+
356
+ return `${base} ${openStyles} ${validationClasses.value} ${disabled} ${props.class ?? ""}`;
356
357
  });
357
358
 
358
359
  // Filtered options — backend does the filtering, serverOptions IS the filteredOptions
@@ -403,8 +404,9 @@ const selectOption = (option) => {
403
404
  if (option.disabled) return;
404
405
 
405
406
  if (props.multiple) {
407
+ const target = getOptionValue(option);
406
408
  const index = selectedOptions.value.findIndex(
407
- (opt) => opt.id === option.id
409
+ (opt) => getOptionValue(opt) === target
408
410
  );
409
411
  if (index > -1) {
410
412
  selectedOptions.value.splice(index, 1);
@@ -428,7 +430,8 @@ const selectOption = (option) => {
428
430
 
429
431
  const removeTag = (option) => {
430
432
  if (props.disabled) return;
431
- const index = selectedOptions.value.findIndex((opt) => opt.id === option.id);
433
+ const target = getOptionValue(option);
434
+ const index = selectedOptions.value.findIndex((opt) => getOptionValue(opt) === target);
432
435
  if (index > -1) {
433
436
  selectedOptions.value.splice(index, 1);
434
437
  localValue.value = selectedOptions.value.map((opt) => getOptionValue(opt));
@@ -443,7 +446,8 @@ const clearSelection = () => {
443
446
  };
444
447
 
445
448
  const isOptionSelected = (option) => {
446
- return selectedOptions.value.some((opt) => opt.id === option.id);
449
+ const target = getOptionValue(option);
450
+ return selectedOptions.value.some((opt) => getOptionValue(opt) === target);
447
451
  };
448
452
 
449
453
  // Keyboard navigation
@@ -524,27 +528,28 @@ onMounted(() => {
524
528
  <!-- Selected values display -->
525
529
  <div v-else class="flex items-center justify-between w-full">
526
530
  <!-- Tags mode for multiple selection -->
527
- <div v-if="multiple && tagsMode && selectedOptions.length" class="flex flex-wrap items-center gap-1 flex-1">
531
+ <div v-if="multiple && tagsMode && selectedOptions.length" class="flex flex-wrap items-center gap-1.5 flex-1">
528
532
  <div v-for="option in selectedOptions" :key="option.id"
529
- class="flex flex-nowrap items-center relative z-10 bg-card border border-card-line rounded-full p-1 m-1">
533
+ class="inline-flex items-center gap-1.5 bg-muted border border-[color:var(--field-border)] rounded-badge ps-2.5 pe-1 py-1 text-xs leading-none">
530
534
  <!-- Avatar/Icon -->
531
- <div v-if="option.avatar || option.icon" class="size-6 me-1">
535
+ <div v-if="option.avatar || option.icon" class="size-4">
532
536
  <img v-if="option.avatar" :src="option.avatar" :alt="getOptionLabel(option)"
533
- class="inline-block rounded-full size-6" />
534
- <div v-else-if="option.icon" v-html="option.icon" class="size-6"></div>
537
+ class="inline-block rounded-avatar size-4" />
538
+ <div v-else-if="option.icon" v-html="option.icon" class="size-4"></div>
535
539
  </div>
536
540
 
537
541
  <!-- Label -->
538
- <div class="whitespace-nowrap text-foreground text-sm pl-1.5">
542
+ <span class="whitespace-nowrap text-foreground">
539
543
  <slot name="tag" :option="option">
540
544
  {{ getOptionLabel(option) }}
541
545
  </slot>
542
- </div>
546
+ </span>
543
547
 
544
548
  <!-- Remove button -->
545
549
  <button type="button" v-if="!disabled" @click.stop="removeTag(option)"
546
- class="inline-flex shrink-0 justify-center items-center size-5 ms-2 rounded-full text-foreground bg-surface-1 hover:bg-surface-1 focus:outline-none focus:ring-2 focus:ring-gray-400 text-sm cursor-pointer">
547
- <svg class="shrink-0 size-3" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
550
+ class="inline-flex shrink-0 justify-center items-center size-4 rounded-badge text-muted-foreground hover:text-foreground focus:outline-none focus-visible:outline-none cursor-pointer"
551
+ tabindex="-1">
552
+ <svg class="shrink-0 size-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
548
553
  fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
549
554
  <path d="M18 6 6 18" />
550
555
  <path d="m6 6 12 12" />
@@ -592,12 +597,12 @@ onMounted(() => {
592
597
  enter-to-class="transform opacity-100 scale-100" leave-active-class="transition ease-in duration-75"
593
598
  leave-from-class="transform opacity-100 scale-100" leave-to-class="transform opacity-0 scale-95">
594
599
  <div v-show="isOpen" ref="optionsListRef" @scroll="handleScroll"
595
- class="absolute z-50 w-full mt-1 bg-dropdown border border-dropdown-line rounded-xl shadow-xl max-h-60 overflow-auto">
600
+ class="absolute z-50 w-full -mt-px bg-[color:var(--field-dropdown-bg)] border border-[color:var(--field-border)] rounded-b-control shadow-lg max-h-60 overflow-auto">
596
601
  <!-- Search input -->
597
602
  <div v-if="searchable" class="p-2 border-b border-card-line relative">
598
603
  <input type="text" :value="searchQuery" :placeholder="searchPlaceholder" @input="handleSearchInput"
599
604
  @keydown.enter.prevent
600
- class="w-full px-3 py-2.5 pr-8 border border-card-line rounded-lg focus:outline-none focus:ring-0 focus:border-gray-400 dark:bg-surface dark:text-white text-sm transition-colors" />
605
+ class="innertia-field innertia-field-sm pe-8" />
601
606
  <button type="button" v-if="searchQuery" @click.prevent="clearSearch"
602
607
  class="absolute top-1/2 right-4 -translate-y-1/2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300">
603
608
  <svg class="size-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
@@ -635,7 +640,7 @@ onMounted(() => {
635
640
  <!-- Multiple selection checkbox -->
636
641
  <div v-if="multiple" class="mr-2">
637
642
  <div :class="[
638
- 'w-4 h-4 rounded-lg border-2 flex items-center justify-center',
643
+ 'w-4 h-4 rounded-control border-2 flex items-center justify-center',
639
644
  isOptionSelected(option)
640
645
  ? 'bg-slate-600 border-slate-600 text-white'
641
646
  : 'border-card-line',
@@ -652,7 +657,7 @@ onMounted(() => {
652
657
  <div class="flex items-center flex-1">
653
658
  <!-- Avatar/Icon -->
654
659
  <div v-if="option.avatar || option.icon" class="mr-3">
655
- <img v-if="option.avatar" :src="option.avatar" :alt="option.label" class="w-6 h-6 rounded-full" />
660
+ <img v-if="option.avatar" :src="option.avatar" :alt="option.label" class="w-6 h-6 rounded-avatar" />
656
661
  <component v-else-if="option.icon" :is="option.icon" class="w-5 h-5 text-gray-500" />
657
662
  </div>
658
663
 
@@ -702,7 +707,7 @@ onMounted(() => {
702
707
  <!-- No data message -->
703
708
  <div v-else-if="!serverOptions.length && !isFetching" class="px-3 py-8 text-center">
704
709
  <div class="flex flex-col items-center gap-y-2">
705
- <div class="size-10 rounded-full bg-muted flex items-center justify-center">
710
+ <div class="size-10 rounded-avatar bg-muted flex items-center justify-center">
706
711
  <svg class="size-5 text-muted-foreground-2" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
707
712
  stroke-width="1.5" stroke="currentColor">
708
713
  <path stroke-linecap="round" stroke-linejoin="round"
@@ -133,17 +133,17 @@ function groupSelectedCount(group) {
133
133
 
134
134
  <!-- Search -->
135
135
  <div class="relative">
136
- <IconSearch class="absolute left-2.5 top-1/2 -translate-y-1/2 size-3 text-slate-400 pointer-events-none" />
136
+ <IconSearch class="absolute right-2.5 top-1/2 -translate-y-1/2 size-3 text-muted-foreground pointer-events-none" />
137
137
  <input
138
138
  v-model="search"
139
- type="search"
139
+ type="text"
140
140
  placeholder="Buscar permiso…"
141
- class="w-full pl-7 pr-3 py-1.5 text-xs bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-700 rounded-md text-slate-700 dark:text-slate-300 placeholder-slate-400 focus:outline-none focus:ring-1 focus:ring-blue-500/40 focus:border-blue-400"
141
+ class="innertia-field innertia-field-sm pe-7"
142
142
  />
143
143
  </div>
144
144
 
145
145
  <!-- Tree -->
146
- <div class="rounded-md border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 overflow-hidden text-sm">
146
+ <div class="rounded-card border border-card-line bg-card overflow-hidden text-sm">
147
147
 
148
148
  <div v-if="visibleApps.length === 0" class="px-3 py-6 text-center text-xs text-slate-400 dark:text-slate-500">
149
149
  {{ search ? 'Sin resultados.' : readonly ? 'Sin permisos asignados.' : 'Sin permisos disponibles.' }}
@@ -82,19 +82,19 @@ defineExpose({ reload })
82
82
  <template>
83
83
  <div class="flex flex-col gap-3">
84
84
  <div v-if="showSearch" class="relative max-w-sm">
85
- <div class="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none">
86
- <IconSearch class="size-4 text-slate-400" stroke="1.5" />
85
+ <div class="absolute inset-y-0 end-0 flex items-center pe-3 pointer-events-none">
86
+ <IconSearch class="size-4 text-muted-foreground" stroke="1.5" />
87
87
  </div>
88
88
  <input
89
89
  v-model="search"
90
- type="search"
90
+ type="text"
91
91
  :placeholder="searchPlaceholder"
92
- class="block w-full rounded-lg border border-card-line bg-card text-foreground py-1.5 ps-9 pe-4 text-sm focus:outline-none focus:ring-1 focus:ring-indigo-500"
92
+ class="innertia-field innertia-field-sm pe-9"
93
93
  />
94
94
  </div>
95
95
 
96
96
  <!-- Dense table wrapper — override Table's default padding with compact styles -->
97
- <div class="overflow-x-auto border border-card-line rounded-xl">
97
+ <div class="overflow-x-auto border border-card-line rounded-card">
98
98
  <Table
99
99
  ref="tableRef"
100
100
  :endpoint="endpoint"
@@ -31,7 +31,7 @@ const exportTable = (format) => {
31
31
  <button
32
32
  id="hs-as-table-table-export-dropdown"
33
33
  type="button"
34
- class="py-1.5 sm:py-2 px-2.5 inline-flex items-center gap-x-1.5 text-sm sm:text-xs font-medium rounded-lg border border-card-line bg-card text-foreground shadow-2xs hover:bg-muted-hover disabled:opacity-50 disabled:pointer-events-none focus:outline-hidden focus:bg-muted-hover"
34
+ class="py-1.5 sm:py-2 px-2.5 inline-flex items-center gap-x-1.5 text-sm sm:text-xs font-medium rounded-control border border-card-line bg-card text-foreground shadow-2xs hover:bg-muted-hover disabled:opacity-50 disabled:pointer-events-none focus:outline-hidden focus:bg-muted-hover"
35
35
  aria-haspopup="menu"
36
36
  aria-expanded="false"
37
37
  aria-label="Dropdown"
@@ -40,14 +40,14 @@ const exportTable = (format) => {
40
40
  Exportar
41
41
  </button>
42
42
  <div
43
- class="hs-dropdown-menu transition-[opacity,margin] duration hs-dropdown-open:opacity-100 opacity-0 hidden divide-y divide-card-line min-w-48 z-10 bg-dropdown shadow-md rounded-lg p-2 mt-2 dark:border dark:border-dropdown-line border-t border-card-line"
43
+ class="hs-dropdown-menu transition-[opacity,margin] duration hs-dropdown-open:opacity-100 opacity-0 hidden divide-y divide-card-line min-w-48 z-10 bg-dropdown shadow-md rounded-popover p-2 mt-2 dark:border dark:border-dropdown-line border-t border-card-line"
44
44
  role="menu"
45
45
  aria-orientation="vertical"
46
46
  aria-labelledby="hs-as-table-table-export-dropdown"
47
47
  >
48
48
  <div class="py-2 first:pt-0 last:pb-0">
49
49
  <a
50
- class="flex items-center gap-x-3 py-2 px-3 rounded-lg text-sm text-foreground hover:bg-muted-hover focus:outline-hidden focus:bg-muted-hover"
50
+ class="flex items-center gap-x-3 py-2 px-3 rounded-control text-sm text-foreground hover:bg-muted-hover focus:outline-hidden focus:bg-muted-hover"
51
51
  href="#"
52
52
  @click="exportTable('xlsx')"
53
53
  >
@@ -55,7 +55,7 @@ const exportTable = (format) => {
55
55
  Excel
56
56
  </a>
57
57
  <a
58
- class="flex items-center gap-x-3 py-2 px-3 rounded-lg text-sm text-foreground hover:bg-muted-hover focus:outline-hidden focus:bg-muted-hover"
58
+ class="flex items-center gap-x-3 py-2 px-3 rounded-control text-sm text-foreground hover:bg-muted-hover focus:outline-hidden focus:bg-muted-hover"
59
59
  href="#"
60
60
  @click="exportTable('csv')"
61
61
  >
@@ -63,7 +63,7 @@ const exportTable = (format) => {
63
63
  CSV
64
64
  </a>
65
65
  <a
66
- class="flex items-center gap-x-3 py-2 px-3 rounded-lg text-sm text-foreground hover:bg-muted-hover focus:outline-hidden focus:bg-muted-hover"
66
+ class="flex items-center gap-x-3 py-2 px-3 rounded-control text-sm text-foreground hover:bg-muted-hover focus:outline-hidden focus:bg-muted-hover"
67
67
  href="#"
68
68
  @click="exportTable('pdf')"
69
69
  >
@@ -71,7 +71,7 @@ const exportTable = (format) => {
71
71
  PDF
72
72
  </a>
73
73
  <a
74
- class="flex items-center gap-x-3 py-2 px-3 rounded-lg text-sm text-foreground hover:bg-muted-hover focus:outline-hidden focus:bg-muted-hover"
74
+ class="flex items-center gap-x-3 py-2 px-3 rounded-control text-sm text-foreground hover:bg-muted-hover focus:outline-hidden focus:bg-muted-hover"
75
75
  href="#"
76
76
  @click="exportTable('json')"
77
77
  >
@@ -90,7 +90,7 @@ const exportTable = (format) => {
90
90
  <input
91
91
  type="checkbox"
92
92
  v-model="exportAllPages"
93
- class="shrink-0 border-card-line rounded-sm text-blue-600 focus:ring-blue-500 dark:bg-card"
93
+ class="shrink-0 border-card-line rounded-control text-blue-600 focus:ring-blue-500 dark:bg-card"
94
94
  />
95
95
  Todas las páginas
96
96
  </label>
@@ -101,7 +101,7 @@ const exportTable = (format) => {
101
101
  <input
102
102
  type="checkbox"
103
103
  v-model="exportFilteredRows"
104
- class="shrink-0 border-card-line rounded-sm text-blue-600 focus:ring-blue-500 dark:bg-card"
104
+ class="shrink-0 border-card-line rounded-control text-blue-600 focus:ring-blue-500 dark:bg-card"
105
105
  />
106
106
  Solo filas filtradas
107
107
  </label>
@@ -248,7 +248,7 @@ defineExpose({ getSelectedRows, reload, clearCache, exportTable, tableRef, close
248
248
 
249
249
  <template>
250
250
  <div class="relative" ref="containerRef">
251
- <div class="bg-card border border-card-line rounded-2xl shadow-sm overflow-hidden">
251
+ <div class="bg-card border border-card-line rounded-card shadow-sm overflow-hidden">
252
252
 
253
253
  <!-- ── Toolbar ──────────────────────────────────────────────────────── -->
254
254
  <div class="flex flex-wrap items-center gap-2 px-4 py-3 border-b border-card-line">
@@ -264,7 +264,7 @@ defineExpose({ getSelectedRows, reload, clearCache, exportTable, tableRef, close
264
264
  type="button"
265
265
  @click="showFilterPanel = !showFilterPanel"
266
266
  :class="[
267
- 'py-1.5 px-3 inline-flex items-center gap-1.5 text-sm font-medium rounded-lg border transition-colors',
267
+ 'py-1.5 px-3 inline-flex items-center gap-1.5 text-sm font-medium rounded-control border transition-colors',
268
268
  showFilterPanel || activeFilterCount > 0
269
269
  ? 'border-primary/50 bg-primary/5 text-primary dark:bg-primary/10'
270
270
  : 'border-card-line bg-card text-muted-foreground-1 hover:bg-muted-hover',
@@ -286,7 +286,7 @@ defineExpose({ getSelectedRows, reload, clearCache, exportTable, tableRef, close
286
286
  <button
287
287
  v-if="showSaveView"
288
288
  type="button"
289
- class="py-1.5 px-3 inline-flex items-center gap-1.5 text-sm font-medium rounded-lg border border-card-line bg-card text-muted-foreground-1 hover:bg-muted-hover transition-colors"
289
+ class="py-1.5 px-3 inline-flex items-center gap-1.5 text-sm font-medium rounded-control border border-card-line bg-card text-muted-foreground-1 hover:bg-muted-hover transition-colors"
290
290
  @click="emit('save-view')"
291
291
  >
292
292
  <IconBookmark class="size-4" stroke="1.5" />
@@ -300,7 +300,7 @@ defineExpose({ getSelectedRows, reload, clearCache, exportTable, tableRef, close
300
300
  type="button"
301
301
  @click="showColumnPanel = !showColumnPanel"
302
302
  :class="[
303
- 'py-1.5 px-3 inline-flex items-center gap-1.5 text-sm font-medium rounded-lg border transition-colors',
303
+ 'py-1.5 px-3 inline-flex items-center gap-1.5 text-sm font-medium rounded-control border transition-colors',
304
304
  showColumnPanel
305
305
  ? 'border-primary/50 bg-primary/5 text-primary dark:bg-primary/10'
306
306
  : 'border-card-line bg-card text-muted-foreground-1 hover:bg-muted-hover',
@@ -333,7 +333,7 @@ defineExpose({ getSelectedRows, reload, clearCache, exportTable, tableRef, close
333
333
  type="button"
334
334
  @click="openChip = openChip === chip.key ? null : chip.key"
335
335
  :class="[
336
- 'inline-flex items-center gap-1 px-2.5 py-1 rounded-lg text-sm border transition-colors',
336
+ 'inline-flex items-center gap-1 px-2.5 py-1 rounded-badge text-sm border transition-colors',
337
337
  chipValues[chip.key]
338
338
  ? 'border-primary/50 bg-primary/5 text-primary font-medium dark:bg-primary/10'
339
339
  : 'border-card-line bg-card text-muted-foreground hover:bg-muted-hover',
@@ -358,7 +358,7 @@ defineExpose({ getSelectedRows, reload, clearCache, exportTable, tableRef, close
358
358
  <div
359
359
  v-if="openChip === chip.key"
360
360
  v-click-outside="() => openChip = null"
361
- class="absolute top-full left-0 z-50 mt-1 bg-dropdown border border-dropdown-line rounded-xl shadow-xl py-1 min-w-40"
361
+ class="absolute top-full left-0 z-50 mt-1 bg-dropdown border border-dropdown-line rounded-popover shadow-xl py-1 min-w-40"
362
362
  >
363
363
  <button
364
364
  type="button"
@@ -405,7 +405,7 @@ defineExpose({ getSelectedRows, reload, clearCache, exportTable, tableRef, close
405
405
  <div
406
406
  v-if="showFilterPanel"
407
407
  ref="filterPanelRef"
408
- class="absolute top-14 left-4 z-50 bg-dropdown border border-dropdown-line rounded-xl shadow-2xl p-3 min-w-64 max-h-96 overflow-y-auto"
408
+ class="absolute top-14 left-4 z-50 bg-dropdown border border-dropdown-line rounded-popover shadow-2xl p-3 min-w-64 max-h-96 overflow-y-auto"
409
409
  >
410
410
  <p class="text-[10px] font-bold text-muted-foreground uppercase tracking-widest mb-3 px-1">Filtros avanzados</p>
411
411
  <TableFilter v-model="activeFilters" :columns="filtersConfig" />
@@ -508,7 +508,7 @@ defineExpose({ getSelectedRows, reload, clearCache, exportTable, tableRef, close
508
508
  <div
509
509
  v-if="showColumnPanel"
510
510
  ref="columnPanelRef"
511
- class="fixed z-50 bg-dropdown border border-dropdown-line rounded-xl shadow-2xl p-3 min-w-56 max-h-80 overflow-y-auto"
511
+ class="fixed z-50 bg-dropdown border border-dropdown-line rounded-popover shadow-2xl p-3 min-w-56 max-h-80 overflow-y-auto"
512
512
  :style="columnPanelStyle"
513
513
  >
514
514
  <p class="text-[10px] font-bold text-muted-foreground uppercase tracking-widest mb-2 px-1">Columnas visibles</p>
@@ -520,7 +520,7 @@ defineExpose({ getSelectedRows, reload, clearCache, exportTable, tableRef, close
520
520
  @dragover="(e) => onDragOver(e, col.key)"
521
521
  @dragleave="onDragLeave"
522
522
  @drop="onDrop(col.key)"
523
- class="flex items-center gap-2 py-1.5 px-2 rounded-lg select-none transition-colors"
523
+ class="flex items-center gap-2 py-1.5 px-2 rounded-control select-none transition-colors"
524
524
  :class="dragOverKey === col.key ? 'bg-primary/10 ring-1 ring-primary/30' : 'hover:bg-muted-hover cursor-grab'"
525
525
  >
526
526
  <IconGripVertical class="size-4 text-muted-foreground-2 shrink-0" />
@@ -55,7 +55,7 @@ const displayText = computed(() => {
55
55
 
56
56
  const selectClasses = computed(() => {
57
57
  const base =
58
- "relative w-full rounded-lg border bg-card transition-colors cursor-pointer text-foreground py-2 px-3 text-sm focus:outline-none focus:ring-0 focus:border-gray-400";
58
+ "innertia-field relative cursor-pointer text-foreground";
59
59
  const validation = "border-card-line";
60
60
  const disabled = props.disabled ? "opacity-50 cursor-not-allowed" : "";
61
61
  return `${base} ${validation} ${disabled}`;
@@ -121,7 +121,7 @@ onUnmounted(() => document.removeEventListener("mousedown", handleClickOutside))
121
121
 
122
122
  <span
123
123
  v-if="clearable && hasSelection && !disabled"
124
- class="absolute end-8 top-1/2 -translate-y-1/2 hover:bg-muted-hover rounded-full p-1 transition-colors focus:outline-none focus:ring-1 focus:ring-slate-400"
124
+ class="absolute end-8 top-1/2 -translate-y-1/2 hover:bg-muted-hover rounded-avatar p-1 transition-colors focus:outline-none focus:ring-1 focus:ring-slate-400"
125
125
  role="button"
126
126
  tabindex="0"
127
127
  @click.stop="clearSelection"
@@ -169,7 +169,7 @@ onUnmounted(() => document.removeEventListener("mousedown", handleClickOutside))
169
169
  <div
170
170
  v-show="isOpen"
171
171
  :class="[
172
- 'absolute z-50 w-full mt-1 bg-dropdown border border-dropdown-line rounded-xl shadow-xl max-h-60 overflow-auto',
172
+ 'absolute z-50 w-full -mt-px bg-[color:var(--field-dropdown-bg)] border border-[color:var(--field-border)] rounded-b-control shadow-lg max-h-60 overflow-auto',
173
173
  menuClass,
174
174
  ]"
175
175
  >
@@ -28,14 +28,14 @@ defineExpose({ reload, clearCache })
28
28
  <template>
29
29
  <div class="flex flex-col gap-4">
30
30
  <div v-if="showSearch" class="relative">
31
- <div class="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none">
32
- <IconSearch class="size-4 text-slate-400" stroke="1.5" />
31
+ <div class="absolute inset-y-0 end-0 flex items-center pe-3 pointer-events-none">
32
+ <IconSearch class="size-4 text-muted-foreground" stroke="1.5" />
33
33
  </div>
34
34
  <input
35
35
  v-model="search"
36
- type="search"
36
+ type="text"
37
37
  :placeholder="searchPlaceholder"
38
- class="block w-full rounded-lg border border-card-line bg-card text-foreground py-2 ps-10 pe-4 text-sm focus:outline-none focus:ring-1 focus:ring-indigo-500"
38
+ class="innertia-field pe-10"
39
39
  />
40
40
  </div>
41
41
 
@@ -135,7 +135,7 @@ defineExpose({ reload, rows })
135
135
  <div
136
136
  v-for="state in states"
137
137
  :key="state.key"
138
- class="flex-shrink-0 w-72 flex flex-col rounded-xl border border-card-line overflow-hidden transition-shadow"
138
+ class="flex-shrink-0 w-72 flex flex-col rounded-card border border-card-line overflow-hidden transition-shadow"
139
139
  :class="dragOverState === state.key ? getColors(state).over : ''"
140
140
  @dragover="onDragOver($event, state.key)"
141
141
  @dragleave="onDragLeave"
@@ -144,7 +144,7 @@ defineExpose({ reload, rows })
144
144
  <!-- Column header -->
145
145
  <div :class="['px-4 py-3 flex items-center justify-between', getColors(state).header]">
146
146
  <span class="font-semibold text-sm">{{ state.label }}</span>
147
- <span class="text-xs font-bold bg-white/60 dark:bg-black/20 rounded-full px-2 py-0.5">
147
+ <span class="text-xs font-bold bg-white/60 dark:bg-black/20 rounded-badge px-2 py-0.5">
148
148
  {{ columnRows[state.key]?.length ?? 0 }}
149
149
  </span>
150
150
  </div>
@@ -156,7 +156,7 @@ defineExpose({ reload, rows })
156
156
  :key="row.id"
157
157
  draggable="true"
158
158
  @dragstart="onDragStart(row, state.key)"
159
- class="bg-card border border-card-line rounded-lg p-3 cursor-grab active:cursor-grabbing hover:shadow-md transition-shadow select-none"
159
+ class="bg-card border border-card-line rounded-card p-3 cursor-grab active:cursor-grabbing hover:shadow-md transition-shadow select-none"
160
160
  :class="draggedId === row.id ? 'opacity-40' : ''"
161
161
  @click="emit('card-click', row)"
162
162
  >
@@ -69,14 +69,14 @@ defineExpose({ reload, rows })
69
69
  <template>
70
70
  <div class="flex flex-col gap-4">
71
71
  <div v-if="showSearch" class="relative">
72
- <div class="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none">
73
- <IconSearch class="size-4 text-slate-400" stroke="1.5" />
72
+ <div class="absolute inset-y-0 end-0 flex items-center pe-3 pointer-events-none">
73
+ <IconSearch class="size-4 text-muted-foreground" stroke="1.5" />
74
74
  </div>
75
75
  <input
76
76
  v-model="search"
77
- type="search"
77
+ type="text"
78
78
  :placeholder="searchPlaceholder"
79
- class="block w-full rounded-lg border border-card-line bg-card text-foreground py-2 ps-10 pe-4 text-sm focus:outline-none focus:ring-1 focus:ring-indigo-500"
79
+ class="innertia-field pe-10"
80
80
  />
81
81
  </div>
82
82
 
@@ -85,7 +85,7 @@ defineExpose({ reload, rows })
85
85
  <div
86
86
  v-for="i in 8"
87
87
  :key="i"
88
- class="h-16 bg-surface rounded-xl animate-pulse"
88
+ class="h-16 bg-surface rounded-card animate-pulse"
89
89
  />
90
90
  </div>
91
91
 
@@ -94,7 +94,7 @@ defineExpose({ reload, rows })
94
94
  <div
95
95
  v-for="row in rows"
96
96
  :key="row.id ?? JSON.stringify(row)"
97
- class="bg-card border border-card-line rounded-xl px-4 py-3 hover:border-indigo-300 dark:hover:border-indigo-600 transition-colors cursor-pointer"
97
+ class="bg-card border border-card-line rounded-card px-4 py-3 hover:border-indigo-300 dark:hover:border-indigo-600 transition-colors cursor-pointer"
98
98
  @click="emit('row-click', row)"
99
99
  >
100
100
  <slot name="item" :row="row">