@pageboard/html 0.11.27 → 0.11.29

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/list.js CHANGED
@@ -1,18 +1,20 @@
1
1
  exports.list_item = {
2
2
  title: 'Item',
3
3
  inplace: true,
4
- contents: "textblock list?",
4
+ contents: "textblock (ul|ol)?",
5
5
  icon: '<i class="list icon"></i>',
6
6
  html: `<li></li>`,
7
7
  };
8
+
8
9
  exports.textblock = {
9
10
  title: 'Text',
10
11
  inplace: true,
11
12
  contents: "inline*",
12
13
  html: '<span class="textblock"></span>'
13
14
  };
14
- exports.list = {
15
- title: 'List',
15
+
16
+ exports.ul = {
17
+ title: 'Unordered List',
16
18
  properties: {
17
19
  marker: {
18
20
  title: 'Marker',
@@ -25,6 +27,36 @@ exports.list = {
25
27
  }, {
26
28
  const: 'square',
27
29
  title: 'Square'
30
+ }, {
31
+ const: 'circle',
32
+ title: 'Circle'
33
+ }]
34
+ }
35
+ },
36
+ inplace: true,
37
+ contents: "list_item+",
38
+ group: "block",
39
+ icon: '<i class="list ul icon"></i>',
40
+ tag: 'ul',
41
+ parse: function(dom) {
42
+ let marker = null;
43
+ const style = dom.style.listStyleType;
44
+ if (style && this.properties.marker.anyOf.some(item => item.const == style)) {
45
+ marker = style;
46
+ }
47
+ return { marker };
48
+ },
49
+ html: `<ul style-list-style-type="[marker]"></ul>`
50
+ };
51
+
52
+ exports.ol = {
53
+ title: 'Ordered List',
54
+ properties: {
55
+ marker: {
56
+ title: 'Marker',
57
+ anyOf: [{
58
+ const: null,
59
+ title: 'Default'
28
60
  }, {
29
61
  const: 'decimal',
30
62
  title: '1, 2, 3'
@@ -65,20 +97,15 @@ exports.list = {
65
97
  contents: "list_item+",
66
98
  group: "block",
67
99
  icon: '<i class="list ol icon"></i>',
68
- tag: 'ul,ol',
69
- parse: function(dom) {
100
+ tag: 'ol',
101
+ parse: function (dom) {
70
102
  let marker = null;
71
103
  const style = dom.style.listStyleType;
72
104
  if (style && this.properties.marker.anyOf.some(item => item.const == style)) {
73
105
  marker = style;
74
- } else if (dom.nodeName == "OL") {
75
- marker = 'decimal';
76
106
  }
77
- return { marker };
107
+ const start = dom.getAttribute('start') || null;
108
+ return { marker, start };
78
109
  },
79
- html: `
80
- <ul style-list-style-type="[marker]">[marker|eq:square:|eq:disc:|!|bmagnet:*]</ul>
81
- <ol style-list-style-type="[marker]" start="[start]">[marker|eq:square:|eq:disc:|bmagnet:*]</ol>
82
- `
110
+ html: `<ol style-list-style-type="[marker]" start="[start]"></ol>`
83
111
  };
84
-
@@ -0,0 +1,176 @@
1
+ exports.time = {
2
+ title: "Date Time",
3
+ icon: '<i class="clock outline icon"></i>',
4
+ inline: true,
5
+ inplace: true,
6
+ group: "inline nolink",
7
+ properties: {
8
+ datetime: {
9
+ title: 'Date and time',
10
+ type: 'string',
11
+ format: 'date-time',
12
+ nullable: true
13
+ },
14
+ timezone: {
15
+ title: ' Time zone',
16
+ type: 'string',
17
+ pattern: /\w+\/\w+/.source,
18
+ nullable: true,
19
+ $filter: {
20
+ name: 'intl',
21
+ of: 'timeZone'
22
+ }
23
+ },
24
+ format: {
25
+ title: 'Format',
26
+ type: 'object',
27
+ properties: {
28
+ weekday: {
29
+ title: 'Weekday',
30
+ anyOf: [
31
+ {
32
+ const: null,
33
+ title: 'none'
34
+ }, {
35
+ const: 'd',
36
+ title: 'short'
37
+ }, {
38
+ const: 'day',
39
+ title: 'long'
40
+ }
41
+ ]
42
+ },
43
+ day: {
44
+ title: 'Day of month',
45
+ anyOf: [
46
+ {
47
+ const: null,
48
+ title: 'none'
49
+ }, {
50
+ const: 'D',
51
+ title: 'numeric'
52
+ }, {
53
+ const: 'DD',
54
+ title: 'two-digits'
55
+ }
56
+ ]
57
+ },
58
+ month: {
59
+ title: 'Month',
60
+ anyOf: [
61
+ {
62
+ const: null,
63
+ title: 'none'
64
+ }, {
65
+ const: 'mon',
66
+ title: 'short'
67
+ }, {
68
+ const: 'month',
69
+ title: 'long'
70
+ }, {
71
+ const: 'M',
72
+ title: 'numeric'
73
+ }, {
74
+ const: 'MM',
75
+ title: 'two-digits'
76
+ }
77
+ ]
78
+ },
79
+ year: {
80
+ title: 'Year',
81
+ anyOf: [
82
+ {
83
+ const: null,
84
+ title: 'none'
85
+ }, {
86
+ const: 'Y',
87
+ title: 'numeric'
88
+ }, {
89
+ const: 'YY',
90
+ title: 'two-digits'
91
+ }
92
+ ]
93
+ },
94
+ hour: {
95
+ title: 'Hour',
96
+ anyOf: [
97
+ {
98
+ const: null,
99
+ title: 'none'
100
+ }, {
101
+ const: 'H',
102
+ title: 'numeric'
103
+ }, {
104
+ const: 'HH',
105
+ title: 'two-digits'
106
+ }
107
+ ]
108
+ },
109
+ minute: {
110
+ title: 'Minute',
111
+ anyOf: [
112
+ {
113
+ const: null,
114
+ title: 'none'
115
+ }, {
116
+ const: 'm',
117
+ title: 'numeric'
118
+ }, {
119
+ const: 'mm',
120
+ title: 'two-digits'
121
+ }
122
+ ]
123
+ },
124
+ second: {
125
+ title: 'Second',
126
+ anyOf: [
127
+ {
128
+ const: null,
129
+ title: 'none'
130
+ }, {
131
+ const: 's',
132
+ title: 'numeric'
133
+ }, {
134
+ const: 'ss',
135
+ title: 'two-digits'
136
+ }
137
+ ]
138
+ },
139
+ timeZoneName: {
140
+ title: 'Timezone',
141
+ anyOf: [
142
+ {
143
+ const: null,
144
+ title: 'none'
145
+ }, {
146
+ const: 'tz',
147
+ title: 'offset'
148
+ }, {
149
+ const: 'timezone',
150
+ title: 'short'
151
+ }
152
+ ]
153
+ }
154
+ }
155
+ }
156
+ },
157
+ parse: function (dom) {
158
+ const format = {};
159
+ const list = (dom.dataset.format ?? "").split(':');
160
+ for (const [key, schema] of Object.entries(this.properties.format.properties)) {
161
+ for (const tok in list) {
162
+ if (tok) {
163
+ const item = schema.anyOf.find(item => item.const === tok);
164
+ if (item) format[key] = item.const;
165
+ }
166
+ }
167
+ }
168
+ return {
169
+ datetime: dom.dateTime,
170
+ timezone: dom.dataset.timezone,
171
+ format
172
+ };
173
+ },
174
+ html: `<time datetime="[datetime|now|isoDate]" data-format="[format|values|join:%3A]" data-timezone="[timezone]" is="element-time"></time>`,
175
+ scripts: ['../ui/time.js']
176
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pageboard/html",
3
- "version": "0.11.27",
3
+ "version": "0.11.29",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "repository": {
package/ui/consent.js CHANGED
@@ -40,7 +40,7 @@ class HTMLCustomConsentElement extends HTMLFormElement {
40
40
  if (consent == null) {
41
41
  return;
42
42
  }
43
- Page.storage.set('consent', consent);
43
+ state.scope.storage.set('consent', consent);
44
44
  state.scope.$consent = consent;
45
45
  state.runChain('consent');
46
46
  }
@@ -67,9 +67,9 @@ Page.ready(() => {
67
67
  VirtualHTMLElement.define(`element-consent`, HTMLCustomConsentElement, 'form');
68
68
  });
69
69
 
70
- Page.State.prototype.consent = function (fn) {
70
+ Page.constructor.prototype.consent = function (fn) {
71
71
  const initial = this.scope.$consent === undefined;
72
- let consent = Page.storage.get('consent');
72
+ let consent = this.scope.storage.get('consent');
73
73
  if (consent == null && initial) consent = undefined;
74
74
  this.scope.$consent = consent;
75
75
  this.chain('consent', fn);
@@ -81,7 +81,7 @@ Page.State.prototype.consent = function (fn) {
81
81
  }
82
82
  };
83
83
 
84
- Page.State.prototype.reconsent = function (fn) {
84
+ Page.constructor.prototype.reconsent = function (fn) {
85
85
  if (fn) this.consent(fn);
86
86
  const consent = this.scope.$consent;
87
87
  let asking = false;
package/ui/image.css CHANGED
@@ -139,3 +139,7 @@ img[is="element-img"] {
139
139
  max-width: 100%;
140
140
  height:auto;
141
141
  }
142
+
143
+ element-image[block-focused="last"] > [block-content="legend"] {
144
+ background-color: rgb(255 255 255 / 50%);
145
+ }
package/ui/image.js CHANGED
@@ -4,6 +4,9 @@ class HTMLElementImage extends VirtualHTMLElement {
4
4
  crop: null
5
5
  };
6
6
 
7
+ static defaultWidth = 240;
8
+ static defaultHeight = 180;
9
+
7
10
  static getZoom({ w, h, rw, rh, fit }) {
8
11
  let z = 100;
9
12
  if (!rw && !rh) return z;
@@ -89,24 +92,31 @@ class HTMLElementImage extends VirtualHTMLElement {
89
92
  }
90
93
  patch(state) {
91
94
  this.classList.remove('loading');
92
- if (!this.options.src) return;
93
95
  if (this.currentSrc != this.options.src) {
94
96
  this.classList.remove('error');
95
97
  }
96
- const loc = Page.parse(this.options.src);
97
- const meta = state.scope.$hrefs?.[loc.pathname] ?? {};
98
- if (!meta || !meta.width || !meta.height) return;
99
- this.dataset.width = meta.width;
100
- this.dataset.height = meta.height;
98
+ this.dataset.width = this.constructor.defaultWidth || "";
99
+ this.dataset.height = this.constructor.defaultHeight || "";
100
+ if (this.options.src) {
101
+ const loc = Page.parse(this.options.src);
102
+ const meta = state.scope.$hrefs?.[loc.pathname];
103
+ if (meta) {
104
+ this.dataset.width = meta.width;
105
+ this.dataset.height = meta.height;
106
+ }
107
+ }
108
+ const { w, h } = this.dimensions;
109
+ if (w) this.image.width = w || this.dataset.width;
110
+ if (h) this.image.height = h || this.dataset.height;
101
111
  if (!this.currentSrc) {
102
112
  this.placeholder();
103
113
  }
104
114
  }
105
115
 
106
116
  get currentSrc() {
107
- const cur = super.currentSrc;
108
- if (!cur && this.src?.startsWith('data:')) return this.src;
109
- else return cur;
117
+ const src = this.image?.currentSrc;
118
+ if (src?.startsWith('data:image/svg')) return null;
119
+ else return src;
110
120
  }
111
121
 
112
122
  reveal(state) {
@@ -152,11 +162,6 @@ class HTMLElementImage extends VirtualHTMLElement {
152
162
  }
153
163
  const curSrc = loc.toString();
154
164
  if (curSrc != this.currentSrc) {
155
- try {
156
- this.currentSrc = curSrc;
157
- } catch (e) {
158
- // pass
159
- }
160
165
  this.classList.add('loading');
161
166
  let done;
162
167
  this.promise = new Promise(resolve => done = resolve);
@@ -174,14 +179,10 @@ class HTMLElementImage extends VirtualHTMLElement {
174
179
  this.promise?.done();
175
180
  this.classList.remove('loading');
176
181
  this.classList.add('error');
177
- this.placeholder();
182
+ this.placeholder(true);
178
183
  }
179
184
  placeholder(error) {
180
- let { w, h } = this.dimensions;
181
- if (Number.isNaN(w)) w = 320;
182
- if (Number.isNaN(h)) h = 240;
183
- this.image.src = "data:image/svg+xml," + encodeURIComponent(
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>`);
185
+ this.image.removeAttribute('src');
185
186
  }
186
187
  }
187
188
 
@@ -198,37 +199,23 @@ class HTMLElementInlineImage extends HTMLImageElement {
198
199
  get image() {
199
200
  return this;
200
201
  }
202
+
203
+ placeholder(error) {
204
+ if (error) {
205
+ this.image.src = "data:image/svg+xml," + encodeURIComponent(
206
+ `<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 ${this.image.width} ${this.image.height}"><text text-anchor="middle" dominant-baseline="central" x="50%" y="50%" fill="#aaa">${error ? '∅' : ''}</text></svg>`);
207
+ } else {
208
+ this.image.removeAttribute('src');
209
+ }
210
+ }
211
+
201
212
  get currentSrc() {
202
213
  const cur = super.currentSrc;
203
- if (!cur && this.src?.startsWith('data:')) return this.src;
214
+ if (!cur && this.image.src?.startsWith('data:')) return this.src;
204
215
  else return cur;
205
216
  }
206
- captureLoad() {
207
- this.promise?.done();
208
- this.classList.remove('loading');
209
- this.fix(this.image);
210
- }
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>`));
222
- }
223
- }
224
-
225
- for (const name of ['patch', 'reveal', 'captureError', 'crop', 'dimensions', 'position', 'fit', 'findClass', 'fix']) {
226
- Object.defineProperty(
227
- HTMLElementInlineImage.prototype,
228
- name,
229
- Object.getOwnPropertyDescriptor(HTMLElementImage.prototype, name)
230
- );
231
217
  }
232
218
 
219
+ VirtualHTMLElement.inherits(HTMLElementInlineImage, HTMLElementImage);
233
220
  VirtualHTMLElement.define('element-image', HTMLElementImage);
234
221
  VirtualHTMLElement.define(`element-img`, HTMLElementInlineImage, 'img');
package/ui/storage.js CHANGED
@@ -68,5 +68,5 @@ class UserStore {
68
68
  }
69
69
  }
70
70
 
71
- Page.setup(() => Page.storage = new UserStore());
71
+ Page.setup(state => state.scope.storage = new UserStore());
72
72
 
package/ui/time.js ADDED
@@ -0,0 +1,22 @@
1
+ class HTMLElementTime extends HTMLTimeElement {
2
+ constructor() {
3
+ super();
4
+ if (this.init) this.init();
5
+ }
6
+
7
+ static defaults = {
8
+ dataFormat: null,
9
+ dataTimezone: null,
10
+ datetime: null
11
+ };
12
+
13
+ patch(state) {
14
+ this.textContent = `[stamp|formatDate:[fmt]:[tz]]`.fuse({
15
+ stamp: this.dateTime,
16
+ fmt: this.dataset.format,
17
+ tz: this.dataset.timezone
18
+ }, state.scope);
19
+ }
20
+ }
21
+
22
+ VirtualHTMLElement.define(`element-time`, HTMLElementTime, 'time');
package/ui/transition.js CHANGED
@@ -4,6 +4,7 @@ Object.defineProperty(document, 'body', {
4
4
  return this.documentElement.querySelector('body:last-of-type');
5
5
  }
6
6
  });
7
+
7
8
  Page.init(state => {
8
9
  const root = document.documentElement;
9
10
  function dtr(state) {
@@ -16,33 +17,10 @@ Page.init(state => {
16
17
  Page.ready(dtr);
17
18
  Page.patch(dtr);
18
19
  Page.setup(dtr);
19
- Page.error(dtr);
20
+ Page.catch(dtr);
20
21
  });
21
22
 
22
- Page.State.prototype.mergeBody = function(body, corpse) {
23
- if (this.referrer.transition) {
24
- this.referrer.transition.end();
25
- }
26
- if (body.isContentEditable || body.getAttribute('block-type') != corpse.getAttribute('block-type')) {
27
- corpse.replaceWith(body);
28
- } else {
29
- this.transition = new Page.Transition(this, body, corpse);
30
- }
31
- };
32
-
33
- Page.setup(state => {
34
- if (state.transition) {
35
- if (state.transition.ok) {
36
- state.finish(() => {
37
- return state.transition.start();
38
- });
39
- } else {
40
- state.transition.end();
41
- }
42
- }
43
- });
44
-
45
- Page.Transition = class {
23
+ class Transition {
46
24
  static event(name) {
47
25
  const low = name.toLowerCase();
48
26
  const caps = name[0].toUpperCase() + low.substring(1);
@@ -144,9 +122,32 @@ Page.Transition = class {
144
122
  this.root.scrollLeft = left;
145
123
  delete this.from;
146
124
  delete this.to;
147
- delete this.state.transition;
125
+ delete this.state.scope.transition;
148
126
  delete this.state;
149
127
  }
128
+ }
129
+
130
+ Page.constructor.prototype.mergeBody = function (body, corpse) {
131
+ if (this.referrer.scope.transition) {
132
+ this.referrer.scope.transition.end();
133
+ }
134
+ if (body.isContentEditable || body.getAttribute('block-type') != corpse.getAttribute('block-type')) {
135
+ corpse.replaceWith(body);
136
+ } else {
137
+ this.scope.transition = new Transition(this, body, corpse);
138
+ }
150
139
  };
151
140
 
141
+ Page.setup(state => {
142
+ const tr = state.scope.transition;
143
+ if (tr) {
144
+ if (tr.ok) {
145
+ state.finish(() => {
146
+ return tr.start();
147
+ });
148
+ } else {
149
+ tr.end();
150
+ }
151
+ }
152
+ });
152
153