@nanoporetech-digital/components 2.12.0 → 2.13.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/CHANGELOG.md +18 -0
- package/dist/cjs/index.cjs.js +2 -0
- package/dist/cjs/index.cjs.js.map +1 -1
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/nano-components.cjs.js +1 -1
- package/dist/cjs/nano-field-validator.cjs.entry.js +250 -118
- package/dist/cjs/nano-field-validator.cjs.entry.js.map +1 -1
- package/dist/cjs/nano-file-upload.cjs.entry.js +1 -1
- package/dist/cjs/nano-file-upload.cjs.entry.js.map +1 -1
- package/dist/cjs/nano-input.cjs.entry.js +16 -3
- package/dist/cjs/nano-input.cjs.entry.js.map +1 -1
- package/dist/cjs/nano-nav-item_2.cjs.entry.js +1 -0
- package/dist/cjs/nano-nav-item_2.cjs.entry.js.map +1 -1
- package/dist/collection/components/accordion/accordion.js +1 -1
- package/dist/collection/components/alert/alert.js +1 -1
- package/dist/collection/components/algolia/algolia-filter.js +2 -2
- package/dist/collection/components/algolia/algolia-input.js +5 -5
- package/dist/collection/components/algolia/algolia-results.js +1 -1
- package/dist/collection/components/algolia/algolia.js +6 -6
- package/dist/collection/components/checkbox/checkbox-group.js +2 -2
- package/dist/collection/components/checkbox/checkbox.js +3 -3
- package/dist/collection/components/datalist/datalist.js +1 -1
- package/dist/collection/components/date-input/date-input.js +8 -8
- package/dist/collection/components/date-picker/date-picker.js +5 -5
- package/dist/collection/components/details/details.js +1 -1
- package/dist/collection/components/dialog/dialog.js +1 -1
- package/dist/collection/components/dropdown/dropdown.js +1 -1
- package/dist/collection/components/field-validator/field-validator-interface.js.map +1 -1
- package/dist/collection/components/field-validator/field-validator.js +329 -124
- package/dist/collection/components/field-validator/field-validator.js.map +1 -1
- package/dist/collection/components/file-upload/file-upload.css +0 -1
- package/dist/collection/components/file-upload/file-upload.js +4 -4
- package/dist/collection/components/global-nav/global-nav.js +4 -4
- package/dist/collection/components/grid/grid-item.js +1 -1
- package/dist/collection/components/icon/icon.js +1 -1
- package/dist/collection/components/input/input.js +37 -8
- package/dist/collection/components/input/input.js.map +1 -1
- package/dist/collection/components/nav-item/nav-item.js +4 -4
- package/dist/collection/components/range/range.js +4 -4
- package/dist/collection/components/resize-observe/resize-observe.js +1 -1
- package/dist/collection/components/select/select.js +8 -7
- package/dist/collection/components/select/select.js.map +1 -1
- package/dist/collection/components/slides/slides.js +7 -7
- package/dist/collection/components/tabs/tab-group.js +2 -2
- package/dist/collection/index.js +1 -0
- package/dist/collection/index.js.map +1 -1
- package/dist/components/index.js +1 -0
- package/dist/components/index.js.map +1 -1
- package/dist/components/input.js +17 -3
- package/dist/components/input.js.map +1 -1
- package/dist/components/nano-field-validator.js +255 -120
- package/dist/components/nano-field-validator.js.map +1 -1
- package/dist/components/nano-file-upload.js +1 -1
- package/dist/components/nano-file-upload.js.map +1 -1
- package/dist/components/select.js +1 -0
- package/dist/components/select.js.map +1 -1
- package/dist/custom-elements/index.js +270 -124
- package/dist/custom-elements/index.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/loader.js +1 -1
- package/dist/esm/nano-components.js +1 -1
- package/dist/esm/nano-field-validator.entry.js +250 -118
- package/dist/esm/nano-field-validator.entry.js.map +1 -1
- package/dist/esm/nano-file-upload.entry.js +1 -1
- package/dist/esm/nano-file-upload.entry.js.map +1 -1
- package/dist/esm/nano-input.entry.js +16 -3
- package/dist/esm/nano-input.entry.js.map +1 -1
- package/dist/esm/nano-nav-item_2.entry.js +1 -0
- package/dist/esm/nano-nav-item_2.entry.js.map +1 -1
- package/dist/esm-es5/index.js +2 -2
- package/dist/esm-es5/index.js.map +1 -1
- package/dist/esm-es5/loader.js +1 -1
- package/dist/esm-es5/loader.js.map +1 -1
- package/dist/esm-es5/nano-components.js +1 -1
- package/dist/esm-es5/nano-components.js.map +1 -1
- package/dist/esm-es5/nano-field-validator.entry.js +2 -2
- package/dist/esm-es5/nano-field-validator.entry.js.map +1 -1
- package/dist/esm-es5/nano-file-upload.entry.js +1 -1
- package/dist/esm-es5/nano-file-upload.entry.js.map +1 -1
- package/dist/esm-es5/nano-input.entry.js +1 -1
- package/dist/esm-es5/nano-input.entry.js.map +1 -1
- package/dist/esm-es5/nano-nav-item_2.entry.js +1 -1
- package/dist/esm-es5/nano-nav-item_2.entry.js.map +1 -1
- package/dist/nano-components/index.esm.js +1 -1
- package/dist/nano-components/index.esm.js.map +1 -1
- package/dist/nano-components/nano-components.esm.js +1 -1
- package/dist/nano-components/nano-components.esm.js.map +1 -1
- package/dist/nano-components/p-0d699368.system.js +5 -0
- package/dist/nano-components/{p-3258c568.system.js.map → p-0d699368.system.js.map} +1 -1
- package/dist/nano-components/p-18863670.system.entry.js +5 -0
- package/dist/nano-components/p-18863670.system.entry.js.map +1 -0
- package/dist/nano-components/p-53957ec6.system.js +1 -1
- package/dist/nano-components/p-53957ec6.system.js.map +1 -1
- package/dist/nano-components/{p-01667573.entry.js → p-634a58f7.entry.js} +2 -2
- package/dist/nano-components/p-634a58f7.entry.js.map +1 -0
- package/dist/nano-components/p-7f051c20.entry.js +5 -0
- package/dist/nano-components/p-7f051c20.entry.js.map +1 -0
- package/dist/nano-components/p-a07cf44c.system.entry.js +5 -0
- package/dist/nano-components/{p-4558a9c6.system.entry.js.map → p-a07cf44c.system.entry.js.map} +1 -1
- package/dist/nano-components/{p-96d9b8b9.system.entry.js → p-c2bbf0fb.system.entry.js} +2 -2
- package/dist/nano-components/p-c2bbf0fb.system.entry.js.map +1 -0
- package/dist/nano-components/{p-72893d12.system.entry.js → p-cb512cff.system.entry.js} +2 -2
- package/dist/nano-components/p-cb512cff.system.entry.js.map +1 -0
- package/dist/nano-components/p-e9fddc1a.entry.js +5 -0
- package/dist/nano-components/{p-91614b43.entry.js.map → p-e9fddc1a.entry.js.map} +1 -1
- package/dist/nano-components/{p-055f7d35.entry.js → p-ed0bdea9.entry.js} +2 -2
- package/dist/nano-components/p-ed0bdea9.entry.js.map +1 -0
- package/dist/themes/nanopore.css +1 -1
- package/dist/themes/nanopore.css.map +1 -1
- package/dist/types/components/field-validator/field-validator-interface.d.ts +5 -1
- package/dist/types/components/field-validator/field-validator.d.ts +61 -12
- package/dist/types/components/input/input.d.ts +6 -1
- package/dist/types/components.d.ts +25 -3
- package/dist/types/index.d.ts +1 -0
- package/docs-json.json +65 -3
- package/docs-vscode.json +6 -2
- package/package.json +2 -2
- package/dist/nano-components/p-01667573.entry.js.map +0 -1
- package/dist/nano-components/p-055f7d35.entry.js.map +0 -1
- package/dist/nano-components/p-2b478ca1.system.entry.js +0 -5
- package/dist/nano-components/p-2b478ca1.system.entry.js.map +0 -1
- package/dist/nano-components/p-3258c568.system.js +0 -5
- package/dist/nano-components/p-4558a9c6.system.entry.js +0 -5
- package/dist/nano-components/p-5f4fc2b4.entry.js +0 -5
- package/dist/nano-components/p-5f4fc2b4.entry.js.map +0 -1
- package/dist/nano-components/p-72893d12.system.entry.js.map +0 -1
- package/dist/nano-components/p-91614b43.entry.js +0 -5
- package/dist/nano-components/p-96d9b8b9.system.entry.js.map +0 -1
@@ -5,7 +5,6 @@ import { Component, Prop, h, Host, Element, State, Watch, Event, Method, } from
|
|
5
5
|
import { createStore } from '@stencil/store';
|
6
6
|
/**
|
7
7
|
* A toolbox for `nano-...` form fields and form validation.
|
8
|
-
*
|
9
8
|
* - Easy to add validation accross field dependencies - e.g. "When Field1 contains '123' Field2 must contain '456'"
|
10
9
|
* - Easy access to whole form and individual field validity states
|
11
10
|
* - Easy access to form data payload
|
@@ -15,8 +14,15 @@ import { createStore } from '@stencil/store';
|
|
15
14
|
export class FieldValidator {
|
16
15
|
constructor() {
|
17
16
|
this.submitted = false;
|
18
|
-
this.
|
19
|
-
|
17
|
+
this.allFields = [];
|
18
|
+
this.nanoFieldSelector = `
|
19
|
+
nano-input,
|
20
|
+
nano-select,
|
21
|
+
nano-file-upload,
|
22
|
+
nano-date-input,
|
23
|
+
nano-checkbox
|
24
|
+
`;
|
25
|
+
// annoyingly, whenever we attempt to `checkValidty()` it fires `invalid` events.
|
20
26
|
// this is used to prevent infinite loops / multiple calls
|
21
27
|
this.internalValidate = false;
|
22
28
|
// Public API
|
@@ -25,12 +31,27 @@ export class FieldValidator {
|
|
25
31
|
/** Tries to scroll to the first invalid field on submit */
|
26
32
|
this.scrollToInvalid = true;
|
27
33
|
this._dirty = false;
|
34
|
+
/** By default, `nano-field-validator` will also track all native form field elements.
|
35
|
+
* You can add extra web-component form fields to listen to
|
36
|
+
* (as long as they match the standard form field spec) by using the `fieldSelector` prop.
|
37
|
+
*/
|
38
|
+
this.extraFieldSelector = 'input, select, textarea';
|
28
39
|
// Event handlers
|
29
|
-
/**
|
40
|
+
/**
|
41
|
+
* Fired whenever store values change and potentially checks validity
|
42
|
+
* @param key - the key of the store that's just changed
|
43
|
+
* @param newVal - the incoming, new value
|
44
|
+
*/
|
30
45
|
this.handleStoreChange = async (key, newVal) => {
|
31
|
-
const found = this.
|
32
|
-
|
46
|
+
const found = this.allFields.find((field) => field.name === key);
|
47
|
+
// field update has come programmatically (not from ui),
|
48
|
+
// so let's update the underlying ui field
|
49
|
+
if ((found &&
|
50
|
+
found.tagName === 'NANO-FILE-UPLOAD' &&
|
51
|
+
!this.fileStateEqual(key, found)) ||
|
52
|
+
(found.tagName !== 'NANO-FILE-UPLOAD' && found.value !== newVal)) {
|
33
53
|
this.storeToFields([found]);
|
54
|
+
}
|
34
55
|
if (this.validateOn === 'dirty' && this.dirty) {
|
35
56
|
this.internalValidate = true;
|
36
57
|
await this.validateAllFields();
|
@@ -39,17 +60,40 @@ export class FieldValidator {
|
|
39
60
|
}
|
40
61
|
this.nanoPayloadChange.emit(this._store.state);
|
41
62
|
};
|
42
|
-
/**
|
63
|
+
/**
|
64
|
+
* Handles nano field value changes and passes to store
|
65
|
+
* @param ev - the incoming change event
|
66
|
+
*/
|
43
67
|
this.handleFieldChange = (ev) => {
|
68
|
+
if (!this.nanoFields.includes(ev.target))
|
69
|
+
return;
|
44
70
|
this._dirty = true;
|
45
71
|
this.fieldsToStore([ev.target]);
|
46
72
|
};
|
47
|
-
/**
|
73
|
+
/**
|
74
|
+
* Handles non-nano field value changes and passes to store
|
75
|
+
* @param ev - the incoming change event
|
76
|
+
*/
|
77
|
+
this.handlePlainFieldChange = (ev) => {
|
78
|
+
if (!this.plainFields.includes(ev.target))
|
79
|
+
return;
|
80
|
+
this.fieldsToStore([ev.target]);
|
81
|
+
};
|
82
|
+
/**
|
83
|
+
* Handles default field validation events
|
84
|
+
* @param ev - the invalid event
|
85
|
+
*/
|
48
86
|
this.handleFormInvalid = async (ev) => {
|
49
|
-
|
87
|
+
// if it's a non-nano field, we'll let default html5 validation do it's thing
|
88
|
+
if (!this.plainFields.includes(ev.target)) {
|
89
|
+
ev.preventDefault();
|
90
|
+
}
|
50
91
|
this._valid = false;
|
92
|
+
// whenever `checkValidity` is called, this handler is in-turn called.
|
93
|
+
// this flag is used to stop infinite loops
|
51
94
|
if (this.internalValidate)
|
52
95
|
return;
|
96
|
+
// a submit must have happened to if 'submitThenDirty' turn on 'dirty' checking now
|
53
97
|
if (this.validateOn === 'submitThenDirty')
|
54
98
|
this.validateOn = 'dirty';
|
55
99
|
this.submitted = true;
|
@@ -69,7 +113,10 @@ export class FieldValidator {
|
|
69
113
|
this.scrollToFirstInvalid();
|
70
114
|
this.nanoInvalid.emit();
|
71
115
|
};
|
72
|
-
/**
|
116
|
+
/**
|
117
|
+
* stops default form submission, checks if valid, then submits manually
|
118
|
+
* @param e - a submit event from the nested form element
|
119
|
+
*/
|
73
120
|
this.handleSubmit = async (e) => {
|
74
121
|
e.preventDefault();
|
75
122
|
if (this.validateOn === 'submitThenDirty')
|
@@ -94,6 +141,7 @@ export class FieldValidator {
|
|
94
141
|
return this._activeForm;
|
95
142
|
}
|
96
143
|
set activeForm(form) {
|
144
|
+
// manages event listners on whatever form is used (slotted on created here)
|
97
145
|
if (!form)
|
98
146
|
return;
|
99
147
|
if (this._activeForm) {
|
@@ -104,7 +152,7 @@ export class FieldValidator {
|
|
104
152
|
}
|
105
153
|
/** Sync up validateOn with all fields */
|
106
154
|
validateOnChange() {
|
107
|
-
this.
|
155
|
+
this.nanoFields.forEach((field) => {
|
108
156
|
if (field.tagName === 'NANO-CHECKBOX') {
|
109
157
|
const cbg = field.closest('nano-checkbox-group');
|
110
158
|
if (cbg)
|
@@ -132,7 +180,7 @@ export class FieldValidator {
|
|
132
180
|
get payload() {
|
133
181
|
return this._store.state;
|
134
182
|
}
|
135
|
-
/** Returns true if validation errors will be displayed to the user */
|
183
|
+
/** Returns true if validation errors will be displayed to the user. @readonly */
|
136
184
|
get showValidation() {
|
137
185
|
return (this.validateOn === 'dirty' && this.dirty) || this.submitted;
|
138
186
|
}
|
@@ -149,26 +197,58 @@ export class FieldValidator {
|
|
149
197
|
```
|
150
198
|
*/
|
151
199
|
get validationState() {
|
200
|
+
// TODO - migrate nano-fields away from using proprietary methods in a bid to be closer to the spec
|
201
|
+
// this is big and ugly.
|
202
|
+
// why? Cos' it must unify checking validity state for both
|
203
|
+
// `nano-...` and plain form fields.
|
152
204
|
const validationState = [];
|
153
|
-
this.
|
205
|
+
this.allFields.forEach(async (field) => {
|
154
206
|
const found = validationState.find((v) => v.name === field.name);
|
207
|
+
let pf;
|
208
|
+
let nf;
|
155
209
|
if (found) {
|
156
|
-
|
157
|
-
|
158
|
-
|
210
|
+
if (field.validationMessage) {
|
211
|
+
pf = field;
|
212
|
+
found.validityMessage = pf.validationMessage.length
|
213
|
+
? pf.validationMessage
|
214
|
+
: found.validityMessage;
|
215
|
+
this.internalValidate = true;
|
216
|
+
if (found.valid && !pf.checkValidity())
|
217
|
+
found.valid = false;
|
218
|
+
this.internalValidate = false;
|
219
|
+
}
|
220
|
+
else if (field.validityMessage) {
|
221
|
+
nf = field;
|
222
|
+
found.validityMessage = nf.validityMessage.length
|
223
|
+
? nf.validityMessage
|
224
|
+
: nf.validityMessage;
|
225
|
+
if (found.valid && nf.invalid)
|
226
|
+
found.valid = false;
|
227
|
+
}
|
159
228
|
if (!found.fields.find((f) => f === field))
|
160
229
|
found.fields.push(field);
|
161
|
-
|
162
|
-
|
163
|
-
|
230
|
+
}
|
231
|
+
let valid;
|
232
|
+
let validityMessage;
|
233
|
+
if (field.checkValidity) {
|
234
|
+
pf = field;
|
235
|
+
this.internalValidate = true;
|
236
|
+
valid = pf.checkValidity();
|
237
|
+
this.internalValidate = false;
|
238
|
+
validityMessage = pf.validationMessage;
|
239
|
+
}
|
240
|
+
else {
|
241
|
+
nf = field;
|
242
|
+
valid = !nf.invalid;
|
243
|
+
validityMessage = nf.validityMessage;
|
164
244
|
}
|
165
245
|
validationState.push({
|
166
246
|
fields: [field],
|
167
247
|
name: field.name,
|
168
|
-
valid: !field.invalid,
|
169
248
|
value: this._store.state[field.name],
|
170
249
|
dirty: false,
|
171
|
-
|
250
|
+
valid,
|
251
|
+
validityMessage,
|
172
252
|
});
|
173
253
|
});
|
174
254
|
return validationState;
|
@@ -180,6 +260,24 @@ export class FieldValidator {
|
|
180
260
|
async setStore(state) {
|
181
261
|
Object.entries(state).forEach(([key, val]) => (this.store.state[key] = val));
|
182
262
|
}
|
263
|
+
/**
|
264
|
+
* Sets custom validity for all / some form fields.
|
265
|
+
* @param validity - a validity object of `{fieldName: errorString}` pairs. Set as an empty string to clear the error.
|
266
|
+
*/
|
267
|
+
async setCustomValidity(validity) {
|
268
|
+
return await Promise.all(Object.entries(validity).map(async ([key, err]) => {
|
269
|
+
const field = this.allFields.find((f) => f.name === key);
|
270
|
+
if (!!field)
|
271
|
+
await this.setFieldError(field, err);
|
272
|
+
}));
|
273
|
+
}
|
274
|
+
/**
|
275
|
+
* Clear all custom validation.
|
276
|
+
* @param validity
|
277
|
+
*/
|
278
|
+
async resetValidity() {
|
279
|
+
return await Promise.all(this.allFields.map(async (field) => await this.setFieldError(field, '')));
|
280
|
+
}
|
183
281
|
// private methods
|
184
282
|
attachSlotObserver() {
|
185
283
|
if (!!this.mo)
|
@@ -199,62 +297,66 @@ export class FieldValidator {
|
|
199
297
|
}
|
200
298
|
/** Checks for new `nano-...` fields and adds them to our watch array and value store */
|
201
299
|
setupFields() {
|
202
|
-
let
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
nano-date-input,
|
207
|
-
nano-checkbox
|
208
|
-
`));
|
209
|
-
fields = fields.filter((f) => !!f.name && !!f.name.length);
|
300
|
+
let nanoFields = Array.from(this.host.querySelectorAll(this.nanoFieldSelector));
|
301
|
+
let plainFields = Array.from(this.host.querySelectorAll(this.extraFieldSelector)).filter((e) => !e.closest(this.nanoFieldSelector));
|
302
|
+
nanoFields = nanoFields.filter((f) => !!f.name && !!f.name.length);
|
303
|
+
plainFields = plainFields.filter((f) => !!f.name && !!f.name.length);
|
210
304
|
// do we have any currently un-watched fields?
|
211
|
-
if (!
|
305
|
+
if (![...nanoFields, ...plainFields].filter((f) => !this.allFields.includes(f)).length)
|
212
306
|
return;
|
213
307
|
// setup the initial store state / refresh on new fields
|
214
|
-
this.
|
215
|
-
this.
|
308
|
+
this.nanoFields = nanoFields;
|
309
|
+
this.plainFields = plainFields;
|
310
|
+
this.allFields = [...nanoFields, ...plainFields];
|
311
|
+
this.storeToFields(this.allFields);
|
216
312
|
this.validateOnChange();
|
217
|
-
this.fieldsToStore(this.
|
313
|
+
this.fieldsToStore(this.allFields);
|
218
314
|
this.nanoPayloadChange.emit(this._store.state);
|
219
315
|
}
|
220
316
|
storeToFields(fields) {
|
221
317
|
fields.forEach((field) => {
|
318
|
+
var _a;
|
222
319
|
const fieldName = field.name;
|
223
320
|
if (!fieldName.length ||
|
224
321
|
typeof this._store.state[fieldName] === 'undefined')
|
225
322
|
return;
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
break;
|
254
|
-
default:
|
255
|
-
field.value = this._store.state[fieldName];
|
256
|
-
break;
|
323
|
+
if (field.tagName === 'NANO-CHECKBOX' ||
|
324
|
+
['radio', 'checkbox'].includes(field.type)) {
|
325
|
+
let cb = field;
|
326
|
+
if (cb.type === 'radio' ||
|
327
|
+
cb.type === 'segment' ||
|
328
|
+
cb.type === 'segment-pill') {
|
329
|
+
// single radio type control
|
330
|
+
if (this._store.state[fieldName] === cb.value)
|
331
|
+
cb.checked = true;
|
332
|
+
else
|
333
|
+
cb.checked = false;
|
334
|
+
}
|
335
|
+
else if (Array.isArray(this._store.state[fieldName])) {
|
336
|
+
// multiple checkbox like controls
|
337
|
+
if (this._store.state[fieldName].includes(cb.value))
|
338
|
+
cb.checked = true;
|
339
|
+
else
|
340
|
+
cb.checked = false;
|
341
|
+
}
|
342
|
+
else {
|
343
|
+
// single checkbox like control
|
344
|
+
if (this._store.state[fieldName] === cb.value)
|
345
|
+
cb.checked = true;
|
346
|
+
else
|
347
|
+
cb.checked = false;
|
348
|
+
}
|
349
|
+
return;
|
257
350
|
}
|
351
|
+
if (field.tagName === 'NANO-FILE-UPLOAD') {
|
352
|
+
const ff = field;
|
353
|
+
// this can only work if the field is empty rn... a one-time deal
|
354
|
+
if (!((_a = ff.files) === null || _a === void 0 ? void 0 : _a.length))
|
355
|
+
ff.files = this._store.state[fieldName];
|
356
|
+
return;
|
357
|
+
}
|
358
|
+
// default
|
359
|
+
field.value = this._store.state[fieldName];
|
258
360
|
});
|
259
361
|
}
|
260
362
|
/** Loops through all `nano-...` fields and extracts their values into our store */
|
@@ -263,52 +365,70 @@ export class FieldValidator {
|
|
263
365
|
const fieldName = field.name;
|
264
366
|
if (!fieldName.length)
|
265
367
|
return;
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
if (cb.
|
286
|
-
|
287
|
-
this._store.state[fieldName] = [...currentArr, cb.value];
|
288
|
-
}
|
289
|
-
}
|
290
|
-
else {
|
291
|
-
this._store.state[fieldName] = currentArr.filter((v) => v !== cb.value);
|
368
|
+
if (field.tagName === 'NANO-CHECKBOX' ||
|
369
|
+
['radio', 'checkbox'].includes(field.type)) {
|
370
|
+
let cb = field;
|
371
|
+
if (cb.type === 'radio' ||
|
372
|
+
cb.type === 'segment' ||
|
373
|
+
cb.type === 'segment-pill') {
|
374
|
+
// radio type control - only one can be checked
|
375
|
+
if (cb.checked)
|
376
|
+
this._store.state[fieldName] = cb.value;
|
377
|
+
}
|
378
|
+
else if (this.allFields.filter((f) => f.name === fieldName &&
|
379
|
+
(f.tagName === 'NANO-CHECKBOX' ||
|
380
|
+
f.type === 'checkbox')).length > 1) {
|
381
|
+
// multiple checkbox type control
|
382
|
+
const currentArr = Array.isArray(this._store.state[fieldName])
|
383
|
+
? this._store.state[fieldName]
|
384
|
+
: [];
|
385
|
+
if (cb.checked) {
|
386
|
+
// checked
|
387
|
+
if (!this._store.state[fieldName].includes(cb.value)) {
|
388
|
+
this._store.state[fieldName] = [...currentArr, cb.value];
|
292
389
|
}
|
293
390
|
}
|
294
391
|
else {
|
295
|
-
//
|
296
|
-
|
297
|
-
this._store.state[fieldName] = cb.value;
|
298
|
-
else
|
299
|
-
this._store.state[fieldName] = '';
|
392
|
+
// unchecked
|
393
|
+
this._store.state[fieldName] = currentArr.filter((v) => v !== cb.value);
|
300
394
|
}
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
395
|
+
}
|
396
|
+
else {
|
397
|
+
// single checkbox - on or off
|
398
|
+
if (cb.checked)
|
399
|
+
this._store.state[fieldName] = cb.value;
|
400
|
+
else
|
401
|
+
this._store.state[fieldName] = '';
|
402
|
+
}
|
403
|
+
return;
|
308
404
|
}
|
405
|
+
if (field.tagName === 'NANO-FILE-UPLOAD') {
|
406
|
+
const ff = field;
|
407
|
+
if (!this.fileStateEqual(fieldName, ff))
|
408
|
+
this._store.state[fieldName] = ff.files;
|
409
|
+
return;
|
410
|
+
}
|
411
|
+
// default
|
412
|
+
this._store.state[fieldName] = field.value;
|
309
413
|
});
|
310
414
|
}
|
311
|
-
/**
|
415
|
+
/**
|
416
|
+
* Tries to ascertain whether the current model
|
417
|
+
* value is the same as the `nano-file-upload` value
|
418
|
+
* @param fieldName - the key to access from the data store
|
419
|
+
* @param field - the nano-file-upload field to assess against
|
420
|
+
* @returns true for equal, false for not equal
|
421
|
+
*/
|
422
|
+
fileStateEqual(fieldName, field) {
|
423
|
+
return (JSON.stringify(this._store.state[fieldName]) ===
|
424
|
+
JSON.stringify(field.files) ||
|
425
|
+
this._store.state[fieldName] == field.files);
|
426
|
+
}
|
427
|
+
/**
|
428
|
+
* Checks for user defined validations
|
429
|
+
* @param key - current key of the data model to validate
|
430
|
+
* @param newVal - the newly set, incoming value to validate
|
431
|
+
*/
|
312
432
|
async validate(key, newVal) {
|
313
433
|
if (!this.validation)
|
314
434
|
return;
|
@@ -321,21 +441,33 @@ export class FieldValidator {
|
|
321
441
|
// collection loop into a promise
|
322
442
|
await Promise.all(Object.entries(res).map(async ([key, o]) => {
|
323
443
|
// switch on/off validation messages
|
324
|
-
const field = this.
|
444
|
+
const field = this.allFields.find((f) => f.name === key);
|
325
445
|
let validityTarget = field;
|
326
446
|
if (field.tagName === 'NANO-CHECKBOX') {
|
447
|
+
// if we have a checkbox-group, set the validation message there
|
327
448
|
const cbg = field.closest('nano-checkbox-group');
|
328
449
|
validityTarget = cbg || field;
|
329
450
|
}
|
330
|
-
|
331
|
-
|
451
|
+
if ((validityTarget.validityMessage ||
|
452
|
+
validityTarget.validationMessage) === o.msg &&
|
453
|
+
o.valid) {
|
454
|
+
// status is now valid - clear the error
|
332
455
|
await this.setFieldError(validityTarget, '');
|
333
|
-
|
456
|
+
}
|
334
457
|
else if (!o.valid) {
|
458
|
+
// status is invalid. Set the error
|
335
459
|
await this.setFieldError(validityTarget, o.msg);
|
336
460
|
}
|
337
461
|
}));
|
338
462
|
}
|
463
|
+
/** Loops through all store entries and checks custom validation */
|
464
|
+
async validateAllFields() {
|
465
|
+
// This forces our loop to `await` and finish sequentially ... silly async stencil methods
|
466
|
+
await Object.entries(this._store.state).reduce(async (memo, [key, value]) => {
|
467
|
+
await memo;
|
468
|
+
await this.validate(key, value);
|
469
|
+
}, undefined);
|
470
|
+
}
|
339
471
|
/**
|
340
472
|
* Utility to smooth out setting error messages
|
341
473
|
* (it's a different method on `nano-checkbox` 'cos they don't show errors themselves)
|
@@ -345,16 +477,10 @@ export class FieldValidator {
|
|
345
477
|
async setFieldError(field, msg) {
|
346
478
|
if (field['showError'])
|
347
479
|
await field.showError(msg);
|
348
|
-
else
|
480
|
+
else if (field['setError'])
|
349
481
|
await field.setError(msg);
|
350
|
-
|
351
|
-
|
352
|
-
async validateAllFields() {
|
353
|
-
// This forces our loop to `await` and finish sequentially ... silly async stencil methods
|
354
|
-
await Object.entries(this._store.state).reduce(async (memo, [key, value]) => {
|
355
|
-
await memo;
|
356
|
-
await this.validate(key, value);
|
357
|
-
}, undefined);
|
482
|
+
else
|
483
|
+
field.setCustomValidity(msg);
|
358
484
|
}
|
359
485
|
scrollToFirstInvalid() {
|
360
486
|
if (!this.scrollToInvalid)
|
@@ -383,17 +509,21 @@ export class FieldValidator {
|
|
383
509
|
requestAnimationFrame(() => {
|
384
510
|
this.setupFields();
|
385
511
|
this.attachSlotObserver();
|
386
|
-
this._store.on('set', (key, value) => this.handleStoreChange(key, value));
|
387
512
|
this.host.addEventListener('nanoChange', this.handleFieldChange);
|
513
|
+
this.host.addEventListener('input', this.handlePlainFieldChange);
|
514
|
+
this.host.addEventListener('change', this.handlePlainFieldChange);
|
388
515
|
this.host.addEventListener('submit', this.handleSubmit);
|
516
|
+
this._store.on('set', this.handleStoreChange);
|
389
517
|
});
|
390
518
|
}
|
391
519
|
disconnectedCallback() {
|
392
520
|
if (this.mo)
|
393
521
|
this.mo.disconnect();
|
394
|
-
this._store.reset();
|
395
522
|
this.host.removeEventListener('nanoChange', this.handleFieldChange);
|
523
|
+
this.host.removeEventListener('input', this.handlePlainFieldChange);
|
524
|
+
this.host.removeEventListener('change', this.handlePlainFieldChange);
|
396
525
|
this.host.removeEventListener('submit', this.handleSubmit);
|
526
|
+
this._store.reset();
|
397
527
|
if (this.activeForm)
|
398
528
|
this.activeForm.removeEventListener('invalid', this.handleFormInvalid, true);
|
399
529
|
}
|
@@ -530,7 +660,7 @@ export class FieldValidator {
|
|
530
660
|
"optional": false,
|
531
661
|
"docs": {
|
532
662
|
"tags": [],
|
533
|
-
"text": "Returns true if validation errors will be displayed to the user"
|
663
|
+
"text": "Returns true if validation errors will be displayed to the user. @readonly"
|
534
664
|
},
|
535
665
|
"getter": true,
|
536
666
|
"setter": false,
|
@@ -546,7 +676,15 @@ export class FieldValidator {
|
|
546
676
|
"references": {
|
547
677
|
"ValidationState": {
|
548
678
|
"location": "import",
|
549
|
-
"path": "/builds/
|
679
|
+
"path": "/builds/f85YXDf1/0/Digital/nano-components/packages/components/src/interface.d.ts"
|
680
|
+
},
|
681
|
+
"PlainFormEles": {
|
682
|
+
"location": "import",
|
683
|
+
"path": "/builds/f85YXDf1/0/Digital/nano-components/packages/components/src/interface.d.ts"
|
684
|
+
},
|
685
|
+
"NanoFormEles": {
|
686
|
+
"location": "import",
|
687
|
+
"path": "/builds/f85YXDf1/0/Digital/nano-components/packages/components/src/interface.d.ts"
|
550
688
|
}
|
551
689
|
}
|
552
690
|
},
|
@@ -559,6 +697,26 @@ export class FieldValidator {
|
|
559
697
|
"getter": true,
|
560
698
|
"setter": false
|
561
699
|
},
|
700
|
+
"extraFieldSelector": {
|
701
|
+
"type": "string",
|
702
|
+
"mutable": false,
|
703
|
+
"complexType": {
|
704
|
+
"original": "string",
|
705
|
+
"resolved": "string",
|
706
|
+
"references": {}
|
707
|
+
},
|
708
|
+
"required": false,
|
709
|
+
"optional": false,
|
710
|
+
"docs": {
|
711
|
+
"tags": [],
|
712
|
+
"text": "By default, `nano-field-validator` will also track all native form field elements.\nYou can add extra web-component form fields to listen to\n(as long as they match the standard form field spec) by using the `fieldSelector` prop."
|
713
|
+
},
|
714
|
+
"getter": false,
|
715
|
+
"setter": false,
|
716
|
+
"attribute": "extra-field-selector",
|
717
|
+
"reflect": false,
|
718
|
+
"defaultValue": "'input, select, textarea'"
|
719
|
+
},
|
562
720
|
"validation": {
|
563
721
|
"type": "unknown",
|
564
722
|
"mutable": false,
|
@@ -568,7 +726,7 @@ export class FieldValidator {
|
|
568
726
|
"references": {
|
569
727
|
"ValidatorValueStore": {
|
570
728
|
"location": "import",
|
571
|
-
"path": "/builds/
|
729
|
+
"path": "/builds/f85YXDf1/0/Digital/nano-components/packages/components/src/interface.d.ts"
|
572
730
|
}
|
573
731
|
}
|
574
732
|
},
|
@@ -595,8 +753,8 @@ export class FieldValidator {
|
|
595
753
|
}
|
596
754
|
}; }
|
597
755
|
static get states() { return {
|
598
|
-
"userForm": {},
|
599
756
|
"submitted": {},
|
757
|
+
"userForm": {},
|
600
758
|
"_dirty": {},
|
601
759
|
"_valid": {},
|
602
760
|
"_store": {}
|
@@ -617,7 +775,7 @@ export class FieldValidator {
|
|
617
775
|
"references": {
|
618
776
|
"ValidatorValueStore": {
|
619
777
|
"location": "import",
|
620
|
-
"path": "/builds/
|
778
|
+
"path": "/builds/f85YXDf1/0/Digital/nano-components/packages/components/src/interface.d.ts"
|
621
779
|
}
|
622
780
|
}
|
623
781
|
}
|
@@ -669,7 +827,7 @@ export class FieldValidator {
|
|
669
827
|
},
|
670
828
|
"ValidatorValueStore": {
|
671
829
|
"location": "import",
|
672
|
-
"path": "/builds/
|
830
|
+
"path": "/builds/f85YXDf1/0/Digital/nano-components/packages/components/src/interface.d.ts"
|
673
831
|
}
|
674
832
|
},
|
675
833
|
"return": "Promise<void>"
|
@@ -681,6 +839,50 @@ export class FieldValidator {
|
|
681
839
|
"text": "state - the state to load in the store"
|
682
840
|
}]
|
683
841
|
}
|
842
|
+
},
|
843
|
+
"setCustomValidity": {
|
844
|
+
"complexType": {
|
845
|
+
"signature": "(validity: { [key: string]: string; }) => Promise<void[]>",
|
846
|
+
"parameters": [{
|
847
|
+
"tags": [{
|
848
|
+
"name": "param",
|
849
|
+
"text": "validity - a validity object of `{fieldName: errorString}` pairs. Set as an empty string to clear the error."
|
850
|
+
}],
|
851
|
+
"text": "- a validity object of `{fieldName: errorString}` pairs. Set as an empty string to clear the error."
|
852
|
+
}],
|
853
|
+
"references": {
|
854
|
+
"Promise": {
|
855
|
+
"location": "global"
|
856
|
+
}
|
857
|
+
},
|
858
|
+
"return": "Promise<void[]>"
|
859
|
+
},
|
860
|
+
"docs": {
|
861
|
+
"text": "Sets custom validity for all / some form fields.",
|
862
|
+
"tags": [{
|
863
|
+
"name": "param",
|
864
|
+
"text": "validity - a validity object of `{fieldName: errorString}` pairs. Set as an empty string to clear the error."
|
865
|
+
}]
|
866
|
+
}
|
867
|
+
},
|
868
|
+
"resetValidity": {
|
869
|
+
"complexType": {
|
870
|
+
"signature": "() => Promise<void[]>",
|
871
|
+
"parameters": [],
|
872
|
+
"references": {
|
873
|
+
"Promise": {
|
874
|
+
"location": "global"
|
875
|
+
}
|
876
|
+
},
|
877
|
+
"return": "Promise<void[]>"
|
878
|
+
},
|
879
|
+
"docs": {
|
880
|
+
"text": "Clear all custom validation.",
|
881
|
+
"tags": [{
|
882
|
+
"name": "param",
|
883
|
+
"text": "validity"
|
884
|
+
}]
|
885
|
+
}
|
684
886
|
}
|
685
887
|
}; }
|
686
888
|
static get elementRef() { return "host"; }
|
@@ -690,6 +892,9 @@ export class FieldValidator {
|
|
690
892
|
}, {
|
691
893
|
"propName": "validateOn",
|
692
894
|
"methodName": "validateOnChange"
|
895
|
+
}, {
|
896
|
+
"propName": "extraFieldSelector",
|
897
|
+
"methodName": "attachSlotObserver"
|
693
898
|
}]; }
|
694
899
|
}
|
695
900
|
//# sourceMappingURL=field-validator.js.map
|