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,42 +1,36 @@
1
- import type { DashboardDeriveMap, DashboardDerivedResources, DashboardEmptyMap, DashboardKeyError, DashboardParamMap, DashboardReservedKey, DashboardSchema, DashboardScopeGuard, DashboardScopeInput, DashboardSourceMap, DashboardViewBuilder, DashboardViewMap } from '../types/index.js';
1
+ import type { LazyTextValue } from '../../shared/types/utils.js';
2
+ import type { DashboardCondition, DashboardDeriveMap, DashboardEmptyMap, DashboardFilterMap, DashboardKeyError, DashboardReservedKey, DashboardSchema, DashboardScopeGuard, DashboardScopeInput, DashboardSourceMap, DashboardView, DashboardViewMap } from '../types/index.js';
2
3
  type DashboardViewsGuard<TQueries, TDerive, TViews> = [
3
4
  Extract<keyof TViews, DashboardReservedKey | keyof TQueries | keyof TDerive>
4
5
  ] extends [never] ? unknown : {
5
6
  views: DashboardKeyError<`View key "${Extract<keyof TViews, DashboardReservedKey | keyof TQueries | keyof TDerive> & string}" is reserved or collides with a root query or derived value.`>;
6
7
  };
7
8
  /**
8
- * Declares a dashboard: typed URL-synced params, staged queries, derived values, and optional
9
- * views (tabs). Nothing runs here — `useDashboard(schema)` instantiates it.
9
+ * Declares a dashboard: its filters, staged queries and derived values, and its views (tabs).
10
+ * Nothing runs here — `useDashboard()` instantiates it.
11
+ *
12
+ * Wrap it in a function taking what the dashboard is about (an account, the workspace): the page
13
+ * passes it, and the function hands it on to its views. Filters are the state the user controls;
14
+ * they are declared inline, with the `f` builder.
10
15
  *
11
16
  * Keys are checked at compile time: a query, derived value, or view named after a runtime member
12
- * (`params`, `options`, `state`, `refreshing`, `refresh`, `updatedAt`, `autoRefresh`, `view`,
13
- * `schema`) or after a sibling is a type error.
17
+ * (`filters`, `controls`, `filtered`, `resetFilters`, `state`, `refreshing`, `refresh`, `updatedAt`,
18
+ * `autoRefresh`, `view`, `schema`) or after a sibling is a type error.
14
19
  *
15
20
  * @example
16
21
  * ```ts
17
- * const schema = defineDashboardSchema({
18
- * key: 'sales',
19
- * params: (p) => ({ period: p.enum([7, 30, 90], { defaultValue: 30 }) }),
20
- * queries: ({ essential, background, params }) => ({
21
- * summary: essential.query(() => ({
22
- * queryKey: ['sales', 'summary', params.period],
23
- * queryFn: () => api.sales.summary({ days: params.period }),
24
- * })),
25
- * channels: background.query({
26
- * defaultValue: [],
27
- * query: () => ({
28
- * queryKey: ['sales', 'channels', params.period],
29
- * queryFn: () => api.sales.channels({ days: params.period }),
30
- * }),
31
- * }),
32
- * }),
33
- * derive: ({ data }) => ({
34
- * channelCount: () => data.channels.length,
35
- * }),
36
- * })
22
+ * export function accountSchema(params: { accountId: string }) {
23
+ * return defineDashboardSchema({
24
+ * key: 'account',
25
+ * views: {
26
+ * activity: accountActivityView(params),
27
+ * invoices: accountInvoicesView(params),
28
+ * },
29
+ * })
30
+ * }
37
31
  * ```
38
32
  */
39
- export declare function defineDashboardSchema<const TParams extends DashboardParamMap = DashboardEmptyMap, const TQueries extends DashboardSourceMap = DashboardEmptyMap, const TDerive extends DashboardDeriveMap = DashboardEmptyMap, const TViews extends DashboardViewMap = DashboardEmptyMap>(schema: {
33
+ export declare function defineDashboardSchema<const TFilters extends DashboardFilterMap = DashboardEmptyMap, const TQueries extends DashboardSourceMap = DashboardEmptyMap, const TDerive extends DashboardDeriveMap = DashboardEmptyMap, const TViews extends DashboardViewMap = DashboardEmptyMap>(schema: {
40
34
  /** Stable identity, used in default query keys and devtools labels. */
41
35
  key: string;
42
36
  /** Prefix prepended to every URL key this dashboard owns. */
@@ -47,11 +41,49 @@ export declare function defineDashboardSchema<const TParams extends DashboardPar
47
41
  */
48
42
  autoRefresh?: number;
49
43
  /**
50
- * Views (tabs). Each view owns params and queries; queries of a view never fetch until the
51
- * view is opened once, then stay warm.
44
+ * Views (tabs), each declared with `defineDashboardView`. A view's queries never fetch until
45
+ * the view is opened once, then stay warm.
52
46
  */
53
- views?: (view: DashboardViewBuilder<TParams, TQueries & DashboardDerivedResources<TDerive>>) => TViews;
47
+ views?: TViews;
54
48
  /** View shown when the URL does not select one. Defaults to the first declared view. */
55
49
  defaultView?: NoInfer<keyof TViews & string>;
56
- } & DashboardScopeInput<DashboardEmptyMap, DashboardEmptyMap, TParams, TQueries, TDerive> & DashboardScopeGuard<TQueries, TDerive> & DashboardViewsGuard<TQueries, TDerive, TViews>): DashboardSchema<TParams, TQueries, TDerive, TViews>;
50
+ } & DashboardScopeInput<TFilters, TQueries, TDerive> & DashboardScopeGuard<TQueries, TDerive> & DashboardViewsGuard<TQueries, TDerive, TViews>): DashboardSchema<NoInfer<TFilters>, NoInfer<TQueries>, NoInfer<TDerive>, NoInfer<TViews>>;
51
+ /**
52
+ * Declares one view (tab), self-contained: its label, its filters, its queries and derived values.
53
+ * Wrap it in a function taking what it needs from the dashboard's input, and give it its own file.
54
+ *
55
+ * A filter key names one state across the dashboard: two views declaring `year` share its value,
56
+ * so it survives a tab change. Components under the dashboard get the typed view handle with
57
+ * `useDashboardView(consumptionView)`.
58
+ *
59
+ * @example
60
+ * ```ts
61
+ * export function consumptionView(params: { workspace: Workspace }) {
62
+ * const { $api, $i18n } = useNuxtApp()
63
+ * return defineDashboardView({
64
+ * label: () => $i18n.t('dashboard.tabs.consumption'),
65
+ * enabled: () => canRead(params.workspace, 'consumption'),
66
+ * filters: (f) => ({
67
+ * year: f.enum([2025, 2026], { defaultValue: 2026, label: () => $i18n.t('filters.year') }),
68
+ * }),
69
+ * queries: ({ essential, filters }) => {
70
+ * const overview = () => $api.consumption.queryOptions({ query: { year: filters.year } })
71
+ * return {
72
+ * summary: essential.query({ query: overview, select: (data) => data.summary }),
73
+ * months: essential.query({ query: overview, select: (data) => data.months }),
74
+ * }
75
+ * },
76
+ * })
77
+ * }
78
+ * ```
79
+ */
80
+ export declare function defineDashboardView<const TFilters extends DashboardFilterMap = DashboardEmptyMap, const TQueries extends DashboardSourceMap = DashboardEmptyMap, const TDerive extends DashboardDeriveMap = DashboardEmptyMap>(view: {
81
+ /** Tab label. The lazy form keeps it translation-friendly. */
82
+ label?: LazyTextValue;
83
+ /**
84
+ * Availability of the view, read lazily: a disabled view has no tab, is never the current view
85
+ * (the URL falls back to an enabled one), and its queries report `disabled`.
86
+ */
87
+ enabled?: DashboardCondition;
88
+ } & DashboardScopeInput<TFilters, TQueries, TDerive> & DashboardScopeGuard<TQueries, TDerive>): DashboardView<NoInfer<TFilters>, NoInfer<TQueries>, NoInfer<TDerive>>;
57
89
  export {};
@@ -1,3 +1,6 @@
1
1
  export function defineDashboardSchema(schema) {
2
2
  return schema;
3
3
  }
4
+ export function defineDashboardView(view) {
5
+ return view;
6
+ }
@@ -2,9 +2,30 @@ import type { ButtonProps } from '@nuxt/ui/components/Button.vue';
2
2
  import type { DropdownMenuItem } from '@nuxt/ui/components/DropdownMenu.vue';
3
3
  import type { RouteLocationRaw } from 'vue-router';
4
4
  import type { LazyTextValue } from '../../shared/types/utils.js';
5
+ import type { DashboardFilterControl } from './controls.js';
5
6
  import type { DashboardBlockUi } from './ui.js';
6
7
  /** Formats a numeric value for axes, tooltips, legends, and totals. */
7
- export type DashboardValueFormat = (value: number) => string;
8
+ export type DashboardValueFormatter = (value: number) => string;
9
+ /**
10
+ * Named number formats, in the dashboard locale:
11
+ *
12
+ * - `number`: grouped, one decimal at most, compact from 10,000 (`12.6K`). The block default.
13
+ * - `integer`: grouped, no decimals (`12,345`).
14
+ * - `decimal`: one decimal at most (`3.5`).
15
+ * - `compact`: short form (`12K`, `1.2M`).
16
+ * - `percent`: a share out of 100 (`57` → `57%`).
17
+ * - `ratio`: a share out of 1 (`0.57` → `57%`).
18
+ * - `delta`: a signed percent change (`12.4` → `+12.4%`).
19
+ * - `points`: a signed difference of percentages (`2.1` → `+2.1 pts`).
20
+ * - `signed`: a signed number (`3` → `+3`).
21
+ */
22
+ export type DashboardFormatPreset = 'number' | 'integer' | 'decimal' | 'compact' | 'percent' | 'ratio' | 'delta' | 'points' | 'signed';
23
+ /**
24
+ * How a block formats numbers: a preset (`'integer'`), `Intl.NumberFormat` options, or a function.
25
+ * Options with a `currency` and no `style` format whole amounts in that currency: `{ currency: 'EUR' }`,
26
+ * or `{ currency: 'EUR', notation: 'compact' }` for an axis.
27
+ */
28
+ export type DashboardValueFormat = DashboardFormatPreset | Intl.NumberFormatOptions | DashboardValueFormatter;
8
29
  /**
9
30
  * Series color: a palette slot (`'series-1'` … `'series-6'`), a Nuxt UI color (`'primary'`,
10
31
  * `'success'`, `'warning'`, `'error'`, `'info'`, `'neutral'`), or any CSS color / `var(--x)`.
@@ -42,10 +63,12 @@ export interface DashboardSeries<TRow> {
42
63
  */
43
64
  export type DashboardHighlight<TRow> = 'max' | 'min' | 'last' | ((row: TRow, index: number) => boolean);
44
65
  /**
45
- * Rows shown as selected, typically the value a `select` handler stored in a param (drill-down,
66
+ * Rows shown as selected, typically the value a `select` handler stored in a filter (drill-down,
46
67
  * cross-filter). Selected rows are marked; in charts the other bars, points, and segments recede.
47
68
  */
48
69
  export type DashboardSelected<TRow> = (row: TRow, index: number) => boolean;
70
+ /** Footer totals of a chart, one per solid series: `true` or `'sum'` adds them up, `'average'` averages them. */
71
+ export type DashboardChartTotals = boolean | 'sum' | 'average';
49
72
  export interface DashboardAxisOptions {
50
73
  format?: DashboardValueFormat;
51
74
  /** Fixed domain bounds. Defaults to `0` and a rounded maximum of the data. */
@@ -192,8 +215,6 @@ export interface DashboardBlockBaseProps {
192
215
  subtitle?: LazyTextValue;
193
216
  /** Grid span, responsive: `"12 md:6 xl:4"`. Defaults to the full row. */
194
217
  size?: string;
195
- /** Grid row span, responsive. */
196
- rows?: string;
197
218
  /** Render without card chrome (no border, padding, or background). Defaults to `true`. */
198
219
  card?: boolean;
199
220
  activation?: DashboardBlockActivation;
@@ -203,6 +224,11 @@ export interface DashboardBlockBaseProps {
203
224
  menu?: DashboardMenu;
204
225
  /** Buttons in the header, next to the menu, or full width under the content (`placement`). */
205
226
  actions?: readonly DashboardAction[];
227
+ /**
228
+ * Filters narrowing this block, such as a drill-down value picked on another block: each one
229
+ * shows as a removable chip in the toolbar while it differs from its default.
230
+ */
231
+ filters?: readonly DashboardFilterControl[];
206
232
  /**
207
233
  * Shows when the data was last fetched ("Updated 3 min ago") under the content. Inherits the
208
234
  * enclosing grid's `freshness` when omitted.
@@ -1,4 +1,4 @@
1
- import type { DashboardValueFormat } from './blocks.js';
1
+ import type { DashboardValueFormatter } from './blocks.js';
2
2
  /** Normalized chart row: series values in series order. */
3
3
  export interface DashboardChartDatum {
4
4
  index: number;
@@ -18,7 +18,7 @@ export interface DashboardChartFrameAxis {
18
18
  domain: [number, number];
19
19
  /** Tick values, evenly spaced on round numbers. */
20
20
  ticks: number[];
21
- format: DashboardValueFormat;
21
+ format: DashboardValueFormatter;
22
22
  }
23
23
  export interface DashboardChartFrameReference {
24
24
  value: number;
@@ -0,0 +1,123 @@
1
+ import type { DashboardFilterKind, DashboardOption, DashboardOptionValue } from './filters.js';
2
+ /**
3
+ * Props ready to spread onto a Nuxt UI `USelectMenu`:
4
+ * `<USelectMenu v-model="dashboard.filters.account" v-bind="dashboard.controls.account.menu" />`.
5
+ */
6
+ export interface DashboardOptionsMenuBindings<TValue extends DashboardOptionValue = DashboardOptionValue> {
7
+ items: DashboardOption<TValue>[];
8
+ valueKey: 'value';
9
+ labelKey: 'label';
10
+ multiple: boolean;
11
+ loading?: boolean;
12
+ searchTerm?: string;
13
+ ignoreFilter?: boolean;
14
+ 'onUpdate:searchTerm'?: (value: string) => void;
15
+ 'onUpdate:open'?: (value: boolean) => void;
16
+ }
17
+ /** A preset as the control exposes it: resolved text, and whether the value matches it. */
18
+ export interface DashboardControlPreset<TValue = unknown> {
19
+ label: string;
20
+ value: TValue;
21
+ icon?: string;
22
+ hint?: string;
23
+ /** The filter's value is this preset's. */
24
+ active: boolean;
25
+ /** Sets the filter's value to this preset's. */
26
+ apply: () => void;
27
+ }
28
+ /**
29
+ * Everything a control needs to drive one filter: its value, how to present it, its option list,
30
+ * and the actions a picker performs. `UiDashboardFilter` renders one; any other component can bind
31
+ * to it directly.
32
+ *
33
+ * Remote lists load nothing until `open` becomes `true` (or `search` is set), then page through the
34
+ * source; selected values restored from the URL are hydrated through `resolveSelected`.
35
+ */
36
+ export interface DashboardFilterControl<TValue = unknown, TItem extends DashboardOptionValue = DashboardOptionValue> {
37
+ readonly key: string;
38
+ readonly kind: DashboardFilterKind;
39
+ readonly multiple: boolean;
40
+ /** Not rendered by `UiDashboardFilters`; ignored by `filtered` and `resetFilters()`. */
41
+ readonly headless: boolean;
42
+ /**
43
+ * The filter's `enabled` condition holds. A disabled filter is out of every bar, reads its
44
+ * default, and ignores writes.
45
+ */
46
+ readonly enabled: boolean;
47
+ /** Filter name. */
48
+ readonly label: string;
49
+ /** Text of an empty selection ("All"). */
50
+ readonly placeholder: string;
51
+ /** Current value. Writable; `null` / `undefined` restore the default. */
52
+ value: TValue;
53
+ readonly defaultValue: TValue;
54
+ /** The value differs from the default. */
55
+ readonly changed: boolean;
56
+ /** Text of the current value: selected labels, or the placeholder. */
57
+ readonly display: string;
58
+ /** Options matching the current search: loaded pages and hydrated selected options for remote lists. */
59
+ readonly items: readonly DashboardOption<TItem>[];
60
+ /** Options of the current value, in value order. */
61
+ readonly selected: readonly DashboardOption<TItem>[];
62
+ /** Menu columns declared on the filter. */
63
+ readonly columns: number | undefined;
64
+ readonly searchable: boolean;
65
+ /** Most values a `multiple` filter picks. */
66
+ readonly max: number | undefined;
67
+ /** `value` is, or contains, this option value. */
68
+ isSelected(value: TItem): boolean;
69
+ /**
70
+ * Picks an option. Multiple filters add or remove it (in item order, up to `max`); single filters
71
+ * take it as their value.
72
+ */
73
+ toggle(value: TItem): void;
74
+ /** Restores the default value. */
75
+ reset(): void;
76
+ /** Shortcut values declared with `presets`. */
77
+ readonly presets: readonly DashboardControlPreset<TValue>[];
78
+ readonly loading: boolean;
79
+ readonly loadingMore: boolean;
80
+ readonly hasMore: boolean;
81
+ readonly error: unknown;
82
+ /** Search term. Writable; remote sources debounce it before loading. */
83
+ search: string;
84
+ /**
85
+ * A picker shows the list. Writable; setting it starts loading a remote list's first page.
86
+ * `UiDashboardFilter` sets it while its menu is open.
87
+ */
88
+ open: boolean;
89
+ loadMore(): void;
90
+ refresh(): Promise<void>;
91
+ readonly menu: DashboardOptionsMenuBindings<TItem>;
92
+ }
93
+ /** Value a filter picks from its list: its value, or its element type for `multiple` filters. */
94
+ type DashboardControlItem<TFilter> = TFilter extends {
95
+ readonly defaultValue: infer TValue;
96
+ } ? Extract<NonNullable<TValue> extends readonly (infer TItem)[] ? TItem : NonNullable<TValue>, DashboardOptionValue> : never;
97
+ type DashboardControlValue<TFilter> = TFilter extends {
98
+ readonly defaultValue: infer TValue;
99
+ } ? TValue : never;
100
+ /** Controls of a filter map, by filter key. */
101
+ export type DashboardFilterControls<TFilters> = {
102
+ readonly [K in keyof TFilters]: DashboardFilterControl<DashboardControlValue<TFilters[K]>, DashboardControlItem<TFilters[K]>>;
103
+ };
104
+ /** What a filter bar drives: a dashboard (its root and current view) or one view handle. */
105
+ export interface DashboardFiltersTarget {
106
+ readonly controls: Readonly<Record<string, DashboardFilterControl>>;
107
+ readonly filtered: boolean;
108
+ resetFilters(): void;
109
+ /** The dashboard's view controller; view handles carry their own meta, without `current`. */
110
+ readonly view?: {
111
+ readonly current?: string;
112
+ };
113
+ }
114
+ type DashboardControlsOf<TTarget> = TTarget extends {
115
+ readonly controls: infer TControls;
116
+ } ? TControls : never;
117
+ /** Keys of every filter a bar may show: the root's, and those of each view. */
118
+ export type DashboardFilterKeysOf<TTarget> = keyof DashboardControlsOf<TTarget> | (TTarget extends {
119
+ readonly view: {
120
+ readonly current: infer TKey;
121
+ };
122
+ } ? TKey extends keyof TTarget ? keyof DashboardControlsOf<TTarget[TKey]> : never : never);
123
+ export {};
@@ -0,0 +1,236 @@
1
+ import type { QueryKey } from '@tanstack/vue-query';
2
+ import type { Ref } from 'vue';
3
+ import type { HistoryMode, QueryCodec, StaticQueryStateOptions } from '../../query-state/index.js';
4
+ import type { QueryDefinition } from '../../shared/types/query.js';
5
+ import type { RemoteOptionsPageRequest, RemoteOptionsPagination, RemoteOptionsResult, RemoteOptionsSearch } from '../../shared/types/remote-options.js';
6
+ import type { LazyTextValue } from '../../shared/types/utils.js';
7
+ import type { dashboardFilterBuilder } from '../utils/builders/dashboard-filters.js';
8
+ import type { DashboardCondition } from './resource.js';
9
+ export type DashboardFilterKind = 'string' | 'number' | 'boolean' | 'date' | 'dateRange' | 'enum' | 'options' | 'remote' | 'comparison' | 'custom';
10
+ /**
11
+ * Period a dashboard compares against: the one right before the current range (`previous`), the
12
+ * same dates a year earlier (`year`), or no comparison (`none`).
13
+ */
14
+ export type DashboardComparison = 'previous' | 'year' | 'none';
15
+ /** Primitive value a filter can pick. */
16
+ export type DashboardOptionValue = string | number | boolean;
17
+ /**
18
+ * Option item shape. It matches Nuxt UI `USelect` / `USelectMenu` items, so filter controls can be
19
+ * bound to those components directly.
20
+ */
21
+ export interface DashboardOption<TValue extends DashboardOptionValue = DashboardOptionValue> {
22
+ value: TValue;
23
+ label: string;
24
+ description?: string;
25
+ /** Short trailing text in filter menus: a count, a code. */
26
+ hint?: string;
27
+ icon?: string;
28
+ avatar?: {
29
+ src?: string;
30
+ alt?: string;
31
+ text?: string;
32
+ };
33
+ disabled?: boolean;
34
+ }
35
+ export interface DashboardDateRange {
36
+ start: Date;
37
+ end: Date;
38
+ }
39
+ /**
40
+ * Where a filter keeps its value.
41
+ *
42
+ * - `'url'` (default): the query string, under the filter's URL key.
43
+ * - `'memory'`: component state, gone on reload.
44
+ * - a `Ref`: an external store, read and written both ways (e.g. a Pinia `storeToRefs` ref).
45
+ * - a getter: an external, read-only source; writes are ignored and the filter is headless.
46
+ *
47
+ * A nullish external value reads as the default value.
48
+ */
49
+ export type DashboardFilterSync<TValue> = 'url' | 'memory' | Ref<TValue | null | undefined> | (() => TValue | null | undefined);
50
+ /** A value a filter offers as a shortcut: "Last 30 days", "English range". */
51
+ export interface DashboardFilterPreset<TValue> {
52
+ label: LazyTextValue;
53
+ value: TValue;
54
+ icon?: string;
55
+ /** Short trailing text: a count, a range. */
56
+ hint?: string;
57
+ }
58
+ /** Presets of a filter: fixed, or read reactively (e.g. from another query's data). */
59
+ export type DashboardFilterPresets<TValue> = readonly DashboardFilterPreset<TValue>[] | (() => readonly DashboardFilterPreset<TValue>[]);
60
+ /**
61
+ * A default value, or a getter reading it from data. A getter may return `undefined` while its data
62
+ * loads; the filter type then keeps `undefined` (a list filter falls back to `[]`).
63
+ */
64
+ export type DashboardFilterDefault<TValue> = TValue | (() => TValue | undefined);
65
+ /** Options shared by every `f.*` builder. `TValue` is the filter value. */
66
+ export interface DashboardFilterOptions<TDefault, TValue = unknown> {
67
+ /**
68
+ * Value used when the filter is unset (missing or unparsable URL key, nullish external value).
69
+ * A non-`undefined` default narrows the filter type: `f.string()` is `string | undefined`,
70
+ * `f.string({ defaultValue: '' })` is `string`. Values equal to the default never reach the URL.
71
+ *
72
+ * A getter makes the default follow data, e.g. the top three products of a loaded list: the
73
+ * filter reads it while unset, and a value equal to it keeps following it.
74
+ */
75
+ defaultValue?: TDefault;
76
+ /** Where the value lives. Defaults to `'url'`. */
77
+ sync?: DashboardFilterSync<TValue>;
78
+ /**
79
+ * Override the URL key. Defaults to the filter key: filters with the same URL key are one
80
+ * filter, so `year` declared by two views keeps its value when the tab changes.
81
+ */
82
+ urlKey?: string;
83
+ /** Remove the key from the URL when the value equals the default. Defaults to `true`. */
84
+ omitDefault?: boolean;
85
+ /** Router history mode for writes to this filter. Filters default to `'replace'`. */
86
+ historyMode?: HistoryMode;
87
+ /** Filter name, shown on its pill and read by assistive tech. Defaults to the filter key. */
88
+ label?: LazyTextValue;
89
+ /** Text of an empty selection. Defaults to the localized "All". */
90
+ placeholder?: LazyTextValue;
91
+ /**
92
+ * State only: `UiDashboardFilters` does not render it, and `filtered` / `resetFilters()` ignore
93
+ * it. Use it for values driven by the app (a store, a chart drill-down you render yourself).
94
+ */
95
+ headless?: boolean;
96
+ /**
97
+ * Whether the filter exists right now, e.g. an account filter only an admin gets. While it is
98
+ * `false` its control leaves every bar, its value reads as the default (queries never send it),
99
+ * and writes are ignored.
100
+ */
101
+ enabled?: DashboardCondition;
102
+ /** Shortcuts the filter menu offers under its options; picking one sets the whole value. */
103
+ presets?: DashboardFilterPresets<TValue>;
104
+ }
105
+ /** Presentation of filters whose values have no label of their own (`enum`, `boolean`, `number`…). */
106
+ export interface DashboardFilterFormatOptions<TItem> {
107
+ /** Text of one value, in the menu and on the pill. Evaluated reactively. */
108
+ format?: (value: TItem) => string;
109
+ }
110
+ /** Options of filters picked from a list. */
111
+ export interface DashboardFilterListOptions {
112
+ /** Menu columns: `3` lays twelve months out as a 3×4 grid. */
113
+ columns?: number;
114
+ /**
115
+ * Shows a search field in the menu. Remote lists always search on the server; static lists
116
+ * filter their items locally. Defaults to `true` for remote lists, `false` otherwise.
117
+ */
118
+ searchable?: boolean;
119
+ }
120
+ /** Options of `multiple` filters. */
121
+ export interface DashboardFilterMultipleOptions {
122
+ /** Most values the filter picks; the other items disable once it is reached. */
123
+ max?: number;
124
+ }
125
+ /** Request received by a remote option source. */
126
+ export interface DashboardRemoteOptionsRequest {
127
+ /** Current debounced search term. Empty when the menu lists unfiltered options. */
128
+ search: string;
129
+ /** Requested page. */
130
+ page: RemoteOptionsPageRequest;
131
+ }
132
+ /**
133
+ * What a remote source returns for one request: a query definition (the dashboard runs its
134
+ * `queryFn`, e.g. a Tuyau / TanStack `queryOptions()` object), or a promise.
135
+ */
136
+ export type DashboardRemoteResult<TResult> = QueryDefinition<TResult> | Promise<TResult>;
137
+ /**
138
+ * Remote option source, following the same contract as the form engine and table filters:
139
+ * server-side search, page or cursor pagination, and hydration of selected values that the loaded
140
+ * pages do not contain (for example ids restored from the URL). `remoteTableOptions()` builds one
141
+ * for any endpoint that speaks the table request protocol.
142
+ */
143
+ export interface DashboardRemoteOptionsConfig {
144
+ /** Loads one page of options for the current search term. */
145
+ load: (request: DashboardRemoteOptionsRequest) => DashboardRemoteResult<RemoteOptionsResult<DashboardOption<string>>>;
146
+ /** Resolves labels for selected values missing from the loaded pages. */
147
+ resolveSelected?: (request: {
148
+ values: readonly string[];
149
+ }) => DashboardRemoteResult<readonly DashboardOption<string>[]>;
150
+ /** Defaults to `{ type: 'page', size: 25 }`. */
151
+ pagination?: RemoteOptionsPagination;
152
+ search?: RemoteOptionsSearch;
153
+ /** Cache identity of the option source. Defaults to a key derived from the filter location. */
154
+ queryKey?: QueryKey;
155
+ }
156
+ /** Option items of a filter: fixed, or read reactively (e.g. from another query's data). */
157
+ export type DashboardFilterItems = readonly DashboardOption[] | (() => readonly DashboardOption[]);
158
+ /** Runtime view of a filter definition, with its value type erased. */
159
+ export interface DashboardRuntimeFilter extends DashboardFilterLike {
160
+ /** Erased codec, in the shape `useQueryStates` accepts for heterogeneous schemas. */
161
+ readonly codec: StaticQueryStateOptions['codec'];
162
+ /**
163
+ * The current default: the declared value, or what the default getter returns now.
164
+ * `defaultValue` holds the unset form (the static default, or `undefined` / `[]` for a getter).
165
+ */
166
+ readonly resolveDefault: () => unknown;
167
+ readonly sync?: DashboardFilterSync<unknown>;
168
+ readonly urlKey?: string;
169
+ readonly omitDefault?: boolean;
170
+ readonly historyMode?: HistoryMode;
171
+ readonly label?: LazyTextValue;
172
+ readonly placeholder?: LazyTextValue;
173
+ readonly headless?: boolean;
174
+ readonly enabled?: DashboardCondition;
175
+ /** Text of one value. A method, so builders' typed `format` callbacks fit the erased shape. */
176
+ format?(value: unknown): string;
177
+ readonly columns?: number;
178
+ readonly searchable?: boolean;
179
+ readonly max?: number;
180
+ /** Presets, read reactively (fixed lists are normalized to getters). */
181
+ readonly presets?: () => readonly DashboardFilterPreset<unknown>[];
182
+ /** Option items of `f.enum`, `f.options`, `f.boolean`, `f.comparison`, read reactively. */
183
+ readonly items?: () => readonly DashboardOption[];
184
+ /** Remote option source of `f.remote`. */
185
+ readonly remote?: DashboardRemoteOptionsConfig;
186
+ /** Points `value` at the filter's state once its dashboard runs. */
187
+ bind(read: () => unknown): void;
188
+ }
189
+ /**
190
+ * Filter definition produced by the `f.*` builders. `defaultValue` carries the resolved value type:
191
+ * it includes `undefined` only when no default was declared.
192
+ */
193
+ export type DashboardFilterDefinition<TValue> = Omit<DashboardRuntimeFilter, 'bind' | 'codec' | 'defaultValue' | 'value'> & {
194
+ readonly codec: QueryCodec<TValue | undefined>;
195
+ readonly defaultValue: TValue;
196
+ /**
197
+ * Current value, once the dashboard runs (the default before). Read it in another filter's lazy
198
+ * options, e.g. a `format` naming the year before the selected one:
199
+ * `f.boolean({ format: (on) => (on ? String(year.value - 1) : 'None') })`.
200
+ */
201
+ readonly value: TValue;
202
+ };
203
+ /** Structural constraint every filter definition satisfies, whatever its value type. */
204
+ export interface DashboardFilterLike {
205
+ readonly kind: DashboardFilterKind;
206
+ readonly multiple: boolean;
207
+ readonly defaultValue: unknown;
208
+ readonly value: unknown;
209
+ }
210
+ /** Filters of a scope, by key. */
211
+ export type DashboardFilterMap = Record<string, DashboardFilterLike>;
212
+ /** Empty map used when a scope declares no filters, queries, or derived values. */
213
+ export type DashboardEmptyMap = Record<never, never>;
214
+ /** Writable values of a filter map. Every property is a `v-model` target. */
215
+ export type DashboardFilterValues<TFilters> = {
216
+ -readonly [K in keyof TFilters]: TFilters[K] extends {
217
+ readonly defaultValue: infer TValue;
218
+ } ? TValue : never;
219
+ };
220
+ /**
221
+ * Filters of a scope, declared inline with the `f` builder.
222
+ *
223
+ * @example
224
+ * ```ts
225
+ * filters: (f) => {
226
+ * const year = f.enum([2025, 2026], { defaultValue: 2026, label: 'Year' })
227
+ * return {
228
+ * year,
229
+ * compare: f.boolean({ format: (on) => (on ? String(year.value - 1) : 'None') }),
230
+ * }
231
+ * }
232
+ * ```
233
+ */
234
+ export type DashboardFiltersInput<TFilters> = (f: DashboardFilterBuilder) => TFilters;
235
+ /** The `f` builder handed to every `filters` callback. */
236
+ export type DashboardFilterBuilder = typeof dashboardFilterBuilder;
@@ -1,5 +1,6 @@
1
- export type * from './options';
2
- export type * from './params';
1
+ export type * from './controls';
2
+ export type * from './filters';
3
+ export type * from './page';
3
4
  export type * from './resource';
4
5
  export type * from './schema';
5
6
  export type * from './blocks';
@@ -0,0 +1,17 @@
1
+ import type { DashboardFiltersTarget } from './controls.js';
2
+ import type { DashboardViewController } from './schema.js';
3
+ /** What `UiDashboardPage` renders: the dashboard object returned by `useDashboard`. */
4
+ export interface DashboardPageTarget extends DashboardFiltersTarget {
5
+ refresh: () => Promise<void>;
6
+ readonly refreshing: boolean;
7
+ readonly updatedAt: number | undefined;
8
+ autoRefresh: number;
9
+ }
10
+ /** A dashboard with views: `UiDashboardPage` renders their tabs and the current one's slot. */
11
+ export interface DashboardViewsTarget {
12
+ readonly view: DashboardViewController<string>;
13
+ }
14
+ /** Names of a dashboard's views, which name the view slots of `UiDashboardPage`. */
15
+ export type DashboardViewKeysOf<TDashboard> = TDashboard extends {
16
+ readonly view: DashboardViewController<infer TKey>;
17
+ } ? TKey : never;
File without changes