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.
- package/dist/module.json +1 -1
- package/dist/module.mjs +3 -1
- package/dist/runtime/dashboard/composables/use-dashboard-remote-options.js +8 -3
- package/dist/runtime/dashboard/types/filters.d.ts +7 -1
- package/dist/runtime/dashboard/utils/builders/dashboard-filters.js +19 -2
- package/dist/runtime/form/composables/use-field-options.js +19 -0
- package/dist/runtime/form/composables/use-remote-field-options.js +21 -2
- package/dist/runtime/form/types/options.d.ts +22 -2
- 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/shared/index.d.ts +1 -0
- package/dist/runtime/shared/index.js +1 -0
- package/dist/runtime/shared/types/query.d.ts +5 -1
- package/dist/runtime/shared/types/remote-options.d.ts +56 -0
- package/dist/runtime/shared/utils/define-remote-options.d.ts +23 -0
- package/dist/runtime/shared/utils/define-remote-options.js +24 -0
- package/dist/runtime/shared/utils/remote-options-query.d.ts +6 -0
- package/dist/runtime/shared/utils/remote-options-query.js +18 -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/data-list-filter-tags.d.vue.ts +1 -1
- package/dist/runtime/table/components/data-list/data-list-filter-tags.vue +1 -1
- package/dist/runtime/table/components/data-list/data-list-filter-tags.vue.d.ts +1 -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/panel/filter-panel.vue +14 -2
- 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 +6 -5
- 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 +4 -4
- package/dist/runtime/table/components/filters/shared/filter-option-tree-content.vue +12 -8
- 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 +12 -2
- 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-filter-selected-options.js +16 -1
- 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/types/filters.d.ts +7 -0
- package/dist/runtime/table/types/remote-options.d.ts +3 -16
- 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/dist/runtime/table/utils/remote-table-options.js +37 -37
- package/package.json +1 -1
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -36,6 +36,7 @@ function getPublicComponents(runtimeDir, options) {
|
|
|
36
36
|
"SortMenu",
|
|
37
37
|
"LayoutSwitch",
|
|
38
38
|
"Content",
|
|
39
|
+
"ErrorState",
|
|
39
40
|
"Table",
|
|
40
41
|
"Grid",
|
|
41
42
|
"Pagination",
|
|
@@ -143,8 +144,9 @@ function setupImports(runtimeDir) {
|
|
|
143
144
|
{ from: "i18n", name: "provideUiToolsLocale" },
|
|
144
145
|
{ from: "i18n", name: "useUiToolsLocale" }
|
|
145
146
|
]),
|
|
146
|
-
// Shared
|
|
147
|
+
// Shared runtime
|
|
147
148
|
...withRuntime(runtimeDir, [
|
|
149
|
+
{ from: "shared", name: "defineRemoteOptions" },
|
|
148
150
|
{ from: "shared", name: "getResponsiveValue" },
|
|
149
151
|
{ from: "shared", name: "parseResponsiveValue" },
|
|
150
152
|
{ from: "shared", name: "resolveResponsiveValueAtBreakpoint" },
|
|
@@ -10,7 +10,6 @@ import { mergeDashboardOptions, resolveDashboardOptionValues } from "../utils/op
|
|
|
10
10
|
import { runDashboardRemoteResult } from "../utils/remote.js";
|
|
11
11
|
export function useDashboardRemoteOptions(params) {
|
|
12
12
|
const { config } = params;
|
|
13
|
-
const queryKey = config.queryKey ?? params.queryKey;
|
|
14
13
|
const pageSize = Math.max(
|
|
15
14
|
1,
|
|
16
15
|
config.pagination?.size ?? DASHBOARD_REMOTE_OPTIONS_DEFAULTS.pageSize
|
|
@@ -22,6 +21,12 @@ export function useDashboardRemoteOptions(params) {
|
|
|
22
21
|
computed(() => search.value.trim()),
|
|
23
22
|
config.search?.debounce ?? DASHBOARD_REMOTE_OPTIONS_DEFAULTS.searchDebounce
|
|
24
23
|
);
|
|
24
|
+
function queryKeyFor(term2) {
|
|
25
|
+
return config.queryKeyFor?.({
|
|
26
|
+
page: { ...REMOTE_OPTIONS_FIRST_PAGE, size: pageSize },
|
|
27
|
+
search: term2
|
|
28
|
+
}) ?? config.queryKey ?? params.queryKey;
|
|
29
|
+
}
|
|
25
30
|
const pages = useInfiniteQuery(
|
|
26
31
|
computed(() => ({
|
|
27
32
|
enabled: (open.value || search.value !== "") && (term.value.length === 0 || term.value.length >= minLength),
|
|
@@ -33,7 +38,7 @@ export function useDashboardRemoteOptions(params) {
|
|
|
33
38
|
config.load({ page: { ...context.pageParam, size: pageSize }, search: term.value }),
|
|
34
39
|
context
|
|
35
40
|
),
|
|
36
|
-
queryKey: [...
|
|
41
|
+
queryKey: config.queryKeyFor ? [...queryKeyFor(term.value), { remoteOptionPages: pageSize }] : [...queryKeyFor(term.value), "options", term.value],
|
|
37
42
|
staleTime: DASHBOARD_REMOTE_OPTIONS_DEFAULTS.staleTime
|
|
38
43
|
}))
|
|
39
44
|
);
|
|
@@ -50,7 +55,7 @@ export function useDashboardRemoteOptions(params) {
|
|
|
50
55
|
computed(() => ({
|
|
51
56
|
enabled: Boolean(config.resolveSelected) && missing.value.length > 0,
|
|
52
57
|
queryFn: (context) => config.resolveSelected ? runDashboardRemoteResult(config.resolveSelected({ values: missing.value }), context) : Promise.resolve([]),
|
|
53
|
-
queryKey: [...
|
|
58
|
+
queryKey: config.selectedQueryKeyFor ? config.selectedQueryKeyFor({ values: missing.value }) : [...queryKeyFor(""), "selected", missing.value],
|
|
54
59
|
// Hydrated labels stay while the next set of missing values resolves.
|
|
55
60
|
placeholderData: (previous) => previous,
|
|
56
61
|
staleTime: DASHBOARD_REMOTE_OPTIONS_DEFAULTS.selectedStaleTime
|
|
@@ -150,8 +150,14 @@ export interface DashboardRemoteOptionsConfig {
|
|
|
150
150
|
/** Defaults to `{ type: 'page', size: 25 }`. */
|
|
151
151
|
pagination?: RemoteOptionsPagination;
|
|
152
152
|
search?: RemoteOptionsSearch;
|
|
153
|
-
/** Cache identity of
|
|
153
|
+
/** Cache identity of an inline source. Defaults to a key derived from the filter location. */
|
|
154
154
|
queryKey?: QueryKey;
|
|
155
|
+
/** Cache identity of a reusable loader, derived from its endpoint request and current search. */
|
|
156
|
+
queryKeyFor?: (request: DashboardRemoteOptionsRequest) => QueryKey;
|
|
157
|
+
/** Cache identity of a selected lookup, including scope absent from the page endpoint. */
|
|
158
|
+
selectedQueryKeyFor?: (request: {
|
|
159
|
+
values: readonly string[];
|
|
160
|
+
}) => QueryKey;
|
|
155
161
|
}
|
|
156
162
|
/** Option items of a filter: fixed, or read reactively (e.g. from another query's data). */
|
|
157
163
|
export type DashboardFilterItems = readonly DashboardOption[] | (() => readonly DashboardOption[]);
|
|
@@ -79,8 +79,25 @@ function optionsFilter(items, options) {
|
|
|
79
79
|
return defineFilter("options", codec, { ...options, items });
|
|
80
80
|
}
|
|
81
81
|
function remoteFilter(config, extra) {
|
|
82
|
-
const {
|
|
83
|
-
|
|
82
|
+
const {
|
|
83
|
+
load,
|
|
84
|
+
pagination,
|
|
85
|
+
queryKey,
|
|
86
|
+
queryKeyFor,
|
|
87
|
+
resolveSelected,
|
|
88
|
+
search,
|
|
89
|
+
selectedQueryKeyFor,
|
|
90
|
+
...options
|
|
91
|
+
} = config;
|
|
92
|
+
const remote = {
|
|
93
|
+
load,
|
|
94
|
+
pagination,
|
|
95
|
+
queryKey,
|
|
96
|
+
queryKeyFor,
|
|
97
|
+
resolveSelected,
|
|
98
|
+
search,
|
|
99
|
+
selectedQueryKeyFor
|
|
100
|
+
};
|
|
84
101
|
return defineFilter("remote", optionalStringCodec, {
|
|
85
102
|
searchable: true,
|
|
86
103
|
...options,
|
|
@@ -350,8 +350,27 @@ function resolveRemoteOptionConfig(field) {
|
|
|
350
350
|
return null;
|
|
351
351
|
}
|
|
352
352
|
const options = Object.getOwnPropertyDescriptor(field, "options")?.value;
|
|
353
|
+
if (isRemoteLoaderOptionConfig(options)) {
|
|
354
|
+
const { loader, ...fieldOptions } = options;
|
|
355
|
+
return {
|
|
356
|
+
...fieldOptions,
|
|
357
|
+
pagination: loader.pagination,
|
|
358
|
+
queryKeyFor: loader.queryKeyFor,
|
|
359
|
+
resolveSelected: loader.resolveSelected,
|
|
360
|
+
search: loader.search,
|
|
361
|
+
selectedQueryKeyFor: loader.selectedQueryKeyFor,
|
|
362
|
+
source: loader.load
|
|
363
|
+
};
|
|
364
|
+
}
|
|
353
365
|
return isRemoteOptionConfig(options) ? options : null;
|
|
354
366
|
}
|
|
367
|
+
function isRemoteLoaderOptionConfig(value) {
|
|
368
|
+
if (!isRecord(value) || value.mode !== "remote" || !isRecord(value.loader)) {
|
|
369
|
+
return false;
|
|
370
|
+
}
|
|
371
|
+
const { loader } = value;
|
|
372
|
+
return isFunction(loader.load) && isFunction(loader.resolveSelected) && isRecord(loader.pagination);
|
|
373
|
+
}
|
|
355
374
|
function isRemoteOptionConfig(value) {
|
|
356
375
|
return isRecord(value) && value.mode === "remote" && isFunction(Object.getOwnPropertyDescriptor(value, "source")?.value) && isRecord(value.pagination);
|
|
357
376
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useQueryClient } from "@tanstack/vue-query";
|
|
1
|
+
import { hashKey, useQueryClient } from "@tanstack/vue-query";
|
|
2
2
|
import { debounceFilter, watchWithFilter } from "@vueuse/core";
|
|
3
3
|
import { computed, ref, shallowRef, watch } from "vue";
|
|
4
4
|
import { formOptionKey, mergeResolvedOptions, normalizeOptionItems } from "../utils/options.js";
|
|
@@ -384,7 +384,7 @@ export function useRemoteFieldOptions(params) {
|
|
|
384
384
|
selectedError.value = null;
|
|
385
385
|
return;
|
|
386
386
|
}
|
|
387
|
-
retainSelected(
|
|
387
|
+
retainSelected(loaded.value);
|
|
388
388
|
if (missingSelected.value.length) {
|
|
389
389
|
void hydrateSelected({ reconcile: false });
|
|
390
390
|
}
|
|
@@ -410,6 +410,25 @@ export function useRemoteFieldOptions(params) {
|
|
|
410
410
|
},
|
|
411
411
|
{ deep: true }
|
|
412
412
|
);
|
|
413
|
+
const pageIdentity = computed(() => {
|
|
414
|
+
const config = params.config.value;
|
|
415
|
+
return config?.queryKeyFor ? hashKey(
|
|
416
|
+
config.queryKeyFor({
|
|
417
|
+
page: { cursor: null, index: 1, size: pageSize.value },
|
|
418
|
+
search: ""
|
|
419
|
+
})
|
|
420
|
+
) : null;
|
|
421
|
+
});
|
|
422
|
+
const selectedIdentity = computed(() => {
|
|
423
|
+
const config = params.config.value;
|
|
424
|
+
return config?.selectedQueryKeyFor ? hashKey(config.selectedQueryKeyFor({ values: [] })) : null;
|
|
425
|
+
});
|
|
426
|
+
watch([pageIdentity, selectedIdentity], ([page, selected], [previousPage, previousSelected]) => {
|
|
427
|
+
if (page === previousPage && selected === previousSelected) return;
|
|
428
|
+
selectionGeneration.value += 1;
|
|
429
|
+
if (page !== previousPage) void reload({ clearSearch: false });
|
|
430
|
+
if (selectedValues.value.length) void hydrateSelected({ reconcile: true });
|
|
431
|
+
});
|
|
413
432
|
return {
|
|
414
433
|
activate,
|
|
415
434
|
error: errorValue,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { QueryKey } from '@tanstack/vue-query';
|
|
2
|
+
import type { RemoteOptionsLoader, RemoteOptionsPageRequest, RemoteOptionsPagination, RemoteOptionsResult, RemoteOptionsSearch } from '../../shared/types/remote-options.js';
|
|
2
3
|
import type { FormValue } from './/index.js';
|
|
3
4
|
import type { FormFieldCallback, FormFieldCallbackParams } from './callbacks.js';
|
|
4
5
|
import type { FormQueryOptions } from './context.js';
|
|
@@ -92,6 +93,15 @@ export interface FormRemoteOptionConfig<TOption, TContext = NonNullable<unknown>
|
|
|
92
93
|
resolveSelected?: (request: FormRemoteSelectedRequest<TContext, TDeps, TValue, TOption>) => FormRemoteSource<readonly TOption[]>;
|
|
93
94
|
pagination: RemoteOptionsPagination;
|
|
94
95
|
search?: RemoteOptionsSearch;
|
|
96
|
+
/** Identity of the first page, so reusable loaders follow external scope changes. */
|
|
97
|
+
queryKeyFor?: (request: {
|
|
98
|
+
search: string;
|
|
99
|
+
page: RemoteOptionsPageRequest;
|
|
100
|
+
}) => QueryKey;
|
|
101
|
+
/** Identity of selected labels, including scope absent from the page endpoint. */
|
|
102
|
+
selectedQueryKeyFor?: (request: {
|
|
103
|
+
values: readonly FormOptionValue[];
|
|
104
|
+
}) => QueryKey;
|
|
95
105
|
/** Dependency aliases whose changes reset the loaded pages and re-run selected hydration. */
|
|
96
106
|
refreshOn?: readonly string[];
|
|
97
107
|
/** Clears selected values the latest successful selected hydration did not return. Defaults to `false`. */
|
|
@@ -103,4 +113,14 @@ export interface FormRemoteOptionConfig<TOption, TContext = NonNullable<unknown>
|
|
|
103
113
|
/** Disable the field while its option source has no usable data yet. */
|
|
104
114
|
disableOnLoading?: boolean;
|
|
105
115
|
}
|
|
106
|
-
|
|
116
|
+
/** Reuses one remote loader while keeping field-specific refresh and creation behavior local. */
|
|
117
|
+
export interface FormRemoteLoaderOptionConfig<TOption, TContext = NonNullable<unknown>, TDeps = NonNullable<unknown>, TValue = FormValue> extends Omit<FormRemoteOptionConfig<TOption, TContext, TDeps, TValue>, 'source' | 'resolveSelected' | 'pagination' | 'search' | 'queryKeyFor' | 'selectedQueryKeyFor'> {
|
|
118
|
+
loader: RemoteOptionsLoader<TOption>;
|
|
119
|
+
source?: never;
|
|
120
|
+
resolveSelected?: never;
|
|
121
|
+
pagination?: never;
|
|
122
|
+
search?: never;
|
|
123
|
+
queryKeyFor?: never;
|
|
124
|
+
selectedQueryKeyFor?: never;
|
|
125
|
+
}
|
|
126
|
+
export type FormAnyOptionConfig<TOption, TContext = NonNullable<unknown>, TDeps = NonNullable<unknown>, TValue = FormValue> = FormOptionConfig<TOption, TContext, TDeps, TValue> | FormRemoteOptionConfig<TOption, TContext, TDeps, TValue> | FormRemoteLoaderOptionConfig<TOption, TContext, TDeps, TValue> | FormOptionsSource<TOption, TContext, TDeps, TValue>;
|
|
@@ -369,6 +369,7 @@ export default defineUiToolsLocale({
|
|
|
369
369
|
loadMore: "Load more",
|
|
370
370
|
loadingMore: "Loading\u2026",
|
|
371
371
|
noMatchingFilters: "No matching filters.",
|
|
372
|
+
clearSearch: "Clear search",
|
|
372
373
|
resetColumns: "Default",
|
|
373
374
|
resetFilters: "Reset",
|
|
374
375
|
searchColumns: "Search columns...",
|
|
@@ -422,6 +423,24 @@ export default defineUiToolsLocale({
|
|
|
422
423
|
done: "Done",
|
|
423
424
|
order: "Order",
|
|
424
425
|
sortBy: "Sort by"
|
|
426
|
+
},
|
|
427
|
+
date: {
|
|
428
|
+
custom: "Custom range",
|
|
429
|
+
end: "End",
|
|
430
|
+
presets: {
|
|
431
|
+
last30Days: "Last 30 days",
|
|
432
|
+
last7Days: "Last 7 days",
|
|
433
|
+
lastMonth: "Last month",
|
|
434
|
+
sevenDaysAgo: "7 days ago",
|
|
435
|
+
startOfMonth: "Start of month",
|
|
436
|
+
startOfYear: "Start of year",
|
|
437
|
+
thisMonth: "This month",
|
|
438
|
+
today: "Today",
|
|
439
|
+
yearToDate: "Year to date",
|
|
440
|
+
yesterday: "Yesterday"
|
|
441
|
+
},
|
|
442
|
+
range: "Date range",
|
|
443
|
+
start: "Start"
|
|
425
444
|
}
|
|
426
445
|
},
|
|
427
446
|
footer: {
|
|
@@ -463,8 +482,8 @@ export default defineUiToolsLocale({
|
|
|
463
482
|
},
|
|
464
483
|
gridError: {
|
|
465
484
|
action: "Retry",
|
|
466
|
-
description: "The
|
|
467
|
-
title: "
|
|
485
|
+
description: "The last request failed. Try again in a moment.",
|
|
486
|
+
title: "This list could not be loaded"
|
|
468
487
|
},
|
|
469
488
|
loaded: "{count} loaded",
|
|
470
489
|
loadingMore: "Loading more\u2026"
|
|
@@ -472,6 +491,8 @@ export default defineUiToolsLocale({
|
|
|
472
491
|
summaries: {
|
|
473
492
|
filtered: "Filtered",
|
|
474
493
|
page: "Page",
|
|
494
|
+
rowOne: "row",
|
|
495
|
+
rowOther: "rows",
|
|
475
496
|
selection: "Selection",
|
|
476
497
|
total: "Total"
|
|
477
498
|
}
|
|
@@ -369,6 +369,7 @@ export default defineUiToolsLocale({
|
|
|
369
369
|
loadMore: "Charger plus",
|
|
370
370
|
loadingMore: "Chargement\u2026",
|
|
371
371
|
noMatchingFilters: "Aucun filtre correspondant.",
|
|
372
|
+
clearSearch: "Effacer la recherche",
|
|
372
373
|
resetColumns: "Par d\xE9faut",
|
|
373
374
|
resetFilters: "R\xE9initialiser",
|
|
374
375
|
searchColumns: "Rechercher des colonnes...",
|
|
@@ -422,6 +423,24 @@ export default defineUiToolsLocale({
|
|
|
422
423
|
done: "Termin\xE9",
|
|
423
424
|
order: "Ordre",
|
|
424
425
|
sortBy: "Trier par"
|
|
426
|
+
},
|
|
427
|
+
date: {
|
|
428
|
+
custom: "P\xE9riode personnalis\xE9e",
|
|
429
|
+
end: "Fin",
|
|
430
|
+
presets: {
|
|
431
|
+
last30Days: "30 derniers jours",
|
|
432
|
+
last7Days: "7 derniers jours",
|
|
433
|
+
lastMonth: "Le mois dernier",
|
|
434
|
+
sevenDaysAgo: "Il y a 7 jours",
|
|
435
|
+
startOfMonth: "D\xE9but du mois",
|
|
436
|
+
startOfYear: "D\xE9but de l'ann\xE9e",
|
|
437
|
+
thisMonth: "Ce mois-ci",
|
|
438
|
+
today: "Aujourd'hui",
|
|
439
|
+
yearToDate: "Depuis le d\xE9but de l'ann\xE9e",
|
|
440
|
+
yesterday: "Hier"
|
|
441
|
+
},
|
|
442
|
+
range: "P\xE9riode",
|
|
443
|
+
start: "D\xE9but"
|
|
425
444
|
}
|
|
426
445
|
},
|
|
427
446
|
footer: {
|
|
@@ -463,8 +482,8 @@ export default defineUiToolsLocale({
|
|
|
463
482
|
},
|
|
464
483
|
gridError: {
|
|
465
484
|
action: "R\xE9essayer",
|
|
466
|
-
description: "La derni\xE8re requ\xEAte a \xE9chou\xE9. R\xE9essayez
|
|
467
|
-
title: "Impossible de charger cette
|
|
485
|
+
description: "La derni\xE8re requ\xEAte a \xE9chou\xE9. R\xE9essayez dans un instant.",
|
|
486
|
+
title: "Impossible de charger cette liste"
|
|
468
487
|
},
|
|
469
488
|
loaded: "{count} charg\xE9s",
|
|
470
489
|
loadingMore: "Chargement en cours\u2026"
|
|
@@ -472,6 +491,8 @@ export default defineUiToolsLocale({
|
|
|
472
491
|
summaries: {
|
|
473
492
|
filtered: "Filtr\xE9s",
|
|
474
493
|
page: "Page",
|
|
494
|
+
rowOne: "ligne",
|
|
495
|
+
rowOther: "lignes",
|
|
475
496
|
selection: "S\xE9lection",
|
|
476
497
|
total: "Total"
|
|
477
498
|
}
|
|
@@ -21,6 +21,7 @@ export interface UiToolsTableMessages {
|
|
|
21
21
|
};
|
|
22
22
|
controls: {
|
|
23
23
|
addFilter: string;
|
|
24
|
+
clearSearch: string;
|
|
24
25
|
resetFilters: string;
|
|
25
26
|
columnsCount: string;
|
|
26
27
|
close: string;
|
|
@@ -46,6 +47,8 @@ export interface UiToolsTableMessages {
|
|
|
46
47
|
page: string;
|
|
47
48
|
filtered: string;
|
|
48
49
|
selection: string;
|
|
50
|
+
rowOne: string;
|
|
51
|
+
rowOther: string;
|
|
49
52
|
};
|
|
50
53
|
columnsMenu: {
|
|
51
54
|
sortAsc: string;
|
|
@@ -66,6 +69,24 @@ export interface UiToolsTableMessages {
|
|
|
66
69
|
sortBy: string;
|
|
67
70
|
order: string;
|
|
68
71
|
};
|
|
72
|
+
date: {
|
|
73
|
+
range: string;
|
|
74
|
+
custom: string;
|
|
75
|
+
start: string;
|
|
76
|
+
end: string;
|
|
77
|
+
presets: {
|
|
78
|
+
today: string;
|
|
79
|
+
yesterday: string;
|
|
80
|
+
sevenDaysAgo: string;
|
|
81
|
+
startOfMonth: string;
|
|
82
|
+
startOfYear: string;
|
|
83
|
+
last7Days: string;
|
|
84
|
+
last30Days: string;
|
|
85
|
+
thisMonth: string;
|
|
86
|
+
lastMonth: string;
|
|
87
|
+
yearToDate: string;
|
|
88
|
+
};
|
|
89
|
+
};
|
|
69
90
|
panel: {
|
|
70
91
|
trigger: string;
|
|
71
92
|
clearAll: string;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from './composables/use-responsive-value.js';
|
|
2
2
|
export * from './composables/use-range-select.js';
|
|
3
|
+
export { defineRemoteOptions } from './utils/define-remote-options.js';
|
|
3
4
|
export * from './types/responsive.js';
|
|
4
5
|
export * from './types/utils.js';
|
|
5
6
|
export * from './utils/object.js';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from "./composables/use-responsive-value.js";
|
|
2
2
|
export * from "./composables/use-range-select.js";
|
|
3
|
+
export { defineRemoteOptions } from "./utils/define-remote-options.js";
|
|
3
4
|
export * from "./types/responsive.js";
|
|
4
5
|
export * from "./types/utils.js";
|
|
5
6
|
export * from "./utils/object.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { QueryFunction, QueryKey, UseQueryOptions } from '@tanstack/vue-query';
|
|
1
|
+
import type { QueryFunction, QueryKey, QueryOptions, UseQueryOptions } from '@tanstack/vue-query';
|
|
2
2
|
/**
|
|
3
3
|
* TanStack query definition accepted by runtime domains that own the `useQuery` call themselves
|
|
4
4
|
* (table sources, dashboard queries): an explicit `queryKey`, an optional `queryFn`, and any other
|
|
@@ -16,3 +16,7 @@ export interface QueryFnDefinition<TData = unknown> {
|
|
|
16
16
|
queryKey: QueryKey;
|
|
17
17
|
queryFn: QueryFunction<TData, QueryKey, string | null>;
|
|
18
18
|
}
|
|
19
|
+
/** Data returned by a query function, including generated options whose function may be a ref. */
|
|
20
|
+
export type QueryFunctionResult<TQuery> = TQuery extends {
|
|
21
|
+
queryFn: (...args: never[]) => infer TResult;
|
|
22
|
+
} ? Awaited<TResult> : TQuery extends QueryOptions<infer TQueryFnData, infer _TError, infer _TData, infer _TQueryData, infer _TQueryKey> ? Awaited<TQueryFnData> : TQuery extends UseQueryOptions<infer _TQueryFnData, infer _TError, infer TData, infer _TQueryData, infer _TQueryKey> ? Awaited<TData> : never;
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
import type { QueryKey } from '@tanstack/vue-query';
|
|
2
|
+
import type { QueryFnDefinition, QueryFunctionResult } from './query.js';
|
|
3
|
+
export type RemoteOptionValue = string | number | boolean;
|
|
4
|
+
/** Option shape shared by dashboard, table, and form remote pickers. */
|
|
5
|
+
export interface RemoteOption<TValue extends RemoteOptionValue = RemoteOptionValue> {
|
|
6
|
+
value: TValue;
|
|
7
|
+
label: string;
|
|
8
|
+
}
|
|
1
9
|
/**
|
|
2
10
|
* Page envelope returned by an index-paginated remote option source.
|
|
3
11
|
*/
|
|
@@ -35,3 +43,51 @@ export interface RemoteOptionsSearch {
|
|
|
35
43
|
/** Minimum term length before a search request runs. Defaults to 0. */
|
|
36
44
|
minLength?: number;
|
|
37
45
|
}
|
|
46
|
+
/** One reusable, query-backed option source. Inline remote definitions remain supported. */
|
|
47
|
+
export interface RemoteOptionsLoader<TOption> {
|
|
48
|
+
load: (request: {
|
|
49
|
+
search: string;
|
|
50
|
+
page: RemoteOptionsPageRequest;
|
|
51
|
+
}) => QueryFnDefinition<RemoteOptionsResult<TOption>>;
|
|
52
|
+
resolveSelected: (request: {
|
|
53
|
+
values: readonly RemoteOptionValue[];
|
|
54
|
+
}) => QueryFnDefinition<readonly TOption[]>;
|
|
55
|
+
pagination: RemoteOptionsPagination;
|
|
56
|
+
search?: RemoteOptionsSearch;
|
|
57
|
+
/** Identity of a page query, used by pickers that cache an infinite list as one query. */
|
|
58
|
+
queryKeyFor?: (request: {
|
|
59
|
+
search: string;
|
|
60
|
+
page: RemoteOptionsPageRequest;
|
|
61
|
+
}) => QueryKey;
|
|
62
|
+
/** Identity of a selected-value lookup, including any scope used only by that endpoint. */
|
|
63
|
+
selectedQueryKeyFor?: (request: {
|
|
64
|
+
values: readonly RemoteOptionValue[];
|
|
65
|
+
}) => QueryKey;
|
|
66
|
+
}
|
|
67
|
+
/** Query options returned by an endpoint or generated client. */
|
|
68
|
+
export interface RemoteOptionsQueryDefinition {
|
|
69
|
+
queryKey: QueryKey;
|
|
70
|
+
}
|
|
71
|
+
/** Endpoint query definitions; supplied before mapping so their result types are inferred first. */
|
|
72
|
+
export interface RemoteOptionsQueries<TPageQuery extends RemoteOptionsQueryDefinition, TSelectedQuery extends RemoteOptionsQueryDefinition> {
|
|
73
|
+
load: (request: {
|
|
74
|
+
search: string;
|
|
75
|
+
page: RemoteOptionsPageRequest;
|
|
76
|
+
}) => TPageQuery;
|
|
77
|
+
resolveSelected: (request: {
|
|
78
|
+
values: readonly RemoteOptionValue[];
|
|
79
|
+
}) => TSelectedQuery;
|
|
80
|
+
}
|
|
81
|
+
/** Maps endpoint responses to the option result shared by all three pickers. */
|
|
82
|
+
export interface DefineRemoteOptionsConfig<TPageQuery extends RemoteOptionsQueryDefinition, TSelectedQuery extends RemoteOptionsQueryDefinition, TOption extends RemoteOption> {
|
|
83
|
+
/** Stable namespace for mapped query results; requests still use their endpoint query keys. */
|
|
84
|
+
key: string;
|
|
85
|
+
/** Maps the endpoint page to options and the next-page marker. */
|
|
86
|
+
mapPage: (result: QueryFunctionResult<TPageQuery>) => RemoteOptionsResult<TOption>;
|
|
87
|
+
/** Maps selected lookup results to option items. */
|
|
88
|
+
mapSelected: (result: QueryFunctionResult<TSelectedQuery>) => readonly NoInfer<TOption>[];
|
|
89
|
+
/** Page type and size expected by the endpoint. */
|
|
90
|
+
pagination: RemoteOptionsPagination;
|
|
91
|
+
/** Debounce and minimum term length applied by the consuming picker. */
|
|
92
|
+
search?: RemoteOptionsSearch;
|
|
93
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { DefineRemoteOptionsConfig, RemoteOption, RemoteOptionsLoader, RemoteOptionsQueries, RemoteOptionsQueryDefinition } from '../types/remote-options.js';
|
|
2
|
+
/**
|
|
3
|
+
* Defines a query-backed option loader once for dashboard filters, table filters, and form fields.
|
|
4
|
+
* The endpoint's query key separates requests; `key` separates mapped option data from other
|
|
5
|
+
* consumers of that endpoint. Inline remote definitions continue to work in each domain.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* const users = defineRemoteOptions({
|
|
10
|
+
* load: ({ search, page }) => api.users.list.queryOptions({ search, cursor: page.cursor }),
|
|
11
|
+
* resolveSelected: ({ values }) => api.users.byIds.queryOptions({ ids: values }),
|
|
12
|
+
* }, {
|
|
13
|
+
* key: 'users',
|
|
14
|
+
* mapPage: ({ rows, nextCursor }) => ({
|
|
15
|
+
* options: rows.map((user) => ({ value: user.id, label: user.name })),
|
|
16
|
+
* nextCursor,
|
|
17
|
+
* }),
|
|
18
|
+
* mapSelected: ({ rows }) => rows.map((user) => ({ value: user.id, label: user.name })),
|
|
19
|
+
* pagination: { type: 'cursor', size: 25 },
|
|
20
|
+
* })
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare function defineRemoteOptions<TPageQuery extends RemoteOptionsQueryDefinition, TSelectedQuery extends RemoteOptionsQueryDefinition, const TOption extends RemoteOption>(queries: RemoteOptionsQueries<TPageQuery, TSelectedQuery>, config: DefineRemoteOptionsConfig<TPageQuery, TSelectedQuery, TOption>): RemoteOptionsLoader<TOption>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { mapRemoteOptionsQuery } from "./remote-options-query.js";
|
|
2
|
+
export function defineRemoteOptions(queries, config) {
|
|
3
|
+
const suffix = ["remote-options", config.key];
|
|
4
|
+
return {
|
|
5
|
+
load: (request) => mapRemoteOptionsQuery(
|
|
6
|
+
queries.load(request),
|
|
7
|
+
config.mapPage,
|
|
8
|
+
[...suffix, "page"]
|
|
9
|
+
),
|
|
10
|
+
pagination: config.pagination,
|
|
11
|
+
queryKeyFor: (request) => [...queries.load(request).queryKey, ...suffix, "page"],
|
|
12
|
+
resolveSelected: (request) => mapRemoteOptionsQuery(
|
|
13
|
+
queries.resolveSelected(request),
|
|
14
|
+
config.mapSelected,
|
|
15
|
+
[...suffix, "selected"]
|
|
16
|
+
),
|
|
17
|
+
selectedQueryKeyFor: (request) => [
|
|
18
|
+
...queries.resolveSelected(request).queryKey,
|
|
19
|
+
...suffix,
|
|
20
|
+
"selected"
|
|
21
|
+
],
|
|
22
|
+
search: config.search
|
|
23
|
+
};
|
|
24
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { QueryKey } from '@tanstack/vue-query';
|
|
2
|
+
import type { QueryFnDefinition } from '../types/query.js';
|
|
3
|
+
/** Maps an endpoint query while keeping its own key and cancellation context. */
|
|
4
|
+
export declare function mapRemoteOptionsQuery<TData, TResult>(definition: {
|
|
5
|
+
queryKey: QueryKey;
|
|
6
|
+
}, map: (result: TData) => TResult, suffix: readonly unknown[]): QueryFnDefinition<TResult>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { isFunction } from "./predicate.js";
|
|
2
|
+
export function mapRemoteOptionsQuery(definition, map, suffix) {
|
|
3
|
+
return {
|
|
4
|
+
async queryFn(context) {
|
|
5
|
+
if (!hasQueryFunction(definition)) {
|
|
6
|
+
throw new Error(
|
|
7
|
+
"[remote options] `load` and `resolveSelected` must return a query with a queryFn."
|
|
8
|
+
);
|
|
9
|
+
}
|
|
10
|
+
const result = await definition.queryFn({ ...context, queryKey: definition.queryKey });
|
|
11
|
+
return map(result);
|
|
12
|
+
},
|
|
13
|
+
queryKey: [...definition.queryKey, ...suffix]
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function hasQueryFunction(definition) {
|
|
17
|
+
return "queryFn" in definition && isFunction(definition.queryFn);
|
|
18
|
+
}
|
|
@@ -104,7 +104,13 @@ watch(
|
|
|
104
104
|
:error="error"
|
|
105
105
|
:retry="refresh"
|
|
106
106
|
>
|
|
107
|
-
<DataListErrorState
|
|
107
|
+
<DataListErrorState
|
|
108
|
+
:min-height="fit === 'content' ? '16rem' : void 0"
|
|
109
|
+
:class="fit === 'content' ? '' : 'min-h-0 flex-1'"
|
|
110
|
+
:size="resolvedSize"
|
|
111
|
+
:ui="ui"
|
|
112
|
+
@retry="refresh"
|
|
113
|
+
/>
|
|
108
114
|
</slot>
|
|
109
115
|
<slot
|
|
110
116
|
v-else-if="empty && $slots.empty"
|
|
@@ -3,11 +3,28 @@ type __VLS_Props = {
|
|
|
3
3
|
minHeight?: string;
|
|
4
4
|
size?: DataListControlSize;
|
|
5
5
|
ui?: DataListContentUi;
|
|
6
|
+
/** Replaces the localized default title. */
|
|
7
|
+
title?: string;
|
|
8
|
+
/** Replaces the localized default description. */
|
|
9
|
+
description?: string;
|
|
10
|
+
/** Support reference shown under the description, such as a request identifier. */
|
|
11
|
+
reference?: string;
|
|
12
|
+
icon?: string;
|
|
6
13
|
};
|
|
7
|
-
declare
|
|
14
|
+
declare var __VLS_14: {};
|
|
15
|
+
type __VLS_Slots = {} & {
|
|
16
|
+
actions?: (props: typeof __VLS_14) => any;
|
|
17
|
+
};
|
|
18
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
8
19
|
retry: () => any;
|
|
9
20
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
10
21
|
onRetry?: (() => any) | undefined;
|
|
11
22
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
23
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
12
24
|
declare const _default: typeof __VLS_export;
|
|
13
25
|
export default _default;
|
|
26
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
27
|
+
new (): {
|
|
28
|
+
$slots: S;
|
|
29
|
+
};
|
|
30
|
+
};
|