neo.mjs 10.2.0 → 10.3.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.
Files changed (87) hide show
  1. package/.github/CONCEPT.md +2 -4
  2. package/.github/GETTING_STARTED.md +72 -51
  3. package/.github/RELEASE_NOTES/v10.2.1.md +17 -0
  4. package/.github/RELEASE_NOTES/v10.3.0.md +54 -0
  5. package/.github/epic-string-based-templates.md +690 -0
  6. package/ServiceWorker.mjs +2 -2
  7. package/apps/covid/view/GalleryContainer.mjs +1 -1
  8. package/apps/covid/view/HelixContainer.mjs +1 -1
  9. package/apps/covid/view/MainContainer.mjs +1 -1
  10. package/apps/covid/view/WorldMapContainer.mjs +4 -4
  11. package/apps/covid/view/country/Table.mjs +1 -1
  12. package/apps/portal/index.html +1 -1
  13. package/apps/portal/view/home/FooterContainer.mjs +1 -1
  14. package/apps/portal/view/learn/ContentComponent.mjs +1 -1
  15. package/apps/realworld/api/Base.mjs +2 -2
  16. package/apps/sharedcovid/view/GalleryContainer.mjs +1 -1
  17. package/apps/sharedcovid/view/HelixContainer.mjs +1 -1
  18. package/apps/sharedcovid/view/MainContainer.mjs +1 -1
  19. package/apps/sharedcovid/view/MainContainerController.mjs +1 -1
  20. package/apps/sharedcovid/view/WorldMapContainer.mjs +4 -4
  21. package/buildScripts/buildESModules.mjs +23 -75
  22. package/buildScripts/bundleParse5.mjs +27 -0
  23. package/buildScripts/util/astTemplateProcessor.mjs +210 -0
  24. package/buildScripts/util/templateBuildProcessor.mjs +331 -0
  25. package/buildScripts/util/vdomToString.mjs +46 -0
  26. package/buildScripts/webpack/development/webpack.config.appworker.mjs +11 -0
  27. package/buildScripts/webpack/loader/template-loader.mjs +21 -0
  28. package/buildScripts/webpack/production/webpack.config.appworker.mjs +11 -0
  29. package/examples/README.md +1 -1
  30. package/examples/component/wrapper/googleMaps/MarkerDialog.mjs +2 -2
  31. package/examples/form/field/email/MainContainer.mjs +0 -1
  32. package/examples/form/field/number/MainContainer.mjs +0 -1
  33. package/examples/form/field/picker/MainContainer.mjs +0 -1
  34. package/examples/form/field/time/MainContainer.mjs +0 -1
  35. package/examples/form/field/trigger/copyToClipboard/MainContainer.mjs +0 -1
  36. package/examples/form/field/url/MainContainer.mjs +0 -1
  37. package/examples/functional/nestedTemplateComponent/Component.mjs +100 -0
  38. package/examples/functional/nestedTemplateComponent/MainContainer.mjs +48 -0
  39. package/examples/functional/nestedTemplateComponent/app.mjs +6 -0
  40. package/examples/functional/nestedTemplateComponent/index.html +11 -0
  41. package/examples/functional/nestedTemplateComponent/neo-config.json +6 -0
  42. package/examples/functional/templateComponent/Component.mjs +61 -0
  43. package/examples/functional/templateComponent/MainContainer.mjs +48 -0
  44. package/examples/functional/templateComponent/app.mjs +6 -0
  45. package/examples/functional/templateComponent/index.html +11 -0
  46. package/examples/functional/templateComponent/neo-config.json +6 -0
  47. package/learn/gettingstarted/Setup.md +29 -12
  48. package/learn/guides/fundamentals/ApplicationBootstrap.md +2 -2
  49. package/learn/guides/fundamentals/InstanceLifecycle.md +5 -5
  50. package/learn/guides/uibuildingblocks/HtmlTemplates.md +191 -0
  51. package/learn/guides/uibuildingblocks/HtmlTemplatesUnderTheHood.md +156 -0
  52. package/learn/guides/uibuildingblocks/WorkingWithVDom.md +1 -1
  53. package/learn/tree.json +2 -0
  54. package/package.json +62 -56
  55. package/src/DefaultConfig.mjs +3 -3
  56. package/src/button/Base.mjs +13 -4
  57. package/src/calendar/view/calendars/List.mjs +1 -1
  58. package/src/calendar/view/month/Component.mjs +1 -1
  59. package/src/calendar/view/week/Component.mjs +1 -1
  60. package/src/component/Abstract.mjs +1 -1
  61. package/src/component/Base.mjs +33 -27
  62. package/src/container/Base.mjs +14 -7
  63. package/src/controller/Application.mjs +5 -5
  64. package/src/dialog/Base.mjs +6 -6
  65. package/src/draggable/DragProxyComponent.mjs +4 -4
  66. package/src/form/field/ComboBox.mjs +1 -1
  67. package/src/functional/_export.mjs +2 -1
  68. package/src/functional/component/Base.mjs +142 -93
  69. package/src/functional/util/HtmlTemplateProcessor.mjs +243 -0
  70. package/src/functional/util/html.mjs +24 -67
  71. package/src/list/Base.mjs +2 -2
  72. package/src/manager/Toast.mjs +1 -1
  73. package/src/menu/List.mjs +1 -1
  74. package/src/mixin/VdomLifecycle.mjs +87 -90
  75. package/src/tab/Container.mjs +2 -2
  76. package/src/tooltip/Base.mjs +1 -1
  77. package/src/tree/Accordion.mjs +2 -2
  78. package/src/worker/App.mjs +7 -7
  79. package/test/components/files/component/Base.mjs +1 -1
  80. package/test/siesta/siesta.js +2 -0
  81. package/test/siesta/tests/classic/Button.mjs +5 -5
  82. package/test/siesta/tests/functional/Button.mjs +6 -6
  83. package/test/siesta/tests/functional/HtmlTemplateComponent.mjs +193 -33
  84. package/test/siesta/tests/functional/Parse5Processor.mjs +82 -0
  85. package/test/siesta/tests/vdom/VdomRealWorldUpdates.mjs +5 -5
  86. package/.github/epic-functional-components.md +0 -498
  87. package/.github/ticket-asymmetric-vdom-updates.md +0 -122
package/src/menu/List.mjs CHANGED
@@ -394,7 +394,7 @@ class List extends BaseList {
394
394
 
395
395
  if (me.activeSubMenu !== subMenu) {
396
396
  me.activeSubMenu = subMenu;
397
- subMenu.render(true)
397
+ subMenu.initVnode(true)
398
398
  }
399
399
  }
400
400
 
@@ -21,20 +21,17 @@ class VdomLifecycle extends Base {
21
21
  */
22
22
  className: 'Neo.mixin.VdomLifecycle',
23
23
  /**
24
- * True automatically mounts a component after being rendered.
24
+ * True automatically initializes the vnode of a component after being created inside the init call.
25
25
  * Use this for the top level component of your app.
26
- * @member {Boolean} autoMount=false
27
- * @tutorial 02_ClassSystem
26
+ * @member {Boolean} autoInitVnode=false
28
27
  */
29
- autoMount: false,
28
+ autoInitVnode: false,
30
29
  /**
31
- * True automatically renders a component after being created inside the init call.
30
+ * True automatically mounts a component after being rendered.
32
31
  * Use this for the top level component of your app.
33
- * @member {Boolean} autoRender=false
34
- * @see {@link Neo.component.Base#init init}
35
- * @tutorial 02_ClassSystem
32
+ * @member {Boolean} autoMount=false
36
33
  */
37
- autoRender: false,
34
+ autoMount: false,
38
35
  /**
39
36
  * Internal flag for vdom changes after a component got unmounted
40
37
  * (delta updates can no longer get applied & a new render call is required before re-mounting)
@@ -50,6 +47,13 @@ class VdomLifecycle extends Base {
50
47
  * @reactive
51
48
  */
52
49
  isVdomUpdating_: false,
50
+ /**
51
+ * True in case the component is initializing the vnode
52
+ * @member {Boolean} isVnodeInitializing_=false
53
+ * @protected
54
+ * @reactive
55
+ */
56
+ isVnodeInitializing_: false,
53
57
  /**
54
58
  * True in case the component is mounted to the DOM
55
59
  * @member {Boolean} mounted_=false
@@ -64,13 +68,6 @@ class VdomLifecycle extends Base {
64
68
  * @reactive
65
69
  */
66
70
  needsVdomUpdate_: false,
67
- /**
68
- * True in case the component is rendering the vnode
69
- * @member {Boolean} rendering_=false
70
- * @protected
71
- * @reactive
72
- */
73
- rendering_: false,
74
71
  /**
75
72
  * Set this to true for bulk updates. Ensure to set it back to false afterwards.
76
73
  * Internally the value will get saved as a number to ensure that child methods won't stop the silent mode too early.
@@ -89,7 +86,7 @@ class VdomLifecycle extends Base {
89
86
  */
90
87
  updateDepth_: 1,
91
88
  /**
92
- * The component vnode tree. Available after the component got rendered.
89
+ * The component vnode tree. Available after the component got vnodeInitialized.
93
90
  * @member {Object} vnode_=={[isDescriptor]: true, value: null, isEqual: (a, b) => a === b,}
94
91
  * @protected
95
92
  * @reactive
@@ -312,6 +309,64 @@ class VdomLifecycle extends Base {
312
309
  return updateDepth === -1 ? true : distance < updateDepth
313
310
  }
314
311
 
312
+ /**
313
+ * Creates the vnode tree for this component and mounts the component in case
314
+ * - you pass true for the mount param
315
+ * - or the autoMount config is set to true
316
+ * @param {Boolean} [mount] Mount the DOM after the vnode got created
317
+ * @returns {Promise<any>} If getting there, we return the data from vdom.Helper: create(), containing the vnode.
318
+ */
319
+ async initVnode(mount) {
320
+ let me = this,
321
+ autoMount = mount || me.autoMount,
322
+ {app} = me,
323
+ {allowVdomUpdatesInTests, unitTestMode, useVdomWorker} = Neo.config;
324
+
325
+ if (unitTestMode && !allowVdomUpdatesInTests) return;
326
+
327
+ // Verify that the critical rendering path => CSS files for the new tree is in place
328
+ if (!unitTestMode && autoMount && currentWorker.countLoadingThemeFiles !== 0) {
329
+ currentWorker.on('themeFilesLoaded', function() {
330
+ !me.mounted && me.initVnode(mount)
331
+ }, me, {once: true});
332
+
333
+ return
334
+ }
335
+
336
+ me.isVnodeInitializing = true;
337
+
338
+ if (!app.vnodeInitialized) {
339
+ app.isVnodeInitializing = true
340
+ }
341
+
342
+ if (me.vdom) {
343
+ me.isVdomUpdating = true;
344
+
345
+ delete me.vdom.removeDom;
346
+
347
+ me._needsVdomUpdate = false;
348
+ me.afterSetNeedsVdomUpdate?.(false, true);
349
+
350
+ const data = await Promise.resolve(Neo.vdom.Helper.create({
351
+ appName : me.appName,
352
+ autoMount,
353
+ parentId : autoMount ? me.getMountedParentId() : undefined,
354
+ parentIndex: autoMount ? me.getMountedParentIndex() : undefined,
355
+ vdom : TreeBuilder.getVdomTree(me.vdom, -1),
356
+ windowId : me.windowId
357
+ }));
358
+
359
+ me.onInitVnode(data.vnode, useVdomWorker ? autoMount : false);
360
+ me.isVdomUpdating = false;
361
+
362
+ autoMount && !useVdomWorker && me.mount();
363
+
364
+ me.resolveVdomUpdate();
365
+
366
+ return data
367
+ }
368
+ }
369
+
315
370
  /**
316
371
  * Checks for vdom updates inside the parent chain and if found.
317
372
  * Registers the component for a vdom update once done.
@@ -377,23 +432,23 @@ class VdomLifecycle extends Base {
377
432
  }
378
433
 
379
434
  /**
380
- * Gets called from the render() promise success handler
435
+ * Gets called from the initVnode() promise success handler
381
436
  * @param {Object} vnode
382
437
  * @param {Boolean} autoMount Mount the DOM after the vnode got created
383
438
  * @protected
384
439
  */
385
- onRender(vnode, autoMount) {
440
+ onInitVnode(vnode, autoMount) {
386
441
  let me = this,
387
442
  {app} = me;
388
443
 
389
- me.rendering = false;
444
+ me.isVnodeInitializing = false;
390
445
 
391
- // if app is a check to see if the Component got destroyed while rendering => before onRender got triggered
446
+ // if app is a check to see if the Component got destroyed while vnodeInitialising => before onInitVnode got triggered
392
447
  if (app) {
393
- if (!app.rendered) {
394
- app.rendering = false;
395
- app.rendered = true;
396
- app.fire('render')
448
+ if (!app.vnodeInitialized) {
449
+ app.isVnodeInitializing = false;
450
+ app.vnodeInitialized = true;
451
+ app.fire('vnodeInitialized')
397
452
  }
398
453
 
399
454
  me.vnode = vnode;
@@ -407,12 +462,12 @@ class VdomLifecycle extends Base {
407
462
  child = Neo.getComponent(childIds[i]);
408
463
 
409
464
  if (child) {
410
- child.rendered = true
465
+ child.vnodeInitialized = true
411
466
  }
412
467
  }
413
468
 
414
- me._rendered = true; // silent update
415
- me.fire('rendered', me.id);
469
+ me._vnodeInitialized = true; // silent update
470
+ me.fire('vnodeInitialized', me.id);
416
471
 
417
472
  if (autoMount) {
418
473
  me.mounted = true;
@@ -435,64 +490,6 @@ class VdomLifecycle extends Base {
435
490
  })
436
491
  }
437
492
 
438
- /**
439
- * Creates the vnode tree for this component and mounts the component in case
440
- * - you pass true for the mount param
441
- * - or the autoMount config is set to true
442
- * @param {Boolean} [mount] Mount the DOM after the vnode got created
443
- * @returns {Promise<any>} If getting there, we return the data from vdom.Helper: create(), containing the vnode.
444
- */
445
- async render(mount) {
446
- let me = this,
447
- autoMount = mount || me.autoMount,
448
- {app} = me,
449
- {allowVdomUpdatesInTests, unitTestMode, useVdomWorker} = Neo.config;
450
-
451
- if (unitTestMode && !allowVdomUpdatesInTests) return;
452
-
453
- // Verify that the critical rendering path => CSS files for the new tree is in place
454
- if (!unitTestMode && autoMount && currentWorker.countLoadingThemeFiles !== 0) {
455
- currentWorker.on('themeFilesLoaded', function() {
456
- !me.mounted && me.render(mount)
457
- }, me, {once: true});
458
-
459
- return
460
- }
461
-
462
- me.rendering = true;
463
-
464
- if (!app.rendered) {
465
- app.rendering = true
466
- }
467
-
468
- if (me.vdom) {
469
- me.isVdomUpdating = true;
470
-
471
- delete me.vdom.removeDom;
472
-
473
- me._needsVdomUpdate = false;
474
- me.afterSetNeedsVdomUpdate?.(false, true);
475
-
476
- const data = await Promise.resolve(Neo.vdom.Helper.create({
477
- appName : me.appName,
478
- autoMount,
479
- parentId : autoMount ? me.getMountedParentId() : undefined,
480
- parentIndex: autoMount ? me.getMountedParentIndex() : undefined,
481
- vdom : TreeBuilder.getVdomTree(me.vdom, -1),
482
- windowId : me.windowId
483
- }));
484
-
485
- me.onRender(data.vnode, useVdomWorker ? autoMount : false);
486
- me.isVdomUpdating = false;
487
-
488
- autoMount && !useVdomWorker && me.mount();
489
-
490
- me.resolveVdomUpdate();
491
-
492
- return data
493
- }
494
- }
495
-
496
493
  /**
497
494
  * Internal helper fn to resolve the Promise for updateVdom()
498
495
  * @param {Object} [data] The return value of vdom.Helper.update()
@@ -529,7 +526,7 @@ class VdomLifecycle extends Base {
529
526
  /**
530
527
  * In case a component receives a new vnode, we want to do:
531
528
  * - sync the vdom ids
532
- * - setting rendered to true for child components
529
+ * - setting vnodeInitialized to true for child components
533
530
  * - updating the parent component to ensure that the vnode tree stays persistent
534
531
  * @param {Neo.vdom.VNode} [vnode=this.vnode]
535
532
  */
@@ -571,9 +568,9 @@ class VdomLifecycle extends Base {
571
568
  // silent update
572
569
  component._vnode = ComponentManager.addVnodeComponentReferences(childVnode, component.id);
573
570
 
574
- if (!component.rendered) {
575
- component._rendered = true;
576
- component.fire('rendered', component.id)
571
+ if (!component.vnodeInitialized) {
572
+ component._vnodeInitialized = true;
573
+ component.fire('vnodeInitialized', component.id)
577
574
  }
578
575
 
579
576
  component.mounted = true
@@ -30,7 +30,7 @@ class Container extends BaseContainer {
30
30
  */
31
31
  ntype: 'tab-container',
32
32
  /**
33
- * You can use null to not render any items initially
33
+ * You can use null to not mount any items initially
34
34
  * @member {Number|null} activeIndex_=0
35
35
  * @reactive
36
36
  */
@@ -197,7 +197,7 @@ class Container extends BaseContainer {
197
197
  NeoArray.add(cls, 'neo-' + value);
198
198
  me.setSilent({cls});
199
199
 
200
- if (me.rendered) {
200
+ if (me.vnodeInitialized) {
201
201
  me.layout.setSilent(me.getLayoutConfig());
202
202
  me.getTabBar().setSilent({dock: value});
203
203
  me.getTabStrip().setSilent({cls: ['neo-tab-strip', 'neo-dock-' + value]});
@@ -347,7 +347,7 @@ class Tooltip extends Container {
347
347
  me.addTimeout('dismiss', me.hide.bind(me), me.dismissDelay, data)
348
348
  }
349
349
 
350
- !me.mounted && me.render(true)
350
+ !me.mounted && me.initVnode(true)
351
351
  }
352
352
 
353
353
  /**
@@ -130,7 +130,7 @@ class AccordionTree extends TreeList {
130
130
 
131
131
  me[!value ? 'addCls' : 'removeCls']('root-not-collapsible');
132
132
 
133
- if (me.rendered && value === false) {
133
+ if (me.vnodeInitialized && value === false) {
134
134
  let {store} = me;
135
135
 
136
136
  store.items.forEach(record => {
@@ -391,7 +391,7 @@ class AccordionTree extends TreeList {
391
391
 
392
392
  me.clear(false);
393
393
 
394
- if (!me.mounted && me.rendering) {
394
+ if (!me.mounted && me.isVnodeInitializing) {
395
395
  me.on('mounted', () => {
396
396
  me.createItems(null, me.getListItemsRoot(), 0);
397
397
  me.update()
@@ -97,13 +97,13 @@ class App extends Base {
97
97
  * Remote method to use inside main threads for creating neo based class instances.
98
98
  * Be aware that you can only pass configs which can get converted into pure JSON.
99
99
  *
100
- * Rendering a component into the document.body
100
+ * Mounting a component into the document.body
101
101
  * @example:
102
102
  * Neo.worker.App.createNeoInstance({
103
- * ntype : 'button',
104
- * autoMount : true,
105
- * autoRender: true
106
- * text : 'Hi Nige!'
103
+ * ntype : 'button',
104
+ * autoInitVnode: true,
105
+ * autoMount : true,
106
+ * text : 'Hi Nige!'
107
107
  * }).then(id => console.log(id))
108
108
  *
109
109
  * Inserting a component into a container
@@ -150,8 +150,8 @@ class App extends Base {
150
150
  }
151
151
  } else {
152
152
  // default parentId='document.body' => we want it to get shown
153
- config.autoMount = true;
154
- config.autoRender = true;
153
+ config.autoInitVnode = true;
154
+ config.autoMount = true;
155
155
 
156
156
  instance = Neo[config.ntype ? 'ntype' : 'create'](config)
157
157
  }
@@ -65,7 +65,7 @@ StartTest(t => {
65
65
  t.is(buttonId, 'neo-button-1');
66
66
 
67
67
  await t.waitForSelector('.neo-button');
68
- t.diag('Button got rendered.');
68
+ t.diag('Button got vnodeInitialized.');
69
69
 
70
70
  t.diag('Child update before parent update');
71
71
  Neo.worker.App.setConfigs({id: buttonId, text : 'world'});
@@ -84,6 +84,8 @@ project.plan(
84
84
  {
85
85
  group: 'functional',
86
86
  items: [
87
+ 'tests/functional/HtmlTemplateComponent.mjs',
88
+ 'tests/functional/Parse5Processor.mjs',
87
89
  'tests/functional/Button.mjs'
88
90
  ]
89
91
  },
@@ -15,10 +15,10 @@ Neo.config.useDomApiRenderer = true;
15
15
  const appName = 'ClassicButtonTest';
16
16
  Neo.apps = Neo.apps || {};
17
17
  Neo.apps[appName] = {
18
- name : appName,
19
- fire : Neo.emptyFn,
20
- isMounted: () => true,
21
- rendering: false
18
+ name : appName,
19
+ fire : Neo.emptyFn,
20
+ isMounted : () => true,
21
+ vnodeInitialising: false
22
22
  };
23
23
 
24
24
  StartTest(t => {
@@ -35,7 +35,7 @@ StartTest(t => {
35
35
  text : 'Click me'
36
36
  });
37
37
 
38
- ({vnode} = await button.render());
38
+ ({vnode} = await button.initVnode());
39
39
  button.mounted = true; // Manually mount to enable updates in the test env
40
40
  });
41
41
 
@@ -15,10 +15,10 @@ Neo.config.useDomApiRenderer = true;
15
15
  const appName = 'FunctionalButtonTest';
16
16
  Neo.apps = Neo.apps || {};
17
17
  Neo.apps[appName] = {
18
- name : appName,
19
- fire : Neo.emptyFn,
20
- isMounted: () => true,
21
- rendering: false
18
+ name : appName,
19
+ fire : Neo.emptyFn,
20
+ isMounted : () => true,
21
+ vnodeInitialising: false
22
22
  };
23
23
 
24
24
  StartTest(t => {
@@ -35,7 +35,7 @@ StartTest(t => {
35
35
  text : 'Click me'
36
36
  });
37
37
 
38
- ({vnode} = await button.render());
38
+ ({vnode} = await button.initVnode());
39
39
  button.mounted = true; // Manually mount to enable updates in the test env
40
40
  });
41
41
 
@@ -110,4 +110,4 @@ StartTest(t => {
110
110
  t.isDeeply(delta.cls.remove, ['pressed'], 'Delta should remove "pressed" class');
111
111
  t.notOk(updateData.vnode.className.includes('pressed'), 'Vnode should not have "pressed" class');
112
112
  });
113
- });
113
+ });