@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
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');
|
package/src/ui/fx-repeat.js
CHANGED
|
@@ -4,6 +4,7 @@ import { Fore } from '../fore.js';
|
|
|
4
4
|
import { foreElementMixin } from '../ForeElementMixin.js';
|
|
5
5
|
import { evaluateXPath } from '../xpath-evaluation.js';
|
|
6
6
|
import getInScopeContext from '../getInScopeContext.js';
|
|
7
|
+
import {XPathUtil} from "../xpath-util";
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* `fx-repeat`
|
|
@@ -115,14 +116,25 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
|
|
|
115
116
|
console.log('insert catched', nodes, this.index);
|
|
116
117
|
});
|
|
117
118
|
|
|
118
|
-
if (this.getOwnerForm().lazyRefresh) {
|
|
119
|
+
// if (this.getOwnerForm().lazyRefresh) {
|
|
119
120
|
this.mutationObserver = new MutationObserver(mutations => {
|
|
120
121
|
console.log('mutations', mutations);
|
|
121
|
-
|
|
122
|
+
|
|
123
|
+
if(mutations[0].type === "childList"){
|
|
124
|
+
const added = mutations[0].addedNodes[0];
|
|
125
|
+
if(added){
|
|
126
|
+
const path = XPathUtil.getPath(added);
|
|
127
|
+
console.log('path mutated',path);
|
|
128
|
+
// this.dispatch('path-mutated',{'path':path,'nodeset':this.nodeset,'index': this.index});
|
|
129
|
+
this.dispatch('path-mutated',{'path':path,'index': this.index});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
}
|
|
122
133
|
});
|
|
123
|
-
}
|
|
134
|
+
// }
|
|
124
135
|
this.getOwnerForm().registerLazyElement(this);
|
|
125
136
|
|
|
137
|
+
|
|
126
138
|
const style = `
|
|
127
139
|
:host{
|
|
128
140
|
}
|
|
@@ -168,7 +180,7 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
|
|
|
168
180
|
*/
|
|
169
181
|
_evalNodeset() {
|
|
170
182
|
// const inscope = this.getInScopeContext();
|
|
171
|
-
const inscope = getInScopeContext(this, this.ref);
|
|
183
|
+
const inscope = getInScopeContext(this.getAttributeNode('ref') || this, this.ref);
|
|
172
184
|
// console.log('##### inscope ', inscope);
|
|
173
185
|
// console.log('##### ref ', this.ref);
|
|
174
186
|
// now we got a nodeset and attach MutationObserver to it
|
|
@@ -248,6 +260,8 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
|
|
|
248
260
|
newItem.appendChild(clonedTemplate);
|
|
249
261
|
this.appendChild(newItem);
|
|
250
262
|
|
|
263
|
+
this._initVariables(newItem);
|
|
264
|
+
|
|
251
265
|
newItem.nodeset = this.nodeset[position - 1];
|
|
252
266
|
newItem.index = position;
|
|
253
267
|
}
|
|
@@ -353,9 +367,24 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
|
|
|
353
367
|
if (repeatItem.index === 1) {
|
|
354
368
|
this.applyIndex(repeatItem);
|
|
355
369
|
}
|
|
370
|
+
|
|
371
|
+
this._initVariables(repeatItem);
|
|
356
372
|
});
|
|
357
373
|
}
|
|
358
374
|
|
|
375
|
+
_initVariables(newRepeatItem) {
|
|
376
|
+
const inScopeVariables = new Map(this.inScopeVariables);
|
|
377
|
+
newRepeatItem.setInScopeVariables(inScopeVariables);
|
|
378
|
+
(function registerVariables(node) {
|
|
379
|
+
for (const child of node.children) {
|
|
380
|
+
if ('setInScopeVariables' in child) {
|
|
381
|
+
child.setInScopeVariables(inScopeVariables);
|
|
382
|
+
}
|
|
383
|
+
registerVariables(child);
|
|
384
|
+
}
|
|
385
|
+
})(newRepeatItem);
|
|
386
|
+
}
|
|
387
|
+
|
|
359
388
|
_clone() {
|
|
360
389
|
// const content = this.template.content.cloneNode(true);
|
|
361
390
|
this.template = this.shadowRoot.querySelector('template');
|
|
@@ -368,6 +397,12 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
|
|
|
368
397
|
item.removeAttribute('repeat-index');
|
|
369
398
|
});
|
|
370
399
|
}
|
|
400
|
+
|
|
401
|
+
setInScopeVariables(inScopeVariables) {
|
|
402
|
+
// Repeats are interesting: the variables should be scoped per repeat item, they should not be
|
|
403
|
+
// able to see the variables in adjacent repeat items!
|
|
404
|
+
this.inScopeVariables = new Map(inScopeVariables);
|
|
405
|
+
}
|
|
371
406
|
}
|
|
372
407
|
|
|
373
408
|
window.customElements.define('fx-repeat', FxRepeat);
|
package/src/ui/fx-trigger.js
CHANGED
|
@@ -80,6 +80,9 @@ export class FxTrigger extends XfAbstractControl {
|
|
|
80
80
|
repeatedItem.click();
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
// Update all child variables, but only once
|
|
84
|
+
this.querySelectorAll('fx-var').forEach(variableElement => variableElement.refresh());
|
|
85
|
+
|
|
83
86
|
const forLoop = async () => {
|
|
84
87
|
for (let i = 0; i < this.children.length; i += 1) {
|
|
85
88
|
const child = this.children[i];
|
package/src/xpath-evaluation.js
CHANGED
|
@@ -217,6 +217,32 @@ function functionNameResolver({ prefix, localName }, _arity) {
|
|
|
217
217
|
}
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
+
/**
|
|
221
|
+
* Get the variables in scope of the form element. These are the values of the variables that
|
|
222
|
+
* logically precede the formElement that declares the XPath
|
|
223
|
+
*
|
|
224
|
+
* @param {Node} formElement The element that declares the XPath
|
|
225
|
+
*
|
|
226
|
+
* @return {Object} A key-value mapping of the variables
|
|
227
|
+
*/
|
|
228
|
+
function getVariablesInScope(formElement) {
|
|
229
|
+
let closestActualFormElement = formElement;
|
|
230
|
+
while (closestActualFormElement && !('inScopeVariables' in closestActualFormElement)) {
|
|
231
|
+
closestActualFormElement = closestActualFormElement.parentNode;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (!closestActualFormElement) {
|
|
235
|
+
return {};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const variables = {};
|
|
239
|
+
for (const key of closestActualFormElement.inScopeVariables.keys()) {
|
|
240
|
+
const varElement = closestActualFormElement.inScopeVariables.get(key);
|
|
241
|
+
variables[key] = varElement.value;
|
|
242
|
+
}
|
|
243
|
+
return variables;
|
|
244
|
+
}
|
|
245
|
+
|
|
220
246
|
/**
|
|
221
247
|
* Evaluate an XPath to _any_ type. When possible, prefer to use any other function to ensure the
|
|
222
248
|
* type of the output is more predictable.
|
|
@@ -227,15 +253,23 @@ function functionNameResolver({ prefix, localName }, _arity) {
|
|
|
227
253
|
*/
|
|
228
254
|
export function evaluateXPath(xpath, contextNode, formElement, variables = {}) {
|
|
229
255
|
const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
|
|
256
|
+
const variablesInScope = getVariablesInScope(formElement);
|
|
230
257
|
|
|
231
|
-
return fxEvaluateXPath(
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
258
|
+
return fxEvaluateXPath(
|
|
259
|
+
xpath,
|
|
260
|
+
contextNode,
|
|
261
|
+
null,
|
|
262
|
+
{ ...variablesInScope, ...variables },
|
|
263
|
+
'xs:anyType',
|
|
264
|
+
{
|
|
265
|
+
currentContext: { formElement, variables },
|
|
266
|
+
moduleImports: {
|
|
267
|
+
xf: XFORMS_NAMESPACE_URI,
|
|
268
|
+
},
|
|
269
|
+
functionNameResolver,
|
|
270
|
+
namespaceResolver,
|
|
235
271
|
},
|
|
236
|
-
|
|
237
|
-
namespaceResolver,
|
|
238
|
-
});
|
|
272
|
+
);
|
|
239
273
|
}
|
|
240
274
|
|
|
241
275
|
/**
|
|
@@ -248,20 +282,15 @@ export function evaluateXPath(xpath, contextNode, formElement, variables = {}) {
|
|
|
248
282
|
*/
|
|
249
283
|
export function evaluateXPathToFirstNode(xpath, contextNode, formElement) {
|
|
250
284
|
const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
{
|
|
257
|
-
defaultFunctionNamespaceURI: XFORMS_NAMESPACE_URI,
|
|
258
|
-
moduleImports: {
|
|
259
|
-
xf: XFORMS_NAMESPACE_URI,
|
|
260
|
-
},
|
|
261
|
-
currentContext: { formElement },
|
|
262
|
-
namespaceResolver,
|
|
285
|
+
const variablesInScope = getVariablesInScope(formElement);
|
|
286
|
+
return fxEvaluateXPathToFirstNode(xpath, contextNode, null, variablesInScope, {
|
|
287
|
+
defaultFunctionNamespaceURI: XFORMS_NAMESPACE_URI,
|
|
288
|
+
moduleImports: {
|
|
289
|
+
xf: XFORMS_NAMESPACE_URI,
|
|
263
290
|
},
|
|
264
|
-
|
|
291
|
+
currentContext: { formElement },
|
|
292
|
+
namespaceResolver,
|
|
293
|
+
});
|
|
265
294
|
}
|
|
266
295
|
|
|
267
296
|
/**
|
|
@@ -274,20 +303,16 @@ export function evaluateXPathToFirstNode(xpath, contextNode, formElement) {
|
|
|
274
303
|
*/
|
|
275
304
|
export function evaluateXPathToNodes(xpath, contextNode, formElement) {
|
|
276
305
|
const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
{
|
|
283
|
-
|
|
284
|
-
functionNameResolver,
|
|
285
|
-
moduleImports: {
|
|
286
|
-
xf: XFORMS_NAMESPACE_URI,
|
|
287
|
-
},
|
|
288
|
-
namespaceResolver,
|
|
306
|
+
const variablesInScope = getVariablesInScope(formElement);
|
|
307
|
+
|
|
308
|
+
return fxEvaluateXPathToNodes(xpath, contextNode, null, variablesInScope, {
|
|
309
|
+
currentContext: { formElement },
|
|
310
|
+
functionNameResolver,
|
|
311
|
+
moduleImports: {
|
|
312
|
+
xf: XFORMS_NAMESPACE_URI,
|
|
289
313
|
},
|
|
290
|
-
|
|
314
|
+
namespaceResolver,
|
|
315
|
+
});
|
|
291
316
|
}
|
|
292
317
|
|
|
293
318
|
/**
|
|
@@ -300,20 +325,16 @@ export function evaluateXPathToNodes(xpath, contextNode, formElement) {
|
|
|
300
325
|
*/
|
|
301
326
|
export function evaluateXPathToBoolean(xpath, contextNode, formElement) {
|
|
302
327
|
const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
{
|
|
309
|
-
|
|
310
|
-
functionNameResolver,
|
|
311
|
-
moduleImports: {
|
|
312
|
-
xf: XFORMS_NAMESPACE_URI,
|
|
313
|
-
},
|
|
314
|
-
namespaceResolver,
|
|
328
|
+
const variablesInScope = getVariablesInScope(formElement);
|
|
329
|
+
|
|
330
|
+
return fxEvaluateXPathToBoolean(xpath, contextNode, null, variablesInScope, {
|
|
331
|
+
currentContext: { formElement },
|
|
332
|
+
functionNameResolver,
|
|
333
|
+
moduleImports: {
|
|
334
|
+
xf: XFORMS_NAMESPACE_URI,
|
|
315
335
|
},
|
|
316
|
-
|
|
336
|
+
namespaceResolver,
|
|
337
|
+
});
|
|
317
338
|
}
|
|
318
339
|
|
|
319
340
|
/**
|
|
@@ -335,7 +356,38 @@ export function evaluateXPathToString(
|
|
|
335
356
|
namespaceReferenceNode = formElement,
|
|
336
357
|
) {
|
|
337
358
|
const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
|
|
338
|
-
|
|
359
|
+
const variablesInScope = getVariablesInScope(formElement);
|
|
360
|
+
|
|
361
|
+
return fxEvaluateXPathToString(xpath, contextNode, domFacade, variablesInScope, {
|
|
362
|
+
currentContext: { formElement },
|
|
363
|
+
functionNameResolver,
|
|
364
|
+
moduleImports: {
|
|
365
|
+
xf: XFORMS_NAMESPACE_URI,
|
|
366
|
+
},
|
|
367
|
+
namespaceResolver,
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Evaluate an XPath to a set of strings
|
|
373
|
+
*
|
|
374
|
+
* @param {string} xpath The XPath to run
|
|
375
|
+
* @param {Node} contextNode The start of the XPath
|
|
376
|
+
* @param {Node} formElement The form element associated to the XPath
|
|
377
|
+
* @param {DomFacade} [domFacade=null] A DomFacade is used in bindings to intercept DOM
|
|
378
|
+
* access. This is used to determine dependencies between bind elements.
|
|
379
|
+
* @param {Node} formElement The element where the XPath is defined: used for namespace resolving
|
|
380
|
+
* @return {string}
|
|
381
|
+
*/
|
|
382
|
+
export function evaluateXPathToStrings(
|
|
383
|
+
xpath,
|
|
384
|
+
contextNode,
|
|
385
|
+
formElement,
|
|
386
|
+
domFacade = null,
|
|
387
|
+
namespaceReferenceNode = formElement,
|
|
388
|
+
) {
|
|
389
|
+
const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
|
|
390
|
+
return fxEvaluateXPathToStrings(
|
|
339
391
|
xpath,
|
|
340
392
|
contextNode,
|
|
341
393
|
domFacade,
|
|
@@ -371,20 +423,16 @@ export function evaluateXPathToNumber(
|
|
|
371
423
|
namespaceReferenceNode = formElement,
|
|
372
424
|
) {
|
|
373
425
|
const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
{
|
|
380
|
-
|
|
381
|
-
functionNameResolver,
|
|
382
|
-
moduleImports: {
|
|
383
|
-
xf: XFORMS_NAMESPACE_URI,
|
|
384
|
-
},
|
|
385
|
-
namespaceResolver,
|
|
426
|
+
const variablesInScope = getVariablesInScope(formElement);
|
|
427
|
+
|
|
428
|
+
return fxEvaluateXPathToNumber(xpath, contextNode, domFacade, variablesInScope, {
|
|
429
|
+
currentContext: { formElement },
|
|
430
|
+
functionNameResolver,
|
|
431
|
+
moduleImports: {
|
|
432
|
+
xf: XFORMS_NAMESPACE_URI,
|
|
386
433
|
},
|
|
387
|
-
|
|
434
|
+
namespaceResolver,
|
|
435
|
+
});
|
|
388
436
|
}
|
|
389
437
|
|
|
390
438
|
/**
|
|
@@ -487,6 +535,31 @@ export function resolveId(id, sourceObject, nodeName = null) {
|
|
|
487
535
|
return null;
|
|
488
536
|
}
|
|
489
537
|
|
|
538
|
+
const contextFunction = (dynamicContext, string) => {
|
|
539
|
+
const caller = dynamicContext.currentContext.formElement;
|
|
540
|
+
if (string) {
|
|
541
|
+
const instance = resolveId(string, caller);
|
|
542
|
+
if (instance) {
|
|
543
|
+
if (instance.nodeName === 'FX-REPEAT') {
|
|
544
|
+
const { nodeset } = instance;
|
|
545
|
+
for (let parent = caller; parent; parent = parent.parentNode) {
|
|
546
|
+
if (parent.parentNode === instance) {
|
|
547
|
+
const offset = Array.from(parent.parentNode.children).indexOf(parent);
|
|
548
|
+
return nodeset[offset];
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
return instance.nodeset;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
const parent = XPathUtil.getParentBindingElement(caller);
|
|
556
|
+
const p = caller.nodeName;
|
|
557
|
+
// const p = dynamicContext.domFacade.getParentElement();
|
|
558
|
+
|
|
559
|
+
if (parent) return parent;
|
|
560
|
+
return caller.getInScopeContext();
|
|
561
|
+
};
|
|
562
|
+
|
|
490
563
|
/**
|
|
491
564
|
* @param id as string
|
|
492
565
|
* @return instance data for given id serialized to string.
|
|
@@ -495,16 +568,18 @@ registerCustomXPathFunction(
|
|
|
495
568
|
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'context' },
|
|
496
569
|
[],
|
|
497
570
|
'item()?',
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
571
|
+
contextFunction,
|
|
572
|
+
);
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* @param id as string
|
|
576
|
+
* @return instance data for given id serialized to string.
|
|
577
|
+
*/
|
|
578
|
+
registerCustomXPathFunction(
|
|
579
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'context' },
|
|
580
|
+
['xs:string'],
|
|
581
|
+
'item()?',
|
|
582
|
+
contextFunction,
|
|
508
583
|
);
|
|
509
584
|
|
|
510
585
|
/**
|