neo.mjs 6.13.0 → 6.14.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 (44) hide show
  1. package/apps/covid/neo-config.json +1 -1
  2. package/apps/portal/view/ViewportController.mjs +5 -4
  3. package/apps/portal/view/home/ContentBox.mjs +80 -0
  4. package/apps/portal/view/home/MainContainer.mjs +51 -15
  5. package/apps/portal/view/learn/ContentTreeList.mjs +10 -3
  6. package/apps/portal/view/learn/MainContainerController.mjs +37 -5
  7. package/apps/portal/view/learn/MainContainerModel.mjs +51 -7
  8. package/apps/portal/view/learn/PageContainer.mjs +21 -9
  9. package/apps/sharedcovid/neo-config.json +1 -1
  10. package/examples/form/field/select/MainContainer.mjs +1 -1
  11. package/package.json +3 -3
  12. package/resources/data/deck/learnneo/pages/2023-10-14T19-25-08-153Z.md +16 -1
  13. package/resources/data/deck/learnneo/pages/ComponentsAndContainers.md +180 -0
  14. package/resources/data/deck/learnneo/pages/Config.md +11 -4
  15. package/resources/data/deck/learnneo/pages/DescribingTheUI.md +6 -0
  16. package/resources/data/deck/learnneo/pages/Earthquakes.md +36 -9
  17. package/resources/data/deck/learnneo/pages/Events.md +55 -43
  18. package/resources/data/deck/learnneo/pages/GuideEvents.md +9 -8
  19. package/resources/data/deck/learnneo/pages/References.md +14 -7
  20. package/resources/data/deck/learnneo/pages/TodoList.md +241 -0
  21. package/resources/data/deck/learnneo/pages/WhyNeo-Quick.md +6 -11
  22. package/resources/data/deck/learnneo/tree.json +2 -0
  23. package/resources/scss/src/apps/portal/home/ContentBox.scss +26 -0
  24. package/resources/scss/src/apps/portal/home/MainContainer.scss +4 -12
  25. package/resources/scss/src/apps/portal/learn/MainContainer.scss +0 -7
  26. package/resources/scss/src/apps/portal/learn/PageContainer.scss +35 -0
  27. package/resources/scss/src/apps/portal/learn/PageSectionsPanel.scss +8 -0
  28. package/src/component/Base.mjs +16 -1
  29. package/src/controller/Application.mjs +12 -1
  30. package/src/controller/Base.mjs +15 -4
  31. package/src/controller/Component.mjs +5 -1
  32. package/src/core/Observable.mjs +62 -22
  33. package/src/form/field/Base.mjs +21 -9
  34. package/src/form/field/Select.mjs +166 -117
  35. package/src/form/field/Text.mjs +7 -10
  36. package/src/main/DomEvents.mjs +2 -1
  37. package/src/main/addon/MonacoEditor.mjs +2 -2
  38. package/src/main/addon/Navigator.mjs +27 -24
  39. package/src/selection/ListModel.mjs +13 -14
  40. package/src/selection/Model.mjs +10 -10
  41. package/src/util/HashHistory.mjs +1 -0
  42. package/src/worker/App.mjs +5 -1
  43. package/src/worker/Manager.mjs +14 -7
  44. package/test/components/files/form/field/Select.mjs +6 -4
@@ -3,6 +3,6 @@
3
3
  "basePath" : "../../",
4
4
  "environment" : "development",
5
5
  "mainPath" : "./Main.mjs",
6
- "mainThreadAddons": ["AmCharts", "DragDrop", "MapboxGL", "Stylesheet"],
6
+ "mainThreadAddons": ["AmCharts", "DragDrop", "MapboxGL", "Navigator", "Stylesheet"],
7
7
  "themes" : ["neo-theme-dark", "neo-theme-light"]
8
8
  }
@@ -19,10 +19,11 @@ class ViewportController extends Controller {
19
19
  * @member {Object} routes
20
20
  */
21
21
  routes: {
22
- '/blog' : 'onBlogRoute',
23
- '/docs' : 'onDocsRoute',
24
- '/home' : 'onHomeRoute',
25
- '/learn': 'onLearnRoute'
22
+ '/blog' : 'onBlogRoute',
23
+ '/docs' : 'onDocsRoute',
24
+ '/home' : 'onHomeRoute',
25
+ '/learn' : 'onLearnRoute',
26
+ '/learn/{itemId}': 'onLearnRoute'
26
27
  }
27
28
  }
28
29
 
@@ -0,0 +1,80 @@
1
+ import Base from '../../../../src/component/Base.mjs';
2
+
3
+ /**
4
+ * @class Portal.view.home.ContentBox
5
+ * @extends Neo.component.Base
6
+ */
7
+ class ContentBox extends Base {
8
+ static config = {
9
+ /**
10
+ * @member {String} className='Portal.view.home.ContentBox'
11
+ * @protected
12
+ */
13
+ className: 'Portal.view.home.ContentBox',
14
+ /**
15
+ * @member {String[]} baseCls=['portal-content-box']
16
+ * @protected
17
+ */
18
+ baseCls: ['portal-content-box'],
19
+ /**
20
+ * @member {String[]|null} content_=null
21
+ */
22
+ content_: null,
23
+ /**
24
+ * @member {String|null} header_=null
25
+ */
26
+ header_: null,
27
+ /**
28
+ * @member {String|null} route_=null
29
+ */
30
+ route_: null,
31
+ /**
32
+ * @member {Object} _vdom
33
+ */
34
+ _vdom:
35
+ {tag: 'a', cn: [
36
+ {tag: 'h3', cls: ['portal-content-box-headline']},
37
+ {tag: 'ul', cls: ['portal-content-box-content'], cn: []}
38
+ ]}
39
+ }
40
+
41
+ /**
42
+ * Triggered after the content config got changed
43
+ * @param {String[]|null} value
44
+ * @param {String[]|null} oldValue
45
+ * @protected
46
+ */
47
+ afterSetContent(value, oldValue) {
48
+ value?.forEach(item => {
49
+ this.vdom.cn[1].cn.push({tag: 'li', html: item})
50
+ })
51
+
52
+ this.update()
53
+ }
54
+
55
+ /**
56
+ * Triggered after the header config got changed
57
+ * @param {String|null} value
58
+ * @param {String|null} oldValue
59
+ * @protected
60
+ */
61
+ afterSetHeader(value, oldValue) {
62
+ this.vdom.cn[0].html = value;
63
+ this.update()
64
+ }
65
+
66
+ /**
67
+ * Triggered after the route config got changed
68
+ * @param {String|null} value
69
+ * @param {String|null} oldValue
70
+ * @protected
71
+ */
72
+ afterSetRoute(value, oldValue) {
73
+ this.vdom.href = value;
74
+ this.update()
75
+ }
76
+ }
77
+
78
+ Neo.setupClass(ContentBox);
79
+
80
+ export default ContentBox;
@@ -1,5 +1,6 @@
1
- import Button from '../../../../src/button/Base.mjs';
2
- import Container from '../../../../src/container/Base.mjs';
1
+ import Button from '../../../../src/button/Base.mjs';
2
+ import Container from '../../../../src/container/Base.mjs';
3
+ import ContentBox from './ContentBox.mjs';
3
4
 
4
5
  /**
5
6
  * @class Portal.view.home.MainContainer
@@ -16,13 +17,16 @@ class Viewport extends Container {
16
17
  * @member {String[]} cls=['newwebsite-viewport']
17
18
  */
18
19
  cls: ['newwebsite-viewport'],
20
+ /**
21
+ * @member {Object} layout={ntype:'vbox',align:'center',pack:'center'}
22
+ */
23
+ layout: {ntype: 'vbox', align: 'center', pack: 'center'},
19
24
  /**
20
25
  * @member {Object[]} items
21
26
  */
22
27
  items: [{
23
- module: Container,
24
- cls : ['vector'],
25
- flex : 'none'
28
+ cls : ['vector'],
29
+ flex : 'none'
26
30
  }, {
27
31
  cls : 'neo-h1',
28
32
  flex: 'none',
@@ -31,23 +35,55 @@ class Viewport extends Container {
31
35
  module: Container,
32
36
  cls : ['button-group'],
33
37
  flex : 'none',
38
+ layout: {ntype: 'hbox'},
34
39
 
35
40
  items: [{
36
- module : Button,
37
- cls : 'get-started-button',
38
- text : 'Get started',
39
- flex : 'none',
40
- tooltip: {
41
- text : 'Coming soon',
42
- showDelay: 0,
43
- hideDelay: 0
44
- }
45
- }, {
46
41
  module: Button,
47
42
  flex : 'none',
48
43
  text : 'View on GitHub',
49
44
  ui : 'secondary',
50
45
  url : 'https://github.com/neomjs/neo'
46
+ }, {
47
+ module: Button,
48
+ flex : 'none',
49
+ text : 'Get started',
50
+ route : '/learn'
51
+ }]
52
+ }, {
53
+ module: Container,
54
+ flex : '1 0 auto',
55
+ layout: {ntype: 'hbox', align: 'stretch'},
56
+
57
+ items: [{
58
+ module: ContentBox,
59
+ header: 'Quick Application Development',
60
+ route : '#/learn/WhyNeo-Quick',
61
+
62
+ content: [
63
+ 'Property lifecycle hooks',
64
+ 'Elegant state management',
65
+ 'Simple and powerful debugging'
66
+ ]
67
+ }, {
68
+ module: ContentBox,
69
+ header: 'Extreme Speed',
70
+ route : '#/learn/WhyNeo-Speed',
71
+
72
+ content: [
73
+ 'Item 1',
74
+ 'Item 2',
75
+ 'Item 3'
76
+ ]
77
+ }, {
78
+ module: ContentBox,
79
+ header: 'Multi Window Apps',
80
+ route : '#/learn/WhyNeo-Multi-Window',
81
+
82
+ content: [
83
+ 'Item 1',
84
+ 'Item 2',
85
+ 'Item 3'
86
+ ]
51
87
  }]
52
88
  }]
53
89
  }
@@ -51,8 +51,11 @@ class ContentTreeList extends TreeList {
51
51
  * @param {Object} value
52
52
  * @param {Object} oldValue
53
53
  */
54
- afterSetCurrentPageRecord(value, oldValue) {
55
- value && this.selectionModel.select(value)
54
+ async afterSetCurrentPageRecord(value, oldValue) {
55
+ if (value) {
56
+ await this.timeout(20);
57
+ this.selectionModel.select(value)
58
+ }
56
59
  }
57
60
 
58
61
  /**
@@ -77,7 +80,11 @@ class ContentTreeList extends TreeList {
77
80
  */
78
81
  onLeafItemClick(record) {
79
82
  super.onLeafItemClick(record);
80
- this.getModel().data.currentPageRecord = record
83
+
84
+ Neo.Main.setRoute({
85
+ value : `/learn/${record.id}`,
86
+ windowId: this.windowId
87
+ })
81
88
  }
82
89
  }
83
90
 
@@ -10,7 +10,13 @@ class MainContainerController extends Controller {
10
10
  * @member {String} className='Portal.view.learn.MainContainerController'
11
11
  * @protected
12
12
  */
13
- className: 'Portal.view.learn.MainContainerController'
13
+ className: 'Portal.view.learn.MainContainerController',
14
+ /**
15
+ * @member {Object} routes
16
+ */
17
+ routes: {
18
+ '/learn/{itemId}': 'onRouteLearnItem'
19
+ }
14
20
  }
15
21
 
16
22
  /**
@@ -48,6 +54,16 @@ class MainContainerController extends Controller {
48
54
  return searchString ? JSON.parse(`{"${decodeURI(searchString.replace(/&/g, "\",\"").replace(/=/g, "\":\""))}"}`) : {}
49
55
  }
50
56
 
57
+ /**
58
+ * @param {String} learnItem
59
+ */
60
+ navigateTo(learnItem) {
61
+ Neo.Main.setRoute({
62
+ value : `/learn/${learnItem}`,
63
+ windowId: this.component.windowId
64
+ })
65
+ }
66
+
51
67
  /**
52
68
  * @param {Object} data
53
69
  * @param {String} data.appName
@@ -177,16 +193,32 @@ class MainContainerController extends Controller {
177
193
  * @param {Object} data
178
194
  */
179
195
  onNextPageButtonClick(data) {
180
- let model = this.getModel();
181
- model.setData('currentPageRecord', model.getData('nextPageRecord'))
196
+ this.navigateTo(this.getModel().getData('nextPageRecord').id)
182
197
  }
183
198
 
184
199
  /**
185
200
  * @param {Object} data
186
201
  */
187
202
  onPreviousPageButtonClick(data) {
188
- let model = this.getModel();
189
- model.setData('currentPageRecord', model.getData('previousPageRecord'))
203
+ this.navigateTo(this.getModel().getData('previousPageRecord').id)
204
+ }
205
+
206
+ /**
207
+ * @param {Object} data
208
+ */
209
+ onRouteLearnItem(data) {
210
+ let model = this.getModel(),
211
+ store = model.getStore('contentTree');
212
+
213
+ if (store.getCount() > 0) {
214
+ model.data.currentPageRecord = store.get(data.itemId)
215
+ } else {
216
+ store.on({
217
+ load : () => {model.data.currentPageRecord = store.get(data.itemId)},
218
+ delay: 10,
219
+ once : true
220
+ })
221
+ }
190
222
  }
191
223
  }
192
224
 
@@ -40,14 +40,19 @@ class MainContainerModel extends Component {
40
40
  deck: null,
41
41
  /**
42
42
  * The record which gets shown as the content page
43
- * @member {Object} data.currentRecord=null
43
+ * @member {Object} data.nextPageRecord=null
44
44
  */
45
45
  nextPageRecord: null,
46
46
  /**
47
47
  * The record which gets shown as the content page
48
- * @member {Object} data.currentRecord=null
48
+ * @member {Object} data.previousPageRecord=null
49
49
  */
50
- previousPageRecord: null
50
+ previousPageRecord: null,
51
+ /**
52
+ * Merging the direct parent text
53
+ * @member {String|null} data.previousPageText=null
54
+ */
55
+ previousPageText: null
51
56
  },
52
57
  /**
53
58
  * @member {Object} stores
@@ -62,6 +67,22 @@ class MainContainerModel extends Component {
62
67
  }
63
68
  }
64
69
 
70
+ /**
71
+ * Combines the record parent node name (if available) with the record name
72
+ * @param {Object} record
73
+ * @param {Neo.data.Store} store
74
+ * @returns {String|null}
75
+ */
76
+ getRecordTreeName(record, store) {
77
+ let parentText = record.name;
78
+
79
+ if (record.parentId !== null) {
80
+ parentText = store.get(record.parentId).name + ': ' + parentText
81
+ }
82
+
83
+ return parentText
84
+ }
85
+
65
86
  /**
66
87
  * @param {String} key
67
88
  * @param {*} value
@@ -79,32 +100,36 @@ class MainContainerModel extends Component {
79
100
  store = me.getStore('contentTree'),
80
101
  index = store.indexOf(value),
81
102
  nextPageRecord = null,
103
+ nextPageText = null,
82
104
  previousPageRecord = null,
105
+ previousPageText = null,
83
106
  i, record;
84
107
 
85
108
  // the logic assumes that the tree store is sorted
86
109
  for (i=index-1; i >= 0; i--) {
87
110
  record = store.getAt(i);
88
111
 
89
- if (record.isLeaf) {
112
+ if (record.isLeaf && !me.recordIsHidden(record, store)) {
90
113
  previousPageRecord = record;
114
+ previousPageText = me.getRecordTreeName(record, store);
91
115
  break
92
116
  }
93
117
  }
94
118
 
95
- me.setData({previousPageRecord});
119
+ me.setData({previousPageText, previousPageRecord});
96
120
 
97
121
  // the logic assumes that the tree store is sorted
98
122
  for (i=index+1; i < countPages; i++) {
99
123
  record = store.getAt(i);
100
124
 
101
- if (record.isLeaf) {
125
+ if (record.isLeaf && !me.recordIsHidden(record, store)) {
102
126
  nextPageRecord = record;
127
+ nextPageText = me.getRecordTreeName(record, store);
103
128
  break
104
129
  }
105
130
  }
106
131
 
107
- me.setData({nextPageRecord});
132
+ me.setData({nextPageText, nextPageRecord});
108
133
 
109
134
  break
110
135
  }
@@ -118,6 +143,25 @@ class MainContainerModel extends Component {
118
143
  }
119
144
  }
120
145
  }
146
+
147
+ /**
148
+ * We need to check the parent-node chain inside the tree.
149
+ * => Any hidden parent-node results in a hidden record.
150
+ * @param {Object} record
151
+ * @param {Neo.data.Store} store
152
+ * @returns {Boolean}
153
+ */
154
+ recordIsHidden(record, store) {
155
+ if (record.hidden) {
156
+ return true
157
+ }
158
+
159
+ if (record.parentId !== null) {
160
+ return this.recordIsHidden(store.get(record.parentId), store)
161
+ }
162
+
163
+ return false
164
+ }
121
165
  }
122
166
 
123
167
  Neo.setupClass(MainContainerModel);
@@ -38,14 +38,22 @@ class PageContainer extends Container {
38
38
  }, {
39
39
  module: Toolbar,
40
40
  flex : 'none',
41
+ cls : 'content-bottom-toolbar',
41
42
  items : [{
43
+ cls : ['content-bottom-toolbar-previous'],
42
44
  handler : 'onPreviousPageButtonClick',
43
45
  hidden : true,
44
- reference: 'prev-page-button'
45
- }, '->', {
46
- handler : 'onNextPageButtonClick',
47
- hidden : true,
48
- reference: 'next-page-button'
46
+ iconCls : 'fa fa-chevron-left',
47
+ reference: 'prev-page-button',
48
+ ui : 'secondary'
49
+ }, {
50
+ cls : ['content-bottom-toolbar-next'],
51
+ handler : 'onNextPageButtonClick',
52
+ hidden : true,
53
+ iconCls : 'fa fa-chevron-right',
54
+ iconPosition: 'right',
55
+ reference : 'next-page-button',
56
+ ui : 'secondary'
49
57
  }]
50
58
  }],
51
59
  /**
@@ -81,10 +89,12 @@ class PageContainer extends Container {
81
89
  */
82
90
  afterSetNextPageRecord(value, oldValue) {
83
91
  if (oldValue !== undefined) {
92
+ let me = this;
93
+
84
94
  if (value) {
85
- this.nextPageButton.set({hidden: false, text: value.name})
95
+ me.nextPageButton.set({hidden: false, text: me.getModel().getData('nextPageText')})
86
96
  } else {
87
- this.nextPageButton.hidden = true
97
+ me.nextPageButton.hidden = true
88
98
  }
89
99
  }
90
100
  }
@@ -96,10 +106,12 @@ class PageContainer extends Container {
96
106
  */
97
107
  afterSetPreviousPageRecord(value, oldValue) {
98
108
  if (oldValue !== undefined) {
109
+ let me = this;
110
+
99
111
  if (value) {
100
- this.prevPageButton.set({hidden: false, text: value.name})
112
+ me.prevPageButton.set({hidden: false, text: me.getModel().getData('previousPageText')})
101
113
  } else {
102
- this.prevPageButton.hidden = true
114
+ me.prevPageButton.hidden = true
103
115
  }
104
116
  }
105
117
  }
@@ -3,7 +3,7 @@
3
3
  "basePath" : "../../",
4
4
  "environment" : "development",
5
5
  "mainPath" : "./Main.mjs",
6
- "mainThreadAddons": ["AmCharts", "DragDrop", "MapboxGL", "Stylesheet"],
6
+ "mainThreadAddons": ["AmCharts", "DragDrop", "MapboxGL", "Navigator", "Stylesheet"],
7
7
  "themes" : ["neo-theme-dark", "neo-theme-light"],
8
8
  "useSharedWorkers": true
9
9
  }
@@ -165,7 +165,7 @@ class MainContainer extends ConfigurationViewport {
165
165
  labelText : 'US States',
166
166
  labelWidth : 80,
167
167
  store : MainStore,
168
- value : 'Arizona', // or 'AZ'
168
+ value : 'AZ',
169
169
  valueField : 'abbreviation',
170
170
  width : '50%',
171
171
  pickerConfig : {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  {
3
3
  "name": "neo.mjs",
4
- "version": "6.13.0",
4
+ "version": "6.14.0",
5
5
  "description": "The webworkers driven UI framework",
6
6
  "type": "module",
7
7
  "repository": {
@@ -53,13 +53,13 @@
53
53
  "envinfo": "^7.11.1",
54
54
  "fs-extra": "^11.2.0",
55
55
  "highlightjs-line-numbers.js": "^2.8.0",
56
- "inquirer": "^9.2.15",
56
+ "inquirer": "^9.2.16",
57
57
  "marked": "^12.0.1",
58
58
  "monaco-editor": "^0.47.0",
59
59
  "neo-jsdoc": "1.0.1",
60
60
  "neo-jsdoc-x": "1.0.5",
61
61
  "postcss": "^8.4.35",
62
- "sass": "^1.71.1",
62
+ "sass": "^1.72.0",
63
63
  "siesta-lite": "5.5.2",
64
64
  "url": "^0.11.3",
65
65
  "webpack": "^5.90.3",
@@ -1,3 +1,5 @@
1
+ ## Introduction
2
+
1
3
  The purpose of this tutorial is to let you see the structure of a Neo.mjs workspace,
2
4
  and the structure of an application.
3
5
 
@@ -5,7 +7,12 @@ If you wish, you can create a workspace by following the directions in the previ
5
7
 
6
8
  <img src="https://raw.githubusercontent.com/neomjs/pages/main/resources/images/apps/learnneo/NeoWorkspace.png" style="height: 400px;">
7
9
 
8
- As you can see, a Neo.mjs workspace is a conventional npm package. If you run
10
+ As you can see, a Neo.mjs workspace is a conventional npm package.
11
+
12
+
13
+ ## Browsing the workspace
14
+
15
+ If you run
9
16
  the script `npm run server-start` from the workspace, Neo.mjs launches a web
10
17
  server whose doc root is the workspace.
11
18
 
@@ -14,6 +21,8 @@ server whose doc root is the workspace.
14
21
  If you drill down into the `src` directory you'll see your applications.
15
22
  The `docs` directory holds the Neo.mjs API docs.
16
23
 
24
+ ## The structure of a simple app
25
+
17
26
  In order to discuss the structure of an app, we'll create a simple starter
18
27
  app vis this script, run from the workspace. The script prompts for various
19
28
  application settings.
@@ -29,6 +38,8 @@ These files are part of the typical structure of a Neo.mjs application. The file
29
38
  You will, however, edit the main container, and its associated "controller" and "model",
30
39
  as well as create new views classes, their controllers, and other application logic.
31
40
 
41
+ ## Some source code
42
+
32
43
  Now let's look at a source file. This is the contents of `MainView.mjs`.
33
44
 
34
45
  <pre data-javascript>
@@ -61,6 +72,8 @@ This view also has a "controller" and "model". We'll talk about those later, but
61
72
  a controller is where event handling and app logic goes, and a model is where you set up shared
62
73
  bindable data.
63
74
 
75
+ ## Adding a button to the view
76
+
64
77
  We'll go into a lot more depth about view, controllers, and models in other topics, but to let
65
78
  you see how a component is configured let's put a button in the container.
66
79
 
@@ -103,6 +116,8 @@ some value."
103
116
  In another topic you'll learn about the Neo.mjs config system and declaratively describing views,
104
117
  controllers, and other things.
105
118
 
119
+ ## A running example
120
+
106
121
  Here's a simplified running example. The `model` and `controller` are omitted, because they aren't
107
122
  actually used in the example, and the import root path is different to reflect the location of the
108
123
  Neo.mjs library relative to the examples.