@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.
Files changed (66) hide show
  1. package/dist/index.css +1 -1
  2. package/dist/index.js +818 -845
  3. package/formkit.theme.mjs +3415 -3415
  4. package/package.json +31 -1
  5. package/src/assets/css/spark-table.css +1 -1
  6. package/src/assets/css/spark-tooltip.css +9 -3
  7. package/src/components/SparkAddressInput.vue +20 -3
  8. package/src/components/SparkAppSelector.vue +7 -7
  9. package/src/components/SparkBrandSelector.vue +2 -2
  10. package/src/components/SparkButton.vue +4 -4
  11. package/src/components/SparkButtonGroup.vue +2 -2
  12. package/src/components/SparkCard.vue +5 -3
  13. package/src/components/SparkEntityBadge.vue +3 -9
  14. package/src/components/SparkFileDragUpload.vue +13 -9
  15. package/src/components/SparkImageUpload.vue +4 -1
  16. package/src/components/SparkModalContainer.vue +23 -19
  17. package/src/components/SparkModalDialog.vue +36 -17
  18. package/src/components/SparkNotificationOutlet.vue +1 -1
  19. package/src/components/SparkOverlay.vue +20 -24
  20. package/src/components/SparkSubNav.vue +7 -14
  21. package/src/components/SparkTable.vue +72 -73
  22. package/src/components/SparkTablePaginationPaging.vue +2 -2
  23. package/src/components/SparkTableToolbar.vue +1 -1
  24. package/src/components/SparkToastContainer.vue +25 -41
  25. package/src/components/SparkTooltip.vue +14 -22
  26. package/src/components/plugins/SparkTableFilterButtons.vue +5 -1
  27. package/src/components/plugins/SparkTableFilterSelect.vue +1 -4
  28. package/src/components/plugins/SparkTableReset.vue +21 -19
  29. package/src/components/plugins/SparkTableSearch.vue +3 -2
  30. package/src/composables/index.js +1 -1
  31. package/src/composables/sparkModalService.js +64 -64
  32. package/src/composables/sparkNotificationService.js +10 -8
  33. package/src/composables/sparkOverlayService.js +36 -36
  34. package/src/composables/useCrudResource.js +1 -7
  35. package/src/composables/useFormSubmission.js +1 -3
  36. package/src/composables/useSparkOverlay.js +1 -1
  37. package/src/composables/useSparkTableRouteSync.js +4 -6
  38. package/src/composables/useSubNavigation.js +18 -15
  39. package/src/containers/SparkDefaultContainer.vue +8 -8
  40. package/src/containers/SparkPublicContainer.vue +1 -2
  41. package/src/directives/sparkTooltip.js +4 -11
  42. package/src/index.js +1 -1
  43. package/src/plugins/app-bootstrap.js +1 -1
  44. package/src/plugins/axios.js +1 -1
  45. package/src/plugins/fontawesome.js +3 -3
  46. package/src/plugins/index.js +17 -3
  47. package/src/plugins/router.js +8 -12
  48. package/src/stores/auth.js +1 -1
  49. package/src/stores/brand-filter.js +40 -40
  50. package/src/stores/index.js +1 -1
  51. package/src/stores/navigation.js +1 -1
  52. package/src/utils/formatTemporal.js +4 -4
  53. package/src/utils/sparkTable/renderers/badge.js +1 -1
  54. package/src/utils/sparkTable/renderers/boolean.js +3 -3
  55. package/src/utils/sparkTable/renderers/currency.js +1 -1
  56. package/src/utils/sparkTable/renderers/date.js +14 -6
  57. package/src/utils/sparkTable/renderers/datetime.js +1 -1
  58. package/src/utils/sparkTable/renderers/image.js +1 -1
  59. package/src/utils/sparkTable/renderers/link.js +1 -1
  60. package/src/views/SparkError403View.vue +3 -7
  61. package/src/views/SparkError404View.vue +3 -9
  62. package/src/views/SparkErrorGeneralView.vue +1 -3
  63. package/src/views/SparkForgotPasswordView.vue +7 -4
  64. package/src/views/SparkLoginView.vue +8 -7
  65. package/src/views/SparkLogoutView.vue +2 -1
  66. 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
- <spark-table-toolbar v-if="sparkTable.computed.ready && headerPlugins && headerPlugins.length" position="header">
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"></slot>
14
- </spark-table-toolbar>
15
- <div v-else class="pt-5"></div>
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 v-show="!isEmpty" theme-name="ht-theme-classic" ref="table" :settings="sparkTable.tableSettings" />
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 v-if="isEmpty" class="flex flex-col items-center justify-center py-16 text-gray-500 rounded-md border border-gray-300 bg-white">
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
- <spark-table-toolbar v-if="sparkTable.computed.ready" position="footer">
28
- <spark-table-pagination-details
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
- <spark-table-pagination-paging
41
+ <SparkTablePaginationPaging
34
42
  v-if="!isEmpty"
35
43
  :spark-table="sparkTable"
36
44
  @paginate="sparkTable.methods.applyParams"
37
45
  />
38
- <spark-table-pagination-per-page
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"></slot>
50
+ <slot name="footer" :spark-table="sparkTable" :loading="loading" :error="error" />
43
51
  </div>
44
- </spark-table-toolbar>
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 { SparkTableSearch, SparkTableFilterSelect, SparkTableFilterButtons, SparkTableDatePicker, SparkTableReset } from './plugins'
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
- nestedHeaders: get(props.settings, 'nestedHeaders', []),
348
- },
349
- ...(!get(props.settings, 'nestedHeaders') && {
350
- afterGetColHeader: (col, th) => customiseHeader(col, th, sparkTable),
351
- }),
352
- afterChange: (changes, source) => updateRow(changes, source, sparkTable),
353
- afterRender: () => syncSortClasses(sparkTable),
354
- /**
355
- * Prevent columns with explicit width from being stretched
356
- * This hook fires BEFORE stretchH is applied, allowing us to cap specific columns
357
- * while letting others stretch normally
358
- */
359
- beforeStretchingColumnWidth: (stretchedWidth, column) => {
360
- const columns = get(props.settings, 'columns', [])
361
- const columnSettings = columns[column]
362
- if (columnSettings && columnSettings.width !== undefined) {
363
- return columnSettings.width
364
- }
365
- return stretchedWidth
366
- },
367
- /**
368
- * Copy displayed cell content instead of raw data values
369
- * This ensures custom renderers copy their visual output, not the underlying data
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(([_, config]) => isPluginEnabled(config))
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
- const getAllPluginParamKeys = () => {
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
- () => props.url,
506
- async (newUrl, oldUrl) => {
507
- if (newUrl !== oldUrl) {
508
- sparkTable.params.page = 1
509
- await sparkTable.methods.loadTable()
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 class="shrink-0 ml-auto" v-if="pageRange.length > 1">
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,6 +1,6 @@
1
1
  <template>
2
2
  <div v-if="hasContent" :class="toolbarClass">
3
- <slot></slot>
3
+ <slot />
4
4
  </div>
5
5
  </template>
6
6
 
@@ -1,7 +1,9 @@
1
1
  <template>
2
2
  <Teleport to="body">
3
3
  <!-- Top Left -->
4
- <div class="fixed top-4 left-4 z-[2000] flex flex-col gap-3 max-w-sm w-full pointer-events-none">
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 class="fixed top-4 right-4 z-[2000] flex flex-col gap-3 max-w-sm w-full pointer-events-none">
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 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">
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 class="fixed bottom-4 left-4 z-[2000] flex flex-col-reverse gap-3 max-w-sm w-full pointer-events-none">
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 class="fixed bottom-4 right-4 z-[2000] flex flex-col-reverse gap-3 max-w-sm w-full pointer-events-none">
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
- toastState.toasts.filter(t => t.position === 'top-left')
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 { floatingStyles, middlewareData, placement: actualPlacement } = useFloating(
116
- referenceRef,
117
- floatingRef,
118
- {
119
- placement: computed(() => props.placement),
120
- middleware,
121
- whileElementsMounted: autoUpdate,
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 selectedValue.value === null || selectedValue.value === undefined || selectedValue.value === ''
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
- @click="handleReset"
5
- variant="secondary"
6
- button-class="px-3.5"
7
- size="xl"
8
- >
9
- <font-awesome-icon v-if="config.icon" :icon="['far', config.icon]" :class="{'mr-1.5': config.label}"/>
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(([_, pluginConfig]) => {
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 = typeof pluginConfig.enabled === 'function'
92
- ? pluginConfig.enabled(props.sparkTable.params)
93
- : pluginConfig.enabled
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(param =>
123
- props.sparkTable.params[param] !== undefined &&
124
- props.sparkTable.params[param] !== null &&
125
- props.sparkTable.params[param] !== ''
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(([param, value]) =>
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
- suffixIcon="search"
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
- const clearSearch = () => {
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
  }
@@ -12,4 +12,4 @@ export {
12
12
  getDirtyFormMessage,
13
13
  clearAllDirtyForms,
14
14
  } from './useFormDirtyGuard.js'
15
- export { useGooglePlaces } from './useGooglePlaces.js'
15
+ export { useGooglePlaces } from './useGooglePlaces.js'