@jinntec/fore 1.0.0-2 → 1.0.0-3
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/README.md +7 -1
- package/dist/fore-all.js +10 -10
- package/dist/fore.js +1 -1
- package/index.js +6 -1
- package/package.json +1 -1
- package/resources/fore.css +38 -0
- package/src/ForeElementMixin.js +10 -2
- package/src/actions/abstract-action.js +2 -2
- package/src/actions/fx-action.js +5 -1
- package/src/actions/fx-hide.js +23 -0
- package/src/actions/fx-insert.js +1 -1
- package/src/actions/fx-setvalue.js +1 -1
- package/src/actions/fx-show.js +23 -0
- package/src/fore.js +1 -0
- package/src/fx-bind.js +12 -10
- package/src/fx-fore.js +142 -27
- package/src/fx-model.js +6 -2
- package/src/fx-submission.js +3 -0
- package/src/fx-var.js +43 -0
- package/src/getInScopeContext.js +27 -6
- package/src/modelitem.js +1 -0
- package/src/ui/abstract-control.js +9 -1
- package/src/ui/fx-dialog.js +68 -0
- package/src/ui/fx-items.js +5 -2
- package/src/ui/fx-output.js +9 -11
- package/src/ui/fx-repeat.js +39 -4
- package/src/ui/fx-trigger.js +3 -0
- package/src/xpath-evaluation.js +145 -70
- package/src/xpath-util.js +5 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { FxAction } from './fx-action.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `fx-hide`
|
|
5
|
+
* hides a dialog
|
|
6
|
+
*
|
|
7
|
+
* @customElement
|
|
8
|
+
* @demo demo/project.html
|
|
9
|
+
*/
|
|
10
|
+
export class FxHide extends FxAction {
|
|
11
|
+
connectedCallback() {
|
|
12
|
+
this.dialog = this.getAttribute('dialog');
|
|
13
|
+
if(!this.dialog){
|
|
14
|
+
this.dispatch('error',{message:'dialog does not exist'})
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
perform() {
|
|
19
|
+
document.getElementById(this.dialog).hide();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
window.customElements.define('fx-hide', FxHide);
|
package/src/actions/fx-insert.js
CHANGED
|
@@ -86,7 +86,7 @@ export class FxInsert extends AbstractAction {
|
|
|
86
86
|
*/
|
|
87
87
|
|
|
88
88
|
// ### obtaining targetSequence
|
|
89
|
-
const inscope = getInScopeContext(this, this.ref);
|
|
89
|
+
const inscope = getInScopeContext(this.getAttributeNode('ref'), this.ref);
|
|
90
90
|
|
|
91
91
|
// @ts-ignore
|
|
92
92
|
const targetSequence = evaluateXPathToNodes(this.ref, inscope, this.getOwnerForm());
|
|
@@ -44,7 +44,7 @@ export default class FxSetvalue extends AbstractAction {
|
|
|
44
44
|
super.perform();
|
|
45
45
|
let { value } = this;
|
|
46
46
|
if (this.valueAttr !== null) {
|
|
47
|
-
value = evaluateXPath(this.valueAttr, this.nodeset, this
|
|
47
|
+
value = evaluateXPath(this.valueAttr, this.nodeset, this, this.detail);
|
|
48
48
|
} else if (this.textContent !== '') {
|
|
49
49
|
value = this.textContent;
|
|
50
50
|
} else {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { FxAction } from './fx-action.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `fx-confirm`
|
|
5
|
+
* Displays a simple confirmation before actually executing the nested actions.
|
|
6
|
+
*
|
|
7
|
+
* @customElement
|
|
8
|
+
* @demo demo/project.html
|
|
9
|
+
*/
|
|
10
|
+
export class FxShow extends FxAction {
|
|
11
|
+
connectedCallback() {
|
|
12
|
+
this.dialog = this.getAttribute('dialog');
|
|
13
|
+
if(!this.dialog){
|
|
14
|
+
this.dispatch('error',{message:'dialog does not exist'})
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
perform() {
|
|
19
|
+
document.getElementById(this.dialog).open();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
window.customElements.define('fx-show', FxShow);
|
package/src/fore.js
CHANGED
package/src/fx-bind.js
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
evaluateXPathToString,
|
|
8
8
|
} from './xpath-evaluation.js';
|
|
9
9
|
import { XPathUtil } from './xpath-util.js';
|
|
10
|
+
import getInScopeContext from './getInScopeContext.js';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* FxBind declaratively attaches constraints to nodes in the data (instances).
|
|
@@ -229,12 +230,6 @@ export class FxBind extends foreElementMixin(HTMLElement) {
|
|
|
229
230
|
}
|
|
230
231
|
}
|
|
231
232
|
|
|
232
|
-
_addNode(path, node) {
|
|
233
|
-
if (!this.model.mainGraph.hasNode(path)) {
|
|
234
|
-
this.model.mainGraph.addNode(path, { node });
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
233
|
/**
|
|
239
234
|
* Add the dependencies of this bind
|
|
240
235
|
*
|
|
@@ -245,6 +240,7 @@ export class FxBind extends foreElementMixin(HTMLElement) {
|
|
|
245
240
|
* @param {string} property The property with this dependency
|
|
246
241
|
*/
|
|
247
242
|
_addDependencies(refs, node, path, property) {
|
|
243
|
+
// console.log('_addDependencies',path);
|
|
248
244
|
const nodeHash = `${path}:${property}`;
|
|
249
245
|
if (refs.length !== 0) {
|
|
250
246
|
if (!this.model.mainGraph.hasNode(nodeHash)) {
|
|
@@ -252,11 +248,15 @@ export class FxBind extends foreElementMixin(HTMLElement) {
|
|
|
252
248
|
}
|
|
253
249
|
refs.forEach(ref => {
|
|
254
250
|
const otherPath = XPathUtil.getPath(ref);
|
|
251
|
+
// console.log('otherPath', otherPath)
|
|
255
252
|
|
|
256
|
-
|
|
257
|
-
|
|
253
|
+
// todo: nasty hack to prevent duplicate pathes like 'a[1]' and 'a[1]/text()[1]' to end up as separate nodes in the graph
|
|
254
|
+
if(!otherPath.endsWith('text()[1]')){
|
|
255
|
+
if (!this.model.mainGraph.hasNode(otherPath)) {
|
|
256
|
+
this.model.mainGraph.addNode(otherPath, ref);
|
|
257
|
+
}
|
|
258
|
+
this.model.mainGraph.addDependency(nodeHash, otherPath);
|
|
258
259
|
}
|
|
259
|
-
this.model.mainGraph.addDependency(nodeHash, otherPath);
|
|
260
260
|
});
|
|
261
261
|
} else {
|
|
262
262
|
this.model.mainGraph.addNode(nodeHash, node);
|
|
@@ -321,7 +321,7 @@ export class FxBind extends foreElementMixin(HTMLElement) {
|
|
|
321
321
|
* overwrites
|
|
322
322
|
*/
|
|
323
323
|
_evalInContext() {
|
|
324
|
-
const inscopeContext = this.
|
|
324
|
+
const inscopeContext = getInScopeContext(this.getAttributeNode('ref') || this, this.ref);
|
|
325
325
|
|
|
326
326
|
// reset nodeset
|
|
327
327
|
this.nodeset = [];
|
|
@@ -509,6 +509,8 @@ export class FxBind extends foreElementMixin(HTMLElement) {
|
|
|
509
509
|
* @param {string} propertyExpr The XPath to get the referenced nodes from
|
|
510
510
|
*
|
|
511
511
|
* @return {Node[]} The nodes that are referenced by the XPath
|
|
512
|
+
*
|
|
513
|
+
* todo: DependencyNotifyingDomFacade reports back too much in some cases like 'a[1]' and 'a[1]/text[1]'
|
|
512
514
|
*/
|
|
513
515
|
_getReferencesForProperty(propertyExpr) {
|
|
514
516
|
if (propertyExpr) {
|
package/src/fx-fore.js
CHANGED
|
@@ -64,7 +64,7 @@ export class FxFore extends HTMLElement {
|
|
|
64
64
|
|
|
65
65
|
const style = `
|
|
66
66
|
:host {
|
|
67
|
-
display: none;
|
|
67
|
+
// display: none;
|
|
68
68
|
height:auto;
|
|
69
69
|
padding:var(--model-element-padding);
|
|
70
70
|
font-family:Roboto, sans-serif;
|
|
@@ -97,7 +97,7 @@ export class FxFore extends HTMLElement {
|
|
|
97
97
|
visibility: visible;
|
|
98
98
|
opacity: 1;
|
|
99
99
|
}
|
|
100
|
-
|
|
100
|
+
|
|
101
101
|
.popup {
|
|
102
102
|
margin: 70px auto;
|
|
103
103
|
background: #fff;
|
|
@@ -133,7 +133,7 @@ export class FxFore extends HTMLElement {
|
|
|
133
133
|
.popup .close:focus{
|
|
134
134
|
outline:none;
|
|
135
135
|
}
|
|
136
|
-
|
|
136
|
+
|
|
137
137
|
.popup .close:hover {
|
|
138
138
|
color: #06D85F;
|
|
139
139
|
}
|
|
@@ -170,6 +170,10 @@ export class FxFore extends HTMLElement {
|
|
|
170
170
|
</style>
|
|
171
171
|
${html}
|
|
172
172
|
`;
|
|
173
|
+
|
|
174
|
+
this.toRefresh = [];
|
|
175
|
+
this.initialRun = true;
|
|
176
|
+
this.someInstanceDataStructureChanged = false;
|
|
173
177
|
}
|
|
174
178
|
|
|
175
179
|
connectedCallback() {
|
|
@@ -183,6 +187,12 @@ export class FxFore extends HTMLElement {
|
|
|
183
187
|
this.intersectionObserver = new IntersectionObserver(this.handleIntersect, options);
|
|
184
188
|
}
|
|
185
189
|
|
|
190
|
+
this.src = this.hasAttribute('src')? this.getAttribute('src'):null;
|
|
191
|
+
if(this.src){
|
|
192
|
+
this._loadFromSrc();
|
|
193
|
+
return ;
|
|
194
|
+
}
|
|
195
|
+
|
|
186
196
|
const slot = this.shadowRoot.querySelector('slot');
|
|
187
197
|
slot.addEventListener('slotchange', event => {
|
|
188
198
|
const children = event.target.assignedElements();
|
|
@@ -198,10 +208,71 @@ export class FxFore extends HTMLElement {
|
|
|
198
208
|
console.log(
|
|
199
209
|
`########## FORE: kick off processing for ... ${window.location.href} ##########`,
|
|
200
210
|
);
|
|
211
|
+
if(this.src){
|
|
212
|
+
console.log('########## FORE: loaded from ... ', this.src, '##########');
|
|
213
|
+
}
|
|
201
214
|
modelElement.modelConstruct();
|
|
202
215
|
}
|
|
203
216
|
this.model = modelElement;
|
|
204
217
|
});
|
|
218
|
+
this.addEventListener('path-mutated', (e) =>{
|
|
219
|
+
console.log('path-mutated event received', e.detail.path, e.detail.index);
|
|
220
|
+
this.someInstanceDataStructureChanged = true;
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
addToRefresh(modelItem){
|
|
226
|
+
const found = this.toRefresh.find(mi => mi.path === modelItem.path );
|
|
227
|
+
if(!found){
|
|
228
|
+
this.toRefresh.push(modelItem);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* loads a Fore from an URL given by `src`.
|
|
234
|
+
*
|
|
235
|
+
* Will extract the `fx-fore` element from that target file and use and replace current `fx-fore` element with the loaded one.
|
|
236
|
+
* @private
|
|
237
|
+
*/
|
|
238
|
+
_loadFromSrc() {
|
|
239
|
+
console.log('########## loading Fore from ',this.src ,'##########');
|
|
240
|
+
fetch(this.src, {
|
|
241
|
+
method: 'GET',
|
|
242
|
+
mode: 'cors',
|
|
243
|
+
credentials: 'include',
|
|
244
|
+
headers: {
|
|
245
|
+
'Content-Type': 'text/html',
|
|
246
|
+
},
|
|
247
|
+
})
|
|
248
|
+
.then(response => {
|
|
249
|
+
const responseContentType = response.headers.get('content-type').toLowerCase();
|
|
250
|
+
console.log('********** responseContentType *********', responseContentType);
|
|
251
|
+
if (responseContentType.startsWith('text/html')) {
|
|
252
|
+
// const htmlResponse = response.text();
|
|
253
|
+
// return new DOMParser().parseFromString(htmlResponse, 'text/html');
|
|
254
|
+
// return response.text();
|
|
255
|
+
return response.text().then(result =>
|
|
256
|
+
// console.log('xml ********', result);
|
|
257
|
+
new DOMParser().parseFromString(result, 'text/html'),
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
return 'done';
|
|
261
|
+
})
|
|
262
|
+
.then(data => {
|
|
263
|
+
// const theFore = fxEvaluateXPathToFirstNode('//fx-fore', data.firstElementChild);
|
|
264
|
+
const theFore = data.querySelector('fx-fore');
|
|
265
|
+
|
|
266
|
+
// console.log('thefore', theFore)
|
|
267
|
+
if(!theFore){
|
|
268
|
+
this.dispatchEvent(new CustomEvent('error',{detail:{message: `Fore element not found in '${this.src}'. Maybe wrapped within 'template' element?`}}));
|
|
269
|
+
}
|
|
270
|
+
theFore.setAttribute('from-src', this.src);
|
|
271
|
+
this.replaceWith(theFore);
|
|
272
|
+
})
|
|
273
|
+
.catch(error => {
|
|
274
|
+
this.dispatchEvent(new CustomEvent('error',{detail:{message: `'${this.src}' not found or does not contain Fore element.`}}));
|
|
275
|
+
});
|
|
205
276
|
}
|
|
206
277
|
|
|
207
278
|
/**
|
|
@@ -266,32 +337,56 @@ export class FxFore extends HTMLElement {
|
|
|
266
337
|
|
|
267
338
|
console.time('refresh');
|
|
268
339
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
340
|
+
// ### refresh Fore UI elements
|
|
341
|
+
console.time('refreshChildren');
|
|
342
|
+
console.log('toRefresh',this.toRefresh);
|
|
343
|
+
|
|
344
|
+
if(!this.initialRun && this.toRefresh.length !== 0){
|
|
345
|
+
let needsRefresh = false;
|
|
346
|
+
|
|
347
|
+
// ### after recalculation the changed modelItems are copied to 'toRefresh' array for processing
|
|
348
|
+
this.toRefresh.forEach(modelItem => {
|
|
349
|
+
// check if modelItem has boundControls - if so, call refresh() for each of them
|
|
350
|
+
const controlsToRefresh = modelItem.boundControls;
|
|
351
|
+
if(controlsToRefresh){
|
|
352
|
+
controlsToRefresh.forEach(ctrl => {
|
|
353
|
+
ctrl.refresh();
|
|
354
|
+
});
|
|
278
355
|
}
|
|
279
|
-
}
|
|
280
|
-
});
|
|
281
|
-
this.getModel().changed = [];
|
|
282
356
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
357
|
+
// ### check if other controls depend on current modelItem
|
|
358
|
+
const mainGraph = this.getModel().mainGraph;
|
|
359
|
+
if(mainGraph && mainGraph.hasNode(modelItem.path)){
|
|
360
|
+
const deps = this.getModel().mainGraph.dependentsOf(modelItem.path, false);
|
|
361
|
+
// ### iterate dependant modelItems and refresh all their boundControls
|
|
362
|
+
if(deps.length !== 0){
|
|
363
|
+
deps.forEach(dep => {
|
|
364
|
+
// ### if changed modelItem has a 'facet' path we use the basePath that is the locationPath without facet name
|
|
365
|
+
const basePath = XPathUtil.getBasePath(dep);
|
|
366
|
+
const modelItemOfDep = this.getModel().modelItems.find(mip => mip.path === basePath);
|
|
367
|
+
// ### refresh all boundControls
|
|
368
|
+
modelItemOfDep.boundControls.forEach(control =>{control.refresh()});
|
|
369
|
+
});
|
|
370
|
+
needsRefresh = true;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
this.toRefresh = [];
|
|
375
|
+
if(!needsRefresh){
|
|
376
|
+
console.log('skipping refresh - no dependants');
|
|
377
|
+
}
|
|
378
|
+
}else{
|
|
379
|
+
Fore.refreshChildren(this, true);
|
|
380
|
+
console.timeEnd('refreshChildren');
|
|
286
381
|
}
|
|
287
|
-
*/
|
|
288
|
-
// ### refresh Fore UI elements
|
|
289
|
-
console.time('refreshChildren');
|
|
290
|
-
Fore.refreshChildren(this, true);
|
|
291
|
-
console.timeEnd('refreshChildren');
|
|
292
382
|
|
|
293
383
|
// ### refresh template expressions
|
|
294
|
-
this.
|
|
384
|
+
if(this.initialRun || this.someInstanceDataStructureChanged){
|
|
385
|
+
this._updateTemplateExpressions();
|
|
386
|
+
this.someInstanceDataStructureChanged = false; //reset
|
|
387
|
+
}
|
|
388
|
+
this._processTemplateExpressions();
|
|
389
|
+
|
|
295
390
|
console.timeEnd('refresh');
|
|
296
391
|
|
|
297
392
|
console.groupEnd();
|
|
@@ -319,6 +414,8 @@ export class FxFore extends HTMLElement {
|
|
|
319
414
|
this.storedTemplateExpressions = [];
|
|
320
415
|
}
|
|
321
416
|
|
|
417
|
+
console.log('######### storedTemplateExpressions', this.storedTemplateExpressions.length);
|
|
418
|
+
|
|
322
419
|
/*
|
|
323
420
|
storing expressions and their nodes for re-evaluation
|
|
324
421
|
*/
|
|
@@ -329,21 +426,26 @@ export class FxFore extends HTMLElement {
|
|
|
329
426
|
}
|
|
330
427
|
const expr = this._getTemplateExpression(node);
|
|
331
428
|
|
|
429
|
+
// console.log('storedTemplateExpressionByNode', this.storedTemplateExpressionByNode);
|
|
332
430
|
this.storedTemplateExpressionByNode.set(node, expr);
|
|
333
431
|
});
|
|
432
|
+
console.log('stored template expressions ', this.storedTemplateExpressionByNode);
|
|
334
433
|
|
|
335
434
|
// TODO: Should we clean up nodes that existed but are now gone?
|
|
435
|
+
this._processTemplateExpressions();
|
|
436
|
+
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
_processTemplateExpressions() {
|
|
336
440
|
for (const node of this.storedTemplateExpressionByNode.keys()) {
|
|
337
441
|
this._processTemplateExpression({
|
|
338
442
|
node,
|
|
339
443
|
expr: this.storedTemplateExpressionByNode.get(node),
|
|
340
444
|
});
|
|
341
445
|
}
|
|
342
|
-
|
|
343
|
-
console.log('stored template expressions ', this.storedTemplateExpressionByNode);
|
|
344
446
|
}
|
|
345
447
|
|
|
346
|
-
|
|
448
|
+
// eslint-disable-next-line class-methods-use-this
|
|
347
449
|
_processTemplateExpression(exprObj) {
|
|
348
450
|
// console.log('processing template expression ', exprObj);
|
|
349
451
|
|
|
@@ -553,6 +655,18 @@ export class FxFore extends HTMLElement {
|
|
|
553
655
|
|
|
554
656
|
await this._lazyCreateInstance();
|
|
555
657
|
|
|
658
|
+
console.log('registering variables!');
|
|
659
|
+
const variables = new Map();
|
|
660
|
+
(function registerVariables(node) {
|
|
661
|
+
for (const child of node.children) {
|
|
662
|
+
if ('setInScopeVariables' in child) {
|
|
663
|
+
child.setInScopeVariables(variables);
|
|
664
|
+
}
|
|
665
|
+
registerVariables(child);
|
|
666
|
+
}
|
|
667
|
+
})(this);
|
|
668
|
+
console.log('Found variables:', variables);
|
|
669
|
+
|
|
556
670
|
const options = {
|
|
557
671
|
root: null,
|
|
558
672
|
rootMargin: '0px',
|
|
@@ -564,6 +678,7 @@ export class FxFore extends HTMLElement {
|
|
|
564
678
|
this.classList.add('fx-ready');
|
|
565
679
|
|
|
566
680
|
this.ready = true;
|
|
681
|
+
this.initialRun = false;
|
|
567
682
|
console.log('### <<<<< dispatching ready >>>>>');
|
|
568
683
|
console.log('########## modelItems: ', this.getModel().modelItems);
|
|
569
684
|
console.log('########## FORE: form fully initialized... ##########');
|
package/src/fx-model.js
CHANGED
|
@@ -157,7 +157,7 @@ export class FxModel extends HTMLElement {
|
|
|
157
157
|
rebuild() {
|
|
158
158
|
console.group('### rebuild');
|
|
159
159
|
console.time('rebuild');
|
|
160
|
-
this.mainGraph = new DepGraph(false);
|
|
160
|
+
this.mainGraph = new DepGraph(false); //do: should be moved down below binds.length check but causes errors in tests.
|
|
161
161
|
this.modelItems = [];
|
|
162
162
|
|
|
163
163
|
// trigger recursive initialization of the fx-bind elements
|
|
@@ -167,6 +167,7 @@ export class FxModel extends HTMLElement {
|
|
|
167
167
|
this.skipUpdate = true;
|
|
168
168
|
return ;
|
|
169
169
|
}
|
|
170
|
+
|
|
170
171
|
binds.forEach(bind => {
|
|
171
172
|
bind.init(this);
|
|
172
173
|
});
|
|
@@ -195,7 +196,7 @@ export class FxModel extends HTMLElement {
|
|
|
195
196
|
*/
|
|
196
197
|
recalculate() {
|
|
197
198
|
console.group('### recalculate');
|
|
198
|
-
console.log('
|
|
199
|
+
console.log('changed nodes ', this.changed);
|
|
199
200
|
|
|
200
201
|
console.time('recalculate');
|
|
201
202
|
this.computes = 0;
|
|
@@ -242,6 +243,8 @@ export class FxModel extends HTMLElement {
|
|
|
242
243
|
this.compute(node, path);
|
|
243
244
|
}
|
|
244
245
|
});
|
|
246
|
+
const toRefresh = [...this.changed];
|
|
247
|
+
this.formElement.toRefresh = toRefresh;
|
|
245
248
|
this.changed = [];
|
|
246
249
|
console.log('subgraph', this.subgraph);
|
|
247
250
|
this.dispatchEvent(
|
|
@@ -380,6 +383,7 @@ export class FxModel extends HTMLElement {
|
|
|
380
383
|
const compute = evaluateXPathToBoolean(constraint, modelItem.node, this);
|
|
381
384
|
console.log('modelItem validity computed: ', compute);
|
|
382
385
|
modelItem.constraint = compute;
|
|
386
|
+
this.formElement.addToRefresh(modelItem); // let fore know that modelItem needs refresh
|
|
383
387
|
if (!compute) valid = false;
|
|
384
388
|
// ### alerts are added only once during model-construct. Otherwise they would add up in each run of revalidate()
|
|
385
389
|
if (!this.modelConstructed) {
|
package/src/fx-submission.js
CHANGED
|
@@ -81,6 +81,9 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
|
|
|
81
81
|
const valid = model.revalidate();
|
|
82
82
|
if (!valid) {
|
|
83
83
|
console.log('validation failed. Bubmission stopped');
|
|
84
|
+
// ### allow alerts to pop up
|
|
85
|
+
this.dispatch('submit-error', {});
|
|
86
|
+
this.getModel().parentNode.refresh();
|
|
84
87
|
return;
|
|
85
88
|
}
|
|
86
89
|
}
|
package/src/fx-var.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import './fx-instance.js';
|
|
2
|
+
import { evaluateXPath } from './xpath-evaluation.js';
|
|
3
|
+
import { foreElementMixin } from './ForeElementMixin.js';
|
|
4
|
+
import getInScopeContext from './getInScopeContext.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @ts-check
|
|
8
|
+
*/
|
|
9
|
+
export class FxVariable extends foreElementMixin(HTMLElement) {
|
|
10
|
+
constructor() {
|
|
11
|
+
super();
|
|
12
|
+
|
|
13
|
+
this.attachShadow({ mode: 'open' });
|
|
14
|
+
this.name = '';
|
|
15
|
+
this.valueQuery = '';
|
|
16
|
+
this.value = null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
connectedCallback() {
|
|
20
|
+
this.name = this.getAttribute('name');
|
|
21
|
+
this.valueQuery = this.getAttribute('value');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
refresh() {
|
|
25
|
+
const inscope = getInScopeContext(this, this.valueQuery);
|
|
26
|
+
|
|
27
|
+
this.value = evaluateXPath(this.valueQuery, inscope, this, this.precedingVariables);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
setInScopeVariables(inScopeVariables) {
|
|
31
|
+
if (inScopeVariables.has(this.name)) {
|
|
32
|
+
console.error(`The variable ${this.name} is declared more than once`);
|
|
33
|
+
this.dispatch('xforms-binding-error');
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
inScopeVariables.set(this.name, this);
|
|
37
|
+
// Clone the preceding variables to make sure we are not going to get access to variables we
|
|
38
|
+
// should not get access to
|
|
39
|
+
this.inScopeVariables = new Map(inScopeVariables);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
customElements.define('fx-var', FxVariable);
|
package/src/getInScopeContext.js
CHANGED
|
@@ -2,10 +2,18 @@ import { evaluateXPathToFirstNode } from './xpath-evaluation.js';
|
|
|
2
2
|
|
|
3
3
|
import { XPathUtil } from './xpath-util.js';
|
|
4
4
|
|
|
5
|
-
function
|
|
5
|
+
function _getElement(node) {
|
|
6
6
|
if (node.nodeType === Node.ATTRIBUTE_NODE) {
|
|
7
|
+
// The context of an attribute is the ref of the element it's defined on
|
|
7
8
|
return node.ownerElement;
|
|
8
9
|
}
|
|
10
|
+
|
|
11
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
12
|
+
// The context of a query should be the element having a ref
|
|
13
|
+
return node;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// For text nodes, just start looking from the parent element
|
|
9
17
|
return node.parentNode;
|
|
10
18
|
}
|
|
11
19
|
|
|
@@ -38,7 +46,7 @@ function _getInitialContext(node, ref) {
|
|
|
38
46
|
}
|
|
39
47
|
|
|
40
48
|
export default function getInScopeContext(node, ref) {
|
|
41
|
-
const parentElement =
|
|
49
|
+
const parentElement = _getElement(node);
|
|
42
50
|
/*
|
|
43
51
|
if(parentElement.nodeName.toUpperCase() === 'FX-REPEATITEM'){
|
|
44
52
|
return parentElement.nodeset;
|
|
@@ -50,10 +58,23 @@ export default function getInScopeContext(node, ref) {
|
|
|
50
58
|
return repeatItem.nodeset;
|
|
51
59
|
}
|
|
52
60
|
|
|
53
|
-
if (node.nodeType === Node.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
61
|
+
if (node.nodeType === Node.ATTRIBUTE_NODE && node.nodeName === 'ref') {
|
|
62
|
+
// Note: do not consider the ref of the owner element since it should not be used to define the
|
|
63
|
+
// context
|
|
64
|
+
if (node.ownerElement.hasAttribute('context')) {
|
|
65
|
+
const initialContext = _getInitialContext(node.ownerElement.parentNode, ref);
|
|
66
|
+
const contextAttr = node.ownerElement.getAttribute('context');
|
|
67
|
+
return evaluateXPathToFirstNode(contextAttr, initialContext, _getForeContext(parentElement));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Never resolve the context from a ref itself!
|
|
71
|
+
return _getInitialContext(parentElement.parentNode, ref);
|
|
57
72
|
}
|
|
73
|
+
|
|
74
|
+
// if (node.nodeType === Node.ELEMENT_NODE && node.hasAttribute('context')) {
|
|
75
|
+
// const initialContext = _getInitialContext(node.parentNode, ref);
|
|
76
|
+
// const contextAttr = node.getAttribute('context');
|
|
77
|
+
// return evaluateXPathToFirstNode(contextAttr, initialContext, _getForeContext(parentElement));
|
|
78
|
+
// }
|
|
58
79
|
return _getInitialContext(parentElement, ref);
|
|
59
80
|
}
|
package/src/modelitem.js
CHANGED
|
@@ -31,7 +31,7 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
|
|
|
31
31
|
|
|
32
32
|
const currentVal = this.value;
|
|
33
33
|
|
|
34
|
-
// if(this.repeated) return
|
|
34
|
+
// if(this.repeated) return
|
|
35
35
|
if (this.isNotBound()) return;
|
|
36
36
|
|
|
37
37
|
// await this.updateComplete;
|
|
@@ -52,6 +52,14 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
|
|
|
52
52
|
// console.log('### XfAbstractControl.refresh modelItem : ', this.modelItem);
|
|
53
53
|
|
|
54
54
|
this.value = this.modelItem.value;
|
|
55
|
+
|
|
56
|
+
/*
|
|
57
|
+
this is another case that highlights the fact that an init() function might make sense in general.
|
|
58
|
+
*/
|
|
59
|
+
if(!this.modelItem.boundControls.includes(this)){
|
|
60
|
+
this.modelItem.boundControls.push(this);
|
|
61
|
+
}
|
|
62
|
+
|
|
55
63
|
// console.log('>>>>>>>> abstract refresh ', this.control);
|
|
56
64
|
// this.control[this.valueProp] = this.value;
|
|
57
65
|
await this.updateWidgetValue();
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export class FxDialog extends HTMLElement {
|
|
2
|
+
|
|
3
|
+
static get properties() {
|
|
4
|
+
return {
|
|
5
|
+
id: String
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
constructor() {
|
|
10
|
+
super();
|
|
11
|
+
this.attachShadow({mode: 'open'});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
connectedCallback() {
|
|
15
|
+
const style = `
|
|
16
|
+
:host {
|
|
17
|
+
display: none;
|
|
18
|
+
height: 100vh;
|
|
19
|
+
width:100vw;
|
|
20
|
+
position:fixed;
|
|
21
|
+
left:0;
|
|
22
|
+
top:0;
|
|
23
|
+
right:0;
|
|
24
|
+
bottom:0;
|
|
25
|
+
}
|
|
26
|
+
`;
|
|
27
|
+
|
|
28
|
+
this.shadowRoot.innerHTML = this.render(style);
|
|
29
|
+
this.id = this.getAttribute('id');
|
|
30
|
+
|
|
31
|
+
// const dialog = document.getElementById(this.id);
|
|
32
|
+
|
|
33
|
+
const closeBtn = this.querySelector('.close-dialog');
|
|
34
|
+
if (closeBtn) {
|
|
35
|
+
closeBtn.addEventListener('click', (e) => {
|
|
36
|
+
document.getElementById(this.id).classList.remove('show');
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
this.focus();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
render(styles) {
|
|
44
|
+
return `
|
|
45
|
+
<style>
|
|
46
|
+
${styles}
|
|
47
|
+
</style>
|
|
48
|
+
<slot></slot>
|
|
49
|
+
`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
open(){
|
|
53
|
+
window.addEventListener('keyup', (e) => {
|
|
54
|
+
if (e.key === "Escape") {
|
|
55
|
+
this.hide();
|
|
56
|
+
}
|
|
57
|
+
},{once:true});
|
|
58
|
+
|
|
59
|
+
this.classList.add('show');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
hide(){
|
|
63
|
+
this.classList.remove('show');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
customElements.define('fx-dialog', FxDialog);
|
package/src/ui/fx-items.js
CHANGED
|
@@ -105,10 +105,13 @@ export class FxItems extends FxControl {
|
|
|
105
105
|
const cutted = expr.substring(1, expr.length - 1);
|
|
106
106
|
const evaluated = evaluateXPath(cutted, node, newEntry);
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
// adding space around value to allow matching of 'words'
|
|
109
|
+
const spaced = ` ${evaluated} `;
|
|
110
|
+
|
|
111
|
+
const valAttr = ` ${this.getAttribute('value')} `;
|
|
109
112
|
input.value = evaluated;
|
|
110
113
|
input.setAttribute('id', id);
|
|
111
|
-
if (valAttr.indexOf(
|
|
114
|
+
if (valAttr.indexOf(spaced) !== -1) {
|
|
112
115
|
input.checked = true;
|
|
113
116
|
}
|
|
114
117
|
}
|