@innertia-solutions/nuxt-theme-spark 0.1.113 → 0.1.115
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.
- package/components/Admin/Base.vue +1 -1
- package/components/TableExportable.vue +93 -165
- package/package.json +1 -1
|
@@ -49,7 +49,7 @@ const userInitial = computed(() =>
|
|
|
49
49
|
]"
|
|
50
50
|
>
|
|
51
51
|
<!-- Logo + mobile close -->
|
|
52
|
-
<header class="flex items-center gap-x-1 px-3 pt-4 pb-
|
|
52
|
+
<header class="flex items-center gap-x-1 px-3 pt-4 pb-5 shrink-0">
|
|
53
53
|
<div class="flex-1 min-w-0">
|
|
54
54
|
<slot name="logo" />
|
|
55
55
|
</div>
|
|
@@ -1,205 +1,133 @@
|
|
|
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:
|
|
14
|
-
columns:
|
|
12
|
+
name: { type: String, default: 'export' },
|
|
13
|
+
columns: { type: Array, default: () => [] },
|
|
15
14
|
})
|
|
16
15
|
|
|
17
|
-
const isOpen
|
|
18
|
-
const format
|
|
16
|
+
const isOpen = ref(false)
|
|
17
|
+
const format = ref('xlsx')
|
|
19
18
|
const filename = ref(props.name)
|
|
20
|
-
const selectedColumns = ref([])
|
|
21
|
-
|
|
22
|
-
watch(() => props.columns, (cols) => {
|
|
23
|
-
selectedColumns.value = cols.map(c => c.key)
|
|
24
|
-
}, { immediate: true })
|
|
25
19
|
|
|
26
20
|
watch(() => props.name, (v) => { filename.value = v })
|
|
27
21
|
|
|
28
22
|
const formats = [
|
|
29
|
-
{ value: 'xlsx', label: 'Excel'
|
|
30
|
-
{ value: 'csv',
|
|
31
|
-
{ value: 'pdf',
|
|
32
|
-
{ value: 'json', label: 'JSON'
|
|
23
|
+
{ value: 'xlsx', label: 'Excel' },
|
|
24
|
+
{ value: 'csv', label: 'CSV' },
|
|
25
|
+
{ value: 'pdf', label: 'PDF' },
|
|
26
|
+
{ value: 'json', label: 'JSON' },
|
|
33
27
|
]
|
|
34
28
|
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
else selectedColumns.value.push(key)
|
|
39
|
-
}
|
|
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 doExport = () => {
|
|
30
|
+
props.tableRef?.exportTable(format.value, true, true)
|
|
31
|
+
isOpen.value = false
|
|
46
32
|
}
|
|
47
33
|
|
|
48
|
-
const
|
|
49
|
-
const
|
|
34
|
+
const panelRef = ref(null)
|
|
35
|
+
const triggerRef = ref(null)
|
|
50
36
|
|
|
51
|
-
const
|
|
52
|
-
if (
|
|
53
|
-
|
|
37
|
+
const onOutsideClick = (e) => {
|
|
38
|
+
if (
|
|
39
|
+
panelRef.value && !panelRef.value.contains(e.target) &&
|
|
40
|
+
triggerRef.value && !triggerRef.value.contains(e.target)
|
|
41
|
+
) {
|
|
42
|
+
isOpen.value = false
|
|
54
43
|
}
|
|
55
|
-
isOpen.value = false
|
|
56
44
|
}
|
|
57
45
|
|
|
58
|
-
|
|
46
|
+
watch(isOpen, (v) => {
|
|
47
|
+
if (v) document.addEventListener('mousedown', onOutsideClick)
|
|
48
|
+
else document.removeEventListener('mousedown', onOutsideClick)
|
|
49
|
+
})
|
|
59
50
|
|
|
60
|
-
defineExpose({ open })
|
|
51
|
+
defineExpose({ open: () => { isOpen.value = true } })
|
|
61
52
|
</script>
|
|
62
53
|
|
|
63
54
|
<template>
|
|
64
|
-
<div>
|
|
55
|
+
<div class="relative">
|
|
65
56
|
<button
|
|
57
|
+
ref="triggerRef"
|
|
66
58
|
type="button"
|
|
67
|
-
@click="isOpen =
|
|
68
|
-
class="
|
|
59
|
+
@click="isOpen = !isOpen"
|
|
60
|
+
:class="[
|
|
61
|
+
'py-1.5 px-3 inline-flex items-center gap-2 text-sm font-medium rounded-lg border transition-colors',
|
|
62
|
+
isOpen
|
|
63
|
+
? 'border-blue-500 bg-blue-50 text-blue-700 dark:bg-blue-900/20 dark:border-blue-500 dark:text-blue-300'
|
|
64
|
+
: 'border-card-line bg-card text-muted-foreground-1 hover:bg-muted-hover'
|
|
65
|
+
]"
|
|
69
66
|
>
|
|
70
|
-
<IconDownload class="
|
|
67
|
+
<IconDownload class="size-4" stroke="1.5" />
|
|
71
68
|
Exportar
|
|
72
69
|
</button>
|
|
73
70
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
71
|
+
<Transition
|
|
72
|
+
enter-active-class="transition ease-out duration-150"
|
|
73
|
+
enter-from-class="opacity-0 translate-y-1 scale-95"
|
|
74
|
+
enter-to-class="opacity-100 translate-y-0 scale-100"
|
|
75
|
+
leave-active-class="transition ease-in duration-100"
|
|
76
|
+
leave-from-class="opacity-100 translate-y-0 scale-100"
|
|
77
|
+
leave-to-class="opacity-0 translate-y-1 scale-95"
|
|
78
|
+
>
|
|
79
|
+
<div
|
|
80
|
+
v-if="isOpen"
|
|
81
|
+
ref="panelRef"
|
|
82
|
+
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
83
|
>
|
|
84
|
-
<
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
84
|
+
<p class="text-[10px] font-bold text-muted-foreground uppercase tracking-widest mb-3 px-1">Exportar</p>
|
|
85
|
+
|
|
86
|
+
<!-- Format -->
|
|
87
|
+
<div class="grid grid-cols-4 gap-1.5 mb-3">
|
|
88
|
+
<button
|
|
89
|
+
v-for="f in formats"
|
|
90
|
+
:key="f.value"
|
|
91
|
+
type="button"
|
|
92
|
+
@click="format = f.value"
|
|
93
|
+
:class="[
|
|
94
|
+
'flex flex-col items-center gap-1 py-2 rounded-lg border text-xs font-medium transition-colors',
|
|
95
|
+
format === f.value
|
|
96
|
+
? 'border-blue-500 bg-blue-50 text-blue-700 dark:bg-blue-900/20 dark:border-blue-500 dark:text-blue-300'
|
|
97
|
+
: 'border-card-line text-muted-foreground-1 hover:bg-muted-hover'
|
|
98
|
+
]"
|
|
93
99
|
>
|
|
94
|
-
<
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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>
|
|
100
|
+
<IconFileTypeXls v-if="f.value === 'xlsx'" class="size-4" stroke="1.5" />
|
|
101
|
+
<IconFileTypeCsv v-else-if="f.value === 'csv'" class="size-4" stroke="1.5" />
|
|
102
|
+
<IconFileTypePdf v-else-if="f.value === 'pdf'" class="size-4" stroke="1.5" />
|
|
103
|
+
<IconCodeDots v-else class="size-4" stroke="1.5" />
|
|
104
|
+
{{ f.label }}
|
|
105
|
+
</button>
|
|
106
|
+
</div>
|
|
181
107
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
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>
|
|
108
|
+
<!-- Filename -->
|
|
109
|
+
<div class="mb-3 px-1">
|
|
110
|
+
<label class="text-[10px] font-bold text-muted-foreground uppercase tracking-widest block mb-1.5">Archivo</label>
|
|
111
|
+
<div class="flex items-center gap-1.5">
|
|
112
|
+
<input
|
|
113
|
+
v-model="filename"
|
|
114
|
+
type="text"
|
|
115
|
+
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"
|
|
116
|
+
/>
|
|
117
|
+
<span class="text-xs text-muted-foreground shrink-0">.{{ format }}</span>
|
|
118
|
+
</div>
|
|
201
119
|
</div>
|
|
202
|
-
|
|
203
|
-
|
|
120
|
+
|
|
121
|
+
<!-- Export button -->
|
|
122
|
+
<button
|
|
123
|
+
type="button"
|
|
124
|
+
@click="doExport"
|
|
125
|
+
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"
|
|
126
|
+
>
|
|
127
|
+
<IconDownload class="size-4" stroke="1.5" />
|
|
128
|
+
Exportar
|
|
129
|
+
</button>
|
|
130
|
+
</div>
|
|
131
|
+
</Transition>
|
|
204
132
|
</div>
|
|
205
133
|
</template>
|