@wishbone-media/spark 0.18.0 → 0.19.0
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/dist/index.css +1 -1
- package/dist/index.js +1274 -1092
- package/formkit.theme.mjs +4 -4
- package/package.json +1 -1
- package/src/assets/css/spark-table.css +31 -6
- package/src/assets/images/brands/mr-antenna.png +0 -0
- package/src/assets/images/brands/mr-group.png +0 -0
- package/src/assets/images/brands/mr-gutter-cleaning.png +0 -0
- package/src/assets/images/brands/mr-pest-controller.png +0 -0
- package/src/components/SparkBrandSelector.vue +9 -9
- package/src/components/SparkModalContainer.vue +1 -1
- package/src/components/SparkOverlay.vue +1 -1
- package/src/components/SparkTable.vue +145 -13
- package/src/components/SparkTablePaginationDetails.vue +1 -1
- package/src/components/SparkTableToolbar.vue +1 -1
- package/src/components/plugins/SparkTableFilterSelect.vue +8 -2
- package/src/components/plugins/SparkTableReset.vue +43 -14
- package/src/components/plugins/SparkTableSearch.vue +10 -5
- package/src/composables/index.js +2 -1
- package/src/composables/useSparkTableRouteSync.js +291 -0
- package/src/containers/SparkDefaultContainer.vue +11 -1
- package/src/plugins/fontawesome.js +5 -0
- package/src/stores/app-selector.js +8 -2
- package/src/stores/brand-filter.js +1 -1
- package/src/stores/navigation.js +1 -0
package/formkit.theme.mjs
CHANGED
|
@@ -344,7 +344,7 @@ const classes = {
|
|
|
344
344
|
"flex": true,
|
|
345
345
|
"items-center": true,
|
|
346
346
|
"text-gray-700": true,
|
|
347
|
-
"z-
|
|
347
|
+
"z-1000": true,
|
|
348
348
|
"dark:text-gray-300": true,
|
|
349
349
|
"data-[disabled]:cursor-not-allowed": true
|
|
350
350
|
},
|
|
@@ -356,7 +356,7 @@ const classes = {
|
|
|
356
356
|
"items-center": true,
|
|
357
357
|
"text-gray-700": true,
|
|
358
358
|
"hover:text-red-400": true,
|
|
359
|
-
"z-
|
|
359
|
+
"z-1000": true,
|
|
360
360
|
"dark:text-gray-300": true
|
|
361
361
|
},
|
|
362
362
|
"family:dropdown__controlLabel": {
|
|
@@ -1615,7 +1615,7 @@ const classes = {
|
|
|
1615
1615
|
"mr-2.5": true,
|
|
1616
1616
|
"text-gray-700": true,
|
|
1617
1617
|
"hover:text-red-400": true,
|
|
1618
|
-
"z-
|
|
1618
|
+
"z-1000": true,
|
|
1619
1619
|
"dark:text-gray-300": true
|
|
1620
1620
|
},
|
|
1621
1621
|
"datepicker__clearIcon": {
|
|
@@ -1631,7 +1631,7 @@ const classes = {
|
|
|
1631
1631
|
"rounded-md": true,
|
|
1632
1632
|
"p-5": true,
|
|
1633
1633
|
"bg-white": true,
|
|
1634
|
-
"z-
|
|
1634
|
+
"z-1000": true,
|
|
1635
1635
|
"dark:bg-gray-800": true,
|
|
1636
1636
|
"[@media(max-width:431px)_and_(hover:none)]:group-[&:not([data-inline])]:!fixed": true,
|
|
1637
1637
|
"[@media(max-width:431px)_and_(hover:none)]:group-[&:not([data-inline])]:top-auto": true,
|
package/package.json
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
.ht-theme-classic,
|
|
2
|
+
.ht-theme-classic-dark,
|
|
3
|
+
.ht-theme-classic-dark-auto {
|
|
4
|
+
--ht-font-size: 12px;
|
|
5
|
+
--ht-line-height: 16px;
|
|
6
|
+
--ht-wrapper-border-width: 1px;
|
|
7
|
+
--ht-wrapper-border-radius: 6px;
|
|
8
|
+
--ht-wrapper-border-color: var(--color-gray-300);
|
|
9
|
+
--ht-header-highlighted-background-color: transparent;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.spark-table .ht_master .wtBorder {
|
|
13
|
+
@apply !hidden;
|
|
14
|
+
}
|
|
15
|
+
|
|
1
16
|
.spark-table-table {
|
|
2
17
|
tbody {
|
|
3
18
|
tr:nth-child(even) td {
|
|
@@ -30,17 +45,13 @@
|
|
|
30
45
|
@apply cursor-pointer;
|
|
31
46
|
}
|
|
32
47
|
|
|
33
|
-
&.ht__highlight .relative {
|
|
34
|
-
@apply bg-gray-200;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
48
|
&.ht__active_highlight .relative {
|
|
38
49
|
@apply bg-blue-400 text-black;
|
|
39
50
|
}
|
|
40
51
|
|
|
41
52
|
& > .relative {
|
|
42
53
|
@apply text-left text-xs font-semibold bg-gray-50 text-gray-800 whitespace-normal
|
|
43
|
-
tracking-wider focus:outline-hidden
|
|
54
|
+
tracking-wider focus:outline-hidden filter flex items-center;
|
|
44
55
|
|
|
45
56
|
@apply !px-5 !py-3;
|
|
46
57
|
|
|
@@ -73,7 +84,7 @@
|
|
|
73
84
|
}
|
|
74
85
|
|
|
75
86
|
.colHeader {
|
|
76
|
-
@apply leading-
|
|
87
|
+
@apply leading-4 text-gray-800;
|
|
77
88
|
}
|
|
78
89
|
}
|
|
79
90
|
|
|
@@ -97,3 +108,17 @@
|
|
|
97
108
|
.spark-table-head-sorting {
|
|
98
109
|
@apply absolute right-5 w-5 h-5 border border-gray-200 rounded-md grid place-items-center;
|
|
99
110
|
}
|
|
111
|
+
|
|
112
|
+
.spark-table-head-title-wrapper {
|
|
113
|
+
@apply h-full;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.spark-table-toolbar-header {
|
|
117
|
+
::placeholder {
|
|
118
|
+
@apply text-gray-700;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.spark-table-toolbar-footer {
|
|
123
|
+
@apply min-h-15;
|
|
124
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -25,24 +25,24 @@
|
|
|
25
25
|
class="flex px-[22px] py-[15px] cursor-pointer"
|
|
26
26
|
@click="selectBrand(brand)"
|
|
27
27
|
>
|
|
28
|
-
<div class="gap-y-1 flex">
|
|
29
|
-
<div class="flex
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
<div class="ml-auto flex flex-col">
|
|
33
|
-
<div class="text-base text-gray-800 flex items-center">
|
|
34
|
-
<div class="font-medium">{{ brand.name }}</div>
|
|
28
|
+
<div class="w-full gap-y-1 flex justify-between">
|
|
29
|
+
<div class="flex flex-col">
|
|
30
|
+
<div class="flex items-center">
|
|
31
|
+
<div class="font-medium text-base text-gray-900">{{ brand.name }}</div>
|
|
35
32
|
<span
|
|
36
33
|
v-if="brand.current"
|
|
37
|
-
class="inline-flex items-center rounded-full bg-green-100 px-1.5 py-0.5 text-xs font-medium text-green-
|
|
34
|
+
class="inline-flex items-center rounded-full bg-green-100 px-1.5 py-0.5 text-xs font-medium text-green-800 ml-1"
|
|
38
35
|
>
|
|
39
36
|
Current
|
|
40
37
|
</span>
|
|
41
38
|
</div>
|
|
42
|
-
<div class="text-sm text-gray-500">
|
|
39
|
+
<div class="text-sm text-gray-500 font-normal">
|
|
43
40
|
{{ brand.current ? 'Current Brand' : 'Change to' }}
|
|
44
41
|
</div>
|
|
45
42
|
</div>
|
|
43
|
+
<div class="flex items-center">
|
|
44
|
+
<img :src="brand.logo" :alt="`${brand.name} logo`" class="h-8 w-auto" />
|
|
45
|
+
</div>
|
|
46
46
|
</div>
|
|
47
47
|
</div>
|
|
48
48
|
<div></div>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<TransitionRoot as="template" :show="sparkModalService.state.isVisible">
|
|
3
|
-
<Dialog class="relative z-
|
|
3
|
+
<Dialog class="relative z-1000" @close="sparkModalService.hide">
|
|
4
4
|
<TransitionChild
|
|
5
5
|
as="template"
|
|
6
6
|
enter="ease-out duration-300"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="spark-table">
|
|
3
3
|
<!-- Header Toolbar: All plugins flow left to right -->
|
|
4
|
-
<spark-table-toolbar v-if="sparkTable.computed.ready" position="header">
|
|
4
|
+
<spark-table-toolbar v-if="sparkTable.computed.ready && headerPlugins && headerPlugins.length" position="header">
|
|
5
5
|
<component
|
|
6
6
|
v-for="plugin in headerPlugins"
|
|
7
7
|
:key="plugin.name"
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
/>
|
|
13
13
|
<slot name="header" :spark-table="sparkTable" :loading="loading" :error="error"></slot>
|
|
14
14
|
</spark-table-toolbar>
|
|
15
|
+
<div v-else class="pt-5"></div>
|
|
15
16
|
|
|
16
17
|
<!-- Table Grid -->
|
|
17
18
|
<HotTable theme-name="ht-theme-classic" ref="table" :settings="sparkTable.tableSettings" />
|
|
@@ -38,8 +39,8 @@
|
|
|
38
39
|
|
|
39
40
|
<script>
|
|
40
41
|
const defaultOptions = {
|
|
41
|
-
perPages: [100, 200, 500],
|
|
42
|
-
limit:
|
|
42
|
+
perPages: [15, 30, 50, 100, 200, 500],
|
|
43
|
+
limit: 15,
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
const defaultParams = {
|
|
@@ -60,7 +61,7 @@ const defaultTableSettings = {
|
|
|
60
61
|
}
|
|
61
62
|
</script>
|
|
62
63
|
<script setup>
|
|
63
|
-
import { computed, onMounted, ref, reactive, inject } from 'vue'
|
|
64
|
+
import { computed, onMounted, onUnmounted, ref, reactive, inject, watch } from 'vue'
|
|
64
65
|
import nprogress from 'nprogress'
|
|
65
66
|
import { has, get, find } from 'lodash'
|
|
66
67
|
import { HotTable } from '@handsontable/vue3'
|
|
@@ -78,6 +79,7 @@ import { watchDebounced } from '@vueuse/core'
|
|
|
78
79
|
import { customiseHeader, syncSortClasses } from '@/utils/sparkTable/header.js'
|
|
79
80
|
import { registerSparkRenderers } from '@/utils/sparkTable/renderers'
|
|
80
81
|
import { updateRow } from '@/utils/sparkTable/update-row.js'
|
|
82
|
+
import { useSparkTableRouteSync } from '@/composables/useSparkTableRouteSync.js'
|
|
81
83
|
import SparkTablePaginationDetails from './SparkTablePaginationDetails.vue'
|
|
82
84
|
import SparkTablePaginationPaging from './SparkTablePaginationPaging.vue'
|
|
83
85
|
import SparkTablePaginationPerPage from './SparkTablePaginationPerPage.vue'
|
|
@@ -141,6 +143,16 @@ const props = defineProps({
|
|
|
141
143
|
type: Object,
|
|
142
144
|
default: () => ({}),
|
|
143
145
|
},
|
|
146
|
+
|
|
147
|
+
syncToRoute: {
|
|
148
|
+
type: [Boolean, String],
|
|
149
|
+
default: false,
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
persistToStorage: {
|
|
153
|
+
type: Boolean,
|
|
154
|
+
default: false,
|
|
155
|
+
},
|
|
144
156
|
})
|
|
145
157
|
|
|
146
158
|
registerAllCellTypes()
|
|
@@ -155,6 +167,7 @@ const emit = defineEmits([
|
|
|
155
167
|
'viewRow',
|
|
156
168
|
'editRow',
|
|
157
169
|
'deleteRow',
|
|
170
|
+
'downloadRow',
|
|
158
171
|
'ready',
|
|
159
172
|
'loading',
|
|
160
173
|
'load',
|
|
@@ -168,6 +181,7 @@ const axios = inject('axios')
|
|
|
168
181
|
const table = ref(null)
|
|
169
182
|
const loading = ref(false)
|
|
170
183
|
const error = ref(null)
|
|
184
|
+
let isUnmounted = false
|
|
171
185
|
|
|
172
186
|
// Plugin component mapping by type
|
|
173
187
|
const pluginComponentsByType = {
|
|
@@ -178,16 +192,9 @@ const pluginComponentsByType = {
|
|
|
178
192
|
reset: SparkTableReset,
|
|
179
193
|
}
|
|
180
194
|
|
|
181
|
-
// Header plugins: all enabled plugins flow left to right
|
|
182
|
-
const headerPlugins = computed(() => {
|
|
183
|
-
return Object.entries(props.plugins)
|
|
184
|
-
.filter(([_, config]) => config && config.enabled)
|
|
185
|
-
.map(([name, config]) => ({ name, config }))
|
|
186
|
-
})
|
|
187
|
-
|
|
188
195
|
const sparkTable = reactive({
|
|
189
196
|
hotInstance: null,
|
|
190
|
-
url: props.url,
|
|
197
|
+
url: computed(() => props.url),
|
|
191
198
|
plugins: props.plugins,
|
|
192
199
|
response: {},
|
|
193
200
|
params: {
|
|
@@ -242,6 +249,9 @@ const sparkTable = reactive({
|
|
|
242
249
|
return
|
|
243
250
|
}
|
|
244
251
|
|
|
252
|
+
// Check if component was unmounted during async operation
|
|
253
|
+
if (isUnmounted) return
|
|
254
|
+
|
|
245
255
|
sparkTable.hotInstance.updateData(sparkTable.response.data)
|
|
246
256
|
|
|
247
257
|
if (sparkTable.options.callback && typeof sparkTable.options.callback === 'function') {
|
|
@@ -249,8 +259,15 @@ const sparkTable = reactive({
|
|
|
249
259
|
}
|
|
250
260
|
|
|
251
261
|
// @see: https://forum.handsontable.com/t/table-not-maintaning-width/3116/11
|
|
262
|
+
// Selectively recalculate column widths - skip columns with explicit width
|
|
252
263
|
const autoColumnSize = sparkTable.hotInstance.getPlugin('autoColumnSize')
|
|
253
|
-
|
|
264
|
+
const columns = get(props.settings, 'columns', [])
|
|
265
|
+
|
|
266
|
+
columns.forEach((column, index) => {
|
|
267
|
+
if (!column.width) {
|
|
268
|
+
autoColumnSize.calculateColumnsWidth(index, index, true)
|
|
269
|
+
}
|
|
270
|
+
})
|
|
254
271
|
|
|
255
272
|
emit('load', {
|
|
256
273
|
data: sparkTable.response.data,
|
|
@@ -325,11 +342,112 @@ const sparkTable = reactive({
|
|
|
325
342
|
}),
|
|
326
343
|
afterChange: (changes, source) => updateRow(changes, source, sparkTable),
|
|
327
344
|
afterRender: () => syncSortClasses(sparkTable),
|
|
345
|
+
/**
|
|
346
|
+
* Prevent columns with explicit width from being stretched
|
|
347
|
+
* This hook fires BEFORE stretchH is applied, allowing us to cap specific columns
|
|
348
|
+
* while letting others stretch normally
|
|
349
|
+
*/
|
|
350
|
+
beforeStretchingColumnWidth: (stretchedWidth, column) => {
|
|
351
|
+
const columns = get(props.settings, 'columns', [])
|
|
352
|
+
const columnSettings = columns[column]
|
|
353
|
+
if (columnSettings && columnSettings.width !== undefined) {
|
|
354
|
+
return columnSettings.width
|
|
355
|
+
}
|
|
356
|
+
return stretchedWidth
|
|
357
|
+
},
|
|
328
358
|
},
|
|
329
359
|
...props.settings,
|
|
330
360
|
})),
|
|
331
361
|
})
|
|
332
362
|
|
|
363
|
+
/**
|
|
364
|
+
* Get the param key for a plugin based on its type and configuration
|
|
365
|
+
*/
|
|
366
|
+
const getPluginParamKey = (pluginConfig) => {
|
|
367
|
+
if (!pluginConfig) return null
|
|
368
|
+
|
|
369
|
+
switch (pluginConfig.type) {
|
|
370
|
+
case 'search':
|
|
371
|
+
return pluginConfig.param || 'search'
|
|
372
|
+
case 'filterSelect':
|
|
373
|
+
case 'filterButtons':
|
|
374
|
+
case 'datePicker':
|
|
375
|
+
return pluginConfig.param || `filter[${pluginConfig.key}]`
|
|
376
|
+
default:
|
|
377
|
+
return null
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Check if a plugin is currently enabled
|
|
383
|
+
*/
|
|
384
|
+
const isPluginEnabled = (pluginConfig) => {
|
|
385
|
+
if (!pluginConfig) return false
|
|
386
|
+
return typeof pluginConfig.enabled === 'function'
|
|
387
|
+
? pluginConfig.enabled(sparkTable.params)
|
|
388
|
+
: pluginConfig.enabled
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Header plugins: all enabled plugins flow left to right
|
|
392
|
+
// `enabled` can be a boolean or a function that receives current params
|
|
393
|
+
const headerPlugins = computed(() => {
|
|
394
|
+
return Object.entries(props.plugins)
|
|
395
|
+
.filter(([_, config]) => isPluginEnabled(config))
|
|
396
|
+
.map(([name, config]) => ({ name, config }))
|
|
397
|
+
})
|
|
398
|
+
|
|
399
|
+
// Get all plugin param keys (regardless of enabled state)
|
|
400
|
+
const getAllPluginParamKeys = () => {
|
|
401
|
+
const keys = []
|
|
402
|
+
Object.values(props.plugins).forEach((config) => {
|
|
403
|
+
const paramKey = getPluginParamKey(config)
|
|
404
|
+
if (paramKey) keys.push(paramKey)
|
|
405
|
+
})
|
|
406
|
+
return keys
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Watch sparkTable.params for changes and clear params of disabled plugins
|
|
410
|
+
// Uses flush: 'sync' to run synchronously before other watchers (like the debounced loadTable)
|
|
411
|
+
watch(
|
|
412
|
+
() => ({ ...sparkTable.params }),
|
|
413
|
+
() => {
|
|
414
|
+
// Check each plugin - if it's disabled but has a param value, remove it
|
|
415
|
+
const paramsToRemove = []
|
|
416
|
+
|
|
417
|
+
Object.values(props.plugins).forEach((config) => {
|
|
418
|
+
const paramKey = getPluginParamKey(config)
|
|
419
|
+
if (!paramKey) return
|
|
420
|
+
|
|
421
|
+
const isEnabled = isPluginEnabled(config)
|
|
422
|
+
const hasValue = sparkTable.params[paramKey] !== undefined
|
|
423
|
+
|
|
424
|
+
if (!isEnabled && hasValue) {
|
|
425
|
+
paramsToRemove.push(paramKey)
|
|
426
|
+
}
|
|
427
|
+
})
|
|
428
|
+
|
|
429
|
+
// Clear disabled plugin params synchronously before loadTable runs
|
|
430
|
+
if (paramsToRemove.length > 0) {
|
|
431
|
+
paramsToRemove.forEach((paramKey) => {
|
|
432
|
+
delete sparkTable.params[paramKey]
|
|
433
|
+
})
|
|
434
|
+
}
|
|
435
|
+
},
|
|
436
|
+
{ deep: true, flush: 'sync' }
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
// Setup route sync and/or storage persistence if enabled
|
|
440
|
+
if (props.syncToRoute || props.persistToStorage) {
|
|
441
|
+
// If syncToRoute is a string, use it as namespace (for multi-table pages)
|
|
442
|
+
// If syncToRoute is true (boolean), use null for flat URLs
|
|
443
|
+
const hasExplicitNamespace = typeof props.syncToRoute === 'string'
|
|
444
|
+
useSparkTableRouteSync(sparkTable, {
|
|
445
|
+
namespace: hasExplicitNamespace ? props.syncToRoute : null,
|
|
446
|
+
syncToRoute: !!props.syncToRoute,
|
|
447
|
+
persistToStorage: props.persistToStorage,
|
|
448
|
+
})
|
|
449
|
+
}
|
|
450
|
+
|
|
333
451
|
watchDebounced(
|
|
334
452
|
() => props.params,
|
|
335
453
|
async () => {
|
|
@@ -346,11 +464,25 @@ watchDebounced(
|
|
|
346
464
|
{ debounce: 50, maxWait: 1000 },
|
|
347
465
|
)
|
|
348
466
|
|
|
467
|
+
watch(
|
|
468
|
+
() => props.url,
|
|
469
|
+
async (newUrl, oldUrl) => {
|
|
470
|
+
if (newUrl !== oldUrl) {
|
|
471
|
+
sparkTable.params.page = 1
|
|
472
|
+
await sparkTable.methods.loadTable()
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
)
|
|
476
|
+
|
|
349
477
|
onMounted(async () => {
|
|
350
478
|
await sparkTable.methods.loadTable()
|
|
351
479
|
emit('ready')
|
|
352
480
|
})
|
|
353
481
|
|
|
482
|
+
onUnmounted(() => {
|
|
483
|
+
isUnmounted = true
|
|
484
|
+
})
|
|
485
|
+
|
|
354
486
|
registerSparkRenderers(sparkTable)
|
|
355
487
|
|
|
356
488
|
defineExpose({
|
|
@@ -20,7 +20,7 @@ const slots = useSlots()
|
|
|
20
20
|
const hasContent = computed(() => !!slots.default)
|
|
21
21
|
|
|
22
22
|
const toolbarClass = computed(() => {
|
|
23
|
-
const baseClasses = 'spark-table-toolbar flex flex-wrap items-center gap-x-5
|
|
23
|
+
const baseClasses = 'spark-table-toolbar flex flex-wrap items-center gap-x-5 w-full'
|
|
24
24
|
|
|
25
25
|
// Footer has pagination details on left, controls on right
|
|
26
26
|
if (props.position === 'footer') {
|
|
@@ -105,11 +105,17 @@ watch(selectedValue, (newValue) => {
|
|
|
105
105
|
}
|
|
106
106
|
})
|
|
107
107
|
|
|
108
|
-
// Watch for external param changes
|
|
108
|
+
// Watch for external param changes (e.g., from route sync, reset button)
|
|
109
109
|
watch(
|
|
110
110
|
() => props.sparkTable.params[paramKey],
|
|
111
111
|
(newValue) => {
|
|
112
|
-
if (
|
|
112
|
+
if (newValue !== undefined && newValue !== null && newValue !== '') {
|
|
113
|
+
// Param was set externally (e.g., restored from URL)
|
|
114
|
+
if (selectedValue.value !== newValue) {
|
|
115
|
+
selectedValue.value = newValue
|
|
116
|
+
}
|
|
117
|
+
} else if (selectedValue.value) {
|
|
118
|
+
// Param was cleared externally
|
|
113
119
|
selectedValue.value = ''
|
|
114
120
|
}
|
|
115
121
|
},
|
|
@@ -77,42 +77,71 @@ const handleReset = () => {
|
|
|
77
77
|
// Get all plugin params from the plugins config
|
|
78
78
|
const plugins = props.sparkTable.plugins || {}
|
|
79
79
|
|
|
80
|
-
// Collect
|
|
80
|
+
// Collect params to clear and params to reset to initialValue
|
|
81
81
|
const paramsToClear = []
|
|
82
|
+
const paramsToReset = {}
|
|
82
83
|
|
|
83
|
-
Object.entries(plugins).forEach(([
|
|
84
|
-
if (!pluginConfig ||
|
|
84
|
+
Object.entries(plugins).forEach(([_, pluginConfig]) => {
|
|
85
|
+
if (!pluginConfig || pluginConfig.type === 'reset') {
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Check if plugin is enabled (can be boolean or function)
|
|
90
|
+
const isEnabled = typeof pluginConfig.enabled === 'function'
|
|
91
|
+
? pluginConfig.enabled(props.sparkTable.params)
|
|
92
|
+
: pluginConfig.enabled
|
|
93
|
+
|
|
94
|
+
if (!isEnabled) {
|
|
85
95
|
return
|
|
86
96
|
}
|
|
87
97
|
|
|
88
98
|
// Get the param key based on plugin type
|
|
99
|
+
let param = null
|
|
89
100
|
if (pluginConfig.type === 'search') {
|
|
90
|
-
|
|
91
|
-
paramsToClear.push(pluginConfig.param)
|
|
92
|
-
}
|
|
101
|
+
param = pluginConfig.param || 'search'
|
|
93
102
|
} else if (pluginConfig.type === 'filterSelect' || pluginConfig.type === 'filterButtons') {
|
|
94
|
-
|
|
95
|
-
paramsToClear.push(param)
|
|
103
|
+
param = pluginConfig.param || `filter[${pluginConfig.key}]`
|
|
96
104
|
} else if (pluginConfig.type === 'datePicker') {
|
|
97
|
-
|
|
105
|
+
param = pluginConfig.param || `filter[${pluginConfig.key}]`
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (!param) return
|
|
109
|
+
|
|
110
|
+
// Check if this plugin has an initialValue
|
|
111
|
+
if (pluginConfig.initialValue !== undefined && pluginConfig.initialValue !== null) {
|
|
112
|
+
// Reset to initialValue
|
|
113
|
+
paramsToReset[param] = pluginConfig.initialValue
|
|
114
|
+
} else {
|
|
115
|
+
// Clear the param
|
|
98
116
|
paramsToClear.push(param)
|
|
99
117
|
}
|
|
100
118
|
})
|
|
101
119
|
|
|
102
|
-
// Check if any params actually
|
|
120
|
+
// Check if any params actually need to be changed
|
|
103
121
|
const paramsToActuallyClear = paramsToClear.filter(param =>
|
|
104
122
|
props.sparkTable.params[param] !== undefined &&
|
|
105
123
|
props.sparkTable.params[param] !== null &&
|
|
106
124
|
props.sparkTable.params[param] !== ''
|
|
107
125
|
)
|
|
108
126
|
|
|
109
|
-
|
|
110
|
-
|
|
127
|
+
const paramsToActuallyReset = Object.entries(paramsToReset).filter(([param, value]) =>
|
|
128
|
+
props.sparkTable.params[param] !== value
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
// If nothing to change, don't do anything
|
|
132
|
+
if (paramsToActuallyClear.length === 0 && paramsToActuallyReset.length === 0) {
|
|
111
133
|
return
|
|
112
134
|
}
|
|
113
135
|
|
|
114
|
-
//
|
|
115
|
-
|
|
136
|
+
// Build the new params object
|
|
137
|
+
// First, clear all the params that should be cleared
|
|
138
|
+
paramsToActuallyClear.forEach(param => {
|
|
139
|
+
delete props.sparkTable.params[param]
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
// Then apply the reset values and go to page 1
|
|
143
|
+
const resetParams = Object.fromEntries(paramsToActuallyReset)
|
|
144
|
+
props.sparkTable.methods.applyParams({ ...resetParams, page: 1 })
|
|
116
145
|
}
|
|
117
146
|
</script>
|
|
118
147
|
|
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
v-model="searchValue"
|
|
5
5
|
type="text"
|
|
6
6
|
:placeholder="placeholder"
|
|
7
|
+
suffixIcon="search"
|
|
7
8
|
outer-class="!mb-0"
|
|
8
9
|
wrapper-class="!mb-0"
|
|
9
|
-
input-class="!w-
|
|
10
|
+
input-class="!w-44 !pr-8"
|
|
10
11
|
v-bind="props.config.formkitProps || {}"
|
|
11
12
|
/>
|
|
12
13
|
</div>
|
|
@@ -25,7 +26,7 @@
|
|
|
25
26
|
* enabled: true,
|
|
26
27
|
* placeholder: 'Search brands...',
|
|
27
28
|
* param: 'search',
|
|
28
|
-
* debounce:
|
|
29
|
+
* debounce: 400,
|
|
29
30
|
* position: 'header-left',
|
|
30
31
|
* },
|
|
31
32
|
* }
|
|
@@ -54,7 +55,7 @@ const props = defineProps({
|
|
|
54
55
|
default: () => ({
|
|
55
56
|
placeholder: 'Search...',
|
|
56
57
|
param: 'search',
|
|
57
|
-
debounce:
|
|
58
|
+
debounce: 400,
|
|
58
59
|
}),
|
|
59
60
|
},
|
|
60
61
|
})
|
|
@@ -97,11 +98,15 @@ watch(searchValue, (newValue) => {
|
|
|
97
98
|
|
|
98
99
|
const placeholder = props.config.placeholder || 'Search...'
|
|
99
100
|
|
|
100
|
-
// Watch for external param changes
|
|
101
|
+
// Watch for external param changes (e.g., from route sync, reset button)
|
|
101
102
|
watch(
|
|
102
103
|
() => props.sparkTable.params[paramKey],
|
|
103
104
|
(newValue) => {
|
|
104
|
-
if (
|
|
105
|
+
if (newValue && newValue !== searchValue.value) {
|
|
106
|
+
// Param was set externally (e.g., restored from URL)
|
|
107
|
+
searchValue.value = newValue
|
|
108
|
+
} else if (!newValue && searchValue.value) {
|
|
109
|
+
// Param was cleared externally
|
|
105
110
|
searchValue.value = ''
|
|
106
111
|
}
|
|
107
112
|
},
|
package/src/composables/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { sparkModalService } from './sparkModalService.js'
|
|
2
2
|
export { sparkOverlayService } from './sparkOverlayService.js'
|
|
3
|
-
export { useSparkOverlay } from './useSparkOverlay.js'
|
|
3
|
+
export { useSparkOverlay } from './useSparkOverlay.js'
|
|
4
|
+
export { useSparkTableRouteSync } from './useSparkTableRouteSync.js'
|