neo.mjs 6.18.1 → 6.18.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/ServiceWorker.mjs +2 -2
- package/apps/colors/view/HeaderToolbar.mjs +6 -6
- package/apps/portal/view/HeaderToolbar.mjs +1 -2
- package/apps/portal/view/home/MainContainer.mjs +21 -21
- package/apps/portal/view/home/parts/AfterMath.mjs +19 -16
- package/apps/portal/view/home/parts/BaseContainer.mjs +38 -0
- package/apps/portal/view/home/parts/Colors.mjs +15 -15
- package/apps/portal/view/home/parts/Features.mjs +6 -6
- package/apps/portal/view/home/parts/Helix.mjs +9 -12
- package/apps/portal/view/home/parts/How.mjs +8 -8
- package/apps/portal/view/home/parts/MainNeo.mjs +14 -12
- package/apps/portal/view/learn/ContentView.mjs +3 -1
- package/examples/ServiceWorker.mjs +2 -2
- package/examples/model/dialog/EditUserDialog.mjs +1 -8
- package/package.json +4 -4
- package/resources/data/deck/learnneo/pages/2023-10-14T19-25-08-153Z.md +2 -2
- package/resources/data/deck/learnneo/pages/ComponentModels.md +6 -6
- package/resources/data/deck/learnneo/pages/ComponentsAndContainers.md +10 -10
- package/resources/data/deck/learnneo/pages/Config.md +6 -6
- package/resources/data/deck/learnneo/pages/CustomComponents.md +4 -4
- package/resources/data/deck/learnneo/pages/DescribingTheUI.md +4 -4
- package/resources/data/deck/learnneo/pages/Earthquakes.md +2 -2
- package/resources/data/deck/learnneo/pages/Events.md +7 -7
- package/resources/data/deck/learnneo/pages/Extending.md +7 -7
- package/resources/data/deck/learnneo/pages/GuideEvents.md +17 -18
- package/resources/data/deck/learnneo/pages/GuideViewModels.md +444 -0
- package/resources/data/deck/learnneo/pages/References.md +8 -8
- package/resources/data/deck/learnneo/pages/TestLivePreview.md +5 -4
- package/resources/data/deck/learnneo/pages/TodoList.md +9 -9
- package/resources/data/deck/learnneo/pages/Welcome.md +3 -3
- package/resources/data/deck/learnneo/pages/WhyNeo-Multi-Window.md +2 -2
- package/resources/data/deck/learnneo/pages/WhyNeo-Speed.md +2 -2
- package/resources/data/deck/learnneo/tree.json +1 -0
- package/resources/scss/src/apps/portal/HeaderToolbar.scss +1 -47
- package/resources/scss/src/apps/portal/home/MainContainer.scss +0 -32
- package/resources/scss/src/apps/portal/home/parts/BaseContainer.scss +56 -0
- package/resources/scss/src/apps/portal/home/parts/MainNeo.scss +3 -1
- package/resources/scss/src/apps/portal/learn/ContentTreeList.scss +1 -1
- package/resources/scss/src/calendar/view/EditEventContainer.scss +1 -1
- package/resources/scss/src/calendar/view/calendars/List.scss +1 -1
- package/resources/scss/src/{apps/portal/learn → code}/LivePreview.scss +1 -1
- package/resources/scss/src/component/Helix.scss +1 -2
- package/resources/scss/src/dialog/Base.scss +1 -6
- package/resources/scss/theme-dark/dialog/Base.scss +1 -0
- package/resources/scss/theme-light/dialog/Base.scss +1 -0
- package/resources/scss/theme-neo-light/Global.scss +13 -11
- package/resources/scss/theme-neo-light/dialog/Base.scss +1 -0
- package/src/DefaultConfig.mjs +2 -2
- package/src/button/Base.mjs +3 -2
- package/src/calendar/view/EditEventContainer.mjs +1 -1
- package/{apps/portal/view/learn → src/code}/LivePreview.mjs +43 -27
- package/src/component/Base.mjs +1 -6
- package/src/controller/Base.mjs +5 -5
- package/src/dialog/Base.mjs +23 -45
- package/src/form/field/Color.mjs +5 -5
- package/src/main/addon/ResizeObserver.mjs +18 -2
- package/src/selection/DateSelectorModel.mjs +2 -2
- package/src/util/HashHistory.mjs +45 -12
- package/src/worker/Base.mjs +15 -8
- package/apps/portal/view/home/parts/HelloWorld.mjs +0 -85
- package/apps/portal/view/home/preview/PageCodeContainer.mjs +0 -55
- package/resources/scss/src/apps/portal/Viewport.scss +0 -3
- package/resources/scss/src/apps/portal/home/preview/PageCodeContainer.scss +0 -115
@@ -0,0 +1,444 @@
|
|
1
|
+
View models (VMs) in Neo.mjs are state providers.
|
2
|
+
|
3
|
+
While Components can manage their own state using the Class Config System,
|
4
|
+
you want to use VMs as soon as you want to share data properties with multiple child Components.
|
5
|
+
|
6
|
+
Rules of thumb:
|
7
|
+
1. Leaf Components inside the Component Tree (Container items) will most likely not need a VM.
|
8
|
+
2. We can define multiple VMs as needed (they do communicate).
|
9
|
+
3. We want to define shared state data properties as low inside the component tree as possible.
|
10
|
+
|
11
|
+
We often reference a VM as `model.Component` (the class name inside Neo.mjs),
|
12
|
+
other libraries or frameworks often call them Stores.
|
13
|
+
|
14
|
+
Since we also have Data Stores (tabular data), we chose to use the name VM to avoid confusion.
|
15
|
+
|
16
|
+
## Inline Models
|
17
|
+
### Direct Bindings
|
18
|
+
<pre data-neo>
|
19
|
+
import Button from '../button/Base.mjs';
|
20
|
+
import Container from '../container/Base.mjs';
|
21
|
+
import Label from '../component/Label.mjs';
|
22
|
+
|
23
|
+
class MainView extends Container {
|
24
|
+
static config = {
|
25
|
+
className: 'Example.view.MainView',
|
26
|
+
model: {
|
27
|
+
data: {
|
28
|
+
hello: 'Hello',
|
29
|
+
world: 'world!'
|
30
|
+
}
|
31
|
+
},
|
32
|
+
itemDefaults: {
|
33
|
+
module: Label,
|
34
|
+
style : {margin: '1em'}
|
35
|
+
},
|
36
|
+
items: [{
|
37
|
+
bind: {
|
38
|
+
text: data => data.hello
|
39
|
+
}
|
40
|
+
}, {
|
41
|
+
bind: {
|
42
|
+
text: data => data.world
|
43
|
+
}
|
44
|
+
}, {
|
45
|
+
module : Button,
|
46
|
+
handler: data => data.component.getModel().setData({hello: 'Hi'}),
|
47
|
+
text : 'Change Hello'
|
48
|
+
}, {
|
49
|
+
module : Button,
|
50
|
+
handler: data => data.component.getModel().setData({world: 'Neo.mjs!'}),
|
51
|
+
text : 'Change World'
|
52
|
+
}],
|
53
|
+
layout: {ntype: 'vbox', align: 'start'}
|
54
|
+
}
|
55
|
+
}
|
56
|
+
Neo.setupClass(MainView);
|
57
|
+
</pre>
|
58
|
+
|
59
|
+
We use a Container with a VM containing the data props `hello` and `world`.
|
60
|
+
Inside the Container are 2 Labels which bind their `text` config to a data prop directly.
|
61
|
+
|
62
|
+
We can easily bind 1:1 to specific data props using the following syntax:</br>
|
63
|
+
`bind: {text: data => data.hello}`
|
64
|
+
|
65
|
+
### Bindings with multiple data props
|
66
|
+
<pre data-neo>
|
67
|
+
import Button from '../button/Base.mjs';
|
68
|
+
import Container from '../container/Base.mjs';
|
69
|
+
import Label from '../component/Label.mjs';
|
70
|
+
|
71
|
+
class MainView extends Container {
|
72
|
+
static config = {
|
73
|
+
className: 'Example.view.MainView',
|
74
|
+
model: {
|
75
|
+
data: {
|
76
|
+
hello: 'Hello',
|
77
|
+
world: 'world!'
|
78
|
+
}
|
79
|
+
},
|
80
|
+
itemDefaults: {
|
81
|
+
module: Label,
|
82
|
+
style : {margin: '1em'}
|
83
|
+
},
|
84
|
+
items: [{
|
85
|
+
bind: {
|
86
|
+
// We can use template literals containing VM data props
|
87
|
+
text: data => `${data.hello} ${data.world}`
|
88
|
+
}
|
89
|
+
}, {
|
90
|
+
bind: {
|
91
|
+
// We can also use VM data props directly inside fat arrow function bodies
|
92
|
+
text: data => data.hello + ' ' + data.world
|
93
|
+
}
|
94
|
+
}, {
|
95
|
+
bind: {
|
96
|
+
// We can convert the config into a function to use VM data props
|
97
|
+
text(data) {return data.hello + ' ' + data.world}
|
98
|
+
}
|
99
|
+
}, {
|
100
|
+
module : Button,
|
101
|
+
handler: data => data.component.getModel().setData({hello: 'Hi'}),
|
102
|
+
text : 'Change Hello'
|
103
|
+
}, {
|
104
|
+
module : Button,
|
105
|
+
handler: data => data.component.getModel().setData({world: 'Neo.mjs!'}),
|
106
|
+
text : 'Change World'
|
107
|
+
}],
|
108
|
+
layout: {ntype: 'vbox', align: 'start'}
|
109
|
+
}
|
110
|
+
}
|
111
|
+
Neo.setupClass(MainView);
|
112
|
+
</pre>
|
113
|
+
|
114
|
+
We use a Container with a VM containing the data props `hello` and `world`.
|
115
|
+
Inside the Container are 3 Labels which bind their `text` config to a combination of both data props.
|
116
|
+
|
117
|
+
We are showcasing 3 different ways how you can define your binding (resulting in the same output).
|
118
|
+
|
119
|
+
In case any of the bound data props changes, all bound Configs will check for an update.
|
120
|
+
|
121
|
+
Important: The Config setter will only trigger in case there is a real change for the bound output.
|
122
|
+
|
123
|
+
We also added 2 Buttons to change the value of each data prop, so that we can see that the bound Label texts
|
124
|
+
update right away.
|
125
|
+
|
126
|
+
Let us take a look at the Button handler:</br>
|
127
|
+
`data.component.getModel().setData({world: 'Neo.mjs!'})`
|
128
|
+
|
129
|
+
data.component equals to the Button instance itself. Since the Button instance does not have its own VM,
|
130
|
+
`getModel()` will return the closest VM inside the parent chain.
|
131
|
+
|
132
|
+
### Nested Inline Models
|
133
|
+
<pre data-neo>
|
134
|
+
import Button from '../button/Base.mjs';
|
135
|
+
import Container from '../container/Base.mjs';
|
136
|
+
import Label from '../component/Label.mjs';
|
137
|
+
|
138
|
+
class MainView extends Container {
|
139
|
+
static config = {
|
140
|
+
className: 'Example.view.MainView',
|
141
|
+
model: {
|
142
|
+
data: {
|
143
|
+
hello: 'Hello'
|
144
|
+
}
|
145
|
+
},
|
146
|
+
layout: 'fit',
|
147
|
+
items : [{
|
148
|
+
module: Container,
|
149
|
+
model: {
|
150
|
+
data: {
|
151
|
+
world: 'world!'
|
152
|
+
}
|
153
|
+
},
|
154
|
+
itemDefaults: {
|
155
|
+
module: Label,
|
156
|
+
style : {margin: '1em'}
|
157
|
+
},
|
158
|
+
items: [{
|
159
|
+
bind: {
|
160
|
+
// We can use template literals containing VM data props
|
161
|
+
text: data => `${data.hello} ${data.world}`
|
162
|
+
}
|
163
|
+
}, {
|
164
|
+
bind: {
|
165
|
+
// We can also use VM data props directly inside fat arrow function bodies
|
166
|
+
text: data => data.hello + ' ' + data.world
|
167
|
+
}
|
168
|
+
}, {
|
169
|
+
bind: {
|
170
|
+
// We can convert the config into a function to use VM data props
|
171
|
+
text(data) {return data.hello + ' ' + data.world}
|
172
|
+
}
|
173
|
+
}, {
|
174
|
+
module : Button,
|
175
|
+
handler: data => data.component.getModel().setData({hello: 'Hi'}),
|
176
|
+
text : 'Change Hello'
|
177
|
+
}, {
|
178
|
+
module : Button,
|
179
|
+
handler: data => data.component.getModel().setData({world: 'Neo.mjs!'}),
|
180
|
+
text : 'Change World'
|
181
|
+
}],
|
182
|
+
layout: {ntype: 'vbox', align: 'start'}
|
183
|
+
}]
|
184
|
+
}
|
185
|
+
}
|
186
|
+
Neo.setupClass(MainView);
|
187
|
+
</pre>
|
188
|
+
|
189
|
+
The output of this demo is supposed to exactly look the same like the previous demo.
|
190
|
+
|
191
|
+
This time we nest our Labels into a Container with a fit layout.
|
192
|
+
Just for demo purposes, we want to avoid overnesting inside real apps.
|
193
|
+
|
194
|
+
Our top level VM now only contains the `hello` data prop, and we added a second VM inside the nested Container
|
195
|
+
which contains the `world` data prop.
|
196
|
+
|
197
|
+
As a result, the bindings for all 3 Labels contain a combination of data props which live inside different VMs.
|
198
|
+
As long as these VMs are inside the parent hierarchy this works fine.
|
199
|
+
|
200
|
+
The same goes for the Button handlers: `setData()` will find the closest matching data prop inside the VM parent chain.
|
201
|
+
|
202
|
+
We can even change data props which live inside different VMs at once. As easy as this:</br>
|
203
|
+
`setData({hello: 'foo', world: 'bar'})`
|
204
|
+
|
205
|
+
Hint: Modify the example code (Button handler) to try it out right away!
|
206
|
+
|
207
|
+
### Nested Data Properties
|
208
|
+
<pre data-neo>
|
209
|
+
import Button from '../button/Base.mjs';
|
210
|
+
import Container from '../container/Base.mjs';
|
211
|
+
import Label from '../component/Label.mjs';
|
212
|
+
|
213
|
+
class MainView extends Container {
|
214
|
+
static config = {
|
215
|
+
className: 'Example.view.MainView',
|
216
|
+
model: {
|
217
|
+
data: {
|
218
|
+
user: {
|
219
|
+
firstname: 'Tobias',
|
220
|
+
lastname : 'Uhlig'
|
221
|
+
}
|
222
|
+
}
|
223
|
+
},
|
224
|
+
itemDefaults: {
|
225
|
+
module: Label,
|
226
|
+
style : {margin: '1em'}
|
227
|
+
},
|
228
|
+
items: [{
|
229
|
+
bind: {
|
230
|
+
text: data => `${data.user.firstname} ${data.user.lastname}`
|
231
|
+
}
|
232
|
+
}, {
|
233
|
+
bind: {
|
234
|
+
text: data => data.user.firstname + ' ' + data.user.lastname
|
235
|
+
}
|
236
|
+
}, {
|
237
|
+
module : Button,
|
238
|
+
handler: data => data.component.getModel().setData({user: {firstname: 'Max'}}),
|
239
|
+
text : 'Change Firstname'
|
240
|
+
}, {
|
241
|
+
module : Button,
|
242
|
+
handler: data => data.component.getModel().setData({'user.lastname': 'Rahder'}),
|
243
|
+
text : 'Change Lastname'
|
244
|
+
}],
|
245
|
+
layout: {ntype: 'vbox', align: 'start'}
|
246
|
+
}
|
247
|
+
}
|
248
|
+
Neo.setupClass(MainView);
|
249
|
+
</pre>
|
250
|
+
Data props inside VMs can be nested. Our VM contains a `user` data prop as an object,
|
251
|
+
which contains the nested props `firstname` and `lastname`.
|
252
|
+
|
253
|
+
We can bind to these nested props like before:</br>
|
254
|
+
`bind: {text: data => data.user.firstname + ' ' + data.user.lastname}`
|
255
|
+
|
256
|
+
Any change of a nested data prop will directly get reflected into the bound components.
|
257
|
+
|
258
|
+
We can update a nested data prop with passing its path:</br>
|
259
|
+
`data => data.component.getModel().setData({'user.lastname': 'Rahder'})`
|
260
|
+
|
261
|
+
Or we can directly pass the object containing the change(s):</br>
|
262
|
+
`data => data.component.getModel().setData({user: {firstname: 'Max'}})`
|
263
|
+
|
264
|
+
Hint: This will not override left out nested data props (lastname in this case).
|
265
|
+
|
266
|
+
### Dialog connecting to a Container
|
267
|
+
<pre data-neo>
|
268
|
+
import Controller from '../controller/Component.mjs';
|
269
|
+
import Dialog from '../dialog/Base.mjs';
|
270
|
+
import Panel from '../container/Panel.mjs';
|
271
|
+
import TextField from '../form/field/Text.mjs';
|
272
|
+
import Viewport from '../container/Viewport.mjs';
|
273
|
+
|
274
|
+
class EditUserDialogController extends Controller {
|
275
|
+
static config = {
|
276
|
+
className: 'Neo.examples.model.dialog.EditUserDialogController'
|
277
|
+
}
|
278
|
+
|
279
|
+
onFirstnameTextFieldChange(data) {
|
280
|
+
this.getModel().setData({
|
281
|
+
'user.firstname': data.value || ''
|
282
|
+
})
|
283
|
+
}
|
284
|
+
|
285
|
+
onLastnameTextFieldChange(data) {
|
286
|
+
this.getModel().setData({
|
287
|
+
'user.lastname': data.value || ''
|
288
|
+
})
|
289
|
+
}
|
290
|
+
}
|
291
|
+
Neo.setupClass(EditUserDialogController);
|
292
|
+
|
293
|
+
class EditUserDialog extends Dialog {
|
294
|
+
static config = {
|
295
|
+
className : 'Neo.examples.model.dialog.EditUserDialog',
|
296
|
+
containerConfig: {style: {padding: '1em'}},
|
297
|
+
controller : EditUserDialogController,
|
298
|
+
title : 'Edit User',
|
299
|
+
itemDefaults : {module: TextField, flex: 'none', labelWidth: 110},
|
300
|
+
items: [{
|
301
|
+
bind : {value: data => data.user.firstname},
|
302
|
+
labelText: 'Firstname:',
|
303
|
+
listeners: {change: 'onFirstnameTextFieldChange'}
|
304
|
+
}, {
|
305
|
+
bind : {value: data => data.user.lastname},
|
306
|
+
labelText: 'Lastname:',
|
307
|
+
listeners: {change: 'onLastnameTextFieldChange'}
|
308
|
+
}],
|
309
|
+
wrapperStyle: {height: '300px', width : '400px'}
|
310
|
+
}
|
311
|
+
}
|
312
|
+
Neo.setupClass(EditUserDialog);
|
313
|
+
|
314
|
+
class MainContainerController extends Controller {
|
315
|
+
static config = {
|
316
|
+
className: 'Neo.examples.model.dialog.MainContainerController',
|
317
|
+
dialog : null
|
318
|
+
}
|
319
|
+
|
320
|
+
onEditUserButtonClick(data) {
|
321
|
+
let me = this;
|
322
|
+
|
323
|
+
if (!me.dialog) {
|
324
|
+
me.dialog = Neo.create({
|
325
|
+
module : EditUserDialog,
|
326
|
+
animateTargetId: me.getReference('edit-user-button').id,
|
327
|
+
appName : me.component.appName,
|
328
|
+
closeAction : 'hide',
|
329
|
+
|
330
|
+
model: {
|
331
|
+
parent: me.getModel()
|
332
|
+
}
|
333
|
+
})
|
334
|
+
} else {
|
335
|
+
me.dialog.show()
|
336
|
+
}
|
337
|
+
}
|
338
|
+
}
|
339
|
+
Neo.setupClass(MainContainerController);
|
340
|
+
|
341
|
+
class MainView extends Viewport {
|
342
|
+
static config = {
|
343
|
+
className : 'Neo.examples.model.dialog.MainContainer',
|
344
|
+
controller: MainContainerController,
|
345
|
+
model: {
|
346
|
+
data: {
|
347
|
+
user: {
|
348
|
+
firstname: 'Tobias',
|
349
|
+
lastname : 'Uhlig'
|
350
|
+
}
|
351
|
+
}
|
352
|
+
},
|
353
|
+
style: {padding: '20px'},
|
354
|
+
items: [{
|
355
|
+
module: Panel,
|
356
|
+
containerConfig: {
|
357
|
+
layout: {ntype: 'vbox', align: 'start'},
|
358
|
+
style : {padding: '20px'}
|
359
|
+
},
|
360
|
+
headers: [{
|
361
|
+
dock : 'top',
|
362
|
+
items: [{
|
363
|
+
ntype: 'label',
|
364
|
+
bind : {
|
365
|
+
text: data => `Current user: ${data.user.firstname} ${data.user.lastname}`
|
366
|
+
}
|
367
|
+
}, {
|
368
|
+
ntype: 'component',
|
369
|
+
flex : 1
|
370
|
+
}, {
|
371
|
+
handler : 'onEditUserButtonClick',
|
372
|
+
iconCls : 'fa fa-user',
|
373
|
+
reference: 'edit-user-button',
|
374
|
+
text : 'Edit user'
|
375
|
+
}]
|
376
|
+
}],
|
377
|
+
|
378
|
+
items: [{
|
379
|
+
ntype: 'label',
|
380
|
+
text : 'Click the edit user button to edit the user data </br> inside this container view model.'
|
381
|
+
}]
|
382
|
+
}]
|
383
|
+
}
|
384
|
+
}
|
385
|
+
|
386
|
+
Neo.setupClass(MainView);
|
387
|
+
</pre>
|
388
|
+
|
389
|
+
## Class based Models
|
390
|
+
When your models contain many data props or need custom logic, you can easily move them into their own classes.
|
391
|
+
|
392
|
+
### Direct Bindings
|
393
|
+
<pre data-neo>
|
394
|
+
import Button from '../button/Base.mjs';
|
395
|
+
import Container from '../container/Base.mjs';
|
396
|
+
import Label from '../component/Label.mjs';
|
397
|
+
import ViewModel from '../model/Component.mjs';
|
398
|
+
|
399
|
+
class MainViewModel extends ViewModel {
|
400
|
+
static config = {
|
401
|
+
className: 'Example.view.MainViewModel',
|
402
|
+
data: {
|
403
|
+
hello: 'Hello',
|
404
|
+
world: 'world!'
|
405
|
+
}
|
406
|
+
}
|
407
|
+
onDataPropertyChange(key, value, oldValue) {
|
408
|
+
super.onDataPropertyChange(key, value, oldValue);
|
409
|
+
// do custom things there, like firing events
|
410
|
+
Neo.Main.log({value: `onDataPropertyChange: key: ${key}, value: ${value}, oldValue: ${oldValue}`})
|
411
|
+
}
|
412
|
+
}
|
413
|
+
|
414
|
+
class MainView extends Container {
|
415
|
+
static config = {
|
416
|
+
className: 'Example.view.MainView',
|
417
|
+
model : MainViewModel, // directly assign the imported module
|
418
|
+
|
419
|
+
itemDefaults: {
|
420
|
+
module: Label,
|
421
|
+
style : {margin: '1em'}
|
422
|
+
},
|
423
|
+
items: [{
|
424
|
+
bind: {
|
425
|
+
text: data => data.hello
|
426
|
+
}
|
427
|
+
}, {
|
428
|
+
bind: {
|
429
|
+
text: data => data.world
|
430
|
+
}
|
431
|
+
}, {
|
432
|
+
module : Button,
|
433
|
+
handler: data => data.component.getModel().setData({hello: 'Hi'}),
|
434
|
+
text : 'Change Hello'
|
435
|
+
}, {
|
436
|
+
module : Button,
|
437
|
+
handler: data => data.component.getModel().setData({world: 'Neo.mjs!'}),
|
438
|
+
text : 'Change World'
|
439
|
+
}],
|
440
|
+
layout: {ntype: 'vbox', align: 'start'}
|
441
|
+
}
|
442
|
+
}
|
443
|
+
Neo.setupClass(MainView);
|
444
|
+
</pre>
|
@@ -11,9 +11,9 @@ Here's an example with one button. Clicking on the button will disable it.
|
|
11
11
|
As you can see, the handler uses the component reference pass in via `data.component`.
|
12
12
|
|
13
13
|
<pre data-neo>
|
14
|
-
import Button from '
|
15
|
-
import Container from '
|
16
|
-
import Controller from '
|
14
|
+
import Button from '../button/Base.mjs';
|
15
|
+
import Container from '../container/Base.mjs';
|
16
|
+
import Controller from '../controller/Component.mjs';
|
17
17
|
|
18
18
|
class MainViewController extends Controller {
|
19
19
|
static config = {
|
@@ -48,9 +48,9 @@ you tag the component you need with a `reference` config, then use `getReference
|
|
48
48
|
the controller.
|
49
49
|
|
50
50
|
<pre data-neo>
|
51
|
-
import Button from '
|
52
|
-
import Container from '
|
53
|
-
import Controller from '
|
51
|
+
import Button from '../button/Base.mjs';
|
52
|
+
import Container from '../container/Base.mjs';
|
53
|
+
import Controller from '../controller/Component.mjs';
|
54
54
|
|
55
55
|
class MainViewController extends Controller {
|
56
56
|
static config = {
|
@@ -103,8 +103,8 @@ The following example gets a reference to the _Learn_ button at the top of this
|
|
103
103
|
Again — that use of `Neo.findFirst()` might be handy when debugging, but it should never be used in app logic.
|
104
104
|
|
105
105
|
<pre data-neo>
|
106
|
-
import Button from '
|
107
|
-
import Container from '
|
106
|
+
import Button from '../button/Base.mjs';
|
107
|
+
import Container from '../container/Base.mjs';
|
108
108
|
|
109
109
|
class MainView extends Container {
|
110
110
|
static config = {
|
@@ -4,15 +4,16 @@ To code a live preview, enclose the code in a `pre` tag with the `data-neo` attr
|
|
4
4
|
|
5
5
|
Imports are relative to the portal app running within the framework. That means
|
6
6
|
Neo.mjs imports should be coded to go up four levels, then look into the `src`
|
7
|
-
directory. For example, to import _container_, use `import Base from '
|
7
|
+
directory. For example, to import _container_, use `import Base from '../container/Base.mjs`
|
8
8
|
|
9
9
|
You can define as many classes you need, such as component models and controllers, but the the _last_
|
10
10
|
class being defined is assumed to be the view being rendered. In other words, if the final class definition is a component, it's rendered.
|
11
11
|
|
12
12
|
<pre data-neo>
|
13
|
-
import Base
|
14
|
-
import Button from '
|
15
|
-
import Split
|
13
|
+
import Base from '../container/Base.mjs';
|
14
|
+
import Button from '../button/Base.mjs';
|
15
|
+
import Split from '../button/Split.mjs';
|
16
|
+
|
16
17
|
class Bar extends Base {
|
17
18
|
static config = {
|
18
19
|
ntype: 'demoFoo',
|
@@ -4,9 +4,9 @@ In case you did not work with neo yet, but come from a more HTML driven ecosyste
|
|
4
4
|
you could achieve the task in a similar way.
|
5
5
|
|
6
6
|
<pre data-neo>
|
7
|
-
import Component from '
|
8
|
-
import NeoArray from '
|
9
|
-
import VdomUtil from '
|
7
|
+
import Component from '../component/Base.mjs';
|
8
|
+
import NeoArray from '../util/Array.mjs';
|
9
|
+
import VdomUtil from '../util/VDom.mjs';
|
10
10
|
|
11
11
|
class MainComponent extends Component {
|
12
12
|
static config = {
|
@@ -117,12 +117,12 @@ Neo.setupClass(MainComponent);
|
|
117
117
|
content
|
118
118
|
|
119
119
|
<pre data-neo>
|
120
|
-
import Container from '
|
121
|
-
import List from '
|
122
|
-
import Model from '
|
123
|
-
import Store from '
|
124
|
-
import TextField from '
|
125
|
-
import Toolbar from '
|
120
|
+
import Container from '../container/Base.mjs';
|
121
|
+
import List from '../list/Base.mjs';
|
122
|
+
import Model from '../data/Model.mjs';
|
123
|
+
import Store from '../data/Store.mjs';
|
124
|
+
import TextField from '../form/field/Text.mjs';
|
125
|
+
import Toolbar from '../toolbar/Base.mjs';
|
126
126
|
|
127
127
|
class TodoListModel extends Model {
|
128
128
|
static config = {
|
@@ -50,8 +50,8 @@ icon on the right <span class="far fa-xs fa-window-maximize"></span>. This web
|
|
50
50
|
and the ability to launch browser windows — all integrated within a single app — is a unique feature of Neo.mjs!
|
51
51
|
|
52
52
|
<pre data-neo>
|
53
|
-
import Button from '
|
54
|
-
import Container from '
|
53
|
+
import Button from '../button/Base.mjs';
|
54
|
+
import Container from '../container/Base.mjs';
|
55
55
|
|
56
56
|
class MainView extends Container {
|
57
57
|
static config = {
|
@@ -65,4 +65,4 @@ class MainView extends Container {
|
|
65
65
|
}
|
66
66
|
|
67
67
|
Neo.setupClass(MainView);
|
68
|
-
</pre>
|
68
|
+
</pre>
|
@@ -15,8 +15,8 @@ running the code. Even though it's running in a new window, it's still part of t
|
|
15
15
|
seamlessly share events, data, etc. — the code doesn't care that some code is running in a
|
16
16
|
separate window.
|
17
17
|
<pre data-neo>
|
18
|
-
import Button from '
|
19
|
-
import Container from '
|
18
|
+
import Button from '../button/Base.mjs';
|
19
|
+
import Container from '../container/Base.mjs';
|
20
20
|
|
21
21
|
class MainView extends Container {
|
22
22
|
static config = {
|
@@ -21,8 +21,8 @@ If you move quickly, you might reach 20,000 or 30,000 delta updates per second.
|
|
21
21
|
second — but we've never actually hit the limit.
|
22
22
|
|
23
23
|
<pre data-neo>
|
24
|
-
import Base from '
|
25
|
-
import Helix from '
|
24
|
+
import Base from '../container/Base.mjs';
|
25
|
+
import Helix from '../component/Helix.mjs';
|
26
26
|
class Foo extends Base {
|
27
27
|
static config = {
|
28
28
|
className: 'Foo',
|
@@ -26,6 +26,7 @@
|
|
26
26
|
{"name": "User Input (Forms)", "parentId": "InDepth", "isLeaf": false, "id": "Forms"},
|
27
27
|
{"name": "Component and Container Basics", "parentId": "InDepth", "isLeaf": true, "id": "ComponentsAndContainers"},
|
28
28
|
{"name": "Layouts", "parentId": "InDepth", "isLeaf": false, "id": "Layouts"},
|
29
|
+
{"name": "View Models", "parentId": "InDepth", "isLeaf": true, "id": "GuideViewModels"},
|
29
30
|
{"name": "Custom Components", "parentId": "InDepth", "isLeaf": true, "id": "CustomComponents"},
|
30
31
|
{"name": "Events", "parentId": "InDepth", "isLeaf": true, "expanded": false, "id": "GuideEvents"},
|
31
32
|
{"name": "Tables (Stores)", "parentId": "InDepth", "isLeaf": false, "id": "Tables"},
|
@@ -59,7 +59,7 @@
|
|
59
59
|
|
60
60
|
|
61
61
|
/* Style and position the progress bar */
|
62
|
-
|
62
|
+
.portal-home-progress {
|
63
63
|
position: fixed;
|
64
64
|
z-index: 1000;
|
65
65
|
top: 0;
|
@@ -82,52 +82,6 @@
|
|
82
82
|
}
|
83
83
|
}
|
84
84
|
|
85
|
-
.hello-world {
|
86
|
-
animation-timeline: view(block 100% -500%);
|
87
|
-
|
88
|
-
animation-name: appear-animation;
|
89
|
-
animation-fill-mode: both;
|
90
|
-
animation-duration: 1ms; /* Firefox requires this to apply the animation */
|
91
|
-
animation-timing-function: linear;
|
92
|
-
}
|
93
|
-
|
94
|
-
@keyframes appear-animation {
|
95
|
-
0% {
|
96
|
-
transform: translateX(-400%);
|
97
|
-
opacity : 0;
|
98
|
-
}
|
99
|
-
90% {
|
100
|
-
opacity: .3;
|
101
|
-
}
|
102
|
-
100% {
|
103
|
-
transform: translateX(0);
|
104
|
-
opacity : 1;
|
105
|
-
}
|
106
|
-
}
|
107
|
-
|
108
|
-
.cool-stuff {
|
109
|
-
animation-timeline : view(block 100% -500%);
|
110
|
-
animation-name : appear-cool-stuff;
|
111
|
-
animation-fill-mode : both;
|
112
|
-
animation-duration : 1ms; /* Firefox requires this to apply the animation */
|
113
|
-
animation-timing-function: linear;
|
114
|
-
}
|
115
|
-
|
116
|
-
@keyframes appear-cool-stuff {
|
117
|
-
0% {
|
118
|
-
transform: translateX(400%);
|
119
|
-
opacity : 0;
|
120
|
-
}
|
121
|
-
90% {
|
122
|
-
opacity: .3;
|
123
|
-
}
|
124
|
-
100% {
|
125
|
-
transform: translateX(0);
|
126
|
-
opacity : 1;
|
127
|
-
}
|
128
|
-
}
|
129
|
-
|
130
|
-
|
131
85
|
//.page {
|
132
86
|
// scroll-timeline: --page block;
|
133
87
|
//}
|
@@ -2,36 +2,4 @@
|
|
2
2
|
overflow-x : hidden!important;
|
3
3
|
scroll-behavior : smooth;
|
4
4
|
scroll-snap-type: y mandatory;
|
5
|
-
|
6
|
-
.page {
|
7
|
-
min-height : 100%;
|
8
|
-
scroll-snap-align: center;
|
9
|
-
|
10
|
-
.neo-h1 {
|
11
|
-
font-size: min(max(5.5vw, 30px), 64px);
|
12
|
-
text-align : center;
|
13
|
-
margin : 0;
|
14
|
-
line-height : 1em;
|
15
|
-
}
|
16
|
-
|
17
|
-
.neo-h2 {
|
18
|
-
font-size: min(max(3.5vw, 24px), 44px);
|
19
|
-
text-align : center;
|
20
|
-
margin : 0;
|
21
|
-
line-height : 1em;
|
22
|
-
}
|
23
|
-
|
24
|
-
.neo-content {
|
25
|
-
font-size: min(max(2.3vw, 16px), 30px);
|
26
|
-
}
|
27
|
-
|
28
|
-
.page-live-preview {
|
29
|
-
margin: 0;
|
30
|
-
}
|
31
|
-
}
|
32
|
-
|
33
|
-
.neo-h1 {
|
34
|
-
font-size : 32px;
|
35
|
-
font-weight: 600;
|
36
|
-
}
|
37
5
|
}
|