@jinntec/fore 1.9.0 → 1.10.1

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.
@@ -2,8 +2,8 @@ import '../fx-model.js';
2
2
  import { foreElementMixin } from '../ForeElementMixin.js';
3
3
  import { ModelItem } from '../modelitem.js';
4
4
  import { Fore } from '../fore.js';
5
- import {XPathUtil} from "../xpath-util";
6
- import getInScopeContext from "../getInScopeContext";
5
+ import { XPathUtil } from '../xpath-util.js';
6
+ import getInScopeContext from '../getInScopeContext.js';
7
7
  import { evaluateXPathToFirstNode} from '../xpath-evaluation.js';
8
8
 
9
9
  /**
@@ -14,7 +14,7 @@ import { evaluateXPathToFirstNode} from '../xpath-evaluation.js';
14
14
  export default class AbstractControl extends foreElementMixin(HTMLElement) {
15
15
  constructor() {
16
16
  super();
17
- this.value = '';
17
+ this.value = null;
18
18
  this.display = this.style.display;
19
19
  this.required = false;
20
20
  this.readonly = false;
@@ -24,27 +24,6 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
24
24
  // this.attachShadow({ mode: 'open' });
25
25
  }
26
26
 
27
-
28
- // todo: discuss - this is a hack to circumvent that modelItems in toRefresh diverge from the modelItems in
29
- // the model in some situations. This code first looks for refresh
30
- /*
31
- getModelItem() {
32
- console.log('toRefreshModelItems', this.getOwnerForm().toRefresh);
33
- const s = this.modelItem.path;
34
- console.log('toRefreshModelItems path', s);
35
-
36
- const toRefresh = this.getOwnerForm().toRefresh;
37
- let mi;
38
- if(toRefresh){
39
- mi = this.getOwnerForm().toRefresh.find(m => m.path === s);
40
- }
41
-
42
- return mi? mi: super.getModelItem();
43
- // console.log('toRefreshModelItems realitem', mi);
44
-
45
- }
46
- */
47
-
48
27
  // eslint-disable-next-line class-methods-use-this
49
28
  getWidget() {
50
29
  throw new Error('You have to implement the method getWidget!');
@@ -65,6 +44,8 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
65
44
  // await this.updateComplete;
66
45
  // await this.getWidget();
67
46
  this.oldVal = this.nodeset ? this.nodeset : null;
47
+ // console.log('oldVal',this.oldVal);
48
+
68
49
  this.evalInContext();
69
50
 
70
51
  // todo this if should be removed - see above
@@ -76,7 +57,7 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
76
57
  const create = this.closest('[create]');
77
58
  if(create){
78
59
  // ### check if parent element exists
79
- let attrName,parentPath, parentNode;
60
+ let attrName; let parentPath; let parentNode;
80
61
 
81
62
  if(this.ref.includes('/')){
82
63
  parentPath = this.ref.substring(0, this.ref.indexOf('/'));
@@ -92,7 +73,7 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
92
73
  }
93
74
  }
94
75
  }else{
95
- let inscope = getInScopeContext(this, this.ref);
76
+ const inscope = getInScopeContext(this, this.ref);
96
77
 
97
78
  if(this.ref.includes('@')) {
98
79
  attrName = this.ref.substring(this.ref.indexOf('@') + 1);
@@ -105,7 +86,8 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
105
86
  }else{
106
87
  // ### this actually makes the control nonrelevant
107
88
  // todo: we should call a template function here to allow detachment of event-listeners and resetting eventual state
108
- this.style.display = 'none';
89
+ // this.style.display = 'none';
90
+ this.setAttribute('nonrelevant','');
109
91
  }
110
92
  return;
111
93
  }
@@ -124,6 +106,7 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
124
106
  } else {
125
107
  this.value = this.modelItem.value;
126
108
  }
109
+ // console.log('newVal',this.value);
127
110
 
128
111
  // console.log('value of widget',this.value);
129
112
 
@@ -160,9 +143,8 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
160
143
  Fore.dispatch(this,'init',{});
161
144
  }
162
145
  if (!this.getOwnerForm().ready) return; // state change event do not fire during init phase (initial refresh)
163
- if (currentVal !== this.value ) {
164
- // todo: discuss how to prevent unnecessary/unwanted value-changes e.g. when repeatitems are inserted
165
- // if (currentVal !== this.value && this.visited) {
146
+ // if oldVal is null we haven't received a concrete value yet
147
+ if (this.oldVal !== null && currentVal !== this.value) {
166
148
  Fore.dispatch(this, 'value-changed', { path: this.modelItem.path , value:this.modelItem.value});
167
149
  }
168
150
  }
@@ -206,12 +188,13 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
206
188
  // eslint-disable-next-line class-methods-use-this
207
189
  handleRequired() {
208
190
  // console.log('mip required', this.modelItem.required);
209
- this.widget = this.getWidget();
191
+ this.widget = this.getWidget();
192
+ const wasRequired = this.isRequired();
210
193
 
211
194
  if(!this.modelItem.required){
212
195
  this.widget.removeAttribute('required');
213
196
  this.removeAttribute('required');
214
- if (this.isRequired() !== this.modelItem.required){
197
+ if (wasRequired !== this.modelItem.required){
215
198
  this._dispatchEvent('optional');
216
199
  }
217
200
  return;
@@ -229,7 +212,7 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
229
212
  }
230
213
  this.widget.setAttribute('required', '');
231
214
  this.setAttribute('required', '');
232
- if (this.isRequired() !== this.modelItem.required) {
215
+ if (wasRequired !== this.modelItem.required) {
233
216
  this._dispatchEvent('required');
234
217
  }
235
218
 
@@ -336,20 +319,25 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
336
319
  handleRelevant() {
337
320
  // console.log('mip valid', this.modelItem.enabled);
338
321
  const item = this.modelItem.node;
322
+ this.removeAttribute('relevant');
323
+ this.removeAttribute('nonrelevant');
339
324
  if (Array.isArray(item) && item.length === 0) {
340
325
  this._dispatchEvent('nonrelevant');
341
- this.style.display = 'none';
326
+ this.setAttribute('nonrelevant','');
327
+ // this.style.display = 'none';
342
328
  return;
343
329
  }
344
330
  if (this.isEnabled() !== this.modelItem.relevant) {
345
331
  if (this.modelItem.relevant) {
346
332
  this._dispatchEvent('relevant');
347
333
  // this._fadeIn(this, this.display);
348
- this.style.display = this.display;
334
+ this.setAttribute('relevant','');
335
+ // this.style.display = this.display;
349
336
  } else {
350
337
  this._dispatchEvent('nonrelevant');
351
338
  // this._fadeOut(this);
352
- this.style.display = 'none';
339
+ this.setAttribute('nonrelevant','');
340
+ // this.style.display = 'none';
353
341
  }
354
342
  }
355
343
  }
@@ -359,11 +347,7 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
359
347
  }
360
348
 
361
349
  isValid() {
362
- // return this.valid;
363
- if (this.hasAttribute('invalid')) {
364
- return false;
365
- }
366
- return true;
350
+ return this.hasAttribute('invalid') ? false : true;
367
351
  }
368
352
 
369
353
  isReadonly() {
@@ -372,11 +356,7 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
372
356
  }
373
357
 
374
358
  isEnabled() {
375
- // if(this.style.display === 'none' || this.classList.contains('non-relevant')){
376
- if (this.style.display === 'none') {
377
- return false;
378
- }
379
- return true;
359
+ return !this.hasAttribute('nonrelevant');
380
360
  }
381
361
 
382
362
  // eslint-disable-next-line class-methods-use-this
@@ -2,7 +2,7 @@ import XfAbstractControl from './abstract-control.js';
2
2
  import {
3
3
  evaluateXPath,
4
4
  evaluateXPathToString,
5
- evaluateXPathToFirstNode, resolveId,
5
+ evaluateXPathToFirstNode,
6
6
  } from '../xpath-evaluation.js';
7
7
  import getInScopeContext from '../getInScopeContext.js';
8
8
  import {Fore} from '../fore.js';
@@ -123,12 +123,8 @@ export default class FxControl extends XfAbstractControl {
123
123
 
124
124
  this.addEventListener('keyup', () => {
125
125
  FxModel.dataChanged = true;
126
- /*
127
- if (!this.classList.contains('visited')) {
128
- this.classList.add('visited');
129
- }
130
- */
131
126
  });
127
+
132
128
  // ### convenience marker event
133
129
  if (this.updateEvent === 'enter') {
134
130
  this.widget.addEventListener('keyup', event => {
@@ -494,9 +490,13 @@ export default class FxControl extends XfAbstractControl {
494
490
  this.dispatchEvent(new CustomEvent('loaded', {detail: {fore: theFore}}));
495
491
  } catch (error) {
496
492
  // console.log('error', error);
497
- this.getOwnerForm().dispatchEvent(
498
- new CustomEvent('error', {detail: {message: `${this.url} not found`}}),
499
- );
493
+ Fore.dispatch(this, 'error', {
494
+ origin: this,
495
+ message: `control couldn't be loaded from url '${this.url}'`,
496
+ expr:xpath,
497
+ level:'Error'
498
+ });
499
+
500
500
  }
501
501
  }
502
502
 
@@ -550,9 +550,6 @@ export default class FxControl extends XfAbstractControl {
550
550
  // const nodeset = evaluateXPathToNodes(ref, inscope, this);
551
551
  const nodeset = evaluateXPath(ref, inscope, this);
552
552
 
553
- // ### bail out when nodeset is array and empty
554
- if (Array.isArray(nodeset) && nodeset.length === 0) return;
555
-
556
553
  // ### clear items
557
554
  const {children} = widget;
558
555
  Array.from(children).forEach(child => {
@@ -561,6 +558,9 @@ export default class FxControl extends XfAbstractControl {
561
558
  }
562
559
  });
563
560
 
561
+ // ### bail out when nodeset is array and empty
562
+ if (Array.isArray(nodeset) && nodeset.length === 0) return;
563
+
564
564
  // ### build the items
565
565
  const {template} = this;
566
566
  if (template) {
@@ -574,7 +574,7 @@ export default class FxControl extends XfAbstractControl {
574
574
  this.widget.insertBefore(option, firstTemplateChild);
575
575
  }
576
576
 
577
- if (nodeset.length) {
577
+ if (nodeset.length !== 0) {
578
578
  // console.log('nodeset', nodeset);
579
579
  const fragment = document.createDocumentFragment();
580
580
  // console.time('offscreen');
@@ -39,6 +39,8 @@ export class FxDialog extends HTMLElement {
39
39
  this.classList.remove('show');
40
40
  });
41
41
  }
42
+ this.setAttribute('role','dialog');
43
+ this.setAttribute('aria-modal','false');
42
44
 
43
45
  /*
44
46
  this.addEventListener('transitionend', () => {
@@ -33,7 +33,10 @@ class FxGroup extends FxContainer {
33
33
  super();
34
34
  this.collapse = false;
35
35
  }
36
-
36
+ connectedCallback() {
37
+ super.connectedCallback();
38
+ this.setAttribute('role','group');
39
+ }
37
40
  render() {
38
41
  return `
39
42
  <slot></slot>
@@ -98,6 +98,7 @@ export class FxRepeatAttributes extends foreElementMixin(HTMLElement) {
98
98
  const refd = this.querySelector('[data-ref]');
99
99
  return refd.children;
100
100
  }
101
+
101
102
  async connectedCallback() {
102
103
  // console.log('connectedCallback',this);
103
104
  // this.display = window.getComputedStyle(this, null).getPropertyValue("display");
@@ -134,7 +135,9 @@ export class FxRepeatAttributes extends foreElementMixin(HTMLElement) {
134
135
  if (mutations[0].type === 'childList') {
135
136
  const added = mutations[0].addedNodes[0];
136
137
  if (added) {
137
- const path = XPathUtil.getPath(added);
138
+
139
+ const instance = XPathUtil.resolveInstance(this,this.ref);
140
+ const path = XPathUtil.getPath(added, instance);
138
141
  // this.dispatch('path-mutated',{'path':path,'nodeset':this.nodeset,'index': this.index});
139
142
  // this.index = index;
140
143
  // const prev = mutations[0].previousSibling.previousElementSibling;
@@ -5,6 +5,7 @@ import { foreElementMixin } from '../ForeElementMixin.js';
5
5
  import { evaluateXPath } from '../xpath-evaluation.js';
6
6
  import getInScopeContext from '../getInScopeContext.js';
7
7
  import { XPathUtil } from '../xpath-util.js';
8
+ import {DependencyNotifyingDomFacade} from '../DependencyNotifyingDomFacade';
8
9
 
9
10
  /**
10
11
  * `fx-repeat`
@@ -131,7 +132,8 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
131
132
  if (mutations[0].type === 'childList') {
132
133
  const added = mutations[0].addedNodes[0];
133
134
  if (added) {
134
- const path = XPathUtil.getPath(added);
135
+ const instance = XPathUtil.resolveInstance(this, this.ref);
136
+ const path = XPathUtil.getPath(added, instance);
135
137
  // console.log('path mutated', path);
136
138
  // this.dispatch('path-mutated',{'path':path,'nodeset':this.nodeset,'index': this.index});
137
139
  // this.index = index;
@@ -205,7 +207,17 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
205
207
  });
206
208
  }
207
209
 
210
+ /*
211
+ this.touchedPaths = new Set();
212
+ const instance = XPathUtil.resolveInstance(this, this.ref);
213
+ const depTrackDomfacade = new DependencyNotifyingDomFacade((node) => {
214
+ this.touchedPaths.add(XPathUtil.getPath(node, instance));
215
+ });
216
+ const rawNodeset = evaluateXPath(this.ref, inscope, this, {}, {}, depTrackDomfacade );
217
+ */
208
218
  const rawNodeset = evaluateXPath(this.ref, inscope, this);
219
+
220
+ // console.log('Touched!', this.ref, [...this.touchedPaths].join(', '));
209
221
  if (rawNodeset.length === 1 && Array.isArray(rawNodeset[0])) {
210
222
  // This XPath likely returned an XPath array. Just collapse to that array
211
223
  this.nodeset = rawNodeset[0];
@@ -220,6 +232,14 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
220
232
  if (!this.inited) this.init();
221
233
  // console.time('repeat-refresh', this);
222
234
  this._evalNodeset();
235
+
236
+ // ### register ourselves as boundControl
237
+ /*
238
+ const modelItem = this.getModelItem();
239
+ if (!modelItem.boundControls.includes(this)) {
240
+ modelItem.boundControls.push(this);
241
+ }
242
+ */
223
243
  // console.log('repeat refresh nodeset ', this.nodeset);
224
244
  // console.log('repeatCount', this.repeatCount);
225
245
 
@@ -58,7 +58,9 @@ export class FxRepeatitem extends foreElementMixin(HTMLElement) {
58
58
  this.shadowRoot.innerHTML = `
59
59
  ${html}
60
60
  `;
61
- this.getOwnerForm().registerLazyElement(this);
61
+ this.getOwnerForm().registerLazyElement(this);
62
+
63
+ this.ref = `${this.parentNode.ref}`;
62
64
  }
63
65
 
64
66
  disconnectedCallback() {
@@ -89,15 +91,9 @@ export class FxRepeatitem extends foreElementMixin(HTMLElement) {
89
91
  }
90
92
 
91
93
  if (this.modelItem && !this.modelItem.relevant) {
92
- // await Fore.fadeOutElement(this)
93
- // this.style.display = 'none';
94
- this.classList.add('nonrelevant');
94
+ this.setAttribute('nonrelevant','');
95
95
  } else {
96
- // if(this.hasAttribute('repeat-index')){
97
- // Fore.fadeInElement(this);
98
- // }
99
- // this.style.display = this.display;
100
- this.classList.remove('nonrelevant');
96
+ this.setAttribute('relevant','');
101
97
  }
102
98
 
103
99
  /*
@@ -25,7 +25,9 @@ export class FxTrigger extends XfAbstractControl {
25
25
  slot.addEventListener('slotchange', () => {
26
26
  const elements = slot.assignedElements({ flatten: true });
27
27
  elements[0].setAttribute('tabindex', '0');
28
- elements[0].setAttribute('role', 'button');
28
+ if(elements[0].nodeName !== 'BUTTON'){
29
+ elements[0].setAttribute('role', 'button');
30
+ }
29
31
 
30
32
  const element = elements[0];
31
33
 
@@ -114,19 +116,9 @@ export class FxTrigger extends XfAbstractControl {
114
116
  }
115
117
  }
116
118
 
117
- /*
118
- async refresh() {
119
- super.refresh();
120
- // console.log('fx-button refresh');
121
-
122
- const elements = this.querySelectorAll(':scope > *');
123
- elements.forEach(element => {
124
- if (typeof element.refresh === 'function') {
125
- element.refresh();
126
- }
127
- });
128
- }
129
- */
119
+ async refresh() {
120
+ super.refresh();
121
+ }
130
122
  }
131
123
 
132
124
  if (!customElements.get('fx-trigger')) {