@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
@@ -2,7 +2,7 @@ import './fx-repeatitem.js';
2
2
 
3
3
  import { Fore } from '../fore.js';
4
4
  import { foreElementMixin } from '../ForeElementMixin.js';
5
- import { evaluateXPath, evaluateXPathToNodes } from '../xpath-evaluation.js';
5
+ import { evaluateXPath } from '../xpath-evaluation.js';
6
6
  import getInScopeContext from '../getInScopeContext.js';
7
7
 
8
8
  /**
@@ -25,9 +25,6 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
25
25
  index: {
26
26
  type: Number,
27
27
  },
28
- ref: {
29
- type: String,
30
- },
31
28
  template: {
32
29
  type: Object,
33
30
  },
@@ -57,7 +54,7 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
57
54
  this.inited = false;
58
55
  this.index = 1;
59
56
  this.repeatSize = 0;
60
- this.attachShadow({ mode: 'open' });
57
+ this.attachShadow({ mode: 'open', delegatesFocus: true });
61
58
  }
62
59
 
63
60
  get repeatSize() {
@@ -91,6 +88,7 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
91
88
  }
92
89
 
93
90
  connectedCallback() {
91
+ // this.display = window.getComputedStyle(this, null).getPropertyValue("display");
94
92
  this.ref = this.getAttribute('ref');
95
93
  // console.log('### fx-repeat connected ', this.id);
96
94
  this.addEventListener('item-changed', e => {
@@ -103,6 +101,7 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
103
101
  // todo: review - this is just used by append action - event consolidation ?
104
102
  this.addEventListener('index-changed', e => {
105
103
  e.stopPropagation();
104
+ if (!e.target === this) return;
106
105
  console.log('handle index event ', e);
107
106
  // const { item } = e.detail;
108
107
  // const idx = Array.from(this.children).indexOf(item);
@@ -116,23 +115,28 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
116
115
  console.log('insert catched', nodes, this.index);
117
116
  });
118
117
 
118
+ if (this.getOwnerForm().lazyRefresh) {
119
+ this.mutationObserver = new MutationObserver(mutations => {
120
+ console.log('mutations', mutations);
121
+ this.refresh(true);
122
+ });
123
+ }
124
+ this.getOwnerForm().registerLazyElement(this);
125
+
119
126
  const style = `
120
- :host {
121
- // display: none;
122
- }
123
- ::slotted(*){
124
- // display:none;
125
- }
126
- .fade-out-bottom {
127
- -webkit-animation: fade-out-bottom 0.7s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
128
- animation: fade-out-bottom 0.7s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
129
- }
130
- .fade-out-bottom {
131
- -webkit-animation: fade-out-bottom 0.7s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
132
- animation: fade-out-bottom 0.7s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
133
- }
134
- `;
127
+ :host{
128
+ }
129
+ .fade-out-bottom {
130
+ -webkit-animation: fade-out-bottom 0.7s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
131
+ animation: fade-out-bottom 0.7s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
132
+ }
133
+ .fade-out-bottom {
134
+ -webkit-animation: fade-out-bottom 0.7s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
135
+ animation: fade-out-bottom 0.7s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
136
+ }
137
+ `;
135
138
  const html = `
139
+ <slot name="header"></slot>
136
140
  <slot></slot>
137
141
  `;
138
142
  this.shadowRoot.innerHTML = `
@@ -167,6 +171,14 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
167
171
  const inscope = getInScopeContext(this, this.ref);
168
172
  // console.log('##### inscope ', inscope);
169
173
  // console.log('##### ref ', this.ref);
174
+ // now we got a nodeset and attach MutationObserver to it
175
+
176
+ if (this.mutationObserver && inscope.nodeName) {
177
+ this.mutationObserver.observe(inscope, {
178
+ childList: true,
179
+ subtree: true,
180
+ });
181
+ }
170
182
 
171
183
  const seq = evaluateXPath(this.ref, inscope, this.getOwnerForm());
172
184
  // const seq = evaluateXPathToNodes(this.ref, inscope, this.getOwnerForm());
@@ -195,18 +207,14 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
195
207
  throw new Error(`Unexpected result of repeat nodeset: ${seq}`);
196
208
  }
197
209
 
198
- async refresh() {
199
- console.group('fx-repeat.refresh on', this.id);
210
+ async refresh(force) {
211
+ // console.group('fx-repeat.refresh on', this.id);
200
212
 
201
213
  if (!this.inited) this.init();
202
-
203
- /*
204
- const inscope = this.getInScopeContext();
205
- this.nodeset = evaluateXPathToNodes(this.ref, inscope, this.getOwnerForm());
206
- */
207
-
214
+ console.time('repeat-refresh', this);
208
215
  this._evalNodeset();
209
216
  // console.log('repeat refresh nodeset ', this.nodeset);
217
+ // console.log('repeatCount', this.repeatCount);
210
218
 
211
219
  const repeatItems = this.querySelectorAll(':scope > fx-repeatitem');
212
220
  const repeatItemCount = repeatItems.length;
@@ -218,103 +226,102 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
218
226
 
219
227
  // const contextSize = this.nodeset.length;
220
228
  const contextSize = nodeCount;
221
- const modified = [];
229
+ // todo: review - cant the context really never be smaller than the repeat count?
230
+ // todo: this code can be deprecated probably but check first
222
231
  if (contextSize < repeatItemCount) {
223
232
  for (let position = repeatItemCount; position > contextSize; position -= 1) {
224
233
  // remove repeatitem
225
234
  const itemToRemove = repeatItems[position - 1];
226
- this._fadeOut(itemToRemove);
227
- // setTimeout(itemToRemove.parentNode.removeChild(itemToRemove),1000);
228
- // itemToRemove.parentNode.removeChild(itemToRemove);
229
- // modified.push(itemToRemove);
235
+ itemToRemove.parentNode.removeChild(itemToRemove);
236
+ this.getOwnerForm().unRegisterLazyElement(itemToRemove);
237
+ // this._fadeOut(itemToRemove);
238
+ // Fore.fadeOutElement(itemToRemove)
230
239
  }
231
-
232
- // todo: update index
233
240
  }
234
241
 
235
242
  if (contextSize > repeatItemCount) {
236
243
  for (let position = repeatItemCount + 1; position <= contextSize; position += 1) {
237
244
  // add new repeatitem
238
245
 
239
- // const lastRepeatItem = repeatItems[repeatItemCount-1];
240
- // const newItem = lastRepeatItem.cloneNode(true);
241
-
242
246
  const newItem = document.createElement('fx-repeatitem');
243
247
  const clonedTemplate = this._clone();
244
-
245
- // newItem.style.display = 'none';
246
- newItem.style.opacity = '0';
247
248
  newItem.appendChild(clonedTemplate);
248
- this._fadeIn(newItem);
249
- // const tmpl = this.shadowRoot.querySelector('template');
250
- // const newItem = tmpl.content.cloneNode(true);
249
+ this.appendChild(newItem);
251
250
 
252
251
  newItem.nodeset = this.nodeset[position - 1];
253
252
  newItem.index = position;
254
- this.appendChild(newItem);
255
- // modified.push(newItem);
256
253
  }
257
254
  }
258
255
 
256
+ // ### update nodeset of repeatitems
259
257
  for (let position = 0; position < repeatItemCount; position += 1) {
260
258
  const item = repeatItems[position];
259
+ this.getOwnerForm().registerLazyElement(item);
260
+
261
261
  if (item.nodeset !== this.nodeset[position]) {
262
262
  item.nodeset = this.nodeset[position];
263
263
  }
264
264
  }
265
- Fore.refreshChildren(this);
266
- /*
267
- if (!this.inited) {
268
- Fore.refreshChildren(this);
269
- }
270
- if (contextSize === repeatItemCount) {
271
- Fore.refreshChildren(this);
265
+
266
+ // Fore.refreshChildren(clone,true);
267
+ const fore = this.getOwnerForm();
268
+ if (!fore.lazyRefresh || force) {
269
+ Fore.refreshChildren(this, force);
272
270
  }
273
- */
271
+ // this.style.display = 'block';
272
+ // this.style.display = this.display;
274
273
  this.setIndex(this.index);
274
+ console.timeEnd('repeat-refresh');
275
+
276
+ // this.replaceWith(clone);
277
+
278
+ // this.repeatCount = contextSize;
279
+ // console.log('repeatCount', this.repeatCount);
275
280
  console.groupEnd();
276
281
  }
277
282
 
283
+ // eslint-disable-next-line class-methods-use-this
284
+ _fadeOut(el) {
285
+ el.style.opacity = 1;
286
+
287
+ (function fade() {
288
+ // eslint-disable-next-line no-cond-assign
289
+ if ((el.style.opacity -= 0.1) < 0) {
290
+ el.style.display = 'none';
291
+ } else {
292
+ requestAnimationFrame(fade);
293
+ }
294
+ })();
295
+ }
296
+
278
297
  // eslint-disable-next-line class-methods-use-this
279
298
  _fadeIn(el) {
280
- // eslint-disable-next-line no-param-reassign
299
+ if (!el) return;
300
+
281
301
  el.style.opacity = 0;
282
- // eslint-disable-next-line no-param-reassign
302
+ el.style.display = this.display;
303
+
283
304
  (function fade() {
305
+ // setTimeout(() => {
284
306
  let val = parseFloat(el.style.opacity);
285
- val += 0.1;
286
- if (!(val > 1)) {
287
- // eslint-disable-next-line no-param-reassign
307
+ // eslint-disable-next-line no-cond-assign
308
+ if (!((val += 0.1) > 1)) {
288
309
  el.style.opacity = val;
289
310
  requestAnimationFrame(fade);
290
311
  }
312
+ // }, 40);
291
313
  })();
292
314
  }
293
315
 
294
- // eslint-disable-next-line class-methods-use-this
295
- _fadeOut(el) {
296
- el.classList.add('fade-out-bottom');
297
- /*
298
- el.style.opacity = 1;
299
-
300
- (function fade() {
301
- if ((el.style.opacity -= .01) < 0) {
302
- el.style.display = "none";
303
- } else {
304
- requestAnimationFrame(fade);
305
- }
306
- })();
307
- */
308
- }
309
-
310
316
  _initTemplate() {
311
- const shadowTemplate = this.shadowRoot.querySelector('template');
312
- console.log('shadowtempl ', shadowTemplate);
317
+ // const shadowTemplate = this.shadowRoot.querySelector('template');
318
+ // console.log('shadowtempl ', shadowTemplate);
313
319
 
314
320
  // const defaultSlot = this.shadowRoot.querySelector('slot');
315
321
  // todo: this is still weak - should handle that better maybe by an explicit slot?
316
- this.template = this.firstElementChild;
317
- console.log('### init template for repeat ', this.id, this.template);
322
+ // this.template = this.firstElementChild;
323
+ this.template = this.querySelector('template');
324
+ // console.log('### init template for repeat ', this.id, this.template);
318
325
 
319
326
  if (this.template === null) {
320
327
  // console.error('### no template found for this repeat:', this.id);
@@ -333,7 +340,7 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
333
340
 
334
341
  _initRepeatItems() {
335
342
  // const model = this.getModel();
336
- this.textContent = '';
343
+ // this.textContent = '';
337
344
  this.nodeset.forEach((item, index) => {
338
345
  const repeatItem = document.createElement('fx-repeatitem');
339
346
  repeatItem.nodeset = this.nodeset[index];
@@ -20,8 +20,23 @@ export class FxRepeatitem extends foreElementMixin(HTMLElement) {
20
20
  constructor() {
21
21
  super();
22
22
  this.inited = false;
23
+
23
24
  this.addEventListener('click', this._dispatchIndexChange);
24
- this.attachShadow({ mode: 'open' });
25
+ // this.addEventListener('focusin', this._handleFocus);
26
+ this.addEventListener('focusin', this._dispatchIndexChange);
27
+
28
+ this.attachShadow({ mode: 'open', delegatesFocus: true });
29
+ }
30
+
31
+ _handleFocus() {
32
+ this.parentNode.setIndex(this.index);
33
+ // TODO: do this somewhere else, somewhere more central
34
+
35
+ /**
36
+ * todo: resolve - this is problematic as it triggers a lot of unneeded refreshes but it needed
37
+ * when you want to support activating the right repeatitem when the user tabs through controls.
38
+ */
39
+ // this.closest('fx-fore').refresh();
25
40
  }
26
41
 
27
42
  _dispatchIndexChange() {
@@ -34,6 +49,8 @@ export class FxRepeatitem extends foreElementMixin(HTMLElement) {
34
49
  }
35
50
 
36
51
  connectedCallback() {
52
+ this.display = this.style.display;
53
+
37
54
  const html = `
38
55
  <slot></slot>
39
56
  `;
@@ -41,11 +58,13 @@ export class FxRepeatitem extends foreElementMixin(HTMLElement) {
41
58
  this.shadowRoot.innerHTML = `
42
59
  ${html}
43
60
  `;
61
+ this.getOwnerForm().registerLazyElement(this);
44
62
  }
45
63
 
46
64
  disconnectedCallback() {
47
65
  // console.log('disconnectedCallback ', this);
48
66
  this.removeEventListener('click', this._dispatchIndexChange());
67
+ this.removeEventListener('focusin', this._handleFocus);
49
68
  }
50
69
 
51
70
  init() {
@@ -60,59 +79,30 @@ export class FxRepeatitem extends foreElementMixin(HTMLElement) {
60
79
  return this.getModelItem()[this.index];
61
80
  }
62
81
 
63
- /*
64
- _initializeChildren(node) {
65
- const children = Array.from(node.children);
66
- // console.log('_initializeChildren ', children);
67
-
68
- children.forEach(child => {
69
- if (Fore.isUiElement(child.nodeName)) {
70
- child.repeated = true;
71
- } else if (child.children.length !== 0) {
72
- const grantChildren = Array.from(child.children);
73
- grantChildren.forEach(grantChild => {
74
- this._initializeChildren(grantChild);
75
- });
76
- }
77
-
78
- });
79
- }
80
- */
82
+ refresh(force) {
83
+ // console.log('refresh repeatitem: ',this.nodeset);
84
+ // console.log('refresh repeatitem nodeset: ',this.nodeset);
85
+ this.modelItem = this.getModel().getModelItem(this.nodeset);
81
86
 
82
- /*
83
- firstUpdated(_changedProperties) {
84
- // console.log('### fx-repeatitem firstUpdated index ', this.index);
85
- // console.log('### fx-repeatitem firstUpdated nodeset ', this.nodeset);
86
- // console.log('### fx-repeatitem firstUpdated model ', this.model);
87
- this.dispatchEvent(new CustomEvent('repeatitem-created', {
88
- composed: true,
89
- bubbles: true,
90
- detail: {item: this}
91
- }));
92
- // this.init();
87
+ if (this.modelItem && !this.modelItem.relevant) {
88
+ // await Fore.fadeOutElement(this)
89
+ this.style.display = 'none';
90
+ } else {
91
+ // if(this.hasAttribute('repeat-index')){
92
+ // Fore.fadeInElement(this);
93
+ // }
94
+ this.style.display = this.display;
93
95
  }
94
- */
95
96
 
96
- refresh() {
97
- // console.log('refresh repeatitem: ',this.nodeset);
98
97
  /*
99
- if(!this.inited){
100
- this.init();
101
- }
102
- */
103
- // console.log('refresh repeatitem nodeset: ',this.nodeset);
104
- Fore.refreshChildren(this);
105
- }
106
-
107
- /*
108
- createRenderRoot() {
109
- /!**
110
- * Render template without shadow DOM. Note that shadow DOM features like
111
- * encapsulated CSS and slots are unavailable.
112
- *!/
113
- return this;
98
+ if (this?.modelItem?.relevant) {
99
+ // Fore.refreshChildren(this);
100
+ } else {
114
101
  }
115
102
  */
103
+
104
+ Fore.refreshChildren(this, force);
105
+ }
116
106
  }
117
107
 
118
108
  window.customElements.define('fx-repeatitem', FxRepeatitem);
@@ -25,7 +25,7 @@ export class FxTrigger extends XfAbstractControl {
25
25
 
26
26
  const element = elements[0];
27
27
  element.addEventListener('click', e => this.performActions(e));
28
-
28
+ this.widget = element;
29
29
  // # terrible hack but browser behaves strange - seems to fire a 'click' for a button when it receives a
30
30
  // # 'Space' or 'Enter' key
31
31
  if (element.nodeName !== 'BUTTON') {
@@ -37,13 +37,13 @@ export class FxTrigger extends XfAbstractControl {
37
37
  }
38
38
  });
39
39
  /*
40
- this.addEventListener('click', e => this.performActions(e));
41
- this.addEventListener('keypress', (e) => {
42
- if(e.code === 'Space' || e.code === 'Enter'){
43
- this.performActions(e);
44
- }
45
- });
46
- */
40
+ this.addEventListener('click', e => this.performActions(e));
41
+ this.addEventListener('keypress', (e) => {
42
+ if(e.code === 'Space' || e.code === 'Enter'){
43
+ this.performActions(e);
44
+ }
45
+ });
46
+ */
47
47
  }
48
48
 
49
49
  // eslint-disable-next-line class-methods-use-this
@@ -53,36 +53,58 @@ export class FxTrigger extends XfAbstractControl {
53
53
  `;
54
54
  }
55
55
 
56
- performActions(e) {
57
- console.log('performActions ', this.children);
56
+ getWidget() {
57
+ return this.widget;
58
+ }
59
+
60
+ async updateWidgetValue() {
61
+ console.log('trigger update', this);
62
+ return null;
63
+ }
64
+
65
+ handleReadonly() {
66
+ super.handleReadonly();
67
+ // ### add disabled attribute in case we're readonly. This is special behavior of fx-trigger
68
+ if (this.widget.hasAttribute('readonly')) {
69
+ this.widget.setAttribute('disabled', 'disabled');
70
+ } else {
71
+ this.widget.removeAttribute('disabled');
72
+ }
73
+ }
74
+
75
+ async performActions(e) {
76
+ // todo: support readonly for trigger not executing the action
58
77
  const repeatedItem = this.closest('fx-repeatitem');
59
78
  if (repeatedItem) {
60
79
  console.log('repeated click');
61
80
  repeatedItem.click();
62
81
  }
63
- for (let i = 0; i < this.children.length; i += 1) {
64
- // console.log('child ', this.children[i]);
65
- const child = this.children[i];
66
82
 
67
- if (typeof child.execute === 'function') {
68
- child.execute(e);
83
+ const forLoop = async () => {
84
+ for (let i = 0; i < this.children.length; i += 1) {
85
+ const child = this.children[i];
86
+ if (typeof child.execute === 'function') {
87
+ // eslint-disable-next-line no-await-in-loop
88
+ await child.execute(e);
89
+ }
69
90
  }
70
- }
91
+ };
92
+ forLoop();
71
93
  }
72
94
 
73
95
  /*
74
- async refresh() {
75
- super.refresh();
76
- // console.log('fx-button refresh');
96
+ async refresh() {
97
+ super.refresh();
98
+ // console.log('fx-button refresh');
77
99
 
78
- const elements = this.querySelectorAll(':scope > *');
79
- elements.forEach(element => {
80
- if (typeof element.refresh === 'function') {
81
- element.refresh();
82
- }
83
- });
84
- }
85
- */
100
+ const elements = this.querySelectorAll(':scope > *');
101
+ elements.forEach(element => {
102
+ if (typeof element.refresh === 'function') {
103
+ element.refresh();
104
+ }
105
+ });
106
+ }
107
+ */
86
108
  }
87
109
 
88
110
  customElements.define('fx-trigger', FxTrigger);