@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.
Files changed (129) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/cjs/index.cjs.js +2 -0
  3. package/dist/cjs/index.cjs.js.map +1 -1
  4. package/dist/cjs/loader.cjs.js +1 -1
  5. package/dist/cjs/nano-components.cjs.js +1 -1
  6. package/dist/cjs/nano-field-validator.cjs.entry.js +250 -118
  7. package/dist/cjs/nano-field-validator.cjs.entry.js.map +1 -1
  8. package/dist/cjs/nano-file-upload.cjs.entry.js +1 -1
  9. package/dist/cjs/nano-file-upload.cjs.entry.js.map +1 -1
  10. package/dist/cjs/nano-input.cjs.entry.js +16 -3
  11. package/dist/cjs/nano-input.cjs.entry.js.map +1 -1
  12. package/dist/cjs/nano-nav-item_2.cjs.entry.js +1 -0
  13. package/dist/cjs/nano-nav-item_2.cjs.entry.js.map +1 -1
  14. package/dist/collection/components/accordion/accordion.js +1 -1
  15. package/dist/collection/components/alert/alert.js +1 -1
  16. package/dist/collection/components/algolia/algolia-filter.js +2 -2
  17. package/dist/collection/components/algolia/algolia-input.js +5 -5
  18. package/dist/collection/components/algolia/algolia-results.js +1 -1
  19. package/dist/collection/components/algolia/algolia.js +6 -6
  20. package/dist/collection/components/checkbox/checkbox-group.js +2 -2
  21. package/dist/collection/components/checkbox/checkbox.js +3 -3
  22. package/dist/collection/components/datalist/datalist.js +1 -1
  23. package/dist/collection/components/date-input/date-input.js +8 -8
  24. package/dist/collection/components/date-picker/date-picker.js +5 -5
  25. package/dist/collection/components/details/details.js +1 -1
  26. package/dist/collection/components/dialog/dialog.js +1 -1
  27. package/dist/collection/components/dropdown/dropdown.js +1 -1
  28. package/dist/collection/components/field-validator/field-validator-interface.js.map +1 -1
  29. package/dist/collection/components/field-validator/field-validator.js +329 -124
  30. package/dist/collection/components/field-validator/field-validator.js.map +1 -1
  31. package/dist/collection/components/file-upload/file-upload.css +0 -1
  32. package/dist/collection/components/file-upload/file-upload.js +4 -4
  33. package/dist/collection/components/global-nav/global-nav.js +4 -4
  34. package/dist/collection/components/grid/grid-item.js +1 -1
  35. package/dist/collection/components/icon/icon.js +1 -1
  36. package/dist/collection/components/input/input.js +37 -8
  37. package/dist/collection/components/input/input.js.map +1 -1
  38. package/dist/collection/components/nav-item/nav-item.js +4 -4
  39. package/dist/collection/components/range/range.js +4 -4
  40. package/dist/collection/components/resize-observe/resize-observe.js +1 -1
  41. package/dist/collection/components/select/select.js +8 -7
  42. package/dist/collection/components/select/select.js.map +1 -1
  43. package/dist/collection/components/slides/slides.js +7 -7
  44. package/dist/collection/components/tabs/tab-group.js +2 -2
  45. package/dist/collection/index.js +1 -0
  46. package/dist/collection/index.js.map +1 -1
  47. package/dist/components/index.js +1 -0
  48. package/dist/components/index.js.map +1 -1
  49. package/dist/components/input.js +17 -3
  50. package/dist/components/input.js.map +1 -1
  51. package/dist/components/nano-field-validator.js +255 -120
  52. package/dist/components/nano-field-validator.js.map +1 -1
  53. package/dist/components/nano-file-upload.js +1 -1
  54. package/dist/components/nano-file-upload.js.map +1 -1
  55. package/dist/components/select.js +1 -0
  56. package/dist/components/select.js.map +1 -1
  57. package/dist/custom-elements/index.js +270 -124
  58. package/dist/custom-elements/index.js.map +1 -1
  59. package/dist/esm/index.js +1 -0
  60. package/dist/esm/index.js.map +1 -1
  61. package/dist/esm/loader.js +1 -1
  62. package/dist/esm/nano-components.js +1 -1
  63. package/dist/esm/nano-field-validator.entry.js +250 -118
  64. package/dist/esm/nano-field-validator.entry.js.map +1 -1
  65. package/dist/esm/nano-file-upload.entry.js +1 -1
  66. package/dist/esm/nano-file-upload.entry.js.map +1 -1
  67. package/dist/esm/nano-input.entry.js +16 -3
  68. package/dist/esm/nano-input.entry.js.map +1 -1
  69. package/dist/esm/nano-nav-item_2.entry.js +1 -0
  70. package/dist/esm/nano-nav-item_2.entry.js.map +1 -1
  71. package/dist/esm-es5/index.js +2 -2
  72. package/dist/esm-es5/index.js.map +1 -1
  73. package/dist/esm-es5/loader.js +1 -1
  74. package/dist/esm-es5/loader.js.map +1 -1
  75. package/dist/esm-es5/nano-components.js +1 -1
  76. package/dist/esm-es5/nano-components.js.map +1 -1
  77. package/dist/esm-es5/nano-field-validator.entry.js +2 -2
  78. package/dist/esm-es5/nano-field-validator.entry.js.map +1 -1
  79. package/dist/esm-es5/nano-file-upload.entry.js +1 -1
  80. package/dist/esm-es5/nano-file-upload.entry.js.map +1 -1
  81. package/dist/esm-es5/nano-input.entry.js +1 -1
  82. package/dist/esm-es5/nano-input.entry.js.map +1 -1
  83. package/dist/esm-es5/nano-nav-item_2.entry.js +1 -1
  84. package/dist/esm-es5/nano-nav-item_2.entry.js.map +1 -1
  85. package/dist/nano-components/index.esm.js +1 -1
  86. package/dist/nano-components/index.esm.js.map +1 -1
  87. package/dist/nano-components/nano-components.esm.js +1 -1
  88. package/dist/nano-components/nano-components.esm.js.map +1 -1
  89. package/dist/nano-components/p-0d699368.system.js +5 -0
  90. package/dist/nano-components/{p-3258c568.system.js.map → p-0d699368.system.js.map} +1 -1
  91. package/dist/nano-components/p-18863670.system.entry.js +5 -0
  92. package/dist/nano-components/p-18863670.system.entry.js.map +1 -0
  93. package/dist/nano-components/p-53957ec6.system.js +1 -1
  94. package/dist/nano-components/p-53957ec6.system.js.map +1 -1
  95. package/dist/nano-components/{p-01667573.entry.js → p-634a58f7.entry.js} +2 -2
  96. package/dist/nano-components/p-634a58f7.entry.js.map +1 -0
  97. package/dist/nano-components/p-7f051c20.entry.js +5 -0
  98. package/dist/nano-components/p-7f051c20.entry.js.map +1 -0
  99. package/dist/nano-components/p-a07cf44c.system.entry.js +5 -0
  100. package/dist/nano-components/{p-4558a9c6.system.entry.js.map → p-a07cf44c.system.entry.js.map} +1 -1
  101. package/dist/nano-components/{p-96d9b8b9.system.entry.js → p-c2bbf0fb.system.entry.js} +2 -2
  102. package/dist/nano-components/p-c2bbf0fb.system.entry.js.map +1 -0
  103. package/dist/nano-components/{p-72893d12.system.entry.js → p-cb512cff.system.entry.js} +2 -2
  104. package/dist/nano-components/p-cb512cff.system.entry.js.map +1 -0
  105. package/dist/nano-components/p-e9fddc1a.entry.js +5 -0
  106. package/dist/nano-components/{p-91614b43.entry.js.map → p-e9fddc1a.entry.js.map} +1 -1
  107. package/dist/nano-components/{p-055f7d35.entry.js → p-ed0bdea9.entry.js} +2 -2
  108. package/dist/nano-components/p-ed0bdea9.entry.js.map +1 -0
  109. package/dist/themes/nanopore.css +1 -1
  110. package/dist/themes/nanopore.css.map +1 -1
  111. package/dist/types/components/field-validator/field-validator-interface.d.ts +5 -1
  112. package/dist/types/components/field-validator/field-validator.d.ts +61 -12
  113. package/dist/types/components/input/input.d.ts +6 -1
  114. package/dist/types/components.d.ts +25 -3
  115. package/dist/types/index.d.ts +1 -0
  116. package/docs-json.json +65 -3
  117. package/docs-vscode.json +6 -2
  118. package/package.json +2 -2
  119. package/dist/nano-components/p-01667573.entry.js.map +0 -1
  120. package/dist/nano-components/p-055f7d35.entry.js.map +0 -1
  121. package/dist/nano-components/p-2b478ca1.system.entry.js +0 -5
  122. package/dist/nano-components/p-2b478ca1.system.entry.js.map +0 -1
  123. package/dist/nano-components/p-3258c568.system.js +0 -5
  124. package/dist/nano-components/p-4558a9c6.system.entry.js +0 -5
  125. package/dist/nano-components/p-5f4fc2b4.entry.js +0 -5
  126. package/dist/nano-components/p-5f4fc2b4.entry.js.map +0 -1
  127. package/dist/nano-components/p-72893d12.system.entry.js.map +0 -1
  128. package/dist/nano-components/p-91614b43.entry.js +0 -5
  129. package/dist/nano-components/p-96d9b8b9.system.entry.js.map +0 -1
@@ -195,8 +195,15 @@ let FieldValidator = class extends HTMLElement {
195
195
  this.nanoSubmit = createEvent(this, "nanoSubmit", 7);
196
196
  this.nanoInvalid = createEvent(this, "nanoInvalid", 7);
197
197
  this.submitted = false;
198
- this.fields = [];
199
- // annoyingly, whenever we attempt to checkValidty it fires `invalid` events.
198
+ this.allFields = [];
199
+ this.nanoFieldSelector = `
200
+ nano-input,
201
+ nano-select,
202
+ nano-file-upload,
203
+ nano-date-input,
204
+ nano-checkbox
205
+ `;
206
+ // annoyingly, whenever we attempt to `checkValidty()` it fires `invalid` events.
200
207
  // this is used to prevent infinite loops / multiple calls
201
208
  this.internalValidate = false;
202
209
  // Public API
@@ -205,12 +212,27 @@ let FieldValidator = class extends HTMLElement {
205
212
  /** Tries to scroll to the first invalid field on submit */
206
213
  this.scrollToInvalid = true;
207
214
  this._dirty = false;
215
+ /** By default, `nano-field-validator` will also track all native form field elements.
216
+ * You can add extra web-component form fields to listen to
217
+ * (as long as they match the standard form field spec) by using the `fieldSelector` prop.
218
+ */
219
+ this.extraFieldSelector = 'input, select, textarea';
208
220
  // Event handlers
209
- /** Fired whenever store values change and potentially checks validity */
221
+ /**
222
+ * Fired whenever store values change and potentially checks validity
223
+ * @param key - the key of the store that's just changed
224
+ * @param newVal - the incoming, new value
225
+ */
210
226
  this.handleStoreChange = async (key, newVal) => {
211
- const found = this.fields.find((field) => field.name === key);
212
- if (found && found.value !== newVal)
227
+ const found = this.allFields.find((field) => field.name === key);
228
+ // field update has come programmatically (not from ui),
229
+ // so let's update the underlying ui field
230
+ if ((found &&
231
+ found.tagName === 'NANO-FILE-UPLOAD' &&
232
+ !this.fileStateEqual(key, found)) ||
233
+ (found.tagName !== 'NANO-FILE-UPLOAD' && found.value !== newVal)) {
213
234
  this.storeToFields([found]);
235
+ }
214
236
  if (this.validateOn === 'dirty' && this.dirty) {
215
237
  this.internalValidate = true;
216
238
  await this.validateAllFields();
@@ -219,17 +241,40 @@ let FieldValidator = class extends HTMLElement {
219
241
  }
220
242
  this.nanoPayloadChange.emit(this._store.state);
221
243
  };
222
- /** Handles field value changes and passes to store */
244
+ /**
245
+ * Handles nano field value changes and passes to store
246
+ * @param ev - the incoming change event
247
+ */
223
248
  this.handleFieldChange = (ev) => {
249
+ if (!this.nanoFields.includes(ev.target))
250
+ return;
224
251
  this._dirty = true;
225
252
  this.fieldsToStore([ev.target]);
226
253
  };
227
- /** Handles default field validation events */
254
+ /**
255
+ * Handles non-nano field value changes and passes to store
256
+ * @param ev - the incoming change event
257
+ */
258
+ this.handlePlainFieldChange = (ev) => {
259
+ if (!this.plainFields.includes(ev.target))
260
+ return;
261
+ this.fieldsToStore([ev.target]);
262
+ };
263
+ /**
264
+ * Handles default field validation events
265
+ * @param ev - the invalid event
266
+ */
228
267
  this.handleFormInvalid = async (ev) => {
229
- ev.preventDefault();
268
+ // if it's a non-nano field, we'll let default html5 validation do it's thing
269
+ if (!this.plainFields.includes(ev.target)) {
270
+ ev.preventDefault();
271
+ }
230
272
  this._valid = false;
273
+ // whenever `checkValidity` is called, this handler is in-turn called.
274
+ // this flag is used to stop infinite loops
231
275
  if (this.internalValidate)
232
276
  return;
277
+ // a submit must have happened to if 'submitThenDirty' turn on 'dirty' checking now
233
278
  if (this.validateOn === 'submitThenDirty')
234
279
  this.validateOn = 'dirty';
235
280
  this.submitted = true;
@@ -249,7 +294,10 @@ let FieldValidator = class extends HTMLElement {
249
294
  this.scrollToFirstInvalid();
250
295
  this.nanoInvalid.emit();
251
296
  };
252
- /** stops default form submission, checks if valid, then submits manually */
297
+ /**
298
+ * stops default form submission, checks if valid, then submits manually
299
+ * @param e - a submit event from the nested form element
300
+ */
253
301
  this.handleSubmit = async (e) => {
254
302
  e.preventDefault();
255
303
  if (this.validateOn === 'submitThenDirty')
@@ -274,6 +322,7 @@ let FieldValidator = class extends HTMLElement {
274
322
  return this._activeForm;
275
323
  }
276
324
  set activeForm(form) {
325
+ // manages event listners on whatever form is used (slotted on created here)
277
326
  if (!form)
278
327
  return;
279
328
  if (this._activeForm) {
@@ -284,7 +333,7 @@ let FieldValidator = class extends HTMLElement {
284
333
  }
285
334
  /** Sync up validateOn with all fields */
286
335
  validateOnChange() {
287
- this.fields.forEach((field) => {
336
+ this.nanoFields.forEach((field) => {
288
337
  if (field.tagName === 'NANO-CHECKBOX') {
289
338
  const cbg = field.closest('nano-checkbox-group');
290
339
  if (cbg)
@@ -312,7 +361,7 @@ let FieldValidator = class extends HTMLElement {
312
361
  get payload() {
313
362
  return this._store.state;
314
363
  }
315
- /** Returns true if validation errors will be displayed to the user */
364
+ /** Returns true if validation errors will be displayed to the user. @readonly */
316
365
  get showValidation() {
317
366
  return (this.validateOn === 'dirty' && this.dirty) || this.submitted;
318
367
  }
@@ -329,26 +378,58 @@ let FieldValidator = class extends HTMLElement {
329
378
  ```
330
379
  */
331
380
  get validationState() {
381
+ // TODO - migrate nano-fields away from using proprietary methods in a bid to be closer to the spec
382
+ // this is big and ugly.
383
+ // why? Cos' it must unify checking validity state for both
384
+ // `nano-...` and plain form fields.
332
385
  const validationState = [];
333
- this.fields.forEach(async (field) => {
386
+ this.allFields.forEach(async (field) => {
334
387
  const found = validationState.find((v) => v.name === field.name);
388
+ let pf;
389
+ let nf;
335
390
  if (found) {
336
- found.validityMessage = field.validityMessage.length
337
- ? field.validityMessage
338
- : found.validityMessage;
391
+ if (field.validationMessage) {
392
+ pf = field;
393
+ found.validityMessage = pf.validationMessage.length
394
+ ? pf.validationMessage
395
+ : found.validityMessage;
396
+ this.internalValidate = true;
397
+ if (found.valid && !pf.checkValidity())
398
+ found.valid = false;
399
+ this.internalValidate = false;
400
+ }
401
+ else if (field.validityMessage) {
402
+ nf = field;
403
+ found.validityMessage = nf.validityMessage.length
404
+ ? nf.validityMessage
405
+ : nf.validityMessage;
406
+ if (found.valid && nf.invalid)
407
+ found.valid = false;
408
+ }
339
409
  if (!found.fields.find((f) => f === field))
340
410
  found.fields.push(field);
341
- if (found.valid && field.invalid)
342
- found.valid = false;
343
- return;
411
+ }
412
+ let valid;
413
+ let validityMessage;
414
+ if (field.checkValidity) {
415
+ pf = field;
416
+ this.internalValidate = true;
417
+ valid = pf.checkValidity();
418
+ this.internalValidate = false;
419
+ validityMessage = pf.validationMessage;
420
+ }
421
+ else {
422
+ nf = field;
423
+ valid = !nf.invalid;
424
+ validityMessage = nf.validityMessage;
344
425
  }
345
426
  validationState.push({
346
427
  fields: [field],
347
428
  name: field.name,
348
- valid: !field.invalid,
349
429
  value: this._store.state[field.name],
350
430
  dirty: false,
351
- validityMessage: field.validityMessage,
431
+ valid,
432
+ validityMessage,
352
433
  });
353
434
  });
354
435
  return validationState;
@@ -360,6 +441,24 @@ let FieldValidator = class extends HTMLElement {
360
441
  async setStore(state) {
361
442
  Object.entries(state).forEach(([key, val]) => (this.store.state[key] = val));
362
443
  }
444
+ /**
445
+ * Sets custom validity for all / some form fields.
446
+ * @param validity - a validity object of `{fieldName: errorString}` pairs. Set as an empty string to clear the error.
447
+ */
448
+ async setCustomValidity(validity) {
449
+ return await Promise.all(Object.entries(validity).map(async ([key, err]) => {
450
+ const field = this.allFields.find((f) => f.name === key);
451
+ if (!!field)
452
+ await this.setFieldError(field, err);
453
+ }));
454
+ }
455
+ /**
456
+ * Clear all custom validation.
457
+ * @param validity
458
+ */
459
+ async resetValidity() {
460
+ return await Promise.all(this.allFields.map(async (field) => await this.setFieldError(field, '')));
461
+ }
363
462
  // private methods
364
463
  attachSlotObserver() {
365
464
  if (!!this.mo)
@@ -379,62 +478,66 @@ let FieldValidator = class extends HTMLElement {
379
478
  }
380
479
  /** Checks for new `nano-...` fields and adds them to our watch array and value store */
381
480
  setupFields() {
382
- let fields = Array.from(this.host.querySelectorAll(`
383
- nano-input,
384
- nano-select,
385
- nano-file-upload,
386
- nano-date-input,
387
- nano-checkbox
388
- `));
389
- fields = fields.filter((f) => !!f.name && !!f.name.length);
481
+ let nanoFields = Array.from(this.host.querySelectorAll(this.nanoFieldSelector));
482
+ let plainFields = Array.from(this.host.querySelectorAll(this.extraFieldSelector)).filter((e) => !e.closest(this.nanoFieldSelector));
483
+ nanoFields = nanoFields.filter((f) => !!f.name && !!f.name.length);
484
+ plainFields = plainFields.filter((f) => !!f.name && !!f.name.length);
390
485
  // do we have any currently un-watched fields?
391
- if (!fields.filter((f) => !this.fields.includes(f)).length)
486
+ if (![...nanoFields, ...plainFields].filter((f) => !this.allFields.includes(f)).length)
392
487
  return;
393
488
  // setup the initial store state / refresh on new fields
394
- this.fields = fields;
395
- this.storeToFields(this.fields);
489
+ this.nanoFields = nanoFields;
490
+ this.plainFields = plainFields;
491
+ this.allFields = [...nanoFields, ...plainFields];
492
+ this.storeToFields(this.allFields);
396
493
  this.validateOnChange();
397
- this.fieldsToStore(this.fields);
494
+ this.fieldsToStore(this.allFields);
398
495
  this.nanoPayloadChange.emit(this._store.state);
399
496
  }
400
497
  storeToFields(fields) {
401
498
  fields.forEach((field) => {
499
+ var _a;
402
500
  const fieldName = field.name;
403
501
  if (!fieldName.length ||
404
502
  typeof this._store.state[fieldName] === 'undefined')
405
503
  return;
406
- switch (field.tagName) {
407
- case 'NANO-CHECKBOX':
408
- let cb = field;
409
- if (cb.type === 'radio' ||
410
- cb.type === 'segment' ||
411
- cb.type === 'segment-pill') {
412
- if (this._store.state[fieldName] === cb.value)
413
- cb.checked = true;
414
- else
415
- cb.checked = false;
416
- }
417
- else if (Array.isArray(this._store.state[fieldName])) {
418
- if (this._store.state[fieldName].includes(cb.value))
419
- cb.checked = true;
420
- else
421
- cb.checked = false;
422
- }
423
- else {
424
- if (this._store.state[fieldName] === cb.value)
425
- cb.checked = true;
426
- else
427
- cb.checked = false;
428
- }
429
- break;
430
- case 'NANO-FILE-UPLOAD':
431
- field.files =
432
- this._store.state[fieldName];
433
- break;
434
- default:
435
- field.value = this._store.state[fieldName];
436
- break;
504
+ if (field.tagName === 'NANO-CHECKBOX' ||
505
+ ['radio', 'checkbox'].includes(field.type)) {
506
+ let cb = field;
507
+ if (cb.type === 'radio' ||
508
+ cb.type === 'segment' ||
509
+ cb.type === 'segment-pill') {
510
+ // single radio type control
511
+ if (this._store.state[fieldName] === cb.value)
512
+ cb.checked = true;
513
+ else
514
+ cb.checked = false;
515
+ }
516
+ else if (Array.isArray(this._store.state[fieldName])) {
517
+ // multiple checkbox like controls
518
+ if (this._store.state[fieldName].includes(cb.value))
519
+ cb.checked = true;
520
+ else
521
+ cb.checked = false;
522
+ }
523
+ else {
524
+ // single checkbox like control
525
+ if (this._store.state[fieldName] === cb.value)
526
+ cb.checked = true;
527
+ else
528
+ cb.checked = false;
529
+ }
530
+ return;
437
531
  }
532
+ if (field.tagName === 'NANO-FILE-UPLOAD') {
533
+ const ff = field;
534
+ // this can only work if the field is empty rn... a one-time deal
535
+ if (!((_a = ff.files) === null || _a === void 0 ? void 0 : _a.length))
536
+ ff.files = this._store.state[fieldName];
537
+ return;
538
+ }
539
+ // default
540
+ field.value = this._store.state[fieldName];
438
541
  });
439
542
  }
440
543
  /** Loops through all `nano-...` fields and extracts their values into our store */
@@ -443,52 +546,70 @@ let FieldValidator = class extends HTMLElement {
443
546
  const fieldName = field.name;
444
547
  if (!fieldName.length)
445
548
  return;
446
- switch (field.tagName) {
447
- case 'NANO-CHECKBOX':
448
- let cb = field;
449
- if (cb.type === 'radio' ||
450
- cb.type === 'segment' ||
451
- cb.type === 'segment-pill') {
452
- // radio type control - only one can be checked
453
- if (cb.checked)
454
- this._store.state[fieldName] = cb.value;
455
- else if (!cb.checked &&
456
- (cb.value === this._store.state[fieldName] ||
457
- !this._store.state[fieldName]))
458
- this._store.state[fieldName] = '';
459
- }
460
- else if (this.fields.filter((f) => f.name === fieldName && f.tagName === 'NANO-CHECKBOX').length > 1) {
461
- // multiple checkbox type control
462
- const currentArr = Array.isArray(this._store.state[fieldName])
463
- ? this._store.state[fieldName]
464
- : [];
465
- if (cb.checked) {
466
- if (!this._store.state[fieldName].includes(cb.value)) {
467
- this._store.state[fieldName] = [...currentArr, cb.value];
468
- }
469
- }
470
- else {
471
- this._store.state[fieldName] = currentArr.filter((v) => v !== cb.value);
549
+ if (field.tagName === 'NANO-CHECKBOX' ||
550
+ ['radio', 'checkbox'].includes(field.type)) {
551
+ let cb = field;
552
+ if (cb.type === 'radio' ||
553
+ cb.type === 'segment' ||
554
+ cb.type === 'segment-pill') {
555
+ // radio type control - only one can be checked
556
+ if (cb.checked)
557
+ this._store.state[fieldName] = cb.value;
558
+ }
559
+ else if (this.allFields.filter((f) => f.name === fieldName &&
560
+ (f.tagName === 'NANO-CHECKBOX' ||
561
+ f.type === 'checkbox')).length > 1) {
562
+ // multiple checkbox type control
563
+ const currentArr = Array.isArray(this._store.state[fieldName])
564
+ ? this._store.state[fieldName]
565
+ : [];
566
+ if (cb.checked) {
567
+ // checked
568
+ if (!this._store.state[fieldName].includes(cb.value)) {
569
+ this._store.state[fieldName] = [...currentArr, cb.value];
472
570
  }
473
571
  }
474
572
  else {
475
- // single checkbox - on or off
476
- if (cb.checked)
477
- this._store.state[fieldName] = cb.value;
478
- else
479
- this._store.state[fieldName] = '';
573
+ // unchecked
574
+ this._store.state[fieldName] = currentArr.filter((v) => v !== cb.value);
480
575
  }
481
- break;
482
- case 'NANO-FILE-UPLOAD':
483
- this._store.state[fieldName] = field.files;
484
- break;
485
- default:
486
- this._store.state[fieldName] = field.value;
487
- break;
576
+ }
577
+ else {
578
+ // single checkbox - on or off
579
+ if (cb.checked)
580
+ this._store.state[fieldName] = cb.value;
581
+ else
582
+ this._store.state[fieldName] = '';
583
+ }
584
+ return;
488
585
  }
586
+ if (field.tagName === 'NANO-FILE-UPLOAD') {
587
+ const ff = field;
588
+ if (!this.fileStateEqual(fieldName, ff))
589
+ this._store.state[fieldName] = ff.files;
590
+ return;
591
+ }
592
+ // default
593
+ this._store.state[fieldName] = field.value;
489
594
  });
490
595
  }
491
- /** Checks for user defined validations */
596
+ /**
597
+ * Tries to ascertain whether the current model
598
+ * value is the same as the `nano-file-upload` value
599
+ * @param fieldName - the key to access from the data store
600
+ * @param field - the nano-file-upload field to assess against
601
+ * @returns true for equal, false for not equal
602
+ */
603
+ fileStateEqual(fieldName, field) {
604
+ return (JSON.stringify(this._store.state[fieldName]) ===
605
+ JSON.stringify(field.files) ||
606
+ this._store.state[fieldName] == field.files);
607
+ }
608
+ /**
609
+ * Checks for user defined validations
610
+ * @param key - current key of the data model to validate
611
+ * @param newVal - the newly set, incoming value to validate
612
+ */
492
613
  async validate(key, newVal) {
493
614
  if (!this.validation)
494
615
  return;
@@ -501,21 +622,33 @@ let FieldValidator = class extends HTMLElement {
501
622
  // collection loop into a promise
502
623
  await Promise.all(Object.entries(res).map(async ([key, o]) => {
503
624
  // switch on/off validation messages
504
- const field = this.fields.find((f) => f.name === key);
625
+ const field = this.allFields.find((f) => f.name === key);
505
626
  let validityTarget = field;
506
627
  if (field.tagName === 'NANO-CHECKBOX') {
628
+ // if we have a checkbox-group, set the validation message there
507
629
  const cbg = field.closest('nano-checkbox-group');
508
630
  validityTarget = cbg || field;
509
631
  }
510
- // status is now valid - clear the error
511
- if (validityTarget.validityMessage === o.msg && o.valid)
632
+ if ((validityTarget.validityMessage ||
633
+ validityTarget.validationMessage) === o.msg &&
634
+ o.valid) {
635
+ // status is now valid - clear the error
512
636
  await this.setFieldError(validityTarget, '');
513
- // status is invalid. Set the error
637
+ }
514
638
  else if (!o.valid) {
639
+ // status is invalid. Set the error
515
640
  await this.setFieldError(validityTarget, o.msg);
516
641
  }
517
642
  }));
518
643
  }
644
+ /** Loops through all store entries and checks custom validation */
645
+ async validateAllFields() {
646
+ // This forces our loop to `await` and finish sequentially ... silly async stencil methods
647
+ await Object.entries(this._store.state).reduce(async (memo, [key, value]) => {
648
+ await memo;
649
+ await this.validate(key, value);
650
+ }, undefined);
651
+ }
519
652
  /**
520
653
  * Utility to smooth out setting error messages
521
654
  * (it's a different method on `nano-checkbox` 'cos they don't show errors themselves)
@@ -525,16 +658,10 @@ let FieldValidator = class extends HTMLElement {
525
658
  async setFieldError(field, msg) {
526
659
  if (field['showError'])
527
660
  await field.showError(msg);
528
- else
661
+ else if (field['setError'])
529
662
  await field.setError(msg);
530
- }
531
- /** Loops through all store entries and checks field validity */
532
- async validateAllFields() {
533
- // This forces our loop to `await` and finish sequentially ... silly async stencil methods
534
- await Object.entries(this._store.state).reduce(async (memo, [key, value]) => {
535
- await memo;
536
- await this.validate(key, value);
537
- }, undefined);
663
+ else
664
+ field.setCustomValidity(msg);
538
665
  }
539
666
  scrollToFirstInvalid() {
540
667
  if (!this.scrollToInvalid)
@@ -563,17 +690,21 @@ let FieldValidator = class extends HTMLElement {
563
690
  requestAnimationFrame(() => {
564
691
  this.setupFields();
565
692
  this.attachSlotObserver();
566
- this._store.on('set', (key, value) => this.handleStoreChange(key, value));
567
693
  this.host.addEventListener('nanoChange', this.handleFieldChange);
694
+ this.host.addEventListener('input', this.handlePlainFieldChange);
695
+ this.host.addEventListener('change', this.handlePlainFieldChange);
568
696
  this.host.addEventListener('submit', this.handleSubmit);
697
+ this._store.on('set', this.handleStoreChange);
569
698
  });
570
699
  }
571
700
  disconnectedCallback() {
572
701
  if (this.mo)
573
702
  this.mo.disconnect();
574
- this._store.reset();
575
703
  this.host.removeEventListener('nanoChange', this.handleFieldChange);
704
+ this.host.removeEventListener('input', this.handlePlainFieldChange);
705
+ this.host.removeEventListener('change', this.handlePlainFieldChange);
576
706
  this.host.removeEventListener('submit', this.handleSubmit);
707
+ this._store.reset();
577
708
  if (this.activeForm)
578
709
  this.activeForm.removeEventListener('invalid', this.handleFormInvalid, true);
579
710
  }
@@ -583,7 +714,8 @@ let FieldValidator = class extends HTMLElement {
583
714
  get host() { return this; }
584
715
  static get watchers() { return {
585
716
  "userForm": ["userFormChange"],
586
- "validateOn": ["validateOnChange"]
717
+ "validateOn": ["validateOnChange"],
718
+ "extraFieldSelector": ["attachSlotObserver"]
587
719
  }; }
588
720
  };
589
721
  FieldValidator = /*@__PURE__*/ proxyCustomElement(FieldValidator, [4, "nano-field-validator", {
@@ -595,13 +727,16 @@ FieldValidator = /*@__PURE__*/ proxyCustomElement(FieldValidator, [4, "nano-fiel
595
727
  "payload": [2064],
596
728
  "showValidation": [2052, "show-validation"],
597
729
  "validationState": [2064],
730
+ "extraFieldSelector": [1, "extra-field-selector"],
598
731
  "validation": [16],
599
- "userForm": [32],
600
732
  "submitted": [32],
733
+ "userForm": [32],
601
734
  "_dirty": [32],
602
735
  "_valid": [32],
603
736
  "_store": [32],
604
- "setStore": [64]
737
+ "setStore": [64],
738
+ "setCustomValidity": [64],
739
+ "resetValidity": [64]
605
740
  }]);
606
741
  function defineCustomElement$1() {
607
742
  if (typeof customElements === "undefined") {