nuxt-ui-tools 1.4.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +3 -1
  3. package/dist/runtime/dashboard/composables/use-dashboard-remote-options.js +8 -3
  4. package/dist/runtime/dashboard/types/filters.d.ts +7 -1
  5. package/dist/runtime/dashboard/utils/builders/dashboard-filters.js +19 -2
  6. package/dist/runtime/form/composables/use-field-options.js +19 -0
  7. package/dist/runtime/form/composables/use-remote-field-options.js +21 -2
  8. package/dist/runtime/form/types/options.d.ts +22 -2
  9. package/dist/runtime/i18n/locales/en.js +23 -2
  10. package/dist/runtime/i18n/locales/fr.js +23 -2
  11. package/dist/runtime/i18n/types.d.ts +21 -0
  12. package/dist/runtime/shared/index.d.ts +1 -0
  13. package/dist/runtime/shared/index.js +1 -0
  14. package/dist/runtime/shared/types/query.d.ts +5 -1
  15. package/dist/runtime/shared/types/remote-options.d.ts +56 -0
  16. package/dist/runtime/shared/utils/define-remote-options.d.ts +23 -0
  17. package/dist/runtime/shared/utils/define-remote-options.js +24 -0
  18. package/dist/runtime/shared/utils/remote-options-query.d.ts +6 -0
  19. package/dist/runtime/shared/utils/remote-options-query.js +18 -0
  20. package/dist/runtime/table/components/data-list/data-list-content.vue +7 -1
  21. package/dist/runtime/table/components/data-list/data-list-error-state.d.vue.ts +18 -1
  22. package/dist/runtime/table/components/data-list/data-list-error-state.vue +65 -41
  23. package/dist/runtime/table/components/data-list/data-list-error-state.vue.d.ts +18 -1
  24. package/dist/runtime/table/components/data-list/data-list-filter-tags.d.vue.ts +1 -1
  25. package/dist/runtime/table/components/data-list/data-list-filter-tags.vue +1 -1
  26. package/dist/runtime/table/components/data-list/data-list-filter-tags.vue.d.ts +1 -1
  27. package/dist/runtime/table/components/data-list/sort-menu-panel.vue +33 -25
  28. package/dist/runtime/table/components/drawers/column-panel.vue +19 -6
  29. package/dist/runtime/table/components/filters/panel/filter-panel.vue +14 -2
  30. package/dist/runtime/table/components/filters/shared/dynamic-filter-picker.vue +1 -1
  31. package/dist/runtime/table/components/filters/shared/filter-editor-header.d.vue.ts +16 -0
  32. package/dist/runtime/table/components/filters/shared/filter-editor-header.vue +46 -0
  33. package/dist/runtime/table/components/filters/shared/filter-editor-header.vue.d.ts +16 -0
  34. package/dist/runtime/table/components/filters/shared/filter-option-multiple-list.d.vue.ts +3 -1
  35. package/dist/runtime/table/components/filters/shared/filter-option-multiple-list.vue +6 -5
  36. package/dist/runtime/table/components/filters/shared/filter-option-multiple-list.vue.d.ts +3 -1
  37. package/dist/runtime/table/components/filters/shared/filter-option-picker-content.vue +4 -1
  38. package/dist/runtime/table/components/filters/shared/filter-option-single-list.vue +4 -4
  39. package/dist/runtime/table/components/filters/shared/filter-option-tree-content.vue +12 -8
  40. package/dist/runtime/table/components/filters/shared/filter-popover-shell.vue +10 -1
  41. package/dist/runtime/table/components/filters/shared/filter-searchable-panel.vue +14 -6
  42. package/dist/runtime/table/components/filters/shared/filter-trigger-tag.vue +12 -2
  43. package/dist/runtime/table/components/filters/tags/boolean-filter-tag.d.vue.ts +6 -1
  44. package/dist/runtime/table/components/filters/tags/boolean-filter-tag.vue +19 -5
  45. package/dist/runtime/table/components/filters/tags/boolean-filter-tag.vue.d.ts +6 -1
  46. package/dist/runtime/table/components/filters/tags/date-filter-tag.d.vue.ts +6 -1
  47. package/dist/runtime/table/components/filters/tags/date-filter-tag.vue +65 -29
  48. package/dist/runtime/table/components/filters/tags/date-filter-tag.vue.d.ts +6 -1
  49. package/dist/runtime/table/components/filters/tags/number-filter-tag.d.vue.ts +6 -1
  50. package/dist/runtime/table/components/filters/tags/number-filter-tag.vue +66 -18
  51. package/dist/runtime/table/components/filters/tags/number-filter-tag.vue.d.ts +6 -1
  52. package/dist/runtime/table/components/filters/tags/option-filter-tag.d.vue.ts +2 -2
  53. package/dist/runtime/table/components/filters/tags/option-filter-tag.vue +10 -34
  54. package/dist/runtime/table/components/filters/tags/option-filter-tag.vue.d.ts +2 -2
  55. package/dist/runtime/table/components/filters/tags/text-filter-tag.d.vue.ts +6 -1
  56. package/dist/runtime/table/components/filters/tags/text-filter-tag.vue +39 -14
  57. package/dist/runtime/table/components/filters/tags/text-filter-tag.vue.d.ts +6 -1
  58. package/dist/runtime/table/components/grid/grid-renderer.d.vue.ts +8 -2
  59. package/dist/runtime/table/components/grid/grid-renderer.vue +47 -115
  60. package/dist/runtime/table/components/grid/grid-renderer.vue.d.ts +8 -2
  61. package/dist/runtime/table/components/layout/progress-line.d.vue.ts +9 -0
  62. package/dist/runtime/table/components/layout/progress-line.vue +43 -0
  63. package/dist/runtime/table/components/layout/progress-line.vue.d.ts +9 -0
  64. package/dist/runtime/table/components/table/table-empty-state.d.vue.ts +1 -1
  65. package/dist/runtime/table/components/table/table-empty-state.vue +1 -1
  66. package/dist/runtime/table/components/table/table-empty-state.vue.d.ts +1 -1
  67. package/dist/runtime/table/components/table/table-layout.d.ts +57 -0
  68. package/dist/runtime/table/components/table/table-layout.js +23 -0
  69. package/dist/runtime/table/components/table/table-renderer.d.vue.ts +8 -16
  70. package/dist/runtime/table/components/table/table-renderer.vue +180 -274
  71. package/dist/runtime/table/components/table/table-renderer.vue.d.ts +8 -16
  72. package/dist/runtime/table/components/table/table-row.d.vue.ts +19 -0
  73. package/dist/runtime/table/components/table/table-row.vue +51 -0
  74. package/dist/runtime/table/components/table/table-row.vue.d.ts +19 -0
  75. package/dist/runtime/table/components/table/table-skeleton-cell.d.ts +9 -0
  76. package/dist/runtime/table/components/table/table-skeleton-cell.js +104 -0
  77. package/dist/runtime/table/components/utils/search-query-input.vue +27 -1
  78. package/dist/runtime/table/composables/use-deferred-commit.d.ts +20 -0
  79. package/dist/runtime/table/composables/use-deferred-commit.js +31 -0
  80. package/dist/runtime/table/composables/use-table-data.d.ts +5 -1
  81. package/dist/runtime/table/composables/use-table-data.js +20 -2
  82. package/dist/runtime/table/composables/use-table-filter-selected-options.js +16 -1
  83. package/dist/runtime/table/composables/use-table-summaries.js +10 -1
  84. package/dist/runtime/table/types/columns.d.ts +15 -1
  85. package/dist/runtime/table/types/data-list.d.ts +1 -0
  86. package/dist/runtime/table/types/filters.d.ts +7 -0
  87. package/dist/runtime/table/types/remote-options.d.ts +3 -16
  88. package/dist/runtime/table/utils/columns/defs.js +1 -1
  89. package/dist/runtime/table/utils/filters/date.d.ts +6 -0
  90. package/dist/runtime/table/utils/filters/date.js +26 -2
  91. package/dist/runtime/table/utils/filters/index.d.ts +1 -0
  92. package/dist/runtime/table/utils/filters/index.js +1 -0
  93. package/dist/runtime/table/utils/filters/number.d.ts +5 -0
  94. package/dist/runtime/table/utils/filters/number.js +11 -0
  95. package/dist/runtime/table/utils/filters/ui.js +5 -5
  96. package/dist/runtime/table/utils/remote-table-options.js +37 -37
  97. package/package.json +1 -1
package/dist/module.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "configKey": "nuxtUiTools",
6
6
  "docs": "https://ui.nuxt.com/docs/getting-started/installation/nuxt",
7
7
  "name": "nuxt-ui-tools",
8
- "version": "1.4.0",
8
+ "version": "1.6.0",
9
9
  "builder": {
10
10
  "@nuxt/module-builder": "1.0.3",
11
11
  "unbuild": "unknown"
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 responsive helpers
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: [...queryKey, "options", term.value],
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: [...queryKey, "selected", missing.value],
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 the option source. Defaults to a key derived from the filter location. */
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 { load, pagination, queryKey, resolveSelected, search, ...options } = config;
83
- const remote = { load, pagination, queryKey, resolveSelected, search };
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 { RemoteOptionsPageRequest, RemoteOptionsPagination, RemoteOptionsResult, RemoteOptionsSearch } from '../../shared/types/remote-options.js';
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
- export type FormAnyOptionConfig<TOption, TContext = NonNullable<unknown>, TDeps = NonNullable<unknown>, TValue = FormValue> = FormOptionConfig<TOption, TContext, TDeps, TValue> | FormRemoteOptionConfig<TOption, TContext, TDeps, TValue> | FormOptionsSource<TOption, TContext, TDeps, TValue>;
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 latest request failed. Try again once the data source is available.",
467
- title: "Unable to load this grid"
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 une fois la source de donn\xE9es disponible.",
467
- title: "Impossible de charger cette grille"
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 min-height="16rem" :size="resolvedSize" :ui="ui" @retry="refresh" />
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 const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
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
+ };