@xleddyl/443-components-vue 0.2.3 → 0.4.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 (236) hide show
  1. package/README.md +106 -19
  2. package/dist/chart/BarChart.vue.d.ts +2 -2
  3. package/dist/chart/ChartLegend.vue.d.ts +4 -4
  4. package/dist/chart/ChartPanel.vue.d.ts +2 -2
  5. package/dist/chart/ChartSummary.vue.d.ts +2 -2
  6. package/dist/chart/DoughnutChart.vue.d.ts +2 -2
  7. package/dist/chart/LineChart.vue.d.ts +2 -2
  8. package/dist/components/core/Button.vue.d.ts +2 -2
  9. package/dist/components/core/FieldBoundary.vue +14 -0
  10. package/dist/components/core/FieldBoundary.vue.d.ts +15 -0
  11. package/dist/components/core/FieldMessage.vue +28 -0
  12. package/dist/components/core/FieldMessage.vue.d.ts +409 -0
  13. package/dist/components/core/Form.vue +109 -11
  14. package/dist/components/core/Form.vue.d.ts +45 -6
  15. package/dist/components/core/FormAlerts.vue +30 -0
  16. package/dist/components/core/FormAlerts.vue.d.ts +393 -0
  17. package/dist/components/core/FormField.vue +54 -17
  18. package/dist/components/core/FormField.vue.d.ts +5 -4
  19. package/dist/components/core/Icon.vue.d.ts +2 -2
  20. package/dist/components/core/Input.vue.d.ts +4 -4
  21. package/dist/components/core/Kbd.vue.d.ts +2 -2
  22. package/dist/components/core/Root.vue.d.ts +6 -6
  23. package/dist/components/core/Spinner.vue +13 -1
  24. package/dist/components/core/Spinner.vue.d.ts +4 -2
  25. package/dist/components/core/Textarea.vue.d.ts +4 -4
  26. package/dist/components/core/VisuallyHidden.vue.d.ts +6 -6
  27. package/dist/components/core/index.d.ts +3 -0
  28. package/dist/components/core/index.mjs +3 -0
  29. package/dist/components/data/Avatar.vue.d.ts +2 -2
  30. package/dist/components/data/AvatarGroup.vue.d.ts +2 -2
  31. package/dist/components/data/Badge.vue.d.ts +2 -2
  32. package/dist/components/data/DescriptionList.vue.d.ts +2 -2
  33. package/dist/components/data/FilterPill.vue +35 -0
  34. package/dist/components/data/FilterPill.vue.d.ts +445 -0
  35. package/dist/components/data/FilterPills.vue +100 -27
  36. package/dist/components/data/FilterPills.vue.d.ts +47 -411
  37. package/dist/components/data/Pagination.vue.d.ts +4 -4
  38. package/dist/components/data/SelectionBar.vue +81 -0
  39. package/dist/components/data/SelectionBar.vue.d.ts +427 -0
  40. package/dist/components/data/Stat.vue.d.ts +8 -8
  41. package/dist/components/data/Tag.vue.d.ts +4 -4
  42. package/dist/components/data/Timeline.vue.d.ts +2 -2
  43. package/dist/components/data/Tree.vue +245 -21
  44. package/dist/components/data/Tree.vue.d.ts +40 -10
  45. package/dist/components/data/index.d.ts +3 -0
  46. package/dist/components/data/index.mjs +3 -0
  47. package/dist/components/data/types.d.ts +26 -2
  48. package/dist/components/feedback/Alert.vue.d.ts +18 -18
  49. package/dist/components/feedback/EmptyState.vue.d.ts +2 -2
  50. package/dist/components/feedback/LoadingOverlay.vue.d.ts +2 -2
  51. package/dist/components/feedback/Meter.vue.d.ts +2 -2
  52. package/dist/components/feedback/Progress.vue.d.ts +2 -2
  53. package/dist/components/feedback/ProgressRing.vue.d.ts +2 -2
  54. package/dist/components/feedback/Skeleton.vue.d.ts +3 -3
  55. package/dist/components/forms/Calendar.vue.d.ts +4 -4
  56. package/dist/components/forms/CheckboxGroup.vue +2 -1
  57. package/dist/components/forms/ColorInput.vue.d.ts +6 -6
  58. package/dist/components/forms/DateInput.vue.d.ts +14 -14
  59. package/dist/components/forms/DatePicker.vue.d.ts +6 -6
  60. package/dist/components/forms/FileInput.vue +160 -34
  61. package/dist/components/forms/FileInput.vue.d.ts +43 -15
  62. package/dist/components/forms/InputAddon.vue.d.ts +2 -2
  63. package/dist/components/forms/InputGroup.vue.d.ts +2 -2
  64. package/dist/components/forms/NumberInput.vue.d.ts +6 -6
  65. package/dist/components/forms/PasswordInput.vue.d.ts +16 -16
  66. package/dist/components/forms/PinInput.vue.d.ts +6 -6
  67. package/dist/components/forms/Radio.vue.d.ts +2 -2
  68. package/dist/components/forms/RadioGroup.vue +2 -1
  69. package/dist/components/forms/SegmentedControl.vue +33 -8
  70. package/dist/components/forms/SegmentedControl.vue.d.ts +15 -7
  71. package/dist/components/forms/Slider.vue.d.ts +6 -6
  72. package/dist/components/forms/Switch.vue.d.ts +6 -6
  73. package/dist/components/forms/TimeInput.vue.d.ts +16 -16
  74. package/dist/components/forms/types.d.ts +17 -0
  75. package/dist/components/layout/Accordion.vue +25 -5
  76. package/dist/components/layout/Accordion.vue.d.ts +11 -8
  77. package/dist/components/layout/AppShell.vue +1 -1
  78. package/dist/components/layout/AppShell.vue.d.ts +7 -7
  79. package/dist/components/layout/Breadcrumbs.vue +28 -20
  80. package/dist/components/layout/Breadcrumbs.vue.d.ts +18 -3
  81. package/dist/components/layout/Card.vue +22 -3
  82. package/dist/components/layout/Card.vue.d.ts +10 -4
  83. package/dist/components/layout/CopyButton.vue.d.ts +4 -4
  84. package/dist/components/layout/Grid.vue.d.ts +2 -2
  85. package/dist/components/layout/NavLink.vue.d.ts +8 -8
  86. package/dist/components/layout/PageHeader.vue +10 -1
  87. package/dist/components/layout/PageHeader.vue.d.ts +17 -9
  88. package/dist/components/layout/Separator.vue.d.ts +2 -2
  89. package/dist/components/layout/Sidebar.vue.d.ts +2 -2
  90. package/dist/components/layout/SidebarSection.vue.d.ts +2 -2
  91. package/dist/components/layout/Stack.vue.d.ts +6 -6
  92. package/dist/components/layout/Tabs.vue +37 -7
  93. package/dist/components/layout/Tabs.vue.d.ts +11 -4
  94. package/dist/components/layout/ThemeSwitch.vue.d.ts +2 -2
  95. package/dist/components/layout/Toolbar.vue.d.ts +2 -2
  96. package/dist/components/layout/types.d.ts +8 -1
  97. package/dist/components/overlays/CommandPalette.vue +35 -8
  98. package/dist/components/overlays/CommandPalette.vue.d.ts +273 -497
  99. package/dist/components/overlays/ConfirmDialog.vue.d.ts +8 -6
  100. package/dist/components/overlays/ConfirmHost.vue +5 -1
  101. package/dist/components/overlays/ConfirmHost.vue.d.ts +10 -8
  102. package/dist/components/overlays/ContextMenu.vue.d.ts +4 -4
  103. package/dist/components/overlays/Drawer.vue +4 -2
  104. package/dist/components/overlays/Drawer.vue.d.ts +8 -6
  105. package/dist/components/overlays/DropdownMenu.vue.d.ts +2 -2
  106. package/dist/components/overlays/HoverCard.vue +2 -1
  107. package/dist/components/overlays/HoverCard.vue.d.ts +8 -8
  108. package/dist/components/overlays/Modal.vue +5 -3
  109. package/dist/components/overlays/Modal.vue.d.ts +8 -6
  110. package/dist/components/overlays/Popover.vue +2 -1
  111. package/dist/components/overlays/Popover.vue.d.ts +8 -8
  112. package/dist/components/overlays/Toaster.vue.d.ts +4 -4
  113. package/dist/components/overlays/Tooltip.vue.d.ts +2 -2
  114. package/dist/components/overlays/types.d.ts +11 -5
  115. package/dist/components/templates/AccountMenu.vue.d.ts +6 -6
  116. package/dist/components/templates/AppearanceSettings.vue.d.ts +2 -2
  117. package/dist/components/templates/AuthLayout.vue.d.ts +2 -2
  118. package/dist/components/templates/Brand.vue +34 -5
  119. package/dist/components/templates/Brand.vue.d.ts +20 -15
  120. package/dist/components/templates/DashboardLayout.vue +49 -10
  121. package/dist/components/templates/DashboardLayout.vue.d.ts +332 -553
  122. package/dist/components/templates/DashboardPage.vue.d.ts +2 -2
  123. package/dist/components/templates/DetailPage.vue.d.ts +4 -4
  124. package/dist/components/templates/EmptyPage.vue.d.ts +2 -2
  125. package/dist/components/templates/ErrorPage.vue.d.ts +2 -2
  126. package/dist/components/templates/ListPage.vue +17 -11
  127. package/dist/components/templates/ListPage.vue.d.ts +3 -3
  128. package/dist/components/templates/NotFoundPage.vue.d.ts +1 -1
  129. package/dist/components/templates/SaveBar.vue.d.ts +4 -4
  130. package/dist/components/templates/SettingsGroup.vue.d.ts +2 -2
  131. package/dist/components/templates/SettingsModal.vue +5 -2
  132. package/dist/components/templates/SettingsModal.vue.d.ts +13 -8
  133. package/dist/components/templates/SettingsPage.vue.d.ts +4 -4
  134. package/dist/components/templates/SettingsSection.vue.d.ts +12 -8
  135. package/dist/components/templates/types.d.ts +25 -2
  136. package/dist/composables/core.d.ts +1 -0
  137. package/dist/composables/core.mjs +1 -0
  138. package/dist/composables/useField.d.ts +8 -1
  139. package/dist/composables/useField.mjs +41 -10
  140. package/dist/composables/useFieldBoundary.d.ts +2 -0
  141. package/dist/composables/useFieldBoundary.mjs +9 -0
  142. package/dist/composables/useFileDrop.d.ts +20 -0
  143. package/dist/composables/useFileDrop.mjs +115 -7
  144. package/dist/composables/useForm.d.ts +36 -19
  145. package/dist/composables/useForm.mjs +258 -51
  146. package/dist/editor/RichTextEditor.vue +32 -4
  147. package/dist/editor/RichTextEditor.vue.d.ts +11 -6
  148. package/dist/editor/rich-text-editor.css +1 -1
  149. package/dist/editor/types.d.ts +3 -0
  150. package/dist/grid/GridToolbar.vue.d.ts +4 -4
  151. package/dist/grid/cells/BadgeCell.vue.d.ts +2 -2
  152. package/dist/grid/cells/BooleanCell.vue.d.ts +2 -2
  153. package/dist/grid/cells/DateCell.vue.d.ts +2 -2
  154. package/dist/grid/cells/ImageCell.vue.d.ts +2 -2
  155. package/dist/grid/cells/LinkCell.vue.d.ts +2 -2
  156. package/dist/internal/core/ButtonElement.d.ts +2 -0
  157. package/dist/internal/core/ButtonElement.mjs +8 -0
  158. package/dist/internal/data/tree.d.ts +8 -0
  159. package/dist/internal/data/tree.mjs +41 -0
  160. package/dist/internal/forms/files.d.ts +9 -0
  161. package/dist/internal/forms/files.mjs +32 -1
  162. package/dist/internal/forms/standard-schema.d.ts +17 -0
  163. package/dist/internal/forms/standard-schema.mjs +40 -0
  164. package/dist/internal/keys.d.ts +2 -1
  165. package/dist/internal/keys.mjs +1 -0
  166. package/dist/internal/overlays/DialogFrame.vue +17 -7
  167. package/dist/internal/overlays/DialogFrame.vue.d.ts +9 -5
  168. package/dist/internal/overlays/MenuList.vue +2 -0
  169. package/dist/internal/overlays/MenuList.vue.d.ts +5 -5
  170. package/dist/internal/overlays/OverlayView.vue +2 -0
  171. package/dist/internal/overlays/OverlayView.vue.d.ts +14 -12
  172. package/dist/internal/overlays/dialog.d.ts +4 -1
  173. package/dist/internal/overlays/dialog.mjs +39 -5
  174. package/dist/internal/overlays/stack.d.ts +2 -1
  175. package/dist/locale/en.mjs +19 -2
  176. package/dist/locale/it.mjs +19 -2
  177. package/dist/locale/types.d.ts +17 -0
  178. package/dist/nuxt/module.d.ts +1 -0
  179. package/dist/nuxt/module.mjs +6 -3
  180. package/dist/nuxt/runtime/layouts/auth.vue.d.ts +8 -8
  181. package/dist/nuxt/runtime/layouts/dashboard.vue.d.ts +8 -8
  182. package/dist/styles/base.css +2 -0
  183. package/dist/styles/core/button.css +1 -13
  184. package/dist/styles/core/field-message.css +34 -0
  185. package/dist/styles/core/form-field.css +0 -23
  186. package/dist/styles/core/form.css +11 -0
  187. package/dist/styles/core/kbd.css +2 -2
  188. package/dist/styles/core/spinner.css +19 -0
  189. package/dist/styles/core.css +1 -0
  190. package/dist/styles/data/avatar.css +1 -1
  191. package/dist/styles/data/badge.css +1 -1
  192. package/dist/styles/data/filter-pills.css +40 -4
  193. package/dist/styles/data/pagination.css +0 -1
  194. package/dist/styles/data/selection-bar.css +89 -0
  195. package/dist/styles/data/stat.css +1 -6
  196. package/dist/styles/data/table.css +1 -3
  197. package/dist/styles/data/tag.css +1 -4
  198. package/dist/styles/data/tree.css +163 -2
  199. package/dist/styles/data.css +1 -0
  200. package/dist/styles/feedback/alert.css +0 -1
  201. package/dist/styles/feedback/empty-state.css +0 -1
  202. package/dist/styles/forms/calendar.css +1 -1
  203. package/dist/styles/forms/color-input.css +1 -1
  204. package/dist/styles/forms/combobox.css +0 -1
  205. package/dist/styles/forms/date-picker.css +1 -1
  206. package/dist/styles/forms/file-input.css +17 -1
  207. package/dist/styles/forms/number-input.css +1 -1
  208. package/dist/styles/forms/password-input.css +1 -5
  209. package/dist/styles/forms/segmented.css +8 -0
  210. package/dist/styles/forms/select.css +1 -1
  211. package/dist/styles/forms/slider.css +2 -2
  212. package/dist/styles/forms/switch.css +0 -1
  213. package/dist/styles/layout/accordion.css +22 -1
  214. package/dist/styles/layout/app-shell.css +4 -0
  215. package/dist/styles/layout/breadcrumbs.css +16 -1
  216. package/dist/styles/layout/card.css +20 -6
  217. package/dist/styles/layout/copy-button.css +2 -2
  218. package/dist/styles/layout/nav-link.css +2 -3
  219. package/dist/styles/layout/sidebar-nav.css +1 -1
  220. package/dist/styles/layout/sidebar.css +2 -2
  221. package/dist/styles/layout/tabs.css +65 -3
  222. package/dist/styles/layout/theme-switch.css +1 -3
  223. package/dist/styles/overlays/command.css +21 -3
  224. package/dist/styles/overlays/confirm.css +0 -1
  225. package/dist/styles/overlays/menu.css +3 -5
  226. package/dist/styles/overlays/modal.css +38 -0
  227. package/dist/styles/overlays/toast.css +0 -1
  228. package/dist/styles/templates/auth-layout.css +0 -1
  229. package/dist/styles/templates/brand.css +15 -0
  230. package/dist/styles/templates/empty-page.css +4 -4
  231. package/dist/styles/templates/form-page.css +1 -1
  232. package/dist/styles/templates/list-page.css +2 -6
  233. package/dist/styles/templates/page.css +1 -1
  234. package/dist/styles/templates/settings-modal.css +2 -3
  235. package/dist/styles/tokens.css +1 -1
  236. package/package.json +1 -1
@@ -1,5 +1,23 @@
1
- import { computed, inject, reactive, ref, watch } from "vue";
2
- import { issuesByField, runStandardSchema } from "../internal/forms/standard-schema.mjs";
1
+ import {
2
+ computed,
3
+ inject,
4
+ isRef,
5
+ provide,
6
+ reactive,
7
+ readonly,
8
+ ref,
9
+ shallowRef,
10
+ toValue,
11
+ watch,
12
+ watchEffect
13
+ } from "vue";
14
+ import {
15
+ defaultIssueKey,
16
+ groupIssues,
17
+ isStandardSchema,
18
+ keyPath,
19
+ schemaIssues
20
+ } from "../internal/forms/standard-schema.mjs";
3
21
  import { FORM_KEY } from "../internal/keys.mjs";
4
22
  import { useLocale } from "./useLocale.mjs";
5
23
  function localeFormMessages(t) {
@@ -54,35 +72,96 @@ function measure(value) {
54
72
  if (typeof value === "string" || Array.isArray(value)) return value.length;
55
73
  return void 0;
56
74
  }
75
+ function isThenable(value) {
76
+ return typeof value?.then === "function";
77
+ }
78
+ function settle(value, apply) {
79
+ if (isThenable(value)) void value.then(apply);
80
+ else apply(value);
81
+ }
82
+ function chain(value, next) {
83
+ return isThenable(value) ? value.then(next) : next(value);
84
+ }
85
+ function isWithin(key, name) {
86
+ return key === name || key.startsWith(`${name}.`);
87
+ }
88
+ function uniqueMessages(...lists) {
89
+ return [...new Set(lists.flat().filter(Boolean))];
90
+ }
91
+ function replaceEntries(target, next, inScope = () => true) {
92
+ for (const key of Object.keys(target)) {
93
+ if (inScope(key) && !(key in next)) delete target[key];
94
+ }
95
+ for (const [key, message] of Object.entries(next)) {
96
+ if (inScope(key) && target[key] !== message) target[key] = message;
97
+ }
98
+ }
57
99
  export function useForm(options) {
58
- const { schema } = options;
59
- const names = Object.keys(schema);
100
+ const schema = options.schema ?? {};
101
+ const schemaNames = Object.keys(schema);
102
+ const external = options.values !== void 0;
60
103
  const { t } = useLocale();
61
104
  const messages = computed(() => ({ ...localeFormMessages(t), ...options.messages }));
62
105
  const validateOn = options.validateOn ?? "submit";
63
- const initialValues = () => Object.fromEntries(names.map((name) => [name, resolveInitial(schema[name])]));
64
- let baseline = initialValues();
65
- const values = reactive(cloneValue(baseline));
106
+ const issueKey = options.issueKey ?? defaultIssueKey;
107
+ const initialValues = () => Object.fromEntries(schemaNames.map((name) => [name, resolveInitial(schema[name])]));
108
+ const internalValues = external ? void 0 : reactive(initialValues());
109
+ const target = () => external ? toValue(options.values) : internalValues;
110
+ let baseline = cloneValue(target());
111
+ const baselineVersion = ref(0);
112
+ const names = computed(() => {
113
+ if (!external) return schemaNames;
114
+ return [.../* @__PURE__ */ new Set([...schemaNames, ...Object.keys(target())])];
115
+ });
66
116
  const errors = reactive({});
117
+ const warnings = reactive({});
118
+ const rootErrors = shallowRef([]);
119
+ const rootWarnings = shallowRef([]);
67
120
  const touched = reactive({});
68
121
  const submitCount = ref(0);
69
122
  const submitting = ref(false);
70
- const baselineVersion = ref(0);
71
- const readValue = (name) => values[name];
123
+ const ruleErrors = {};
124
+ const manualErrors = {};
125
+ let issueErrors = {};
126
+ let issueRoot = [];
127
+ let manualRoot = [];
128
+ const readValue = (name) => target()[name];
72
129
  const dirtyFields = computed(() => {
73
130
  void baselineVersion.value;
74
- return names.filter(
75
- (name) => !isEqualValue(readValue(name), baseline[name])
76
- );
131
+ const keys = external ? [.../* @__PURE__ */ new Set([...names.value, ...Object.keys(baseline)])] : names.value;
132
+ return keys.filter((name) => !isEqualValue(readValue(name), baseline[name]));
77
133
  });
78
134
  const dirty = computed(() => dirtyFields.value.length > 0);
79
- const valid = computed(() => names.every((name) => !errors[name]));
135
+ const valid = computed(
136
+ () => !Object.values(errors).some(Boolean) && rootErrors.value.length === 0
137
+ );
138
+ function composeErrors() {
139
+ const next = {};
140
+ for (const source of [manualErrors, ruleErrors, issueErrors]) {
141
+ for (const [key, message] of Object.entries(source)) {
142
+ if (message && !(key in next)) next[key] = message;
143
+ }
144
+ }
145
+ return next;
146
+ }
147
+ function syncErrors(inScope) {
148
+ replaceEntries(errors, composeErrors(), inScope);
149
+ if (!inScope) rootErrors.value = uniqueMessages(manualRoot, issueRoot);
150
+ }
151
+ function evaluateValidator() {
152
+ const validator = toValue(options.validator);
153
+ if (!validator) return groupIssues([]);
154
+ return chain(
155
+ validator["~standard"].validate(cloneValue(target())),
156
+ (result) => groupIssues(schemaIssues(result.issues ?? []), issueKey)
157
+ );
158
+ }
80
159
  async function runValidator() {
81
- if (!options.validator) return {};
82
- return issuesByField(await runStandardSchema(options.validator, cloneValue(values)));
160
+ return evaluateValidator();
83
161
  }
84
- async function runRules(name, formIssues) {
162
+ function evaluateRules(name) {
85
163
  const definition = schema[name];
164
+ if (!definition) return void 0;
86
165
  const value = readValue(name);
87
166
  const fieldMessages = { ...messages.value, ...definition.messages };
88
167
  const context = {
@@ -114,53 +193,144 @@ export function useForm(options) {
114
193
  if (!definition.pattern.test(value)) return fieldMessages.pattern(context);
115
194
  }
116
195
  }
117
- if (definition.validate) {
118
- const result = await definition.validate(value, values);
196
+ const runSchema = () => {
197
+ if (!definition.schema) return void 0;
198
+ return chain(definition.schema["~standard"].validate(value), (result) => {
199
+ const issues = result.issues ?? [];
200
+ return issues.length ? issues[0].message || fieldMessages.invalid(context) : void 0;
201
+ });
202
+ };
203
+ if (!definition.validate) return runSchema();
204
+ return chain(definition.validate(value, target()), (result) => {
119
205
  if (typeof result === "string" && result) return result;
120
206
  if (result === false) return fieldMessages.invalid(context);
121
- }
122
- if (definition.schema) {
123
- const issues = await runStandardSchema(definition.schema, value);
124
- if (issues.length) return issues[0].message || fieldMessages.invalid(context);
125
- }
126
- return formIssues[name];
207
+ return runSchema();
208
+ });
209
+ }
210
+ async function runRules(name) {
211
+ return evaluateRules(name);
127
212
  }
128
- async function applyField(name, formIssues) {
129
- const message = await runRules(name, formIssues);
130
- errors[name] = message;
131
- return !message;
213
+ function hasErrorWithin(name) {
214
+ return Object.entries(errors).some(([key, message]) => message && isWithin(key, name));
132
215
  }
216
+ let validationRun = 0;
217
+ const fieldRuns = /* @__PURE__ */ new Map();
133
218
  async function validateField(name) {
134
- return applyField(name, await runValidator());
219
+ const run = (fieldRuns.get(name) ?? 0) + 1;
220
+ fieldRuns.set(name, run);
221
+ const [issues, message] = await Promise.all([runValidator(), runRules(name)]);
222
+ if (fieldRuns.get(name) !== run) return !message && !(name in issues.fields);
223
+ issueErrors = issues.fields;
224
+ issueRoot = issues.root;
225
+ ruleErrors[name] = message;
226
+ for (const key of Object.keys(manualErrors)) {
227
+ if (isWithin(key, name)) delete manualErrors[key];
228
+ }
229
+ syncErrors(submitCount.value > 0 ? void 0 : (key) => isWithin(key, name));
230
+ return !hasErrorWithin(name);
231
+ }
232
+ function applyValidation(fields, issues, results) {
233
+ issueErrors = issues.fields;
234
+ issueRoot = issues.root;
235
+ for (const key of Object.keys(ruleErrors)) delete ruleErrors[key];
236
+ fields.forEach((name, index) => ruleErrors[name] = results[index]);
237
+ for (const key of Object.keys(manualErrors)) delete manualErrors[key];
238
+ manualRoot = [];
239
+ syncErrors();
240
+ }
241
+ function isValidResult(issues, results) {
242
+ return results.every((message) => !message) && Object.keys(issues.fields).length === 0 && issues.root.length === 0;
135
243
  }
136
244
  async function validate() {
137
- const formIssues = await runValidator();
138
- const results = await Promise.all(names.map((name) => applyField(name, formIssues)));
139
- return results.every(Boolean);
245
+ const run = ++validationRun;
246
+ const fields = names.value;
247
+ const [issues, results] = await Promise.all([
248
+ runValidator(),
249
+ Promise.all(fields.map((name) => runRules(name)))
250
+ ]);
251
+ if (run === validationRun) applyValidation(fields, issues, results);
252
+ return isValidResult(issues, results);
253
+ }
254
+ function validateSync() {
255
+ const fields = names.value;
256
+ const issues = evaluateValidator();
257
+ const results = fields.map((name) => evaluateRules(name));
258
+ if (isThenable(issues) || results.some((result) => isThenable(result))) {
259
+ for (const pending of [issues, ...results]) {
260
+ if (isThenable(pending)) void pending.catch(() => void 0);
261
+ }
262
+ throw new Error(
263
+ "[k] useForm: validateSync() found an async rule or validator. Use validate() instead."
264
+ );
265
+ }
266
+ const messages2 = results;
267
+ ++validationRun;
268
+ applyValidation(fields, issues, messages2);
269
+ return isValidResult(issues, messages2);
140
270
  }
141
271
  function setError(name, message) {
142
- errors[name] = message;
272
+ if (name === "") {
273
+ manualRoot = message ? [message] : [];
274
+ rootErrors.value = uniqueMessages(manualRoot, issueRoot);
275
+ return;
276
+ }
277
+ manualErrors[name] = message;
278
+ if (!message) {
279
+ delete ruleErrors[name];
280
+ delete issueErrors[name];
281
+ }
282
+ syncErrors((key) => key === name);
143
283
  }
144
284
  function setErrors(next) {
145
- for (const name of names) errors[name] = next[name];
285
+ for (const key of Object.keys(manualErrors)) delete manualErrors[key];
286
+ for (const key of Object.keys(ruleErrors)) delete ruleErrors[key];
287
+ issueErrors = {};
288
+ for (const [key, message] of Object.entries(next)) {
289
+ if (key === "") manualRoot = message ? [message] : [];
290
+ else manualErrors[key] = message;
291
+ }
292
+ syncErrors();
146
293
  }
147
294
  function clearErrors() {
148
- for (const name of names) errors[name] = void 0;
295
+ for (const key of Object.keys(manualErrors)) delete manualErrors[key];
296
+ for (const key of Object.keys(ruleErrors)) delete ruleErrors[key];
297
+ issueErrors = {};
298
+ issueRoot = [];
299
+ manualRoot = [];
300
+ syncErrors();
149
301
  }
150
302
  function touch(name) {
151
- if (!(name in schema)) return;
303
+ const field = name.split(".")[0];
304
+ if (!names.value.includes(field)) return;
152
305
  touched[name] = true;
153
- if (validateOn === "blur" || submitCount.value > 0) void validateField(name);
306
+ touched[field] = true;
307
+ if (validateOn === "blur" || submitCount.value > 0) void validateField(field);
154
308
  }
309
+ const snapshots = /* @__PURE__ */ new Map();
310
+ const takeSnapshots = () => {
311
+ snapshots.clear();
312
+ for (const name of names.value) snapshots.set(name, cloneValue(readValue(name)));
313
+ };
314
+ takeSnapshots();
155
315
  function reset(next) {
156
- const fresh = initialValues();
157
- if (next) Object.assign(fresh, cloneValue(next));
158
- baseline = fresh;
316
+ const values = target();
317
+ if (external) {
318
+ const fresh = cloneValue(next ?? baseline);
319
+ for (const key of Object.keys(values)) {
320
+ if (!(key in fresh)) delete values[key];
321
+ }
322
+ Object.assign(values, fresh);
323
+ baseline = cloneValue(fresh);
324
+ } else {
325
+ const fresh = initialValues();
326
+ if (next) Object.assign(fresh, cloneValue(next));
327
+ baseline = fresh;
328
+ Object.assign(values, cloneValue(fresh));
329
+ }
159
330
  baselineVersion.value++;
160
- Object.assign(values, cloneValue(fresh));
161
- for (const name of names) snapshots.set(name, cloneValue(readValue(name)));
331
+ takeSnapshots();
162
332
  clearErrors();
163
- for (const name of names) touched[name] = false;
333
+ for (const key of Object.keys(touched)) touched[key] = false;
164
334
  submitCount.value = 0;
165
335
  }
166
336
  async function submit() {
@@ -173,21 +343,18 @@ export function useForm(options) {
173
343
  }
174
344
  submitting.value = true;
175
345
  try {
176
- await options.onSubmit?.(cloneValue(values));
346
+ await options.onSubmit?.(cloneValue(target()));
177
347
  } finally {
178
348
  submitting.value = false;
179
349
  }
180
350
  return true;
181
351
  }
182
- const snapshots = new Map(
183
- names.map((name) => [name, cloneValue(readValue(name))])
184
- );
185
352
  watch(
186
- () => names.map((name) => readValue(name)),
353
+ () => names.value.map((name) => readValue(name)),
187
354
  () => {
188
- for (const name of names) {
355
+ for (const name of names.value) {
189
356
  const current = readValue(name);
190
- if (isEqualValue(current, snapshots.get(name))) continue;
357
+ if (snapshots.has(name) && isEqualValue(current, snapshots.get(name))) continue;
191
358
  snapshots.set(name, cloneValue(current));
192
359
  if (validateOn === "input" || submitCount.value > 0 || validateOn === "blur" && touched[name])
193
360
  void validateField(name);
@@ -195,10 +362,45 @@ export function useForm(options) {
195
362
  },
196
363
  { deep: true }
197
364
  );
365
+ watch(
366
+ () => toValue(options.validator),
367
+ () => {
368
+ if (submitCount.value > 0) void validate();
369
+ }
370
+ );
371
+ const warningSource = options.warnings;
372
+ if (warningSource) {
373
+ let warningRun = 0;
374
+ const applyWarnings = (result) => {
375
+ const list = Array.isArray(result) ? schemaIssues(result) : Object.entries(result ?? {}).filter((entry) => Boolean(entry[1])).map(([key, message]) => ({ message, path: keyPath(key) }));
376
+ const grouped = groupIssues(list, issueKey);
377
+ replaceEntries(warnings, grouped.fields);
378
+ rootWarnings.value = grouped.root;
379
+ };
380
+ const evaluate = () => {
381
+ if (typeof warningSource === "function") return warningSource(target());
382
+ const schemaSource = isRef(warningSource) ? warningSource.value : warningSource;
383
+ if (!schemaSource || !isStandardSchema(schemaSource)) return void 0;
384
+ const result = schemaSource["~standard"].validate(cloneValue(target()));
385
+ const issuesOf = (outcome) => outcome.issues ?? [];
386
+ return isThenable(result) ? result.then(issuesOf) : issuesOf(result);
387
+ };
388
+ watchEffect(() => {
389
+ const run = ++warningRun;
390
+ settle(evaluate(), (result) => {
391
+ if (run === warningRun) applyWarnings(result);
392
+ });
393
+ });
394
+ }
198
395
  return {
199
396
  schema,
200
- values,
397
+ get values() {
398
+ return target();
399
+ },
201
400
  errors,
401
+ warnings: readonly(warnings),
402
+ rootErrors,
403
+ rootWarnings,
202
404
  touched,
203
405
  dirty,
204
406
  dirtyFields,
@@ -207,6 +409,7 @@ export function useForm(options) {
207
409
  submitting,
208
410
  isDirty: (name) => dirtyFields.value.includes(name),
209
411
  validate,
412
+ validateSync,
210
413
  validateField,
211
414
  setError,
212
415
  setErrors,
@@ -220,3 +423,7 @@ export function useForm(options) {
220
423
  export function useFormContext() {
221
424
  return inject(FORM_KEY, null);
222
425
  }
426
+ export function provideForm(form) {
427
+ provide(FORM_KEY, form);
428
+ return form;
429
+ }
@@ -117,6 +117,8 @@ const props = defineProps({
117
117
  maxHeight: { type: String, required: false },
118
118
  label: { type: String, required: false },
119
119
  headings: { type: Array, required: false, default: () => [2, 3, 4] },
120
+ headingLabel: { type: String, required: false, default: "level" },
121
+ toolbar: { type: Array, required: false },
120
122
  extensions: { type: Array, required: false, default: () => [] },
121
123
  messages: { type: Object, required: false }
122
124
  });
@@ -124,7 +126,8 @@ const model = defineModel({ type: [String, Object, null], ...{ default: null } }
124
126
  const control = useFieldControl({
125
127
  invalid: () => props.invalid,
126
128
  disabled: () => props.disabled,
127
- required: () => props.required
129
+ required: () => props.required,
130
+ labelable: false
128
131
  });
129
132
  const { locale } = useLocale();
130
133
  const text = computed(() => ({ ...locale.value.messages.editor, ...props.messages }));
@@ -207,7 +210,8 @@ watch(
207
210
  () => editor.value?.setOptions({ editorProps: { attributes: contentAttributes() } })
208
211
  );
209
212
  onBeforeUnmount(() => editor.value?.destroy());
210
- function heading(level, rank) {
213
+ function heading(level, index) {
214
+ const rank = props.headingLabel === "relative" ? index + 1 : level;
211
215
  return {
212
216
  id: `h${level}`,
213
217
  label: formatLocaleMessage(locale.value.code, text.value.heading, { level: rank }),
@@ -227,7 +231,7 @@ function mark(id, label, glyph, glyphClass, name, toggle) {
227
231
  run: toggle
228
232
  };
229
233
  }
230
- const groups = computed(() => [
234
+ const defaultToolbar = computed(() => [
231
235
  [
232
236
  {
233
237
  id: "undo",
@@ -246,7 +250,7 @@ const groups = computed(() => [
246
250
  run: () => editor.value?.chain().focus().redo().run()
247
251
  }
248
252
  ],
249
- props.headings.map((level, index) => heading(level, index + 1)),
253
+ props.headings.map((level, index) => heading(level, index)),
250
254
  [
251
255
  mark(
252
256
  "bold",
@@ -314,6 +318,30 @@ const groups = computed(() => [
314
318
  }
315
319
  ]
316
320
  ]);
321
+ function isToolbarGroups(toolbar) {
322
+ return toolbar.some((entry) => Array.isArray(entry));
323
+ }
324
+ function splitToolbar(toolbar) {
325
+ if (isToolbarGroups(toolbar)) return toolbar;
326
+ const result = [[]];
327
+ for (const entry of toolbar) {
328
+ if (entry === "|") result.push([]);
329
+ else result[result.length - 1].push(entry);
330
+ }
331
+ return result;
332
+ }
333
+ const groups = computed(() => {
334
+ if (!props.toolbar) return defaultToolbar.value.filter((group) => group.length);
335
+ const byId = new Map(defaultToolbar.value.flat().map((item) => [item.id, item]));
336
+ const headingItems = defaultToolbar.value[1] ?? [];
337
+ return splitToolbar(props.toolbar).map(
338
+ (group) => group.flatMap((entry) => {
339
+ if (entry === "headings") return headingItems;
340
+ const item = byId.get(entry);
341
+ return item ? [item] : [];
342
+ })
343
+ ).filter((group) => group.length);
344
+ });
317
345
  function itemAttributes(item) {
318
346
  const disabled = item.enabled ? !item.enabled() : false;
319
347
  return {
@@ -1,6 +1,6 @@
1
1
  import type { Editor } from '@tiptap/vue-3';
2
2
  import type { Extensions } from '@tiptap/vue-3';
3
- import type { EditorFormat, EditorHeadingLevel, EditorMessages, EditorValue } from './types';
3
+ import type { EditorFormat, EditorHeadingLabel, EditorHeadingLevel, EditorMessages, EditorToolbar, EditorValue } from './types';
4
4
  declare const __VLS_export: import("vue").DefineComponent<{
5
5
  format?: EditorFormat;
6
6
  placeholder?: string | undefined;
@@ -14,6 +14,8 @@ declare const __VLS_export: import("vue").DefineComponent<{
14
14
  maxHeight?: string;
15
15
  label?: string;
16
16
  headings?: EditorHeadingLevel[];
17
+ headingLabel?: EditorHeadingLabel;
18
+ toolbar?: EditorToolbar;
17
19
  extensions?: Extensions;
18
20
  messages?: Partial<EditorMessages>;
19
21
  modelValue?: EditorValue;
@@ -22,7 +24,6 @@ declare const __VLS_export: import("vue").DefineComponent<{
22
24
  title?: string | undefined | undefined;
23
25
  style?: import("vue").StyleValue;
24
26
  results?: (string | number) | undefined;
25
- ref?: import("vue").VNodeRef | undefined;
26
27
  class?: import("vue").ClassValue;
27
28
  role?: string | undefined | undefined;
28
29
  'aria-label'?: string | undefined | undefined;
@@ -135,7 +136,7 @@ declare const __VLS_export: import("vue").DefineComponent<{
135
136
  'aria-colindex'?: (string | number) | undefined;
136
137
  'aria-colspan'?: (string | number) | undefined;
137
138
  'aria-controls'?: string | undefined | undefined;
138
- 'aria-current'?: "page" | "location" | "time" | (boolean | "true" | "false") | "step" | "date" | undefined;
139
+ 'aria-current'?: "page" | "time" | (boolean | "true" | "false") | "step" | "location" | "date" | undefined;
139
140
  'aria-describedby'?: string | undefined | undefined;
140
141
  'aria-details'?: string | undefined | undefined;
141
142
  'aria-disabled'?: (boolean | "true" | "false") | undefined;
@@ -172,6 +173,7 @@ declare const __VLS_export: import("vue").DefineComponent<{
172
173
  'aria-valuemin'?: (string | number) | undefined;
173
174
  'aria-valuenow'?: (string | number) | undefined;
174
175
  'aria-valuetext'?: string | undefined | undefined;
176
+ ref?: import("vue").VNodeRef | undefined;
175
177
  ref_for?: boolean | undefined | undefined;
176
178
  ref_key?: string | undefined | undefined;
177
179
  accesskey?: string | undefined | undefined;
@@ -226,6 +228,8 @@ declare const __VLS_export: import("vue").DefineComponent<{
226
228
  maxHeight?: string;
227
229
  label?: string;
228
230
  headings?: EditorHeadingLevel[];
231
+ headingLabel?: EditorHeadingLabel;
232
+ toolbar?: EditorToolbar;
229
233
  extensions?: Extensions;
230
234
  messages?: Partial<EditorMessages>;
231
235
  modelValue?: EditorValue;
@@ -234,7 +238,6 @@ declare const __VLS_export: import("vue").DefineComponent<{
234
238
  title?: string | undefined | undefined;
235
239
  style?: import("vue").StyleValue;
236
240
  results?: (string | number) | undefined;
237
- ref?: import("vue").VNodeRef | undefined;
238
241
  class?: import("vue").ClassValue;
239
242
  role?: string | undefined | undefined;
240
243
  'aria-label'?: string | undefined | undefined;
@@ -347,7 +350,7 @@ declare const __VLS_export: import("vue").DefineComponent<{
347
350
  'aria-colindex'?: (string | number) | undefined;
348
351
  'aria-colspan'?: (string | number) | undefined;
349
352
  'aria-controls'?: string | undefined | undefined;
350
- 'aria-current'?: "page" | "location" | "time" | (boolean | "true" | "false") | "step" | "date" | undefined;
353
+ 'aria-current'?: "page" | "time" | (boolean | "true" | "false") | "step" | "location" | "date" | undefined;
351
354
  'aria-describedby'?: string | undefined | undefined;
352
355
  'aria-details'?: string | undefined | undefined;
353
356
  'aria-disabled'?: (boolean | "true" | "false") | undefined;
@@ -384,6 +387,7 @@ declare const __VLS_export: import("vue").DefineComponent<{
384
387
  'aria-valuemin'?: (string | number) | undefined;
385
388
  'aria-valuenow'?: (string | number) | undefined;
386
389
  'aria-valuetext'?: string | undefined | undefined;
390
+ ref?: import("vue").VNodeRef | undefined;
387
391
  ref_for?: boolean | undefined | undefined;
388
392
  ref_key?: string | undefined | undefined;
389
393
  accesskey?: string | undefined | undefined;
@@ -425,8 +429,9 @@ declare const __VLS_export: import("vue").DefineComponent<{
425
429
  }>, {
426
430
  invalid: boolean;
427
431
  format: EditorFormat;
428
- minHeight: string;
429
432
  headings: EditorHeadingLevel[];
433
+ minHeight: string;
434
+ headingLabel: EditorHeadingLabel;
430
435
  extensions: Extensions;
431
436
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
432
437
  declare const _default: typeof __VLS_export;
@@ -95,7 +95,7 @@
95
95
 
96
96
  .k-editor-glyph-text.is-mono {
97
97
  font-family: var(--k-font-mono);
98
- font-size: 0.6875rem;
98
+ font-size: 0.75rem;
99
99
  }
100
100
 
101
101
  .k-editor-glyph-text.is-tabular {
@@ -4,3 +4,6 @@ export type EditorFormat = 'html' | 'json';
4
4
  export type EditorValue = string | JSONContent | null;
5
5
  export type EditorMessages = KMessages['editor'];
6
6
  export type EditorHeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
7
+ export type EditorHeadingLabel = 'level' | 'relative';
8
+ export type EditorToolbarItem = 'undo' | 'redo' | 'headings' | `h${EditorHeadingLevel}` | 'bold' | 'italic' | 'strike' | 'code' | 'bulletList' | 'orderedList' | 'blockquote' | 'link' | '|';
9
+ export type EditorToolbar = EditorToolbarItem[] | EditorToolbarItem[][];
@@ -18,7 +18,6 @@ declare const __VLS_base: import("vue").DefineComponent<{
18
18
  style?: import("vue").StyleValue;
19
19
  results?: (string | number) | undefined;
20
20
  placeholder?: string | undefined | undefined;
21
- ref?: import("vue").VNodeRef | undefined;
22
21
  class?: import("vue").ClassValue;
23
22
  role?: string | undefined | undefined;
24
23
  'aria-label'?: string | undefined | undefined;
@@ -131,7 +130,7 @@ declare const __VLS_base: import("vue").DefineComponent<{
131
130
  'aria-colindex'?: (string | number) | undefined;
132
131
  'aria-colspan'?: (string | number) | undefined;
133
132
  'aria-controls'?: string | undefined | undefined;
134
- 'aria-current'?: "page" | "location" | "time" | (boolean | "true" | "false") | "step" | "date" | undefined;
133
+ 'aria-current'?: "page" | "time" | (boolean | "true" | "false") | "step" | "location" | "date" | undefined;
135
134
  'aria-describedby'?: string | undefined | undefined;
136
135
  'aria-details'?: string | undefined | undefined;
137
136
  'aria-disabled'?: (boolean | "true" | "false") | undefined;
@@ -168,6 +167,7 @@ declare const __VLS_base: import("vue").DefineComponent<{
168
167
  'aria-valuemin'?: (string | number) | undefined;
169
168
  'aria-valuenow'?: (string | number) | undefined;
170
169
  'aria-valuetext'?: string | undefined | undefined;
170
+ ref?: import("vue").VNodeRef | undefined;
171
171
  ref_for?: boolean | undefined | undefined;
172
172
  ref_key?: string | undefined | undefined;
173
173
  accesskey?: string | undefined | undefined;
@@ -222,7 +222,6 @@ declare const __VLS_base: import("vue").DefineComponent<{
222
222
  style?: import("vue").StyleValue;
223
223
  results?: (string | number) | undefined;
224
224
  placeholder?: string | undefined | undefined;
225
- ref?: import("vue").VNodeRef | undefined;
226
225
  class?: import("vue").ClassValue;
227
226
  role?: string | undefined | undefined;
228
227
  'aria-label'?: string | undefined | undefined;
@@ -335,7 +334,7 @@ declare const __VLS_base: import("vue").DefineComponent<{
335
334
  'aria-colindex'?: (string | number) | undefined;
336
335
  'aria-colspan'?: (string | number) | undefined;
337
336
  'aria-controls'?: string | undefined | undefined;
338
- 'aria-current'?: "page" | "location" | "time" | (boolean | "true" | "false") | "step" | "date" | undefined;
337
+ 'aria-current'?: "page" | "time" | (boolean | "true" | "false") | "step" | "location" | "date" | undefined;
339
338
  'aria-describedby'?: string | undefined | undefined;
340
339
  'aria-details'?: string | undefined | undefined;
341
340
  'aria-disabled'?: (boolean | "true" | "false") | undefined;
@@ -372,6 +371,7 @@ declare const __VLS_base: import("vue").DefineComponent<{
372
371
  'aria-valuemin'?: (string | number) | undefined;
373
372
  'aria-valuenow'?: (string | number) | undefined;
374
373
  'aria-valuetext'?: string | undefined | undefined;
374
+ ref?: import("vue").VNodeRef | undefined;
375
375
  ref_for?: boolean | undefined | undefined;
376
376
  ref_key?: string | undefined | undefined;
377
377
  accesskey?: string | undefined | undefined;
@@ -56,7 +56,7 @@ declare const __VLS_export: import("vue").DefineComponent<{
56
56
  'aria-colindex'?: (string | number) | undefined;
57
57
  'aria-colspan'?: (string | number) | undefined;
58
58
  'aria-controls'?: string | undefined | undefined;
59
- 'aria-current'?: "page" | "location" | "time" | (boolean | "true" | "false") | "step" | "date" | undefined;
59
+ 'aria-current'?: "page" | "time" | (boolean | "true" | "false") | "step" | "location" | "date" | undefined;
60
60
  'aria-describedby'?: string | undefined | undefined;
61
61
  'aria-details'?: string | undefined | undefined;
62
62
  'aria-disabled'?: (boolean | "true" | "false") | undefined;
@@ -250,7 +250,7 @@ declare const __VLS_export: import("vue").DefineComponent<{
250
250
  'aria-colindex'?: (string | number) | undefined;
251
251
  'aria-colspan'?: (string | number) | undefined;
252
252
  'aria-controls'?: string | undefined | undefined;
253
- 'aria-current'?: "page" | "location" | "time" | (boolean | "true" | "false") | "step" | "date" | undefined;
253
+ 'aria-current'?: "page" | "time" | (boolean | "true" | "false") | "step" | "location" | "date" | undefined;
254
254
  'aria-describedby'?: string | undefined | undefined;
255
255
  'aria-details'?: string | undefined | undefined;
256
256
  'aria-disabled'?: (boolean | "true" | "false") | undefined;
@@ -56,7 +56,7 @@ declare const __VLS_export: import("vue").DefineComponent<{
56
56
  'aria-colindex'?: (string | number) | undefined;
57
57
  'aria-colspan'?: (string | number) | undefined;
58
58
  'aria-controls'?: string | undefined | undefined;
59
- 'aria-current'?: "page" | "location" | "time" | (boolean | "true" | "false") | "step" | "date" | undefined;
59
+ 'aria-current'?: "page" | "time" | (boolean | "true" | "false") | "step" | "location" | "date" | undefined;
60
60
  'aria-describedby'?: string | undefined | undefined;
61
61
  'aria-details'?: string | undefined | undefined;
62
62
  'aria-disabled'?: (boolean | "true" | "false") | undefined;
@@ -250,7 +250,7 @@ declare const __VLS_export: import("vue").DefineComponent<{
250
250
  'aria-colindex'?: (string | number) | undefined;
251
251
  'aria-colspan'?: (string | number) | undefined;
252
252
  'aria-controls'?: string | undefined | undefined;
253
- 'aria-current'?: "page" | "location" | "time" | (boolean | "true" | "false") | "step" | "date" | undefined;
253
+ 'aria-current'?: "page" | "time" | (boolean | "true" | "false") | "step" | "location" | "date" | undefined;
254
254
  'aria-describedby'?: string | undefined | undefined;
255
255
  'aria-details'?: string | undefined | undefined;
256
256
  'aria-disabled'?: (boolean | "true" | "false") | undefined;