@jinntec/fore 1.2.0 → 1.4.0

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 (51) hide show
  1. package/dist/fore-dev.js +8 -8
  2. package/dist/fore-dev.js.map +1 -1
  3. package/dist/fore.js +7 -7
  4. package/dist/fore.js.map +1 -1
  5. package/index.js +1 -0
  6. package/package.json +2 -2
  7. package/resources/fore.css +100 -72
  8. package/src/ForeElementMixin.js +4 -0
  9. package/src/actions/abstract-action.js +102 -18
  10. package/src/actions/fx-action.js +9 -10
  11. package/src/actions/fx-append.js +1 -1
  12. package/src/actions/fx-confirm.js +3 -3
  13. package/src/actions/fx-copy.js +68 -0
  14. package/src/actions/fx-delete.js +53 -62
  15. package/src/actions/fx-dispatch.js +1 -1
  16. package/src/actions/fx-hide.js +4 -2
  17. package/src/actions/fx-insert.js +1 -1
  18. package/src/actions/fx-message.js +26 -2
  19. package/src/actions/fx-refresh.js +2 -2
  20. package/src/actions/fx-reload.js +30 -0
  21. package/src/actions/fx-replace.js +1 -1
  22. package/src/actions/fx-return.js +1 -1
  23. package/src/actions/fx-send.js +13 -3
  24. package/src/actions/fx-setfocus.js +33 -6
  25. package/src/actions/fx-setvalue.js +5 -5
  26. package/src/actions/fx-show.js +2 -1
  27. package/src/actions/fx-toggle.js +6 -1
  28. package/src/actions/fx-update.js +1 -1
  29. package/src/events.js +24 -0
  30. package/src/fore.js +128 -17
  31. package/src/fx-bind.js +6 -1
  32. package/src/fx-fore.js +93 -41
  33. package/src/fx-instance.js +95 -70
  34. package/src/fx-model.js +430 -441
  35. package/src/fx-submission.js +423 -405
  36. package/src/getInScopeContext.js +88 -82
  37. package/src/modelitem.js +5 -3
  38. package/src/relevance.js +1 -1
  39. package/src/ui/abstract-control.js +108 -22
  40. package/src/ui/fx-alert.js +0 -1
  41. package/src/ui/fx-container.js +22 -26
  42. package/src/ui/fx-control.js +84 -34
  43. package/src/ui/fx-group.js +5 -0
  44. package/src/ui/fx-inspector.js +5 -0
  45. package/src/ui/fx-output.js +14 -14
  46. package/src/ui/fx-repeat.js +2 -2
  47. package/src/ui/fx-repeatitem.js +5 -3
  48. package/src/ui/fx-switch.js +20 -8
  49. package/src/ui/fx-trigger.js +26 -19
  50. package/src/xpath-evaluation.js +49 -26
  51. package/src/xpath-util.js +26 -28
@@ -1,106 +1,112 @@
1
- import { evaluateXPathToFirstNode } from './xpath-evaluation.js';
2
- import { Fore } from './fore.js';
1
+ import {evaluateXPathToFirstNode} from './xpath-evaluation.js';
2
+ import {Fore} from './fore.js';
3
3
 
4
- import { XPathUtil } from './xpath-util.js';
4
+ import {XPathUtil} from './xpath-util.js';
5
5
 
6
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
9
- return node.ownerElement;
10
- }
11
-
12
- if (node.nodeType === Node.ELEMENT_NODE) {
13
- // The context of a query should be the element having a ref
14
- return node;
15
- }
16
-
17
- // For text nodes, just start looking from the parent element
18
- return node.parentNode;
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
9
+ return node.ownerElement;
10
+ }
11
+
12
+ if (node.nodeType === Node.ELEMENT_NODE) {
13
+ // The context of a query should be the element having a ref
14
+ return node;
15
+ }
16
+
17
+ // For text nodes, just start looking from the parent element
18
+ return node.parentNode;
19
19
  }
20
20
 
21
21
  function _getForeContext(node) {
22
- return node.closest('fx-fore');
22
+ return node.closest('fx-fore');
23
23
  }
24
24
 
25
25
  function _getModelInContext(node) {
26
- // const ownerForm = node.closest('fx-fore');
27
- const ownerForm = _getForeContext(node);
28
- return ownerForm.getModel();
26
+ // const ownerForm = node.closest('fx-fore');
27
+ const ownerForm = _getForeContext(node);
28
+ return ownerForm.getModel();
29
29
  }
30
30
 
31
31
  function _getInitialContext(node, ref) {
32
- const parentBind = Fore.getClosest('[ref]', node);
33
- const localFore = Fore.getClosest('fx-fore', node);
34
-
35
- const model = _getModelInContext(node);
36
-
37
- if (parentBind !== null) {
38
- /*
39
- make sure that the closest ref belongs to the same fx-fore element
40
- */
41
- const parentBindFore = parentBind.closest('fx-fore');
42
- if (localFore === parentBindFore) {
43
- return parentBind.nodeset;
32
+ const parentBind = Fore.getClosest('[ref]', node);
33
+ const localFore = Fore.getClosest('fx-fore', node);
34
+
35
+ const model = _getModelInContext(node);
36
+
37
+ if (parentBind !== null) {
38
+ /*
39
+ make sure that the closest ref belongs to the same fx-fore element
40
+ */
41
+ const parentBindFore = parentBind.closest('fx-fore');
42
+ if (localFore === parentBindFore) {
43
+ return parentBind.nodeset;
44
+ }
45
+ return model.getDefaultInstance().getDefaultContext();
44
46
  }
45
- return model.getDefaultInstance().getDefaultContext();
46
- }
47
47
 
48
- if (XPathUtil.isAbsolutePath(ref)) {
49
- const instanceId = XPathUtil.getInstanceId(ref);
50
- if (instanceId) {
51
- return model.getInstance(instanceId).getDefaultContext();
48
+ if (XPathUtil.isAbsolutePath(ref)) {
49
+ const instanceId = XPathUtil.getInstanceId(ref);
50
+ if (instanceId) {
51
+ return model.getInstance(instanceId).getDefaultContext();
52
+ }
53
+ return model.getDefaultInstance().getDefaultContext();
52
54
  }
55
+ // should always return default context if all other fails
53
56
  return model.getDefaultInstance().getDefaultContext();
54
- }
55
- if (model.getDefaultInstance() !== null && model.inited) {
56
- return model.getDefaultInstance().getDefaultContext();
57
- }
58
- return [];
59
57
  }
60
58
 
61
59
  export default function getInScopeContext(node, ref) {
62
- const parentElement = _getElement(node);
63
-
64
- const repeatItem = Fore.getClosest('fx-repeatitem', parentElement);
65
- if (repeatItem) {
66
- if (node.nodeName === 'context') {
67
- return evaluateXPathToFirstNode(
68
- node.nodeValue,
69
- repeatItem.nodeset,
70
- _getForeContext(parentElement),
71
- );
60
+ const parentElement = _getElement(node);
61
+
62
+ if(parentElement.nodeName === 'FX-FORE'){
63
+ return parentElement.getModel().getDefaultInstance().getDefaultContext();
72
64
  }
73
- return repeatItem.nodeset;
74
- }
75
-
76
- if (parentElement.hasAttribute('context')) {
77
- const initialContext = _getInitialContext(parentElement.parentNode, ref);
78
- const contextAttr = parentElement.getAttribute('context');
79
- return evaluateXPathToFirstNode(contextAttr, initialContext, _getForeContext(parentElement));
80
- }
81
-
82
- if (node.nodeType === Node.ATTRIBUTE_NODE && node.nodeName === 'context') {
83
- const initialContext = _getInitialContext(node.ownerElement.parentNode, ref);
84
- const contextAttr = node.ownerElement.getAttribute('context');
85
- return evaluateXPathToFirstNode(contextAttr, initialContext, _getForeContext(parentElement));
86
- }
87
- if (node.nodeType === Node.ATTRIBUTE_NODE && node.nodeName === 'ref') {
88
- // Note: do not consider the ref of the owner element since it should not be used to define the
89
- // context
90
- if (node.ownerElement.hasAttribute('context')) {
91
- const initialContext = _getInitialContext(node.ownerElement.parentNode, ref);
92
- const contextAttr = node.ownerElement.getAttribute('context');
93
- return evaluateXPathToFirstNode(contextAttr, initialContext, _getForeContext(parentElement));
65
+ const parentBind = Fore.getClosest('[ref]', parentElement.parentNode);
66
+ if (parentBind && parentBind.nodeName === 'FX-GROUP') {
67
+ return parentBind.nodeset;
94
68
  }
95
69
 
96
- // Never resolve the context from a ref itself!
97
- return _getInitialContext(parentElement.parentNode, ref);
98
- }
70
+ const repeatItem = Fore.getClosest('fx-repeatitem', parentElement);
71
+ if (repeatItem) {
72
+ if (node.nodeName === 'context') {
73
+ return evaluateXPathToFirstNode(
74
+ node.nodeValue,
75
+ repeatItem.nodeset,
76
+ _getForeContext(parentElement),
77
+ );
78
+ }
79
+ return repeatItem.nodeset;
80
+ }
81
+
82
+ if (parentElement.hasAttribute('context')) {
83
+ const initialContext = _getInitialContext(parentElement.parentNode, ref);
84
+ const contextAttr = parentElement.getAttribute('context');
85
+ return evaluateXPathToFirstNode(contextAttr, initialContext, _getForeContext(parentElement));
86
+ }
87
+
88
+ if (node.nodeType === Node.ATTRIBUTE_NODE && node.nodeName === 'context') {
89
+ const initialContext = _getInitialContext(node.ownerElement.parentNode, ref);
90
+ const contextAttr = node.ownerElement.getAttribute('context');
91
+ return evaluateXPathToFirstNode(contextAttr, initialContext, _getForeContext(parentElement));
92
+ }
93
+ if (node.nodeType === Node.ATTRIBUTE_NODE && node.nodeName === 'ref') {
94
+ // Note: do not consider the ref of the owner element since it should not be used to define the
95
+ // context
96
+ if (node.ownerElement.hasAttribute('context')) {
97
+ const initialContext = _getInitialContext(node.ownerElement.parentNode, ref);
98
+ const contextAttr = node.ownerElement.getAttribute('context');
99
+ return evaluateXPathToFirstNode(contextAttr, initialContext, _getForeContext(parentElement));
100
+ }
101
+
102
+ // Never resolve the context from a ref itself!
103
+ return _getInitialContext(parentElement.parentNode, ref);
104
+ }
99
105
 
100
- // if (node.nodeType === Node.ELEMENT_NODE && node.hasAttribute('context')) {
101
- // const initialContext = _getInitialContext(node.parentNode, ref);
102
- // const contextAttr = node.getAttribute('context');
103
- // return evaluateXPathToFirstNode(contextAttr, initialContext, _getForeContext(parentElement));
104
- // }
105
- return _getInitialContext(parentElement, ref);
106
+ // if (node.nodeType === Node.ELEMENT_NODE && node.hasAttribute('context')) {
107
+ // const initialContext = _getInitialContext(node.parentNode, ref);
108
+ // const contextAttr = node.getAttribute('context');
109
+ // return evaluateXPathToFirstNode(contextAttr, initialContext, _getForeContext(parentElement));
110
+ // }
111
+ return _getInitialContext(parentElement, ref);
106
112
  }
package/src/modelitem.js CHANGED
@@ -50,8 +50,8 @@ export class ModelItem {
50
50
  }
51
51
 
52
52
  set value(newVal) {
53
- console.log('modelitem.setvalue oldVal', this.value);
54
- console.log('modelitem.setvalue newVal', newVal);
53
+ // console.log('modelitem.setvalue oldVal', this.value);
54
+ // console.log('modelitem.setvalue newVal', newVal);
55
55
 
56
56
  if (newVal.nodeType === Node.DOCUMENT_NODE) {
57
57
  this.node.replaceWith(newVal.firstElementChild);
@@ -67,7 +67,9 @@ export class ModelItem {
67
67
  }
68
68
 
69
69
  addAlert(alert) {
70
- this.alerts.push(alert);
70
+ if(!this.alerts.includes(alert)){
71
+ this.alerts.push(alert);
72
+ }
71
73
  }
72
74
 
73
75
  cleanAlerts() {
package/src/relevance.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export class Relevance {
2
2
  static selectRelevant(element, type) {
3
- console.log('selectRelevant', type);
3
+ // console.log('selectRelevant', type);
4
4
  switch (type) {
5
5
  case 'xml':
6
6
  return Relevance._relevantXmlNodes(element);
@@ -16,9 +16,32 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
16
16
  this.required = false;
17
17
  this.readonly = false;
18
18
  this.widget = null;
19
+ this.visited = false;
20
+ this.force = false;
19
21
  // this.attachShadow({ mode: 'open' });
20
22
  }
21
23
 
24
+
25
+ // todo: discuss - this is a hack to circumvent that modelItems in toRefresh diverge from the modelItems in
26
+ // the model in some situations. This code first looks for refresh
27
+ /*
28
+ getModelItem() {
29
+ console.log('toRefreshModelItems', this.getOwnerForm().toRefresh);
30
+ const s = this.modelItem.path;
31
+ console.log('toRefreshModelItems path', s);
32
+
33
+ const toRefresh = this.getOwnerForm().toRefresh;
34
+ let mi;
35
+ if(toRefresh){
36
+ mi = this.getOwnerForm().toRefresh.find(m => m.path === s);
37
+ }
38
+
39
+ return mi? mi: super.getModelItem();
40
+ // console.log('toRefreshModelItems realitem', mi);
41
+
42
+ }
43
+ */
44
+
22
45
  // eslint-disable-next-line class-methods-use-this
23
46
  getWidget() {
24
47
  throw new Error('You have to implement the method getWidget!');
@@ -27,7 +50,8 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
27
50
  /**
28
51
  * (re)apply all modelItem state properties to this control. model -> UI
29
52
  */
30
- async refresh() {
53
+ async refresh(force) {
54
+ if(force) this.force=true;
31
55
  // console.log('### AbstractControl.refresh on : ', this);
32
56
 
33
57
  const currentVal = this.value;
@@ -40,6 +64,7 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
40
64
  this.oldVal = this.nodeset ? this.nodeset : null;
41
65
  this.evalInContext();
42
66
 
67
+ // todo this if should be removed - see above
43
68
  if (this.isBound()) {
44
69
  // this.control = this.querySelector('#control');
45
70
 
@@ -49,6 +74,7 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
49
74
  }
50
75
 
51
76
  this.modelItem = this.getModelItem();
77
+ // console.log('refresh modelItem', this.modelItem);
52
78
 
53
79
  if (this.modelItem instanceof ModelItem) {
54
80
  // console.log('### XfAbstractControl.refresh modelItem : ', this.modelItem);
@@ -94,13 +120,19 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
94
120
 
95
121
  // if(!this.closest('fx-fore').ready) return; // state change event do not fire during init phase (initial refresh)
96
122
  if (!this.getOwnerForm().ready) return; // state change event do not fire during init phase (initial refresh)
97
- if (currentVal !== this.value) {
98
- Fore.dispatch(this, 'value-changed', { path: this.modelItem.path });
123
+ if (currentVal !== this.value ) {
124
+ // todo: discuss how to prevent unnecessary/unwanted value-changes e.g. when repeatitems are inserted
125
+ // if (currentVal !== this.value && this.visited) {
126
+ Fore.dispatch(this, 'value-changed', { path: this.modelItem.path , value:this.modelItem.value});
99
127
  }
100
128
  }
101
129
  }
102
130
  }
103
131
 
132
+ refreshFromModelItem(modelItem){
133
+
134
+ }
135
+
104
136
  /**
105
137
  *
106
138
  * @returns {Promise<void>}
@@ -135,24 +167,71 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
135
167
  handleRequired() {
136
168
  // console.log('mip required', this.modelItem.required);
137
169
  this.widget = this.getWidget();
138
- // if (this.required !== this.modelItem.required) {
170
+
171
+ if(!this.modelItem.required){
172
+ this.widget.removeAttribute('required');
173
+ this.removeAttribute('required');
174
+ if (this.isRequired() !== this.modelItem.required){
175
+ this._dispatchEvent('optional');
176
+ }
177
+ return;
178
+ }
179
+
180
+ // ### modelItem is required
181
+ if (this.visited || this.force) {
182
+ if (this.modelItem.value === '') {
183
+ this.classList.add('isEmpty');
184
+ this._toggleValid(false);
185
+ } else {
186
+ this.classList.remove('isEmpty');
187
+ this._toggleValid(true);
188
+ }
189
+ }
190
+ this.widget.setAttribute('required', '');
191
+ this.setAttribute('required', '');
139
192
  if (this.isRequired() !== this.modelItem.required) {
140
- if (this.modelItem.required) {
141
- if (this.getOwnerForm().ready){
142
- if(this.widget.value === ''){
143
- this.classList.add('isRequiredFalse');
144
- }else{
145
- this.classList.remove('isRequiredFalse');
146
- }
193
+ this._dispatchEvent('required');
194
+ }
195
+
196
+ /*
197
+ if (this.isRequired() !== this.modelItem.required) {
198
+ this._updateRequired();
199
+ }
200
+ */
201
+ }
202
+
203
+
204
+ _updateRequired() {
205
+ if (this.modelItem.required) {
206
+ // if (this.getOwnerForm().ready){
207
+ if (this.visited || this.force) {
208
+ // if (this.visited ) {
209
+ // if (this.widget.value === '') {
210
+ if (this.modelItem.value === '') {
211
+ this.classList.add('isEmpty');
212
+ this._toggleValid(false);
213
+ } else {
214
+ this.classList.remove('isEmpty');
215
+ this._toggleValid(true);
147
216
  }
148
- this.widget.setAttribute('required', '');
149
- this.setAttribute('required', '');
150
- this._dispatchEvent('required');
151
- } else {
152
- this.widget.removeAttribute('required');
153
- this.removeAttribute('required');
154
- this._dispatchEvent('optional');
155
217
  }
218
+ this.widget.setAttribute('required', '');
219
+ this.setAttribute('required', '');
220
+ this._dispatchEvent('required');
221
+ } else {
222
+ this.widget.removeAttribute('required');
223
+ this.removeAttribute('required');
224
+ this._dispatchEvent('optional');
225
+ }
226
+ }
227
+
228
+ _toggleValid(valid){
229
+ if(valid){
230
+ this.removeAttribute('invalid');
231
+ this.setAttribute('valid','');
232
+ }else{
233
+ this.removeAttribute('valid');
234
+ this.setAttribute('invalid','');
156
235
  }
157
236
  }
158
237
 
@@ -177,28 +256,35 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
177
256
  // console.log('mip valid', this.modelItem.required);
178
257
  const alert = this.querySelector('fx-alert');
179
258
 
259
+ const mi = this.getModelItem();
260
+ // console.log('late modelItem', mi);
180
261
  if (this.isValid() !== this.modelItem.constraint) {
181
262
  if (this.modelItem.constraint) {
182
- if (alert) alert.style.display = 'none';
263
+ // if (alert) alert.style.display = 'none';
183
264
  this._dispatchEvent('valid');
265
+ this.setAttribute('valid','');
184
266
  this.removeAttribute('invalid');
185
267
  } else {
186
268
  this.setAttribute('invalid', '');
269
+ this.removeAttribute('valid');
187
270
  // ### constraint is invalid - handle alerts
271
+ /*
188
272
  if (alert) {
189
273
  alert.style.display = 'block';
190
274
  }
275
+ */
191
276
  if (this.modelItem.alerts.length !== 0) {
192
- const { alerts } = this.modelItem;
193
- // console.log('alerts from bind: ', alerts);
194
277
 
195
278
  const controlAlert = this.querySelector('fx-alert');
196
279
  if (!controlAlert) {
280
+ const { alerts } = this.modelItem;
281
+ // console.log('alerts from bind: ', alerts);
197
282
  alerts.forEach(modelAlert => {
198
283
  const newAlert = document.createElement('fx-alert');
284
+ // const newAlert = document.createElement('span');
199
285
  newAlert.innerHTML = modelAlert;
200
286
  this.appendChild(newAlert);
201
- newAlert.style.display = 'block';
287
+ // newAlert.style.display = 'block';
202
288
  });
203
289
  }
204
290
  }
@@ -13,7 +13,6 @@ export class FxAlert extends AbstractControl {
13
13
  font-size: 0.8em;
14
14
  font-weight: 400;
15
15
  color: red;
16
- display: none;
17
16
  }
18
17
  `;
19
18
 
@@ -37,7 +37,7 @@ export class FxContainer extends foreElementMixin(HTMLElement) {
37
37
  /**
38
38
  * (re)apply all state properties to this control.
39
39
  */
40
- refresh(force) {
40
+ async refresh(force) {
41
41
  if (!force && this.hasAttribute('refresh-on-view')) return;
42
42
  // console.log('### FxContainer.refresh on : ', this);
43
43
 
@@ -47,21 +47,20 @@ export class FxContainer extends foreElementMixin(HTMLElement) {
47
47
  if (this.modelItem && !this.modelItem.boundControls.includes(this)) {
48
48
  this.modelItem.boundControls.push(this);
49
49
  }
50
-
51
- // this.value = this.modelItem.value;
50
+ this.handleModelItemProperties();
52
51
  }
53
52
 
54
- // await this.updateComplete;
55
-
56
53
  // state change event do not fire during init phase (initial refresh)
57
- if (this._getForm().ready) {
58
- this.handleModelItemProperties();
59
- }
60
- Fore.refreshChildren(this, force);
54
+ // if (this._getForm().ready) {
55
+ // this.handleModelItemProperties();
56
+ // }
57
+ // Fore.refreshChildren(this, force);
61
58
  }
62
59
 
60
+ /**
61
+ * anly relevance is processed for container controls
62
+ */
63
63
  handleModelItemProperties() {
64
- this.handleReadonly();
65
64
  this.handleRelevant();
66
65
  }
67
66
 
@@ -69,28 +68,25 @@ export class FxContainer extends foreElementMixin(HTMLElement) {
69
68
  return this.getModel().parentNode;
70
69
  }
71
70
 
72
- handleReadonly() {
73
- // console.log('mip readonly', this.modelItem.isReadonly);
74
- if (this.isReadonly() !== this.modelItem.readonly) {
75
- if (this.modelItem.readonly) {
76
- this.setAttribute('readonly', 'readonly');
77
- this.dispatchEvent(new CustomEvent('readonly', {}));
78
- }
79
- if (!this.modelItem.readonly) {
80
- this.removeAttribute('readonly');
81
- this.dispatchEvent(new CustomEvent('readwrite', {}));
82
- }
83
- }
84
- }
85
-
86
71
  handleRelevant() {
87
72
  // console.log('mip valid', this.modelItem.enabled);
88
- if (!this.modelItem) return;
73
+ if (!this.modelItem) {
74
+ console.log('container is not relevant');
75
+ this.removeAttribute('relevant','');
76
+ this.setAttribute('nonrelevant','');
77
+ this.dispatchEvent(new CustomEvent('disabled', {}));
78
+ return;
79
+ }
89
80
 
90
81
  if (this.isEnabled() !== this.modelItem.enabled) {
91
- if (this.modelItem.enabled) {
82
+ if (this.modelItem.relevant) {
83
+ // this.style.display = 'block';
84
+ this.removeAttribute('nonrelevant','');
85
+ this.setAttribute('relevant','');
92
86
  this.dispatchEvent(new CustomEvent('enabled', {}));
93
87
  } else {
88
+ this.removeAttribute('relevant','');
89
+ this.setAttribute('nonrelevant','');
94
90
  this.dispatchEvent(new CustomEvent('disabled', {}));
95
91
  }
96
92
  }