neo.mjs 6.15.5 → 6.15.6

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 (60) hide show
  1. package/apps/ServiceWorker.mjs +2 -2
  2. package/apps/form/view/FormContainerController.mjs +1 -1
  3. package/apps/form/view/ViewportController.mjs +2 -2
  4. package/apps/portal/view/home/MainContainer.mjs +2 -2
  5. package/apps/portal/view/learn/ContentView.mjs +1 -1
  6. package/apps/portal/view/learn/LivePreview.mjs +0 -27
  7. package/apps/realworld2/view/user/LoginFormContainer.mjs +1 -1
  8. package/docs/app/view/MainContainer.mjs +1 -11
  9. package/examples/ServiceWorker.mjs +2 -2
  10. package/examples/component/timer/MainContainerController.mjs +2 -2
  11. package/examples/component/toast/MainContainerController.mjs +2 -2
  12. package/examples/date/selectorContainer/MainContainer.mjs +215 -0
  13. package/examples/date/selectorContainer/app.mjs +6 -0
  14. package/examples/date/selectorContainer/index.html +11 -0
  15. package/examples/date/selectorContainer/neo-config.json +6 -0
  16. package/package.json +3 -3
  17. package/resources/data/deck/learnneo/pages/Welcome.md +8 -0
  18. package/resources/data/deck/learnneo/pages/WhyNeo-Speed.md +5 -3
  19. package/resources/data/deck/learnneo/tree.json +4 -4
  20. package/resources/scss/src/date/SelectorContainer.scss +120 -0
  21. package/resources/scss/theme-dark/date/SelectorContainer.scss +24 -0
  22. package/resources/scss/theme-light/date/SelectorContainer.scss +24 -0
  23. package/resources/scss/theme-neo-light/date/SelectorContainer.scss +24 -0
  24. package/src/DefaultConfig.mjs +2 -2
  25. package/src/Neo.mjs +5 -5
  26. package/src/component/Base.mjs +1 -1
  27. package/src/container/Base.mjs +42 -17
  28. package/src/controller/Component.mjs +5 -4
  29. package/src/core/Observable.mjs +30 -5
  30. package/src/core/Util.mjs +1 -1
  31. package/src/date/DayViewComponent.mjs +251 -0
  32. package/src/date/SelectorContainer.mjs +352 -0
  33. package/src/date/SelectorContainerModel.mjs +33 -0
  34. package/src/form/Container.mjs +10 -2
  35. package/src/form/field/Base.mjs +10 -2
  36. package/src/form/field/CheckBox.mjs +13 -5
  37. package/src/form/field/ComboBox.mjs +20 -15
  38. package/src/form/field/Date.mjs +2 -2
  39. package/src/form/field/Text.mjs +18 -17
  40. package/src/main/addon/IntersectionObserver.mjs +27 -20
  41. package/src/tab/Container.mjs +56 -55
  42. package/docs/app/model/Tutorial.mjs +0 -41
  43. package/docs/app/store/Tutorials.mjs +0 -28
  44. package/docs/app/view/TutorialsTreeList.mjs +0 -51
  45. package/docs/tutorials/01_Concept.html +0 -45
  46. package/docs/tutorials/01_Concept.json +0 -123
  47. package/docs/tutorials/01_Concept.md +0 -55
  48. package/docs/tutorials/02_ClassSystem.html +0 -171
  49. package/docs/tutorials/02_ClassSystem.md +0 -187
  50. package/docs/tutorials/03_ComponentLifecycle.html +0 -28
  51. package/docs/tutorials/03_ComponentLifecycle.md +0 -23
  52. package/docs/tutorials/04_VdomVnode.html +0 -161
  53. package/docs/tutorials/05_RemoteMethodAccess.html +0 -82
  54. package/docs/tutorials/06_EcmaScript6Plus.html +0 -10
  55. package/docs/tutorials/07_WebWorkers.html +0 -9
  56. package/docs/tutorials/08_DomEvents.html +0 -7
  57. package/docs/tutorials/09_TodoList_version1.html +0 -503
  58. package/docs/tutorials/11_CreateApp.html +0 -94
  59. package/docs/tutorials/tutorials.json +0 -100
  60. package/resources/scss/src/apps/docs/TutorialsTreeList.scss +0 -7
@@ -80,16 +80,25 @@ class NeoIntersectionObserver extends Base {
80
80
  /**
81
81
  * Add more or new items into an existing observer instance
82
82
  * @param {Object} data
83
- * @param {Boolean} data.disconnect=false true removes all currently observed targets
83
+ * @param {Boolean} [data.disconnect=false] true removes all currently observed targets
84
84
  * @param {String} data.id
85
- * @param {String} data.observe The querySelector to match elements
85
+ * @param {String|String[]} data.observe The querySelector to match elements
86
86
  * @returns {Boolean} true in case the targets got observed, false in case they got cached prior to a register() call
87
87
  */
88
88
  observe(data) {
89
- let me = this,
90
- cache = me.cache,
91
- targets = document.querySelectorAll(data.observe),
92
- observer = me.map[data.id];
89
+ let me = this,
90
+ cache = me.cache,
91
+ {id, observe} = data,
92
+ observer = me.map[data.id],
93
+ targets = [];
94
+
95
+ if (!Neo.isArray(observe)) {
96
+ observe = [observe]
97
+ }
98
+
99
+ observe.forEach(item => {
100
+ targets.push(...document.querySelectorAll(item))
101
+ })
93
102
 
94
103
  if (observer) {
95
104
  data.disconnect && observer.disconnect();
@@ -98,11 +107,11 @@ class NeoIntersectionObserver extends Base {
98
107
  observer.observe(target)
99
108
  })
100
109
  } else {
101
- if (!cache[data.id]) {
102
- cache[data.id] = []
110
+ if (!cache[id]) {
111
+ cache[id] = []
103
112
  }
104
113
 
105
- cache[data.id].push(data);
114
+ cache[id].push(data);
106
115
  }
107
116
 
108
117
  return !!observer
@@ -112,18 +121,18 @@ class NeoIntersectionObserver extends Base {
112
121
  * @param {Object} data
113
122
  * @param {String} data.callback
114
123
  * @param {String} data.id
115
- * @param {String} [data.observe] The querySelector to match elements
124
+ * @param {String|String[]} [data.observe] The querySelector to match elements
116
125
  * @param {String} data.root
117
126
  * @param {String} data.rootMargin='0px'
118
127
  * @param {Number|Number[]} data.threshold=0.0
119
128
  */
120
129
  register(data) {
121
- let me = this,
122
- cache = me.cache,
123
- targets = data.observe && document.querySelectorAll(data.observe),
130
+ let me = this,
131
+ cache = me.cache,
132
+ {id, observe} = data,
124
133
  observer;
125
134
 
126
- me.map[data.id] = observer = new IntersectionObserver(me[data.callback].bind(me), {
135
+ me.map[id] = observer = new IntersectionObserver(me[data.callback].bind(me), {
127
136
  root : document.querySelector(data.root),
128
137
  rootMargin: data.rootMargin || '0px',
129
138
  threshold : data.threshold || 0.0
@@ -131,13 +140,11 @@ class NeoIntersectionObserver extends Base {
131
140
 
132
141
  observer.rootId = data.id; // storing the component id
133
142
 
134
- targets?.forEach(target => {
135
- observer.observe(target)
136
- });
143
+ observe && me.observe({id, observe});
137
144
 
138
- if (cache[data.id]) {
139
- cache[data.id].forEach(item => me.observe(item));
140
- delete cache[data.id]
145
+ if (cache[id]) {
146
+ cache[id].forEach(item => me.observe(item));
147
+ delete cache[id]
141
148
  }
142
149
  }
143
150
 
@@ -110,7 +110,7 @@ class Container extends BaseContainer {
110
110
  * @returns {Neo.component.Base|Neo.component.Base[]}
111
111
  */
112
112
  add(item) {
113
- return this.insert(this.getTabBar().items.length, item);
113
+ return this.insert(this.getTabBar().items.length, item)
114
114
  }
115
115
 
116
116
  /**
@@ -125,8 +125,8 @@ class Container extends BaseContainer {
125
125
 
126
126
  if (Neo.isNumber(value) && value > -1 && !cardContainer) {
127
127
  me.on('constructed', () => {
128
- me.afterSetActiveIndex(value, oldValue);
129
- });
128
+ me.afterSetActiveIndex(value, oldValue)
129
+ })
130
130
  } else {
131
131
  if (Neo.isNumber(value) && value > -1) {
132
132
  // we need to ensure the afterSet method triggers when lazy loading the module
@@ -140,7 +140,7 @@ class Container extends BaseContainer {
140
140
  item: me.getActiveCard(),
141
141
  oldValue,
142
142
  value
143
- });
143
+ })
144
144
  }
145
145
  }
146
146
  }
@@ -157,7 +157,7 @@ class Container extends BaseContainer {
157
157
  cls = me.cls;
158
158
 
159
159
  NeoArray[value ? 'unshift' : 'remove'](cls, me.tabContainerCls + '-plain');
160
- me.cls = cls;
160
+ me.cls = cls
161
161
  }
162
162
 
163
163
  /**
@@ -168,7 +168,7 @@ class Container extends BaseContainer {
168
168
  */
169
169
  afterSetSortable(value, oldValue) {
170
170
  if (oldValue !== undefined) {
171
- this.getTabBar().sortable = value;
171
+ this.getTabBar().sortable = value
172
172
  }
173
173
  }
174
174
 
@@ -195,7 +195,7 @@ class Container extends BaseContainer {
195
195
  component: me,
196
196
  oldValue,
197
197
  value
198
- });
198
+ })
199
199
  }
200
200
  }
201
201
 
@@ -208,7 +208,7 @@ class Container extends BaseContainer {
208
208
  afterSetUseActiveTabIndicator(value, oldValue) {
209
209
  if (oldValue !== undefined) {
210
210
  this.getTabBar() .useActiveTabIndicator = value;
211
- this.getTabStrip().useActiveTabIndicator = value;
211
+ this.getTabStrip().useActiveTabIndicator = value
212
212
  }
213
213
  }
214
214
 
@@ -220,7 +220,7 @@ class Container extends BaseContainer {
220
220
  * @returns {String} value
221
221
  */
222
222
  beforeSetTabBarPosition(value, oldValue) {
223
- return this.beforeSetEnumValue(value, oldValue, 'tabBarPosition');
223
+ return this.beforeSetEnumValue(value, oldValue, 'tabBarPosition')
224
224
  }
225
225
 
226
226
  /**
@@ -242,10 +242,10 @@ class Container extends BaseContainer {
242
242
  tabButtons.push(me.getTabButtonConfig(item.tabButtonConfig, index));
243
243
 
244
244
  if (!(item instanceof Neo.component.Base)) {
245
- item = {flex: 1, ...me.itemDefaults, isTab: true, ...item};
245
+ item = {flex: 1, ...me.itemDefaults, isTab: true, ...item}
246
246
  }
247
247
 
248
- tabComponents.push(item);
248
+ tabComponents.push(item)
249
249
  });
250
250
 
251
251
  me.items = [{
@@ -278,7 +278,7 @@ class Container extends BaseContainer {
278
278
 
279
279
  me.itemDefaults = null;
280
280
 
281
- super.createItems();
281
+ super.createItems()
282
282
  }
283
283
 
284
284
  /**
@@ -286,7 +286,7 @@ class Container extends BaseContainer {
286
286
  * @returns {Neo.component.Base|null}
287
287
  */
288
288
  getActiveCard() {
289
- return this.getCardContainer().items[this.activeIndex] || null;
289
+ return this.getCardContainer().items[this.activeIndex] || null
290
290
  }
291
291
 
292
292
  /**
@@ -295,14 +295,14 @@ class Container extends BaseContainer {
295
295
  * @returns {Neo.component.Base|null}
296
296
  */
297
297
  getCard(index) {
298
- return this.getCardContainer().items[index] || null;
298
+ return this.getCardContainer().items[index] || null
299
299
  }
300
300
 
301
301
  /**
302
302
  * @returns {Neo.container.Base}
303
303
  */
304
304
  getCardContainer() {
305
- return Neo.getComponent(this.cardContainerId);
305
+ return Neo.getComponent(this.cardContainerId)
306
306
  }
307
307
 
308
308
  /**
@@ -310,7 +310,7 @@ class Container extends BaseContainer {
310
310
  * @returns {Number}
311
311
  */
312
312
  getCount() {
313
- return this.getTabBar().items.length;
313
+ return this.getTabBar().items.length
314
314
  }
315
315
 
316
316
  /**
@@ -354,7 +354,7 @@ class Container extends BaseContainer {
354
354
  break;
355
355
  }
356
356
 
357
- return layoutConfig;
357
+ return layoutConfig
358
358
  }
359
359
 
360
360
  /**
@@ -362,14 +362,14 @@ class Container extends BaseContainer {
362
362
  * @returns {Neo.tab.header.Button|null}
363
363
  */
364
364
  getTabAtIndex(index) {
365
- return this.getTabBar().items[index] || null;
365
+ return this.getTabBar().items[index] || null
366
366
  }
367
367
 
368
368
  /**
369
369
  * @returns {Neo.toolbar.Base}
370
370
  */
371
371
  getTabBar() {
372
- return Neo.getComponent(this.tabBarId);
372
+ return Neo.getComponent(this.tabBarId)
373
373
  }
374
374
 
375
375
  /**
@@ -380,28 +380,29 @@ class Container extends BaseContainer {
380
380
  */
381
381
  getTabButtonConfig(config, index) {
382
382
  let me = this,
383
- defaultConfig = {
384
- module : HeaderButton,
385
- flex : 'none',
386
- index : index,
387
- pressed: me.activeIndex === index,
388
-
389
- domListeners: [{
390
- click: function(data) {
391
- me.activeIndex = data.component.index;
392
- },
393
- scope: me
394
- }]
395
- };
396
-
397
- return {...defaultConfig, ...config};
383
+
384
+ defaultConfig = {
385
+ module : HeaderButton,
386
+ flex : 'none',
387
+ index : index,
388
+ pressed: me.activeIndex === index,
389
+
390
+ domListeners: [{
391
+ click: function(data) {
392
+ me.activeIndex = data.component.index
393
+ },
394
+ scope: me
395
+ }]
396
+ };
397
+
398
+ return {...defaultConfig, ...config}
398
399
  }
399
400
 
400
401
  /**
401
402
  * @returns {Neo.tab.Strip}
402
403
  */
403
404
  getTabStrip() {
404
- return Neo.getComponent(this.tabStripId);
405
+ return Neo.getComponent(this.tabStripId)
405
406
  }
406
407
 
407
408
  /**
@@ -425,7 +426,7 @@ class Container extends BaseContainer {
425
426
 
426
427
  for (; i < len; i++) {
427
428
  // insert the array backwards
428
- returnArray.unshift(me.insert(index, item[len - 1 - i], true));
429
+ returnArray.unshift(me.insert(index, item[len - 1 - i], true))
429
430
  }
430
431
 
431
432
  superItem = returnArray;
@@ -439,7 +440,7 @@ class Container extends BaseContainer {
439
440
  superItem = cardContainer.items[i];
440
441
 
441
442
  if (me.activateInsertedTabs) {
442
- me.activeIndex = i;
443
+ me.activeIndex = i
443
444
  }
444
445
 
445
446
  break;
@@ -456,7 +457,7 @@ class Container extends BaseContainer {
456
457
  len = tabBar.items.length;
457
458
 
458
459
  for (; i < len; i++) {
459
- tabBar.items[i].index = i;
460
+ tabBar.items[i].index = i
460
461
  }
461
462
 
462
463
  item.flex = 1;
@@ -464,9 +465,9 @@ class Container extends BaseContainer {
464
465
 
465
466
  if (me.activateInsertedTabs) {
466
467
  if (!me.vnode) {
467
- me.activeIndex = index;
468
+ me.activeIndex = index
468
469
  } else {
469
- tab.on('mounted', me.onTabButtonMounted, me);
470
+ tab.on('mounted', me.onTabButtonMounted, me)
470
471
  }
471
472
  }
472
473
  }
@@ -496,17 +497,17 @@ class Container extends BaseContainer {
496
497
  if (index !== me.activeIndex) {
497
498
  // silent updates
498
499
  me._activeIndex = index;
499
- cardContainer.layout._activeIndex = index;
500
+ cardContainer.layout._activeIndex = index
500
501
  }
501
502
 
502
503
  returnValue = cardContainer.moveTo(fromIndex, toIndex);
503
504
 
504
505
  me.fire('moveTo', {
505
- fromIndex: fromIndex,
506
- toIndex : toIndex
506
+ fromIndex,
507
+ toIndex
507
508
  });
508
509
 
509
- return returnValue;
510
+ return returnValue
510
511
  }
511
512
 
512
513
  /**
@@ -514,7 +515,7 @@ class Container extends BaseContainer {
514
515
  */
515
516
  onConstructed() {
516
517
  this._layout = this.getLayoutConfig(); // silent update
517
- super.onConstructed();
518
+ super.onConstructed()
518
519
  }
519
520
 
520
521
  /**
@@ -535,7 +536,7 @@ class Container extends BaseContainer {
535
536
  for (; i < len; i++) {
536
537
  if (tabBar.items[i].id === buttonId) {
537
538
  index = i;
538
- break;
539
+ break
539
540
  }
540
541
  }
541
542
 
@@ -545,10 +546,10 @@ class Container extends BaseContainer {
545
546
  if (me.vnode && !card.mounted) {
546
547
  listenerId = card.on('mounted', () => {
547
548
  card.un('mounted', listenerId);
548
- me.activeIndex = index;
549
- });
549
+ me.activeIndex = index
550
+ })
550
551
  } else {
551
- me.activeIndex = index;
552
+ me.activeIndex = index
552
553
  }
553
554
  }
554
555
  }
@@ -566,7 +567,7 @@ class Container extends BaseContainer {
566
567
 
567
568
  for (; i < len; i++) {
568
569
  if (items[i].id === component.id) {
569
- this.removeAt(i, destroyItem, silent);
570
+ this.removeAt(i, destroyItem, silent)
570
571
  }
571
572
  }
572
573
  }
@@ -589,9 +590,9 @@ class Container extends BaseContainer {
589
590
  if (index < activeIndex) {
590
591
  // silent updates
591
592
  me._activeIndex = activeIndex - 1;
592
- cardContainer.layout._activeIndex = activeIndex - 1;
593
+ cardContainer.layout._activeIndex = activeIndex - 1
593
594
  } else if (index === activeIndex) {
594
- me.activeIndex = activeIndex - 1;
595
+ me.activeIndex = activeIndex - 1
595
596
  }
596
597
 
597
598
  // todo: non index based matching of tab buttons and cards
@@ -599,7 +600,7 @@ class Container extends BaseContainer {
599
600
  len = tabBar.items.length;
600
601
 
601
602
  for (; i < len; i++) {
602
- tabBar.items[i].index = i;
603
+ tabBar.items[i].index = i
603
604
  }
604
605
  }
605
606
 
@@ -612,8 +613,8 @@ class Container extends BaseContainer {
612
613
  tabButtons = me.getTabBar().items || [];
613
614
 
614
615
  tabButtons.forEach((item, index) => {
615
- item.pressed = index === activeIndex;
616
- });
616
+ item.pressed = index === activeIndex
617
+ })
617
618
  }
618
619
  }
619
620
 
@@ -1,41 +0,0 @@
1
- import Model from '../../../src/data/Model.mjs';
2
-
3
- /**
4
- * @class Docs.app.model.Tutorial
5
- * @extends Neo.data.Model
6
- */
7
- class Tutorial extends Model {
8
- static config = {
9
- /**
10
- * @member {String} className='Docs.app.model.Tutorial'
11
- * @protected
12
- */
13
- className: 'Docs.app.model.Tutorial',
14
- /**
15
- * @member {Object[]} fields
16
- */
17
- fields: [{
18
- name: 'fileName',
19
- type: 'String'
20
- }, {
21
- name: 'id',
22
- type: 'Integer'
23
- }, {
24
- name: 'isLeaf',
25
- type: 'Boolean'
26
- }, {
27
- name: 'name',
28
- type: 'String'
29
- }, {
30
- name: 'parentId',
31
- type: 'Integer'
32
- }, {
33
- name: 'type',
34
- type: 'String'
35
- }]
36
- }
37
- }
38
-
39
- Neo.setupClass(Tutorial);
40
-
41
- export default Tutorial;
@@ -1,28 +0,0 @@
1
- import Store from '../../../src/data/Store.mjs';
2
- import Tutorial from '../model/Tutorial.mjs';
3
-
4
- /**
5
- * @class Docs.app.store.Tutorials
6
- * @extends Neo.data.Store
7
- */
8
- class Tutorials extends Store {
9
- static config = {
10
- /**
11
- * @member {String} className='Docs.app.store.Tutorials'
12
- * @protected
13
- */
14
- className: 'Docs.app.store.Tutorials',
15
- /**
16
- * @member {String} keyProperty='id'
17
- */
18
- keyProperty: 'id',
19
- /**
20
- * @member {Neo.data.Model} model=Tutorial
21
- */
22
- model: Tutorial
23
- }
24
- }
25
-
26
- Neo.setupClass(Tutorials);
27
-
28
- export default Tutorials;
@@ -1,51 +0,0 @@
1
- import TreeList from '../../../src/tree/List.mjs';
2
- import TutorialsStore from '../store/Tutorials.mjs';
3
-
4
- /**
5
- * @class Docs.view.TutorialsTreeList
6
- * @extends Neo.tree.List
7
- */
8
- class TutorialsTreeList extends TreeList {
9
- static config = {
10
- /**
11
- * @member {String} className='Docs.view.TutorialsTreeList'
12
- * @protected
13
- */
14
- className: 'Docs.view.TutorialsTreeList',
15
- /**
16
- * @member {String} ntype='tutorials-treelist'
17
- * @protected
18
- */
19
- ntype: 'tutorials-treelist',
20
- /**
21
- * @member {String[]} cls=['docs-tutorials-treelist']
22
- */
23
- cls: ['docs-tutorials-treelist'],
24
- /**
25
- * @member {Neo.data.Store|null} store=TutorialsStore
26
- * @protected
27
- */
28
- store: TutorialsStore
29
- }
30
-
31
- /**
32
- *
33
- */
34
- onConstructed() {
35
- super.onConstructed();
36
-
37
- let me = this;
38
-
39
- Neo.Xhr.promiseJson({
40
- url: '../../docs/tutorials/tutorials.json'
41
- }).then(data => {
42
- me.store.data = data.json;
43
- me.createItems(null, me.getListItemsRoot(), 0);
44
- me.update();
45
- });
46
- }
47
- }
48
-
49
- Neo.setupClass(TutorialsTreeList);
50
-
51
- export default TutorialsTreeList;
@@ -1,45 +0,0 @@
1
- <div class="neo-header-text-container">
2
- <span class="neo-header-text">Tutorial: Concept</span>
3
- </div>
4
- <article>
5
- <h4>Why yet another Javascript framework?</h4>
6
- <ol>
7
- <li>neo.mjs is web workers driven</li>
8
- <li>neo.mjs does not need any cryptic XML markups</li>
9
- <li>neo.mjs uses a custom blazing fast virtual dom engine</li>
10
- <li>neo.mjs is based on EcmaScript 8 (ES8)</li>
11
- <li>neo.mjs uses HTML5 and CSS3 to the fullest</li>
12
- <li>Simplicity</li>
13
- <li>No transpiled code</li>
14
- </ol>
15
- <hr>
16
- <p>(1) neo.mjs uses 3 web workers:</p>
17
- <ul>
18
- <li>App</li>
19
- <li>Data</li>
20
- <li>Vdom</li>
21
- </ul>
22
- <p>The main reason is that browsers by default only use 1 CPU core, while most computers and mobile devices have
23
- more. Multi-Threading solves this performance bottleneck and it will ensure that there are no hidden
24
- background-tasks in the main thread, which mess with your beautiful animations.</p>
25
- <p>In short: Most parts of neo.mjs as well as all apps you will create are inside the App worker.</p>
26
- <p>The main thread is only responsible to manipulate the DOM and delegate DOM events to the App worker.</p>
27
- <p>The Data worker will perform all Ajax requests to your backend and host the Store instances.</p>
28
- <p>The Vdom worker parses your JSON based markup (Vdom) into a virtual node (Vnode) and creates deltas
29
- when you dynamically change your Vdom.</p>
30
- <p>(2) Did you ever enjoy writing pseudo-html including curly braces like {tpl if}, {tpl for} or even better
31
- {[this.doSomething()]}? Those XML templates evolved to a point, where they try to do everything what
32
- Javascript itself is capable of and you will most likely have encountered scoping issues (where does &quot;this&quot;
33
- point to now?). The solution is as simple as it should have been obvious for quite a while: JSON. You can easily manipulate Javascript objects with Javascript.</p>
34
- <p>(3) Since neo.mjs does not need to parse XML templates back into JSON and then create virtual DOM nodes,
35
- the custom Vdom engine is blazing fast. The algorithms to create deltas are highly efficient and
36
- recognise moved DOM subtrees immediately.</p>
37
- <p>(4) neo.mjs is most likely the first UI framework out there which does not only allow devs to create
38
- ES6 classes on top of ES5 prototypes, but is itself fully written in ES6.</p>
39
- <p>(5) neo.mjs uses the latest web APIs and CSS features to the fullest and give you an easy and intuitive
40
- access to them.</p>
41
- <p>(6) The first and most important design goal of neo.mjs is simplicity. This does not only mean to keep the
42
- code base as clean and simple as possible, but also to keep the created DOM as lightweight and minimal as
43
- possible.</p>
44
- <p>(7) neo.mjs does not use Babel or any other tool to transpile code (which does not mean you can't use it for your own apps).</p>
45
- </article>