@pageboard/html 0.12.1 → 0.12.3

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/image.js CHANGED
@@ -132,6 +132,8 @@ exports.image = {
132
132
  class="[display.fit|or:none] [display.horizontal|or:] [display.vertical|or:]"
133
133
  alt="[alt]"
134
134
  data-src="[url]"
135
+ data-width="[url|meta:width]"
136
+ data-height="[url|meta:height]"
135
137
  data-crop="[crop.x|or:50];[crop.y|or:50];[crop.width|or:100];[crop.height|or:100];[crop.zoom|or:100]"
136
138
  >
137
139
  <div block-content="legend"></div>
@@ -279,6 +281,8 @@ exports.inlineImage = {
279
281
  tag: "img",
280
282
  html: `<img is="element-img"
281
283
  data-src="[url]"
284
+ data-width="[url|meta:width]"
285
+ data-height="[url|meta:height]"
282
286
  data-crop="[crop.x];[crop.y];[crop.width];[crop.height];[crop.zoom]"
283
287
  alt="" class="ui inline image
284
288
  [display.avatar]
package/elements/media.js CHANGED
@@ -67,7 +67,7 @@ exports.video = {
67
67
  },
68
68
  html: `<video is="element-video" data-src="[url]" class="[display.fit|or:none]"
69
69
  preload="metadata" autoplay="[autoplay]" loop="[loop]"
70
- muted="[muted]" controls="[controls]"></video>`,
70
+ muted="[muted]" controls="[controls]" width="[url|meta:width]" height="[url|meta:height]"></video>`,
71
71
  scripts: [
72
72
  '../ui/media.js'
73
73
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pageboard/html",
3
- "version": "0.12.1",
3
+ "version": "0.12.3",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "repository": {
package/ui/consent.js CHANGED
@@ -13,7 +13,7 @@ class HTMLElementConsent extends Page.create(HTMLFormElement) {
13
13
  return !tacit;
14
14
  }
15
15
  setup(state) {
16
- if (this.isContentEditable) return;
16
+ if (state.scope.$write) return;
17
17
  if (this.options.transient) {
18
18
  const tmpl = this.ownTpl.prerender();
19
19
  if (tmpl.content && tmpl.children.length == 0) {
@@ -30,7 +30,7 @@ class HTMLElementConsent extends Page.create(HTMLFormElement) {
30
30
  }
31
31
  handleSubmit(e, state) {
32
32
  if (e.type == "submit") e.preventDefault();
33
- if (this.isContentEditable) return;
33
+ if (state.scope.$write) return;
34
34
  const fd = window.HTMLElementForm.prototype.read.call(this);
35
35
  const consent = fd.consent;
36
36
  if (consent == null) {
@@ -44,7 +44,7 @@ class HTMLElementConsent extends Page.create(HTMLFormElement) {
44
44
  this.handleSubmit(e, state);
45
45
  }
46
46
  patch(state) {
47
- if (this.isContentEditable) return;
47
+ if (state.scope.$write) return;
48
48
  if (this.options.transient) {
49
49
  this.ownTpl.prerender();
50
50
  }
package/ui/embed.js CHANGED
@@ -9,7 +9,7 @@ class HTMLElementEmbed extends Page.Element {
9
9
  reveal(state) {
10
10
  this.classList.add('waiting');
11
11
  state.consent(this);
12
- return this.#defer;
12
+ // return this.#defer; // do not hang the chain
13
13
  }
14
14
  consent(state) {
15
15
  const consent = state.scope.$consent;
@@ -83,10 +83,10 @@ class HTMLElementFieldsetList extends Page.Element {
83
83
  this.#model = model;
84
84
  }
85
85
 
86
- #prepare() {
86
+ #prepare(scope) {
87
87
  const tpl = this.ownTpl;
88
88
  tpl.prerender();
89
- if (this.isContentEditable) {
89
+ if (scope.$write) {
90
90
  this.#modelize(tpl);
91
91
  return;
92
92
  }
@@ -96,13 +96,13 @@ class HTMLElementFieldsetList extends Page.Element {
96
96
  }
97
97
  }
98
98
 
99
- patch(state) {
100
- this.#prepare();
101
- if (!this.#size) this.#resize(0, state.scope);
99
+ patch({ scope }) {
100
+ this.#prepare(scope);
101
+ if (!this.#size) this.#resize(0, scope);
102
102
  }
103
103
 
104
- setup() {
105
- this.#prepare();
104
+ setup({ scope }) {
105
+ this.#prepare(scope);
106
106
  }
107
107
 
108
108
  #selector(name) {
@@ -110,7 +110,7 @@ class HTMLElementFieldsetList extends Page.Element {
110
110
  }
111
111
 
112
112
  #resize(size, scope) {
113
- if (this.isContentEditable) return;
113
+ if (scope.$write) return;
114
114
  const len = Math.max(Number(this.dataset.size) || 0, size);
115
115
  if (this.#size === len) return;
116
116
  this.#size = len;
@@ -205,7 +205,7 @@ class HTMLElementFieldsetList extends Page.Element {
205
205
  }
206
206
 
207
207
  handleClick(e, state) {
208
- if (this.isContentEditable) return;
208
+ if (state.scope.$write) return;
209
209
  const btn = e.target.closest('button');
210
210
  if (!btn) return;
211
211
  const action = btn.value;
package/ui/fieldset.js CHANGED
@@ -4,8 +4,8 @@ class HTMLElementFieldSet extends Page.create(HTMLFieldSetElement) {
4
4
  dataValue: null
5
5
  };
6
6
 
7
- fill(query) {
8
- if (this.isContentEditable || !this.options?.name || !this.form) return;
7
+ fill(query, scope) {
8
+ if (scope.$write || !this.options?.name || !this.form) return;
9
9
  if (!query) query = this.form.read(true);
10
10
  const val = query[this.options.name];
11
11
  const disabled = this.disabled = this.hidden = val != this.options.value;
@@ -16,8 +16,8 @@ class HTMLElementFieldSet extends Page.create(HTMLFieldSetElement) {
16
16
 
17
17
  patch(state) {
18
18
  // before/after form#fill
19
- this.fill();
20
- state.finish(() => this.fill());
19
+ this.fill(null, state.scope);
20
+ state.finish(() => this.fill(null, state.scope));
21
21
  }
22
22
  setup() {
23
23
  this.form?.addEventListener('change', this);
@@ -25,9 +25,9 @@ class HTMLElementFieldSet extends Page.create(HTMLFieldSetElement) {
25
25
  close() {
26
26
  this.form?.removeEventListener('change', this);
27
27
  }
28
- handleEvent(e) {
28
+ handleEvent(e, state) {
29
29
  if (e.type == "change") {
30
- this.fill();
30
+ this.fill(null, state.scope);
31
31
  }
32
32
  }
33
33
  }
package/ui/form.js CHANGED
@@ -96,7 +96,7 @@ class HTMLElementForm extends Page.create(HTMLFormElement) {
96
96
  return window.HTMLElementTemplate.prototype.getRedirect.call(this, status, this);
97
97
  }
98
98
  patch(state) {
99
- if (this.isContentEditable) return;
99
+ if (state.scope.$write) return;
100
100
  if (this.method != "get") {
101
101
  // ?submit=<name> for auto-submit
102
102
  const name = state.query.submit;
@@ -220,7 +220,7 @@ class HTMLElementForm extends Page.create(HTMLFormElement) {
220
220
  }
221
221
  }
222
222
  for (const node of this.querySelectorAll('fieldset[is="element-fieldset"]')) {
223
- node.fill(query);
223
+ node.fill(query, scope);
224
224
  }
225
225
  return vars;
226
226
  }
@@ -268,7 +268,7 @@ class HTMLElementForm extends Page.create(HTMLFormElement) {
268
268
  }
269
269
  handleSubmit(e, state) {
270
270
  if (e.type == "submit") e.preventDefault();
271
- if (this.isContentEditable) return;
271
+ if (state.scope.$write) return;
272
272
  this.toggleMessages();
273
273
  if (this.matches('.loading')) return;
274
274
  if (e.type != "submit" && this.querySelector('[type="submit"]')) return;
@@ -299,17 +299,18 @@ class HTMLElementForm extends Page.create(HTMLFormElement) {
299
299
  if (loc.samePathname(state)) {
300
300
  loc.query = { ...state.query, ...loc.query };
301
301
  }
302
- let status = 200;
303
- const p = this.ignoreInputChange
302
+ const nstate = this.ignoreInputChange
304
303
  ? state.replace(loc)
305
304
  : state.push(loc);
306
- return p.catch(err => {
307
- if (err.status != null) status = err.status;
308
- else status = 0;
309
- }).then(() => {
305
+ nstate.catch(state => {
306
+ const status = state.error?.status ?? 0;
310
307
  this.toggleMessages(status);
311
308
  });
312
309
  }
310
+ catch(state) {
311
+ const status = state.error?.status ?? 0;
312
+ this.toggleMessages(status);
313
+ }
313
314
  async postMethod(e, state) {
314
315
  if (e.type != "submit") return;
315
316
  const form = this;
@@ -365,7 +366,7 @@ class HTMLElementForm extends Page.create(HTMLFormElement) {
365
366
  }
366
367
  scope.$vary = vary;
367
368
  }
368
- return state.push(loc, { vary });
369
+ state.push(loc, { vary });
369
370
  }
370
371
  }
371
372
  window.HTMLElementForm = HTMLElementForm;
package/ui/image.js CHANGED
@@ -7,7 +7,7 @@ const HTMLElementImageConstructor = Superclass => class extends Superclass {
7
7
  static defaultWidth = 240;
8
8
  static defaultHeight = 180;
9
9
 
10
- #defer = new Deferred();
10
+ #defer;
11
11
 
12
12
  static getZoom({ w, h, rw, rh, fit }) {
13
13
  let z = 100;
@@ -93,19 +93,17 @@ const HTMLElementImageConstructor = Superclass => class extends Superclass {
93
93
  if (this.currentSrc != this.options.src) {
94
94
  this.classList.remove('error');
95
95
  }
96
- this.dataset.width = this.constructor.defaultWidth || "";
97
- this.dataset.height = this.constructor.defaultHeight || "";
98
- if (this.options.src) {
99
- const loc = Page.parse(this.options.src);
100
- const meta = state.scope.$hrefs?.[loc.pathname];
101
- if (meta) {
102
- this.dataset.width = meta.width;
103
- this.dataset.height = meta.height;
104
- }
96
+ const d = this.dataset;
97
+ if (d.width == null || d.height == null) {
98
+ const meta = state.scope.$hrefs?.[this.options.src];
99
+ if (meta?.width) d.width = meta.width;
100
+ if (meta?.height) d.height = meta.height;
105
101
  }
102
+ d.width ??= this.constructor.defaultWidth || "";
103
+ d.height ??= this.constructor.defaultHeight || "";
106
104
  const { w, h } = this.dimensions;
107
- if (w) this.image.width = w || this.dataset.width;
108
- if (h) this.image.height = h || this.dataset.height;
105
+ if (w) this.image.width = w || d.width;
106
+ if (h) this.image.height = h || d.height;
109
107
  if (!this.currentSrc) {
110
108
  this.placeholder();
111
109
  }
@@ -123,6 +121,9 @@ const HTMLElementImageConstructor = Superclass => class extends Superclass {
123
121
  this.placeholder(true);
124
122
  return;
125
123
  }
124
+ if (this.classList.contains('loading')) {
125
+ return;
126
+ }
126
127
  const fit = this.fit;
127
128
  const r = this.crop;
128
129
 
@@ -154,16 +155,17 @@ const HTMLElementImageConstructor = Superclass => class extends Superclass {
154
155
  const rh = rect.height;
155
156
  if (rw == 0 && rh == 0) {
156
157
  // don't show
157
- return this.#defer;
158
+ return;
158
159
  }
159
160
  loc.query.rs = "z-" + this.constructor.getZoom({ w, h, rw, rh, fit });
160
161
  }
161
162
  const curSrc = loc.toString();
162
163
  if (curSrc != this.currentSrc) {
163
164
  this.classList.add('loading');
165
+ this.#defer = new Deferred();
164
166
  img.setAttribute('src', curSrc);
167
+ return this.#defer;
165
168
  }
166
- return this.#defer;
167
169
  }
168
170
  captureLoad() {
169
171
  this.#defer.resolve();
package/ui/input-range.js CHANGED
@@ -82,7 +82,7 @@ class HTMLElementInputRange extends Page.create(HTMLInputElement) {
82
82
  start: opts.multiple ? [null, null] : [null],
83
83
  connect: true
84
84
  }, this.convertOptions(opts))).on('change', (values) => {
85
- if (this.isContentEditable) return;
85
+ if (state.scope.$write) return;
86
86
  const isInt = parseInt(opts.step) == opts.step;
87
87
  helper.classList.remove('indeterminate');
88
88
  if (isInt) values = values.map(n => parseInt(n));
@@ -96,8 +96,8 @@ class HTMLElementInputRange extends Page.create(HTMLInputElement) {
96
96
  helper.addEventListener('dblclick', this, true);
97
97
  this.patch(state);
98
98
  }
99
- handleEvent(e) {
100
- if (this.isContentEditable) return;
99
+ handleEvent(e, state) {
100
+ if (state.scope.$write) return;
101
101
  if (e.type == "dblclick" || e.keyCode == 8 || e.keyCode == 46) {
102
102
  this.fill();
103
103
  this.dispatchEvent(new Event('change', {
package/ui/layout.js CHANGED
@@ -8,17 +8,11 @@ class HTMLElementLayout extends Page.create(HTMLDivElement) {
8
8
  dataPosition:null
9
9
  };
10
10
 
11
+ #defer;
12
+
11
13
  get fit() {
12
14
  return this.options.size || 'none';
13
15
  }
14
- patch(state) {
15
- if (!this.options.src) return;
16
- const loc = Page.parse(this.options.src);
17
- const meta = state.scope.$hrefs?.[loc.pathname] ?? {};
18
- if (!meta || !meta.width || !meta.height) return;
19
- this.dataset.width = meta.width;
20
- this.dataset.height = meta.height;
21
- }
22
16
  reveal(state) {
23
17
  this.style.backgroundRepeat = this.options.repeat;
24
18
  this.style.backgroundSize = this.options.size;
@@ -71,11 +65,14 @@ class HTMLElementLayout extends Page.create(HTMLDivElement) {
71
65
  // pass
72
66
  }
73
67
  this.style.backgroundImage = `url(${curSrc})`;
68
+ this.#defer = new Deferred();
69
+ const img = new Image();
70
+ img.addEventListener('load', this.#defer.resolve);
71
+ img.addEventListener('error', this.#defer.resolve);
72
+ img.src = curSrc;
73
+ return this.#defer;
74
74
  }
75
75
  }
76
- captureLoad() {}
77
- captureError() {}
78
- placeholder() {}
79
76
  }
80
77
 
81
78
  (function(HTMLElementImage) {
package/ui/media.js CHANGED
@@ -3,11 +3,6 @@ const HTMLElementMediaConstructor = Superclass => class extends Superclass {
3
3
 
4
4
  patch(state) {
5
5
  this.classList.remove('error', 'loading');
6
- const loc = Page.parse(this.options.src);
7
- const meta = state.scope.$hrefs?.[loc.pathname] ?? {};
8
- if (!meta || !meta.width || !meta.height) return;
9
- this.width = meta.width;
10
- this.height = meta.height;
11
6
  }
12
7
  reveal(state) {
13
8
  const curSrc = this.options.src;
@@ -19,11 +14,11 @@ const HTMLElementMediaConstructor = Superclass => class extends Superclass {
19
14
  }
20
15
  this.setAttribute('src', curSrc);
21
16
  }
22
- if (this.isContentEditable) this.pause();
23
- return this.#defer;
17
+ if (state.scope.$write) this.pause();
18
+ // return this.#defer; // do not hang chain
24
19
  }
25
- handleClick(e) {
26
- if (this.isContentEditable) e.preventDefault();
20
+ handleClick(e, state) {
21
+ if (state.scope.$write) e.preventDefault();
27
22
  }
28
23
  captureLoad() {
29
24
  this.#defer.resolve();
package/ui/menu.js CHANGED
@@ -24,7 +24,7 @@ class HTMLElementMenu extends Page.Element {
24
24
  });
25
25
  }
26
26
  setup(state) {
27
- if (this.isContentEditable || this.matches('.vertical')) return;
27
+ if (state.scope.$write || this.matches('.vertical')) return;
28
28
  const menu = this.firstElementChild;
29
29
  const helper = this.lastElementChild;
30
30
  if (helper == menu) {
package/ui/pagination.js CHANGED
@@ -1,6 +1,6 @@
1
1
  class HTMLElementPagination extends Page.create(HTMLAnchorElement) {
2
2
  patch(state) {
3
- if (this.isContentEditable) return;
3
+ if (state.scope.$write) return;
4
4
  state.finish(() => {
5
5
  this.#update(state);
6
6
  });
package/ui/query-tags.js CHANGED
@@ -27,7 +27,7 @@ class HTMLElementQueryTags extends Page.Element {
27
27
  const query = state.query;
28
28
  state.finish(() => {
29
29
  this.labels.textContent = '';
30
- if (this.isContentEditable) this.insertLabel('', '', 'Auto');
30
+ if (state.scope.$write) this.insertLabel('', '', 'Auto');
31
31
  else for (const [name, val] of Object.entries(query)) {
32
32
  this.add(name, val);
33
33
  }
package/ui/scroll-link.js CHANGED
@@ -4,7 +4,7 @@ class HTMLScrollLinkElement extends Page.create(HTMLAnchorElement) {
4
4
  };
5
5
 
6
6
  handleClick(e, state) {
7
- if (this.isContentEditable) return;
7
+ if (state.scope.$write) return;
8
8
  if (this.options.to == "home") {
9
9
  const main = document.querySelector('body > main') || document.body;
10
10
  main.scrollIntoView({
package/ui/select.js CHANGED
@@ -24,7 +24,7 @@ class HTMLElementSelect extends Page.Element {
24
24
  }
25
25
 
26
26
  handleClick(e, state) {
27
- if (this.isContentEditable) return;
27
+ if (state.scope.$write) return;
28
28
  const node = e.target;
29
29
  const item = node.closest('element-select .item');
30
30
  if (item) {
@@ -143,7 +143,7 @@ class HTMLElementSelect extends Page.Element {
143
143
  }
144
144
 
145
145
  build(state) {
146
- if (this.isContentEditable) return;
146
+ if (state.scope.$write) return;
147
147
  if (this.children.length == 1) {
148
148
  this.insertAdjacentHTML(
149
149
  'afterBegin',
@@ -169,7 +169,7 @@ class HTMLElementSelect extends Page.Element {
169
169
  }
170
170
 
171
171
  patch(state) {
172
- if (this.isContentEditable) return;
172
+ if (this.state.scope.$write) return;
173
173
  if (this.children.length == 1) this.build(state);
174
174
 
175
175
  state.finish(() => {
package/ui/sitemap.css CHANGED
@@ -2,6 +2,7 @@
2
2
  min-height:2em;
3
3
  position:relative;
4
4
  display: block;
5
+ width: 100%;
5
6
  }
6
7
  [block-type="sitemap"] .caret-icon::before {
7
8
  right:auto;
package/ui/sitemap.js CHANGED
@@ -53,7 +53,7 @@ class HTMLElementSitemap extends Page.Element {
53
53
  }
54
54
 
55
55
  async build(state) {
56
- if (this.firstElementChild.children.length > 0 && this.isContentEditable) {
56
+ if (this.firstElementChild.children.length > 0 && state.scope.$write) {
57
57
  // workaround... build is called a second time with pagecut-placeholder set
58
58
  return;
59
59
  }
package/ui/transition.css CHANGED
@@ -1,47 +1,45 @@
1
1
  /* async progress bar and error signal */
2
2
 
3
- body::after {
3
+ body::before {
4
4
  content: "";
5
5
  position:fixed;
6
6
  display:block;
7
7
  left:0;
8
- top:1px;
8
+ top:0px;
9
9
  width:0%;
10
10
  height:0;
11
11
  opacity:0.7;
12
12
  background: #4486cc;
13
13
  box-shadow: 0 0 5px rgb(99 162 235 / 70%);
14
- z-index:1000;
14
+ z-index:10000000;
15
15
  }
16
- html[data-stage] > body::after {
16
+ html[data-stage] > body::before {
17
17
  transition-property:width;
18
- transition-duration:1s;
18
+ transition-duration:0.5s;
19
19
  transition-timing-function:linear;
20
20
  }
21
- html[data-stage="route"] > body::after,
22
- html[data-stage="ready"] > body::after,
23
- html[data-stage="setup"] > body::after,
24
- html[data-stage="error"] > body::after {
21
+ html[data-stage="route"] > body::before,
22
+ html[data-stage="ready"] > body::before,
23
+ html[data-stage="paint"] > body::before,
24
+ html[data-stage="error"] > body::before {
25
25
  height:2px;
26
26
  }
27
27
 
28
- html[data-stage="route"] > body::after {
28
+ html[data-stage="route"] > body::before {
29
+ width:25%;
30
+ }
31
+ html[data-stage="ready"] > body::before {
29
32
  width:50%;
30
33
  }
31
- html[data-stage="ready"] > body::after,
32
- html[data-stage="setup"] > body::after {
34
+ html[data-stage="paint"] > body::before {
33
35
  width:100%;
34
36
  }
35
- html[data-stage="error"] > body::after {
37
+ html[data-stage="error"] > body::before {
36
38
  display:block;
37
39
  width:100%;
38
40
  background:#c33;
39
41
  }
40
42
 
41
- html.transition > body::after {
42
- display:none !important;
43
- }
44
-
45
43
  /* transitions */
46
44
  html.transition {
47
45
  width:100%;
package/ui/transition.js CHANGED
@@ -9,15 +9,15 @@ Page.route(state => {
9
9
  const root = document.documentElement;
10
10
  function dtr(state) {
11
11
  root.dataset.stage = state.stage;
12
- if (state.stage == "setup" || state.stage == "patch") {
13
- setTimeout(() => root.removeAttribute('data-stage'), 500);
12
+ if (state.stage == "paint") {
13
+ setTimeout(() => root.removeAttribute('data-stage'), 700);
14
14
  }
15
15
  }
16
16
  dtr(state);
17
- Page.ready(dtr);
18
- Page.patch(dtr);
19
- Page.setup(dtr);
20
- Page.catch(dtr);
17
+ state.ready(dtr);
18
+ state.patch(dtr);
19
+ state.paint(dtr);
20
+ state.catch(dtr);
21
21
  });
22
22
 
23
23
  class Transition {
@@ -131,7 +131,7 @@ Page.constructor.prototype.mergeBody = function (body, corpse) {
131
131
  if (this.referrer.scope.transition) {
132
132
  this.referrer.scope.transition.end();
133
133
  }
134
- if (body.isContentEditable || body.getAttribute('block-type') != corpse.getAttribute('block-type')) {
134
+ if (this.scope.$write || body.getAttribute('block-type') != corpse.getAttribute('block-type')) {
135
135
  corpse.replaceWith(body);
136
136
  } else {
137
137
  this.scope.transition = new Transition(this, body, corpse);