@jinntec/fore 1.0.0-2 → 1.0.0-5
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 +37 -19
- package/dist/fore-all.js +10 -10
- package/dist/fore-debug.js +140 -0
- package/index.js +8 -1
- package/package.json +4 -4
- package/resources/fore.css +55 -0
- package/src/DependencyNotifyingDomFacade.js +9 -7
- package/src/ForeElementMixin.js +25 -3
- package/src/actions/abstract-action.js +5 -2
- package/src/actions/fx-action.js +6 -1
- package/src/actions/fx-hide.js +24 -0
- package/src/actions/fx-insert.js +32 -35
- package/src/actions/fx-setvalue.js +1 -1
- package/src/actions/fx-show.js +23 -0
- package/src/actions/fx-toggle.js +2 -1
- package/src/dep_graph.js +3 -1
- package/src/fore.js +34 -2
- package/src/fx-bind.js +15 -13
- package/src/fx-fore.js +146 -29
- 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 +47 -7
- package/src/modelitem.js +1 -0
- package/src/ui/abstract-control.js +9 -1
- package/src/ui/fx-container.js +4 -0
- package/src/ui/fx-dialog.js +77 -0
- package/src/ui/fx-items.js +6 -5
- package/src/ui/fx-output.js +9 -11
- package/src/ui/fx-repeat.js +51 -11
- package/src/ui/fx-trigger.js +3 -0
- package/src/xpath-evaluation.js +150 -97
- package/src/xpath-util.js +18 -2
- package/dist/fore.js +0 -2
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
|
}
|
|
@@ -152,6 +152,7 @@ export class FxFore extends HTMLElement {
|
|
|
152
152
|
|
|
153
153
|
const html = `
|
|
154
154
|
<jinn-toast id="message" gravity="bottom" position="left"></jinn-toast>
|
|
155
|
+
<jinn-toast id="sticky" gravity="bottom" position="left" duration="-1" close="true" data-class="sticky-message"></jinn-toast>
|
|
155
156
|
<jinn-toast id="error" text="error" duration="-1" data-class="error" close="true" position="left" gravity="bottom"></jinn-toast>
|
|
156
157
|
<slot></slot>
|
|
157
158
|
<div id="modalMessage" class="overlay">
|
|
@@ -170,6 +171,10 @@ export class FxFore extends HTMLElement {
|
|
|
170
171
|
</style>
|
|
171
172
|
${html}
|
|
172
173
|
`;
|
|
174
|
+
|
|
175
|
+
this.toRefresh = [];
|
|
176
|
+
this.initialRun = true;
|
|
177
|
+
this.someInstanceDataStructureChanged = false;
|
|
173
178
|
}
|
|
174
179
|
|
|
175
180
|
connectedCallback() {
|
|
@@ -183,6 +188,12 @@ export class FxFore extends HTMLElement {
|
|
|
183
188
|
this.intersectionObserver = new IntersectionObserver(this.handleIntersect, options);
|
|
184
189
|
}
|
|
185
190
|
|
|
191
|
+
this.src = this.hasAttribute('src')? this.getAttribute('src'):null;
|
|
192
|
+
if(this.src){
|
|
193
|
+
this._loadFromSrc();
|
|
194
|
+
return ;
|
|
195
|
+
}
|
|
196
|
+
|
|
186
197
|
const slot = this.shadowRoot.querySelector('slot');
|
|
187
198
|
slot.addEventListener('slotchange', event => {
|
|
188
199
|
const children = event.target.assignedElements();
|
|
@@ -198,10 +209,71 @@ export class FxFore extends HTMLElement {
|
|
|
198
209
|
console.log(
|
|
199
210
|
`########## FORE: kick off processing for ... ${window.location.href} ##########`,
|
|
200
211
|
);
|
|
212
|
+
if(this.src){
|
|
213
|
+
console.log('########## FORE: loaded from ... ', this.src, '##########');
|
|
214
|
+
}
|
|
201
215
|
modelElement.modelConstruct();
|
|
202
216
|
}
|
|
203
217
|
this.model = modelElement;
|
|
204
218
|
});
|
|
219
|
+
this.addEventListener('path-mutated', (e) =>{
|
|
220
|
+
console.log('path-mutated event received', e.detail.path, e.detail.index);
|
|
221
|
+
this.someInstanceDataStructureChanged = true;
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
addToRefresh(modelItem){
|
|
227
|
+
const found = this.toRefresh.find(mi => mi.path === modelItem.path );
|
|
228
|
+
if(!found){
|
|
229
|
+
this.toRefresh.push(modelItem);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* loads a Fore from an URL given by `src`.
|
|
235
|
+
*
|
|
236
|
+
* Will extract the `fx-fore` element from that target file and use and replace current `fx-fore` element with the loaded one.
|
|
237
|
+
* @private
|
|
238
|
+
*/
|
|
239
|
+
_loadFromSrc() {
|
|
240
|
+
console.log('########## loading Fore from ',this.src ,'##########');
|
|
241
|
+
fetch(this.src, {
|
|
242
|
+
method: 'GET',
|
|
243
|
+
mode: 'cors',
|
|
244
|
+
credentials: 'include',
|
|
245
|
+
headers: {
|
|
246
|
+
'Content-Type': 'text/html',
|
|
247
|
+
},
|
|
248
|
+
})
|
|
249
|
+
.then(response => {
|
|
250
|
+
const responseContentType = response.headers.get('content-type').toLowerCase();
|
|
251
|
+
console.log('********** responseContentType *********', responseContentType);
|
|
252
|
+
if (responseContentType.startsWith('text/html')) {
|
|
253
|
+
// const htmlResponse = response.text();
|
|
254
|
+
// return new DOMParser().parseFromString(htmlResponse, 'text/html');
|
|
255
|
+
// return response.text();
|
|
256
|
+
return response.text().then(result =>
|
|
257
|
+
// console.log('xml ********', result);
|
|
258
|
+
new DOMParser().parseFromString(result, 'text/html'),
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
return 'done';
|
|
262
|
+
})
|
|
263
|
+
.then(data => {
|
|
264
|
+
// const theFore = fxEvaluateXPathToFirstNode('//fx-fore', data.firstElementChild);
|
|
265
|
+
const theFore = data.querySelector('fx-fore');
|
|
266
|
+
|
|
267
|
+
// console.log('thefore', theFore)
|
|
268
|
+
if(!theFore){
|
|
269
|
+
this.dispatchEvent(new CustomEvent('error',{detail:{message: `Fore element not found in '${this.src}'. Maybe wrapped within 'template' element?`}}));
|
|
270
|
+
}
|
|
271
|
+
theFore.setAttribute('from-src', this.src);
|
|
272
|
+
this.replaceWith(theFore);
|
|
273
|
+
})
|
|
274
|
+
.catch(error => {
|
|
275
|
+
this.dispatchEvent(new CustomEvent('error',{detail:{message: `'${this.src}' not found or does not contain Fore element.`}}));
|
|
276
|
+
});
|
|
205
277
|
}
|
|
206
278
|
|
|
207
279
|
/**
|
|
@@ -266,32 +338,56 @@ export class FxFore extends HTMLElement {
|
|
|
266
338
|
|
|
267
339
|
console.time('refresh');
|
|
268
340
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
341
|
+
// ### refresh Fore UI elements
|
|
342
|
+
console.time('refreshChildren');
|
|
343
|
+
console.log('toRefresh',this.toRefresh);
|
|
344
|
+
|
|
345
|
+
if(!this.initialRun && this.toRefresh.length !== 0){
|
|
346
|
+
let needsRefresh = false;
|
|
347
|
+
|
|
348
|
+
// ### after recalculation the changed modelItems are copied to 'toRefresh' array for processing
|
|
349
|
+
this.toRefresh.forEach(modelItem => {
|
|
350
|
+
// check if modelItem has boundControls - if so, call refresh() for each of them
|
|
351
|
+
const controlsToRefresh = modelItem.boundControls;
|
|
352
|
+
if(controlsToRefresh){
|
|
353
|
+
controlsToRefresh.forEach(ctrl => {
|
|
354
|
+
ctrl.refresh();
|
|
355
|
+
});
|
|
278
356
|
}
|
|
279
|
-
}
|
|
280
|
-
});
|
|
281
|
-
this.getModel().changed = [];
|
|
282
357
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
358
|
+
// ### check if other controls depend on current modelItem
|
|
359
|
+
const mainGraph = this.getModel().mainGraph;
|
|
360
|
+
if(mainGraph && mainGraph.hasNode(modelItem.path)){
|
|
361
|
+
const deps = this.getModel().mainGraph.dependentsOf(modelItem.path, false);
|
|
362
|
+
// ### iterate dependant modelItems and refresh all their boundControls
|
|
363
|
+
if(deps.length !== 0){
|
|
364
|
+
deps.forEach(dep => {
|
|
365
|
+
// ### if changed modelItem has a 'facet' path we use the basePath that is the locationPath without facet name
|
|
366
|
+
const basePath = XPathUtil.getBasePath(dep);
|
|
367
|
+
const modelItemOfDep = this.getModel().modelItems.find(mip => mip.path === basePath);
|
|
368
|
+
// ### refresh all boundControls
|
|
369
|
+
modelItemOfDep.boundControls.forEach(control =>{control.refresh()});
|
|
370
|
+
});
|
|
371
|
+
needsRefresh = true;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
this.toRefresh = [];
|
|
376
|
+
if(!needsRefresh){
|
|
377
|
+
console.log('skipping refresh - no dependants');
|
|
378
|
+
}
|
|
379
|
+
}else{
|
|
380
|
+
Fore.refreshChildren(this, true);
|
|
381
|
+
console.timeEnd('refreshChildren');
|
|
286
382
|
}
|
|
287
|
-
*/
|
|
288
|
-
// ### refresh Fore UI elements
|
|
289
|
-
console.time('refreshChildren');
|
|
290
|
-
Fore.refreshChildren(this, true);
|
|
291
|
-
console.timeEnd('refreshChildren');
|
|
292
383
|
|
|
293
384
|
// ### refresh template expressions
|
|
294
|
-
this.
|
|
385
|
+
if(this.initialRun || this.someInstanceDataStructureChanged){
|
|
386
|
+
this._updateTemplateExpressions();
|
|
387
|
+
this.someInstanceDataStructureChanged = false; //reset
|
|
388
|
+
}
|
|
389
|
+
this._processTemplateExpressions();
|
|
390
|
+
|
|
295
391
|
console.timeEnd('refresh');
|
|
296
392
|
|
|
297
393
|
console.groupEnd();
|
|
@@ -319,6 +415,8 @@ export class FxFore extends HTMLElement {
|
|
|
319
415
|
this.storedTemplateExpressions = [];
|
|
320
416
|
}
|
|
321
417
|
|
|
418
|
+
console.log('######### storedTemplateExpressions', this.storedTemplateExpressions.length);
|
|
419
|
+
|
|
322
420
|
/*
|
|
323
421
|
storing expressions and their nodes for re-evaluation
|
|
324
422
|
*/
|
|
@@ -329,21 +427,26 @@ export class FxFore extends HTMLElement {
|
|
|
329
427
|
}
|
|
330
428
|
const expr = this._getTemplateExpression(node);
|
|
331
429
|
|
|
430
|
+
// console.log('storedTemplateExpressionByNode', this.storedTemplateExpressionByNode);
|
|
332
431
|
this.storedTemplateExpressionByNode.set(node, expr);
|
|
333
432
|
});
|
|
433
|
+
console.log('stored template expressions ', this.storedTemplateExpressionByNode);
|
|
334
434
|
|
|
335
435
|
// TODO: Should we clean up nodes that existed but are now gone?
|
|
436
|
+
this._processTemplateExpressions();
|
|
437
|
+
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
_processTemplateExpressions() {
|
|
336
441
|
for (const node of this.storedTemplateExpressionByNode.keys()) {
|
|
337
442
|
this._processTemplateExpression({
|
|
338
443
|
node,
|
|
339
444
|
expr: this.storedTemplateExpressionByNode.get(node),
|
|
340
445
|
});
|
|
341
446
|
}
|
|
342
|
-
|
|
343
|
-
console.log('stored template expressions ', this.storedTemplateExpressionByNode);
|
|
344
447
|
}
|
|
345
448
|
|
|
346
|
-
|
|
449
|
+
// eslint-disable-next-line class-methods-use-this
|
|
347
450
|
_processTemplateExpression(exprObj) {
|
|
348
451
|
// console.log('processing template expression ', exprObj);
|
|
349
452
|
|
|
@@ -553,6 +656,18 @@ export class FxFore extends HTMLElement {
|
|
|
553
656
|
|
|
554
657
|
await this._lazyCreateInstance();
|
|
555
658
|
|
|
659
|
+
console.log('registering variables!');
|
|
660
|
+
const variables = new Map();
|
|
661
|
+
(function registerVariables(node) {
|
|
662
|
+
for (const child of node.children) {
|
|
663
|
+
if ('setInScopeVariables' in child) {
|
|
664
|
+
child.setInScopeVariables(variables);
|
|
665
|
+
}
|
|
666
|
+
registerVariables(child);
|
|
667
|
+
}
|
|
668
|
+
})(this);
|
|
669
|
+
console.log('Found variables:', variables);
|
|
670
|
+
|
|
556
671
|
const options = {
|
|
557
672
|
root: null,
|
|
558
673
|
rootMargin: '0px',
|
|
@@ -562,8 +677,10 @@ export class FxFore extends HTMLElement {
|
|
|
562
677
|
await this.refresh();
|
|
563
678
|
// this.style.display='block'
|
|
564
679
|
this.classList.add('fx-ready');
|
|
680
|
+
document.body.classList.add('fx-ready');
|
|
565
681
|
|
|
566
682
|
this.ready = true;
|
|
683
|
+
this.initialRun = false;
|
|
567
684
|
console.log('### <<<<< dispatching ready >>>>>');
|
|
568
685
|
console.log('########## modelItems: ', this.getModel().modelItems);
|
|
569
686
|
console.log('########## FORE: form fully initialized... ##########');
|
|
@@ -613,9 +730,9 @@ export class FxFore extends HTMLElement {
|
|
|
613
730
|
this.shadowRoot.getElementById('messageContent').innerText = msg;
|
|
614
731
|
// this.shadowRoot.getElementById('modalMessage').open();
|
|
615
732
|
this.shadowRoot.getElementById('modalMessage').classList.add('show');
|
|
616
|
-
} else if (level === '
|
|
733
|
+
} else if (level === 'sticky') {
|
|
617
734
|
// const notification = this.$.modeless;
|
|
618
|
-
this.shadowRoot.querySelector('#
|
|
735
|
+
this.shadowRoot.querySelector('#sticky').showToast(msg);
|
|
619
736
|
} else {
|
|
620
737
|
const toast = this.shadowRoot.querySelector('#message');
|
|
621
738
|
toast.showToast(msg);
|
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,20 @@ import { evaluateXPathToFirstNode } from './xpath-evaluation.js';
|
|
|
2
2
|
|
|
3
3
|
import { XPathUtil } from './xpath-util.js';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
|
|
6
|
+
function _getElement(node) {
|
|
7
|
+
if (node && node.nodeType && node.nodeType === Node.ATTRIBUTE_NODE) {
|
|
8
|
+
// The context of an attribute is the ref of the element it's defined on
|
|
7
9
|
return node.ownerElement;
|
|
8
10
|
}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
14
|
+
// The context of a query should be the element having a ref
|
|
15
|
+
return node;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// For text nodes, just start looking from the parent element
|
|
9
19
|
return node.parentNode;
|
|
10
20
|
}
|
|
11
21
|
|
|
@@ -29,7 +39,10 @@ function _getInitialContext(node, ref) {
|
|
|
29
39
|
const model = _getModelInContext(node);
|
|
30
40
|
if (XPathUtil.isAbsolutePath(ref)) {
|
|
31
41
|
const instanceId = XPathUtil.getInstanceId(ref);
|
|
32
|
-
|
|
42
|
+
if(instanceId){
|
|
43
|
+
return model.getInstance(instanceId).getDefaultContext();
|
|
44
|
+
}
|
|
45
|
+
return model.getDefaultInstance().getDefaultContext();
|
|
33
46
|
}
|
|
34
47
|
if (model.getDefaultInstance() !== null && model.inited) {
|
|
35
48
|
return model.getDefaultInstance().getDefaultContext();
|
|
@@ -38,7 +51,7 @@ function _getInitialContext(node, ref) {
|
|
|
38
51
|
}
|
|
39
52
|
|
|
40
53
|
export default function getInScopeContext(node, ref) {
|
|
41
|
-
const parentElement =
|
|
54
|
+
const parentElement = _getElement(node);
|
|
42
55
|
/*
|
|
43
56
|
if(parentElement.nodeName.toUpperCase() === 'FX-REPEATITEM'){
|
|
44
57
|
return parentElement.nodeset;
|
|
@@ -47,13 +60,40 @@ export default function getInScopeContext(node, ref) {
|
|
|
47
60
|
|
|
48
61
|
const repeatItem = parentElement.closest('fx-repeatitem');
|
|
49
62
|
if (repeatItem) {
|
|
63
|
+
if(node.nodeName === 'context'){
|
|
64
|
+
return evaluateXPathToFirstNode(node.nodeValue, repeatItem.nodeset, _getForeContext(parentElement));
|
|
65
|
+
}
|
|
50
66
|
return repeatItem.nodeset;
|
|
51
67
|
}
|
|
52
68
|
|
|
53
|
-
if (
|
|
54
|
-
const initialContext = _getInitialContext(node.parentNode, ref);
|
|
55
|
-
const contextAttr = node.getAttribute('context');
|
|
69
|
+
if (parentElement.hasAttribute('context')) {
|
|
70
|
+
const initialContext = _getInitialContext(node.ownerElement.parentNode, ref);
|
|
71
|
+
const contextAttr = node.ownerElement.getAttribute('context');
|
|
56
72
|
return evaluateXPathToFirstNode(contextAttr, initialContext, _getForeContext(parentElement));
|
|
57
73
|
}
|
|
74
|
+
|
|
75
|
+
if (node.nodeType === Node.ATTRIBUTE_NODE && node.nodeName === 'context') {
|
|
76
|
+
const initialContext = _getInitialContext(node.ownerElement.parentNode, ref);
|
|
77
|
+
const contextAttr = node.ownerElement.getAttribute('context');
|
|
78
|
+
return evaluateXPathToFirstNode(contextAttr, initialContext, _getForeContext(parentElement));
|
|
79
|
+
}
|
|
80
|
+
if (node.nodeType === Node.ATTRIBUTE_NODE && node.nodeName === 'ref') {
|
|
81
|
+
// Note: do not consider the ref of the owner element since it should not be used to define the
|
|
82
|
+
// context
|
|
83
|
+
if (node.ownerElement.hasAttribute('context')) {
|
|
84
|
+
const initialContext = _getInitialContext(node.ownerElement.parentNode, ref);
|
|
85
|
+
const contextAttr = node.ownerElement.getAttribute('context');
|
|
86
|
+
return evaluateXPathToFirstNode(contextAttr, initialContext, _getForeContext(parentElement));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Never resolve the context from a ref itself!
|
|
90
|
+
return _getInitialContext(parentElement.parentNode, ref);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// if (node.nodeType === Node.ELEMENT_NODE && node.hasAttribute('context')) {
|
|
94
|
+
// const initialContext = _getInitialContext(node.parentNode, ref);
|
|
95
|
+
// const contextAttr = node.getAttribute('context');
|
|
96
|
+
// return evaluateXPathToFirstNode(contextAttr, initialContext, _getForeContext(parentElement));
|
|
97
|
+
// }
|
|
58
98
|
return _getInitialContext(parentElement, ref);
|
|
59
99
|
}
|
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();
|
package/src/ui/fx-container.js
CHANGED
|
@@ -44,6 +44,10 @@ export class FxContainer extends foreElementMixin(HTMLElement) {
|
|
|
44
44
|
if (this.isBound()) {
|
|
45
45
|
this.evalInContext();
|
|
46
46
|
this.modelItem = this.getModelItem();
|
|
47
|
+
if(!this.modelItem.boundControls.includes(this)){
|
|
48
|
+
this.modelItem.boundControls.push(this);
|
|
49
|
+
}
|
|
50
|
+
|
|
47
51
|
// this.value = this.modelItem.value;
|
|
48
52
|
}
|
|
49
53
|
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import {Fore} from '../fore.js';
|
|
2
|
+
export class FxDialog extends HTMLElement {
|
|
3
|
+
|
|
4
|
+
static get properties() {
|
|
5
|
+
return {
|
|
6
|
+
id: String
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
constructor() {
|
|
11
|
+
super();
|
|
12
|
+
this.attachShadow({mode: 'open'});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
connectedCallback() {
|
|
16
|
+
const style = `
|
|
17
|
+
:host {
|
|
18
|
+
display:none;
|
|
19
|
+
height: 100vh;
|
|
20
|
+
width:100vw;
|
|
21
|
+
position:fixed;
|
|
22
|
+
left:0;
|
|
23
|
+
top:0;
|
|
24
|
+
right:0;
|
|
25
|
+
bottom:0;
|
|
26
|
+
transition:opacity 0.4s linear;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
`;
|
|
30
|
+
|
|
31
|
+
this.shadowRoot.innerHTML = this.render(style);
|
|
32
|
+
this.id = this.getAttribute('id');
|
|
33
|
+
|
|
34
|
+
// const dialog = document.getElementById(this.id);
|
|
35
|
+
|
|
36
|
+
const closeBtn = this.querySelector('.close-dialog');
|
|
37
|
+
if (closeBtn) {
|
|
38
|
+
closeBtn.addEventListener('click', (e) => {
|
|
39
|
+
document.getElementById(this.id).classList.remove('show');
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
this.addEventListener('transitionend',()=>{
|
|
44
|
+
console.log('transitionend');
|
|
45
|
+
// this.style.display = 'none';
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
this.focus();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
render(styles) {
|
|
52
|
+
return `
|
|
53
|
+
<style>
|
|
54
|
+
${styles}
|
|
55
|
+
</style>
|
|
56
|
+
<slot></slot>
|
|
57
|
+
`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
open(){
|
|
61
|
+
window.addEventListener('keyup', (e) => {
|
|
62
|
+
if (e.key === "Escape") {
|
|
63
|
+
this.hide();
|
|
64
|
+
}
|
|
65
|
+
},{once:true});
|
|
66
|
+
|
|
67
|
+
this.classList.add('show');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async hide(){
|
|
71
|
+
await Fore.fadeOutElement(this,400);
|
|
72
|
+
this.classList.remove('show');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
customElements.define('fx-dialog', FxDialog);
|
package/src/ui/fx-items.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import XfAbstractControl from './abstract-control.js';
|
|
2
1
|
import { evaluateXPath, evaluateXPathToString, evaluateXPathToNodes } from '../xpath-evaluation.js';
|
|
3
|
-
import getInScopeContext from '../getInScopeContext.js';
|
|
4
2
|
import FxControl from './fx-control.js';
|
|
5
3
|
import { Fore } from '../fore.js';
|
|
6
4
|
|
|
@@ -103,12 +101,15 @@ export class FxItems extends FxControl {
|
|
|
103
101
|
// getting expr
|
|
104
102
|
const expr = input.value;
|
|
105
103
|
const cutted = expr.substring(1, expr.length - 1);
|
|
106
|
-
const evaluated =
|
|
104
|
+
const evaluated = evaluateXPathToString(cutted, node, newEntry);
|
|
107
105
|
|
|
108
|
-
|
|
106
|
+
// adding space around value to allow matching of 'words'
|
|
107
|
+
const spaced = ` ${evaluated} `;
|
|
108
|
+
|
|
109
|
+
const valAttr = ` ${this.getAttribute('value')} `;
|
|
109
110
|
input.value = evaluated;
|
|
110
111
|
input.setAttribute('id', id);
|
|
111
|
-
if (valAttr.indexOf(
|
|
112
|
+
if (valAttr.indexOf(spaced) !== -1) {
|
|
112
113
|
input.checked = true;
|
|
113
114
|
}
|
|
114
115
|
}
|
package/src/ui/fx-output.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import XfAbstractControl from './abstract-control.js';
|
|
2
|
-
import { evaluateXPath,
|
|
2
|
+
import { evaluateXPath, evaluateXPathToStrings } from '../xpath-evaluation.js';
|
|
3
3
|
import getInScopeContext from '../getInScopeContext.js';
|
|
4
4
|
// import {markdown} from '../drawdown.js';
|
|
5
5
|
|
|
@@ -68,19 +68,16 @@ export class FxOutput extends XfAbstractControl {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
async refresh() {
|
|
71
|
-
//
|
|
72
|
-
|
|
71
|
+
// Resolve the ref first. The ref will set the `nodeset` which is important for the 'context'
|
|
72
|
+
if (this.ref) {
|
|
73
|
+
await super.refresh();
|
|
74
|
+
}
|
|
73
75
|
|
|
76
|
+
// ### 2. Eval the value
|
|
74
77
|
if (this.valueAttr) {
|
|
75
78
|
this.value = this.getValue();
|
|
76
79
|
await this.updateWidgetValue();
|
|
77
|
-
return;
|
|
78
80
|
}
|
|
79
|
-
// ### 2. eval 'ref' attr
|
|
80
|
-
if (this.ref) {
|
|
81
|
-
super.refresh();
|
|
82
|
-
}
|
|
83
|
-
// ### 3. use inline content which is there anyway
|
|
84
81
|
}
|
|
85
82
|
|
|
86
83
|
getValue() {
|
|
@@ -90,7 +87,8 @@ export class FxOutput extends XfAbstractControl {
|
|
|
90
87
|
if (this.hasAttribute('html')) {
|
|
91
88
|
return evaluateXPath(this.valueAttr, inscopeContext, this);
|
|
92
89
|
}
|
|
93
|
-
|
|
90
|
+
|
|
91
|
+
return evaluateXPathToStrings(this.valueAttr, inscopeContext, this)[0];
|
|
94
92
|
} catch (error) {
|
|
95
93
|
console.error(error);
|
|
96
94
|
this.dispatch('error', { message: error });
|
|
@@ -119,7 +117,7 @@ export class FxOutput extends XfAbstractControl {
|
|
|
119
117
|
return;
|
|
120
118
|
*/
|
|
121
119
|
|
|
122
|
-
const node = this.modelItem
|
|
120
|
+
const { node } = this.modelItem;
|
|
123
121
|
|
|
124
122
|
if (node.nodeType) {
|
|
125
123
|
// const mainSlot = this.shadowRoot.querySelector('#main');
|