neo.mjs 9.15.0 → 10.0.0-alpha.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/ServiceWorker.mjs +2 -2
- package/apps/email/view/Viewport.mjs +2 -2
- package/apps/form/view/Viewport.mjs +1 -1
- package/apps/portal/index.html +1 -1
- package/apps/portal/view/examples/List.mjs +1 -1
- package/apps/portal/view/home/FooterContainer.mjs +1 -1
- package/apps/portal/view/learn/ContentComponent.mjs +5 -5
- package/apps/realworld2/view/HomeContainer.mjs +1 -1
- package/apps/route/view/center/CardAdministration.mjs +3 -3
- package/apps/route/view/center/CardAdministrationDenied.mjs +2 -2
- package/apps/route/view/center/CardContact.mjs +2 -2
- package/apps/route/view/center/CardHome.mjs +2 -2
- package/apps/route/view/center/CardSection1.mjs +2 -2
- package/apps/route/view/center/CardSection2.mjs +2 -2
- package/buildScripts/createApp.mjs +2 -2
- package/docs/app/view/classdetails/HeaderComponent.mjs +3 -3
- package/docs/app/view/classdetails/MembersList.mjs +43 -46
- package/docs/app/view/classdetails/SourceViewComponent.mjs +1 -1
- package/docs/app/view/classdetails/TutorialComponent.mjs +1 -1
- package/examples/component/toast/MainContainer.mjs +16 -16
- package/examples/component/wrapper/googleMaps/MarkerDialog.mjs +4 -4
- package/examples/fields/MainContainer.mjs +1 -1
- package/examples/panel/MainContainer.mjs +2 -2
- package/examples/tab/container/MainContainer.mjs +3 -3
- package/examples/tabs/MainContainer.mjs +2 -2
- package/examples/tabs/MainContainer2.mjs +3 -3
- package/examples/viewport/MainContainer.mjs +2 -2
- package/package.json +3 -3
- package/resources/data/deck/learnneo/pages/UsingTheseTopics.md +65 -0
- package/resources/data/deck/learnneo/pages/benefits/ConfigSystem.md +1 -1
- package/resources/data/deck/learnneo/pages/benefits/FormsEngine.md +7 -7
- package/resources/data/deck/learnneo/pages/benefits/FourEnvironments.md +10 -11
- package/resources/data/deck/learnneo/pages/benefits/Introduction.md +38 -5
- package/resources/data/deck/learnneo/pages/benefits/MultiWindow.md +1 -1
- package/resources/data/deck/learnneo/pages/benefits/Speed.md +1 -1
- package/resources/data/deck/learnneo/pages/gettingstarted/ComponentModels.md +2 -2
- package/resources/data/deck/learnneo/pages/gettingstarted/Config.md +3 -3
- package/resources/data/deck/learnneo/pages/gettingstarted/DescribingTheUI.md +2 -2
- package/resources/data/deck/learnneo/pages/gettingstarted/Events.md +3 -3
- package/resources/data/deck/learnneo/pages/gettingstarted/Extending.md +2 -2
- package/resources/data/deck/learnneo/pages/gettingstarted/References.md +3 -3
- package/resources/data/deck/learnneo/pages/gettingstarted/Workspaces.md +3 -3
- package/resources/data/deck/learnneo/pages/guides/ComponentsAndContainers.md +6 -6
- package/resources/data/deck/learnneo/pages/guides/CustomComponents.md +1 -1
- package/resources/data/deck/learnneo/pages/guides/MainThreadAddonIntro.md +1 -1
- package/resources/data/deck/learnneo/pages/guides/StateProviders.md +6 -6
- package/resources/data/deck/learnneo/pages/guides/events/CustomEvents.md +8 -8
- package/resources/data/deck/learnneo/pages/guides/events/DomEvents.md +11 -11
- package/resources/data/deck/learnneo/pages/javascript/Classes.md +4 -4
- package/resources/data/deck/learnneo/pages/javascript/NewNode.md +2 -2
- package/resources/data/deck/learnneo/pages/javascript/Overrides.md +4 -4
- package/resources/data/deck/learnneo/pages/tutorials/Earthquakes.md +21 -21
- package/resources/data/deck/learnneo/pages/tutorials/TodoList.md +2 -2
- package/resources/data/deck/learnneo/tree.json +1 -1
- package/resources/data/deck/training/pages/2022-12-27T21-55-23-144Z.md +2 -2
- package/resources/data/deck/training/pages/2022-12-29T18-36-08-226Z.md +1 -1
- package/resources/data/deck/training/pages/2022-12-29T18-36-56-893Z.md +2 -2
- package/resources/data/deck/training/pages/2022-12-29T20-37-08-919Z.md +2 -2
- package/resources/data/deck/training/pages/2022-12-29T20-37-20-344Z.md +2 -2
- package/resources/data/deck/training/pages/2023-01-13T21-48-17-258Z.md +2 -2
- package/resources/data/deck/training/pages/2023-02-05T17-44-53-815Z.md +9 -9
- package/resources/data/deck/training/pages/2023-10-14T19-25-08-153Z.md +1 -1
- package/resources/scss/src/apps/portal/learn/ContentComponent.scss +17 -13
- package/src/DefaultConfig.mjs +14 -2
- package/src/Main.mjs +14 -5
- package/src/button/Base.mjs +1 -1
- package/src/calendar/view/calendars/List.mjs +1 -1
- package/src/component/Base.mjs +11 -11
- package/src/component/Chip.mjs +1 -1
- package/src/component/Helix.mjs +3 -3
- package/src/component/Process.mjs +2 -2
- package/src/component/StatusBadge.mjs +2 -2
- package/src/component/Timer.mjs +1 -1
- package/src/component/Toast.mjs +2 -2
- package/src/container/Base.mjs +1 -1
- package/src/form/field/CheckBox.mjs +2 -2
- package/src/form/field/FileUpload.mjs +14 -14
- package/src/form/field/Range.mjs +1 -1
- package/src/form/field/Text.mjs +1 -1
- package/src/form/field/trigger/Base.mjs +2 -2
- package/src/form/field/trigger/SpinUpDown.mjs +2 -2
- package/src/grid/View.mjs +1 -1
- package/src/main/DeltaUpdates.mjs +382 -0
- package/src/main/DomAccess.mjs +13 -36
- package/src/main/render/DomApiRenderer.mjs +138 -0
- package/src/main/render/StringBasedRenderer.mjs +58 -0
- package/src/table/View.mjs +1 -1
- package/src/table/plugin/CellEditing.mjs +1 -1
- package/src/tree/Accordion.mjs +11 -11
- package/src/tree/List.mjs +12 -5
- package/src/vdom/Helper.mjs +174 -292
- package/src/vdom/VNode.mjs +47 -11
- package/src/vdom/domConstants.mjs +65 -0
- package/src/vdom/util/DomApiVnodeCreator.mjs +51 -0
- package/src/vdom/util/StringFromVnode.mjs +123 -0
- package/src/worker/mixin/RemoteMethodAccess.mjs +13 -1
- package/resources/data/deck/learnneo/pages/Welcome.md +0 -64
- package/src/main/mixin/DeltaUpdates.mjs +0 -352
package/src/vdom/Helper.mjs
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
-
import Base
|
2
|
-
import NeoArray
|
3
|
-
import
|
4
|
-
import
|
5
|
-
import VNode
|
1
|
+
import Base from '../core/Base.mjs';
|
2
|
+
import NeoArray from '../util/Array.mjs';
|
3
|
+
import Style from '../util/Style.mjs';
|
4
|
+
import {rawDimensionTags} from './domConstants.mjs';
|
5
|
+
import VNode from './VNode.mjs';
|
6
|
+
|
7
|
+
const NeoConfig = Neo.config;
|
6
8
|
|
7
9
|
/**
|
8
10
|
* The central class for the VDom worker to create vnodes & delta updates.
|
@@ -35,122 +37,6 @@ class Helper extends Base {
|
|
35
37
|
singleton: true
|
36
38
|
}
|
37
39
|
|
38
|
-
/**
|
39
|
-
* The following top-level attributes will get converted into styles:
|
40
|
-
* height, maxHeight, maxWidth, minHeight, minWidth, width
|
41
|
-
*
|
42
|
-
* Some tags must not do the transformation, so we add them here.
|
43
|
-
* @member {Set} rawDimensionTags
|
44
|
-
*/
|
45
|
-
rawDimensionTags = new Set([
|
46
|
-
'circle',
|
47
|
-
'clipPath',
|
48
|
-
'ellipse',
|
49
|
-
'filter',
|
50
|
-
'foreignObject',
|
51
|
-
'image',
|
52
|
-
'marker',
|
53
|
-
'mask',
|
54
|
-
'pattern',
|
55
|
-
'rect',
|
56
|
-
'svg',
|
57
|
-
'use'
|
58
|
-
])
|
59
|
-
/**
|
60
|
-
* @member {Boolean} returnChildNodeOuterHtml=false
|
61
|
-
*/
|
62
|
-
returnChildNodeOuterHtml = false
|
63
|
-
/**
|
64
|
-
* Void attributes inside html tags
|
65
|
-
* @member {Set} voidAttributes
|
66
|
-
* @protected
|
67
|
-
*/
|
68
|
-
voidAttributes = new Set([
|
69
|
-
'checked',
|
70
|
-
'defer',
|
71
|
-
'disabled',
|
72
|
-
'ismap',
|
73
|
-
'multiple',
|
74
|
-
'nohref',
|
75
|
-
'noresize',
|
76
|
-
'noshade',
|
77
|
-
'nowrap',
|
78
|
-
'open',
|
79
|
-
'readonly',
|
80
|
-
'required',
|
81
|
-
'reversed',
|
82
|
-
'selected'
|
83
|
-
])
|
84
|
-
/**
|
85
|
-
* Void html tags
|
86
|
-
* @member {Set} voidElements
|
87
|
-
* @protected
|
88
|
-
*/
|
89
|
-
voidElements = new Set([
|
90
|
-
'area',
|
91
|
-
'base',
|
92
|
-
'br',
|
93
|
-
'col',
|
94
|
-
'embed',
|
95
|
-
'hr',
|
96
|
-
'img',
|
97
|
-
'input',
|
98
|
-
'link',
|
99
|
-
'meta',
|
100
|
-
'param',
|
101
|
-
'source',
|
102
|
-
'track',
|
103
|
-
'wbr'
|
104
|
-
])
|
105
|
-
|
106
|
-
/**
|
107
|
-
* Creates a Neo.vdom.VNode tree for the given vdom template.
|
108
|
-
* The top level vnode contains the outerHTML as a string.
|
109
|
-
* @param {Object} opts
|
110
|
-
* @param {String} opts.appName
|
111
|
-
* @param {Boolean} [opts.autoMount]
|
112
|
-
* @param {String} opts.parentId
|
113
|
-
* @param {Number} opts.parentIndex
|
114
|
-
* @param {Object} opts.vdom
|
115
|
-
* @param {Number} opts.windowId
|
116
|
-
* @returns {Neo.vdom.VNode|Promise<Neo.vdom.VNode>}
|
117
|
-
*/
|
118
|
-
create(opts) {
|
119
|
-
let me = this,
|
120
|
-
autoMount = opts.autoMount === true,
|
121
|
-
{appName, parentId, parentIndex, windowId} = opts,
|
122
|
-
node;
|
123
|
-
|
124
|
-
delete opts.appName;
|
125
|
-
delete opts.autoMount;
|
126
|
-
delete opts.parentId;
|
127
|
-
delete opts.parentIndex;
|
128
|
-
delete opts.windowId;
|
129
|
-
|
130
|
-
node = me.createVnode(opts);
|
131
|
-
node.outerHTML = me.createStringFromVnode(node);
|
132
|
-
|
133
|
-
if (autoMount) {
|
134
|
-
Object.assign(node, {
|
135
|
-
appName,
|
136
|
-
autoMount: true,
|
137
|
-
parentId,
|
138
|
-
parentIndex,
|
139
|
-
windowId
|
140
|
-
})
|
141
|
-
}
|
142
|
-
|
143
|
-
return Neo.config.useVdomWorker ? node : Promise.resolve(node)
|
144
|
-
}
|
145
|
-
|
146
|
-
/**
|
147
|
-
* @param {Object} vnode
|
148
|
-
* @protected
|
149
|
-
*/
|
150
|
-
createCloseTag(vnode) {
|
151
|
-
return this.voidElements.has(vnode.nodeName) ? '' : '</' + vnode.nodeName + '>'
|
152
|
-
}
|
153
|
-
|
154
40
|
/**
|
155
41
|
* @param {Object} config
|
156
42
|
* @param {Object} config.deltas
|
@@ -158,6 +44,7 @@ class Helper extends Base {
|
|
158
44
|
* @param {Neo.vdom.VNode} config.vnode
|
159
45
|
* @param {Map} config.vnodeMap
|
160
46
|
* @returns {Object} deltas
|
47
|
+
* @protected
|
161
48
|
*/
|
162
49
|
compareAttributes(config) {
|
163
50
|
let {deltas, oldVnode, vnode, vnodeMap} = config,
|
@@ -219,6 +106,7 @@ class Helper extends Base {
|
|
219
106
|
break
|
220
107
|
case 'nodeName':
|
221
108
|
case 'innerHTML':
|
109
|
+
case 'textContent':
|
222
110
|
if (value !== oldVnode[prop]) {
|
223
111
|
delta[prop] = value
|
224
112
|
}
|
@@ -258,13 +146,46 @@ class Helper extends Base {
|
|
258
146
|
}
|
259
147
|
|
260
148
|
/**
|
261
|
-
*
|
262
|
-
*
|
263
|
-
*
|
264
|
-
* @param {
|
265
|
-
* @param {
|
266
|
-
* @param {
|
149
|
+
* Creates a Neo.vdom.VNode tree for the given vdom template.
|
150
|
+
* The top level vnode contains the outerHTML as a string,
|
151
|
+
* in case Neo.config.useStringBasedMounting === true
|
152
|
+
* @param {Object} opts
|
153
|
+
* @param {String} opts.appName
|
154
|
+
* @param {Boolean} [opts.autoMount]
|
155
|
+
* @param {String} opts.parentId
|
156
|
+
* @param {Number} opts.parentIndex
|
157
|
+
* @param {Object} opts.vdom
|
158
|
+
* @param {Number} opts.windowId
|
159
|
+
* @returns {Promise<Object>}
|
160
|
+
*/
|
161
|
+
async create(opts) {
|
162
|
+
let me = this,
|
163
|
+
returnValue, vnode;
|
164
|
+
|
165
|
+
await me.importDomApiVnodeCreator();
|
166
|
+
await me.importStringFromVnode();
|
167
|
+
|
168
|
+
vnode = me.createVnode(opts.vdom);
|
169
|
+
returnValue = {...opts, vnode};
|
170
|
+
|
171
|
+
delete returnValue.vdom;
|
172
|
+
|
173
|
+
if (NeoConfig.useStringBasedMounting) {
|
174
|
+
returnValue.outerHTML = Neo.vdom.util.StringFromVnode.create(vnode)
|
175
|
+
}
|
176
|
+
|
177
|
+
return returnValue
|
178
|
+
}
|
179
|
+
|
180
|
+
/**
|
181
|
+
* @param {Object} config
|
182
|
+
* @param {Object} [config.deltas={default: [], remove: []}]
|
183
|
+
* @param {Neo.vdom.VNode|Object} config.oldVnode
|
184
|
+
* @param {Map} [config.oldVnodeMap]
|
185
|
+
* @param {Neo.vdom.VNode|Object} config.vnode
|
186
|
+
* @param {Map} [config.vnodeMap]
|
267
187
|
* @returns {Object} deltas
|
188
|
+
* @protected
|
268
189
|
*/
|
269
190
|
createDeltas(config) {
|
270
191
|
let {deltas={default: [], remove: []}, oldVnode, vnode} = config,
|
@@ -356,124 +277,16 @@ class Helper extends Base {
|
|
356
277
|
return deltas
|
357
278
|
}
|
358
279
|
|
359
|
-
/**
|
360
|
-
* @param {Object} vnode
|
361
|
-
* @protected
|
362
|
-
*/
|
363
|
-
createOpenTag(vnode) {
|
364
|
-
let string = '<' + vnode.nodeName,
|
365
|
-
{attributes} = vnode,
|
366
|
-
cls = vnode.className,
|
367
|
-
style;
|
368
|
-
|
369
|
-
if (vnode.style) {
|
370
|
-
style = Neo.createStyles(vnode.style);
|
371
|
-
|
372
|
-
if (style !== '') {
|
373
|
-
string += ` style="${style}"`
|
374
|
-
}
|
375
|
-
}
|
376
|
-
|
377
|
-
if (cls) {
|
378
|
-
if (Array.isArray(cls)) {
|
379
|
-
cls = cls.join(' ')
|
380
|
-
}
|
381
|
-
|
382
|
-
if (cls !== '') {
|
383
|
-
string += ` class="${cls}"`
|
384
|
-
}
|
385
|
-
}
|
386
|
-
|
387
|
-
if (vnode.id) {
|
388
|
-
if (Neo.config.useDomIds) {
|
389
|
-
string += ` id="${vnode.id}"`
|
390
|
-
} else {
|
391
|
-
string += ` data-neo-id="${vnode.id}"`
|
392
|
-
}
|
393
|
-
}
|
394
|
-
|
395
|
-
Object.entries(attributes).forEach(([key, value]) => {
|
396
|
-
if (this.voidAttributes.has(key)) {
|
397
|
-
if (value === 'true') { // vnode attribute values get converted into strings
|
398
|
-
string += ` ${key}`
|
399
|
-
}
|
400
|
-
} else if (key !== 'removeDom') {
|
401
|
-
if (key === 'value') {
|
402
|
-
value = NeoString.escapeHtml(value)
|
403
|
-
}
|
404
|
-
|
405
|
-
string += ` ${key}="${value?.replaceAll?.('"', '"') ?? value}"`
|
406
|
-
}
|
407
|
-
});
|
408
|
-
|
409
|
-
return string + '>'
|
410
|
-
}
|
411
|
-
|
412
|
-
/**
|
413
|
-
* @param {Neo.vdom.VNode} vnode
|
414
|
-
* @param {Map} [movedNodes]
|
415
|
-
*/
|
416
|
-
createStringFromVnode(vnode, movedNodes) {
|
417
|
-
let me = this,
|
418
|
-
id = vnode?.id;
|
419
|
-
|
420
|
-
if (id && movedNodes?.get(id)) {
|
421
|
-
return ''
|
422
|
-
}
|
423
|
-
|
424
|
-
switch (vnode.vtype) {
|
425
|
-
case 'root':
|
426
|
-
return me.createStringFromVnode(vnode.childNodes[0], movedNodes)
|
427
|
-
case 'text':
|
428
|
-
return vnode.innerHTML === undefined ? '' : String(vnode.innerHTML)
|
429
|
-
case 'vnode':
|
430
|
-
return me.createOpenTag(vnode) + me.createTagContent(vnode, movedNodes) + me.createCloseTag(vnode)
|
431
|
-
default:
|
432
|
-
return ''
|
433
|
-
}
|
434
|
-
}
|
435
|
-
|
436
|
-
/**
|
437
|
-
* @param {Neo.vdom.VNode} vnode
|
438
|
-
* @param {Map} [movedNodes]
|
439
|
-
* @protected
|
440
|
-
*/
|
441
|
-
createTagContent(vnode, movedNodes) {
|
442
|
-
if (vnode.innerHTML) {
|
443
|
-
return vnode.innerHTML
|
444
|
-
}
|
445
|
-
|
446
|
-
let string = '',
|
447
|
-
len = vnode.childNodes ? vnode.childNodes.length : 0,
|
448
|
-
i = 0,
|
449
|
-
childNode, outerHTML;
|
450
|
-
|
451
|
-
for (; i < len; i++) {
|
452
|
-
childNode = vnode.childNodes[i];
|
453
|
-
outerHTML = this.createStringFromVnode(childNode, movedNodes);
|
454
|
-
|
455
|
-
if (childNode.innerHTML !== outerHTML) {
|
456
|
-
if (this.returnChildNodeOuterHtml) {
|
457
|
-
childNode.outerHTML = outerHTML
|
458
|
-
}
|
459
|
-
}
|
460
|
-
|
461
|
-
string += outerHTML
|
462
|
-
}
|
463
|
-
|
464
|
-
return string
|
465
|
-
}
|
466
|
-
|
467
280
|
/**
|
468
281
|
* @param {Object} opts
|
469
282
|
* @returns {Object|Neo.vdom.VNode|null}
|
283
|
+
* @protected
|
470
284
|
*/
|
471
285
|
createVnode(opts) {
|
472
286
|
// do not create vnode instances for component reference objects
|
473
287
|
if (opts.componentId) {
|
474
|
-
|
475
|
-
|
476
|
-
}
|
288
|
+
opts.childNodes ??= []; // Consistency: Every VNode has a childNodes array
|
289
|
+
opts.id ??= opts.componentId
|
477
290
|
|
478
291
|
return opts
|
479
292
|
}
|
@@ -482,43 +295,24 @@ class Helper extends Base {
|
|
482
295
|
return null
|
483
296
|
}
|
484
297
|
|
485
|
-
if (typeof opts === 'string') {
|
486
|
-
|
487
|
-
}
|
488
|
-
|
489
|
-
if (opts.vtype === 'text') {
|
490
|
-
if (!opts.id) {
|
491
|
-
opts.id = Neo.getId('vtext') // adding an id to be able to find vtype='text' items inside the vnode tree
|
492
|
-
}
|
493
|
-
|
494
|
-
opts.innerHTML = `<!-- ${opts.id} -->${opts.html || ''}<!-- /neo-vtext -->`;
|
495
|
-
delete opts.html;
|
496
|
-
return opts
|
497
|
-
}
|
498
|
-
|
499
298
|
let me = this,
|
500
|
-
node = {attributes: {},
|
299
|
+
node = {attributes: {}, style: {}},
|
501
300
|
potentialNode;
|
502
301
|
|
503
|
-
if (!opts.tag) {
|
504
|
-
opts.tag = 'div'
|
505
|
-
}
|
506
|
-
|
507
302
|
Object.entries(opts).forEach(([key, value]) => {
|
508
|
-
|
303
|
+
if (value !== undefined && value !== null && key !== 'flag' && key !== 'removeDom') {
|
304
|
+
let hasUnit, newValue, style;
|
509
305
|
|
510
|
-
|
511
|
-
switch (key) {
|
306
|
+
switch(key) {
|
512
307
|
case 'tag':
|
513
|
-
case 'nodeName':
|
514
308
|
node.nodeName = value;
|
515
309
|
break
|
516
310
|
case 'html':
|
517
|
-
case 'innerHTML':
|
518
311
|
node.innerHTML = value.toString(); // support for numbers
|
519
312
|
break
|
520
|
-
case '
|
521
|
-
|
313
|
+
case 'text':
|
314
|
+
node.textContent = value
|
315
|
+
break
|
522
316
|
case 'cn':
|
523
317
|
if (!Array.isArray(value)) {
|
524
318
|
value = [value]
|
@@ -559,7 +353,7 @@ class Helper extends Base {
|
|
559
353
|
case 'minHeight':
|
560
354
|
case 'minWidth':
|
561
355
|
case 'width':
|
562
|
-
if (
|
356
|
+
if (rawDimensionTags.has(node.nodeName)) {
|
563
357
|
node.attributes[key] = value + ''
|
564
358
|
} else {
|
565
359
|
hasUnit = value != parseInt(value);
|
@@ -580,14 +374,22 @@ class Helper extends Base {
|
|
580
374
|
}
|
581
375
|
break
|
582
376
|
default:
|
583
|
-
|
584
|
-
node.attributes[key] = value + ''
|
585
|
-
}
|
377
|
+
node.attributes[key] = value + '';
|
586
378
|
break
|
587
379
|
}
|
588
380
|
}
|
589
381
|
});
|
590
382
|
|
383
|
+
// Especially relevant for vtype='text'
|
384
|
+
if (Object.keys(node.attributes).length < 1) {
|
385
|
+
delete node.attributes
|
386
|
+
}
|
387
|
+
|
388
|
+
// Especially relevant for vtype='text'
|
389
|
+
if (Object.keys(node.style).length < 1) {
|
390
|
+
delete node.style
|
391
|
+
}
|
392
|
+
|
591
393
|
return new VNode(node)
|
592
394
|
}
|
593
395
|
|
@@ -603,6 +405,7 @@ class Helper extends Base {
|
|
603
405
|
* {Number} index
|
604
406
|
* {String} parentId
|
605
407
|
* {Neo.vdom.VNode} vnode
|
408
|
+
* @protected
|
606
409
|
*/
|
607
410
|
createVnodeMap(config) {
|
608
411
|
let {vnode, parentNode=null, index=0, map=new Map()} = config,
|
@@ -630,6 +433,7 @@ class Helper extends Base {
|
|
630
433
|
* @param {Neo.vdom.VNode} config.vnode
|
631
434
|
* @param {Map} config.vnodeMap
|
632
435
|
* @returns {Map}
|
436
|
+
* @protected
|
633
437
|
*/
|
634
438
|
findMovedNodes(config) {
|
635
439
|
let {movedNodes=new Map(), oldVnodeMap, vnode, vnodeMap} = config,
|
@@ -650,6 +454,28 @@ class Helper extends Base {
|
|
650
454
|
return movedNodes
|
651
455
|
}
|
652
456
|
|
457
|
+
/**
|
458
|
+
* Only import for the DOM API based mount adapter.
|
459
|
+
* @returns {Promise<void>}
|
460
|
+
* @protected
|
461
|
+
*/
|
462
|
+
async importDomApiVnodeCreator() {
|
463
|
+
if (!NeoConfig.useStringBasedMounting && !Neo.vdom.util?.DomApiVnodeCreator) {
|
464
|
+
await import('./util/DomApiVnodeCreator.mjs')
|
465
|
+
}
|
466
|
+
}
|
467
|
+
|
468
|
+
/**
|
469
|
+
* Only import for the string based mount adapter.
|
470
|
+
* @returns {Promise<void>}
|
471
|
+
* @protected
|
472
|
+
*/
|
473
|
+
async importStringFromVnode() {
|
474
|
+
if (NeoConfig.useStringBasedMounting && !Neo.vdom.util?.StringFromVnode) {
|
475
|
+
await import('./util/StringFromVnode.mjs')
|
476
|
+
}
|
477
|
+
}
|
478
|
+
|
653
479
|
/**
|
654
480
|
* @param {Object} config
|
655
481
|
* @param {Object} config.deltas
|
@@ -657,16 +483,44 @@ class Helper extends Base {
|
|
657
483
|
* @param {Map} config.oldVnodeMap
|
658
484
|
* @param {Neo.vdom.VNode} config.vnode
|
659
485
|
* @param {Map} config.vnodeMap
|
486
|
+
* @protected
|
660
487
|
*/
|
661
|
-
insertNode(
|
662
|
-
let
|
663
|
-
|
664
|
-
parentId
|
665
|
-
me
|
666
|
-
movedNodes
|
667
|
-
|
488
|
+
insertNode({deltas, index, oldVnodeMap, vnode, vnodeMap}) {
|
489
|
+
let details = vnodeMap.get(vnode.id),
|
490
|
+
{parentNode} = details,
|
491
|
+
parentId = parentNode.id,
|
492
|
+
me = this,
|
493
|
+
movedNodes = me.findMovedNodes({oldVnodeMap, vnode, vnodeMap}),
|
494
|
+
delta = {action: 'insertNode', parentId},
|
495
|
+
hasLeadingTextChildren = false,
|
496
|
+
physicalIndex = index, // Start with the logical index
|
497
|
+
i = 0,
|
498
|
+
siblingVnode;
|
499
|
+
|
500
|
+
// Calculate physicalIndex for DOM insertion and hasLeadingTextChildren flag
|
501
|
+
// This loop processes the children of the *NEW* parent's VNode in the *current* state (parentNode.childNodes)
|
502
|
+
// up to the logical insertion point.
|
503
|
+
for (; i < index; i++) {
|
504
|
+
siblingVnode = parentNode.childNodes[i];
|
505
|
+
|
506
|
+
// If we encounter a text VNode before the insertion point, adjust physicalIndex
|
507
|
+
if (siblingVnode?.vtype === 'text') {
|
508
|
+
physicalIndex += 2; // Each text VNode adds 2 comment nodes to the physical count
|
509
|
+
hasLeadingTextChildren = true
|
510
|
+
}
|
511
|
+
}
|
668
512
|
|
669
|
-
|
513
|
+
Object.assign(delta, {hasLeadingTextChildren, index: physicalIndex});
|
514
|
+
|
515
|
+
if (NeoConfig.useStringBasedMounting) {
|
516
|
+
// For string-based mounting, pass a string excluding moved nodes
|
517
|
+
delta.outerHTML = Neo.vdom.util.StringFromVnode.create(vnode, movedNodes)
|
518
|
+
} else {
|
519
|
+
// For direct DOM API mounting, pass the pruned VNode tree
|
520
|
+
delta.vnode = Neo.vdom.util.DomApiVnodeCreator.create(vnode, movedNodes)
|
521
|
+
}
|
522
|
+
|
523
|
+
deltas.default.push(delta);
|
670
524
|
|
671
525
|
// Insert the new node into the old tree, to simplify future OPs
|
672
526
|
oldVnodeMap.get(parentId).vnode.childNodes.splice(index, 0, vnode);
|
@@ -686,6 +540,7 @@ class Helper extends Base {
|
|
686
540
|
* @param {Neo.vdom.VNode} vnode
|
687
541
|
* @param {Map} oldVnodeMap
|
688
542
|
* @returns {Boolean}
|
543
|
+
* @protected
|
689
544
|
*/
|
690
545
|
isMovedNode(vnode, oldVnodeMap) {
|
691
546
|
let oldVnode = oldVnodeMap.get(vnode.id);
|
@@ -703,33 +558,56 @@ class Helper extends Base {
|
|
703
558
|
* @param {Map} config.oldVnodeMap
|
704
559
|
* @param {Neo.vdom.VNode} config.vnode
|
705
560
|
* @param {Map} config.vnodeMap
|
561
|
+
* @protected
|
706
562
|
*/
|
707
|
-
moveNode(
|
708
|
-
let
|
709
|
-
details = vnodeMap.get(vnode.id),
|
563
|
+
moveNode({deltas, insertDelta, oldVnodeMap, vnode, vnodeMap}) {
|
564
|
+
let details = vnodeMap.get(vnode.id),
|
710
565
|
{index, parentNode} = details,
|
711
566
|
parentId = parentNode.id,
|
712
567
|
movedNode = oldVnodeMap.get(vnode.id),
|
713
568
|
movedParentNode = movedNode.parentNode,
|
714
|
-
{childNodes} = movedParentNode
|
569
|
+
{childNodes} = movedParentNode,
|
570
|
+
delta = {action: 'moveNode', id: vnode.id, parentId},
|
571
|
+
physicalIndex = index, // Start with the logical index
|
572
|
+
i = 0,
|
573
|
+
siblingVnode;
|
574
|
+
|
575
|
+
// Calculate physicalIndex for DOM insertion.
|
576
|
+
// This loop processes the children of the *NEW* parent's VNode in the *current* state (parentNode.childNodes)
|
577
|
+
// up to the logical insertion point.
|
578
|
+
for (; i < index; i++) {
|
579
|
+
siblingVnode = parentNode.childNodes[i];
|
580
|
+
|
581
|
+
if (siblingVnode?.vtype === 'text') {
|
582
|
+
// Each text VNode adds 2 comment nodes to the physical count
|
583
|
+
physicalIndex += 2
|
584
|
+
}
|
585
|
+
}
|
715
586
|
|
587
|
+
Object.assign(delta, {index: physicalIndex + insertDelta});
|
588
|
+
deltas.default.push(delta);
|
589
|
+
|
590
|
+
// This block implements the "corrupting the old tree" optimization for performance.
|
591
|
+
// It pre-modifies the old VNode map to reflect the move, preventing redundant deltas later.
|
716
592
|
if (parentId !== movedParentNode.id) {
|
717
593
|
// We need to remove the node from the old parent childNodes
|
718
594
|
// (which must not be the same as the node they got moved into)
|
719
595
|
NeoArray.remove(childNodes, movedNode.vnode);
|
720
596
|
|
597
|
+
// Get the VNode representing the *new parent* from the 'old VNode map'.
|
598
|
+
// This is crucial: 'oldParentNode' here is the *old state's VNode for the new parent*.
|
721
599
|
let oldParentNode = oldVnodeMap.get(parentId);
|
722
600
|
|
723
601
|
if (oldParentNode) {
|
724
602
|
// If moved into a new parent node, update the reference inside the flat map
|
725
603
|
movedNode.parentNode = oldParentNode.vnode;
|
726
604
|
|
605
|
+
// Reassign 'childNodes' property to now point to the 'childNodes' array
|
606
|
+
// of this 'old state's VNode for the new parent'.
|
727
607
|
childNodes = movedNode.parentNode.childNodes
|
728
608
|
}
|
729
609
|
}
|
730
610
|
|
731
|
-
deltas.default.push({action: 'moveNode', id: vnode.id, index: index + insertDelta, parentId});
|
732
|
-
|
733
611
|
// Add the node into the old vnode tree to simplify future OPs.
|
734
612
|
// NeoArray.insert() will switch to move() in case the node already exists.
|
735
613
|
NeoArray.insert(childNodes, index, movedNode.vnode);
|
@@ -742,10 +620,11 @@ class Helper extends Base {
|
|
742
620
|
* @param {Object} config.deltas
|
743
621
|
* @param {Neo.vdom.VNode} config.oldVnode
|
744
622
|
* @param {Map} config.oldVnodeMap
|
623
|
+
* @protected
|
745
624
|
*/
|
746
625
|
removeNode({deltas, oldVnode, oldVnodeMap}) {
|
747
|
-
if (oldVnode.componentId
|
748
|
-
oldVnode.id
|
626
|
+
if (oldVnode.componentId) {
|
627
|
+
oldVnode.id ??= oldVnode.componentId
|
749
628
|
}
|
750
629
|
|
751
630
|
let delta = {action: 'removeNode', id: oldVnode.id},
|
@@ -766,20 +645,23 @@ class Helper extends Base {
|
|
766
645
|
* @param {Object} opts
|
767
646
|
* @param {Object} opts.vdom
|
768
647
|
* @param {Object} opts.vnode
|
769
|
-
* @returns {
|
648
|
+
* @returns {Promise<Object>}
|
770
649
|
*/
|
771
|
-
update(opts) {
|
772
|
-
let me
|
773
|
-
vnode
|
774
|
-
|
650
|
+
async update(opts) {
|
651
|
+
let me = this,
|
652
|
+
deltas, vnode;
|
653
|
+
|
654
|
+
await me.importDomApiVnodeCreator();
|
655
|
+
await me.importStringFromVnode();
|
656
|
+
|
657
|
+
vnode = me.createVnode(opts.vdom);
|
658
|
+
deltas = me.createDeltas({oldVnode: opts.vnode, vnode});
|
775
659
|
|
776
660
|
// Trees to remove could contain nodes which we want to re-use (move),
|
777
661
|
// so we need to execute the removeNode OPs last.
|
778
662
|
deltas = deltas.default.concat(deltas.remove);
|
779
663
|
|
780
|
-
|
781
|
-
|
782
|
-
return Neo.config.useVdomWorker ? returnObj : Promise.resolve(returnObj)
|
664
|
+
return {deltas, updateVdom: true, vnode}
|
783
665
|
}
|
784
666
|
}
|
785
667
|
|