@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.
@@ -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);
@@ -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.getOwnerForm(), this.detail);
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
@@ -83,6 +83,7 @@ export class Fore {
83
83
  'FX-TEXTAREA',
84
84
  'FX-TRIGGER',
85
85
  'FX-UPLOAD',
86
+ 'FX-VAR',
86
87
  ];
87
88
  }
88
89
 
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
- if (!this.model.mainGraph.hasNode(otherPath)) {
257
- this.model.mainGraph.addNode(otherPath, ref);
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.getInScopeContext();
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
- const changedModelItems = this.getModel().changed;
271
- const graph = this.getModel().mainGraph;
272
- let doRefresh = true;
273
- changedModelItems.forEach(item => {
274
- if(graph.hasNode(item.path)) {
275
- const deps = graph.dependentsOf(item.path, false);
276
- if (deps.length === 0) {
277
- doRefresh=false;
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
- if (!doRefresh) {
284
- this.dispatchEvent(new CustomEvent('refresh-done'));
285
- return ;
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._updateTemplateExpressions();
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
- // eslint-disable-next-line class-methods-use-this
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('recalculate instances ', this.instances);
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) {
@@ -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);
@@ -2,10 +2,18 @@ import { evaluateXPathToFirstNode } from './xpath-evaluation.js';
2
2
 
3
3
  import { XPathUtil } from './xpath-util.js';
4
4
 
5
- function _getParentElement(node) {
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 = _getParentElement(node);
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.ELEMENT_NODE && node.hasAttribute('context')) {
54
- const initialContext = _getInitialContext(node.parentNode, ref);
55
- const contextAttr = node.getAttribute('context');
56
- return evaluateXPathToFirstNode(contextAttr, initialContext, _getForeContext(parentElement));
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
@@ -30,6 +30,7 @@ export class ModelItem {
30
30
  this.bind = bind;
31
31
  this.changed = false;
32
32
  this.alerts = [];
33
+ this.boundControls = [];
33
34
  // this.value = this._getValue();
34
35
  }
35
36
 
@@ -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);
@@ -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
- const valAttr = this.getAttribute('value');
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(input.value) !== -1) {
114
+ if (valAttr.indexOf(spaced) !== -1) {
112
115
  input.checked = true;
113
116
  }
114
117
  }