nuxt-ui-tools 1.5.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.
- package/dist/module.json +1 -1
- package/dist/module.mjs +1 -0
- package/dist/runtime/i18n/locales/en.js +23 -2
- package/dist/runtime/i18n/locales/fr.js +23 -2
- package/dist/runtime/i18n/types.d.ts +21 -0
- package/dist/runtime/table/components/data-list/data-list-content.vue +7 -1
- package/dist/runtime/table/components/data-list/data-list-error-state.d.vue.ts +18 -1
- package/dist/runtime/table/components/data-list/data-list-error-state.vue +65 -41
- package/dist/runtime/table/components/data-list/data-list-error-state.vue.d.ts +18 -1
- package/dist/runtime/table/components/data-list/sort-menu-panel.vue +33 -25
- package/dist/runtime/table/components/drawers/column-panel.vue +19 -6
- package/dist/runtime/table/components/filters/shared/dynamic-filter-picker.vue +1 -1
- package/dist/runtime/table/components/filters/shared/filter-editor-header.d.vue.ts +16 -0
- package/dist/runtime/table/components/filters/shared/filter-editor-header.vue +46 -0
- package/dist/runtime/table/components/filters/shared/filter-editor-header.vue.d.ts +16 -0
- package/dist/runtime/table/components/filters/shared/filter-option-multiple-list.d.vue.ts +3 -1
- package/dist/runtime/table/components/filters/shared/filter-option-multiple-list.vue +5 -4
- package/dist/runtime/table/components/filters/shared/filter-option-multiple-list.vue.d.ts +3 -1
- package/dist/runtime/table/components/filters/shared/filter-option-picker-content.vue +4 -1
- package/dist/runtime/table/components/filters/shared/filter-option-single-list.vue +3 -3
- package/dist/runtime/table/components/filters/shared/filter-option-tree-content.vue +7 -3
- package/dist/runtime/table/components/filters/shared/filter-popover-shell.vue +10 -1
- package/dist/runtime/table/components/filters/shared/filter-searchable-panel.vue +14 -6
- package/dist/runtime/table/components/filters/shared/filter-trigger-tag.vue +5 -1
- package/dist/runtime/table/components/filters/tags/boolean-filter-tag.d.vue.ts +6 -1
- package/dist/runtime/table/components/filters/tags/boolean-filter-tag.vue +19 -5
- package/dist/runtime/table/components/filters/tags/boolean-filter-tag.vue.d.ts +6 -1
- package/dist/runtime/table/components/filters/tags/date-filter-tag.d.vue.ts +6 -1
- package/dist/runtime/table/components/filters/tags/date-filter-tag.vue +65 -29
- package/dist/runtime/table/components/filters/tags/date-filter-tag.vue.d.ts +6 -1
- package/dist/runtime/table/components/filters/tags/number-filter-tag.d.vue.ts +6 -1
- package/dist/runtime/table/components/filters/tags/number-filter-tag.vue +66 -18
- package/dist/runtime/table/components/filters/tags/number-filter-tag.vue.d.ts +6 -1
- package/dist/runtime/table/components/filters/tags/option-filter-tag.d.vue.ts +2 -2
- package/dist/runtime/table/components/filters/tags/option-filter-tag.vue +10 -34
- package/dist/runtime/table/components/filters/tags/option-filter-tag.vue.d.ts +2 -2
- package/dist/runtime/table/components/filters/tags/text-filter-tag.d.vue.ts +6 -1
- package/dist/runtime/table/components/filters/tags/text-filter-tag.vue +39 -14
- package/dist/runtime/table/components/filters/tags/text-filter-tag.vue.d.ts +6 -1
- package/dist/runtime/table/components/grid/grid-renderer.d.vue.ts +8 -2
- package/dist/runtime/table/components/grid/grid-renderer.vue +47 -115
- package/dist/runtime/table/components/grid/grid-renderer.vue.d.ts +8 -2
- package/dist/runtime/table/components/layout/progress-line.d.vue.ts +9 -0
- package/dist/runtime/table/components/layout/progress-line.vue +43 -0
- package/dist/runtime/table/components/layout/progress-line.vue.d.ts +9 -0
- package/dist/runtime/table/components/table/table-empty-state.d.vue.ts +1 -1
- package/dist/runtime/table/components/table/table-empty-state.vue +1 -1
- package/dist/runtime/table/components/table/table-empty-state.vue.d.ts +1 -1
- package/dist/runtime/table/components/table/table-layout.d.ts +57 -0
- package/dist/runtime/table/components/table/table-layout.js +23 -0
- package/dist/runtime/table/components/table/table-renderer.d.vue.ts +8 -16
- package/dist/runtime/table/components/table/table-renderer.vue +180 -274
- package/dist/runtime/table/components/table/table-renderer.vue.d.ts +8 -16
- package/dist/runtime/table/components/table/table-row.d.vue.ts +19 -0
- package/dist/runtime/table/components/table/table-row.vue +51 -0
- package/dist/runtime/table/components/table/table-row.vue.d.ts +19 -0
- package/dist/runtime/table/components/table/table-skeleton-cell.d.ts +9 -0
- package/dist/runtime/table/components/table/table-skeleton-cell.js +104 -0
- package/dist/runtime/table/components/utils/search-query-input.vue +27 -1
- package/dist/runtime/table/composables/use-deferred-commit.d.ts +20 -0
- package/dist/runtime/table/composables/use-deferred-commit.js +31 -0
- package/dist/runtime/table/composables/use-table-data.d.ts +5 -1
- package/dist/runtime/table/composables/use-table-data.js +20 -2
- package/dist/runtime/table/composables/use-table-summaries.js +10 -1
- package/dist/runtime/table/types/columns.d.ts +15 -1
- package/dist/runtime/table/types/data-list.d.ts +1 -0
- package/dist/runtime/table/utils/columns/defs.js +1 -1
- package/dist/runtime/table/utils/filters/date.d.ts +6 -0
- package/dist/runtime/table/utils/filters/date.js +26 -2
- package/dist/runtime/table/utils/filters/index.d.ts +1 -0
- package/dist/runtime/table/utils/filters/index.js +1 -0
- package/dist/runtime/table/utils/filters/number.d.ts +5 -0
- package/dist/runtime/table/utils/filters/number.js +11 -0
- package/dist/runtime/table/utils/filters/ui.js +5 -5
- 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
|
-
}>, {
|
|
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
|
|
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
|
-
}>, {
|
|
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.
|
|
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
|
|
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(
|
|
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-
|
|
86
|
-
<
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
|
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
|
-
}>, {
|
|
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
|
|
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
|
-
}>, {
|
|
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
|
-
}>, {
|
|
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
|
|
188
|
-
|
|
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:
|
|
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:
|
|
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 (
|
|
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 (
|
|
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
|
|
273
|
+
function pickSingleDate(value) {
|
|
251
274
|
localDate.value = coerceCalendarDate(value);
|
|
252
|
-
if (
|
|
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
|
-
|
|
259
|
-
applyFilter();
|
|
260
|
-
}
|
|
285
|
+
scheduleCommit();
|
|
261
286
|
}
|
|
262
287
|
function setRangeEnd(value) {
|
|
263
288
|
localRangeEnd.value = coerceCalendarDate(value);
|
|
264
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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']">
|
|
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']">
|
|
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="
|
|
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="
|
|
614
|
+
@update:model-value="pickSingleDate"
|
|
579
615
|
/>
|
|
580
616
|
</div>
|
|
581
617
|
|
|
582
618
|
<div
|
|
583
|
-
v-if="
|
|
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
|
-
}>, {
|
|
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;
|