@optionfactory/ful 0.19.0 → 0.20.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/dist/ful.mjs CHANGED
@@ -1,102 +1,3 @@
1
- /* global CSS */
2
-
3
- function extract(extractors, el) {
4
- const maybeExtractor = extractors[el.dataset['bindExtractor']] || extractors[el.dataset['bindProvide']];
5
- if (maybeExtractor) {
6
- return maybeExtractor(el);
7
- }
8
- if (el.getAttribute('type') === 'radio') {
9
- if (!el.checked) {
10
- return undefined;
11
- }
12
- return el.dataset['bindType'] === 'boolean' ? el.value === 'true' : el.value;
13
- }
14
- if (el.getAttribute('type') === 'checkbox') {
15
- return el.checked;
16
- }
17
- if (el.dataset['bindType'] === 'boolean') {
18
- return !el.value ? null : el.value === 'true';
19
- }
20
- return el.value || null;
21
- }
22
-
23
- function mutate(mutators, el, raw, key, values) {
24
- const maybeMutator = mutators[el.dataset['bindMutator']] || mutators[el.dataset['bindProvide']];
25
- if (maybeMutator) {
26
- maybeMutator(el, raw, key, values);
27
- return;
28
- }
29
- if (el.getAttribute('type') === 'radio') {
30
- el.checked = el.getAttribute('value') === raw;
31
- return;
32
- }
33
- if (el.getAttribute('type') === 'checkbox') {
34
- el.checked = raw;
35
- return;
36
- }
37
- el.value = raw;
38
- }
39
-
40
-
41
- function providePath(result, path, value) {
42
- const keys = path.split(".").map((k) => k.match(/^[0-9]+$/) ? +k : k);
43
- let current = result;
44
- let previous = null;
45
- for (let i = 0; ; ++i) {
46
- const ckey = keys[i];
47
- const pkey = keys[i - 1];
48
- if (Number.isInteger(ckey) && !Array.isArray(current)) {
49
- if (previous !== null) {
50
- previous[pkey] = current = [];
51
- } else {
52
- result = current = [];
53
- }
54
- }
55
- if (i === keys.length - 1) {
56
- //when value is undefined we only want to define the property if it's not defined
57
- current[ckey] = value !== undefined ? value : (ckey in current ? current[ckey] : null);
58
- return result;
59
- }
60
- if (current[ckey] === undefined) {
61
- current[ckey] = {};
62
- }
63
- previous = current;
64
- current = current[ckey];
65
- }
66
- }
67
-
68
- class Bindings {
69
-
70
- constructor( {extractors, mutators, ignoredChildrenSelector, valueHoldersSelector}) {
71
- this.extractors = extractors || {};
72
- this.mutators = mutators || {};
73
- this.valueHoldersSelector = valueHoldersSelector || 'input[name], select[name], textarea[name]';
74
- this.ignoredChildrenSelector = ignoredChildrenSelector || '.d-none';
75
- }
76
- setValues(el, values) {
77
- for (let k in values) {
78
- if (!values.hasOwnProperty(k)) {
79
- continue;
80
- }
81
- Array.from(el.querySelectorAll(`[name='${CSS.escape(k)}']`)).forEach((el) => {
82
- mutate(this.mutators, el, values[k], k, values);
83
- });
84
- }
85
- }
86
- getValues(el) {
87
- return Array.from(el.querySelectorAll(this.valueHoldersSelector))
88
- .filter((el) => {
89
- if (el.dataset['bindInclude'] === 'never') {
90
- return false;
91
- }
92
- return el.dataset['bindInclude'] === 'always' || el.closest(this.ignoredChildrenSelector) === null;
93
- })
94
- .reduce((result, el) => {
95
- return providePath(result, el.getAttribute('name'), extract(this.extractors, el));
96
- }, {});
97
- }
98
- }
99
-
100
1
  class Base64 {
101
2
  static encode(arrayBuffer, dialect) {
102
3
  const d = dialect || Base64.URL_SAFE;
@@ -169,69 +70,44 @@ class Hex {
169
70
  }
170
71
  }
171
72
 
172
- /* global Infinity, CSS */
173
-
174
-
175
- class Form {
176
- constructor(el, bindings, {globalErrorsEl, fieldContainerSelector, errorClass, hideClass}) {
177
- this.el = el;
178
- this.bindings = bindings;
179
- this.globalErrorsEl = globalErrorsEl;
180
- this.fieldContainerSelector = fieldContainerSelector !== undefined ? fieldContainerSelector : Form.DEFAULT_FIELD_CONTAINER_SELECTOR;
181
- this.errorClass = errorClass || Form.DEFAULT_ERROR_CLASS;
182
- this.hideClass = hideClass || Form.DEFAULT_HIDE_CLASS;
183
- }
184
- setValues(values) {
185
- return this.bindings.setValues(this.el, values);
186
- }
187
- getValues() {
188
- return this.bindings.getValues(this.el);
73
+ class Observable {
74
+ constructor() {
75
+ this.listeners = {};
189
76
  }
190
- setErrors(errors, scrollFirstErrorIntoView, context) {
191
-
192
- this.clearErrors();
193
- errors
194
- .map(this.mapError ? this.mapError : (e) => e)
195
- .filter((e) => e.type === 'FIELD_ERROR' || e.type === 'INVALID_FORMAT')
196
- .forEach((e) => {
197
- const name = e.context.replace("[", ".").replace("].", ".");
198
- Array.from(this.el.querySelectorAll(`[name='${CSS.escape(name)}']`))
199
- .map(el => this.fieldContainerSelector ? el.closest(this.fieldContainerSelector) : el)
200
- .filter(el => el !== null)
201
- .forEach(label => {
202
- label.classList.add(this.errorClass);
203
- label.dataset['error'] = e.reason;
204
- });
205
- });
206
- if (this.globalErrorsEl) {
207
- const globalErrors = errors.filter((e) => e.type !== 'FIELD_ERROR' && e.type !== 'INVALID_FORMAT');
208
- this.globalErrorsEl.innerHTML = globalErrors.map(e => e.reason).join("\n");
209
- if (globalErrors.length !== 0) {
210
- this.globalErrorsEl.classList.remove(this.hideClass);
211
- }
212
- }
213
- if (!scrollFirstErrorIntoView) {
214
- return;
215
- }
216
- const yOffsets = Array.from(this.el.querySelectorAll('.${CSS.escape(this.errorClass)}'))
217
- .map((label) => label.getBoundingClientRect().y + window.scrollY);
218
- const firstErrorScrollY = Math.min(...yOffsets);
219
- if (firstErrorScrollY !== Infinity) {
220
- window.scroll(window.scrollX, firstErrorScrollY > 100 ? firstErrorScrollY - 100 : 0);
77
+ fireSync(event, data, initialAcc) {
78
+ const listeners = this.listeners[event] || [];
79
+ let acc = initialAcc;
80
+ for (const l of listeners) {
81
+ acc = l(data, this, acc);
221
82
  }
83
+ return acc;
222
84
  }
223
- clearErrors() {
224
- this.el.querySelectorAll(`.${CSS.escape(this.errorClass)}`).forEach(l => l.classList.remove(this.errorClass));
225
- if (this.globalErrorsEl) {
226
- this.globalErrorsEl.innerHTML = '';
227
- this.globalErrorsEl.classList.add(this.hideClass);
85
+ async fire(event, data, initialAcc) {
86
+ const listeners = this.listeners[event] || [];
87
+ let acc = initialAcc;
88
+ for (const l of listeners) {
89
+ acc = await l(data, this, acc);
228
90
  }
91
+ return acc;
92
+ }
93
+ on(event, listener) {
94
+ this.listeners[event] = this.listeners[event] || [];
95
+ this.listeners[event].push(listener);
96
+ }
97
+ un(event, listener) {
98
+ const listeners = this.listeners[event] || [];
99
+ const idx = listeners.indexOf(listener);
100
+ return idx === -1 ? [] : listeners.splice(idx, 1);
101
+ }
102
+ static mixin(self) {
103
+ self.listeners = {};
104
+ self.fireSync = Observable.prototype.fireSync;
105
+ self.fire = Observable.prototype.fire;
106
+ self.on = Observable.prototype.on;
107
+ self.un = Observable.prototype.un;
229
108
  }
230
- }
231
109
 
232
- Form.DEFAULT_FIELD_CONTAINER_SELECTOR = 'label';
233
- Form.DEFAULT_ERROR_CLASS = 'has-error';
234
- Form.DEFAULT_HIDE_CLASS = 'd-none';
110
+ }
235
111
 
236
112
  class ContextInterceptor {
237
113
  constructor() {
@@ -373,30 +249,456 @@ class HttpClient {
373
249
  }]);
374
250
  }
375
251
  }
376
- async form(resource, options, uiOptions) {
377
- const ui = uiOptions || {};
378
- ui.buttons?.forEach(el => {
379
- el.setAttribute("disabled", "disabled");
380
- if (ui.loader) {
381
- el.dataset['oldContent'] = el.innerHTML;
382
- el.innerHTML = ui.loader;
252
+ }
253
+
254
+ /* global Infinity, CSS */
255
+
256
+ class CustomElements {
257
+ static id = 0;
258
+ static uid(prefix) {
259
+ return `${prefix}-${++CustomElements.id}`;
260
+ }
261
+ static forwardAttributes(from, to, except) {
262
+ from.getAttributeNames().filter(a => except.indexOf(a) === -1)
263
+ .filter(a => a[0] === '@')
264
+ .forEach(a => {
265
+ if (a === '@class') {
266
+ to.classList.add(...from.getAttribute("@class").split(" ").filter(a => a.length));
267
+ return;
268
+ }
269
+ to.setAttribute(a.substring(1), from.getAttribute(a));
270
+ });
271
+ }
272
+ static extractSlots(el) {
273
+ const slotted = Object.fromEntries([...el.querySelectorAll("[slot]")].map(el => {
274
+ el.parentElement.removeChild(el);
275
+ const slot = el.getAttribute("slot");
276
+ el.removeAttribute("slot");
277
+ return [slot, el];
278
+ }));
279
+ slotted.default = new DocumentFragment();
280
+ slotted.default.append(...el.childNodes);
281
+ return slotted;
282
+ }
283
+ static labelAndInputGroup(id, name, isFloating, slotted) {
284
+ if (isFloating) {
285
+ /**
286
+ * <div class="input-group has-validation">
287
+ * <span data-tpl-if="slotted.before" class="input-group-text">{{{{ slotted.before }}}}</span>
288
+ * <div class="form-floating">
289
+ * {{{{ slotted.input }}}}
290
+ * <label data-tpl-for="name" class="form-label">{{{{ slotted.default }}}}</label>
291
+ * </div>
292
+ * <span data-tpl-if="slotted.after" class="input-group-text">{{{{ slotted.after }}}}</span>
293
+ * <ful-field-error data-tpl-field="name"></ful-field-error>
294
+ * </div>
295
+ */
296
+ const label = document.createElement("label");
297
+ label.setAttribute("for", id);
298
+ label.classList.add('form-label');
299
+ label.append(slotted.default);
300
+
301
+ const ff = document.createElement('div');
302
+ ff.classList.add("form-floating");
303
+ ff.append(slotted.input, label);
304
+
305
+ const ffe = document.createElement('ful-field-error');
306
+ ffe.setAttribute("field", name);
307
+
308
+ const ig = document.createElement("div");
309
+ ig.classList.add('input-group', 'has-validtion');
310
+
311
+ if (slotted.before) {
312
+ ig.append(slotted.before);
313
+ } else if (slotted.ibefore) {
314
+ const igt = document.createElement('div');
315
+ igt.classList.add('input-group-text');
316
+ igt.append(slotted.ibefore);
317
+ ig.append(igt);
318
+ }
319
+ ig.append(ff);
320
+ if (slotted.after) {
321
+ ig.append(slotted.after);
322
+ } else if (slotted.iafter) {
323
+ const igt = document.createElement('div');
324
+ igt.classList.add('input-group-text');
325
+ igt.append(slotted.iafter);
326
+ ig.append(igt);
327
+ }
328
+ ig.append(ffe);
329
+ return ig;
330
+ }
331
+ /**
332
+ <label data-tpl-for="name" class="form-label">{{{{ slotted.default }}}}</label>
333
+ <div class="input-group has-validation">
334
+ <span data-tpl-if="slotted.before" class="input-group-text">{{{{ slotted.before }}}}</span>
335
+ {{{{ slotted.input }}}}
336
+ <span data-tpl-if="slotted.after" class="input-group-text">{{{{ slotted.after }}}}</span>
337
+ <ful-field-error data-tpl-field="name"></ful-field-error>
338
+ </div>
339
+ */
340
+
341
+ const label = document.createElement("label");
342
+ label.setAttribute("for", name);
343
+ label.classList.add('form-label');
344
+ label.append(slotted.default);
345
+
346
+ const ffe = document.createElement('ful-field-error');
347
+ ffe.setAttribute("field", name);
348
+
349
+ const ig = document.createElement("div");
350
+ ig.classList.add('input-group', 'has-validation');
351
+
352
+ if (slotted.before) {
353
+ ig.append(slotted.before);
354
+ } else if (slotted.ibefore) {
355
+ const igt = document.createElement('div');
356
+ igt.classList.add('input-group-text');
357
+ igt.append(slotted.ibefore);
358
+ ig.append(igt);
359
+ }
360
+ ig.append(slotted.input);
361
+ if (slotted.after) {
362
+ ig.append(slotted.after);
363
+ } else if (slotted.iafter) {
364
+ const igt = document.createElement('div');
365
+ igt.classList.add('input-group-text');
366
+ igt.append(slotted.iafter);
367
+ ig.append(igt);
368
+ }
369
+ ig.append(ffe);
370
+
371
+ const fragment = new DocumentFragment();
372
+ fragment.append(label, ig);
373
+ return fragment;
374
+ }
375
+
376
+ }
377
+
378
+
379
+ class FieldError extends HTMLElement {
380
+ constructor() {
381
+ super();
382
+ }
383
+ connectedCallback() {
384
+ this.classList.add('invalid-feedback');
385
+ }
386
+ static configure() {
387
+ customElements.define('ful-field-error', FieldError);
388
+ }
389
+ }
390
+
391
+ class Errors extends HTMLElement {
392
+ constructor() {
393
+ super();
394
+ }
395
+ connectedCallback() {
396
+ this.classList.add('alert', 'alert-danger', 'd-none');
397
+ }
398
+ static configure() {
399
+ customElements.define('ful-errors', Errors);
400
+ }
401
+
402
+ }
403
+
404
+ class Spinner extends HTMLElement {
405
+ constructor() {
406
+ super();
407
+ }
408
+ connectedCallback() {
409
+ this.classList.add('spinner-border', 'spinner-border-sm', 'd-none');
410
+ this.setAttribute("aria-hidden", "true");
411
+ }
412
+ show() {
413
+ this.classList.remove("d-none");
414
+ }
415
+ hide() {
416
+ this.classList.add("d-none");
417
+ }
418
+ static configure() {
419
+ customElements.define('ful-spinner', Spinner);
420
+ }
421
+ }
422
+
423
+
424
+
425
+ class Input extends HTMLElement {
426
+ constructor() {
427
+ super();
428
+ const id = CustomElements.uid('ful-input');
429
+ const name = this.getAttribute('@name');
430
+ const floating = this.hasAttribute('@floating');
431
+ const slotted = CustomElements.extractSlots(this);
432
+ slotted.input = slotted.input || (() => {
433
+ const el = document.createElement("input");
434
+ el.classList.add("form-control");
435
+ return el;
436
+ })();
437
+ CustomElements.forwardAttributes(this, slotted.input, ['@floating']);
438
+ const attrIfMissing = (el, k, v) => !el.hasAttribute(k) && el.setAttribute(k, v);
439
+ attrIfMissing(slotted.input, "name", id);
440
+ attrIfMissing(slotted.input, "id", id);
441
+ attrIfMissing(slotted.input, "type", "text");
442
+ attrIfMissing(slotted.input, "placeholder", " ");
443
+ this.innerHTML = '';
444
+ this.append(CustomElements.labelAndInputGroup(id, name || id, floating, slotted));
445
+ }
446
+ static configure() {
447
+ customElements.define('ful-input', Input);
448
+ }
449
+ }
450
+
451
+
452
+
453
+ /**
454
+ * <script src="tom-select.complete.js"></script>
455
+ * <link href="tom-select.bootstrap5.css" rel="stylesheet" />
456
+ */
457
+ class Select extends HTMLElement {
458
+ constructor(tsConfig) {
459
+ super();
460
+ Observable.mixin(this);
461
+ const id = CustomElements.uid('ful-select');
462
+ const name = this.getAttribute('@name');
463
+ const floating = this.hasAttribute('@floating');
464
+ const remote = this.hasAttribute('@remote');
465
+ const slotted = CustomElements.extractSlots(this);
466
+ slotted.input = slotted.input || (() => {
467
+ return document.createElement("select");
468
+ })();
469
+ CustomElements.forwardAttributes(this, slotted.input, ['@floating', '@remote']);
470
+ const attrIfMissing = (el, k, v) => !el.hasAttribute(k) && el.setAttribute(k, v);
471
+ attrIfMissing(slotted.input, "name", id);
472
+ attrIfMissing(slotted.input, "id", id);
473
+ attrIfMissing(slotted.input, "placeholder", " ");
474
+ this.innerHTML = '';
475
+ this.append(CustomElements.labelAndInputGroup(id, name || id, floating, slotted));
476
+ this.loaded = !remote;
477
+ this.ts = new TomSelect(slotted.input, Object.assign(remote ? {
478
+ preload: 'focus',
479
+ load: async (query, callback) => {
480
+ if (this.loaded) {
481
+ callback();
482
+ return;
483
+ }
484
+ const data = await this.fire('load', query, []);
485
+ this.loaded = true;
486
+ callback(data);
487
+ }
488
+ } : {}, tsConfig));
489
+ slotted.input.setValue = this.setValue.bind(this);
490
+ slotted.input.getValue = this.getValue.bind(this);
491
+ }
492
+ async setValue(v){
493
+ if(!this.loaded){
494
+ await this.ts.load();
495
+ }
496
+ this.ts.setValue(v);
497
+ }
498
+ getValue(){
499
+ const v = this.ts.getValue();
500
+ return v === '' ? null : v;
501
+ }
502
+ static custom(tagName, configuration) {
503
+ customElements.define(tagName, class extends Select {
504
+ constructor() {
505
+ super(configuration);
383
506
  }
384
507
  });
385
- try {
386
- const r = await this.json(resource, options);
387
- ui.form?.clearErrors();
388
- return r;
389
- } catch (e) {
390
- ui.form?.setErrors(e.problems);
391
- throw e;
392
- } finally {
393
- ui.buttons?.forEach(el => {
394
- el.removeAttribute("disabled");
395
- el.innerHTML = el.dataset['oldContent'];
396
- delete el.dataset['oldContent'];
508
+ }
509
+ static configure() {
510
+ return Select.custom('ful-select');
511
+ }
512
+
513
+ }
514
+
515
+ class Form extends HTMLElement {
516
+ constructor({ mutators, extractors, valueHoldersSelector, ignoredChildrenSelector }) {
517
+ super();
518
+ Observable.mixin(this);
519
+ this.mutators = mutators || {};
520
+ this.extractors = extractors || {};
521
+ this.valueHoldersSelector = valueHoldersSelector || '[name]';
522
+ this.ignoredChildrenSelector = ignoredChildrenSelector || '.d-none';
523
+
524
+ const form = document.createElement('form');
525
+ form.append(...this.childNodes);
526
+ this.appendChild(form);
527
+
528
+ form.addEventListener('submit', async (e) => {
529
+ e.preventDefault();
530
+ this.spinner(true);
531
+ try {
532
+ await this.fire('submit', this.getValues(), this);
533
+ } catch (e) {
534
+ if (e instanceof Failure) {
535
+ this.setErrors(e.problems);
536
+ return;
537
+ }
538
+ throw e;
539
+ } finally {
540
+ this.spinner(false);
541
+ }
542
+ });
543
+ }
544
+ spinner(spin) {
545
+ this.querySelectorAll('ful-spinner').forEach(el => {
546
+ el[spin ? 'show' : 'hide']();
547
+ });
548
+ this.querySelectorAll('[type=submit],[type=reset]').forEach(el => {
549
+ el.disabled = spin;
550
+ });
551
+ }
552
+ setValues(values) {
553
+ for (let k in values) {
554
+ if (!values.hasOwnProperty(k)) {
555
+ continue;
556
+ }
557
+ Array.from(this.querySelectorAll(`[name='${CSS.escape(k)}']`)).forEach((el) => {
558
+ Form.mutate(this.mutators, el, values[k], k, values);
397
559
  });
398
560
  }
399
561
  }
562
+ getValues() {
563
+ return Array.from(this.querySelectorAll(this.valueHoldersSelector))
564
+ .filter((el) => {
565
+ if (el.dataset['fulBindInclude'] === 'never') {
566
+ return false;
567
+ }
568
+ return el.dataset['fulBindInclude'] === 'always' || el.closest(this.ignoredChildrenSelector) === null;
569
+ })
570
+ .reduce((result, el) => {
571
+ return Form.providePath(result, el.getAttribute('name'), Form.extract(this.extractors, el));
572
+ }, {});
573
+ }
574
+ setErrors(errors, scroll) {
575
+ this.clearErrors();
576
+ errors
577
+ .filter((e) => e.type === 'FIELD_ERROR' || e.type === 'INVALID_FORMAT')
578
+ .forEach((e) => {
579
+ const name = e.context.replace("[", ".").replace("].", ".");
580
+ this.querySelectorAll(`[name='${CSS.escape(name)}']`)
581
+ .forEach(input => {
582
+ input.classList.add('is-invalid');
583
+ if (input.parentElement.classList.contains("form-floating")) {
584
+ input.parentElement.classList.add('is-invalid');
585
+ }
586
+ });
587
+ this.querySelectorAll(`ful-field-error[field='${CSS.escape(name)}']`)
588
+ .forEach(el => el.innerText = e.reason);
589
+ });
590
+ this.querySelectorAll("ful-errors")
591
+ .forEach(el => {
592
+ const globalErrors = errors.filter((e) => e.type !== 'FIELD_ERROR' && e.type !== 'INVALID_FORMAT');
593
+ el.innerHTML = globalErrors.map(e => e.reason).join("\n");
594
+ if (globalErrors.length !== 0) {
595
+ el.classList.remove('d-none');
596
+ }
597
+ });
598
+
599
+ if (!scroll) {
600
+ return;
601
+ }
602
+ const ys = Array.from(this.querySelectorAll('ful-field-error:not(.d-none)'))
603
+ .map(el => el.getBoundingClientRect().y + window.scrollY);
604
+ const miny = Math.min(...ys);
605
+ if (miny !== Infinity) {
606
+ window.scroll(window.scrollX, miny > 100 ? miny - 100 : 0);
607
+ }
608
+ }
609
+ clearErrors() {
610
+ this.querySelectorAll('[name].is-invalid, .form-floating.is-invalid')
611
+ .forEach(el => el.classList.remove('is-invalid'));
612
+ this.querySelectorAll("ful-errors")
613
+ .forEach(el => {
614
+ el.innerHTML = '';
615
+ el.classList.add('d-none');
616
+ });
617
+ }
618
+ static extract(extractors, el) {
619
+ const maybeExtractor = extractors[el.dataset['fulBindExtractor']] || extractors[el.dataset['fulBindProvide']];
620
+ if (maybeExtractor) {
621
+ return maybeExtractor(el);
622
+ }
623
+ if (el.getAttribute('type') === 'radio') {
624
+ if (!el.checked) {
625
+ return undefined;
626
+ }
627
+ return el.dataset['fulBindType'] === 'boolean' ? el.value === 'true' : el.value;
628
+ }
629
+ if (el.getAttribute('type') === 'checkbox') {
630
+ return el.checked;
631
+ }
632
+ if (el.dataset['fulBindType'] === 'boolean') {
633
+ return !el.value ? null : el.value === 'true';
634
+ }
635
+ if (el.getValue) {
636
+ return el.getValue();
637
+ }
638
+ return el.value || null;
639
+ }
640
+ static mutate(mutators, el, raw, key, values) {
641
+ const maybeMutator = mutators[el.dataset['fulBindMutator']] || mutators[el.dataset['fulBindProvide']];
642
+ if (maybeMutator) {
643
+ maybeMutator(el, raw, key, values);
644
+ return;
645
+ }
646
+ if (el.getAttribute('type') === 'radio') {
647
+ el.checked = el.getAttribute('value') === raw;
648
+ return;
649
+ }
650
+ if (el.getAttribute('type') === 'checkbox') {
651
+ el.checked = raw;
652
+ return;
653
+ }
654
+ if (el.setValue) {
655
+ el.setValue(raw);
656
+ return;
657
+ }
658
+ el.value = raw;
659
+ }
660
+
661
+ static providePath(result, path, value) {
662
+ const keys = path.split(".").map((k) => k.match(/^[0-9]+$/) ? +k : k);
663
+ let current = result;
664
+ let previous = null;
665
+ for (let i = 0; ; ++i) {
666
+ const ckey = keys[i];
667
+ const pkey = keys[i - 1];
668
+ if (Number.isInteger(ckey) && !Array.isArray(current)) {
669
+ if (previous !== null) {
670
+ previous[pkey] = current = [];
671
+ } else {
672
+ result = current = [];
673
+ }
674
+ }
675
+ if (i === keys.length - 1) {
676
+ //when value is undefined we only want to define the property if it's not defined
677
+ current[ckey] = value !== undefined ? value : (ckey in current ? current[ckey] : null);
678
+ return result;
679
+ }
680
+ if (current[ckey] === undefined) {
681
+ current[ckey] = {};
682
+ }
683
+ previous = current;
684
+ current = current[ckey];
685
+ }
686
+ }
687
+ static custom(tagName, configuration) {
688
+ customElements.define(tagName, class extends Form {
689
+ constructor() {
690
+ super(configuration);
691
+ }
692
+ });
693
+ }
694
+ static configure(configuration) {
695
+ FieldError.configure();
696
+ Errors.configure();
697
+ Spinner.configure();
698
+ Input.configure();
699
+ Select.configure();
700
+ Form.custom('ful-form', configuration || {});
701
+ }
400
702
  }
401
703
 
402
704
  class Storage {
@@ -581,7 +883,7 @@ class AuthorizationCodeFlowSession {
581
883
  ])
582
884
  });
583
885
  if (!response.ok) {
584
- throw new Error("Error:" + response.code + ": " + response.text());
886
+ throw new Error("Error:" + response.status + ": " + response.text());
585
887
  }
586
888
  const token = await response.json();
587
889
  this.token = token;
@@ -719,10 +1021,10 @@ const timing = {
719
1021
  }
720
1022
  };
721
1023
 
722
- class Wizard {
723
- constructor(el) {
724
- this.el = el;
725
- this.progress = [...el.children].filter(e => e.matches("header,ol,ul"));
1024
+ class Wizard extends HTMLElement {
1025
+ constructor() {
1026
+ super();
1027
+ this.progress = [...this.children].filter(e => e.matches("header,ol,ul"));
726
1028
 
727
1029
  this.progress.forEach(p => {
728
1030
  const children = [...p.children];
@@ -731,8 +1033,8 @@ class Wizard {
731
1033
  children[0].classList.add('active');
732
1034
  }
733
1035
  });
734
- if (this.el.querySelector('section.current') === null) {
735
- const firstSection = this.el.querySelector('section:first-of-type');
1036
+ if (this.querySelector('section.current') === null) {
1037
+ const firstSection = this.querySelector('section:first-of-type');
736
1038
  if (firstSection !== null) {
737
1039
  firstSection.classList.add('current');
738
1040
  }
@@ -745,11 +1047,11 @@ class Wizard {
745
1047
  current?.classList.remove('active');
746
1048
  current?.nextElementSibling?.classList.add('active');
747
1049
  });
748
- const currentSection = this.el.querySelector('section.current');
1050
+ const currentSection = this.querySelector('section.current');
749
1051
  currentSection.classList.remove("current");
750
1052
  currentSection.nextElementSibling.classList.add('current');
751
1053
 
752
- this.el.dispatchEvent(new CustomEvent('wizard:activate', {
1054
+ this.dispatchEvent(new CustomEvent('wizard:activate', {
753
1055
  bubbles: true,
754
1056
  cancelable: true
755
1057
  }));
@@ -762,10 +1064,10 @@ class Wizard {
762
1064
  current?.classList.remove('active');
763
1065
  current?.previousElementSibling?.classList.add('active');
764
1066
  });
765
- const currentSection = this.el.querySelector('section.current');
1067
+ const currentSection = this.querySelector('section.current');
766
1068
  currentSection.classList.remove("current");
767
1069
  currentSection.previousElementSibling.classList.add('current');
768
- this.el.dispatchEvent(new CustomEvent('wizard:activate', {
1070
+ this.dispatchEvent(new CustomEvent('wizard:activate', {
769
1071
  bubbles: true,
770
1072
  cancelable: true
771
1073
  }));
@@ -777,15 +1079,25 @@ class Wizard {
777
1079
  current?.classList.remove('active');
778
1080
  p.children[+n]?.classList.add('active');
779
1081
  });
780
- const currentSection = this.el.querySelector('section.current');
1082
+ const currentSection = this.querySelector('section.current');
781
1083
  currentSection?.classList.remove("current");
782
- const nthSection = this.el.querySelector(`section:nth-child(${+n})`);
1084
+ const nthSection = this.querySelector(`section:nth-child(${+n})`);
783
1085
  nthSection.classList.add('current');
784
- this.el.dispatchEvent(new CustomEvent('wizard:activate', {
1086
+ this.dispatchEvent(new CustomEvent('wizard:activate', {
785
1087
  bubbles: true,
786
1088
  cancelable: true
787
1089
  }));
788
1090
  }
1091
+ static custom(tagName, configuration) {
1092
+ customElements.define(tagName, class extends Wizard {
1093
+ constructor() {
1094
+ super(configuration);
1095
+ }
1096
+ });
1097
+ }
1098
+ static configure() {
1099
+ return Wizard.custom('ful-wizard');
1100
+ }
789
1101
  }
790
1102
 
791
1103
  class App {
@@ -814,5 +1126,5 @@ class App {
814
1126
  }
815
1127
  }
816
1128
 
817
- export { App, AuthorizationCodeFlow, AuthorizationCodeFlowInterceptor, AuthorizationCodeFlowSession, Base64, Bindings, Failure, Form, Hex, HttpClient, LocalStorage, SessionStorage, VersionedStorage, Wizard, timing };
1129
+ export { App, AuthorizationCodeFlow, AuthorizationCodeFlowInterceptor, AuthorizationCodeFlowSession, Base64, CustomElements, Errors, Failure, FieldError, Form, Hex, HttpClient, Input, LocalStorage, Observable, Select, SessionStorage, Spinner, VersionedStorage, Wizard, timing };
818
1130
  //# sourceMappingURL=ful.mjs.map