nuxt-ui-tools 1.4.0 → 1.6.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 (97) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +3 -1
  3. package/dist/runtime/dashboard/composables/use-dashboard-remote-options.js +8 -3
  4. package/dist/runtime/dashboard/types/filters.d.ts +7 -1
  5. package/dist/runtime/dashboard/utils/builders/dashboard-filters.js +19 -2
  6. package/dist/runtime/form/composables/use-field-options.js +19 -0
  7. package/dist/runtime/form/composables/use-remote-field-options.js +21 -2
  8. package/dist/runtime/form/types/options.d.ts +22 -2
  9. package/dist/runtime/i18n/locales/en.js +23 -2
  10. package/dist/runtime/i18n/locales/fr.js +23 -2
  11. package/dist/runtime/i18n/types.d.ts +21 -0
  12. package/dist/runtime/shared/index.d.ts +1 -0
  13. package/dist/runtime/shared/index.js +1 -0
  14. package/dist/runtime/shared/types/query.d.ts +5 -1
  15. package/dist/runtime/shared/types/remote-options.d.ts +56 -0
  16. package/dist/runtime/shared/utils/define-remote-options.d.ts +23 -0
  17. package/dist/runtime/shared/utils/define-remote-options.js +24 -0
  18. package/dist/runtime/shared/utils/remote-options-query.d.ts +6 -0
  19. package/dist/runtime/shared/utils/remote-options-query.js +18 -0
  20. package/dist/runtime/table/components/data-list/data-list-content.vue +7 -1
  21. package/dist/runtime/table/components/data-list/data-list-error-state.d.vue.ts +18 -1
  22. package/dist/runtime/table/components/data-list/data-list-error-state.vue +65 -41
  23. package/dist/runtime/table/components/data-list/data-list-error-state.vue.d.ts +18 -1
  24. package/dist/runtime/table/components/data-list/data-list-filter-tags.d.vue.ts +1 -1
  25. package/dist/runtime/table/components/data-list/data-list-filter-tags.vue +1 -1
  26. package/dist/runtime/table/components/data-list/data-list-filter-tags.vue.d.ts +1 -1
  27. package/dist/runtime/table/components/data-list/sort-menu-panel.vue +33 -25
  28. package/dist/runtime/table/components/drawers/column-panel.vue +19 -6
  29. package/dist/runtime/table/components/filters/panel/filter-panel.vue +14 -2
  30. package/dist/runtime/table/components/filters/shared/dynamic-filter-picker.vue +1 -1
  31. package/dist/runtime/table/components/filters/shared/filter-editor-header.d.vue.ts +16 -0
  32. package/dist/runtime/table/components/filters/shared/filter-editor-header.vue +46 -0
  33. package/dist/runtime/table/components/filters/shared/filter-editor-header.vue.d.ts +16 -0
  34. package/dist/runtime/table/components/filters/shared/filter-option-multiple-list.d.vue.ts +3 -1
  35. package/dist/runtime/table/components/filters/shared/filter-option-multiple-list.vue +6 -5
  36. package/dist/runtime/table/components/filters/shared/filter-option-multiple-list.vue.d.ts +3 -1
  37. package/dist/runtime/table/components/filters/shared/filter-option-picker-content.vue +4 -1
  38. package/dist/runtime/table/components/filters/shared/filter-option-single-list.vue +4 -4
  39. package/dist/runtime/table/components/filters/shared/filter-option-tree-content.vue +12 -8
  40. package/dist/runtime/table/components/filters/shared/filter-popover-shell.vue +10 -1
  41. package/dist/runtime/table/components/filters/shared/filter-searchable-panel.vue +14 -6
  42. package/dist/runtime/table/components/filters/shared/filter-trigger-tag.vue +12 -2
  43. package/dist/runtime/table/components/filters/tags/boolean-filter-tag.d.vue.ts +6 -1
  44. package/dist/runtime/table/components/filters/tags/boolean-filter-tag.vue +19 -5
  45. package/dist/runtime/table/components/filters/tags/boolean-filter-tag.vue.d.ts +6 -1
  46. package/dist/runtime/table/components/filters/tags/date-filter-tag.d.vue.ts +6 -1
  47. package/dist/runtime/table/components/filters/tags/date-filter-tag.vue +65 -29
  48. package/dist/runtime/table/components/filters/tags/date-filter-tag.vue.d.ts +6 -1
  49. package/dist/runtime/table/components/filters/tags/number-filter-tag.d.vue.ts +6 -1
  50. package/dist/runtime/table/components/filters/tags/number-filter-tag.vue +66 -18
  51. package/dist/runtime/table/components/filters/tags/number-filter-tag.vue.d.ts +6 -1
  52. package/dist/runtime/table/components/filters/tags/option-filter-tag.d.vue.ts +2 -2
  53. package/dist/runtime/table/components/filters/tags/option-filter-tag.vue +10 -34
  54. package/dist/runtime/table/components/filters/tags/option-filter-tag.vue.d.ts +2 -2
  55. package/dist/runtime/table/components/filters/tags/text-filter-tag.d.vue.ts +6 -1
  56. package/dist/runtime/table/components/filters/tags/text-filter-tag.vue +39 -14
  57. package/dist/runtime/table/components/filters/tags/text-filter-tag.vue.d.ts +6 -1
  58. package/dist/runtime/table/components/grid/grid-renderer.d.vue.ts +8 -2
  59. package/dist/runtime/table/components/grid/grid-renderer.vue +47 -115
  60. package/dist/runtime/table/components/grid/grid-renderer.vue.d.ts +8 -2
  61. package/dist/runtime/table/components/layout/progress-line.d.vue.ts +9 -0
  62. package/dist/runtime/table/components/layout/progress-line.vue +43 -0
  63. package/dist/runtime/table/components/layout/progress-line.vue.d.ts +9 -0
  64. package/dist/runtime/table/components/table/table-empty-state.d.vue.ts +1 -1
  65. package/dist/runtime/table/components/table/table-empty-state.vue +1 -1
  66. package/dist/runtime/table/components/table/table-empty-state.vue.d.ts +1 -1
  67. package/dist/runtime/table/components/table/table-layout.d.ts +57 -0
  68. package/dist/runtime/table/components/table/table-layout.js +23 -0
  69. package/dist/runtime/table/components/table/table-renderer.d.vue.ts +8 -16
  70. package/dist/runtime/table/components/table/table-renderer.vue +180 -274
  71. package/dist/runtime/table/components/table/table-renderer.vue.d.ts +8 -16
  72. package/dist/runtime/table/components/table/table-row.d.vue.ts +19 -0
  73. package/dist/runtime/table/components/table/table-row.vue +51 -0
  74. package/dist/runtime/table/components/table/table-row.vue.d.ts +19 -0
  75. package/dist/runtime/table/components/table/table-skeleton-cell.d.ts +9 -0
  76. package/dist/runtime/table/components/table/table-skeleton-cell.js +104 -0
  77. package/dist/runtime/table/components/utils/search-query-input.vue +27 -1
  78. package/dist/runtime/table/composables/use-deferred-commit.d.ts +20 -0
  79. package/dist/runtime/table/composables/use-deferred-commit.js +31 -0
  80. package/dist/runtime/table/composables/use-table-data.d.ts +5 -1
  81. package/dist/runtime/table/composables/use-table-data.js +20 -2
  82. package/dist/runtime/table/composables/use-table-filter-selected-options.js +16 -1
  83. package/dist/runtime/table/composables/use-table-summaries.js +10 -1
  84. package/dist/runtime/table/types/columns.d.ts +15 -1
  85. package/dist/runtime/table/types/data-list.d.ts +1 -0
  86. package/dist/runtime/table/types/filters.d.ts +7 -0
  87. package/dist/runtime/table/types/remote-options.d.ts +3 -16
  88. package/dist/runtime/table/utils/columns/defs.js +1 -1
  89. package/dist/runtime/table/utils/filters/date.d.ts +6 -0
  90. package/dist/runtime/table/utils/filters/date.js +26 -2
  91. package/dist/runtime/table/utils/filters/index.d.ts +1 -0
  92. package/dist/runtime/table/utils/filters/index.js +1 -0
  93. package/dist/runtime/table/utils/filters/number.d.ts +5 -0
  94. package/dist/runtime/table/utils/filters/number.js +11 -0
  95. package/dist/runtime/table/utils/filters/ui.js +5 -5
  96. package/dist/runtime/table/utils/remote-table-options.js +37 -37
  97. package/package.json +1 -1
@@ -37,9 +37,9 @@ const modelValue = defineModel({ type: null, ...{
37
37
  :size="size"
38
38
  :ui="{
39
39
  root: 'w-full',
40
- fieldset: mergeDataListUiClass('grid gap-0.5', void 0, ui?.list),
40
+ fieldset: mergeDataListUiClass('grid grid-cols-1 gap-0.5', void 0, ui?.list),
41
41
  item: mergeDataListUiClass(
42
- `flex items-center rounded-md transition-colors hover:bg-elevated data-[state=checked]:bg-elevated ${sizeClasses.option}`,
42
+ `relative flex min-w-0 cursor-pointer items-center rounded-md transition-colors hover:bg-elevated data-[state=checked]:bg-elevated ${sizeClasses.option}`,
43
43
  void 0,
44
44
  ui?.option
45
45
  ),
@@ -47,7 +47,7 @@ const modelValue = defineModel({ type: null, ...{
47
47
  base: 'cursor-pointer',
48
48
  wrapper: 'min-w-0 flex-1',
49
49
  label: mergeDataListUiClass(
50
- `w-full cursor-pointer text-default ${sizeClasses.optionLabel}`,
50
+ `w-full cursor-pointer text-default before:absolute before:inset-0 ${sizeClasses.optionLabel}`,
51
51
  void 0,
52
52
  ui?.optionLabel
53
53
  )
@@ -67,7 +67,7 @@ const modelValue = defineModel({ type: null, ...{
67
67
  "
68
68
  />
69
69
 
70
- <span class="min-w-0 flex-1 truncate">
70
+ <span class="min-w-0 flex-1 truncate" :title="item.label">
71
71
  {{ item.label }}
72
72
  </span>
73
73
  <USkeleton
@@ -55,18 +55,22 @@ function activateTreeEntry(entry) {
55
55
  </script>
56
56
 
57
57
  <template>
58
- <div v-if="multiple" :class="mergeDataListUiClass('grid gap-0.5', void 0, ui?.list)">
58
+ <div
59
+ v-if="multiple"
60
+ :class="mergeDataListUiClass('grid grid-cols-1 gap-0.5', void 0, ui?.list)"
61
+ >
59
62
  <div
60
63
  v-for="entry in entries"
61
64
  :key="entry.id"
62
65
  :class="
63
66
  mergeDataListUiClass(
64
- `flex items-center rounded-md text-left transition-colors hover:bg-elevated ${sizeClasses.option} ${entry.selected ? 'bg-elevated text-highlighted' : 'text-default'}`,
67
+ `flex cursor-pointer items-center rounded-md text-left transition-colors hover:bg-elevated ${sizeClasses.option} ${entry.selected ? 'bg-elevated text-highlighted' : 'text-default'}`,
65
68
  void 0,
66
69
  ui?.option
67
70
  )
68
71
  "
69
72
  :style="getTreeIndentStyle(entry.depth)"
73
+ @click="activateTreeEntry(entry)"
70
74
  >
71
75
  <button
72
76
  v-if="entry.expandable"
@@ -111,7 +115,6 @@ function activateTreeEntry(entry) {
111
115
  type="button"
112
116
  class="flex min-w-0 flex-1 items-center text-left"
113
117
  :class="geometry.toolbarGap"
114
- @click="activateTreeEntry(entry)"
115
118
  >
116
119
  <UIcon
117
120
  v-if="entry.icon"
@@ -132,6 +135,7 @@ function activateTreeEntry(entry) {
132
135
  ui?.optionLabel
133
136
  )
134
137
  "
138
+ :title="entry.label"
135
139
  >
136
140
  {{ entry.label }}
137
141
  </span>
@@ -159,9 +163,9 @@ function activateTreeEntry(entry) {
159
163
  :size="size"
160
164
  :ui="{
161
165
  root: 'w-full',
162
- fieldset: mergeDataListUiClass('grid gap-0.5', void 0, ui?.list),
166
+ fieldset: mergeDataListUiClass('grid grid-cols-1 gap-0.5', void 0, ui?.list),
163
167
  item: mergeDataListUiClass(
164
- `flex items-center rounded-md transition-colors hover:bg-elevated data-[state=checked]:bg-elevated ${sizeClasses.option}`,
168
+ `relative flex cursor-pointer items-center rounded-md transition-colors hover:bg-elevated data-[state=checked]:bg-elevated ${sizeClasses.option}`,
165
169
  void 0,
166
170
  ui?.option
167
171
  ),
@@ -169,7 +173,7 @@ function activateTreeEntry(entry) {
169
173
  base: 'cursor-pointer',
170
174
  wrapper: 'min-w-0 flex-1',
171
175
  label: mergeDataListUiClass(
172
- `w-full cursor-pointer text-default ${sizeClasses.optionLabel}`,
176
+ `w-full cursor-pointer text-default before:absolute before:inset-0 ${sizeClasses.optionLabel}`,
173
177
  void 0,
174
178
  ui?.optionLabel
175
179
  )
@@ -187,7 +191,7 @@ function activateTreeEntry(entry) {
187
191
  :aria-label="item.expanded ? `Collapse ${item.label}` : `Expand ${item.label}`"
188
192
  :aria-expanded="item.expanded"
189
193
  :class="[
190
- 'flex shrink-0 items-center justify-center rounded-sm text-muted transition-transform hover:bg-accented',
194
+ 'relative z-10 flex shrink-0 items-center justify-center rounded-sm text-muted transition-transform hover:bg-accented',
191
195
  sizeClasses.optionIcon,
192
196
  item.expanded ? 'rotate-90' : ''
193
197
  ]"
@@ -206,7 +210,7 @@ function activateTreeEntry(entry) {
206
210
  :class="[sizeClasses.optionIcon, 'shrink-0 text-muted']"
207
211
  />
208
212
 
209
- <span class="min-w-0 flex-1" :class="item.truncate ? 'truncate' : ''">
213
+ <span class="min-w-0 flex-1" :class="item.truncate ? 'truncate' : ''" :title="item.label">
210
214
  {{ item.label }}
211
215
  </span>
212
216
  <USkeleton
@@ -12,6 +12,14 @@ const resolvedContentClass = computed(
12
12
  () => resolveDataListPopoverContentClass("fit", props.contentClass)
13
13
  );
14
14
  const emit = defineEmits(["updateOpen"]);
15
+ function focusContent(event) {
16
+ const scope = event.target instanceof HTMLElement ? event.target : null;
17
+ const dialog = scope?.closest('[role="dialog"]') ?? scope?.querySelector('[role="dialog"]');
18
+ if (dialog instanceof HTMLElement) {
19
+ event.preventDefault();
20
+ dialog.focus({ preventScroll: true });
21
+ }
22
+ }
15
23
  function handleFocusOutside(event) {
16
24
  if (props.transitioning) {
17
25
  event.preventDefault();
@@ -31,7 +39,8 @@ function handleFocusOutside(event) {
31
39
  side: 'bottom',
32
40
  align: 'start',
33
41
  sideOffset: 8,
34
- onFocusOutside: handleFocusOutside
42
+ onFocusOutside: handleFocusOutside,
43
+ onOpenAutoFocus: focusContent
35
44
  }"
36
45
  :ui="{ content: resolvedContentClass }"
37
46
  @update:open="emit('updateOpen', $event)"
@@ -1,4 +1,5 @@
1
1
  <script setup>
2
+ import UIcon from "@nuxt/ui/components/Icon.vue";
2
3
  import UInput from "@nuxt/ui/components/Input.vue";
3
4
  import UScrollArea from "@nuxt/ui/components/ScrollArea.vue";
4
5
  import { computed } from "vue";
@@ -78,15 +79,22 @@ const sizeClasses = computed(() => resolveFilterEditorSizeClasses(size));
78
79
  <div
79
80
  v-if="showEmpty"
80
81
  :class="
81
- mergeDataListUiClass(`${sizeClasses.empty} text-center text-muted`, void 0, ui?.empty)
82
+ mergeDataListUiClass(
83
+ `nut-dl-editor__empty ${sizeClasses.empty} text-center text-muted`,
84
+ void 0,
85
+ ui?.empty
86
+ )
82
87
  "
83
88
  >
84
89
  <slot name="empty">
85
- <div class="grid justify-items-center gap-2">
86
- <span class="flex size-8 items-center justify-center rounded-full bg-elevated">
87
- <span class="i-lucide-search-x size-4" aria-hidden="true" />
88
- </span>
89
- <span>{{ emptyLabel }}</span>
90
+ <div class="grid justify-items-center gap-1.5">
91
+ <UIcon name="i-lucide-search-x" class="size-4 text-dimmed" aria-hidden="true" />
92
+ <span class="text-[12.5px] leading-[18px]">{{ emptyLabel }}</span>
93
+ <span
94
+ v-if="searchQuery.trim()"
95
+ class="max-w-full truncate text-[12px] leading-[16px] text-dimmed"
96
+ >« {{ searchQuery.trim() }} »</span
97
+ >
90
98
  </div>
91
99
  </slot>
92
100
  </div>
@@ -68,7 +68,13 @@ const labelClass = computed(() => `flex min-w-0 items-center ${geometry.value.to
68
68
 
69
69
  <template>
70
70
  <div
71
- :class="mergeDataListUiClass('inline-flex min-w-0 max-w-full align-top', void 0, ui?.root)"
71
+ :class="
72
+ mergeDataListUiClass(
73
+ 'nut-dl-tag-wrap inline-flex min-w-0 max-w-full align-top',
74
+ void 0,
75
+ ui?.root
76
+ )
77
+ "
72
78
  >
73
79
  <template v-if="showOperatorPickerFirst">
74
80
  <UButton
@@ -108,7 +114,7 @@ const labelClass = computed(() => `flex min-w-0 items-center ${geometry.value.to
108
114
  :size="size"
109
115
  :class="
110
116
  mergeDataListUiClass(
111
- 'nut-dl-tag nut-dl-tag--active min-w-0 max-w-full rounded-md bg-elevated ring ring-inset ring-default',
117
+ 'nut-dl-tag nut-dl-tag--active min-w-0 max-w-full rounded-md ring ring-inset ring-default',
112
118
  void 0,
113
119
  ui?.activeRoot
114
120
  )
@@ -225,3 +231,7 @@ const labelClass = computed(() => `flex min-w-0 items-center ${geometry.value.to
225
231
  </UFieldGroup>
226
232
  </div>
227
233
  </template>
234
+
235
+ <style>
236
+ .nut-dl-tag--active{--nut-dl-tag-bg:var(--ui-bg-elevated);background-color:var(--nut-dl-tag-bg);transition:background-color .12s ease}.nut-dl-tag--active:hover{--tw-ring-color:var(--ui-border-accented);background-color:color-mix(in srgb,var(--nut-dl-tag-bg),var(--ui-text-highlighted) 6%)}
237
+ </style>
@@ -4,6 +4,7 @@ type __VLS_Props = {
4
4
  dynamic?: boolean;
5
5
  session?: boolean;
6
6
  embedded?: boolean;
7
+ header?: boolean;
7
8
  };
8
9
  declare var __VLS_10: {
9
10
  preview: {
@@ -27,11 +28,15 @@ type __VLS_Slots = {} & {
27
28
  };
28
29
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
29
30
  dismiss: () => any;
31
+ back: () => any;
30
32
  sessionClosed: () => any;
31
33
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
32
34
  onDismiss?: (() => any) | undefined;
35
+ onBack?: (() => any) | undefined;
33
36
  onSessionClosed?: (() => any) | undefined;
34
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
37
+ }>, {
38
+ header: boolean;
39
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
35
40
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
36
41
  declare const _default: typeof __VLS_export;
37
42
  export default _default;
@@ -16,21 +16,26 @@ import {
16
16
  resolveFilterEditorSizeClasses,
17
17
  resolveFilterTriggerIcon
18
18
  } from "../../../utils";
19
+ import FilterEditorHeader from "../shared/filter-editor-header.vue";
19
20
  import FilterPopoverShell from "../shared/filter-popover-shell.vue";
20
21
  import TableFilterTrigger from "../shared/filter-trigger-tag.vue";
21
22
  const props = defineProps({
22
23
  definition: { type: Object, required: true },
23
24
  dynamic: { type: Boolean, required: false },
24
25
  session: { type: Boolean, required: false },
25
- embedded: { type: Boolean, required: false }
26
+ embedded: { type: Boolean, required: false },
27
+ header: { type: Boolean, required: false, default: true }
26
28
  });
27
- const emit = defineEmits(["dismiss", "sessionClosed"]);
29
+ const emit = defineEmits(["back", "dismiss", "sessionClosed"]);
28
30
  const internals = useTableInternals();
29
31
  const dataListUi = useDataListUi();
30
32
  const dataListFilterUi = computed(() => dataListUi.ui.value.filterTags?.ui);
31
33
  const size = computed(() => dataListUi.ui.value.filterTags?.size ?? dataListUi.controlSize.value);
32
34
  const sizeClasses = computed(() => resolveFilterEditorSizeClasses(size.value));
33
35
  const geometry = computed(() => resolveDataListControlGeometry(size.value));
36
+ const showHeader = computed(
37
+ () => props.header && dataListUi.ui.value.filterTags?.props?.editorHeader !== false
38
+ );
34
39
  const searchQuery = ref("");
35
40
  const isSessionOpen = ref(false);
36
41
  const isContentReady = ref(false);
@@ -186,6 +191,15 @@ function clearFilter() {
186
191
  "
187
192
  @vue:mounted="handleContentMounted"
188
193
  >
194
+ <FilterEditorHeader
195
+ v-if="showHeader"
196
+ :label="internals.filters.getFilterLabelText({ label: definition.label })"
197
+ :active="preview.active"
198
+ :embedded="embedded"
199
+ :ui="dataListFilterUi"
200
+ @back="emit('back')"
201
+ @clear="clearFilter"
202
+ />
189
203
  <div
190
204
  :class="mergeDataListUiClass(sizeClasses.scrollArea, void 0, dataListFilterUi?.list)"
191
205
  >
@@ -199,7 +213,7 @@ function clearFilter() {
199
213
  root: 'w-full',
200
214
  fieldset: 'grid gap-0.5',
201
215
  item: mergeDataListUiClass(
202
- `flex items-center rounded-md transition-colors hover:bg-elevated data-[state=checked]:bg-elevated ${sizeClasses.option}`,
216
+ `relative flex items-center rounded-md transition-colors hover:bg-elevated data-[state=checked]:bg-elevated ${sizeClasses.option}`,
203
217
  void 0,
204
218
  dataListFilterUi?.option
205
219
  ),
@@ -207,7 +221,7 @@ function clearFilter() {
207
221
  base: 'cursor-pointer',
208
222
  wrapper: 'min-w-0 flex-1',
209
223
  label: mergeDataListUiClass(
210
- `w-full cursor-pointer text-default ${sizeClasses.optionLabel}`,
224
+ `w-full cursor-pointer text-default before:absolute before:inset-0 ${sizeClasses.optionLabel}`,
211
225
  void 0,
212
226
  dataListFilterUi?.optionLabel
213
227
  )
@@ -249,7 +263,7 @@ function clearFilter() {
249
263
  </div>
250
264
 
251
265
  <div
252
- v-if="filterUi.commitMode === 'manual'"
266
+ v-if="filterUi.commitMode !== 'auto'"
253
267
  :class="
254
268
  mergeDataListUiClass(
255
269
  `flex items-center justify-between border-t border-default ${sizeClasses.footer}`,
@@ -4,6 +4,7 @@ type __VLS_Props = {
4
4
  dynamic?: boolean;
5
5
  session?: boolean;
6
6
  embedded?: boolean;
7
+ header?: boolean;
7
8
  };
8
9
  declare var __VLS_10: {
9
10
  preview: {
@@ -27,11 +28,15 @@ type __VLS_Slots = {} & {
27
28
  };
28
29
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
29
30
  dismiss: () => any;
31
+ back: () => any;
30
32
  sessionClosed: () => any;
31
33
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
32
34
  onDismiss?: (() => any) | undefined;
35
+ onBack?: (() => any) | undefined;
33
36
  onSessionClosed?: (() => any) | undefined;
34
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
37
+ }>, {
38
+ header: boolean;
39
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
35
40
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
36
41
  declare const _default: typeof __VLS_export;
37
42
  export default _default;
@@ -5,6 +5,7 @@ type __VLS_Props = {
5
5
  dynamic?: boolean;
6
6
  session?: boolean;
7
7
  embedded?: boolean;
8
+ header?: boolean;
8
9
  initialOperator?: TableFilterOperator;
9
10
  };
10
11
  declare var __VLS_10: {
@@ -29,11 +30,15 @@ type __VLS_Slots = {} & {
29
30
  };
30
31
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
31
32
  dismiss: () => any;
33
+ back: () => any;
32
34
  sessionClosed: () => any;
33
35
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
34
36
  onDismiss?: (() => any) | undefined;
37
+ onBack?: (() => any) | undefined;
35
38
  onSessionClosed?: (() => any) | undefined;
36
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
39
+ }>, {
40
+ header: boolean;
41
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
37
42
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
38
43
  declare const _default: typeof __VLS_export;
39
44
  export default _default;
@@ -15,6 +15,7 @@ import {
15
15
  isNullish
16
16
  } from "../../../../shared/utils/predicate";
17
17
  import { useDataListUi } from "../../../composables/use-data-list-ui";
18
+ import { useDeferredCommit } from "../../../composables/use-deferred-commit";
18
19
  import { useFilterTagSession } from "../../../composables/use-filter-tag-session";
19
20
  import { useTableInternals } from "../../../composables/use-table-internals";
20
21
  import {
@@ -29,6 +30,7 @@ import {
29
30
  resolveDateFilterScalarPresets,
30
31
  resolveDateFilterUi
31
32
  } from "../../../utils";
33
+ import FilterEditorHeader from "../shared/filter-editor-header.vue";
32
34
  import FilterMatchModePanel from "../shared/filter-match-mode-panel.vue";
33
35
  import FilterPopoverShell from "../shared/filter-popover-shell.vue";
34
36
  import FilterStageTransition from "../shared/filter-stage-transition.vue";
@@ -38,15 +40,19 @@ const props = defineProps({
38
40
  dynamic: { type: Boolean, required: false },
39
41
  session: { type: Boolean, required: false },
40
42
  embedded: { type: Boolean, required: false },
43
+ header: { type: Boolean, required: false, default: true },
41
44
  initialOperator: { type: String, required: false }
42
45
  });
43
- const emit = defineEmits(["dismiss", "sessionClosed"]);
46
+ const emit = defineEmits(["back", "dismiss", "sessionClosed"]);
44
47
  const internals = useTableInternals();
45
48
  const dataListUi = useDataListUi();
46
49
  const dataListFilterUi = computed(() => dataListUi.ui.value.filterTags?.ui);
47
50
  const size = computed(() => dataListUi.ui.value.filterTags?.size ?? dataListUi.controlSize.value);
48
51
  const sizeClasses = computed(() => resolveFilterEditorSizeClasses(size.value));
49
52
  const geometry = computed(() => resolveDataListControlGeometry(size.value));
53
+ const showHeader = computed(
54
+ () => props.header && dataListUi.ui.value.filterTags?.props?.editorHeader !== false
55
+ );
50
56
  const { t } = useUiToolsLocale();
51
57
  const isMobile = useMediaQuery("(max-width: 639px)");
52
58
  const pendingOperator = ref(props.initialOperator);
@@ -68,6 +74,8 @@ const operator = computed(() => {
68
74
  return value === "isNot" || value === "before" || value === "after" || value === "between" ? value : "is";
69
75
  });
70
76
  const filterUi = computed(() => resolveDateFilterUi(props.definition, operator.value));
77
+ const autoCommit = computed(() => filterUi.value.commitMode === "auto");
78
+ const deferred = useDeferredCommit({ commit: commitValue, flushOnDispose: props.embedded });
71
79
  const operatorLabel = computed(
72
80
  () => internals.filters.getFilterOperatorOptions({
73
81
  key: props.definition.key
@@ -81,12 +89,14 @@ const operatorItems = computed(
81
89
  const scalarPresets = computed(
82
90
  () => resolveDateFilterScalarPresets({
83
91
  definition: props.definition,
84
- operator: operator.value === "before" || operator.value === "after" || operator.value === "isNot" ? operator.value : "is"
92
+ operator: operator.value === "before" || operator.value === "after" || operator.value === "isNot" ? operator.value : "is",
93
+ translate: (key) => t(`table.filters.date.presets.${key}`)
85
94
  })
86
95
  );
87
96
  const rangePresets = computed(
88
97
  () => resolveDateFilterRangePresets({
89
- definition: props.definition
98
+ definition: props.definition,
99
+ translate: (key) => t(`table.filters.date.presets.${key}`)
90
100
  })
91
101
  );
92
102
  const rangeCalendarPanels = computed(
@@ -141,6 +151,11 @@ const session = useFilterTagSession({
141
151
  embedded: props.embedded,
142
152
  hasCommittedState: () => !isNullish(internals.filters.getActiveFilterState({ key: props.definition.key })),
143
153
  onClose: () => {
154
+ if (autoCommit.value) {
155
+ deferred.flush();
156
+ } else {
157
+ deferred.cancel();
158
+ }
144
159
  pendingOperator.value = void 0;
145
160
  },
146
161
  onDismiss: () => emit("dismiss"),
@@ -184,11 +199,8 @@ function initLocalState() {
184
199
  localRangeStart.value = void 0;
185
200
  localRangeEnd.value = void 0;
186
201
  }
187
- function applyFilter() {
188
- const currentOperator = operator.value;
189
- const nextOperator = pendingOperator.value;
190
- pendingOperator.value = void 0;
191
- if (currentOperator === "between") {
202
+ function commitValue() {
203
+ if (operator.value === "between") {
192
204
  const from = localRangeStart.value ? toJsDate(localRangeStart.value) : void 0;
193
205
  const to = localRangeEnd.value ? toJsDate(localRangeEnd.value) : void 0;
194
206
  const value = {};
@@ -200,20 +212,30 @@ function applyFilter() {
200
212
  }
201
213
  internals.filters.setScalarFilterValue({
202
214
  key: props.definition.key,
203
- operator: nextOperator,
215
+ operator: pendingOperator.value,
204
216
  value: from || to ? value : void 0
205
217
  });
206
- session.close();
207
218
  return;
208
219
  }
209
220
  internals.filters.setScalarFilterValue({
210
221
  key: props.definition.key,
211
- operator: nextOperator,
222
+ operator: pendingOperator.value,
212
223
  value: localDate.value ? toJsDate(localDate.value) : void 0
213
224
  });
225
+ }
226
+ function applyFilter() {
227
+ deferred.cancel();
228
+ commitValue();
229
+ pendingOperator.value = void 0;
214
230
  session.close();
215
231
  }
232
+ function scheduleCommit() {
233
+ if (autoCommit.value) {
234
+ deferred.schedule();
235
+ }
236
+ }
216
237
  function clearFilter() {
238
+ deferred.cancel();
217
239
  internals.filters.clearFilter({ key: props.definition.key });
218
240
  localDate.value = void 0;
219
241
  localRangeStart.value = void 0;
@@ -221,6 +243,7 @@ function clearFilter() {
221
243
  session.close();
222
244
  }
223
245
  function handleOperatorChange(op) {
246
+ deferred.cancel();
224
247
  localDate.value = void 0;
225
248
  localRangeStart.value = void 0;
226
249
  localRangeEnd.value = void 0;
@@ -231,14 +254,14 @@ function handleOperatorChange(op) {
231
254
  }
232
255
  function applyScalarPreset(value) {
233
256
  localDate.value = toCalendarDate(value);
234
- if (filterUi.value.commitMode === "auto") {
257
+ if (autoCommit.value) {
235
258
  applyFilter();
236
259
  }
237
260
  }
238
261
  function applyRangePreset(value) {
239
262
  localRangeStart.value = value.from ? toCalendarDate(value.from) : void 0;
240
263
  localRangeEnd.value = value.to ? toCalendarDate(value.to) : void 0;
241
- if (filterUi.value.commitMode === "auto") {
264
+ if (autoCommit.value) {
242
265
  applyFilter();
243
266
  }
244
267
  }
@@ -247,23 +270,23 @@ function isRangePresetActive(value) {
247
270
  const toMatches = areSameCalendarDay(localRangeEnd.value, toCalendarDate(value.to));
248
271
  return fromMatches && toMatches;
249
272
  }
250
- function setSingleDate(value) {
273
+ function pickSingleDate(value) {
251
274
  localDate.value = coerceCalendarDate(value);
252
- if (filterUi.value.commitMode === "auto") {
275
+ if (autoCommit.value) {
253
276
  applyFilter();
254
277
  }
255
278
  }
279
+ function typeSingleDate(value) {
280
+ localDate.value = coerceCalendarDate(value);
281
+ scheduleCommit();
282
+ }
256
283
  function setRangeStart(value) {
257
284
  localRangeStart.value = coerceCalendarDate(value);
258
- if (filterUi.value.commitMode === "auto" && filterUi.value.range.display === "inputs") {
259
- applyFilter();
260
- }
285
+ scheduleCommit();
261
286
  }
262
287
  function setRangeEnd(value) {
263
288
  localRangeEnd.value = coerceCalendarDate(value);
264
- if (filterUi.value.commitMode === "auto" && filterUi.value.range.display === "inputs") {
265
- applyFilter();
266
- }
289
+ scheduleCommit();
267
290
  }
268
291
  function setCalendarRange(value) {
269
292
  if (!isObject(value) || isArray(value)) {
@@ -273,7 +296,7 @@ function setCalendarRange(value) {
273
296
  }
274
297
  localRangeStart.value = "start" in value ? coerceCalendarDate(value.start) : void 0;
275
298
  localRangeEnd.value = "end" in value ? coerceCalendarDate(value.end) : void 0;
276
- if (filterUi.value.commitMode === "auto" && filterUi.value.range.display === "calendar") {
299
+ if (autoCommit.value && localRangeStart.value && localRangeEnd.value) {
277
300
  applyFilter();
278
301
  }
279
302
  }
@@ -374,6 +397,15 @@ function areSameCalendarDay(left, right) {
374
397
  )
375
398
  "
376
399
  >
400
+ <FilterEditorHeader
401
+ v-if="showHeader"
402
+ :label="internals.filters.getFilterLabelText({ label: definition.label })"
403
+ :active="preview.active"
404
+ :embedded="embedded"
405
+ :ui="dataListFilterUi"
406
+ @back="emit('back')"
407
+ @clear="clearFilter"
408
+ />
377
409
  <div v-if="operator === 'between'">
378
410
  <div
379
411
  class="grid gap-0"
@@ -394,7 +426,7 @@ function areSameCalendarDay(left, right) {
394
426
  <div
395
427
  :class="[geometry.caption, 'font-medium uppercase tracking-[0.14em] text-muted']"
396
428
  >
397
- Date range
429
+ {{ t("table.filters.date.range") }}
398
430
  </div>
399
431
 
400
432
  <div
@@ -466,7 +498,7 @@ function areSameCalendarDay(left, right) {
466
498
  <div
467
499
  :class="[geometry.caption, 'font-medium uppercase tracking-[0.14em] text-muted']"
468
500
  >
469
- Custom range
501
+ {{ t("table.filters.date.custom") }}
470
502
  </div>
471
503
  <div :class="[geometry.caption, 'truncate text-muted']">
472
504
  {{ rangeSummary }}
@@ -475,7 +507,9 @@ function areSameCalendarDay(left, right) {
475
507
 
476
508
  <div :class="['mt-2 grid sm:grid-cols-2', geometry.toolbarGap]">
477
509
  <div class="space-y-1">
478
- <span :class="[geometry.caption, 'px-1 text-muted']">Start</span>
510
+ <span :class="[geometry.caption, 'px-1 text-muted']">{{
511
+ t("table.filters.date.start")
512
+ }}</span>
479
513
  <UInputDate
480
514
  :model-value="localRangeStart"
481
515
  leading
@@ -492,7 +526,9 @@ function areSameCalendarDay(left, right) {
492
526
  </div>
493
527
 
494
528
  <div class="space-y-1">
495
- <span :class="[geometry.caption, 'px-1 text-muted']">End</span>
529
+ <span :class="[geometry.caption, 'px-1 text-muted']">{{
530
+ t("table.filters.date.end")
531
+ }}</span>
496
532
  <UInputDate
497
533
  :model-value="localRangeEnd"
498
534
  leading
@@ -557,7 +593,7 @@ function areSameCalendarDay(left, right) {
557
593
  leading-icon="i-lucide-calendar-days"
558
594
  :size="size"
559
595
  class="w-full"
560
- @update:model-value="setSingleDate"
596
+ @update:model-value="typeSingleDate"
561
597
  />
562
598
 
563
599
  <UCalendar
@@ -575,12 +611,12 @@ function areSameCalendarDay(left, right) {
575
611
  ),
576
612
  cellTrigger: 'rounded-md'
577
613
  }"
578
- @update:model-value="setSingleDate"
614
+ @update:model-value="pickSingleDate"
579
615
  />
580
616
  </div>
581
617
 
582
618
  <div
583
- v-if="filterUi.commitMode === 'manual'"
619
+ v-if="!autoCommit"
584
620
  :class="
585
621
  mergeDataListUiClass(
586
622
  `flex items-center border-t border-default ${sizeClasses.footer} ${operator === 'between' ? 'justify-between' : 'justify-end'}`,
@@ -5,6 +5,7 @@ type __VLS_Props = {
5
5
  dynamic?: boolean;
6
6
  session?: boolean;
7
7
  embedded?: boolean;
8
+ header?: boolean;
8
9
  initialOperator?: TableFilterOperator;
9
10
  };
10
11
  declare var __VLS_10: {
@@ -29,11 +30,15 @@ type __VLS_Slots = {} & {
29
30
  };
30
31
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
31
32
  dismiss: () => any;
33
+ back: () => any;
32
34
  sessionClosed: () => any;
33
35
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
34
36
  onDismiss?: (() => any) | undefined;
37
+ onBack?: (() => any) | undefined;
35
38
  onSessionClosed?: (() => any) | undefined;
36
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
39
+ }>, {
40
+ header: boolean;
41
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
37
42
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
38
43
  declare const _default: typeof __VLS_export;
39
44
  export default _default;
@@ -4,6 +4,7 @@ type __VLS_Props = {
4
4
  dynamic?: boolean;
5
5
  session?: boolean;
6
6
  embedded?: boolean;
7
+ header?: boolean;
7
8
  initialOperator?: TableFilterOperator;
8
9
  };
9
10
  declare var __VLS_10: {
@@ -28,11 +29,15 @@ type __VLS_Slots = {} & {
28
29
  };
29
30
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
30
31
  dismiss: () => any;
32
+ back: () => any;
31
33
  sessionClosed: () => any;
32
34
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
33
35
  onDismiss?: (() => any) | undefined;
36
+ onBack?: (() => any) | undefined;
34
37
  onSessionClosed?: (() => any) | undefined;
35
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
38
+ }>, {
39
+ header: boolean;
40
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
36
41
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
37
42
  declare const _default: typeof __VLS_export;
38
43
  export default _default;