@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.
- package/dist/fore-dev.js +2 -2
- package/dist/fore-dev.js.map +1 -1
- package/dist/fore.js +2 -2
- package/dist/fore.js.map +1 -1
- package/package.json +1 -1
- package/resources/fore.css +6 -41
- package/src/DependencyNotifyingDomFacade.js +2 -2
- package/src/actions/abstract-action.js +43 -41
- package/src/actions/fx-call.js +9 -0
- package/src/actions/fx-delete.js +1 -1
- package/src/actions/fx-insert.js +15 -4
- package/src/actions/fx-load.js +5 -2
- package/src/actions/fx-reload.js +1 -1
- package/src/actions/fx-send.js +8 -3
- package/src/actions/fx-setfocus.js +7 -2
- package/src/fore.js +241 -1
- package/src/fx-bind.js +12 -6
- package/src/fx-fore.js +99 -36
- package/src/fx-model.js +22 -7
- package/src/fx-submission.js +13 -14
- package/src/tools/adi.js +16 -7
- package/src/tools/fx-action-log.js +1 -1
- package/src/ui/abstract-control.js +26 -46
- package/src/ui/fx-control.js +13 -13
- package/src/ui/fx-dialog.js +2 -0
- package/src/ui/fx-group.js +4 -1
- package/src/ui/fx-repeat-attributes.js +4 -1
- package/src/ui/fx-repeat.js +21 -1
- package/src/ui/fx-repeatitem.js +5 -9
- package/src/ui/fx-trigger.js +6 -14
- package/src/xpath-evaluation.js +324 -73
- package/src/xpath-util.js +137 -132
|
@@ -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
|
|
6
|
-
import getInScopeContext from
|
|
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
|
|
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
|
-
|
|
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
|
|
164
|
-
|
|
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
|
-
|
|
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 (
|
|
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 (
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
package/src/ui/fx-control.js
CHANGED
|
@@ -2,7 +2,7 @@ import XfAbstractControl from './abstract-control.js';
|
|
|
2
2
|
import {
|
|
3
3
|
evaluateXPath,
|
|
4
4
|
evaluateXPathToString,
|
|
5
|
-
evaluateXPathToFirstNode,
|
|
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
|
-
|
|
498
|
-
|
|
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');
|
package/src/ui/fx-dialog.js
CHANGED
package/src/ui/fx-group.js
CHANGED
|
@@ -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
|
-
|
|
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;
|
package/src/ui/fx-repeat.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
package/src/ui/fx-repeatitem.js
CHANGED
|
@@ -58,7 +58,9 @@ export class FxRepeatitem extends foreElementMixin(HTMLElement) {
|
|
|
58
58
|
this.shadowRoot.innerHTML = `
|
|
59
59
|
${html}
|
|
60
60
|
`;
|
|
61
|
-
|
|
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
|
-
|
|
93
|
-
// this.style.display = 'none';
|
|
94
|
-
this.classList.add('nonrelevant');
|
|
94
|
+
this.setAttribute('nonrelevant','');
|
|
95
95
|
} else {
|
|
96
|
-
|
|
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
|
/*
|
package/src/ui/fx-trigger.js
CHANGED
|
@@ -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].
|
|
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
|
-
|
|
119
|
-
|
|
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')) {
|