nuxt-ui-tools 1.17.1 → 1.19.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 (178) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +1 -0
  3. package/dist/runtime/form/components/page/form-page-header.d.vue.ts +3 -1
  4. package/dist/runtime/form/components/page/form-page-header.vue +44 -34
  5. package/dist/runtime/form/components/page/form-page-header.vue.d.ts +3 -1
  6. package/dist/runtime/form/components/page/form-page-navigation.d.vue.ts +5 -3
  7. package/dist/runtime/form/components/page/form-page-navigation.vue +6 -3
  8. package/dist/runtime/form/components/page/form-page-navigation.vue.d.ts +5 -3
  9. package/dist/runtime/form/components/page/form-page-section.vue +6 -5
  10. package/dist/runtime/form/components/page/form-page-sections.d.vue.ts +4 -3
  11. package/dist/runtime/form/components/page/form-page-sections.vue +7 -2
  12. package/dist/runtime/form/components/page/form-page-sections.vue.d.ts +4 -3
  13. package/dist/runtime/form/components/page/form-page.d.vue.ts +2 -0
  14. package/dist/runtime/form/components/page/form-page.vue +11 -2
  15. package/dist/runtime/form/components/page/form-page.vue.d.ts +2 -0
  16. package/dist/runtime/form/components/renderer/form-field-renderer.vue +6 -4
  17. package/dist/runtime/form/components/renderer/form-field-shell.d.vue.ts +4 -4
  18. package/dist/runtime/form/components/renderer/form-field-shell.vue +28 -4
  19. package/dist/runtime/form/components/renderer/form-field-shell.vue.d.ts +4 -4
  20. package/dist/runtime/form/components/root/form.vue +2 -2
  21. package/dist/runtime/form/components/utils/form-option-choice-label.d.vue.ts +16 -0
  22. package/dist/runtime/form/components/utils/form-option-choice-label.vue +37 -0
  23. package/dist/runtime/form/components/utils/form-option-choice-label.vue.d.ts +16 -0
  24. package/dist/runtime/form/components/utils/form-option-display-value.d.vue.ts +14 -0
  25. package/dist/runtime/form/components/utils/form-option-display-value.vue +20 -0
  26. package/dist/runtime/form/components/utils/form-option-display-value.vue.d.ts +14 -0
  27. package/dist/runtime/form/components/utils/form-option-item-label.d.vue.ts +9 -2
  28. package/dist/runtime/form/components/utils/form-option-item-label.vue +20 -13
  29. package/dist/runtime/form/components/utils/form-option-item-label.vue.d.ts +9 -2
  30. package/dist/runtime/form/components/utils/form-option-leading.d.vue.ts +23 -0
  31. package/dist/runtime/form/components/utils/form-option-leading.vue +41 -0
  32. package/dist/runtime/form/components/utils/form-option-leading.vue.d.ts +23 -0
  33. package/dist/runtime/form/components/utils/form-option-render.d.ts +11 -0
  34. package/dist/runtime/form/components/utils/form-option-render.js +5 -0
  35. package/dist/runtime/form/components/utils/form-option-suffix.d.vue.ts +8 -0
  36. package/dist/runtime/form/components/utils/form-option-suffix.vue +15 -0
  37. package/dist/runtime/form/components/utils/form-option-suffix.vue.d.ts +8 -0
  38. package/dist/runtime/form/components/utils/form-option-value-overlay.d.vue.ts +30 -0
  39. package/dist/runtime/form/components/utils/form-option-value-overlay.vue +44 -0
  40. package/dist/runtime/form/components/utils/form-option-value-overlay.vue.d.ts +30 -0
  41. package/dist/runtime/form/composables/use-choice-group-items.d.ts +27 -0
  42. package/dist/runtime/form/composables/use-choice-group-items.js +24 -0
  43. package/dist/runtime/form/composables/use-field-control.d.ts +6 -6
  44. package/dist/runtime/form/composables/use-field-control.js +9 -21
  45. package/dist/runtime/form/composables/use-field-options.js +8 -4
  46. package/dist/runtime/form/composables/use-form-layout.js +4 -3
  47. package/dist/runtime/form/composables/use-form-page.d.ts +11 -1
  48. package/dist/runtime/form/composables/use-form-page.js +12 -6
  49. package/dist/runtime/form/composables/use-form-runtime.js +1 -1
  50. package/dist/runtime/form/composables/use-option-menu.d.ts +20 -0
  51. package/dist/runtime/form/composables/use-option-menu.js +41 -0
  52. package/dist/runtime/form/fields/array-primitive/types.d.ts +1 -1
  53. package/dist/runtime/form/fields/auto-complete/component.vue +92 -51
  54. package/dist/runtime/form/fields/auto-complete/config.d.ts +1 -0
  55. package/dist/runtime/form/fields/auto-complete/config.js +1 -1
  56. package/dist/runtime/form/fields/auto-complete/types.d.ts +2 -2
  57. package/dist/runtime/form/fields/checkbox-card/component.vue +55 -29
  58. package/dist/runtime/form/fields/checkbox-card/config.d.ts +1 -0
  59. package/dist/runtime/form/fields/checkbox-card/config.js +1 -1
  60. package/dist/runtime/form/fields/checkbox-card/types.d.ts +3 -3
  61. package/dist/runtime/form/fields/checkbox-group/component.vue +18 -4
  62. package/dist/runtime/form/fields/checkbox-group/types.d.ts +2 -2
  63. package/dist/runtime/form/fields/choice-card/choice-card-label.d.vue.ts +7 -0
  64. package/dist/runtime/form/fields/choice-card/choice-card-label.vue +27 -1
  65. package/dist/runtime/form/fields/choice-card/choice-card-label.vue.d.ts +7 -0
  66. package/dist/runtime/form/fields/choice-card/types.d.ts +35 -1
  67. package/dist/runtime/form/fields/choice-card/use-choice-card.d.ts +7 -3
  68. package/dist/runtime/form/fields/choice-card/use-choice-card.js +37 -15
  69. package/dist/runtime/form/fields/choice-card/utils.d.ts +10 -0
  70. package/dist/runtime/form/fields/choice-card/utils.js +6 -0
  71. package/dist/runtime/form/fields/hierarchy/component.vue +3 -2
  72. package/dist/runtime/form/fields/index.d.ts +4 -0
  73. package/dist/runtime/form/fields/radio/component.vue +19 -2
  74. package/dist/runtime/form/fields/radio/types.d.ts +2 -2
  75. package/dist/runtime/form/fields/radio-card/component.vue +58 -28
  76. package/dist/runtime/form/fields/radio-card/config.d.ts +1 -0
  77. package/dist/runtime/form/fields/radio-card/config.js +1 -1
  78. package/dist/runtime/form/fields/radio-card/types.d.ts +3 -3
  79. package/dist/runtime/form/fields/select/component.vue +56 -58
  80. package/dist/runtime/form/fields/select/config.d.ts +1 -0
  81. package/dist/runtime/form/fields/select/config.js +1 -1
  82. package/dist/runtime/form/fields/select/types.d.ts +2 -2
  83. package/dist/runtime/form/types/field-base.d.ts +6 -0
  84. package/dist/runtime/form/types/field-kind.d.ts +3 -1
  85. package/dist/runtime/form/types/field.d.ts +1 -0
  86. package/dist/runtime/form/types/layout.d.ts +6 -0
  87. package/dist/runtime/form/types/options.d.ts +137 -0
  88. package/dist/runtime/form/types/page.d.ts +20 -8
  89. package/dist/runtime/form/types/ui.d.ts +29 -4
  90. package/dist/runtime/form/utils/disabled.d.ts +11 -0
  91. package/dist/runtime/form/utils/disabled.js +11 -0
  92. package/dist/runtime/form/utils/field-instance.js +3 -0
  93. package/dist/runtime/form/utils/options.d.ts +56 -11
  94. package/dist/runtime/form/utils/options.js +86 -0
  95. package/dist/runtime/form/utils/page-surface.d.ts +26 -0
  96. package/dist/runtime/form/utils/page-surface.js +28 -0
  97. package/dist/runtime/form/utils/page.d.ts +21 -1
  98. package/dist/runtime/form/utils/page.js +24 -2
  99. package/dist/runtime/i18n/locales/en.js +28 -8
  100. package/dist/runtime/i18n/locales/fr.js +28 -8
  101. package/dist/runtime/i18n/types.d.ts +46 -7
  102. package/dist/runtime/spreadsheet/components/internal/spreadsheet-import-column-row.d.vue.ts +2 -2
  103. package/dist/runtime/spreadsheet/components/internal/spreadsheet-import-column-row.vue.d.ts +2 -2
  104. package/dist/runtime/table/components/actions/row-actions.d.vue.ts +2 -0
  105. package/dist/runtime/table/components/actions/row-actions.vue +8 -3
  106. package/dist/runtime/table/components/actions/row-actions.vue.d.ts +2 -0
  107. package/dist/runtime/table/components/data-list/data-list-content.vue +7 -1
  108. package/dist/runtime/table/components/data-list/data-list-default.d.vue.ts +16 -16
  109. package/dist/runtime/table/components/data-list/data-list-default.vue +3 -0
  110. package/dist/runtime/table/components/data-list/data-list-default.vue.d.ts +16 -16
  111. package/dist/runtime/table/components/data-list/data-list-layout-switch.vue +7 -9
  112. package/dist/runtime/table/components/data-list/data-list-root.vue +5 -6
  113. package/dist/runtime/table/components/data-list/data-list-sort-menu.vue +3 -1
  114. package/dist/runtime/table/components/data-list/data-list-table.d.vue.ts +8 -1
  115. package/dist/runtime/table/components/data-list/data-list-table.vue +18 -1
  116. package/dist/runtime/table/components/data-list/data-list-table.vue.d.ts +8 -1
  117. package/dist/runtime/table/components/data-list/data-list-view-toggle.d.vue.ts +25 -0
  118. package/dist/runtime/table/components/data-list/data-list-view-toggle.vue +57 -0
  119. package/dist/runtime/table/components/data-list/data-list-view-toggle.vue.d.ts +25 -0
  120. package/dist/runtime/table/components/data-list/sort-menu-panel.vue +1 -1
  121. package/dist/runtime/table/components/filters/tags/boolean-filter-tag.d.vue.ts +2 -2
  122. package/dist/runtime/table/components/filters/tags/boolean-filter-tag.vue.d.ts +2 -2
  123. package/dist/runtime/table/components/filters/tags/date-filter-tag.d.vue.ts +2 -2
  124. package/dist/runtime/table/components/filters/tags/date-filter-tag.vue.d.ts +2 -2
  125. package/dist/runtime/table/components/filters/tags/number-filter-tag.d.vue.ts +2 -2
  126. package/dist/runtime/table/components/filters/tags/number-filter-tag.vue.d.ts +2 -2
  127. package/dist/runtime/table/components/filters/tags/option-filter-tag.d.vue.ts +2 -2
  128. package/dist/runtime/table/components/filters/tags/option-filter-tag.vue.d.ts +2 -2
  129. package/dist/runtime/table/components/filters/tags/text-filter-tag.d.vue.ts +2 -2
  130. package/dist/runtime/table/components/filters/tags/text-filter-tag.vue.d.ts +2 -2
  131. package/dist/runtime/table/components/layout/table-footer.d.vue.ts +6 -4
  132. package/dist/runtime/table/components/layout/table-footer.vue +80 -3
  133. package/dist/runtime/table/components/layout/table-footer.vue.d.ts +6 -4
  134. package/dist/runtime/table/components/summary/summary-content.d.ts +13 -0
  135. package/dist/runtime/table/components/summary/summary-content.js +5 -0
  136. package/dist/runtime/table/components/summary/summary-renderer.d.vue.ts +31 -0
  137. package/dist/runtime/table/components/summary/summary-renderer.vue +282 -0
  138. package/dist/runtime/table/components/summary/summary-renderer.vue.d.ts +31 -0
  139. package/dist/runtime/table/components/summary/summary-row.d.vue.ts +19 -0
  140. package/dist/runtime/table/components/summary/summary-row.vue +102 -0
  141. package/dist/runtime/table/components/summary/summary-row.vue.d.ts +19 -0
  142. package/dist/runtime/table/components/table/table-empty-state.vue +39 -9
  143. package/dist/runtime/table/components/table/table-renderer.vue +1 -1
  144. package/dist/runtime/table/components/table/table-row-actions-control.vue +10 -6
  145. package/dist/runtime/table/components/table/table-selection-control.d.vue.ts +5 -1
  146. package/dist/runtime/table/components/table/table-selection-control.vue +17 -4
  147. package/dist/runtime/table/components/table/table-selection-control.vue.d.ts +5 -1
  148. package/dist/runtime/table/components/table/table-tree-control.vue +2 -1
  149. package/dist/runtime/table/components/table/table-tree-header.vue +0 -1
  150. package/dist/runtime/table/composables/use-table-api.d.ts +2 -0
  151. package/dist/runtime/table/composables/use-table-api.js +8 -0
  152. package/dist/runtime/table/composables/use-table-columns.d.ts +2 -0
  153. package/dist/runtime/table/composables/use-table-controls.d.ts +6 -0
  154. package/dist/runtime/table/composables/use-table-controls.js +6 -3
  155. package/dist/runtime/table/composables/use-table-internals.d.ts +33 -0
  156. package/dist/runtime/table/composables/use-table-internals.js +9 -0
  157. package/dist/runtime/table/composables/use-table-row-labels.d.ts +16 -0
  158. package/dist/runtime/table/composables/use-table-row-labels.js +22 -0
  159. package/dist/runtime/table/composables/use-table-row-summary.d.ts +18 -0
  160. package/dist/runtime/table/composables/use-table-row-summary.js +41 -0
  161. package/dist/runtime/table/constants/mobile.d.ts +79 -0
  162. package/dist/runtime/table/constants/mobile.js +17 -0
  163. package/dist/runtime/table/constants/summary.d.ts +67 -0
  164. package/dist/runtime/table/constants/summary.js +31 -0
  165. package/dist/runtime/table/types/data-list.d.ts +58 -8
  166. package/dist/runtime/table/types/layout.d.ts +73 -0
  167. package/dist/runtime/table/types/remote-options.d.ts +4 -1
  168. package/dist/runtime/table/types/schema.d.ts +9 -1
  169. package/dist/runtime/table/types/table-api.d.ts +12 -0
  170. package/dist/runtime/table/utils/columns/defs.js +3 -1
  171. package/dist/runtime/table/utils/columns/render.d.ts +2 -0
  172. package/dist/runtime/table/utils/columns/render.js +2 -2
  173. package/dist/runtime/table/utils/data-list-ui.d.ts +1 -1
  174. package/dist/runtime/table/utils/data-list-ui.js +3 -1
  175. package/dist/runtime/table/utils/mobile-defaults.d.ts +7 -0
  176. package/dist/runtime/table/utils/mobile-defaults.js +25 -0
  177. package/dist/runtime/table/utils/remote-table-options.js +1 -1
  178. package/package.json +1 -1
@@ -1,24 +1,69 @@
1
- import type { FormValue, FormFieldCallbackParams, FormOptionItem, FormOptionValue } from '../types/index.js';
1
+ import type { FormValue, FormField, FormFieldCallbackParams, FormOptionItem, FormOptionValue, ResolvedFormOption } from '../types/index.js';
2
2
  export interface FormOptionKeys {
3
3
  value?: string;
4
4
  label?: string;
5
5
  children?: string;
6
+ /**
7
+ * Property holding the heading an option is listed under. Options are not grouped without it:
8
+ * a `group` property is never read unless it is mapped here.
9
+ */
10
+ group?: string;
6
11
  }
7
- export interface ResolvedFormOption {
8
- value: FormOptionValue;
9
- label: string;
10
- description?: string;
11
- disabled?: boolean;
12
- icon?: string;
13
- children?: readonly ResolvedFormOption[];
14
- /** True when the option has children that load on demand. */
15
- lazy?: boolean;
16
- }
12
+ export type { ResolvedFormOption } from '../types/index.js';
13
+ /** The one visual an option draws before its label: its icon, else its avatar, else its chip. */
14
+ export type OptionLeading = 'icon' | 'avatar' | 'chip';
15
+ export declare function resolveOptionLeading(option: ResolvedFormOption): OptionLeading | undefined;
16
+ export declare function resolveOptionDescriptionIndent(option: ResolvedFormOption): string | undefined;
17
17
  export declare function normalizeOptionItem(option: FormValue, keys?: FormOptionKeys): ResolvedFormOption;
18
18
  export declare function normalizeOptionItems(options: readonly FormValue[] | undefined, keys?: FormOptionKeys): ResolvedFormOption[];
19
+ /**
20
+ * The `options` of a field as authored: its array, function or query source, whether given
21
+ * directly or inside a `source` property.
22
+ */
23
+ export declare function readOptionSource(field: FormField): FormValue;
24
+ /**
25
+ * Property the options of a field hold their heading in, for the field kinds that list options
26
+ * under headings. The field's `groupKey` names it, read statically from the definition like
27
+ * `labelKey` and `valueKey`. Without it, options written inline in the schema group by their
28
+ * `group`, the way a literal option is authored. The records a function, a promise or a query
29
+ * returns belong to the author's data, so a `group` they happen to carry is not read until
30
+ * `groupKey` maps it. `groupKey: false` opts out.
31
+ */
32
+ export declare function resolveOptionGroupKey(field: FormField): string | undefined;
33
+ export interface OptionGroup<TItem> {
34
+ /** Stable key of the group: its heading, or an empty string for the options without one. */
35
+ key: string;
36
+ /** Heading shown above the options of the group; none for options that declare no group. */
37
+ heading?: string;
38
+ /** The group's options, as the mutable list the Nuxt UI groups take. */
39
+ items: TItem[];
40
+ }
41
+ /**
42
+ * Lists options under their group headings. Headings follow the order in which they first appear
43
+ * in `items`, and each keeps its options in their authored order. The options without a group form
44
+ * one more group with no heading, placed where its first option appears. Without any group the
45
+ * result is a single headingless group, so an ungrouped list renders as it always did.
46
+ */
47
+ export declare function groupOptions<TItem extends {
48
+ group?: string;
49
+ }>(items: readonly TItem[]): readonly OptionGroup<TItem>[];
19
50
  export declare const LOAD_MORE_OPTION_VALUE = "__nut:load-more__";
20
51
  export declare function isLoadMoreOption(item: FormValue): boolean;
21
52
  export declare function appendLoadMoreOption(items: readonly ResolvedFormOption[], show: boolean, label: string): readonly ResolvedFormOption[];
53
+ /** Item of a Nuxt UI menu: an option, or the heading (`type: 'label'`) the menu draws above a group. */
54
+ export type OptionMenuItem = ResolvedFormOption & {
55
+ type?: 'label';
56
+ };
57
+ /**
58
+ * Items for the Nuxt UI menu of a select or auto-complete: the options as one flat list, or, when
59
+ * some options belong to a group, one array per group with a heading item first. Nuxt UI draws
60
+ * each array as a group and keeps a heading only while its group still has a match for the search.
61
+ * The load-more row, when shown, is last and belongs to no group.
62
+ */
63
+ export declare function toMenuItems(options: readonly ResolvedFormOption[], loadMore: {
64
+ show: boolean;
65
+ label: string;
66
+ }): OptionMenuItem[] | OptionMenuItem[][];
22
67
  export declare function formOptionKey(value: FormOptionValue): string;
23
68
  export declare function flattenResolvedOptions(options: readonly ResolvedFormOption[]): readonly ResolvedFormOption[];
24
69
  export declare function mergeResolvedOptions(...collections: readonly (readonly ResolvedFormOption[])[]): readonly ResolvedFormOption[];
@@ -1,21 +1,55 @@
1
+ import { createFormFieldInstance } from "./field-instance.js";
1
2
  import { isRecord } from "./path.js";
2
3
  import { isBoolean, isFunction, isNumber, isString, isUndefined } from "./predicate.js";
3
4
  import { resolveFormText } from "./text.js";
5
+ const DEFAULT_GROUP_KEY = "group";
6
+ const CHIP_COLORS = [
7
+ "primary",
8
+ "secondary",
9
+ "success",
10
+ "info",
11
+ "warning",
12
+ "error",
13
+ "neutral"
14
+ ];
15
+ export function resolveOptionLeading(option) {
16
+ if (option.icon) return "icon";
17
+ if (option.avatar) return "avatar";
18
+ if (option.chip) return "chip";
19
+ return void 0;
20
+ }
21
+ const DESCRIPTION_INDENT = {
22
+ avatar: "ps-8",
23
+ chip: "ps-6",
24
+ icon: "ps-6"
25
+ };
26
+ export function resolveOptionDescriptionIndent(option) {
27
+ const leading = resolveOptionLeading(option);
28
+ return leading ? DESCRIPTION_INDENT[leading] : void 0;
29
+ }
4
30
  export function normalizeOptionItem(option, keys = {}) {
31
+ const item = isFormOptionItem(option) ? option : void 0;
5
32
  if (isRecord(option)) {
6
33
  const rawValue = readOptionProperty(option, keys.value ?? "value");
7
34
  const fallbackValue = isUndefined(rawValue) ? option.key : rawValue;
8
35
  const rawChildren = readOptionProperty(option, keys.children ?? "children");
9
36
  const rawDescription = readOptionProperty(option, "description");
37
+ const rawGroup = keys.group ? readOptionProperty(option, keys.group) : void 0;
10
38
  const children = Array.isArray(rawChildren) ? rawChildren.map((child) => normalizeOptionItem(child, keys)) : void 0;
11
39
  const lazy = option.isLeaf === false || option.hasChildren === true;
12
40
  const value = normalizeOptionValue(fallbackValue);
13
41
  const rawIcon = readOptionProperty(option, "icon");
42
+ const rawSuffix = readOptionProperty(option, "suffix");
14
43
  const normalized = {
44
+ avatar: normalizeOptionAvatar(readOptionProperty(option, "avatar")),
45
+ chip: normalizeOptionChip(readOptionProperty(option, "chip")),
15
46
  description: isFormText(rawDescription) ? resolveFormText(rawDescription) : void 0,
16
47
  disabled: option.disabled === true,
48
+ group: isFormText(rawGroup) ? resolveFormText(rawGroup) : void 0,
17
49
  icon: isString(rawIcon) ? rawIcon : void 0,
50
+ item,
18
51
  label: resolveFormText(readOptionProperty(option, keys.label ?? "label")) ?? String(value),
52
+ suffix: isFormText(rawSuffix) ? resolveFormText(rawSuffix) : void 0,
19
53
  value
20
54
  };
21
55
  if (children) {
@@ -29,13 +63,52 @@ export function normalizeOptionItem(option, keys = {}) {
29
63
  return normalized;
30
64
  }
31
65
  return {
66
+ item,
32
67
  label: String(option),
33
68
  value: normalizeOptionValue(option)
34
69
  };
35
70
  }
71
+ function isFormOptionItem(value) {
72
+ return isOptionValue(value) || isRecord(value);
73
+ }
74
+ function normalizeOptionAvatar(raw) {
75
+ if (!isRecord(raw)) return void 0;
76
+ const { alt, src, text } = raw;
77
+ return {
78
+ alt: isString(alt) ? alt : void 0,
79
+ src: isString(src) ? src : void 0,
80
+ text: isString(text) ? text : void 0
81
+ };
82
+ }
83
+ function normalizeOptionChip(raw) {
84
+ if (!isRecord(raw)) return void 0;
85
+ return { color: CHIP_COLORS.find((color) => color === raw.color) };
86
+ }
36
87
  export function normalizeOptionItems(options, keys = {}) {
37
88
  return (options ?? []).map((option) => normalizeOptionItem(option, keys));
38
89
  }
90
+ export function readOptionSource(field) {
91
+ const options = Object.getOwnPropertyDescriptor(field, "options")?.value;
92
+ return isRecord(options) && "source" in options ? options.source : options;
93
+ }
94
+ export function resolveOptionGroupKey(field) {
95
+ if (!createFormFieldInstance(field).capability.has("optionGroups")) return void 0;
96
+ const mapped = Object.getOwnPropertyDescriptor(field, "groupKey")?.value;
97
+ if (mapped === false) return void 0;
98
+ if (isString(mapped) && mapped) return mapped;
99
+ return Array.isArray(readOptionSource(field)) ? DEFAULT_GROUP_KEY : void 0;
100
+ }
101
+ export function groupOptions(items) {
102
+ const groups = /* @__PURE__ */ new Map();
103
+ for (const item of items) {
104
+ const key = item.group?.trim() ?? "";
105
+ const members = groups.get(key);
106
+ if (members) members.push(item);
107
+ else groups.set(key, [item]);
108
+ }
109
+ if (groups.size === 0) return [{ items: [], key: "" }];
110
+ return [...groups].map(([key, members]) => ({ heading: key || void 0, items: members, key }));
111
+ }
39
112
  export const LOAD_MORE_OPTION_VALUE = "__nut:load-more__";
40
113
  export function isLoadMoreOption(item) {
41
114
  return isRecord(item) && item.value === LOAD_MORE_OPTION_VALUE;
@@ -46,6 +119,19 @@ export function appendLoadMoreOption(items, show, label) {
46
119
  }
47
120
  return [...items, { disabled: true, label, value: LOAD_MORE_OPTION_VALUE }];
48
121
  }
122
+ function menuHeading(heading) {
123
+ return { label: heading, type: "label", value: "" };
124
+ }
125
+ export function toMenuItems(options, loadMore) {
126
+ const groups = groupOptions(options);
127
+ if (!groups.some((group) => group.heading)) {
128
+ return [...appendLoadMoreOption(options, loadMore.show, loadMore.label)];
129
+ }
130
+ const nested = groups.map(
131
+ (group) => group.heading ? [menuHeading(group.heading), ...group.items] : group.items
132
+ );
133
+ return loadMore.show ? [...nested, [...appendLoadMoreOption([], true, loadMore.label)]] : nested;
134
+ }
49
135
  export function formOptionKey(value) {
50
136
  const type = isString(value) ? "string" : isNumber(value) ? "number" : isBoolean(value) ? "boolean" : "unknown";
51
137
  return `${type}:${String(value)}`;
@@ -0,0 +1,26 @@
1
+ import type { FormPageSurface, FormValue } from '../types/index.js';
2
+ /**
3
+ * What a surface sets on the parts of a form page it restyles. These are the default classes of
4
+ * each slot: the `FormPageUi` slots merge over them.
5
+ */
6
+ export interface FormPageSurfaceClasses {
7
+ /** Background the sections sit on. */
8
+ root: string;
9
+ /** Gap between the sections. */
10
+ sections: string;
11
+ /** The box of a card, or the spacing, hairline, and modified marker of a plain section. */
12
+ section: string;
13
+ /** Where the description sits against the title. */
14
+ sectionDescription: string;
15
+ /** Placeholder shown while the schema context loads. */
16
+ sectionSkeleton: string;
17
+ }
18
+ /**
19
+ * The centered column of a page without a navigation. The body and the row of the header share
20
+ * it, so both start and end on the same edges, padding included.
21
+ */
22
+ export declare const FORM_PAGE_COLUMN = "mx-auto w-full max-w-[816px]";
23
+ export declare const DEFAULT_FORM_PAGE_SURFACE: FormPageSurface;
24
+ /** The surface a page draws its sections with, `card` unless the config names a known one. */
25
+ export declare function resolveFormPageSurface(value: FormValue): FormPageSurface;
26
+ export declare function getFormPageSurfaceClasses(surface: FormPageSurface): FormPageSurfaceClasses;
@@ -0,0 +1,28 @@
1
+ import { isString } from "./predicate.js";
2
+ export const FORM_PAGE_COLUMN = "mx-auto w-full max-w-[816px]";
3
+ export const DEFAULT_FORM_PAGE_SURFACE = "card";
4
+ const FORM_PAGE_SURFACES = {
5
+ card: {
6
+ root: "bg-muted",
7
+ section: "gap-[18px] rounded-xl border border-default bg-default px-5 pt-5 pb-6 transition-[border-color,box-shadow] duration-200 @3xl/form-page:px-6 @3xl/form-page:pt-[22px] data-[dirty]:border-primary data-[dirty]:ring-4 data-[dirty]:ring-primary/20 data-[dirty]:ring-inset",
8
+ sectionDescription: "order-last basis-full @3xl/form-page:order-none @3xl/form-page:basis-auto",
9
+ sectionSkeleton: "rounded-xl border border-default bg-default px-6 pt-[22px] pb-6",
10
+ sections: "gap-[18px]"
11
+ },
12
+ plain: {
13
+ root: "bg-default",
14
+ section: "relative gap-[18px] border-t border-default py-8 first:border-t-0 first:pt-0 last:pb-0 before:pointer-events-none before:absolute before:inset-y-8 before:-start-3 before:w-0.5 before:rounded-full before:transition-colors first:before:top-0 last:before:bottom-0 data-[dirty]:before:bg-primary",
15
+ sectionDescription: "order-last basis-full",
16
+ sectionSkeleton: "border-t border-default py-8 first:border-t-0 first:pt-0",
17
+ sections: "gap-0"
18
+ }
19
+ };
20
+ export function resolveFormPageSurface(value) {
21
+ return isFormPageSurface(value) ? value : DEFAULT_FORM_PAGE_SURFACE;
22
+ }
23
+ export function getFormPageSurfaceClasses(surface) {
24
+ return FORM_PAGE_SURFACES[surface];
25
+ }
26
+ function isFormPageSurface(value) {
27
+ return isString(value) && Object.hasOwn(FORM_PAGE_SURFACES, value);
28
+ }
@@ -1,10 +1,30 @@
1
- import type { FormObject, FormPageSectionEntry, FormPageSectionState, FormRuntime, FormValue } from '../types/index.js';
1
+ import type { FormObject, FormPageSectionEntry, FormPageSectionState, FormPageSectionStatus, FormRuntime, FormValue } from '../types/index.js';
2
2
  /** Pairs each section of a page schema with the card `defineFormPageSchema` generated for it. */
3
3
  export declare function getFormPageSections(schema: FormValue): readonly FormPageSectionEntry[];
4
4
  /** Resolved heading of the page navigation. */
5
5
  export declare function getFormPageNavigationTitle(schema: FormValue): string | undefined;
6
+ /**
7
+ * Number of visible sections the navigation needs to show. Zero, the default, shows it whatever
8
+ * the number of sections; anything that is not a count above one means the same.
9
+ */
10
+ export declare function getFormPageNavigationMinSections(schema: FormValue): number;
6
11
  /** True while the section's `condition` lets it render. */
7
12
  export declare function isFormPageSectionVisible(entry: FormPageSectionEntry, runtime: FormRuntime): boolean;
13
+ /**
14
+ * Where a section stands, from what it shows and what it still misses. An optional section nothing
15
+ * was entered in is `optional`, not `pending`: the user has nothing to do there.
16
+ */
17
+ export declare function resolveFormPageSectionStatus(params: {
18
+ /** The section shows a validation error, which needs the user to leave a field or submit. */
19
+ invalid: boolean;
20
+ /** Number of required fields without a value. */
21
+ missing: number;
22
+ optional: boolean;
23
+ /** The user entered a value, or the form input brought one. Only read for an optional section. */
24
+ provided: boolean;
25
+ }): FormPageSectionStatus;
26
+ /** True while the section keeps the form from being submitted: the sections left to complete. */
27
+ export declare function isFormPageSectionBlocking(section: Pick<FormPageSectionState, 'status'>): boolean;
8
28
  /**
9
29
  * Live state of one visible section: what it still misses, whether it shows errors, and what
10
30
  * changed since the baseline.
@@ -13,6 +13,12 @@ import {
13
13
  resolveRequired
14
14
  } from "./state.js";
15
15
  import { resolveFormText } from "./text.js";
16
+ const BLOCKS_SUBMIT = {
17
+ complete: false,
18
+ invalid: true,
19
+ optional: false,
20
+ pending: true
21
+ };
16
22
  export function getFormPageSections(schema) {
17
23
  if (!isRecord(schema) || !Array.isArray(schema.sections)) {
18
24
  return [];
@@ -30,9 +36,25 @@ export function getFormPageNavigationTitle(schema) {
30
36
  const { title } = schema.navigation;
31
37
  return isFormText(title) ? resolveFormText(title) : void 0;
32
38
  }
39
+ export function getFormPageNavigationMinSections(schema) {
40
+ if (!isRecord(schema) || !isRecord(schema.navigation)) {
41
+ return 0;
42
+ }
43
+ const { minSections } = schema.navigation;
44
+ return isNumber(minSections) && minSections > 1 ? Math.trunc(minSections) : 0;
45
+ }
33
46
  export function isFormPageSectionVisible(entry, runtime) {
34
47
  return !hasCondition(entry.card) || runtime.shouldRender(entry.card, [entry.card.key]);
35
48
  }
49
+ export function resolveFormPageSectionStatus(params) {
50
+ if (params.invalid) return "invalid";
51
+ if (params.missing > 0) return "pending";
52
+ if (params.optional && !params.provided) return "optional";
53
+ return "complete";
54
+ }
55
+ export function isFormPageSectionBlocking(section) {
56
+ return BLOCKS_SUBMIT[section.status];
57
+ }
36
58
  export function resolveFormPageSectionState(params) {
37
59
  const { entry, runtime } = params;
38
60
  const leaves = collectVisibleLeaves(entry.section.fields, [], runtime);
@@ -43,7 +65,7 @@ export function resolveFormPageSectionState(params) {
43
65
  const required = params.requiredEnforced ? leaves.filter((leaf) => isRequiredLeaf(leaf, runtime)) : [];
44
66
  const missing = required.filter((leaf) => isEmptyValue(runtime.getValue(leaf.path))).length;
45
67
  const optional = entry.section.optional ?? required.every((leaf) => leaf.item);
46
- const filled = !optional || leaves.some((leaf) => isProvided(leaf, runtime, dirtyPaths, params.input));
68
+ const provided = optional && leaves.some((leaf) => isProvided(leaf, runtime, dirtyPaths, params.input));
47
69
  return {
48
70
  description: resolveFormText(entry.section.description),
49
71
  dirty: dirtyPaths.length > 0,
@@ -53,7 +75,7 @@ export function resolveFormPageSectionState(params) {
53
75
  label: resolveFormText(entry.section.label) ?? entry.section.key,
54
76
  missing,
55
77
  optional,
56
- status: invalid ? "invalid" : missing === 0 && filled ? "complete" : "pending"
78
+ status: resolveFormPageSectionStatus({ invalid, missing, optional, provided })
57
79
  };
58
80
  }
59
81
  function collectVisibleLeaves(fields, parentPath, runtime, item = false) {
@@ -198,6 +198,7 @@ export default defineUiToolsLocale({
198
198
  },
199
199
  actions: {
200
200
  cancelButton: "Cancel",
201
+ closeButton: "Close form",
201
202
  nextButton: "Next",
202
203
  prevButton: "Back",
203
204
  resetButton: "Reset",
@@ -208,14 +209,14 @@ export default defineUiToolsLocale({
208
209
  modifiedOne: "{count} modified.",
209
210
  modifiedOther: "{count} modified.",
210
211
  navigation: "Form sections",
211
- optional: "optional",
212
212
  remaining: "{count} left to complete.",
213
213
  resetSection: "Reset",
214
214
  sectionsOne: "{count} section",
215
215
  sectionsOther: "{count} sections",
216
216
  status: {
217
- complete: "Complete",
218
- invalid: "Has errors",
217
+ complete: "Completed",
218
+ invalid: "To fix",
219
+ optional: "Optional",
219
220
  pending: "To complete"
220
221
  },
221
222
  unmodified: "No unsaved changes.",
@@ -284,9 +285,13 @@ export default defineUiToolsLocale({
284
285
  rating: {
285
286
  value: "{value} of {max}"
286
287
  },
288
+ shell: {
289
+ collapse: "Collapse",
290
+ expand: "Expand",
291
+ reset: "Reset field"
292
+ },
287
293
  text: {
288
- clear: "Clear",
289
- defaultPlaceholder: "Enter a value"
294
+ clear: "Clear"
290
295
  },
291
296
  time: {
292
297
  clear: "Clear time",
@@ -294,6 +299,10 @@ export default defineUiToolsLocale({
294
299
  hour: "Hour",
295
300
  minute: "Minute"
296
301
  },
302
+ unsupported: {
303
+ description: 'The "{type}" field type cannot be displayed.',
304
+ title: "Unsupported field"
305
+ },
297
306
  upload: {
298
307
  cancel: "Cancel upload",
299
308
  failed: "Upload failed.",
@@ -314,6 +323,9 @@ export default defineUiToolsLocale({
314
323
  title: "Form data unavailable"
315
324
  }
316
325
  },
326
+ steps: {
327
+ fallback: "Step {number}"
328
+ },
317
329
  validation: {
318
330
  dateMax: "The date must be on or before {max}",
319
331
  dateMin: "The date must be on or after {min}",
@@ -692,8 +704,18 @@ export default defineUiToolsLocale({
692
704
  header: {
693
705
  gridView: "Grid view",
694
706
  refreshData: "Refresh data",
707
+ summaryView: "List view",
695
708
  tableView: "Table view"
696
709
  },
710
+ rowActions: {
711
+ label: "Actions for {label}",
712
+ labelAt: "Actions for row {position}"
713
+ },
714
+ selection: {
715
+ selectAll: "Select all rows",
716
+ selectRow: "Select {label}",
717
+ selectRowAt: "Select row {position}"
718
+ },
697
719
  selectionBar: {
698
720
  allResults: "All results",
699
721
  clear: "Clear selection",
@@ -732,9 +754,7 @@ export default defineUiToolsLocale({
732
754
  collapse: "Collapse row",
733
755
  collapseAll: "Collapse all rows",
734
756
  expand: "Expand row",
735
- expandAll: "Expand all rows",
736
- selectAll: "Select all rows",
737
- selectRow: "Select row"
757
+ expandAll: "Expand all rows"
738
758
  }
739
759
  }
740
760
  },
@@ -198,6 +198,7 @@ export default defineUiToolsLocale({
198
198
  },
199
199
  actions: {
200
200
  cancelButton: "Annuler",
201
+ closeButton: "Fermer le formulaire",
201
202
  nextButton: "Suivant",
202
203
  prevButton: "Pr\xE9c\xE9dent",
203
204
  resetButton: "R\xE9initialiser",
@@ -208,14 +209,14 @@ export default defineUiToolsLocale({
208
209
  modifiedOne: "{count} modifi\xE9e.",
209
210
  modifiedOther: "{count} modifi\xE9es.",
210
211
  navigation: "Sections du formulaire",
211
- optional: "optionnel",
212
212
  remaining: "{count} \xE0 compl\xE9ter.",
213
213
  resetSection: "R\xE9initialiser",
214
214
  sectionsOne: "{count} section",
215
215
  sectionsOther: "{count} sections",
216
216
  status: {
217
- complete: "Compl\xE8te",
218
- invalid: "Contient des erreurs",
217
+ complete: "Compl\xE9t\xE9",
218
+ invalid: "\xC0 corriger",
219
+ optional: "Facultatif",
219
220
  pending: "\xC0 compl\xE9ter"
220
221
  },
221
222
  unmodified: "Aucune modification en cours.",
@@ -284,9 +285,13 @@ export default defineUiToolsLocale({
284
285
  rating: {
285
286
  value: "{value} sur {max}"
286
287
  },
288
+ shell: {
289
+ collapse: "Replier",
290
+ expand: "D\xE9plier",
291
+ reset: "R\xE9initialiser le champ"
292
+ },
287
293
  text: {
288
- clear: "Effacer",
289
- defaultPlaceholder: "Saisissez une valeur"
294
+ clear: "Effacer"
290
295
  },
291
296
  time: {
292
297
  clear: "Effacer l\u2019heure",
@@ -294,6 +299,10 @@ export default defineUiToolsLocale({
294
299
  hour: "Heure",
295
300
  minute: "Minute"
296
301
  },
302
+ unsupported: {
303
+ description: "Le type de champ \xAB {type} \xBB ne peut pas \xEAtre affich\xE9.",
304
+ title: "Champ non pris en charge"
305
+ },
297
306
  upload: {
298
307
  cancel: "Annuler le t\xE9l\xE9versement",
299
308
  failed: "\xC9chec du t\xE9l\xE9versement.",
@@ -314,6 +323,9 @@ export default defineUiToolsLocale({
314
323
  title: "Donn\xE9es du formulaire indisponibles"
315
324
  }
316
325
  },
326
+ steps: {
327
+ fallback: "\xC9tape {number}"
328
+ },
317
329
  validation: {
318
330
  dateMax: "La date doit \xEAtre au plus tard le {max}",
319
331
  dateMin: "La date doit \xEAtre au plus t\xF4t le {min}",
@@ -692,8 +704,18 @@ export default defineUiToolsLocale({
692
704
  header: {
693
705
  gridView: "Vue grille",
694
706
  refreshData: "Actualiser les donn\xE9es",
707
+ summaryView: "Vue liste",
695
708
  tableView: "Vue tableau"
696
709
  },
710
+ rowActions: {
711
+ label: "Actions pour {label}",
712
+ labelAt: "Actions pour la ligne {position}"
713
+ },
714
+ selection: {
715
+ selectAll: "S\xE9lectionner toutes les lignes",
716
+ selectRow: "S\xE9lectionner {label}",
717
+ selectRowAt: "S\xE9lectionner la ligne {position}"
718
+ },
697
719
  selectionBar: {
698
720
  allResults: "Tous les r\xE9sultats",
699
721
  clear: "Effacer la s\xE9lection",
@@ -732,9 +754,7 @@ export default defineUiToolsLocale({
732
754
  collapse: "Replier la ligne",
733
755
  collapseAll: "Replier toutes les lignes",
734
756
  expand: "D\xE9plier la ligne",
735
- expandAll: "D\xE9plier toutes les lignes",
736
- selectAll: "S\xE9lectionner toutes les lignes",
737
- selectRow: "S\xE9lectionner la ligne"
757
+ expandAll: "D\xE9plier toutes les lignes"
738
758
  }
739
759
  }
740
760
  },
@@ -2,8 +2,11 @@ import type { DeepPartial } from '#ui-tools/shared/types/utils';
2
2
  export interface UiToolsTableMessages {
3
3
  header: {
4
4
  refreshData: string;
5
+ /** Names the table layout, and the toggle that leaves summary rows for the table. */
5
6
  tableView: string;
6
7
  gridView: string;
8
+ /** Toggle that brings summary rows back from the table. */
9
+ summaryView: string;
7
10
  };
8
11
  footer: {
9
12
  rowsSelected: string;
@@ -12,6 +15,7 @@ export interface UiToolsTableMessages {
12
15
  range: string;
13
16
  rangeEmpty: string;
14
17
  pageOf: string;
18
+ /** Accessible name of a page button: "Page {current} of {total}". */
15
19
  page: string;
16
20
  pageSizeOption: string;
17
21
  previousPage: string;
@@ -19,6 +23,21 @@ export interface UiToolsTableMessages {
19
23
  firstPage: string;
20
24
  lastPage: string;
21
25
  };
26
+ /** Accessible names of the selection checkboxes. */
27
+ selection: {
28
+ selectAll: string;
29
+ /** A record with a label: "Select {label}". */
30
+ selectRow: string;
31
+ /** A record without one, by its position on the page: "Select row {position}". */
32
+ selectRowAt: string;
33
+ };
34
+ /** Accessible names of a row's actions menu. */
35
+ rowActions: {
36
+ /** A record with a label: "Actions for {label}". */
37
+ label: string;
38
+ /** A record without one, by its position on the page: "Actions for row {position}". */
39
+ labelAt: string;
40
+ };
22
41
  controls: {
23
42
  addFilter: string;
24
43
  clearSearch: string;
@@ -55,8 +74,6 @@ export interface UiToolsTableMessages {
55
74
  collapse: string;
56
75
  expandAll: string;
57
76
  collapseAll: string;
58
- selectRow: string;
59
- selectAll: string;
60
77
  };
61
78
  columnsMenu: {
62
79
  sortAsc: string;
@@ -429,13 +446,13 @@ export interface UiToolsFormMessages {
429
446
  submitButton: string;
430
447
  cancelButton: string;
431
448
  resetButton: string;
449
+ /** Accessible name of the close button of a modal, drawer, or fullscreen form. */
450
+ closeButton: string;
432
451
  };
433
452
  /** Form page chrome. Counts use `Intl.PluralRules`: `One` for "one", `Other` otherwise. */
434
453
  page: {
435
454
  /** Accessible name of the section navigation when the schema gives it no title. */
436
455
  navigation: string;
437
- /** Marks an optional section, in the navigation and on its card. */
438
- optional: string;
439
456
  /** Badge under the page title and name of a section's dirty marker. */
440
457
  unsavedChanges: string;
441
458
  /** Button of a modified section that puts its values back. */
@@ -452,11 +469,19 @@ export interface UiToolsFormMessages {
452
469
  modifiedOther: string;
453
470
  /** Navigation summary of a form with dirty checking and no change. */
454
471
  unmodified: string;
455
- /** Accessible status of a navigation entry. */
472
+ /**
473
+ * Where a section stands: the accessible status of a navigation entry, and the tag that marks
474
+ * an optional section nothing was entered in (`optional`), in the navigation and on its card.
475
+ */
456
476
  status: {
477
+ /** Nothing required is missing and no field shows an error. */
457
478
  complete: string;
458
- pending: string;
479
+ /** A field shows an error, once the user left it or tried to submit. */
459
480
  invalid: string;
481
+ /** An optional section nobody entered a value in. */
482
+ optional: string;
483
+ /** A required field has no value yet. */
484
+ pending: string;
460
485
  };
461
486
  };
462
487
  states: {
@@ -466,6 +491,10 @@ export interface UiToolsFormMessages {
466
491
  action: string;
467
492
  };
468
493
  };
494
+ steps: {
495
+ /** Name of a step that declares no title: "Step {number}". */
496
+ fallback: string;
497
+ };
469
498
  validation: {
470
499
  required: string;
471
500
  dateMin: string;
@@ -473,7 +502,6 @@ export interface UiToolsFormMessages {
473
502
  };
474
503
  fields: {
475
504
  text: {
476
- defaultPlaceholder: string;
477
505
  clear: string;
478
506
  };
479
507
  description: {
@@ -562,6 +590,17 @@ export interface UiToolsFormMessages {
562
590
  number: string;
563
591
  clear: string;
564
592
  };
593
+ /** Chrome around a stateful field: its collapsible body and its dirty-reset button. */
594
+ shell: {
595
+ collapse: string;
596
+ expand: string;
597
+ reset: string;
598
+ };
599
+ /** Fallback for a field kind that has no renderer: "{type}" is the kind. */
600
+ unsupported: {
601
+ title: string;
602
+ description: string;
603
+ };
565
604
  };
566
605
  }
567
606
  export interface UiToolsDashboardMessages {
@@ -6,12 +6,12 @@ type __VLS_Props = {
6
6
  };
7
7
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
8
8
  reset: () => any;
9
- assign: (header: number) => any;
10
9
  ignore: () => any;
10
+ assign: (header: number) => any;
11
11
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
12
12
  onReset?: (() => any) | undefined;
13
- onAssign?: ((header: number) => any) | undefined;
14
13
  onIgnore?: (() => any) | undefined;
14
+ onAssign?: ((header: number) => any) | undefined;
15
15
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
16
16
  declare const _default: typeof __VLS_export;
17
17
  export default _default;