@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 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|ornull|templates:$query]"
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|or:[$element.resources.empty]]"
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|or:[$element.resources.empty]]"
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
  ],
@@ -76,7 +76,7 @@ exports.strike = {
76
76
  contents: "text*",
77
77
  inline: true,
78
78
  inplace: true,
79
- group: "inline nolink",
79
+ group: "nolink",
80
80
  tag: 's',
81
81
  html: '<s></s>'
82
82
  };
@@ -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
+ };
@@ -1,82 +1,21 @@
1
- exports.fetch.install = function() {
2
- this.dom.classList.add('ui');
3
- };
4
- exports.message.install = function(node, d, scope) {
5
- this.dom.classList.add('ui', '[inverted|?]');
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pageboard/html",
3
- "version": "0.11.25",
3
+ "version": "0.11.27",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "repository": {
package/ui/image.css CHANGED
@@ -1,10 +1,3 @@
1
- img.loading[data-src]::after {
2
- content: none;
3
- }
4
- img.error::after {
5
- width: 100%;
6
- }
7
-
8
1
  element-image {
9
2
  display:flex;
10
3
  position:relative;
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) this.placeholder();
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
- img.removeAttribute('src');
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.done();
169
+ this.promise?.done();
161
170
  this.classList.remove('loading');
162
171
  this.fix(this.image);
163
172
  }
164
173
  captureError() {
165
- this.promise.done();
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
- const { w, h } = this.dimensions;
172
- this.image.width = w;
173
- this.image.height = h;
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.done();
207
+ this.promise?.done();
195
208
  this.classList.remove('loading');
196
209
  this.fix(this.image);
197
210
  }
198
- placeholder() {
199
- const { w, h } = this.dimensions;
200
- this.width = w;
201
- this.height = h;
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
@@ -9,6 +9,7 @@
9
9
  width: 100%;
10
10
  top: 50%;
11
11
  margin-top: -0.5rem;
12
+ line-height: 1rem;
12
13
  }
13
14
 
14
15
  @keyframes spinner {
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 || this.closest('[block-content="template"]')) {
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.#reached = false;
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
- #updateFetchSize() {
64
- const old = this.#size;
65
- const cur = this.#size = this.#findFetch()?.ownView.children.length ?? 0;
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 (!fetch) {
76
- console.warn(`Pagination need Fetch to use $query.${name} parameter`);
13
+ if (!node) {
14
+ console.warn("pagination does not find fetch node", this.dataset.fetch);
15
+ return;
77
16
  }
78
- return fetch;
79
- }
80
-
81
- handleClick(e, state) {
82
- const fetch = this.#findFetch();
83
- if (fetch) fetch.infinite = false;
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
- paint(state) {
87
- const name = this.dataset.name || 'offset';
88
- const off = parseInt(state.query[name]) || 0;
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
- setup(state) {
96
- if (this.isContentEditable || !this.#infinite) return;
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 #infinite() {
124
- return this.hasAttribute('data-infinite');
45
+ get disabled() {
46
+ return this.classList.contains('disabled');
125
47
  }
126
48
  }
127
49
 
package/ui/site.css CHANGED
@@ -293,6 +293,8 @@ body .ui.inverted::-webkit-scrollbar-thumb:hover {
293
293
  }
294
294
  .ui.grid.full {
295
295
  width: 100% !important;
296
+ margin-left: auto !important;
297
+ margin-right: auto !important;
296
298
  }
297
299
 
298
300
  /* for editor only, but semantic-ui dependent */
package/ui/pagination.css DELETED
@@ -1,3 +0,0 @@
1
- a[is="element-pagination"].disabled {
2
- display: none !important;
3
- }