@pageboard/html 0.14.37 → 0.15.0

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
@@ -38,7 +38,7 @@ exports.query_form = {
38
38
  title: 'Page',
39
39
  nullable: true,
40
40
  type: "string",
41
- format: "pathname",
41
+ format: "page",
42
42
  $helper: "page"
43
43
  },
44
44
  parameters: {
@@ -79,7 +79,7 @@ exports.api_form = {
79
79
  properties: {
80
80
  name: {
81
81
  title: 'Name',
82
- description: "Used by query 'submit' or 'toggle'\nExposes /.api/form/$name",
82
+ description: "Used by query 'submit' or 'toggle'\nExposes /@api/form/$name",
83
83
  type: 'string',
84
84
  format: 'id',
85
85
  nullable: true
@@ -101,6 +101,16 @@ exports.api_form = {
101
101
  description: 'Choose a service',
102
102
  $ref: '/writes'
103
103
  },
104
+ request: {
105
+ title: 'Map inputs',
106
+ type: 'object',
107
+ nullable: true
108
+ },
109
+ response: {
110
+ title: 'Map outputs',
111
+ type: 'object',
112
+ nullable: true
113
+ },
104
114
  redirection: {
105
115
  title: 'Success',
106
116
  type: 'object',
@@ -109,7 +119,7 @@ exports.api_form = {
109
119
  title: 'Page',
110
120
  nullable: true,
111
121
  type: "string",
112
- format: "pathname",
122
+ format: "page",
113
123
  $helper: "page"
114
124
  },
115
125
  parameters: {
@@ -160,7 +170,7 @@ exports.api_form = {
160
170
  contents: 'block+',
161
171
  tag: 'form[method="post"]',
162
172
  html: `<form is="element-form" method="post" name="[name]" masked="[masked]"
163
- action="/.api/form/[name|else:$id]"
173
+ action="/@api/form/[name|else:$id]"
164
174
  parameters="[$expr?.action?.parameters|as:expressions]"
165
175
  success="[redirection.url][redirection.parameters|as:query]"
166
176
  badrequest="[badrequest.url][badrequest.parameters|as:query]"
@@ -50,7 +50,7 @@ exports.input_property = {
50
50
  return node;
51
51
  }
52
52
  // list[0] = "data";
53
- // /.api/form wraps it into block.data
53
+ // /@api/form wraps it into block.data
54
54
  list.shift();
55
55
  name = list.join('.');
56
56
  const id = scope.$id;
@@ -133,6 +133,64 @@ exports.layout = {
133
133
  heightUnits: {
134
134
  $ref: "#/definitions/units/properties/data/properties/block"
135
135
  },
136
+ column: {
137
+ title: 'Column',
138
+ nullable: true,
139
+ properties: {
140
+ count: {
141
+ title: 'Count',
142
+ description: 'empty for auto',
143
+ type: 'integer',
144
+ nullable: true,
145
+ minimum: 1
146
+ },
147
+ width: {
148
+ title: 'Width',
149
+ properties: {
150
+ length: {
151
+ title: 'Length',
152
+ type: 'number',
153
+ default: 0,
154
+ minimum: 0,
155
+ multipleOf: 0.01,
156
+ nullable: true
157
+ },
158
+ unit: {
159
+ $ref: "#/definitions/units/properties/data/properties/inline"
160
+ }
161
+ }
162
+ },
163
+ fill: {
164
+ title: 'Fill',
165
+ anyOf: [{
166
+ type: 'null',
167
+ title: 'Auto'
168
+ }, {
169
+ const: 'balance',
170
+ title: 'Balance'
171
+ }, {
172
+ const: 'balance-all',
173
+ title: 'Balance All'
174
+ }]
175
+ },
176
+ gap: {
177
+ title: 'Gap',
178
+ properties: {
179
+ length: {
180
+ title: 'Length',
181
+ type: 'number',
182
+ default: 0,
183
+ minimum: 0,
184
+ multipleOf: 0.01,
185
+ nullable: true
186
+ },
187
+ unit: {
188
+ $ref: "#/definitions/units/properties/data/properties/inline"
189
+ }
190
+ }
191
+ }
192
+ }
193
+ },
136
194
  margins: {
137
195
  title: 'Margins',
138
196
  type: 'object',
@@ -391,6 +449,10 @@ exports.layout = {
391
449
  style-background-repeat="[background.repeat]"
392
450
  style-background-attachment="[background.attachment]"
393
451
  style-background-position="[background.position]"
452
+ style-column-gap="[column.gap.length|fail:][column.gap.unit]"
453
+ style-column-fill="[column.fill|or:auto]"
454
+ style-column-count="[column.count|or:auto]"
455
+ style-column-width="[column.width.length|fail:][column.width.unit]"
394
456
  ></div>`,
395
457
  stylesheets: [
396
458
  '../ui/components/container.css',
@@ -400,4 +462,3 @@ exports.layout = {
400
462
  '../ui/layout.js'
401
463
  ]
402
464
  };
403
-
package/elements/link.js CHANGED
@@ -29,7 +29,7 @@ exports.link = {
29
29
  nullable: true,
30
30
  $helper: {
31
31
  name: 'datalist',
32
- url: '/.api/languages'
32
+ url: '/@api/languages'
33
33
  }
34
34
  },
35
35
  id: {
@@ -75,7 +75,7 @@ exports.link_button = {
75
75
  nullable: true,
76
76
  $helper: {
77
77
  name: 'datalist',
78
- url: '/.api/languages'
78
+ url: '/@api/languages'
79
79
  }
80
80
  },
81
81
  full: {
@@ -108,7 +108,7 @@ exports.link_button = {
108
108
  default: null
109
109
  }
110
110
  },
111
- contents: "text*",
111
+ contents: "inline*",
112
112
  group: "block",
113
113
  html: '<a href="[url|lang:[lang]]" hreflang="[lang]" class="ui [full|alt:fluid:] [icon] [compact] [float|post:%20floated] button"></a>',
114
114
  stylesheets: [
package/elements/menu.js CHANGED
@@ -105,7 +105,7 @@ exports.menu_item_link = {
105
105
  nullable: true,
106
106
  $helper: {
107
107
  name: 'datalist',
108
- url: '/.api/languages'
108
+ url: '/@api/languages'
109
109
  }
110
110
  },
111
111
  labeled: {
@@ -20,7 +20,7 @@ exports.nav = {
20
20
  }
21
21
  },
22
22
  group: "block",
23
- html: `<a class="ui icon button [$links.[relation]|as:array|.first|alt::disabled]" href="[$links.[relation]|as:array|.first.url]" title="[$links.[relation]|as:array|.first.title]"><i class="icon [rel]"></i></a>`
23
+ html: `<a class="ui icon button [$links.[relation]|as:array|.first|alt::disabled]" href="[$links.[relation]|as:array|.first.url]" title="[$links.[relation]|as:array|.first.title]"><i class="icon [relation]"></i></a>`
24
24
  };
25
25
 
26
26
  exports.scrollLink = {
@@ -0,0 +1,21 @@
1
+ exports.textcard = {
2
+ title: 'Text Card',
3
+ group: 'block',
4
+ icon: '<i class="icons"><i class="small heading icon"></i><i class="bottom right corner text cursor icon"></i></i>',
5
+ contents: [{
6
+ id: 'title',
7
+ nodes: "inline*",
8
+ title: 'title'
9
+ }, {
10
+ id: 'content',
11
+ nodes: "inline*",
12
+ title: 'content'
13
+ }],
14
+ html: `<div class="ui textcard">
15
+ <div class="header" block-content="title"></div>
16
+ <div class="content" block-content="content"></div>
17
+ </div>`,
18
+ stylesheets: [
19
+ "../ui/textcard.css"
20
+ ]
21
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pageboard/html",
3
- "version": "0.14.37",
3
+ "version": "0.15.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "repository": {
@@ -16,7 +16,7 @@
16
16
  "dependencies": {},
17
17
  "devDependencies": {
18
18
  "nouislider": "^15.7.1",
19
- "postinstall": "^0.9.0"
19
+ "postinstall": "^0.10.3"
20
20
  },
21
21
  "postinstall": {},
22
22
  "prepare": {
package/ui/image.js CHANGED
@@ -9,27 +9,20 @@ const HTMLElementImageConstructor = Superclass => class extends Superclass {
9
9
 
10
10
  #defer;
11
11
 
12
- static getZoom({ w, h, rw, rh, fit }) {
13
- let z = 100;
14
- if (!rw && !rh) return z;
15
- if (!rw) rw = rh * w / h;
16
- if (!rh) rh = rw * h / w;
17
- z = Math.ceil((fit == "contain" ? Math.min : Math.max)(rw / w, rh / h) * 100 * (window.devicePixelRatio || 1));
18
- // svg need to be resized to scale to its intrinsic dimension
19
- if (z > 100) z = 100;
20
- const zstep = 5;
21
- z = Math.ceil(z / zstep) * zstep;
22
- return z;
23
- }
24
12
  get dimensions() {
13
+ // dimension in pixels of the (extracted) source image
25
14
  let w = parseInt(this.dataset.width);
26
15
  let h = parseInt(this.dataset.height);
27
- const r = this.crop;
28
- w = Math.round(w * r.w / 100);
29
- h = Math.round(h * r.h / 100);
30
- if (r.z != 100 && this.fit == "none") {
31
- w = Math.round(w * r.z / 100);
32
- h = Math.round(h * r.z / 100);
16
+ const { crop } = this;
17
+ if (crop.w != 100) {
18
+ w = Math.round(w * crop.w / 100);
19
+ }
20
+ if (crop.h != 100) {
21
+ h = Math.round(h * crop.h / 100);
22
+ }
23
+ if (crop.z != 100) {
24
+ w = Math.round(w * crop.z / 100);
25
+ h = Math.round(h * crop.z / 100);
33
26
  }
34
27
  return { w, h };
35
28
  }
@@ -53,12 +46,12 @@ const HTMLElementImageConstructor = Superclass => class extends Superclass {
53
46
  });
54
47
  }
55
48
  get crop() {
56
- let [x, y, w, h, z] = (this.dataset.crop || ";;;;").split(";").map(x => parseFloat(x));
57
- if (Number.isNaN(x)) x = 50;
58
- if (Number.isNaN(y)) y = 50;
59
- if (Number.isNaN(w)) w = 100;
60
- if (Number.isNaN(h)) h = 100;
61
- if (Number.isNaN(z)) z = 100;
49
+ const defs = [50, 50, 100, 100, 100];
50
+ const list = (this.dataset.crop || ";;;;").split(";").map(x => parseFloat(x));
51
+ for (let i = 0; i < defs.length; i++) {
52
+ if (Number.isNaN(list[i])) list[i] = defs[i];
53
+ }
54
+ const [x, y, w, h, z] = list;
62
55
  return { x, y, w, h, z };
63
56
  }
64
57
  set crop({ x, y, w, h, z }) {
@@ -99,6 +92,44 @@ const HTMLElementImageConstructor = Superclass => class extends Superclass {
99
92
  else return src;
100
93
  }
101
94
 
95
+ requestSrc(srcLoc) {
96
+ const { crop, fit } = this;
97
+ const { w, h } = this.dimensions;
98
+ const r = {};
99
+ if (!Number.isNaN(w) && !Number.isNaN(h)) {
100
+ if (fit == "none") {
101
+ r.width = w;
102
+ r.height = h;
103
+ } else {
104
+ const rect = this.getBoundingClientRect();
105
+ r.width = rect.width;
106
+ r.height = rect.height;
107
+ }
108
+ }
109
+ if (!r.width && !r.height) {
110
+ // don't show
111
+ return;
112
+ }
113
+ if (crop.x != 50 || crop.y != 50 || crop.w != 100 || crop.h != 100) {
114
+ if (Math.round((crop.x - crop.w / 2) * 100) < 0 || Math.round((crop.x + crop.w / 2) * 100) > 10000) {
115
+ crop.w = 2 * Math.min(crop.x, 100 - crop.x);
116
+ }
117
+ if (Math.round((crop.y - crop.h / 2) * 100) < 0 || Math.round((crop.y + crop.h / 2) * 100) > 10000) {
118
+ crop.h = 2 * Math.min(crop.y, 100 - crop.y);
119
+ }
120
+ srcLoc.query.ex = `x-${crop.x}_y-${crop.y}_w-${crop.w}_h-${crop.h}`;
121
+ }
122
+
123
+ if (!r.width) r.width = r.height * w / h;
124
+ if (!r.height) r.height = r.width * h / w;
125
+ const clientHintWidth = Math.ceil(
126
+ (fit == "contain" ? Math.min : Math.max)(r.width / w, r.height / h)
127
+ * w * (window.devicePixelRatio || 1)
128
+ );
129
+ srcLoc.query.rs = `w-${clientHintWidth}`; // max, min ?
130
+ return srcLoc.toString();
131
+ }
132
+
102
133
  reveal(state) {
103
134
  const img = this.image;
104
135
  if (!this.options.src) {
@@ -108,47 +139,15 @@ const HTMLElementImageConstructor = Superclass => class extends Superclass {
108
139
  if (this.classList.contains('loading')) {
109
140
  return;
110
141
  }
111
- const fit = this.fit;
112
- const r = this.crop;
113
-
114
- let loc = Page.parse(this.options.src);
115
- if (loc.hostname && loc.hostname != document.location.hostname) {
116
- loc = Page.parse({
117
- pathname: "/.api/image",
118
- query: {
119
- url: this.options.src
120
- }
121
- });
122
- }
123
-
124
- if (r.x != 50 || r.y != 50 || r.w != 100 || r.h != 100) {
125
- if (Math.round((r.x - r.w / 2) * 100) < 0 || Math.round((r.x + r.w / 2) * 100) > 10000) {
126
- r.w = 2 * Math.min(r.x, 100 - r.x);
127
- }
128
- if (Math.round((r.y - r.h / 2) * 100) < 0 || Math.round((r.y + r.h / 2) * 100) > 10000) {
129
- r.h = 2 * Math.min(r.y, 100 - r.y);
130
- }
131
- loc.query.ex = `x-${r.x}_y-${r.y}_w-${r.w}_h-${r.h}`;
132
- }
133
- const { w, h } = this.dimensions;
134
- if (fit == "none") {
135
- loc.query.rs = `z-${r.z}`;
136
- } else if (!Number.isNaN(w) && !Number.isNaN(h)) {
137
- const rect = this.getBoundingClientRect();
138
- const rw = rect.width;
139
- const rh = rect.height;
140
- if (rw == 0 && rh == 0) {
141
- // don't show
142
- return;
143
- }
144
- loc.query.rs = "z-" + this.constructor.getZoom({ w, h, rw, rh, fit });
145
- }
146
- const curSrc = loc.toString();
147
- if (curSrc != this.currentSrc) {
142
+ const srcLoc = Page.parse(this.options.src);
143
+ const reqSrc = this.requestSrc(srcLoc);
144
+ if (!reqSrc) {
145
+ img.setAttribute('src', '');
146
+ } else if (reqSrc != this.currentSrc) {
148
147
  this.classList.add('loading');
149
148
  this.#defer?.resolve();
150
149
  this.#defer = new Deferred();
151
- img.setAttribute('src', curSrc);
150
+ img.setAttribute('src', reqSrc);
152
151
  return this.#defer;
153
152
  }
154
153
  }
package/ui/input-file.js CHANGED
@@ -73,9 +73,10 @@ class HTMLElementInputFile extends Page.create(HTMLInputElement) {
73
73
  this.#defer = null;
74
74
  };
75
75
  const pass = (obj) => {
76
- if (!obj.items || obj.items.length == 0) return fail(new Error("File rejected"));
77
- const val = obj.items[0];
78
- this.value = val;
76
+ if (!obj.hrefs?.length) {
77
+ return fail(new Error("File rejected"));
78
+ }
79
+ this.value = obj.hrefs[0]?.pathname;
79
80
  field.classList.add('success');
80
81
  field.classList.remove('loading');
81
82
  this.#xhr = null;
@@ -124,7 +125,7 @@ class HTMLElementInputFile extends Page.create(HTMLInputElement) {
124
125
  fail(err);
125
126
  });
126
127
  try {
127
- xhr.open("POST", `/.api/upload/${this.id}`, true);
128
+ xhr.open("POST", `/@api/upload/${this.id}`, true);
128
129
  xhr.setRequestHeader('Accept', "application/json; q=1.0");
129
130
  xhr.send(fd);
130
131
  this.#xhr = xhr;
package/ui/layout.js CHANGED
@@ -14,48 +14,16 @@ class HTMLElementLayout extends Page.create(HTMLDivElement) {
14
14
  this.style.backgroundImage = '';
15
15
  return;
16
16
  }
17
- const fit = this.fit;
18
- const r = this.crop;
19
-
20
- let loc = Page.parse(this.options.src);
21
- if (loc.hostname && loc.hostname != document.location.hostname) {
22
- loc = Page.parse({
23
- pathname: "/.api/image",
24
- query: {
25
- url: this.options.src
26
- }
27
- });
28
- }
29
-
30
- if (r.x != 50 || r.y != 50 || r.w != 100 || r.h != 100) {
31
- if (Math.round((r.x - r.w / 2) * 100) < 0 || Math.round((r.x + r.w / 2) * 100) > 10000) {
32
- r.w = 2 * Math.min(r.x, 100 - r.x);
33
- }
34
- if (Math.round((r.y - r.h / 2) * 100) < 0 || Math.round((r.y + r.h / 2) * 100) > 10000) {
35
- r.h = 2 * Math.min(r.y, 100 - r.y);
36
- }
37
- loc.query.ex = `x-${r.x}_y-${r.y}_w-${r.w}_h-${r.h}`;
38
- }
39
- const {w, h} = this.dimensions;
40
- if (fit == "none") {
41
- loc.query.rs = `z-${r.z}`;
42
- } else if (!Number.isNaN(w) && !Number.isNaN(h)) {
43
- const rect = this.getBoundingClientRect();
44
- const rw = rect.width;
45
- const rh = rect.height;
46
- if (rw == 0 && rh == 0) {
47
- // don't show
48
- return;
49
- }
50
- loc.query.rs = "z-" + HTMLElementLayout.getZoom({w, h, rw, rh, fit});
17
+ const srcLoc = Page.parse(this.options.src);
18
+ const reqSrc = this.requestSrc(srcLoc);
19
+ try {
20
+ this.currentSrc = reqSrc;
21
+ } catch (e) {
22
+ // pass
51
23
  }
52
- const curSrc = loc.toString();
53
- if (curSrc != this.currentSrc) {
54
- try {
55
- this.currentSrc = curSrc;
56
- } catch(e) {
57
- // pass
58
- }
24
+ if (!reqSrc) {
25
+ this.style.backgroundImage = '';
26
+ } else if (reqSrc != this.currentSrc) {
59
27
  this.#defer?.resolve();
60
28
  this.#defer = new Deferred();
61
29
  const img = new Image();
@@ -63,8 +31,8 @@ class HTMLElementLayout extends Page.create(HTMLDivElement) {
63
31
  img.addEventListener('error',
64
32
  e => this.#defer.reject(new Error(this.currentSrc))
65
33
  );
66
- img.src = curSrc;
67
- this.style.backgroundImage = `url("${curSrc}")`;
34
+ img.src = reqSrc;
35
+ this.style.backgroundImage = `url("${reqSrc}")`;
68
36
  return this.#defer;
69
37
  }
70
38
  }
@@ -74,14 +42,13 @@ class HTMLElementLayout extends Page.create(HTMLDivElement) {
74
42
  }
75
43
 
76
44
  (function(HTMLElementImage) {
77
- HTMLElementLayout.getZoom = HTMLElementImage.getZoom;
78
- for (const name of ['crop', 'dimensions']) {
79
- Object.defineProperty(
80
- HTMLElementLayout.prototype,
81
- name,
82
- Object.getOwnPropertyDescriptor(HTMLElementImage.prototype, name)
83
- );
84
- }
45
+ for (const name of ['crop', 'dimensions', 'requestSrc']) {
46
+ Object.defineProperty(
47
+ HTMLElementLayout.prototype,
48
+ name,
49
+ Object.getOwnPropertyDescriptor(HTMLElementImage.prototype, name)
50
+ );
51
+ }
85
52
  })(window.customElements.get('element-image'));
86
53
 
87
54
  Page.define(`element-layout`, HTMLElementLayout, 'div');
package/ui/pagination.js CHANGED
@@ -9,7 +9,7 @@ class HTMLElementPagination extends Page.create(HTMLAnchorElement) {
9
9
  #update(state) {
10
10
  const { fetch } = this.dataset;
11
11
  const node = this.ownerDocument.querySelector(
12
- `element-template[action="/.api/query/${fetch}"]`
12
+ `element-template[action="/@api/query/${fetch}"]`
13
13
  );
14
14
  if (!node) {
15
15
  console.warn("pagination does not find fetch node", fetch);
@@ -0,0 +1,3 @@
1
+ .textcard > .header {
2
+ font-weight:bold;
3
+ }