neo.mjs 10.2.0 → 10.2.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.
@@ -0,0 +1,17 @@
1
+ # Neo.mjs v10.2.1 Release Notes
2
+
3
+ ## Introduction
4
+
5
+ Version 10.2.1 is a patch release that addresses a critical layout regression discovered in lazy-loaded tabs and introduces a significant enhancement to the `button.Base` component, improving both its flexibility and security.
6
+
7
+ ## Key Changes
8
+
9
+ ### Enhancements
10
+
11
+ - **Button Text as VDOM**: The `text` config for `Neo.button.Base` has been enhanced to accept a VDOM `cn` (children) array in addition to a string. This allows for rich, complex button content to be defined in a structured, secure, and performant way, avoiding the need for `innerHTML` and enabling granular delta updates for the button's content. See ticket [#7135](https://github.com/neomjs/neo/issues/7135).
12
+
13
+ ### Bug Fixes
14
+
15
+ - **Lazy-Loaded Tab Layout Regression**: Fixed a critical regression where the `layout` config of a lazy-loaded tab (e.g., a `Panel`) was being mutated on the class prototype, causing subsequent lazy-loaded tabs to fail with a missing `ntype` error. The fix, implemented in `container.Base`, ensures that each container instance gets a unique, deep-cloned copy of its layout config, preventing prototype pollution. See ticket [#7134](https://github.com/neomjs/neo/issues/7134).
16
+
17
+ - **Covid App Navigation Rendering**: Fixed a rendering issue in the `GalleryContainer` and `HelixContainer` of the Covid apps where the navigation instructions were not being displayed correctly. The `text` config was changed to `html` on the `BoxLabel` components to ensure the HTML content is rendered as expected. See issue [#7133](https://github.com/neomjs/neo/issues/7133).
package/ServiceWorker.mjs CHANGED
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='10.2.0'
23
+ * @member {String} version='10.2.1'
24
24
  */
25
- version: '10.2.0'
25
+ version: '10.2.1'
26
26
  }
27
27
 
28
28
  /**
@@ -183,7 +183,7 @@ class GalleryContainer extends Container {
183
183
  }]
184
184
  }, {
185
185
  module: BoxLabel,
186
- text : [
186
+ html : [
187
187
  '<b>Navigation Concept</b>',
188
188
  '<p>You can use the Arrow Keys to walk through the items.</p>'
189
189
  ].join('')
@@ -227,7 +227,7 @@ class HelixContainer extends Container {
227
227
  }
228
228
  }, {
229
229
  module: BoxLabel,
230
- text : [
230
+ html : [
231
231
  '<b>Navigation Concept</b>',
232
232
  '<p>Click on an item to select it. Afterwards you can use the Arrow Keys to walk through the items.</p>',
233
233
  '<p>Hit the Space Key to rotate the currently selected item to the front.</p>',
@@ -56,21 +56,21 @@ class WorldMapContainer extends Container {
56
56
  handler: 'onSeriesButtonClick',
57
57
  series : 'cases',
58
58
  style : {marginRight: '2px'},
59
- text : '<span style="color: #bbbbbb">●</span> Cases'
59
+ text : [{tag: 'span', style: {color: '#bbbbbb'}, text: '●'}, {vtype: 'text', text: ' Cases'}]
60
60
  }, {
61
61
  handler: 'onSeriesButtonClick',
62
62
  series : 'active',
63
63
  style : {marginRight: '2px'},
64
- text : '<span style="color: #64b5f6">●</span> Active'
64
+ text : [{tag: 'span', style: {color: '#64b5f6'}, text: '●'}, {vtype: 'text', text: ' Active'}]
65
65
  }, {
66
66
  handler: 'onSeriesButtonClick',
67
67
  series : 'recovered',
68
68
  style : {marginRight: '2px'},
69
- text : '<span style="color: #28ca68">●</span> Recovered'
69
+ text : [{tag: 'span', style: {color: '#28ca68'}, text: '●'}, {vtype: 'text', text: ' Recovered'}]
70
70
  }, {
71
71
  handler: 'onSeriesButtonClick',
72
72
  series : 'deaths',
73
- text : '<span style="color: #fb6767">●</span> Deaths'
73
+ text : [{tag: 'span', style: {color: '#fb6767'}, text: '●'}, {vtype: 'text', text: ' Deaths'}]
74
74
  }]
75
75
  }, {
76
76
  module : WorldMapComponent,
@@ -108,7 +108,7 @@ class FooterContainer extends Container {
108
108
  }, {
109
109
  module: Component,
110
110
  cls : ['neo-version'],
111
- text : 'v10.2.0'
111
+ text : 'v10.2.1'
112
112
  }]
113
113
  }],
114
114
  /**
@@ -187,7 +187,7 @@ class GalleryContainer extends Container {
187
187
  }]
188
188
  }, {
189
189
  module: BoxLabel,
190
- text : [
190
+ html : [
191
191
  '<b>Navigation Concept</b>',
192
192
  '<p>You can use the Arrow Keys to walk through the items.</p>'
193
193
  ].join('')
@@ -231,7 +231,7 @@ class HelixContainer extends Container {
231
231
  }
232
232
  }, {
233
233
  module: BoxLabel,
234
- text : [
234
+ html : [
235
235
  '<b>Navigation Concept</b>',
236
236
  '<p>Click on an item to select it. Afterwards you can use the Arrow Keys to walk through the items.</p>',
237
237
  '<p>Hit the Space Key to rotate the currently selected item to the front.</p>',
@@ -56,21 +56,21 @@ class WorldMapContainer extends Container {
56
56
  handler: 'onSeriesButtonClick',
57
57
  series : 'cases',
58
58
  style : {marginRight: '2px'},
59
- text : '<span style="color: #bbbbbb">●</span> Cases'
59
+ text : [{tag: 'span', style: {color: '#bbbbbb'}, text: '●'}, {vtype: 'text', text: ' Cases'}]
60
60
  }, {
61
61
  handler: 'onSeriesButtonClick',
62
62
  series : 'active',
63
63
  style : {marginRight: '2px'},
64
- text : '<span style="color: #64b5f6">●</span> Active'
64
+ text : [{tag: 'span', style: {color: '#64b5f6'}, text: '●'}, {vtype: 'text', text: ' Active'}]
65
65
  }, {
66
66
  handler: 'onSeriesButtonClick',
67
67
  series : 'recovered',
68
68
  style : {marginRight: '2px'},
69
- text : '<span style="color: #28ca68">●</span> Recovered'
69
+ text : [{tag: 'span', style: {color: '#28ca68'}, text: '●'}, {vtype: 'text', text: ' Recovered'}]
70
70
  }, {
71
71
  handler: 'onSeriesButtonClick',
72
72
  series : 'deaths',
73
- text : '<span style="color: #fb6767">●</span> Deaths'
73
+ text : [{tag: 'span', style: {color: '#fb6767'}, text: '●'}, {vtype: 'text', text: ' Deaths'}]
74
74
  }]
75
75
  }, {
76
76
  module : WorldMapComponent,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name" : "neo.mjs",
3
- "version" : "10.2.0",
3
+ "version" : "10.2.1",
4
4
  "description" : "Neo.mjs: The multi-threaded UI framework for building ultra-fast, desktop-like web applications with uncompromised responsiveness, inherent security, and a transpilation-free dev mode.",
5
5
  "type" : "module",
6
6
  "repository" : {
@@ -299,12 +299,12 @@ const DefaultConfig = {
299
299
  useVdomWorker: true,
300
300
  /**
301
301
  * buildScripts/injectPackageVersion.mjs will update this value
302
- * @default '10.2.0'
302
+ * @default '10.2.1'
303
303
  * @memberOf! module:Neo
304
304
  * @name config.version
305
305
  * @type String
306
306
  */
307
- version: '10.2.0'
307
+ version: '10.2.1'
308
308
  };
309
309
 
310
310
  Object.assign(DefaultConfig, {
@@ -109,7 +109,10 @@ class Button extends Component {
109
109
  route_: null,
110
110
  /**
111
111
  * The text displayed on the button [optional]
112
- * @member {String|null} text=null
112
+ * You can either pass a string, or a vdom cn array.
113
+ * @example
114
+ * text: [{tag: 'span', style: {color: '#bbbbbb'}, text: '●'}, {vtype: 'text', text: ' Cases'}]
115
+ * @member {Object[]|String|null} text=null
113
116
  * @reactive
114
117
  */
115
118
  text: null,
@@ -378,8 +381,8 @@ class Button extends Component {
378
381
 
379
382
  /**
380
383
  * Triggered after the text config got changed
381
- * @param {String|null} value
382
- * @param {String|null} oldValue
384
+ * @param {Object[]|String|null} value
385
+ * @param {Object[]|String|null} oldValue
383
386
  * @protected
384
387
  */
385
388
  afterSetText(value, oldValue) {
@@ -393,7 +396,13 @@ class Button extends Component {
393
396
  textNode.removeDom = isEmpty;
394
397
 
395
398
  if (!isEmpty) {
396
- textNode.text = value
399
+ if (Neo.isArray(value)) {
400
+ textNode.cn = value;
401
+ delete textNode.text
402
+ } else {
403
+ textNode.text = value;
404
+ delete textNode.cn
405
+ }
397
406
  }
398
407
 
399
408
  me.update()
@@ -671,10 +671,17 @@ class Container extends Component {
671
671
  *
672
672
  */
673
673
  onConstructed() {
674
- let me = this;
674
+ let me = this,
675
+ layoutConfig = me.layout;
676
+
677
+ // If the layout is a config object (not an instance), deep clone it
678
+ // to prevent prototype pollution.
679
+ if (layoutConfig && !(layoutConfig instanceof LayoutBase)) {
680
+ layoutConfig = Neo.clone(layoutConfig, true)
681
+ }
675
682
 
676
683
  // in case the Container does not have a layout config, the setter won't trigger
677
- me._layout = me.createLayout(me.layout);
684
+ me._layout = me.createLayout(layoutConfig);
678
685
  me._layout?.applyRenderAttributes();
679
686
 
680
687
  super.onConstructed();