@jinntec/fore 1.0.0 → 1.1.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.
package/src/fx-model.js CHANGED
@@ -202,9 +202,14 @@ export class FxModel extends HTMLElement {
202
202
  * todo: use 'changed' flag on modelItems to determine subgraph for recalculation. Flag already exists but is not used.
203
203
  */
204
204
  recalculate() {
205
+ if(!this.mainGraph){
206
+ return;
207
+ }
208
+
205
209
  console.group('### recalculate');
206
210
  console.log('changed nodes ', this.changed);
207
211
 
212
+
208
213
  console.time('recalculate');
209
214
  this.computes = 0;
210
215
 
@@ -215,7 +220,7 @@ export class FxModel extends HTMLElement {
215
220
  this.subgraph.addNode(modelItem.path, modelItem.node);
216
221
  // const dependents = this.mainGraph.dependantsOf(modelItem.path, false);
217
222
  // this._addSubgraphDependencies(modelItem.path);
218
- if (this.mainGraph && this.mainGraph.hasNode(modelItem.path)) {
223
+ if (this.mainGraph.hasNode(modelItem.path)) {
219
224
  // const dependents = this.mainGraph.directDependantsOf(modelItem.path)
220
225
 
221
226
  const all = this.mainGraph.dependantsOf(modelItem.path, false);
@@ -245,7 +250,7 @@ export class FxModel extends HTMLElement {
245
250
  // ### compute the subgraph
246
251
  const ordered = this.subgraph.overallOrder(false);
247
252
  ordered.forEach(path => {
248
- if (this.mainGraph && this.mainGraph.hasNode(path)) {
253
+ if (this.mainGraph.hasNode(path)) {
249
254
  const node = this.mainGraph.getNodeData(path);
250
255
  this.compute(node, path);
251
256
  }
@@ -371,8 +376,9 @@ export class FxModel extends HTMLElement {
371
376
  *
372
377
  */
373
378
  revalidate() {
374
- console.group('### revalidate');
379
+ if(this.modelItems.length === 0) return true;
375
380
 
381
+ console.group('### revalidate');
376
382
  console.time('revalidate');
377
383
  let valid = true;
378
384
  this.modelItems.forEach(modelItem => {
@@ -402,6 +408,28 @@ export class FxModel extends HTMLElement {
402
408
  }
403
409
  }
404
410
  }
411
+ if (typeof bind.hasAttribute === 'function' && bind.hasAttribute('required')) {
412
+ const required = bind.getAttribute('required');
413
+ if (required) {
414
+ const compute = evaluateXPathToBoolean(required, modelItem.node, this);
415
+ console.log('modelItem required computed: ', compute);
416
+ modelItem.required = compute;
417
+ this.formElement.addToRefresh(modelItem); // let fore know that modelItem needs refresh
418
+ if(!modelItem.node.textContent){
419
+ valid = false;
420
+ }
421
+ // if (!compute) valid = false;
422
+ /*
423
+ if (!this.modelConstructed) {
424
+ // todo: get alert from attribute or child element
425
+ const alert = bind.getAlert();
426
+ if (alert) {
427
+ modelItem.addAlert(alert);
428
+ }
429
+ }
430
+ */
431
+ }
432
+ }
405
433
  }
406
434
  });
407
435
  console.timeEnd('revalidate');
@@ -428,7 +456,7 @@ export class FxModel extends HTMLElement {
428
456
  }
429
457
 
430
458
  getDefaultInstance() {
431
- return this.instances[0];
459
+ return this?.instances[0];
432
460
  }
433
461
 
434
462
  getDefaultInstanceData() {
@@ -78,7 +78,7 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
78
78
 
79
79
  model.recalculate();
80
80
 
81
- if (this.validate) {
81
+ if (this.validate==='true') {
82
82
  const valid = model.revalidate();
83
83
  if (!valid) {
84
84
  console.log('validation failed. Bubmission stopped');
@@ -145,18 +145,39 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
145
145
 
146
146
  // if (resolvedUrl === '#echo') {
147
147
  if (resolvedUrl.startsWith('#echo')) {
148
- let data = null;
149
- if (serialized && instance.type === 'xml') {
150
- data = new DOMParser().parseFromString(serialized, 'application/xml');
151
- }
152
- if (serialized && instance.type === 'json') {
153
- data = JSON.parse(serialized);
154
- }
148
+ let data = this._parse(serialized, instance);
155
149
  this._handleResponse(data);
156
150
  // this.dispatch('submit-done', {});
157
151
  Fore.dispatch(this, 'submit-done', {});
158
152
  return;
159
153
  }
154
+
155
+ if(resolvedUrl.startsWith('localStore:') && this.method === 'post'){
156
+ // let data = this._parse(serialized, instance);
157
+ const key = resolvedUrl.substring(resolvedUrl.indexOf(':')+1);
158
+ localStorage.setItem(key,serialized);
159
+ Fore.dispatch(this, 'submit-done', {});
160
+ return;
161
+ }
162
+
163
+ if(resolvedUrl.startsWith('localStore:') && (this.method === 'consume' || this.method === 'get')){
164
+ // let data = this._parse(serialized, instance);
165
+ this.replace = 'instance';
166
+ const key = resolvedUrl.substring(resolvedUrl.indexOf(':')+1);
167
+ const serialized = localStorage.getItem(key);
168
+ if(!serialized){
169
+ Fore.dispatch(this, 'submit-error', { message: `Error reading key ${key} from localstorage` });
170
+ return;
171
+ }
172
+ let data = this._parse(serialized, instance);
173
+ this._handleResponse(data);
174
+ if(this.method === 'consume'){
175
+ localStorage.removeItem(key);
176
+ }
177
+ Fore.dispatch(this, 'submit-done', {});
178
+ return;
179
+ }
180
+
160
181
  // ### setting headers
161
182
  const headers = this._getHeaders();
162
183
  console.log('headers', headers);
@@ -209,6 +230,17 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
209
230
  Fore.dispatch(this, 'submit-done', {});
210
231
  }
211
232
 
233
+ _parse(serialized, instance) {
234
+ let data = null;
235
+ if (serialized && instance.type === 'xml') {
236
+ data = new DOMParser().parseFromString(serialized, 'application/xml');
237
+ }
238
+ if (serialized && instance.type === 'json') {
239
+ data = JSON.parse(serialized);
240
+ }
241
+ return data;
242
+ }
243
+
212
244
  _serialize(instanceType, relevantNodes) {
213
245
  if (this.method === 'urlencoded-post') {
214
246
  // this.method = 'post';
package/src/relevance.js CHANGED
@@ -21,8 +21,7 @@ export class Relevance {
21
21
  const mi = element.getModel().getModelItem(element.nodeset);
22
22
  if (mi && !mi.relevant) return null;
23
23
 
24
- const doc = new DOMParser().parseFromString('<data></data>', 'application/xml');
25
- const root = doc.firstElementChild;
24
+ const root = element.nodeset.cloneNode(false);
26
25
 
27
26
  if (element.nodeset.children.length === 0 && Relevance._isRelevant(element, element.nodeset)) {
28
27
  return element.nodeset;
@@ -56,7 +56,7 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
56
56
  if (this.hasAttribute('as') && this.getAttribute('as') === 'node') {
57
57
  console.log('as', this.nodeset);
58
58
  this.modelItem.value = this.nodeset;
59
- this.value = this.modelItem.value;
59
+ this.value = this.modelItem.node;
60
60
  } else {
61
61
  this.value = this.modelItem.value;
62
62
  }
@@ -135,8 +135,15 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
135
135
  // console.log('mip required', this.modelItem.required);
136
136
  this.widget = this.getWidget();
137
137
  // if (this.required !== this.modelItem.required) {
138
- if (this.isRequired() !== this.modelItem.required) {
138
+ // if (this.isRequired() !== this.modelItem.required) {
139
139
  if (this.modelItem.required) {
140
+ if (this.getOwnerForm().ready){
141
+ if(this.widget.value === ''){
142
+ this.classList.add('isRequiredFalse');
143
+ }else{
144
+ this.classList.remove('isRequiredFalse');
145
+ }
146
+ }
140
147
  this.widget.setAttribute('required', '');
141
148
  this.setAttribute('required', '');
142
149
  this._dispatchEvent('required');
@@ -145,7 +152,7 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
145
152
  this.removeAttribute('required');
146
153
  this._dispatchEvent('optional');
147
154
  }
148
- }
155
+ // }
149
156
  }
150
157
 
151
158
  handleReadonly() {
package/src/ui/fx-case.js CHANGED
@@ -26,7 +26,7 @@ class FxCase extends HTMLElement {
26
26
 
27
27
  const style = `
28
28
  :host {
29
- display: none;
29
+ visibility: none;
30
30
  }
31
31
  `;
32
32
  const html = `
@@ -40,7 +40,6 @@ class FxCase extends HTMLElement {
40
40
  ${html}
41
41
  `;
42
42
 
43
- this.style.display = 'none';
44
43
  }
45
44
  }
46
45
 
@@ -62,7 +62,15 @@ export default class FxControl extends XfAbstractControl {
62
62
 
63
63
  this.widget = this.getWidget();
64
64
  // console.log('widget ', this.widget);
65
+ let listenOn = this.widget // default: usually listening on widget
65
66
 
67
+ if(this.hasAttribute('listen-on')){
68
+ const q = this.getAttribute('listen-on');
69
+ const target = this.querySelector(q);
70
+ if(target){
71
+ listenOn = target;
72
+ }
73
+ }
66
74
  // ### convenience marker event
67
75
  if (this.updateEvent === 'enter') {
68
76
  this.widget.addEventListener('keyup', event => {
@@ -75,7 +83,7 @@ export default class FxControl extends XfAbstractControl {
75
83
  this.updateEvent = 'blur'; // needs to be registered too
76
84
  }
77
85
  if (this.debounceDelay) {
78
- this.widget.addEventListener(
86
+ listenOn.addEventListener(
79
87
  this.updateEvent,
80
88
  debounce(() => {
81
89
  console.log('eventlistener ', this.updateEvent);
@@ -83,7 +91,7 @@ export default class FxControl extends XfAbstractControl {
83
91
  }, this.debounceDelay),
84
92
  );
85
93
  } else {
86
- this.widget.addEventListener(this.updateEvent, () => {
94
+ listenOn.addEventListener(this.updateEvent, () => {
87
95
  console.log('eventlistener ', this.updateEvent);
88
96
  this.setValue(this.widget[this.valueProp]);
89
97
  });
@@ -141,13 +149,16 @@ export default class FxControl extends XfAbstractControl {
141
149
  setValue(val) {
142
150
  const modelitem = this.getModelItem();
143
151
 
144
- if (modelitem?.readonly) return; // do nothing when modelItem is readonly
152
+ if (modelitem?.readonly){
153
+ console.warn('attempt to change readonly node', modelitem);
154
+ return; // do nothing when modelItem is readonly
155
+ }
145
156
 
146
157
  if (this.getAttribute('as') === 'node') {
147
158
  const widgetValue = this.getWidget().value;
148
159
  const replace = this.shadowRoot.getElementById('replace');
149
160
  replace.replace(this.nodeset, this.getWidget().value);
150
- if (widgetValue && widgetValue !== modelitem.value) {
161
+ if (modelitem && widgetValue && widgetValue !== modelitem.value) {
151
162
  modelitem.value = widgetValue;
152
163
  replace.actionPerformed();
153
164
  }
@@ -238,15 +249,19 @@ export default class FxControl extends XfAbstractControl {
238
249
  widget.value = pretty;
239
250
  }
240
251
  if (as === 'node' && this.nodeset !== widget.value) {
241
- const oldVal = this.nodeset.innerHTML;
252
+ // const oldVal = this.nodeset.innerHTML;
253
+ const oldVal = this.nodeset;
242
254
  if (widget.value) {
243
255
  if (oldVal !== this.widget.value) {
244
256
  console.log('changed');
257
+ widget.value = this.nodeset.cloneNode(true);
245
258
  return;
246
259
  }
247
260
  }
248
261
 
249
262
  widget.value = this.nodeset.cloneNode(true);
263
+ // todo: should be more like below but that can cause infinite loop when controll trigger update event due to calling a setter for property
264
+ // widget[this.valueProp] = this.nodeset.cloneNode(true);
250
265
  console.log('passed value to widget', widget.value);
251
266
  }
252
267
 
@@ -334,11 +349,13 @@ export default class FxControl extends XfAbstractControl {
334
349
  console.log('subcomponent ready', e.target);
335
350
  const defaultInst = theFore.querySelector('fx-instance');
336
351
  console.log('defaultInst', defaultInst);
337
- const doc = new DOMParser().parseFromString('<data></data>', 'application/xml');
338
- // Note: Clone the input to prevent the inner fore from editing the outer node
339
- doc.firstElementChild.appendChild(this.initialNode.cloneNode(true));
340
- // defaultinst.setInstanceData(this.initialNode);
341
- defaultInst.setInstanceData(doc);
352
+ if(this.initialNode){
353
+ const doc = new DOMParser().parseFromString('<data></data>', 'application/xml');
354
+ // Note: Clone the input to prevent the inner fore from editing the outer node
355
+ doc.firstElementChild.appendChild(this.initialNode.cloneNode(true));
356
+ // defaultinst.setInstanceData(this.initialNode);
357
+ defaultInst.setInstanceData(doc);
358
+ }
342
359
  console.log('new data', defaultInst.getInstanceData());
343
360
  // theFore.getModel().modelConstruct();
344
361
  theFore.getModel().updateModel();
@@ -372,7 +389,7 @@ export default class FxControl extends XfAbstractControl {
372
389
 
373
390
  async refresh(force) {
374
391
  // console.log('fx-control refresh', this);
375
- super.refresh();
392
+ super.refresh(force);
376
393
  // console.log('refresh template', this.template);
377
394
  // const {widget} = this;
378
395
 
@@ -418,6 +435,14 @@ export default class FxControl extends XfAbstractControl {
418
435
 
419
436
  // ### build the items
420
437
  if (this.template) {
438
+ if(this.widget.nodeName === "SELECT" &&
439
+ this.widget.hasAttribute('selection') &&
440
+ this.widget.getAttribute('selection') === 'open'){
441
+ const firstTemplateChild=this.template.firstElementChild;
442
+ const option = document.createElement('option');
443
+ this.widget.insertBefore(option,firstTemplateChild);
444
+ }
445
+
421
446
  if (nodeset.length) {
422
447
  // console.log('nodeset', nodeset);
423
448
  Array.from(nodeset).forEach(node => {
@@ -36,6 +36,7 @@ export class FxInspector extends HTMLElement {
36
36
  padding-left:var(--inspector-handle-width);
37
37
  color:var(--inspector-color);
38
38
  overflow:scroll;
39
+ height:100%;
39
40
  }
40
41
  pre{
41
42
  background:var(--inspector-pre-bg);
@@ -45,7 +46,7 @@ export class FxInspector extends HTMLElement {
45
46
  }
46
47
  .handle{
47
48
  display:block;
48
- height:100vh;
49
+ height:100%;
49
50
  width:var(--inspector-handle-width);
50
51
  background:var(--inspector-handle-bg);
51
52
  opacity:0.7;
@@ -83,9 +84,9 @@ export class FxInspector extends HTMLElement {
83
84
  }
84
85
 
85
86
  update() {
86
- console.log('update');
87
+ // console.log('update');
87
88
  const pre = this.shadowRoot.querySelectorAll('pre');
88
- console.log('pre', pre);
89
+ // console.log('pre', pre);
89
90
  const fore = this.closest('fx-fore');
90
91
 
91
92
  Array.from(pre).forEach(element => {
@@ -111,7 +112,7 @@ export class FxInspector extends HTMLElement {
111
112
  <span class="handle"></span>
112
113
  ${instances.map(
113
114
  (instance, index) => `
114
- <details open>
115
+ <details>
115
116
  <summary>${instance.id}</summary>
116
117
  <pre id="${instance.id}"></pre>
117
118
  </details>
@@ -120,9 +121,10 @@ export class FxInspector extends HTMLElement {
120
121
  </div>
121
122
  `;
122
123
 
124
+ /*
123
125
  const handle = this.shadowRoot.querySelector('.handle');
124
126
  handle.addEventListener('click', e => {
125
- console.log('toggling');
127
+ // console.log('toggling');
126
128
  const { target } = e;
127
129
  if (this.hasAttribute('open')) {
128
130
  this.removeAttribute('open');
@@ -130,10 +132,11 @@ export class FxInspector extends HTMLElement {
130
132
  this.setAttribute('open', 'open');
131
133
  }
132
134
  });
135
+ */
133
136
  }
134
137
 
135
138
  serializeDOM(data) {
136
- console.log('serializeDOM', data);
139
+ // console.log('serializeDOM', data);
137
140
  const ser = new XMLSerializer().serializeToString(data);
138
141
  return Fore.prettifyXml(ser);
139
142
  }
@@ -8,6 +8,7 @@ import getInScopeContext from '../getInScopeContext.js';
8
8
  * todo: review placing of value. should probably work with value attribute and not allow slotted content.
9
9
  */
10
10
  export class FxOutput extends XfAbstractControl {
11
+ /*
11
12
  static get properties() {
12
13
  return {
13
14
  ...super.properties,
@@ -17,6 +18,7 @@ export class FxOutput extends XfAbstractControl {
17
18
  };
18
19
  }
19
20
 
21
+ */
20
22
  constructor() {
21
23
  super();
22
24
  this.attachShadow({ mode: 'open' });
@@ -39,7 +39,7 @@ class FxSwitch extends FxContainer {
39
39
  refresh() {
40
40
  super.refresh();
41
41
  console.log('refresh on switch ');
42
- const cases = this.querySelectorAll('fx-case');
42
+ const cases = this.querySelectorAll(':scope > fx-case');
43
43
  if (this.isBound()) {
44
44
  Array.from(cases).forEach(caseElem => {
45
45
  const name = caseElem.getAttribute('name');
@@ -50,7 +50,7 @@ class FxSwitch extends FxContainer {
50
50
  }
51
51
  });
52
52
  } else {
53
- const selected = this.querySelector('.selected-case');
53
+ const selected = this.querySelector(':scope > .selected-case');
54
54
  if (!selected) {
55
55
  cases[0].classList.add('selected-case');
56
56
  }