@keysdown/form-wrapper 1.0.2 → 2.0.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.
- package/README.md +342 -31
- package/dist/form-wrapper.cjs +1 -1
- package/dist/form-wrapper.js +115 -29
- package/dist/form-wrapper.umd.cjs +1 -1
- package/dist/plugins/formValidation.cjs +1 -0
- package/dist/plugins/formValidation.d.ts +4 -0
- package/dist/plugins/formValidation.js +7 -0
- package/dist/plugins/locales.cjs +1 -0
- package/dist/plugins/locales.d.ts +2 -0
- package/dist/plugins/locales.js +115 -0
- package/dist/plugins/rules.cjs +1 -0
- package/dist/plugins/rules.d.ts +2 -0
- package/dist/plugins/rules.js +2 -0
- package/dist/rules-BIt-pifQ.cjs +1 -0
- package/dist/rules-DZmnRoUy.js +196 -0
- package/dist/src/core/Errors.d.ts +1 -1
- package/dist/src/core/Form.d.ts +12 -0
- package/dist/src/plugins/formValidation.d.ts +3 -0
- package/dist/src/plugins/locales/en.d.ts +3 -0
- package/dist/src/plugins/locales/es.d.ts +3 -0
- package/dist/src/plugins/locales/index.d.ts +3 -0
- package/dist/src/plugins/locales/pt.d.ts +3 -0
- package/dist/src/plugins/rules/alpha.d.ts +2 -0
- package/dist/src/plugins/rules/alphaNumeric.d.ts +2 -0
- package/dist/src/plugins/rules/array.d.ts +2 -0
- package/dist/src/plugins/rules/between.d.ts +2 -0
- package/dist/src/plugins/rules/boolean.d.ts +2 -0
- package/dist/src/plugins/rules/confirmed.d.ts +2 -0
- package/dist/src/plugins/rules/date.d.ts +2 -0
- package/dist/src/plugins/rules/different.d.ts +2 -0
- package/dist/src/plugins/rules/digits.d.ts +2 -0
- package/dist/src/plugins/rules/digitsBetween.d.ts +2 -0
- package/dist/src/plugins/rules/email.d.ts +2 -0
- package/dist/src/plugins/rules/endsWith.d.ts +2 -0
- package/dist/src/plugins/rules/greaterThan.d.ts +2 -0
- package/dist/src/plugins/rules/greaterThanOrEqual.d.ts +2 -0
- package/dist/src/plugins/rules/in.d.ts +2 -0
- package/dist/src/plugins/rules/index.d.ts +35 -0
- package/dist/src/plugins/rules/integer.d.ts +2 -0
- package/dist/src/plugins/rules/ip.d.ts +2 -0
- package/dist/src/plugins/rules/json.d.ts +2 -0
- package/dist/src/plugins/rules/lessThan.d.ts +2 -0
- package/dist/src/plugins/rules/lessThanOrEqual.d.ts +2 -0
- package/dist/src/plugins/rules/max.d.ts +2 -0
- package/dist/src/plugins/rules/min.d.ts +2 -0
- package/dist/src/plugins/rules/notIn.d.ts +2 -0
- package/dist/src/plugins/rules/nullable.d.ts +2 -0
- package/dist/src/plugins/rules/numeric.d.ts +2 -0
- package/dist/src/plugins/rules/regex.d.ts +2 -0
- package/dist/src/plugins/rules/required.d.ts +2 -0
- package/dist/src/plugins/rules/same.d.ts +2 -0
- package/dist/src/plugins/rules/size.d.ts +2 -0
- package/dist/src/plugins/rules/startsWith.d.ts +2 -0
- package/dist/src/plugins/rules/string.d.ts +2 -0
- package/dist/src/plugins/rules/url.d.ts +2 -0
- package/dist/src/plugins/rules/uuid.d.ts +2 -0
- package/dist/src/types/fields.d.ts +1 -0
- package/dist/src/types/locale.d.ts +7 -0
- package/dist/src/types/plugin.d.ts +2 -0
- package/dist/src/types/rules.d.ts +7 -1
- package/dist/src/types/validations.d.ts +3 -2
- package/dist/src/utils/collections.d.ts +1 -1
- package/dist/src/utils/rule.d.ts +2 -0
- package/package.json +17 -2
- package/dist/form-wrapper.iife.js +0 -1
- package/dist/src/utils/validations.d.ts +0 -3
package/README.md
CHANGED
|
@@ -9,7 +9,11 @@
|
|
|
9
9
|
<img src="https://img.shields.io/github/license/keysdown/form-wrapper.svg" alt="MIT"/>
|
|
10
10
|
</p>
|
|
11
11
|
|
|
12
|
-
> A
|
|
12
|
+
> A zero-dependency, framework-agnostic form state management library with a plugin-based validation system. Manage form fields, validate with customizable error messages, and extend with built-in validation rules, only importing what you need.
|
|
13
|
+
|
|
14
|
+
<p align="center">
|
|
15
|
+
<strong>Bundle size (minified + gzip):</strong> ~1.5 kB core / ~3 kB with all 33 rules
|
|
16
|
+
</p>
|
|
13
17
|
|
|
14
18
|
## Installation
|
|
15
19
|
|
|
@@ -39,6 +43,211 @@ const form = ref(new FormWrapper({
|
|
|
39
43
|
}))
|
|
40
44
|
```
|
|
41
45
|
|
|
46
|
+
## Validation Plugins
|
|
47
|
+
|
|
48
|
+
Validation rules are loaded via a plugin system, no rules are bundled in core. This keeps the base library minimal and lets you import only what you need.
|
|
49
|
+
|
|
50
|
+
### Loading all rules
|
|
51
|
+
|
|
52
|
+
Use the `formValidation` plugin to register all 33 validation rules at once:
|
|
53
|
+
|
|
54
|
+
```js
|
|
55
|
+
import FormWrapper from '@keysdown/form-wrapper'
|
|
56
|
+
import formValidation from '@keysdown/form-wrapper/plugins/formValidation'
|
|
57
|
+
|
|
58
|
+
FormWrapper.extend(formValidation)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Default error messages with locales
|
|
62
|
+
|
|
63
|
+
Validation rules have built-in default error messages. By default, messages are in **English**. You can change the language by loading a locale:
|
|
64
|
+
|
|
65
|
+
```js
|
|
66
|
+
import FormWrapper from '@keysdown/form-wrapper'
|
|
67
|
+
import formValidation from '@keysdown/form-wrapper/plugins/formValidation'
|
|
68
|
+
import pt from '@keysdown/form-wrapper/plugins/locales/pt'
|
|
69
|
+
|
|
70
|
+
FormWrapper.extend(formValidation)
|
|
71
|
+
FormWrapper.locale(pt) // switch to Portuguese
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Available locales: `en` (English, default), `pt` (Portuguese), `es` (Spanish).
|
|
75
|
+
|
|
76
|
+
Default messages support **interpolation**, placeholders like `:field`, `:min`, `:max`, `:size`, `:digits`, `:other`, `:values` are replaced with actual values:
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
The first name field is required.
|
|
80
|
+
The password must be at least 6.
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Custom field display names with `attribute`
|
|
84
|
+
|
|
85
|
+
By default, the `:field` placeholder uses the field name with underscores replaced by spaces (e.g., `first_name` → "first name"). You can customize this with the `attribute` property:
|
|
86
|
+
|
|
87
|
+
```js
|
|
88
|
+
const form = new FormWrapper({
|
|
89
|
+
email: {
|
|
90
|
+
value: null,
|
|
91
|
+
validation: {
|
|
92
|
+
rules: ['required', 'email']
|
|
93
|
+
},
|
|
94
|
+
attribute: 'contact email'
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
// Default error message would show:
|
|
99
|
+
// "The contact email field is required."
|
|
100
|
+
// instead of "The email field is required."
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
```js
|
|
104
|
+
const form = new FormWrapper({
|
|
105
|
+
password: {
|
|
106
|
+
value: null,
|
|
107
|
+
validation: {
|
|
108
|
+
rules: ['required', 'min:8']
|
|
109
|
+
},
|
|
110
|
+
attribute: 'senha'
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
// With pt locale: "O campo senha deve ter no mínimo 8."
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
User-provided messages always **override** default messages:
|
|
118
|
+
|
|
119
|
+
```js
|
|
120
|
+
const form = new FormWrapper({
|
|
121
|
+
email: {
|
|
122
|
+
value: null,
|
|
123
|
+
validation: {
|
|
124
|
+
rules: ['required', 'email'],
|
|
125
|
+
messages: {
|
|
126
|
+
required: 'Email is required.' // overrides the default message
|
|
127
|
+
// email uses the default locale message
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Tree-shaking individual rules
|
|
135
|
+
|
|
136
|
+
Import only the rules you need and use them directly in the `rules` array for tree-shaking:
|
|
137
|
+
|
|
138
|
+
```js
|
|
139
|
+
import FormWrapper from '@keysdown/form-wrapper'
|
|
140
|
+
import { required, email } from '@keysdown/form-wrapper/plugins/rules'
|
|
141
|
+
|
|
142
|
+
const form = new FormWrapper({
|
|
143
|
+
email: {
|
|
144
|
+
value: null,
|
|
145
|
+
validation: {
|
|
146
|
+
rules: [required, email],
|
|
147
|
+
messages: {
|
|
148
|
+
required: 'The email field is required.',
|
|
149
|
+
email: 'The email must be a valid address.'
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
})
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
You can also mix function rules with string rules (useful for parameterized rules like `min:6`):
|
|
157
|
+
|
|
158
|
+
```js
|
|
159
|
+
import { required, email } from '@keysdown/form-wrapper/plugins/rules'
|
|
160
|
+
|
|
161
|
+
rules: [required, email, 'min:6']
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Custom rules
|
|
165
|
+
|
|
166
|
+
Add inline validation rules directly in the `rules` array. A custom rule receives a destructured object `{value, fail, form, field}`:
|
|
167
|
+
|
|
168
|
+
```js
|
|
169
|
+
const form = createForm({
|
|
170
|
+
email: {
|
|
171
|
+
value: null,
|
|
172
|
+
validation: {
|
|
173
|
+
rules: [({value, fail}) => {
|
|
174
|
+
if (!value) fail('The email field is required.')
|
|
175
|
+
}]
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
})
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Access other form fields via the `form` parameter:
|
|
182
|
+
|
|
183
|
+
```js
|
|
184
|
+
const form = createForm({
|
|
185
|
+
password: { value: null, validation: {rules: []} },
|
|
186
|
+
password_confirmation: {
|
|
187
|
+
value: null,
|
|
188
|
+
validation: {
|
|
189
|
+
rules: [({value, fail, form}) => {
|
|
190
|
+
if (value !== form.password) fail('Passwords do not match.')
|
|
191
|
+
}]
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
})
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Custom rules support **async** validation (e.g., API calls):
|
|
198
|
+
|
|
199
|
+
```js
|
|
200
|
+
const form = createForm({
|
|
201
|
+
email: {
|
|
202
|
+
value: null,
|
|
203
|
+
validation: {
|
|
204
|
+
rules: [async ({value, fail}) => {
|
|
205
|
+
const response = await fetch(`/api/check-email?email=${encodeURIComponent(value)}`)
|
|
206
|
+
const { available } = await response.json()
|
|
207
|
+
if (!available) fail('This email is already taken.')
|
|
208
|
+
}]
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
})
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
You can mix custom rules with built-in rules:
|
|
215
|
+
|
|
216
|
+
```js
|
|
217
|
+
import { required, email } from '@keysdown/form-wrapper/plugins/rules'
|
|
218
|
+
|
|
219
|
+
const form = createForm({
|
|
220
|
+
email: {
|
|
221
|
+
value: null,
|
|
222
|
+
validation: {
|
|
223
|
+
rules: [
|
|
224
|
+
required,
|
|
225
|
+
email,
|
|
226
|
+
async ({value, fail}) => {
|
|
227
|
+
const response = await fetch(`/api/check-email?email=${encodeURIComponent(value)}`)
|
|
228
|
+
const { available } = await response.json()
|
|
229
|
+
if (!available) fail('This email is already taken.')
|
|
230
|
+
}
|
|
231
|
+
],
|
|
232
|
+
messages: {
|
|
233
|
+
required: 'The email field is required.',
|
|
234
|
+
email: 'The email must be a valid address.'
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
})
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Static methods on FormWrapper
|
|
242
|
+
|
|
243
|
+
| Method | Description |
|
|
244
|
+
|---|---|
|
|
245
|
+
| `FormWrapper.extend(plugin)` | Register a plugin (e.g., `formValidation`) |
|
|
246
|
+
| `FormWrapper.addRule(name, handler)` | Register a single validation rule |
|
|
247
|
+
| `FormWrapper.locale(locale)` | Set the default error messages locale |
|
|
248
|
+
| `FormWrapper.rules` | Static rule registry |
|
|
249
|
+
| `FormWrapper.defaultMessages` | Static default messages registry |
|
|
250
|
+
|
|
42
251
|
## Basic example
|
|
43
252
|
|
|
44
253
|
Basic example using Vue.
|
|
@@ -99,29 +308,38 @@ Basic example with validation using Vue.
|
|
|
99
308
|
<script setup lang="ts">
|
|
100
309
|
import axios from 'axios'
|
|
101
310
|
import {ref} from 'vue'
|
|
102
|
-
import
|
|
311
|
+
import FormWrapper from '@keysdown/form-wrapper'
|
|
312
|
+
import formValidation from '@keysdown/form-wrapper/plugins/formValidation'
|
|
103
313
|
|
|
104
|
-
|
|
314
|
+
FormWrapper.extend(formValidation)
|
|
315
|
+
|
|
316
|
+
const form = ref(new FormWrapper({
|
|
105
317
|
first_name: {
|
|
106
318
|
value: null,
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
319
|
+
validation: {
|
|
320
|
+
rules: ['required'],
|
|
321
|
+
messages: {
|
|
322
|
+
required: 'The first name field is required.'
|
|
323
|
+
}
|
|
110
324
|
}
|
|
111
325
|
},
|
|
112
326
|
last_name: {
|
|
113
327
|
value: null,
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
328
|
+
validation: {
|
|
329
|
+
rules: ['required'],
|
|
330
|
+
messages: {
|
|
331
|
+
required: 'The last name field is required.'
|
|
332
|
+
}
|
|
117
333
|
}
|
|
118
334
|
},
|
|
119
335
|
username: {
|
|
120
336
|
value: null,
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
337
|
+
validation: {
|
|
338
|
+
rules: ['required', 'min:6'],
|
|
339
|
+
messages: {
|
|
340
|
+
required: 'The username field is required.',
|
|
341
|
+
min: 'The username field must have at least 6 characters'
|
|
342
|
+
}
|
|
125
343
|
}
|
|
126
344
|
}
|
|
127
345
|
}))
|
|
@@ -150,7 +368,6 @@ Basic example with validation using Vue.
|
|
|
150
368
|
</script>
|
|
151
369
|
```
|
|
152
370
|
|
|
153
|
-
|
|
154
371
|
## Form methods
|
|
155
372
|
|
|
156
373
|
### addField(field, value)
|
|
@@ -161,6 +378,7 @@ Method used to add a single field to the form.
|
|
|
161
378
|
form.addField('username', null)
|
|
162
379
|
|
|
163
380
|
form.addField('username', {
|
|
381
|
+
value: null,
|
|
164
382
|
validation: {
|
|
165
383
|
rules: ['required'],
|
|
166
384
|
messages: {
|
|
@@ -182,6 +400,7 @@ form.addFields({
|
|
|
182
400
|
|
|
183
401
|
form.addFields({
|
|
184
402
|
full_name: {
|
|
403
|
+
value: null,
|
|
185
404
|
validation: {
|
|
186
405
|
rules: ['required'],
|
|
187
406
|
messages: {
|
|
@@ -190,6 +409,7 @@ form.addFields({
|
|
|
190
409
|
}
|
|
191
410
|
},
|
|
192
411
|
username: {
|
|
412
|
+
value: null,
|
|
193
413
|
validation: {
|
|
194
414
|
rules: ['required'],
|
|
195
415
|
messages: {
|
|
@@ -268,6 +488,44 @@ form.reset()
|
|
|
268
488
|
console.log(form.values()) // {username: null}
|
|
269
489
|
```
|
|
270
490
|
|
|
491
|
+
### wasChanged(field)
|
|
492
|
+
|
|
493
|
+
Method used to check if one or more fields have been changed from their original values. When an array is provided, returns `true` if **any** of the fields were changed.
|
|
494
|
+
|
|
495
|
+
```js
|
|
496
|
+
const form = createForm({
|
|
497
|
+
name: null,
|
|
498
|
+
username: null
|
|
499
|
+
})
|
|
500
|
+
|
|
501
|
+
console.log(form.wasChanged('username')) // false
|
|
502
|
+
|
|
503
|
+
form.username = 'keysdown'
|
|
504
|
+
|
|
505
|
+
console.log(form.wasChanged('username')) // true
|
|
506
|
+
|
|
507
|
+
console.log(form.wasChanged(['name', 'username'])) // true (any changed)
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
### filled(field)
|
|
511
|
+
|
|
512
|
+
Method used to check if one or more fields are filled (not `null`, not `undefined`, not empty string `''`). When an array is provided, returns `true` only if **all** fields are filled.
|
|
513
|
+
|
|
514
|
+
```js
|
|
515
|
+
const form = createForm({
|
|
516
|
+
name: null,
|
|
517
|
+
username: null
|
|
518
|
+
})
|
|
519
|
+
|
|
520
|
+
form.username = 'keysdown'
|
|
521
|
+
|
|
522
|
+
console.log(form.filled('username')) // true
|
|
523
|
+
|
|
524
|
+
console.log(form.filled('name')) // false
|
|
525
|
+
|
|
526
|
+
console.log(form.filled(['name', 'username'])) // false (name is not filled)
|
|
527
|
+
```
|
|
528
|
+
|
|
271
529
|
### setAwaiting(awaiting = true)
|
|
272
530
|
|
|
273
531
|
Method used to change the form loading state.
|
|
@@ -296,9 +554,11 @@ Method used to validate the entire form or a specific field.
|
|
|
296
554
|
const form = createForm({
|
|
297
555
|
username: {
|
|
298
556
|
value: null,
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
557
|
+
validation: {
|
|
558
|
+
rules: ['required'],
|
|
559
|
+
messages: {
|
|
560
|
+
required: 'The username field is required.'
|
|
561
|
+
}
|
|
302
562
|
}
|
|
303
563
|
}
|
|
304
564
|
})
|
|
@@ -330,9 +590,11 @@ Method used to validate a specific field.
|
|
|
330
590
|
const form = createForm({
|
|
331
591
|
username: {
|
|
332
592
|
value: null,
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
593
|
+
validation: {
|
|
594
|
+
rules: ['required'],
|
|
595
|
+
messages: {
|
|
596
|
+
required: 'The username field is required.'
|
|
597
|
+
}
|
|
336
598
|
}
|
|
337
599
|
}
|
|
338
600
|
})
|
|
@@ -364,9 +626,11 @@ Method used to validate the entire form.
|
|
|
364
626
|
const form = createForm({
|
|
365
627
|
username: {
|
|
366
628
|
value: null,
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
629
|
+
validation: {
|
|
630
|
+
rules: ['required'],
|
|
631
|
+
messages: {
|
|
632
|
+
required: 'The username field is required.'
|
|
633
|
+
}
|
|
370
634
|
}
|
|
371
635
|
}
|
|
372
636
|
})
|
|
@@ -500,6 +764,16 @@ form.messages.first('username')
|
|
|
500
764
|
form.rules.first('username')
|
|
501
765
|
```
|
|
502
766
|
|
|
767
|
+
#### get(key)
|
|
768
|
+
|
|
769
|
+
Returns the value for the given key, or `null` if not found.
|
|
770
|
+
|
|
771
|
+
```js
|
|
772
|
+
form.errors.get('username')
|
|
773
|
+
form.messages.get('username')
|
|
774
|
+
form.rules.get('username')
|
|
775
|
+
```
|
|
776
|
+
|
|
503
777
|
#### any()
|
|
504
778
|
|
|
505
779
|
Returns true if the collection has any items and false if it is empty.
|
|
@@ -543,15 +817,16 @@ form.messages.push('username', {
|
|
|
543
817
|
required: 'The username field is required.'
|
|
544
818
|
})
|
|
545
819
|
|
|
546
|
-
form.rules.push('username', 'required')
|
|
820
|
+
form.rules.push('username', ['required'])
|
|
547
821
|
```
|
|
548
822
|
|
|
549
823
|
#### has(key)
|
|
550
824
|
|
|
551
|
-
Checking if the collection has an item with the key.
|
|
825
|
+
Checking if the collection has an item with the key. Accepts a single key or an array of keys. When an array is provided, returns `true` if **any** of the keys exist.
|
|
552
826
|
|
|
553
827
|
```js
|
|
554
828
|
form.errors.has('username')
|
|
829
|
+
form.errors.has(['username', 'email'])
|
|
555
830
|
form.messages.has('username')
|
|
556
831
|
form.rules.has('username')
|
|
557
832
|
```
|
|
@@ -566,7 +841,7 @@ form.messages.unset('username')
|
|
|
566
841
|
form.rules.unset('username')
|
|
567
842
|
```
|
|
568
843
|
|
|
569
|
-
#### clear(
|
|
844
|
+
#### clear()
|
|
570
845
|
|
|
571
846
|
Clears the entire collection, making it empty.
|
|
572
847
|
|
|
@@ -578,8 +853,44 @@ form.rules.clear()
|
|
|
578
853
|
|
|
579
854
|
### Validation rules
|
|
580
855
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
856
|
+
The `formValidation` plugin provides 33 validation rules. Rules are loaded via plugins, see [Validation Plugins](#validation-plugins) for setup instructions.
|
|
857
|
+
|
|
858
|
+
| Rule | Parameters | Description |
|
|
859
|
+
|---|---|---|
|
|
860
|
+
| `required` | — | Field must be present and not empty |
|
|
861
|
+
| `email` | — | Must be a valid email address |
|
|
862
|
+
| `url` | — | Must be a valid URL |
|
|
863
|
+
| `min` | `min:6` | Minimum length (string) or value (number) or count (array) |
|
|
864
|
+
| `max` | `max:255` | Maximum length/value/count |
|
|
865
|
+
| `between` | `between:1,10` | Between min and max |
|
|
866
|
+
| `size` | `size:10` | Exact length/value/count |
|
|
867
|
+
| `alpha` | — | Only alphabetic characters |
|
|
868
|
+
| `alphaNumeric` | — | Only letters and numbers |
|
|
869
|
+
| `string` | — | Must be a string |
|
|
870
|
+
| `integer` | — | Must be an integer |
|
|
871
|
+
| `numeric` | — | Must be numeric |
|
|
872
|
+
| `array` | — | Must be an array |
|
|
873
|
+
| `boolean` | — | Must be boolean (true, false, 0, 1, '0', '1') |
|
|
874
|
+
| `date` | — | Must be a valid date |
|
|
875
|
+
| `same` | `same:field` | Must match another field |
|
|
876
|
+
| `different` | `different:field` | Must differ from another field |
|
|
877
|
+
| `confirmed` | `confirmed:field` | Must match field_confirmation |
|
|
878
|
+
| `in` | `in:admin,user` | Value must be in list |
|
|
879
|
+
| `notIn` | `notIn:admin,user` | Value must not be in list |
|
|
880
|
+
| `regex` | `regex:/pattern/` | Must match regex pattern |
|
|
881
|
+
| `startsWith` | `startsWith:foo,bar` | Must start with one of the values |
|
|
882
|
+
| `endsWith` | `endsWith:foo,bar` | Must end with one of the values |
|
|
883
|
+
| `digits` | `digits:4` | Must have exactly N digits |
|
|
884
|
+
| `digitsBetween` | `digitsBetween:3,6` | Must have between min and max digits |
|
|
885
|
+
| `ip` | — | Must be a valid IP (v4 or v6) |
|
|
886
|
+
| `json` | — | Must be a valid JSON string |
|
|
887
|
+
| `uuid` | — | Must be a valid UUID |
|
|
888
|
+
| `lessThan` | `lessThan:field` | Numeric value less than another field |
|
|
889
|
+
| `greaterThan` | `greaterThan:field` | Numeric value greater than another field |
|
|
890
|
+
| `lessThanOrEqual` | `lessThanOrEqual:field` | Less than or equal to another field |
|
|
891
|
+
| `greaterThanOrEqual` | `greaterThanOrEqual:field` | Greater than or equal to another field |
|
|
892
|
+
| `nullable` | — | Allows null/empty (always passes) |
|
|
893
|
+
|
|
894
|
+
## License
|
|
895
|
+
|
|
896
|
+
[MIT](LICENSE)
|
package/dist/form-wrapper.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});var e=e=>typeof e==`string
|
|
1
|
+
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});var e=e=>{if(typeof e==`string`){let t=[],n=e;for(;n.length>0;){let e=n.indexOf(`regex:`);if(e!==-1){let r=n.substring(0,e);r&&t.push(...r.split(`|`).filter(Boolean));let i=n.substring(e),a=i.indexOf(`:/`);if(a!==-1){let e=a+1,r=i.lastIndexOf(`/`);if(r>e){let e=i.substring(r+1),a=e.match(/^[gimsuy]*/)[0],o=i.substring(0,r+1+a.length);t.push(o),n=e.substring(a.length),n.startsWith(`|`)&&(n=n.substring(1));continue}}t.push(i);break}else{t.push(...n.split(`|`).filter(Boolean));break}}return t}return e},t=t=>({validation:{rules:t.validation?.rules?e(t.validation.rules):[],messages:t.validation?.messages??{}},value:t.value??null}),n=class{collection={};all(){return this.collection}first(e){let t=this.get(e);if(t!==null){let e=Array.isArray(t)?t[0]:t;if(e!=null)return String(e)}return null}any(){return Object.keys(this.collection).length>0}fill(e){return this.collection=e,this}push(e,t){return this.collection={...this.collection,[e]:t},this}has(e){return Array.isArray(e)?e.some(e=>this.collection.hasOwnProperty(e)):this.collection.hasOwnProperty(e)}get(e,t=null){return this.has(e)?this.collection[e]:t}unset(e){return this.has(e)&&delete this.collection[e],this}clear(){return this.collection={},this}},r=class extends n{},i=class extends n{},a=class extends n{push(e,t){let n=this.get(e)||[];return this.collection={...this.collection,[e]:[...n,t]},this}get(e){return super.get(e,[])}},o=class{errors=new a;messages=new r;rules=new i},s=e=>typeof e==`object`&&!!e&&!Array.isArray(e),c=(e,t,n=null)=>{let r=t||new FormData;return Object.keys(e).forEach(t=>{let i=e[t];if(t=n?`${n}[${t}]`:t,!([void 0,null].indexOf(i)>-1)){if(s(i)&&!(i instanceof File)||Array.isArray(i)){c(i,r,t);return}r.append(t,i)}}),r},l=class e{static rules={};static defaultMessages={};static interpolateMessage(e,t,n,r){let i=r[t]||t.replace(/_/g,` `);return e.replace(/:field/g,i).replace(/:min/g,n[0]||``).replace(/:max/g,n[1]||``).replace(/:size/g,n[0]||``).replace(/:digits/g,n[0]||``).replace(/:other/g,n[0]?r[n[0]]||n[0].replace(/_/g,` `):``).replace(/:values/g,n.join(`, `))}awaiting=!1;originalValues={};fieldAttributes={};validation=new o;constructor(e){this.addFields(e)}static extend(t){return t(e,e.rules),e}static addRule(t,n){return e.rules[t]=Object.assign(n,{ruleName:t}),e}static locale(t){return e.defaultMessages=t.messages,e}addField(e,n){if(typeof n==`object`&&n&&Object.prototype.toString.call(n)===`[object Object]`&&`value`in n){let r=t(n);this[e]=r.value,this.originalValues[e]=r.value,this.validation.messages.push(e,r.validation.messages),this.validation.rules.push(e,r.validation.rules),n.attribute&&(this.fieldAttributes[e]=n.attribute)}else this[e]=n,this.originalValues[e]=n;return this}addFields(e){return Object.keys(e).forEach(t=>{this.addField(t,e[t])}),this}get errors(){return this.validation.errors}fill(e,t=!1){return Object.keys(e).forEach(n=>{let r=e[n];t&&(this.originalValues[n]=r),this[n]=r,n in this.originalValues||(this.originalValues[n]=r)}),this}get messages(){return this.validation.messages}removeField(e){return delete this[e],delete this.originalValues[e],delete this.fieldAttributes[e],this.validation.errors.unset(e),this.validation.messages.unset(e),this.validation.rules.unset(e),this}removeFields(e){return e.forEach(e=>{this.removeField(e)}),this}reset(){return this.validation.errors.clear(),Object.keys(this.originalValues).forEach(e=>{this[e]=this.originalValues[e]}),this}wasChanged(e){return Array.isArray(e)?e.some(e=>this[e]!==this.originalValues[e]):this[e]!==this.originalValues[e]}filled(e){return Array.isArray(e)?e.every(e=>this[e]!==null&&this[e]!==void 0&&this[e]!==``):this[e]!==null&&this[e]!==void 0&&this[e]!==``}get rules(){return this.validation.rules}setAwaiting(e=!0){return this.awaiting=e,this}validate(e=null){return e?this.validateField(e):this.validateForm()}validateField(t){this.validation.errors.unset(t);let n=this.validation.rules.get(t);if(n&&n.length>0){let r=this.constructor.rules;return(async()=>{for(let i of n){let n=i;if(typeof n==`function`){if(`ruleName`in n){let r=n.ruleName;if(r===`nullable`&&(this[t]===null||this[t]===void 0||this[t]===``))return;try{await n(this[t],[],this,t)}catch(n){let i=this.validation.messages.get(t);throw r&&i&&r in i?this.validation.errors.push(t,i[r]):r in e.defaultMessages&&this.validation.errors.push(t,e.interpolateMessage(e.defaultMessages[r],t,[],this.fieldAttributes)),n}}else await new Promise((e,r)=>{let i=!1,a=e=>{i=!0,e&&this.validation.errors.push(t,e)};try{let o=n({value:this[t],fail:a,form:this,field:t});o instanceof Promise?o.then(()=>{i?r():e()}).catch(()=>{r()}):i?r():e()}catch{r()}});continue}let a=n.indexOf(`:`),o=a===-1?n:n.substring(0,a);if(o===`nullable`&&(this[t]===null||this[t]===void 0||this[t]===``))return;let s=a===-1?[]:n.substring(a+1).split(`,`);if(o in r)try{await r[o](this[t],s,this,t)}catch(n){let r=this.validation.messages.get(t);throw r&&o in r?this.validation.errors.push(t,r[o]):o in e.defaultMessages&&this.validation.errors.push(t,e.interpolateMessage(e.defaultMessages[o],t,s,this.fieldAttributes)),n}else throw Error(`There is no validation rule called "${o}"`)}})()}return Promise.resolve()}validateForm(){let e=Object.keys(this.originalValues).map(e=>this.validateField(e));return Promise.all(e).then(()=>Promise.resolve(this)).catch(()=>Promise.reject(this))}values(e){let t={};return Object.keys(this.originalValues).forEach(n=>{(!e||e.includes(n))&&(t[n]=this[n])}),t}valuesAsFormData(e){return c(this.values(e))}},u=e=>new l(e),d=l;exports.createForm=u,exports.default=d;
|