@pageboard/html 0.10.14 → 0.11.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/elements/card.js +8 -9
- package/elements/fieldsets.js +46 -11
- package/elements/form.js +1 -1
- package/elements/grid.js +4 -3
- package/elements/image.js +4 -2
- package/elements/input-date.js +2 -2
- package/elements/input-property.js +10 -1
- package/elements/inputs.js +6 -5
- package/elements/layout.js +6 -5
- package/elements/media.js +4 -2
- package/elements/menu.js +2 -5
- package/elements/page.js +5 -6
- package/elements/paragraph.js +7 -8
- package/elements/rating.js +1 -1
- package/elements/sitemap.js +5 -4
- package/lib/nouislider.js +31 -31
- package/package.json +2 -2
- package/ui/fieldset-list.js +160 -61
- package/ui/fieldset.js +2 -2
- package/ui/form.js +81 -74
- package/ui/input-file.js +1 -2
- package/ui/input-range.js +8 -6
- package/ui/menu.js +9 -7
- package/ui/pagination.js +3 -2
- package/ui/query-tags.js +4 -3
- package/ui/tab.js +1 -1
package/ui/form.js
CHANGED
|
@@ -33,27 +33,26 @@ class HTMLCustomFormElement extends HTMLFormElement {
|
|
|
33
33
|
delete state.query.submit;
|
|
34
34
|
state.finish(() => {
|
|
35
35
|
if (state.status != 200) return;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
this.dispatchEvent(new Event('submit', {
|
|
37
|
+
bubbles: true,
|
|
38
|
+
cancelable: true
|
|
39
|
+
}));
|
|
39
40
|
});
|
|
40
41
|
}
|
|
41
|
-
read(withDefaults) {
|
|
42
|
+
read(withDefaults = false) {
|
|
42
43
|
const fd = new FormData(this);
|
|
43
44
|
const query = {};
|
|
44
45
|
fd.forEach((val, key) => {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
val = undefined;
|
|
49
|
-
} else {
|
|
50
|
-
val = null;
|
|
51
|
-
}
|
|
46
|
+
const cur = this.querySelectorAll(`[name="${key}"]`).slice(-1).pop();
|
|
47
|
+
if (cur.type == "file") {
|
|
48
|
+
val = cur.value;
|
|
52
49
|
}
|
|
50
|
+
if (val == "") val = null;
|
|
51
|
+
// build array-like values
|
|
53
52
|
const old = query[key];
|
|
54
53
|
if (old !== undefined) {
|
|
55
54
|
if (!Array.isArray(old)) {
|
|
56
|
-
query[key] = [old];
|
|
55
|
+
query[key] = old == null ? [] : [old];
|
|
57
56
|
}
|
|
58
57
|
if (val !== undefined) query[key].push(val);
|
|
59
58
|
} else {
|
|
@@ -61,55 +60,63 @@ class HTMLCustomFormElement extends HTMLFormElement {
|
|
|
61
60
|
}
|
|
62
61
|
});
|
|
63
62
|
|
|
63
|
+
// withDefaults: keep value if equals to its default
|
|
64
|
+
// else unset value
|
|
64
65
|
for (const node of this.elements) {
|
|
65
|
-
|
|
66
|
+
const { name, type } = node;
|
|
67
|
+
if (name == null || name == "" || type == "button") {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
66
70
|
let val = node.value;
|
|
67
71
|
if (val == "") val = null;
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
72
|
+
let defVal = node.defaultValue;
|
|
73
|
+
if (defVal == "") defVal = null;
|
|
74
|
+
|
|
75
|
+
switch (type) {
|
|
76
|
+
case "radio":
|
|
77
|
+
if (!withDefaults && node.checked == node.defaultChecked) {
|
|
78
|
+
if (query[name] == val) {
|
|
79
|
+
query[name] = undefined;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
break;
|
|
83
|
+
case "checkbox":
|
|
84
|
+
if (!withDefaults) {
|
|
85
|
+
if (!(name in query)) {
|
|
86
|
+
query[name] = undefined;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
break;
|
|
90
|
+
case "hidden":
|
|
91
|
+
break;
|
|
92
|
+
default:
|
|
93
|
+
if (withDefaults) {
|
|
94
|
+
if (query[name] === undefined) {
|
|
95
|
+
query[name] = defVal;
|
|
96
|
+
}
|
|
97
|
+
} else if (val === defVal) {
|
|
98
|
+
query[name] = node.required ? null : undefined;
|
|
99
|
+
}
|
|
90
100
|
}
|
|
91
101
|
}
|
|
102
|
+
// FIXME use e.submitter polyfill when available
|
|
103
|
+
// https://github.com/Financial-Times/polyfill-library/issues/1111
|
|
92
104
|
const btn = document.activeElement;
|
|
93
|
-
// FIXME https://github.com/whatwg/html/issues/3195 use e.submitter polyfill
|
|
94
|
-
// https://github.com/whatwg/xhr/issues/262 it's a mess
|
|
95
105
|
if (btn && btn.type == "submit" && btn.name && btn.value) {
|
|
96
106
|
query[btn.name] = btn.value;
|
|
97
107
|
}
|
|
98
108
|
return query;
|
|
99
109
|
}
|
|
100
110
|
fill(query, scope) {
|
|
101
|
-
//
|
|
102
|
-
const
|
|
103
|
-
const FieldSet = VirtualHTMLElement.define(tagList);
|
|
104
|
-
for (const node of this.querySelectorAll(tagList)) {
|
|
105
|
-
if (!node.fill) Object.setPrototypeOf(node, FieldSet.prototype);
|
|
111
|
+
// fieldset-list are not custom inputs yet
|
|
112
|
+
for (const node of this.querySelectorAll("element-fieldset-list")) {
|
|
106
113
|
node.fill(query, scope);
|
|
107
114
|
}
|
|
108
115
|
const vars = [];
|
|
109
116
|
for (const elem of this.elements) {
|
|
110
117
|
const name = elem.name;
|
|
111
118
|
if (!name) continue;
|
|
112
|
-
if (
|
|
119
|
+
if (name in query && !vars.includes(name)) vars.push(name);
|
|
113
120
|
const val = query[name];
|
|
114
121
|
const str = ((v) => {
|
|
115
122
|
if (v == null) return "";
|
|
@@ -218,7 +225,7 @@ class HTMLCustomFormElement extends HTMLFormElement {
|
|
|
218
225
|
const loc = Page.parse(redirect);
|
|
219
226
|
Object.assign(loc.query, this.read(false));
|
|
220
227
|
if (loc.samePathname(state)) {
|
|
221
|
-
loc.query =
|
|
228
|
+
loc.query = { ...state.query, ...loc.query };
|
|
222
229
|
}
|
|
223
230
|
let status = 200;
|
|
224
231
|
const p = this.ignoreInputChange
|
|
@@ -345,7 +352,8 @@ HTMLInputElement.prototype.fill = function (val) {
|
|
|
345
352
|
if (this.type == "radio" || this.type == "checkbox") {
|
|
346
353
|
this.checked = val;
|
|
347
354
|
} else if (this.type == "file") {
|
|
348
|
-
this.
|
|
355
|
+
if (val == '' || val == null) this.removeAttribute('value');
|
|
356
|
+
else this.setAttribute('value', val);
|
|
349
357
|
} else {
|
|
350
358
|
this.value = val;
|
|
351
359
|
}
|
|
@@ -363,7 +371,7 @@ HTMLInputElement.prototype.save = function () {
|
|
|
363
371
|
if (this.type == "radio" || this.type == "checkbox") {
|
|
364
372
|
this.defaultChecked = this.checked;
|
|
365
373
|
} else if (this.type == "file") {
|
|
366
|
-
this.defaultValue = this.getAttribute('value');
|
|
374
|
+
this.defaultValue = this.getAttribute('value') || '';
|
|
367
375
|
} else {
|
|
368
376
|
this.defaultValue = this.value;
|
|
369
377
|
}
|
|
@@ -381,11 +389,13 @@ Object.defineProperty(HTMLInputElement.prototype, 'defaultValue', {
|
|
|
381
389
|
configurable: true,
|
|
382
390
|
enumerable: true,
|
|
383
391
|
get: function () {
|
|
392
|
+
// FIXME might not be needed anymore
|
|
384
393
|
if (this.form?.method == "get") return '';
|
|
385
394
|
else return this.getAttribute('value');
|
|
386
395
|
},
|
|
387
396
|
set: function (val) {
|
|
388
|
-
this.
|
|
397
|
+
if (val == '' || val == null) this.removeAttribute('value');
|
|
398
|
+
else this.setAttribute('value', val);
|
|
389
399
|
}
|
|
390
400
|
});
|
|
391
401
|
|
|
@@ -417,7 +427,7 @@ Page.setup((state) => {
|
|
|
417
427
|
}
|
|
418
428
|
});
|
|
419
429
|
|
|
420
|
-
Page.
|
|
430
|
+
Page.patch(state => {
|
|
421
431
|
const filters = state.scope.$filters;
|
|
422
432
|
|
|
423
433
|
function linearizeValues(query, obj = {}, prefix) {
|
|
@@ -448,35 +458,32 @@ Page.ready((state) => {
|
|
|
448
458
|
if (action == "toggle") {
|
|
449
459
|
action = val ? "enable" : "disable";
|
|
450
460
|
}
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
if (
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
461
|
+
|
|
462
|
+
state.finish(() => {
|
|
463
|
+
if (action == "enable") {
|
|
464
|
+
form.enable();
|
|
465
|
+
} else if (action == "disable") {
|
|
466
|
+
form.disable();
|
|
467
|
+
} else if (action == "fill") {
|
|
468
|
+
if (val == null) {
|
|
469
|
+
form.reset();
|
|
470
|
+
} else if (typeof val == "object") {
|
|
471
|
+
let values = val;
|
|
472
|
+
if (val.id && val.data) {
|
|
473
|
+
// old way
|
|
474
|
+
values = { ...val.data };
|
|
475
|
+
for (const key of Object.keys(val)) {
|
|
476
|
+
if (key != "data") values['$' + key] = val[key];
|
|
477
|
+
}
|
|
478
|
+
} else {
|
|
479
|
+
// new way
|
|
466
480
|
}
|
|
467
|
-
|
|
468
|
-
|
|
481
|
+
form.fill(linearizeValues(values), state.scope);
|
|
482
|
+
form.save();
|
|
469
483
|
}
|
|
470
|
-
HTMLCustomFormElement.prototype.fill.call(form, linearizeValues(values), state.scope);
|
|
471
|
-
HTMLCustomFormElement.prototype.save.call(form);
|
|
472
|
-
}
|
|
473
|
-
} else if (action == "read") {
|
|
474
|
-
const obj = {};
|
|
475
|
-
for (const [key, kval] of Object.entries(val)) {
|
|
476
|
-
if (form.querySelector(`[name="${key}"]`)) obj[key] = kval;
|
|
477
484
|
}
|
|
478
|
-
|
|
479
|
-
|
|
485
|
+
});
|
|
486
|
+
|
|
480
487
|
return val;
|
|
481
488
|
};
|
|
482
489
|
});
|
package/ui/input-file.js
CHANGED
|
@@ -14,7 +14,6 @@ class HTMLElementInputFile extends HTMLInputElement {
|
|
|
14
14
|
super();
|
|
15
15
|
if (this.init) this.init();
|
|
16
16
|
this.save();
|
|
17
|
-
|
|
18
17
|
}
|
|
19
18
|
get defaultValue() {
|
|
20
19
|
return this.#defaultValue;
|
|
@@ -26,7 +25,7 @@ class HTMLElementInputFile extends HTMLInputElement {
|
|
|
26
25
|
return this.getAttribute('value');
|
|
27
26
|
}
|
|
28
27
|
set value(str) {
|
|
29
|
-
if (str
|
|
28
|
+
if (str) {
|
|
30
29
|
this.setAttribute('value', str);
|
|
31
30
|
} else {
|
|
32
31
|
this.removeAttribute('value');
|
package/ui/input-range.js
CHANGED
|
@@ -81,9 +81,10 @@ class HTMLElementInputRange extends HTMLInputElement {
|
|
|
81
81
|
helper.classList.remove('indeterminate');
|
|
82
82
|
if (isInt) values = values.map((n) => parseInt(n));
|
|
83
83
|
this.rangeValue = values;
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
this.dispatchEvent(new Event('change', {
|
|
85
|
+
bubbles: true,
|
|
86
|
+
cancelable: true
|
|
87
|
+
}));
|
|
87
88
|
});
|
|
88
89
|
helper.addEventListener('keydown', this, true);
|
|
89
90
|
helper.addEventListener('dblclick', this, true);
|
|
@@ -92,9 +93,10 @@ class HTMLElementInputRange extends HTMLInputElement {
|
|
|
92
93
|
handleEvent(e) {
|
|
93
94
|
if (e.type == "dblclick" || e.keyCode == 8 || e.keyCode == 46) {
|
|
94
95
|
this.fill();
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
this.dispatchEvent(new Event('change', {
|
|
97
|
+
bubbles: true,
|
|
98
|
+
cancelable: true
|
|
99
|
+
}));
|
|
98
100
|
}
|
|
99
101
|
}
|
|
100
102
|
|
package/ui/menu.js
CHANGED
|
@@ -27,15 +27,17 @@ class HTMLElementMenu extends VirtualHTMLElement {
|
|
|
27
27
|
const menu = this.firstElementChild;
|
|
28
28
|
const helper = this.lastElementChild;
|
|
29
29
|
helper.lastElementChild.lastElementChild.appendChild(this.toHelper(menu));
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
state.finish(() => {
|
|
31
|
+
this.observer = new ResizeObserver((entries, observer) => {
|
|
32
|
+
window.requestAnimationFrame(() => {
|
|
33
|
+
const styles = window.getComputedStyle(this);
|
|
34
|
+
const parentWidth = parseFloat(styles.marginLeft) + parseFloat(styles.marginRight) + this.offsetWidth;
|
|
35
|
+
const menuWidth = menu.offsetWidth;
|
|
36
|
+
this.classList.toggle('burger', parentWidth <= menuWidth);
|
|
37
|
+
});
|
|
36
38
|
});
|
|
39
|
+
this.observer.observe(this.parentNode);
|
|
37
40
|
});
|
|
38
|
-
this.observer.observe(this.parentNode);
|
|
39
41
|
}
|
|
40
42
|
close(state) {
|
|
41
43
|
if (this.observer) this.observer.disconnect();
|
package/ui/pagination.js
CHANGED
|
@@ -27,9 +27,10 @@ class HTMLElementPagination extends HTMLAnchorElement {
|
|
|
27
27
|
} else {
|
|
28
28
|
this.setAttribute('href', Page.format({
|
|
29
29
|
pathname: state.pathname,
|
|
30
|
-
query:
|
|
30
|
+
query: {
|
|
31
|
+
...state.query,
|
|
31
32
|
[name]: cur || undefined
|
|
32
|
-
}
|
|
33
|
+
}
|
|
33
34
|
}));
|
|
34
35
|
}
|
|
35
36
|
state.finish(() => {
|
package/ui/query-tags.js
CHANGED
|
@@ -90,9 +90,10 @@ class HTMLElementQueryTags extends VirtualHTMLElement {
|
|
|
90
90
|
else if (control.selected) control.selected = false;
|
|
91
91
|
else if (control.reset) control.reset();
|
|
92
92
|
else if (control.value) control.value = "";
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
control.form.dispatchEvent(new Event('submit', {
|
|
94
|
+
bubbles: true,
|
|
95
|
+
cancelable: true
|
|
96
|
+
}));
|
|
96
97
|
}
|
|
97
98
|
label.remove();
|
|
98
99
|
}
|
package/ui/tab.js
CHANGED
|
@@ -14,7 +14,7 @@ class HTMLElementTabs extends VirtualHTMLElement {
|
|
|
14
14
|
const id = this.id;
|
|
15
15
|
|
|
16
16
|
this.items.children.forEach((item, i) => {
|
|
17
|
-
const query =
|
|
17
|
+
const query = { ...state.query };
|
|
18
18
|
const key = `${id}.index`;
|
|
19
19
|
if (i == 0) delete query[key];
|
|
20
20
|
else query[key] = i;
|