@pageboard/html 0.12.13 → 0.12.15
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/fieldsets.js +1 -1
- package/elements/image.js +1 -1
- package/elements/inputs.js +30 -15
- package/elements/layout.js +14 -0
- package/elements/page.js +2 -2
- package/elements/sitemap.js +1 -1
- package/package.json +1 -1
- package/ui/embed.js +0 -1
- package/ui/fieldset-list.js +15 -1
- package/ui/form.css +18 -0
- package/ui/form.js +36 -11
- package/ui/input_radio.css +18 -0
- package/ui/layout.css +11 -2
- package/ui/textarea.css +3 -0
- package/ui/textarea.js +21 -0
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/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 = {
|
|
@@ -346,18 +354,13 @@ exports.input_radio = {
|
|
|
346
354
|
format: "singleline",
|
|
347
355
|
$helper: 'element-property'
|
|
348
356
|
},
|
|
349
|
-
checked: {
|
|
350
|
-
title: "Checked",
|
|
351
|
-
type: "boolean",
|
|
352
|
-
default: false
|
|
353
|
-
},
|
|
354
357
|
value: {
|
|
355
358
|
title: "Value",
|
|
356
359
|
type: "string",
|
|
357
360
|
format: "singleline"
|
|
358
361
|
},
|
|
359
|
-
|
|
360
|
-
title: "
|
|
362
|
+
checked: {
|
|
363
|
+
title: "Checked",
|
|
361
364
|
type: "boolean",
|
|
362
365
|
default: false
|
|
363
366
|
},
|
|
@@ -365,22 +368,34 @@ exports.input_radio = {
|
|
|
365
368
|
title: 'Required',
|
|
366
369
|
type: 'boolean',
|
|
367
370
|
default: false
|
|
371
|
+
},
|
|
372
|
+
disabled: {
|
|
373
|
+
title: "Disabled",
|
|
374
|
+
type: "boolean",
|
|
375
|
+
default: false
|
|
376
|
+
},
|
|
377
|
+
button: {
|
|
378
|
+
title: 'Button',
|
|
379
|
+
description:'hide radio toggle opacity',
|
|
380
|
+
type: "boolean",
|
|
381
|
+
default: false
|
|
368
382
|
}
|
|
369
383
|
},
|
|
370
384
|
contents: {
|
|
371
385
|
id: 'label',
|
|
372
386
|
nodes: 'inline*'
|
|
373
387
|
},
|
|
374
|
-
html: `<div class="field">
|
|
375
|
-
<div class="ui radio checkbox">
|
|
388
|
+
html: `<div class="field [button]">
|
|
389
|
+
<div class="ui radio [button|alt::checkbox]">
|
|
376
390
|
<input type="radio" disabled="[disabled]" required="[required]"
|
|
377
|
-
name="[name]" value="[value
|
|
391
|
+
name="[name]" value="[value]" checked="[checked]"
|
|
378
392
|
id="for-[name][value|pre:-]" />
|
|
379
393
|
<label block-content="label" for="for-[name][value|pre:-]">Label</label>
|
|
380
394
|
</div>
|
|
381
395
|
</div>`,
|
|
382
396
|
stylesheets: [
|
|
383
|
-
'../lib/components/checkbox.css'
|
|
397
|
+
'../lib/components/checkbox.css',
|
|
398
|
+
'../ui/input_radio.css'
|
|
384
399
|
]
|
|
385
400
|
};
|
|
386
401
|
|
|
@@ -470,7 +485,7 @@ exports.input_select_option = {
|
|
|
470
485
|
}
|
|
471
486
|
},
|
|
472
487
|
contents: 'inline*',
|
|
473
|
-
html: `<element-select-option class="item" data-value="[value
|
|
488
|
+
html: `<element-select-option class="item" data-value="[value]"
|
|
474
489
|
></element-select-option>`
|
|
475
490
|
};
|
|
476
491
|
|
package/elements/layout.js
CHANGED
|
@@ -58,6 +58,19 @@ exports.layout = {
|
|
|
58
58
|
title: "Row"
|
|
59
59
|
}]
|
|
60
60
|
},
|
|
61
|
+
wrap: {
|
|
62
|
+
title: 'Wrap',
|
|
63
|
+
anyOf: [{
|
|
64
|
+
type: 'null',
|
|
65
|
+
title: 'No wrap'
|
|
66
|
+
}, {
|
|
67
|
+
const: 'wrap',
|
|
68
|
+
title: 'Wrap'
|
|
69
|
+
}, {
|
|
70
|
+
const: 'wrap-reverse',
|
|
71
|
+
title: 'Reverse'
|
|
72
|
+
}]
|
|
73
|
+
},
|
|
61
74
|
width: {
|
|
62
75
|
title: 'Width',
|
|
63
76
|
anyOf: [{
|
|
@@ -293,6 +306,7 @@ exports.layout = {
|
|
|
293
306
|
[width|switch:full:fullwidth:contained:ui container]
|
|
294
307
|
[horizontal]
|
|
295
308
|
[vertical]
|
|
309
|
+
[wrap]
|
|
296
310
|
[direction]
|
|
297
311
|
[background.invert|alt:inverted]"
|
|
298
312
|
is="element-layout"
|
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/embed.js
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
|
+
// unflatten array-values
|
|
39
|
+
if (this.#prefix == null) return;
|
|
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);
|
|
@@ -198,7 +209,6 @@ class HTMLElementFieldsetList extends Page.Element {
|
|
|
198
209
|
|
|
199
210
|
#listFromValues(values) {
|
|
200
211
|
const list = [];
|
|
201
|
-
// just unflatten the array
|
|
202
212
|
for (const [key, val] of Object.entries(values)) {
|
|
203
213
|
if (!this.#prefixed(key)) continue;
|
|
204
214
|
const parts = this.#parts(key).slice(this.#prefix.length);
|
|
@@ -278,6 +288,10 @@ class HTMLElementFieldsetList extends Page.Element {
|
|
|
278
288
|
live.replaceWith(node);
|
|
279
289
|
}
|
|
280
290
|
}
|
|
291
|
+
this.dispatchEvent(new Event('change', {
|
|
292
|
+
bubbles: true,
|
|
293
|
+
cancelable: true
|
|
294
|
+
}));
|
|
281
295
|
}
|
|
282
296
|
|
|
283
297
|
#parseName(name) {
|
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") {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
.field.button > .radio > input {
|
|
2
|
+
display:none;
|
|
3
|
+
}
|
|
4
|
+
.field.button > .radio > input + label {
|
|
5
|
+
opacity: 0.5;
|
|
6
|
+
padding: 0.5em;
|
|
7
|
+
display: block;
|
|
8
|
+
position: relative;
|
|
9
|
+
border: 1px solid gray;
|
|
10
|
+
border-radius: 4px;
|
|
11
|
+
}
|
|
12
|
+
.field.button > .radio > input + label:hover {
|
|
13
|
+
opacity:1;
|
|
14
|
+
}
|
|
15
|
+
.field.button > .radio > input:checked + label {
|
|
16
|
+
opacity:1;
|
|
17
|
+
font-weight: bold;
|
|
18
|
+
}
|
package/ui/layout.css
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
|
-
.layout
|
|
2
|
-
|
|
1
|
+
.layout {
|
|
2
|
+
display:flex;
|
|
3
3
|
flex: 0 0 auto;
|
|
4
|
+
}
|
|
5
|
+
.ui.container.layout {
|
|
4
6
|
display:flex;
|
|
7
|
+
flex: 1 0 auto;
|
|
8
|
+
}
|
|
9
|
+
.layout.wrap {
|
|
10
|
+
flex-wrap:wrap;
|
|
11
|
+
}
|
|
12
|
+
.layout.wrap-reverse {
|
|
13
|
+
flex-wrap:wrap-reverse;
|
|
5
14
|
}
|
|
6
15
|
.layout.column {
|
|
7
16
|
flex-direction: column;
|
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');
|