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.
Files changed (73) hide show
  1. package/README.md +127 -121
  2. package/ServiceWorker.mjs +2 -2
  3. package/apps/email/view/Viewport.mjs +2 -2
  4. package/apps/form/view/Viewport.mjs +1 -1
  5. package/apps/portal/index.html +1 -1
  6. package/apps/portal/view/examples/List.mjs +1 -1
  7. package/apps/portal/view/home/FooterContainer.mjs +1 -1
  8. package/apps/realworld2/view/HomeContainer.mjs +1 -1
  9. package/apps/route/view/center/CardAdministration.mjs +3 -3
  10. package/apps/route/view/center/CardAdministrationDenied.mjs +2 -2
  11. package/apps/route/view/center/CardContact.mjs +2 -2
  12. package/apps/route/view/center/CardHome.mjs +2 -2
  13. package/apps/route/view/center/CardSection1.mjs +2 -2
  14. package/apps/route/view/center/CardSection2.mjs +2 -2
  15. package/buildScripts/createApp.mjs +2 -2
  16. package/docs/app/view/classdetails/HeaderComponent.mjs +3 -3
  17. package/docs/app/view/classdetails/MembersList.mjs +43 -46
  18. package/docs/app/view/classdetails/SourceViewComponent.mjs +1 -1
  19. package/docs/app/view/classdetails/TutorialComponent.mjs +1 -1
  20. package/examples/component/toast/MainContainer.mjs +16 -16
  21. package/examples/component/wrapper/googleMaps/MarkerDialog.mjs +4 -4
  22. package/examples/fields/MainContainer.mjs +1 -1
  23. package/examples/panel/MainContainer.mjs +2 -2
  24. package/examples/tab/container/MainContainer.mjs +3 -3
  25. package/examples/tabs/MainContainer.mjs +2 -2
  26. package/examples/tabs/MainContainer2.mjs +3 -3
  27. package/examples/viewport/MainContainer.mjs +2 -2
  28. package/package.json +33 -5
  29. package/resources/data/deck/learnneo/pages/benefits/FourEnvironments.md +1 -1
  30. package/resources/data/deck/learnneo/pages/benefits/Introduction.md +4 -3
  31. package/resources/data/deck/learnneo/pages/guides/events/DomEvents.md +5 -5
  32. package/resources/data/deck/training/pages/2022-12-27T21-55-23-144Z.md +2 -2
  33. package/resources/data/deck/training/pages/2022-12-29T18-36-08-226Z.md +1 -1
  34. package/resources/data/deck/training/pages/2022-12-29T18-36-56-893Z.md +2 -2
  35. package/resources/data/deck/training/pages/2022-12-29T20-37-08-919Z.md +2 -2
  36. package/resources/data/deck/training/pages/2022-12-29T20-37-20-344Z.md +2 -2
  37. package/resources/data/deck/training/pages/2023-01-13T21-48-17-258Z.md +2 -2
  38. package/resources/data/deck/training/pages/2023-02-05T17-44-53-815Z.md +9 -9
  39. package/resources/data/deck/training/pages/2023-10-14T19-25-08-153Z.md +1 -1
  40. package/src/DefaultConfig.mjs +14 -2
  41. package/src/Main.mjs +15 -6
  42. package/src/button/Base.mjs +1 -1
  43. package/src/calendar/view/calendars/List.mjs +1 -1
  44. package/src/component/Base.mjs +11 -11
  45. package/src/component/Chip.mjs +1 -1
  46. package/src/component/Helix.mjs +3 -3
  47. package/src/component/Process.mjs +2 -2
  48. package/src/component/StatusBadge.mjs +2 -2
  49. package/src/component/Timer.mjs +1 -1
  50. package/src/component/Toast.mjs +2 -2
  51. package/src/container/Base.mjs +1 -1
  52. package/src/form/field/CheckBox.mjs +2 -2
  53. package/src/form/field/FileUpload.mjs +14 -14
  54. package/src/form/field/Range.mjs +1 -1
  55. package/src/form/field/Text.mjs +1 -1
  56. package/src/form/field/trigger/Base.mjs +2 -2
  57. package/src/form/field/trigger/SpinUpDown.mjs +2 -2
  58. package/src/grid/View.mjs +1 -1
  59. package/src/main/DeltaUpdates.mjs +442 -0
  60. package/src/main/DomAccess.mjs +13 -95
  61. package/src/main/render/DomApiRenderer.mjs +138 -0
  62. package/src/main/render/StringBasedRenderer.mjs +58 -0
  63. package/src/table/View.mjs +1 -1
  64. package/src/table/plugin/CellEditing.mjs +1 -1
  65. package/src/tree/Accordion.mjs +11 -11
  66. package/src/tree/List.mjs +12 -5
  67. package/src/vdom/Helper.mjs +179 -309
  68. package/src/vdom/VNode.mjs +47 -11
  69. package/src/vdom/domConstants.mjs +65 -0
  70. package/src/vdom/util/DomApiVnodeCreator.mjs +51 -0
  71. package/src/vdom/util/StringFromVnode.mjs +123 -0
  72. package/src/worker/mixin/RemoteMethodAccess.mjs +13 -1
  73. 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;
@@ -231,7 +231,7 @@ class View extends Component {
231
231
  }
232
232
 
233
233
  if (Neo.typeOf(rendererOutput) === 'Object') {
234
- cellConfig.innerHTML = rendererOutput.html || ''
234
+ cellConfig.html = rendererOutput.html || ''
235
235
  } else {
236
236
  cellConfig.cn = rendererOutput
237
237
  }
@@ -161,7 +161,7 @@ class CellEditing extends Plugin {
161
161
  me.mountedEditor = editor;
162
162
 
163
163
  cellNode.cn = [editor.createVdomReference()];
164
- delete cellNode.innerHTML;
164
+ delete cellNode.html;
165
165
 
166
166
  view.updateDepth = -1;
167
167
 
@@ -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 : 'name',
277
- tag : 'span',
278
- cls : [itemCls + '-content-header'],
279
- id : id + '__item-content-header',
280
- innerHTML: item[me.fields.header]
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 : 'content',
283
- tag : 'span',
284
- cls : [itemCls + '-content-text'],
285
- id : id + '__item-content-text',
286
- innerHTML: item[me.fields.text]
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 : 'span',
205
- cls : [itemCls + '-content', record.iconCls],
206
- innerHTML: record.name,
207
- style : {pointerEvents: 'none'}
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].innerHTML = item[property].replace(valueRegEx, match => {
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
  });