neo.mjs 6.19.0 → 6.19.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.
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='6.19.0'
23
+ * @member {String} version='6.19.1'
24
24
  */
25
- version: '6.19.0'
25
+ version: '6.19.1'
26
26
  }
27
27
 
28
28
  /**
@@ -35,7 +35,7 @@ class ViewportController extends Controller {
35
35
  */
36
36
  defaultHash: '/home',
37
37
  /**
38
- * @member {String} mainContentLayout_='cube'
38
+ * @member {String} mainContentLayout_='mixed'
39
39
  */
40
40
  mainContentLayout_: 'mixed',
41
41
  /**
@@ -188,7 +188,7 @@ class ViewportController extends Controller {
188
188
  * @param {Object} oldValue
189
189
  */
190
190
  onDocsRoute(params, value, oldValue) {
191
- this.setMainContentIndex(4)
191
+ this.setMainContentIndex(3)
192
192
  }
193
193
 
194
194
  /**
@@ -0,0 +1,123 @@
1
+ import Button from '../../../../src/button/Base.mjs';
2
+ import Component from '../../../../src/component/Base.mjs';
3
+ import Container from '../../../../src/container/Base.mjs';
4
+
5
+ /**
6
+ * @class Portal.view.home.FooterContainer
7
+ * @extends Neo.container.Base
8
+ */
9
+ class FooterContainer extends Container {
10
+ static config = {
11
+ /**
12
+ * @member {String} className='Portal.view.home.FooterContainer'
13
+ * @protected
14
+ */
15
+ className: 'Portal.view.home.FooterContainer',
16
+ /**
17
+ * @member {String[]} cls=['portal-home-footer-container']
18
+ */
19
+ cls: ['portal-home-footer-container'],
20
+ /**
21
+ * @member {Object} itemDefaults
22
+ */
23
+ itemDefaults: {
24
+ module: Container,
25
+ cls : ['portal-home-footer-section'],
26
+ layout: {ntype: 'vbox', align: 'start'},
27
+
28
+ itemDefaults: {
29
+ module: Button,
30
+ ui : 'ghost'
31
+ }
32
+ },
33
+ /**
34
+ * @member {Object[]} items
35
+ */
36
+ items: [{
37
+ items: [{
38
+ module: Component,
39
+ cls : ['neo-h2'],
40
+ html : 'Content',
41
+ tag : 'h2'
42
+ }, {
43
+ iconCls: 'fas fa-people-group',
44
+ route : '/about-us',
45
+ text : 'About Us'
46
+ }, {
47
+ iconCls: 'fas fa-book',
48
+ route : '/docs',
49
+ text : 'API Docs'
50
+ }, {
51
+ iconCls: 'fas fa-blog',
52
+ route : '/blog',
53
+ text : 'Blog'
54
+ }, {
55
+ iconCls: 'fas fa-graduation-cap',
56
+ route : '/learn',
57
+ text : 'Learn'
58
+ }, {
59
+ iconCls: 'fas fa-handshake-angle',
60
+ route : '/services',
61
+ text : 'Services'
62
+ }]
63
+ }, {
64
+ items: [{
65
+ module: Component,
66
+ cls : ['neo-h2'],
67
+ html : 'Community',
68
+ tag : 'h2'
69
+ }, {
70
+ iconCls: 'fa-brands fa-github',
71
+ text : 'Contribute',
72
+ url : 'https://github.com/neomjs/neo/blob/dev/CONTRIBUTING.md'
73
+ }, {
74
+ iconCls: 'fa-brands fa-github',
75
+ text : 'Code of Conduct',
76
+ url : 'https://github.com/neomjs/neo/blob/dev/.github/CODE_OF_CONDUCT.md'
77
+ }, {
78
+ iconCls: 'fa-brands fa-github',
79
+ text : 'Report Issues',
80
+ url : 'https://github.com/neomjs/neo/issues'
81
+ }, {
82
+ iconCls: 'fa-brands fa-slack',
83
+ text : 'Slack',
84
+ url : 'https://join.slack.com/t/neomjs/shared_invite/zt-6c50ueeu-3E1~M4T9xkNnb~M_prEEOA'
85
+ }, {
86
+ iconCls: 'fa-brands fa-discord',
87
+ text : 'Discord',
88
+ url : 'https://discord.gg/6p8paPq'
89
+ }]
90
+ }, {
91
+ items: [{
92
+ module: Component,
93
+ cls : ['neo-h2'],
94
+ html : 'Social Media',
95
+ tag : 'h2'
96
+ }, {
97
+ iconCls: 'fa-brands fa-linkedin',
98
+ text : 'LinkedIn',
99
+ url : 'https://www.linkedin.com/company/neo-mjs/'
100
+ }, {
101
+ iconCls: 'fa-brands fa-facebook',
102
+ text : 'Facebook',
103
+ url : 'https://www.facebook.com/neo.mjs'
104
+ }, {
105
+ iconCls: 'fa-brands fa-x-twitter',
106
+ text : 'X',
107
+ url : 'https://x.com/neomjs1'
108
+ }]
109
+ }],
110
+ /**
111
+ * @member {Object} layout={ntype:'hbox',align:'stretch'}
112
+ */
113
+ layout: {ntype: 'hbox', align: 'stretch'},
114
+ /**
115
+ * @member {String} tag='footer'
116
+ */
117
+ tag: 'footer'
118
+ }
119
+ }
120
+
121
+ Neo.setupClass(FooterContainer);
122
+
123
+ export default FooterContainer;
@@ -1,4 +1,5 @@
1
- import BaseContainer from './BaseContainer.mjs';
1
+ import BaseContainer from './BaseContainer.mjs';
2
+ import FooterContainer from '../FooterContainer.mjs';
2
3
 
3
4
  /**
4
5
  * @class Portal.view.home.parts.AfterMath
@@ -11,6 +12,10 @@ class AfterMath extends BaseContainer {
11
12
  * @protected
12
13
  */
13
14
  className: 'Portal.view.home.parts.AfterMath',
15
+ /**
16
+ * @member {String[]} cls=['portal-home-aftermath']
17
+ */
18
+ cls: ['portal-home-aftermath'],
14
19
  /**
15
20
  * @member {Object} layout={ntype:'vbox',align:'stretch',pack:'center'}
16
21
  */
@@ -19,39 +24,27 @@ class AfterMath extends BaseContainer {
19
24
  * @member {Object[]} items
20
25
  */
21
26
  items: [{
22
- ntype: 'container',
23
- flex : 1
27
+ flex: 1
24
28
  }, {
25
- cls : 'neo-h1',
29
+ cls : ['neo-h1'],
26
30
  flex: 'none',
27
31
  html: 'Additional Stuff',
28
- vdom: {tag: 'h1'}
32
+ tag : 'h1'
29
33
  }, {
30
- cls : 'neo-h2',
34
+ cls : ['neo-h2'],
31
35
  flex: 'none',
32
36
  html: 'More to come here',
33
- vdom: {tag: 'h2'}
37
+ tag : 'h2'
34
38
  }, {
35
- cls : 'neo-content',
39
+ cls : ['neo-content'],
36
40
  flex: 'none',
37
- html: 'Neo uses several cores to run the application. See the spinner on the page?',
38
- vdom: {tag: 'p'}
41
+ html: 'Lorem Ipsum',
42
+ tag : 'p'
39
43
  }, {
40
- ntype: 'container',
41
- flex : 1
44
+ flex: 1
42
45
  }, {
43
- ntype : 'container',
44
- cls : 'home-footer',
45
- height: '40%',
46
- html : 'This is the footer',
47
- vdom : {tag: 'footer'},
48
-
49
- style : { // todo: css
50
- background: 'black',
51
- color : 'white',
52
- height : '40%',
53
- padding : '15px'
54
- }
46
+ module: FooterContainer,
47
+ height: '35%'
55
48
  }]
56
49
  }
57
50
  }
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='6.19.0'
23
+ * @member {String} version='6.19.1'
24
24
  */
25
- version: '6.19.0'
25
+ version: '6.19.1'
26
26
  }
27
27
 
28
28
  /**
@@ -0,0 +1,76 @@
1
+ import ComponentModel from '../../../src/model/Component.mjs';
2
+ import Label from '../../../src/component/Label.mjs';
3
+ import TextField from '../../../src/form/field/Text.mjs';
4
+ import Viewport from '../../../src/container/Viewport.mjs';
5
+
6
+ /**
7
+ * @class Neo.examples.model.twoWay.MainContainer
8
+ * @extends Neo.container.Viewport
9
+ */
10
+ class MainContainer extends Viewport {
11
+ static config = {
12
+ /**
13
+ * @member {String} className='Neo.examples.model.twoWay.MainContainer'
14
+ * @protected
15
+ */
16
+ className: 'Neo.examples.model.twoWay.MainContainer',
17
+ /**
18
+ * @member {Object|Neo.model.Component} model
19
+ */
20
+ model: {
21
+ data: {
22
+ user: {
23
+ details: {
24
+ firstname: 'Tobias',
25
+ lastname : 'Uhlig'
26
+ }
27
+ }
28
+ }
29
+ },
30
+ /**
31
+ * @member {Object[]} items
32
+ */
33
+ items: [{
34
+ module : Label,
35
+ flex : 'none',
36
+ labelText : 'Firstname',
37
+ labelWidth: 110,
38
+ width : 300,
39
+
40
+ bind: {
41
+ text: data => data.user.details.firstname + ' ' + data.user.details.lastname
42
+ },
43
+ }, {
44
+ module : TextField,
45
+ flex : 'none',
46
+ labelText : 'Firstname',
47
+ labelWidth: 110,
48
+ style : {marginTop: '2em'},
49
+ width : 300,
50
+
51
+ bind: {
52
+ value: {twoWay: true, value: data => data.user.details.firstname}
53
+ },
54
+ }, {
55
+ module : TextField,
56
+ flex : 'none',
57
+ labelText : 'Lastname',
58
+ labelWidth: 110,
59
+ width : 300,
60
+
61
+ bind: {
62
+ value: {twoWay: true, value: data => data.user.details.lastname}
63
+ },
64
+ }],
65
+ /**
66
+ * @member {Object} style
67
+ */
68
+ style: {
69
+ padding: '5em'
70
+ }
71
+ }
72
+ }
73
+
74
+ Neo.setupClass(MainContainer);
75
+
76
+ export default MainContainer;
@@ -0,0 +1,6 @@
1
+ import MainContainer from './MainContainer.mjs';
2
+
3
+ export const onStart = () => Neo.app({
4
+ mainView: MainContainer,
5
+ name : 'Neo.examples.model.twoWay'
6
+ });
@@ -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>model.Component: 2 way binding</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/model/twoWay/app.mjs",
3
+ "basePath" : "../../../",
4
+ "environment": "development",
5
+ "mainPath" : "./Main.mjs"
6
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neo.mjs",
3
- "version": "6.19.0",
3
+ "version": "6.19.1",
4
4
  "description": "The webworkers driven UI framework",
5
5
  "type": "module",
6
6
  "repository": {
@@ -43,7 +43,7 @@
43
43
  },
44
44
  "homepage": "https://neomjs.com/",
45
45
  "devDependencies": {
46
- "@fortawesome/fontawesome-free": "^6.5.2",
46
+ "@fortawesome/fontawesome-free": "^6.6.0",
47
47
  "autoprefixer": "^10.4.19",
48
48
  "chalk": "^5.3.0",
49
49
  "clean-webpack-plugin": "^4.0.0",
@@ -52,7 +52,7 @@
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.0.1",
55
+ "inquirer": "^10.0.4",
56
56
  "marked": "^13.0.2",
57
57
  "monaco-editor": "^0.50.0",
58
58
  "neo-jsdoc": "1.0.1",
@@ -0,0 +1,31 @@
1
+ .portal-home-footer-container {
2
+ background-color: black;
3
+ padding : 2em;
4
+
5
+ .neo-button.neo-button-ghost {
6
+ justify-content: start;
7
+ min-width : 10em;
8
+
9
+ &:hover {
10
+ background-color: #333;
11
+ }
12
+
13
+ .neo-button-glyph {
14
+ color : white;
15
+ font-size: 16px;
16
+ }
17
+
18
+ .neo-button-text {
19
+ color: white;
20
+ }
21
+ }
22
+
23
+ h2.neo-h2 {
24
+ color: white;
25
+ }
26
+
27
+ .portal-home-footer-section {
28
+ background-color: transparent;
29
+ color : white;
30
+ }
31
+ }
@@ -0,0 +1,5 @@
1
+ .portal-home-aftermath {
2
+ p {
3
+ text-align: center;
4
+ }
5
+ }
@@ -18,9 +18,9 @@
18
18
  --rot-y: 0deg;
19
19
  --rot-z: 0deg;
20
20
 
21
- --side-x: 300px; /* horizontal */
22
- --side-y: 300px; /* vertical */
23
- --side-z: 300px; /* depth */
21
+ --side-x: 300px;
22
+ --side-y: 300px;
23
+ --side-z: 300px;
24
24
 
25
25
  background-color : transparent;
26
26
  height : var(--side-y);
@@ -260,12 +260,12 @@ const DefaultConfig = {
260
260
  useVdomWorker: true,
261
261
  /**
262
262
  * buildScripts/injectPackageVersion.mjs will update this value
263
- * @default '6.19.0'
263
+ * @default '6.19.1'
264
264
  * @memberOf! module:Neo
265
265
  * @name config.version
266
266
  * @type String
267
267
  */
268
- version: '6.19.0'
268
+ version: '6.19.1'
269
269
  };
270
270
 
271
271
  Object.assign(DefaultConfig, {
@@ -468,11 +468,12 @@ class MainContainer extends Container {
468
468
  flex : 1,
469
469
  layout: {ntype: 'hbox', align: 'stretch'},
470
470
  items : [{
471
- module: Container,
472
- cls : ['neo-calendar-sidebar', 'neo-container'],
473
- layout: {ntype: 'vbox', align: 'stretch'},
474
- width : me.sideBarWidth,
475
- items : [me.dateSelector, me.calendarsContainer]
471
+ module : Container,
472
+ cls : ['neo-calendar-sidebar', 'neo-container'],
473
+ layout : {ntype: 'vbox', align: 'stretch'},
474
+ minWidth: me.sideBarWidth,
475
+ width : me.sideBarWidth,
476
+ items : [me.dateSelector, me.calendarsContainer]
476
477
  }, {
477
478
  module : Container,
478
479
  flex : 1,
@@ -637,8 +638,8 @@ class MainContainer extends Container {
637
638
  * @param {Number} multiplier
638
639
  */
639
640
  switchInterval(multiplier) {
640
- let me = this,
641
- currentDate = me.data.currentDate,
641
+ let me = this,
642
+ {currentDate} = me.data,
642
643
 
643
644
  map = {
644
645
  day : () => {currentDate.setDate( currentDate.getDate() + multiplier)},
@@ -13,10 +13,11 @@ import VDomUtil from '../util/VDom.mjs';
13
13
  import VNodeUtil from '../util/VNode.mjs';
14
14
 
15
15
  const
16
- addUnits = value => value == null ? value : isNaN(value) ? value : `${value}px`,
17
- closestController = Symbol.for('closestController'),
18
- closestModel = Symbol.for('closestModel'),
19
- lengthRE = /^\d+\w+$/;
16
+ addUnits = value => value == null ? value : isNaN(value) ? value : `${value}px`,
17
+ closestController = Symbol.for('closestController'),
18
+ closestModel = Symbol.for('closestModel'),
19
+ lengthRE = /^\d+\w+$/,
20
+ twoWayBindingSymbol = Symbol.for('twoWayBinding');
20
21
 
21
22
  /**
22
23
  * @class Neo.component.Base
@@ -555,11 +556,13 @@ class Base extends CoreBase {
555
556
  * @protected
556
557
  */
557
558
  afterSetConfig(key, value, oldValue) {
558
- if (Neo.currentWorker.isUsingViewModels && oldValue !== undefined) {
559
- let binding = this.bind?.[key];
559
+ let me = this;
560
+
561
+ if (Neo.currentWorker.isUsingViewModels && me[twoWayBindingSymbol] && oldValue !== undefined) {
562
+ let binding = me.bind?.[key];
560
563
 
561
564
  if (binding?.twoWay) {
562
- this.getModel()?.setData(key, value)
565
+ this.getModel()?.setData(binding.key, value)
563
566
  }
564
567
  }
565
568
  }
@@ -759,7 +759,7 @@ class DateSelector extends Component {
759
759
  */
760
760
  onComponentWheel(data) {
761
761
  let me = this,
762
- {deltaX, deltaY} = me,
762
+ {deltaX, deltaY} = data,
763
763
  wheelDelta = me.mouseWheelDelta,
764
764
  date, monthIncrement, yearIncrement;
765
765
 
@@ -830,7 +830,7 @@ class DateSelector extends Component {
830
830
  // using force => we do want to keep the same ids
831
831
  syncIds && me.syncVdomIds(me.vnode, me.vdom, true);
832
832
 
833
- me.triggerVdomUpdate(silent)
833
+ !silent && me.update()
834
834
  }
835
835
 
836
836
  /**
@@ -92,9 +92,7 @@ class Time extends Picker {
92
92
 
93
93
  me.originalConfig.value = me.formatTime(me.value);
94
94
 
95
- if (clearTrigger) {
96
- clearTrigger.onFieldChange();
97
- }
95
+ clearTrigger?.onFieldChange();
98
96
 
99
97
  me.collection = Neo.create({
100
98
  module : Collection,
@@ -115,9 +113,8 @@ class Time extends Picker {
115
113
  );
116
114
 
117
115
  me.list.on({
118
- itemClick : me.onListItemClick,
119
- itemNavigate: me.onListItemNavigate,
120
- scope : me
116
+ itemClick: me.onListItemClick,
117
+ scope : me
121
118
  })
122
119
  }
123
120
 
@@ -273,7 +270,8 @@ class Time extends Picker {
273
270
 
274
271
  while (currentDate <= endDate) {
275
272
  listItems.push({
276
- value: dt.format(currentDate)
273
+ isRecord: true,
274
+ value : dt.format(currentDate)
277
275
  });
278
276
 
279
277
  currentDate.setSeconds(currentDate.getSeconds() + me.stepSize)
@@ -350,10 +348,12 @@ class Time extends Picker {
350
348
  }
351
349
 
352
350
  /**
353
- * @param {Object} record
351
+ * @param {Object} data
352
+ * @param {Object} data.record
354
353
  */
355
- onListItemClick(record) {
354
+ onListItemClick(data) {
356
355
  let me = this,
356
+ {record} = data,
357
357
  oldValue = me.value,
358
358
  {value} = record;
359
359
 
@@ -365,13 +365,6 @@ class Time extends Picker {
365
365
  }
366
366
  }
367
367
 
368
- /**
369
- * @param {Object} record
370
- */
371
- onListItemNavigate(record) {
372
- this.onListItemClick(record)
373
- }
374
-
375
368
  /**
376
369
  *
377
370
  */
@@ -409,6 +402,15 @@ class Time extends Picker {
409
402
  list.selectionModel.select(id);
410
403
 
411
404
  if (!preventFocus) {
405
+
406
+ if (list.mounted) {
407
+ list.focus(id)
408
+ } else {
409
+ list.on('mounted', () => {
410
+ list.focus(id)
411
+ }, me, {once: true})
412
+ }
413
+
412
414
  list.focus(id)
413
415
  } else {
414
416
  Neo.main.DomAccess.scrollIntoView({
@@ -168,7 +168,9 @@ class Cube extends Card {
168
168
  me.updateContainerSize()
169
169
  } else {
170
170
  container.on('mounted', () => {
171
- me.updateContainerSize()
171
+ me.timeout(50).then(() => {
172
+ me.updateContainerSize()
173
+ })
172
174
  })
173
175
  }
174
176
  }
@@ -415,6 +417,8 @@ class Cube extends Card {
415
417
  let {container} = this,
416
418
  {height, width} = await container.getDomRect(container.id);
417
419
 
420
+ console.log({height, width});
421
+
418
422
  this.set({
419
423
  sideX: width,
420
424
  sideY: height,
package/src/list/Base.mjs CHANGED
@@ -593,10 +593,10 @@ class Base extends Component {
593
593
 
594
594
  /**
595
595
  * Calls focus() on the top level DOM node of this component or on a given node via id
596
- * @param {String} id
596
+ * @param {String} [id=this.id]
597
597
  */
598
- focus(id) {
599
- Neo.main.addon.Navigator.navigateTo([id, this.navigator])
598
+ focus(id=this.id) {
599
+ this.mounted && Neo.main.addon.Navigator.navigateTo([id, this.navigator])
600
600
  }
601
601
 
602
602
  /**
@@ -3,8 +3,9 @@ import ClassSystemUtil from '../util/ClassSystem.mjs';
3
3
  import NeoArray from '../util/Array.mjs';
4
4
  import Observable from '../core/Observable.mjs';
5
5
 
6
- const dataVariableRegex = /data((?!(\.[a-z_]\w*\(\)))\.[a-z_]\w*)+/gi,
7
- variableNameRegex = /^\w*/;
6
+ const dataVariableRegex = /data((?!(\.[a-z_]\w*\(\)))\.[a-z_]\w*)+/gi,
7
+ twoWayBindingSymbol = Symbol.for('twoWayBinding'),
8
+ variableNameRegex = /^\w*/;
8
9
 
9
10
  /**
10
11
  * An optional component (view) model for adding bindings to configs
@@ -213,6 +214,7 @@ class Component extends Base {
213
214
  * @param {String} componentId
214
215
  * @param {String} formatter
215
216
  * @param {String} value
217
+ * @returns {String[]}
216
218
  */
217
219
  createBindingByFormatter(componentId, formatter, value) {
218
220
  let me = this,
@@ -220,7 +222,9 @@ class Component extends Base {
220
222
 
221
223
  formatterVars.forEach(key => {
222
224
  me.createBinding(componentId, key, value, formatter)
223
- })
225
+ });
226
+
227
+ return formatterVars
224
228
  }
225
229
 
226
230
  /**
@@ -228,12 +232,21 @@ class Component extends Base {
228
232
  */
229
233
  createBindings(component) {
230
234
  Object.entries(component.bind).forEach(([key, value]) => {
235
+ let twoWayBinding = false,
236
+ formatterVars;
237
+
231
238
  if (Neo.isObject(value)) {
232
- value = value.value
239
+ twoWayBinding = true;
240
+ value = value.value
233
241
  }
234
242
 
235
243
  if (!this.isStoreValue(value)) {
236
- this.createBindingByFormatter(component.id, value, key)
244
+ formatterVars = this.createBindingByFormatter(component.id, value, key);
245
+
246
+ if (twoWayBinding) {
247
+ component.bind[key].key = formatterVars[0];
248
+ component[twoWayBindingSymbol] = true;
249
+ }
237
250
  }
238
251
  })
239
252
  }
@@ -279,7 +292,13 @@ class Component extends Base {
279
292
 
280
293
  Object.defineProperty(root, key, {
281
294
  get() {
282
- return root['_' + key]
295
+ let value = root['_' + key];
296
+
297
+ if (Neo.typeOf(value) === 'Date') {
298
+ value = new Date(value.valueOf())
299
+ }
300
+
301
+ return value
283
302
  },
284
303
 
285
304
  set(value) {
@@ -257,6 +257,7 @@ class Helper extends Base {
257
257
  oldChildNodes = oldVnode.childNodes || [],
258
258
  i = 0,
259
259
  indexDelta = 0,
260
+ insertDelta = 0,
260
261
  len = Math.max(childNodes.length, oldChildNodes.length),
261
262
  childNode, nodeInNewTree, oldChildNode;
262
263
 
@@ -271,6 +272,8 @@ class Helper extends Base {
271
272
  childNode = childNodes[i];
272
273
  oldChildNode = oldChildNodes[i + indexDelta];
273
274
 
275
+ // console.log(childNode?.id, oldChildNode?.id);
276
+
274
277
  if (!childNode && !oldChildNode) {
275
278
  break
276
279
  }
@@ -288,6 +291,7 @@ class Helper extends Base {
288
291
  if (!nodeInNewTree) {
289
292
  me.removeNode({deltas, oldVnode: oldChildNode, oldVnodeMap});
290
293
  i--;
294
+ insertDelta++;
291
295
  continue
292
296
  }
293
297
 
@@ -300,7 +304,15 @@ class Helper extends Base {
300
304
  }
301
305
 
302
306
  if (childNode) {
303
- me[oldVnodeMap.get(childNode.id) ? 'moveNode' : 'insertNode']({deltas, oldVnodeMap, vnode: childNode, vnodeMap})
307
+ if (oldVnodeMap.get(childNode.id)) {
308
+ me.moveNode({deltas, insertDelta, oldVnodeMap, vnode: childNode, vnodeMap});
309
+ } else {
310
+ me.insertNode({deltas, index: i + insertDelta, oldVnodeMap, vnode: childNode, vnodeMap});
311
+ }
312
+
313
+ if (oldChildNode && vnodeId === vnodeMap.get(oldChildNode.id)?.parentNode.id) {
314
+ len++;
315
+ }
304
316
  }
305
317
  }
306
318
 
@@ -590,14 +602,14 @@ class Helper extends Base {
590
602
  /**
591
603
  * @param {Object} config
592
604
  * @param {Object} config.deltas
605
+ * @param {Number} config.index
593
606
  * @param {Map} config.oldVnodeMap
594
607
  * @param {Neo.vdom.VNode} config.vnode
595
608
  * @param {Map} config.vnodeMap
596
609
  */
597
610
  insertNode(config) {
598
- let {deltas, oldVnodeMap, vnode, vnodeMap} = config,
611
+ let {deltas, index, oldVnodeMap, vnode, vnodeMap} = config,
599
612
  details = vnodeMap.get(vnode.id),
600
- {index} = details,
601
613
  parentId = details.parentNode.id,
602
614
  me = this,
603
615
  movedNodes = me.findMovedNodes({oldVnodeMap, vnode, vnodeMap}),
@@ -621,12 +633,13 @@ class Helper extends Base {
621
633
  /**
622
634
  * @param {Object} config
623
635
  * @param {Object} config.deltas
636
+ * @param {Number} config.insertDelta
624
637
  * @param {Map} config.oldVnodeMap
625
638
  * @param {Neo.vdom.VNode} config.vnode
626
639
  * @param {Map} config.vnodeMap
627
640
  */
628
641
  moveNode(config) {
629
- let {deltas, oldVnodeMap, vnode, vnodeMap} = config,
642
+ let {deltas, insertDelta, oldVnodeMap, vnode, vnodeMap} = config,
630
643
  details = vnodeMap.get(vnode.id),
631
644
  {index, parentNode} = details,
632
645
  parentId = parentNode.id,
@@ -649,7 +662,7 @@ class Helper extends Base {
649
662
  }
650
663
  }
651
664
 
652
- deltas.default.push({action: 'moveNode', id: vnode.id, index, parentId});
665
+ deltas.default.push({action: 'moveNode', id: vnode.id, index: index + insertDelta, parentId});
653
666
 
654
667
  // Add the node into the old vnode tree to simplify future OPs.
655
668
  // NeoArray.insert() will switch to move() in case the node already exists.
@@ -8,6 +8,88 @@ import VDomUtil from '../../../src/util/VDom.mjs';
8
8
  let deltas, output, vdom, vnode;
9
9
 
10
10
  StartTest(t => {
11
+ t.it('Week view: Infinite Scrolling', t => {
12
+ vdom =
13
+ {id: 'neo-vnode-1', cn: [
14
+ {id: 'col-6'},
15
+ {id: 'col-7'},
16
+ {id: 'col-8'},
17
+ {id: 'col-9'},
18
+ {id: 'col-10'},
19
+ {id: 'col-11'},
20
+ {id: 'col-12'},
21
+ {id: 'col-13'},
22
+ {id: 'col-14'},
23
+ {id: 'col-15'}
24
+ ]};
25
+
26
+ vnode = VdomHelper.create(vdom);
27
+
28
+ vdom =
29
+ {id: 'neo-vnode-1', cn: [
30
+ {id: 'col-1'},
31
+ {id: 'col-2'},
32
+ {id: 'col-3'},
33
+ {id: 'col-4'},
34
+ {id: 'col-5'},
35
+ {id: 'col-6'},
36
+ {id: 'col-7'},
37
+ {id: 'col-8'},
38
+ {id: 'col-9'},
39
+ {id: 'col-10'}
40
+ ]};
41
+
42
+ output = VdomHelper.update({vdom, vnode}); deltas = output.deltas; vnode = output.vnode;
43
+
44
+ t.is(deltas.length, 10, 'Count deltas equals 10');
45
+
46
+ t.isDeeplyStrict(deltas, [
47
+ {action: 'insertNode', index: 0, outerHTML: '<div id="col-1"></div>', parentId: 'neo-vnode-1'},
48
+ {action: 'insertNode', index: 1, outerHTML: '<div id="col-2"></div>', parentId: 'neo-vnode-1'},
49
+ {action: 'insertNode', index: 2, outerHTML: '<div id="col-3"></div>', parentId: 'neo-vnode-1'},
50
+ {action: 'insertNode', index: 3, outerHTML: '<div id="col-4"></div>', parentId: 'neo-vnode-1'},
51
+ {action: 'insertNode', index: 4, outerHTML: '<div id="col-5"></div>', parentId: 'neo-vnode-1'},
52
+ {action: 'removeNode', id: 'col-11'},
53
+ {action: 'removeNode', id: 'col-12'},
54
+ {action: 'removeNode', id: 'col-13'},
55
+ {action: 'removeNode', id: 'col-14'},
56
+ {action: 'removeNode', id: 'col-15'}
57
+ ], 'deltas got created successfully');
58
+
59
+ t.diag('Revert operation');
60
+
61
+ vdom =
62
+ {id: 'neo-vnode-1', cn: [
63
+ {id: 'col-6'},
64
+ {id: 'col-7'},
65
+ {id: 'col-8'},
66
+ {id: 'col-9'},
67
+ {id: 'col-10'},
68
+ {id: 'col-11'},
69
+ {id: 'col-12'},
70
+ {id: 'col-13'},
71
+ {id: 'col-14'},
72
+ {id: 'col-15'}
73
+ ]};
74
+
75
+ output = VdomHelper.update({vdom, vnode}); deltas = output.deltas; vnode = output.vnode;
76
+
77
+ t.is(deltas.length, 10, 'Count deltas equals 10');
78
+
79
+ t.isDeeplyStrict(deltas, [
80
+ {action: 'insertNode', index: 10, outerHTML: '<div id="col-11"></div>', parentId: 'neo-vnode-1'},
81
+ {action: 'insertNode', index: 11, outerHTML: '<div id="col-12"></div>', parentId: 'neo-vnode-1'},
82
+ {action: 'insertNode', index: 12, outerHTML: '<div id="col-13"></div>', parentId: 'neo-vnode-1'},
83
+ {action: 'insertNode', index: 13, outerHTML: '<div id="col-14"></div>', parentId: 'neo-vnode-1'},
84
+ {action: 'insertNode', index: 14, outerHTML: '<div id="col-15"></div>', parentId: 'neo-vnode-1'},
85
+ {action: 'removeNode', id: 'col-1'},
86
+ {action: 'removeNode', id: 'col-2'},
87
+ {action: 'removeNode', id: 'col-3'},
88
+ {action: 'removeNode', id: 'col-4'},
89
+ {action: 'removeNode', id: 'col-5'}
90
+ ], 'deltas got created successfully');
91
+ });
92
+
11
93
  t.it('Drag an event to the top inside the same column', t => {
12
94
  vdom =
13
95
  {id: 'neo-calendar-week', cn: [
@@ -2390,14 +2472,14 @@ StartTest(t => {
2390
2472
  t.is(deltas.length, 16, 'Count deltas equals 16');
2391
2473
 
2392
2474
  t.isDeeplyStrict(deltas, [
2393
- {action: 'insertNode', index: 14, parentId: 'neo-vnode-150', outerHTML: t.any(String)},
2394
- {action: 'insertNode', index: 15, parentId: 'neo-vnode-150', outerHTML: t.any(String)},
2395
- {action: 'insertNode', index: 16, parentId: 'neo-vnode-150', outerHTML: t.any(String)},
2396
- {action: 'insertNode', index: 17, parentId: 'neo-vnode-150', outerHTML: t.any(String)},
2397
- {action: 'insertNode', index: 18, parentId: 'neo-vnode-150', outerHTML: t.any(String)},
2398
- {action: 'insertNode', index: 19, parentId: 'neo-vnode-150', outerHTML: t.any(String)},
2399
- {action: 'insertNode', index: 20, parentId: 'neo-vnode-150', outerHTML: t.any(String)},
2400
- {action: 'insertNode', index: 21, parentId: 'neo-vnode-150', outerHTML: t.any(String)},
2475
+ {action: 'insertNode', index: 22, parentId: 'neo-vnode-150', outerHTML: t.any(String)},
2476
+ {action: 'insertNode', index: 23, parentId: 'neo-vnode-150', outerHTML: t.any(String)},
2477
+ {action: 'insertNode', index: 24, parentId: 'neo-vnode-150', outerHTML: t.any(String)},
2478
+ {action: 'insertNode', index: 25, parentId: 'neo-vnode-150', outerHTML: t.any(String)},
2479
+ {action: 'insertNode', index: 26, parentId: 'neo-vnode-150', outerHTML: t.any(String)},
2480
+ {action: 'insertNode', index: 27, parentId: 'neo-vnode-150', outerHTML: t.any(String)},
2481
+ {action: 'insertNode', index: 28, parentId: 'neo-vnode-150', outerHTML: t.any(String)},
2482
+ {action: 'insertNode', index: 29, parentId: 'neo-vnode-150', outerHTML: t.any(String)},
2401
2483
  {action: 'removeNode', id: 'neo-component-6__week__2021-02-21'},
2402
2484
  {action: 'removeNode', id: 'neo-vnode-168'},
2403
2485
  {action: 'removeNode', id: 'neo-component-6__week__2021-02-28'},
@@ -494,8 +494,10 @@ StartTest(t => {
494
494
  ]
495
495
  }, 'vnode got updated successfully');
496
496
 
497
+ t.is(deltas.length, 3, 'Count deltas equals 3');
498
+
497
499
  t.isDeeplyStrict(deltas, [
498
- {action: 'moveNode', id: 'neo-list-1__tobiu', index: 2, parentId: 'neo-list-1'},
500
+ {action: 'moveNode', id: 'neo-list-1__tobiu', index: 3, parentId: 'neo-list-1'},
499
501
  {action: 'removeNode', id: 'neo-list-1__jsakalos'},
500
502
  {action: 'removeNode', id: 'neo-list-1__rwaters'}
501
503
  ], 'deltas got created successfully');
@@ -696,7 +698,7 @@ StartTest(t => {
696
698
  t.is(deltas.length, 2, 'Count deltas equals 2');
697
699
 
698
700
  t.isDeeplyStrict(deltas, [
699
- {action: 'moveNode', id: 'level-3', index: 0, parentId: 'level-1'},
701
+ {action: 'moveNode', id: 'level-3', index: 1, parentId: 'level-1'},
700
702
  {action: 'removeNode', id: 'level-2'}
701
703
  ], 'deltas got created successfully');
702
704
  });
@@ -791,8 +793,8 @@ StartTest(t => {
791
793
  }, 'vnode got updated successfully');
792
794
 
793
795
  t.isDeeplyStrict(deltas, [
794
- {action: 'moveNode', id: 'level-3-1', index: 0, parentId: 'level-1'},
795
- {action: 'moveNode', id: 'level-3-2', index: 1, parentId: 'level-1'},
796
+ {action: 'moveNode', id: 'level-3-1', index: 1, parentId: 'level-1'},
797
+ {action: 'moveNode', id: 'level-3-2', index: 2, parentId: 'level-1'},
796
798
  {action: 'removeNode', id: 'level-2'}
797
799
  ], 'deltas got created successfully');
798
800
  });
@@ -810,11 +812,12 @@ StartTest(t => {
810
812
 
811
813
  vnode = VdomHelper.create(vdom);
812
814
 
813
- // replace level 2 with level 3
814
- vdom.cn = vdom.cn[0].cn;
815
-
816
- // adding a new node
817
- vdom.cn.splice(1, 0, {id: 'new-node'})
815
+ vdom =
816
+ {id: 'level-1', cn: [
817
+ {id: 'level-3-1'},
818
+ {id: 'new-node'},
819
+ {id: 'level-3-2'}
820
+ ]};
818
821
 
819
822
  output = VdomHelper.update({vdom, vnode}); deltas = output.deltas; vnode = output.vnode;
820
823
 
@@ -860,9 +863,9 @@ StartTest(t => {
860
863
  t.is(deltas.length, 4, 'Count deltas equals 4');
861
864
 
862
865
  t.isDeeplyStrict(deltas, [
863
- {action: 'moveNode', id: 'level-3-1', index: 0, parentId: 'level-1'},
864
- {action: 'insertNode', index: 1, parentId: 'level-1', outerHTML: t.any(String)},
865
- {action: 'moveNode', id: 'level-3-2', index: 2, parentId: 'level-1'},
866
+ {action: 'moveNode', id: 'level-3-1', index: 1, parentId: 'level-1'},
867
+ {action: 'insertNode', index: 2, parentId: 'level-1', outerHTML: t.any(String)},
868
+ {action: 'moveNode', id: 'level-3-2', index: 3, parentId: 'level-1'},
866
869
  {action: 'removeNode', id: 'level-2'}
867
870
  ], 'deltas got created successfully');
868
871
  });
@@ -216,14 +216,15 @@ StartTest(t => {
216
216
 
217
217
  output = VdomHelper.update({vdom, vnode}); deltas = output.deltas; vnode = output.vnode;
218
218
 
219
- t.is(deltas.length, 12, 'Count deltas equals 12');
219
+ t.is(deltas.length, 13, 'Count deltas equals 13');
220
220
 
221
221
  t.isDeeplyStrict(deltas, [
222
222
  {action: 'moveNode', id: 'neo-event-1__time', index: 0, parentId: 'neo-event-1'},
223
223
  {action: 'moveNode', id: 'neo-event-1__title', index: 1, parentId: 'neo-event-1'},
224
- {action: 'moveNode', id: 'neo-event-2', index: 1, parentId: 'neo-column-1'},
224
+ {action: 'moveNode', id: 'neo-event-2', index: 3, parentId: 'neo-column-1'},
225
225
  { id: 'neo-event-2', cls: {add: ['foo2']}},
226
- {action: 'insertNode', index: 2, parentId: 'neo-column-1', outerHTML: t.any(String)},
226
+ {action: 'insertNode', index: 4, parentId: 'neo-column-1', outerHTML: t.any(String)},
227
+ {action: 'moveNode', id: 'neo-event-4', index: 5, parentId: 'neo-column-1'}, // todo: redundant
227
228
  {action: 'moveNode', id: 'neo-event-4__time', index: 0, parentId: 'neo-event-4'},
228
229
  {action: 'moveNode', id: 'neo-event-4__title', index: 1, parentId: 'neo-event-4'},
229
230
  { id: 'neo-event-4__title', innerHTML: 'Event 4'},
@@ -309,17 +310,17 @@ StartTest(t => {
309
310
  t.is(deltas.length, 16, 'Count deltas equals 16');
310
311
 
311
312
  t.isDeeplyStrict(deltas, [
312
- {action: 'moveNode', id: 'neo-component-1', index: 0, parentId: 'neo-container-1'},
313
+ {action: 'moveNode', id: 'neo-component-1', index: 4, parentId: 'neo-container-1'},
313
314
  { id: 'neo-component-1', cls: {remove: ['foo1']}},
314
- {action: 'moveNode', id: 'neo-component-2', index: 1, parentId: 'neo-container-1'},
315
+ {action: 'moveNode', id: 'neo-component-2', index: 5, parentId: 'neo-container-1'},
315
316
  { id: 'neo-component-2', cls: {remove: ['foo2']}},
316
- {action: 'moveNode', id: 'neo-component-3', index: 2, parentId: 'neo-container-1'},
317
+ {action: 'moveNode', id: 'neo-component-3', index: 6, parentId: 'neo-container-1'},
317
318
  { id: 'neo-component-3', cls: {remove: ['foo3']}},
318
- {action: 'moveNode', id: 'neo-component-4', index: 3, parentId: 'neo-container-1'},
319
+ {action: 'moveNode', id: 'neo-component-4', index: 7, parentId: 'neo-container-1'},
319
320
  { id: 'neo-component-4', cls: {remove: ['foo4']}},
320
- {action: 'moveNode', id: 'neo-component-5', index: 4, parentId: 'neo-container-1'},
321
+ {action: 'moveNode', id: 'neo-component-5', index: 8, parentId: 'neo-container-1'},
321
322
  { id: 'neo-component-5', cls: {remove: ['foo5']}},
322
- {action: 'moveNode', id: 'neo-component-6', index: 5, parentId: 'neo-container-1'},
323
+ {action: 'moveNode', id: 'neo-component-6', index: 9, parentId: 'neo-container-1'},
323
324
  { id: 'neo-component-6', cls: {remove: ['foo6']}},
324
325
  {action: 'removeNode', id: 'neo-wrapper-1'},
325
326
  {action: 'removeNode', id: 'neo-wrapper-2'},
@@ -84,12 +84,12 @@ StartTest(t => {
84
84
  t.is(deltas.length, 7, 'Count deltas equals 7');
85
85
 
86
86
  t.isDeeplyStrict(deltas, [
87
- {action: 'moveNode', id: 'neo-component-1', index: 0, parentId: 'neo-container-1'},
88
- {action: 'moveNode', id: 'neo-component-2', index: 1, parentId: 'neo-container-1'},
89
- {action: 'moveNode', id: 'neo-component-3', index: 2, parentId: 'neo-container-1'},
90
- {action: 'moveNode', id: 'neo-component-4', index: 3, parentId: 'neo-container-1'},
91
- {action: 'moveNode', id: 'neo-component-5', index: 4, parentId: 'neo-container-1'},
92
- {action: 'moveNode', id: 'neo-component-6', index: 5, parentId: 'neo-container-1'},
87
+ {action: 'moveNode', id: 'neo-component-1', index: 1, parentId: 'neo-container-1'},
88
+ {action: 'moveNode', id: 'neo-component-2', index: 2, parentId: 'neo-container-1'},
89
+ {action: 'moveNode', id: 'neo-component-3', index: 3, parentId: 'neo-container-1'},
90
+ {action: 'moveNode', id: 'neo-component-4', index: 4, parentId: 'neo-container-1'},
91
+ {action: 'moveNode', id: 'neo-component-5', index: 5, parentId: 'neo-container-1'},
92
+ {action: 'moveNode', id: 'neo-component-6', index: 6, parentId: 'neo-container-1'},
93
93
  {action: 'removeNode', id: 'neo-vnode-1'}
94
94
  ], 'Deltas got created successfully');
95
95
  });
@@ -175,14 +175,14 @@ StartTest(t => {
175
175
  t.is(deltas.length, 9, 'Count deltas equals 9');
176
176
 
177
177
  t.isDeeplyStrict(deltas, [
178
- {action: 'moveNode', id: 'neo-component-1', index: 0, parentId: 'neo-container-1'},
179
- {action: 'moveNode', id: 'neo-component-2', index: 1, parentId: 'neo-container-1'},
178
+ {action: 'moveNode', id: 'neo-component-1', index: 1, parentId: 'neo-container-1'},
179
+ {action: 'moveNode', id: 'neo-component-2', index: 2, parentId: 'neo-container-1'},
180
180
  {id: 'neo-component-2', style: {backgroundColor: null}},
181
- {action: 'moveNode', id: 'neo-component-3', index: 2, parentId: 'neo-container-1'},
182
- {action: 'moveNode', id: 'neo-component-4', index: 3, parentId: 'neo-container-1'},
181
+ {action: 'moveNode', id: 'neo-component-3', index: 3, parentId: 'neo-container-1'},
182
+ {action: 'moveNode', id: 'neo-component-4', index: 4, parentId: 'neo-container-1'},
183
183
  {id: 'neo-component-4', cls: {remove: ['custom-cls']}},
184
- {action: 'moveNode', id: 'neo-component-5', index: 4, parentId: 'neo-container-1'},
185
- {action: 'moveNode', id: 'neo-component-6', index: 5, parentId: 'neo-container-1'},
184
+ {action: 'moveNode', id: 'neo-component-5', index: 5, parentId: 'neo-container-1'},
185
+ {action: 'moveNode', id: 'neo-component-6', index: 6, parentId: 'neo-container-1'},
186
186
  {action: 'removeNode', id: 'neo-vnode-1'}
187
187
  ], 'Deltas got created successfully');
188
188
  });