@keysdown/form-wrapper 1.0.5 → 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 +299 -31
- package/dist/form-wrapper.cjs +1 -1
- package/dist/form-wrapper.js +108 -28
- 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 +10 -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/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,10 +9,10 @@
|
|
|
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
13
|
|
|
14
14
|
<p align="center">
|
|
15
|
-
<strong>Bundle size (minified + gzip):</strong> ~1.
|
|
15
|
+
<strong>Bundle size (minified + gzip):</strong> ~1.5 kB core / ~3 kB with all 33 rules
|
|
16
16
|
</p>
|
|
17
17
|
|
|
18
18
|
## Installation
|
|
@@ -43,6 +43,211 @@ const form = ref(new FormWrapper({
|
|
|
43
43
|
}))
|
|
44
44
|
```
|
|
45
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
|
+
|
|
46
251
|
## Basic example
|
|
47
252
|
|
|
48
253
|
Basic example using Vue.
|
|
@@ -103,29 +308,38 @@ Basic example with validation using Vue.
|
|
|
103
308
|
<script setup lang="ts">
|
|
104
309
|
import axios from 'axios'
|
|
105
310
|
import {ref} from 'vue'
|
|
106
|
-
import
|
|
311
|
+
import FormWrapper from '@keysdown/form-wrapper'
|
|
312
|
+
import formValidation from '@keysdown/form-wrapper/plugins/formValidation'
|
|
107
313
|
|
|
108
|
-
|
|
314
|
+
FormWrapper.extend(formValidation)
|
|
315
|
+
|
|
316
|
+
const form = ref(new FormWrapper({
|
|
109
317
|
first_name: {
|
|
110
318
|
value: null,
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
319
|
+
validation: {
|
|
320
|
+
rules: ['required'],
|
|
321
|
+
messages: {
|
|
322
|
+
required: 'The first name field is required.'
|
|
323
|
+
}
|
|
114
324
|
}
|
|
115
325
|
},
|
|
116
326
|
last_name: {
|
|
117
327
|
value: null,
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
328
|
+
validation: {
|
|
329
|
+
rules: ['required'],
|
|
330
|
+
messages: {
|
|
331
|
+
required: 'The last name field is required.'
|
|
332
|
+
}
|
|
121
333
|
}
|
|
122
334
|
},
|
|
123
335
|
username: {
|
|
124
336
|
value: null,
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
+
}
|
|
129
343
|
}
|
|
130
344
|
}
|
|
131
345
|
}))
|
|
@@ -154,7 +368,6 @@ Basic example with validation using Vue.
|
|
|
154
368
|
</script>
|
|
155
369
|
```
|
|
156
370
|
|
|
157
|
-
|
|
158
371
|
## Form methods
|
|
159
372
|
|
|
160
373
|
### addField(field, value)
|
|
@@ -165,6 +378,7 @@ Method used to add a single field to the form.
|
|
|
165
378
|
form.addField('username', null)
|
|
166
379
|
|
|
167
380
|
form.addField('username', {
|
|
381
|
+
value: null,
|
|
168
382
|
validation: {
|
|
169
383
|
rules: ['required'],
|
|
170
384
|
messages: {
|
|
@@ -186,6 +400,7 @@ form.addFields({
|
|
|
186
400
|
|
|
187
401
|
form.addFields({
|
|
188
402
|
full_name: {
|
|
403
|
+
value: null,
|
|
189
404
|
validation: {
|
|
190
405
|
rules: ['required'],
|
|
191
406
|
messages: {
|
|
@@ -194,6 +409,7 @@ form.addFields({
|
|
|
194
409
|
}
|
|
195
410
|
},
|
|
196
411
|
username: {
|
|
412
|
+
value: null,
|
|
197
413
|
validation: {
|
|
198
414
|
rules: ['required'],
|
|
199
415
|
messages: {
|
|
@@ -338,9 +554,11 @@ Method used to validate the entire form or a specific field.
|
|
|
338
554
|
const form = createForm({
|
|
339
555
|
username: {
|
|
340
556
|
value: null,
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
557
|
+
validation: {
|
|
558
|
+
rules: ['required'],
|
|
559
|
+
messages: {
|
|
560
|
+
required: 'The username field is required.'
|
|
561
|
+
}
|
|
344
562
|
}
|
|
345
563
|
}
|
|
346
564
|
})
|
|
@@ -372,9 +590,11 @@ Method used to validate a specific field.
|
|
|
372
590
|
const form = createForm({
|
|
373
591
|
username: {
|
|
374
592
|
value: null,
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
593
|
+
validation: {
|
|
594
|
+
rules: ['required'],
|
|
595
|
+
messages: {
|
|
596
|
+
required: 'The username field is required.'
|
|
597
|
+
}
|
|
378
598
|
}
|
|
379
599
|
}
|
|
380
600
|
})
|
|
@@ -406,9 +626,11 @@ Method used to validate the entire form.
|
|
|
406
626
|
const form = createForm({
|
|
407
627
|
username: {
|
|
408
628
|
value: null,
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
629
|
+
validation: {
|
|
630
|
+
rules: ['required'],
|
|
631
|
+
messages: {
|
|
632
|
+
required: 'The username field is required.'
|
|
633
|
+
}
|
|
412
634
|
}
|
|
413
635
|
}
|
|
414
636
|
})
|
|
@@ -542,6 +764,16 @@ form.messages.first('username')
|
|
|
542
764
|
form.rules.first('username')
|
|
543
765
|
```
|
|
544
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
|
+
|
|
545
777
|
#### any()
|
|
546
778
|
|
|
547
779
|
Returns true if the collection has any items and false if it is empty.
|
|
@@ -585,7 +817,7 @@ form.messages.push('username', {
|
|
|
585
817
|
required: 'The username field is required.'
|
|
586
818
|
})
|
|
587
819
|
|
|
588
|
-
form.rules.push('username', 'required')
|
|
820
|
+
form.rules.push('username', ['required'])
|
|
589
821
|
```
|
|
590
822
|
|
|
591
823
|
#### has(key)
|
|
@@ -609,7 +841,7 @@ form.messages.unset('username')
|
|
|
609
841
|
form.rules.unset('username')
|
|
610
842
|
```
|
|
611
843
|
|
|
612
|
-
#### clear(
|
|
844
|
+
#### clear()
|
|
613
845
|
|
|
614
846
|
Clears the entire collection, making it empty.
|
|
615
847
|
|
|
@@ -621,8 +853,44 @@ form.rules.clear()
|
|
|
621
853
|
|
|
622
854
|
### Validation rules
|
|
623
855
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
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;
|
package/dist/form-wrapper.js
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
//#region src/utils/fields.ts
|
|
2
|
-
var e = (e) =>
|
|
2
|
+
var e = (e) => {
|
|
3
|
+
if (typeof e == "string") {
|
|
4
|
+
let t = [], n = e;
|
|
5
|
+
for (; n.length > 0;) {
|
|
6
|
+
let e = n.indexOf("regex:");
|
|
7
|
+
if (e !== -1) {
|
|
8
|
+
let r = n.substring(0, e);
|
|
9
|
+
r && t.push(...r.split("|").filter(Boolean));
|
|
10
|
+
let i = n.substring(e), a = i.indexOf(":/");
|
|
11
|
+
if (a !== -1) {
|
|
12
|
+
let e = a + 1, r = i.lastIndexOf("/");
|
|
13
|
+
if (r > e) {
|
|
14
|
+
let e = i.substring(r + 1), a = e.match(/^[gimsuy]*/)[0], o = i.substring(0, r + 1 + a.length);
|
|
15
|
+
t.push(o), n = e.substring(a.length), n.startsWith("|") && (n = n.substring(1));
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
t.push(i);
|
|
20
|
+
break;
|
|
21
|
+
} else {
|
|
22
|
+
t.push(...n.split("|").filter(Boolean));
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return t;
|
|
27
|
+
}
|
|
28
|
+
return e;
|
|
29
|
+
}, t = (t) => ({
|
|
3
30
|
validation: {
|
|
4
31
|
rules: t.validation?.rules ? e(t.validation.rules) : [],
|
|
5
32
|
messages: t.validation?.messages ?? {}
|
|
@@ -12,9 +39,9 @@ var e = (e) => typeof e == "string" ? e.split("|") : e, t = (t) => ({
|
|
|
12
39
|
}
|
|
13
40
|
first(e) {
|
|
14
41
|
let t = this.get(e);
|
|
15
|
-
if (t) {
|
|
42
|
+
if (t !== null) {
|
|
16
43
|
let e = Array.isArray(t) ? t[0] : t;
|
|
17
|
-
if (e) return String(e);
|
|
44
|
+
if (e != null) return String(e);
|
|
18
45
|
}
|
|
19
46
|
return null;
|
|
20
47
|
}
|
|
@@ -57,32 +84,45 @@ var e = (e) => typeof e == "string" ? e.split("|") : e, t = (t) => ({
|
|
|
57
84
|
errors = new a();
|
|
58
85
|
messages = new r();
|
|
59
86
|
rules = new i();
|
|
60
|
-
}, s =
|
|
61
|
-
if (e == null) return n();
|
|
62
|
-
String(e).replace(/\s/g, "").length > 0 ? t(e) : n();
|
|
63
|
-
}) }, c = (e) => typeof e == "object" && !!e && !Array.isArray(e), l = (e, t, n = null) => {
|
|
87
|
+
}, s = (e) => typeof e == "object" && !!e && !Array.isArray(e), c = (e, t, n = null) => {
|
|
64
88
|
let r = t || new FormData();
|
|
65
89
|
return Object.keys(e).forEach((t) => {
|
|
66
90
|
let i = e[t];
|
|
67
91
|
if (t = n ? `${n}[${t}]` : t, !([void 0, null].indexOf(i) > -1)) {
|
|
68
|
-
if (
|
|
69
|
-
|
|
92
|
+
if (s(i) && !(i instanceof File) || Array.isArray(i)) {
|
|
93
|
+
c(i, r, t);
|
|
70
94
|
return;
|
|
71
95
|
}
|
|
72
96
|
r.append(t, i);
|
|
73
97
|
}
|
|
74
98
|
}), r;
|
|
75
|
-
},
|
|
99
|
+
}, l = class e {
|
|
100
|
+
static rules = {};
|
|
101
|
+
static defaultMessages = {};
|
|
102
|
+
static interpolateMessage(e, t, n, r) {
|
|
103
|
+
let i = r[t] || t.replace(/_/g, " ");
|
|
104
|
+
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(", "));
|
|
105
|
+
}
|
|
76
106
|
awaiting = !1;
|
|
77
107
|
originalValues = {};
|
|
108
|
+
fieldAttributes = {};
|
|
78
109
|
validation = new o();
|
|
79
110
|
constructor(e) {
|
|
80
111
|
this.addFields(e);
|
|
81
112
|
}
|
|
113
|
+
static extend(t) {
|
|
114
|
+
return t(e, e.rules), e;
|
|
115
|
+
}
|
|
116
|
+
static addRule(t, n) {
|
|
117
|
+
return e.rules[t] = Object.assign(n, { ruleName: t }), e;
|
|
118
|
+
}
|
|
119
|
+
static locale(t) {
|
|
120
|
+
return e.defaultMessages = t.messages, e;
|
|
121
|
+
}
|
|
82
122
|
addField(e, n) {
|
|
83
123
|
if (typeof n == "object" && n && Object.prototype.toString.call(n) === "[object Object]" && "value" in n) {
|
|
84
124
|
let r = t(n);
|
|
85
|
-
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);
|
|
125
|
+
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);
|
|
86
126
|
} else this[e] = n, this.originalValues[e] = n;
|
|
87
127
|
return this;
|
|
88
128
|
}
|
|
@@ -97,14 +137,14 @@ var e = (e) => typeof e == "string" ? e.split("|") : e, t = (t) => ({
|
|
|
97
137
|
fill(e, t = !1) {
|
|
98
138
|
return Object.keys(e).forEach((n) => {
|
|
99
139
|
let r = e[n];
|
|
100
|
-
t && (this.originalValues[n] = r), this[n] = r;
|
|
140
|
+
t && (this.originalValues[n] = r), this[n] = r, n in this.originalValues || (this.originalValues[n] = r);
|
|
101
141
|
}), this;
|
|
102
142
|
}
|
|
103
143
|
get messages() {
|
|
104
144
|
return this.validation.messages;
|
|
105
145
|
}
|
|
106
146
|
removeField(e) {
|
|
107
|
-
return delete this[e], delete this.originalValues[e], this.validation.errors.unset(e), this.validation.messages.unset(e), this.validation.rules.unset(e), this;
|
|
147
|
+
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;
|
|
108
148
|
}
|
|
109
149
|
removeFields(e) {
|
|
110
150
|
return e.forEach((e) => {
|
|
@@ -131,18 +171,58 @@ var e = (e) => typeof e == "string" ? e.split("|") : e, t = (t) => ({
|
|
|
131
171
|
validate(e = null) {
|
|
132
172
|
return e ? this.validateField(e) : this.validateForm();
|
|
133
173
|
}
|
|
134
|
-
validateField(
|
|
135
|
-
this.validation.errors.unset(
|
|
136
|
-
let
|
|
137
|
-
if (
|
|
138
|
-
let
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
let n =
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
174
|
+
validateField(t) {
|
|
175
|
+
this.validation.errors.unset(t);
|
|
176
|
+
let n = this.validation.rules.get(t);
|
|
177
|
+
if (n && n.length > 0) {
|
|
178
|
+
let r = this.constructor.rules;
|
|
179
|
+
return (async () => {
|
|
180
|
+
for (let i of n) {
|
|
181
|
+
let n = i;
|
|
182
|
+
if (typeof n == "function") {
|
|
183
|
+
if ("ruleName" in n) {
|
|
184
|
+
let r = n.ruleName;
|
|
185
|
+
if (r === "nullable" && (this[t] === null || this[t] === void 0 || this[t] === "")) return;
|
|
186
|
+
try {
|
|
187
|
+
await n(this[t], [], this, t);
|
|
188
|
+
} catch (n) {
|
|
189
|
+
let i = this.validation.messages.get(t);
|
|
190
|
+
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;
|
|
191
|
+
}
|
|
192
|
+
} else await new Promise((e, r) => {
|
|
193
|
+
let i = !1, a = (e) => {
|
|
194
|
+
i = !0, e && this.validation.errors.push(t, e);
|
|
195
|
+
};
|
|
196
|
+
try {
|
|
197
|
+
let o = n({
|
|
198
|
+
value: this[t],
|
|
199
|
+
fail: a,
|
|
200
|
+
form: this,
|
|
201
|
+
field: t
|
|
202
|
+
});
|
|
203
|
+
o instanceof Promise ? o.then(() => {
|
|
204
|
+
i ? r() : e();
|
|
205
|
+
}).catch(() => {
|
|
206
|
+
r();
|
|
207
|
+
}) : i ? r() : e();
|
|
208
|
+
} catch {
|
|
209
|
+
r();
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
let a = n.indexOf(":"), o = a === -1 ? n : n.substring(0, a);
|
|
215
|
+
if (o === "nullable" && (this[t] === null || this[t] === void 0 || this[t] === "")) return;
|
|
216
|
+
let s = a === -1 ? [] : n.substring(a + 1).split(",");
|
|
217
|
+
if (o in r) try {
|
|
218
|
+
await r[o](this[t], s, this, t);
|
|
219
|
+
} catch (n) {
|
|
220
|
+
let r = this.validation.messages.get(t);
|
|
221
|
+
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;
|
|
222
|
+
}
|
|
223
|
+
else throw Error(`There is no validation rule called "${o}"`);
|
|
224
|
+
}
|
|
225
|
+
})();
|
|
146
226
|
}
|
|
147
227
|
return Promise.resolve();
|
|
148
228
|
}
|
|
@@ -157,8 +237,8 @@ var e = (e) => typeof e == "string" ? e.split("|") : e, t = (t) => ({
|
|
|
157
237
|
}), t;
|
|
158
238
|
}
|
|
159
239
|
valuesAsFormData(e) {
|
|
160
|
-
return
|
|
240
|
+
return c(this.values(e));
|
|
161
241
|
}
|
|
162
|
-
},
|
|
242
|
+
}, u = (e) => new l(e), d = l;
|
|
163
243
|
//#endregion
|
|
164
|
-
export {
|
|
244
|
+
export { u as createForm, d as default };
|