@pageboard/html 0.12.4 → 0.12.6
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 +29 -11
- package/elements/heading.js +4 -4
- package/elements/time.js +3 -2
- package/package.json +1 -1
- package/ui/embed-helper.js +10 -0
- package/ui/embed.css +16 -0
- package/ui/embed.js +21 -29
- package/ui/fieldset-list.js +43 -29
- package/ui/fieldset.js +2 -8
- package/ui/heading.css +0 -9
- package/ui/linkable.css +5 -0
- package/ui/media.js +0 -4
- package/ui/select.js +1 -1
- package/ui/tab.css +1 -0
- package/ui/tab.js +2 -2
- package/ui/time.js +1 -1
package/elements/embed.js
CHANGED
|
@@ -8,31 +8,49 @@ exports.embed = {
|
|
|
8
8
|
description: 'The iframe src URL',
|
|
9
9
|
nullable: true,
|
|
10
10
|
type: 'string',
|
|
11
|
-
format: 'uri-reference'
|
|
12
|
-
|
|
11
|
+
format: 'uri-reference',
|
|
12
|
+
$helper: {
|
|
13
|
+
name: 'href',
|
|
14
|
+
filter: {
|
|
15
|
+
type: ["embed"]
|
|
16
|
+
}
|
|
17
|
+
}
|
|
13
18
|
},
|
|
14
|
-
|
|
15
|
-
title: '
|
|
19
|
+
linkable: {
|
|
20
|
+
title: 'Show hash link',
|
|
21
|
+
type: 'boolean',
|
|
22
|
+
default: false
|
|
23
|
+
},
|
|
24
|
+
id: {
|
|
25
|
+
nullable: true,
|
|
16
26
|
type: 'string',
|
|
17
|
-
|
|
27
|
+
pattern: /^[a-z0-9-]*$/.source
|
|
28
|
+
},
|
|
29
|
+
query: {
|
|
30
|
+
title: 'Additional query parameters',
|
|
31
|
+
type: 'object',
|
|
18
32
|
nullable: true
|
|
19
33
|
}
|
|
20
34
|
},
|
|
21
35
|
group: "block",
|
|
22
|
-
parse: function(dom) {
|
|
36
|
+
parse: function (dom) {
|
|
37
|
+
if (dom.matches('element-embed')) return;
|
|
23
38
|
return {
|
|
24
|
-
url: dom.
|
|
39
|
+
url: dom.getAttribute('src')
|
|
25
40
|
};
|
|
26
41
|
},
|
|
27
42
|
tag: 'iframe,element-embed',
|
|
28
|
-
html: `<element-embed
|
|
43
|
+
html: `<element-embed data-src="[url|meta:source][query|as:query]" id="[id]" title="[url|meta:title]" style="padding-bottom:calc([url|meta:height] / [url|meta:width] * 100%)">
|
|
44
|
+
<a aria-hidden="true" class="linkable" href="[$loc.pathname][$loc.search][id|pre:%23]">[linkable|prune:*]#</a>
|
|
45
|
+
<iframe loading="lazy" allowfullscreen frameborder="0" scrolling="no"></iframe>
|
|
46
|
+
</element-embed>`,
|
|
29
47
|
scripts: [
|
|
30
48
|
'../ui/embed.js'
|
|
31
49
|
],
|
|
32
50
|
stylesheets: [
|
|
33
51
|
'../ui/loading.css',
|
|
34
|
-
'../
|
|
35
|
-
'../ui/
|
|
52
|
+
'../ui/embed.css',
|
|
53
|
+
'../ui/linkable.css'
|
|
36
54
|
]
|
|
37
55
|
};
|
|
38
|
-
|
|
56
|
+
exports.editor.scripts.push('../ui/embed-helper.js');
|
package/elements/heading.js
CHANGED
|
@@ -33,7 +33,6 @@ exports.heading = {
|
|
|
33
33
|
},
|
|
34
34
|
linkable: {
|
|
35
35
|
title: 'Show hash link',
|
|
36
|
-
description: 'On hover',
|
|
37
36
|
type: 'boolean',
|
|
38
37
|
default: false
|
|
39
38
|
},
|
|
@@ -55,8 +54,8 @@ exports.heading = {
|
|
|
55
54
|
group: "block",
|
|
56
55
|
icon: '<i class="icon header"></i>',
|
|
57
56
|
tag: 'h1,h2,h3,h4,h5,h6',
|
|
58
|
-
html: `<h[level] class="ui [align|or:left] aligned header" is="h[level]-helper" id="[id]
|
|
59
|
-
<a aria-hidden="true" href="[$loc.pathname][$loc.search][id|pre:%23]">[linkable|prune:*]#</a>
|
|
57
|
+
html: `<h[level] class="ui [align|or:left] aligned header" is="h[level]-helper" id="[id]" entitled="[entitled]">
|
|
58
|
+
<a aria-hidden="true" class="linkable" href="[$loc.pathname][$loc.search][id|pre:%23]">[linkable|prune:*]#</a>
|
|
60
59
|
<div block-content="text">Heading</div>
|
|
61
60
|
</hn>`,
|
|
62
61
|
parse: function (dom) {
|
|
@@ -65,7 +64,8 @@ exports.heading = {
|
|
|
65
64
|
};
|
|
66
65
|
},
|
|
67
66
|
stylesheets: [
|
|
68
|
-
'../ui/heading.css'
|
|
67
|
+
'../ui/heading.css',
|
|
68
|
+
'../ui/linkable.css'
|
|
69
69
|
],
|
|
70
70
|
scripts: [
|
|
71
71
|
'../ui/heading.js'
|
package/elements/time.js
CHANGED
|
@@ -158,7 +158,7 @@ exports.time = {
|
|
|
158
158
|
const format = {};
|
|
159
159
|
const list = (dom.dataset.format ?? "").split(':');
|
|
160
160
|
for (const [key, schema] of Object.entries(this.properties.format.properties)) {
|
|
161
|
-
for (const tok
|
|
161
|
+
for (const tok of list) {
|
|
162
162
|
if (tok) {
|
|
163
163
|
const item = schema.anyOf.find(item => item.const === tok);
|
|
164
164
|
if (item) format[key] = item.const;
|
|
@@ -171,6 +171,7 @@ exports.time = {
|
|
|
171
171
|
format
|
|
172
172
|
};
|
|
173
173
|
},
|
|
174
|
-
|
|
174
|
+
tag: 'time',
|
|
175
|
+
html: `<time datetime="[datetime|or:now|date:iso]" data-format="[format|as:values|join:%3A]" data-timezone="[timezone]" is="element-time"></time>`,
|
|
175
176
|
scripts: ['../ui/time.js']
|
|
176
177
|
};
|
package/package.json
CHANGED
package/ui/embed.css
CHANGED
|
@@ -1,8 +1,24 @@
|
|
|
1
1
|
element-embed {
|
|
2
2
|
display:block;
|
|
3
|
+
position:relative;
|
|
4
|
+
max-width:100%;
|
|
3
5
|
width:100%;
|
|
6
|
+
background-color:#DDD;
|
|
7
|
+
height:0;
|
|
8
|
+
padding-bottom:56.25%;
|
|
4
9
|
}
|
|
5
10
|
|
|
6
11
|
[contenteditable] element-embed iframe {
|
|
7
12
|
pointer-events:none;
|
|
8
13
|
}
|
|
14
|
+
|
|
15
|
+
element-embed > iframe {
|
|
16
|
+
position: absolute;
|
|
17
|
+
border: none;
|
|
18
|
+
width: 100%;
|
|
19
|
+
height: 100%;
|
|
20
|
+
top: 0px;
|
|
21
|
+
left: 0px;
|
|
22
|
+
margin: 0em;
|
|
23
|
+
padding: 0em;
|
|
24
|
+
}
|
package/ui/embed.js
CHANGED
|
@@ -4,72 +4,64 @@ class HTMLElementEmbed extends Page.Element {
|
|
|
4
4
|
hash: null
|
|
5
5
|
};
|
|
6
6
|
static revealRatio = 0.2;
|
|
7
|
-
#defer = new Deferred();
|
|
8
7
|
|
|
9
8
|
reveal(state) {
|
|
10
9
|
this.classList.add('waiting');
|
|
11
10
|
state.consent(this);
|
|
12
|
-
|
|
11
|
+
}
|
|
12
|
+
get currentSrc() {
|
|
13
|
+
return this.querySelector('iframe')?.src ?? "about:blank";
|
|
13
14
|
}
|
|
14
15
|
consent(state) {
|
|
15
16
|
const consent = state.scope.$consent;
|
|
16
17
|
this.classList.toggle('denied', consent == "no");
|
|
17
18
|
this.classList.toggle('waiting', consent == null);
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
const iframe = this.querySelector('iframe');
|
|
20
21
|
if (consent != "yes") {
|
|
21
|
-
|
|
22
|
-
this.#defer.resolve();
|
|
22
|
+
iframe.src = "";
|
|
23
23
|
return;
|
|
24
24
|
}
|
|
25
|
-
if (!
|
|
26
|
-
this.innerHTML = `<iframe width="100%" height="100%" frameborder="0" scrolling="no" allow="autoplay; fullscreen; accelerometer; gyroscope"></iframe>`;
|
|
27
|
-
this.iframe = this.firstElementChild;
|
|
28
|
-
}
|
|
29
|
-
if (!this.iframe.allow) this.iframe.allowFullscreen = true;
|
|
25
|
+
if (!iframe.allow) iframe.allowFullscreen = true;
|
|
30
26
|
|
|
31
27
|
const opts = this.options;
|
|
32
|
-
const prev = Page.parse(this.currentSrc
|
|
28
|
+
const prev = Page.parse(this.currentSrc);
|
|
33
29
|
const cur = Page.parse(opts.src || "about:blank");
|
|
34
30
|
cur.hash = opts.hash;
|
|
35
|
-
|
|
31
|
+
const curSrc = cur.toString();
|
|
36
32
|
|
|
37
33
|
if (cur.samePath(prev) == false) {
|
|
38
34
|
this.classList.remove('error');
|
|
39
35
|
this.classList.add('loading');
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
36
|
+
if (!state.scope.$write) {
|
|
37
|
+
iframe.allow = "accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture";
|
|
38
|
+
}
|
|
39
|
+
iframe.src = curSrc;
|
|
40
|
+
} else if (cur.hash != prev.hash) {
|
|
41
|
+
try {
|
|
42
|
+
iframe.contentWindow.location.replace(curSrc);
|
|
43
|
+
} catch (err) {
|
|
44
|
+
iframe.src = curSrc;
|
|
48
45
|
}
|
|
49
|
-
this.#defer.resolve();
|
|
50
46
|
}
|
|
51
47
|
}
|
|
52
48
|
captureClick(e, state) {
|
|
53
49
|
if (this.matches('.denied')) state.reconsent();
|
|
54
50
|
}
|
|
55
51
|
captureLoad() {
|
|
56
|
-
this.#defer.resolve();
|
|
57
52
|
this.classList.remove('loading');
|
|
58
53
|
}
|
|
59
54
|
captureError() {
|
|
60
|
-
this.#defer.resolve();
|
|
61
55
|
this.classList.add('error');
|
|
62
56
|
}
|
|
63
57
|
handleAllMessage(e, state) {
|
|
64
|
-
if (!e.origin || !this.options.
|
|
65
|
-
if (this.options.
|
|
58
|
+
if (!e.origin || !this.options.source) return;
|
|
59
|
+
if (this.options.source.startsWith(e.origin) == false) return;
|
|
66
60
|
if (this.receiveMessage) this.receiveMessage(e.data ?? {});
|
|
67
61
|
}
|
|
68
62
|
close() {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
delete this.iframe;
|
|
72
|
-
}
|
|
63
|
+
const iframe = this.querySelector('iframe');
|
|
64
|
+
if (iframe) iframe.src = "";
|
|
73
65
|
}
|
|
74
66
|
}
|
|
75
67
|
|
package/ui/fieldset-list.js
CHANGED
|
@@ -54,8 +54,8 @@ class HTMLElementFieldsetList extends Page.Element {
|
|
|
54
54
|
for (const node of inputs) {
|
|
55
55
|
keys.add(node.name);
|
|
56
56
|
}
|
|
57
|
-
const splits = Array.from(keys).map(name => name
|
|
58
|
-
const
|
|
57
|
+
const splits = Array.from(keys).map(name => this.#parts(name));
|
|
58
|
+
const prefix = [];
|
|
59
59
|
let pos = 0, com = null;
|
|
60
60
|
while (splits.length && splits.every(list => {
|
|
61
61
|
if (com == null) {
|
|
@@ -69,16 +69,16 @@ class HTMLElementFieldsetList extends Page.Element {
|
|
|
69
69
|
return list[pos] == com;
|
|
70
70
|
}
|
|
71
71
|
})) {
|
|
72
|
-
|
|
72
|
+
prefix.push(com);
|
|
73
73
|
com = null;
|
|
74
74
|
pos++;
|
|
75
75
|
}
|
|
76
|
-
if (coms.length) coms.push('');
|
|
77
|
-
const prefix = coms.join('.');
|
|
78
76
|
this.#prefix = prefix;
|
|
79
77
|
const model = {};
|
|
80
78
|
for (const key of keys) {
|
|
81
|
-
if (key
|
|
79
|
+
if (this.#prefixed(key)) {
|
|
80
|
+
model[this.#parts(key).slice(prefix.length).join('.')] = null;
|
|
81
|
+
}
|
|
82
82
|
}
|
|
83
83
|
this.#model = model;
|
|
84
84
|
}
|
|
@@ -109,6 +109,22 @@ class HTMLElementFieldsetList extends Page.Element {
|
|
|
109
109
|
return `[block-type="fieldlist_button"][value="${name}"]`;
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
#prefixed(key, p = this.#prefix) {
|
|
113
|
+
const parts = this.#parts(key);
|
|
114
|
+
for (let i = 0; i < p.length; i++) {
|
|
115
|
+
if (parts[i] != p[i]) return false;
|
|
116
|
+
}
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
#incrementkey(index, name) {
|
|
121
|
+
if (!this.#prefixed(name)) return null;
|
|
122
|
+
const parts = this.#prefix.slice();
|
|
123
|
+
parts.push(index);
|
|
124
|
+
parts.push(...this.#parts(name).slice(this.#prefix.length));
|
|
125
|
+
return parts.join('.');
|
|
126
|
+
}
|
|
127
|
+
|
|
112
128
|
#resize(size, scope) {
|
|
113
129
|
if (scope.$write) return;
|
|
114
130
|
const len = Math.max(Number(this.dataset.size) || 0, size);
|
|
@@ -119,25 +135,17 @@ class HTMLElementFieldsetList extends Page.Element {
|
|
|
119
135
|
return { index: i };
|
|
120
136
|
});
|
|
121
137
|
const inputs = tpl.querySelectorAll('[name]');
|
|
122
|
-
const prefix = this.#prefix;
|
|
123
138
|
for (const node of inputs) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
node.id = `for-${prefix}[fielditem.index].${node.id.substring(4 + prefix.length)}`;
|
|
128
|
-
}
|
|
139
|
+
const name = this.#incrementkey('[fielditem.index]', node.name);
|
|
140
|
+
if (name != null) {
|
|
141
|
+
node.name = name;
|
|
129
142
|
}
|
|
130
143
|
}
|
|
131
144
|
const conditionalFieldsets = tpl.querySelectorAll('[is="element-fieldset"]');
|
|
132
145
|
for (const node of conditionalFieldsets) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
const labels = tpl.querySelectorAll('label[for]');
|
|
138
|
-
for (const node of labels) {
|
|
139
|
-
if (node.htmlFor?.startsWith('for-' + prefix)) {
|
|
140
|
-
node.htmlFor = `for-${prefix}[fielditem.index].${node.htmlFor.substring(4 + prefix.length)}`;
|
|
146
|
+
const name = this.#incrementkey('[fielditem.index]', node.dataset.name);
|
|
147
|
+
if (name != null) {
|
|
148
|
+
node.dataset.name = name;
|
|
141
149
|
}
|
|
142
150
|
}
|
|
143
151
|
|
|
@@ -159,7 +167,7 @@ class HTMLElementFieldsetList extends Page.Element {
|
|
|
159
167
|
{
|
|
160
168
|
const hidden = tpl.ownerDocument.createElement('input');
|
|
161
169
|
hidden.type = "hidden";
|
|
162
|
-
hidden.name = prefix.
|
|
170
|
+
hidden.name = this.#prefix.join('.');
|
|
163
171
|
tpl.appendChild(hidden);
|
|
164
172
|
}
|
|
165
173
|
}
|
|
@@ -177,13 +185,16 @@ class HTMLElementFieldsetList extends Page.Element {
|
|
|
177
185
|
});
|
|
178
186
|
}
|
|
179
187
|
|
|
188
|
+
#parts(key) {
|
|
189
|
+
return key ? key.split(".") : [];
|
|
190
|
+
}
|
|
191
|
+
|
|
180
192
|
#listFromValues(values) {
|
|
181
193
|
const list = [];
|
|
182
|
-
const prefix = this.#prefix;
|
|
183
194
|
// just unflatten the array
|
|
184
195
|
for (const [key, val] of Object.entries(values)) {
|
|
185
|
-
if (!key
|
|
186
|
-
const parts = key.slice(prefix.length)
|
|
196
|
+
if (!this.#prefixed(key)) continue;
|
|
197
|
+
const parts = this.#parts(key).slice(this.#prefix.length);
|
|
187
198
|
const index = Number(parts.shift());
|
|
188
199
|
if (!Number.isInteger(index)) continue;
|
|
189
200
|
delete values[key];
|
|
@@ -195,11 +206,13 @@ class HTMLElementFieldsetList extends Page.Element {
|
|
|
195
206
|
}
|
|
196
207
|
|
|
197
208
|
#listToValues(values, list) {
|
|
198
|
-
const prefix = this.#prefix;
|
|
199
209
|
for (let i = 0; i < list.length; i++) {
|
|
200
210
|
const obj = list[i];
|
|
201
211
|
for (const [key, val] of Object.entries(obj)) {
|
|
202
|
-
|
|
212
|
+
const parts = this.#prefix.slice();
|
|
213
|
+
parts.push(i);
|
|
214
|
+
parts.push(...this.#parts(key));
|
|
215
|
+
values[parts.join('.')] = val;
|
|
203
216
|
}
|
|
204
217
|
}
|
|
205
218
|
}
|
|
@@ -261,9 +274,10 @@ class HTMLElementFieldsetList extends Page.Element {
|
|
|
261
274
|
}
|
|
262
275
|
|
|
263
276
|
#parseName(name) {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
277
|
+
if (!this.#prefixed(name)) {
|
|
278
|
+
return { index: -1 };
|
|
279
|
+
}
|
|
280
|
+
const parts = this.#parts(name).slice(this.#prefix.length);
|
|
267
281
|
const index = Number(parts.shift());
|
|
268
282
|
if (!Number.isInteger(index)) return { index: -1 };
|
|
269
283
|
return { index, sub: parts.join('.') };
|
package/ui/fieldset.js
CHANGED
|
@@ -19,14 +19,8 @@ class HTMLElementFieldSet extends Page.create(HTMLFieldSetElement) {
|
|
|
19
19
|
this.fill(null, state.scope);
|
|
20
20
|
state.finish(() => this.fill(null, state.scope));
|
|
21
21
|
}
|
|
22
|
-
|
|
23
|
-
this.form
|
|
24
|
-
}
|
|
25
|
-
close() {
|
|
26
|
-
this.form?.removeEventListener('change', this);
|
|
27
|
-
}
|
|
28
|
-
handleEvent(e, state) {
|
|
29
|
-
if (e.type == "change") {
|
|
22
|
+
handleAllChange(e, state) {
|
|
23
|
+
if (this.form.contains(e.target)) {
|
|
30
24
|
this.fill(null, state.scope);
|
|
31
25
|
}
|
|
32
26
|
}
|
package/ui/heading.css
CHANGED
package/ui/linkable.css
ADDED
package/ui/media.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const HTMLElementMediaConstructor = Superclass => class extends Superclass {
|
|
2
|
-
#defer = new Deferred();
|
|
3
2
|
|
|
4
3
|
patch(state) {
|
|
5
4
|
this.classList.remove('error', 'loading');
|
|
@@ -15,17 +14,14 @@ const HTMLElementMediaConstructor = Superclass => class extends Superclass {
|
|
|
15
14
|
this.setAttribute('src', curSrc);
|
|
16
15
|
}
|
|
17
16
|
if (state.scope.$write) this.pause();
|
|
18
|
-
// return this.#defer; // do not hang chain
|
|
19
17
|
}
|
|
20
18
|
handleClick(e, state) {
|
|
21
19
|
if (state.scope.$write) e.preventDefault();
|
|
22
20
|
}
|
|
23
21
|
captureLoad() {
|
|
24
|
-
this.#defer.resolve();
|
|
25
22
|
this.classList.remove('loading');
|
|
26
23
|
}
|
|
27
24
|
captureError() {
|
|
28
|
-
this.#defer.resolve();
|
|
29
25
|
this.classList.remove('loading');
|
|
30
26
|
this.classList.add('error');
|
|
31
27
|
}
|
package/ui/select.js
CHANGED
package/ui/tab.css
CHANGED
package/ui/tab.js
CHANGED
|
@@ -11,11 +11,11 @@ class HTMLElementTabs extends Page.Element {
|
|
|
11
11
|
}
|
|
12
12
|
patch(state) {
|
|
13
13
|
const pos = this.options.index;
|
|
14
|
-
const
|
|
14
|
+
const key = `${this.id}.index`;
|
|
15
|
+
state.vars[key] = true;
|
|
15
16
|
|
|
16
17
|
this.items.children.forEach((item, i) => {
|
|
17
18
|
const query = { ...state.query };
|
|
18
|
-
const key = `${id}.index`;
|
|
19
19
|
if (i == 0) delete query[key];
|
|
20
20
|
else query[key] = i;
|
|
21
21
|
item.setAttribute('href', Page.format({
|
package/ui/time.js
CHANGED
|
@@ -6,7 +6,7 @@ class HTMLElementTime extends Page.create(HTMLTimeElement) {
|
|
|
6
6
|
};
|
|
7
7
|
|
|
8
8
|
patch(state) {
|
|
9
|
-
this.textContent = `[stamp|
|
|
9
|
+
this.textContent = `[stamp|or:now|date:[fmt]:[tz]]`.fuse({
|
|
10
10
|
stamp: this.dateTime,
|
|
11
11
|
fmt: this.dataset.format,
|
|
12
12
|
tz: this.dataset.timezone
|