@pageboard/html 0.10.16 → 0.11.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/card.js CHANGED
@@ -159,14 +159,14 @@ exports.card_header = {
159
159
  contents: "inline*",
160
160
  html: '<div class="header">Header</div>'
161
161
  };
162
- exports.card_header_nolink = Object.assign({}, exports.card_header, {
162
+ exports.card_header_nolink = { ...exports.card_header,
163
163
  context: 'cardlink//',
164
164
  contents: {
165
165
  nodes: "inline*",
166
166
  marks: "nolink"
167
167
  },
168
168
  html: '<div class="header">Header</div>'
169
- });
169
+ };
170
170
 
171
171
  exports.card_meta = {
172
172
  title: 'meta',
@@ -177,14 +177,14 @@ exports.card_meta = {
177
177
  contents: "inline*",
178
178
  html: '<div class="meta">Meta</div>'
179
179
  };
180
- exports.card_meta_nolink = Object.assign({}, exports.card_meta, {
180
+ exports.card_meta_nolink = { ...exports.card_meta,
181
181
  context: 'cardlink//',
182
182
  contents: {
183
183
  nodes: "inline*",
184
184
  marks: "nolink"
185
185
  },
186
186
  html: '<div class="meta">Meta</div>'
187
- });
187
+ };
188
188
 
189
189
  exports.card_description = {
190
190
  title: 'description',
@@ -195,9 +195,9 @@ exports.card_description = {
195
195
  contents: "paragraph+",
196
196
  html: '<div class="description"></div>'
197
197
  };
198
- exports.card_description_nolink = Object.assign({}, exports.card_description, {
198
+ exports.card_description_nolink = { ...exports.card_description,
199
199
  context: 'cardlink//',
200
200
  contents: "paragraph_nolink+",
201
201
  html: '<div class="description"></div>'
202
- });
202
+ };
203
203
 
@@ -42,10 +42,10 @@ exports.fieldset_legend = {
42
42
  };
43
43
 
44
44
  exports.fieldset_list = {
45
- title: 'FieldList',
45
+ title: 'Field List',
46
46
  menu: "form",
47
47
  icon: '<i class="icons"><i class="folder outline icon"></i><i class="corner add icon"></i></i>',
48
- group: "block",
48
+ group: 'block template',
49
49
  context: 'form//',
50
50
  priority: 0,
51
51
  properties: {
@@ -54,23 +54,58 @@ exports.fieldset_list = {
54
54
  type: "integer",
55
55
  minimum: 0,
56
56
  default: 1
57
- },
58
- prefix: {
59
- title: 'Prefix',
60
- description: '',
61
- type: "string",
62
- format: 'singleline',
63
- nullable: true
64
57
  }
65
58
  },
66
59
  contents: [{
67
60
  id: 'template',
68
- nodes: 'block+'
61
+ nodes: 'block+',
62
+ expressions: true
69
63
  }],
70
- html: `<element-fieldset-list data-size="[size]" data-prefix="[prefix]">
64
+ html: `<element-fieldset-list data-size="[size]">
71
65
  <template block-content="template"></template>
72
66
  <div class="view"></div>
73
67
  </element-fieldset-list>`,
74
68
  scripts: ['../ui/fieldset-list.js'],
75
69
  stylesheets: ['../ui/fieldset-list.css']
76
70
  };
71
+
72
+
73
+ exports.fieldlist_button = {
74
+ title: 'Field List Button',
75
+ menu: "form",
76
+ icon: '<i class="icons"><i class="folder outline icon"></i><i class="corner hand pointer icon"></i></i>',
77
+ group: 'block',
78
+ context: 'fieldset_list//',
79
+ properties: {
80
+ type: {
81
+ title: 'Type',
82
+ default: 'add',
83
+ anyOf: [{
84
+ title: 'Add',
85
+ const: 'add'
86
+ }, {
87
+ title: 'Delete',
88
+ const: 'del'
89
+ }, {
90
+ title: 'Up',
91
+ const: 'up'
92
+ }, {
93
+ title: 'Down',
94
+ const: 'down'
95
+ }]
96
+ },
97
+ full: {
98
+ title: 'Full width',
99
+ type: 'boolean',
100
+ default: false
101
+ }
102
+ },
103
+ contents: {
104
+ nodes: "inline*",
105
+ marks: "nolink"
106
+ },
107
+ html: '<button type="button" class="ui [full|?:fluid:] button" value="[type]">Label</button>',
108
+ stylesheets: [
109
+ '../lib/components/button.css',
110
+ ]
111
+ };
@@ -1,5 +1,5 @@
1
1
  exports.input_date_time = {
2
- title: 'DateTime',
2
+ title: 'Date Time',
3
3
  icon: '<i class="calendar outline icon"></i>',
4
4
  menu: "form",
5
5
  required: ["name"],
@@ -89,7 +89,7 @@ exports.input_date_time = {
89
89
  };
90
90
 
91
91
  exports.input_date_slot = {
92
- title: 'DateSlot',
92
+ title: 'Date Slot',
93
93
  icon: '<i class="calendar outline icon"></i>',
94
94
  menu: "form",
95
95
  required: ["nameStart", "nameEnd"],
@@ -59,16 +59,25 @@ exports.input_property = {
59
59
  let propKey;
60
60
  let required = false;
61
61
  let cases = null;
62
+ let discKey = null;
62
63
  for (let i = 0; i < list.length; i++) {
63
64
  propKey = list[i];
64
65
  required = prop.required && prop.required.indexOf(propKey) >= 0;
65
66
  if (cases) {
66
- prop = cases[propKey];
67
+ if (Array.isArray(cases)) {
68
+ prop = cases.find(obj => obj.properties?.[discKey]?.const == propKey);
69
+ } else {
70
+ prop = cases[propKey];
71
+ }
67
72
  name = list.slice(0, i - 1).concat(list.slice(i + 1)).join('.');
68
73
  cases = null;
74
+ discKey = null;
69
75
  } else {
70
76
  if (prop.select && prop.select.$data == `0/${propKey}`) {
71
77
  cases = prop.selectCases;
78
+ } else if (prop.discriminator && prop.discriminator.propertyName == propKey) {
79
+ cases = prop.oneOf;
80
+ discKey = propKey;
72
81
  }
73
82
  prop = (prop.items && prop.items.properties || prop.properties || {})[propKey] || null;
74
83
  }
@@ -156,8 +156,8 @@ exports.input_text = {
156
156
  nodes: 'inline*'
157
157
  },
158
158
  patterns: {
159
- tel: /^(\(\d+\))? *\d+([ .\-]?\d+)*$/.source,
160
- email: /^[\w.!#$%&'*+\/=?^`{|}~-]+@\w(?:[\w-]{0,61}\w)?(?:\.\w(?:[\w-]{0,61}\w)?)*$/.source
159
+ tel: /^(\(\d+\))? *\d+([ .-]?\d+)*$/.source,
160
+ email: /^[\w.!#$%&'*+/=?^`{|}~-]+@\w(?:[\w-]{0,61}\w)?(?:\.\w(?:[\w-]{0,61}\w)?)*$/.source
161
161
  },
162
162
  html: `<div class="[width|num: wide] field [type|eq:hidden:hidden:]">
163
163
  <label block-content="label">Label</label>
@@ -233,7 +233,7 @@ exports.input_range = {
233
233
  menu: "form",
234
234
  group: "block",
235
235
  context: 'form//',
236
- properties: Object.assign({
236
+ properties: {
237
237
  multiple: {
238
238
  title: 'Multiple',
239
239
  type: 'boolean',
@@ -243,8 +243,9 @@ exports.input_range = {
243
243
  title: 'Pips',
244
244
  type: 'boolean',
245
245
  default: true
246
- }
247
- }, exports.input_number.properties),
246
+ },
247
+ ...exports.input_number.properties
248
+ },
248
249
  contents: {
249
250
  id: 'label',
250
251
  nodes: 'inline*'
package/elements/menu.js CHANGED
@@ -102,14 +102,14 @@ exports.menu_item_link = {
102
102
  html: '<a class="[labeled|?] item" href="[url|autolink]">Link</a>'
103
103
  };
104
104
 
105
- exports.menu_item_block = Object.assign({}, exports.menu_item_link, {
105
+ exports.menu_item_block = { ...exports.menu_item_link,
106
106
  title: 'Block',
107
107
  priority: 11,
108
108
  contents: {
109
109
  nodes: "block+",
110
110
  marks: "nolink"
111
111
  }
112
- });
112
+ };
113
113
 
114
114
  exports.menu_item_text = {
115
115
  priority: 11,
package/elements/page.js CHANGED
@@ -5,9 +5,10 @@ exports.page.stylesheets = [
5
5
  '../ui/transition.css'
6
6
  ];
7
7
 
8
- exports.page.scripts = exports.page.scripts.concat([
8
+ exports.page.scripts = [
9
+ ...exports.page.scripts,
9
10
  '../ui/transition.js'
10
- ]);
11
+ ];
11
12
 
12
13
  exports.page.properties.transition = {
13
14
  title: 'Transition',
@@ -1,11 +1,11 @@
1
- exports.paragraph_nolink = Object.assign({}, exports.paragraph, {
1
+ exports.paragraph_nolink = { ...exports.paragraph,
2
2
  priority: exports.paragraph.priority - 1,
3
3
  group: null,
4
4
  contents: {
5
5
  nodes: "inline*",
6
6
  marks: "nolink"
7
7
  }
8
- });
8
+ };
9
9
 
10
10
  exports.segment = {
11
11
  title: "Segment",
@@ -159,13 +159,12 @@ exports.heading = {
159
159
  };
160
160
 
161
161
 
162
- exports.heading_nolink = Object.assign({}, exports.heading, {
162
+ exports.heading_nolink = {
163
+ ...exports.heading,
163
164
  priority: exports.heading.priority - 1,
164
165
  group: null,
165
- contents: Object.assign({}, exports.heading.contents, {
166
- marks: "nolink"
167
- })
168
- });
166
+ contents: { ...exports.heading.contents, marks: "nolink" }
167
+ };
169
168
 
170
169
  exports.divider = {
171
170
  title: "Divider",
@@ -1,5 +1,5 @@
1
1
  exports.sitemap = {
2
- title: "Site map",
2
+ title: "Sitemap",
3
3
  group: "block",
4
4
  icon: '<i class="sitemap icon"></i>',
5
5
  menu: 'link',
@@ -30,12 +30,13 @@ exports.sitemap = {
30
30
  title: schema.title,
31
31
  icon: schema.icon,
32
32
  standalone: true,
33
- properties: Object.assign({
33
+ properties: {
34
34
  leaf: {
35
35
  type: 'boolean',
36
36
  default: Boolean(leaf)
37
- }
38
- }, schema.properties),
37
+ },
38
+ ...schema.properties
39
+ },
39
40
  menu: "link",
40
41
  group: 'sitemap_item',
41
42
  virtual: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pageboard/html",
3
- "version": "0.10.16",
3
+ "version": "0.11.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -1,62 +1,145 @@
1
+ class WalkIndex {
2
+ #walk;
3
+ #find;
4
+ #index;
5
+ constructor(root, fn) {
6
+ this.#find = fn;
7
+ this.#walk = root.ownerDocument.createTreeWalker(
8
+ root,
9
+ NodeFilter.SHOW_ELEMENT,
10
+ this
11
+ );
12
+ }
13
+ acceptNode(node) {
14
+ const index = this.#find(node);
15
+ if (index != null) {
16
+ this.#index = index;
17
+ return NodeFilter.FILTER_ACCEPT;
18
+ } else {
19
+ return NodeFilter.FILTER_SKIP;
20
+ }
21
+ }
22
+ findBefore(node) {
23
+ this.#index = null;
24
+ this.#walk.currentNode = node;
25
+ this.#walk.previousNode();
26
+ return this.#index;
27
+ }
28
+ }
29
+
1
30
  class HTMLElementFieldsetList extends VirtualHTMLElement {
31
+ #size;
32
+ #prefix;
33
+ #model;
34
+ #walk;
35
+
2
36
  fill(values, scope) {
3
- const list = this.listFromValues(Object.assign({}, values));
4
- this.resize(list.length, scope);
37
+ const list = this.#listFromValues({ ...values });
38
+ this.#resize(list.length, scope);
5
39
  }
6
40
 
7
- patch(state) {
41
+ #prepare() {
8
42
  this.ownTpl.prerender();
9
43
  if (this.isContentEditable) return;
10
- if (!this.size) this.resize(0, state.scope);
44
+ for (const node of this.ownTpl.content.querySelectorAll('[block-id]')) {
45
+ node.removeAttribute('block-id');
46
+ }
47
+ const keys = new Set();
48
+ const inputs = this.ownTpl.content.querySelectorAll('[name]');
49
+ for (const node of inputs) {
50
+ keys.add(node.name);
51
+ }
52
+ const splits = Array.from(keys).map(name => name.split('.'));
53
+ const coms = [];
54
+ let pos = 0, com = null;
55
+ while (splits.every(list => {
56
+ if (com == null) {
57
+ if (pos < list.length) {
58
+ com = list[pos];
59
+ return true;
60
+ } else {
61
+ return false;
62
+ }
63
+ } else {
64
+ return list[pos] == com;
65
+ }
66
+ })) {
67
+ coms.push(com);
68
+ com = null;
69
+ pos++;
70
+ }
71
+ if (coms.length) coms.push('');
72
+ const prefix = coms.join('.');
73
+ this.#prefix = prefix;
74
+ const model = {};
75
+ for (const key of keys) {
76
+ if (key.startsWith(prefix)) model[key.substring(prefix.length)] = null;
77
+ }
78
+ this.#model = model;
11
79
  }
12
80
 
13
- setup(state) {
14
- this.ownTpl.prerender();
81
+ patch(state) {
82
+ this.#prepare();
83
+ if (!this.#size) this.#resize(0, state.scope);
15
84
  }
16
85
 
17
- resize(size, scope) {
18
- const len = Math.max(Number(this.dataset.size) || 0, size);
19
- if (this.size == len) return;
20
- this.size = len;
86
+ setup() {
87
+ this.#prepare();
88
+ }
21
89
 
22
- const tpl = this.ownTpl.content.cloneNode(true);
23
- for (const node of tpl.querySelectorAll('[block-id]')) {
24
- node.removeAttribute('block-id');
25
- }
26
- const anc = tpl.querySelectorAll('[name]:not(button)').ancestor();
90
+ #selector(name) {
91
+ return `[block-type="fieldlist_button"][value="${name}"]`;
92
+ }
27
93
 
28
- for (let i = len - 1; i >= 1; i--) {
29
- const clone = this.updateAncestor(anc.cloneNode(true), i);
30
- clone.fuse({ $fieldset: { index: i } }, scope);
31
- anc.parentNode.insertBefore(clone, anc.nextSibling);
94
+ #resize(size, scope) {
95
+ if (this.isContentEditable) return;
96
+ const len = Math.max(Number(this.dataset.size) || 0, size);
97
+ if (this.#size === len) return;
98
+ this.#size = len;
99
+ let tpl = this.ownTpl.content.cloneNode(true);
100
+ const $fieldset = Array.from(Array(len)).map((x, i) => {
101
+ return { index: i };
102
+ });
103
+ const inputs = tpl.querySelectorAll('[name]');
104
+ const prefix = this.#prefix;
105
+ for (const node of inputs) {
106
+ if (node.name.startsWith(prefix)) {
107
+ node.name = `${prefix}[$field.index].${node.name.substring(prefix.length)}`;
108
+ }
109
+ }
110
+ const subtpl = inputs.ancestor();
111
+ if (!subtpl) {
112
+ console.warn("fieldset-list should contain input[name]", this);
113
+ return;
32
114
  }
33
- this.updateAncestor(anc, 0);
34
- anc.fuse({ $fieldset: { index: 0 } }, scope);
35
- tpl.fuse({ $fieldset: { count: len } }, scope);
115
+ subtpl.appendChild(
116
+ subtpl.ownerDocument.createTextNode('[$fieldset|repeat:*:$field|]')
117
+ );
118
+ if (len == 0) {
119
+ let node = tpl.querySelector(this.#selector('add'));
120
+ while (node != null && node != tpl && node != subtpl) {
121
+ while (node.nextSibling) node.nextSibling.remove();
122
+ while (node.previousSibling) node.previousSibling.remove();
123
+ node = node.parentNode;
124
+ }
125
+ }
126
+ tpl = tpl.fuse({ $fieldset }, scope);
127
+
36
128
  const view = this.ownView;
37
129
  view.textContent = '';
38
130
  view.appendChild(tpl);
39
- }
40
131
 
41
- updateAncestor(node, i) {
42
- const prefix = this.prefix;
43
- for (const child of node.querySelectorAll('[name]:not(button)')) {
44
- child.name = `${prefix}${i}.${child.name}`;
45
- }
46
- return node;
132
+ view.querySelectorAll(this.#selector('up')).forEach((node, i) => {
133
+ node.disabled = i == 0;
134
+ });
135
+ view.querySelectorAll(this.#selector('down')).forEach((node, i, arr) => {
136
+ node.disabled = i == arr.length - 1;
137
+ });
47
138
  }
48
139
 
49
- modelFromTemplate() {
50
- const obj = {};
51
- for (const node of this.ownTpl.content.querySelectorAll('[name]:not(button)')) {
52
- obj[node.name] = null;
53
- }
54
- return obj;
55
- }
56
-
57
- listFromValues(values) {
140
+ #listFromValues(values) {
58
141
  const list = [];
59
- const prefix = this.prefix;
142
+ const prefix = this.#prefix;
60
143
  // just unflatten the array
61
144
  for (const [key, val] of Object.entries(values)) {
62
145
  if (!key.startsWith(prefix)) continue;
@@ -71,8 +154,8 @@ class HTMLElementFieldsetList extends VirtualHTMLElement {
71
154
  return list;
72
155
  }
73
156
 
74
- listToValues(values, list) {
75
- const prefix = this.prefix;
157
+ #listToValues(values, list) {
158
+ const prefix = this.#prefix;
76
159
  for (let i = 0; i < list.length; i++) {
77
160
  const obj = list[i];
78
161
  for (const [key, val] of Object.entries(obj)) {
@@ -83,23 +166,47 @@ class HTMLElementFieldsetList extends VirtualHTMLElement {
83
166
 
84
167
  handleClick(e, state) {
85
168
  if (this.isContentEditable) return;
86
- const btn = e.target.closest('button[type="button"][name]');
169
+ const btn = e.target.closest('button');
87
170
  if (!btn) return;
88
- if (["add", "del"].includes(btn.name) == false) return;
171
+ const action = btn.value;
172
+ if (["add", "del", "up", "down"].includes(action) == false) return;
89
173
 
90
174
  const form = this.closest('form');
91
175
  const values = form.read(true);
92
- const list = this.listFromValues(values);
93
- const index = Number(btn.value);
94
- if (!Number.isInteger(index) || index < 0 || index > list.length) {
95
- throw new Error(`fieldset-list expects ${btn.outerHTML} to have a value with a valid index`);
96
- }
97
- if (btn.name == "add") {
98
- list.splice(index + 1, 0, this.modelFromTemplate());
99
- } else if (btn.name == "del") {
100
- list.splice(index, 1);
176
+ const list = this.#listFromValues(values);
177
+ const prefix = this.#prefix;
178
+ if (!this.#walk) this.#walk = new WalkIndex(this, (node) => {
179
+ if (node.name?.startsWith(prefix)) {
180
+ const index = Number(node.name.substring(prefix.length).split('.').shift());
181
+ if (Number.isInteger(index) || index >= 0 || index < list.length) {
182
+ return index;
183
+ }
184
+ }
185
+ return null;
186
+ });
187
+ let index;
188
+
189
+ switch (action) {
190
+ case "add":
191
+ list.splice((this.#walk.findBefore(btn) ?? -1) + 1, 0, this.#model);
192
+ break;
193
+ case "del":
194
+ list.splice(this.#walk.findBefore(btn) ?? 0, 1);
195
+ break;
196
+ case "up":
197
+ index = this.querySelectorAll(this.#selector('up')).indexOf(btn);
198
+ if (index > 0) {
199
+ list.splice(index - 1, 0, list.splice(index, 1).pop());
200
+ }
201
+ break;
202
+ case "down":
203
+ index = this.querySelectorAll(this.#selector('down')).indexOf(btn);
204
+ if (index < list.length - 1) {
205
+ list.splice(index + 1, 0, list.splice(index, 1).pop());
206
+ }
207
+ break;
101
208
  }
102
- this.listToValues(values, list);
209
+ this.#listToValues(values, list);
103
210
  form.fill(values, state.scope);
104
211
  }
105
212
 
@@ -111,14 +218,6 @@ class HTMLElementFieldsetList extends VirtualHTMLElement {
111
218
  get ownView() {
112
219
  return this.children.find(node => node.matches('.view'));
113
220
  }
114
- get prefix() {
115
- const prefix = this.dataset.prefix;
116
- if (prefix) return prefix + ".";
117
- else return "";
118
- }
119
221
  }
120
222
 
121
223
  VirtualHTMLElement.define('element-fieldset-list', HTMLElementFieldsetList);
122
-
123
-
124
-
package/ui/fieldset.js CHANGED
@@ -15,8 +15,8 @@ class HTMLCustomFieldSetElement extends HTMLFieldSetElement {
15
15
  this.disabled = this.hidden = val != this.options.value;
16
16
  }
17
17
 
18
- patch() {
19
- this.#update();
18
+ patch(state) {
19
+ state.finish(() => this.#update());
20
20
  }
21
21
  setup() {
22
22
  this.form?.addEventListener('change', this);
package/ui/form.js CHANGED
@@ -108,18 +108,15 @@ class HTMLCustomFormElement extends HTMLFormElement {
108
108
  return query;
109
109
  }
110
110
  fill(query, scope) {
111
- // workaround for merging arrays
112
- const tagList = "element-fieldset-list";
113
- const FieldSet = VirtualHTMLElement.define(tagList);
114
- for (const node of this.querySelectorAll(tagList)) {
115
- if (!node.fill) Object.setPrototypeOf(node, FieldSet.prototype);
111
+ // fieldset-list are not custom inputs yet
112
+ for (const node of this.querySelectorAll("element-fieldset-list")) {
116
113
  node.fill(query, scope);
117
114
  }
118
115
  const vars = [];
119
116
  for (const elem of this.elements) {
120
117
  const name = elem.name;
121
118
  if (!name) continue;
122
- if (Object.prototype.hasOwnProperty.call(query, name) && !vars.includes(name)) vars.push(name);
119
+ if (name in query && !vars.includes(name)) vars.push(name);
123
120
  const val = query[name];
124
121
  const str = ((v) => {
125
122
  if (v == null) return "";
@@ -228,7 +225,7 @@ class HTMLCustomFormElement extends HTMLFormElement {
228
225
  const loc = Page.parse(redirect);
229
226
  Object.assign(loc.query, this.read(false));
230
227
  if (loc.samePathname(state)) {
231
- loc.query = Object.assign({}, state.query, loc.query);
228
+ loc.query = { ...state.query, ...loc.query };
232
229
  }
233
230
  let status = 200;
234
231
  const p = this.ignoreInputChange
@@ -430,7 +427,7 @@ Page.setup((state) => {
430
427
  }
431
428
  });
432
429
 
433
- Page.ready((state) => {
430
+ Page.patch(state => {
434
431
  const filters = state.scope.$filters;
435
432
 
436
433
  function linearizeValues(query, obj = {}, prefix) {
@@ -461,35 +458,32 @@ Page.ready((state) => {
461
458
  if (action == "toggle") {
462
459
  action = val ? "enable" : "disable";
463
460
  }
464
- // NB: call Class methods to deal with uninstantiated custom form
465
- if (action == "enable") {
466
- HTMLCustomFormElement.prototype.enable.call(form);
467
- } else if (action == "disable") {
468
- HTMLCustomFormElement.prototype.disable.call(form);
469
- } else if (action == "fill") {
470
- if (val == null) {
471
- form.reset();
472
- } else if (typeof val == "object") {
473
- let values = val;
474
- if (val.id && val.data) {
475
- // old way
476
- values = Object.assign({}, val.data);
477
- for (const key of Object.keys(val)) {
478
- if (key != "data") values['$' + key] = val[key];
461
+
462
+ state.finish(() => {
463
+ if (action == "enable") {
464
+ form.enable();
465
+ } else if (action == "disable") {
466
+ form.disable();
467
+ } else if (action == "fill") {
468
+ if (val == null) {
469
+ form.reset();
470
+ } else if (typeof val == "object") {
471
+ let values = val;
472
+ if (val.id && val.data) {
473
+ // old way
474
+ values = { ...val.data };
475
+ for (const key of Object.keys(val)) {
476
+ if (key != "data") values['$' + key] = val[key];
477
+ }
478
+ } else {
479
+ // new way
479
480
  }
480
- } else {
481
- // new way
481
+ form.fill(linearizeValues(values), state.scope);
482
+ form.save();
482
483
  }
483
- HTMLCustomFormElement.prototype.fill.call(form, linearizeValues(values), state.scope);
484
- HTMLCustomFormElement.prototype.save.call(form);
485
484
  }
486
- } else if (action == "read") {
487
- const obj = {};
488
- for (const [key, kval] of Object.entries(val)) {
489
- if (form.querySelector(`[name="${key}"]`)) obj[key] = kval;
490
- }
491
- return obj;
492
- }
485
+ });
486
+
493
487
  return val;
494
488
  };
495
489
  });
package/ui/pagination.js CHANGED
@@ -27,9 +27,10 @@ class HTMLElementPagination extends HTMLAnchorElement {
27
27
  } else {
28
28
  this.setAttribute('href', Page.format({
29
29
  pathname: state.pathname,
30
- query: Object.assign({}, state.query, {
30
+ query: {
31
+ ...state.query,
31
32
  [name]: cur || undefined
32
- })
33
+ }
33
34
  }));
34
35
  }
35
36
  state.finish(() => {
package/ui/tab.js CHANGED
@@ -14,7 +14,7 @@ class HTMLElementTabs extends VirtualHTMLElement {
14
14
  const id = this.id;
15
15
 
16
16
  this.items.children.forEach((item, i) => {
17
- const query = Object.assign({}, state.query);
17
+ const query = { ...state.query };
18
18
  const key = `${id}.index`;
19
19
  if (i == 0) delete query[key];
20
20
  else query[key] = i;