@jinntec/fore 2.7.0 → 2.7.1
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/dist/fore-dev.js +4 -4
- package/dist/fore.js +4 -4
- package/package.json +1 -1
- package/src/actions/fx-load.js +2 -2
- package/src/ui/fx-repeat.updated.js +0 -821
package/dist/fore-dev.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Version: 2.7.
|
|
1
|
+
/* Version: 2.7.1 - December 4, 2025 12:22:40 */
|
|
2
2
|
function t$2(t, s, r, i) {
|
|
3
3
|
const n = {
|
|
4
4
|
op: s,
|
|
@@ -23815,7 +23815,7 @@ class FxFore extends HTMLElement {
|
|
|
23815
23815
|
this._createRepeatsFromAttributes();
|
|
23816
23816
|
this.inited = true;
|
|
23817
23817
|
};
|
|
23818
|
-
this.version = 'Version: 2.7.
|
|
23818
|
+
this.version = 'Version: 2.7.1 - built on December 4, 2025 12:22:40';
|
|
23819
23819
|
|
|
23820
23820
|
/**
|
|
23821
23821
|
* @type {import('./fx-model.js').FxModel}
|
|
@@ -35422,10 +35422,10 @@ class FxLoad extends AbstractAction {
|
|
|
35422
35422
|
}
|
|
35423
35423
|
const resolvedUrl = this.evaluateAttributeTemplateExpression(this.url, this);
|
|
35424
35424
|
if (this.attachTo === '_blank') {
|
|
35425
|
-
window.open(
|
|
35425
|
+
window.open(resolvedUrl);
|
|
35426
35426
|
}
|
|
35427
35427
|
if (this.attachTo === '_self') {
|
|
35428
|
-
window.location.href =
|
|
35428
|
+
window.location.href = resolvedUrl;
|
|
35429
35429
|
}
|
|
35430
35430
|
try {
|
|
35431
35431
|
const response = await fetch(resolvedUrl, {
|
package/dist/fore.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Version: 2.7.
|
|
1
|
+
/* Version: 2.7.1 - December 4, 2025 12:22:39 */
|
|
2
2
|
function t$2(t, s, r, i) {
|
|
3
3
|
const n = {
|
|
4
4
|
op: s,
|
|
@@ -23758,7 +23758,7 @@ class FxFore extends HTMLElement {
|
|
|
23758
23758
|
this._createRepeatsFromAttributes();
|
|
23759
23759
|
this.inited = true;
|
|
23760
23760
|
};
|
|
23761
|
-
this.version = 'Version: 2.7.
|
|
23761
|
+
this.version = 'Version: 2.7.1 - built on December 4, 2025 12:22:39';
|
|
23762
23762
|
|
|
23763
23763
|
/**
|
|
23764
23764
|
* @type {import('./fx-model.js').FxModel}
|
|
@@ -35262,10 +35262,10 @@ class FxLoad extends AbstractAction {
|
|
|
35262
35262
|
}
|
|
35263
35263
|
const resolvedUrl = this.evaluateAttributeTemplateExpression(this.url, this);
|
|
35264
35264
|
if (this.attachTo === '_blank') {
|
|
35265
|
-
window.open(
|
|
35265
|
+
window.open(resolvedUrl);
|
|
35266
35266
|
}
|
|
35267
35267
|
if (this.attachTo === '_self') {
|
|
35268
|
-
window.location.href =
|
|
35268
|
+
window.location.href = resolvedUrl;
|
|
35269
35269
|
}
|
|
35270
35270
|
try {
|
|
35271
35271
|
const response = await fetch(resolvedUrl, {
|
package/package.json
CHANGED
package/src/actions/fx-load.js
CHANGED
|
@@ -131,11 +131,11 @@ class FxLoad extends AbstractAction {
|
|
|
131
131
|
}
|
|
132
132
|
const resolvedUrl = this.evaluateAttributeTemplateExpression(this.url, this);
|
|
133
133
|
if (this.attachTo === '_blank') {
|
|
134
|
-
window.open(
|
|
134
|
+
window.open(resolvedUrl);
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
if (this.attachTo === '_self') {
|
|
138
|
-
window.location.href =
|
|
138
|
+
window.location.href = resolvedUrl;
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
try {
|
|
@@ -1,821 +0,0 @@
|
|
|
1
|
-
import './fx-repeatitem.js';
|
|
2
|
-
|
|
3
|
-
import { Fore } from '../fore.js';
|
|
4
|
-
import ForeElementMixin from '../ForeElementMixin.js';
|
|
5
|
-
import { evaluateXPath } from '../xpath-evaluation.js';
|
|
6
|
-
import getInScopeContext from '../getInScopeContext.js';
|
|
7
|
-
import { XPathUtil } from '../xpath-util.js';
|
|
8
|
-
import { withDraggability } from '../withDraggability.js';
|
|
9
|
-
import { UIElement } from './UIElement.js';
|
|
10
|
-
import { getPath } from '../xpath-path.js';
|
|
11
|
-
import { FxModel } from '../fx-model.js';
|
|
12
|
-
import { FxBind } from '../fx-bind.js';
|
|
13
|
-
|
|
14
|
-
// import {DependencyNotifyingDomFacade} from '../DependencyNotifyingDomFacade';
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* `fx-repeat`
|
|
18
|
-
*
|
|
19
|
-
* Repeats its template for each node in its' bound nodeset.
|
|
20
|
-
*
|
|
21
|
-
* Template is a standard HTML `<template>` element. Once instanciated the template
|
|
22
|
-
* is moved to the shadowDOM of the repeat for safe re-use.
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* @customElement
|
|
27
|
-
* @demo demo/todo.html
|
|
28
|
-
*
|
|
29
|
-
* todo: it should be seriously be considered to extend FxContainer instead but needs refactoring first.
|
|
30
|
-
* @extends {ForeElementMixin}
|
|
31
|
-
*/
|
|
32
|
-
export class FxRepeat extends withDraggability(UIElement, false) {
|
|
33
|
-
static get properties() {
|
|
34
|
-
return {
|
|
35
|
-
...super.properties,
|
|
36
|
-
index: {
|
|
37
|
-
type: Number,
|
|
38
|
-
},
|
|
39
|
-
template: {
|
|
40
|
-
type: Object,
|
|
41
|
-
},
|
|
42
|
-
focusOnCreate: {
|
|
43
|
-
type: String,
|
|
44
|
-
},
|
|
45
|
-
initDone: {
|
|
46
|
-
type: Boolean,
|
|
47
|
-
},
|
|
48
|
-
repeatIndex: {
|
|
49
|
-
type: Number,
|
|
50
|
-
},
|
|
51
|
-
nodeset: {
|
|
52
|
-
type: Array,
|
|
53
|
-
},
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
constructor() {
|
|
58
|
-
super();
|
|
59
|
-
this.ref = '';
|
|
60
|
-
this.dataTemplate = [];
|
|
61
|
-
this.isDraggable = null;
|
|
62
|
-
this.dropTarget = null;
|
|
63
|
-
this.focusOnCreate = '';
|
|
64
|
-
this.initDone = false;
|
|
65
|
-
this.repeatIndex = 1;
|
|
66
|
-
this.nodeset = [];
|
|
67
|
-
this.inited = false;
|
|
68
|
-
this.index = 1;
|
|
69
|
-
this.repeatSize = 0;
|
|
70
|
-
this.attachShadow({ mode: 'open', delegatesFocus: true });
|
|
71
|
-
this.opNum = 0; // global number of operations
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
get repeatSize() {
|
|
75
|
-
return this.querySelectorAll(':scope > fx-repeatitem').length;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
set repeatSize(size) {
|
|
79
|
-
this.size = size;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
setIndex(index) {
|
|
83
|
-
// console.log('new repeat index ', index);
|
|
84
|
-
this.index = index;
|
|
85
|
-
const rItems = this.querySelectorAll(':scope > fx-repeatitem');
|
|
86
|
-
this.applyIndex(rItems[this.index - 1]);
|
|
87
|
-
|
|
88
|
-
// trying to do without
|
|
89
|
-
// this.getOwnerForm().refresh({ reason: 'index-function', elementLocalnamesWithChanges: [] });
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
applyIndex(repeatItem) {
|
|
93
|
-
this._removeIndexMarker();
|
|
94
|
-
if (repeatItem) {
|
|
95
|
-
repeatItem.setAttribute('repeat-index', '');
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
get index() {
|
|
100
|
-
return parseInt(this.getAttribute('index'), 10);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
set index(idx) {
|
|
104
|
-
this.setAttribute('index', idx);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
_getRef() {
|
|
108
|
-
return this.getAttribute('ref');
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
connectedCallback() {
|
|
112
|
-
super.connectedCallback();
|
|
113
|
-
// console.log('connectedCallback',this);
|
|
114
|
-
// this.display = window.getComputedStyle(this, null).getPropertyValue("display");
|
|
115
|
-
this.ref = this.getAttribute('ref');
|
|
116
|
-
this.dependencies.addXPath(this.ref);
|
|
117
|
-
// this.ref = this._getRef();
|
|
118
|
-
// console.log('### fx-repeat connected ', this.id);
|
|
119
|
-
this.addEventListener('item-changed', e => {
|
|
120
|
-
const { item } = e.detail;
|
|
121
|
-
this.setIndex(item.index);
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
// Listen for insertion events
|
|
125
|
-
this.getOwnerForm().addEventListener('insert', event => {
|
|
126
|
-
const { detail } = event;
|
|
127
|
-
console.log('insert catched', detail);
|
|
128
|
-
|
|
129
|
-
// Step 1: Refresh/re-evaluate the nodeset
|
|
130
|
-
const oldNodesetLength = this.nodeset.length;
|
|
131
|
-
this._evalNodeset();
|
|
132
|
-
const newNodesetLength = this.nodeset.length;
|
|
133
|
-
if (oldNodesetLength === newNodesetLength) {
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* @type {number}
|
|
139
|
-
*/
|
|
140
|
-
// const insertionIndex = detail.index;
|
|
141
|
-
/**
|
|
142
|
-
* The newly inserted node. TODO: handle multiple?
|
|
143
|
-
* @type {Node}
|
|
144
|
-
*/
|
|
145
|
-
const insertedNode = detail.insertedNodes;
|
|
146
|
-
const insertionIndex = this.nodeset.indexOf(insertedNode) + 1;
|
|
147
|
-
// Step 2: Get current repeat items and create a new item
|
|
148
|
-
/**
|
|
149
|
-
* @type {import('./fx-repeatitem.js').FxRepeatitem[]}
|
|
150
|
-
*/
|
|
151
|
-
const repeatItems = Array.from(
|
|
152
|
-
this.querySelectorAll(
|
|
153
|
-
':scope > fx-repeat-item, :scope > fx-repeatitem, :scope > .repeat-item',
|
|
154
|
-
),
|
|
155
|
-
);
|
|
156
|
-
|
|
157
|
-
// todo: search fx-bind elements with same nodeset as this repeat - if present update modelItem instead of creating one
|
|
158
|
-
const newRepeatItem = this._createNewRepeatItem();
|
|
159
|
-
|
|
160
|
-
// Step 3: Insert the new repeatItem at the correct position
|
|
161
|
-
const beforeNode = repeatItems[insertionIndex - 1] ?? null; // Null appends by default
|
|
162
|
-
this.insertBefore(newRepeatItem, beforeNode);
|
|
163
|
-
newRepeatItem.index = insertionIndex;
|
|
164
|
-
|
|
165
|
-
// Step 4: Assign the inserted nodeset to the new `repeatItem`
|
|
166
|
-
newRepeatItem.nodeset = detail.insertedNodes;
|
|
167
|
-
// this.setAttribute('index', detail.index);
|
|
168
|
-
// this.applyIndex(newRepeatItem);
|
|
169
|
-
|
|
170
|
-
// Update all the indices following here
|
|
171
|
-
for (let i = insertionIndex - 1; i < repeatItems.length; ++i) {
|
|
172
|
-
const sibling = repeatItems[i];
|
|
173
|
-
// TODO: handle the next ones
|
|
174
|
-
sibling.index += 1;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
this.setIndex(insertionIndex); // sets attribute + applies repeat-index + refresh
|
|
178
|
-
|
|
179
|
-
// Generate the parent `modelItem` for the new repeat item
|
|
180
|
-
this.opNum++;
|
|
181
|
-
const parentModelItem = FxBind.createModelItem(
|
|
182
|
-
this.ref,
|
|
183
|
-
detail.insertedNodes,
|
|
184
|
-
newRepeatItem,
|
|
185
|
-
this.opNum,
|
|
186
|
-
);
|
|
187
|
-
newRepeatItem.modelItem = parentModelItem;
|
|
188
|
-
|
|
189
|
-
this.getModel().registerModelItem(parentModelItem);
|
|
190
|
-
|
|
191
|
-
// Step 5: Create modelItems recursively for child elements
|
|
192
|
-
this._createModelItemsRecursively(newRepeatItem, parentModelItem);
|
|
193
|
-
|
|
194
|
-
// Step 6: Notify and refresh the UI
|
|
195
|
-
this.getOwnerForm().scanForNewTemplateExpressionsNextRefresh();
|
|
196
|
-
this.getOwnerForm().addToBatchedNotifications(newRepeatItem);
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
this.handleDelete = event => {
|
|
200
|
-
console.log('delete catched', event);
|
|
201
|
-
const { detail } = event;
|
|
202
|
-
if (!detail || !detail.deletedNodes) {
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// Remove corresponding repeat items for deleted nodes
|
|
207
|
-
detail.deletedNodes.forEach(node => {
|
|
208
|
-
this.handleDelete(node);
|
|
209
|
-
// this.removeRepeatItemForNode(node);
|
|
210
|
-
});
|
|
211
|
-
this.getOwnerForm().addToBatchedNotifications(this);
|
|
212
|
-
};
|
|
213
|
-
this.getOwnerForm().addEventListener('deleted', this.handleDelete);
|
|
214
|
-
|
|
215
|
-
// if (this.getOwnerForm().lazyRefresh) {
|
|
216
|
-
/**
|
|
217
|
-
* @type {MutationRecord[]}
|
|
218
|
-
*/
|
|
219
|
-
let bufferedMutationRecords = [];
|
|
220
|
-
let debouncedOnMutations = null;
|
|
221
|
-
this.mutationObserver = new MutationObserver(mutations => {
|
|
222
|
-
bufferedMutationRecords.push(...mutations);
|
|
223
|
-
if (!debouncedOnMutations) {
|
|
224
|
-
debouncedOnMutations = new Promise(() => {
|
|
225
|
-
debouncedOnMutations = null;
|
|
226
|
-
const records = bufferedMutationRecords;
|
|
227
|
-
bufferedMutationRecords = [];
|
|
228
|
-
let shouldRefresh = false;
|
|
229
|
-
for (const mutation of records) {
|
|
230
|
-
if (mutation.type === 'childList') {
|
|
231
|
-
const added = mutation.addedNodes[0];
|
|
232
|
-
if (added) {
|
|
233
|
-
const instance = XPathUtil.resolveInstance(this, this.ref);
|
|
234
|
-
const path = getPath(added, instance);
|
|
235
|
-
// this.handleInsert(added);
|
|
236
|
-
// // console.log('path mutated', path);
|
|
237
|
-
// this.dispatch('path-mutated',{'path':path,'nodeset':this.nodeset,'index': this.index});
|
|
238
|
-
// this.index = index;
|
|
239
|
-
// const prev = mutations[0].previousSibling.previousElementSibling;
|
|
240
|
-
// const index = prev.index();
|
|
241
|
-
// this.applyIndex(this.index -1);
|
|
242
|
-
|
|
243
|
-
Fore.dispatch(this, 'path-mutated', { path, index: this.index });
|
|
244
|
-
}
|
|
245
|
-
const deleted = mutation.removedNodes[0];
|
|
246
|
-
if (deleted) {
|
|
247
|
-
// this.handleDelete(deleted);
|
|
248
|
-
}
|
|
249
|
-
/*
|
|
250
|
-
if (!this.getOwnerForm().initialRun) {
|
|
251
|
-
shouldRefresh = true;
|
|
252
|
-
}
|
|
253
|
-
*/
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/*
|
|
258
|
-
if (shouldRefresh) {
|
|
259
|
-
this.refresh();
|
|
260
|
-
}
|
|
261
|
-
*/
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
// console.log('mutations', mutations);
|
|
267
|
-
// }
|
|
268
|
-
this.getOwnerForm().registerLazyElement(this);
|
|
269
|
-
|
|
270
|
-
const style = `
|
|
271
|
-
:host{
|
|
272
|
-
}
|
|
273
|
-
.fade-out-bottom {
|
|
274
|
-
-webkit-animation: fade-out-bottom 0.7s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
|
|
275
|
-
animation: fade-out-bottom 0.7s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
|
|
276
|
-
}
|
|
277
|
-
.fade-out-bottom {
|
|
278
|
-
-webkit-animation: fade-out-bottom 0.7s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
|
|
279
|
-
animation: fade-out-bottom 0.7s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
|
|
280
|
-
}
|
|
281
|
-
`;
|
|
282
|
-
const html = `
|
|
283
|
-
<slot name="header"></slot>
|
|
284
|
-
<slot></slot>
|
|
285
|
-
<slot name="footer"></slot>
|
|
286
|
-
`;
|
|
287
|
-
this.shadowRoot.innerHTML = `
|
|
288
|
-
<style>
|
|
289
|
-
${style}
|
|
290
|
-
</style>
|
|
291
|
-
${html}
|
|
292
|
-
`;
|
|
293
|
-
|
|
294
|
-
// this.init();
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
= event;
|
|
298
|
-
if (!detail || !detail.deletedNodes) {
|
|
299
|
-
return;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// Remove corresponding repeat items for deleted nodes
|
|
303
|
-
detail.deletedNodes.forEach(node => {
|
|
304
|
-
this.handleDelete(node);
|
|
305
|
-
// this.removeRepeatItemForNode(node);
|
|
306
|
-
});
|
|
307
|
-
this.getOwnerForm().addToBatchedNotifications(this);
|
|
308
|
-
};
|
|
309
|
-
this.getOwnerForm().addEventListener('deleted', this.handleDelete);
|
|
310
|
-
|
|
311
|
-
// if (this.getOwnerForm().lazyRefresh) {
|
|
312
|
-
/**
|
|
313
|
-
* @type {MutationRecord[]}
|
|
314
|
-
*/
|
|
315
|
-
let bufferedMutationRecords = [];
|
|
316
|
-
let debouncedOnMutations = null;
|
|
317
|
-
this.mutationObserver = new MutationObserver(mutations => {
|
|
318
|
-
bufferedMutationRecords.push(...mutations);
|
|
319
|
-
if (!debouncedOnMutations) {
|
|
320
|
-
debouncedOnMutations = new Promise(() => {
|
|
321
|
-
debouncedOnMutations = null;
|
|
322
|
-
const records = bufferedMutationRecords;
|
|
323
|
-
bufferedMutationRecords = [];
|
|
324
|
-
let shouldRefresh = false;
|
|
325
|
-
for (const mutation of records) {
|
|
326
|
-
if (mutation.type === 'childList') {
|
|
327
|
-
const added = mutation.addedNodes[0];
|
|
328
|
-
if (added) {
|
|
329
|
-
const instance = XPathUtil.resolveInstance(this, this.ref);
|
|
330
|
-
const path = getPath(added, instance);
|
|
331
|
-
// this.handleInsert(added);
|
|
332
|
-
// // console.log('path mutated', path);
|
|
333
|
-
// this.dispatch('path-mutated',{'path':path,'nodeset':this.nodeset,'index': this.index});
|
|
334
|
-
// this.index = index;
|
|
335
|
-
// const prev = mutations[0].previousSibling.previousElementSibling;
|
|
336
|
-
// const index = prev.index();
|
|
337
|
-
// this.applyIndex(this.index -1);
|
|
338
|
-
|
|
339
|
-
Fore.dispatch(this, 'path-mutated', { path, index: this.index });
|
|
340
|
-
}
|
|
341
|
-
const deleted = mutation.removedNodes[0];
|
|
342
|
-
if (deleted) {
|
|
343
|
-
// this.handleDelete(deleted);
|
|
344
|
-
}
|
|
345
|
-
/*
|
|
346
|
-
if (!this.getOwnerForm().initialRun) {
|
|
347
|
-
shouldRefresh = true;
|
|
348
|
-
}
|
|
349
|
-
*/
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
/*
|
|
354
|
-
if (shouldRefresh) {
|
|
355
|
-
this.refresh();
|
|
356
|
-
}
|
|
357
|
-
*/
|
|
358
|
-
});
|
|
359
|
-
}
|
|
360
|
-
});
|
|
361
|
-
|
|
362
|
-
// console.log('mutations', mutations);
|
|
363
|
-
// }
|
|
364
|
-
this.getOwnerForm().registerLazyElement(this);
|
|
365
|
-
|
|
366
|
-
const style = `
|
|
367
|
-
:host{
|
|
368
|
-
}
|
|
369
|
-
.fade-out-bottom {
|
|
370
|
-
-webkit-animation: fade-out-bottom 0.7s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
|
|
371
|
-
animation: fade-out-bottom 0.7s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
|
|
372
|
-
}
|
|
373
|
-
.fade-out-bottom {
|
|
374
|
-
-webkit-animation: fade-out-bottom 0.7s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
|
|
375
|
-
animation: fade-out-bottom 0.7s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
|
|
376
|
-
}
|
|
377
|
-
`;
|
|
378
|
-
const html = `
|
|
379
|
-
<slot name="header"></slot>
|
|
380
|
-
<slot></slot>
|
|
381
|
-
<slot name="footer"></slot>
|
|
382
|
-
`;
|
|
383
|
-
this.shadowRoot.innerHTML = `
|
|
384
|
-
<style>
|
|
385
|
-
${style}
|
|
386
|
-
</style>
|
|
387
|
-
${html}
|
|
388
|
-
`;
|
|
389
|
-
|
|
390
|
-
// this.init();
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
_createModelItemsRecursively(parentNode, parentModelItem) {
|
|
394
|
-
const parentWithDewey = parentModelItem?.path || null; // e.g., $default/task[1]_1
|
|
395
|
-
const parentBase = parentWithDewey ? parentWithDewey.replace(/_\d+$/, '') : null; // $default/task[1]
|
|
396
|
-
|
|
397
|
-
Array.from(parentNode.children).forEach(child => {
|
|
398
|
-
let nextParentMI = parentModelItem; // default: keep walking with current parent
|
|
399
|
-
|
|
400
|
-
if (child.hasAttribute('ref')) {
|
|
401
|
-
const ref = child.getAttribute('ref').trim();
|
|
402
|
-
|
|
403
|
-
// todo: dot ref handling ????
|
|
404
|
-
// if (ref !== '.') {
|
|
405
|
-
// Evaluate relative to the *node* of the parent repeat item
|
|
406
|
-
let nodeset = evaluateXPath(ref, parentModelItem.node, this);
|
|
407
|
-
if (Array.isArray(nodeset)) nodeset = nodeset[0];
|
|
408
|
-
if (!nodeset) {
|
|
409
|
-
// This is not relevant, or an empty repeat
|
|
410
|
-
return;
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
let modelItem = this.getModel().getModelItem(nodeset);
|
|
414
|
-
if (!modelItem) {
|
|
415
|
-
// Already known. Ignore.
|
|
416
|
-
// Create child modelItem with NO opNum — children must not get their own _n
|
|
417
|
-
modelItem = FxBind.createModelItem(ref, nodeset, child, null);
|
|
418
|
-
modelItem.parentModelItem = parentModelItem;
|
|
419
|
-
|
|
420
|
-
// Register
|
|
421
|
-
this.getModel().registerModelItem(modelItem);
|
|
422
|
-
|
|
423
|
-
// Prefix-replace once: $default/task[1] -> $default/task[1]_1
|
|
424
|
-
if (
|
|
425
|
-
parentWithDewey &&
|
|
426
|
-
parentBase &&
|
|
427
|
-
typeof modelItem.path === 'string' &&
|
|
428
|
-
modelItem.path.startsWith(parentBase) && // child under parent
|
|
429
|
-
!modelItem.path.startsWith(parentWithDewey) // not already suffixed
|
|
430
|
-
) {
|
|
431
|
-
modelItem.path = parentWithDewey + modelItem.path.slice(parentBase.length);
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
// const nodeset = evaluateXPath(ref, parentModelItem.node, this);
|
|
435
|
-
child.nodeset = nodeset;
|
|
436
|
-
if (child.attachObserver) {
|
|
437
|
-
child.attachObserver();
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
// Recurse down with whichever parent we decided on
|
|
442
|
-
this._createModelItemsRecursively(child, nextParentMI);
|
|
443
|
-
});
|
|
444
|
-
}
|
|
445
|
-
*/
|
|
446
|
-
|
|
447
|
-
/**
|
|
448
|
-
* Removes the repeat item corresponding to a deleted node.
|
|
449
|
-
* Cleans up its observers and notifies the parent form.
|
|
450
|
-
* @param {Node} node - The deleted node
|
|
451
|
-
*/
|
|
452
|
-
removeRepeatItemForNode(node) {
|
|
453
|
-
const index = this.nodeset.indexOf(node);
|
|
454
|
-
if (index === -1) return;
|
|
455
|
-
|
|
456
|
-
const repeatItem = this.querySelector(`fx-repeatitem:nth-of-type(${index + 1})`);
|
|
457
|
-
if (repeatItem) {
|
|
458
|
-
this.removeChild(repeatItem);
|
|
459
|
-
this.getOwnerForm().addToBatchedNotifications(this);
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
// Remove the node from the nodeset
|
|
463
|
-
this.nodeset.splice(index, 1);
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
/* old version with mutation observers
|
|
467
|
-
async handleInsert(added) {
|
|
468
|
-
console.log('handleInsert', added);
|
|
469
|
-
this._evalNodeset();
|
|
470
|
-
|
|
471
|
-
const items = Array.from(
|
|
472
|
-
this.querySelectorAll(':scope > fx-repeatitem, :scope > .repeat-item'),
|
|
473
|
-
);
|
|
474
|
-
|
|
475
|
-
// search fx-bind elements with same nodeset as this repeat - if present update modelItem instead of creating one
|
|
476
|
-
const newRepeatItem = this._createNewRepeatItem();
|
|
477
|
-
// insert so the new item becomes position `pos`
|
|
478
|
-
const beforeNode = items[this.index - 1] || null; // null appends
|
|
479
|
-
this.insertBefore(newRepeatItem, beforeNode);
|
|
480
|
-
newRepeatItem.nodeset = added;
|
|
481
|
-
this.setAttribute('index', this.index);
|
|
482
|
-
this.applyIndex(newRepeatItem);
|
|
483
|
-
|
|
484
|
-
const newModelItem = FxModel.lazyCreateModelItem(
|
|
485
|
-
this.getModel(),
|
|
486
|
-
this.ref,
|
|
487
|
-
added,
|
|
488
|
-
newRepeatItem,
|
|
489
|
-
);
|
|
490
|
-
|
|
491
|
-
newModelItem.path += '_1';
|
|
492
|
-
console.log('newModelItem', newModelItem);
|
|
493
|
-
|
|
494
|
-
newRepeatItem.modelItem = newModelItem;
|
|
495
|
-
this.getModel().registerModelItem(newModelItem);
|
|
496
|
-
|
|
497
|
-
this.getOwnerForm().scanForNewTemplateExpressionsNextRefresh();
|
|
498
|
-
this.getOwnerForm().addToBatchedNotifications(newRepeatItem);
|
|
499
|
-
}
|
|
500
|
-
*/
|
|
501
|
-
|
|
502
|
-
handleDelete(deleted) {
|
|
503
|
-
console.log('handleDelete', deleted);
|
|
504
|
-
// grab the current repeat items (tweak selector if yours differs)
|
|
505
|
-
/**
|
|
506
|
-
* @type {import('./fx-repeatitem.js').FxRepeatitem[]}
|
|
507
|
-
*/
|
|
508
|
-
const items = Array.from(
|
|
509
|
-
this.querySelectorAll(
|
|
510
|
-
':scope > fx-repeat-item, :scope > fx-repeatitem, :scope > .repeat-item',
|
|
511
|
-
),
|
|
512
|
-
);
|
|
513
|
-
|
|
514
|
-
this._evalNodeset();
|
|
515
|
-
|
|
516
|
-
const indexToRemove = items.findIndex(item => item.nodeset === deleted);
|
|
517
|
-
if (indexToRemove === -1) {
|
|
518
|
-
return;
|
|
519
|
-
}
|
|
520
|
-
const itemToRemove = items[indexToRemove];
|
|
521
|
-
|
|
522
|
-
itemToRemove.remove();
|
|
523
|
-
|
|
524
|
-
// Make the next item the 'current'
|
|
525
|
-
this.setIndex(indexToRemove + 1);
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
/**
|
|
529
|
-
* @returns {import('./fx-repeatitem.js').FxRepeatitem}
|
|
530
|
-
*/
|
|
531
|
-
_createNewRepeatItem() {
|
|
532
|
-
const newItem = document.createElement('fx-repeatitem');
|
|
533
|
-
|
|
534
|
-
if (this.isDraggable) {
|
|
535
|
-
newItem.setAttribute('draggable', 'true');
|
|
536
|
-
newItem.setAttribute('tabindex', 0);
|
|
537
|
-
}
|
|
538
|
-
const clone = this._clone();
|
|
539
|
-
newItem.appendChild(clone);
|
|
540
|
-
|
|
541
|
-
return newItem;
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
init() {
|
|
545
|
-
// ### there must be a single 'template' child
|
|
546
|
-
// console.log('##### repeat init ', this.id);
|
|
547
|
-
// if(!this.inited) this.init();
|
|
548
|
-
// does not use this.evalInContext as it is expecting a nodeset instead of single node
|
|
549
|
-
this._evalNodeset();
|
|
550
|
-
// console.log('##### ',this.id, this.nodeset);
|
|
551
|
-
|
|
552
|
-
this._initTemplate();
|
|
553
|
-
this._initRepeatItems();
|
|
554
|
-
|
|
555
|
-
this.setAttribute('index', this.index);
|
|
556
|
-
this.inited = true;
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
/**
|
|
560
|
-
* repeat has no own modelItems
|
|
561
|
-
* @private
|
|
562
|
-
*/
|
|
563
|
-
_evalNodeset() {
|
|
564
|
-
// const inscope = this.getInScopeContext();
|
|
565
|
-
const inscope = getInScopeContext(this.getAttributeNode('ref') || this, this.ref);
|
|
566
|
-
// console.log('##### inscope ', inscope);
|
|
567
|
-
// console.log('##### ref ', this.ref);
|
|
568
|
-
// now we got a nodeset and attach MutationObserver to it
|
|
569
|
-
|
|
570
|
-
if (!inscope) return;
|
|
571
|
-
if (this.mutationObserver && inscope.nodeName) {
|
|
572
|
-
this.mutationObserver.observe(inscope, {
|
|
573
|
-
childList: true,
|
|
574
|
-
subtree: true,
|
|
575
|
-
});
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
/*
|
|
579
|
-
this.touchedPaths = new Set();
|
|
580
|
-
const instance = XPathUtil.resolveInstance(this, this.ref);
|
|
581
|
-
const depTrackDomfacade = new DependencyNotifyingDomFacade((node) => {
|
|
582
|
-
this.touchedPaths.add(XPathUtil.getPath(node, instance));
|
|
583
|
-
});
|
|
584
|
-
const rawNodeset = evaluateXPath(this.ref, inscope, this, {}, {}, depTrackDomfacade );
|
|
585
|
-
*/
|
|
586
|
-
const rawNodeset = evaluateXPath(this.ref, inscope, this);
|
|
587
|
-
|
|
588
|
-
// console.log('Touched!', this.ref, [...this.touchedPaths].join(', '));
|
|
589
|
-
if (rawNodeset.length === 1 && Array.isArray(rawNodeset[0])) {
|
|
590
|
-
// This XPath likely returned an XPath array. Just collapse to that array
|
|
591
|
-
this.nodeset = rawNodeset[0];
|
|
592
|
-
return;
|
|
593
|
-
}
|
|
594
|
-
this.nodeset = rawNodeset;
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
async refresh(force) {
|
|
598
|
-
console.log('🔄 fx-repeat.refresh on', this.id);
|
|
599
|
-
|
|
600
|
-
if (!this.inited) this.init();
|
|
601
|
-
// console.time('repeat-refresh', this);
|
|
602
|
-
this._evalNodeset();
|
|
603
|
-
|
|
604
|
-
// console.log('repeat refresh nodeset ', this.nodeset);
|
|
605
|
-
// console.log('repeatCount', this.repeatCount);
|
|
606
|
-
|
|
607
|
-
const repeatItems = this.querySelectorAll(':scope > fx-repeatitem');
|
|
608
|
-
const repeatItemCount = repeatItems.length;
|
|
609
|
-
|
|
610
|
-
let nodeCount = 1;
|
|
611
|
-
if (Array.isArray(this.nodeset)) {
|
|
612
|
-
nodeCount = this.nodeset.length;
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
// const contextSize = this.nodeset.length;
|
|
616
|
-
const contextSize = nodeCount;
|
|
617
|
-
// todo: review - cant the context really never be smaller than the repeat count?
|
|
618
|
-
// todo: this code can be deprecated probably but check first
|
|
619
|
-
if (contextSize < repeatItemCount) {
|
|
620
|
-
for (let position = repeatItemCount; position > contextSize; position -= 1) {
|
|
621
|
-
// remove repeatitem
|
|
622
|
-
const itemToRemove = repeatItems[position - 1];
|
|
623
|
-
itemToRemove.parentNode.removeChild(itemToRemove);
|
|
624
|
-
this.getOwnerForm().unRegisterLazyElement(itemToRemove);
|
|
625
|
-
// this._fadeOut(itemToRemove);
|
|
626
|
-
// Fore.fadeOutElement(itemToRemove)
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
if (contextSize > repeatItemCount) {
|
|
631
|
-
for (let position = repeatItemCount + 1; position <= contextSize; position += 1) {
|
|
632
|
-
// add new repeatitem
|
|
633
|
-
|
|
634
|
-
const newItem = this._createNewRepeatItem();
|
|
635
|
-
|
|
636
|
-
this.appendChild(newItem);
|
|
637
|
-
|
|
638
|
-
this._initVariables(newItem);
|
|
639
|
-
|
|
640
|
-
newItem.nodeset = this.nodeset[position - 1];
|
|
641
|
-
newItem.index = position;
|
|
642
|
-
|
|
643
|
-
if (this.getOwnerForm().createNodes) {
|
|
644
|
-
this.getOwnerForm().initData(newItem);
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
// Tell the owner form we might have new template expressions here
|
|
648
|
-
this.getOwnerForm().scanForNewTemplateExpressionsNextRefresh();
|
|
649
|
-
|
|
650
|
-
newItem.refresh(true);
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
// ### update nodeset of repeatitems
|
|
655
|
-
for (let position = 0; position < repeatItemCount; position += 1) {
|
|
656
|
-
const item = repeatItems[position];
|
|
657
|
-
this.getOwnerForm().registerLazyElement(item);
|
|
658
|
-
|
|
659
|
-
if (item.nodeset !== this.nodeset[position]) {
|
|
660
|
-
item.nodeset = this.nodeset[position];
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
// Fore.refreshChildren(clone, true);
|
|
665
|
-
const fore = this.getOwnerForm();
|
|
666
|
-
// if (!fore.lazyRefresh || force) {
|
|
667
|
-
if (!fore.lazyRefresh || force) {
|
|
668
|
-
// Turn the possibly conditional force refresh into a forced one: we changed our children
|
|
669
|
-
Fore.refreshChildren(this, force);
|
|
670
|
-
}
|
|
671
|
-
// this.style.display = 'block';
|
|
672
|
-
// this.style.display = this.display;
|
|
673
|
-
this.setIndex(this.index);
|
|
674
|
-
// console.timeEnd('repeat-refresh');
|
|
675
|
-
|
|
676
|
-
// this.replaceWith(clone);
|
|
677
|
-
|
|
678
|
-
// this.repeatCount = contextSize;
|
|
679
|
-
// console.log('repeatCount', this.repeatCount);
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
// eslint-disable-next-line class-methods-use-this
|
|
683
|
-
_fadeOut(el) {
|
|
684
|
-
el.style.opacity = 1;
|
|
685
|
-
|
|
686
|
-
(function fade() {
|
|
687
|
-
// eslint-disable-next-line no-cond-assign
|
|
688
|
-
if ((el.style.opacity -= 0.1) < 0) {
|
|
689
|
-
el.style.display = 'none';
|
|
690
|
-
} else {
|
|
691
|
-
requestAnimationFrame(fade);
|
|
692
|
-
}
|
|
693
|
-
})();
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
// eslint-disable-next-line class-methods-use-this
|
|
697
|
-
_fadeIn(el) {
|
|
698
|
-
if (!el) return;
|
|
699
|
-
|
|
700
|
-
el.style.opacity = 0;
|
|
701
|
-
el.style.display = this.display;
|
|
702
|
-
|
|
703
|
-
(function fade() {
|
|
704
|
-
// setTimeout(() => {
|
|
705
|
-
let val = parseFloat(el.style.opacity);
|
|
706
|
-
// eslint-disable-next-line no-cond-assign
|
|
707
|
-
if (!((val += 0.1) > 1)) {
|
|
708
|
-
el.style.opacity = val;
|
|
709
|
-
requestAnimationFrame(fade);
|
|
710
|
-
}
|
|
711
|
-
// }, 40);
|
|
712
|
-
})();
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
_initTemplate() {
|
|
716
|
-
this.template = this.querySelector('template');
|
|
717
|
-
// console.log('### init template for repeat ', this.id, this.template);
|
|
718
|
-
// todo: this.dropTarget not needed?
|
|
719
|
-
this.dropTarget = this.template.getAttribute('drop-target');
|
|
720
|
-
this.isDraggable = this.template.hasAttribute('draggable')
|
|
721
|
-
? this.template.getAttribute('draggable')
|
|
722
|
-
: null;
|
|
723
|
-
|
|
724
|
-
if (this.template === null) {
|
|
725
|
-
// todo: catch this on form element
|
|
726
|
-
this.dispatchEvent(
|
|
727
|
-
new CustomEvent('no-template-error', {
|
|
728
|
-
composed: true,
|
|
729
|
-
bubbles: true,
|
|
730
|
-
detail: { message: `no template found for repeat:${this.id}` },
|
|
731
|
-
}),
|
|
732
|
-
);
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
this.shadowRoot.appendChild(this.template);
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
_initRepeatItems() {
|
|
739
|
-
this.nodeset.forEach((item, index) => {
|
|
740
|
-
const repeatItem = this._createNewRepeatItem();
|
|
741
|
-
repeatItem.nodeset = this.nodeset[index];
|
|
742
|
-
repeatItem.index = index + 1; // 1-based index
|
|
743
|
-
|
|
744
|
-
this.appendChild(repeatItem);
|
|
745
|
-
|
|
746
|
-
if (this.getOwnerForm().createNodes) {
|
|
747
|
-
this.getOwnerForm().initData(repeatItem);
|
|
748
|
-
const repeatItemClone = repeatItem.nodeset.cloneNode(true);
|
|
749
|
-
this.clearTextValues(repeatItemClone);
|
|
750
|
-
|
|
751
|
-
// this.createdNodeset = repeatItem.nodeset.cloneNode(true);
|
|
752
|
-
this.createdNodeset = repeatItemClone;
|
|
753
|
-
// console.log('createdNodeset', this.createdNodeset)
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
if (repeatItem.index === 1) {
|
|
757
|
-
this.applyIndex(repeatItem);
|
|
758
|
-
}
|
|
759
|
-
// console.log('*********repeat item created', repeatItem.nodeset);
|
|
760
|
-
Fore.dispatch(this, 'item-created', { nodeset: repeatItem.nodeset, pos: index + 1 });
|
|
761
|
-
this._initVariables(repeatItem);
|
|
762
|
-
});
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
clearTextValues(node) {
|
|
766
|
-
if (!node) return;
|
|
767
|
-
|
|
768
|
-
// Clear text node content
|
|
769
|
-
if (node.nodeType === Node.TEXT_NODE) {
|
|
770
|
-
node.nodeValue = '';
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
// Clear all attribute values
|
|
774
|
-
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
775
|
-
for (const attr of Array.from(node.attributes)) {
|
|
776
|
-
attr.value = ''; // Clear attribute value
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
// Recursively clear child nodes
|
|
781
|
-
for (const child of node.childNodes) {
|
|
782
|
-
this.clearTextValues(child);
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
_initVariables(newRepeatItem) {
|
|
787
|
-
const inScopeVariables = new Map(this.inScopeVariables);
|
|
788
|
-
newRepeatItem.setInScopeVariables(inScopeVariables);
|
|
789
|
-
(function registerVariables(node) {
|
|
790
|
-
for (const child of node.children) {
|
|
791
|
-
if ('setInScopeVariables' in child) {
|
|
792
|
-
child.setInScopeVariables(inScopeVariables);
|
|
793
|
-
}
|
|
794
|
-
registerVariables(child);
|
|
795
|
-
}
|
|
796
|
-
})(newRepeatItem);
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
_clone() {
|
|
800
|
-
// const content = this.template.content.cloneNode(true);
|
|
801
|
-
this.template = this.shadowRoot.querySelector('template');
|
|
802
|
-
const content = this.template.content.cloneNode(true);
|
|
803
|
-
return document.importNode(content, true);
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
_removeIndexMarker() {
|
|
807
|
-
Array.from(this.children).forEach(item => {
|
|
808
|
-
item.removeAttribute('repeat-index');
|
|
809
|
-
});
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
setInScopeVariables(inScopeVariables) {
|
|
813
|
-
// Repeats are interesting: the variables should be scoped per repeat item, they should not be
|
|
814
|
-
// able to see the variables in adjacent repeat items!
|
|
815
|
-
this.inScopeVariables = new Map(inScopeVariables);
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
if (!customElements.get('fx-repeat')) {
|
|
820
|
-
window.customElements.define('fx-repeat', FxRepeat);
|
|
821
|
-
}
|