neo.mjs 6.32.0 → 6.33.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
@@ -6,7 +6,7 @@
6
6
  <a href="https://npmcharts.com/compare/neo.mjs?minimal=true"><img src="https://img.shields.io/npm/dm/neo.mjs.svg?label=Downloads" alt="Downloads"></a>
7
7
  <a href="https://www.npmjs.com/package/neo.mjs"><img src="https://img.shields.io/npm/v/neo.mjs.svg?logo=npm" alt="Version"></a>
8
8
  <a href="https://www.npmjs.com/package/neo.mjs"><img src="https://img.shields.io/npm/l/neo.mjs.svg?label=License" alt="License"></a>
9
- <a href="https://join.slack.com/t/neomjs/shared_invite/zt-6c50ueeu-3E1~M4T9xkNnb~M_prEEOA"><img src="https://img.shields.io/badge/Slack-neo.mjs-brightgreen.svg?logo=slack" alt="Join the Slack channel"></a>
9
+ <a href="https://join.slack.com/t/neomjs/shared_invite/zt-6c50ueeu-3E1~M4T9xkNnb~M_prEEOA"><img src="https://img.shields.io/badge/Slack-Neo.mjs-brightgreen.svg?logo=slack" alt="Join the Slack channel"></a>
10
10
  <a href="https://discord.gg/6p8paPq"><img src="https://img.shields.io/discord/656620537514164249?label=Discord&logo=discord&logoColor=white" alt="Discord Chat"></a>
11
11
  <a href="./CONTRIBUTING.md"><img src="https://img.shields.io/badge/PRs-welcome-green.svg?logo=GitHub&logoColor=white" alt="PRs Welcome"></a>
12
12
  </p>
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='6.32.0'
23
+ * @member {String} version='6.33.0'
24
24
  */
25
- version: '6.32.0'
25
+ version: '6.33.0'
26
26
  }
27
27
 
28
28
  /**
@@ -25,6 +25,11 @@ class List extends BaseList {
25
25
  * @member {String[]} baseCls=['portal-blog-list','neo-list']
26
26
  */
27
27
  baseCls: ['portal-blog-list', 'neo-list'],
28
+ /**
29
+ * Specify how many blog item images to preload when intersecting
30
+ * @member {Number} preloadImages=5
31
+ */
32
+ preloadImages: 5,
28
33
  /**
29
34
  * @member {Neo.data.Store} store=BlogPosts
30
35
  */
@@ -42,6 +47,61 @@ class List extends BaseList {
42
47
  ]}
43
48
  }
44
49
 
50
+ /**
51
+ * @member {String} basePath
52
+ */
53
+ get basePath() {
54
+ let basePath;
55
+
56
+ if (Neo.config.isGitHubPages) {
57
+ basePath = '../../../../resources_pub/website';
58
+
59
+ if (Neo.config.environment !== 'development') {
60
+ basePath = '../../' + basePath
61
+ }
62
+ } else {
63
+ basePath = 'https://raw.githubusercontent.com/neomjs/pages/main/resources_pub/website'
64
+ }
65
+
66
+ return basePath
67
+ }
68
+
69
+ /**
70
+ * @param {Object} config
71
+ */
72
+ construct(config) {
73
+ super.construct(config);
74
+
75
+ let me = this;
76
+
77
+ me.addDomListeners({
78
+ intersect: me.onIntersect,
79
+ scope : me
80
+ })
81
+ }
82
+
83
+ /**
84
+ * Triggered after the mounted config got changed
85
+ * @param {Boolean} value
86
+ * @param {Boolean} oldValue
87
+ * @protected
88
+ */
89
+ afterSetMounted(value, oldValue) {
90
+ super.afterSetMounted(value, oldValue);
91
+
92
+ let me = this;
93
+
94
+ value && me.timeout(50).then(() => {
95
+ Neo.main.addon.IntersectionObserver.register({
96
+ callback: 'isVisible',
97
+ id : me.id,
98
+ observe : ['.content'],
99
+ root : `#${me.parentId}`,
100
+ windowId: me.windowId
101
+ })
102
+ })
103
+ }
104
+
45
105
  /**
46
106
  * @param {Neo.data.Store} value
47
107
  * @param {Neo.data.Store} oldValue
@@ -59,23 +119,13 @@ class List extends BaseList {
59
119
  * @param {Object} record
60
120
  */
61
121
  createItemContent(record) {
62
- let basePath;
63
-
64
- if (Neo.config.isGitHubPages) {
65
- basePath = '../../../../resources_pub/website';
66
-
67
- if (Neo.config.environment !== 'development') {
68
- basePath = '../../' + basePath
69
- }
70
- } else {
71
- basePath = 'https://raw.githubusercontent.com/neomjs/pages/main/resources_pub/website'
72
- }
122
+ let {basePath} = this;
73
123
 
74
124
  const vdomCn = [
75
- {cls: ['content'], cn: [
125
+ {cls: ['content'], data: {recordId: record.id}, cn: [
76
126
  {cls: ['neo-relative'], cn: [
77
- {cls: ['neo-full-size', 'preview-image'], style: {
78
- backgroundImage: `url('${basePath}/blog/${record.image}'), linear-gradient(#777, #333)`}
127
+ {cls: ['neo-full-size', 'preview-image'], flag: `image-${record.id}`
128
+ //backgroundImage: `url('${basePath}/blog/${record.image}'), linear-gradient(#777, #333)`
79
129
  },
80
130
  {cls: ['neo-absolute', 'neo-item-bottom-position'], cn: [
81
131
  {tag: 'a', cls: ['neo-title'], href: record.url, target: '_blank', cn: [
@@ -166,6 +216,33 @@ class List extends BaseList {
166
216
  getVnodeRoot() {
167
217
  return this.vnode.childNodes[0]
168
218
  }
219
+
220
+ /**
221
+ * @param {Object} data
222
+ */
223
+ onIntersect(data) {
224
+ let me = this,
225
+ {basePath, store} = me,
226
+ record = store.get(parseInt(data.data.recordId)),
227
+ i = store.indexOf(record),
228
+ len = Math.min(i + me.preloadImages, store.getCount()),
229
+ needsUpdate = false,
230
+ node;
231
+
232
+ for (; i < len; i++) {
233
+ node = VDomUtil.getByFlag(me.vdom, `image-${record.id}`);
234
+
235
+ if (!node.style) {
236
+ needsUpdate = true;
237
+
238
+ node.style = {
239
+ backgroundImage: `url('${basePath}/blog/${record.image}'), linear-gradient(#777, #333)`
240
+ }
241
+ }
242
+ }
243
+
244
+ needsUpdate && me.update()
245
+ }
169
246
  }
170
247
 
171
248
  Neo.setupClass(List);
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='6.32.0'
23
+ * @member {String} version='6.33.0'
24
24
  */
25
- version: '6.32.0'
25
+ version: '6.33.0'
26
26
  }
27
27
 
28
28
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neo.mjs",
3
- "version": "6.32.0",
3
+ "version": "6.33.0",
4
4
  "description": "The webworkers driven UI framework",
5
5
  "type": "module",
6
6
  "repository": {
@@ -52,12 +52,12 @@
52
52
  "envinfo": "^7.13.0",
53
53
  "fs-extra": "^11.2.0",
54
54
  "highlightjs-line-numbers.js": "^2.8.0",
55
- "inquirer": "^10.1.7",
55
+ "inquirer": "^10.1.8",
56
56
  "marked": "^13.0.3",
57
57
  "monaco-editor": "^0.50.0",
58
58
  "neo-jsdoc": "1.0.1",
59
59
  "neo-jsdoc-x": "1.0.5",
60
- "postcss": "^8.4.40",
60
+ "postcss": "^8.4.41",
61
61
  "sass": "^1.77.8",
62
62
  "siesta-lite": "5.5.2",
63
63
  "url": "^0.11.4",
@@ -619,14 +619,15 @@ The feed looks like this. (For simplicity, some values are omitted.)
619
619
  "longitude": -21.949,
620
620
  "size": 0.6,
621
621
  "humanReadableLocation": "6,1 km SV af Helgafelli"
622
- }, {
622
+ }, {
623
623
  "timestamp": "2017-10-13T09:50:50.000Z",
624
624
  "latitude": 65.124,
625
625
  "longitude": -16.288,
626
626
  "size": 0.9,
627
627
  "humanReadableLocation": "6,1 km NA af Her\u00F0ubrei\u00F0art\u00F6glum"
628
- },
629
- ...]
628
+ }, //...
629
+ ]
630
+ }
630
631
  </pre>
631
632
 
632
633
  The store defines a `type` for the date field. There are a few pre-defined field types that convert
@@ -79,7 +79,7 @@ class MainView extends Container {
79
79
  }
80
80
  }]
81
81
  }
82
- foo(data){
82
+ foo(data) {
83
83
  Neo.Main.log({value:data.value})
84
84
  }
85
85
  }
@@ -99,7 +99,7 @@ class MainViewController extends Controller {
99
99
  static config = {
100
100
  className: 'Example.view.MainViewController'
101
101
  }
102
- foo(data){
102
+ foo(data) {
103
103
  Neo.Main.log({value:data.value})
104
104
  }
105
105
  }
@@ -111,11 +111,9 @@ import TextField from '../form/field/Text.mjs';
111
111
 
112
112
  class MainView extends Container {
113
113
  static config = {
114
- controller: {
115
- module: MainViewController
116
- },
117
- className: 'Example.view.MainView',
118
- layout : {ntype:'vbox', align:'start'},
114
+ className : 'Example.view.MainView',
115
+ controller: MainViewController,
116
+ layout : {ntype:'vbox', align:'start'},
119
117
 
120
118
  items: [{
121
119
  module : TextField,
@@ -143,10 +141,10 @@ class MainViewController extends Controller {
143
141
  static config = {
144
142
  className: 'Example.view.MainViewController'
145
143
  }
146
- foo(data){
144
+ foo(data) {
147
145
  Neo.Main.log({value:data.value})
148
146
  }
149
- onComponentConstructed(){
147
+ onComponentConstructed() {
150
148
  debugger
151
149
  }
152
150
  }
@@ -158,11 +156,9 @@ import TextField from '../form/field/Text.mjs';
158
156
 
159
157
  class MainView extends Container {
160
158
  static config = {
161
- controller: {
162
- module: MainViewController
163
- },
164
159
  className: 'Example.view.MainView',
165
- layout : {ntype:'vbox', align:'start'},
160
+ controller: MainViewController,
161
+ layout : {ntype:'vbox', align:'start'},
166
162
 
167
163
  items: [{
168
164
  module : TextField,
@@ -185,12 +181,12 @@ class MainViewController extends Controller {
185
181
  static config = {
186
182
  className: 'Example.view.MainViewController'
187
183
  }
188
- foo(data){
189
- Neo.Main.log('foo' + {value:data.value}
184
+ foo(data) {
185
+ Neo.Main.log('foo' + {value:data.value})
190
186
  }
191
187
 
192
188
  // This is a controller lifecycle method run after the controller's view has been constructed
193
- onComponentConstructed(){
189
+ onComponentConstructed() {
194
190
  // Note the use of this.getReference() -- that's used to get a component reference with the specified name
195
191
  this.getReference('nameTextfield').on('change', this.foo, this)
196
192
  }
@@ -203,11 +199,9 @@ import TextField from '../form/field/Text.mjs';
203
199
 
204
200
  class MainView extends Container {
205
201
  static config = {
206
- controller: {
207
- module: MainViewController
208
- },
209
202
  className: 'Example.view.MainView',
210
- layout : {ntype:'vbox', align:'start'},
203
+ controller: MainViewController,
204
+ layout : {ntype:'vbox', align:'start'},
211
205
 
212
206
  items: [{
213
207
  module : TextField,
@@ -219,7 +213,6 @@ class MainView extends Container {
219
213
  Neo.setupClass(MainView);
220
214
  </pre>
221
215
 
222
-
223
216
  ## Events versus binding
224
217
 
225
218
  Events and binding are similar concepts &mdash; both are a way to detect, and react to, some kind of action or change.
@@ -228,9 +221,7 @@ Events are fired for user actions, such as a button click event, a component rec
228
221
  Non-visual classes can also fire an event; for example, a `Neo.data.Store` fires a `load` event, and other events
229
222
  relating to changes to the store. The event handler if a function run when the event fires.
230
223
 
231
- A binding detects a changed view model value, and assigns it to a property.
232
-
233
-
224
+ A binding detects a changed view model value, and assigns it to a property.
234
225
 
235
226
  ### When to use an event
236
227
 
@@ -240,7 +231,6 @@ might have a Save button, and in its click event handler you'd write logic to ma
240
231
  Events can be fired for a state change, such as an input field's value changing, or something like a user event,
241
232
  like a button click or component focus.
242
233
 
243
-
244
234
  ### When to use a binding
245
235
 
246
236
  A binding is a way to keep properties in sync with values in the view model hierarchy. For example, button text,
@@ -255,9 +245,9 @@ To contrast syntax, and to illustrate the simplicity of a binding, let's look at
255
245
  to reflect the value of a text field. THe first example uses events; the second uses bindings.
256
246
 
257
247
  <pre data-neo>
248
+ import Component from '../component/Base.mjs';
258
249
  import Container from '../container/Base.mjs';
259
250
  import TextField from '../form/field/Text.mjs';
260
- import Component from '../component/Base.mjs';
261
251
 
262
252
  class MainView extends Container {
263
253
  static config = {
@@ -268,24 +258,24 @@ class MainView extends Container {
268
258
  module : TextField,
269
259
  labelText: 'Text',
270
260
  reference: 'textFieldOne',
271
- value: 'Hello',
261
+ value : 'Hello',
272
262
  listeners: {
273
- change : 'up.onTextChange'
263
+ change: 'up.onTextChange'
274
264
  }
275
265
  }, {
276
266
  module : TextField,
277
267
  labelText: 'Text',
278
268
  reference: 'textFieldTwo',
279
- value: 'world!',
269
+ value : 'world!',
280
270
  listeners: {
281
- change : 'up.onTextChange'
271
+ change: 'up.onTextChange'
282
272
  }
283
273
  }, {
284
- module: Component,
285
- reference: 'foo',
274
+ module : Component,
275
+ reference: 'foo'
286
276
  }]
287
277
  }
288
- onTextChange(data){
278
+ onTextChange(data) {
289
279
  this.getReference('foo').html = `${this.getReference('textFieldOne').value} ${this.getReference('textFieldTwo').value}`
290
280
  }
291
281
  }
@@ -293,9 +283,9 @@ Neo.setupClass(MainView);
293
283
  </pre>
294
284
 
295
285
  <pre data-neo>
286
+ import Component from '../component/Base.mjs';
296
287
  import Container from '../container/Base.mjs';
297
288
  import TextField from '../form/field/Text.mjs';
298
- import Component from '../component/Base.mjs';
299
289
 
300
290
  class MainView extends Container {
301
291
  static config = {
@@ -306,22 +296,22 @@ class MainView extends Container {
306
296
  bar: 'world!'
307
297
  }
308
298
  },
309
- layout : {ntype:'vbox', align:'start'},
310
-
311
- items: [{
299
+
300
+ layout: {ntype:'vbox', align:'start'},
301
+ items : [{
312
302
  module : TextField,
313
303
  labelText: 'Text',
314
- bind: {value: {twoWay: true, value: data => data.foo}}
304
+ bind : {value: {twoWay: true, value: data => data.foo}}
315
305
  }, {
316
306
  module : TextField,
317
307
  labelText: 'Text',
318
- bind: {value: {twoWay: true, value: data => data.bar}}
308
+ bind : {value: {twoWay: true, value: data => data.bar}}
319
309
  }, {
320
310
  module: Component,
321
- bind: {html: data => `${data.foo} ${data.bar}`}
311
+ bind : {html: data => `${data.foo} ${data.bar}`}
322
312
  }]
323
313
  }
324
- onTextChange(data){
314
+ onTextChange(data) {
325
315
  this.getReference('foo').html = data.value;
326
316
  }
327
317
  }
@@ -1,9 +1,9 @@
1
- Neo.mjs is multi-threaded. There are app worker threads
1
+ Neo.mjs is multi-threaded. There are worker threads
2
2
  that handle data access, application logic,
3
3
  and keeping track of DOM updates. Practically all your
4
4
  application logic is run in parallel in these threads.
5
5
  However, anything that needs to actually reference or update
6
- the DOM, or use the `window` object, must be done in the main
6
+ the DOM (`window.document`), or just use the `window` object, must be done in the main
7
7
  application thread.
8
8
 
9
9
  That's the purpose of main thread addons. These are classes whose
@@ -18,15 +18,14 @@ please return a specified `window` property." Neo.mjs
18
18
  lets you do that via `Neo.Main.getByPath()`. For
19
19
  example, the following statement logs the URL query string.
20
20
 
21
-
22
21
  <pre data-javascript>
23
- const search = await Neo.Main.getByPath('window.location.search');
22
+ const search = await Neo.Main.getByPath({path: 'window.location.search'});
24
23
  console.log(search); // Logs the search string
25
24
  </pre>
26
25
 
27
- `Neo.Main` has some simple methods for accessing the
28
- main thread, but for something with a non-trivial API
29
- you'd use a _main thread addon_.
26
+ `Neo.Main` & `Neo.main.DomAccess` provide some basic methods for accessing the
27
+ main thread, but in case you want to use a third party library which relies on directly
28
+ working with the DOM, you'd use a _main thread addon_.
30
29
 
31
30
  Google Maps is a good example of this. In Neo.mjs, most
32
31
  views are responsible for updating their own vdom, but
@@ -36,11 +35,10 @@ certain things via the Google Maps API. Therefore, in Neo.mjs,
36
35
  Google Maps is implemented as a main thread addon which
37
36
  loads the libraries and exposes whatever methods we'll need
38
37
  to run from the other Neo.mjs threads. In addition, in a
39
- Neo.mjs application we want to use Google maps like any other
38
+ Neo.mjs application we want to use Google Maps like any other
40
39
  component, so Neo.mjs also provides a component wrapper. In
41
40
  summary:
42
41
  - The main-thread addon contains the code run in the main thread,
43
- and exposes what methods can be run by other web-workders,
44
- and
42
+ and exposes what methods can be run by other web-workers (remote method access)
45
43
  - The component wrapper lets you use it like any other component,
46
44
  internally calling the main thread methods as needed.
@@ -8,6 +8,10 @@
8
8
  padding : 0 3rem;
9
9
  width : -webkit-fill-available;
10
10
 
11
+ @media (max-width: 600px) {
12
+ padding: 1rem 1rem 0;
13
+ }
14
+
11
15
  pre[data-javascript] {
12
16
  border : thin solid lightgray;
13
17
  border-radius: 4px;
@@ -29,14 +29,15 @@
29
29
  }
30
30
 
31
31
  .sidenav-button {
32
- box-shadow: 0 5px 10px rgba(0,0,0,.3);
33
- display : flex;
34
- height : 35px;
35
- min-width : 35px;
36
- position : absolute;
37
- right : -42px;
38
- width : 35px;
39
- z-index : 101;
32
+ background-color: #fff;
33
+ box-shadow : 0 5px 10px rgba(0,0,0,.3);
34
+ display : flex;
35
+ height : 35px;
36
+ min-width : 35px;
37
+ position : absolute;
38
+ right : -42px;
39
+ width : 35px;
40
+ z-index : 101;
40
41
 
41
42
  .neo-button-glyph {
42
43
  font-size: 13px;
@@ -5,7 +5,8 @@
5
5
 
6
6
  .info-block {
7
7
  background-color: lighten(#8BA6FF, 10%);
8
- border-radius : 10px;
8
+ border-radius : 8px;
9
+ box-shadow : 0 3px 6px rgba(0, 0, 0, 0.3);
9
10
  margin-left : auto;
10
11
  margin-right : auto;
11
12
  margin-top : 2em;
@@ -260,12 +260,12 @@ const DefaultConfig = {
260
260
  useVdomWorker: true,
261
261
  /**
262
262
  * buildScripts/injectPackageVersion.mjs will update this value
263
- * @default '6.32.0'
263
+ * @default '6.33.0'
264
264
  * @memberOf! module:Neo
265
265
  * @name config.version
266
266
  * @type String
267
267
  */
268
- version: '6.32.0'
268
+ version: '6.33.0'
269
269
  };
270
270
 
271
271
  Object.assign(DefaultConfig, {