@innertia-solutions/nuxt-theme-spark 0.1.114 → 0.1.116

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.
@@ -1,205 +1,170 @@
1
1
  <script setup>
2
2
  import {
3
3
  IconFileTypeXls,
4
- IconCodeDots,
5
- IconFileTypePdf,
6
4
  IconFileTypeCsv,
5
+ IconFileTypePdf,
6
+ IconCodeDots,
7
7
  IconDownload,
8
8
  } from '@tabler/icons-vue'
9
9
 
10
- // Modal with format selector, pre-filled filename, columns checkboxes
11
10
  const props = defineProps({
12
11
  tableRef: { type: Object, default: null },
13
- name: { type: String, default: 'export' },
14
- columns: { type: Array, default: () => [] }, // [{ key, label }]
12
+ name: { type: String, default: 'export' },
13
+ columns: { type: Array, default: () => [] },
15
14
  })
16
15
 
17
- const isOpen = ref(false)
18
- const format = ref('xlsx')
19
- const filename = ref(props.name)
16
+ const isOpen = ref(false)
17
+ const format = ref('xlsx')
18
+ const filename = ref(props.name)
20
19
  const selectedColumns = ref([])
21
20
 
22
- watch(() => props.columns, (cols) => {
23
- selectedColumns.value = cols.map(c => c.key)
24
- }, { immediate: true })
25
-
21
+ watch(() => props.columns, (cols) => { selectedColumns.value = cols.map(c => c.key) }, { immediate: true })
26
22
  watch(() => props.name, (v) => { filename.value = v })
27
23
 
28
- const formats = [
29
- { value: 'xlsx', label: 'Excel', icon: 'xlsx' },
30
- { value: 'csv', label: 'CSV', icon: 'csv' },
31
- { value: 'pdf', label: 'PDF', icon: 'pdf' },
32
- { value: 'json', label: 'JSON', icon: 'json' },
33
- ]
34
-
35
24
  const toggleColumn = (key) => {
36
25
  const idx = selectedColumns.value.indexOf(key)
37
26
  if (idx >= 0) selectedColumns.value.splice(idx, 1)
38
27
  else selectedColumns.value.push(key)
39
28
  }
40
-
41
- const toggleAll = () => {
42
- if (selectedColumns.value.length === props.columns.length)
43
- selectedColumns.value = []
44
- else
45
- selectedColumns.value = props.columns.map(c => c.key)
29
+ const allSelected = computed(() => selectedColumns.value.length === props.columns.length)
30
+ const toggleAll = () => {
31
+ selectedColumns.value = allSelected.value ? [] : props.columns.map(c => c.key)
46
32
  }
47
33
 
48
- const allSelected = computed(() => selectedColumns.value.length === props.columns.length)
49
- const indeterminate = computed(() => selectedColumns.value.length > 0 && !allSelected.value)
34
+ const formats = [
35
+ { value: 'xlsx', label: 'Excel' },
36
+ { value: 'csv', label: 'CSV' },
37
+ { value: 'pdf', label: 'PDF' },
38
+ { value: 'json', label: 'JSON' },
39
+ ]
50
40
 
51
41
  const doExport = () => {
52
- if (props.tableRef) {
53
- props.tableRef.exportTable(format.value, true, true)
54
- }
42
+ props.tableRef?.exportTable(format.value, true, true, selectedColumns.value)
55
43
  isOpen.value = false
56
44
  }
57
45
 
58
- const open = () => { isOpen.value = true }
46
+ const panelRef = ref(null)
47
+ const triggerRef = ref(null)
59
48
 
60
- defineExpose({ open })
49
+ const onOutsideClick = (e) => {
50
+ if (
51
+ panelRef.value && !panelRef.value.contains(e.target) &&
52
+ triggerRef.value && !triggerRef.value.contains(e.target)
53
+ ) {
54
+ isOpen.value = false
55
+ }
56
+ }
57
+
58
+ watch(isOpen, (v) => {
59
+ if (v) document.addEventListener('mousedown', onOutsideClick)
60
+ else document.removeEventListener('mousedown', onOutsideClick)
61
+ })
62
+
63
+ defineExpose({ open: () => { isOpen.value = true } })
61
64
  </script>
62
65
 
63
66
  <template>
64
- <div>
67
+ <div class="relative">
65
68
  <button
69
+ ref="triggerRef"
66
70
  type="button"
67
- @click="isOpen = true"
68
- class="py-1.5 sm:py-2 px-2.5 inline-flex items-center gap-x-1.5 text-sm 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"
71
+ @click="isOpen = !isOpen"
72
+ :class="[
73
+ 'py-1.5 px-3 inline-flex items-center gap-2 text-sm font-medium rounded-lg border transition-colors',
74
+ isOpen
75
+ ? 'border-blue-500 bg-blue-50 text-blue-700 dark:bg-blue-900/20 dark:border-blue-500 dark:text-blue-300'
76
+ : 'border-card-line bg-card text-muted-foreground-1 hover:bg-muted-hover'
77
+ ]"
69
78
  >
70
- <IconDownload class="shrink-0 size-4" stroke="1.5" />
79
+ <IconDownload class="size-4" stroke="1.5" />
71
80
  Exportar
72
81
  </button>
73
82
 
74
- <!-- Modal -->
75
- <Teleport to="body">
76
- <Transition
77
- enter-active-class="transition ease-out duration-200"
78
- enter-from-class="opacity-0"
79
- enter-to-class="opacity-100"
80
- leave-active-class="transition ease-in duration-150"
81
- leave-from-class="opacity-100"
82
- leave-to-class="opacity-0"
83
+ <Transition
84
+ enter-active-class="transition ease-out duration-150"
85
+ enter-from-class="opacity-0 translate-y-1 scale-95"
86
+ enter-to-class="opacity-100 translate-y-0 scale-100"
87
+ leave-active-class="transition ease-in duration-100"
88
+ leave-from-class="opacity-100 translate-y-0 scale-100"
89
+ leave-to-class="opacity-0 translate-y-1 scale-95"
90
+ >
91
+ <div
92
+ v-if="isOpen"
93
+ ref="panelRef"
94
+ class="absolute top-full right-0 z-50 mt-1.5 bg-dropdown border border-dropdown-line rounded-xl shadow-2xl p-3 w-64"
83
95
  >
84
- <div
85
- v-if="isOpen"
86
- class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/40 backdrop-blur-sm"
87
- @click.self="isOpen = false"
88
- >
89
- <Transition
90
- enter-active-class="transition ease-out duration-200"
91
- enter-from-class="opacity-0 scale-95"
92
- enter-to-class="opacity-100 scale-100"
96
+ <p class="text-[10px] font-bold text-muted-foreground uppercase tracking-widest mb-3 px-1">Exportar</p>
97
+
98
+ <!-- Format -->
99
+ <div class="grid grid-cols-4 gap-1.5 mb-3">
100
+ <button
101
+ v-for="f in formats"
102
+ :key="f.value"
103
+ type="button"
104
+ @click="format = f.value"
105
+ :class="[
106
+ 'flex flex-col items-center gap-1 py-2 rounded-lg border text-xs font-medium transition-colors',
107
+ format === f.value
108
+ ? 'border-blue-500 bg-blue-50 text-blue-700 dark:bg-blue-900/20 dark:border-blue-500 dark:text-blue-300'
109
+ : 'border-card-line text-muted-foreground-1 hover:bg-muted-hover'
110
+ ]"
93
111
  >
94
- <div
95
- v-if="isOpen"
96
- class="bg-card rounded-2xl shadow-2xl w-full max-w-md border border-card-line"
112
+ <IconFileTypeXls v-if="f.value === 'xlsx'" class="size-4" stroke="1.5" />
113
+ <IconFileTypeCsv v-else-if="f.value === 'csv'" class="size-4" stroke="1.5" />
114
+ <IconFileTypePdf v-else-if="f.value === 'pdf'" class="size-4" stroke="1.5" />
115
+ <IconCodeDots v-else class="size-4" stroke="1.5" />
116
+ {{ f.label }}
117
+ </button>
118
+ </div>
119
+
120
+ <!-- Filename -->
121
+ <div class="mb-3 px-1">
122
+ <label class="text-[10px] font-bold text-muted-foreground uppercase tracking-widest block mb-1.5">Archivo</label>
123
+ <div class="flex items-center gap-1.5">
124
+ <input
125
+ v-model="filename"
126
+ type="text"
127
+ class="flex-1 rounded-lg border border-card-line bg-card text-foreground py-1.5 px-2.5 text-xs focus:outline-none focus:ring-1 focus:ring-blue-500 min-w-0"
128
+ />
129
+ <span class="text-xs text-muted-foreground shrink-0">.{{ format }}</span>
130
+ </div>
131
+ </div>
132
+
133
+ <!-- Columns -->
134
+ <div v-if="columns.length > 0" class="mb-3 px-1">
135
+ <div class="flex items-center justify-between mb-1.5">
136
+ <label class="text-[10px] font-bold text-muted-foreground uppercase tracking-widest">Columnas</label>
137
+ <button type="button" @click="toggleAll" class="text-[10px] text-blue-600 dark:text-blue-400 hover:underline">
138
+ {{ allSelected ? 'Ninguna' : 'Todas' }}
139
+ </button>
140
+ </div>
141
+ <div class="max-h-32 overflow-y-auto space-y-0.5">
142
+ <label
143
+ v-for="col in columns"
144
+ :key="col.key"
145
+ class="flex items-center gap-2 py-1 px-1.5 rounded-lg hover:bg-muted-hover cursor-pointer"
97
146
  >
98
- <div class="flex items-center justify-between px-6 py-4 border-b border-card-line">
99
- <h3 class="font-semibold text-foreground">Exportar tabla</h3>
100
- <button
101
- type="button"
102
- @click="isOpen = false"
103
- class="p-1.5 rounded-lg hover:bg-muted-hover transition-colors"
104
- >
105
- <svg class="size-4 text-muted-foreground" fill="none" stroke="currentColor" viewBox="0 0 24 24">
106
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
107
- </svg>
108
- </button>
109
- </div>
110
-
111
- <div class="px-6 py-5 space-y-5">
112
- <!-- Format selector -->
113
- <div>
114
- <p class="text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-2">Formato</p>
115
- <div class="grid grid-cols-4 gap-2">
116
- <button
117
- v-for="f in formats"
118
- :key="f.value"
119
- type="button"
120
- @click="format = f.value"
121
- :class="[
122
- 'flex flex-col items-center gap-1.5 p-3 rounded-xl border text-sm font-medium transition-colors',
123
- format === f.value
124
- ? 'border-indigo-500 bg-indigo-50 text-indigo-700 dark:bg-indigo-900/30 dark:border-indigo-500 dark:text-indigo-300'
125
- : 'border-card-line text-muted-foreground-1 hover:bg-muted-hover'
126
- ]"
127
- >
128
- <IconFileTypeXls v-if="f.value === 'xlsx'" class="size-5" stroke="1.5" />
129
- <IconFileTypeCsv v-else-if="f.value === 'csv'" class="size-5" stroke="1.5" />
130
- <IconFileTypePdf v-else-if="f.value === 'pdf'" class="size-5" stroke="1.5" />
131
- <IconCodeDots v-else class="size-5" stroke="1.5" />
132
- {{ f.label }}
133
- </button>
134
- </div>
135
- </div>
136
-
137
- <!-- Filename -->
138
- <div>
139
- <label class="text-xs font-semibold text-muted-foreground uppercase tracking-wider block mb-2">
140
- Nombre de archivo
141
- </label>
142
- <div class="flex items-center gap-2">
143
- <input
144
- v-model="filename"
145
- type="text"
146
- class="flex-1 rounded-lg border border-card-line bg-card text-foreground py-2 px-3 text-sm focus:outline-none focus:ring-1 focus:ring-indigo-500"
147
- />
148
- <span class="text-sm text-muted-foreground">.{{ format }}</span>
149
- </div>
150
- </div>
151
-
152
- <!-- Columns -->
153
- <div v-if="columns.length > 0">
154
- <div class="flex items-center justify-between mb-2">
155
- <p class="text-xs font-semibold text-muted-foreground uppercase tracking-wider">Columnas</p>
156
- <button
157
- type="button"
158
- @click="toggleAll"
159
- class="text-xs text-indigo-600 dark:text-indigo-400 hover:underline"
160
- >
161
- {{ allSelected ? 'Deseleccionar todas' : 'Seleccionar todas' }}
162
- </button>
163
- </div>
164
- <div class="grid grid-cols-2 gap-1.5 max-h-40 overflow-y-auto pr-1">
165
- <label
166
- v-for="col in columns"
167
- :key="col.key"
168
- class="flex items-center gap-2 py-1.5 px-2 rounded-lg hover:bg-muted-hover cursor-pointer"
169
- >
170
- <input
171
- type="checkbox"
172
- :checked="selectedColumns.includes(col.key)"
173
- @change="toggleColumn(col.key)"
174
- class="rounded border-card-line dark:bg-surface text-indigo-600"
175
- />
176
- <span class="text-sm text-foreground truncate">{{ col.label }}</span>
177
- </label>
178
- </div>
179
- </div>
180
- </div>
181
-
182
- <div class="flex justify-end gap-2 px-6 py-4 border-t border-card-line">
183
- <button
184
- type="button"
185
- @click="isOpen = false"
186
- class="py-2 px-4 text-sm font-medium rounded-lg border border-card-line text-muted-foreground-1 hover:bg-muted-hover transition-colors"
187
- >
188
- Cancelar
189
- </button>
190
- <button
191
- type="button"
192
- @click="doExport"
193
- class="py-2 px-4 text-sm font-medium rounded-lg bg-indigo-600 text-white hover:bg-indigo-700 transition-colors inline-flex items-center gap-2"
194
- >
195
- <IconDownload class="size-4" stroke="1.5" />
196
- Exportar
197
- </button>
198
- </div>
199
- </div>
200
- </Transition>
147
+ <input
148
+ type="checkbox"
149
+ :checked="selectedColumns.includes(col.key)"
150
+ @change="toggleColumn(col.key)"
151
+ class="rounded border-card-line bg-surface shrink-0 cursor-pointer"
152
+ />
153
+ <span class="text-xs text-foreground truncate">{{ col.label }}</span>
154
+ </label>
155
+ </div>
201
156
  </div>
202
- </Transition>
203
- </Teleport>
157
+
158
+ <!-- Export button -->
159
+ <button
160
+ type="button"
161
+ @click="doExport"
162
+ class="w-full py-1.5 px-3 rounded-lg bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium transition-colors inline-flex items-center justify-center gap-2"
163
+ >
164
+ <IconDownload class="size-4" stroke="1.5" />
165
+ Exportar
166
+ </button>
167
+ </div>
168
+ </Transition>
204
169
  </div>
205
170
  </template>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@innertia-solutions/nuxt-theme-spark",
3
- "version": "0.1.114",
3
+ "version": "0.1.116",
4
4
  "description": "Innertia Solutions — Spark theme: backoffice, landing and mobile components and layouts",
5
5
  "keywords": [
6
6
  "nuxt",