@pageboard/html 0.12.14 → 0.12.16
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/embed.js +2 -2
- package/elements/fieldsets.js +1 -1
- package/elements/form.js +1 -1
- package/elements/image.js +1 -1
- package/elements/inputs.js +13 -5
- package/elements/page.js +2 -2
- package/elements/sitemap.js +1 -1
- package/package.json +1 -1
- package/ui/fieldset-list.js +25 -13
- package/ui/form.css +18 -0
- package/ui/form.js +36 -11
- package/ui/input_radio.css +4 -1
- package/ui/textarea.css +3 -0
- package/ui/textarea.js +21 -0
package/elements/embed.js
CHANGED
|
@@ -40,8 +40,8 @@ exports.embed = {
|
|
|
40
40
|
};
|
|
41
41
|
},
|
|
42
42
|
tag: 'iframe,element-embed',
|
|
43
|
-
html: `<element-embed data-src="[url|
|
|
44
|
-
<a aria-hidden="true" class="linkable" href="[$loc
|
|
43
|
+
html: `<element-embed data-src="[url]" data-query="[query|as:query]" id="[id]">
|
|
44
|
+
<a aria-hidden="true" class="linkable" href="[$loc.pathname][$loc.search][id|pre:%23]">[linkable|prune:*]#</a>
|
|
45
45
|
<iframe loading="lazy" allowfullscreen frameborder="0" scrolling="no"></iframe>
|
|
46
46
|
</element-embed>`,
|
|
47
47
|
scripts: [
|
package/elements/fieldsets.js
CHANGED
|
@@ -74,7 +74,7 @@ exports.fieldlist_button = {
|
|
|
74
74
|
title: 'Field List Button',
|
|
75
75
|
menu: "form",
|
|
76
76
|
icon: '<i class="icons"><i class="folder outline icon"></i><i class="corner hand pointer icon"></i></i>',
|
|
77
|
-
group: 'block',
|
|
77
|
+
group: 'block input_field',
|
|
78
78
|
context: 'fieldset_list//',
|
|
79
79
|
properties: {
|
|
80
80
|
type: {
|
package/elements/form.js
CHANGED
|
@@ -47,7 +47,7 @@ exports.query_form = {
|
|
|
47
47
|
contents: 'block+',
|
|
48
48
|
tag: 'form[method="get"]',
|
|
49
49
|
html: `<form is="element-form" method="get" name="[name]"
|
|
50
|
-
action="[redirection
|
|
50
|
+
action="[redirection|urltpl:url:parameters]"
|
|
51
51
|
autocomplete="off" class="ui form"></form>`,
|
|
52
52
|
stylesheets: [
|
|
53
53
|
'../lib/components/form.css',
|
package/elements/image.js
CHANGED
|
@@ -129,7 +129,7 @@ exports.image = {
|
|
|
129
129
|
nodes: "inline*"
|
|
130
130
|
},
|
|
131
131
|
html: `<element-image
|
|
132
|
-
class="[display.fit|or:none] [display.horizontal
|
|
132
|
+
class="[display.fit|or:none] [display.horizontal?] [display.vertical?]"
|
|
133
133
|
alt="[alt]"
|
|
134
134
|
data-src="[url]"
|
|
135
135
|
data-crop="[crop.x|or:50];[crop.y|or:50];[crop.width|or:100];[crop.height|or:100];[crop.zoom|or:100]"
|
package/elements/inputs.js
CHANGED
|
@@ -66,10 +66,15 @@ exports.input_fields = {
|
|
|
66
66
|
title: "Inline",
|
|
67
67
|
type: 'boolean',
|
|
68
68
|
default: false
|
|
69
|
+
},
|
|
70
|
+
full: {
|
|
71
|
+
title: 'Full width',
|
|
72
|
+
type: 'boolean',
|
|
73
|
+
default: false
|
|
69
74
|
}
|
|
70
75
|
},
|
|
71
76
|
contents: "input_field+",
|
|
72
|
-
html: `<div class="[inline] fields"></div>`
|
|
77
|
+
html: `<div class="[inline] [full|alt:fluid:] fields"></div>`
|
|
73
78
|
};
|
|
74
79
|
|
|
75
80
|
exports.input_text = {
|
|
@@ -160,9 +165,10 @@ exports.input_text = {
|
|
|
160
165
|
tel: /^(\(\d+\))? *\d+([ .-]?\d+)*$/.source,
|
|
161
166
|
email: /^[\w.!#$%&'*+/=?^`{|}~-]+@\w(?:[\w-]{0,61}\w)?(?:\.\w(?:[\w-]{0,61}\w)?)*$/.source
|
|
162
167
|
},
|
|
163
|
-
html: `<div class="[width|as:colnums|post: wide] field [type|eq:hidden
|
|
168
|
+
html: `<div class="[width|as:colnums|post: wide] field [type|eq:hidden]">
|
|
164
169
|
<label block-content="label">Label</label>
|
|
165
170
|
[type|eq:textarea|prune:*:1]<textarea
|
|
171
|
+
is="element-textarea"
|
|
166
172
|
name="[name]"
|
|
167
173
|
required="[required]"
|
|
168
174
|
readonly="[readonly]"
|
|
@@ -178,7 +184,9 @@ exports.input_text = {
|
|
|
178
184
|
pattern="[$element.patterns.[type]]"
|
|
179
185
|
value="[value]"
|
|
180
186
|
autocomplete="[type|eq:new-password|fail:]" />
|
|
181
|
-
</div
|
|
187
|
+
</div>`,
|
|
188
|
+
scripts: ['../ui/textarea.js'],
|
|
189
|
+
stylesheets: ['../ui/textarea.css']
|
|
182
190
|
};
|
|
183
191
|
|
|
184
192
|
exports.input_number = {
|
|
@@ -380,7 +388,7 @@ exports.input_radio = {
|
|
|
380
388
|
html: `<div class="field [button]">
|
|
381
389
|
<div class="ui radio [button|alt::checkbox]">
|
|
382
390
|
<input type="radio" disabled="[disabled]" required="[required]"
|
|
383
|
-
name="[name]" value="[value
|
|
391
|
+
name="[name]" value="[value]" checked="[checked]"
|
|
384
392
|
id="for-[name][value|pre:-]" />
|
|
385
393
|
<label block-content="label" for="for-[name][value|pre:-]">Label</label>
|
|
386
394
|
</div>
|
|
@@ -477,7 +485,7 @@ exports.input_select_option = {
|
|
|
477
485
|
}
|
|
478
486
|
},
|
|
479
487
|
contents: 'inline*',
|
|
480
|
-
html: `<element-select-option class="item" data-value="[value
|
|
488
|
+
html: `<element-select-option class="item" data-value="[value]"
|
|
481
489
|
></element-select-option>`
|
|
482
490
|
};
|
|
483
491
|
|
package/elements/page.js
CHANGED
|
@@ -65,7 +65,7 @@ exports.page.properties.transition = {
|
|
|
65
65
|
exports.page.fragments.push({
|
|
66
66
|
path: 'body',
|
|
67
67
|
attributes: {
|
|
68
|
-
"data-transition-close": "[transition.close
|
|
69
|
-
"data-transition-open": "[transition.open
|
|
68
|
+
"data-transition-close": "[transition.close?]",
|
|
69
|
+
"data-transition-open": "[transition.open?]",
|
|
70
70
|
}
|
|
71
71
|
});
|
package/elements/sitemap.js
CHANGED
|
@@ -49,7 +49,7 @@ exports.sitemap = {
|
|
|
49
49
|
<span class="ui mini type label">[$grants.webmaster|prune:*][$type]</span>
|
|
50
50
|
<span class="ui mini black label">[$grants.webmaster|prune:*][nositemap|prune:*]no sitemap</span>
|
|
51
51
|
<span class="ui mini orange label">[$grants.webmaster|prune:*][noindex|prune:*]no index</span>
|
|
52
|
-
<span class="ui mini red label">[$grants.webmaster|prune:*][$lock
|
|
52
|
+
<span class="ui mini red label">[$grants.webmaster|prune:*][$lock|fail:*]</span>
|
|
53
53
|
<br>
|
|
54
54
|
<a href="[url]" class="description">[url|or:-]</a>
|
|
55
55
|
<a href="[redirect|fail:*]" class="redirection"> ➜ [redirect]</a>
|
package/package.json
CHANGED
package/ui/fieldset-list.js
CHANGED
|
@@ -35,6 +35,17 @@ class HTMLElementFieldsetList extends Page.Element {
|
|
|
35
35
|
#walk;
|
|
36
36
|
|
|
37
37
|
fill(values, scope) {
|
|
38
|
+
if (scope.$write || this.prefix == null) return;
|
|
39
|
+
// unflatten array-values
|
|
40
|
+
for (const [key, val] of Object.entries(values)) {
|
|
41
|
+
if (!this.#prefixed(key)) continue;
|
|
42
|
+
if (Array.isArray(val)) {
|
|
43
|
+
for (let i = 0; i < val.length; i++) {
|
|
44
|
+
values[key + '.' + i] = val[i];
|
|
45
|
+
}
|
|
46
|
+
delete values[key];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
38
49
|
const list = this.#listFromValues({ ...values });
|
|
39
50
|
if (this.#initialSize == null) this.#initialSize = list.length;
|
|
40
51
|
this.#resize(list.length, scope);
|
|
@@ -83,13 +94,9 @@ class HTMLElementFieldsetList extends Page.Element {
|
|
|
83
94
|
this.#model = model;
|
|
84
95
|
}
|
|
85
96
|
|
|
86
|
-
#prepare(
|
|
97
|
+
#prepare() {
|
|
87
98
|
const tpl = this.ownTpl;
|
|
88
99
|
tpl.prerender();
|
|
89
|
-
if (scope.$write) {
|
|
90
|
-
this.#modelize(tpl);
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
100
|
this.#modelize(tpl.content);
|
|
94
101
|
for (const node of tpl.content.querySelectorAll('[block-id]')) {
|
|
95
102
|
node.removeAttribute('block-id');
|
|
@@ -97,19 +104,18 @@ class HTMLElementFieldsetList extends Page.Element {
|
|
|
97
104
|
}
|
|
98
105
|
|
|
99
106
|
patch({ scope }) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
this.#prepare(scope);
|
|
107
|
+
if (scope.$write) {
|
|
108
|
+
this.#modelize(this.ownTpl);
|
|
109
|
+
} else if (!this.#size) {
|
|
110
|
+
this.#resize(0, scope);
|
|
111
|
+
}
|
|
106
112
|
}
|
|
107
113
|
|
|
108
114
|
#selector(name) {
|
|
109
115
|
return `[block-type="fieldlist_button"][value="${name}"]`;
|
|
110
116
|
}
|
|
111
117
|
|
|
112
|
-
#prefixed(key, p = this
|
|
118
|
+
#prefixed(key, p = this.prefix) {
|
|
113
119
|
const parts = this.#parts(key);
|
|
114
120
|
for (let i = 0; i < p.length; i++) {
|
|
115
121
|
if (parts[i] != p[i]) return false;
|
|
@@ -198,7 +204,6 @@ class HTMLElementFieldsetList extends Page.Element {
|
|
|
198
204
|
|
|
199
205
|
#listFromValues(values) {
|
|
200
206
|
const list = [];
|
|
201
|
-
// just unflatten the array
|
|
202
207
|
for (const [key, val] of Object.entries(values)) {
|
|
203
208
|
if (!this.#prefixed(key)) continue;
|
|
204
209
|
const parts = this.#parts(key).slice(this.#prefix.length);
|
|
@@ -278,6 +283,10 @@ class HTMLElementFieldsetList extends Page.Element {
|
|
|
278
283
|
live.replaceWith(node);
|
|
279
284
|
}
|
|
280
285
|
}
|
|
286
|
+
this.dispatchEvent(new Event('change', {
|
|
287
|
+
bubbles: true,
|
|
288
|
+
cancelable: true
|
|
289
|
+
}));
|
|
281
290
|
}
|
|
282
291
|
|
|
283
292
|
#parseName(name) {
|
|
@@ -299,6 +308,9 @@ class HTMLElementFieldsetList extends Page.Element {
|
|
|
299
308
|
return this.children.find(node => node.matches('.view'));
|
|
300
309
|
}
|
|
301
310
|
get prefix() {
|
|
311
|
+
if (this.#prefix == null) {
|
|
312
|
+
this.#prepare();
|
|
313
|
+
}
|
|
302
314
|
return this.#prefix;
|
|
303
315
|
}
|
|
304
316
|
}
|
package/ui/form.css
CHANGED
|
@@ -22,3 +22,21 @@ element-select {
|
|
|
22
22
|
[contenteditable] .ui.form .field.hidden {
|
|
23
23
|
display: block !important;
|
|
24
24
|
}
|
|
25
|
+
.ui.form {
|
|
26
|
+
width: 100%;
|
|
27
|
+
}
|
|
28
|
+
.ui.form .inline.fluid.fields {
|
|
29
|
+
width:100%;
|
|
30
|
+
column-gap: 1em;
|
|
31
|
+
padding:0 1em;
|
|
32
|
+
}
|
|
33
|
+
.ui.form .inline.fluid.fields > .field {
|
|
34
|
+
margin:0;
|
|
35
|
+
padding:0;
|
|
36
|
+
flex: 1 1 auto;
|
|
37
|
+
position:relative;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.ui.form[method="post"]:not(.unsaved,:hover) button[type="submit"] {
|
|
41
|
+
opacity:0.2;
|
|
42
|
+
}
|
package/ui/form.js
CHANGED
|
@@ -34,21 +34,33 @@ class HTMLElementForm extends Page.create(HTMLFormElement) {
|
|
|
34
34
|
|
|
35
35
|
state.finish(() => {
|
|
36
36
|
if (action == "enable") {
|
|
37
|
-
form.enable();
|
|
37
|
+
form.enable?.();
|
|
38
38
|
} else if (action == "disable") {
|
|
39
|
-
form.disable();
|
|
39
|
+
form.disable?.();
|
|
40
40
|
} else if (action == "fill") {
|
|
41
41
|
if (val == null) {
|
|
42
|
-
form.reset();
|
|
42
|
+
form.reset?.();
|
|
43
43
|
} else if (typeof val == "object") {
|
|
44
|
-
form.fill(this.linearizeValues(val), state.scope);
|
|
45
|
-
form.save();
|
|
44
|
+
form.fill?.(this.linearizeValues(val), state.scope);
|
|
45
|
+
form.save?.();
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
});
|
|
49
49
|
|
|
50
50
|
return val;
|
|
51
51
|
};
|
|
52
|
+
state.finish(() => {
|
|
53
|
+
let index = 0;
|
|
54
|
+
for (const node of document.querySelectorAll('label[for]')) {
|
|
55
|
+
const prev = node.previousElementSibling;
|
|
56
|
+
if (prev?.nodeName != "INPUT") continue;
|
|
57
|
+
const others = document.querySelectorAll(`input[id="${node.htmlFor}"]`);
|
|
58
|
+
if (others.length > 1) {
|
|
59
|
+
node.htmlFor += `-${index++}`;
|
|
60
|
+
prev.id = node.htmlFor;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
});
|
|
52
64
|
}
|
|
53
65
|
|
|
54
66
|
static setup(state) {
|
|
@@ -79,6 +91,10 @@ class HTMLElementForm extends Page.create(HTMLFormElement) {
|
|
|
79
91
|
}
|
|
80
92
|
}
|
|
81
93
|
|
|
94
|
+
#isPureButtons() {
|
|
95
|
+
return this.elements.every(item => item.type == "submit");
|
|
96
|
+
}
|
|
97
|
+
|
|
82
98
|
toggleMessages(status) {
|
|
83
99
|
return window.HTMLElementTemplate.prototype.toggleMessages.call(this, status, this);
|
|
84
100
|
}
|
|
@@ -98,6 +114,7 @@ class HTMLElementForm extends Page.create(HTMLFormElement) {
|
|
|
98
114
|
this.setAttribute('data-' + key, val);
|
|
99
115
|
}
|
|
100
116
|
this.restore(state.scope);
|
|
117
|
+
if (this.#isPureButtons()) this.classList.add('unsaved');
|
|
101
118
|
} else {
|
|
102
119
|
for (const name of this.fill(state.query, state.scope)) {
|
|
103
120
|
state.vars[name] = true;
|
|
@@ -162,7 +179,7 @@ class HTMLElementForm extends Page.create(HTMLFormElement) {
|
|
|
162
179
|
query[name] = val;
|
|
163
180
|
break;
|
|
164
181
|
case "radio":
|
|
165
|
-
if (!withDefaults && node.checked == node.defaultChecked) {
|
|
182
|
+
if (!withDefaults && (node.checked == node.defaultChecked || val == null)) {
|
|
166
183
|
if (query[name] == val) {
|
|
167
184
|
query[name] = undefined;
|
|
168
185
|
}
|
|
@@ -264,7 +281,12 @@ class HTMLElementForm extends Page.create(HTMLFormElement) {
|
|
|
264
281
|
if (state.scope.$write) return;
|
|
265
282
|
this.toggleMessages();
|
|
266
283
|
if (this.matches('.loading')) return;
|
|
267
|
-
if (e.type != "submit" && this.querySelector('[type="submit"]'))
|
|
284
|
+
if (e.type != "submit" && this.querySelector('[type="submit"]')) {
|
|
285
|
+
if (this.method == "post") {
|
|
286
|
+
this.classList.add('unsaved');
|
|
287
|
+
}
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
268
290
|
let fn = this[this.method + 'Method'];
|
|
269
291
|
if (e.type == "input" && (!e.target || !["radio", "checkbox"].includes(e.target.type))) {
|
|
270
292
|
fn = this[this.method + 'MethodLater'] || fn;
|
|
@@ -307,6 +329,7 @@ class HTMLElementForm extends Page.create(HTMLFormElement) {
|
|
|
307
329
|
async postMethod(e, state) {
|
|
308
330
|
if (e.type != "submit") return;
|
|
309
331
|
const form = this;
|
|
332
|
+
if (!this.#isPureButtons()) form.classList.remove('unsaved');
|
|
310
333
|
form.classList.add('loading');
|
|
311
334
|
|
|
312
335
|
await Promise.all(
|
|
@@ -381,9 +404,6 @@ HTMLFormElement.prototype.disable = function () {
|
|
|
381
404
|
}
|
|
382
405
|
};
|
|
383
406
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
407
|
HTMLSelectElement.prototype.fill = function (val) {
|
|
388
408
|
if (!Array.isArray(val)) val = [val];
|
|
389
409
|
for (let i = 0; i < this.options.length; i++) {
|
|
@@ -394,7 +414,12 @@ HTMLSelectElement.prototype.fill = function (val) {
|
|
|
394
414
|
HTMLSelectElement.prototype.reset = function () {
|
|
395
415
|
for (let i = 0; i < this.options.length; i++) this.options[i].selected = false;
|
|
396
416
|
};
|
|
397
|
-
|
|
417
|
+
HTMLButtonElement.prototype.fill = function(val) {
|
|
418
|
+
if (this.name && this.type == "submit") this.value = val;
|
|
419
|
+
};
|
|
420
|
+
HTMLButtonElement.prototype.fill = function (val) {
|
|
421
|
+
if (this.name && this.type == "submit") this.value = val;
|
|
422
|
+
};
|
|
398
423
|
HTMLInputElement.prototype.fill = function (val) {
|
|
399
424
|
if (val == null) val = "";
|
|
400
425
|
if (this.type == "radio" || this.type == "checkbox") {
|
package/ui/input_radio.css
CHANGED
|
@@ -2,14 +2,17 @@
|
|
|
2
2
|
display:none;
|
|
3
3
|
}
|
|
4
4
|
.field.button > .radio > input + label {
|
|
5
|
-
opacity:0.
|
|
5
|
+
opacity: 0.5;
|
|
6
6
|
padding: 0.5em;
|
|
7
7
|
display: block;
|
|
8
8
|
position: relative;
|
|
9
|
+
border: 1px solid gray;
|
|
10
|
+
border-radius: 4px;
|
|
9
11
|
}
|
|
10
12
|
.field.button > .radio > input + label:hover {
|
|
11
13
|
opacity:1;
|
|
12
14
|
}
|
|
13
15
|
.field.button > .radio > input:checked + label {
|
|
14
16
|
opacity:1;
|
|
17
|
+
font-weight: bold;
|
|
15
18
|
}
|
package/ui/textarea.css
ADDED
package/ui/textarea.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
class HTMLElementTextArea extends Page.create(HTMLTextAreaElement) {
|
|
2
|
+
handleChange(e, state) {
|
|
3
|
+
this.#resize(state);
|
|
4
|
+
}
|
|
5
|
+
handleInput(e, state) {
|
|
6
|
+
this.#resize(state);
|
|
7
|
+
}
|
|
8
|
+
setup(state) {
|
|
9
|
+
this.#resize(state);
|
|
10
|
+
}
|
|
11
|
+
#resize(state) {
|
|
12
|
+
if (state.scope.$write) {
|
|
13
|
+
delete this.style.height;
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
this.style.height = 0;
|
|
17
|
+
this.style.height = `calc(${this.scrollHeight}px + 1em)`;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
Page.define('element-textarea', HTMLElementTextArea, 'textarea');
|