nuxt-ui-tools 1.2.0 → 1.3.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 (179) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +10 -1
  3. package/dist/runtime/dashboard/components/block/dashboard-block-state.d.vue.ts +2 -0
  4. package/dist/runtime/dashboard/components/block/dashboard-block-state.vue +4 -1
  5. package/dist/runtime/dashboard/components/block/dashboard-block-state.vue.d.ts +2 -0
  6. package/dist/runtime/dashboard/components/block/dashboard-chips.d.vue.ts +20 -0
  7. package/dist/runtime/dashboard/components/block/dashboard-chips.vue +42 -0
  8. package/dist/runtime/dashboard/components/block/dashboard-chips.vue.d.ts +20 -0
  9. package/dist/runtime/dashboard/components/block/dashboard-series-picker.d.vue.ts +8 -0
  10. package/dist/runtime/dashboard/components/block/dashboard-series-picker.vue +29 -0
  11. package/dist/runtime/dashboard/components/block/dashboard-series-picker.vue.d.ts +8 -0
  12. package/dist/runtime/dashboard/components/block/dashboard-skeleton.d.vue.ts +1 -1
  13. package/dist/runtime/dashboard/components/block/dashboard-skeleton.vue.d.ts +1 -1
  14. package/dist/runtime/dashboard/components/block/dashboard-view-scope.d.vue.ts +21 -0
  15. package/dist/runtime/dashboard/components/block/dashboard-view-scope.vue +12 -0
  16. package/dist/runtime/dashboard/components/block/dashboard-view-scope.vue.d.ts +21 -0
  17. package/dist/runtime/dashboard/components/dashboard-alerts.vue +3 -3
  18. package/dist/runtime/dashboard/components/dashboard-bar-chart.d.vue.ts +14 -3
  19. package/dist/runtime/dashboard/components/dashboard-bar-chart.vue +30 -9
  20. package/dist/runtime/dashboard/components/dashboard-bar-chart.vue.d.ts +14 -3
  21. package/dist/runtime/dashboard/components/dashboard-bars.vue +4 -4
  22. package/dist/runtime/dashboard/components/dashboard-card.vue +28 -6
  23. package/dist/runtime/dashboard/components/dashboard-combo-chart.d.vue.ts +6 -1
  24. package/dist/runtime/dashboard/components/dashboard-combo-chart.vue +13 -3
  25. package/dist/runtime/dashboard/components/dashboard-combo-chart.vue.d.ts +6 -1
  26. package/dist/runtime/dashboard/components/dashboard-donut-chart.vue +5 -5
  27. package/dist/runtime/dashboard/components/dashboard-feed.vue +1 -1
  28. package/dist/runtime/dashboard/components/dashboard-filter.d.vue.ts +67 -0
  29. package/dist/runtime/dashboard/components/dashboard-filter.vue +134 -0
  30. package/dist/runtime/dashboard/components/dashboard-filter.vue.d.ts +67 -0
  31. package/dist/runtime/dashboard/components/dashboard-filters.d.vue.ts +49 -0
  32. package/dist/runtime/dashboard/components/dashboard-filters.vue +96 -0
  33. package/dist/runtime/dashboard/components/dashboard-filters.vue.d.ts +49 -0
  34. package/dist/runtime/dashboard/components/dashboard-funnel.vue +5 -5
  35. package/dist/runtime/dashboard/components/dashboard-gauge.vue +3 -3
  36. package/dist/runtime/dashboard/components/dashboard-grid.d.vue.ts +10 -3
  37. package/dist/runtime/dashboard/components/dashboard-grid.vue +24 -9
  38. package/dist/runtime/dashboard/components/dashboard-grid.vue.d.ts +10 -3
  39. package/dist/runtime/dashboard/components/dashboard-line-chart.d.vue.ts +14 -3
  40. package/dist/runtime/dashboard/components/dashboard-line-chart.vue +30 -9
  41. package/dist/runtime/dashboard/components/dashboard-line-chart.vue.d.ts +14 -3
  42. package/dist/runtime/dashboard/components/dashboard-list.d.vue.ts +3 -0
  43. package/dist/runtime/dashboard/components/dashboard-list.vue +18 -6
  44. package/dist/runtime/dashboard/components/dashboard-list.vue.d.ts +3 -0
  45. package/dist/runtime/dashboard/components/dashboard-page.d.vue.ts +53 -0
  46. package/dist/runtime/dashboard/components/dashboard-page.vue +156 -0
  47. package/dist/runtime/dashboard/components/dashboard-page.vue.d.ts +53 -0
  48. package/dist/runtime/dashboard/components/dashboard-paired-bars.vue +4 -4
  49. package/dist/runtime/dashboard/components/dashboard-refresh.d.vue.ts +1 -1
  50. package/dist/runtime/dashboard/components/dashboard-refresh.vue +1 -0
  51. package/dist/runtime/dashboard/components/dashboard-refresh.vue.d.ts +1 -1
  52. package/dist/runtime/dashboard/components/dashboard-stack-bar.vue +4 -4
  53. package/dist/runtime/dashboard/components/dashboard-stat.d.vue.ts +14 -2
  54. package/dist/runtime/dashboard/components/dashboard-stat.vue +28 -10
  55. package/dist/runtime/dashboard/components/dashboard-stat.vue.d.ts +14 -2
  56. package/dist/runtime/dashboard/components/dashboard-stats.vue +3 -3
  57. package/dist/runtime/dashboard/components/dashboard-table.d.vue.ts +1 -1
  58. package/dist/runtime/dashboard/components/dashboard-table.vue +3 -3
  59. package/dist/runtime/dashboard/components/dashboard-table.vue.d.ts +1 -1
  60. package/dist/runtime/dashboard/components/dashboard-tabs.d.vue.ts +1 -1
  61. package/dist/runtime/dashboard/components/dashboard-tabs.vue.d.ts +1 -1
  62. package/dist/runtime/dashboard/components/dashboard-total.d.vue.ts +7 -2
  63. package/dist/runtime/dashboard/components/dashboard-total.vue +10 -2
  64. package/dist/runtime/dashboard/components/dashboard-total.vue.d.ts +7 -2
  65. package/dist/runtime/dashboard/components/dashboard-view-tabs.d.vue.ts +33 -0
  66. package/dist/runtime/dashboard/components/dashboard-view-tabs.vue +66 -0
  67. package/dist/runtime/dashboard/components/dashboard-view-tabs.vue.d.ts +33 -0
  68. package/dist/runtime/dashboard/components/dashboard-widget.vue +1 -1
  69. package/dist/runtime/dashboard/components/filter/dashboard-filter-menu.d.vue.ts +44 -0
  70. package/dist/runtime/dashboard/components/filter/dashboard-filter-menu.vue +220 -0
  71. package/dist/runtime/dashboard/components/filter/dashboard-filter-menu.vue.d.ts +44 -0
  72. package/dist/runtime/dashboard/composables/use-dashboard-api.d.ts +6 -1
  73. package/dist/runtime/dashboard/composables/use-dashboard-api.js +29 -6
  74. package/dist/runtime/dashboard/composables/use-dashboard-block.d.ts +5 -4
  75. package/dist/runtime/dashboard/composables/use-dashboard-block.js +32 -15
  76. package/dist/runtime/dashboard/composables/use-dashboard-chart.d.ts +8 -1
  77. package/dist/runtime/dashboard/composables/use-dashboard-chart.js +16 -4
  78. package/dist/runtime/dashboard/composables/use-dashboard-context.d.ts +50 -0
  79. package/dist/runtime/dashboard/composables/use-dashboard-context.js +56 -0
  80. package/dist/runtime/dashboard/composables/use-dashboard-derived.js +6 -1
  81. package/dist/runtime/dashboard/composables/use-dashboard-filter-control.d.ts +16 -0
  82. package/dist/runtime/dashboard/composables/use-dashboard-filter-control.js +161 -0
  83. package/dist/runtime/dashboard/composables/use-dashboard-filter-scope.d.ts +37 -0
  84. package/dist/runtime/dashboard/composables/use-dashboard-filter-scope.js +107 -0
  85. package/dist/runtime/dashboard/composables/use-dashboard-format.d.ts +46 -6
  86. package/dist/runtime/dashboard/composables/use-dashboard-format.js +51 -8
  87. package/dist/runtime/dashboard/composables/use-dashboard-options.d.ts +11 -4
  88. package/dist/runtime/dashboard/composables/use-dashboard-options.js +33 -47
  89. package/dist/runtime/dashboard/composables/use-dashboard-remote-options.d.ts +8 -5
  90. package/dist/runtime/dashboard/composables/use-dashboard-remote-options.js +59 -70
  91. package/dist/runtime/dashboard/composables/use-dashboard-resource.d.ts +15 -4
  92. package/dist/runtime/dashboard/composables/use-dashboard-resource.js +52 -17
  93. package/dist/runtime/dashboard/composables/use-dashboard-scope.d.ts +10 -7
  94. package/dist/runtime/dashboard/composables/use-dashboard-scope.js +26 -16
  95. package/dist/runtime/dashboard/composables/use-dashboard-series-picker.d.ts +20 -0
  96. package/dist/runtime/dashboard/composables/use-dashboard-series-picker.js +30 -0
  97. package/dist/runtime/dashboard/composables/use-dashboard-time.d.ts +2 -0
  98. package/dist/runtime/dashboard/composables/use-dashboard-time.js +4 -1
  99. package/dist/runtime/dashboard/composables/use-dashboard-ui.d.ts +12 -2
  100. package/dist/runtime/dashboard/composables/use-dashboard-ui.js +3 -0
  101. package/dist/runtime/dashboard/composables/use-dashboard-views.d.ts +15 -3
  102. package/dist/runtime/dashboard/composables/use-dashboard-views.js +33 -10
  103. package/dist/runtime/dashboard/composables/use-dashboard.d.ts +18 -12
  104. package/dist/runtime/dashboard/composables/use-dashboard.js +98 -9
  105. package/dist/runtime/dashboard/index.d.ts +3 -1
  106. package/dist/runtime/dashboard/index.js +3 -1
  107. package/dist/runtime/dashboard/schema/index.d.ts +62 -30
  108. package/dist/runtime/dashboard/schema/index.js +3 -0
  109. package/dist/runtime/dashboard/types/blocks.d.ts +30 -4
  110. package/dist/runtime/dashboard/types/charts.d.ts +2 -2
  111. package/dist/runtime/dashboard/types/controls.d.ts +123 -0
  112. package/dist/runtime/dashboard/types/filters.d.ts +236 -0
  113. package/dist/runtime/dashboard/types/index.d.ts +3 -2
  114. package/dist/runtime/dashboard/types/page.d.ts +17 -0
  115. package/dist/runtime/dashboard/types/page.js +0 -0
  116. package/dist/runtime/dashboard/types/resource.d.ts +66 -28
  117. package/dist/runtime/dashboard/types/runtime.d.ts +39 -8
  118. package/dist/runtime/dashboard/types/schema.d.ts +94 -54
  119. package/dist/runtime/dashboard/types/ui.d.ts +78 -0
  120. package/dist/runtime/dashboard/utils/builders/dashboard-filters.d.ts +142 -0
  121. package/dist/runtime/dashboard/utils/builders/dashboard-filters.js +135 -0
  122. package/dist/runtime/dashboard/utils/charts.d.ts +4 -15
  123. package/dist/runtime/dashboard/utils/charts.js +26 -28
  124. package/dist/runtime/dashboard/utils/{params/codecs.d.ts → codecs.d.ts} +2 -2
  125. package/dist/runtime/dashboard/utils/{params/codecs.js → codecs.js} +1 -1
  126. package/dist/runtime/dashboard/utils/environment.d.ts +36 -0
  127. package/dist/runtime/dashboard/utils/environment.js +30 -0
  128. package/dist/runtime/dashboard/utils/filters.d.ts +44 -0
  129. package/dist/runtime/dashboard/utils/filters.js +62 -0
  130. package/dist/runtime/dashboard/utils/format.d.ts +25 -0
  131. package/dist/runtime/dashboard/utils/format.js +66 -0
  132. package/dist/runtime/dashboard/utils/grid.d.ts +17 -0
  133. package/dist/runtime/dashboard/utils/grid.js +14 -0
  134. package/dist/runtime/dashboard/utils/options.d.ts +3 -1
  135. package/dist/runtime/dashboard/utils/options.js +6 -0
  136. package/dist/runtime/dashboard/utils/remote.d.ts +7 -0
  137. package/dist/runtime/dashboard/utils/remote.js +14 -0
  138. package/dist/runtime/dashboard/utils/resource.d.ts +16 -7
  139. package/dist/runtime/dashboard/utils/resource.js +15 -9
  140. package/dist/runtime/dashboard/utils/schema.d.ts +8 -3
  141. package/dist/runtime/dashboard/utils/schema.js +9 -4
  142. package/dist/runtime/dashboard/utils/state.d.ts +11 -6
  143. package/dist/runtime/dashboard/utils/state.js +10 -5
  144. package/dist/runtime/dashboard/utils/time.d.ts +2 -0
  145. package/dist/runtime/dashboard/utils/time.js +11 -1
  146. package/dist/runtime/dashboard/utils/ui.d.ts +1 -1
  147. package/dist/runtime/dashboard/utils/ui.js +1 -1
  148. package/dist/runtime/form/components/root/form.vue +1 -1
  149. package/dist/runtime/form/fields/card-selection.d.ts +1 -0
  150. package/dist/runtime/form/fields/card-selection.js +1 -0
  151. package/dist/runtime/form/fields/checkbox-card/component.vue +3 -1
  152. package/dist/runtime/form/fields/checkbox-group/component.vue +5 -0
  153. package/dist/runtime/form/fields/radio/component.vue +10 -0
  154. package/dist/runtime/form/fields/radio-card/component.vue +3 -1
  155. package/dist/runtime/form/types/options.d.ts +1 -11
  156. package/dist/runtime/form/utils/focus.js +1 -1
  157. package/dist/runtime/i18n/locales/en.js +29 -0
  158. package/dist/runtime/i18n/locales/fr.js +29 -0
  159. package/dist/runtime/i18n/types.d.ts +41 -0
  160. package/dist/runtime/shared/styles/tokens.css +1 -1
  161. package/dist/runtime/shared/types/query.d.ts +8 -0
  162. package/dist/runtime/table/types/index.d.ts +1 -0
  163. package/dist/runtime/table/types/remote-options.d.ts +64 -0
  164. package/dist/runtime/table/types/remote-options.js +0 -0
  165. package/dist/runtime/table/types/source.d.ts +10 -1
  166. package/dist/runtime/table/utils/builders/table-source.d.ts +2 -6
  167. package/dist/runtime/table/utils/index.d.ts +1 -0
  168. package/dist/runtime/table/utils/index.js +1 -0
  169. package/dist/runtime/table/utils/remote-table-options.d.ts +26 -0
  170. package/dist/runtime/table/utils/remote-table-options.js +88 -0
  171. package/package.json +4 -1
  172. package/dist/runtime/dashboard/composables/use-dashboard-param-scope.d.ts +0 -30
  173. package/dist/runtime/dashboard/composables/use-dashboard-param-scope.js +0 -71
  174. package/dist/runtime/dashboard/types/options.d.ts +0 -46
  175. package/dist/runtime/dashboard/types/params.d.ts +0 -115
  176. package/dist/runtime/dashboard/utils/builders/dashboard-params.d.ts +0 -85
  177. package/dist/runtime/dashboard/utils/builders/dashboard-params.js +0 -87
  178. /package/dist/runtime/dashboard/types/{options.js → controls.js} +0 -0
  179. /package/dist/runtime/dashboard/types/{params.js → filters.js} +0 -0
@@ -1,12 +1,55 @@
1
- import { computed } from "vue";
1
+ import { computed, markRaw } from "vue";
2
2
  import { useUiToolsLocale } from "../../i18n/use-locale.js";
3
- import { resolveDashboardFormats } from "../utils/charts.js";
3
+ import { isNumber } from "../../shared/utils/predicate.js";
4
+ import { currentDashboardLocale } from "../utils/environment.js";
5
+ import {
6
+ formatDashboardMonth,
7
+ resolveDashboardFormats,
8
+ resolveDashboardNumberFormat
9
+ } from "../utils/format.js";
4
10
  export function useDashboardFormat() {
5
- const { code } = useUiToolsLocale();
11
+ const { code, t } = currentDashboardLocale() ?? useUiToolsLocale();
6
12
  const formats = computed(() => resolveDashboardFormats(code.value));
7
- return {
8
- delta: computed(() => formats.value.delta),
9
- number: computed(() => formats.value.number),
10
- percent: computed(() => formats.value.percent)
11
- };
13
+ const plurals = computed(() => new Intl.PluralRules(code.value));
14
+ function points(value) {
15
+ const form = plurals.value.select(Math.abs(value)) === "one" ? "pointsOne" : "pointsOther";
16
+ return t(`dashboard.format.${form}`, { value: formats.value.signed(value) });
17
+ }
18
+ function resolve(format, fallback = "number") {
19
+ const current = format ?? fallback;
20
+ if (typeof current === "function") return current;
21
+ if (typeof current === "string") return current === "points" ? points : formats.value[current];
22
+ return resolveDashboardNumberFormat(code.value, current);
23
+ }
24
+ return markRaw({
25
+ /** `12600` → "12.6K". */
26
+ compact: orMissing((value) => formats.value.compact(value)),
27
+ /** An amount in a currency, rounded to units unless `options` say otherwise. */
28
+ currency: (value, currency, options) => isPresent(value) ? resolveDashboardNumberFormat(code.value, { currency, ...options })(value) : DASHBOARD_MISSING_VALUE,
29
+ /** One decimal at most. */
30
+ decimal: orMissing((value) => formats.value.decimal(value)),
31
+ /** Signed percent change: `12.4` → "+12.4%". */
32
+ delta: orMissing((value) => formats.value.delta(value)),
33
+ /** Grouped, no decimals. */
34
+ integer: orMissing((value) => formats.value.integer(value)),
35
+ /** Month name, capitalized: `3` (1–12) or a date → "Mar" (`'long'`: "March"). */
36
+ month: (value, width = "short") => formatDashboardMonth(code.value, value, width),
37
+ /** Grouped, one decimal at most, compact from 10,000. */
38
+ number: orMissing((value) => formats.value.number(value)),
39
+ /** A share out of 100: `57` → "57%". */
40
+ percent: orMissing((value) => formats.value.percent(value)),
41
+ points: orMissing(points),
42
+ /** A share out of 1: `0.57` → "57%". */
43
+ ratio: orMissing((value) => formats.value.ratio(value)),
44
+ resolve,
45
+ /** Signed number: `3` → "+3". */
46
+ signed: orMissing((value) => formats.value.signed(value))
47
+ });
48
+ }
49
+ export const DASHBOARD_MISSING_VALUE = "\u2014";
50
+ function isPresent(value) {
51
+ return isNumber(value) && Number.isFinite(value);
52
+ }
53
+ function orMissing(format) {
54
+ return (value) => isPresent(value) ? format(value) : DASHBOARD_MISSING_VALUE;
12
55
  }
@@ -1,8 +1,15 @@
1
1
  import type { QueryKey } from '@tanstack/vue-query';
2
- import type { DashboardOptionsHandle, DashboardRuntimeParam } from '../types/index.js';
3
- /** Option handle of one option-backed param: static items, or a remote source. */
2
+ import type { DashboardRuntimeFilter } from '../types/index.js';
3
+ import type { DashboardRuntimeOptionList } from '../types/runtime.js';
4
+ import type { DashboardLocale } from '../utils/environment.js';
5
+ /**
6
+ * Option list of one filter: static items (fixed, or read from data), or a remote source. Items of
7
+ * values without labels of their own (`enum`, `boolean`) take their text from `format`, and
8
+ * booleans and comparison periods fall back to localized labels.
9
+ */
4
10
  export declare function useDashboardOptions(params: {
5
- definition: DashboardRuntimeParam;
11
+ definition: DashboardRuntimeFilter;
6
12
  queryKey: QueryKey;
13
+ locale: DashboardLocale;
7
14
  value: () => unknown;
8
- }): DashboardOptionsHandle;
15
+ }): DashboardRuntimeOptionList;
@@ -1,7 +1,9 @@
1
- import { computed, markRaw, shallowRef } from "vue";
2
- import { useUiToolsLocale } from "../../i18n/use-locale.js";
3
- import { resolveDashboardOptionValues } from "../utils/options.js";
1
+ import { computed, shallowRef } from "vue";
2
+ import { matchesDashboardSearch } from "../utils/options.js";
4
3
  import { useDashboardRemoteOptions } from "./use-dashboard-remote-options.js";
4
+ const noMenuBindings = computed(() => ({}));
5
+ const noError = computed(() => void 0);
6
+ const off = computed(() => false);
5
7
  export function useDashboardOptions(params) {
6
8
  const { definition } = params;
7
9
  if (definition.remote)
@@ -10,54 +12,38 @@ export function useDashboardOptions(params) {
10
12
  queryKey: params.queryKey,
11
13
  value: params.value
12
14
  });
13
- const items = definition.items ?? [];
14
- if (definition.kind !== "comparison") return createStaticOptionsHandle(() => items, params.value);
15
- const { t } = useUiToolsLocale();
16
- const localized = computed(
17
- () => items.map((item) => ({ ...item, label: t(`dashboard.compare.${String(item.value)}`) }))
18
- );
19
- return createStaticOptionsHandle(() => localized.value, params.value);
20
- }
21
- function createStaticOptionsHandle(items, value) {
15
+ const { t } = params.locale;
22
16
  const search = shallowRef("");
23
17
  const open = shallowRef(false);
24
- const byValue = computed(() => new Map(items().map((item) => [String(item.value), item])));
25
- const selected = computed(
26
- () => resolveDashboardOptionValues(value()).flatMap((entry) => byValue.value.get(entry) ?? [])
18
+ function label(item) {
19
+ if (definition.kind === "comparison") return t(`dashboard.compare.${String(item.value)}`);
20
+ if (definition.format && definition.kind !== "options") return definition.format(item.value);
21
+ if (definition.kind === "boolean")
22
+ return t(item.value === true ? "dashboard.filters.yes" : "dashboard.filters.no");
23
+ return item.label;
24
+ }
25
+ const all = computed(
26
+ () => (definition.items?.() ?? []).map((item) => ({ ...item, label: label(item) }))
27
27
  );
28
- const menu = computed(() => ({
29
- items: [...items()],
30
- labelKey: "label",
31
- valueKey: "value"
32
- }));
33
- return markRaw({
34
- error: void 0,
35
- hasMore: false,
36
- get items() {
37
- return items();
38
- },
39
- loading: false,
40
- loadingMore: false,
28
+ const known = computed(() => new Map(all.value.map((item) => [String(item.value), item])));
29
+ const order = computed(() => new Map(all.value.map((item, index) => [String(item.value), index])));
30
+ const items = computed(
31
+ () => search.value ? all.value.filter((item) => matchesDashboardSearch(item.label, search.value)) : all.value
32
+ );
33
+ return {
34
+ error: noError,
35
+ hasMore: off,
36
+ items,
37
+ known,
41
38
  loadMore() {
42
39
  },
43
- get menu() {
44
- return menu.value;
45
- },
46
- get open() {
47
- return open.value;
48
- },
49
- set open(next) {
50
- open.value = next;
51
- },
40
+ loading: off,
41
+ loadingMore: off,
42
+ menu: noMenuBindings,
43
+ open,
44
+ order,
52
45
  refresh: () => Promise.resolve(),
53
- get search() {
54
- return search.value;
55
- },
56
- set search(next) {
57
- search.value = next;
58
- },
59
- get selected() {
60
- return selected.value;
61
- }
62
- });
46
+ resolving: off,
47
+ search
48
+ };
63
49
  }
@@ -1,12 +1,15 @@
1
1
  import type { QueryKey } from '@tanstack/vue-query';
2
- import type { DashboardOptionsHandle, DashboardRemoteOptionsConfig } from '../types/index.js';
2
+ import type { DashboardRemoteOptionsConfig } from '../types/index.js';
3
+ import type { DashboardRuntimeOptionList } from '../types/runtime.js';
3
4
  /**
4
- * Remote option handle. Nothing loads until the picker opens or a search term is typed; pages are
5
- * cached per search term by TanStack Query, and selected values missing from the loaded pages are
6
- * hydrated through `resolveSelected` so URL-restored ids render their labels.
5
+ * Remote option list. Nothing loads until the picker opens or a search term is typed; pages are
6
+ * cached per search term by TanStack Query (the previous term's options stay on screen while the
7
+ * next one loads), and selected values missing from the loaded pages are hydrated through
8
+ * `resolveSelected` so URL-restored ids render their labels. Sources may return query definitions
9
+ * or promises.
7
10
  */
8
11
  export declare function useDashboardRemoteOptions(params: {
9
12
  config: DashboardRemoteOptionsConfig;
10
13
  queryKey: QueryKey;
11
14
  value: () => unknown;
12
- }): DashboardOptionsHandle;
15
+ }): DashboardRuntimeOptionList;
@@ -1,30 +1,39 @@
1
1
  import { useInfiniteQuery, useQuery } from "@tanstack/vue-query";
2
2
  import { refDebounced } from "@vueuse/core";
3
- import { computed, markRaw, shallowRef } from "vue";
3
+ import { computed, shallowRef } from "vue";
4
4
  import {
5
5
  REMOTE_OPTIONS_FIRST_PAGE,
6
6
  resolveRemoteOptionsNextPage
7
7
  } from "../../shared/utils/remote-options.js";
8
8
  import { DASHBOARD_REMOTE_OPTIONS_DEFAULTS } from "../constants/query.js";
9
9
  import { mergeDashboardOptions, resolveDashboardOptionValues } from "../utils/options.js";
10
+ import { runDashboardRemoteResult } from "../utils/remote.js";
10
11
  export function useDashboardRemoteOptions(params) {
11
12
  const { config } = params;
12
13
  const queryKey = config.queryKey ?? params.queryKey;
13
- const pageSize = config.pagination?.size ?? DASHBOARD_REMOTE_OPTIONS_DEFAULTS.pageSize;
14
+ const pageSize = Math.max(
15
+ 1,
16
+ config.pagination?.size ?? DASHBOARD_REMOTE_OPTIONS_DEFAULTS.pageSize
17
+ );
14
18
  const minLength = config.search?.minLength ?? 0;
15
19
  const open = shallowRef(false);
16
- const searchInput = shallowRef("");
17
- const search = refDebounced(
18
- searchInput,
20
+ const search = shallowRef("");
21
+ const term = refDebounced(
22
+ computed(() => search.value.trim()),
19
23
  config.search?.debounce ?? DASHBOARD_REMOTE_OPTIONS_DEFAULTS.searchDebounce
20
24
  );
21
25
  const pages = useInfiniteQuery(
22
26
  computed(() => ({
23
- enabled: (open.value || searchInput.value !== "") && (search.value.length === 0 || search.value.length >= minLength),
27
+ enabled: (open.value || search.value !== "") && (term.value.length === 0 || term.value.length >= minLength),
24
28
  getNextPageParam: (lastPage, allPages) => resolveRemoteOptionsNextPage(lastPage, allPages.length),
25
29
  initialPageParam: REMOTE_OPTIONS_FIRST_PAGE,
26
- queryFn: ({ pageParam }) => config.load({ page: { ...pageParam, size: pageSize }, search: search.value }),
27
- queryKey: [...queryKey, "options", search.value],
30
+ // Keeps the previous term's options on screen while the next term loads.
31
+ placeholderData: (previous) => previous,
32
+ queryFn: (context) => runDashboardRemoteResult(
33
+ config.load({ page: { ...context.pageParam, size: pageSize }, search: term.value }),
34
+ context
35
+ ),
36
+ queryKey: [...queryKey, "options", term.value],
28
37
  staleTime: DASHBOARD_REMOTE_OPTIONS_DEFAULTS.staleTime
29
38
  }))
30
39
  );
@@ -34,84 +43,64 @@ export function useDashboardRemoteOptions(params) {
34
43
  const loadedByValue = computed(
35
44
  () => new Map(loaded.value.map((option) => [option.value, option]))
36
45
  );
37
- const selectedValues = computed(() => resolveDashboardOptionValues(params.value()));
38
46
  const missing = computed(
39
- () => selectedValues.value.filter((value) => !loadedByValue.value.has(value))
47
+ () => resolveDashboardOptionValues(params.value()).filter((value) => !loadedByValue.value.has(value))
40
48
  );
41
49
  const hydration = useQuery(
42
50
  computed(() => ({
43
51
  enabled: Boolean(config.resolveSelected) && missing.value.length > 0,
44
- queryFn: () => config.resolveSelected?.({ values: missing.value }) ?? Promise.resolve([]),
52
+ queryFn: (context) => config.resolveSelected ? runDashboardRemoteResult(config.resolveSelected({ values: missing.value }), context) : Promise.resolve([]),
45
53
  queryKey: [...queryKey, "selected", missing.value],
54
+ // Hydrated labels stay while the next set of missing values resolves.
55
+ placeholderData: (previous) => previous,
46
56
  staleTime: DASHBOARD_REMOTE_OPTIONS_DEFAULTS.selectedStaleTime
47
57
  }))
48
58
  );
49
- const selected = computed(() => {
50
- const hydrated = new Map((hydration.data.value ?? []).map((option) => [option.value, option]));
51
- return selectedValues.value.map(
52
- (value) => loadedByValue.value.get(value) ?? hydrated.get(value) ?? { label: value, value }
53
- );
54
- });
55
- const items = computed(
56
- () => mergeDashboardOptions(loaded.value, selected.value)
59
+ const hydrated = computed(() => hydration.data.value ?? []);
60
+ const known = computed(
61
+ () => new Map(
62
+ mergeDashboardOptions(loaded.value, hydrated.value).map((option) => [
63
+ String(option.value),
64
+ option
65
+ ])
66
+ )
57
67
  );
58
- const loading = computed(
59
- () => pages.isFetching.value && !pages.isFetchingNextPage.value || hydration.isFetching.value
68
+ const items = computed(
69
+ () => term.value ? loaded.value : mergeDashboardOptions(
70
+ loaded.value,
71
+ hydrated.value.filter((option) => missing.value.includes(option.value))
72
+ )
60
73
  );
61
- function setSearch(next) {
62
- searchInput.value = next;
63
- }
64
- function setOpen(next) {
65
- open.value = next;
66
- }
67
- return markRaw({
68
- get error() {
69
- return pages.error.value ?? hydration.error.value;
70
- },
71
- get hasMore() {
72
- return pages.hasNextPage.value;
73
- },
74
- get items() {
75
- return items.value;
76
- },
77
- get loading() {
78
- return loading.value;
74
+ const loading = computed(() => pages.isFetching.value && !pages.isFetchingNextPage.value);
75
+ const menu = computed(() => ({
76
+ ignoreFilter: true,
77
+ loading: loading.value,
78
+ "onUpdate:open": (next) => {
79
+ open.value = next;
79
80
  },
80
- get loadingMore() {
81
- return pages.isFetchingNextPage.value;
81
+ "onUpdate:searchTerm": (next) => {
82
+ search.value = next;
82
83
  },
84
+ searchTerm: search.value
85
+ }));
86
+ return {
87
+ error: computed(() => pages.error.value ?? hydration.error.value ?? void 0),
88
+ hasMore: computed(() => pages.hasNextPage.value),
89
+ items,
90
+ known,
83
91
  loadMore() {
84
- if (pages.hasNextPage.value && !pages.isFetchingNextPage.value) void pages.fetchNextPage();
85
- },
86
- get menu() {
87
- return {
88
- ignoreFilter: true,
89
- items: items.value,
90
- labelKey: "label",
91
- loading: loading.value,
92
- "onUpdate:open": setOpen,
93
- "onUpdate:searchTerm": setSearch,
94
- searchTerm: searchInput.value,
95
- valueKey: "value"
96
- };
97
- },
98
- get open() {
99
- return open.value;
100
- },
101
- set open(next) {
102
- setOpen(next);
92
+ if (!pages.hasNextPage.value || pages.isFetching.value) return;
93
+ void pages.fetchNextPage();
103
94
  },
95
+ loading,
96
+ loadingMore: computed(() => pages.isFetchingNextPage.value),
97
+ menu,
98
+ open,
99
+ order: computed(() => null),
104
100
  async refresh() {
105
101
  await pages.refetch();
106
102
  },
107
- get search() {
108
- return searchInput.value;
109
- },
110
- set search(next) {
111
- setSearch(next);
112
- },
113
- get selected() {
114
- return selected.value;
115
- }
116
- });
103
+ resolving: computed(() => hydration.isFetching.value),
104
+ search
105
+ };
117
106
  }
@@ -2,10 +2,16 @@ import type { QueryKey } from '@tanstack/vue-query';
2
2
  import type { ComputedRef } from 'vue';
3
3
  import type { DashboardResourceState, DashboardStage } from '../types/index.js';
4
4
  import type { DashboardRuntimeQueryInput } from '../types/runtime.js';
5
+ import type { DashboardEnvironment } from '../utils/environment.js';
6
+ import type { DashboardReadTracker } from '../utils/tracker.js';
5
7
  /**
6
- * Owns one declared query: exactly one `useQuery`, its widget params, and the deferred activation
8
+ * Owns one declared query: exactly one `useQuery`, its own filters, and the deferred activation
7
9
  * latch. Gating rewrites `enabled` on a stable definition, so hook order never changes, and a gated
8
- * resource never evaluates its factory (it does not subscribe to params it cannot use yet).
10
+ * resource never evaluates its factory (it does not subscribe to filters it cannot use yet).
11
+ *
12
+ * `requires` runs through the read tracker: while it returns nothing, the resource follows the
13
+ * resources it read (loading while they load), then settles on its default value once they are
14
+ * ready without satisfying it.
9
15
  */
10
16
  export declare function useDashboardResource(params: {
11
17
  id: string;
@@ -15,21 +21,26 @@ export declare function useDashboardResource(params: {
15
21
  scope: {
16
22
  active: ComputedRef<boolean>;
17
23
  settled: ComputedRef<boolean>;
24
+ /** The scope's own availability: a disabled view disables every query it declares. */
25
+ enabled: ComputedRef<boolean>;
18
26
  prefix: string;
19
27
  queryKey: QueryKey;
20
28
  /** Dashboard auto-refresh interval in milliseconds, `0` when off. */
21
29
  refetchInterval: ComputedRef<number>;
22
30
  };
31
+ tracker: DashboardReadTracker;
32
+ environment: DashboardEnvironment;
23
33
  }): {
24
34
  activate: () => void;
25
35
  active: ComputedRef<boolean>;
26
36
  data: ComputedRef<unknown>;
27
- error: import("vue").Ref<Error, Error> | import("vue").Ref<null, null>;
37
+ error: ComputedRef<unknown>;
28
38
  id: string;
29
39
  refresh: () => Promise<void>;
40
+ fetching: ComputedRef<boolean>;
30
41
  refreshing: ComputedRef<boolean>;
31
42
  stage: DashboardStage;
32
43
  state: ComputedRef<DashboardResourceState>;
33
44
  updatedAt: ComputedRef<number | undefined>;
34
- widget: import("./use-dashboard-param-scope.js").DashboardParamScope;
45
+ filters: import("./use-dashboard-filter-scope.js").DashboardFilterScope;
35
46
  };
@@ -2,30 +2,49 @@ import { keepPreviousData, useQuery } from "@tanstack/vue-query";
2
2
  import { computed, shallowRef } from "vue";
3
3
  import { hasProperty, isNullish } from "../../shared/utils/predicate.js";
4
4
  import { DASHBOARD_QUERY_DEFAULTS } from "../constants/query.js";
5
- import { dashboardParamBuilder } from "../utils/builders/dashboard-params.js";
6
- import { joinDashboardUrlKey } from "../utils/state.js";
7
- import { useDashboardParamScope } from "./use-dashboard-param-scope.js";
5
+ import { dashboardFilterBuilder } from "../utils/builders/dashboard-filters.js";
6
+ import { resolveDashboardFilters } from "../utils/schema.js";
7
+ import {
8
+ combineDashboardStates,
9
+ joinDashboardUrlKey,
10
+ refreshDashboardSources,
11
+ resolveDashboardCondition
12
+ } from "../utils/state.js";
13
+ import { useDashboardFilterScope } from "./use-dashboard-filter-scope.js";
8
14
  export function useDashboardResource(params) {
9
15
  const { input, stage, scope } = params;
10
16
  const activated = shallowRef(stage !== "deferred");
11
- const widget = useDashboardParamScope({
12
- definitions: input.params?.(dashboardParamBuilder) ?? {},
17
+ const filters = useDashboardFilterScope({
18
+ definitions: resolveDashboardFilters({ builder: dashboardFilterBuilder, input: input.filters }),
19
+ environment: params.environment,
20
+ owner: `query "${params.id}"`,
13
21
  prefix: joinDashboardUrlKey(scope.prefix, params.key),
14
- queryKey: [...scope.queryKey, params.id, "params"]
22
+ queryKey: [...scope.queryKey, params.id, "filters"]
15
23
  });
16
24
  const active = computed(() => scope.active.value && activated.value);
25
+ const available = computed(
26
+ () => scope.enabled.value && resolveDashboardCondition(input.enabled)
27
+ );
17
28
  const gate = computed(() => {
18
- if (!active.value) return false;
29
+ if (!active.value || !available.value) return false;
19
30
  if (stage === "background" && !scope.settled.value) return false;
20
- return input.enabled?.() ?? true;
31
+ return true;
32
+ });
33
+ const requirement = computed(() => {
34
+ const sources = /* @__PURE__ */ new Set();
35
+ if (!gate.value || !input.requires) return { sources: [], value: true };
36
+ const value = params.tracker.run(sources, input.requires);
37
+ return { sources: [...sources], value };
21
38
  });
22
- const required = computed(() => gate.value && input.requires ? input.requires() : true);
23
- const enabled = computed(() => gate.value && !isNullish(required.value));
39
+ const enabled = computed(() => gate.value && !isNullish(requirement.value.value));
40
+ const upstream = computed(
41
+ () => combineDashboardStates(requirement.value.sources.map((source) => source.state))
42
+ );
24
43
  const query = useQuery(
25
44
  computed(() => enabled.value ? resolveDefinition() : idleDefinition(params.id))
26
45
  );
27
46
  function resolveDefinition() {
28
- const definition = input.query({ params: widget.values, required: required.value });
47
+ const definition = input.query({ filters: filters.values, required: requirement.value.value });
29
48
  const interval = scope.refetchInterval.value;
30
49
  return {
31
50
  placeholderData: input.keepPreviousData === false ? void 0 : keepPreviousData,
@@ -38,14 +57,28 @@ export function useDashboardResource(params) {
38
57
  };
39
58
  }
40
59
  const state = computed(() => {
41
- if (!enabled.value) return "idle";
60
+ if (!available.value) return "disabled";
61
+ if (!gate.value) return "idle";
62
+ if (!enabled.value) {
63
+ if (upstream.value !== "ready") return upstream.value;
64
+ return input.defaultValue === void 0 ? "idle" : "ready";
65
+ }
42
66
  if (query.isError.value) return "error";
43
67
  return query.data.value === void 0 ? "loading" : "ready";
44
68
  });
45
- const data = computed(
46
- () => enabled.value && query.data.value !== void 0 ? query.data.value : input.defaultValue
69
+ const data = computed(() => {
70
+ if (!enabled.value || query.data.value === void 0) return input.defaultValue;
71
+ return input.select ? input.select(query.data.value) : query.data.value;
72
+ });
73
+ const error = computed(
74
+ () => enabled.value ? query.error.value ?? void 0 : requirement.value.sources.find((source) => source.state === "error")?.error
47
75
  );
48
76
  const refreshing = computed(() => state.value === "ready" && query.isFetching.value);
77
+ const fetching = computed(() => {
78
+ if (!available.value) return false;
79
+ if (enabled.value) return query.isFetching.value;
80
+ return requirement.value.sources.some((source) => source.fetching ?? source.refreshing);
81
+ });
49
82
  const updatedAt = computed(
50
83
  () => state.value === "ready" && query.dataUpdatedAt.value > 0 ? query.dataUpdatedAt.value : void 0
51
84
  );
@@ -53,22 +86,24 @@ export function useDashboardResource(params) {
53
86
  activated.value = true;
54
87
  }
55
88
  async function refresh() {
89
+ if (!available.value) return;
56
90
  if (!activated.value) return activate();
57
- if (!enabled.value) return;
91
+ if (!enabled.value) return refreshDashboardSources(requirement.value.sources);
58
92
  await query.refetch({ throwOnError: true });
59
93
  }
60
94
  return {
61
95
  activate,
62
96
  active,
63
97
  data,
64
- error: query.error,
98
+ error,
65
99
  id: params.id,
66
100
  refresh,
101
+ fetching,
67
102
  refreshing,
68
103
  stage,
69
104
  state,
70
105
  updatedAt,
71
- widget
106
+ filters
72
107
  };
73
108
  }
74
109
  function idleDefinition(id) {
@@ -1,10 +1,10 @@
1
1
  import type { ComputedRef } from 'vue';
2
2
  import type { DashboardResourceState, DashboardSourceLike, DashboardStage } from '../types/index.js';
3
3
  import type { DashboardRuntimeScopeInput } from '../types/runtime.js';
4
+ import type { DashboardEnvironment } from '../utils/environment.js';
4
5
  import type { DashboardReadTracker } from '../utils/tracker.js';
5
- import { useDashboardParamScope } from './use-dashboard-param-scope.js';
6
6
  /**
7
- * One scope of a dashboard: the root, or one view. It owns the scope params, declares its queries
7
+ * One scope of a dashboard: the root, or one view. It owns the scope filters, declares its queries
8
8
  * in a single synchronous pass, instantiates them once their keys are known, and derives the scope
9
9
  * state from its essential queries.
10
10
  */
@@ -15,31 +15,34 @@ export declare function useDashboardScope(params: {
15
15
  input: DashboardRuntimeScopeInput;
16
16
  prefix: string;
17
17
  active: ComputedRef<boolean>;
18
+ /** The scope is part of the dashboard: always for the root, a view's `enabled` otherwise. */
19
+ enabled?: ComputedRef<boolean>;
18
20
  /** Dashboard auto-refresh interval in milliseconds, `0` when off. */
19
21
  refetchInterval: ComputedRef<number>;
20
- /** Root params, visible inside a view's builders. */
21
- shared?: ReturnType<typeof useDashboardParamScope>;
22
22
  /** Root queries and derived values, readable from a view's `derive`. */
23
23
  rootSources?: ReadonlyMap<string, DashboardSourceLike>;
24
24
  tracker: DashboardReadTracker;
25
+ environment: DashboardEnvironment;
25
26
  }): {
27
+ enabled: ComputedRef<boolean>;
28
+ filterScope: import("./use-dashboard-filter-scope.js").DashboardFilterScope;
26
29
  label: import("../../shared/index.js").LazyTextValue | undefined;
27
30
  members: Map<string, DashboardSourceLike<unknown>>;
28
- paramScope: import("./use-dashboard-param-scope.js").DashboardParamScope;
29
31
  refresh: () => Promise<void>;
30
32
  refreshing: import("vue").ShallowRef<boolean, boolean>;
31
33
  resources: {
32
34
  activate: () => void;
33
35
  active: ComputedRef<boolean>;
34
36
  data: ComputedRef<unknown>;
35
- error: import("vue").Ref<Error, Error> | import("vue").Ref<null, null>;
37
+ error: ComputedRef<unknown>;
36
38
  id: string;
37
39
  refresh: () => Promise<void>;
40
+ fetching: ComputedRef<boolean>;
38
41
  refreshing: ComputedRef<boolean>;
39
42
  stage: DashboardStage;
40
43
  state: ComputedRef<DashboardResourceState>;
41
44
  updatedAt: ComputedRef<number | undefined>;
42
- widget: import("./use-dashboard-param-scope.js").DashboardParamScope;
45
+ filters: import("./use-dashboard-filter-scope.js").DashboardFilterScope;
43
46
  }[];
44
47
  state: ComputedRef<DashboardResourceState>;
45
48
  updatedAt: ComputedRef<number | undefined>;