@wishbone-media/spark 0.38.0 → 0.40.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 +818 -845
- package/formkit.theme.mjs +3415 -3415
- package/package.json +31 -1
- package/src/assets/css/spark-table.css +1 -1
- package/src/assets/css/spark-tooltip.css +9 -3
- package/src/components/SparkAddressInput.vue +20 -3
- package/src/components/SparkAppSelector.vue +7 -7
- package/src/components/SparkBrandSelector.vue +2 -2
- package/src/components/SparkButton.vue +4 -4
- package/src/components/SparkButtonGroup.vue +2 -2
- package/src/components/SparkCard.vue +5 -3
- package/src/components/SparkEntityBadge.vue +3 -9
- package/src/components/SparkFileDragUpload.vue +13 -9
- package/src/components/SparkImageUpload.vue +4 -1
- package/src/components/SparkModalContainer.vue +23 -19
- package/src/components/SparkModalDialog.vue +36 -17
- package/src/components/SparkNotificationOutlet.vue +1 -1
- package/src/components/SparkOverlay.vue +20 -24
- package/src/components/SparkSubNav.vue +7 -14
- package/src/components/SparkTable.vue +72 -73
- package/src/components/SparkTablePaginationPaging.vue +2 -2
- package/src/components/SparkTableToolbar.vue +1 -1
- package/src/components/SparkToastContainer.vue +25 -41
- package/src/components/SparkTooltip.vue +14 -22
- package/src/components/plugins/SparkTableFilterButtons.vue +5 -1
- package/src/components/plugins/SparkTableFilterSelect.vue +1 -4
- package/src/components/plugins/SparkTableReset.vue +21 -19
- package/src/components/plugins/SparkTableSearch.vue +3 -2
- package/src/composables/index.js +1 -1
- package/src/composables/sparkModalService.js +64 -64
- package/src/composables/sparkNotificationService.js +10 -8
- package/src/composables/sparkOverlayService.js +36 -36
- package/src/composables/useCrudResource.js +1 -7
- package/src/composables/useFormSubmission.js +1 -3
- package/src/composables/useSparkOverlay.js +1 -1
- package/src/composables/useSparkTableRouteSync.js +4 -6
- package/src/composables/useSubNavigation.js +18 -15
- package/src/containers/SparkDefaultContainer.vue +8 -8
- package/src/containers/SparkPublicContainer.vue +1 -2
- package/src/directives/sparkTooltip.js +4 -11
- package/src/index.js +1 -1
- package/src/plugins/app-bootstrap.js +1 -1
- package/src/plugins/axios.js +1 -1
- package/src/plugins/fontawesome.js +3 -3
- package/src/plugins/index.js +17 -3
- package/src/plugins/router.js +8 -12
- package/src/stores/auth.js +1 -1
- package/src/stores/brand-filter.js +40 -40
- package/src/stores/index.js +1 -1
- package/src/stores/navigation.js +1 -1
- package/src/utils/formatTemporal.js +4 -4
- package/src/utils/sparkTable/renderers/badge.js +1 -1
- package/src/utils/sparkTable/renderers/boolean.js +3 -3
- package/src/utils/sparkTable/renderers/currency.js +1 -1
- package/src/utils/sparkTable/renderers/date.js +14 -6
- package/src/utils/sparkTable/renderers/datetime.js +1 -1
- package/src/utils/sparkTable/renderers/image.js +1 -1
- package/src/utils/sparkTable/renderers/link.js +1 -1
- package/src/views/SparkError403View.vue +3 -7
- package/src/views/SparkError404View.vue +3 -9
- package/src/views/SparkErrorGeneralView.vue +1 -3
- package/src/views/SparkForgotPasswordView.vue +7 -4
- package/src/views/SparkLoginView.vue +8 -7
- package/src/views/SparkLogoutView.vue +2 -1
- package/src/views/SparkResetPasswordView.vue +4 -6
|
@@ -1,47 +1,55 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="spark-table">
|
|
3
3
|
<!-- Header Toolbar: All plugins flow left to right -->
|
|
4
|
-
<
|
|
4
|
+
<SparkTableToolbar
|
|
5
|
+
v-if="sparkTable.computed.ready && headerPlugins && headerPlugins.length"
|
|
6
|
+
position="header"
|
|
7
|
+
>
|
|
5
8
|
<component
|
|
9
|
+
:is="pluginComponentsByType[plugin.config.type]"
|
|
6
10
|
v-for="plugin in headerPlugins"
|
|
7
11
|
:key="plugin.name"
|
|
8
|
-
:is="pluginComponentsByType[plugin.config.type]"
|
|
9
12
|
:class="plugin.config.align ? `self-${plugin.config.align}` : ''"
|
|
10
13
|
:spark-table="sparkTable"
|
|
11
14
|
:config="plugin.config"
|
|
12
15
|
/>
|
|
13
|
-
<slot name="header" :spark-table="sparkTable" :loading="loading" :error="error"
|
|
14
|
-
</
|
|
15
|
-
<div v-else class="pt-5"
|
|
16
|
+
<slot name="header" :spark-table="sparkTable" :loading="loading" :error="error" />
|
|
17
|
+
</SparkTableToolbar>
|
|
18
|
+
<div v-else class="pt-5" />
|
|
16
19
|
|
|
17
20
|
<!-- Table Grid -->
|
|
18
|
-
<HotTable
|
|
21
|
+
<HotTable
|
|
22
|
+
v-show="!isEmpty"
|
|
23
|
+
ref="table"
|
|
24
|
+
theme-name="ht-theme-classic"
|
|
25
|
+
:settings="sparkTable.tableSettings"
|
|
26
|
+
/>
|
|
19
27
|
|
|
20
28
|
<!-- Empty State -->
|
|
21
|
-
<div
|
|
29
|
+
<div
|
|
30
|
+
v-if="isEmpty"
|
|
31
|
+
class="flex flex-col items-center justify-center py-16 text-gray-500 rounded-md border border-gray-300 bg-white"
|
|
32
|
+
>
|
|
22
33
|
<font-awesome-icon :icon="Icons.farInbox" class="size-12 mb-4 text-gray-300" />
|
|
23
34
|
<p class="text-sm">No records found</p>
|
|
24
35
|
</div>
|
|
25
36
|
|
|
26
37
|
<!-- Footer Toolbar: Pagination always on the right -->
|
|
27
|
-
<
|
|
28
|
-
<spark-table
|
|
29
|
-
v-if="!isEmpty"
|
|
30
|
-
:spark-table="sparkTable"
|
|
31
|
-
/>
|
|
38
|
+
<SparkTableToolbar v-if="sparkTable.computed.ready" position="footer">
|
|
39
|
+
<SparkTablePaginationDetails v-if="!isEmpty" :spark-table="sparkTable" />
|
|
32
40
|
<div class="flex items-center gap-x-3 ml-auto">
|
|
33
|
-
<
|
|
41
|
+
<SparkTablePaginationPaging
|
|
34
42
|
v-if="!isEmpty"
|
|
35
43
|
:spark-table="sparkTable"
|
|
36
44
|
@paginate="sparkTable.methods.applyParams"
|
|
37
45
|
/>
|
|
38
|
-
<
|
|
46
|
+
<SparkTablePaginationPerPage
|
|
39
47
|
:spark-table="sparkTable"
|
|
40
48
|
@paginate="sparkTable.methods.applyParams"
|
|
41
49
|
/>
|
|
42
|
-
<slot name="footer" :spark-table="sparkTable" :loading="loading" :error="error"
|
|
50
|
+
<slot name="footer" :spark-table="sparkTable" :loading="loading" :error="error" />
|
|
43
51
|
</div>
|
|
44
|
-
</
|
|
52
|
+
</SparkTableToolbar>
|
|
45
53
|
</div>
|
|
46
54
|
</template>
|
|
47
55
|
|
|
@@ -92,16 +100,24 @@ import SparkTablePaginationDetails from './SparkTablePaginationDetails.vue'
|
|
|
92
100
|
import SparkTablePaginationPaging from './SparkTablePaginationPaging.vue'
|
|
93
101
|
import SparkTablePaginationPerPage from './SparkTablePaginationPerPage.vue'
|
|
94
102
|
import SparkTableToolbar from './SparkTableToolbar.vue'
|
|
95
|
-
import {
|
|
103
|
+
import {
|
|
104
|
+
SparkTableSearch,
|
|
105
|
+
SparkTableFilterSelect,
|
|
106
|
+
SparkTableFilterButtons,
|
|
107
|
+
SparkTableDatePicker,
|
|
108
|
+
SparkTableReset,
|
|
109
|
+
} from './plugins'
|
|
96
110
|
import { Icons } from '@/plugins/fontawesome.js'
|
|
97
111
|
|
|
98
112
|
const props = defineProps({
|
|
99
113
|
url: {
|
|
100
114
|
type: String,
|
|
115
|
+
default: null,
|
|
101
116
|
},
|
|
102
117
|
|
|
103
118
|
dataProvider: {
|
|
104
119
|
type: Function,
|
|
120
|
+
default: null,
|
|
105
121
|
},
|
|
106
122
|
|
|
107
123
|
dataTransformer: {
|
|
@@ -316,7 +332,7 @@ const sparkTable = reactive({
|
|
|
316
332
|
clearParams: (keys) => {
|
|
317
333
|
// Clear multiple params at once without triggering multiple reloads
|
|
318
334
|
const keysArray = Array.isArray(keys) ? keys : [keys]
|
|
319
|
-
keysArray.forEach(key => {
|
|
335
|
+
keysArray.forEach((key) => {
|
|
320
336
|
if (sparkTable.params[key] !== undefined) {
|
|
321
337
|
delete sparkTable.params[key]
|
|
322
338
|
}
|
|
@@ -342,60 +358,42 @@ const sparkTable = reactive({
|
|
|
342
358
|
|
|
343
359
|
tableSettings: computed(() => ({
|
|
344
360
|
...defaultTableSettings,
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
*/
|
|
371
|
-
beforeCopy: (data, coords) => {
|
|
372
|
-
const hot = table.value?.hotInstance
|
|
373
|
-
if (!hot) return
|
|
374
|
-
|
|
375
|
-
coords.forEach((range) => {
|
|
376
|
-
for (let row = range.startRow; row <= range.endRow; row++) {
|
|
377
|
-
for (let col = range.startCol; col <= range.endCol; col++) {
|
|
378
|
-
const td = hot.getCell(row, col)
|
|
379
|
-
if (td) {
|
|
380
|
-
const dataRow = row - coords[0].startRow
|
|
381
|
-
const dataCol = col - coords[0].startCol
|
|
382
|
-
// Prefer data-copy-value (for renderers like boolean with icons)
|
|
383
|
-
// Fall back to textContent for standard rendered content
|
|
384
|
-
data[dataRow][dataCol] = td.dataset.copyValue ?? td.textContent ?? ''
|
|
385
|
-
}
|
|
361
|
+
nestedHeaders: get(props.settings, 'nestedHeaders', []),
|
|
362
|
+
...(!get(props.settings, 'nestedHeaders') && {
|
|
363
|
+
afterGetColHeader: (col, th) => customiseHeader(col, th, sparkTable),
|
|
364
|
+
}),
|
|
365
|
+
afterChange: (changes, source) => updateRow(changes, source, sparkTable),
|
|
366
|
+
afterRender: () => syncSortClasses(sparkTable),
|
|
367
|
+
beforeStretchingColumnWidth: (stretchedWidth, column) => {
|
|
368
|
+
const columns = get(props.settings, 'columns', [])
|
|
369
|
+
const columnSettings = columns[column]
|
|
370
|
+
if (columnSettings && columnSettings.width !== undefined) {
|
|
371
|
+
return columnSettings.width
|
|
372
|
+
}
|
|
373
|
+
return stretchedWidth
|
|
374
|
+
},
|
|
375
|
+
beforeCopy: (data, coords) => {
|
|
376
|
+
const hot = table.value?.hotInstance
|
|
377
|
+
if (!hot) return
|
|
378
|
+
coords.forEach((range) => {
|
|
379
|
+
for (let row = range.startRow; row <= range.endRow; row++) {
|
|
380
|
+
for (let col = range.startCol; col <= range.endCol; col++) {
|
|
381
|
+
const td = hot.getCell(row, col)
|
|
382
|
+
if (td) {
|
|
383
|
+
const dataRow = row - coords[0].startRow
|
|
384
|
+
const dataCol = col - coords[0].startCol
|
|
385
|
+
data[dataRow][dataCol] = td.dataset.copyValue ?? td.textContent ?? ''
|
|
386
386
|
}
|
|
387
387
|
}
|
|
388
|
-
}
|
|
389
|
-
}
|
|
388
|
+
}
|
|
389
|
+
})
|
|
390
390
|
},
|
|
391
391
|
...props.settings,
|
|
392
392
|
})),
|
|
393
393
|
})
|
|
394
394
|
|
|
395
395
|
// Empty state detection - true when data has loaded but contains no records
|
|
396
|
-
const isEmpty = computed(() =>
|
|
397
|
-
sparkTable.computed.ready && sparkTable.response.data?.length === 0
|
|
398
|
-
)
|
|
396
|
+
const isEmpty = computed(() => sparkTable.computed.ready && sparkTable.response.data?.length === 0)
|
|
399
397
|
|
|
400
398
|
/**
|
|
401
399
|
* Get the param key for a plugin based on its type and configuration
|
|
@@ -429,12 +427,13 @@ const isPluginEnabled = (pluginConfig) => {
|
|
|
429
427
|
// `enabled` can be a boolean or a function that receives current params
|
|
430
428
|
const headerPlugins = computed(() => {
|
|
431
429
|
return Object.entries(props.plugins)
|
|
432
|
-
.filter(([
|
|
430
|
+
.filter(([, config]) => isPluginEnabled(config))
|
|
433
431
|
.map(([name, config]) => ({ name, config }))
|
|
434
432
|
})
|
|
435
433
|
|
|
436
434
|
// Get all plugin param keys (regardless of enabled state)
|
|
437
|
-
|
|
435
|
+
// eslint-disable-next-line no-unused-vars -- reserved for future external use
|
|
436
|
+
const _getAllPluginParamKeys = () => {
|
|
438
437
|
const keys = []
|
|
439
438
|
Object.values(props.plugins).forEach((config) => {
|
|
440
439
|
const paramKey = getPluginParamKey(config)
|
|
@@ -470,7 +469,7 @@ watch(
|
|
|
470
469
|
})
|
|
471
470
|
}
|
|
472
471
|
},
|
|
473
|
-
{ deep: true, flush: 'sync' }
|
|
472
|
+
{ deep: true, flush: 'sync' },
|
|
474
473
|
)
|
|
475
474
|
|
|
476
475
|
// Setup route sync and/or storage persistence if enabled
|
|
@@ -502,13 +501,13 @@ watchDebounced(
|
|
|
502
501
|
)
|
|
503
502
|
|
|
504
503
|
watch(
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
}
|
|
504
|
+
() => props.url,
|
|
505
|
+
async (newUrl, oldUrl) => {
|
|
506
|
+
if (newUrl !== oldUrl) {
|
|
507
|
+
sparkTable.params.page = 1
|
|
508
|
+
await sparkTable.methods.loadTable()
|
|
511
509
|
}
|
|
510
|
+
},
|
|
512
511
|
)
|
|
513
512
|
|
|
514
513
|
onMounted(async () => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div>
|
|
3
3
|
<div class="flex items-center gap-4 px-4 py-3">
|
|
4
|
-
<div
|
|
4
|
+
<div v-if="pageRange.length > 1" class="shrink-0 ml-auto">
|
|
5
5
|
<div>
|
|
6
6
|
<nav
|
|
7
7
|
class="isolate inline-flex -space-x-px rounded-md shadow-xs bg-white"
|
|
@@ -33,9 +33,9 @@
|
|
|
33
33
|
|
|
34
34
|
<a
|
|
35
35
|
:class="canNext ? '' : 'disabled'"
|
|
36
|
-
@click.prevent="pageChanged(1)"
|
|
37
36
|
href="#"
|
|
38
37
|
class="relative inline-flex items-center rounded-r-md px-2 py-[9px] text-gray-400 ring-1 ring-gray-300 ring-inset hover:bg-gray-50 focus:z-20 focus:outline-offset-0"
|
|
38
|
+
@click.prevent="pageChanged(1)"
|
|
39
39
|
>
|
|
40
40
|
<font-awesome-icon :icon="Icons.farChevronRight" class="size-5" />
|
|
41
41
|
</a>
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<Teleport to="body">
|
|
3
3
|
<!-- Top Left -->
|
|
4
|
-
<div
|
|
4
|
+
<div
|
|
5
|
+
class="fixed top-4 left-4 z-[2000] flex flex-col gap-3 max-w-sm w-full pointer-events-none"
|
|
6
|
+
>
|
|
5
7
|
<TransitionGroup
|
|
6
8
|
enter-active-class="transition-all duration-300 ease-out"
|
|
7
9
|
enter-from-class="opacity-0 -translate-x-4"
|
|
@@ -18,11 +20,7 @@
|
|
|
18
20
|
@mouseenter="handleMouseEnter(toast.id)"
|
|
19
21
|
@mouseleave="handleMouseLeave(toast.id)"
|
|
20
22
|
>
|
|
21
|
-
<component
|
|
22
|
-
v-if="toast.component"
|
|
23
|
-
:is="toast.component"
|
|
24
|
-
v-bind="toast.props"
|
|
25
|
-
/>
|
|
23
|
+
<component :is="toast.component" v-if="toast.component" v-bind="toast.props" />
|
|
26
24
|
<template v-else>{{ toast.message }}</template>
|
|
27
25
|
</SparkAlert>
|
|
28
26
|
</div>
|
|
@@ -30,7 +28,9 @@
|
|
|
30
28
|
</div>
|
|
31
29
|
|
|
32
30
|
<!-- Top Right -->
|
|
33
|
-
<div
|
|
31
|
+
<div
|
|
32
|
+
class="fixed top-4 right-4 z-[2000] flex flex-col gap-3 max-w-sm w-full pointer-events-none"
|
|
33
|
+
>
|
|
34
34
|
<TransitionGroup
|
|
35
35
|
enter-active-class="transition-all duration-300 ease-out"
|
|
36
36
|
enter-from-class="opacity-0 translate-x-4"
|
|
@@ -47,11 +47,7 @@
|
|
|
47
47
|
@mouseenter="handleMouseEnter(toast.id)"
|
|
48
48
|
@mouseleave="handleMouseLeave(toast.id)"
|
|
49
49
|
>
|
|
50
|
-
<component
|
|
51
|
-
v-if="toast.component"
|
|
52
|
-
:is="toast.component"
|
|
53
|
-
v-bind="toast.props"
|
|
54
|
-
/>
|
|
50
|
+
<component :is="toast.component" v-if="toast.component" v-bind="toast.props" />
|
|
55
51
|
<template v-else>{{ toast.message }}</template>
|
|
56
52
|
</SparkAlert>
|
|
57
53
|
</div>
|
|
@@ -59,7 +55,9 @@
|
|
|
59
55
|
</div>
|
|
60
56
|
|
|
61
57
|
<!-- Center (slightly above center) -->
|
|
62
|
-
<div
|
|
58
|
+
<div
|
|
59
|
+
class="fixed top-[40%] left-1/2 -translate-x-1/2 -translate-y-1/2 z-[2000] flex flex-col gap-3 max-w-sm w-full pointer-events-none"
|
|
60
|
+
>
|
|
63
61
|
<TransitionGroup
|
|
64
62
|
enter-active-class="transition-all duration-300 ease-out"
|
|
65
63
|
enter-from-class="opacity-0 scale-95"
|
|
@@ -76,11 +74,7 @@
|
|
|
76
74
|
@mouseenter="handleMouseEnter(toast.id)"
|
|
77
75
|
@mouseleave="handleMouseLeave(toast.id)"
|
|
78
76
|
>
|
|
79
|
-
<component
|
|
80
|
-
v-if="toast.component"
|
|
81
|
-
:is="toast.component"
|
|
82
|
-
v-bind="toast.props"
|
|
83
|
-
/>
|
|
77
|
+
<component :is="toast.component" v-if="toast.component" v-bind="toast.props" />
|
|
84
78
|
<template v-else>{{ toast.message }}</template>
|
|
85
79
|
</SparkAlert>
|
|
86
80
|
</div>
|
|
@@ -88,7 +82,9 @@
|
|
|
88
82
|
</div>
|
|
89
83
|
|
|
90
84
|
<!-- Bottom Left -->
|
|
91
|
-
<div
|
|
85
|
+
<div
|
|
86
|
+
class="fixed bottom-4 left-4 z-[2000] flex flex-col-reverse gap-3 max-w-sm w-full pointer-events-none"
|
|
87
|
+
>
|
|
92
88
|
<TransitionGroup
|
|
93
89
|
enter-active-class="transition-all duration-300 ease-out"
|
|
94
90
|
enter-from-class="opacity-0 -translate-x-4"
|
|
@@ -105,11 +101,7 @@
|
|
|
105
101
|
@mouseenter="handleMouseEnter(toast.id)"
|
|
106
102
|
@mouseleave="handleMouseLeave(toast.id)"
|
|
107
103
|
>
|
|
108
|
-
<component
|
|
109
|
-
v-if="toast.component"
|
|
110
|
-
:is="toast.component"
|
|
111
|
-
v-bind="toast.props"
|
|
112
|
-
/>
|
|
104
|
+
<component :is="toast.component" v-if="toast.component" v-bind="toast.props" />
|
|
113
105
|
<template v-else>{{ toast.message }}</template>
|
|
114
106
|
</SparkAlert>
|
|
115
107
|
</div>
|
|
@@ -117,7 +109,9 @@
|
|
|
117
109
|
</div>
|
|
118
110
|
|
|
119
111
|
<!-- Bottom Right -->
|
|
120
|
-
<div
|
|
112
|
+
<div
|
|
113
|
+
class="fixed bottom-4 right-4 z-[2000] flex flex-col-reverse gap-3 max-w-sm w-full pointer-events-none"
|
|
114
|
+
>
|
|
121
115
|
<TransitionGroup
|
|
122
116
|
enter-active-class="transition-all duration-300 ease-out"
|
|
123
117
|
enter-from-class="opacity-0 translate-x-4"
|
|
@@ -134,11 +128,7 @@
|
|
|
134
128
|
@mouseenter="handleMouseEnter(toast.id)"
|
|
135
129
|
@mouseleave="handleMouseLeave(toast.id)"
|
|
136
130
|
>
|
|
137
|
-
<component
|
|
138
|
-
v-if="toast.component"
|
|
139
|
-
:is="toast.component"
|
|
140
|
-
v-bind="toast.props"
|
|
141
|
-
/>
|
|
131
|
+
<component :is="toast.component" v-if="toast.component" v-bind="toast.props" />
|
|
142
132
|
<template v-else>{{ toast.message }}</template>
|
|
143
133
|
</SparkAlert>
|
|
144
134
|
</div>
|
|
@@ -154,20 +144,14 @@ import SparkAlert from '@/components/SparkAlert.vue'
|
|
|
154
144
|
|
|
155
145
|
const toastState = sparkNotificationService.toastState
|
|
156
146
|
|
|
157
|
-
const topLeftToasts = computed(() =>
|
|
158
|
-
|
|
159
|
-
)
|
|
160
|
-
const topRightToasts = computed(() =>
|
|
161
|
-
toastState.toasts.filter(t => t.position === 'top-right')
|
|
162
|
-
)
|
|
163
|
-
const centerToasts = computed(() =>
|
|
164
|
-
toastState.toasts.filter(t => t.position === 'center')
|
|
165
|
-
)
|
|
147
|
+
const topLeftToasts = computed(() => toastState.toasts.filter((t) => t.position === 'top-left'))
|
|
148
|
+
const topRightToasts = computed(() => toastState.toasts.filter((t) => t.position === 'top-right'))
|
|
149
|
+
const centerToasts = computed(() => toastState.toasts.filter((t) => t.position === 'center'))
|
|
166
150
|
const bottomLeftToasts = computed(() =>
|
|
167
|
-
toastState.toasts.filter(t => t.position === 'bottom-left')
|
|
151
|
+
toastState.toasts.filter((t) => t.position === 'bottom-left'),
|
|
168
152
|
)
|
|
169
153
|
const bottomRightToasts = computed(() =>
|
|
170
|
-
toastState.toasts.filter(t => t.position === 'bottom-right')
|
|
154
|
+
toastState.toasts.filter((t) => t.position === 'bottom-right'),
|
|
171
155
|
)
|
|
172
156
|
|
|
173
157
|
const handleClose = (toastId) => {
|
|
@@ -20,15 +20,11 @@
|
|
|
20
20
|
role="tooltip"
|
|
21
21
|
>
|
|
22
22
|
<slot name="content">
|
|
23
|
+
<!-- eslint-disable-next-line vue/no-v-html -- html prop is opt-in, consumer responsibility -->
|
|
23
24
|
<span v-if="html" v-html="content" />
|
|
24
25
|
<span v-else>{{ content }}</span>
|
|
25
26
|
</slot>
|
|
26
|
-
<div
|
|
27
|
-
v-if="showArrow"
|
|
28
|
-
ref="arrowRef"
|
|
29
|
-
class="spark-tooltip-arrow"
|
|
30
|
-
:style="arrowStyles"
|
|
31
|
-
/>
|
|
27
|
+
<div v-if="showArrow" ref="arrowRef" class="spark-tooltip-arrow" :style="arrowStyles" />
|
|
32
28
|
</div>
|
|
33
29
|
</Transition>
|
|
34
30
|
</Teleport>
|
|
@@ -101,26 +97,22 @@ let showTimeout = null
|
|
|
101
97
|
let hideTimeout = null
|
|
102
98
|
|
|
103
99
|
const middleware = computed(() => {
|
|
104
|
-
const mw = [
|
|
105
|
-
offsetMiddleware(props.offset),
|
|
106
|
-
flip(),
|
|
107
|
-
shift({ padding: 8 }),
|
|
108
|
-
]
|
|
100
|
+
const mw = [offsetMiddleware(props.offset), flip(), shift({ padding: 8 })]
|
|
109
101
|
if (props.showArrow) {
|
|
110
102
|
mw.push(arrowMiddleware({ element: arrowRef, padding: 5 }))
|
|
111
103
|
}
|
|
112
104
|
return mw
|
|
113
105
|
})
|
|
114
106
|
|
|
115
|
-
const {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
)
|
|
107
|
+
const {
|
|
108
|
+
floatingStyles,
|
|
109
|
+
middlewareData,
|
|
110
|
+
placement: actualPlacement,
|
|
111
|
+
} = useFloating(referenceRef, floatingRef, {
|
|
112
|
+
placement: computed(() => props.placement),
|
|
113
|
+
middleware,
|
|
114
|
+
whileElementsMounted: autoUpdate,
|
|
115
|
+
})
|
|
124
116
|
|
|
125
117
|
const arrowStyles = computed(() => {
|
|
126
118
|
const data = middlewareData.value?.arrow
|
|
@@ -141,7 +133,7 @@ function show() {
|
|
|
141
133
|
if (!props.content && !slots.content) return
|
|
142
134
|
|
|
143
135
|
clearTimeout(hideTimeout)
|
|
144
|
-
const delay = typeof props.delay === 'number' ? props.delay : props.delay.show ?? 200
|
|
136
|
+
const delay = typeof props.delay === 'number' ? props.delay : (props.delay.show ?? 200)
|
|
145
137
|
showTimeout = setTimeout(() => {
|
|
146
138
|
isVisible.value = true
|
|
147
139
|
}, delay)
|
|
@@ -149,7 +141,7 @@ function show() {
|
|
|
149
141
|
|
|
150
142
|
function hide() {
|
|
151
143
|
clearTimeout(showTimeout)
|
|
152
|
-
const delay = typeof props.delay === 'number' ? 0 : props.delay.hide ?? 0
|
|
144
|
+
const delay = typeof props.delay === 'number' ? 0 : (props.delay.hide ?? 0)
|
|
153
145
|
hideTimeout = setTimeout(() => {
|
|
154
146
|
isVisible.value = false
|
|
155
147
|
}, delay)
|
|
@@ -89,7 +89,11 @@ if (props.sparkTable.params[paramKey] !== undefined) {
|
|
|
89
89
|
const isSelected = (value) => {
|
|
90
90
|
// Handle null/undefined comparison
|
|
91
91
|
if (value === null || value === undefined) {
|
|
92
|
-
return
|
|
92
|
+
return (
|
|
93
|
+
selectedValue.value === null ||
|
|
94
|
+
selectedValue.value === undefined ||
|
|
95
|
+
selectedValue.value === ''
|
|
96
|
+
)
|
|
93
97
|
}
|
|
94
98
|
return selectedValue.value === value
|
|
95
99
|
}
|
|
@@ -87,10 +87,7 @@ const label = props.config.label || null
|
|
|
87
87
|
|
|
88
88
|
// Format options for FormKit - add empty option for "All"
|
|
89
89
|
const selectOptions = computed(() => {
|
|
90
|
-
return [
|
|
91
|
-
{ label: props.config.placeholder || 'All', value: '' },
|
|
92
|
-
...props.config.options,
|
|
93
|
-
]
|
|
90
|
+
return [{ label: props.config.placeholder || 'All', value: '' }, ...props.config.options]
|
|
94
91
|
})
|
|
95
92
|
|
|
96
93
|
// Watch selectedValue for changes and apply filter
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="spark-table-reset">
|
|
3
|
-
<SparkButton
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
<
|
|
10
|
-
<span v-if="config.label" >{{ config.label }}</span>
|
|
3
|
+
<SparkButton variant="secondary" button-class="px-3.5" size="xl" @click="handleReset">
|
|
4
|
+
<font-awesome-icon
|
|
5
|
+
v-if="config.icon"
|
|
6
|
+
:icon="['far', config.icon]"
|
|
7
|
+
:class="{ 'mr-1.5': config.label }"
|
|
8
|
+
/>
|
|
9
|
+
<span v-if="config.label">{{ config.label }}</span>
|
|
11
10
|
</SparkButton>
|
|
12
11
|
</div>
|
|
13
12
|
</template>
|
|
@@ -82,15 +81,16 @@ const handleReset = () => {
|
|
|
82
81
|
const paramsToClear = []
|
|
83
82
|
const paramsToReset = {}
|
|
84
83
|
|
|
85
|
-
Object.entries(plugins).forEach(([
|
|
84
|
+
Object.entries(plugins).forEach(([, pluginConfig]) => {
|
|
86
85
|
if (!pluginConfig || pluginConfig.type === 'reset') {
|
|
87
86
|
return
|
|
88
87
|
}
|
|
89
88
|
|
|
90
89
|
// Check if plugin is enabled (can be boolean or function)
|
|
91
|
-
const isEnabled =
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
const isEnabled =
|
|
91
|
+
typeof pluginConfig.enabled === 'function'
|
|
92
|
+
? pluginConfig.enabled(props.sparkTable.params)
|
|
93
|
+
: pluginConfig.enabled
|
|
94
94
|
|
|
95
95
|
if (!isEnabled) {
|
|
96
96
|
return
|
|
@@ -119,14 +119,15 @@ const handleReset = () => {
|
|
|
119
119
|
})
|
|
120
120
|
|
|
121
121
|
// Check if any params actually need to be changed
|
|
122
|
-
const paramsToActuallyClear = paramsToClear.filter(
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
122
|
+
const paramsToActuallyClear = paramsToClear.filter(
|
|
123
|
+
(param) =>
|
|
124
|
+
props.sparkTable.params[param] !== undefined &&
|
|
125
|
+
props.sparkTable.params[param] !== null &&
|
|
126
|
+
props.sparkTable.params[param] !== '',
|
|
126
127
|
)
|
|
127
128
|
|
|
128
|
-
const paramsToActuallyReset = Object.entries(paramsToReset).filter(
|
|
129
|
-
props.sparkTable.params[param] !== value
|
|
129
|
+
const paramsToActuallyReset = Object.entries(paramsToReset).filter(
|
|
130
|
+
([param, value]) => props.sparkTable.params[param] !== value,
|
|
130
131
|
)
|
|
131
132
|
|
|
132
133
|
// If nothing to change, don't do anything
|
|
@@ -136,7 +137,8 @@ const handleReset = () => {
|
|
|
136
137
|
|
|
137
138
|
// Build the new params object
|
|
138
139
|
// First, clear all the params that should be cleared
|
|
139
|
-
paramsToActuallyClear.forEach(param => {
|
|
140
|
+
paramsToActuallyClear.forEach((param) => {
|
|
141
|
+
// eslint-disable-next-line vue/no-mutating-props -- sparkTable.params is a shared reactive object
|
|
140
142
|
delete props.sparkTable.params[param]
|
|
141
143
|
})
|
|
142
144
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
v-model="searchValue"
|
|
5
5
|
type="text"
|
|
6
6
|
:placeholder="placeholder"
|
|
7
|
-
|
|
7
|
+
suffix-icon="search"
|
|
8
8
|
outer-class="!mb-0"
|
|
9
9
|
wrapper-class="!mb-0"
|
|
10
10
|
input-class="!w-44 !pr-8"
|
|
@@ -74,7 +74,8 @@ if (props.sparkTable.params[paramKey]) {
|
|
|
74
74
|
})
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
// eslint-disable-next-line no-unused-vars -- reserved for programmatic search reset
|
|
78
|
+
const _clearSearch = () => {
|
|
78
79
|
searchValue.value = ''
|
|
79
80
|
props.sparkTable.methods.removeParam(paramKey)
|
|
80
81
|
}
|
package/src/composables/index.js
CHANGED