@jinntec/fore 2.3.1 → 2.4.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jinntec/fore",
3
- "version": "2.3.1",
3
+ "version": "2.4.0",
4
4
  "description": "Fore - declarative user interfaces in plain HTML",
5
5
  "module": "./index.js",
6
6
  "publishConfig": {
@@ -74,6 +74,9 @@
74
74
  fx-case {
75
75
  display: none;
76
76
  }
77
+ fx-case.selected-case{
78
+ display:block;
79
+ }
77
80
 
78
81
  fx-output[readonly] img {
79
82
  background: inherit;
@@ -0,0 +1,51 @@
1
+ export default class DependentXPathQueries {
2
+ constructor() {
3
+ /**
4
+ * @type {Set<string>}
5
+ */
6
+ this._xpaths = new Set();
7
+
8
+ this._parentDependencies = null;
9
+ }
10
+
11
+ /**
12
+ * Returns true if this Fore element should be refreshed if a result of an index function changes
13
+ *
14
+ * @returns {boolean}
15
+ */
16
+ isInvalidatedByIndexFunction() {
17
+ for (const xpath of this._xpaths) {
18
+ // TODO: this can be done a lot better with parsing / checking for function references
19
+ if (xpath.includes('index(')) {
20
+ return true;
21
+ }
22
+ }
23
+
24
+ // We can also depend on the index function if it was used in our ancestry
25
+ return !!this._parentDependencies?.isInvalidatedByIndexFunction();
26
+ }
27
+
28
+ /**
29
+ * Add an XPath to the dependencies
30
+ *
31
+ * @param {string} xpath the XPath to add
32
+ */
33
+ addXPath(xpath) {
34
+ this._xpaths.add(xpath);
35
+ }
36
+
37
+ /**
38
+ * Reset the dependencies on refresh
39
+ *
40
+ */
41
+ resetDependencies() {
42
+ this._xpaths.clear();
43
+ }
44
+
45
+ /**
46
+ * @param {DependentXPathQueries} deps
47
+ */
48
+ setParentDependencies(deps) {
49
+ this._parentDependencies = deps;
50
+ }
51
+ }
@@ -7,6 +7,7 @@ import {
7
7
  } from './xpath-evaluation.js';
8
8
  import getInScopeContext from './getInScopeContext.js';
9
9
  import { Fore } from './fore.js';
10
+ import DependentXPathQueries from './DependentXPathQueries.js';
10
11
 
11
12
  /**
12
13
  * Mixin containing all general functions that are shared by all Fore element classes.
@@ -61,6 +62,9 @@ export default class ForeElementMixin extends HTMLElement {
61
62
  * @type {Map<string, import('./fx-var.js').FxVariable>}
62
63
  */
63
64
  this.inScopeVariables = new Map();
65
+
66
+ this._dependencies = new DependentXPathQueries();
67
+ this._dependencies.setParentDependencies(this.parent?.closest('[ref]')?._dependencies);
64
68
  }
65
69
 
66
70
  /**
@@ -103,6 +107,7 @@ export default class ForeElementMixin extends HTMLElement {
103
107
  * evaluation of fx-bind and UiElements differ in details so that each class needs it's own implementation.
104
108
  */
105
109
  evalInContext() {
110
+ this._dependencies.resetDependencies();
106
111
  // const inscopeContext = this.getInScopeContext();
107
112
  const model = this.getModel();
108
113
  if (!model) {
@@ -114,6 +119,7 @@ export default class ForeElementMixin extends HTMLElement {
114
119
  }
115
120
  if (this.hasAttribute('ref')) {
116
121
  inscopeContext = getInScopeContext(this.getAttributeNode('ref') || this, this.ref);
122
+ this._dependencies.addXPath(this.ref);
117
123
  }
118
124
  if (!inscopeContext && this.getModel().instances.length !== 0) {
119
125
  // ### always fall back to default context with there's neither a 'context' or 'ref' present
package/src/fore.js CHANGED
@@ -275,7 +275,8 @@ export class Fore {
275
275
  * recursively refreshes all UI Elements.
276
276
  *
277
277
  * @param {HTMLElement} startElement
278
- * @param {boolean} force
278
+ * @param {(boolean|{reason:'index-function'})} force Whether to do a forced refresh. Forced
279
+ * refreshes are very bad for performance, try to limit them. If the forced refresh is because index functions may change, it is better to pass the reason
279
280
  * @returns {Promise<void>}
280
281
  */
281
282
  static async refreshChildren(startElement, force) {
@@ -298,19 +299,35 @@ export class Fore {
298
299
  */
299
300
  const { children } = startElement;
300
301
  if (children) {
301
- Array.from(children).forEach(element => {
302
+ for (const element of Array.from(children)) {
302
303
  if (element.nodeName.toUpperCase() === 'FX-FORE') {
303
- resolve('done');
304
- return;
304
+ break;
305
305
  }
306
- if (Fore.isUiElement(element.nodeName) && typeof element.refresh === 'function') {
306
+ if (Fore.isUiElement(element.nodeName)) {
307
+ /**
308
+ * @type {import('./ForeElementMixin.js').default}
309
+ */
310
+ const foreElement = element;
311
+
312
+ if (typeof foreElement.refresh === 'function') {
313
+ if (
314
+ force &&
315
+ typeof force === 'object' &&
316
+ force.reason === 'index-function' &&
317
+ foreElement._dependencies.isInvalidatedByIndexFunction()
318
+ ) {
319
+ element.refresh(force);
320
+ continue;
321
+ } else if (force === true) {
322
+ element.refresh(force);
323
+ }
324
+ }
307
325
  // console.log('refreshing', element, element?.ref);
308
326
  // console.log('refreshing ',element);
309
- element.refresh(force);
310
327
  } else if (element.nodeName.toUpperCase() !== 'FX-MODEL') {
311
328
  Fore.refreshChildren(element, force);
312
329
  }
313
- });
330
+ }
314
331
  }
315
332
  resolve('done');
316
333
  });
package/src/fx-fore.js CHANGED
@@ -484,6 +484,9 @@ export class FxFore extends HTMLElement {
484
484
  }
485
485
 
486
486
  // async refresh(force, changedPaths) {
487
+ /**
488
+ * @param {(boolean|{reason:'index-function'})} [force]fx-fore
489
+ */
487
490
  async refresh(force) {
488
491
  /*
489
492
 
@@ -578,7 +581,7 @@ export class FxFore extends HTMLElement {
578
581
  */
579
582
 
580
583
  if (this.inited) {
581
- Fore.refreshChildren(this, true);
584
+ Fore.refreshChildren(this, force);
582
585
  }
583
586
  // console.timeEnd('refreshChildren');
584
587
  }
@@ -620,7 +623,7 @@ export class FxFore extends HTMLElement {
620
623
  for (const subFore of subFores) {
621
624
  // subFore.refresh(false, changedPaths);
622
625
  if (subFore.ready) {
623
- await subFore.refresh(false);
626
+ await subFore.refresh(force);
624
627
  }
625
628
  }
626
629
  this.isRefreshing = false;
@@ -9,6 +9,11 @@ function isDifferent(oldNodeValue, oldControlValue, newControlValue) {
9
9
  if (oldNodeValue === null) {
10
10
  return false;
11
11
  }
12
+ /*
13
+ if the oldControlValue is null we know the widget is used for the first time and is not considered
14
+ a value change.
15
+ */
16
+ if(oldControlValue === null) return false;
12
17
 
13
18
  if (newControlValue && oldControlValue && newControlValue.nodeType && oldControlValue.nodeType) {
14
19
  return newControlValue.outerHTML !== oldControlValue.outerHTML;
@@ -165,7 +170,8 @@ export default class AbstractControl extends ForeElementMixin {
165
170
  // if oldVal is null we haven't received a concrete value yet
166
171
 
167
172
  if (this.localName !== 'fx-control') return;
168
- if (isDifferent(this.oldVal, this.value, oldValue)) {
173
+ if (isDifferent(this.oldVal, oldValue, this.value)) {
174
+ const model = this.getModel();
169
175
  Fore.dispatch(this, 'value-changed', {
170
176
  path: this.modelItem.path,
171
177
  value: this.modelItem.value,
@@ -91,7 +91,9 @@ class FxGroup extends FxContainer {
91
91
 
92
92
  async refresh(force) {
93
93
  super.refresh(force);
94
- Fore.refreshChildren(this, force);
94
+ // Make the maybe filtered refresh an unconditional forced refresh: This fx-group changes the
95
+ // context item
96
+ Fore.refreshChildren(this, !!force);
95
97
  }
96
98
  }
97
99
 
@@ -80,7 +80,7 @@ export class FxRepeat extends withDraggability(ForeElementMixin, false) {
80
80
  const rItems = this.querySelectorAll(':scope > fx-repeatitem');
81
81
  this.applyIndex(rItems[this.index - 1]);
82
82
 
83
- this.getOwnerForm().refresh(true);
83
+ this.getOwnerForm().refresh({ reason: 'index-function' });
84
84
  }
85
85
 
86
86
  applyIndex(repeatItem) {
@@ -28,7 +28,7 @@ class FxSwitch extends FxContainer {
28
28
  }
29
29
  const style = `
30
30
  :host ::slotted(fx-case.selected-case){
31
- display: block !important;
31
+ display: block;
32
32
  }
33
33
  `;
34
34
  const html = `