@jinntec/fore 0.25.0 → 1.0.0-2

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.
Files changed (41) hide show
  1. package/README.md +75 -22
  2. package/dist/fore-all.js +11 -11
  3. package/dist/fore.js +1 -1
  4. package/index.js +5 -1
  5. package/package.json +17 -6
  6. package/resources/fore.css +121 -4
  7. package/resources/toastify.css +3 -1
  8. package/src/DependencyNotifyingDomFacade.js +9 -1
  9. package/src/ForeElementMixin.js +83 -12
  10. package/src/actions/abstract-action.js +101 -27
  11. package/src/actions/fx-action.js +4 -2
  12. package/src/actions/fx-append.js +21 -18
  13. package/src/actions/fx-confirm.js +22 -0
  14. package/src/actions/fx-dispatch.js +10 -2
  15. package/src/actions/fx-insert.js +35 -30
  16. package/src/actions/fx-message.js +7 -1
  17. package/src/actions/fx-send.js +1 -1
  18. package/src/actions/fx-setvalue.js +2 -9
  19. package/src/dep_graph.js +9 -0
  20. package/src/drawdown.js +172 -0
  21. package/src/fore.js +126 -18
  22. package/src/functions/fx-function.js +2 -2
  23. package/src/fx-bind.js +11 -7
  24. package/src/fx-fore.js +283 -67
  25. package/src/fx-header.js +20 -0
  26. package/src/fx-instance.js +54 -10
  27. package/src/fx-model.js +175 -38
  28. package/src/fx-submission.js +235 -53
  29. package/src/getInScopeContext.js +2 -3
  30. package/src/ui/abstract-control.js +23 -44
  31. package/src/ui/fx-alert.js +20 -19
  32. package/src/ui/fx-container.js +9 -4
  33. package/src/ui/fx-control.js +92 -37
  34. package/src/ui/fx-inspector.js +44 -0
  35. package/src/ui/fx-items.js +104 -20
  36. package/src/ui/fx-output.js +92 -3
  37. package/src/ui/fx-repeat.js +87 -80
  38. package/src/ui/fx-repeatitem.js +38 -48
  39. package/src/ui/fx-trigger.js +49 -27
  40. package/src/xpath-evaluation.js +533 -235
  41. package/src/xpath-util.js +50 -12
@@ -1,9 +1,29 @@
1
1
  @import 'toastify.css';
2
+ @import 'vars.css';
2
3
 
4
+ html{
5
+ --inspector-bg:var(--paper-grey-500);
6
+ --inspector-pre-bg:var(--paper-grey-100);
7
+ --inspector-color:var(--paper-grey-800);
8
+ --inspector-instance-height:200px;
9
+ }
3
10
  [unresolved]{
4
11
  display: none;
5
12
  }
13
+ [disabled] {
14
+ pointer-events: none;
15
+ cursor: default;
16
+ }
17
+ fx-trigger a[disabled] {
18
+ color:lightgrey;
19
+ }
20
+ fx-trigger img[disabled]{
21
+ filter:blur(2px);
22
+ }
6
23
 
24
+ .error{
25
+ background: var(--paper-red-500);
26
+ }
7
27
  fx-alert{
8
28
  color:darkred;
9
29
  font-size: 0.9rem;
@@ -13,16 +33,91 @@ fx-model, fx-model *, fx-model ::slotted(fx-instance), fx-instance{
13
33
  display:none;
14
34
  }
15
35
 
16
- fx-control{
36
+ fx-control, fx-trigger{
37
+ white-space: nowrap;
38
+ position: relative;
39
+ }
40
+ .fx-checkbox{
17
41
  white-space: nowrap;
18
42
  }
43
+ fx-hint{
44
+ display: none;
45
+ }
46
+ fx-repeatitem{
47
+ position:relative;
48
+ /*
49
+ opacity:1;
50
+ -webkit-transition: opacity 3s;
51
+ -moz-transition: opacity 3s;
52
+ transition: opacity 3s;
53
+ */
54
+ }
55
+ .hidden {
56
+ visibility: hidden;
57
+ opacity: 0;
58
+ transition: visibility 0s 2s, opacity 2s linear;
59
+ }
60
+
61
+ /* fx-inspector styles */
62
+ fx-inspector{
63
+ position:fixed;
64
+ left:0;
65
+ right:0;
66
+ bottom:0;
67
+ width: 100%;
68
+ background: var(--inspector-bg);
69
+ color: white;
70
+ max-height: 33%;
71
+ overflow: scroll;
72
+ border-top:3px solid black;
73
+ opacity: 0.9;
74
+ }
75
+ fx-inspector::before{
76
+ content:'Instance Inspector';
77
+ font-style:italic;
78
+ position:absolute;
79
+ top:0;
80
+ right: 0.3rem;
81
+ z-index: 10;
82
+ font-size: 0.8rem;
83
+ }
84
+ fx-inspector details{
85
+ padding: 1rem;
86
+ }
87
+ fx-inspector pre{
88
+ background: var(--inspector-pre-bg);
89
+ border-radius: 0.5rem;
90
+ padding: 0.3rem;
91
+ max-height: var(--inspector-instance-height);
92
+ /*overflow: scroll;*/
93
+ }
94
+ fx-inspector details{
95
+ overflow: auto;
96
+ margin:0;
97
+ }
98
+ fx-inspector summary{
99
+ padding: 0;
100
+ }
101
+
102
+
103
+ /*
104
+ .visible {
105
+ visibility: visible;
106
+ opacity: 1;
107
+ transition: opacity 2s linear;
108
+ }
109
+ */
110
+
19
111
  .required:after {
20
112
  content: '*';
21
113
  color: red;
22
114
  padding-left: 4px;
115
+ right:3px;
116
+ top:3px;
117
+ z-index: 1;
23
118
  }
24
119
 
25
- fx-control [required]:after {
120
+ [required]:after {
26
121
  content: "*";
27
122
  display: inline;
28
123
  color: red;
@@ -34,8 +129,30 @@ fx-control [required]:after {
34
129
  .logtree details summary{
35
130
 
36
131
  }
37
-
132
+ .non-relevant{
133
+ opacity: 0;
134
+ height: 0;
135
+ transition: opacity 1s;
136
+ }
38
137
  .vertical label{
39
138
  display: block;
40
139
  }
41
-
140
+ [refresh-on-view]{
141
+ /*opacity: 0;*/
142
+ }
143
+ .loaded{
144
+ animation: fadein 0.3s forwards;
145
+ }
146
+ /* avoid flicker from nested lazily loaded elements */
147
+ .loaded .loaded{
148
+ animation: none;
149
+ opacity: 1;
150
+ }
151
+ @keyframes fadein {
152
+ 0% {
153
+ opacity:0;
154
+ }
155
+ 100% {
156
+ opacity:1;
157
+ }
158
+ }
@@ -12,7 +12,7 @@
12
12
  display: inline-block;
13
13
  box-shadow: 0 3px 6px -1px rgba(0, 0, 0, 0.12), 0 10px 36px -4px rgba(77, 96, 232, 0.3);
14
14
  background: -webkit-linear-gradient(315deg, #73a5ff, #5477f5);
15
- background: linear-gradient(135deg, #73a5ff, #5477f5);
15
+ background: linear-gradient(135deg, #f8f8f9, #cfcfcf);
16
16
  position: fixed;
17
17
  opacity: 0;
18
18
  transition: all 0.4s cubic-bezier(0.215, 0.61, 0.355, 1);
@@ -21,6 +21,8 @@
21
21
  text-decoration: none;
22
22
  max-width: calc(50% - 20px);
23
23
  z-index: 2147483647;
24
+ font-weight: 300;
25
+ color:black;
24
26
  }
25
27
 
26
28
  .toastify.on {
@@ -9,7 +9,7 @@ import { getBucketsForNode } from 'fontoxpath';
9
9
  */
10
10
  export class DependencyNotifyingDomFacade {
11
11
  /**
12
- * @param {function(touchedNode: Node): void)} onNodeTouched A function what will be executed whenever a node is 'touched' by the XPath
12
+ * @param onNodeTouched - onNodeTouched A function what will be executed whenever a node is 'touched' by the XPath
13
13
  */
14
14
  constructor(onNodeTouched) {
15
15
  this._onNodeTouched = onNodeTouched;
@@ -45,13 +45,21 @@ export class DependencyNotifyingDomFacade {
45
45
  * @param bucket - The bucket that matches the attribute that will be used.
46
46
  */
47
47
  // eslint-disable-next-line class-methods-use-this
48
+ /*
48
49
  getChildNodes(node, bucket) {
49
50
  const matchingNodes = Array.from(node.childNodes).filter(
50
51
  childNode => !bucket || getBucketsForNode(childNode).includes(bucket),
51
52
  );
52
53
  return matchingNodes;
53
54
  }
55
+ */
54
56
 
57
+ getChildNodes(node, bucket) {
58
+ const matchingNodes = Array.from(node.childNodes).filter(
59
+ childNode => !bucket || getBucketsForNode(childNode).includes(bucket));
60
+ matchingNodes.forEach(matchingNode => this._onNodeTouched(matchingNode));
61
+ return matchingNodes;
62
+ }
55
63
  /**
56
64
  * Get the data of this node.
57
65
  *
@@ -1,24 +1,46 @@
1
1
  import { XPathUtil } from './xpath-util.js';
2
2
  import { FxModel } from './fx-model.js';
3
- import { evaluateXPath, evaluateXPathToFirstNode } from './xpath-evaluation.js';
3
+ import {
4
+ evaluateXPath,
5
+ evaluateXPathToFirstNode,
6
+ evaluateXPathToString,
7
+ } from './xpath-evaluation.js';
4
8
  import getInScopeContext from './getInScopeContext.js';
5
9
 
6
10
  export const foreElementMixin = superclass =>
7
11
  class ForeElementMixin extends superclass {
8
12
  static get properties() {
9
13
  return {
14
+ /**
15
+ * context object for evaluation
16
+ */
10
17
  context: {
11
18
  type: Object,
12
19
  },
20
+ /**
21
+ * the model of this element
22
+ */
13
23
  model: {
14
24
  type: Object,
15
25
  },
16
- ref: {
17
- type: String,
18
- },
26
+ /**
27
+ * The modelitem object associated to the bound node holding the evaluated state.
28
+ */
19
29
  modelItem: {
20
30
  type: Object,
21
31
  },
32
+ /**
33
+ * the node(s) bound by this element
34
+ */
35
+ nodeset: {
36
+ type: Object,
37
+ },
38
+ /**
39
+ * XPath binding expression pointing to bound node
40
+ */
41
+ ref: {
42
+ type: String,
43
+ },
22
44
  };
23
45
  }
24
46
 
@@ -41,6 +63,10 @@ export const foreElementMixin = superclass =>
41
63
  return ownerForm.querySelector('fx-model');
42
64
  }
43
65
 
66
+ /**
67
+ *
68
+ * @returns {{parentNode}|ForeElementMixin}
69
+ */
44
70
  getOwnerForm() {
45
71
  let currentElement = this;
46
72
  while (currentElement && currentElement.parentNode) {
@@ -65,29 +91,33 @@ export const foreElementMixin = superclass =>
65
91
  evalInContext() {
66
92
  // const inscopeContext = this.getInScopeContext();
67
93
  const inscopeContext = getInScopeContext(this, this.ref);
94
+ if (!inscopeContext) {
95
+ console.warn('no in scopeContext for ', this);
96
+ return;
97
+ }
68
98
  if (this.ref === '') {
69
99
  this.nodeset = inscopeContext;
70
100
  } else if (Array.isArray(inscopeContext)) {
101
+ /*
71
102
  inscopeContext.forEach(n => {
72
103
  if (XPathUtil.isSelfReference(this.ref)) {
73
104
  this.nodeset = inscopeContext;
74
105
  } else {
75
- const localResult = evaluateXPathToFirstNode(this.ref, n, null);
106
+ const localResult = evaluateXPathToFirstNode(this.ref, n, this);
76
107
  // console.log('local result: ', localResult);
77
108
  this.nodeset.push(localResult);
78
109
  }
79
110
  });
111
+ */
112
+ this.nodeset = evaluateXPathToFirstNode(this.ref, inscopeContext[0], this);
80
113
  } else {
81
114
  // this.nodeset = fx.evaluateXPathToFirstNode(this.ref, inscopeContext, null, {namespaceResolver: this.namespaceResolver});
82
-
83
- // todo: code below fails - why?
84
- const formElement = this.getOwnerForm();
85
- if (inscopeContext.nodeType) {
86
- this.nodeset = evaluateXPathToFirstNode(this.ref, inscopeContext, formElement);
115
+ const { nodeType } = inscopeContext;
116
+ if (nodeType) {
117
+ this.nodeset = evaluateXPathToFirstNode(this.ref, inscopeContext, this);
87
118
  } else {
88
- this.nodeset = evaluateXPath(this.ref, inscopeContext, formElement);
119
+ this.nodeset = evaluateXPath(this.ref, inscopeContext, this);
89
120
  }
90
- // this.nodeset = evaluateXPath(this.ref,inscopeContext,formElement)
91
121
  }
92
122
  // console.log('UiElement evaluated to nodeset: ', this.nodeset);
93
123
  }
@@ -112,6 +142,14 @@ export const foreElementMixin = superclass =>
112
142
  return parent.getAttribute('ref');
113
143
  }
114
144
 
145
+ getInstance() {
146
+ if (this.ref.startsWith('instance(')) {
147
+ const instId = XPathUtil.getInstanceId(this.ref);
148
+ return this.getModel().getInstance(instId);
149
+ }
150
+ return this.getModel().getInstance('default');
151
+ }
152
+
115
153
  _getParentBindingElement(start) {
116
154
  if (start.parentNode.host) {
117
155
  const { host } = start.parentNode;
@@ -155,7 +193,40 @@ export const foreElementMixin = superclass =>
155
193
  return existed;
156
194
  }
157
195
 
196
+ /**
197
+ * Returns the effective value for the element.
198
+ * a: look for 'value' attribute and if present evaluate it and return the resulting value
199
+ * b: look for textContent and return the value if present
200
+ * c: return null
201
+ */
202
+ getValue() {
203
+ if (this.hasAttribute('value')) {
204
+ const valAttr = this.getAttribute('value');
205
+ try {
206
+ const inscopeContext = getInScopeContext(this, valAttr);
207
+ return evaluateXPathToString(valAttr, inscopeContext, this.getOwnerForm());
208
+ } catch (error) {
209
+ console.error(error);
210
+ this.dispatch('error', { message: error });
211
+ }
212
+ }
213
+ if (this.textContent) {
214
+ return this.textContent;
215
+ }
216
+ return null;
217
+ }
218
+
158
219
  getInScopeContext() {
159
220
  return getInScopeContext(this, this.ref);
160
221
  }
222
+
223
+ dispatch(eventName, detail) {
224
+ const event = new CustomEvent(eventName, {
225
+ composed: true,
226
+ bubbles: true,
227
+ detail,
228
+ });
229
+ // console.log('firing', event);
230
+ this.dispatchEvent(event);
231
+ }
161
232
  };
@@ -1,10 +1,15 @@
1
1
  import { foreElementMixin } from '../ForeElementMixin.js';
2
2
  import { evaluateXPathToBoolean } from '../xpath-evaluation.js';
3
3
 
4
+ async function wait(howLong) {
5
+ return new Promise(resolve => setTimeout(() => resolve(), howLong));
6
+ }
7
+
4
8
  /**
5
- * `fx-action`
6
- * a button triggering Fore actions
9
+ * Superclass for all action elements. Provides basic wiring of events to targets as well as
10
+ * handle conditionals and loops of actions.
7
11
  *
12
+ * @fires action-performed - is dispatched after each execution of an action.
8
13
  * @customElement
9
14
  * @demo demo/index.html
10
15
  */
@@ -12,12 +17,49 @@ export class AbstractAction extends foreElementMixin(HTMLElement) {
12
17
  static get properties() {
13
18
  return {
14
19
  ...super.properties,
20
+ /**
21
+ * detail - event detail object
22
+ */
15
23
  detail: {
16
24
  type: Object,
17
25
  },
26
+ /**
27
+ * wether nor not an action needs to run the update cycle
28
+ */
18
29
  needsUpdate: {
19
30
  type: Boolean,
20
31
  },
32
+ /**
33
+ * event to listen for
34
+ */
35
+ event: {
36
+ type: Object,
37
+ },
38
+ /**
39
+ * id of target element to attach listener to
40
+ */
41
+ target: {
42
+ type: String,
43
+ },
44
+ /**
45
+ * boolean XPath expression. If true the action will be executed.
46
+ */
47
+ ifExpr: {
48
+ type: String,
49
+ },
50
+ /**
51
+ * boolean XPath expression. If true loop will be executed. If an ifExpr is present this also needs to be true
52
+ * to actually run the action.
53
+ */
54
+ whileExpr: {
55
+ type: String,
56
+ },
57
+ /**
58
+ * delay before executing action in milliseconds
59
+ */
60
+ delay: {
61
+ type: Number,
62
+ },
21
63
  };
22
64
  }
23
65
 
@@ -39,7 +81,9 @@ export class AbstractAction extends foreElementMixin(HTMLElement) {
39
81
 
40
82
  this.target = this.getAttribute('target');
41
83
  if (this.target) {
42
- if (this.target === '#document') {
84
+ if (this.target === '#window') {
85
+ window.addEventListener(this.event, e => this.execute(e));
86
+ } else if (this.target === '#document') {
43
87
  document.addEventListener(this.event, e => this.execute(e));
44
88
  } else {
45
89
  this.targetElement = document.getElementById(this.target);
@@ -57,12 +101,18 @@ export class AbstractAction extends foreElementMixin(HTMLElement) {
57
101
  }
58
102
 
59
103
  /**
60
- * executes the action. This function is usually triggered by a fx-trigger.
104
+ * executes the action.
105
+ *
106
+ * Will first evaluate ifExpr and continue only if it evaluates to 'true'. The 'whileExpr' will be executed
107
+ * considering the delay if present.
108
+ *
109
+ * After calling `perform' which actually implements the semantics of an concrete action
110
+ * `actionPerformed` will make sure that update cycle is run if 'needsUpdate' is true.
61
111
  *
62
112
  * @param e
63
113
  */
64
- // eslint-disable-next-line no-unused-vars
65
- execute(e) {
114
+ async execute(e) {
115
+ console.log('executing', this);
66
116
  if (e && e.detail) {
67
117
  this.detail = e.detail;
68
118
  }
@@ -73,38 +123,57 @@ export class AbstractAction extends foreElementMixin(HTMLElement) {
73
123
  this.nodeset = this.targetElement.nodeset;
74
124
  }
75
125
 
76
- if (this.ifExpr) {
77
- if (evaluateXPathToBoolean(this.ifExpr, this.nodeset, this.getOwnerForm()) === false) {
78
- return;
79
- }
126
+ // First check if 'if' condition is true - otherwise exist right away
127
+ if (this.ifExpr && !evaluateXPathToBoolean(this.ifExpr, this.nodeset, this.getOwnerForm())) {
128
+ return;
80
129
  }
81
130
 
82
131
  if (this.whileExpr) {
83
- while (evaluateXPathToBoolean(this.whileExpr, this.nodeset, this.getOwnerForm()) === true) {
84
- // if(this.delay){
85
- // setTimeout(() => {
86
- // this.perform();
87
- // },this.delay);
88
- // }else{
132
+ // While: while the condition is true, delay a bit and execute the action
133
+ const loop = async () => {
134
+ // Start by waiting
135
+ await wait(this.delay || 0);
136
+
137
+ if (!this.ownerDocument.contains(this)) {
138
+ // We are no longer in the document. Stop working
139
+ return;
140
+ }
141
+
142
+ if (!evaluateXPathToBoolean(this.whileExpr, this.nodeset, this.getOwnerForm())) {
143
+ // Done with iterating
144
+ return;
145
+ }
146
+
147
+ // Perform the action once
89
148
  this.perform();
90
- // }
91
- }
149
+
150
+ // Go for one more iteration
151
+ await loop();
152
+ };
153
+
154
+ // After loop is done call actionPerformed to update the model and UI
155
+ await loop();
92
156
  this.actionPerformed();
93
157
  return;
94
158
  }
95
159
 
96
160
  if (this.delay) {
97
- setTimeout(() => {
98
- this.perform();
99
- }, this.delay);
100
- } else {
101
- this.perform();
161
+ // Delay further execution until the delay is done
162
+ await wait(this.delay);
163
+ if (!this.ownerDocument.contains(this)) {
164
+ // We are no longer in the document. Stop working
165
+ this.actionPerformed();
166
+ return;
167
+ }
102
168
  }
169
+
170
+ this.perform();
103
171
  this.actionPerformed();
104
172
  }
105
173
 
106
174
  /**
107
- * this should only be called by bound actions
175
+ * Template method to be implemented by each action that is called by execute() as part of
176
+ * the processing.
108
177
  *
109
178
  * todo: review - this could probably just be empty or throw error signalling that extender needs to implement it
110
179
  */
@@ -114,18 +183,23 @@ export class AbstractAction extends foreElementMixin(HTMLElement) {
114
183
  }
115
184
  }
116
185
 
186
+ /**
187
+ * calls the update cycle if action signalled that update is needed.
188
+ */
117
189
  actionPerformed() {
118
190
  // console.log('actionPerformed action parentNode ', this.parentNode);
119
191
  if (this.needsUpdate) {
120
192
  const model = this.getModel();
121
193
  model.recalculate();
122
194
  model.revalidate();
123
- model.parentNode.refresh();
124
- this._dispatchActionPerformed();
195
+ model.parentNode.refresh(true);
196
+ this.dispatchActionPerformed();
125
197
  }
126
198
  }
127
199
 
128
- _dispatchActionPerformed() {
200
+ /**
201
+ */
202
+ dispatchActionPerformed() {
129
203
  console.log('action-performed ', this);
130
204
  this.dispatchEvent(
131
205
  new CustomEvent('action-performed', { composed: true, bubbles: true, detail: {} }),
@@ -43,9 +43,11 @@ export class FxAction extends AbstractAction {
43
43
  } else {
44
44
  Array.from(children).forEach(action => {
45
45
  action.detail = this.detail;
46
- action.perform();
46
+ // action.perform();
47
+ action.execute();
47
48
  });
48
- this.needsUpdate = true;
49
+ this.dispatchActionPerformed();
50
+ // this.needsUpdate = false;
49
51
  }
50
52
  }
51
53
  }
@@ -2,31 +2,32 @@ import { AbstractAction } from './abstract-action.js';
2
2
  import { Fore } from '../fore.js';
3
3
 
4
4
  /**
5
- * `fx-append`
6
- *
7
- *
5
+ * `fx-append` appends an entry to a repeat.
8
6
  *
9
7
  *
10
8
  *
9
+ * @deprecated - will be replaced with `fx-insert`
10
+ * @fires index-changed - fired after new item is appended
11
11
  * @customElement
12
12
  */
13
13
  // class FxAppend extends FxAction {
14
14
  class FxAppend extends AbstractAction {
15
- /*
16
- static get properties() {
17
- return {
18
- ref: {
19
- type: String,
20
- },
21
- repeat: {
22
- type: String,
23
- },
24
- clear: {
25
- type: String,
26
- },
27
- };
28
- }
29
- */
15
+ static get properties() {
16
+ return {
17
+ ref: {
18
+ type: String,
19
+ },
20
+ /**
21
+ * the repeat this action is appending to.
22
+ */
23
+ repeat: {
24
+ type: String,
25
+ },
26
+ clear: {
27
+ type: String,
28
+ },
29
+ };
30
+ }
30
31
 
31
32
  constructor() {
32
33
  super();
@@ -138,6 +139,8 @@ class FxAppend extends AbstractAction {
138
139
  * clear all text nodes and attribute values to get a 'clean' template.
139
140
  * @param n
140
141
  * @private
142
+ *
143
+ *
141
144
  */
142
145
  _clear(n) {
143
146
  let node = n.firstChild;
@@ -0,0 +1,22 @@
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 FxConfirm extends FxAction {
11
+ connectedCallback() {
12
+ this.message = this.hasAttribute('message') ? this.getAttribute('message') : null;
13
+ }
14
+
15
+ perform() {
16
+ if (window.confirm(this.message)) {
17
+ super.perform();
18
+ }
19
+ }
20
+ }
21
+
22
+ window.customElements.define('fx-confirm', FxConfirm);
@@ -1,5 +1,6 @@
1
1
  import { AbstractAction } from './abstract-action.js';
2
- import { evaluateXPath } from '../xpath-evaluation.js';
2
+ import { evaluateXPath, resolveId } from '../xpath-evaluation.js';
3
+ import { XPathUtil } from '../xpath-util.js';
3
4
 
4
5
  /**
5
6
  * `fx-dispatch`
@@ -89,7 +90,14 @@ export class FxDispatch extends AbstractAction {
89
90
 
90
91
  // ### when targetid is given dispatch to that if present (throw an error if not) - otherwise dispatch to document
91
92
  if (this.targetid) {
92
- const target = document.getElementById(this.targetid);
93
+ // const target = document.getElementById(this.targetid);
94
+ let target;
95
+ if (XPathUtil.isRepeated(this)) {
96
+ target = resolveId(this.targetid, this.parentNode, null);
97
+ } else {
98
+ target = document.getElementById(this.targetid);
99
+ }
100
+ console.log('target', target);
93
101
  if (!target) {
94
102
  throw new Error(`targetid ${this.targetid} does not exist in document`);
95
103
  }