@jinntec/fore 2.4.2 → 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 +63 -34
- package/src/fx-submission.js +14 -3
- 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-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),
|
|
@@ -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
|
@@ -83,7 +83,6 @@ export class FxSubmission extends ForeElementMixin {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
async _submit() {
|
|
86
|
-
console.log('submitting....', this.getAttribute('id'));
|
|
87
86
|
console.info(
|
|
88
87
|
`%csubmitting #${this.id}`,
|
|
89
88
|
'background:yellow; color:black; padding:.5rem; display:inline-block; white-space: nowrap; border-radius:0.3rem;width:100%;',
|
|
@@ -265,10 +264,12 @@ export class FxSubmission extends ForeElementMixin {
|
|
|
265
264
|
// this.dispatch('submit-done', {});
|
|
266
265
|
// console.log(`### <<<<< ${this.id} submit-done >>>>>`);
|
|
267
266
|
Fore.dispatch(this, 'submit-done', {});
|
|
267
|
+
/*
|
|
268
268
|
console.info(
|
|
269
269
|
`%csubmit-done #${this.id}`,
|
|
270
270
|
'background:green; color:white; padding:.5rem; display:inline-block; white-space: nowrap; border-radius:0.3rem;width:100%;',
|
|
271
271
|
);
|
|
272
|
+
*/
|
|
272
273
|
|
|
273
274
|
} catch (error) {
|
|
274
275
|
Fore.dispatch(this, 'submit-error', { error: error.message });
|
|
@@ -465,12 +466,22 @@ export class FxSubmission extends ForeElementMixin {
|
|
|
465
466
|
}
|
|
466
467
|
// document.getElementsByTagName('html')[0].innerHTML = data;
|
|
467
468
|
}
|
|
468
|
-
if (this.replace === 'target'
|
|
469
|
+
if (this.replace === 'target') {
|
|
469
470
|
// const target = this.getAttribute('target');
|
|
470
471
|
const target = this._getProperty('target');
|
|
471
472
|
const targetNode = document.querySelector(target);
|
|
472
473
|
if (targetNode) {
|
|
473
|
-
|
|
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
|
+
}
|
|
474
485
|
} else {
|
|
475
486
|
Fore.dispatch(this, 'submit-error', {
|
|
476
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
|
+
}
|