@innertia-solutions/nuxt-theme-spark 0.1.31 → 0.1.33
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/Table/Standard.vue +170 -85
- package/components/Table.vue +16 -90
- package/package.json +1 -1
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { IconSearch, IconAdjustmentsHorizontal, IconLayoutColumns } from '@tabler/icons-vue'
|
|
2
|
+
import { IconSearch, IconAdjustmentsHorizontal, IconLayoutColumns, IconGripVertical } from '@tabler/icons-vue'
|
|
3
3
|
|
|
4
|
-
// Standard admin table: search + filters + Table (TanStack) + export
|
|
5
4
|
const props = defineProps({
|
|
6
5
|
endpoint: { type: String, required: true },
|
|
7
6
|
columns: { type: Array, required: true },
|
|
@@ -32,12 +31,52 @@ const activeFilterCount = computed(() =>
|
|
|
32
31
|
Object.values(filters.value).filter(v => v !== null && v !== undefined && v !== '').length
|
|
33
32
|
)
|
|
34
33
|
|
|
35
|
-
// Merge filters into params
|
|
36
34
|
const mergedParams = computed(() => ({
|
|
37
35
|
...props.params,
|
|
38
36
|
...filters.value,
|
|
39
37
|
}))
|
|
40
38
|
|
|
39
|
+
// ─── Column panel ─────────────────────────────────────────────────────────────
|
|
40
|
+
const showColumnPanel = ref(false)
|
|
41
|
+
const columnPanelRef = ref(null)
|
|
42
|
+
|
|
43
|
+
const orderedColumns = computed(() => {
|
|
44
|
+
if (!tableRef.value) return props.columns
|
|
45
|
+
const ids = tableRef.value.table.getAllLeafColumns().map(c => c.id)
|
|
46
|
+
return ids.map(id => props.columns.find(c => c.key === id)).filter(Boolean)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
let draggedKey = null
|
|
50
|
+
const dragOverKey = ref(null)
|
|
51
|
+
|
|
52
|
+
const onDragStart = (key) => { draggedKey = key }
|
|
53
|
+
const onDragOver = (e, key) => { e.preventDefault(); dragOverKey.value = key }
|
|
54
|
+
const onDragLeave = () => { dragOverKey.value = null }
|
|
55
|
+
const onDrop = (key) => {
|
|
56
|
+
if (!draggedKey || draggedKey === key) return
|
|
57
|
+
const ids = tableRef.value?.table.getAllLeafColumns().map(c => c.id) ?? []
|
|
58
|
+
const from = ids.indexOf(draggedKey)
|
|
59
|
+
const to = ids.indexOf(key)
|
|
60
|
+
if (from < 0 || to < 0) return
|
|
61
|
+
ids.splice(from, 1)
|
|
62
|
+
ids.splice(to, 0, draggedKey)
|
|
63
|
+
tableRef.value?.setColumnOrder(ids)
|
|
64
|
+
draggedKey = null
|
|
65
|
+
dragOverKey.value = null
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const onPanelOutsideClick = (e) => {
|
|
69
|
+
if (columnPanelRef.value && !columnPanelRef.value.contains(e.target)) {
|
|
70
|
+
showColumnPanel.value = false
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
watch(showColumnPanel, (v) => {
|
|
75
|
+
if (v) document.addEventListener('mousedown', onPanelOutsideClick)
|
|
76
|
+
else document.removeEventListener('mousedown', onPanelOutsideClick)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
// ─── Expose ───────────────────────────────────────────────────────────────────
|
|
41
80
|
const getSelectedRows = () => tableRef.value?.getSelectedRows()
|
|
42
81
|
const reload = () => tableRef.value?.reload()
|
|
43
82
|
const clearCache = () => tableRef.value?.clearCache()
|
|
@@ -47,103 +86,149 @@ defineExpose({ getSelectedRows, reload, clearCache, exportTable, tableRef })
|
|
|
47
86
|
</script>
|
|
48
87
|
|
|
49
88
|
<template>
|
|
50
|
-
<div class="
|
|
51
|
-
<!--
|
|
52
|
-
<div class="
|
|
53
|
-
<!--
|
|
54
|
-
<div
|
|
55
|
-
|
|
56
|
-
|
|
89
|
+
<div class="relative">
|
|
90
|
+
<!-- Card -->
|
|
91
|
+
<div class="bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-xl overflow-hidden">
|
|
92
|
+
<!-- Toolbar -->
|
|
93
|
+
<div class="flex flex-wrap items-center gap-3 px-4 py-3 border-b border-slate-200 dark:border-slate-700">
|
|
94
|
+
<!-- Search -->
|
|
95
|
+
<div v-if="showSearch" class="relative flex-1 min-w-48">
|
|
96
|
+
<div class="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none">
|
|
97
|
+
<IconSearch class="size-4 text-slate-400" stroke="1.5" />
|
|
98
|
+
</div>
|
|
99
|
+
<input
|
|
100
|
+
v-model="search"
|
|
101
|
+
type="search"
|
|
102
|
+
:placeholder="searchPlaceholder"
|
|
103
|
+
class="block w-full rounded-lg border border-slate-200 dark:border-slate-700 bg-slate-50 dark:bg-slate-900 text-slate-900 dark:text-white py-1.5 ps-9 pe-4 text-sm focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500"
|
|
104
|
+
/>
|
|
57
105
|
</div>
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
106
|
+
|
|
107
|
+
<!-- Filter toggle -->
|
|
108
|
+
<button
|
|
109
|
+
v-if="showFilters && hasFilterableColumns"
|
|
110
|
+
type="button"
|
|
111
|
+
@click="showFilterPanel = !showFilterPanel"
|
|
112
|
+
:class="[
|
|
113
|
+
'py-1.5 px-3 inline-flex items-center gap-2 text-sm font-medium rounded-lg border transition-colors',
|
|
114
|
+
showFilterPanel || activeFilterCount > 0
|
|
115
|
+
? 'border-blue-500 bg-blue-50 text-blue-700 dark:bg-blue-900/20 dark:border-blue-500 dark:text-blue-300'
|
|
116
|
+
: 'border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 text-slate-600 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-slate-700'
|
|
117
|
+
]"
|
|
118
|
+
>
|
|
119
|
+
<IconAdjustmentsHorizontal class="size-4" stroke="1.5" />
|
|
120
|
+
Filtros
|
|
121
|
+
<span
|
|
122
|
+
v-if="activeFilterCount > 0"
|
|
123
|
+
class="inline-flex items-center justify-center size-5 rounded-full bg-blue-600 text-white text-xs font-bold"
|
|
124
|
+
>{{ activeFilterCount }}</span>
|
|
125
|
+
</button>
|
|
126
|
+
|
|
127
|
+
<slot name="actions" />
|
|
128
|
+
|
|
129
|
+
<!-- Column visibility toggle -->
|
|
130
|
+
<button
|
|
131
|
+
type="button"
|
|
132
|
+
@click="showColumnPanel = !showColumnPanel"
|
|
133
|
+
:class="[
|
|
134
|
+
'py-1.5 px-3 inline-flex items-center gap-2 text-sm font-medium rounded-lg border transition-colors',
|
|
135
|
+
showColumnPanel
|
|
136
|
+
? 'border-blue-500 bg-blue-50 text-blue-700 dark:bg-blue-900/20 dark:border-blue-500 dark:text-blue-300'
|
|
137
|
+
: 'border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 text-slate-600 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-slate-700'
|
|
138
|
+
]"
|
|
139
|
+
>
|
|
140
|
+
<IconLayoutColumns class="size-4" />
|
|
141
|
+
Columnas
|
|
142
|
+
</button>
|
|
143
|
+
|
|
144
|
+
<!-- Export -->
|
|
145
|
+
<TableExportable
|
|
146
|
+
v-if="showExport"
|
|
147
|
+
:table-ref="tableRef"
|
|
148
|
+
:name="name"
|
|
149
|
+
:columns="columns"
|
|
63
150
|
/>
|
|
64
151
|
</div>
|
|
65
152
|
|
|
66
|
-
<!-- Filter
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
? 'border-indigo-500 bg-indigo-50 text-indigo-700 dark:bg-indigo-900/20 dark:border-indigo-500 dark:text-indigo-300'
|
|
75
|
-
: 'border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 text-slate-600 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-slate-700'
|
|
76
|
-
]"
|
|
77
|
-
>
|
|
78
|
-
<IconAdjustmentsHorizontal class="size-4" stroke="1.5" />
|
|
79
|
-
Filtros
|
|
80
|
-
<span
|
|
81
|
-
v-if="activeFilterCount > 0"
|
|
82
|
-
class="inline-flex items-center justify-center size-5 rounded-full bg-indigo-600 text-white text-xs font-bold"
|
|
83
|
-
>{{ activeFilterCount }}</span>
|
|
84
|
-
</button>
|
|
85
|
-
|
|
86
|
-
<slot name="actions" />
|
|
87
|
-
|
|
88
|
-
<!-- Column visibility -->
|
|
89
|
-
<button
|
|
90
|
-
type="button"
|
|
91
|
-
@click="tableRef?.toggleColumnPanel()"
|
|
92
|
-
:class="[
|
|
93
|
-
'py-1.5 px-3 inline-flex items-center gap-2 text-sm font-medium rounded-lg border transition-colors',
|
|
94
|
-
tableRef?.showColumnPanel
|
|
95
|
-
? 'border-indigo-500 bg-indigo-50 text-indigo-700 dark:bg-indigo-900/20 dark:border-indigo-500 dark:text-indigo-300'
|
|
96
|
-
: 'border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 text-slate-600 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-slate-700'
|
|
97
|
-
]"
|
|
153
|
+
<!-- Filter panel -->
|
|
154
|
+
<Transition
|
|
155
|
+
enter-active-class="transition ease-out duration-150"
|
|
156
|
+
enter-from-class="opacity-0 -translate-y-2"
|
|
157
|
+
enter-to-class="opacity-100 translate-y-0"
|
|
158
|
+
leave-active-class="transition ease-in duration-100"
|
|
159
|
+
leave-from-class="opacity-100 translate-y-0"
|
|
160
|
+
leave-to-class="opacity-0 -translate-y-2"
|
|
98
161
|
>
|
|
99
|
-
<
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
162
|
+
<div
|
|
163
|
+
v-if="showFilterPanel && hasFilterableColumns"
|
|
164
|
+
class="px-4 py-3 border-b border-slate-200 dark:border-slate-700 bg-slate-50 dark:bg-slate-900/50"
|
|
165
|
+
>
|
|
166
|
+
<TableFilter v-model="filters" :columns="columns" />
|
|
167
|
+
</div>
|
|
168
|
+
</Transition>
|
|
169
|
+
|
|
170
|
+
<!-- Table -->
|
|
171
|
+
<Table
|
|
172
|
+
ref="tableRef"
|
|
173
|
+
:endpoint="endpoint"
|
|
108
174
|
:columns="columns"
|
|
109
|
-
|
|
175
|
+
:name="name"
|
|
176
|
+
:params="mergedParams"
|
|
177
|
+
:search="search"
|
|
178
|
+
:checkable="checkable"
|
|
179
|
+
:cached="cached"
|
|
180
|
+
:show-reload-button="showReloadButton"
|
|
181
|
+
:click-row-to-open="clickRowToOpen"
|
|
182
|
+
@row-click="emit('row-click', $event)"
|
|
183
|
+
@loaded="emit('loaded', $event)"
|
|
184
|
+
>
|
|
185
|
+
<template v-for="(_, name) in $slots" #[name]="slotProps">
|
|
186
|
+
<slot :name="name" v-bind="slotProps ?? {}" />
|
|
187
|
+
</template>
|
|
188
|
+
</Table>
|
|
110
189
|
</div>
|
|
111
190
|
|
|
112
|
-
<!--
|
|
191
|
+
<!-- Column panel — outside overflow-hidden so never clipped -->
|
|
113
192
|
<Transition
|
|
114
193
|
enter-active-class="transition ease-out duration-150"
|
|
115
|
-
enter-from-class="opacity-0
|
|
116
|
-
enter-to-class="opacity-100 translate-y-0"
|
|
194
|
+
enter-from-class="opacity-0 translate-y-1 scale-95"
|
|
195
|
+
enter-to-class="opacity-100 translate-y-0 scale-100"
|
|
117
196
|
leave-active-class="transition ease-in duration-100"
|
|
118
|
-
leave-from-class="opacity-100 translate-y-0"
|
|
119
|
-
leave-to-class="opacity-0
|
|
197
|
+
leave-from-class="opacity-100 translate-y-0 scale-100"
|
|
198
|
+
leave-to-class="opacity-0 translate-y-1 scale-95"
|
|
120
199
|
>
|
|
121
200
|
<div
|
|
122
|
-
v-if="
|
|
123
|
-
|
|
201
|
+
v-if="showColumnPanel"
|
|
202
|
+
ref="columnPanelRef"
|
|
203
|
+
class="absolute top-12 right-0 z-50 bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-xl shadow-2xl p-3 min-w-56 max-h-80 overflow-y-auto"
|
|
124
204
|
>
|
|
125
|
-
<
|
|
205
|
+
<p class="text-[10px] font-bold text-slate-400 dark:text-slate-500 uppercase tracking-widest mb-2 px-1">
|
|
206
|
+
Columnas visibles
|
|
207
|
+
</p>
|
|
208
|
+
<div
|
|
209
|
+
v-for="col in orderedColumns"
|
|
210
|
+
:key="col.key"
|
|
211
|
+
draggable="true"
|
|
212
|
+
@dragstart="onDragStart(col.key)"
|
|
213
|
+
@dragover="(e) => onDragOver(e, col.key)"
|
|
214
|
+
@dragleave="onDragLeave"
|
|
215
|
+
@drop="onDrop(col.key)"
|
|
216
|
+
class="flex items-center gap-2 py-1.5 px-2 rounded-lg select-none transition-colors"
|
|
217
|
+
:class="dragOverKey === col.key
|
|
218
|
+
? 'bg-blue-50 dark:bg-blue-900/20 ring-1 ring-blue-300 dark:ring-blue-700'
|
|
219
|
+
: 'hover:bg-slate-50 dark:hover:bg-slate-700 cursor-grab'"
|
|
220
|
+
>
|
|
221
|
+
<IconGripVertical class="size-4 text-slate-300 dark:text-slate-600 shrink-0" />
|
|
222
|
+
<input
|
|
223
|
+
type="checkbox"
|
|
224
|
+
:checked="tableRef?.table.getColumn(col.key)?.getIsVisible() ?? true"
|
|
225
|
+
@change="tableRef?.table.getColumn(col.key)?.toggleVisibility()"
|
|
226
|
+
@click.stop
|
|
227
|
+
class="rounded border-gray-300 dark:bg-slate-700 dark:border-slate-600 shrink-0 cursor-pointer"
|
|
228
|
+
/>
|
|
229
|
+
<span class="text-sm text-slate-700 dark:text-slate-200 truncate">{{ col.label }}</span>
|
|
230
|
+
</div>
|
|
126
231
|
</div>
|
|
127
232
|
</Transition>
|
|
128
|
-
|
|
129
|
-
<!-- Table -->
|
|
130
|
-
<Table
|
|
131
|
-
ref="tableRef"
|
|
132
|
-
:endpoint="endpoint"
|
|
133
|
-
:columns="columns"
|
|
134
|
-
:name="name"
|
|
135
|
-
:params="mergedParams"
|
|
136
|
-
:search="search"
|
|
137
|
-
:checkable="checkable"
|
|
138
|
-
:cached="cached"
|
|
139
|
-
:show-reload-button="showReloadButton"
|
|
140
|
-
:click-row-to-open="clickRowToOpen"
|
|
141
|
-
@row-click="emit('row-click', $event)"
|
|
142
|
-
@loaded="emit('loaded', $event)"
|
|
143
|
-
>
|
|
144
|
-
<template v-for="(_, name) in $slots" #[name]="slotProps">
|
|
145
|
-
<slot :name="name" v-bind="slotProps ?? {}" />
|
|
146
|
-
</template>
|
|
147
|
-
</Table>
|
|
148
233
|
</div>
|
|
149
234
|
</template>
|
package/components/Table.vue
CHANGED
|
@@ -6,8 +6,6 @@ import {
|
|
|
6
6
|
IconSortDescendingSmallBig,
|
|
7
7
|
IconReload,
|
|
8
8
|
IconBolt,
|
|
9
|
-
IconLayoutColumns,
|
|
10
|
-
IconGripVertical,
|
|
11
9
|
} from '@tabler/icons-vue'
|
|
12
10
|
|
|
13
11
|
const props = defineProps({
|
|
@@ -285,47 +283,7 @@ onBeforeUnmount(() => {
|
|
|
285
283
|
})
|
|
286
284
|
|
|
287
285
|
// ─── Column settings panel ────────────────────────────────────────────────────
|
|
288
|
-
const
|
|
289
|
-
const columnPanelRef = ref(null)
|
|
290
|
-
|
|
291
|
-
const orderedColumns = computed(() => {
|
|
292
|
-
if (!columnOrder.value.length) return props.columns
|
|
293
|
-
return [...props.columns].sort((a, b) => {
|
|
294
|
-
const ia = columnOrder.value.indexOf(a.key)
|
|
295
|
-
const ib = columnOrder.value.indexOf(b.key)
|
|
296
|
-
return (ia < 0 ? 999 : ia) - (ib < 0 ? 999 : ib)
|
|
297
|
-
})
|
|
298
|
-
})
|
|
299
|
-
|
|
300
|
-
let draggedPanelKey = null
|
|
301
|
-
const dragOverPanelKey = ref(null)
|
|
302
|
-
|
|
303
|
-
const onPanelDragStart = (key) => { draggedPanelKey = key }
|
|
304
|
-
const onPanelDragOver = (e, key) => { e.preventDefault(); dragOverPanelKey.value = key }
|
|
305
|
-
const onPanelDragLeave = () => { dragOverPanelKey.value = null }
|
|
306
|
-
const onPanelDrop = (key) => {
|
|
307
|
-
if (!draggedPanelKey || draggedPanelKey === key) return
|
|
308
|
-
const order = [...columnOrder.value]
|
|
309
|
-
const from = order.indexOf(draggedPanelKey)
|
|
310
|
-
const to = order.indexOf(key)
|
|
311
|
-
if (from < 0 || to < 0) return
|
|
312
|
-
order.splice(from, 1)
|
|
313
|
-
order.splice(to, 0, draggedPanelKey)
|
|
314
|
-
columnOrder.value = order
|
|
315
|
-
draggedPanelKey = null
|
|
316
|
-
dragOverPanelKey.value = null
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
const handlePanelOutsideClick = (e) => {
|
|
320
|
-
if (columnPanelRef.value && !columnPanelRef.value.contains(e.target)) {
|
|
321
|
-
showColumnPanel.value = false
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
watch(showColumnPanel, (v) => {
|
|
326
|
-
if (v) document.addEventListener('mousedown', handlePanelOutsideClick)
|
|
327
|
-
else document.removeEventListener('mousedown', handlePanelOutsideClick)
|
|
328
|
-
})
|
|
286
|
+
const setColumnOrder = (order) => { columnOrder.value = order }
|
|
329
287
|
|
|
330
288
|
// ─── Header drag reorder ──────────────────────────────────────────────────────
|
|
331
289
|
let draggedHeaderId = null
|
|
@@ -446,8 +404,6 @@ const reloadTable = () => {
|
|
|
446
404
|
fetchData()
|
|
447
405
|
}
|
|
448
406
|
|
|
449
|
-
const toggleColumnPanel = () => { showColumnPanel.value = !showColumnPanel.value }
|
|
450
|
-
|
|
451
407
|
defineExpose({
|
|
452
408
|
getSelectedRows,
|
|
453
409
|
loading,
|
|
@@ -455,57 +411,13 @@ defineExpose({
|
|
|
455
411
|
reload: reloadTable,
|
|
456
412
|
clearCache,
|
|
457
413
|
table,
|
|
458
|
-
|
|
459
|
-
toggleColumnPanel,
|
|
414
|
+
setColumnOrder,
|
|
460
415
|
})
|
|
461
416
|
</script>
|
|
462
417
|
|
|
463
418
|
<template>
|
|
464
419
|
<div class="relative">
|
|
465
420
|
|
|
466
|
-
<!-- Column settings panel -->
|
|
467
|
-
<Transition
|
|
468
|
-
enter-active-class="transition ease-out duration-150"
|
|
469
|
-
enter-from-class="opacity-0 translate-y-1 scale-95"
|
|
470
|
-
enter-to-class="opacity-100 translate-y-0 scale-100"
|
|
471
|
-
leave-active-class="transition ease-in duration-100"
|
|
472
|
-
leave-from-class="opacity-100 translate-y-0 scale-100"
|
|
473
|
-
leave-to-class="opacity-0 translate-y-1 scale-95"
|
|
474
|
-
>
|
|
475
|
-
<div
|
|
476
|
-
v-if="showColumnPanel"
|
|
477
|
-
ref="columnPanelRef"
|
|
478
|
-
class="absolute top-0 right-0 z-50 bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-xl shadow-2xl p-3 min-w-56 max-h-80 overflow-y-auto"
|
|
479
|
-
>
|
|
480
|
-
<p class="text-[10px] font-bold text-slate-400 dark:text-slate-500 uppercase tracking-widest mb-2 px-1">
|
|
481
|
-
Columnas visibles
|
|
482
|
-
</p>
|
|
483
|
-
<div
|
|
484
|
-
v-for="col in orderedColumns"
|
|
485
|
-
:key="col.key"
|
|
486
|
-
draggable="true"
|
|
487
|
-
@dragstart="onPanelDragStart(col.key)"
|
|
488
|
-
@dragover="(e) => onPanelDragOver(e, col.key)"
|
|
489
|
-
@dragleave="onPanelDragLeave"
|
|
490
|
-
@drop="onPanelDrop(col.key)"
|
|
491
|
-
class="flex items-center gap-2 py-1.5 px-2 rounded-lg select-none transition-colors"
|
|
492
|
-
:class="dragOverPanelKey === col.key
|
|
493
|
-
? 'bg-indigo-50 dark:bg-indigo-900/20 ring-1 ring-indigo-300 dark:ring-indigo-700'
|
|
494
|
-
: 'hover:bg-slate-50 dark:hover:bg-slate-700 cursor-grab'"
|
|
495
|
-
>
|
|
496
|
-
<IconGripVertical class="size-4 text-slate-300 dark:text-slate-600 shrink-0" />
|
|
497
|
-
<input
|
|
498
|
-
type="checkbox"
|
|
499
|
-
:checked="table.getColumn(col.key)?.getIsVisible() ?? true"
|
|
500
|
-
@change="table.getColumn(col.key)?.toggleVisibility()"
|
|
501
|
-
@click.stop
|
|
502
|
-
class="rounded border-gray-300 dark:bg-slate-700 dark:border-slate-600 shrink-0 cursor-pointer"
|
|
503
|
-
/>
|
|
504
|
-
<span class="text-sm text-slate-700 dark:text-slate-200 truncate">{{ col.label }}</span>
|
|
505
|
-
</div>
|
|
506
|
-
</div>
|
|
507
|
-
</Transition>
|
|
508
|
-
|
|
509
421
|
<!-- Table view -->
|
|
510
422
|
<div v-if="!isGridView" class="overflow-x-auto relative">
|
|
511
423
|
<table
|
|
@@ -686,6 +598,20 @@ defineExpose({
|
|
|
686
598
|
</template>
|
|
687
599
|
</td>
|
|
688
600
|
</tr>
|
|
601
|
+
|
|
602
|
+
<!-- Filler rows: pad table to full page height when data < perPage -->
|
|
603
|
+
<tr
|
|
604
|
+
v-if="!loading && tableData.length > 0 && tableData.length < pagination.pageSize"
|
|
605
|
+
v-for="i in (pagination.pageSize - tableData.length)"
|
|
606
|
+
:key="'fill-' + i"
|
|
607
|
+
class="divide-x divide-gray-200 dark:divide-slate-700 bg-white dark:bg-slate-800"
|
|
608
|
+
>
|
|
609
|
+
<td
|
|
610
|
+
v-for="header in (table.getHeaderGroups()[0]?.headers ?? [])"
|
|
611
|
+
:key="'fillc-' + header.id"
|
|
612
|
+
:style="{ height: lastRowHeight + 'px' }"
|
|
613
|
+
/>
|
|
614
|
+
</tr>
|
|
689
615
|
</tbody>
|
|
690
616
|
</table>
|
|
691
617
|
|