neo.mjs 9.16.0 → 10.0.0-alpha.2
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/README.md +127 -121
- 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/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 +33 -5
- package/resources/data/deck/learnneo/pages/benefits/FourEnvironments.md +1 -1
- package/resources/data/deck/learnneo/pages/benefits/Introduction.md +4 -3
- package/resources/data/deck/learnneo/pages/guides/events/DomEvents.md +5 -5
- 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/src/DefaultConfig.mjs +14 -2
- package/src/Main.mjs +15 -6
- 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 +442 -0
- package/src/main/DomAccess.mjs +13 -95
- 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 +179 -309
- 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/src/main/mixin/DeltaUpdates.mjs +0 -352
@@ -0,0 +1,138 @@
|
|
1
|
+
import {voidAttributes} from '../../vdom/domConstants.mjs';
|
2
|
+
|
3
|
+
const DomApiRenderer = {
|
4
|
+
/**
|
5
|
+
* Recursively creates a DOM element (or DocumentFragment) from a VNode tree.
|
6
|
+
* This method handles two primary modes based on `isRoot`:
|
7
|
+
* 1. If `isRoot` is true:
|
8
|
+
* a. Builds a detached DocumentFragment: if `parentNode` is null. Returns the fragment.
|
9
|
+
* b. Builds and inserts directly into a host DOM: if `parentNode` is provided. Inserts the fragment.
|
10
|
+
* 2. If `isRoot` is false (default for recursive calls):
|
11
|
+
* Appends created DOM nodes directly to the provided `parentNode` (which is the DOM element of the direct parent VNode).
|
12
|
+
*
|
13
|
+
* @param {Object} config
|
14
|
+
* @param {Number} [config.index] The index within `parentNode` to insert the root fragment (used when `isRoot` is true).
|
15
|
+
* @param {Boolean} [config.isRoot=false] If true, this is the root call for the VNode tree.
|
16
|
+
* @param {HTMLElement} [config.parentNode=null] The parent DOM node to insert into. Its role changes based on `isRoot`.
|
17
|
+
* @param {Object} config.vnode The VNode object to convert to a real DOM element.
|
18
|
+
* @returns {DocumentFragment|HTMLElement|null} The created DOM node, the root DocumentFragment, or null.
|
19
|
+
* @private
|
20
|
+
*/
|
21
|
+
createDomTree({index, isRoot=false, parentNode, vnode}) {
|
22
|
+
let domNode;
|
23
|
+
|
24
|
+
// No node or just a reference node, opt out
|
25
|
+
if (!vnode || vnode.componentId) {
|
26
|
+
return null
|
27
|
+
}
|
28
|
+
|
29
|
+
// Handle text nodes
|
30
|
+
if (vnode.vtype === 'text') {
|
31
|
+
domNode = document.createTextNode(vnode.textContent || '');
|
32
|
+
|
33
|
+
// Wrap in comment for consistency with delta updates
|
34
|
+
const
|
35
|
+
commentStart = document.createComment(` ${vnode.id} `),
|
36
|
+
commentEnd = document.createComment(' /neo-vtext '),
|
37
|
+
fragment = document.createDocumentFragment();
|
38
|
+
|
39
|
+
fragment.append(commentStart, domNode, commentEnd);
|
40
|
+
domNode = fragment
|
41
|
+
}
|
42
|
+
// Handle regular elements
|
43
|
+
else if (vnode.nodeName) {
|
44
|
+
if (vnode.ns) { // For SVG, ensure correct namespace
|
45
|
+
domNode = document.createElementNS(vnode.ns, vnode.nodeName)
|
46
|
+
} else {
|
47
|
+
domNode = document.createElement(vnode.nodeName)
|
48
|
+
}
|
49
|
+
|
50
|
+
// Apply the top-level 'id' property first (guaranteed to exist)
|
51
|
+
domNode[Neo.config.useDomIds ? 'id' : 'data-neo-id'] = vnode.id;
|
52
|
+
|
53
|
+
// Apply Attributes
|
54
|
+
Object.entries(vnode.attributes).forEach(([key, value]) => {
|
55
|
+
if (voidAttributes.has(key)) {
|
56
|
+
domNode[key] = (value === 'true' || value === true)
|
57
|
+
} else if (key === 'value') {
|
58
|
+
domNode.value = value
|
59
|
+
} else if (value !== null && value !== undefined) {
|
60
|
+
domNode.setAttribute(key, value)
|
61
|
+
}
|
62
|
+
});
|
63
|
+
|
64
|
+
// Apply Classes
|
65
|
+
if (vnode.className.length > 0) {
|
66
|
+
domNode.classList.add(...vnode.className)
|
67
|
+
}
|
68
|
+
|
69
|
+
// Apply Styles
|
70
|
+
if (Neo.isObject(vnode.style)) {
|
71
|
+
Object.entries(vnode.style).forEach(([key, value]) => {
|
72
|
+
let important;
|
73
|
+
|
74
|
+
if (Neo.isString(value) && value.includes('!important')) {
|
75
|
+
value = value.replace('!important', '').trim();
|
76
|
+
domNode.style.setProperty(Neo.decamel(key), value, 'important');
|
77
|
+
important = 'important'
|
78
|
+
}
|
79
|
+
|
80
|
+
domNode.style.setProperty(Neo.decamel(key), value, important)
|
81
|
+
})
|
82
|
+
}
|
83
|
+
|
84
|
+
// Handle innerHTML & textContent
|
85
|
+
// This applies to elements that contain only plain text (e.g., <span>Hello</span>)
|
86
|
+
// If the VNode has childNodes, this block is skipped, and content is handled recursively.
|
87
|
+
if (vnode.childNodes.length < 1) {
|
88
|
+
if (vnode.innerHTML) {
|
89
|
+
domNode.innerHTML = vnode.innerHTML
|
90
|
+
} else if (vnode.textContent) {
|
91
|
+
domNode.textContent = vnode.textContent
|
92
|
+
}
|
93
|
+
}
|
94
|
+
} else {
|
95
|
+
console.error('Unhandled VNode type or missing nodeName:', vnode);
|
96
|
+
return null
|
97
|
+
}
|
98
|
+
|
99
|
+
// Recursively process children
|
100
|
+
vnode.childNodes.forEach(childVnode => {
|
101
|
+
this.createDomTree({parentNode: domNode, vnode: childVnode})
|
102
|
+
})
|
103
|
+
|
104
|
+
// Final step: handle insertion based on `isRoot` and `parentNode`
|
105
|
+
if (isRoot) {
|
106
|
+
// This will be either HTMLElement or a DocumentFragment (for text vnodes)
|
107
|
+
let nodeToInsert = domNode;
|
108
|
+
|
109
|
+
if (nodeToInsert && parentNode && index !== -1) {
|
110
|
+
// If a specific host and index are provided, perform the insertion directly
|
111
|
+
if (index < parentNode.childNodes.length) {
|
112
|
+
parentNode.insertBefore(nodeToInsert, parentNode.childNodes[index])
|
113
|
+
} else {
|
114
|
+
parentNode.appendChild(nodeToInsert)
|
115
|
+
}
|
116
|
+
|
117
|
+
// Return the actual root DOM node (or fragment for text) that was inserted
|
118
|
+
return domNode
|
119
|
+
} else {
|
120
|
+
// If no specific host or index, return the detached nodeToInsert (HTMLElement or DocumentFragment)
|
121
|
+
return nodeToInsert
|
122
|
+
}
|
123
|
+
} else {
|
124
|
+
// For recursive calls (isRoot is false), append directly to the provided parentNode.
|
125
|
+
if (parentNode) { // parentNode here is the intermediate DOM parent
|
126
|
+
parentNode.append(domNode)
|
127
|
+
}
|
128
|
+
|
129
|
+
// Return the appended node (or null)
|
130
|
+
return domNode
|
131
|
+
}
|
132
|
+
}
|
133
|
+
};
|
134
|
+
|
135
|
+
const ns = Neo.ns('Neo.main.render', true);
|
136
|
+
ns.DomApiRenderer = DomApiRenderer;
|
137
|
+
|
138
|
+
export default DomApiRenderer;
|
@@ -0,0 +1,58 @@
|
|
1
|
+
const StringBasedRenderer = {
|
2
|
+
/**
|
3
|
+
* @param {String} html representing a single element
|
4
|
+
* @returns {DocumentFragment}
|
5
|
+
*/
|
6
|
+
htmlStringToElement(html) {
|
7
|
+
const template = document.createElement('template');
|
8
|
+
template.innerHTML = html;
|
9
|
+
return template.content
|
10
|
+
},
|
11
|
+
|
12
|
+
/**
|
13
|
+
* Handles string-based insertion of a new node into the DOM.
|
14
|
+
* This method is called by `insertNode()` when `NeoConfig.useStringBasedMounting` is true.
|
15
|
+
*
|
16
|
+
* @param {Object} data
|
17
|
+
* @param {Boolean} data.hasLeadingTextChildren Flag to honor leading comments.
|
18
|
+
* @param {Number} data.index The index at which to insert the new node.
|
19
|
+
* @param {String} data.outerHTML The HTML string of the node to insert.
|
20
|
+
* @param {HTMLElement} data.parentNode The parent DOM node to insert into.
|
21
|
+
* @private
|
22
|
+
*/
|
23
|
+
insertNodeAsString({hasLeadingTextChildren, index, outerHTML, parentNode}) {
|
24
|
+
let me = this;
|
25
|
+
|
26
|
+
// If comments detected, parse HTML string to a node and use insertBefore/appendChild on childNodes.
|
27
|
+
if (hasLeadingTextChildren) {
|
28
|
+
let node = me.htmlStringToElement(outerHTML);
|
29
|
+
|
30
|
+
if (index < parentNode.childNodes.length) {
|
31
|
+
parentNode.insertBefore(node, parentNode.childNodes[index])
|
32
|
+
} else {
|
33
|
+
parentNode.appendChild(node)
|
34
|
+
}
|
35
|
+
}
|
36
|
+
// If no comments detected, use insertAdjacentHTML for element nodes.
|
37
|
+
else {
|
38
|
+
let countChildren = parentNode.children.length; // Use `children` for `insertAdjacentHTML` context
|
39
|
+
|
40
|
+
if (index > 0 && index >= countChildren) {
|
41
|
+
parentNode.insertAdjacentHTML('beforeend', outerHTML);
|
42
|
+
return
|
43
|
+
}
|
44
|
+
if (countChildren > 0 && countChildren > index) {
|
45
|
+
parentNode.children[index].insertAdjacentHTML('beforebegin', outerHTML)
|
46
|
+
} else if (countChildren > 0) {
|
47
|
+
parentNode.children[countChildren - 1].insertAdjacentHTML('afterend', outerHTML)
|
48
|
+
} else {
|
49
|
+
parentNode.insertAdjacentHTML('beforeend', outerHTML)
|
50
|
+
}
|
51
|
+
}
|
52
|
+
}
|
53
|
+
};
|
54
|
+
|
55
|
+
const ns = Neo.ns('Neo.main.render', true);
|
56
|
+
ns.StringBasedRenderer = StringBasedRenderer;
|
57
|
+
|
58
|
+
export default StringBasedRenderer;
|
package/src/table/View.mjs
CHANGED
package/src/tree/Accordion.mjs
CHANGED
@@ -272,18 +272,18 @@ class AccordionTree extends TreeList {
|
|
272
272
|
cls : [itemCls + '-content'],
|
273
273
|
id : id + '__item-content',
|
274
274
|
style: {pointerEvents: 'none'},
|
275
|
-
cn
|
276
|
-
flag
|
277
|
-
tag
|
278
|
-
cls
|
279
|
-
id
|
280
|
-
|
275
|
+
cn: [{
|
276
|
+
flag: 'name',
|
277
|
+
tag : 'span',
|
278
|
+
cls : [itemCls + '-content-header'],
|
279
|
+
id : id + '__item-content-header',
|
280
|
+
html: item[me.fields.header]
|
281
281
|
}, {
|
282
|
-
flag
|
283
|
-
tag
|
284
|
-
cls
|
285
|
-
id
|
286
|
-
|
282
|
+
flag: 'content',
|
283
|
+
tag : 'span',
|
284
|
+
cls : [itemCls + '-content-text'],
|
285
|
+
id : id + '__item-content-text',
|
286
|
+
html: item[me.fields.text]
|
287
287
|
}]
|
288
288
|
}],
|
289
289
|
style: {
|
package/src/tree/List.mjs
CHANGED
@@ -184,8 +184,15 @@ class Tree extends Base {
|
|
184
184
|
let me = this,
|
185
185
|
{folderCls, itemCls} = me,
|
186
186
|
cls = [itemCls],
|
187
|
+
contentCls = [itemCls + '-content'],
|
187
188
|
itemVdom;
|
188
189
|
|
190
|
+
if (record.iconCls) {
|
191
|
+
contentCls.push(
|
192
|
+
Array.isArray(record.iconCls) ? record.iconCls : record.iconCls.split(' ')
|
193
|
+
)
|
194
|
+
}
|
195
|
+
|
189
196
|
if (record.isLeaf) {
|
190
197
|
cls.push(itemCls + (record.singleton ? '-leaf-singleton' : '-leaf'))
|
191
198
|
} else {
|
@@ -201,10 +208,10 @@ class Tree extends Base {
|
|
201
208
|
cls,
|
202
209
|
id : me.getItemId(record.id),
|
203
210
|
cn : [{
|
204
|
-
tag
|
205
|
-
cls
|
206
|
-
|
207
|
-
style
|
211
|
+
tag : 'span',
|
212
|
+
cls : contentCls,
|
213
|
+
html : record.name,
|
214
|
+
style: {pointerEvents: 'none'}
|
208
215
|
}],
|
209
216
|
style: {
|
210
217
|
display : record.hidden ? 'none' : 'flex',
|
@@ -329,7 +336,7 @@ class Tree extends Base {
|
|
329
336
|
directMatch = false;
|
330
337
|
node = me.getVdomChild(me.getItemId(item.id), me.vdom);
|
331
338
|
|
332
|
-
node.cn[0].
|
339
|
+
node.cn[0].html = item[property].replace(valueRegEx, match => {
|
333
340
|
directMatch = true;
|
334
341
|
return `<span class="neo-highlight-search">${match}</span>`
|
335
342
|
});
|