neo.mjs 9.0.2 → 9.2.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/README.md CHANGED
@@ -50,7 +50,7 @@ Key Benefits of Neo.mjs
50
50
  - Neo is built on modern web standards like JavaScript modules and worker threads, ensuring compatibility with the latest browser features.
51
51
  - By embracing the OMT paradigm, Neo is uniquely positioned to take advantage of future advancements in web development.
52
52
 
53
- **Real-World Applications**
53
+ ## Real-World Applications
54
54
 
55
55
  Neo is ideal for:
56
56
  - **Data-intensive applications**: Handle large datasets and complex calculations without compromising UI responsiveness.
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='9.0.2'
23
+ * @member {String} version='9.2.0'
24
24
  */
25
- version: '9.0.2'
25
+ version: '9.2.0'
26
26
  }
27
27
 
28
28
  /**
@@ -16,7 +16,7 @@
16
16
  "@type": "Organization",
17
17
  "name": "Neo.mjs"
18
18
  },
19
- "datePublished": "2025-05-12",
19
+ "datePublished": "2025-05-23",
20
20
  "publisher": {
21
21
  "@type": "Organization",
22
22
  "name": "Neo.mjs"
@@ -59,24 +59,24 @@ class HeaderToolbar extends Base {
59
59
  url : 'https://github.com/neomjs/neo',
60
60
  tooltip: {
61
61
  html : 'GitHub',
62
- showDelay: '0',
63
- hideDelay: '0'
62
+ showDelay: 0,
63
+ hideDelay: 0
64
64
  }
65
65
  }, {
66
66
  iconCls: 'fa-brands fa-slack',
67
67
  url : 'https://join.slack.com/t/neomjs/shared_invite/zt-6c50ueeu-3E1~M4T9xkNnb~M_prEEOA',
68
68
  tooltip: {
69
69
  html : 'Join Slack',
70
- showDelay: '0',
71
- hideDelay: '0'
70
+ showDelay: 0,
71
+ hideDelay: 0
72
72
  }
73
73
  }, {
74
74
  iconCls: 'fa-brands fa-discord',
75
75
  url : 'https://discord.gg/6p8paPq',
76
76
  tooltip: {
77
77
  html : 'Join Discord',
78
- showDelay: '0',
79
- hideDelay: '0'
78
+ showDelay: 0,
79
+ hideDelay: 0
80
80
  }
81
81
  }]
82
82
  }]
@@ -107,7 +107,7 @@ class FooterContainer extends Container {
107
107
  }, {
108
108
  module: Component,
109
109
  cls : ['neo-version'],
110
- html : 'v9.0.2'
110
+ html : 'v9.2.0'
111
111
  }]
112
112
  }],
113
113
  /**
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='9.0.2'
23
+ * @member {String} version='9.2.0'
24
24
  */
25
- version: '9.0.2'
25
+ version: '9.2.0'
26
26
  }
27
27
 
28
28
  /**
@@ -17,6 +17,10 @@ class MainContainer extends Viewport {
17
17
  * @member {String[]} cls=['neo-examples-bigdata-maincontainer']
18
18
  */
19
19
  cls: ['neo-examples-bigdata-maincontainer'],
20
+ /**
21
+ * @member {Object} layout={ntype:'hbox',align:'stretch'}
22
+ */
23
+ layout: {ntype: 'hbox', align: 'stretch'},
20
24
  /**
21
25
  * @member {Object[]} items
22
26
  */
@@ -25,11 +29,7 @@ class MainContainer extends Viewport {
25
29
  reference: 'grid'
26
30
  }, {
27
31
  module: ControlsContainer
28
- }],
29
- /**
30
- * @member {Object} layout={ntype:'hbox',align:'stretch'}
31
- */
32
- layout: {ntype: 'hbox', align: 'stretch'}
32
+ }]
33
33
  }
34
34
 
35
35
  /**
@@ -0,0 +1,11 @@
1
+ Neo.overwrites = {
2
+ Neo: {
3
+ button: {
4
+ Base: {
5
+ editRoute: false
6
+ }
7
+ }
8
+ }
9
+ }
10
+
11
+ export default Neo.overwrites;
@@ -0,0 +1,40 @@
1
+ import BaseViewport from '../../../src/container/Viewport.mjs';
2
+ import Button from '../../../src/button/Base.mjs';
3
+ import Toolbar from '../../../src/toolbar/Base.mjs';
4
+
5
+ /**
6
+ * @class Neo.examples.serverside.toolbarItems.Viewport
7
+ * @extends Neo.container.Viewport
8
+ */
9
+ class Viewport extends BaseViewport {
10
+ static config = {
11
+ className: 'Neo.examples.serverside.toolbarItems.Viewport',
12
+ cls : ['neo-serverside-toolbaritems-viewport'],
13
+ layout : 'base',
14
+
15
+ items: [{
16
+ module : Toolbar,
17
+ reference: 'toolbar'
18
+ }, {
19
+ module : Button,
20
+ handler: 'up.onLoadItemsButtonClick',
21
+ style : {marginTop: '1em'},
22
+ text : 'Load Toolbar Items'
23
+ }]
24
+ }
25
+
26
+ /**
27
+ * @param {Object} data
28
+ * @returns {Promise<void>}
29
+ */
30
+ async onLoadItemsButtonClick(data) {
31
+ data.component.disabled = true;
32
+
33
+ let response = await fetch('../../examples/serverside/toolbarItems/resources/data/toolbar-items.json'),
34
+ remoteData = await response.json();
35
+
36
+ this.getReference('toolbar').add(remoteData.items)
37
+ }
38
+ }
39
+
40
+ export default Neo.setupClass(Viewport);
@@ -0,0 +1,7 @@
1
+ import Overwrites from './Overwrites.mjs';
2
+ import Viewport from './Viewport.mjs';
3
+
4
+ export const onStart = () => Neo.app({
5
+ mainView: Viewport,
6
+ name : 'Neo.examples.serverside.toolbarItems'
7
+ });
@@ -0,0 +1,11 @@
1
+ <!DOCTYPE HTML>
2
+ <html>
3
+ <head>
4
+ <meta name="viewport" content="width=device-width, initial-scale=1">
5
+ <meta charset="UTF-8">
6
+ <title>Neo.mjs - Load Toolbar Items</title>
7
+ </head>
8
+ <body>
9
+ <script src="../../../src/MicroLoader.mjs" type="module"></script>
10
+ </body>
11
+ </html>
@@ -0,0 +1,6 @@
1
+ {
2
+ "appPath" : "examples/serverside/toolbarItems/app.mjs",
3
+ "basePath" : "../../../",
4
+ "environment": "development",
5
+ "mainPath" : "./Main.mjs"
6
+ }
@@ -0,0 +1,43 @@
1
+ {
2
+ "items": [{
3
+ "minWidth": 60,
4
+ "route" : "/home",
5
+ "text" : "Neo.mjs"
6
+ }, "->", {
7
+ "text" : "Learn",
8
+ "route": "/learn"
9
+ }, {
10
+ "text" : "Blog",
11
+ "route": "/blog"
12
+ }, {
13
+ "text" : "Examples",
14
+ "route": "/examples"
15
+ }, {
16
+ "text" : "Services",
17
+ "route": "/services"
18
+ }, {
19
+ "iconCls": "fa-brands fa-github",
20
+ "url" : "https://github.com/neomjs/neo",
21
+ "tooltip": {
22
+ "html" : "GitHub",
23
+ "showDelay": 0,
24
+ "hideDelay": 0
25
+ }
26
+ }, {
27
+ "iconCls": "fa-brands fa-slack",
28
+ "url" : "https://join.slack.com/t/neomjs/shared_invite/zt-6c50ueeu-3E1~M4T9xkNnb~M_prEEOA",
29
+ "tooltip": {
30
+ "html" : "Join Slack",
31
+ "showDelay": 0,
32
+ "hideDelay": 0
33
+ }
34
+ }, {
35
+ "iconCls": "fa-brands fa-discord",
36
+ "url" : "https://discord.gg/6p8paPq",
37
+ "tooltip": {
38
+ "html" : "Join Discord",
39
+ "showDelay": 0,
40
+ "hideDelay": 0
41
+ }
42
+ }]
43
+ }
@@ -35,7 +35,8 @@ class MainContainer extends Viewport {
35
35
  iconCls: 'fa fa-edit',
36
36
  style : {marginRight: '10px'},
37
37
  text : 'Change Cell Value',
38
- handler: function () {
38
+
39
+ handler() {
39
40
  let tabContainer = Neo.getComponent('myTableStoreContainer'),
40
41
  store = tabContainer.store,
41
42
  record = store.items[0];
@@ -47,7 +48,8 @@ class MainContainer extends Viewport {
47
48
  iconCls: 'fa fa-edit',
48
49
  style : {marginRight: '10px'},
49
50
  text : 'Update all cells 100x',
50
- handler: function () {
51
+
52
+ handler() {
51
53
  let tabContainer = Neo.getComponent('myTableStoreContainer'),
52
54
  store = tabContainer.store,
53
55
  countRecords = store.getCount(),
@@ -61,7 +63,7 @@ class MainContainer extends Viewport {
61
63
  for (i=0; i < countRecords; i++) {
62
64
  record = store.items[i];
63
65
 
64
- Object.entries(record[Symbol.for('data')]).forEach(([field, value]) => {
66
+ Object.entries(record.toJSON()).forEach(([field, value]) => {
65
67
  if (field !== 'githubId') {
66
68
  if (value.includes(string1)) {
67
69
  newValue = value.replace(string1, string2);
@@ -81,7 +83,8 @@ class MainContainer extends Viewport {
81
83
  ntype : 'button',
82
84
  iconCls: 'fa fa-sync-alt',
83
85
  text : 'Reset Sorting',
84
- handler: function () {
86
+
87
+ handler() {
85
88
  Neo.getComponent('myTableStoreContainer').store.sort();
86
89
  }
87
90
  }]
@@ -105,90 +108,51 @@ class MainContainer extends Viewport {
105
108
  checked : true,
106
109
  hideLabel : true,
107
110
  hideValueLabel: false,
111
+ listeners : {change: 'up.updateSelectionModel'},
108
112
  name : 'selectionModel',
109
113
  value : 'selection-table-cellmodel',
110
- valueLabelText: 'Cell',
111
- listeners: {
112
- change: function(data) {
113
- if (data.value) {
114
- Neo.getComponent('myTableStoreContainer').view.selectionModel = {
115
- ntype: this.value
116
- };
117
- }
118
- }
119
- }
114
+ valueLabelText: 'Cell'
120
115
  }, {
121
116
  ntype : 'radiofield',
122
117
  hideLabel : true,
123
118
  hideValueLabel: false,
119
+ listeners : {change: 'up.updateSelectionModel'},
124
120
  name : 'selectionModel',
125
121
  style : {marginLeft: '20px'},
126
122
  value : 'selection-table-columnmodel',
127
- valueLabelText: 'Column',
128
- listeners: {
129
- change: function(data) {
130
- if (data.value) {
131
- Neo.getComponent('myTableStoreContainer').view.selectionModel = {
132
- ntype: this.value
133
- };
134
- }
135
- }
136
- }
123
+ valueLabelText: 'Column'
137
124
  }, {
138
125
  ntype : 'radiofield',
139
126
  hideLabel : true,
140
127
  hideValueLabel: false,
128
+ listeners : {change: 'up.updateSelectionModel'},
141
129
  name : 'selectionModel',
142
130
  style : {marginLeft: '20px'},
143
131
  value : 'selection-table-rowmodel',
144
- valueLabelText: 'Row',
145
- listeners: {
146
- change: function(data) {
147
- if (data.value) {
148
- Neo.getComponent('myTableStoreContainer').view.selectionModel = {
149
- ntype: this.value
150
- };
151
- }
152
- }
153
- }
132
+ valueLabelText: 'Row'
154
133
  }, {
155
134
  ntype : 'radiofield',
156
135
  hideLabel : true,
157
136
  hideValueLabel: false,
137
+ listeners : {change: 'up.updateSelectionModel'},
158
138
  name : 'selectionModel',
159
139
  style : {marginLeft: '20px'},
160
140
  value : 'selection-table-cellcolumnmodel',
161
- valueLabelText: 'Cell & Column',
162
- listeners: {
163
- change: function(data) {
164
- if (data.value) {
165
- Neo.getComponent('myTableStoreContainer').view.selectionModel = {
166
- ntype: this.value
167
- };
168
- }
169
- }
170
- }
141
+ valueLabelText: 'Cell & Column'
171
142
  }, {
172
143
  ntype : 'radiofield',
173
144
  hideLabel : true,
174
145
  hideValueLabel: false,
146
+ listeners : {change: 'up.updateSelectionModel'},
175
147
  name : 'selectionModel',
176
148
  style : {marginLeft: '20px'},
177
149
  value : 'selection-table-cellrowmodel',
178
- valueLabelText: 'Cell & Row',
179
- listeners: {
180
- change: function(data) {
181
- if (data.value) {
182
- Neo.getComponent('myTableStoreContainer').view.selectionModel = {
183
- ntype: this.value
184
- };
185
- }
186
- }
187
- }
150
+ valueLabelText: 'Cell & Row'
188
151
  }]
189
152
  }, {
190
153
  module : TableContainer,
191
154
  id : 'myTableStoreContainer',
155
+ reference : 'table',
192
156
  store : MainStore,
193
157
  viewConfig : {selectionModel: CellModel},
194
158
  width : '100%',
@@ -209,6 +173,17 @@ class MainContainer extends Viewport {
209
173
  }]
210
174
  }]
211
175
  }
176
+
177
+ /**
178
+ * @param {Object} data
179
+ */
180
+ updateSelectionModel(data) {
181
+ if (data.value) {
182
+ this.getReference('table').view.selectionModel = {
183
+ ntype: data.component.value
184
+ }
185
+ }
186
+ }
212
187
  }
213
188
 
214
189
  export default Neo.setupClass(MainContainer);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neo.mjs",
3
- "version": "9.0.2",
3
+ "version": "9.2.0",
4
4
  "description": "The webworkers driven UI framework",
5
5
  "type": "module",
6
6
  "repository": {
@@ -51,21 +51,21 @@
51
51
  "autoprefixer": "^10.4.21",
52
52
  "chalk": "^5.4.1",
53
53
  "clean-webpack-plugin": "^4.0.0",
54
- "commander": "^13.1.0",
54
+ "commander": "^14.0.0",
55
55
  "cssnano": "^7.0.7",
56
56
  "envinfo": "^7.14.0",
57
57
  "fs-extra": "^11.3.0",
58
58
  "highlightjs-line-numbers.js": "^2.9.0",
59
59
  "inquirer": "^12.6.1",
60
- "marked": "^15.0.11",
60
+ "marked": "^15.0.12",
61
61
  "monaco-editor": "0.50.0",
62
62
  "neo-jsdoc": "1.0.1",
63
63
  "neo-jsdoc-x": "1.0.5",
64
64
  "postcss": "^8.5.3",
65
- "sass": "^1.88.0",
65
+ "sass": "^1.89.0",
66
66
  "siesta-lite": "5.5.2",
67
67
  "url": "^0.11.4",
68
- "webpack": "^5.99.8",
68
+ "webpack": "^5.99.9",
69
69
  "webpack-cli": "^6.0.1",
70
70
  "webpack-dev-server": "^5.2.1",
71
71
  "webpack-hook-plugin": "^1.0.7",
@@ -1336,7 +1336,7 @@ Add this config to the map.
1336
1336
  <pre data-javascript>
1337
1337
  bind: {
1338
1338
  markerStore: 'stores.earthquakes'
1339
- },
1339
+ }
1340
1340
  </pre>
1341
1341
  <img style="width:80%" src="https://s3.amazonaws.com/mjs.neo.learning.images/earthquakes/InitialMapWithMarkers.png"></img>
1342
1342
 
@@ -1359,15 +1359,19 @@ via the `component.on()` method.
1359
1359
  In this lab you'll set up an event handler for the table and map.
1360
1360
 
1361
1361
  <details>
1362
- <summary>Add a listener to the table</summary>
1362
+ <summary>Add a listener to the table.View</summary>
1363
1363
 
1364
- Tables fire a select event, passing an object that contains a reference to the corresponding row.
1364
+ Table Views fire a select event, passing an object that contains a reference to the corresponding row.
1365
1365
 
1366
1366
  Add this table config:
1367
1367
 
1368
+ <pre data-javascript>
1369
+ viewConfig: {
1368
1370
  listeners: {
1369
1371
  select: (data) => console.log(data.record)
1370
1372
  }
1373
+ }
1374
+ </pre>
1371
1375
 
1372
1376
  Save and refresh, then click on a table row. If you look at the debugger console you'll see the record being logged.
1373
1377
 
@@ -1387,9 +1391,11 @@ After changing the value you should immediately see it reflected in the table ro
1387
1391
 
1388
1392
  Now add a `markerClick` listener to the Google Map.
1389
1393
 
1390
- listeners: {
1391
- markerClick: data => console.log(data.data.record)
1392
- },
1394
+ <pre data-javascript>
1395
+ listeners: {
1396
+ markerClick: data => console.log(data.data.record)
1397
+ }
1398
+ </pre>
1393
1399
 
1394
1400
  Save, refresh, and confirm that you see the value logged when you click on a map marker.
1395
1401
 
@@ -0,0 +1,14 @@
1
+ .neo-viewport.neo-serverside-toolbaritems-viewport {
2
+ padding: 1em;
3
+
4
+ .neo-toolbar {
5
+ background-color: #f2f2f2;
6
+ padding : 10px 5px 10px 10px;
7
+
8
+ .neo-button {
9
+ &:not(:last-child) {
10
+ margin-right: .2em;
11
+ }
12
+ }
13
+ }
14
+ }
@@ -263,12 +263,12 @@ const DefaultConfig = {
263
263
  useVdomWorker: true,
264
264
  /**
265
265
  * buildScripts/injectPackageVersion.mjs will update this value
266
- * @default '9.0.2'
266
+ * @default '9.2.0'
267
267
  * @memberOf! module:Neo
268
268
  * @name config.version
269
269
  * @type String
270
270
  */
271
- version: '9.0.2'
271
+ version: '9.2.0'
272
272
  };
273
273
 
274
274
  Object.assign(DefaultConfig, {
@@ -154,14 +154,14 @@ class Helix extends Component {
154
154
  mouseWheelEnabled_: true,
155
155
  /**
156
156
  * The DOM element offsetHeight of the top level div.
157
- * Gets fetched after the helix got mounted.
157
+ * Gets fetched on resize.
158
158
  * @member {Number|null} offsetHeight=null
159
159
  * @protected
160
160
  */
161
161
  offsetHeight: null,
162
162
  /**
163
163
  * The DOM element offsetWidth of the top level div.
164
- * Gets fetched after the helix got mounted.
164
+ * Gets fetched on resize.
165
165
  * @member {Number|null} offsetWidth=null
166
166
  * @protected
167
167
  */
@@ -259,12 +259,24 @@ class Helix extends Component {
259
259
 
260
260
  me.addDomListeners({
261
261
  click : me.onClick,
262
+ resize : me.onResize,
262
263
  touchmove: me.onTouchMove,
263
264
  wheel : me.onMouseWheel,
264
265
  scope : me
265
266
  })
266
267
  }
267
268
 
269
+ /**
270
+ * @param {Boolean} mounted
271
+ * @protected
272
+ */
273
+ async addResizeObserver(mounted) {
274
+ let {id, windowId} = this,
275
+ ResizeObserver = await Neo.currentWorker.getAddon('ResizeObserver', windowId);
276
+
277
+ ResizeObserver[mounted ? 'register' : 'unregister']({id, windowId})
278
+ }
279
+
268
280
  /**
269
281
  * Triggered after the followSelection config got changed
270
282
  * @param {Boolean} value
@@ -329,7 +341,10 @@ class Helix extends Component {
329
341
  */
330
342
  afterSetMounted(value, oldValue) {
331
343
  super.afterSetMounted(value, oldValue);
332
- value && this.getOffsetValues()
344
+
345
+ if (oldValue !== undefined) {
346
+ this.addResizeObserver(value)
347
+ }
333
348
  }
334
349
 
335
350
  /**
@@ -749,25 +764,6 @@ class Helix extends Component {
749
764
  return this.id + '__' + id
750
765
  }
751
766
 
752
- /**
753
- * @param {Number} [delay=100]
754
- */
755
- getOffsetValues(delay=100) {
756
- let me = this;
757
-
758
- me.timeout(delay).then(() => {
759
- Neo.currentWorker.promiseMessage('main', {
760
- action : 'readDom',
761
- appName : me.appName,
762
- attributes: ['offsetHeight', 'offsetWidth'],
763
- vnodeId : me.id
764
- }).then(data => {
765
- me.offsetHeight = data.attributes.offsetHeight;
766
- me.offsetWidth = data.attributes.offsetWidth
767
- })
768
- })
769
- }
770
-
771
767
  /**
772
768
  *
773
769
  */
@@ -778,7 +774,7 @@ class Helix extends Component {
778
774
  insideNeo: true,
779
775
  url : me.url
780
776
  }).catch(err => {
781
- console.log('Error for Neo.Xhr.request', err, me.id);
777
+ console.log('Error for Neo.Xhr.request', err, me.id)
782
778
  }).then(data => {
783
779
  me.store.items = data.json.data;
784
780
  me.createItems()
@@ -840,6 +836,33 @@ class Helix extends Component {
840
836
  }
841
837
  }
842
838
 
839
+ /**
840
+ * @param {Object} data
841
+ * @protected
842
+ */
843
+ async onResize(data) {
844
+ let me = this,
845
+ {appName, clonedItems} = me,
846
+ id;
847
+
848
+ me.offsetHeight = data.rect.height;
849
+ me.offsetWidth = data.rect.width;
850
+
851
+ if (clonedItems.length > 0) {
852
+ id = clonedItems[0].id;
853
+
854
+ await Neo.applyDeltas(appName, {
855
+ id,
856
+ cls : {remove: ['neo-transition-600']},
857
+ style: {transform: me.getCloneTransform()}
858
+ });
859
+
860
+ await me.timeout(10);
861
+
862
+ await Neo.applyDeltas(appName, {id, cls: {add: ['neo-transition-600']}})
863
+ }
864
+ }
865
+
843
866
  /**
844
867
  * @param {String[]} value
845
868
  * @param {String[]} oldValue
@@ -229,7 +229,7 @@ class MagicMoveText extends Component {
229
229
  me.previousChars = []
230
230
  }
231
231
 
232
- if(oldValue !== undefined) {
232
+ if (oldValue !== undefined) {
233
233
  me.addResizeObserver(value);
234
234
 
235
235
  me.autoCycle && me.startAutoCycle(value)
@@ -584,7 +584,8 @@ class Container extends Component {
584
584
  */
585
585
  mergeConfig(...args) {
586
586
  let me = this,
587
- config = super.mergeConfig(...args);
587
+ config = super.mergeConfig(...args),
588
+ ctorItems;
588
589
 
589
590
  // avoid any interference on prototype level
590
591
  // does not clone existing Neo instances
@@ -595,9 +596,15 @@ class Container extends Component {
595
596
  }
596
597
 
597
598
  if (config.items) {
599
+ ctorItems = me.constructor.config.items;
600
+
598
601
  // If we are passed an object, merge the class's own items object into it
599
602
  if (Neo.typeOf(config.items) === 'Object') {
600
- me.items = Neo.merge(Neo.clone(me.constructor.config.items), config.items)
603
+ if (Neo.isArray(ctorItems)) {
604
+ me.items = Neo.clone(config.items, true, true)
605
+ } else {
606
+ me.items = Neo.merge(Neo.clone(ctorItems), config.items)
607
+ }
601
608
  } else {
602
609
  me._items = Neo.clone(config.items, true, true)
603
610
  }