nuxt-ui-tools 1.5.0 → 1.6.1

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 (76) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +1 -0
  3. package/dist/runtime/i18n/locales/en.js +23 -2
  4. package/dist/runtime/i18n/locales/fr.js +23 -2
  5. package/dist/runtime/i18n/types.d.ts +21 -0
  6. package/dist/runtime/table/components/data-list/data-list-content.vue +7 -1
  7. package/dist/runtime/table/components/data-list/data-list-error-state.d.vue.ts +18 -1
  8. package/dist/runtime/table/components/data-list/data-list-error-state.vue +65 -41
  9. package/dist/runtime/table/components/data-list/data-list-error-state.vue.d.ts +18 -1
  10. package/dist/runtime/table/components/data-list/sort-menu-panel.vue +33 -25
  11. package/dist/runtime/table/components/drawers/column-panel.vue +19 -6
  12. package/dist/runtime/table/components/filters/shared/dynamic-filter-picker.vue +1 -1
  13. package/dist/runtime/table/components/filters/shared/filter-editor-header.d.vue.ts +16 -0
  14. package/dist/runtime/table/components/filters/shared/filter-editor-header.vue +46 -0
  15. package/dist/runtime/table/components/filters/shared/filter-editor-header.vue.d.ts +16 -0
  16. package/dist/runtime/table/components/filters/shared/filter-option-multiple-list.d.vue.ts +3 -1
  17. package/dist/runtime/table/components/filters/shared/filter-option-multiple-list.vue +5 -4
  18. package/dist/runtime/table/components/filters/shared/filter-option-multiple-list.vue.d.ts +3 -1
  19. package/dist/runtime/table/components/filters/shared/filter-option-picker-content.vue +4 -1
  20. package/dist/runtime/table/components/filters/shared/filter-option-single-list.vue +3 -3
  21. package/dist/runtime/table/components/filters/shared/filter-option-tree-content.vue +7 -3
  22. package/dist/runtime/table/components/filters/shared/filter-popover-shell.vue +10 -1
  23. package/dist/runtime/table/components/filters/shared/filter-searchable-panel.vue +14 -6
  24. package/dist/runtime/table/components/filters/shared/filter-trigger-tag.vue +5 -1
  25. package/dist/runtime/table/components/filters/tags/boolean-filter-tag.d.vue.ts +6 -1
  26. package/dist/runtime/table/components/filters/tags/boolean-filter-tag.vue +19 -5
  27. package/dist/runtime/table/components/filters/tags/boolean-filter-tag.vue.d.ts +6 -1
  28. package/dist/runtime/table/components/filters/tags/date-filter-tag.d.vue.ts +6 -1
  29. package/dist/runtime/table/components/filters/tags/date-filter-tag.vue +65 -29
  30. package/dist/runtime/table/components/filters/tags/date-filter-tag.vue.d.ts +6 -1
  31. package/dist/runtime/table/components/filters/tags/number-filter-tag.d.vue.ts +6 -1
  32. package/dist/runtime/table/components/filters/tags/number-filter-tag.vue +66 -18
  33. package/dist/runtime/table/components/filters/tags/number-filter-tag.vue.d.ts +6 -1
  34. package/dist/runtime/table/components/filters/tags/option-filter-tag.d.vue.ts +2 -2
  35. package/dist/runtime/table/components/filters/tags/option-filter-tag.vue +10 -34
  36. package/dist/runtime/table/components/filters/tags/option-filter-tag.vue.d.ts +2 -2
  37. package/dist/runtime/table/components/filters/tags/text-filter-tag.d.vue.ts +6 -1
  38. package/dist/runtime/table/components/filters/tags/text-filter-tag.vue +39 -14
  39. package/dist/runtime/table/components/filters/tags/text-filter-tag.vue.d.ts +6 -1
  40. package/dist/runtime/table/components/grid/grid-renderer.d.vue.ts +8 -2
  41. package/dist/runtime/table/components/grid/grid-renderer.vue +47 -115
  42. package/dist/runtime/table/components/grid/grid-renderer.vue.d.ts +8 -2
  43. package/dist/runtime/table/components/layout/progress-line.d.vue.ts +9 -0
  44. package/dist/runtime/table/components/layout/progress-line.vue +43 -0
  45. package/dist/runtime/table/components/layout/progress-line.vue.d.ts +9 -0
  46. package/dist/runtime/table/components/table/table-empty-state.d.vue.ts +1 -1
  47. package/dist/runtime/table/components/table/table-empty-state.vue +1 -1
  48. package/dist/runtime/table/components/table/table-empty-state.vue.d.ts +1 -1
  49. package/dist/runtime/table/components/table/table-layout.d.ts +57 -0
  50. package/dist/runtime/table/components/table/table-layout.js +23 -0
  51. package/dist/runtime/table/components/table/table-renderer.d.vue.ts +8 -16
  52. package/dist/runtime/table/components/table/table-renderer.vue +180 -274
  53. package/dist/runtime/table/components/table/table-renderer.vue.d.ts +8 -16
  54. package/dist/runtime/table/components/table/table-row.d.vue.ts +19 -0
  55. package/dist/runtime/table/components/table/table-row.vue +51 -0
  56. package/dist/runtime/table/components/table/table-row.vue.d.ts +19 -0
  57. package/dist/runtime/table/components/table/table-skeleton-cell.d.ts +9 -0
  58. package/dist/runtime/table/components/table/table-skeleton-cell.js +104 -0
  59. package/dist/runtime/table/components/utils/search-query-input.vue +27 -1
  60. package/dist/runtime/table/composables/use-deferred-commit.d.ts +20 -0
  61. package/dist/runtime/table/composables/use-deferred-commit.js +31 -0
  62. package/dist/runtime/table/composables/use-table-data.d.ts +5 -1
  63. package/dist/runtime/table/composables/use-table-data.js +20 -2
  64. package/dist/runtime/table/composables/use-table-summaries.js +10 -1
  65. package/dist/runtime/table/types/columns.d.ts +18 -3
  66. package/dist/runtime/table/types/data-list.d.ts +1 -0
  67. package/dist/runtime/table/types/layout.d.ts +1 -1
  68. package/dist/runtime/table/utils/columns/defs.js +1 -1
  69. package/dist/runtime/table/utils/filters/date.d.ts +6 -0
  70. package/dist/runtime/table/utils/filters/date.js +26 -2
  71. package/dist/runtime/table/utils/filters/index.d.ts +1 -0
  72. package/dist/runtime/table/utils/filters/index.js +1 -0
  73. package/dist/runtime/table/utils/filters/number.d.ts +5 -0
  74. package/dist/runtime/table/utils/filters/number.js +11 -0
  75. package/dist/runtime/table/utils/filters/ui.js +5 -5
  76. package/package.json +1 -1
@@ -36,6 +36,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
36
36
  index: number;
37
37
  sectionKey: string;
38
38
  }) => any) | undefined;
39
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
39
+ }>, {
40
+ truncate: boolean;
41
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
40
42
  declare const _default: typeof __VLS_export;
41
43
  export default _default;
@@ -15,7 +15,7 @@ const props = defineProps({
15
15
  showCounts: { type: Boolean, required: true },
16
16
  countLoading: { type: Boolean, required: true },
17
17
  selectedIcon: { type: String, required: false },
18
- truncate: { type: Boolean, required: false },
18
+ truncate: { type: Boolean, required: false, default: true },
19
19
  size: { type: String, required: false },
20
20
  ui: { type: Object, required: false }
21
21
  });
@@ -36,7 +36,7 @@ const geometry = computed(() => resolveDataListControlGeometry(size.value));
36
36
  </script>
37
37
 
38
38
  <template>
39
- <div :class="mergeDataListUiClass('grid gap-0.5', void 0, ui?.list)">
39
+ <div :class="mergeDataListUiClass('grid grid-cols-1 gap-0.5', void 0, ui?.list)">
40
40
  <template v-for="section in props.sections" :key="section.key">
41
41
  <div
42
42
  v-if="section.dividerBefore && section.entries.length"
@@ -48,7 +48,7 @@ const geometry = computed(() => resolveDataListControlGeometry(size.value));
48
48
  :key="entry.value == null ? entry.label : String(entry.value)"
49
49
  :class="
50
50
  mergeDataListUiClass(
51
- `nut-dl-option flex cursor-pointer items-center rounded-md text-left outline-none transition-colors hover:bg-elevated ${sizeClasses.option} ${entry.selected ? 'text-highlighted' : 'text-default'}`,
51
+ `nut-dl-option flex min-w-0 cursor-pointer items-center rounded-md text-left outline-none transition-colors hover:bg-elevated ${sizeClasses.option} ${entry.selected ? 'text-highlighted' : 'text-default'}`,
52
52
  void 0,
53
53
  ui?.option
54
54
  )
@@ -89,11 +89,12 @@ const geometry = computed(() => resolveDataListControlGeometry(size.value));
89
89
  <span
90
90
  :class="
91
91
  mergeDataListUiClass(
92
- `min-w-0 flex-1 ${sizeClasses.optionLabel} ${props.truncate ?? true ? 'truncate' : ''}`,
92
+ `min-w-0 flex-1 ${sizeClasses.optionLabel} ${props.truncate ? 'truncate' : ''}`,
93
93
  void 0,
94
94
  ui?.optionLabel
95
95
  )
96
96
  "
97
+ :title="entry.label"
97
98
  >
98
99
  {{ entry.label }}
99
100
  </span>
@@ -36,6 +36,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
36
36
  index: number;
37
37
  sectionKey: string;
38
38
  }) => any) | undefined;
39
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
39
+ }>, {
40
+ truncate: boolean;
41
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
40
42
  declare const _default: typeof __VLS_export;
41
43
  export default _default;
@@ -1,4 +1,5 @@
1
1
  <script setup>
2
+ import { useMediaQuery } from "@vueuse/core";
2
3
  import { isNullish } from "../../../../shared/utils/predicate";
3
4
  import FilterOptionListEnd from "./filter-option-list-end.vue";
4
5
  import FilterOptionLoadingList from "./filter-option-loading-list.vue";
@@ -13,6 +14,7 @@ const props = defineProps({
13
14
  ui: { type: Object, required: false }
14
15
  });
15
16
  const searchQuery = defineModel("searchQuery", { type: String, ...{ default: "" } });
17
+ const finePointer = useMediaQuery("(pointer: fine)");
16
18
  const flatRadioValue = defineModel("flatRadioValue", { type: null, ...{ default: void 0 } });
17
19
  const treeRadioValue = defineModel("treeRadioValue", { type: null, ...{ default: void 0 } });
18
20
  const emit = defineEmits(["selectEntry", "toggleTreeEntry", "toggleExpanded"]);
@@ -33,10 +35,11 @@ function handleSelect(options) {
33
35
  <FilterSearchablePanel
34
36
  v-model:search-query="searchQuery"
35
37
  :searchable="props.state.filterUi.value.searchable"
38
+ :autofocus="props.state.filterUi.value.searchable && finePointer"
36
39
  :search-placeholder="props.state.filterUi.value.labels.searchPlaceholder"
37
40
  :search-loading="props.state.optionSource.isStaleLoading.value"
38
41
  :show-empty="
39
- !props.state.optionSource.isLoading.value && !props.state.displayEntries.value.length && !props.state.visibleTreeEntries.value.length
42
+ !props.state.optionSource.isLoading.value && (props.state.filterUi.value.presentation === 'tree' ? !props.state.visibleTreeEntries.value.length : !props.state.displayEntries.value.length)
40
43
  "
41
44
  :empty-label="props.state.filterUi.value.labels.empty"
42
45
  :size="props.size"
@@ -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
- `relative flex cursor-pointer 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
  ),
@@ -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,7 +55,10 @@ 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"
@@ -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,7 +163,7 @@ 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
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,
@@ -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>
@@ -114,7 +114,7 @@ const labelClass = computed(() => `flex min-w-0 items-center ${geometry.value.to
114
114
  :size="size"
115
115
  :class="
116
116
  mergeDataListUiClass(
117
- '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',
118
118
  void 0,
119
119
  ui?.activeRoot
120
120
  )
@@ -231,3 +231,7 @@ const labelClass = computed(() => `flex min-w-0 items-center ${geometry.value.to
231
231
  </UFieldGroup>
232
232
  </div>
233
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;