@form-observer/vue 0.0.0-experimental-2
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/LICENSE +21 -0
- package/README.md +148 -0
- package/createFormValidityObserver.cjs +98 -0
- package/createFormValidityObserver.d.cts +10 -0
- package/createFormValidityObserver.d.ts +10 -0
- package/createFormValidityObserver.js +94 -0
- package/index.cjs +4 -0
- package/index.d.cts +3 -0
- package/index.d.ts +3 -0
- package/index.js +2 -0
- package/package.json +52 -0
- package/types.d.cts +68 -0
- package/types.d.ts +68 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 enthusiastic-js
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Form Observer: Vue Integration
|
|
2
|
+
|
|
3
|
+
A Vue-specific utility package that provides a more ergonomic developer experience for the complex classes in [`@form-observer/core`](https://www.npmjs.com/package/@form-observer/core). For convenience, this package also exposes all of the utilities in `@form-observer/core`.
|
|
4
|
+
|
|
5
|
+
What distinguishes `@form-observer/vue` from `@form-observer/core` is the enhanced developer experience that it provides for the `FormValidityObserver`. The core `FormValidityObserver` allows you to validate form fields as users interact with them, and to configure custom error messages for those fields. The `createFormValidityObserver` function provided by `@form-observer/vue` goes a step further by allowing you to configure the constraints for your fields as well.
|
|
6
|
+
|
|
7
|
+
## Features and Benefits
|
|
8
|
+
|
|
9
|
+
<!--
|
|
10
|
+
Note: This section should have the benefits listed in `@form-observer/core`, but the details should be catered to Vue.
|
|
11
|
+
-->
|
|
12
|
+
|
|
13
|
+
- **Performant**: The `Form Observer` leverages [event delegation](https://gomakethings.com/why-is-javascript-event-delegation-better-than-attaching-events-to-each-element/) to minimize memory usage. Moreover, it avoids any of the overhead that could come from relying on state.
|
|
14
|
+
- **No External Dependencies**: The `Form Observer` packs _a lot_ of power into a _tiny_ bundle to give your users the best experience.
|
|
15
|
+
- **Simple and Familiar API**: The `Form Observer` gives you a clear, easy-to-use API that has a similar feel to the standardized observers, such as the [`Mutation Observer`](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) and the [`Intersection Observer`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver).
|
|
16
|
+
- [**Web Component Support**](https://developer.mozilla.org/en-US/docs/Web/API/Web_components)
|
|
17
|
+
- **Flexible**: Without requiring any additional setup, the `Form Observer` allows you to work with fields dynamically added to (or removed from) your forms, fields [externally associated](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#form) with your forms, and more.
|
|
18
|
+
- **Easily Extendable**: If you have a set of sophisticated form logic that you'd like to reuse, you can [extend](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/extends) the `Form Observer` to encapsulate all of your functionality. We provide a [local storage](https://github.com/enthusiastic-js/form-observer/tree/main/docs/form-storage-observer) solution and a [form validation](https://github.com/enthusiastic-js/form-observer/blob/main/docs/form-validity-observer/integrations/vue.md) solution out of the box.
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
npm install @form-observer/vue
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
```vue
|
|
29
|
+
<template>
|
|
30
|
+
<form id="example" :ref="autoObserve()">
|
|
31
|
+
<h1>Feedback Form</h1>
|
|
32
|
+
|
|
33
|
+
<!-- The browser's default error messages for `#name` will be accessibly displayed inside `#name-error` -->
|
|
34
|
+
<label for="name">Full Name</label>
|
|
35
|
+
<input id="name" name="name" type="text" required aria-describedby="name-error" />
|
|
36
|
+
<div id="name-error"></div>
|
|
37
|
+
|
|
38
|
+
<!-- Custom error messages for `#email` will be accessibly displayed inside `#email-error` -->
|
|
39
|
+
<label for="email">Email</label>
|
|
40
|
+
<input
|
|
41
|
+
id="email"
|
|
42
|
+
v-bind="
|
|
43
|
+
configure('email', {
|
|
44
|
+
type: { value: 'email', message: 'Email is invalid' },
|
|
45
|
+
required: { message: 'You MUST allow us to stalk you!' },
|
|
46
|
+
})
|
|
47
|
+
"
|
|
48
|
+
aria-describedby="email-error"
|
|
49
|
+
/>
|
|
50
|
+
<div id="email-error"></div>
|
|
51
|
+
|
|
52
|
+
<!-- A custom error message will be accessibly displayed for the `pattern` constraint. -->
|
|
53
|
+
<!-- The browser's default error message will be accessibly displayed for the `required` constraint. -->
|
|
54
|
+
<label for="donation">Donation</label>
|
|
55
|
+
<input
|
|
56
|
+
id="donation"
|
|
57
|
+
v-bind="configure('donation', { pattern: { value: '\\d+', message: 'Please provide a valid number' } })"
|
|
58
|
+
inputmode="numeric"
|
|
59
|
+
required
|
|
60
|
+
aria-describedby="donation-error"
|
|
61
|
+
/>
|
|
62
|
+
<div id="donation-error"></div>
|
|
63
|
+
</form>
|
|
64
|
+
|
|
65
|
+
<label for="comments">Comments</label>
|
|
66
|
+
<textarea id="comments" name="comments" form="example" minlength="30" aria-describedby="comments-error"></textarea>
|
|
67
|
+
<div id="comments-error"></div>
|
|
68
|
+
|
|
69
|
+
<button type="submit" form="example">Submit</button>
|
|
70
|
+
</template>
|
|
71
|
+
|
|
72
|
+
<script setup>
|
|
73
|
+
import { createFormValidityObserver } from "@form-observer/vue";
|
|
74
|
+
const { autoObserve, configure, validateFields } = createFormValidityObserver("focusout");
|
|
75
|
+
|
|
76
|
+
function handleSubmit(event) {
|
|
77
|
+
event.preventDefault();
|
|
78
|
+
const success = validateFields({ focus: true });
|
|
79
|
+
|
|
80
|
+
if (success) {
|
|
81
|
+
// Submit data to server
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
</script>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
For more details on what `createFormValidityObserver` can do (like custom validation, manual error handling, and more), see our [documentation](https://github.com/enthusiastic-js/form-observer/blob/main/docs/form-validity-observer/integrations/vue.md).
|
|
88
|
+
|
|
89
|
+
## Other Uses
|
|
90
|
+
|
|
91
|
+
In addition to providing an enhanced version of the `FormValidityObserver`, `@form-observer/vue` exposes all of the utilities found in `@form-observer/core`. You can learn more about these tools from our [core documentation](https://github.com/enthusiastic-js/form-observer/tree/main/docs).
|
|
92
|
+
|
|
93
|
+
### `FormObserver`
|
|
94
|
+
|
|
95
|
+
```vue
|
|
96
|
+
<template>
|
|
97
|
+
<form id="example" ref="form" @submit="handleSubmit">
|
|
98
|
+
<!-- Internal Fields -->
|
|
99
|
+
</form>
|
|
100
|
+
</template>
|
|
101
|
+
|
|
102
|
+
<!-- External Fields -->
|
|
103
|
+
|
|
104
|
+
<script setup>
|
|
105
|
+
import { onMounted, onUnmounted, ref } from "vue";
|
|
106
|
+
import { FormObserver } from "@form-observer/vue";
|
|
107
|
+
|
|
108
|
+
const form = ref(null);
|
|
109
|
+
const observer = new FormObserver("focusout", (event) => event.target.setAttribute("data-visited", String(true)));
|
|
110
|
+
|
|
111
|
+
onMounted(() => observer.observe(form.value));
|
|
112
|
+
onUnmounted(() => observer.disconnect());
|
|
113
|
+
|
|
114
|
+
function handleSubmit(event) {
|
|
115
|
+
event.preventDefault();
|
|
116
|
+
const visitedFields = Array.from(event.currentTarget.elements).filter((e) => e.hasAttribute("data-visited"));
|
|
117
|
+
// Do something with visited fields...
|
|
118
|
+
}
|
|
119
|
+
</script>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### `FormStorageObserver`
|
|
123
|
+
|
|
124
|
+
```vue
|
|
125
|
+
<template>
|
|
126
|
+
<form id="example" ref="form" @submit="handleSubmit">
|
|
127
|
+
<!-- Internal Fields -->
|
|
128
|
+
</form>
|
|
129
|
+
</template>
|
|
130
|
+
|
|
131
|
+
<!-- External Fields -->
|
|
132
|
+
|
|
133
|
+
<script setup>
|
|
134
|
+
import { onMounted, onUnmounted, ref } from "vue";
|
|
135
|
+
import { FormStorageObserver } from "@form-observer/vue";
|
|
136
|
+
|
|
137
|
+
const form = ref(null);
|
|
138
|
+
const observer = new FormStorageObserver("change");
|
|
139
|
+
|
|
140
|
+
onMounted(() => observer.observe(form.value));
|
|
141
|
+
onUnmounted(() => observer.disconnect());
|
|
142
|
+
|
|
143
|
+
function handleSubmit(event) {
|
|
144
|
+
event.preventDefault();
|
|
145
|
+
FormStorageObserver.clear(event.currentTarget); // User no longer needs their progress saved after a form submission
|
|
146
|
+
}
|
|
147
|
+
</script>
|
|
148
|
+
```
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const FormValidityObserver = require("@form-observer/core/FormValidityObserver");
|
|
4
|
+
|
|
5
|
+
module.exports = createFormValidityObserver;
|
|
6
|
+
module.exports.default = createFormValidityObserver;
|
|
7
|
+
/**
|
|
8
|
+
* Creates an enhanced version of the {@link FormValidityObserver} that's more convenient for `Vue` apps
|
|
9
|
+
*
|
|
10
|
+
* @template {import("./index.d.cts").OneOrMany<import("./index.d.cts").EventType>} T
|
|
11
|
+
* @template [M=string]
|
|
12
|
+
* @param {T} types
|
|
13
|
+
* @param {import("./index.d.cts").FormValidityObserverOptions<M>} [options]
|
|
14
|
+
* @returns {import("./types.d.cts").VueFormValidityObserver<M>}
|
|
15
|
+
*/
|
|
16
|
+
function createFormValidityObserver(types, options) {
|
|
17
|
+
const observer = /** @type {import("./types.d.cts").VueFormValidityObserver<M>} */ (
|
|
18
|
+
/** @type {unknown} */ (new FormValidityObserver(types, options))
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
/* -------------------- Bindings -------------------- */
|
|
22
|
+
// Form Observer Methods
|
|
23
|
+
observer.observe = observer.observe.bind(observer);
|
|
24
|
+
observer.unobserve = observer.unobserve.bind(observer);
|
|
25
|
+
observer.disconnect = observer.disconnect.bind(observer);
|
|
26
|
+
|
|
27
|
+
// Validation Methods
|
|
28
|
+
observer.validateFields = observer.validateFields.bind(observer);
|
|
29
|
+
observer.validateField = observer.validateField.bind(observer);
|
|
30
|
+
observer.setFieldError = observer.setFieldError.bind(observer);
|
|
31
|
+
observer.clearFieldError = observer.clearFieldError.bind(observer);
|
|
32
|
+
|
|
33
|
+
/** **Private** reference to the original {@link FormValidityObserver.configure} method */
|
|
34
|
+
const originalConfigure = /** @type {FormValidityObserver<M>["configure"]} */ (observer.configure.bind(observer));
|
|
35
|
+
|
|
36
|
+
/* -------------------- Enhancements -------------------- */
|
|
37
|
+
// Add automatic setup/teardown
|
|
38
|
+
observer.autoObserve = function autoObserve(novalidate = true) {
|
|
39
|
+
/** @type {HTMLFormElement | null} */
|
|
40
|
+
let form;
|
|
41
|
+
|
|
42
|
+
/** @param {typeof form} vueRef @returns {void} */
|
|
43
|
+
return (vueRef) => {
|
|
44
|
+
if (vueRef) {
|
|
45
|
+
form = vueRef;
|
|
46
|
+
observer.observe(form);
|
|
47
|
+
if (novalidate) form.setAttribute("novalidate", "");
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
observer.unobserve(/** @type {HTMLFormElement} */ (form));
|
|
52
|
+
form = null;
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// Enhanced `configure` method
|
|
57
|
+
observer.configure = function configure(name, errorMessages) {
|
|
58
|
+
const keys = /** @type {Array<keyof import("./types.d.cts").VueValidationErrors<M>>} */ (Object.keys(errorMessages));
|
|
59
|
+
const props = /** @type {import("./types.d.cts").VueFieldProps} */ ({ name });
|
|
60
|
+
const config = /** @type {import("./index.d.cts").ValidationErrors<M>} */ ({});
|
|
61
|
+
|
|
62
|
+
// Build `props` object and error `config` object from `errorMessages`
|
|
63
|
+
for (let i = 0; i < keys.length; i++) {
|
|
64
|
+
const constraint = keys[i];
|
|
65
|
+
|
|
66
|
+
// Type narrowing on each individual property would be redundant and increase bundle size. So we're using `any`.
|
|
67
|
+
const constraintValue = /** @type {any} */ (errorMessages[constraint]);
|
|
68
|
+
|
|
69
|
+
// Constraint Was Omitted
|
|
70
|
+
if (constraintValue == null) continue;
|
|
71
|
+
if (constraint === "required" && constraintValue === false) continue;
|
|
72
|
+
|
|
73
|
+
/* ----- Custom Validation Properties ----- */
|
|
74
|
+
if (constraint === "badinput" || constraint === "validate") {
|
|
75
|
+
config[constraint] = constraintValue;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/* ----- Standrd HTML Attributes ----- */
|
|
80
|
+
// Value Only
|
|
81
|
+
if (typeof constraintValue !== "object") {
|
|
82
|
+
if (constraint === "required" && typeof constraintValue !== "boolean") config[constraint] = constraintValue;
|
|
83
|
+
props[constraint] = constraint === "required" ? true : constraintValue;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Value and Message
|
|
88
|
+
if (constraint === "required" && constraintValue.value === false) continue;
|
|
89
|
+
props[constraint] = constraintValue.value;
|
|
90
|
+
config[constraint] = constraintValue;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
originalConfigure(name, config);
|
|
94
|
+
return props;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
return observer;
|
|
98
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates an enhanced version of the {@link FormValidityObserver} that's more convenient for `Vue` apps
|
|
3
|
+
*
|
|
4
|
+
* @template {import("./index.d.cts").OneOrMany<import("./index.d.cts").EventType>} T
|
|
5
|
+
* @template [M=string]
|
|
6
|
+
* @param {T} types
|
|
7
|
+
* @param {import("./index.d.cts").FormValidityObserverOptions<M>} [options]
|
|
8
|
+
* @returns {import("./types.d.cts").VueFormValidityObserver<M>}
|
|
9
|
+
*/
|
|
10
|
+
export default function createFormValidityObserver<T extends import("@form-observer/core/types").OneOrMany<keyof DocumentEventMap>, M = string>(types: T, options?: import("@form-observer/core/FormValidityObserver").FormValidityObserverOptions<M> | undefined): import("./types.d.cts").VueFormValidityObserver<M>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates an enhanced version of the {@link FormValidityObserver} that's more convenient for `Vue` apps
|
|
3
|
+
*
|
|
4
|
+
* @template {import("./index.d.ts").OneOrMany<import("./index.d.ts").EventType>} T
|
|
5
|
+
* @template [M=string]
|
|
6
|
+
* @param {T} types
|
|
7
|
+
* @param {import("./index.d.ts").FormValidityObserverOptions<M>} [options]
|
|
8
|
+
* @returns {import("./types.d.ts").VueFormValidityObserver<M>}
|
|
9
|
+
*/
|
|
10
|
+
export default function createFormValidityObserver<T extends import("@form-observer/core/types").OneOrMany<keyof DocumentEventMap>, M = string>(types: T, options?: import("@form-observer/core/FormValidityObserver").FormValidityObserverOptions<M> | undefined): import("./types.d.ts").VueFormValidityObserver<M>;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import FormValidityObserver from "@form-observer/core/FormValidityObserver";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Creates an enhanced version of the {@link FormValidityObserver} that's more convenient for `Vue` apps
|
|
5
|
+
*
|
|
6
|
+
* @template {import("./index.d.ts").OneOrMany<import("./index.d.ts").EventType>} T
|
|
7
|
+
* @template [M=string]
|
|
8
|
+
* @param {T} types
|
|
9
|
+
* @param {import("./index.d.ts").FormValidityObserverOptions<M>} [options]
|
|
10
|
+
* @returns {import("./types.d.ts").VueFormValidityObserver<M>}
|
|
11
|
+
*/
|
|
12
|
+
export default function createFormValidityObserver(types, options) {
|
|
13
|
+
const observer = /** @type {import("./types.d.ts").VueFormValidityObserver<M>} */ (
|
|
14
|
+
/** @type {unknown} */ (new FormValidityObserver(types, options))
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
/* -------------------- Bindings -------------------- */
|
|
18
|
+
// Form Observer Methods
|
|
19
|
+
observer.observe = observer.observe.bind(observer);
|
|
20
|
+
observer.unobserve = observer.unobserve.bind(observer);
|
|
21
|
+
observer.disconnect = observer.disconnect.bind(observer);
|
|
22
|
+
|
|
23
|
+
// Validation Methods
|
|
24
|
+
observer.validateFields = observer.validateFields.bind(observer);
|
|
25
|
+
observer.validateField = observer.validateField.bind(observer);
|
|
26
|
+
observer.setFieldError = observer.setFieldError.bind(observer);
|
|
27
|
+
observer.clearFieldError = observer.clearFieldError.bind(observer);
|
|
28
|
+
|
|
29
|
+
/** **Private** reference to the original {@link FormValidityObserver.configure} method */
|
|
30
|
+
const originalConfigure = /** @type {FormValidityObserver<M>["configure"]} */ (observer.configure.bind(observer));
|
|
31
|
+
|
|
32
|
+
/* -------------------- Enhancements -------------------- */
|
|
33
|
+
// Add automatic setup/teardown
|
|
34
|
+
observer.autoObserve = function autoObserve(novalidate = true) {
|
|
35
|
+
/** @type {HTMLFormElement | null} */
|
|
36
|
+
let form;
|
|
37
|
+
|
|
38
|
+
/** @param {typeof form} vueRef @returns {void} */
|
|
39
|
+
return (vueRef) => {
|
|
40
|
+
if (vueRef) {
|
|
41
|
+
form = vueRef;
|
|
42
|
+
observer.observe(form);
|
|
43
|
+
if (novalidate) form.setAttribute("novalidate", "");
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
observer.unobserve(/** @type {HTMLFormElement} */ (form));
|
|
48
|
+
form = null;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// Enhanced `configure` method
|
|
53
|
+
observer.configure = function configure(name, errorMessages) {
|
|
54
|
+
const keys = /** @type {Array<keyof import("./types.d.ts").VueValidationErrors<M>>} */ (Object.keys(errorMessages));
|
|
55
|
+
const props = /** @type {import("./types.d.ts").VueFieldProps} */ ({ name });
|
|
56
|
+
const config = /** @type {import("./index.d.ts").ValidationErrors<M>} */ ({});
|
|
57
|
+
|
|
58
|
+
// Build `props` object and error `config` object from `errorMessages`
|
|
59
|
+
for (let i = 0; i < keys.length; i++) {
|
|
60
|
+
const constraint = keys[i];
|
|
61
|
+
|
|
62
|
+
// Type narrowing on each individual property would be redundant and increase bundle size. So we're using `any`.
|
|
63
|
+
const constraintValue = /** @type {any} */ (errorMessages[constraint]);
|
|
64
|
+
|
|
65
|
+
// Constraint Was Omitted
|
|
66
|
+
if (constraintValue == null) continue;
|
|
67
|
+
if (constraint === "required" && constraintValue === false) continue;
|
|
68
|
+
|
|
69
|
+
/* ----- Custom Validation Properties ----- */
|
|
70
|
+
if (constraint === "badinput" || constraint === "validate") {
|
|
71
|
+
config[constraint] = constraintValue;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* ----- Standrd HTML Attributes ----- */
|
|
76
|
+
// Value Only
|
|
77
|
+
if (typeof constraintValue !== "object") {
|
|
78
|
+
if (constraint === "required" && typeof constraintValue !== "boolean") config[constraint] = constraintValue;
|
|
79
|
+
props[constraint] = constraint === "required" ? true : constraintValue;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Value and Message
|
|
84
|
+
if (constraint === "required" && constraintValue.value === false) continue;
|
|
85
|
+
props[constraint] = constraintValue.value;
|
|
86
|
+
config[constraint] = constraintValue;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
originalConfigure(name, config);
|
|
90
|
+
return props;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
return observer;
|
|
94
|
+
}
|
package/index.cjs
ADDED
package/index.d.cts
ADDED
package/index.d.ts
ADDED
package/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@form-observer/vue",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.0.0-experimental-2",
|
|
5
|
+
"description": "Convenience functions for the `@form-observer/core` package, designed for Vue apps",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"require": {
|
|
9
|
+
"types": "./index.d.cts",
|
|
10
|
+
"default": "./index.cjs"
|
|
11
|
+
},
|
|
12
|
+
"types": "./index.d.ts",
|
|
13
|
+
"default": "./index.js"
|
|
14
|
+
},
|
|
15
|
+
"./createFormValidityObserver": {
|
|
16
|
+
"require": {
|
|
17
|
+
"types": "./createFormValidityObserver.d.cts",
|
|
18
|
+
"default": "./createFormValidityObserver.cjs"
|
|
19
|
+
},
|
|
20
|
+
"types": "./createFormValidityObserver.d.ts",
|
|
21
|
+
"default": "./createFormValidityObserver.js"
|
|
22
|
+
},
|
|
23
|
+
"./types": {
|
|
24
|
+
"types": "./types.d.ts"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"@form-observer/core": "*"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@testing-library/vue": "^7.0.0",
|
|
32
|
+
"@vitejs/plugin-vue": "^4.4.0",
|
|
33
|
+
"vue": "^3.3.6"
|
|
34
|
+
},
|
|
35
|
+
"author": "Isaiah Thomason",
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"keywords": [
|
|
38
|
+
"form",
|
|
39
|
+
"observer",
|
|
40
|
+
"web",
|
|
41
|
+
"validation",
|
|
42
|
+
"vue"
|
|
43
|
+
],
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "git+https://github.com/enthusiastic-js/form-observer.git"
|
|
47
|
+
},
|
|
48
|
+
"bugs": {
|
|
49
|
+
"url": "https://github.com/enthusiastic-js/form-observer/issues"
|
|
50
|
+
},
|
|
51
|
+
"homepage": "https://github.com/enthusiastic-js/form-observer#readme"
|
|
52
|
+
}
|
package/types.d.cts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { ErrorMessage, ValidationErrors, ValidatableField, FormValidityObserver } from "@form-observer/core";
|
|
2
|
+
import type { InputHTMLAttributes } from "vue";
|
|
3
|
+
|
|
4
|
+
export interface VueFormValidityObserver<M = string> extends Omit<FormValidityObserver<M>, "configure"> {
|
|
5
|
+
/**
|
|
6
|
+
* An enhanced version of {@link FormValidityObserver.configure} for `Vue`. In addition to configuring a field's
|
|
7
|
+
* error messages, it generates the props that should be applied to the field based on the provided arguments.
|
|
8
|
+
*
|
|
9
|
+
* Note: If the field is _only_ using the browser's default error messages, it does _not_ need to be `configure`d.
|
|
10
|
+
*
|
|
11
|
+
* @param name The `name` of the form field
|
|
12
|
+
* @param errorMessages A `key`-`value` pair of validation constraints (key) and their corresponding
|
|
13
|
+
* configurations (value)
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* // If the field is not a valid number, the error will display: "Number is invalid"
|
|
17
|
+
* <input v-bind="configure('amount', { pattern: { value: '\\d+', message: 'Number is invalid' } })" />
|
|
18
|
+
*
|
|
19
|
+
* // If the field is too long, the error will display the browser's `tooLong` error string.
|
|
20
|
+
* <input v-bind="configure('comment', { maxlength: 10 })" />
|
|
21
|
+
* <input name="another-comment" maxlength="10" />
|
|
22
|
+
*/
|
|
23
|
+
configure<E extends ValidatableField>(name: string, errorMessages: VueValidationErrors<M, E>): VueFieldProps;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Creates a Vue function `ref` used to automatically setup and cleanup a form's observer.
|
|
27
|
+
*
|
|
28
|
+
* **Note**: If you use this `ref`, you should **not** call `observe`, `unobserve`, or `disconnect` directly.
|
|
29
|
+
*
|
|
30
|
+
* @param novalidate Indicates that the
|
|
31
|
+
* {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#novalidate novalidate} attribute should
|
|
32
|
+
* be applied to the `form` element when JavaScript is enabled. Defaults to `true`.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* <form :ref="autoObserve()">
|
|
36
|
+
* <input name="first-name" type="textbox" required />
|
|
37
|
+
* </form>
|
|
38
|
+
*/
|
|
39
|
+
autoObserve(novalidate?: boolean): (formRef: HTMLFormElement) => void;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export type VueFieldProps = Pick<
|
|
43
|
+
InputHTMLAttributes,
|
|
44
|
+
"name" | "required" | "minlength" | "min" | "maxlength" | "max" | "step" | "type" | "pattern"
|
|
45
|
+
>;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* An augmetation of {@link ValidationErrors} for `Vue`. Represents the constraints that should be applied
|
|
49
|
+
* to a form field, and the error messages that should be displayed when those constraints are broken.
|
|
50
|
+
*/
|
|
51
|
+
export interface VueValidationErrors<M, E extends ValidatableField = ValidatableField>
|
|
52
|
+
extends Pick<ValidationErrors<M, E>, "badinput" | "validate"> {
|
|
53
|
+
// Standard HTML Attributes
|
|
54
|
+
required?: VueErrorDetails<M, InputHTMLAttributes["required"], E> | ErrorMessage<string, E>;
|
|
55
|
+
minlength?: VueErrorDetails<M, InputHTMLAttributes["minlength"], E>;
|
|
56
|
+
min?: VueErrorDetails<M, InputHTMLAttributes["min"], E>;
|
|
57
|
+
maxlength?: VueErrorDetails<M, InputHTMLAttributes["maxlength"], E>;
|
|
58
|
+
max?: VueErrorDetails<M, InputHTMLAttributes["max"], E>;
|
|
59
|
+
step?: VueErrorDetails<M, InputHTMLAttributes["step"], E>;
|
|
60
|
+
type?: VueErrorDetails<M, InputHTMLAttributes["type"], E>;
|
|
61
|
+
pattern?: VueErrorDetails<M, InputHTMLAttributes["pattern"], E>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** An augmentation of `ErrorDetails` for `Vue`. */
|
|
65
|
+
export type VueErrorDetails<M, V, E extends ValidatableField = ValidatableField> =
|
|
66
|
+
| V
|
|
67
|
+
| { render: true; message: ErrorMessage<M, E>; value: V }
|
|
68
|
+
| { render?: false; message: ErrorMessage<string, E>; value: V };
|
package/types.d.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { ErrorMessage, ValidationErrors, ValidatableField, FormValidityObserver } from "@form-observer/core";
|
|
2
|
+
import type { InputHTMLAttributes } from "vue";
|
|
3
|
+
|
|
4
|
+
export interface VueFormValidityObserver<M = string> extends Omit<FormValidityObserver<M>, "configure"> {
|
|
5
|
+
/**
|
|
6
|
+
* An enhanced version of {@link FormValidityObserver.configure} for `Vue`. In addition to configuring a field's
|
|
7
|
+
* error messages, it generates the props that should be applied to the field based on the provided arguments.
|
|
8
|
+
*
|
|
9
|
+
* Note: If the field is _only_ using the browser's default error messages, it does _not_ need to be `configure`d.
|
|
10
|
+
*
|
|
11
|
+
* @param name The `name` of the form field
|
|
12
|
+
* @param errorMessages A `key`-`value` pair of validation constraints (key) and their corresponding
|
|
13
|
+
* configurations (value)
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* // If the field is not a valid number, the error will display: "Number is invalid"
|
|
17
|
+
* <input v-bind="configure('amount', { pattern: { value: '\\d+', message: 'Number is invalid' } })" />
|
|
18
|
+
*
|
|
19
|
+
* // If the field is too long, the error will display the browser's `tooLong` error string.
|
|
20
|
+
* <input v-bind="configure('comment', { maxlength: 10 })" />
|
|
21
|
+
* <input name="another-comment" maxlength="10" />
|
|
22
|
+
*/
|
|
23
|
+
configure<E extends ValidatableField>(name: string, errorMessages: VueValidationErrors<M, E>): VueFieldProps;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Creates a Vue function `ref` used to automatically setup and cleanup a form's observer.
|
|
27
|
+
*
|
|
28
|
+
* **Note**: If you use this `ref`, you should **not** call `observe`, `unobserve`, or `disconnect` directly.
|
|
29
|
+
*
|
|
30
|
+
* @param novalidate Indicates that the
|
|
31
|
+
* {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#novalidate novalidate} attribute should
|
|
32
|
+
* be applied to the `form` element when JavaScript is enabled. Defaults to `true`.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* <form :ref="autoObserve()">
|
|
36
|
+
* <input name="first-name" type="textbox" required />
|
|
37
|
+
* </form>
|
|
38
|
+
*/
|
|
39
|
+
autoObserve(novalidate?: boolean): (formRef: HTMLFormElement) => void;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export type VueFieldProps = Pick<
|
|
43
|
+
InputHTMLAttributes,
|
|
44
|
+
"name" | "required" | "minlength" | "min" | "maxlength" | "max" | "step" | "type" | "pattern"
|
|
45
|
+
>;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* An augmetation of {@link ValidationErrors} for `Vue`. Represents the constraints that should be applied
|
|
49
|
+
* to a form field, and the error messages that should be displayed when those constraints are broken.
|
|
50
|
+
*/
|
|
51
|
+
export interface VueValidationErrors<M, E extends ValidatableField = ValidatableField>
|
|
52
|
+
extends Pick<ValidationErrors<M, E>, "badinput" | "validate"> {
|
|
53
|
+
// Standard HTML Attributes
|
|
54
|
+
required?: VueErrorDetails<M, InputHTMLAttributes["required"], E> | ErrorMessage<string, E>;
|
|
55
|
+
minlength?: VueErrorDetails<M, InputHTMLAttributes["minlength"], E>;
|
|
56
|
+
min?: VueErrorDetails<M, InputHTMLAttributes["min"], E>;
|
|
57
|
+
maxlength?: VueErrorDetails<M, InputHTMLAttributes["maxlength"], E>;
|
|
58
|
+
max?: VueErrorDetails<M, InputHTMLAttributes["max"], E>;
|
|
59
|
+
step?: VueErrorDetails<M, InputHTMLAttributes["step"], E>;
|
|
60
|
+
type?: VueErrorDetails<M, InputHTMLAttributes["type"], E>;
|
|
61
|
+
pattern?: VueErrorDetails<M, InputHTMLAttributes["pattern"], E>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** An augmentation of `ErrorDetails` for `Vue`. */
|
|
65
|
+
export type VueErrorDetails<M, V, E extends ValidatableField = ValidatableField> =
|
|
66
|
+
| V
|
|
67
|
+
| { render: true; message: ErrorMessage<M, E>; value: V }
|
|
68
|
+
| { render?: false; message: ErrorMessage<string, E>; value: V };
|