@jinntec/fore 1.0.0-1 → 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.
- package/README.md +10 -0
- package/dist/fore-all.js +10 -10
- package/dist/fore.js +1 -1
- package/index.js +3 -0
- package/package.json +4 -1
- package/resources/fore.css +73 -1
- package/src/DependencyNotifyingDomFacade.js +9 -1
- package/src/ForeElementMixin.js +15 -15
- package/src/actions/abstract-action.js +6 -5
- package/src/actions/fx-action.js +1 -0
- package/src/actions/fx-dispatch.js +10 -2
- package/src/actions/fx-insert.js +8 -8
- package/src/actions/fx-send.js +1 -1
- package/src/actions/fx-setvalue.js +1 -3
- package/src/dep_graph.js +9 -0
- package/src/drawdown.js +172 -0
- package/src/fore.js +53 -3
- package/src/fx-bind.js +8 -7
- package/src/fx-fore.js +180 -31
- package/src/fx-instance.js +27 -6
- package/src/fx-model.js +175 -34
- package/src/fx-submission.js +26 -7
- package/src/getInScopeContext.js +1 -1
- package/src/ui/abstract-control.js +8 -3
- package/src/ui/fx-alert.js +16 -20
- package/src/ui/fx-container.js +9 -4
- package/src/ui/fx-control.js +76 -39
- package/src/ui/fx-inspector.js +44 -0
- package/src/ui/fx-items.js +117 -0
- package/src/ui/fx-output.js +42 -2
- package/src/ui/fx-repeat.js +47 -15
- package/src/ui/fx-repeatitem.js +11 -4
- package/src/xpath-evaluation.js +242 -80
- package/src/xpath-util.js +47 -12
package/src/ui/fx-control.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import XfAbstractControl from './abstract-control.js';
|
|
2
|
-
import { evaluateXPath, evaluateXPathToString } from '../xpath-evaluation.js';
|
|
2
|
+
import { evaluateXPath, evaluateXPathToString, evaluateXPathToNodes } from '../xpath-evaluation.js';
|
|
3
|
+
import getInScopeContext from '../getInScopeContext.js';
|
|
4
|
+
import { Fore } from '../fore.js';
|
|
3
5
|
|
|
4
6
|
const WIDGETCLASS = 'widget';
|
|
5
7
|
|
|
@@ -12,7 +14,7 @@ const WIDGETCLASS = 'widget';
|
|
|
12
14
|
* @customElement
|
|
13
15
|
* @demo demo/index.html
|
|
14
16
|
*/
|
|
15
|
-
class FxControl extends XfAbstractControl {
|
|
17
|
+
export default class FxControl extends XfAbstractControl {
|
|
16
18
|
constructor() {
|
|
17
19
|
super();
|
|
18
20
|
this.inited = false;
|
|
@@ -54,7 +56,7 @@ class FxControl extends XfAbstractControl {
|
|
|
54
56
|
`;
|
|
55
57
|
|
|
56
58
|
this.widget = this.getWidget();
|
|
57
|
-
console.log('widget ', this.widget);
|
|
59
|
+
// console.log('widget ', this.widget);
|
|
58
60
|
|
|
59
61
|
// ### convenience marker event
|
|
60
62
|
if (this.updateEvent === 'enter') {
|
|
@@ -64,15 +66,17 @@ class FxControl extends XfAbstractControl {
|
|
|
64
66
|
event.preventDefault();
|
|
65
67
|
this.setValue(this.widget[this.valueProp]);
|
|
66
68
|
}
|
|
67
|
-
// console.log('enter handler ', this.updateEvent);
|
|
68
|
-
// this.setValue(this.widget[this.valueProp]);
|
|
69
69
|
});
|
|
70
70
|
this.updateEvent = 'blur'; // needs to be registered too
|
|
71
71
|
}
|
|
72
72
|
this.widget.addEventListener(this.updateEvent, () => {
|
|
73
|
-
console.log('eventlistener ', this.updateEvent);
|
|
73
|
+
// console.log('eventlistener ', this.updateEvent);
|
|
74
74
|
this.setValue(this.widget[this.valueProp]);
|
|
75
75
|
});
|
|
76
|
+
|
|
77
|
+
const slot = this.shadowRoot.querySelector('slot');
|
|
78
|
+
this.template = this.querySelector('template');
|
|
79
|
+
// console.log('template',this.template);
|
|
76
80
|
}
|
|
77
81
|
|
|
78
82
|
setValue(val) {
|
|
@@ -95,6 +99,7 @@ class FxControl extends XfAbstractControl {
|
|
|
95
99
|
* @returns {HTMLElement|*}
|
|
96
100
|
*/
|
|
97
101
|
getWidget() {
|
|
102
|
+
if (this.widget) return this.widget;
|
|
98
103
|
let widget = this.querySelector(`.${WIDGETCLASS}`);
|
|
99
104
|
if (!widget) {
|
|
100
105
|
widget = this.querySelector('input');
|
|
@@ -127,23 +132,34 @@ class FxControl extends XfAbstractControl {
|
|
|
127
132
|
}
|
|
128
133
|
}
|
|
129
134
|
|
|
130
|
-
|
|
135
|
+
getTemplate() {
|
|
136
|
+
return this.querySelector('template');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async refresh(force) {
|
|
140
|
+
// console.log('fx-control refresh', this);
|
|
131
141
|
super.refresh();
|
|
142
|
+
// console.log('refresh template', this.template);
|
|
132
143
|
// const {widget} = this;
|
|
133
144
|
|
|
134
145
|
// ### if we find a ref on control we have a 'select' control of some kind
|
|
135
|
-
|
|
136
|
-
if (
|
|
137
|
-
const tmpl = this.widget.querySelector('template');
|
|
138
|
-
|
|
146
|
+
const widget = this.getWidget();
|
|
147
|
+
if (widget.hasAttribute('ref')) {
|
|
139
148
|
// ### eval nodeset for list control
|
|
140
|
-
const ref =
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
149
|
+
const ref = widget.getAttribute('ref');
|
|
150
|
+
/*
|
|
151
|
+
actually a ref on a select or similar component should point to a different instance
|
|
152
|
+
with an absolute expr e.g. 'instance('theId')/...'
|
|
153
|
+
|
|
154
|
+
todo: even bail out if ref is not absolute?
|
|
155
|
+
*/
|
|
156
|
+
|
|
157
|
+
const inscope = getInScopeContext(this, ref);
|
|
158
|
+
// const nodeset = evaluateXPathToNodes(ref, inscope, this);
|
|
159
|
+
const nodeset = evaluateXPath(ref, inscope, this);
|
|
144
160
|
|
|
145
161
|
// ### clear items
|
|
146
|
-
const { children } =
|
|
162
|
+
const { children } = widget;
|
|
147
163
|
Array.from(children).forEach(child => {
|
|
148
164
|
if (child.nodeName.toLowerCase() !== 'template') {
|
|
149
165
|
child.parentNode.removeChild(child);
|
|
@@ -151,33 +167,54 @@ class FxControl extends XfAbstractControl {
|
|
|
151
167
|
});
|
|
152
168
|
|
|
153
169
|
// ### build the items
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
if (this.value === evaluated) {
|
|
170
|
-
newEntry.setAttribute('selected', 'selected');
|
|
170
|
+
if (this.template) {
|
|
171
|
+
if (nodeset.length) {
|
|
172
|
+
// console.log('nodeset', nodeset);
|
|
173
|
+
Array.from(nodeset).forEach(node => {
|
|
174
|
+
// console.log('#### node', node);
|
|
175
|
+
const newEntry = this.createEntry();
|
|
176
|
+
|
|
177
|
+
// ### initialize new entry
|
|
178
|
+
// ### set value
|
|
179
|
+
this.updateEntry(newEntry, node);
|
|
180
|
+
});
|
|
181
|
+
} else {
|
|
182
|
+
const newEntry = this.createEntry();
|
|
183
|
+
this.updateEntry(newEntry, nodeset);
|
|
171
184
|
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
Fore.refreshChildren(this, force);
|
|
188
|
+
}
|
|
172
189
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
const labelExpr = optionLabel.substring(1, optionLabel.length - 1);
|
|
190
|
+
updateEntry(newEntry, node) {
|
|
191
|
+
// ### >>> todo: needs rework this code is heavily assuming a select control with 'value' attribute - not generic at all yet.
|
|
176
192
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
193
|
+
if (this.widget.nodeName !== 'SELECT') return;
|
|
194
|
+
const valueAttribute = this._getValueAttribute(newEntry);
|
|
195
|
+
const valueExpr = valueAttribute.value;
|
|
196
|
+
const cutted = valueExpr.substring(1, valueExpr.length - 1);
|
|
197
|
+
const evaluated = evaluateXPath(cutted, node, newEntry);
|
|
198
|
+
valueAttribute.value = evaluated;
|
|
199
|
+
|
|
200
|
+
if (this.value === evaluated) {
|
|
201
|
+
newEntry.setAttribute('selected', 'selected');
|
|
180
202
|
}
|
|
203
|
+
|
|
204
|
+
// ### set label
|
|
205
|
+
const optionLabel = newEntry.textContent;
|
|
206
|
+
const labelExpr = optionLabel.substring(1, optionLabel.length - 1);
|
|
207
|
+
|
|
208
|
+
const label = evaluateXPathToString(labelExpr, node, newEntry);
|
|
209
|
+
newEntry.textContent = label;
|
|
210
|
+
// ### <<< needs rework
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
createEntry() {
|
|
214
|
+
const content = this.template.content.firstElementChild.cloneNode(true);
|
|
215
|
+
const newEntry = document.importNode(content, true);
|
|
216
|
+
this.template.parentNode.appendChild(newEntry);
|
|
217
|
+
return newEntry;
|
|
181
218
|
}
|
|
182
219
|
|
|
183
220
|
// eslint-disable-next-line class-methods-use-this
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lists out all live instances in html 'details' and 'summary' elements.
|
|
3
|
+
*/
|
|
4
|
+
export class FxInspector extends HTMLElement {
|
|
5
|
+
connectedCallback() {
|
|
6
|
+
const style = `
|
|
7
|
+
:host {
|
|
8
|
+
display: block;
|
|
9
|
+
width:100%;
|
|
10
|
+
background:var(--inspector-bg);
|
|
11
|
+
}
|
|
12
|
+
pre{
|
|
13
|
+
background:var(--inspector-pre-bg);
|
|
14
|
+
color:var(--inspector-color);
|
|
15
|
+
max-height:var(--inspector-instance-height,300px);
|
|
16
|
+
overflow:scroll;
|
|
17
|
+
}
|
|
18
|
+
`;
|
|
19
|
+
|
|
20
|
+
const instances = Array.from(document.querySelectorAll('fx-instance'));
|
|
21
|
+
this.innerHTML = `
|
|
22
|
+
<style>
|
|
23
|
+
${style}
|
|
24
|
+
</style>
|
|
25
|
+
<slot></slot>
|
|
26
|
+
${instances
|
|
27
|
+
.map(
|
|
28
|
+
(instance, index) => `
|
|
29
|
+
<details ${index === 0 ? `open` : ''}>
|
|
30
|
+
<summary>${instance.id}</summary>
|
|
31
|
+
<pre>{log('${instance.id}')}</pre>
|
|
32
|
+
</details>
|
|
33
|
+
`,
|
|
34
|
+
)
|
|
35
|
+
.join('')}
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
this.addEventListener('slotchange', e => {
|
|
39
|
+
console.log('slotchange ', e);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
customElements.define('fx-inspector', FxInspector);
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import XfAbstractControl from './abstract-control.js';
|
|
2
|
+
import { evaluateXPath, evaluateXPathToString, evaluateXPathToNodes } from '../xpath-evaluation.js';
|
|
3
|
+
import getInScopeContext from '../getInScopeContext.js';
|
|
4
|
+
import FxControl from './fx-control.js';
|
|
5
|
+
import { Fore } from '../fore.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* FxItems provices a templated list over its bound nodes. It is not standalone but expects to be used
|
|
9
|
+
* within an fx-control element.
|
|
10
|
+
*
|
|
11
|
+
*
|
|
12
|
+
*
|
|
13
|
+
* @demo demo/selects3.html
|
|
14
|
+
*/
|
|
15
|
+
export class FxItems extends FxControl {
|
|
16
|
+
static get properties() {
|
|
17
|
+
return {
|
|
18
|
+
...super.properties,
|
|
19
|
+
valueAttr: {
|
|
20
|
+
type: String,
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
constructor() {
|
|
26
|
+
super();
|
|
27
|
+
this.valueAttr = this.hasAttribute('value') ? this.getAttribute('value') : null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
connectedCallback() {
|
|
31
|
+
super.connectedCallback();
|
|
32
|
+
|
|
33
|
+
this.addEventListener('click', e => {
|
|
34
|
+
const items = this.querySelectorAll('[value]');
|
|
35
|
+
|
|
36
|
+
let target;
|
|
37
|
+
if (e.target.nodeName === 'LABEL') {
|
|
38
|
+
target = document.getElementById(e.target.getAttribute('for'));
|
|
39
|
+
target.checked = !target.checked;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let val = '';
|
|
43
|
+
Array.from(items).forEach(item => {
|
|
44
|
+
if (item.checked) {
|
|
45
|
+
val += ` ${item.getAttribute('value')}`;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
this.setAttribute('value', val.trim());
|
|
49
|
+
|
|
50
|
+
// ### check for parent control
|
|
51
|
+
const parentBind = this.parentNode.closest('[ref]');
|
|
52
|
+
if (!parentBind) return;
|
|
53
|
+
const modelitem = parentBind.getModelItem();
|
|
54
|
+
const setval = this.shadowRoot.getElementById('setvalue');
|
|
55
|
+
setval.setValue(modelitem, val.trim());
|
|
56
|
+
setval.actionPerformed();
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
getWidget() {
|
|
61
|
+
return this;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async updateWidgetValue() {
|
|
65
|
+
// console.log('setting items value');
|
|
66
|
+
|
|
67
|
+
const parentBind = this.parentNode.closest('[ref]');
|
|
68
|
+
if (parentBind) {
|
|
69
|
+
this.value = parentBind.value;
|
|
70
|
+
}
|
|
71
|
+
this.setAttribute('value', this.value);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Updates an entry by setting the label and the value.
|
|
76
|
+
*
|
|
77
|
+
* Will connect label and control with `for` attribute with generated id.
|
|
78
|
+
*
|
|
79
|
+
* attention: limitations here: assumes that there's an `label` element plus an element with an `value`
|
|
80
|
+
* attribute which it will update.
|
|
81
|
+
*
|
|
82
|
+
*
|
|
83
|
+
*
|
|
84
|
+
* @param newEntry
|
|
85
|
+
* @param node
|
|
86
|
+
*/
|
|
87
|
+
updateEntry(newEntry, node) {
|
|
88
|
+
// console.log('fx-items updateEntry', this.value);
|
|
89
|
+
// super.updateEntry(newEntry,node);
|
|
90
|
+
|
|
91
|
+
// ### danger zone - highly specific - assumes knowledge of the template structure ###
|
|
92
|
+
// ### danger zone - highly specific - assumes knowledge of the template structure ###
|
|
93
|
+
// ### danger zone - highly specific - assumes knowledge of the template structure ###
|
|
94
|
+
|
|
95
|
+
const label = newEntry.querySelector('label');
|
|
96
|
+
label.textContent = node.textContent;
|
|
97
|
+
|
|
98
|
+
const id = Fore.createUUID();
|
|
99
|
+
label.setAttribute('for', id);
|
|
100
|
+
|
|
101
|
+
// getting element which has 'value' attr
|
|
102
|
+
const input = newEntry.querySelector('[value]');
|
|
103
|
+
// getting expr
|
|
104
|
+
const expr = input.value;
|
|
105
|
+
const cutted = expr.substring(1, expr.length - 1);
|
|
106
|
+
const evaluated = evaluateXPath(cutted, node, newEntry);
|
|
107
|
+
|
|
108
|
+
const valAttr = this.getAttribute('value');
|
|
109
|
+
input.value = evaluated;
|
|
110
|
+
input.setAttribute('id', id);
|
|
111
|
+
if (valAttr.indexOf(input.value) !== -1) {
|
|
112
|
+
input.checked = true;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
customElements.define('fx-items', FxItems);
|
package/src/ui/fx-output.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import XfAbstractControl from './abstract-control.js';
|
|
2
2
|
import { evaluateXPath, evaluateXPathToString } from '../xpath-evaluation.js';
|
|
3
3
|
import getInScopeContext from '../getInScopeContext.js';
|
|
4
|
+
// import {markdown} from '../drawdown.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* todo: review placing of value. should probably work with value attribute and not allow slotted content.
|
|
@@ -32,12 +33,21 @@ export class FxOutput extends XfAbstractControl {
|
|
|
32
33
|
.label{
|
|
33
34
|
display: inline-block;
|
|
34
35
|
}
|
|
36
|
+
table,tbody{
|
|
37
|
+
width:100%;
|
|
38
|
+
}
|
|
39
|
+
th{
|
|
40
|
+
text-align:left;
|
|
41
|
+
}
|
|
42
|
+
td{
|
|
43
|
+
padding-right:1rem;
|
|
44
|
+
}
|
|
35
45
|
`;
|
|
36
46
|
|
|
37
47
|
const outputHtml = `
|
|
38
48
|
<slot name="label"></slot>
|
|
39
49
|
<span id="value">
|
|
40
|
-
<slot></slot>
|
|
50
|
+
<slot id="main"></slot>
|
|
41
51
|
</span>
|
|
42
52
|
`;
|
|
43
53
|
|
|
@@ -50,6 +60,7 @@ export class FxOutput extends XfAbstractControl {
|
|
|
50
60
|
// this.widget = this.shadowRoot.querySelector('#widget');
|
|
51
61
|
// this.widget = this.getWidget();
|
|
52
62
|
// console.log('widget ', this.widget);
|
|
63
|
+
this.mediatype = this.hasAttribute('mediatype') ? this.getAttribute('mediatype') : null;
|
|
53
64
|
|
|
54
65
|
this.addEventListener('slotchange', e => {
|
|
55
66
|
console.log('slotchange ', e);
|
|
@@ -93,12 +104,41 @@ export class FxOutput extends XfAbstractControl {
|
|
|
93
104
|
}
|
|
94
105
|
|
|
95
106
|
async updateWidgetValue() {
|
|
107
|
+
console.log('updateWidgetValue');
|
|
96
108
|
const valueWrapper = this.shadowRoot.getElementById('value');
|
|
97
109
|
|
|
98
|
-
if (this.
|
|
110
|
+
if (this.mediatype === 'markdown') {
|
|
111
|
+
const md = markdown(this.nodeset);
|
|
112
|
+
this.innerHtml = md;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (this.mediatype === 'html') {
|
|
99
116
|
if (this.modelItem.node) {
|
|
117
|
+
/*
|
|
100
118
|
valueWrapper.innerHTML = this.modelItem.node.outerHTML;
|
|
101
119
|
return;
|
|
120
|
+
*/
|
|
121
|
+
|
|
122
|
+
const node = this.modelItem.node;
|
|
123
|
+
|
|
124
|
+
if (node.nodeType) {
|
|
125
|
+
// const mainSlot = this.shadowRoot.querySelector('#main');
|
|
126
|
+
// valueWrapper.appendChild(node);
|
|
127
|
+
|
|
128
|
+
// todo: checking if ownerDocument of node and ownerDocument of this are the same - otherwise import first
|
|
129
|
+
// const imported = this.ownerDocument.importNode(node,true);
|
|
130
|
+
// const clone = node.cloneNode(true);
|
|
131
|
+
|
|
132
|
+
this.appendChild(node);
|
|
133
|
+
// this.innerHtml = node;
|
|
134
|
+
// this.innerHTML = node;
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
Object.entries(node).map(obj => {
|
|
138
|
+
// valueWrapper.appendChild(obj[1]);
|
|
139
|
+
this.appendChild(obj[1]);
|
|
140
|
+
});
|
|
141
|
+
return;
|
|
102
142
|
}
|
|
103
143
|
|
|
104
144
|
// this.innerHTML = this.value.outerHTML;
|
package/src/ui/fx-repeat.js
CHANGED
|
@@ -88,6 +88,7 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
connectedCallback() {
|
|
91
|
+
// this.display = window.getComputedStyle(this, null).getPropertyValue("display");
|
|
91
92
|
this.ref = this.getAttribute('ref');
|
|
92
93
|
// console.log('### fx-repeat connected ', this.id);
|
|
93
94
|
this.addEventListener('item-changed', e => {
|
|
@@ -114,16 +115,26 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
|
|
|
114
115
|
console.log('insert catched', nodes, this.index);
|
|
115
116
|
});
|
|
116
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
|
+
|
|
117
126
|
const style = `
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
+
`;
|
|
127
138
|
const html = `
|
|
128
139
|
<slot name="header"></slot>
|
|
129
140
|
<slot></slot>
|
|
@@ -160,6 +171,14 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
|
|
|
160
171
|
const inscope = getInScopeContext(this, this.ref);
|
|
161
172
|
// console.log('##### inscope ', inscope);
|
|
162
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
|
+
}
|
|
163
182
|
|
|
164
183
|
const seq = evaluateXPath(this.ref, inscope, this.getOwnerForm());
|
|
165
184
|
// const seq = evaluateXPathToNodes(this.ref, inscope, this.getOwnerForm());
|
|
@@ -188,12 +207,13 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
|
|
|
188
207
|
throw new Error(`Unexpected result of repeat nodeset: ${seq}`);
|
|
189
208
|
}
|
|
190
209
|
|
|
191
|
-
async refresh() {
|
|
192
|
-
console.group('fx-repeat.refresh on', this.id);
|
|
210
|
+
async refresh(force) {
|
|
211
|
+
// console.group('fx-repeat.refresh on', this.id);
|
|
193
212
|
|
|
194
213
|
if (!this.inited) this.init();
|
|
214
|
+
console.time('repeat-refresh', this);
|
|
195
215
|
this._evalNodeset();
|
|
196
|
-
console.log('repeat refresh nodeset ', this.nodeset);
|
|
216
|
+
// console.log('repeat refresh nodeset ', this.nodeset);
|
|
197
217
|
// console.log('repeatCount', this.repeatCount);
|
|
198
218
|
|
|
199
219
|
const repeatItems = this.querySelectorAll(':scope > fx-repeatitem');
|
|
@@ -213,7 +233,7 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
|
|
|
213
233
|
// remove repeatitem
|
|
214
234
|
const itemToRemove = repeatItems[position - 1];
|
|
215
235
|
itemToRemove.parentNode.removeChild(itemToRemove);
|
|
216
|
-
|
|
236
|
+
this.getOwnerForm().unRegisterLazyElement(itemToRemove);
|
|
217
237
|
// this._fadeOut(itemToRemove);
|
|
218
238
|
// Fore.fadeOutElement(itemToRemove)
|
|
219
239
|
}
|
|
@@ -236,13 +256,25 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
|
|
|
236
256
|
// ### update nodeset of repeatitems
|
|
237
257
|
for (let position = 0; position < repeatItemCount; position += 1) {
|
|
238
258
|
const item = repeatItems[position];
|
|
259
|
+
this.getOwnerForm().registerLazyElement(item);
|
|
260
|
+
|
|
239
261
|
if (item.nodeset !== this.nodeset[position]) {
|
|
240
262
|
item.nodeset = this.nodeset[position];
|
|
241
263
|
}
|
|
242
264
|
}
|
|
243
265
|
|
|
244
|
-
Fore.refreshChildren(
|
|
266
|
+
// Fore.refreshChildren(clone,true);
|
|
267
|
+
const fore = this.getOwnerForm();
|
|
268
|
+
if (!fore.lazyRefresh || force) {
|
|
269
|
+
Fore.refreshChildren(this, force);
|
|
270
|
+
}
|
|
271
|
+
// this.style.display = 'block';
|
|
272
|
+
// this.style.display = this.display;
|
|
245
273
|
this.setIndex(this.index);
|
|
274
|
+
console.timeEnd('repeat-refresh');
|
|
275
|
+
|
|
276
|
+
// this.replaceWith(clone);
|
|
277
|
+
|
|
246
278
|
// this.repeatCount = contextSize;
|
|
247
279
|
// console.log('repeatCount', this.repeatCount);
|
|
248
280
|
console.groupEnd();
|
|
@@ -289,7 +321,7 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
|
|
|
289
321
|
// todo: this is still weak - should handle that better maybe by an explicit slot?
|
|
290
322
|
// this.template = this.firstElementChild;
|
|
291
323
|
this.template = this.querySelector('template');
|
|
292
|
-
console.log('### init template for repeat ', this.id, this.template);
|
|
324
|
+
// console.log('### init template for repeat ', this.id, this.template);
|
|
293
325
|
|
|
294
326
|
if (this.template === null) {
|
|
295
327
|
// console.error('### no template found for this repeat:', this.id);
|
package/src/ui/fx-repeatitem.js
CHANGED
|
@@ -22,7 +22,8 @@ export class FxRepeatitem extends foreElementMixin(HTMLElement) {
|
|
|
22
22
|
this.inited = false;
|
|
23
23
|
|
|
24
24
|
this.addEventListener('click', this._dispatchIndexChange);
|
|
25
|
-
this.addEventListener('focusin', this._handleFocus);
|
|
25
|
+
// this.addEventListener('focusin', this._handleFocus);
|
|
26
|
+
this.addEventListener('focusin', this._dispatchIndexChange);
|
|
26
27
|
|
|
27
28
|
this.attachShadow({ mode: 'open', delegatesFocus: true });
|
|
28
29
|
}
|
|
@@ -30,7 +31,12 @@ export class FxRepeatitem extends foreElementMixin(HTMLElement) {
|
|
|
30
31
|
_handleFocus() {
|
|
31
32
|
this.parentNode.setIndex(this.index);
|
|
32
33
|
// TODO: do this somewhere else, somewhere more central
|
|
33
|
-
|
|
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();
|
|
34
40
|
}
|
|
35
41
|
|
|
36
42
|
_dispatchIndexChange() {
|
|
@@ -52,6 +58,7 @@ export class FxRepeatitem extends foreElementMixin(HTMLElement) {
|
|
|
52
58
|
this.shadowRoot.innerHTML = `
|
|
53
59
|
${html}
|
|
54
60
|
`;
|
|
61
|
+
this.getOwnerForm().registerLazyElement(this);
|
|
55
62
|
}
|
|
56
63
|
|
|
57
64
|
disconnectedCallback() {
|
|
@@ -72,7 +79,7 @@ export class FxRepeatitem extends foreElementMixin(HTMLElement) {
|
|
|
72
79
|
return this.getModelItem()[this.index];
|
|
73
80
|
}
|
|
74
81
|
|
|
75
|
-
refresh() {
|
|
82
|
+
refresh(force) {
|
|
76
83
|
// console.log('refresh repeatitem: ',this.nodeset);
|
|
77
84
|
// console.log('refresh repeatitem nodeset: ',this.nodeset);
|
|
78
85
|
this.modelItem = this.getModel().getModelItem(this.nodeset);
|
|
@@ -94,7 +101,7 @@ export class FxRepeatitem extends foreElementMixin(HTMLElement) {
|
|
|
94
101
|
}
|
|
95
102
|
*/
|
|
96
103
|
|
|
97
|
-
Fore.refreshChildren(this);
|
|
104
|
+
Fore.refreshChildren(this, force);
|
|
98
105
|
}
|
|
99
106
|
}
|
|
100
107
|
|