@jinntec/fore 1.0.0-3 → 1.0.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 (62) hide show
  1. package/README.md +25 -34
  2. package/dist/fore-dev.js +43 -0
  3. package/dist/fore-dev.js.map +1 -0
  4. package/dist/fore.js +36 -1
  5. package/dist/fore.js.map +1 -0
  6. package/index.js +3 -0
  7. package/package.json +34 -38
  8. package/resources/fore.css +35 -50
  9. package/src/DependencyNotifyingDomFacade.js +10 -16
  10. package/src/ForeElementMixin.js +25 -18
  11. package/src/actions/abstract-action.js +17 -9
  12. package/src/actions/fx-action.js +6 -4
  13. package/src/actions/fx-append.js +8 -17
  14. package/src/actions/fx-confirm.js +3 -1
  15. package/src/actions/fx-delete.js +6 -3
  16. package/src/actions/fx-dispatch.js +9 -8
  17. package/src/actions/fx-hide.js +10 -6
  18. package/src/actions/fx-insert.js +49 -39
  19. package/src/actions/fx-message.js +3 -1
  20. package/src/actions/fx-refresh.js +11 -1
  21. package/src/actions/fx-replace.js +68 -0
  22. package/src/actions/fx-return.js +42 -0
  23. package/src/actions/fx-send.js +3 -1
  24. package/src/actions/fx-setvalue.js +58 -51
  25. package/src/actions/fx-show.js +8 -4
  26. package/src/actions/fx-toggle.js +15 -10
  27. package/src/actions/fx-update.js +3 -1
  28. package/src/dep_graph.js +4 -2
  29. package/src/drawdown.js +67 -82
  30. package/src/fore.js +158 -11
  31. package/src/functions/fx-function.js +11 -3
  32. package/src/fx-bind.js +42 -202
  33. package/src/fx-fore.js +105 -70
  34. package/src/fx-header.js +3 -1
  35. package/src/fx-instance.js +9 -1
  36. package/src/fx-model.js +31 -23
  37. package/src/fx-submission.js +73 -47
  38. package/src/fx-var.js +7 -4
  39. package/src/getInScopeContext.js +37 -11
  40. package/src/modelitem.js +4 -4
  41. package/src/relevance.js +65 -0
  42. package/src/ui/abstract-control.js +55 -35
  43. package/src/ui/fx-alert.js +7 -1
  44. package/src/ui/fx-case.js +3 -1
  45. package/src/ui/fx-container.js +7 -1
  46. package/src/ui/fx-control.js +283 -33
  47. package/src/ui/fx-dialog.js +54 -40
  48. package/src/ui/fx-group.js +3 -1
  49. package/src/ui/fx-hint.js +4 -1
  50. package/src/ui/fx-inspector.js +117 -17
  51. package/src/ui/fx-items.js +8 -8
  52. package/src/ui/fx-output.js +14 -5
  53. package/src/ui/fx-repeat.js +33 -41
  54. package/src/ui/fx-repeatitem.js +10 -4
  55. package/src/ui/fx-switch.js +3 -1
  56. package/src/ui/fx-trigger.js +3 -1
  57. package/src/xpath-evaluation.js +121 -131
  58. package/src/xpath-util.js +14 -7
  59. package/dist/fore-all.js +0 -140
  60. package/src/.DS_Store +0 -0
  61. package/src/actions/.DS_Store +0 -0
  62. package/src/ui/.DS_Store +0 -0
@@ -1,4 +1,5 @@
1
1
  import { Fore } from './fore.js';
2
+ import { Relevance } from './relevance.js';
2
3
  import { foreElementMixin } from './ForeElementMixin.js';
3
4
  import { evaluateXPathToString, evaluateXPath } from './xpath-evaluation.js';
4
5
  import getInScopeContext from './getInScopeContext.js';
@@ -66,7 +67,7 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
66
67
  }
67
68
 
68
69
  async submit() {
69
- await this.dispatch('submit', { submission: this });
70
+ await Fore.dispatch(this, 'submit', { submission: this });
70
71
  this._submit();
71
72
  }
72
73
 
@@ -82,7 +83,8 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
82
83
  if (!valid) {
83
84
  console.log('validation failed. Bubmission stopped');
84
85
  // ### allow alerts to pop up
85
- this.dispatch('submit-error', {});
86
+ // this.dispatch('submit-error', {});
87
+ Fore.dispatch(this, 'submit-error', {});
86
88
  this.getModel().parentNode.refresh();
87
89
  return;
88
90
  }
@@ -117,24 +119,20 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
117
119
  /**
118
120
  * sends the data after evaluating
119
121
  *
120
- * todo: can send only XML at the moment
121
122
  * @private
122
123
  */
123
124
  async _serializeAndSend() {
124
125
  const resolvedUrl = this._evaluateAttributeTemplateExpression(this.url, this);
125
126
 
126
127
  const instance = this.getInstance();
127
- if (instance.type !== 'xml') {
128
- console.error('JSON serialization is not supported yet');
129
- return;
130
- }
128
+ console.log('instance type', instance.type);
131
129
 
132
- // let serialized = serializer.serializeToString(this.nodeset);
133
130
  let serialized;
134
131
  if (this.serialization === 'none') {
135
132
  serialized = undefined;
136
133
  } else {
137
- const relevant = this.selectRelevant();
134
+ // const relevant = this.selectRelevant(instance.type);
135
+ const relevant = Relevance.selectRelevant(this, instance.type);
138
136
  serialized = this._serialize(instance.type, relevant);
139
137
  }
140
138
 
@@ -145,17 +143,18 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
145
143
  // console.log('data being send', serialized);
146
144
  // console.log('submitting data',serialized);
147
145
 
148
- if (resolvedUrl === '#echo') {
149
- let doc;
150
- if (serialized) {
151
- doc = new DOMParser().parseFromString(serialized, 'application/xml');
152
- } else {
153
- doc = undefined;
146
+ // if (resolvedUrl === '#echo') {
147
+ if (resolvedUrl.startsWith('#echo')) {
148
+ let data = null;
149
+ if (serialized && instance.type === 'xml') {
150
+ data = new DOMParser().parseFromString(serialized, 'application/xml');
151
+ }
152
+ if (serialized && instance.type === 'json') {
153
+ data = JSON.parse(serialized);
154
154
  }
155
- // const doc = new DOMParser().parseFromString(serialized, 'application/xml');
156
- // const newDoc = doc.replaceChild(relevant, doc.firstElementChild);
157
- this._handleResponse(doc);
158
- this.dispatch('submit-done', {});
155
+ this._handleResponse(data);
156
+ // this.dispatch('submit-done', {});
157
+ Fore.dispatch(this, 'submit-done', {});
159
158
  return;
160
159
  }
161
160
  // ### setting headers
@@ -168,7 +167,8 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
168
167
  }
169
168
 
170
169
  if (!this.methods.includes(this.method.toLowerCase())) {
171
- this.dispatch('error', { message: `Unknown method ${this.method}` });
170
+ // this.dispatch('error', { message: `Unknown method ${this.method}` });
171
+ Fore.dispatch(this, 'error', { message: `Unknown method ${this.method}` });
172
172
  return;
173
173
  }
174
174
  const response = await fetch(resolvedUrl, {
@@ -180,7 +180,8 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
180
180
  });
181
181
 
182
182
  if (!response.ok || response.status > 400) {
183
- this.dispatch('submit-error', { message: `Error while submitting ${this.id}` });
183
+ // this.dispatch('submit-error', { message: `Error while submitting ${this.id}` });
184
+ Fore.dispatch(this, 'submit-error', { message: `Error while submitting ${this.id}` });
184
185
  return;
185
186
  }
186
187
 
@@ -204,7 +205,8 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
204
205
  this._handleResponse(blob);
205
206
  }
206
207
 
207
- this.dispatch('submit-done', {});
208
+ // this.dispatch('submit-done', {});
209
+ Fore.dispatch(this, 'submit-done', {});
208
210
  }
209
211
 
210
212
  _serialize(instanceType, relevantNodes) {
@@ -221,11 +223,10 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
221
223
  const serializer = new XMLSerializer();
222
224
  return serializer.serializeToString(relevantNodes);
223
225
  }
224
- /*
225
- if(instanceType === 'json'){
226
- console.warn('JSON serialization is not yet supported')
227
- }
228
- */
226
+ if (instanceType === 'json') {
227
+ // console.warn('JSON serialization is not yet supported')
228
+ return JSON.stringify(relevantNodes);
229
+ }
229
230
  throw new Error('unknown instance type ', instanceType);
230
231
  }
231
232
 
@@ -269,13 +270,28 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
269
270
  return targetInstance;
270
271
  }
271
272
 
273
+ /**
274
+ * handles replacement of instance data from response data.
275
+ *
276
+ * Please note that data might be
277
+ * @param data
278
+ * @private
279
+ */
272
280
  _handleResponse(data) {
273
281
  console.log('_handleResponse ', data);
282
+
283
+ /*
284
+ // ### responses need to be handled depending on their type.
285
+ if(this.type === 'json'){
286
+
287
+ }
288
+ */
289
+
274
290
  if (this.replace === 'instance') {
275
291
  const targetInstance = this._getTargetInstance();
276
292
  if (targetInstance) {
277
293
  if (this.targetref) {
278
- const theTarget = evaluateXPath(
294
+ const [theTarget] = evaluateXPath(
279
295
  this.targetref,
280
296
  targetInstance.instanceData.firstElementChild,
281
297
  this,
@@ -286,7 +302,7 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
286
302
  parent.replaceChild(clone, theTarget);
287
303
  console.log('finally ', parent);
288
304
  } else if (this.into) {
289
- const theTarget = evaluateXPath(
305
+ const [theTarget] = evaluateXPath(
290
306
  this.into,
291
307
  targetInstance.instanceData.firstElementChild,
292
308
  this,
@@ -296,6 +312,7 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
296
312
  } else {
297
313
  const instanceData = data;
298
314
  targetInstance.instanceData = instanceData;
315
+ console.log('### replaced instance ', this.getModel().instances);
299
316
  console.log('### replaced instance ', targetInstance.instanceData);
300
317
  }
301
318
 
@@ -318,26 +335,29 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
318
335
  if (this.replace === 'redirect') {
319
336
  window.location.href = data;
320
337
  }
321
-
322
- /*
323
- const event = new CustomEvent('submit-done', {
324
- composed: true,
325
- bubbles: true,
326
- detail: {},
327
- });
328
- console.log('firing',event);
329
- this.dispatchEvent(event);
330
- */
331
- // this.dispatch('submit-done', {});
332
338
  }
333
339
 
334
340
  /**
335
341
  * select relevant nodes
336
342
  *
337
- * todo: support for 'empty'
338
343
  * @returns {*}
339
344
  */
340
- selectRelevant() {
345
+ /*
346
+ selectRelevant(type) {
347
+ console.log('selectRelevant' ,type)
348
+ switch (type){
349
+ case 'xml':
350
+ return this._relevantXmlNodes();
351
+ default:
352
+ console.warn(`relevance selection not supported for type:${this.type}`);
353
+ return this.nodeset;
354
+ }
355
+ }
356
+ */
357
+
358
+ // todo: support for 'empty'
359
+ /*
360
+ _relevantXmlNodes() {
341
361
  // ### no relevance selection - current nodeset is used 'as-is'
342
362
  if (this.nonrelevant === 'keep') {
343
363
  return this.nodeset;
@@ -353,10 +373,11 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
353
373
  if (this.nodeset.children.length === 0 && this._isRelevant(this.nodeset)) {
354
374
  return this.nodeset;
355
375
  }
356
- const result = this._filterRelevant(this.nodeset, root);
357
- return result;
376
+ return this._filterRelevant(this.nodeset, root);
358
377
  }
378
+ */
359
379
 
380
+ /*
360
381
  _filterRelevant(node, result) {
361
382
  const { childNodes } = node;
362
383
  Array.from(childNodes).forEach(n => {
@@ -381,7 +402,9 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
381
402
  });
382
403
  return result;
383
404
  }
405
+ */
384
406
 
407
+ /*
385
408
  _isRelevant(node) {
386
409
  const mi = this.getModel().getModelItem(node);
387
410
  if (!mi || mi.relevant) {
@@ -389,9 +412,11 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
389
412
  }
390
413
  return false;
391
414
  }
415
+ */
392
416
 
393
417
  _handleError() {
394
- this.dispatch('submit-error', {});
418
+ // this.dispatch('submit-error', {});
419
+ Fore.dispatch(this, 'submit-error', {});
395
420
  /*
396
421
  console.log('ERRRORRRRR');
397
422
  this.dispatchEvent(
@@ -404,5 +429,6 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
404
429
  */
405
430
  }
406
431
  }
407
-
408
- customElements.define('fx-submission', FxSubmission);
432
+ if (!customElements.get('fx-submission')) {
433
+ customElements.define('fx-submission', FxSubmission);
434
+ }
package/src/fx-var.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { Fore } from './fore.js';
1
2
  import './fx-instance.js';
2
3
  import { evaluateXPath } from './xpath-evaluation.js';
3
4
  import { foreElementMixin } from './ForeElementMixin.js';
@@ -24,13 +25,14 @@ export class FxVariable extends foreElementMixin(HTMLElement) {
24
25
  refresh() {
25
26
  const inscope = getInScopeContext(this, this.valueQuery);
26
27
 
27
- this.value = evaluateXPath(this.valueQuery, inscope, this, this.precedingVariables);
28
+ const values = evaluateXPath(this.valueQuery, inscope, this, this.precedingVariables);
29
+ [this.value] = values;
28
30
  }
29
31
 
30
32
  setInScopeVariables(inScopeVariables) {
31
33
  if (inScopeVariables.has(this.name)) {
32
34
  console.error(`The variable ${this.name} is declared more than once`);
33
- this.dispatch('xforms-binding-error');
35
+ Fore.dispatch(this, 'xforms-binding-error', {});
34
36
  return;
35
37
  }
36
38
  inScopeVariables.set(this.name, this);
@@ -39,5 +41,6 @@ export class FxVariable extends foreElementMixin(HTMLElement) {
39
41
  this.inScopeVariables = new Map(inScopeVariables);
40
42
  }
41
43
  }
42
-
43
- customElements.define('fx-var', FxVariable);
44
+ if (!customElements.get('fx-var')) {
45
+ customElements.define('fx-var', FxVariable);
46
+ }
@@ -1,9 +1,10 @@
1
1
  import { evaluateXPathToFirstNode } from './xpath-evaluation.js';
2
+ import { Fore } from './fore.js';
2
3
 
3
4
  import { XPathUtil } from './xpath-util.js';
4
5
 
5
6
  function _getElement(node) {
6
- if (node.nodeType === Node.ATTRIBUTE_NODE) {
7
+ if (node && node.nodeType && node.nodeType === Node.ATTRIBUTE_NODE) {
7
8
  // The context of an attribute is the ref of the element it's defined on
8
9
  return node.ownerElement;
9
10
  }
@@ -28,16 +29,28 @@ function _getModelInContext(node) {
28
29
  }
29
30
 
30
31
  function _getInitialContext(node, ref) {
31
- const parentBind = node.closest('[ref]');
32
+ const parentBind = Fore.getClosest('[ref]', node);
33
+ const localFore = Fore.getClosest('fx-fore', node);
34
+
35
+ const model = _getModelInContext(node);
32
36
 
33
37
  if (parentBind !== null) {
34
- return parentBind.nodeset;
38
+ /*
39
+ make sure that the closest ref belongs to the same fx-fore element
40
+ */
41
+ const parentBindFore = parentBind.closest('fx-fore');
42
+ if (localFore === parentBindFore) {
43
+ return parentBind.nodeset;
44
+ }
45
+ return model.getDefaultInstance().getDefaultContext();
35
46
  }
36
47
 
37
- const model = _getModelInContext(node);
38
48
  if (XPathUtil.isAbsolutePath(ref)) {
39
49
  const instanceId = XPathUtil.getInstanceId(ref);
40
- return model.getInstance(instanceId).getDefaultContext();
50
+ if (instanceId) {
51
+ return model.getInstance(instanceId).getDefaultContext();
52
+ }
53
+ return model.getDefaultInstance().getDefaultContext();
41
54
  }
42
55
  if (model.getDefaultInstance() !== null && model.inited) {
43
56
  return model.getDefaultInstance().getDefaultContext();
@@ -47,17 +60,30 @@ function _getInitialContext(node, ref) {
47
60
 
48
61
  export default function getInScopeContext(node, ref) {
49
62
  const parentElement = _getElement(node);
50
- /*
51
- if(parentElement.nodeName.toUpperCase() === 'FX-REPEATITEM'){
52
- return parentElement.nodeset;
53
- }
54
- */
55
63
 
56
- const repeatItem = parentElement.closest('fx-repeatitem');
64
+ const repeatItem = Fore.getClosest('fx-repeatitem', parentElement);
57
65
  if (repeatItem) {
66
+ if (node.nodeName === 'context') {
67
+ return evaluateXPathToFirstNode(
68
+ node.nodeValue,
69
+ repeatItem.nodeset,
70
+ _getForeContext(parentElement),
71
+ );
72
+ }
58
73
  return repeatItem.nodeset;
59
74
  }
60
75
 
76
+ if (parentElement.hasAttribute('context')) {
77
+ const initialContext = _getInitialContext(parentElement.parentNode, ref);
78
+ const contextAttr = parentElement.getAttribute('context');
79
+ return evaluateXPathToFirstNode(contextAttr, initialContext, _getForeContext(parentElement));
80
+ }
81
+
82
+ if (node.nodeType === Node.ATTRIBUTE_NODE && node.nodeName === 'context') {
83
+ const initialContext = _getInitialContext(node.ownerElement.parentNode, ref);
84
+ const contextAttr = node.ownerElement.getAttribute('context');
85
+ return evaluateXPathToFirstNode(contextAttr, initialContext, _getForeContext(parentElement));
86
+ }
61
87
  if (node.nodeType === Node.ATTRIBUTE_NODE && node.nodeName === 'ref') {
62
88
  // Note: do not consider the ref of the owner element since it should not be used to define the
63
89
  // context
package/src/modelitem.js CHANGED
@@ -54,11 +54,11 @@ export class ModelItem {
54
54
  console.log('modelitem.setvalue newVal', newVal);
55
55
 
56
56
  if (newVal.nodeType === Node.DOCUMENT_NODE) {
57
- // this.node.replaceWith(newVal.firstElementChild);
58
- this.node.appendChild(newVal.firstElementChild);
57
+ this.node.replaceWith(newVal.firstElementChild);
58
+ // this.node.appendChild(newVal.firstElementChild);
59
59
  } else if (newVal.nodeType === Node.ELEMENT_NODE) {
60
- // this.node.replaceWith(newVal);
61
- this.node.appendChild(newVal);
60
+ this.node.replaceWith(newVal);
61
+ // this.node.appendChild(newVal);
62
62
  } else if (this.node.nodeType === Node.ATTRIBUTE_NODE) {
63
63
  this.node.nodeValue = newVal;
64
64
  } else {
@@ -0,0 +1,65 @@
1
+ export class Relevance {
2
+ static selectRelevant(element, type) {
3
+ console.log('selectRelevant', type);
4
+ switch (type) {
5
+ case 'xml':
6
+ return Relevance._relevantXmlNodes(element);
7
+ default:
8
+ console.warn(`relevance selection not supported for type:${element.type}`);
9
+ return element.nodeset;
10
+ }
11
+ }
12
+
13
+ static _relevantXmlNodes(element) {
14
+ // ### no relevance selection - current nodeset is used 'as-is'
15
+ const nonrelevant = element.getAttribute('nonrelevant');
16
+ if (nonrelevant === 'keep') {
17
+ return element.nodeset;
18
+ }
19
+
20
+ // first check if nodeset of submission is relevant - otherwise bail out
21
+ const mi = element.getModel().getModelItem(element.nodeset);
22
+ if (mi && !mi.relevant) return null;
23
+
24
+ const doc = new DOMParser().parseFromString('<data></data>', 'application/xml');
25
+ const root = doc.firstElementChild;
26
+
27
+ if (element.nodeset.children.length === 0 && Relevance._isRelevant(element, element.nodeset)) {
28
+ return element.nodeset;
29
+ }
30
+ return Relevance._filterRelevant(element, element.nodeset, root);
31
+ }
32
+
33
+ static _filterRelevant(element, node, result) {
34
+ const { childNodes } = node;
35
+ Array.from(childNodes).forEach(n => {
36
+ if (Relevance._isRelevant(element, n)) {
37
+ const clone = n.cloneNode(false);
38
+ result.appendChild(clone);
39
+ const { attributes } = n;
40
+ if (attributes) {
41
+ Array.from(attributes).forEach(attr => {
42
+ if (Relevance._isRelevant(element, attr)) {
43
+ clone.setAttribute(attr.nodeName, attr.value);
44
+ } else if (element.nonrelevant === 'empty') {
45
+ clone.setAttribute(attr.nodeName, '');
46
+ } else {
47
+ clone.removeAttribute(attr.nodeName);
48
+ }
49
+ });
50
+ }
51
+ return Relevance._filterRelevant(element, n, clone);
52
+ }
53
+ return null;
54
+ });
55
+ return result;
56
+ }
57
+
58
+ static _isRelevant(element, node) {
59
+ const mi = element.getModel().getModelItem(node);
60
+ if (!mi || mi.relevant) {
61
+ return true;
62
+ }
63
+ return false;
64
+ }
65
+ }
@@ -1,6 +1,7 @@
1
1
  import '../fx-model.js';
2
2
  import { foreElementMixin } from '../ForeElementMixin.js';
3
3
  import { ModelItem } from '../modelitem.js';
4
+ import { Fore } from '../fore.js';
4
5
 
5
6
  /**
6
7
  * `AbstractControl` -
@@ -26,7 +27,7 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
26
27
  /**
27
28
  * (re)apply all modelItem state properties to this control. model -> UI
28
29
  */
29
- async refresh(force) {
30
+ async refresh() {
30
31
  // console.log('### AbstractControl.refresh on : ', this);
31
32
 
32
33
  const currentVal = this.value;
@@ -36,6 +37,7 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
36
37
 
37
38
  // await this.updateComplete;
38
39
  // await this.getWidget();
40
+ this.oldVal = this.nodeset ? this.nodeset : null;
39
41
  this.evalInContext();
40
42
 
41
43
  if (this.isBound()) {
@@ -51,12 +53,36 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
51
53
  if (this.modelItem instanceof ModelItem) {
52
54
  // console.log('### XfAbstractControl.refresh modelItem : ', this.modelItem);
53
55
 
54
- this.value = this.modelItem.value;
56
+ if (this.hasAttribute('as') && this.getAttribute('as') === 'node') {
57
+ console.log('as', this.nodeset);
58
+ this.modelItem.value = this.nodeset;
59
+ this.value = this.modelItem.value;
60
+ } else {
61
+ this.value = this.modelItem.value;
62
+ }
63
+
64
+ // console.log('value of widget',this.value);
65
+
66
+ /*
67
+ * todo: find out on which foreign modelitems we might be dependant on when no binds are used.
68
+ *
69
+ * e.g. filter expr on 'ref' 'instance('countries')//country[@continent = instance('default')/continent]'
70
+ *
71
+ * the country node is dependant on instance('default')/continent here (foreign node).
72
+ *
73
+ * possible approach:
74
+ * - pipe ref expression through DependencyNotifyingDomFacade to get referred nodes.
75
+ * - lookup modelItems of referred nodes
76
+ * - add ourselves to boundControls of foreign modelItem -> this control will then get refreshed when the foreign modelItem is changed.
77
+ */
78
+
79
+ // const touched = FxBind.getReferencesForRef(this.ref,Array.from(this.nodeset));
80
+ // console.log('touched',touched);
55
81
 
56
82
  /*
57
83
  this is another case that highlights the fact that an init() function might make sense in general.
58
84
  */
59
- if(!this.modelItem.boundControls.includes(this)){
85
+ if (!this.modelItem.boundControls.includes(this)) {
60
86
  this.modelItem.boundControls.push(this);
61
87
  }
62
88
 
@@ -68,15 +94,10 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
68
94
  // if(!this.closest('fx-fore').ready) return; // state change event do not fire during init phase (initial refresh)
69
95
  if (!this.getOwnerForm().ready) return; // state change event do not fire during init phase (initial refresh)
70
96
  if (currentVal !== this.value) {
71
- // console.log('dispatching value-changed for ', this);
72
- // console.log('value-changed path ', this.modelItem.path);
73
- this.dispatch('value-changed', { path: this.modelItem.path });
97
+ Fore.dispatch(this, 'value-changed', { path: this.modelItem.path });
74
98
  }
75
- // this.requestUpdate();
76
99
  }
77
100
  }
78
- // Fore.refreshChildren(this,force);
79
- // await this.updateComplete;
80
101
  }
81
102
 
82
103
  /**
@@ -89,13 +110,14 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
89
110
  }
90
111
 
91
112
  handleModelItemProperties() {
92
- // console.log('form ready', this.getOwnerForm().ready);
113
+ // console.log('handleModelItemProperties',this.modelItem);
93
114
  this.handleRequired();
94
115
  this.handleReadonly();
95
116
  if (this.getOwnerForm().ready) {
96
117
  this.handleValid();
97
118
  }
98
119
  this.handleRelevant();
120
+ // todo: handleType()
99
121
  }
100
122
 
101
123
  _getForm() {
@@ -104,25 +126,23 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
104
126
 
105
127
  _dispatchEvent(event) {
106
128
  if (this.getOwnerForm().ready) {
107
- this.dispatch(event, {});
129
+ Fore.dispatch(this, event, {});
108
130
  }
109
131
  }
110
132
 
111
133
  // eslint-disable-next-line class-methods-use-this
112
134
  handleRequired() {
113
135
  // console.log('mip required', this.modelItem.required);
114
- // const control = this.querySelector('#control');
115
136
  this.widget = this.getWidget();
137
+ // if (this.required !== this.modelItem.required) {
116
138
  if (this.isRequired() !== this.modelItem.required) {
117
139
  if (this.modelItem.required) {
118
- this.widget.setAttribute('required', 'required');
119
- this.classList.add('required');
140
+ this.widget.setAttribute('required', '');
141
+ this.setAttribute('required', '');
120
142
  this._dispatchEvent('required');
121
143
  } else {
122
144
  this.widget.removeAttribute('required');
123
- this.required = false;
124
- // this.removeAttribute('required');
125
- this.classList.toggle('required');
145
+ this.removeAttribute('required');
126
146
  this._dispatchEvent('optional');
127
147
  }
128
148
  }
@@ -132,15 +152,13 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
132
152
  // console.log('mip readonly', this.modelItem.isReadonly);
133
153
  if (this.isReadonly() !== this.modelItem.readonly) {
134
154
  if (this.modelItem.readonly) {
135
- this.widget.setAttribute('readonly', 'readonly');
136
- // this.setAttribute('readonly','readonly');
137
- this.classList.toggle('readonly');
155
+ this.widget.setAttribute('readonly', '');
156
+ this.setAttribute('readonly', '');
138
157
  this._dispatchEvent('readonly');
139
158
  }
140
159
  if (!this.modelItem.readonly) {
141
160
  this.widget.removeAttribute('readonly');
142
- // this.removeAttribute('readonly');
143
- this.classList.toggle('readonly');
161
+ this.removeAttribute('readonly');
144
162
  this._dispatchEvent('readwrite');
145
163
  }
146
164
  }
@@ -153,12 +171,12 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
153
171
 
154
172
  if (this.isValid() !== this.modelItem.constraint) {
155
173
  if (this.modelItem.constraint) {
156
- this.classList.remove('invalid');
157
174
  if (alert) alert.style.display = 'none';
158
175
  this._dispatchEvent('valid');
176
+ this.removeAttribute('invalid');
159
177
  } else {
178
+ this.setAttribute('invalid', '');
160
179
  // ### constraint is invalid - handle alerts
161
- this.classList.add('invalid');
162
180
  if (alert) {
163
181
  alert.style.display = 'block';
164
182
  }
@@ -185,6 +203,12 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
185
203
 
186
204
  handleRelevant() {
187
205
  // console.log('mip valid', this.modelItem.enabled);
206
+ const item = this.modelItem.node;
207
+ if (Array.isArray(item) && item.length === 0) {
208
+ this._dispatchEvent('nonrelevant');
209
+ this.style.display = 'none';
210
+ return;
211
+ }
188
212
  if (this.isEnabled() !== this.modelItem.relevant) {
189
213
  if (this.modelItem.relevant) {
190
214
  this._dispatchEvent('relevant');
@@ -199,14 +223,12 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
199
223
  }
200
224
 
201
225
  isRequired() {
202
- if (this.widget.hasAttribute('required')) {
203
- return true;
204
- }
205
- return false;
226
+ return this.hasAttribute('required');
206
227
  }
207
228
 
208
229
  isValid() {
209
- if (this.classList.contains('invalid')) {
230
+ // return this.valid;
231
+ if (this.hasAttribute('invalid')) {
210
232
  return false;
211
233
  }
212
234
  return true;
@@ -214,10 +236,7 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
214
236
 
215
237
  isReadonly() {
216
238
  // const widget = this.querySelector('#widget');
217
- if (this.widget.hasAttribute('readonly')) {
218
- return true;
219
- }
220
- return false;
239
+ return this.hasAttribute('readonly');
221
240
  }
222
241
 
223
242
  isEnabled() {
@@ -257,5 +276,6 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
257
276
  })();
258
277
  }
259
278
  }
260
-
261
- window.customElements.define('fx-abstract-control', AbstractControl);
279
+ if (!customElements.get('fx-abstract-control')) {
280
+ window.customElements.define('fx-abstract-control', AbstractControl);
281
+ }
@@ -29,9 +29,15 @@ export class FxAlert extends AbstractControl {
29
29
  `;
30
30
  }
31
31
 
32
+ getWidget() {
33
+ return this;
34
+ }
35
+
32
36
  async updateWidgetValue() {
33
37
  console.log('alert update', this);
34
38
  this.innerHTML = this.value;
35
39
  }
36
40
  }
37
- customElements.define('fx-alert', FxAlert);
41
+ if (!customElements.get('fx-alert')) {
42
+ customElements.define('fx-alert', FxAlert);
43
+ }