@maz-ui/mcp 5.0.1-beta.0 → 5.1.0-beta.1
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.
- package/README.md +61 -228
- package/bin/maz-ui-mcp.mjs +2 -5
- package/dist/cli.d.mts +7 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.mjs +16 -0
- package/dist/index.d.mts +345 -0
- package/dist/index.d.ts +345 -0
- package/dist/index.mjs +16 -0
- package/dist/shared/mcp.DseFuKdl.mjs +2968 -0
- package/docs/README.md +207 -0
- package/docs/agents/agents-md.md +52 -0
- package/docs/agents/claude-rule.md +52 -0
- package/docs/components/maz-accordion.md +78 -0
- package/docs/{src/components → components}/maz-alert.md +31 -164
- package/docs/components/maz-animated-counter.md +87 -0
- package/docs/components/maz-animated-element.md +46 -0
- package/docs/components/maz-animated-text.md +53 -0
- package/docs/components/maz-avatar.md +193 -0
- package/docs/components/maz-backdrop.md +178 -0
- package/docs/{src/components → components}/maz-badge.md +26 -67
- package/docs/components/maz-bottom-sheet.md +257 -0
- package/docs/{src/components → components}/maz-btn-group.md +27 -189
- package/docs/{src/components → components}/maz-btn.md +62 -245
- package/docs/components/maz-card-spotlight.md +84 -0
- package/docs/{src/components → components}/maz-card.md +67 -192
- package/docs/components/maz-carousel.md +153 -0
- package/docs/{src/components → components}/maz-chart.md +17 -161
- package/docs/components/maz-checkbox.md +138 -0
- package/docs/components/maz-checklist.md +251 -0
- package/docs/components/maz-circular-progress-bar.md +109 -0
- package/docs/{src/components → components}/maz-code-highlight.md +15 -93
- package/docs/components/maz-container.md +241 -0
- package/docs/{src/components → components}/maz-date-picker.md +85 -454
- package/docs/components/maz-dialog-confirm.md +215 -0
- package/docs/components/maz-dialog.md +143 -0
- package/docs/components/maz-drawer.md +107 -0
- package/docs/components/maz-dropdown.md +419 -0
- package/docs/{src/components → components}/maz-dropzone.md +80 -221
- package/docs/components/maz-expand-animation.md +82 -0
- package/docs/components/maz-fullscreen-loader.md +58 -0
- package/docs/components/maz-gallery.md +79 -0
- package/docs/{src/components → components}/maz-icon.md +32 -83
- package/docs/components/maz-input-code.md +73 -0
- package/docs/components/maz-input-number.md +79 -0
- package/docs/components/maz-input-phone-number.md +536 -0
- package/docs/components/maz-input-price.md +68 -0
- package/docs/components/maz-input-tags.md +112 -0
- package/docs/{src/components → components}/maz-input.md +74 -221
- package/docs/components/maz-lazy-img.md +113 -0
- package/docs/components/maz-link.md +146 -0
- package/docs/components/maz-loading-bar.md +38 -0
- package/docs/{src/components → components}/maz-markdown-editor.md +72 -125
- package/docs/components/maz-pagination.md +82 -0
- package/docs/components/maz-popover.md +794 -0
- package/docs/{src/components → components}/maz-pull-to-refresh.md +59 -45
- package/docs/components/maz-radio-buttons.md +313 -0
- package/docs/components/maz-radio.md +119 -0
- package/docs/{src/components → components}/maz-read-more.md +31 -122
- package/docs/components/maz-reading-progress-bar.md +96 -0
- package/docs/{src/components → components}/maz-select-country.md +80 -340
- package/docs/{src/components → components}/maz-select.md +76 -206
- package/docs/components/maz-sidebar-content.md +25 -0
- package/docs/components/maz-sidebar-footer.md +25 -0
- package/docs/components/maz-sidebar-group.md +27 -0
- package/docs/components/maz-sidebar-header.md +25 -0
- package/docs/components/maz-sidebar-menu-button.md +41 -0
- package/docs/components/maz-sidebar-menu-item.md +25 -0
- package/docs/components/maz-sidebar-menu-sub.md +32 -0
- package/docs/components/maz-sidebar-menu.md +25 -0
- package/docs/components/maz-sidebar-separator.md +21 -0
- package/docs/components/maz-sidebar-trigger.md +25 -0
- package/docs/{src/components → components}/maz-sidebar.md +117 -329
- package/docs/{src/components → components}/maz-skeleton.md +27 -156
- package/docs/{src/components → components}/maz-slider.md +33 -66
- package/docs/components/maz-spinner.md +84 -0
- package/docs/{src/components → components}/maz-stepper.md +48 -185
- package/docs/components/maz-switch.md +86 -0
- package/docs/components/maz-table-cell.md +27 -0
- package/docs/components/maz-table-row.md +28 -0
- package/docs/components/maz-table-title.md +27 -0
- package/docs/components/maz-table-virtualizer.md +31 -0
- package/docs/components/maz-table.md +426 -0
- package/docs/components/maz-tabs-bar.md +43 -0
- package/docs/components/maz-tabs-content-item.md +27 -0
- package/docs/components/maz-tabs-content.md +27 -0
- package/docs/{src/components → components}/maz-tabs.md +89 -126
- package/docs/components/maz-textarea.md +255 -0
- package/docs/components/maz-ticker.md +427 -0
- package/docs/components/maz-timeline.md +304 -0
- package/docs/components/maz-ui-provider.md +26 -0
- package/docs/{src/components → components}/maz-window-mockup.md +38 -105
- package/docs/composables/use-aos.md +41 -0
- package/docs/composables/use-breakpoints.md +42 -0
- package/docs/composables/use-dialog.md +58 -0
- package/docs/{src/composables → composables}/use-display-names.md +12 -59
- package/docs/{src/composables → composables}/use-drag.md +14 -68
- package/docs/{src/composables → composables}/use-form-validator.md +22 -696
- package/docs/{src/composables → composables}/use-idle-timeout.md +13 -97
- package/docs/{src/composables → composables}/use-pull-to-refresh.md +14 -7
- package/docs/{src/composables → composables}/use-reading-time.md +20 -56
- package/docs/composables/use-string-matching.md +41 -0
- package/docs/{src/composables → composables}/use-swipe.md +14 -81
- package/docs/{src/composables → composables}/use-timer.md +12 -38
- package/docs/composables/use-toast.md +50 -0
- package/docs/{src/composables → composables}/use-user-visibility.md +14 -56
- package/docs/composables/use-wait.md +50 -0
- package/docs/composables/use-window-size.md +25 -0
- package/docs/{src/directives → directives}/click-outside.md +13 -99
- package/docs/{src/directives → directives}/fullscreen-img.md +10 -28
- package/docs/{src/directives → directives}/lazy-img.md +14 -49
- package/docs/{src/directives → directives}/tooltip.md +12 -217
- package/docs/{src/directives → directives}/zoom-img.md +11 -38
- package/docs/{src/ecosystem → ecosystem}/eslint-config.md +37 -29
- package/docs/ecosystem/icons/icon-set.md +25 -0
- package/docs/{src/ecosystem → ecosystem}/icons/index.md +21 -15
- package/docs/ecosystem/mcp.md +165 -0
- package/docs/{src/ecosystem → ecosystem}/node/exec-promise.md +12 -6
- package/docs/ecosystem/node/index.md +53 -0
- package/docs/{src/ecosystem → ecosystem}/node/logger.md +12 -6
- package/docs/{src/ecosystem → ecosystem}/node/print-banner.md +12 -4
- package/docs/ecosystem/node/setup-agents.md +102 -0
- package/docs/{src/ecosystem → ecosystem}/nuxt.md +50 -19
- package/docs/{src/ecosystem → ecosystem}/stylelint-config.md +33 -25
- package/docs/{src/ecosystem → ecosystem}/themes.md +72 -235
- package/docs/{src/ecosystem → ecosystem}/translations.md +25 -23
- package/docs/ecosystem/utils/camel-case.md +37 -0
- package/docs/ecosystem/utils/capitalize.md +37 -0
- package/docs/{src/ecosystem → ecosystem}/utils/check-availability.md +14 -8
- package/docs/{src/ecosystem → ecosystem}/utils/cookie.md +18 -12
- package/docs/ecosystem/utils/country-code-to-unicode-flag.md +128 -0
- package/docs/{src/ecosystem → ecosystem}/utils/currency.md +12 -26
- package/docs/{src/ecosystem → ecosystem}/utils/date.md +10 -24
- package/docs/ecosystem/utils/debounce-callback.md +44 -0
- package/docs/{src/ecosystem → ecosystem}/utils/debounce-id.md +14 -8
- package/docs/{src/ecosystem → ecosystem}/utils/debounce.md +14 -8
- package/docs/ecosystem/utils/fetch-locale-ip.md +39 -0
- package/docs/{src/ecosystem → ecosystem}/utils/format-json.md +11 -5
- package/docs/{src/ecosystem → ecosystem}/utils/format-phone-number.md +12 -6
- package/docs/ecosystem/utils/get-browser-locale.md +35 -0
- package/docs/ecosystem/utils/get-country-flag-url.md +67 -0
- package/docs/{src/ecosystem → ecosystem}/utils/get-error-message.md +10 -4
- package/docs/{src/ecosystem → ecosystem}/utils/idle-timeout.md +15 -9
- package/docs/{src/ecosystem → ecosystem}/utils/index.md +10 -13
- package/docs/{src/ecosystem → ecosystem}/utils/is-client.md +11 -5
- package/docs/{src/ecosystem → ecosystem}/utils/is-equal.md +14 -8
- package/docs/{src/ecosystem → ecosystem}/utils/is-ios.md +12 -6
- package/docs/ecosystem/utils/is-server.md +37 -0
- package/docs/ecosystem/utils/is-standalone-mode.md +49 -0
- package/docs/{src/ecosystem → ecosystem}/utils/kebab-case.md +10 -4
- package/docs/{src/ecosystem → ecosystem}/utils/normalize-string.md +10 -4
- package/docs/{src/ecosystem → ecosystem}/utils/number.md +10 -24
- package/docs/{src/ecosystem → ecosystem}/utils/pascal-case.md +11 -5
- package/docs/{src/ecosystem → ecosystem}/utils/prevent-ios-input-zoom.md +14 -8
- package/docs/{src/ecosystem → ecosystem}/utils/script-loader.md +16 -8
- package/docs/{src/ecosystem → ecosystem}/utils/sleep.md +10 -4
- package/docs/{src/ecosystem → ecosystem}/utils/snake-case.md +10 -4
- package/docs/{src/ecosystem → ecosystem}/utils/swipe-handler.md +14 -8
- package/docs/{src/ecosystem → ecosystem}/utils/textarea-autogrow.md +12 -6
- package/docs/{src/ecosystem → ecosystem}/utils/throttle-id.md +13 -7
- package/docs/{src/ecosystem → ecosystem}/utils/throttle.md +14 -8
- package/docs/ecosystem/utils/truthy-filter.md +37 -0
- package/docs/{src/ecosystem → ecosystem}/utils/types/deep-key-of.md +11 -5
- package/docs/{src/ecosystem → ecosystem}/utils/types/deep-partial.md +10 -4
- package/docs/ecosystem/utils/types/deep-required.md +45 -0
- package/docs/ecosystem/utils/types/flatten-object-keys.md +50 -0
- package/docs/{src/ecosystem → ecosystem}/utils/types/generic-instance-type.md +10 -4
- package/docs/{src/ecosystem → ecosystem}/utils/types/infer-maybe-ref.md +10 -4
- package/docs/ecosystem/utils/upper-first.md +38 -0
- package/docs/{src/ecosystem → ecosystem}/utils/user-visibility.md +12 -6
- package/docs/guide/ai-agents.md +239 -0
- package/docs/{src/guide → guide}/browser-support.md +22 -15
- package/docs/{src/guide → guide}/getting-started.md +34 -139
- package/docs/{src/guide → guide}/global-defaults.md +12 -11
- package/docs/{src/guide → guide}/maz-ui-provider.md +17 -16
- package/docs/{src/guide → guide}/migration-v5.md +95 -88
- package/docs/{src/guide → guide}/resolvers.md +33 -100
- package/docs/guide/tailwind.md +507 -0
- package/docs/{src/guide → guide}/vue.md +33 -39
- package/docs/manifest.json +1 -0
- package/docs/{src/plugins → plugins}/aos.md +20 -55
- package/docs/{src/plugins → plugins}/dialog.md +14 -161
- package/docs/plugins/toast.md +221 -0
- package/docs/plugins/wait.md +78 -0
- package/docs/reference/icons.md +46 -0
- package/docs/reference/tailwind-tokens.md +191 -0
- package/docs/rules.md +59 -0
- package/package.json +86 -22
- package/dist/mcp.d.mts +0 -153
- package/dist/mcp.d.ts +0 -153
- package/dist/mcp.mjs +0 -1511
- package/docs/generated-docs/maz-accordion.doc.md +0 -21
- package/docs/generated-docs/maz-alert.doc.md +0 -20
- package/docs/generated-docs/maz-animated-counter.doc.md +0 -17
- package/docs/generated-docs/maz-animated-element.doc.md +0 -14
- package/docs/generated-docs/maz-animated-text.doc.md +0 -17
- package/docs/generated-docs/maz-avatar.doc.md +0 -45
- package/docs/generated-docs/maz-backdrop.doc.md +0 -55
- package/docs/generated-docs/maz-badge.doc.md +0 -16
- package/docs/generated-docs/maz-bottom-sheet.doc.md +0 -24
- package/docs/generated-docs/maz-btn-group.doc.md +0 -20
- package/docs/generated-docs/maz-btn.doc.md +0 -33
- package/docs/generated-docs/maz-card-spotlight.doc.md +0 -16
- package/docs/generated-docs/maz-card.doc.md +0 -39
- package/docs/generated-docs/maz-carousel.doc.md +0 -17
- package/docs/generated-docs/maz-chart.doc.md +0 -10
- package/docs/generated-docs/maz-checkbox.doc.md +0 -33
- package/docs/generated-docs/maz-checklist.doc.md +0 -30
- package/docs/generated-docs/maz-circular-progress-bar.doc.md +0 -27
- package/docs/generated-docs/maz-code-highlight.doc.md +0 -11
- package/docs/generated-docs/maz-container.doc.md +0 -26
- package/docs/generated-docs/maz-date-picker.doc.md +0 -56
- package/docs/generated-docs/maz-dialog-confirm.doc.md +0 -24
- package/docs/generated-docs/maz-dialog.doc.md +0 -22
- package/docs/generated-docs/maz-drawer.doc.md +0 -25
- package/docs/generated-docs/maz-dropdown.doc.md +0 -45
- package/docs/generated-docs/maz-dropzone.doc.md +0 -82
- package/docs/generated-docs/maz-expand-animation.doc.md +0 -12
- package/docs/generated-docs/maz-fullscreen-loader.doc.md +0 -13
- package/docs/generated-docs/maz-gallery.doc.md +0 -17
- package/docs/generated-docs/maz-icon.doc.md +0 -16
- package/docs/generated-docs/maz-input-code.doc.md +0 -25
- package/docs/generated-docs/maz-input-number.doc.md +0 -33
- package/docs/generated-docs/maz-input-phone-number.doc.md +0 -62
- package/docs/generated-docs/maz-input-price.doc.md +0 -26
- package/docs/generated-docs/maz-input-tags.doc.md +0 -24
- package/docs/generated-docs/maz-input.doc.md +0 -54
- package/docs/generated-docs/maz-lazy-img.doc.md +0 -49
- package/docs/generated-docs/maz-link.doc.md +0 -31
- package/docs/generated-docs/maz-loading-bar.doc.md +0 -6
- package/docs/generated-docs/maz-markdown-editor.doc.md +0 -65
- package/docs/generated-docs/maz-pagination.doc.md +0 -21
- package/docs/generated-docs/maz-popover.doc.md +0 -79
- package/docs/generated-docs/maz-pull-to-refresh.doc.md +0 -50
- package/docs/generated-docs/maz-radio-buttons.doc.md +0 -34
- package/docs/generated-docs/maz-radio.doc.md +0 -33
- package/docs/generated-docs/maz-read-more.doc.md +0 -18
- package/docs/generated-docs/maz-reading-progress-bar.doc.md +0 -18
- package/docs/generated-docs/maz-select-country.doc.md +0 -45
- package/docs/generated-docs/maz-select.doc.md +0 -32
- package/docs/generated-docs/maz-sidebar-content.doc.md +0 -5
- package/docs/generated-docs/maz-sidebar-footer.doc.md +0 -5
- package/docs/generated-docs/maz-sidebar-group.doc.md +0 -11
- package/docs/generated-docs/maz-sidebar-header.doc.md +0 -5
- package/docs/generated-docs/maz-sidebar-menu-button.doc.md +0 -27
- package/docs/generated-docs/maz-sidebar-menu-item.doc.md +0 -5
- package/docs/generated-docs/maz-sidebar-menu-sub.doc.md +0 -16
- package/docs/generated-docs/maz-sidebar-menu.doc.md +0 -5
- package/docs/generated-docs/maz-sidebar-separator.doc.md +0 -0
- package/docs/generated-docs/maz-sidebar-trigger.doc.md +0 -5
- package/docs/generated-docs/maz-sidebar.doc.md +0 -36
- package/docs/generated-docs/maz-skeleton.doc.md +0 -12
- package/docs/generated-docs/maz-slider.doc.md +0 -20
- package/docs/generated-docs/maz-spinner.doc.md +0 -6
- package/docs/generated-docs/maz-stepper.doc.md +0 -30
- package/docs/generated-docs/maz-switch.doc.md +0 -31
- package/docs/generated-docs/maz-table-cell.doc.md +0 -5
- package/docs/generated-docs/maz-table-row.doc.md +0 -12
- package/docs/generated-docs/maz-table-title.doc.md +0 -5
- package/docs/generated-docs/maz-table-virtualizer.doc.md +0 -20
- package/docs/generated-docs/maz-table.doc.md +0 -72
- package/docs/generated-docs/maz-tabs-bar.doc.md +0 -29
- package/docs/generated-docs/maz-tabs-content-item.doc.md +0 -11
- package/docs/generated-docs/maz-tabs-content.doc.md +0 -5
- package/docs/generated-docs/maz-tabs.doc.md +0 -20
- package/docs/generated-docs/maz-textarea.doc.md +0 -46
- package/docs/generated-docs/maz-ticker.doc.md +0 -25
- package/docs/generated-docs/maz-timeline.doc.md +0 -32
- package/docs/generated-docs/maz-ui-provider.doc.md +0 -12
- package/docs/generated-docs/maz-window-mockup.doc.md +0 -23
- package/docs/src/blog/v4.md +0 -276
- package/docs/src/blog/v5.md +0 -184
- package/docs/src/changelog.md +0 -7
- package/docs/src/components/index.md +0 -8
- package/docs/src/components/maz-accordion.md +0 -80
- package/docs/src/components/maz-animated-counter.md +0 -124
- package/docs/src/components/maz-animated-element.md +0 -36
- package/docs/src/components/maz-animated-text.md +0 -36
- package/docs/src/components/maz-avatar.md +0 -227
- package/docs/src/components/maz-backdrop.md +0 -250
- package/docs/src/components/maz-bottom-sheet.md +0 -533
- package/docs/src/components/maz-card-spotlight.md +0 -163
- package/docs/src/components/maz-carousel.md +0 -231
- package/docs/src/components/maz-checkbox.md +0 -168
- package/docs/src/components/maz-checklist.md +0 -414
- package/docs/src/components/maz-circular-progress-bar.md +0 -145
- package/docs/src/components/maz-container.md +0 -363
- package/docs/src/components/maz-dialog-confirm.md +0 -240
- package/docs/src/components/maz-dialog.md +0 -164
- package/docs/src/components/maz-drawer.md +0 -181
- package/docs/src/components/maz-dropdown.md +0 -691
- package/docs/src/components/maz-expand-animation.md +0 -99
- package/docs/src/components/maz-fullscreen-loader.md +0 -58
- package/docs/src/components/maz-gallery.md +0 -85
- package/docs/src/components/maz-input-code.md +0 -61
- package/docs/src/components/maz-input-number.md +0 -81
- package/docs/src/components/maz-input-phone-number.md +0 -942
- package/docs/src/components/maz-input-price.md +0 -67
- package/docs/src/components/maz-input-tags.md +0 -114
- package/docs/src/components/maz-lazy-img.md +0 -88
- package/docs/src/components/maz-link.md +0 -178
- package/docs/src/components/maz-loading-bar.md +0 -26
- package/docs/src/components/maz-pagination.md +0 -79
- package/docs/src/components/maz-popover.md +0 -1416
- package/docs/src/components/maz-radio-buttons.md +0 -462
- package/docs/src/components/maz-radio.md +0 -142
- package/docs/src/components/maz-reading-progress-bar.md +0 -74
- package/docs/src/components/maz-spinner.md +0 -93
- package/docs/src/components/maz-switch.md +0 -92
- package/docs/src/components/maz-table.md +0 -732
- package/docs/src/components/maz-textarea.md +0 -400
- package/docs/src/components/maz-ticker.md +0 -692
- package/docs/src/components/maz-timeline.md +0 -478
- package/docs/src/composables/use-aos.md +0 -34
- package/docs/src/composables/use-breakpoints.md +0 -35
- package/docs/src/composables/use-dialog.md +0 -88
- package/docs/src/composables/use-string-matching.md +0 -63
- package/docs/src/composables/use-toast.md +0 -71
- package/docs/src/composables/use-wait.md +0 -62
- package/docs/src/composables/use-window-size.md +0 -18
- package/docs/src/ecosystem/icons/icon-set.md +0 -123
- package/docs/src/ecosystem/mcp.md +0 -210
- package/docs/src/ecosystem/node/index.md +0 -53
- package/docs/src/ecosystem/utils/camel-case.md +0 -31
- package/docs/src/ecosystem/utils/capitalize.md +0 -50
- package/docs/src/ecosystem/utils/country-code-to-unicode-flag.md +0 -213
- package/docs/src/ecosystem/utils/debounce-callback.md +0 -38
- package/docs/src/ecosystem/utils/fetch-locale-ip.md +0 -33
- package/docs/src/ecosystem/utils/get-browser-locale.md +0 -29
- package/docs/src/ecosystem/utils/get-country-flag-url.md +0 -156
- package/docs/src/ecosystem/utils/is-server.md +0 -31
- package/docs/src/ecosystem/utils/is-standalone-mode.md +0 -43
- package/docs/src/ecosystem/utils/truthy-filter.md +0 -31
- package/docs/src/ecosystem/utils/types/deep-required.md +0 -39
- package/docs/src/ecosystem/utils/types/flatten-object-keys.md +0 -44
- package/docs/src/ecosystem/utils/upper-first.md +0 -32
- package/docs/src/guide/cli.md +0 -144
- package/docs/src/guide/migration-v4.md +0 -925
- package/docs/src/guide/tailwind.md +0 -158
- package/docs/src/index.md +0 -603
- package/docs/src/made-with-maz-ui.md +0 -58
- package/docs/src/plugins/toast.md +0 -349
- package/docs/src/plugins/wait.md +0 -109
- package/docs/src/theme-editor.md +0 -21
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: useFormValidator
|
|
3
|
-
description: Vue composables for form validation with any Standard Schema library (Valibot, Zod, ArkType...) - useFormValidator and useFormField provide a flexible and typed approach to handle form validation in your Vue applications.
|
|
2
|
+
title: "useFormValidator"
|
|
3
|
+
description: "Vue composables for form validation with any Standard Schema library (Valibot, Zod, ArkType...) - useFormValidator and useFormField provide a flexible and typed approach to handle form validation in your Vue applications."
|
|
4
|
+
package: "maz-ui"
|
|
5
|
+
url: "https://maz-ui.com/composables/use-form-validator"
|
|
4
6
|
---
|
|
5
7
|
|
|
6
|
-
#
|
|
8
|
+
# useFormValidator
|
|
7
9
|
|
|
8
|
-
|
|
10
|
+
> Vue composables for form validation with any Standard Schema library (Valibot, Zod, ArkType...) - useFormValidator and useFormField provide a flexible and typed approach to handle form validation in your Vue applications.
|
|
11
|
+
|
|
12
|
+
- Package: `maz-ui`
|
|
13
|
+
- Import: `import { useFormValidator } from 'maz-ui/composables/useFormValidator'`
|
|
14
|
+
- Nuxt: auto-imported by `@maz-ui/nuxt`, no import needed in a Nuxt app
|
|
15
|
+
- Web: https://maz-ui.com/composables/use-form-validator
|
|
9
16
|
|
|
10
17
|
## Introduction
|
|
11
18
|
|
|
12
|
-
`useFormValidator` and `useFormField` are two Vue composables that work together to provide powerful form validation. They are **validation library agnostic**: any library implementing [Standard Schema](https://standardschema.dev) works out of the box, like [Valibot](https://valibot.dev), [Zod](https://zod.dev) or [ArkType](https://arktype.io). See
|
|
19
|
+
`useFormValidator` and `useFormField` are two Vue composables that work together to provide powerful form validation. They are **validation library agnostic**: any library implementing [Standard Schema](https://standardschema.dev) works out of the box, like [Valibot](https://valibot.dev), [Zod](https://zod.dev) or [ArkType](https://arktype.io). See Validation Libraries.
|
|
13
20
|
|
|
14
21
|
- **useFormValidator**: Initializes form validation for your entire form. Use it in your form's parent component.
|
|
15
22
|
- **useFormField**: Manages individual field validation states. Use it when you need fine-grained control over a field or when fields are in child components.
|
|
@@ -25,8 +32,6 @@ description: Vue composables for form validation with any Standard Schema librar
|
|
|
25
32
|
|
|
26
33
|
Install the validation library of your choice (Valibot is used in most examples of this page):
|
|
27
34
|
|
|
28
|
-
::: code-group
|
|
29
|
-
|
|
30
35
|
```bash [valibot]
|
|
31
36
|
pnpm add valibot
|
|
32
37
|
```
|
|
@@ -39,35 +44,8 @@ pnpm add zod
|
|
|
39
44
|
pnpm add arktype
|
|
40
45
|
```
|
|
41
46
|
|
|
42
|
-
:::
|
|
43
|
-
|
|
44
47
|
Here's the simplest form you can create with `useFormValidator`:
|
|
45
48
|
|
|
46
|
-
<ComponentDemo>
|
|
47
|
-
<form class="maz:flex maz:flex-col maz:gap-4" @submit="onSubmitQuickStart">
|
|
48
|
-
<MazInput
|
|
49
|
-
v-model="quickStartModel.email"
|
|
50
|
-
label="Email"
|
|
51
|
-
type="email"
|
|
52
|
-
:hint="quickStartErrors.email"
|
|
53
|
-
:error="!!quickStartErrors.email"
|
|
54
|
-
:success="quickStartStates.email.valid"
|
|
55
|
-
/>
|
|
56
|
-
<MazInput
|
|
57
|
-
v-model="quickStartModel.password"
|
|
58
|
-
label="Password"
|
|
59
|
-
type="password"
|
|
60
|
-
:hint="quickStartErrors.password"
|
|
61
|
-
:error="!!quickStartErrors.password"
|
|
62
|
-
:success="quickStartStates.password.valid"
|
|
63
|
-
/>
|
|
64
|
-
<MazBtn type="submit" :loading="quickStartSubmitting">
|
|
65
|
-
Login
|
|
66
|
-
</MazBtn>
|
|
67
|
-
</form>
|
|
68
|
-
|
|
69
|
-
<template #code>
|
|
70
|
-
|
|
71
49
|
```vue
|
|
72
50
|
<script lang="ts" setup>
|
|
73
51
|
import { useFormValidator } from 'maz-ui/composables'
|
|
@@ -119,9 +97,6 @@ const onSubmit = handleSubmit((data) => {
|
|
|
119
97
|
</template>
|
|
120
98
|
```
|
|
121
99
|
|
|
122
|
-
</template>
|
|
123
|
-
</ComponentDemo>
|
|
124
|
-
|
|
125
100
|
## Validation Libraries
|
|
126
101
|
|
|
127
102
|
`useFormValidator` relies on [Standard Schema](https://standardschema.dev), a common interface shared by the main validation libraries. Maz-UI does not ship any validation library: install the one you prefer and pass its schemas. Valibot, Zod and ArkType are declared as optional peer dependencies of `maz-ui`, so your package manager warns you if the installed version is not supported.
|
|
@@ -137,8 +112,6 @@ The `schema` option is always a **plain object** where each key is a field name
|
|
|
137
112
|
|
|
138
113
|
### Same form, different libraries
|
|
139
114
|
|
|
140
|
-
::: code-group
|
|
141
|
-
|
|
142
115
|
```ts [valibot]
|
|
143
116
|
import { useFormValidator } from 'maz-ui/composables'
|
|
144
117
|
import { email, minLength, nonEmpty, pipe, string } from 'valibot'
|
|
@@ -175,34 +148,8 @@ const { model, errorMessages, handleSubmit } = useFormValidator({
|
|
|
175
148
|
})
|
|
176
149
|
```
|
|
177
150
|
|
|
178
|
-
:::
|
|
179
|
-
|
|
180
151
|
### Live example with Zod
|
|
181
152
|
|
|
182
|
-
<ComponentDemo>
|
|
183
|
-
<form class="maz:flex maz:flex-col maz:gap-4" @submit="onSubmitZod">
|
|
184
|
-
<MazInput
|
|
185
|
-
v-model="zodModel.username"
|
|
186
|
-
label="Username"
|
|
187
|
-
:hint="zodErrors.username"
|
|
188
|
-
:error="!!zodErrors.username"
|
|
189
|
-
:success="zodStates.username.valid"
|
|
190
|
-
/>
|
|
191
|
-
<MazInput
|
|
192
|
-
v-model="zodModel.age"
|
|
193
|
-
label="Age"
|
|
194
|
-
type="number"
|
|
195
|
-
:hint="zodErrors.age"
|
|
196
|
-
:error="!!zodErrors.age"
|
|
197
|
-
:success="zodStates.age.valid"
|
|
198
|
-
/>
|
|
199
|
-
<MazBtn type="submit" :loading="zodSubmitting">
|
|
200
|
-
Submit
|
|
201
|
-
</MazBtn>
|
|
202
|
-
</form>
|
|
203
|
-
|
|
204
|
-
<template #code>
|
|
205
|
-
|
|
206
153
|
```vue
|
|
207
154
|
<script lang="ts" setup>
|
|
208
155
|
import { useFormValidator } from 'maz-ui/composables'
|
|
@@ -251,9 +198,6 @@ const onSubmit = handleSubmit((data) => {
|
|
|
251
198
|
</template>
|
|
252
199
|
```
|
|
253
200
|
|
|
254
|
-
</template>
|
|
255
|
-
</ComponentDemo>
|
|
256
|
-
|
|
257
201
|
### Mixing libraries
|
|
258
202
|
|
|
259
203
|
Each field is validated by its own schema, so you can mix libraries in the same form (useful during a progressive migration from one library to another):
|
|
@@ -335,36 +279,6 @@ Each field in `fieldsStates` contains:
|
|
|
335
279
|
| `validated` | `boolean` | Validation has run at least once |
|
|
336
280
|
| `validating` | `boolean` | Async validation is in progress |
|
|
337
281
|
|
|
338
|
-
<ComponentDemo>
|
|
339
|
-
<div class="maz:flex maz:flex-col maz:gap-4">
|
|
340
|
-
<form class="maz:flex maz:flex-col maz:gap-4" @submit="onSubmitState">
|
|
341
|
-
<MazInput
|
|
342
|
-
v-model="stateModel.name"
|
|
343
|
-
label="Name (min 3 characters)"
|
|
344
|
-
:hint="stateErrors.name"
|
|
345
|
-
:error="!!stateErrors.name"
|
|
346
|
-
:success="stateFields.name.valid"
|
|
347
|
-
/>
|
|
348
|
-
<MazInput
|
|
349
|
-
v-model="stateModel.age"
|
|
350
|
-
label="Age (18-100)"
|
|
351
|
-
type="number"
|
|
352
|
-
:hint="stateErrors.age"
|
|
353
|
-
:error="!!stateErrors.age"
|
|
354
|
-
:success="stateFields.age.valid"
|
|
355
|
-
/>
|
|
356
|
-
<MazBtn type="submit">Submit</MazBtn>
|
|
357
|
-
</form>
|
|
358
|
-
<div class="maz:rounded-md">
|
|
359
|
-
<p class="maz:font-semibold maz:mb-2">Form State:</p>
|
|
360
|
-
<pre class="maz:text-xs maz:bg-surface-600/70 maz:dark:bg-surface-600/60 maz:p-2 maz:rounded-md">{{ JSON.stringify({ isValid: stateValid, isDirty: stateDirty, isSubmitted: stateSubmitted, isSubmitting: stateSubmitting }, null, 2) }}</pre>
|
|
361
|
-
<p class="maz:font-semibold maz:mb-2 maz:mt-4">Fields States:</p>
|
|
362
|
-
<pre class="maz:text-xs maz:bg-surface-600/70 maz:dark:bg-surface-600/60 maz:p-2 maz:rounded-md">{{ JSON.stringify(stateFields, null, 2) }}</pre>
|
|
363
|
-
</div>
|
|
364
|
-
</div>
|
|
365
|
-
|
|
366
|
-
<template #code>
|
|
367
|
-
|
|
368
282
|
```vue
|
|
369
283
|
<script lang="ts" setup>
|
|
370
284
|
import { useFormValidator } from 'maz-ui/composables'
|
|
@@ -403,9 +317,6 @@ const onSubmit = handleSubmit((data) => {
|
|
|
403
317
|
</template>
|
|
404
318
|
```
|
|
405
319
|
|
|
406
|
-
</template>
|
|
407
|
-
</ComponentDemo>
|
|
408
|
-
|
|
409
320
|
## Validation Modes
|
|
410
321
|
|
|
411
322
|
Validation modes control **when** validation runs and **when** errors are displayed. Choose the mode that best fits your UX needs.
|
|
@@ -418,41 +329,14 @@ Validation modes control **when** validation runs and **when** errors are displa
|
|
|
418
329
|
| `blur` | Only on blur | After blur | Minimal interruption |
|
|
419
330
|
| `progressive` | Silently, shows on blur if invalid | After blur or validation | Optimal UX |
|
|
420
331
|
|
|
421
|
-
|
|
332
|
+
**Tip:**
|
|
333
|
+
|
|
422
334
|
For `eager`, `blur`, and `progressive` modes, you must use `useFormField` with the `ref` option or `validationEvents` to capture blur events.
|
|
423
|
-
:::
|
|
424
335
|
|
|
425
336
|
### Lazy Mode (Default)
|
|
426
337
|
|
|
427
338
|
The default mode. Validates when field values change. Errors only appear if the field is not empty.
|
|
428
339
|
|
|
429
|
-
<ComponentDemo>
|
|
430
|
-
<div class="maz:mb-4">
|
|
431
|
-
<p class="maz:text-sm maz:text-muted">Type in the field and clear it - notice the error appears only when there's content.</p>
|
|
432
|
-
</div>
|
|
433
|
-
<form class="maz:flex maz:flex-col maz:gap-4" @submit="onSubmitLazy">
|
|
434
|
-
<MazInput
|
|
435
|
-
v-model="lazyModel.name"
|
|
436
|
-
label="Name (min 3 characters)"
|
|
437
|
-
:hint="lazyErrors.name"
|
|
438
|
-
:error="!!lazyErrors.name"
|
|
439
|
-
:success="lazyStates.name.valid"
|
|
440
|
-
:class="{ 'has-error-lazy': !!lazyErrors.name }"
|
|
441
|
-
/>
|
|
442
|
-
<MazInput
|
|
443
|
-
v-model="lazyModel.email"
|
|
444
|
-
label="Email"
|
|
445
|
-
type="email"
|
|
446
|
-
:hint="lazyErrors.email"
|
|
447
|
-
:error="!!lazyErrors.email"
|
|
448
|
-
:success="lazyStates.email.valid"
|
|
449
|
-
:class="{ 'has-error-lazy': !!lazyErrors.email }"
|
|
450
|
-
/>
|
|
451
|
-
<MazBtn type="submit" :loading="lazySubmitting">Submit</MazBtn>
|
|
452
|
-
</form>
|
|
453
|
-
|
|
454
|
-
<template #code>
|
|
455
|
-
|
|
456
340
|
```vue
|
|
457
341
|
<script lang="ts" setup>
|
|
458
342
|
import { useFormValidator } from 'maz-ui/composables'
|
|
@@ -477,38 +361,10 @@ const onSubmit = handleSubmit((data) => {
|
|
|
477
361
|
</script>
|
|
478
362
|
```
|
|
479
363
|
|
|
480
|
-
</template>
|
|
481
|
-
</ComponentDemo>
|
|
482
|
-
|
|
483
364
|
### Aggressive Mode
|
|
484
365
|
|
|
485
366
|
Validates all fields immediately when the form is created and on every change. Errors are always displayed.
|
|
486
367
|
|
|
487
|
-
<ComponentDemo>
|
|
488
|
-
<div class="maz:mb-4">
|
|
489
|
-
<p class="maz:text-sm maz:text-muted">Notice all fields show errors immediately, even before any interaction.</p>
|
|
490
|
-
</div>
|
|
491
|
-
<form class="maz:flex maz:flex-col maz:gap-4" @submit="onSubmitAggressive">
|
|
492
|
-
<MazInput
|
|
493
|
-
v-model="aggressiveModel.name"
|
|
494
|
-
label="Name (min 3 characters)"
|
|
495
|
-
:hint="aggressiveErrors.name"
|
|
496
|
-
:error="!!aggressiveErrors.name"
|
|
497
|
-
:success="aggressiveStates.name.valid"
|
|
498
|
-
/>
|
|
499
|
-
<MazInput
|
|
500
|
-
v-model="aggressiveModel.email"
|
|
501
|
-
label="Email"
|
|
502
|
-
type="email"
|
|
503
|
-
:hint="aggressiveErrors.email"
|
|
504
|
-
:error="!!aggressiveErrors.email"
|
|
505
|
-
:success="aggressiveStates.email.valid"
|
|
506
|
-
/>
|
|
507
|
-
<MazBtn type="submit" :loading="aggressiveSubmitting">Submit</MazBtn>
|
|
508
|
-
</form>
|
|
509
|
-
|
|
510
|
-
<template #code>
|
|
511
|
-
|
|
512
368
|
```vue
|
|
513
369
|
<script lang="ts" setup>
|
|
514
370
|
import { useFormValidator } from 'maz-ui/composables'
|
|
@@ -528,45 +384,13 @@ const { model, errorMessages, fieldsStates, isSubmitting, handleSubmit } = useFo
|
|
|
528
384
|
</script>
|
|
529
385
|
```
|
|
530
386
|
|
|
531
|
-
</template>
|
|
532
|
-
</ComponentDemo>
|
|
533
|
-
|
|
534
387
|
### Eager Mode (Recommended)
|
|
535
388
|
|
|
536
389
|
Validates on blur first (if the field is not empty), then on every change. This provides a good balance between immediate feedback and not overwhelming the user.
|
|
537
390
|
|
|
538
|
-
|
|
539
|
-
Requires `useFormField` with `ref` option or `validationEvents`.
|
|
540
|
-
:::
|
|
541
|
-
|
|
542
|
-
<ComponentDemo>
|
|
543
|
-
<div class="maz:mb-4">
|
|
544
|
-
<p class="maz:text-sm maz:text-muted">Type something, then click outside the field (blur) to see validation. After that, errors update as you type.</p>
|
|
545
|
-
</div>
|
|
546
|
-
<form class="maz:flex maz:flex-col maz:gap-4" @submit="onSubmitEager">
|
|
547
|
-
<MazInput
|
|
548
|
-
ref="eagerNameRef"
|
|
549
|
-
v-model="eagerName"
|
|
550
|
-
label="Name (min 3 characters)"
|
|
551
|
-
:hint="eagerNameError"
|
|
552
|
-
:error="eagerNameHasError"
|
|
553
|
-
:success="eagerNameValid"
|
|
554
|
-
:class="{ 'has-error-eager': eagerNameHasError }"
|
|
555
|
-
/>
|
|
556
|
-
<MazInput
|
|
557
|
-
ref="eagerEmailRef"
|
|
558
|
-
v-model="eagerEmail"
|
|
559
|
-
label="Email"
|
|
560
|
-
type="email"
|
|
561
|
-
:hint="eagerEmailError"
|
|
562
|
-
:error="eagerEmailHasError"
|
|
563
|
-
:success="eagerEmailValid"
|
|
564
|
-
:class="{ 'has-error-eager': eagerEmailHasError }"
|
|
565
|
-
/>
|
|
566
|
-
<MazBtn type="submit" :loading="eagerSubmitting" :disabled="!eagerIsValid">Submit</MazBtn>
|
|
567
|
-
</form>
|
|
391
|
+
**Warning:**
|
|
568
392
|
|
|
569
|
-
|
|
393
|
+
Requires `useFormField` with `ref` option or `validationEvents`.
|
|
570
394
|
|
|
571
395
|
```vue
|
|
572
396
|
<script lang="ts" setup>
|
|
@@ -634,46 +458,14 @@ const {
|
|
|
634
458
|
</template>
|
|
635
459
|
```
|
|
636
460
|
|
|
637
|
-
</template>
|
|
638
|
-
</ComponentDemo>
|
|
639
|
-
|
|
640
461
|
### Blur Mode
|
|
641
462
|
|
|
642
|
-
|
|
463
|
+
**Warning:**
|
|
464
|
+
|
|
643
465
|
Requires `useFormField` with `ref` option or `validationEvents`.
|
|
644
|
-
:::
|
|
645
466
|
|
|
646
467
|
Validates only when the field loses focus. Errors are only shown after blur.
|
|
647
468
|
|
|
648
|
-
<ComponentDemo>
|
|
649
|
-
<div class="maz:mb-4">
|
|
650
|
-
<p class="maz:text-sm maz:text-muted">Type in the field, then click outside. Errors only appear after blur, and don't update while typing.</p>
|
|
651
|
-
</div>
|
|
652
|
-
<form class="maz:flex maz:flex-col maz:gap-4" @submit="onSubmitBlur">
|
|
653
|
-
<MazInput
|
|
654
|
-
ref="blurNameRef"
|
|
655
|
-
v-model="blurName"
|
|
656
|
-
label="Name (min 3 characters)"
|
|
657
|
-
:hint="blurNameError"
|
|
658
|
-
:error="blurNameHasError"
|
|
659
|
-
:success="blurNameValid"
|
|
660
|
-
:class="{ 'has-error-blur': blurNameHasError }"
|
|
661
|
-
/>
|
|
662
|
-
<MazInput
|
|
663
|
-
ref="blurEmailRef"
|
|
664
|
-
v-model="blurEmail"
|
|
665
|
-
label="Email"
|
|
666
|
-
type="email"
|
|
667
|
-
:hint="blurEmailError"
|
|
668
|
-
:error="blurEmailHasError"
|
|
669
|
-
:success="blurEmailValid"
|
|
670
|
-
:class="{ 'has-error-blur': blurEmailHasError }"
|
|
671
|
-
/>
|
|
672
|
-
<MazBtn type="submit" :loading="blurSubmitting">Submit</MazBtn>
|
|
673
|
-
</form>
|
|
674
|
-
|
|
675
|
-
<template #code>
|
|
676
|
-
|
|
677
469
|
```vue
|
|
678
470
|
<script lang="ts" setup>
|
|
679
471
|
import { useFormValidator, useFormField } from 'maz-ui/composables'
|
|
@@ -705,55 +497,14 @@ const { value: email, hasError: emailHasError, errorMessage: emailError, isValid
|
|
|
705
497
|
</script>
|
|
706
498
|
```
|
|
707
499
|
|
|
708
|
-
</template>
|
|
709
|
-
</ComponentDemo>
|
|
710
|
-
|
|
711
500
|
### Progressive Mode
|
|
712
501
|
|
|
713
|
-
|
|
502
|
+
**Warning:**
|
|
503
|
+
|
|
714
504
|
Requires `useFormField` with `ref` option or `validationEvents`.
|
|
715
|
-
:::
|
|
716
505
|
|
|
717
506
|
The most user-friendly mode. Validates silently in the background. Shows errors only on blur if the field is invalid. Once valid, it stays valid until it becomes invalid again.
|
|
718
507
|
|
|
719
|
-
<ComponentDemo>
|
|
720
|
-
<div class="maz:mb-4">
|
|
721
|
-
<p class="maz:text-sm maz:text-muted">Start typing - the field becomes valid (green) as soon as validation passes. Errors only show after blur.</p>
|
|
722
|
-
</div>
|
|
723
|
-
<form class="maz:flex maz:flex-col maz:gap-4" @submit="onSubmitProgressive">
|
|
724
|
-
<MazInput
|
|
725
|
-
ref="progressiveNameRef"
|
|
726
|
-
v-model="progressiveName"
|
|
727
|
-
label="Name (min 3 characters)"
|
|
728
|
-
:hint="progressiveNameError"
|
|
729
|
-
:error="progressiveNameHasError"
|
|
730
|
-
:success="progressiveNameValid"
|
|
731
|
-
:class="{ 'has-error-progressive': progressiveNameHasError }"
|
|
732
|
-
/>
|
|
733
|
-
<MazInput
|
|
734
|
-
ref="progressiveEmailRef"
|
|
735
|
-
v-model="progressiveEmail"
|
|
736
|
-
label="Email"
|
|
737
|
-
type="email"
|
|
738
|
-
:hint="progressiveEmailError"
|
|
739
|
-
:error="progressiveEmailHasError"
|
|
740
|
-
:success="progressiveEmailValid"
|
|
741
|
-
:class="{ 'has-error-progressive': progressiveEmailHasError }"
|
|
742
|
-
/>
|
|
743
|
-
<MazCheckbox
|
|
744
|
-
ref="progressiveAgreeRef"
|
|
745
|
-
v-model="progressiveAgree"
|
|
746
|
-
:hint="progressiveAgreeError"
|
|
747
|
-
:error="progressiveAgreeHasError"
|
|
748
|
-
:class="{ 'has-error-progressive': progressiveAgreeHasError }"
|
|
749
|
-
>
|
|
750
|
-
I agree to the terms and conditions
|
|
751
|
-
</MazCheckbox>
|
|
752
|
-
<MazBtn type="submit" :loading="progressiveSubmitting">Submit</MazBtn>
|
|
753
|
-
</form>
|
|
754
|
-
|
|
755
|
-
<template #code>
|
|
756
|
-
|
|
757
508
|
```vue
|
|
758
509
|
<script lang="ts" setup>
|
|
759
510
|
import { useFormValidator, useFormField } from 'maz-ui/composables'
|
|
@@ -782,9 +533,6 @@ const { value: name, hasError, errorMessage, isValid } = useFormField<string>('n
|
|
|
782
533
|
</script>
|
|
783
534
|
```
|
|
784
535
|
|
|
785
|
-
</template>
|
|
786
|
-
</ComponentDemo>
|
|
787
|
-
|
|
788
536
|
## useFormField for Child Components
|
|
789
537
|
|
|
790
538
|
`useFormField` is essential when:
|
|
@@ -918,7 +666,8 @@ const { value } = useFormField<string>('name', { formIdentifier: 'my-form' })
|
|
|
918
666
|
// value is typed as WritableComputedRef<string>
|
|
919
667
|
```
|
|
920
668
|
|
|
921
|
-
|
|
669
|
+
**Warning: Common TypeScript Errors**
|
|
670
|
+
|
|
922
671
|
If you get circular reference errors with `useTemplateRef`, use the classic `ref()` instead:
|
|
923
672
|
|
|
924
673
|
```ts
|
|
@@ -939,8 +688,6 @@ const { value: email } = useFormField<string>('email', {
|
|
|
939
688
|
})
|
|
940
689
|
```
|
|
941
690
|
|
|
942
|
-
:::
|
|
943
|
-
|
|
944
691
|
## Async Validation
|
|
945
692
|
|
|
946
693
|
Async validations (like checking username availability) are supported by every library: Valibot's `pipeAsync` and `checkAsync`, Zod's async `refine`, etc. The example below uses Valibot, here is the Zod equivalent of the schema:
|
|
@@ -961,26 +708,6 @@ const schema = {
|
|
|
961
708
|
}
|
|
962
709
|
```
|
|
963
710
|
|
|
964
|
-
<ComponentDemo>
|
|
965
|
-
<div class="maz:mb-4">
|
|
966
|
-
<p class="maz:text-sm maz:text-muted">Try typing "taken" - the async validator will reject it after a 2-second delay.</p>
|
|
967
|
-
</div>
|
|
968
|
-
<form class="maz:flex maz:gap-4" @submit="onSubmitAsync">
|
|
969
|
-
<MazInput
|
|
970
|
-
ref="asyncUsernameRef"
|
|
971
|
-
v-model="asyncUsername"
|
|
972
|
-
label="Username"
|
|
973
|
-
:hint="asyncUsernameError"
|
|
974
|
-
:error="asyncUsernameHasError"
|
|
975
|
-
:success="asyncUsernameValid"
|
|
976
|
-
:loading="asyncUsernameValidating"
|
|
977
|
-
class="maz:flex-1"
|
|
978
|
-
/>
|
|
979
|
-
<MazBtn type="submit" :loading="asyncSubmitting">Submit</MazBtn>
|
|
980
|
-
</form>
|
|
981
|
-
|
|
982
|
-
<template #code>
|
|
983
|
-
|
|
984
711
|
```vue
|
|
985
712
|
<script lang="ts" setup>
|
|
986
713
|
import { useFormValidator, useFormField } from 'maz-ui/composables'
|
|
@@ -1029,9 +756,6 @@ const {
|
|
|
1029
756
|
</template>
|
|
1030
757
|
```
|
|
1031
758
|
|
|
1032
|
-
</template>
|
|
1033
|
-
</ComponentDemo>
|
|
1034
|
-
|
|
1035
759
|
## Throttling and Debouncing
|
|
1036
760
|
|
|
1037
761
|
For expensive validations (like API calls), use throttling or debouncing to limit how often validation runs.
|
|
@@ -1041,31 +765,6 @@ For expensive validations (like API calls), use throttling or debouncing to limi
|
|
|
1041
765
|
| `debouncedFields` | Waits until user stops typing | 300ms | Search fields, API calls |
|
|
1042
766
|
| `throttledFields` | Runs at most once per interval | 1000ms | Rate-limited APIs |
|
|
1043
767
|
|
|
1044
|
-
<ComponentDemo>
|
|
1045
|
-
<div class="maz:mb-4">
|
|
1046
|
-
<p class="maz:text-sm maz:text-muted">Name has 500ms debounce, Age has 1000ms throttle. Watch the console to see validation timing.</p>
|
|
1047
|
-
</div>
|
|
1048
|
-
<form class="maz:flex maz:flex-col maz:gap-4" @submit="onSubmitDebounced">
|
|
1049
|
-
<MazInput
|
|
1050
|
-
v-model="debouncedModel.name"
|
|
1051
|
-
label="Name (debounced 500ms)"
|
|
1052
|
-
:hint="debouncedErrors.name"
|
|
1053
|
-
:error="debouncedStates.name.error"
|
|
1054
|
-
:success="debouncedStates.name.valid"
|
|
1055
|
-
/>
|
|
1056
|
-
<MazInput
|
|
1057
|
-
v-model="debouncedModel.age"
|
|
1058
|
-
label="Age (throttled 1000ms)"
|
|
1059
|
-
type="number"
|
|
1060
|
-
:hint="debouncedErrors.age"
|
|
1061
|
-
:error="debouncedStates.age.error"
|
|
1062
|
-
:success="debouncedStates.age.valid"
|
|
1063
|
-
/>
|
|
1064
|
-
<MazBtn type="submit" :loading="debouncedSubmitting">Submit</MazBtn>
|
|
1065
|
-
</form>
|
|
1066
|
-
|
|
1067
|
-
<template #code>
|
|
1068
|
-
|
|
1069
768
|
```vue
|
|
1070
769
|
<script lang="ts" setup>
|
|
1071
770
|
import { useFormValidator } from 'maz-ui/composables'
|
|
@@ -1087,38 +786,10 @@ const { model, errorMessages, fieldsStates, isSubmitting, handleSubmit } = useFo
|
|
|
1087
786
|
</script>
|
|
1088
787
|
```
|
|
1089
788
|
|
|
1090
|
-
</template>
|
|
1091
|
-
</ComponentDemo>
|
|
1092
|
-
|
|
1093
789
|
## Reset Form
|
|
1094
790
|
|
|
1095
791
|
Use `resetForm()` to reset the form to its initial state, or set `resetOnSuccess` to automatically reset after successful submission.
|
|
1096
792
|
|
|
1097
|
-
<ComponentDemo>
|
|
1098
|
-
<form class="maz:flex maz:flex-col maz:gap-4" @submit="onSubmitReset">
|
|
1099
|
-
<MazInput
|
|
1100
|
-
v-model="resetModel.name"
|
|
1101
|
-
label="Name"
|
|
1102
|
-
:hint="resetErrors.name"
|
|
1103
|
-
:error="resetStates.name.error"
|
|
1104
|
-
:success="resetStates.name.valid"
|
|
1105
|
-
/>
|
|
1106
|
-
<MazInput
|
|
1107
|
-
v-model="resetModel.age"
|
|
1108
|
-
label="Age"
|
|
1109
|
-
type="number"
|
|
1110
|
-
:hint="resetErrors.age"
|
|
1111
|
-
:error="resetStates.age.error"
|
|
1112
|
-
:success="resetStates.age.valid"
|
|
1113
|
-
/>
|
|
1114
|
-
<div class="maz:flex maz:gap-2">
|
|
1115
|
-
<MazBtn type="submit" :loading="resetSubmitting">Submit</MazBtn>
|
|
1116
|
-
<MazBtn type="button" color="destructive" @click="resetFormFn">Reset</MazBtn>
|
|
1117
|
-
</div>
|
|
1118
|
-
</form>
|
|
1119
|
-
|
|
1120
|
-
<template #code>
|
|
1121
|
-
|
|
1122
793
|
```vue
|
|
1123
794
|
<script lang="ts" setup>
|
|
1124
795
|
import { useFormValidator } from 'maz-ui/composables'
|
|
@@ -1149,9 +820,6 @@ function handleReset() {
|
|
|
1149
820
|
</script>
|
|
1150
821
|
```
|
|
1151
822
|
|
|
1152
|
-
</template>
|
|
1153
|
-
</ComponentDemo>
|
|
1154
|
-
|
|
1155
823
|
## Multiple Forms on Same Page
|
|
1156
824
|
|
|
1157
825
|
Use the `identifier` option to have multiple independent forms on the same page. Make sure to match the identifier in both `useFormValidator` and `useFormField`.
|
|
@@ -1453,345 +1121,3 @@ const { value } = useFormField<string>('email', {
|
|
|
1453
1121
|
formIdentifier: 'my-form', // ...must match this one
|
|
1454
1122
|
})
|
|
1455
1123
|
```
|
|
1456
|
-
|
|
1457
|
-
<script lang="ts" setup>
|
|
1458
|
-
import { ref, useTemplateRef } from 'vue'
|
|
1459
|
-
import { useFormValidator } from 'maz-ui/composables/useFormValidator'
|
|
1460
|
-
import { useFormField } from 'maz-ui/composables/useFormField'
|
|
1461
|
-
import { useToast } from 'maz-ui/composables/useToast'
|
|
1462
|
-
import { sleep } from '@maz-ui/utils'
|
|
1463
|
-
import {
|
|
1464
|
-
string,
|
|
1465
|
-
nonEmpty,
|
|
1466
|
-
pipe,
|
|
1467
|
-
number,
|
|
1468
|
-
minValue,
|
|
1469
|
-
maxValue,
|
|
1470
|
-
boolean,
|
|
1471
|
-
literal,
|
|
1472
|
-
minLength,
|
|
1473
|
-
email,
|
|
1474
|
-
pipeAsync,
|
|
1475
|
-
checkAsync,
|
|
1476
|
-
} from 'valibot'
|
|
1477
|
-
import { z } from 'zod'
|
|
1478
|
-
|
|
1479
|
-
const toast = useToast()
|
|
1480
|
-
|
|
1481
|
-
// Zod Demo
|
|
1482
|
-
const zodSchema = {
|
|
1483
|
-
username: z.string().min(1, 'Username is required').min(3, 'Min 3 characters'),
|
|
1484
|
-
age: z.number({ error: 'Age is required' }).min(18, 'Min 18').max(100, 'Max 100'),
|
|
1485
|
-
}
|
|
1486
|
-
|
|
1487
|
-
const {
|
|
1488
|
-
model: zodModel,
|
|
1489
|
-
errorMessages: zodErrors,
|
|
1490
|
-
fieldsStates: zodStates,
|
|
1491
|
-
isSubmitting: zodSubmitting,
|
|
1492
|
-
handleSubmit: handleZod,
|
|
1493
|
-
} = useFormValidator({ schema: zodSchema })
|
|
1494
|
-
|
|
1495
|
-
const onSubmitZod = handleZod(async (data) => {
|
|
1496
|
-
await sleep(1000)
|
|
1497
|
-
toast.success(`Welcome ${data.username} (${data.age})!`, { position: 'top' })
|
|
1498
|
-
})
|
|
1499
|
-
|
|
1500
|
-
// Quick Start Demo
|
|
1501
|
-
const quickStartSchema = {
|
|
1502
|
-
email: pipe(string(), nonEmpty('Email is required'), email('Invalid email')),
|
|
1503
|
-
password: pipe(string(), nonEmpty('Password is required'), minLength(8, 'Min 8 characters')),
|
|
1504
|
-
}
|
|
1505
|
-
|
|
1506
|
-
const {
|
|
1507
|
-
model: quickStartModel,
|
|
1508
|
-
errorMessages: quickStartErrors,
|
|
1509
|
-
fieldsStates: quickStartStates,
|
|
1510
|
-
isSubmitting: quickStartSubmitting,
|
|
1511
|
-
handleSubmit: handleQuickStart,
|
|
1512
|
-
} = useFormValidator({ schema: quickStartSchema })
|
|
1513
|
-
|
|
1514
|
-
const onSubmitQuickStart = handleQuickStart(async () => {
|
|
1515
|
-
await sleep(1000)
|
|
1516
|
-
toast.success('Login successful!', { position: 'top' })
|
|
1517
|
-
})
|
|
1518
|
-
|
|
1519
|
-
// Form State Demo
|
|
1520
|
-
const stateSchema = {
|
|
1521
|
-
name: pipe(string(), nonEmpty('Required'), minLength(3, 'Min 3 characters')),
|
|
1522
|
-
age: pipe(number('Must be a number'), minValue(18, 'Min 18'), maxValue(100, 'Max 100')),
|
|
1523
|
-
}
|
|
1524
|
-
|
|
1525
|
-
const {
|
|
1526
|
-
model: stateModel,
|
|
1527
|
-
errorMessages: stateErrors,
|
|
1528
|
-
fieldsStates: stateFields,
|
|
1529
|
-
isValid: stateValid,
|
|
1530
|
-
isDirty: stateDirty,
|
|
1531
|
-
isSubmitted: stateSubmitted,
|
|
1532
|
-
isSubmitting: stateSubmitting,
|
|
1533
|
-
handleSubmit: handleState,
|
|
1534
|
-
} = useFormValidator({ schema: stateSchema })
|
|
1535
|
-
|
|
1536
|
-
const onSubmitState = handleState(() => {
|
|
1537
|
-
toast.success('Submitted!', { position: 'top' })
|
|
1538
|
-
})
|
|
1539
|
-
|
|
1540
|
-
// Lazy Mode Demo
|
|
1541
|
-
const lazySchema = {
|
|
1542
|
-
name: pipe(string(), nonEmpty('Required'), minLength(3, 'Min 3 characters')),
|
|
1543
|
-
email: pipe(string(), nonEmpty('Required'), email('Invalid email')),
|
|
1544
|
-
}
|
|
1545
|
-
|
|
1546
|
-
const {
|
|
1547
|
-
model: lazyModel,
|
|
1548
|
-
errorMessages: lazyErrors,
|
|
1549
|
-
fieldsStates: lazyStates,
|
|
1550
|
-
isSubmitting: lazySubmitting,
|
|
1551
|
-
handleSubmit: handleLazy,
|
|
1552
|
-
} = useFormValidator({
|
|
1553
|
-
schema: lazySchema,
|
|
1554
|
-
options: { mode: 'lazy', scrollToError: '.has-error-lazy' },
|
|
1555
|
-
})
|
|
1556
|
-
|
|
1557
|
-
const onSubmitLazy = handleLazy(async () => {
|
|
1558
|
-
await sleep(1000)
|
|
1559
|
-
toast.success('Submitted!', { position: 'top' })
|
|
1560
|
-
})
|
|
1561
|
-
|
|
1562
|
-
// Aggressive Mode Demo
|
|
1563
|
-
const aggressiveSchema = {
|
|
1564
|
-
name: pipe(string(), nonEmpty('Required'), minLength(3, 'Min 3 characters')),
|
|
1565
|
-
email: pipe(string(), nonEmpty('Required'), email('Invalid email')),
|
|
1566
|
-
}
|
|
1567
|
-
|
|
1568
|
-
const {
|
|
1569
|
-
model: aggressiveModel,
|
|
1570
|
-
errorMessages: aggressiveErrors,
|
|
1571
|
-
fieldsStates: aggressiveStates,
|
|
1572
|
-
isSubmitting: aggressiveSubmitting,
|
|
1573
|
-
handleSubmit: handleAggressive,
|
|
1574
|
-
} = useFormValidator({
|
|
1575
|
-
schema: aggressiveSchema,
|
|
1576
|
-
options: { mode: 'aggressive' },
|
|
1577
|
-
})
|
|
1578
|
-
|
|
1579
|
-
const onSubmitAggressive = handleAggressive(async () => {
|
|
1580
|
-
await sleep(1000)
|
|
1581
|
-
toast.success('Submitted!', { position: 'top' })
|
|
1582
|
-
})
|
|
1583
|
-
|
|
1584
|
-
// Eager Mode Demo
|
|
1585
|
-
const eagerSchema = {
|
|
1586
|
-
name: pipe(string(), nonEmpty('Required'), minLength(3, 'Min 3 characters')),
|
|
1587
|
-
email: pipe(string(), nonEmpty('Required'), email('Invalid email')),
|
|
1588
|
-
}
|
|
1589
|
-
|
|
1590
|
-
const {
|
|
1591
|
-
isSubmitting: eagerSubmitting,
|
|
1592
|
-
handleSubmit: handleEager,
|
|
1593
|
-
isValid: eagerIsValid,
|
|
1594
|
-
} = useFormValidator({
|
|
1595
|
-
schema: eagerSchema,
|
|
1596
|
-
options: { mode: 'eager', scrollToError: '.has-error-eager', identifier: 'form-eager' },
|
|
1597
|
-
})
|
|
1598
|
-
|
|
1599
|
-
const {
|
|
1600
|
-
value: eagerName,
|
|
1601
|
-
hasError: eagerNameHasError,
|
|
1602
|
-
errorMessage: eagerNameError,
|
|
1603
|
-
isValid: eagerNameValid,
|
|
1604
|
-
} = useFormField<string>('name', {
|
|
1605
|
-
ref: useTemplateRef<HTMLInputElement>('eagerNameRef'),
|
|
1606
|
-
formIdentifier: 'form-eager',
|
|
1607
|
-
})
|
|
1608
|
-
|
|
1609
|
-
const {
|
|
1610
|
-
value: eagerEmail,
|
|
1611
|
-
hasError: eagerEmailHasError,
|
|
1612
|
-
errorMessage: eagerEmailError,
|
|
1613
|
-
isValid: eagerEmailValid,
|
|
1614
|
-
} = useFormField<string>('email', {
|
|
1615
|
-
ref: useTemplateRef<HTMLInputElement>('eagerEmailRef'),
|
|
1616
|
-
formIdentifier: 'form-eager',
|
|
1617
|
-
})
|
|
1618
|
-
|
|
1619
|
-
const onSubmitEager = handleEager(async () => {
|
|
1620
|
-
await sleep(1000)
|
|
1621
|
-
toast.success('Submitted!', { position: 'top' })
|
|
1622
|
-
})
|
|
1623
|
-
|
|
1624
|
-
// Blur Mode Demo
|
|
1625
|
-
const blurSchema = {
|
|
1626
|
-
name: pipe(string(), nonEmpty('Required'), minLength(3, 'Min 3 characters')),
|
|
1627
|
-
email: pipe(string(), nonEmpty('Required'), email('Invalid email')),
|
|
1628
|
-
}
|
|
1629
|
-
|
|
1630
|
-
const {
|
|
1631
|
-
isSubmitting: blurSubmitting,
|
|
1632
|
-
handleSubmit: handleBlur,
|
|
1633
|
-
} = useFormValidator({
|
|
1634
|
-
schema: blurSchema,
|
|
1635
|
-
options: { mode: 'blur', scrollToError: '.has-error-blur', identifier: 'form-blur' },
|
|
1636
|
-
})
|
|
1637
|
-
|
|
1638
|
-
const {
|
|
1639
|
-
value: blurName,
|
|
1640
|
-
hasError: blurNameHasError,
|
|
1641
|
-
errorMessage: blurNameError,
|
|
1642
|
-
isValid: blurNameValid,
|
|
1643
|
-
} = useFormField<string>('name', {
|
|
1644
|
-
ref: useTemplateRef<HTMLInputElement>('blurNameRef'),
|
|
1645
|
-
formIdentifier: 'form-blur',
|
|
1646
|
-
})
|
|
1647
|
-
|
|
1648
|
-
const {
|
|
1649
|
-
value: blurEmail,
|
|
1650
|
-
hasError: blurEmailHasError,
|
|
1651
|
-
errorMessage: blurEmailError,
|
|
1652
|
-
isValid: blurEmailValid,
|
|
1653
|
-
} = useFormField<string>('email', {
|
|
1654
|
-
ref: useTemplateRef<HTMLInputElement>('blurEmailRef'),
|
|
1655
|
-
formIdentifier: 'form-blur',
|
|
1656
|
-
})
|
|
1657
|
-
|
|
1658
|
-
const onSubmitBlur = handleBlur(async () => {
|
|
1659
|
-
await sleep(1000)
|
|
1660
|
-
toast.success('Submitted!', { position: 'top' })
|
|
1661
|
-
})
|
|
1662
|
-
|
|
1663
|
-
// Progressive Mode Demo
|
|
1664
|
-
const progressiveSchema = {
|
|
1665
|
-
name: pipe(string(), nonEmpty('Required'), minLength(3, 'Min 3 characters')),
|
|
1666
|
-
email: pipe(string(), nonEmpty('Required'), email('Invalid email')),
|
|
1667
|
-
agree: pipe(boolean(), literal(true, 'You must agree')),
|
|
1668
|
-
}
|
|
1669
|
-
|
|
1670
|
-
const {
|
|
1671
|
-
isSubmitting: progressiveSubmitting,
|
|
1672
|
-
handleSubmit: handleProgressive,
|
|
1673
|
-
} = useFormValidator({
|
|
1674
|
-
schema: progressiveSchema,
|
|
1675
|
-
options: { mode: 'progressive', scrollToError: '.has-error-progressive', identifier: 'form-progressive' },
|
|
1676
|
-
})
|
|
1677
|
-
|
|
1678
|
-
const {
|
|
1679
|
-
value: progressiveName,
|
|
1680
|
-
hasError: progressiveNameHasError,
|
|
1681
|
-
errorMessage: progressiveNameError,
|
|
1682
|
-
isValid: progressiveNameValid,
|
|
1683
|
-
} = useFormField<string>('name', {
|
|
1684
|
-
ref: useTemplateRef<HTMLInputElement>('progressiveNameRef'),
|
|
1685
|
-
formIdentifier: 'form-progressive',
|
|
1686
|
-
})
|
|
1687
|
-
|
|
1688
|
-
const {
|
|
1689
|
-
value: progressiveEmail,
|
|
1690
|
-
hasError: progressiveEmailHasError,
|
|
1691
|
-
errorMessage: progressiveEmailError,
|
|
1692
|
-
isValid: progressiveEmailValid,
|
|
1693
|
-
} = useFormField<string>('email', {
|
|
1694
|
-
ref: useTemplateRef<HTMLInputElement>('progressiveEmailRef'),
|
|
1695
|
-
formIdentifier: 'form-progressive',
|
|
1696
|
-
})
|
|
1697
|
-
|
|
1698
|
-
const {
|
|
1699
|
-
value: progressiveAgree,
|
|
1700
|
-
hasError: progressiveAgreeHasError,
|
|
1701
|
-
errorMessage: progressiveAgreeError,
|
|
1702
|
-
} = useFormField<boolean>('agree', {
|
|
1703
|
-
ref: useTemplateRef<HTMLInputElement>('progressiveAgreeRef'),
|
|
1704
|
-
formIdentifier: 'form-progressive',
|
|
1705
|
-
})
|
|
1706
|
-
|
|
1707
|
-
const onSubmitProgressive = handleProgressive(async () => {
|
|
1708
|
-
await sleep(1000)
|
|
1709
|
-
toast.success('Submitted!', { position: 'top' })
|
|
1710
|
-
})
|
|
1711
|
-
|
|
1712
|
-
// Async Validation Demo
|
|
1713
|
-
const asyncSchema = {
|
|
1714
|
-
username: pipeAsync(
|
|
1715
|
-
string(),
|
|
1716
|
-
nonEmpty('Username is required'),
|
|
1717
|
-
minLength(3, 'Min 3 characters'),
|
|
1718
|
-
checkAsync(async (value) => {
|
|
1719
|
-
await sleep(2000)
|
|
1720
|
-
return value !== 'taken'
|
|
1721
|
-
}, 'Username is already taken'),
|
|
1722
|
-
),
|
|
1723
|
-
}
|
|
1724
|
-
|
|
1725
|
-
const {
|
|
1726
|
-
isSubmitting: asyncSubmitting,
|
|
1727
|
-
handleSubmit: handleAsync,
|
|
1728
|
-
} = useFormValidator({
|
|
1729
|
-
schema: asyncSchema,
|
|
1730
|
-
options: { mode: 'eager', identifier: 'form-async' },
|
|
1731
|
-
})
|
|
1732
|
-
|
|
1733
|
-
const {
|
|
1734
|
-
value: asyncUsername,
|
|
1735
|
-
hasError: asyncUsernameHasError,
|
|
1736
|
-
errorMessage: asyncUsernameError,
|
|
1737
|
-
isValid: asyncUsernameValid,
|
|
1738
|
-
isValidating: asyncUsernameValidating,
|
|
1739
|
-
} = useFormField<string>('username', {
|
|
1740
|
-
ref: useTemplateRef<HTMLInputElement>('asyncUsernameRef'),
|
|
1741
|
-
formIdentifier: 'form-async',
|
|
1742
|
-
})
|
|
1743
|
-
|
|
1744
|
-
const onSubmitAsync = handleAsync(async () => {
|
|
1745
|
-
await sleep(1000)
|
|
1746
|
-
toast.success('Submitted!', { position: 'top' })
|
|
1747
|
-
})
|
|
1748
|
-
|
|
1749
|
-
// Debounce/Throttle Demo
|
|
1750
|
-
const debouncedSchema = {
|
|
1751
|
-
name: pipe(string(), nonEmpty('Required'), minLength(3, 'Min 3 characters')),
|
|
1752
|
-
age: pipe(number('Must be a number'), minValue(18, 'Must be 18+')),
|
|
1753
|
-
}
|
|
1754
|
-
|
|
1755
|
-
const {
|
|
1756
|
-
model: debouncedModel,
|
|
1757
|
-
errorMessages: debouncedErrors,
|
|
1758
|
-
fieldsStates: debouncedStates,
|
|
1759
|
-
isSubmitting: debouncedSubmitting,
|
|
1760
|
-
handleSubmit: handleDebounced,
|
|
1761
|
-
} = useFormValidator({
|
|
1762
|
-
schema: debouncedSchema,
|
|
1763
|
-
options: {
|
|
1764
|
-
debouncedFields: { name: 500 },
|
|
1765
|
-
throttledFields: { age: 1000 },
|
|
1766
|
-
},
|
|
1767
|
-
})
|
|
1768
|
-
|
|
1769
|
-
const onSubmitDebounced = handleDebounced(async () => {
|
|
1770
|
-
await sleep(1000)
|
|
1771
|
-
toast.success('Submitted!', { position: 'top' })
|
|
1772
|
-
})
|
|
1773
|
-
|
|
1774
|
-
// Reset Form Demo
|
|
1775
|
-
const resetSchema = {
|
|
1776
|
-
name: pipe(string(), nonEmpty('Required'), minLength(3, 'Min 3 characters')),
|
|
1777
|
-
age: pipe(number('Must be a number'), minValue(18, 'Must be 18+')),
|
|
1778
|
-
}
|
|
1779
|
-
|
|
1780
|
-
const {
|
|
1781
|
-
model: resetModel,
|
|
1782
|
-
errorMessages: resetErrors,
|
|
1783
|
-
fieldsStates: resetStates,
|
|
1784
|
-
isSubmitting: resetSubmitting,
|
|
1785
|
-
handleSubmit: handleReset,
|
|
1786
|
-
resetForm: resetFormFn,
|
|
1787
|
-
} = useFormValidator({
|
|
1788
|
-
schema: resetSchema,
|
|
1789
|
-
defaultValues: { name: 'John', age: 25 },
|
|
1790
|
-
options: { resetOnSuccess: false },
|
|
1791
|
-
})
|
|
1792
|
-
|
|
1793
|
-
const onSubmitReset = handleReset(async () => {
|
|
1794
|
-
await sleep(1000)
|
|
1795
|
-
toast.success('Submitted!', { position: 'top' })
|
|
1796
|
-
})
|
|
1797
|
-
</script>
|