@pageboard/html 0.11.27 → 0.11.28

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.28",
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
+ this.image.width = w || this.dataset.width;
110
+ 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
 
@@ -195,40 +196,29 @@ class HTMLElementInlineImage extends HTMLImageElement {
195
196
  dataCrop: null
196
197
  };
197
198
 
199
+ static defaultWidth = 40;
200
+ static defaultHeight = 30;
201
+
198
202
  get image() {
199
203
  return this;
200
204
  }
205
+
206
+ placeholder(error) {
207
+ if (error) {
208
+ this.image.src = "data:image/svg+xml," + encodeURIComponent(
209
+ `<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>`);
210
+ } else {
211
+ this.image.removeAttribute('src');
212
+ }
213
+ }
214
+
201
215
  get currentSrc() {
202
216
  const cur = super.currentSrc;
203
- if (!cur && this.src?.startsWith('data:')) return this.src;
217
+ if (!cur && this.image.src?.startsWith('data:')) return this.src;
204
218
  else return cur;
205
219
  }
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
220
  }
232
221
 
222
+ VirtualHTMLElement.inherits(HTMLElementInlineImage, HTMLElementImage);
233
223
  VirtualHTMLElement.define('element-image', HTMLElementImage);
234
224
  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