@jinntec/fore 2.4.1 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/fore-dev.js +9 -2
- package/dist/fore-dev.js.map +1 -1
- package/dist/fore.js +9 -2
- package/dist/fore.js.map +1 -1
- package/index.js +2 -0
- package/package.json +2 -2
- package/resources/fore.css +1 -1
- package/src/ForeElementMixin.js +12 -6
- package/src/actions/abstract-action.js +33 -0
- package/src/actions/fx-delete.js +2 -2
- package/src/actions/fx-insert.js +2 -0
- package/src/actions/fx-send.js +0 -2
- package/src/actions/fx-setvalue.js +5 -1
- package/src/fore.js +2 -1
- package/src/fx-bind.js +3 -2
- package/src/fx-fore.js +280 -123
- package/src/fx-instance.js +3 -2
- package/src/fx-model.js +64 -35
- package/src/fx-submission.js +48 -19
- package/src/getInScopeContext.js +1 -1
- package/src/modelitem.js +7 -2
- package/src/ui/abstract-control.js +6 -4
- package/src/ui/fx-container.js +3 -2
- package/src/ui/fx-control.js +3 -24
- package/src/ui/fx-repeat.js +9 -0
- package/src/ui/fx-repeatitem.js +3 -0
- package/src/ui/fx-select.js +89 -0
- package/src/ui/fx-switch.js +4 -0
- package/src/ui/fx-upload.js +304 -0
- package/src/xpath-util.js +119 -5
package/src/fx-model.js
CHANGED
|
@@ -51,7 +51,7 @@ export class FxModel extends HTMLElement {
|
|
|
51
51
|
<slot></slot>
|
|
52
52
|
`;
|
|
53
53
|
|
|
54
|
-
/*
|
|
54
|
+
/*
|
|
55
55
|
this.addEventListener('model-construct-done', () => {
|
|
56
56
|
// this.modelConstructed = true;
|
|
57
57
|
// console.log('model-construct-done fired ', this.modelConstructed);
|
|
@@ -65,12 +65,38 @@ export class FxModel extends HTMLElement {
|
|
|
65
65
|
this.fore = this.parentNode;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
/**
|
|
69
|
+
* @param {FxModel} model The model to create a model item for
|
|
70
|
+
* @param {string} ref The XPath ref that led to this model item
|
|
71
|
+
* @param {Node} node The node the XPath led to
|
|
72
|
+
* @param {ForeElementMixin} formElement The form element making this model. Used to resolve variables against
|
|
73
|
+
*/
|
|
74
|
+
static lazyCreateModelItem(model, ref, node, formElement) {
|
|
69
75
|
// console.log('lazyCreateModelItem ', node);
|
|
70
|
-
|
|
76
|
+
const instanceId = XPathUtil.resolveInstance(formElement, ref);
|
|
77
|
+
|
|
78
|
+
if (model.parentNode.createNodes && (node === null || node === undefined)) {
|
|
79
|
+
// ### intializing ModelItem with default values (as there is no <fx-bind> matching for given ref)
|
|
80
|
+
const mi = new ModelItem(
|
|
81
|
+
undefined,
|
|
82
|
+
ref,
|
|
83
|
+
Fore.READONLY_DEFAULT,
|
|
84
|
+
false,
|
|
85
|
+
Fore.REQUIRED_DEFAULT,
|
|
86
|
+
Fore.CONSTRAINT_DEFAULT,
|
|
87
|
+
Fore.TYPE_DEFAULT,
|
|
88
|
+
null,
|
|
89
|
+
this,
|
|
90
|
+
instanceId,
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
// console.log('new ModelItem is instanceof ModelItem ', mi instanceof ModelItem);
|
|
94
|
+
model.registerModelItem(mi);
|
|
95
|
+
return mi;
|
|
96
|
+
}
|
|
71
97
|
let targetNode = {};
|
|
72
98
|
if (node === null || node === undefined) return null;
|
|
73
|
-
if (node.nodeType ===
|
|
99
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
74
100
|
// const parent = node.parentNode;
|
|
75
101
|
// console.log('PARENT ', parent);
|
|
76
102
|
targetNode = node.parentNode;
|
|
@@ -81,9 +107,7 @@ export class FxModel extends HTMLElement {
|
|
|
81
107
|
// const path = fx.evaluateXPath('path()',node);
|
|
82
108
|
let path;
|
|
83
109
|
if (node.nodeType) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
path = XPathUtil.getPath(node, instance);
|
|
110
|
+
path = XPathUtil.getPath(node, instanceId);
|
|
87
111
|
} else {
|
|
88
112
|
path = null;
|
|
89
113
|
targetNode = node;
|
|
@@ -101,6 +125,7 @@ export class FxModel extends HTMLElement {
|
|
|
101
125
|
Fore.TYPE_DEFAULT,
|
|
102
126
|
targetNode,
|
|
103
127
|
this,
|
|
128
|
+
instanceId,
|
|
104
129
|
);
|
|
105
130
|
|
|
106
131
|
// console.log('new ModelItem is instanceof ModelItem ', mi instanceof ModelItem);
|
|
@@ -118,7 +143,11 @@ export class FxModel extends HTMLElement {
|
|
|
118
143
|
*
|
|
119
144
|
*/
|
|
120
145
|
async modelConstruct() {
|
|
121
|
-
console.
|
|
146
|
+
console.info(
|
|
147
|
+
`%cdispatching model-construct for #${this.parentNode.id}`,
|
|
148
|
+
'background:lightblue; color:black; padding:.5rem; display:inline-block; white-space: nowrap; border-radius:0.3rem;width:100%;',
|
|
149
|
+
);
|
|
150
|
+
|
|
122
151
|
// this.dispatchEvent(new CustomEvent('model-construct', { detail: this }));
|
|
123
152
|
Fore.dispatch(this, 'model-construct', { model: this });
|
|
124
153
|
|
|
@@ -132,25 +161,25 @@ export class FxModel extends HTMLElement {
|
|
|
132
161
|
|
|
133
162
|
// Wait until all the instances are built
|
|
134
163
|
await Promise.all(promises);
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
164
|
+
this.instances = Array.from(instances);
|
|
165
|
+
// console.log('_modelConstruct this.instances ', this.instances);
|
|
166
|
+
// Await until the model-construct-done event is handled off
|
|
167
|
+
this.modelConstructed = true;
|
|
168
|
+
await Fore.dispatch(this, 'model-construct-done', { model: this });
|
|
169
|
+
this.inited = true;
|
|
170
|
+
this.updateModel();
|
|
171
|
+
} else {
|
|
172
|
+
// ### if there's no instance one will created
|
|
173
|
+
console.log(`### <<<<< dispatching model-construct-done for '${this.fore.id}' >>>>>`);
|
|
174
|
+
this.modelConstructed = true;
|
|
175
|
+
await this.dispatchEvent(
|
|
176
|
+
new CustomEvent('model-construct-done', {
|
|
177
|
+
composed: false,
|
|
178
|
+
bubbles: true,
|
|
179
|
+
detail: { model: this },
|
|
180
|
+
}),
|
|
181
|
+
);
|
|
182
|
+
}
|
|
154
183
|
|
|
155
184
|
const functionlibImports = Array.from(this.querySelectorAll('fx-functionlib'));
|
|
156
185
|
await Promise.all(functionlibImports.map(lib => lib.readyPromise));
|
|
@@ -416,7 +445,7 @@ export class FxModel extends HTMLElement {
|
|
|
416
445
|
modelItem.required = compute;
|
|
417
446
|
this.formElement.addToRefresh(modelItem); // let fore know that modelItem needs refresh
|
|
418
447
|
if (!modelItem.node.textContent) {
|
|
419
|
-
/*
|
|
448
|
+
/*
|
|
420
449
|
console.log(
|
|
421
450
|
'node is required but has no value ',
|
|
422
451
|
XPathUtil.getDocPath(modelItem.node),
|
|
@@ -438,7 +467,7 @@ export class FxModel extends HTMLElement {
|
|
|
438
467
|
}
|
|
439
468
|
}
|
|
440
469
|
});
|
|
441
|
-
|
|
470
|
+
console.log('modelItems after revalidate: ', this.modelItems);
|
|
442
471
|
return valid;
|
|
443
472
|
}
|
|
444
473
|
|
|
@@ -468,17 +497,17 @@ export class FxModel extends HTMLElement {
|
|
|
468
497
|
/**
|
|
469
498
|
* @returns {import('./fx-instance.js').FxInstance}
|
|
470
499
|
*/
|
|
471
|
-
|
|
472
|
-
/*
|
|
500
|
+
getDefaultInstance() {
|
|
501
|
+
/*
|
|
473
502
|
if (this.instances.length === 0) {
|
|
474
503
|
throw new Error('No instances defined. Fore cannot work without any <data/> elements.');
|
|
475
504
|
}
|
|
476
505
|
*/
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
}
|
|
480
|
-
return this.getInstance('default');
|
|
506
|
+
if (this.instances.length) {
|
|
507
|
+
return this.instances[0];
|
|
481
508
|
}
|
|
509
|
+
return this.getInstance('default');
|
|
510
|
+
}
|
|
482
511
|
|
|
483
512
|
getDefaultInstanceData() {
|
|
484
513
|
return this.instances[0].getInstanceData();
|
package/src/fx-submission.js
CHANGED
|
@@ -50,8 +50,8 @@ export class FxSubmission extends ForeElementMixin {
|
|
|
50
50
|
? this.getAttribute('mediatype')
|
|
51
51
|
: 'application/xml';
|
|
52
52
|
|
|
53
|
-
this.responseMediatype = this.hasAttribute('
|
|
54
|
-
? this.getAttribute('
|
|
53
|
+
this.responseMediatype = this.hasAttribute('responsemediatype')
|
|
54
|
+
? this.getAttribute('responsemediatype')
|
|
55
55
|
: this.mediatype;
|
|
56
56
|
this.url = this.hasAttribute('url') ? this.getAttribute('url') : null;
|
|
57
57
|
|
|
@@ -83,7 +83,11 @@ export class FxSubmission extends ForeElementMixin {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
async _submit() {
|
|
86
|
-
|
|
86
|
+
console.info(
|
|
87
|
+
`%csubmitting #${this.id}`,
|
|
88
|
+
'background:yellow; color:black; padding:.5rem; display:inline-block; white-space: nowrap; border-radius:0.3rem;width:100%;',
|
|
89
|
+
);
|
|
90
|
+
|
|
87
91
|
this.evalInContext();
|
|
88
92
|
const model = this.getModel();
|
|
89
93
|
|
|
@@ -232,6 +236,11 @@ export class FxSubmission extends ForeElementMixin {
|
|
|
232
236
|
|
|
233
237
|
if (!response.ok || response.status > 400) {
|
|
234
238
|
// this.dispatch('submit-error', { message: `Error while submitting ${this.id}` });
|
|
239
|
+
console.info(
|
|
240
|
+
`%csubmit-error #${this.id}`,
|
|
241
|
+
'background:red; color:black; padding:.5rem; display:inline-block; white-space: nowrap; border-radius:0.3rem;width:100%;',
|
|
242
|
+
);
|
|
243
|
+
|
|
235
244
|
Fore.dispatch(this, 'submit-error', { message: `Error while submitting ${this.id}` });
|
|
236
245
|
return;
|
|
237
246
|
}
|
|
@@ -255,6 +264,13 @@ export class FxSubmission extends ForeElementMixin {
|
|
|
255
264
|
// this.dispatch('submit-done', {});
|
|
256
265
|
// console.log(`### <<<<< ${this.id} submit-done >>>>>`);
|
|
257
266
|
Fore.dispatch(this, 'submit-done', {});
|
|
267
|
+
/*
|
|
268
|
+
console.info(
|
|
269
|
+
`%csubmit-done #${this.id}`,
|
|
270
|
+
'background:green; color:white; padding:.5rem; display:inline-block; white-space: nowrap; border-radius:0.3rem;width:100%;',
|
|
271
|
+
);
|
|
272
|
+
*/
|
|
273
|
+
|
|
258
274
|
} catch (error) {
|
|
259
275
|
Fore.dispatch(this, 'submit-error', { error: error.message });
|
|
260
276
|
} finally {
|
|
@@ -354,13 +370,6 @@ export class FxSubmission extends ForeElementMixin {
|
|
|
354
370
|
_handleResponse(data, resolvedUrl, contentType) {
|
|
355
371
|
// console.log('_handleResponse ', data);
|
|
356
372
|
|
|
357
|
-
/*
|
|
358
|
-
// ### responses need to be handled depending on their type.
|
|
359
|
-
if(this.type === 'json'){
|
|
360
|
-
|
|
361
|
-
}
|
|
362
|
-
*/
|
|
363
|
-
|
|
364
373
|
const targetInstance = this._getTargetInstance();
|
|
365
374
|
|
|
366
375
|
/*
|
|
@@ -386,16 +395,26 @@ export class FxSubmission extends ForeElementMixin {
|
|
|
386
395
|
if (this.replace === 'instance') {
|
|
387
396
|
if (targetInstance) {
|
|
388
397
|
if (this.targetref) {
|
|
398
|
+
|
|
389
399
|
const [theTarget] = evaluateXPath(
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
400
|
+
this.targetref,
|
|
401
|
+
targetInstance.instanceData.firstElementChild,
|
|
402
|
+
this,
|
|
393
403
|
);
|
|
394
404
|
console.log('theTarget', theTarget);
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
405
|
+
if(this.responseMediatype === 'application/xml' || this.responseMediatype === 'text/html'){
|
|
406
|
+
const clone = data.firstElementChild;
|
|
407
|
+
const parent = theTarget.parentNode;
|
|
408
|
+
parent.replaceChild(clone, theTarget);
|
|
409
|
+
console.log('finally ', parent);
|
|
410
|
+
}
|
|
411
|
+
if(this.responseMediatype.startsWith('text/')){
|
|
412
|
+
theTarget.textContent = data;
|
|
413
|
+
}
|
|
414
|
+
if(this.responseMediatype === 'application/json'){
|
|
415
|
+
console.warn('targetref is not supported for application/json responses')
|
|
416
|
+
}
|
|
417
|
+
|
|
399
418
|
} else if (this.into) {
|
|
400
419
|
const [theTarget] = evaluateXPath(
|
|
401
420
|
this.into,
|
|
@@ -447,12 +466,22 @@ export class FxSubmission extends ForeElementMixin {
|
|
|
447
466
|
}
|
|
448
467
|
// document.getElementsByTagName('html')[0].innerHTML = data;
|
|
449
468
|
}
|
|
450
|
-
if (this.replace === 'target'
|
|
469
|
+
if (this.replace === 'target') {
|
|
451
470
|
// const target = this.getAttribute('target');
|
|
452
471
|
const target = this._getProperty('target');
|
|
453
472
|
const targetNode = document.querySelector(target);
|
|
454
473
|
if (targetNode) {
|
|
455
|
-
|
|
474
|
+
|
|
475
|
+
if(contentType.startsWith('text/html')){
|
|
476
|
+
targetNode.innerHTML = data;
|
|
477
|
+
}
|
|
478
|
+
if(this.responseMediatype.startsWith('image/svg')){
|
|
479
|
+
const parser = new DOMParser();
|
|
480
|
+
const svgDoc = parser.parseFromString(data, 'image/svg+xml');
|
|
481
|
+
|
|
482
|
+
const objectURL = URL.createObjectURL(data);
|
|
483
|
+
targetNode.src = objectURL;
|
|
484
|
+
}
|
|
456
485
|
} else {
|
|
457
486
|
Fore.dispatch(this, 'submit-error', {
|
|
458
487
|
message: `targetNode for selector ${target} not found`,
|
package/src/getInScopeContext.js
CHANGED
|
@@ -48,7 +48,7 @@ function _getInitialContext(node, ref) {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
if (XPathUtil.isAbsolutePath(ref)) {
|
|
51
|
-
const instanceId = XPathUtil.getInstanceId(ref);
|
|
51
|
+
const instanceId = XPathUtil.getInstanceId(ref, node);
|
|
52
52
|
if (instanceId) {
|
|
53
53
|
return model.getInstance(instanceId).getDefaultContext();
|
|
54
54
|
}
|
package/src/modelitem.js
CHANGED
|
@@ -17,8 +17,9 @@ export class ModelItem {
|
|
|
17
17
|
* @param {string} type - string expression to set a datatype
|
|
18
18
|
* @param {Node} node - the node the 'ref' expression is referring to
|
|
19
19
|
* @param {import('./fx-bind').FxBind} bind - the fx-bind element having created this modelItem
|
|
20
|
+
* @param {string} instance - the fx-instance id having created this modelItem
|
|
20
21
|
*/
|
|
21
|
-
constructor(path, ref, readonly, relevant, required, constraint, type, node, bind) {
|
|
22
|
+
constructor(path, ref, readonly, relevant, required, constraint, type, node, bind,instance) {
|
|
22
23
|
/**
|
|
23
24
|
* @type {string}
|
|
24
25
|
*/
|
|
@@ -61,10 +62,11 @@ export class ModelItem {
|
|
|
61
62
|
*/
|
|
62
63
|
this.alerts = [];
|
|
63
64
|
/**
|
|
64
|
-
* @type {import('./ui/
|
|
65
|
+
* @type {import('./ui/abstract-control').default[]}
|
|
65
66
|
*/
|
|
66
67
|
this.boundControls = [];
|
|
67
68
|
// this.value = this._getValue();
|
|
69
|
+
this.instanceId = instance;
|
|
68
70
|
}
|
|
69
71
|
|
|
70
72
|
/*
|
|
@@ -74,6 +76,9 @@ export class ModelItem {
|
|
|
74
76
|
*/
|
|
75
77
|
|
|
76
78
|
get value() {
|
|
79
|
+
if (!this.node) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
77
82
|
if (!this.node.nodeType) return this.node;
|
|
78
83
|
|
|
79
84
|
if (this.node.nodeType === Node.ATTRIBUTE_NODE) {
|
|
@@ -64,10 +64,10 @@ export default class AbstractControl extends ForeElementMixin {
|
|
|
64
64
|
|
|
65
65
|
// await this.updateComplete;
|
|
66
66
|
// await this.getWidget();
|
|
67
|
-
this.oldVal = this.nodeset ? this.nodeset : null;
|
|
68
|
-
// console.log('oldVal',this.oldVal);
|
|
69
67
|
|
|
70
68
|
this.evalInContext();
|
|
69
|
+
this.oldVal = this.nodeset ? this.nodeset : null;
|
|
70
|
+
// console.log('oldVal',this.oldVal);
|
|
71
71
|
|
|
72
72
|
// todo this if should be removed - see above
|
|
73
73
|
if (this.isBound()) {
|
|
@@ -169,13 +169,15 @@ export default class AbstractControl extends ForeElementMixin {
|
|
|
169
169
|
if (!this.getOwnerForm().ready) return; // state change event do not fire during init phase (initial refresh)
|
|
170
170
|
// if oldVal is null we haven't received a concrete value yet
|
|
171
171
|
|
|
172
|
-
if (this.localName
|
|
172
|
+
if (!(this.localName === 'fx-control' || this.localName === 'fx-upload')) return;
|
|
173
173
|
if (isDifferent(this.oldVal, oldValue, this.value)) {
|
|
174
174
|
const model = this.getModel();
|
|
175
175
|
Fore.dispatch(this, 'value-changed', {
|
|
176
176
|
path: this.modelItem.path,
|
|
177
177
|
value: this.modelItem.value,
|
|
178
|
-
oldvalue:
|
|
178
|
+
oldvalue: oldValue,
|
|
179
|
+
instanceId:this.modelItem.instanceId,
|
|
180
|
+
foreId:this.getOwnerForm().id
|
|
179
181
|
});
|
|
180
182
|
}
|
|
181
183
|
}
|
package/src/ui/fx-container.js
CHANGED
|
@@ -41,8 +41,9 @@ export class FxContainer extends ForeElementMixin {
|
|
|
41
41
|
</style>
|
|
42
42
|
${html}
|
|
43
43
|
`;
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
if(this.ref !== ''){
|
|
45
|
+
this.getOwnerForm().registerLazyElement(this);
|
|
46
|
+
}
|
|
46
47
|
|
|
47
48
|
/*
|
|
48
49
|
this.addEventListener('mousedown', e => {
|
package/src/ui/fx-control.js
CHANGED
|
@@ -37,6 +37,7 @@ export default class FxControl extends XfAbstractControl {
|
|
|
37
37
|
constructor() {
|
|
38
38
|
super();
|
|
39
39
|
this.inited = false;
|
|
40
|
+
this.nodeset = null;
|
|
40
41
|
this.attachShadow({ mode: 'open' });
|
|
41
42
|
}
|
|
42
43
|
|
|
@@ -167,28 +168,7 @@ export default class FxControl extends XfAbstractControl {
|
|
|
167
168
|
}
|
|
168
169
|
|
|
169
170
|
this.addEventListener('return', e => {
|
|
170
|
-
// console.log('catched return action on ', this);
|
|
171
|
-
// console.log('return detail', e.detail);
|
|
172
|
-
|
|
173
|
-
// console.log('return triggered on ', this);
|
|
174
|
-
// console.log('this.ref', this.ref);
|
|
175
|
-
// console.log('current outer instance', this.getInstance());
|
|
176
|
-
|
|
177
|
-
/*
|
|
178
|
-
console.log(
|
|
179
|
-
'???? why ???? current nodeset should point to the node of the outer control',
|
|
180
|
-
e.currentTarget.nodeset,
|
|
181
|
-
);
|
|
182
|
-
console.log(
|
|
183
|
-
'???? why ???? current nodeset should point to the node of the outer control',
|
|
184
|
-
this.nodeset,
|
|
185
|
-
);
|
|
186
|
-
*/
|
|
187
171
|
const newNodes = e.detail.nodeset;
|
|
188
|
-
// console.log('new nodeset', newNodes);
|
|
189
|
-
// console.log('currentTarget', e.currentTarget);
|
|
190
|
-
// console.log('target', e.target);
|
|
191
|
-
|
|
192
172
|
e.stopPropagation();
|
|
193
173
|
|
|
194
174
|
this._replaceNode(newNodes);
|
|
@@ -347,9 +327,9 @@ export default class FxControl extends XfAbstractControl {
|
|
|
347
327
|
}
|
|
348
328
|
|
|
349
329
|
// ### value is bound to radio
|
|
350
|
-
if(this.widget.type === 'radio'){
|
|
330
|
+
if (this.widget.type === 'radio') {
|
|
351
331
|
const matches = this.querySelector(`input[value=${this.value}]`);
|
|
352
|
-
if(matches){
|
|
332
|
+
if (matches) {
|
|
353
333
|
matches.checked = true;
|
|
354
334
|
}
|
|
355
335
|
return;
|
|
@@ -367,7 +347,6 @@ export default class FxControl extends XfAbstractControl {
|
|
|
367
347
|
return;
|
|
368
348
|
}
|
|
369
349
|
|
|
370
|
-
|
|
371
350
|
if (this.hasAttribute('as')) {
|
|
372
351
|
const as = this.getAttribute('as');
|
|
373
352
|
|
package/src/ui/fx-repeat.js
CHANGED
|
@@ -298,6 +298,11 @@ export class FxRepeat extends withDraggability(ForeElementMixin, false) {
|
|
|
298
298
|
|
|
299
299
|
newItem.nodeset = this.nodeset[position - 1];
|
|
300
300
|
newItem.index = position;
|
|
301
|
+
|
|
302
|
+
if (this.getOwnerForm().createNodes) {
|
|
303
|
+
this.getOwnerForm().initData(newItem);
|
|
304
|
+
}
|
|
305
|
+
|
|
301
306
|
// Tell the owner form we might have new template expressions here
|
|
302
307
|
this.getOwnerForm().scanForNewTemplateExpressionsNextRefresh();
|
|
303
308
|
}
|
|
@@ -393,6 +398,10 @@ export class FxRepeat extends withDraggability(ForeElementMixin, false) {
|
|
|
393
398
|
|
|
394
399
|
this.appendChild(repeatItem);
|
|
395
400
|
|
|
401
|
+
if (this.getOwnerForm().createNodes) {
|
|
402
|
+
this.getOwnerForm().initData(repeatItem);
|
|
403
|
+
}
|
|
404
|
+
|
|
396
405
|
if (repeatItem.index === 1) {
|
|
397
406
|
this.applyIndex(repeatItem);
|
|
398
407
|
}
|
package/src/ui/fx-repeatitem.js
CHANGED
|
@@ -8,10 +8,13 @@ import { withDraggability } from '../withDraggability.js';
|
|
|
8
8
|
*
|
|
9
9
|
* @customElement
|
|
10
10
|
* @demo demo/index.html
|
|
11
|
+
*
|
|
12
|
+
* @extends {ForeElementMixin}
|
|
11
13
|
*/
|
|
12
14
|
export class FxRepeatitem extends withDraggability(ForeElementMixin, true) {
|
|
13
15
|
static get properties() {
|
|
14
16
|
return {
|
|
17
|
+
...super.properties,
|
|
15
18
|
inited: {
|
|
16
19
|
type: Boolean,
|
|
17
20
|
},
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
export class FxSelect extends HTMLElement {
|
|
2
|
+
constructor() {
|
|
3
|
+
super();
|
|
4
|
+
|
|
5
|
+
// Attach Shadow DOM to encapsulate the component
|
|
6
|
+
this.attachShadow({ mode: 'open' });
|
|
7
|
+
|
|
8
|
+
// Create and attach the <select> element
|
|
9
|
+
this.selectElement = document.createElement('select');
|
|
10
|
+
this.shadowRoot.appendChild(this.selectElement);
|
|
11
|
+
|
|
12
|
+
// Set default values
|
|
13
|
+
this.batchSize = parseInt(this.getAttribute('batch-size')) || 20;
|
|
14
|
+
this.size = parseInt(this.getAttribute('size')) || 5;
|
|
15
|
+
this.currentLoadedItems = 0;
|
|
16
|
+
this.hasMoreItems = true; // Assume there are more items initially
|
|
17
|
+
|
|
18
|
+
// Set the size of the <select> element
|
|
19
|
+
this.selectElement.setAttribute('size', this.size);
|
|
20
|
+
|
|
21
|
+
// Bind the scroll event handler
|
|
22
|
+
this.handleScroll = this.handleScroll.bind(this);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
connectedCallback() {
|
|
26
|
+
// Initial load of options
|
|
27
|
+
this.loadOptions(this.currentLoadedItems, this.batchSize);
|
|
28
|
+
|
|
29
|
+
// Attach scroll event listener
|
|
30
|
+
this.selectElement.addEventListener('scroll', this.handleScroll);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
disconnectedCallback() {
|
|
34
|
+
// Clean up event listeners when the element is removed
|
|
35
|
+
this.selectElement.removeEventListener('scroll', this.handleScroll);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Mock function to simulate fetching more options
|
|
39
|
+
fetchMoreItems(start, count) {
|
|
40
|
+
// Simulate an async fetch or data load (replace with real fetch if needed)
|
|
41
|
+
return new Promise((resolve) => {
|
|
42
|
+
setTimeout(() => {
|
|
43
|
+
const items = [];
|
|
44
|
+
for (let i = start; i < start + count; i++) {
|
|
45
|
+
if (i < 200) { // Let's say we stop after 200 items (remove this limit for real use)
|
|
46
|
+
items.push({ value: i, label: `Option ${i + 1}` });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
resolve(items);
|
|
50
|
+
}, 500); // Simulate fetch delay
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Method to load a batch of options
|
|
55
|
+
async loadOptions(start, count) {
|
|
56
|
+
const fragment = document.createDocumentFragment();
|
|
57
|
+
const newItems = await this.fetchMoreItems(start, count);
|
|
58
|
+
|
|
59
|
+
if (newItems.length > 0) {
|
|
60
|
+
newItems.forEach(item => {
|
|
61
|
+
const option = document.createElement('option');
|
|
62
|
+
option.value = item.value;
|
|
63
|
+
option.textContent = item.label;
|
|
64
|
+
fragment.appendChild(option);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
this.selectElement.appendChild(fragment);
|
|
68
|
+
this.currentLoadedItems += newItems.length;
|
|
69
|
+
} else {
|
|
70
|
+
// If no more items are returned, stop further loading
|
|
71
|
+
this.hasMoreItems = false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Scroll event handler to load more items
|
|
76
|
+
handleScroll() {
|
|
77
|
+
const { scrollTop, scrollHeight, clientHeight } = this.selectElement;
|
|
78
|
+
const isNearBottom = scrollTop + clientHeight >= scrollHeight - 10;
|
|
79
|
+
|
|
80
|
+
if (isNearBottom && this.hasMoreItems) {
|
|
81
|
+
this.loadOptions(this.currentLoadedItems, this.batchSize);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Register the custom element
|
|
87
|
+
if (!customElements.get('fx-select')) {
|
|
88
|
+
customElements.define('fx-select', FxSelect);
|
|
89
|
+
}
|
package/src/ui/fx-switch.js
CHANGED
|
@@ -20,6 +20,7 @@ class FxSwitch extends FxContainer {
|
|
|
20
20
|
this.formerCase = {};
|
|
21
21
|
this.selectedCase = null;
|
|
22
22
|
this.cases = null;
|
|
23
|
+
this.visitedResetted = false;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
connectedCallback() {
|
|
@@ -74,10 +75,13 @@ class FxSwitch extends FxContainer {
|
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
_resetVisited() {
|
|
78
|
+
if(this.visitedResetted) return;
|
|
79
|
+
|
|
77
80
|
const visited = this.selectedCase.querySelectorAll('.visited');
|
|
78
81
|
Array.from(visited).forEach(v => {
|
|
79
82
|
v.classList.remove('visited');
|
|
80
83
|
});
|
|
84
|
+
this.visitedResetted = true;
|
|
81
85
|
}
|
|
82
86
|
|
|
83
87
|
/**
|