@jinntec/fore 1.5.0 → 1.6.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 (71) hide show
  1. package/dist/fore-dev.js +2 -36
  2. package/dist/fore-dev.js.map +1 -1
  3. package/dist/fore.js +2 -30
  4. package/dist/fore.js.map +1 -1
  5. package/index.js +12 -0
  6. package/package.json +9 -5
  7. package/resources/fore.css +178 -92
  8. package/src/DependencyNotifyingDomFacade.js +1 -2
  9. package/src/ForeElementMixin.js +31 -5
  10. package/src/actions/abstract-action.js +379 -297
  11. package/src/actions/fx-action.js +0 -1
  12. package/src/actions/fx-append.js +1 -2
  13. package/src/actions/fx-confirm.js +12 -0
  14. package/src/actions/fx-copy.js +0 -1
  15. package/src/actions/fx-delete.js +31 -9
  16. package/src/actions/fx-dispatch.js +19 -5
  17. package/src/actions/fx-hide.js +19 -0
  18. package/src/actions/fx-insert.js +72 -8
  19. package/src/actions/fx-load.js +253 -0
  20. package/src/actions/fx-message.js +22 -7
  21. package/src/actions/fx-refresh.js +11 -1
  22. package/src/actions/fx-reload.js +12 -2
  23. package/src/actions/fx-replace.js +5 -4
  24. package/src/actions/fx-reset.js +48 -0
  25. package/src/actions/fx-return.js +0 -1
  26. package/src/actions/fx-send.js +40 -2
  27. package/src/actions/fx-setfocus.js +25 -7
  28. package/src/actions/fx-setvalue.js +32 -4
  29. package/src/actions/fx-show.js +14 -2
  30. package/src/actions/fx-toggle.js +0 -1
  31. package/src/actions/fx-update.js +9 -0
  32. package/src/events.js +0 -1
  33. package/src/fore.js +118 -63
  34. package/src/functions/common-function.js +28 -0
  35. package/src/fx-bind.js +7 -7
  36. package/src/fx-fore.js +152 -55
  37. package/src/fx-instance.js +55 -17
  38. package/src/fx-model.js +31 -33
  39. package/src/fx-submission.js +50 -47
  40. package/src/getInScopeContext.js +8 -10
  41. package/src/lab/fore-component.js +90 -0
  42. package/src/lab/instance-inspector.js +56 -0
  43. package/src/lab/template.html +16 -0
  44. package/src/relevance.js +27 -1
  45. package/src/tools/adi.js +1056 -0
  46. package/src/tools/fx-action-log.js +662 -0
  47. package/src/tools/fx-devtools.js +444 -0
  48. package/src/tools/fx-dom-inspector.js +609 -0
  49. package/src/tools/fx-json-instance.js +435 -0
  50. package/src/tools/fx-log-item.js +133 -0
  51. package/src/tools/fx-log-settings.js +474 -0
  52. package/src/tools/fx-minimap.js +194 -0
  53. package/src/tools/helpers.js +132 -0
  54. package/src/ui/abstract-control.js +41 -3
  55. package/src/ui/fx-action-log.js +358 -0
  56. package/src/ui/fx-alert.js +0 -1
  57. package/src/ui/fx-container.js +14 -3
  58. package/src/ui/fx-control.js +553 -474
  59. package/src/ui/fx-dialog.js +2 -0
  60. package/src/ui/fx-dom-inspector.js +1255 -0
  61. package/src/ui/fx-group.js +3 -4
  62. package/src/ui/fx-hint.js +2 -4
  63. package/src/ui/fx-inspector.js +5 -6
  64. package/src/ui/fx-items.js +55 -14
  65. package/src/ui/fx-output.js +36 -17
  66. package/src/ui/fx-repeat-attributes.js +10 -43
  67. package/src/ui/fx-repeat.js +5 -7
  68. package/src/ui/fx-switch.js +14 -3
  69. package/src/ui/fx-trigger.js +13 -1
  70. package/src/xpath-evaluation.js +109 -26
  71. package/src/xpath-util.js +55 -1
@@ -1,6 +1,6 @@
1
1
  import { AbstractAction } from './abstract-action.js';
2
- import {evaluateXPathToString} from "../xpath-evaluation";
3
- import {Fore} from "../fore";
2
+ import {evaluateXPathToString} from "../xpath-evaluation.js";
3
+ import {Fore} from "../fore.js";
4
4
  import getInScopeContext from '../getInScopeContext.js';
5
5
 
6
6
  /**
@@ -16,10 +16,24 @@ class FxMessage extends AbstractAction {
16
16
  this.attachShadow({ mode: 'open' });
17
17
  }
18
18
 
19
+ static get properties () {
20
+ return {
21
+ ...AbstractAction.properties,
22
+ modelItem:undefined,
23
+ messageTextContent: {
24
+ type: String,
25
+ get value() {
26
+ return "here!";
27
+ }
28
+ }
29
+ };
30
+ }
31
+
19
32
  connectedCallback() {
20
33
  super.connectedCallback();
21
34
  this.event = this.hasAttribute('event') ? this.getAttribute('event') : '';
22
35
  this.level = this.hasAttribute('level') ? this.getAttribute('level') : 'ephemeral';
36
+ this.message = '';
23
37
 
24
38
  this.messageTextContent = this.textContent;
25
39
  const style = `
@@ -35,10 +49,12 @@ class FxMessage extends AbstractAction {
35
49
  `;
36
50
  }
37
51
 
52
+ /*
38
53
  disconnectedCallback() {
39
54
  // super.disconnectedCallback();
40
55
  this.targetElement.removeEventListener(this.event, e => this.execute(e));
41
56
  }
57
+ */
42
58
 
43
59
  // eslint-disable-next-line class-methods-use-this
44
60
  renderHTML() {
@@ -49,19 +65,18 @@ class FxMessage extends AbstractAction {
49
65
 
50
66
  async perform() {
51
67
  super.perform();
52
- let message;
53
68
  if (this.hasAttribute('value')) {
54
- message = this._getValue();
69
+ this.message = this._getValue();
55
70
  } else {
56
71
  this.getOwnerForm().evaluateTemplateExpression(this.messageTextContent, this.firstChild);
57
- message = this.textContent;
72
+ this.message = this.textContent;
58
73
  }
59
74
 
60
75
  this.dispatchEvent(
61
76
  new CustomEvent('message', {
62
- composed: true,
77
+ composed: false,
63
78
  bubbles: true,
64
- detail: { level: this.level, message },
79
+ detail: { level: this.level, message:this.message },
65
80
  }),
66
81
  );
67
82
  }
@@ -1,6 +1,7 @@
1
1
  import { AbstractAction } from './abstract-action.js';
2
2
  import { Fore } from '../fore.js';
3
3
  import {resolveId} from "../xpath-evaluation.js";
4
+ import {XPathUtil} from "../xpath-util.js";
4
5
 
5
6
  /**
6
7
  * `fx-refresh`
@@ -10,8 +11,17 @@ import {resolveId} from "../xpath-evaluation.js";
10
11
  */
11
12
  class FxRefresh extends AbstractAction {
12
13
  async perform() {
14
+ this.dispatchEvent(
15
+ new CustomEvent('execute-action', {
16
+ composed: true,
17
+ bubbles: true,
18
+ cancelable:true,
19
+ detail: { action: this, event:this.event},
20
+ }),
21
+ );
22
+
13
23
  if (this.hasAttribute('self')) {
14
- const control = Fore.getClosest('fx-control', this);
24
+ const control = XPathUtil.getClosest('fx-control', this);
15
25
  if (control) {
16
26
  control.refresh();
17
27
  return;
@@ -1,6 +1,5 @@
1
1
  import { Fore } from '../fore.js';
2
2
  import { AbstractAction } from './abstract-action.js';
3
- import { resolveId } from '../xpath-evaluation.js';
4
3
 
5
4
  /**
6
5
  * `fx-reload`
@@ -13,13 +12,24 @@ import { resolveId } from '../xpath-evaluation.js';
13
12
  export class FxReload extends AbstractAction {
14
13
 
15
14
  connectedCallback() {
15
+ if(super.connectedCallback){
16
16
  super.connectedCallback();
17
- this.addEventListener('reload', event => {
17
+ }
18
+ this.addEventListener('reload', () => {
18
19
  window.location.reload();
19
20
  },{once:true});
20
21
  }
21
22
 
22
23
  async perform() {
24
+ this.dispatchEvent(
25
+ new CustomEvent('execute-action', {
26
+ composed: true,
27
+ bubbles: true,
28
+ cancelable:true,
29
+ detail: { action: this, event:this.event},
30
+ }),
31
+ );
32
+
23
33
  Fore.dispatch(this, 'reload', {});
24
34
  }
25
35
  }
@@ -2,7 +2,6 @@
2
2
  import '../fx-model.js';
3
3
  import { AbstractAction } from './abstract-action.js';
4
4
  import { evaluateXPathToFirstNode } from '../xpath-evaluation.js';
5
- import getInScopeContext from "../getInScopeContext";
6
5
 
7
6
  /**
8
7
  * `fx-replace` - replaces the node referred to with 'ref' with node referred to with 'with' attribute.
@@ -34,7 +33,6 @@ export default class FxReplace extends AbstractAction {
34
33
 
35
34
  async perform() {
36
35
  super.perform();
37
- console.log('replace action');
38
36
  // console.log('replace action variables', this.inScopeVariables);
39
37
  // if (!this.nodeset) {
40
38
  // return;
@@ -46,8 +44,12 @@ export default class FxReplace extends AbstractAction {
46
44
  this.replace(this.nodeset, target);
47
45
  }
48
46
 
47
+ actionPerformed() {
48
+ this.getModel().rebuild();
49
+ super.actionPerformed();
50
+ }
51
+
49
52
  replace(toReplace, replaceWith) {
50
- console.log('replace', toReplace, replaceWith);
51
53
  if (!toReplace || !replaceWith) return; // bail out silently
52
54
  if (!toReplace.nodeName || !replaceWith.nodeName) {
53
55
  console.warn('fx-replace: one argument is not a node');
@@ -58,7 +60,6 @@ export default class FxReplace extends AbstractAction {
58
60
  const { ownerElement } = toReplace;
59
61
  ownerElement.setAttribute(replaceWith.nodeName, replaceWith.textContent);
60
62
  ownerElement.removeAttribute(toReplace.nodeName);
61
- console.log('owner', ownerElement);
62
63
  } else if (toReplace.nodeType === Node.ELEMENT_NODE) {
63
64
  const cloned = replaceWith.cloneNode(true);
64
65
  toReplace.replaceWith(cloned);
@@ -0,0 +1,48 @@
1
+ import {Fore} from '../fore.js';
2
+ import {AbstractAction} from './abstract-action.js';
3
+
4
+ /**
5
+ * `fx-reset`
6
+ * resets an instance to use inline template data
7
+ *
8
+ * @customElement
9
+ * @demo demo/project.html
10
+ */
11
+ export class FxReset extends AbstractAction {
12
+ static get properties() {
13
+ return {
14
+ ...super.properties,
15
+ instance: {
16
+ type: String,
17
+ },
18
+ };
19
+ }
20
+
21
+ connectedCallback() {
22
+ super.connectedCallback();
23
+ this.instance = this.getAttribute('instance');
24
+ if (!this.instance) {
25
+ Fore.dispatch(this, 'error', {message: 'instance does not exist'});
26
+ }
27
+ }
28
+
29
+ async perform() {
30
+ this.dispatchEvent(
31
+ new CustomEvent('execute-action', {
32
+ composed: true,
33
+ bubbles: true,
34
+ cancelable: true,
35
+ detail: {action: this, event: this.event},
36
+ }),
37
+ );
38
+
39
+ const model = this.getModel();
40
+ const data = model.getInstance(this.instance);
41
+ data.reset();
42
+ this.needsUpdate = true;
43
+ }
44
+ }
45
+
46
+ if (!customElements.get('fx-reset')) {
47
+ window.customElements.define('fx-reset', FxReset);
48
+ }
@@ -22,7 +22,6 @@ export class FxReturn extends AbstractAction {
22
22
 
23
23
  async perform() {
24
24
  super.perform();
25
- console.log('performing return with nodes', this.nodeset);
26
25
 
27
26
  /*
28
27
  ### note that this event does not use Fore.dispatch as the event uses 'composed:true' to let the event travel
@@ -13,6 +13,8 @@ class FxSend extends AbstractAction {
13
13
  constructor() {
14
14
  super();
15
15
  this.value = '';
16
+ this.url = null;
17
+ this.target=null;
16
18
  }
17
19
 
18
20
  connectedCallback() {
@@ -20,12 +22,16 @@ class FxSend extends AbstractAction {
20
22
  super.connectedCallback();
21
23
  // console.log('connectedCallback ', this);
22
24
  this.submission = this.getAttribute('submission');
25
+ this.url = this.hasAttribute('url') ? this.getAttribute('url'):null;
26
+ this.target = this.hasAttribute('target') ? this.getAttribute('target'):null;
23
27
  }
24
28
 
25
29
  async perform() {
26
30
  super.perform();
27
31
 
28
32
  console.log('submitting ', this.submission);
33
+ // reset CSS class that signalled validation error during last submit
34
+ this.getOwnerForm().classList.remove('submit-validation-failed');
29
35
  // console.log('submitting model', this.getModel());
30
36
 
31
37
  // if not exists signal error
@@ -34,6 +40,7 @@ class FxSend extends AbstractAction {
34
40
  // const submission = fore.querySelector(`#${this.submission}`);
35
41
  const submission = this.getModel().querySelector(`#${this.submission}`);
36
42
  if (submission === null) {
43
+ /*
37
44
  this.dispatchEvent(
38
45
  new CustomEvent('error', {
39
46
  composed: true,
@@ -41,10 +48,41 @@ class FxSend extends AbstractAction {
41
48
  detail: { message: `fx-submission element with id: '${this.submission}' not found` },
42
49
  }),
43
50
  );
44
- throw new Error(`submission with id: ${this.submission} not found`);
51
+ */
52
+ this.dispatchEvent(
53
+ new CustomEvent('log', {
54
+ composed: false,
55
+ bubbles: true,
56
+ cancelable:true,
57
+ detail: { id:this.id, message: `fx-submission element with id: '${this.submission}' not found`, level:'Error'},
58
+ }),
59
+ );
60
+ return;
61
+
62
+ // throw new Error(`submission with id: ${this.submission} not found`);
63
+ }
64
+
65
+ if(this.url){
66
+ const resolved = this.evaluateAttributeTemplateExpression(this.url,this);
67
+ submission.parameters.set('url',resolved);
45
68
  }
69
+ if(this.target){
70
+ const resolved = this.evaluateAttributeTemplateExpression(this.target,this);
71
+ submission.parameters.set('target',resolved);
72
+ }
73
+
74
+
75
+ /*
76
+ Array.from(this.attributes).forEach( attr => {
77
+ if(attr.nodeName !== 'submission'){
78
+ const resolved = this.evaluateAttributeTemplateExpression(attr,this);
79
+ submission.parameters.set(attr.nodeName,resolved);
80
+ }
81
+ });
82
+ */
83
+
46
84
  console.log('submission', submission);
47
- await submission.submit();
85
+ await submission.submit();
48
86
  /*
49
87
  if(submission.replace === 'instance'){
50
88
  this.getModel().updateModel();
@@ -1,5 +1,4 @@
1
- import {AbstractAction} from "./abstract-action";
2
- import {resolveId} from "../xpath-evaluation";
1
+ import {AbstractAction} from "./abstract-action.js";
3
2
 
4
3
  /**
5
4
  * `fx-setfocus`
@@ -9,21 +8,37 @@ import {resolveId} from "../xpath-evaluation";
9
8
  */
10
9
  export class FxSetfocus extends AbstractAction {
11
10
  connectedCallback() {
11
+ if (super.connectedCallback) {
12
12
  super.connectedCallback();
13
+ }
13
14
  this.control = this.hasAttribute('control') ? this.getAttribute('control') : null;
14
15
  }
15
16
 
16
17
  async perform() {
17
- console.log('setting focus', this.control);
18
+
19
+ this.dispatchEvent(
20
+ new CustomEvent('execute-action', {
21
+ composed: true,
22
+ bubbles: true,
23
+ cancelable: true,
24
+ detail: {action: this, event: this.event},
25
+ }),
26
+ );
27
+
18
28
  // super.perform();
19
29
  const selector = '#'+this.control;
20
30
 
21
- let targetElement = this.getOwnerForm().querySelector(selector);;
31
+
32
+ let targetElement = document.querySelector(selector);
33
+
34
+ if(!targetElement) {
35
+ console.warn('targetElement of setfocus action does not exist (yet)', selector);
36
+ return;
37
+ }
22
38
 
23
39
  // ### focus action is itself hosted within a repeat
24
- const parentIItem = this.closest('fx-repeatitem');
40
+ const parentIItem = targetElement.closest('fx-repeatitem');
25
41
  if(parentIItem){
26
- console.log('parentRepeat',parentIItem);
27
42
  targetElement = parentIItem.querySelector(selector);
28
43
  this._focus(targetElement);
29
44
  return;
@@ -45,9 +60,12 @@ export class FxSetfocus extends AbstractAction {
45
60
  }
46
61
 
47
62
  _focus(targetElement){
48
- if(targetElement){
63
+ if(targetElement && typeof targetElement.getWidget === 'function'){
49
64
  targetElement.getWidget().focus();
50
65
  }
66
+ if(targetElement && targetElement.nodeType === Node.ELEMENT_NODE){
67
+ targetElement.click();
68
+ }
51
69
  }
52
70
 
53
71
  _select(targetElement){
@@ -2,6 +2,7 @@
2
2
  import '../fx-model.js';
3
3
  import {AbstractAction} from './abstract-action.js';
4
4
  import {evaluateXPath} from '../xpath-evaluation.js';
5
+ import {Fore} from '../fore.js';
5
6
 
6
7
  /**
7
8
  * `fx-setvalue`
@@ -50,9 +51,7 @@ export default class FxSetvalue extends AbstractAction {
50
51
  } else {
51
52
  value = '';
52
53
  }
53
- console.log('value', value);
54
- if (value.nodeType === Node.ATTRIBUTE_NODE) {
55
- // console.log('value', value.nodeValue);
54
+ if (value?.nodeType && value.nodeType === Node.ATTRIBUTE_NODE) {
56
55
  value = value.nodeValue;
57
56
  }
58
57
  const mi = this.getModelItem();
@@ -62,12 +61,41 @@ export default class FxSetvalue extends AbstractAction {
62
61
 
63
62
  }
64
63
 
64
+ /**
65
+ * need to overwrite default dispatchExecute to do it ourselves. This is necessary for tracking control value changes
66
+ * which call setvalue directly without perform().
67
+ */
68
+ dispatchExecute() {}
69
+
65
70
  setValue(modelItem, newVal) {
66
71
  const item = modelItem;
67
72
  if (!item) return;
68
73
 
69
74
  if (item.value !== newVal) {
70
- item.value = newVal;
75
+ // const path = XPathUtil.getPath(modelItem.node);
76
+ const path = Fore.getDomNodeIndexString(modelItem.node)
77
+
78
+ const ev = this.event;
79
+ const targetElem = this;
80
+ this.dispatchEvent(
81
+ new CustomEvent('execute-action', {
82
+ composed: true,
83
+ bubbles: true,
84
+ cancelable:true,
85
+ detail: { action: targetElem, event:ev, value:newVal, path:path},
86
+ }),
87
+ );
88
+
89
+ if(newVal?.nodeType){
90
+ if(newVal.nodeType === Node.ELEMENT_NODE){
91
+ item.value = newVal.textContent;
92
+ }
93
+ if(newVal.nodeType === Node.ATTRIBUTE_NODE){
94
+ item.value = newVal.getValue()
95
+ }
96
+ }else{
97
+ item.value = newVal;
98
+ }
71
99
  this.getModel().changed.push(modelItem);
72
100
  this.needsUpdate = true;
73
101
  }
@@ -3,14 +3,17 @@ import { FxAction } from './fx-action.js';
3
3
  import { resolveId } from '../xpath-evaluation.js';
4
4
 
5
5
  /**
6
- * `fx-confirm`
6
+ * `fx-show`
7
7
  * Displays a simple confirmation before actually executing the nested actions.
8
8
  *
9
9
  * @customElement
10
- * @demo demo/project.html
10
+ * @event fx-show dispatched when dialog is shown
11
11
  */
12
12
  export class FxShow extends FxAction {
13
13
  connectedCallback() {
14
+ if (super.connectedCallback) {
15
+ super.connectedCallback();
16
+ }
14
17
  this.dialog = this.getAttribute('dialog');
15
18
  if (!this.dialog) {
16
19
  Fore.dispatch(this, 'error', { message: 'dialog does not exist' });
@@ -18,6 +21,15 @@ export class FxShow extends FxAction {
18
21
  }
19
22
 
20
23
  async perform() {
24
+ this.dispatchEvent(
25
+ new CustomEvent('execute-action', {
26
+ composed: true,
27
+ bubbles: true,
28
+ cancelable:true,
29
+ detail: { action: this, event:this.event},
30
+ }),
31
+ );
32
+
21
33
  const targetDlg = resolveId(this.dialog,this);
22
34
  if(!targetDlg){
23
35
  console.error('target dialog with given id does not exist',this.dialog);
@@ -21,7 +21,6 @@ class FxToggle extends AbstractAction {
21
21
 
22
22
  async perform() {
23
23
  super.perform();
24
- console.log('### fx-toggle.execute ');
25
24
  if (this.case) {
26
25
  const ownerForm = this.getOwnerForm();
27
26
  const caseElement = ownerForm.querySelector(`#${this.case}`);
@@ -8,6 +8,15 @@ import { AbstractAction } from './abstract-action.js';
8
8
  */
9
9
  class FxUpdate extends AbstractAction {
10
10
  async perform() {
11
+ this.dispatchEvent(
12
+ new CustomEvent('execute-action', {
13
+ composed: true,
14
+ bubbles: true,
15
+ cancelable:true,
16
+ detail: { action: this, event:this.event},
17
+ }),
18
+ );
19
+
11
20
  this.getModel().updateModel();
12
21
  }
13
22
  }
package/src/events.js CHANGED
@@ -1,5 +1,4 @@
1
1
  export function leadingDebounce(caller, func, timeout = 300){
2
- console.log('debouncing', func);
3
2
  let timer;
4
3
  return (...args) => {
5
4
  if (!timer) {