@jinntec/fore 2.4.2 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/fore-dev.js +34262 -3
- package/dist/fore.js +34124 -3
- package/index.js +2 -0
- package/package.json +2 -2
- package/resources/fore.css +30 -1
- package/src/DataObserver.js +181 -0
- package/src/DependentXPathQueries.js +32 -0
- package/src/ForeElementMixin.js +289 -254
- package/src/actions/abstract-action.js +38 -5
- package/src/actions/fx-append.js +2 -0
- package/src/actions/fx-delete.js +13 -1
- package/src/actions/fx-hide.js +1 -1
- package/src/actions/fx-insert.js +47 -39
- package/src/actions/fx-load.js +7 -2
- package/src/actions/fx-send.js +0 -2
- package/src/actions/fx-setvalue.js +104 -82
- package/src/actions/fx-show.js +4 -2
- package/src/fore.js +40 -22
- package/src/fx-bind.js +3 -2
- package/src/fx-fore.js +320 -164
- package/src/fx-instance.js +3 -2
- package/src/fx-model.js +63 -34
- package/src/fx-submission.js +38 -22
- package/src/getInScopeContext.js +2 -1
- package/src/modelitem.js +107 -91
- package/src/ui/UIElement.js +91 -0
- package/src/ui/abstract-control.js +14 -9
- package/src/ui/fx-container.js +7 -4
- package/src/ui/fx-control-menu.js +198 -0
- package/src/ui/fx-control.js +64 -41
- package/src/ui/fx-dialog.js +2 -2
- package/src/ui/fx-group.js +14 -0
- package/src/ui/fx-repeat.js +42 -3
- package/src/ui/fx-repeatitem.js +3 -0
- package/src/ui/fx-upload.js +304 -0
- package/src/xpath-util.js +310 -168
- package/dist/fore-dev.js.map +0 -1
- package/dist/fore.js.map +0 -1
package/src/fx-fore.js
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
import getInScopeContext from './getInScopeContext.js';
|
|
12
12
|
import { XPathUtil } from './xpath-util.js';
|
|
13
13
|
import { FxRepeatAttributes } from './ui/fx-repeat-attributes.js';
|
|
14
|
+
import { ModelItem } from './modelitem.js';
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Makes the dirty state of the form.
|
|
@@ -49,6 +50,13 @@ export class FxFore extends HTMLElement {
|
|
|
49
50
|
|
|
50
51
|
static get properties() {
|
|
51
52
|
return {
|
|
53
|
+
/**
|
|
54
|
+
* wether to create nodes that are missing in the loaded data and
|
|
55
|
+
* auto-create nodes when there's a binding found in the UI.
|
|
56
|
+
*/
|
|
57
|
+
createNodes: {
|
|
58
|
+
type: Boolean,
|
|
59
|
+
},
|
|
52
60
|
/**
|
|
53
61
|
* ignore certain nodes for template expression search
|
|
54
62
|
*/
|
|
@@ -238,6 +246,9 @@ export class FxFore extends HTMLElement {
|
|
|
238
246
|
: 'update';
|
|
239
247
|
// this.mergePartial = this.hasAttribute('merge-partial')? true:false;
|
|
240
248
|
this.mergePartial = false;
|
|
249
|
+
this.createNodes = this.hasAttribute('create-nodes') ? true : false;
|
|
250
|
+
this._localNamesWithChanges = new Set();
|
|
251
|
+
this.setAttribute('role', 'form'); // set aria role
|
|
241
252
|
}
|
|
242
253
|
|
|
243
254
|
connectedCallback() {
|
|
@@ -245,23 +256,23 @@ export class FxFore extends HTMLElement {
|
|
|
245
256
|
console.time('init');
|
|
246
257
|
this.strict = !!this.hasAttribute('strict');
|
|
247
258
|
/*
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
259
|
+
document.addEventListener('ready', (e) =>{
|
|
260
|
+
if(e.target !== this){
|
|
261
|
+
// e.preventDefault();
|
|
262
|
+
console.log('>>> e', e);
|
|
263
|
+
console.log('event this', this);
|
|
264
|
+
// console.log('event eventPhase', e.eventPhase);
|
|
265
|
+
// console.log('event cancelable', e.cancelable);
|
|
266
|
+
console.log('event target', e.target);
|
|
267
|
+
console.log('event composed', e.composedPath());
|
|
268
|
+
console.log('<<< event stopping');
|
|
269
|
+
e.stopPropagation();
|
|
270
|
+
}else{
|
|
271
|
+
console.log('event proceed', this);
|
|
272
|
+
}
|
|
273
|
+
// e.stopImmediatePropagation();
|
|
274
|
+
},true);
|
|
275
|
+
*/
|
|
265
276
|
this.ignoreExpressions = this.hasAttribute('ignore-expressions')
|
|
266
277
|
? this.getAttribute('ignore-expressions')
|
|
267
278
|
: null;
|
|
@@ -359,6 +370,11 @@ export class FxFore extends HTMLElement {
|
|
|
359
370
|
const devtools = document.createElement('fx-devtools');
|
|
360
371
|
document.body.appendChild(devtools);
|
|
361
372
|
}
|
|
373
|
+
if (urlParams.has('lens')) {
|
|
374
|
+
const lens = document.createElement('fx-lens');
|
|
375
|
+
document.body.appendChild(lens);
|
|
376
|
+
lens.setAttribute('open', 'open');
|
|
377
|
+
}
|
|
362
378
|
}
|
|
363
379
|
|
|
364
380
|
/**
|
|
@@ -374,6 +390,16 @@ export class FxFore extends HTMLElement {
|
|
|
374
390
|
}
|
|
375
391
|
}
|
|
376
392
|
|
|
393
|
+
/**
|
|
394
|
+
* Signal something happened with an element with the given local name. This will be used in the
|
|
395
|
+
* next (non-forceful) refresh to detect whether a component (usually a repeat) should update
|
|
396
|
+
*
|
|
397
|
+
* @param {string} localNameOfElement
|
|
398
|
+
*/
|
|
399
|
+
signalChangeToElement(localNameOfElement) {
|
|
400
|
+
this._localNamesWithChanges.add(localNameOfElement);
|
|
401
|
+
}
|
|
402
|
+
|
|
377
403
|
/**
|
|
378
404
|
* Raise a flag that there might be new template expressions under some node. This happens with
|
|
379
405
|
* repeats updating (new repeat items can have new template expressions) or switches changing their case (new case = new raw HTML)
|
|
@@ -450,12 +476,12 @@ export class FxFore extends HTMLElement {
|
|
|
450
476
|
disconnectedCallback() {
|
|
451
477
|
this.removeEventListener('dragstart', this.dragstart);
|
|
452
478
|
/*
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
479
|
+
this.removeEventListener('model-construct-done', this._handleModelConstructDone);
|
|
480
|
+
this.removeEventListener('message', this._displayMessage);
|
|
481
|
+
this.removeEventListener('error', this._displayError);
|
|
482
|
+
this.storedTemplateExpressionByNode=null;
|
|
483
|
+
this.shadowRoot = undefined;
|
|
484
|
+
*/
|
|
459
485
|
}
|
|
460
486
|
|
|
461
487
|
/**
|
|
@@ -488,106 +514,45 @@ export class FxFore extends HTMLElement {
|
|
|
488
514
|
* @param {(boolean|{reason:'index-function'})} [force]fx-fore
|
|
489
515
|
*/
|
|
490
516
|
async refresh(force) {
|
|
491
|
-
/*
|
|
492
|
-
|
|
493
|
-
if (!changedPaths) {
|
|
494
|
-
changedPaths = this.toRefresh.map(item => item.path);
|
|
495
|
-
} else {
|
|
496
|
-
this.toRefresh.push(
|
|
497
|
-
...changedPaths
|
|
498
|
-
.map(
|
|
499
|
-
path =>
|
|
500
|
-
this.getModel()
|
|
501
|
-
.modelItems
|
|
502
|
-
.find(item => item.path === path)
|
|
503
|
-
)
|
|
504
|
-
.filter(Boolean)
|
|
505
|
-
);
|
|
506
|
-
|
|
507
|
-
for(const changedPath of changedPaths) {
|
|
508
|
-
for (const repeat of this.querySelectorAll('fx-repeat')) {
|
|
509
|
-
if (repeat.closest('fx-fore') !== this) {
|
|
510
|
-
continue;
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
if (repeat.touchedPaths && repeat.touchedPaths.has(changedPath)) {
|
|
514
|
-
// Make a temporary model-item-like structure for this
|
|
515
|
-
this.toRefresh.push({
|
|
516
|
-
path: changedPath,
|
|
517
|
-
boundControls: [repeat]
|
|
518
|
-
});
|
|
519
|
-
|
|
520
|
-
console.log('Found a repeat to update!!!', repeat)
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
*/
|
|
526
517
|
if (this.isRefreshing) {
|
|
527
518
|
return;
|
|
528
519
|
}
|
|
520
|
+
|
|
521
|
+
if (force !== true && this._localNamesWithChanges.size > 0) {
|
|
522
|
+
force = {
|
|
523
|
+
...(force || { reason: undefined }),
|
|
524
|
+
elementLocalnamesWithChanges: Array.from(this._localNamesWithChanges),
|
|
525
|
+
};
|
|
526
|
+
this._localNamesWithChanges.clear();
|
|
527
|
+
}
|
|
528
|
+
|
|
529
529
|
this.isRefreshing = true;
|
|
530
|
-
// console.log(`### <<<<< refresh() on '${this.id}' >>>>>`);
|
|
531
530
|
|
|
532
531
|
// refresh () {
|
|
533
532
|
// ### refresh Fore UI elements
|
|
534
533
|
// if (!this.initialRun && this.toRefresh.length !== 0) {
|
|
534
|
+
// if (!this.initialRun && this.toRefresh.length !== 0) {
|
|
535
535
|
if (!force && !this.initialRun && this.toRefresh.length !== 0) {
|
|
536
|
-
|
|
537
|
-
let needsRefresh = false;
|
|
538
|
-
|
|
539
|
-
// ### after recalculation the changed modelItems are copied to 'toRefresh' array for processing
|
|
540
|
-
this.toRefresh.forEach(modelItem => {
|
|
541
|
-
// check if modelItem has boundControls - if so, call refresh() for each of them
|
|
542
|
-
const controlsToRefresh = modelItem.boundControls;
|
|
543
|
-
if (controlsToRefresh) {
|
|
544
|
-
controlsToRefresh.forEach(ctrl => {
|
|
545
|
-
ctrl.refresh(force);
|
|
546
|
-
});
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
// ### check if other controls depend on current modelItem
|
|
550
|
-
const { mainGraph } = this.getModel();
|
|
551
|
-
if (mainGraph && mainGraph.hasNode(modelItem.path)) {
|
|
552
|
-
const deps = this.getModel().mainGraph.dependentsOf(modelItem.path, false);
|
|
553
|
-
// ### iterate dependant modelItems and refresh all their boundControls
|
|
554
|
-
if (deps.length !== 0) {
|
|
555
|
-
deps.forEach(dep => {
|
|
556
|
-
// ### if changed modelItem has a 'facet' path we use the basePath that is the locationPath without facet name
|
|
557
|
-
const basePath = XPathUtil.getBasePath(dep);
|
|
558
|
-
const modelItemOfDep = this.getModel().modelItems.find(mip => mip.path === basePath);
|
|
559
|
-
// ### refresh all boundControls
|
|
560
|
-
modelItemOfDep.boundControls.forEach(control => {
|
|
561
|
-
control.refresh(force);
|
|
562
|
-
});
|
|
563
|
-
});
|
|
564
|
-
needsRefresh = true;
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
});
|
|
568
|
-
this.toRefresh = [];
|
|
569
|
-
/*
|
|
570
|
-
if (!needsRefresh) {
|
|
571
|
-
console.log('no dependants to refresh');
|
|
572
|
-
}
|
|
573
|
-
*/
|
|
536
|
+
this.refreshChanged(force);
|
|
574
537
|
} else {
|
|
575
538
|
// ### resetting visited state for controls to refresh
|
|
576
539
|
/*
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
*/
|
|
540
|
+
const visited = this.parentNode.querySelectorAll('.visited');
|
|
541
|
+
Array.from(visited).forEach(v =>{
|
|
542
|
+
v.classList.remove('visited');
|
|
543
|
+
});
|
|
544
|
+
*/
|
|
582
545
|
|
|
583
546
|
if (this.inited) {
|
|
547
|
+
console.log(`### <<<<< refresh() on '${this.id}' >>>>>`);
|
|
548
|
+
|
|
584
549
|
Fore.refreshChildren(this, force);
|
|
585
550
|
}
|
|
586
551
|
// console.timeEnd('refreshChildren');
|
|
587
552
|
}
|
|
588
553
|
|
|
589
554
|
// ### refresh template expressions
|
|
590
|
-
if (this.initialRun || this._scanForNewTemplateExpressionsNextRefresh) {
|
|
555
|
+
if (force || this.initialRun || this._scanForNewTemplateExpressionsNextRefresh) {
|
|
591
556
|
this._updateTemplateExpressions();
|
|
592
557
|
this._scanForNewTemplateExpressionsNextRefresh = false; // reset
|
|
593
558
|
}
|
|
@@ -598,7 +563,11 @@ export class FxFore extends HTMLElement {
|
|
|
598
563
|
// this.dispatchEvent(new CustomEvent('refresh-done'));
|
|
599
564
|
// this.initialRun = false;
|
|
600
565
|
this.style.visibility = 'visible';
|
|
601
|
-
console.
|
|
566
|
+
console.info(
|
|
567
|
+
`%crefresh-done on #${this.id}`,
|
|
568
|
+
'background:darkorange; color:black; padding:.5rem; display:inline-block; white-space: nowrap; border-radius:0.3rem;width:100%;',
|
|
569
|
+
);
|
|
570
|
+
|
|
602
571
|
Fore.dispatch(this, 'refresh-done', {});
|
|
603
572
|
|
|
604
573
|
// this.isRefreshing = true;
|
|
@@ -606,29 +575,66 @@ export class FxFore extends HTMLElement {
|
|
|
606
575
|
|
|
607
576
|
const subFores = Array.from(this.querySelectorAll('fx-fore'));
|
|
608
577
|
/*
|
|
609
|
-
|
|
610
|
-
|
|
578
|
+
calling the parent to refresh causes errors and inconsistent state. Also it is questionable
|
|
579
|
+
if a child should actually interact with its parent in this way.
|
|
611
580
|
|
|
612
|
-
|
|
581
|
+
This only affects the refreshing NOT the data mutation itself which is happening as expected.
|
|
613
582
|
|
|
614
|
-
|
|
615
|
-
|
|
583
|
+
Current solution is that a child that wants the parent to refresh must do so by adding an additional
|
|
584
|
+
event handler that dispatches an event upwards and having a handler in the parent to refresh itself.
|
|
616
585
|
|
|
617
|
-
|
|
586
|
+
So refreshed propagate downwards but not upwards which is at least an option to consider.
|
|
618
587
|
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
*/
|
|
588
|
+
if(this.parentNode.nodeType !== Node.DOCUMENT_FRAGMENT_NODE){
|
|
589
|
+
// await this.parentNode.closest('fx-fore')?.refresh(false);
|
|
590
|
+
}
|
|
591
|
+
*/
|
|
623
592
|
for (const subFore of subFores) {
|
|
624
593
|
// subFore.refresh(false, changedPaths);
|
|
625
594
|
if (subFore.ready) {
|
|
626
|
-
|
|
595
|
+
// Do an unconditional hard refresh: there might be changes that are relevant
|
|
596
|
+
await subFore.refresh(true);
|
|
627
597
|
}
|
|
628
598
|
}
|
|
629
599
|
this.isRefreshing = false;
|
|
630
600
|
}
|
|
631
601
|
|
|
602
|
+
refreshChanged(force) {
|
|
603
|
+
console.log('toRefresh', this.toRefresh);
|
|
604
|
+
let needsRefresh = false;
|
|
605
|
+
|
|
606
|
+
// ### after recalculation the changed modelItems are copied to 'toRefresh' array for processing
|
|
607
|
+
this.toRefresh.forEach(modelItem => {
|
|
608
|
+
// check if modelItem has boundControls - if so, call refresh() for each of them
|
|
609
|
+
const controlsToRefresh = modelItem.boundControls;
|
|
610
|
+
if (controlsToRefresh) {
|
|
611
|
+
controlsToRefresh.forEach(ctrl => {
|
|
612
|
+
ctrl.refresh(force);
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// ### check if other controls depend on current modelItem
|
|
617
|
+
const { mainGraph } = this.getModel();
|
|
618
|
+
if (mainGraph && mainGraph.hasNode(modelItem.path)) {
|
|
619
|
+
const deps = this.getModel().mainGraph.dependentsOf(modelItem.path, false);
|
|
620
|
+
// ### iterate dependant modelItems and refresh all their boundControls
|
|
621
|
+
if (deps.length !== 0) {
|
|
622
|
+
deps.forEach(dep => {
|
|
623
|
+
// ### if changed modelItem has a 'facet' path we use the basePath that is the locationPath without facet name
|
|
624
|
+
const basePath = XPathUtil.getBasePath(dep);
|
|
625
|
+
const modelItemOfDep = this.getModel().modelItems.find(mip => mip.path === basePath);
|
|
626
|
+
// ### refresh all boundControls
|
|
627
|
+
modelItemOfDep.boundControls.forEach(control => {
|
|
628
|
+
control.refresh(force);
|
|
629
|
+
});
|
|
630
|
+
});
|
|
631
|
+
needsRefresh = true;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
});
|
|
635
|
+
this.toRefresh = [];
|
|
636
|
+
}
|
|
637
|
+
|
|
632
638
|
/**
|
|
633
639
|
* entry point for processing of template expression enclosed in '{}' brackets.
|
|
634
640
|
*
|
|
@@ -651,8 +657,8 @@ export class FxFore extends HTMLElement {
|
|
|
651
657
|
// console.log('######### storedTemplateExpressions', this.storedTemplateExpressions.length);
|
|
652
658
|
|
|
653
659
|
/*
|
|
654
|
-
|
|
655
|
-
|
|
660
|
+
storing expressions and their nodes for re-evaluation
|
|
661
|
+
*/
|
|
656
662
|
Array.from(tmplExpressions).forEach(node => {
|
|
657
663
|
const ele = node.nodeType === Node.ATTRIBUTE_NODE ? node.ownerElement : node.parentNode;
|
|
658
664
|
if (ele.closest('fx-fore') !== this) {
|
|
@@ -891,43 +897,43 @@ export class FxFore extends HTMLElement {
|
|
|
891
897
|
}
|
|
892
898
|
|
|
893
899
|
/*
|
|
894
|
-
|
|
900
|
+
_createStep(){
|
|
895
901
|
|
|
896
|
-
|
|
897
|
-
|
|
902
|
+
}
|
|
903
|
+
*/
|
|
898
904
|
|
|
899
905
|
/*
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
906
|
+
_generateInstance(start, parent) {
|
|
907
|
+
if (start.hasAttribute('ref')) {
|
|
908
|
+
const ref = start.getAttribute('ref');
|
|
903
909
|
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
910
|
+
if(ref.includes('/')){
|
|
911
|
+
console.log('complex path to create ', ref);
|
|
912
|
+
const steps = ref.split('/');
|
|
913
|
+
steps.forEach(step => {
|
|
914
|
+
console.log('step ', step);
|
|
909
915
|
|
|
910
|
-
|
|
911
|
-
|
|
916
|
+
});
|
|
917
|
+
}
|
|
912
918
|
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
919
|
+
// const generated = document.createElement(ref);
|
|
920
|
+
const generated = parent.ownerDocument.createElement(ref);
|
|
921
|
+
if (start.children.length === 0) {
|
|
922
|
+
generated.textContent = start.textContent;
|
|
923
|
+
}
|
|
924
|
+
parent.appendChild(generated);
|
|
925
|
+
parent = generated;
|
|
917
926
|
}
|
|
918
|
-
parent.appendChild(generated);
|
|
919
|
-
parent = generated;
|
|
920
|
-
}
|
|
921
927
|
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
928
|
+
if (start.hasChildNodes()) {
|
|
929
|
+
const list = start.children;
|
|
930
|
+
for (let i = 0; i < list.length; i += 1) {
|
|
931
|
+
this._generateInstance(list[i], parent);
|
|
932
|
+
}
|
|
926
933
|
}
|
|
934
|
+
return parent;
|
|
927
935
|
}
|
|
928
|
-
|
|
929
|
-
}
|
|
930
|
-
*/
|
|
936
|
+
*/
|
|
931
937
|
|
|
932
938
|
/**
|
|
933
939
|
* Start the initialization of the UI by
|
|
@@ -941,22 +947,29 @@ export class FxFore extends HTMLElement {
|
|
|
941
947
|
*/
|
|
942
948
|
async _initUI() {
|
|
943
949
|
// console.log('### _initUI()');
|
|
944
|
-
console.
|
|
950
|
+
console.info(
|
|
951
|
+
`%cinitUI #${this.id}`,
|
|
952
|
+
'background:lightblue; color:black; padding:.5rem; display:inline-block; white-space: nowrap; border-radius:0.3rem;width:100%;',
|
|
953
|
+
);
|
|
945
954
|
|
|
946
955
|
if (!this.initialRun) return;
|
|
947
956
|
this.classList.add('initialRun');
|
|
948
957
|
await this._lazyCreateInstance();
|
|
949
958
|
|
|
950
959
|
/*
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
960
|
+
const options = {
|
|
961
|
+
root: null,
|
|
962
|
+
rootMargin: '0px',
|
|
963
|
+
threshold: 0.3,
|
|
964
|
+
};
|
|
965
|
+
*/
|
|
957
966
|
|
|
958
967
|
// First refresh should be forced
|
|
968
|
+
if (this.createNodes) {
|
|
969
|
+
this.initData();
|
|
970
|
+
}
|
|
959
971
|
await this.refresh(true);
|
|
972
|
+
|
|
960
973
|
// this.style.display='block'
|
|
961
974
|
this.classList.add('fx-ready');
|
|
962
975
|
document.body.classList.add('fx-ready');
|
|
@@ -964,7 +977,12 @@ export class FxFore extends HTMLElement {
|
|
|
964
977
|
this.ready = true;
|
|
965
978
|
this.initialRun = false;
|
|
966
979
|
// console.log('### >>>>> dispatching ready >>>>>', this);
|
|
967
|
-
console.
|
|
980
|
+
console.info(
|
|
981
|
+
`%c #${this.id} is ready`,
|
|
982
|
+
'background:lightblue; color:black; padding:.5rem; display:inline-block; white-space: nowrap; border-radius:0.3rem;width:100%;',
|
|
983
|
+
);
|
|
984
|
+
|
|
985
|
+
// console.log(`### <<<<< ${this.id} ready >>>>>`);
|
|
968
986
|
|
|
969
987
|
// console.log('### modelItems: ', this.getModel().modelItems);
|
|
970
988
|
Fore.dispatch(this, 'ready', {});
|
|
@@ -982,6 +1000,144 @@ export class FxFore extends HTMLElement {
|
|
|
982
1000
|
});
|
|
983
1001
|
}
|
|
984
1002
|
|
|
1003
|
+
/**
|
|
1004
|
+
* @param {HTMLElement} root The root of the data initialization. fx-repeat overrides this when it makes new repeat items
|
|
1005
|
+
*
|
|
1006
|
+
*/
|
|
1007
|
+
initData(root = this) {
|
|
1008
|
+
// const created = new Promise(resolve => {
|
|
1009
|
+
// console.log('INIT');
|
|
1010
|
+
// const boundControls = Array.from(root.querySelectorAll('[ref]:not(fx-model *),fx-repeatitem'));
|
|
1011
|
+
|
|
1012
|
+
const boundControls = Array.from(
|
|
1013
|
+
root.querySelectorAll(
|
|
1014
|
+
'fx-control[ref],fx-upload[ref],fx-group[ref],fx-repeat[ref], fx-switch[ref]',
|
|
1015
|
+
),
|
|
1016
|
+
);
|
|
1017
|
+
if (root.matches('fx-repeatitem')) {
|
|
1018
|
+
boundControls.unshift(root);
|
|
1019
|
+
}
|
|
1020
|
+
// console.log('_initD', boundControls);
|
|
1021
|
+
for (let i = 0; i < boundControls.length; i++) {
|
|
1022
|
+
const bound = boundControls[i];
|
|
1023
|
+
|
|
1024
|
+
/*
|
|
1025
|
+
ignore bound elements that are enclosed with a control like <select> or <fx-items> and repeated items
|
|
1026
|
+
*/
|
|
1027
|
+
if (!bound.matches('fx-repeatitem') && !bound.parentNode.closest('fx-control')) {
|
|
1028
|
+
// Repeat items are dumb. They do not respond to evalInContext
|
|
1029
|
+
bound.evalInContext();
|
|
1030
|
+
}
|
|
1031
|
+
let ownerDoc;
|
|
1032
|
+
if (bound.nodeset !== null) {
|
|
1033
|
+
// console.log('Node exists', control.nodeset);
|
|
1034
|
+
continue;
|
|
1035
|
+
}
|
|
1036
|
+
// console.log('Node does not exists', control.ref);
|
|
1037
|
+
|
|
1038
|
+
// We need to create that node!
|
|
1039
|
+
const previousControl = boundControls[i - 1];
|
|
1040
|
+
|
|
1041
|
+
// Previous control can either be an ancestor of us, or a previous node, which can be a sibling, or a child of a sibling.
|
|
1042
|
+
// First: parent
|
|
1043
|
+
if (previousControl.contains(bound)) {
|
|
1044
|
+
// Parent is here.
|
|
1045
|
+
// console.log('insert into', control,previousControl);
|
|
1046
|
+
// console.log('insert into nodeset', control.nodeset);
|
|
1047
|
+
const parentNodeset = previousControl.nodeset;
|
|
1048
|
+
// console.log('parentNodeset', parentNodeset);
|
|
1049
|
+
|
|
1050
|
+
// const parentModelItemNode = parentModelItem.node;
|
|
1051
|
+
const ref = bound.ref;
|
|
1052
|
+
// const newElement = parentModelItemNode.ownerDocument.createElement(ref);
|
|
1053
|
+
if (parentNodeset.querySelector(`[ref="${ref}"]`)) {
|
|
1054
|
+
console.log(`Node with ref "${ref}" already exists.`);
|
|
1055
|
+
continue;
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
const newElement = this._createNodes(ref, parentNodeset);
|
|
1059
|
+
|
|
1060
|
+
// Plonk it in at the start!
|
|
1061
|
+
parentNodeset.insertBefore(newElement, parentNodeset.firstChild);
|
|
1062
|
+
bound.evalInContext();
|
|
1063
|
+
// console.log('CREATED child', newElement);
|
|
1064
|
+
// console.log('new control evaluated to ', control.nodeset);
|
|
1065
|
+
// Done!
|
|
1066
|
+
continue;
|
|
1067
|
+
}
|
|
1068
|
+
// console.log('previousControl', previousControl);
|
|
1069
|
+
// console.log('control', control);
|
|
1070
|
+
// Is previousControl a sibling or a descendant of a logical sibling? Keep looking backwards until we share parents!
|
|
1071
|
+
const ourParent = XPathUtil.getParentBindingElement(bound);
|
|
1072
|
+
// console.log('ourParent', ourParent);
|
|
1073
|
+
let siblingControl = null;
|
|
1074
|
+
/*
|
|
1075
|
+
for (let j = i - 1; j >= 0; --j) {
|
|
1076
|
+
const potentialSibling = boundControls[j];
|
|
1077
|
+
if (XPathUtil.getParentBindingElement(potentialSibling) === ourParent) {
|
|
1078
|
+
siblingControl = potentialSibling;
|
|
1079
|
+
break; // Exit once the sibling is found
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
*/
|
|
1083
|
+
for (let j = i - 1; j > 0; --j) {
|
|
1084
|
+
const siblingOrDescendant = boundControls[j];
|
|
1085
|
+
if (XPathUtil.getParentBindingElement(siblingOrDescendant) === ourParent) {
|
|
1086
|
+
siblingControl = siblingOrDescendant;
|
|
1087
|
+
break;
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
if (!siblingControl) {
|
|
1091
|
+
throw new Error('Unexpected! there must be a sibling right?');
|
|
1092
|
+
}
|
|
1093
|
+
// console.log('sibling', siblingControl);
|
|
1094
|
+
// todo: review: should this not just be inscopeContext?
|
|
1095
|
+
const parentNodeset = ourParent.nodeset;
|
|
1096
|
+
const ref = bound.ref;
|
|
1097
|
+
let referenceNodeset = siblingControl.nodeset;
|
|
1098
|
+
const newElement = this._createNodes(ref, parentNodeset);
|
|
1099
|
+
|
|
1100
|
+
// We know which node to insert this new element to, but it might be a descendant of a child of the actual parent. Walk up until we have a reference under our parent
|
|
1101
|
+
while (referenceNodeset?.parentNode && referenceNodeset?.parentNode !== parentNodeset) {
|
|
1102
|
+
referenceNodeset = referenceNodeset.parentNode;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
// Insert before the next sibling our our logical previous sibling
|
|
1106
|
+
parentNodeset.insertBefore(newElement, referenceNodeset.nextElementSibling);
|
|
1107
|
+
/*
|
|
1108
|
+
console.log('control inscope', control.getInScopeContext());
|
|
1109
|
+
console.log('control ref', control.ref);
|
|
1110
|
+
console.log('control new element parent', newElement.parentNode.nodeName);
|
|
1111
|
+
*/
|
|
1112
|
+
bound.evalInContext();
|
|
1113
|
+
// console.log('new control evaluated to ', control.nodeset);
|
|
1114
|
+
// console.log('CREATED sibling', newElement);
|
|
1115
|
+
}
|
|
1116
|
+
// console.log('DATA', this.getModel().getDefaultContext());
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
_createNodes(ref, referenceNode) {
|
|
1120
|
+
// console.log('creating', ref)
|
|
1121
|
+
// console.log('ownerDoc', referenceNode.ownerDocument);
|
|
1122
|
+
/*
|
|
1123
|
+
const existingNode = evaluateXPathToFirstNode(ref, referenceNode, this);
|
|
1124
|
+
if(existingNode){
|
|
1125
|
+
console.log(`Node already exists for ref: ${ref}`);
|
|
1126
|
+
return existingNode;
|
|
1127
|
+
}
|
|
1128
|
+
console.log(`creating new node for ref: ${ref}`);
|
|
1129
|
+
*/
|
|
1130
|
+
let newElement;
|
|
1131
|
+
if (ref.includes('/')) {
|
|
1132
|
+
// multi-step ref expressions
|
|
1133
|
+
newElement = XPathUtil.createNodesFromXPath(ref, referenceNode.ownerDocument, this);
|
|
1134
|
+
// console.log('new subtree', newElement);
|
|
1135
|
+
return newElement;
|
|
1136
|
+
} else {
|
|
1137
|
+
return XPathUtil.createNodesFromXPath(ref, referenceNode.ownerDocument, this);
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
|
|
985
1141
|
_handleDragStart(event) {
|
|
986
1142
|
const draggedItem = event.target.closest('[draggable="true"]');
|
|
987
1143
|
this.originalDraggedItem = draggedItem;
|
|
@@ -1107,18 +1263,18 @@ export class FxFore extends HTMLElement {
|
|
|
1107
1263
|
Array.from(repeats).forEach(item => {
|
|
1108
1264
|
if (item.closest('fx-control')) return;
|
|
1109
1265
|
/*
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
*/
|
|
1266
|
+
const parentRepeat = item.closest('fx-repeat');
|
|
1267
|
+
if(parentRepeat){
|
|
1268
|
+
this.dispatchEvent(
|
|
1269
|
+
new CustomEvent('log', {
|
|
1270
|
+
composed: false,
|
|
1271
|
+
bubbles: true,
|
|
1272
|
+
cancelable:true,
|
|
1273
|
+
detail: { id:this.id, message: `nesting elements with data-ref attributes within fx-repeat is not supported by now`, level:'Error'},
|
|
1274
|
+
}),
|
|
1275
|
+
);
|
|
1276
|
+
}
|
|
1277
|
+
*/
|
|
1122
1278
|
|
|
1123
1279
|
const table = item.parentNode.closest('table');
|
|
1124
1280
|
let host;
|
package/src/fx-instance.js
CHANGED
|
@@ -213,8 +213,9 @@ export class FxInstance extends HTMLElement {
|
|
|
213
213
|
this.originalInstance = [...this.instanceData];
|
|
214
214
|
}
|
|
215
215
|
if (this.type === 'text') {
|
|
216
|
-
this.instanceData =
|
|
217
|
-
this.originalInstance =
|
|
216
|
+
this.instanceData = this.innerText;
|
|
217
|
+
this.originalInstance = this.innerText;
|
|
218
|
+
console.log('text data', this.instanceData);
|
|
218
219
|
}
|
|
219
220
|
}
|
|
220
221
|
|