@jinntec/fore 1.1.0 → 1.3.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.
Files changed (45) hide show
  1. package/dist/fore-dev.js +8 -8
  2. package/dist/fore-dev.js.map +1 -1
  3. package/dist/fore.js +7 -7
  4. package/dist/fore.js.map +1 -1
  5. package/index.js +1 -0
  6. package/package.json +2 -2
  7. package/resources/fore.css +95 -72
  8. package/src/ForeElementMixin.js +1 -1
  9. package/src/actions/abstract-action.js +55 -7
  10. package/src/actions/fx-action.js +2 -2
  11. package/src/actions/fx-confirm.js +2 -2
  12. package/src/actions/fx-delete.js +53 -62
  13. package/src/actions/fx-hide.js +3 -1
  14. package/src/actions/fx-message.js +25 -1
  15. package/src/actions/fx-refresh.js +9 -0
  16. package/src/actions/fx-reload.js +30 -0
  17. package/src/actions/fx-replace.js +1 -0
  18. package/src/actions/fx-send.js +11 -1
  19. package/src/actions/fx-setfocus.js +32 -5
  20. package/src/actions/fx-setvalue.js +4 -4
  21. package/src/actions/fx-show.js +1 -0
  22. package/src/actions/fx-toggle.js +5 -0
  23. package/src/events.js +24 -0
  24. package/src/fore.js +71 -8
  25. package/src/fx-bind.js +2 -2
  26. package/src/fx-fore.js +71 -20
  27. package/src/fx-instance.js +33 -8
  28. package/src/fx-model.js +435 -444
  29. package/src/fx-submission.js +78 -65
  30. package/src/getInScopeContext.js +91 -83
  31. package/src/modelitem.js +2 -2
  32. package/src/relevance.js +1 -1
  33. package/src/ui/abstract-control.js +114 -27
  34. package/src/ui/fx-alert.js +0 -1
  35. package/src/ui/fx-container.js +23 -27
  36. package/src/ui/fx-control.js +90 -34
  37. package/src/ui/fx-group.js +5 -0
  38. package/src/ui/fx-inspector.js +5 -2
  39. package/src/ui/fx-output.js +17 -15
  40. package/src/ui/fx-repeat.js +2 -2
  41. package/src/ui/fx-repeatitem.js +5 -3
  42. package/src/ui/fx-switch.js +11 -7
  43. package/src/ui/fx-trigger.js +15 -9
  44. package/src/xpath-evaluation.js +31 -18
  45. package/src/xpath-util.js +13 -0
@@ -37,31 +37,30 @@ export class FxContainer extends foreElementMixin(HTMLElement) {
37
37
  /**
38
38
  * (re)apply all state properties to this control.
39
39
  */
40
- refresh(force) {
40
+ async refresh(force) {
41
41
  if (!force && this.hasAttribute('refresh-on-view')) return;
42
42
  // console.log('### FxContainer.refresh on : ', this);
43
43
 
44
44
  if (this.isBound()) {
45
45
  this.evalInContext();
46
46
  this.modelItem = this.getModelItem();
47
- if (!this.modelItem.boundControls.includes(this)) {
47
+ if (this.modelItem && !this.modelItem.boundControls.includes(this)) {
48
48
  this.modelItem.boundControls.push(this);
49
49
  }
50
-
51
- // this.value = this.modelItem.value;
50
+ this.handleModelItemProperties();
52
51
  }
53
52
 
54
- // await this.updateComplete;
55
-
56
53
  // state change event do not fire during init phase (initial refresh)
57
- if (this._getForm().ready) {
58
- this.handleModelItemProperties();
59
- }
60
- Fore.refreshChildren(this, force);
54
+ // if (this._getForm().ready) {
55
+ // this.handleModelItemProperties();
56
+ // }
57
+ // Fore.refreshChildren(this, force);
61
58
  }
62
59
 
60
+ /**
61
+ * anly relevance is processed for container controls
62
+ */
63
63
  handleModelItemProperties() {
64
- this.handleReadonly();
65
64
  this.handleRelevant();
66
65
  }
67
66
 
@@ -69,28 +68,25 @@ export class FxContainer extends foreElementMixin(HTMLElement) {
69
68
  return this.getModel().parentNode;
70
69
  }
71
70
 
72
- handleReadonly() {
73
- // console.log('mip readonly', this.modelItem.isReadonly);
74
- if (this.isReadonly() !== this.modelItem.readonly) {
75
- if (this.modelItem.readonly) {
76
- this.setAttribute('readonly', 'readonly');
77
- this.dispatchEvent(new CustomEvent('readonly', {}));
78
- }
79
- if (!this.modelItem.readonly) {
80
- this.removeAttribute('readonly');
81
- this.dispatchEvent(new CustomEvent('readwrite', {}));
82
- }
83
- }
84
- }
85
-
86
71
  handleRelevant() {
87
72
  // console.log('mip valid', this.modelItem.enabled);
88
- if (!this.modelItem) return;
73
+ if (!this.modelItem) {
74
+ console.log('container is not relevant');
75
+ this.removeAttribute('relevant','');
76
+ this.setAttribute('nonrelevant','');
77
+ this.dispatchEvent(new CustomEvent('disabled', {}));
78
+ return;
79
+ }
89
80
 
90
81
  if (this.isEnabled() !== this.modelItem.enabled) {
91
- if (this.modelItem.enabled) {
82
+ if (this.modelItem.relevant) {
83
+ // this.style.display = 'block';
84
+ this.removeAttribute('nonrelevant','');
85
+ this.setAttribute('relevant','');
92
86
  this.dispatchEvent(new CustomEvent('enabled', {}));
93
87
  } else {
88
+ this.removeAttribute('relevant','');
89
+ this.setAttribute('nonrelevant','');
94
90
  this.dispatchEvent(new CustomEvent('disabled', {}));
95
91
  }
96
92
  }
@@ -6,6 +6,9 @@ import {
6
6
  } from '../xpath-evaluation.js';
7
7
  import getInScopeContext from '../getInScopeContext.js';
8
8
  import { Fore } from '../fore.js';
9
+ import {ModelItem} from "../modelitem.js";
10
+ import {debounce} from "../events.js";
11
+ import {FxModel} from "../fx-model";
9
12
 
10
13
  const WIDGETCLASS = 'widget';
11
14
 
@@ -19,7 +22,8 @@ const WIDGETCLASS = 'widget';
19
22
  * @demo demo/index.html
20
23
  */
21
24
 
22
- function debounce(func, timeout = 300) {
25
+ /*
26
+ function debounce( func, timeout = 300) {
23
27
  let timer;
24
28
  return (...args) => {
25
29
  clearTimeout(timer);
@@ -28,6 +32,7 @@ function debounce(func, timeout = 300) {
28
32
  }, timeout);
29
33
  };
30
34
  }
35
+ */
31
36
  export default class FxControl extends XfAbstractControl {
32
37
  constructor() {
33
38
  super();
@@ -71,10 +76,16 @@ export default class FxControl extends XfAbstractControl {
71
76
  listenOn = target;
72
77
  }
73
78
  }
79
+
80
+
81
+ this.addEventListener('keyup', event => {
82
+ FxModel.dataChanged = true;
83
+ });
74
84
  // ### convenience marker event
75
85
  if (this.updateEvent === 'enter') {
76
86
  this.widget.addEventListener('keyup', event => {
77
87
  if (event.keyCode === 13) {
88
+ // console.info('handling Event:', event.type, listenOn);
78
89
  // Cancel the default action, if needed
79
90
  event.preventDefault();
80
91
  this.setValue(this.widget[this.valueProp]);
@@ -85,14 +96,15 @@ export default class FxControl extends XfAbstractControl {
85
96
  if (this.debounceDelay) {
86
97
  listenOn.addEventListener(
87
98
  this.updateEvent,
88
- debounce(() => {
89
- console.log('eventlistener ', this.updateEvent);
99
+ debounce(this,() => {
100
+ // console.log('eventlistener ', this.updateEvent);
101
+ // console.info('handling Event:', event.type, listenOn);
90
102
  this.setValue(this.widget[this.valueProp]);
91
103
  }, this.debounceDelay),
92
104
  );
93
105
  } else {
94
106
  listenOn.addEventListener(this.updateEvent, () => {
95
- console.log('eventlistener ', this.updateEvent);
107
+ // console.info('handling Event:', event.type, listenOn);
96
108
  this.setValue(this.widget[this.valueProp]);
97
109
  });
98
110
  }
@@ -124,6 +136,8 @@ export default class FxControl extends XfAbstractControl {
124
136
  });
125
137
 
126
138
  this.template = this.querySelector('template');
139
+ this.boundInitialized = false;
140
+ this.static = this.widget.hasAttribute('static')? true:false;
127
141
  // console.log('template',this.template);
128
142
  }
129
143
 
@@ -149,6 +163,8 @@ export default class FxControl extends XfAbstractControl {
149
163
  setValue(val) {
150
164
  const modelitem = this.getModelItem();
151
165
 
166
+ this.classList.add('visited');
167
+
152
168
  if (modelitem?.readonly){
153
169
  console.warn('attempt to change readonly node', modelitem);
154
170
  return; // do nothing when modelItem is readonly
@@ -160,13 +176,20 @@ export default class FxControl extends XfAbstractControl {
160
176
  replace.replace(this.nodeset, this.getWidget().value);
161
177
  if (modelitem && widgetValue && widgetValue !== modelitem.value) {
162
178
  modelitem.value = widgetValue;
179
+ FxModel.dataChanged = true;
163
180
  replace.actionPerformed();
164
181
  }
165
182
  return;
166
183
  }
167
184
  const setval = this.shadowRoot.getElementById('setvalue');
168
185
  setval.setValue(modelitem, val);
186
+
187
+ if (this.modelItem instanceof ModelItem && !this.modelItem?.boundControls.includes(this)) {
188
+ this.modelItem.boundControls.push(this);
189
+ }
190
+
169
191
  setval.actionPerformed();
192
+ this.visited = true;
170
193
  }
171
194
 
172
195
  _replaceNode(node) {
@@ -262,7 +285,7 @@ export default class FxControl extends XfAbstractControl {
262
285
  widget.value = this.nodeset.cloneNode(true);
263
286
  // todo: should be more like below but that can cause infinite loop when controll trigger update event due to calling a setter for property
264
287
  // widget[this.valueProp] = this.nodeset.cloneNode(true);
265
- console.log('passed value to widget', widget.value);
288
+ // console.log('passed value to widget', widget.value);
266
289
  }
267
290
 
268
291
  return;
@@ -271,6 +294,7 @@ export default class FxControl extends XfAbstractControl {
271
294
  // ### when there's a url Fore is used as widget and will be loaded from external file
272
295
  if (this.url && !this.loaded) {
273
296
  // ### evaluate initial data if necessary
297
+
274
298
  if (this.initial) {
275
299
  this.initialNode = evaluateXPathToFirstNode(this.initial, this.nodeset, this);
276
300
  console.log('initialNodes', this.initialNode);
@@ -309,7 +333,11 @@ export default class FxControl extends XfAbstractControl {
309
333
  * @private
310
334
  */
311
335
  async _loadForeFromUrl() {
312
- console.log('########## loading Fore from ', this.src, '##########');
336
+ console.log('########## loading Fore from ', this.url, '##########');
337
+ console.info(
338
+ `%cFore is processing URL ${this.url}`,
339
+ "background:#64b5f6; color:white; padding:1rem; display:inline-block; white-space: nowrap; border-radius:0.3rem;width:100%;",
340
+ );
313
341
  try {
314
342
  const response = await fetch(this.url, {
315
343
  method: 'GET',
@@ -319,6 +347,7 @@ export default class FxControl extends XfAbstractControl {
319
347
  'Content-Type': 'text/html',
320
348
  },
321
349
  });
350
+
322
351
  const responseContentType = response.headers.get('content-type').toLowerCase();
323
352
  console.log('********** responseContentType *********', responseContentType);
324
353
  let data;
@@ -332,37 +361,42 @@ export default class FxControl extends XfAbstractControl {
332
361
  }
333
362
  // const theFore = fxEvaluateXPathToFirstNode('//fx-fore', data.firstElementChild);
334
363
  const theFore = data.querySelector('fx-fore');
364
+ const imported = document.importNode(theFore,true);
365
+
335
366
  // console.log('thefore', theFore)
336
- theFore.classList.add('widget'); // is the new widget
367
+ imported.classList.add('widget'); // is the new widget
368
+ console.log(`########## loaded fore as component ##### ${this.url}`);
369
+ imported.addEventListener(
370
+ 'model-construct-done',
371
+ e => {
372
+ console.log('subcomponent ready', e.target);
373
+ const defaultInst = imported.querySelector('fx-instance');
374
+ // console.log('defaultInst', defaultInst);
375
+ if(this.initialNode){
376
+ const doc = new DOMParser().parseFromString('<data></data>', 'application/xml');
377
+ // Note: Clone the input to prevent the inner fore from editing the outer node
378
+ doc.firstElementChild.appendChild(this.initialNode.cloneNode(true));
379
+ // defaultinst.setInstanceData(this.initialNode);
380
+ defaultInst.setInstanceData(doc);
381
+ }
382
+ // console.log('new data', defaultInst.getInstanceData());
383
+ // theFore.getModel().modelConstruct();
384
+ imported.getModel().updateModel();
385
+ imported.refresh();
386
+ },
387
+ { once: true },
388
+ );
389
+
337
390
  const dummy = this.querySelector('input');
338
391
  if (this.hasAttribute('shadow')) {
339
392
  dummy.parentNode.removeChild(dummy);
340
- this.shadowRoot.appendChild(theFore);
393
+ this.shadowRoot.appendChild(imported);
341
394
  } else {
342
- dummy.replaceWith(theFore);
395
+ console.log(this, 'replacing widget with',theFore);
396
+ dummy.replaceWith(imported);
397
+ // this.appendChild(imported);
343
398
  }
344
399
 
345
- console.log(`########## loaded fore as component ##### ${this.url}`);
346
- theFore.addEventListener(
347
- 'model-construct-done',
348
- e => {
349
- console.log('subcomponent ready', e.target);
350
- const defaultInst = theFore.querySelector('fx-instance');
351
- console.log('defaultInst', defaultInst);
352
- if(this.initialNode){
353
- const doc = new DOMParser().parseFromString('<data></data>', 'application/xml');
354
- // Note: Clone the input to prevent the inner fore from editing the outer node
355
- doc.firstElementChild.appendChild(this.initialNode.cloneNode(true));
356
- // defaultinst.setInstanceData(this.initialNode);
357
- defaultInst.setInstanceData(doc);
358
- }
359
- console.log('new data', defaultInst.getInstanceData());
360
- // theFore.getModel().modelConstruct();
361
- theFore.getModel().updateModel();
362
- theFore.refresh();
363
- },
364
- { once: true },
365
- );
366
400
 
367
401
  if (!theFore) {
368
402
  this.dispatchEvent(
@@ -408,6 +442,8 @@ export default class FxControl extends XfAbstractControl {
408
442
  * @private
409
443
  */
410
444
  _handleBoundWidget(widget) {
445
+ if(this.boundInitialized && this.static) return;
446
+
411
447
  if (widget && widget.hasAttribute('ref')) {
412
448
  // ### eval nodeset for list control
413
449
  const ref = widget.getAttribute('ref');
@@ -445,18 +481,36 @@ export default class FxControl extends XfAbstractControl {
445
481
 
446
482
  if (nodeset.length) {
447
483
  // console.log('nodeset', nodeset);
484
+ const fragment = document.createDocumentFragment();
485
+ console.time('offscreen');
486
+ /*
487
+ Array.from(nodeset).forEach(node => {
488
+ // console.log('#### node', node);
489
+ const newEntry = this.createEntry();
490
+ this.template.parentNode.appendChild(newEntry);
491
+ // ### initialize new entry
492
+ // ### set value
493
+ this.updateEntry(newEntry, node);
494
+ });
495
+ */
496
+ // this should actually perform better than the above but does not seem to make a measurable difference.
448
497
  Array.from(nodeset).forEach(node => {
449
498
  // console.log('#### node', node);
450
499
  const newEntry = this.createEntry();
500
+ fragment.appendChild(newEntry);
451
501
 
452
502
  // ### initialize new entry
453
503
  // ### set value
454
504
  this.updateEntry(newEntry, node);
455
505
  });
506
+ this.template.parentNode.appendChild(fragment);
507
+ console.timeEnd('offscreen');
456
508
  } else {
457
509
  const newEntry = this.createEntry();
510
+ this.template.parentNode.appendChild(newEntry);
458
511
  this.updateEntry(newEntry, nodeset);
459
512
  }
513
+ this.boundInitialized = true;
460
514
  }
461
515
  }
462
516
  }
@@ -485,10 +539,12 @@ export default class FxControl extends XfAbstractControl {
485
539
  }
486
540
 
487
541
  createEntry() {
488
- const content = this.template.content.firstElementChild.cloneNode(true);
489
- const newEntry = document.importNode(content, true);
490
- this.template.parentNode.appendChild(newEntry);
491
- return newEntry;
542
+ return this.template.content.firstElementChild.cloneNode(true);
543
+ // const content = this.template.content.firstElementChild.cloneNode(true);
544
+ // return content;
545
+ // const newEntry = document.importNode(content, true);
546
+ // this.template.parentNode.appendChild(newEntry);
547
+ // return newEntry;
492
548
  }
493
549
 
494
550
  // eslint-disable-next-line class-methods-use-this
@@ -84,6 +84,11 @@ class FxGroup extends FxContainer {
84
84
 
85
85
  console.groupEnd();
86
86
  }
87
+
88
+ async refresh(force) {
89
+ super.refresh(force);
90
+ Fore.refreshChildren(this,force);
91
+ }
87
92
  }
88
93
 
89
94
  if (!customElements.get('fx-group')) {
@@ -25,6 +25,7 @@ export class FxInspector extends HTMLElement {
25
25
  /*max-height: 33%;*/
26
26
  overflow: scroll;
27
27
  transition:width 0.3s ease;
28
+ z-index:100;
28
29
  }
29
30
  :host([open]){
30
31
  width: 30%;
@@ -121,7 +122,6 @@ export class FxInspector extends HTMLElement {
121
122
  </div>
122
123
  `;
123
124
 
124
- /*
125
125
  const handle = this.shadowRoot.querySelector('.handle');
126
126
  handle.addEventListener('click', e => {
127
127
  // console.log('toggling');
@@ -132,10 +132,13 @@ export class FxInspector extends HTMLElement {
132
132
  this.setAttribute('open', 'open');
133
133
  }
134
134
  });
135
- */
136
135
  }
137
136
 
138
137
  serializeDOM(data) {
138
+ if(!data){
139
+ console.warn('no data to serialize');
140
+ return ;
141
+ }
139
142
  // console.log('serializeDOM', data);
140
143
  const ser = new XMLSerializer().serializeToString(data);
141
144
  return Fore.prettifyXml(ser);
@@ -51,8 +51,9 @@ export class FxOutput extends XfAbstractControl {
51
51
 
52
52
  const outputHtml = `
53
53
  <slot name="label"></slot>
54
+
54
55
  <span id="value">
55
- <slot id="main"></slot>
56
+ <slot></slot>
56
57
  </span>
57
58
  `;
58
59
 
@@ -67,9 +68,11 @@ export class FxOutput extends XfAbstractControl {
67
68
  // console.log('widget ', this.widget);
68
69
  this.mediatype = this.hasAttribute('mediatype') ? this.getAttribute('mediatype') : null;
69
70
 
71
+ /*
70
72
  this.addEventListener('slotchange', e => {
71
73
  console.log('slotchange ', e);
72
74
  });
75
+ */
73
76
  }
74
77
 
75
78
  async refresh() {
@@ -112,13 +115,14 @@ export class FxOutput extends XfAbstractControl {
112
115
  }
113
116
 
114
117
  async updateWidgetValue() {
115
- console.log('updateWidgetValue');
118
+ // console.log('updateWidgetValue');
116
119
  const valueWrapper = this.shadowRoot.getElementById('value');
117
120
 
118
- if (this.mediatype === 'markdown') {
119
- const md = markdown(this.nodeset);
120
- this.innerHtml = md;
121
- }
121
+
122
+ // if (this.mediatype === 'markdown') {
123
+ // const md = markdown(this.nodeset);
124
+ // this.innerHtml = md;
125
+ // }
122
126
 
123
127
  if (this.mediatype === 'html') {
124
128
  if (this.modelItem.node) {
@@ -130,16 +134,7 @@ export class FxOutput extends XfAbstractControl {
130
134
  const { node } = this.modelItem;
131
135
 
132
136
  if (node.nodeType) {
133
- // const mainSlot = this.shadowRoot.querySelector('#main');
134
- // valueWrapper.appendChild(node);
135
-
136
- // todo: checking if ownerDocument of node and ownerDocument of this are the same - otherwise import first
137
- // const imported = this.ownerDocument.importNode(node,true);
138
- // const clone = node.cloneNode(true);
139
-
140
137
  this.appendChild(node);
141
- // this.innerHtml = node;
142
- // this.innerHTML = node;
143
138
  return;
144
139
  }
145
140
  Object.entries(node).map(obj => {
@@ -156,6 +151,13 @@ export class FxOutput extends XfAbstractControl {
156
151
  return;
157
152
  }
158
153
 
154
+ if(this.mediatype === 'image'){
155
+ const img = document.createElement('img');
156
+ img.setAttribute('src',this.value);
157
+ this.appendChild(img);
158
+ return;
159
+ }
160
+
159
161
  valueWrapper.innerHTML = this.value;
160
162
  }
161
163
 
@@ -212,7 +212,7 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
212
212
  // console.group('fx-repeat.refresh on', this.id);
213
213
 
214
214
  if (!this.inited) this.init();
215
- console.time('repeat-refresh', this);
215
+ // console.time('repeat-refresh', this);
216
216
  this._evalNodeset();
217
217
  // console.log('repeat refresh nodeset ', this.nodeset);
218
218
  // console.log('repeatCount', this.repeatCount);
@@ -275,7 +275,7 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
275
275
  // this.style.display = 'block';
276
276
  // this.style.display = this.display;
277
277
  this.setIndex(this.index);
278
- console.timeEnd('repeat-refresh');
278
+ // console.timeEnd('repeat-refresh');
279
279
 
280
280
  // this.replaceWith(clone);
281
281
 
@@ -43,7 +43,7 @@ export class FxRepeatitem extends foreElementMixin(HTMLElement) {
43
43
  // console.log('_dispatchIndexChange on index ', this.index);
44
44
  if (this.parentNode) {
45
45
  this.parentNode.dispatchEvent(
46
- new CustomEvent('item-changed', { composed: true, bubbles: true, detail: { item: this } }),
46
+ new CustomEvent('item-changed', { composed: false, bubbles: true, detail: { item: this , index:this.index } }),
47
47
  );
48
48
  }
49
49
  }
@@ -90,12 +90,14 @@ export class FxRepeatitem extends foreElementMixin(HTMLElement) {
90
90
 
91
91
  if (this.modelItem && !this.modelItem.relevant) {
92
92
  // await Fore.fadeOutElement(this)
93
- this.style.display = 'none';
93
+ // this.style.display = 'none';
94
+ this.classList.add('nonrelevant');
94
95
  } else {
95
96
  // if(this.hasAttribute('repeat-index')){
96
97
  // Fore.fadeInElement(this);
97
98
  // }
98
- this.style.display = this.display;
99
+ // this.style.display = this.display;
100
+ this.classList.remove('nonrelevant');
99
101
  }
100
102
 
101
103
  /*
@@ -36,28 +36,31 @@ class FxSwitch extends FxContainer {
36
36
  `;
37
37
  }
38
38
 
39
- refresh() {
39
+ async refresh(force) {
40
40
  super.refresh();
41
- console.log('refresh on switch ');
41
+ // console.log('refresh on switch ');
42
42
  const cases = this.querySelectorAll(':scope > fx-case');
43
+ let selectedCase;
43
44
  if (this.isBound()) {
44
45
  Array.from(cases).forEach(caseElem => {
45
46
  const name = caseElem.getAttribute('name');
46
47
  if (name === this.modelItem.value) {
47
48
  caseElem.classList.add('selected-case');
49
+ selectedCase = caseElem;
48
50
  } else {
49
51
  caseElem.classList.remove('selected-case');
50
52
  }
51
53
  });
52
54
  } else {
53
- const selected = this.querySelector(':scope > .selected-case');
54
- if (!selected) {
55
- cases[0].classList.add('selected-case');
55
+ selectedCase = this.querySelector(':scope > .selected-case');
56
+ // if none is selected select the first as default
57
+ if (!selectedCase) {
58
+ selectedCase = cases[0];
59
+ selectedCase.classList.add('selected-case');
56
60
  }
57
61
  }
58
62
 
59
- Fore.refreshChildren(this);
60
- // console.log('value ', this.value);
63
+ Fore.refreshChildren(selectedCase,force);
61
64
  }
62
65
 
63
66
  toggle(caseElement) {
@@ -66,6 +69,7 @@ class FxSwitch extends FxContainer {
66
69
  if (caseElement === c) {
67
70
  // eslint-disable-next-line no-param-reassign
68
71
  c.classList.add('selected-case');
72
+ this.refresh();
69
73
  } else {
70
74
  // eslint-disable-next-line no-param-reassign
71
75
  c.classList.remove('selected-case');
@@ -1,9 +1,12 @@
1
1
  import XfAbstractControl from './abstract-control.js';
2
+ import {leadingDebounce} from "../events.js";
2
3
 
3
4
  export class FxTrigger extends XfAbstractControl {
4
5
  connectedCallback() {
5
6
  this.attachShadow({ mode: 'open' });
6
7
  this.ref = this.hasAttribute('ref') ? this.getAttribute('ref') : null;
8
+ this.debounceDelay = this.hasAttribute('debounce') ? this.getAttribute('debounce') : null;
9
+
7
10
  const style = `
8
11
  :host {
9
12
  cursor:pointer;
@@ -24,7 +27,17 @@ export class FxTrigger extends XfAbstractControl {
24
27
  elements[0].setAttribute('role', 'button');
25
28
 
26
29
  const element = elements[0];
27
- element.addEventListener('click', e => this.performActions(e));
30
+
31
+ if(this.debounceDelay){
32
+ this.addEventListener(
33
+ 'click',
34
+ leadingDebounce(this,(e) => {
35
+ this.performActions(e)
36
+ }, this.debounceDelay),
37
+ );
38
+ }else{
39
+ element.addEventListener('click', e => this.performActions(e));
40
+ }
28
41
  this.widget = element;
29
42
  // # terrible hack but browser behaves strange - seems to fire a 'click' for a button when it receives a
30
43
  // # 'Space' or 'Enter' key
@@ -36,14 +49,6 @@ export class FxTrigger extends XfAbstractControl {
36
49
  });
37
50
  }
38
51
  });
39
- /*
40
- this.addEventListener('click', e => this.performActions(e));
41
- this.addEventListener('keypress', (e) => {
42
- if(e.code === 'Space' || e.code === 'Enter'){
43
- this.performActions(e);
44
- }
45
- });
46
- */
47
52
  }
48
53
 
49
54
  // eslint-disable-next-line class-methods-use-this
@@ -89,6 +94,7 @@ export class FxTrigger extends XfAbstractControl {
89
94
  if (typeof child.execute === 'function') {
90
95
  // eslint-disable-next-line no-await-in-loop
91
96
  await child.execute(e);
97
+ // child.execute(e);
92
98
  }
93
99
  }
94
100
  };
@@ -146,6 +146,30 @@ export function resolveId(id, sourceObject, nodeName = null) {
146
146
  // Make namespace resolving use the `instance` element that is related to here
147
147
  const xmlDocument = new DOMParser().parseFromString('<xml />', 'text/xml');
148
148
 
149
+ function findInstanceReferences(xpathQuery) {
150
+ if (!xpathQuery.includes('instance')) {
151
+ // No call to the instance function anyway: short-circuit and prevent AST processing
152
+ return [];
153
+ }
154
+ const xpathAST = parseScript(xpathQuery, {}, xmlDocument);
155
+ const instanceReferences = fxEvaluateXPathToStrings(
156
+ `descendant::xqx:functionCallExpr
157
+ [xqx:functionName = "instance"]
158
+ /xqx:arguments
159
+ /xqx:stringConstantExpr
160
+ /xqx:value`,
161
+ xpathAST,
162
+ null,
163
+ {},
164
+ {
165
+ namespaceResolver: prefix =>
166
+ prefix === 'xqx' ? 'http://www.w3.org/2005/XQueryX' : undefined,
167
+ },
168
+ );
169
+
170
+ return instanceReferences;
171
+ }
172
+
149
173
  /**
150
174
  * Resolve a namespace. Needs a namespace prefix and the element that is most closely related to the
151
175
  * XPath in which the namespace is being resolved. The prefix will be resolved by using the
@@ -165,25 +189,12 @@ function createNamespaceResolver(xpathQuery, formElement) {
165
189
  if (cachedResolver) {
166
190
  return cachedResolver;
167
191
  }
168
-
169
- const xpathAST = parseScript(xpathQuery, {}, xmlDocument);
170
- let instanceReferences = fxEvaluateXPathToStrings(
171
- `descendant::xqx:functionCallExpr
172
- [xqx:functionName = "instance"]
173
- /xqx:arguments
174
- /xqx:stringConstantExpr
175
- /xqx:value`,
176
- xpathAST,
177
- null,
178
- {},
179
- {
180
- namespaceResolver: prefix =>
181
- prefix === 'xqx' ? 'http://www.w3.org/2005/XQueryX' : undefined,
182
- },
183
- );
192
+ let instanceReferences = findInstanceReferences(xpathQuery);
184
193
  if (instanceReferences.length === 0) {
185
194
  // No instance functions. Look up further in the hierarchy to see if we can deduce the intended context from there
186
- const ancestorComponent = fxEvaluateXPathToFirstNode('ancestor::*[@ref][1]', formElement);
195
+ const ancestorComponent = formElement.parentNode &&
196
+ formElement.parentNode.nodeType === formElement.ELEMENT &&
197
+ formElement.parentNode.closest('[ref]');
187
198
  if (ancestorComponent) {
188
199
  const resolver = createNamespaceResolver(
189
200
  ancestorComponent.getAttribute('ref'),
@@ -327,7 +338,9 @@ function getVariablesInScope(formElement) {
327
338
  if (closestActualFormElement.inScopeVariables) {
328
339
  for (const key of closestActualFormElement.inScopeVariables.keys()) {
329
340
  const varElement = closestActualFormElement.inScopeVariables.get(key);
330
- variables[key] = varElement.value;
341
+ if(varElement){
342
+ variables[key] = varElement.value;
343
+ }
331
344
  }
332
345
  }
333
346
  return variables;