@pageboard/html 0.11.25 → 0.11.27
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/form.js +1 -1
- package/elements/image.js +2 -8
- package/elements/inlines.js +1 -1
- package/elements/pagination.js +35 -0
- package/elements/query-tags.js +30 -0
- package/elements/template.js +13 -74
- package/package.json +1 -1
- package/ui/image.css +0 -7
- package/ui/image.js +35 -15
- package/ui/loading.css +1 -0
- package/ui/pagination.js +32 -110
- package/ui/site.css +2 -0
- package/ui/pagination.css +0 -3
package/elements/form.js
CHANGED
|
@@ -168,7 +168,7 @@ exports.api_form = {
|
|
|
168
168
|
tag: 'form[method="post"]',
|
|
169
169
|
html: `<form is="element-form" method="post" name="[name]"
|
|
170
170
|
action="/.api/form/[$id]"
|
|
171
|
-
parameters="[$expr.action.parameters|
|
|
171
|
+
parameters="[$expr.action.parameters|templates:$query]"
|
|
172
172
|
success="[redirection|urltpl:url:parameters]"
|
|
173
173
|
badrequest="[badrequest|urltpl:url:parameters]"
|
|
174
174
|
unauthorized="[unauthorized|urltpl:url:parameters]"
|
package/elements/image.js
CHANGED
|
@@ -131,14 +131,11 @@ exports.image = {
|
|
|
131
131
|
html: `<element-image
|
|
132
132
|
class="[display.fit|or:none] [display.horizontal|or:] [display.vertical|or:]"
|
|
133
133
|
alt="[alt]"
|
|
134
|
-
data-src="[url
|
|
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]"
|
|
136
136
|
>
|
|
137
137
|
<div block-content="legend"></div>
|
|
138
138
|
</element-image>`,
|
|
139
|
-
resources: {
|
|
140
|
-
empty: '../ui/empty.png'
|
|
141
|
-
},
|
|
142
139
|
stylesheets: [
|
|
143
140
|
'../ui/loading.css',
|
|
144
141
|
'../ui/image.css'
|
|
@@ -281,7 +278,7 @@ exports.inlineImage = {
|
|
|
281
278
|
group: "inline",
|
|
282
279
|
tag: "img",
|
|
283
280
|
html: `<img is="element-img"
|
|
284
|
-
data-src="[url
|
|
281
|
+
data-src="[url]"
|
|
285
282
|
data-crop="[crop.x];[crop.y];[crop.width];[crop.height];[crop.zoom]"
|
|
286
283
|
alt="" class="ui inline image
|
|
287
284
|
[display.avatar|?]
|
|
@@ -290,9 +287,6 @@ exports.inlineImage = {
|
|
|
290
287
|
[display.spaced|?]
|
|
291
288
|
[display.floated|pre:floated ]
|
|
292
289
|
[display.align|post: aligned]" />`,
|
|
293
|
-
resources: {
|
|
294
|
-
empty: '../ui/empty.png'
|
|
295
|
-
},
|
|
296
290
|
stylesheets: [
|
|
297
291
|
'../lib/components/image.css'
|
|
298
292
|
],
|
package/elements/inlines.js
CHANGED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
exports.pagination = {
|
|
2
|
+
priority: 13, // after fetch and after menu items
|
|
3
|
+
title: "Pagination",
|
|
4
|
+
icon: '<b class="icon">±N</b>',
|
|
5
|
+
menu: "link",
|
|
6
|
+
group: "block",
|
|
7
|
+
isolating: true,
|
|
8
|
+
properties: {
|
|
9
|
+
fetch: {
|
|
10
|
+
title: 'Fetch block',
|
|
11
|
+
type: 'string',
|
|
12
|
+
format: 'id',
|
|
13
|
+
$filter: {
|
|
14
|
+
name: 'block',
|
|
15
|
+
types: ["fetch"]
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
dir: {
|
|
19
|
+
title: 'Direction',
|
|
20
|
+
anyOf: [{
|
|
21
|
+
const: '-',
|
|
22
|
+
title: 'Backward'
|
|
23
|
+
}, {
|
|
24
|
+
const: '+',
|
|
25
|
+
title: 'Forward'
|
|
26
|
+
}],
|
|
27
|
+
default: '+'
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
contents: "inline*",
|
|
31
|
+
html: `<a class="ui button pagination" is="element-pagination" data-dir="[dir]" data-fetch="[fetch]">[dir|eq:1:Next:Prev]</a>`,
|
|
32
|
+
scripts: [
|
|
33
|
+
'../ui/pagination.js'
|
|
34
|
+
]
|
|
35
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
exports.query_tags = {
|
|
2
|
+
priority: 10, // after fetch
|
|
3
|
+
title: 'Tags',
|
|
4
|
+
icon: '<i class="tags icon"></i>',
|
|
5
|
+
menu: "form",
|
|
6
|
+
group: "block",
|
|
7
|
+
properties: {
|
|
8
|
+
form: {
|
|
9
|
+
title: 'Form name',
|
|
10
|
+
type: 'string',
|
|
11
|
+
format: 'id',
|
|
12
|
+
nullable: true
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
contents: {
|
|
16
|
+
id: 'title',
|
|
17
|
+
nodes: 'inline*'
|
|
18
|
+
},
|
|
19
|
+
html: `<element-query-tags for="[form]">
|
|
20
|
+
<div block-content="title">Filters:</div>
|
|
21
|
+
<div class="ui labels"></div>
|
|
22
|
+
</element-query-tags>`,
|
|
23
|
+
stylesheets: [
|
|
24
|
+
'../lib/components/label.css',
|
|
25
|
+
'../ui/query-tags.css'
|
|
26
|
+
],
|
|
27
|
+
scripts: [
|
|
28
|
+
'../ui/query-tags.js'
|
|
29
|
+
]
|
|
30
|
+
};
|
package/elements/template.js
CHANGED
|
@@ -1,82 +1,21 @@
|
|
|
1
|
-
exports.fetch.
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
exports.fetch.fragments.push({
|
|
2
|
+
attributes: {
|
|
3
|
+
className: "ui"
|
|
4
|
+
}
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
exports.message.fragments.push({
|
|
8
|
+
attributes: {
|
|
9
|
+
className: '[inverted|?]'
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
|
|
7
13
|
exports.message.properties.inverted = {
|
|
8
14
|
title: 'Inverted',
|
|
9
15
|
type: 'boolean',
|
|
10
16
|
default: false
|
|
11
17
|
};
|
|
18
|
+
|
|
12
19
|
exports.message.stylesheets.unshift(
|
|
13
20
|
'../lib/components/message.css'
|
|
14
21
|
);
|
|
15
|
-
|
|
16
|
-
exports.query_tags = {
|
|
17
|
-
priority: 10, // after fetch
|
|
18
|
-
title: 'Tags',
|
|
19
|
-
icon: '<i class="tags icon"></i>',
|
|
20
|
-
menu: "form",
|
|
21
|
-
group: "block",
|
|
22
|
-
properties: {
|
|
23
|
-
form: {
|
|
24
|
-
title: 'Form name',
|
|
25
|
-
type: 'string',
|
|
26
|
-
format: 'id',
|
|
27
|
-
nullable: true
|
|
28
|
-
}
|
|
29
|
-
},
|
|
30
|
-
contents: {
|
|
31
|
-
id: 'title',
|
|
32
|
-
nodes: 'inline*'
|
|
33
|
-
},
|
|
34
|
-
html: `<element-query-tags for="[form]">
|
|
35
|
-
<div block-content="title">Filters:</div>
|
|
36
|
-
<div class="ui labels"></div>
|
|
37
|
-
</element-query-tags>`,
|
|
38
|
-
stylesheets: [
|
|
39
|
-
'../lib/components/label.css',
|
|
40
|
-
'../ui/query-tags.css'
|
|
41
|
-
],
|
|
42
|
-
scripts: [
|
|
43
|
-
'../ui/query-tags.js'
|
|
44
|
-
]
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
exports.pagination = {
|
|
48
|
-
priority: 13, // after fetch and after menu items
|
|
49
|
-
title: "Pagination",
|
|
50
|
-
icon: '<b class="icon">±N</b>',
|
|
51
|
-
menu: "link",
|
|
52
|
-
context: 'menu//',
|
|
53
|
-
group: "menu_item",
|
|
54
|
-
properties: {
|
|
55
|
-
name: {
|
|
56
|
-
title: 'Offset name',
|
|
57
|
-
description: 'Query parameter used by fetch block',
|
|
58
|
-
type: 'string',
|
|
59
|
-
format: 'id',
|
|
60
|
-
default: 'offset'
|
|
61
|
-
},
|
|
62
|
-
value: {
|
|
63
|
-
title: 'Offset value',
|
|
64
|
-
description: 'Integer, can be negative',
|
|
65
|
-
type: 'integer',
|
|
66
|
-
default: 10
|
|
67
|
-
},
|
|
68
|
-
infinite: {
|
|
69
|
-
title: 'Infinite loading',
|
|
70
|
-
type: 'boolean',
|
|
71
|
-
default: false
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
|
-
contents: "inline*",
|
|
75
|
-
html: `<a class="item" is="element-pagination" data-name="[name]" data-value="[value]" data-infinite="[infinite]"></a>`,
|
|
76
|
-
stylesheets: [
|
|
77
|
-
'../ui/pagination.css'
|
|
78
|
-
],
|
|
79
|
-
scripts: [
|
|
80
|
-
'../ui/pagination.js'
|
|
81
|
-
]
|
|
82
|
-
};
|
package/package.json
CHANGED
package/ui/image.css
CHANGED
package/ui/image.js
CHANGED
|
@@ -98,12 +98,21 @@ class HTMLElementImage extends VirtualHTMLElement {
|
|
|
98
98
|
if (!meta || !meta.width || !meta.height) return;
|
|
99
99
|
this.dataset.width = meta.width;
|
|
100
100
|
this.dataset.height = meta.height;
|
|
101
|
-
if (!this.currentSrc)
|
|
101
|
+
if (!this.currentSrc) {
|
|
102
|
+
this.placeholder();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
get currentSrc() {
|
|
107
|
+
const cur = super.currentSrc;
|
|
108
|
+
if (!cur && this.src?.startsWith('data:')) return this.src;
|
|
109
|
+
else return cur;
|
|
102
110
|
}
|
|
111
|
+
|
|
103
112
|
reveal(state) {
|
|
104
113
|
const img = this.image;
|
|
105
114
|
if (!this.options.src) {
|
|
106
|
-
|
|
115
|
+
this.placeholder(true);
|
|
107
116
|
return;
|
|
108
117
|
}
|
|
109
118
|
const fit = this.fit;
|
|
@@ -157,23 +166,22 @@ class HTMLElementImage extends VirtualHTMLElement {
|
|
|
157
166
|
return this.promise;
|
|
158
167
|
}
|
|
159
168
|
captureLoad() {
|
|
160
|
-
this.promise
|
|
169
|
+
this.promise?.done();
|
|
161
170
|
this.classList.remove('loading');
|
|
162
171
|
this.fix(this.image);
|
|
163
172
|
}
|
|
164
173
|
captureError() {
|
|
165
|
-
this.promise
|
|
174
|
+
this.promise?.done();
|
|
166
175
|
this.classList.remove('loading');
|
|
167
176
|
this.classList.add('error');
|
|
168
177
|
this.placeholder();
|
|
169
178
|
}
|
|
170
|
-
placeholder() {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
179
|
+
placeholder(error) {
|
|
180
|
+
let { w, h } = this.dimensions;
|
|
181
|
+
if (Number.isNaN(w)) w = 320;
|
|
182
|
+
if (Number.isNaN(h)) h = 240;
|
|
174
183
|
this.image.src = "data:image/svg+xml," + encodeURIComponent(
|
|
175
|
-
`<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 ${w} ${h}"></svg>`
|
|
176
|
-
);
|
|
184
|
+
`<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 ${w} ${h}"><text text-anchor="middle" dominant-baseline="central" x="50%" y="50%" fill="#aaa">${error ? '∅' : ''}</text></svg>`);
|
|
177
185
|
}
|
|
178
186
|
}
|
|
179
187
|
|
|
@@ -190,15 +198,27 @@ class HTMLElementInlineImage extends HTMLImageElement {
|
|
|
190
198
|
get image() {
|
|
191
199
|
return this;
|
|
192
200
|
}
|
|
201
|
+
get currentSrc() {
|
|
202
|
+
const cur = super.currentSrc;
|
|
203
|
+
if (!cur && this.src?.startsWith('data:')) return this.src;
|
|
204
|
+
else return cur;
|
|
205
|
+
}
|
|
193
206
|
captureLoad() {
|
|
194
|
-
this.promise
|
|
207
|
+
this.promise?.done();
|
|
195
208
|
this.classList.remove('loading');
|
|
196
209
|
this.fix(this.image);
|
|
197
210
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
this.
|
|
201
|
-
this.
|
|
211
|
+
captureError() {
|
|
212
|
+
this.promise?.done();
|
|
213
|
+
this.classList.remove('loading');
|
|
214
|
+
this.placeholder(true);
|
|
215
|
+
}
|
|
216
|
+
placeholder(error) {
|
|
217
|
+
let { w, h } = this.dimensions;
|
|
218
|
+
w = Number.isNaN(w) ? 40 : w;
|
|
219
|
+
h = Number.isNaN(h) ? 30 : h;
|
|
220
|
+
this.setAttribute('src', "data:image/svg+xml," + encodeURIComponent(
|
|
221
|
+
`<svg xmlns="http://www.w3.org/2000/svg" width="${w}px" height="${h}px" viewBox="0 0 ${w} ${h}"><text text-anchor="middle" dominant-baseline="central" x="50%" y="50%" fill="#aaa">${error ? '∅' : ''}</text></svg>`));
|
|
202
222
|
}
|
|
203
223
|
}
|
|
204
224
|
|
package/ui/loading.css
CHANGED
package/ui/pagination.js
CHANGED
|
@@ -1,127 +1,49 @@
|
|
|
1
1
|
class HTMLElementPagination extends HTMLAnchorElement {
|
|
2
|
-
#observer;
|
|
3
|
-
#queue;
|
|
4
|
-
#reached;
|
|
5
|
-
#size;
|
|
6
|
-
#visible;
|
|
7
|
-
#continue;
|
|
8
|
-
|
|
9
|
-
constructor() {
|
|
10
|
-
super();
|
|
11
|
-
this.init?.();
|
|
12
|
-
}
|
|
13
|
-
|
|
14
2
|
patch(state) {
|
|
15
|
-
if (this.isContentEditable
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
const name = this.dataset.name || 'offset';
|
|
19
|
-
const off = parseInt(state.query[name]) || 0;
|
|
20
|
-
const delta = parseInt(this.dataset.value) || 0;
|
|
21
|
-
const cur = off + delta;
|
|
22
|
-
const disabled = cur < 0 || !this.#findFetch() || this.#continue && delta < 0 || false;
|
|
23
|
-
this.classList.toggle('disabled', disabled);
|
|
24
|
-
|
|
25
|
-
if (disabled) {
|
|
26
|
-
this.removeAttribute('href');
|
|
27
|
-
} else {
|
|
28
|
-
this.setAttribute('href', Page.format({
|
|
29
|
-
pathname: state.pathname,
|
|
30
|
-
query: {
|
|
31
|
-
...state.query,
|
|
32
|
-
[name]: cur || undefined
|
|
33
|
-
}
|
|
34
|
-
}));
|
|
35
|
-
}
|
|
3
|
+
if (this.isContentEditable) return;
|
|
36
4
|
state.finish(() => {
|
|
37
|
-
this.#
|
|
38
|
-
if (this.#updateFetchSize() == false) {
|
|
39
|
-
this.removeAttribute('href');
|
|
40
|
-
this.classList.toggle('disabled', true);
|
|
41
|
-
} else {
|
|
42
|
-
this.#continue = true;
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
#more(state) {
|
|
48
|
-
this.#reached = true;
|
|
49
|
-
if (this.#queue) this.#queue = this.#queue.then(() => {
|
|
50
|
-
if (!this.#continue || !this.hasAttribute('href')) {
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
const fetch = this.#findFetch();
|
|
54
|
-
if (fetch) {
|
|
55
|
-
fetch.infinite = true;
|
|
56
|
-
return state.push(this.getAttribute('href'));
|
|
57
|
-
}
|
|
58
|
-
}).then(() => {
|
|
59
|
-
this.#reached = false;
|
|
5
|
+
this.#update(state);
|
|
60
6
|
});
|
|
61
7
|
}
|
|
62
8
|
|
|
63
|
-
#
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
if (this.#continue) return old != cur;
|
|
67
|
-
else return cur != 0;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
#findFetch() {
|
|
71
|
-
const name = this.dataset.name || 'offset';
|
|
72
|
-
const fetch = this.ownerDocument.querySelector(
|
|
73
|
-
`element-template[block-type="fetch"][parameters~="$query.${name}|or:0"]`
|
|
9
|
+
#update(state) {
|
|
10
|
+
const node = this.ownerDocument.querySelector(
|
|
11
|
+
`element-template[action="/.api/query/${this.dataset.fetch}"]`
|
|
74
12
|
);
|
|
75
|
-
if (!
|
|
76
|
-
console.warn(
|
|
13
|
+
if (!node) {
|
|
14
|
+
console.warn("pagination does not find fetch node", this.dataset.fetch);
|
|
15
|
+
return;
|
|
77
16
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const
|
|
83
|
-
|
|
17
|
+
const name = node.dataset.pagination;
|
|
18
|
+
const start = parseInt(node.dataset.start) || 0;
|
|
19
|
+
const stop = parseInt(node.dataset.stop) || 0;
|
|
20
|
+
const limit = parseInt(node.dataset.limit) || 10;
|
|
21
|
+
const count = parseInt(node.dataset.count) || 0;
|
|
22
|
+
const sign = this.dataset.dir == "-" ? -1 : +1;
|
|
23
|
+
const cur = sign > 0 ? stop : (start - limit);
|
|
24
|
+
|
|
25
|
+
this.setAttribute('href', Page.format({
|
|
26
|
+
pathname: state.pathname,
|
|
27
|
+
query: {
|
|
28
|
+
...state.query,
|
|
29
|
+
[name]: cur || undefined
|
|
30
|
+
}
|
|
31
|
+
}));
|
|
32
|
+
this.disabled = sign < 0 && cur + limit <= 0 || sign > 0 && count < limit;
|
|
84
33
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (off == 0) {
|
|
90
|
-
this.#continue = true;
|
|
91
|
-
if (this.#visible) this.#more(state);
|
|
34
|
+
handleClick(e) {
|
|
35
|
+
if (this.disabled) {
|
|
36
|
+
e.preventDefault();
|
|
37
|
+
e.stopImmediatePropagation();
|
|
92
38
|
}
|
|
93
39
|
}
|
|
94
40
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
this.#queue = Promise.resolve();
|
|
98
|
-
this.#observer = new IntersectionObserver(entries => {
|
|
99
|
-
entries.forEach(entry => {
|
|
100
|
-
if (this.#reached) return;
|
|
101
|
-
if (this.offsetParent && (entry.intersectionRatio || 0) !== 0) {
|
|
102
|
-
this.#visible = true;
|
|
103
|
-
this.#more(state);
|
|
104
|
-
} else {
|
|
105
|
-
this.#visible = false;
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
}, {
|
|
109
|
-
threshold: [1]
|
|
110
|
-
});
|
|
111
|
-
this.#observer.observe(this);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
close() {
|
|
115
|
-
this.#queue = null;
|
|
116
|
-
if (this.#observer) {
|
|
117
|
-
this.#observer.unobserve(this);
|
|
118
|
-
this.#observer.disconnect();
|
|
119
|
-
this.#observer = null;
|
|
120
|
-
}
|
|
41
|
+
set disabled(val) {
|
|
42
|
+
this.classList.toggle('disabled', val);
|
|
121
43
|
}
|
|
122
44
|
|
|
123
|
-
get
|
|
124
|
-
return this.
|
|
45
|
+
get disabled() {
|
|
46
|
+
return this.classList.contains('disabled');
|
|
125
47
|
}
|
|
126
48
|
}
|
|
127
49
|
|
package/ui/site.css
CHANGED
package/ui/pagination.css
DELETED