neo.mjs 8.7.1 → 8.9.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 (34) hide show
  1. package/apps/ServiceWorker.mjs +2 -2
  2. package/apps/portal/index.html +1 -1
  3. package/apps/portal/view/home/FooterContainer.mjs +1 -1
  4. package/examples/ServiceWorker.mjs +2 -2
  5. package/examples/grid/nestedRecordFields/EditUserDialog.mjs +154 -0
  6. package/examples/grid/nestedRecordFields/MainModel.mjs +54 -0
  7. package/examples/grid/nestedRecordFields/MainStore.mjs +54 -0
  8. package/examples/grid/nestedRecordFields/Viewport.mjs +152 -0
  9. package/examples/grid/nestedRecordFields/ViewportStateProvider.mjs +62 -0
  10. package/examples/grid/nestedRecordFields/app.mjs +6 -0
  11. package/examples/grid/nestedRecordFields/index.html +11 -0
  12. package/examples/grid/nestedRecordFields/neo-config.json +8 -0
  13. package/examples/table/nestedRecordFields/{MainContainer.mjs → Viewport.mjs} +50 -21
  14. package/examples/table/nestedRecordFields/{MainContainerStateProvider.mjs → ViewportStateProvider.mjs} +5 -5
  15. package/examples/table/nestedRecordFields/app.mjs +3 -3
  16. package/examples/table/nestedRecordFields/neo-config.json +2 -1
  17. package/package.json +1 -1
  18. package/resources/scss/src/grid/View.scss +16 -0
  19. package/resources/scss/src/grid/header/Button.scss +3 -1
  20. package/resources/scss/src/table/View.scss +2 -2
  21. package/resources/scss/src/table/header/Button.scss +1 -2
  22. package/resources/scss/theme-dark/grid/View.scss +2 -0
  23. package/resources/scss/theme-dark/table/View.scss +2 -0
  24. package/resources/scss/theme-light/grid/View.scss +2 -0
  25. package/resources/scss/theme-light/table/View.scss +2 -0
  26. package/resources/scss/theme-neo-light/grid/View.scss +2 -0
  27. package/resources/scss/theme-neo-light/table/View.scss +2 -0
  28. package/src/DefaultConfig.mjs +2 -2
  29. package/src/Neo.mjs +48 -19
  30. package/src/component/Base.mjs +16 -4
  31. package/src/container/Base.mjs +16 -0
  32. package/src/data/RecordFactory.mjs +41 -48
  33. package/src/grid/View.mjs +23 -5
  34. package/src/table/View.mjs +1 -1
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='8.7.1'
23
+ * @member {String} version='8.9.0'
24
24
  */
25
- version: '8.7.1'
25
+ version: '8.9.0'
26
26
  }
27
27
 
28
28
  /**
@@ -16,7 +16,7 @@
16
16
  "@type": "Organization",
17
17
  "name": "Neo.mjs"
18
18
  },
19
- "datePublished": "2025-01-23",
19
+ "datePublished": "2025-01-25",
20
20
  "publisher": {
21
21
  "@type": "Organization",
22
22
  "name": "Neo.mjs"
@@ -107,7 +107,7 @@ class FooterContainer extends Container {
107
107
  }, {
108
108
  module: Component,
109
109
  cls : ['neo-version'],
110
- html : 'v8.7.1'
110
+ html : 'v8.9.0'
111
111
  }]
112
112
  }],
113
113
  /**
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='8.7.1'
23
+ * @member {String} version='8.9.0'
24
24
  */
25
- version: '8.7.1'
25
+ version: '8.9.0'
26
26
  }
27
27
 
28
28
  /**
@@ -0,0 +1,154 @@
1
+ import CheckBox from '../../../src/form/field/CheckBox.mjs';
2
+ import CountryField from '../../../src/form/field/Country.mjs';
3
+ import Dialog from '../../../src/dialog/Base.mjs';
4
+ import TextField from '../../../src/form/field/Text.mjs';
5
+
6
+ /**
7
+ * @class Neo.examples.grid.nestedRecordFields.EditUserDialog
8
+ * @extends Neo.dialog.Base
9
+ */
10
+ class EditUserDialog extends Dialog {
11
+ static config = {
12
+ /**
13
+ * @member {String} className='Neo.examples.grid.nestedRecordFields.EditUserDialog'
14
+ * @protected
15
+ */
16
+ className: 'Neo.examples.grid.nestedRecordFields.EditUserDialog',
17
+ /**
18
+ * @member {String} closeAction='hide'
19
+ */
20
+ closeAction: 'hide',
21
+ /**
22
+ * @member {Object} containerConfig={style:{padding:'1em'}}
23
+ */
24
+ containerConfig: {
25
+ style: {
26
+ padding: '1em'
27
+ }
28
+ },
29
+ /**
30
+ * @member {Boolean} modal=true
31
+ */
32
+ modal: true,
33
+ /**
34
+ * @member {Record|null} record_=null
35
+ */
36
+ record_: null,
37
+ /**
38
+ * @member {String} title='Edit User'
39
+ */
40
+ title: 'Edit User',
41
+ /**
42
+ * @member {Object} itemDefaults
43
+ */
44
+ itemDefaults: {
45
+ flex : 'none',
46
+ labelWidth: 110
47
+ },
48
+ /**
49
+ * @member {Object[]} items
50
+ */
51
+ items: [{
52
+ module : TextField,
53
+ labelText: 'Firstname',
54
+ listeners: {change: 'up.onFirstnameFieldChange'},
55
+ reference: 'firstname-field'
56
+ }, {
57
+ module : TextField,
58
+ labelText: 'Lastname',
59
+ listeners: {change: 'up.onLastnameFieldChange'},
60
+ reference: 'lastname-field'
61
+ }, {
62
+ module : CountryField,
63
+ bind : {store: 'stores.countries'},
64
+ labelText : 'Country',
65
+ listeners : {change: 'up.onCountryFieldChange'},
66
+ reference : 'country-field',
67
+ valueField: 'code'
68
+ }, {
69
+ module : CheckBox,
70
+ labelText: 'Selected',
71
+ listeners: {change: 'up.onSelectedFieldChange'},
72
+ reference: 'selected-field',
73
+ style : {marginTop: '1em'}
74
+ }]
75
+ }
76
+
77
+ /**
78
+ * Triggered after the record config got changed
79
+ * @param {Record|null} value
80
+ * @param {Record|null} oldValue
81
+ * @protected
82
+ */
83
+ async afterSetRecord(value, oldValue) {
84
+ if (value) {
85
+ let me = this,
86
+ {record} = me;
87
+
88
+ // ensure the store has its data
89
+ await me.timeout(20);
90
+
91
+ me.getItem('country-field') .value = record.country;
92
+ me.getItem('firstname-field').value = record['user.firstname'];
93
+ me.getItem('lastname-field') .value = record['user.lastname'];
94
+ me.getItem('selected-field') .checked = record['annotations.selected'];
95
+ }
96
+ }
97
+
98
+ /**
99
+ * @param {Object} data
100
+ */
101
+ onCountryFieldChange(data) {
102
+ // You can also access the internal setter directly:
103
+ // this.record.country = data.value.code
104
+ // Using the API allows bulk changes
105
+
106
+ this.record.set({country: data.value.code})
107
+ }
108
+
109
+ /**
110
+ * @param {Object} data
111
+ */
112
+ onFirstnameFieldChange(data) {
113
+ // You can also access the internal setter directly:
114
+ // this.record['user.firstname'] = data.value
115
+ // Using the API allows bulk changes
116
+ this.record.set({user: {firstname: data.value}})
117
+
118
+
119
+ }
120
+
121
+ /**
122
+ * @param {Object} data
123
+ */
124
+ onLastnameFieldChange(data) {
125
+ // You can also access the internal setter directly:
126
+ // this.record['user.lastname'] = data.value
127
+ // Using the API allows bulk changes
128
+ this.record.set({user: {lastname: data.value}})
129
+ }
130
+
131
+ /**
132
+ * @param {Object} data
133
+ */
134
+ onSelectedFieldChange(data) {
135
+ let me = this,
136
+ store = me.getStateProvider().getStore('mainStore');
137
+
138
+ if (data.value === false) {
139
+ // You can also access the internal setter directly:
140
+ // me.record['annotations.selected'] = false
141
+ // Using the API allows bulk changes
142
+ me.record.set({annotations: {selected: false}})
143
+ } else {
144
+ // Assuming we want to support a single row selection
145
+ store.items.forEach(record => {
146
+ record.set({annotations: {
147
+ selected: record === me.record ? data.value : false
148
+ }})
149
+ })
150
+ }
151
+ }
152
+ }
153
+
154
+ export default Neo.setupClass(EditUserDialog);
@@ -0,0 +1,54 @@
1
+ import Model from '../../../src/data/Model.mjs';
2
+
3
+ /**
4
+ * @class Neo.examples.grid.nestedRecordFields.MainModel
5
+ * @extends Neo.data.Model
6
+ */
7
+ class MainModel extends Model {
8
+ static config = {
9
+ /**
10
+ * @member {String} className='Neo.examples.grid.nestedRecordFields.MainModel'
11
+ * @protected
12
+ */
13
+ className: 'Neo.examples.grid.nestedRecordFields.MainModel',
14
+ /**
15
+ * @member {Object[]} fields
16
+ */
17
+ fields: [{
18
+ name: 'annotations',
19
+ type: 'Object',
20
+
21
+ fields: [{
22
+ name : 'selected',
23
+ type : 'Boolean',
24
+ defaultValue: false
25
+ }]
26
+ }, {
27
+ name: 'country',
28
+ type: 'String'
29
+ }, {
30
+ name: 'edit',
31
+ type: 'String'
32
+ }, {
33
+ name: 'githubId',
34
+ type: 'String'
35
+ }, {
36
+ name: 'user',
37
+ type: 'Object',
38
+
39
+ fields: [{
40
+ name: 'firstname',
41
+ type: 'String'
42
+ }, {
43
+ name: 'lastname',
44
+ type: 'String'
45
+ }]
46
+ }],
47
+ /**
48
+ * @member {Boolean} trackModifiedFields=true
49
+ */
50
+ trackModifiedFields: true
51
+ }
52
+ }
53
+
54
+ export default Neo.setupClass(MainModel);
@@ -0,0 +1,54 @@
1
+ import Model from './MainModel.mjs';
2
+ import Store from '../../../src/data/Store.mjs';
3
+
4
+ /**
5
+ * @class Neo.examples.grid.nestedRecordFields.MainStore
6
+ * @extends Neo.data.Store
7
+ */
8
+ class MainStore extends Store {
9
+ static config = {
10
+ className : 'Neo.examples.grid.nestedRecordFields.MainStore',
11
+ keyProperty: 'githubId',
12
+ model : Model,
13
+
14
+ data: [{
15
+ country : 'DE',
16
+ githubId: 'tobiu',
17
+
18
+ user: {
19
+ firstname: 'Tobias',
20
+ lastname : 'Uhlig'
21
+ }
22
+ }, {
23
+ annotations: {
24
+ selected: true
25
+ },
26
+
27
+ country : 'US',
28
+ githubId: 'rwaters',
29
+
30
+ user: {
31
+ firstname: 'Rich',
32
+ lastname : 'Waters'
33
+ }
34
+ }, {
35
+ country : 'DE',
36
+ githubId: 'mrsunshine',
37
+
38
+ user: {
39
+ firstname: 'Nils',
40
+ lastname : 'Dehl'
41
+ }
42
+ }, {
43
+ country : 'US',
44
+ githubId: 'camtnbikerrwc',
45
+
46
+ user: {
47
+ firstname: 'Gerard',
48
+ lastname : 'Horan'
49
+ }
50
+ }]
51
+ }
52
+ }
53
+
54
+ export default Neo.setupClass(MainStore);
@@ -0,0 +1,152 @@
1
+ import BaseViewport from '../../../src/container/Viewport.mjs';
2
+ import Button from '../../../src/button/Base.mjs';
3
+ import GridContainer from '../../../src/grid/Container.mjs';
4
+ import ViewportStateProvider from './ViewportStateProvider.mjs';
5
+
6
+ /**
7
+ * @class Neo.examples.grid.nestedRecordFields.Viewport
8
+ * @extends Neo.container.Viewport
9
+ */
10
+ class Viewport extends BaseViewport {
11
+ static config = {
12
+ /**
13
+ * @member {String} className='Neo.examples.grid.nestedRecordFields.Viewport'
14
+ * @protected
15
+ */
16
+ className: 'Neo.examples.grid.nestedRecordFields.Viewport',
17
+ /**
18
+ * @member {Neo.state.Provider} stateProvider=ViewportStateProvider
19
+ */
20
+ stateProvider: ViewportStateProvider,
21
+ /**
22
+ * @member {Object} style={padding:'1em'}
23
+ */
24
+ style: {padding: '1em'},
25
+ /**
26
+ * @member {Object[]} items
27
+ */
28
+ items: [{
29
+ ntype : 'toolbar',
30
+ flex : 'none',
31
+ reference: 'switch-theme-button',
32
+ style : {marginBottom: '1em'},
33
+
34
+ items: ['->', {
35
+ handler: 'up.onSwitchThemeButtonClick',
36
+ iconCls: 'fas fa-sun',
37
+ text : 'Light Theme'
38
+ }]
39
+ }, {
40
+ module: GridContainer,
41
+ bind : {store : 'stores.mainStore'},
42
+
43
+ columnDefaults: {
44
+ width: 200
45
+ },
46
+
47
+ columns: [
48
+ {dataField: 'user.firstname', text: 'Firstname'},
49
+ {dataField: 'user.lastname', text: 'Lastname'},
50
+ {dataField: 'githubId', text: 'Github Id'},
51
+ {dataField: 'country', text: 'Country', renderer: 'up.countryRenderer'},
52
+ {dataField: 'edit', text: 'Edit Action', renderer: 'up.editRenderer'}
53
+ ],
54
+
55
+ viewConfig: {
56
+ highlightModifiedCells: true
57
+ }
58
+ }]
59
+ }
60
+
61
+ /**
62
+ * @member {Neo.dialog.Base|null} dialog=null
63
+ */
64
+ dialog = null
65
+
66
+ /**
67
+ * @param {Object} data
68
+ */
69
+ countryRenderer({record}) {
70
+ let countryStore = this.getStateProvider().getStore('countries');
71
+
72
+ if (countryStore.getCount() > 0) {
73
+ return countryStore.get(record.country).name
74
+ }
75
+
76
+ return ''
77
+ }
78
+
79
+ /**
80
+ * @param {Object} data
81
+ */
82
+ editButtonHandler(data) {
83
+ let me = this,
84
+ button = data.component,
85
+ {appName, dialog, theme, windowId} = me,
86
+ {record} = button;
87
+
88
+ if (!dialog) {
89
+ import('./EditUserDialog.mjs').then(module => {
90
+ me.dialog = Neo.create({
91
+ module : module.default,
92
+ animateTargetId: button.id,
93
+ appName,
94
+ stateProvider : {parent: me.getStateProvider()},
95
+ record,
96
+ theme,
97
+ windowId
98
+ })
99
+ })
100
+ } else {
101
+ dialog.animateTargetId = button.id;
102
+ dialog.record = record;
103
+
104
+ dialog.show()
105
+ }
106
+ }
107
+
108
+ /**
109
+ * @param {Object} data
110
+ */
111
+ editRenderer({column, gridContainer, index, record}) {
112
+ let me = this,
113
+ {appName, windowId} = me,
114
+ widgetId = `${column.id}-widget-${index}`,
115
+ button = (column.widgetMap || (column.widgetMap = {}))[widgetId] || (column.widgetMap[widgetId] = Neo.create({
116
+ module : Button,
117
+ appName,
118
+ handler : 'up.editButtonHandler',
119
+ parentId: gridContainer.id,
120
+ record,
121
+ text : 'Edit',
122
+ windowId
123
+ }));
124
+
125
+ me.view.updateDepth = -1;
126
+
127
+ return button.createVdomReference()
128
+ }
129
+
130
+ /**
131
+ * @param {Object} data
132
+ */
133
+ onSwitchThemeButtonClick(data) {
134
+ let me = this,
135
+ button = data.component,
136
+ isDarkTheme = me.theme !== 'neo-theme-light',
137
+ theme = isDarkTheme ? 'neo-theme-light' : 'neo-theme-dark';
138
+
139
+ button.set({
140
+ iconCls: isDarkTheme ? 'fa fa-moon' : 'fa fa-sun',
141
+ text : isDarkTheme ? 'Dark Theme' : 'Light Theme'
142
+ });
143
+
144
+ me.theme = theme;
145
+
146
+ if (me.dialog) {
147
+ me.dialog.theme = theme
148
+ }
149
+ }
150
+ }
151
+
152
+ export default Neo.setupClass(Viewport);
@@ -0,0 +1,62 @@
1
+ import MainStore from './MainStore.mjs';
2
+ import StateProvider from '../../../src/state/Provider.mjs';
3
+ import Store from '../../../src/data/Store.mjs';
4
+
5
+ const dataSymbol = Symbol.for('data');
6
+
7
+ /**
8
+ * @class Neo.examples.grid.nestedRecordFields.ViewportStateProvider
9
+ * @extends Neo.state.Provider
10
+ */
11
+ class ViewportStateProvider extends StateProvider {
12
+ static config = {
13
+ /**
14
+ * @member {String} className='Neo.examples.grid.nestedRecordFields.ViewportStateProvider'
15
+ * @protected
16
+ */
17
+ className: 'Neo.examples.grid.nestedRecordFields.ViewportStateProvider',
18
+ /**
19
+ * @member {Object} stores
20
+ */
21
+ stores: {
22
+ countries: {
23
+ module : Store,
24
+ autoLoad : true,
25
+ keyProperty: 'code',
26
+ listeners : {load: 'onCountryStoreLoad'},
27
+ url : '../../../resources/examples/data/countries.json',
28
+
29
+ model: {
30
+ fields: [
31
+ {name: 'code'},
32
+ {name: 'name'}
33
+ ]
34
+ }
35
+ },
36
+ mainStore: MainStore
37
+ }
38
+ }
39
+
40
+ /**
41
+ * @param {Record[]} items
42
+ */
43
+ onCountryStoreLoad(items) {
44
+ let me = this,
45
+ mainStore = me.getStore('mainStore'),
46
+ country;
47
+
48
+ // if the main table store is already loaded, the country field renderer had no data
49
+ if (mainStore.getCount() > 0) {
50
+ mainStore.items.forEach(record => {
51
+ country = record.country;
52
+
53
+ // hack resetting the current value to get a new record change
54
+ record[dataSymbol].country = null;
55
+
56
+ record.country = country
57
+ })
58
+ }
59
+ }
60
+ }
61
+
62
+ export default Neo.setupClass(ViewportStateProvider);
@@ -0,0 +1,6 @@
1
+ import Viewport from './Viewport.mjs';
2
+
3
+ export const onStart = () => Neo.app({
4
+ mainView: Viewport,
5
+ name : 'Neo.examples.grid.nestedRecordFields'
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>Neo Grid: Nested Record Fields</title>
7
+ </head>
8
+ <body>
9
+ <script src="../../../src/MicroLoader.mjs" type="module"></script>
10
+ </body>
11
+ </html>
@@ -0,0 +1,8 @@
1
+ {
2
+ "appPath" : "examples/grid/nestedRecordFields/app.mjs",
3
+ "basePath" : "../../../",
4
+ "environment" : "development",
5
+ "mainPath" : "./Main.mjs",
6
+ "mainThreadAddons": ["DragDrop", "Navigator", "ResizeObserver", "Stylesheet"],
7
+ "themes" : ["neo-theme-dark", "neo-theme-light"]
8
+ }
@@ -1,35 +1,42 @@
1
- import Button from '../../../src/button/Base.mjs';
2
- import MainContainerStateProvider from './MainContainerStateProvider.mjs';
3
- import TableContainer from '../../../src/table/Container.mjs';
4
- import Viewport from '../../../src/container/Viewport.mjs';
1
+ import BaseViewport from '../../../src/container/Viewport.mjs';
2
+ import Button from '../../../src/button/Base.mjs';
3
+ import TableContainer from '../../../src/table/Container.mjs';
4
+ import ViewportStateProvider from './ViewportStateProvider.mjs';
5
5
 
6
6
  /**
7
- * @class Neo.examples.table.nestedRecordFields.MainContainer
7
+ * @class Neo.examples.table.nestedRecordFields.Viewport
8
8
  * @extends Neo.container.Viewport
9
9
  */
10
- class MainContainer extends Viewport {
10
+ class Viewport extends BaseViewport {
11
11
  static config = {
12
12
  /**
13
- * @member {String} className='Neo.examples.table.nestedRecordFields.MainContainer'
13
+ * @member {String} className='Neo.examples.table.nestedRecordFields.Viewport'
14
14
  * @protected
15
15
  */
16
- className: 'Neo.examples.table.nestedRecordFields.MainContainer',
16
+ className: 'Neo.examples.table.nestedRecordFields.Viewport',
17
17
  /**
18
- * @member {Object|String} layout='fit'
18
+ * @member {Neo.state.Provider} stateProvider=ViewportStateProvider
19
19
  */
20
- layout: 'fit',
20
+ stateProvider: ViewportStateProvider,
21
21
  /**
22
- * @member {Neo.state.Provider} stateProvider=MainContainerStateProvider
22
+ * @member {Object} style={padding:'1em'}
23
23
  */
24
- stateProvider: MainContainerStateProvider,
25
- /**
26
- * @member {Object} style={padding:'20px'}
27
- */
28
- style: {padding: '20px'},
24
+ style: {padding: '1em'},
29
25
  /**
30
26
  * @member {Object[]} items
31
27
  */
32
28
  items: [{
29
+ ntype : 'toolbar',
30
+ flex : 'none',
31
+ reference: 'switch-theme-button',
32
+ style : {marginBottom: '1em'},
33
+
34
+ items: ['->', {
35
+ handler: 'up.onSwitchThemeButtonClick',
36
+ iconCls: 'fas fa-sun',
37
+ text : 'Light Theme'
38
+ }]
39
+ }, {
33
40
  module: TableContainer,
34
41
  bind : {store : 'stores.mainStore'},
35
42
 
@@ -69,10 +76,10 @@ class MainContainer extends Viewport {
69
76
  * @param {Object} data
70
77
  */
71
78
  editButtonHandler(data) {
72
- let me = this,
73
- button = data.component,
74
- {appName, dialog, windowId} = me,
75
- {record} = button;
79
+ let me = this,
80
+ button = data.component,
81
+ {appName, dialog, theme, windowId} = me,
82
+ {record} = button;
76
83
 
77
84
  if (!dialog) {
78
85
  import('./EditUserDialog.mjs').then(module => {
@@ -82,6 +89,7 @@ class MainContainer extends Viewport {
82
89
  appName,
83
90
  stateProvider : {parent: me.getStateProvider()},
84
91
  record,
92
+ theme,
85
93
  windowId
86
94
  })
87
95
  })
@@ -114,6 +122,27 @@ class MainContainer extends Viewport {
114
122
 
115
123
  return button.createVdomReference()
116
124
  }
125
+
126
+ /**
127
+ * @param {Object} data
128
+ */
129
+ onSwitchThemeButtonClick(data) {
130
+ let me = this,
131
+ button = data.component,
132
+ isDarkTheme = me.theme !== 'neo-theme-light',
133
+ theme = isDarkTheme ? 'neo-theme-light' : 'neo-theme-dark';
134
+
135
+ button.set({
136
+ iconCls: isDarkTheme ? 'fa fa-moon' : 'fa fa-sun',
137
+ text : isDarkTheme ? 'Dark Theme' : 'Light Theme'
138
+ });
139
+
140
+ me.theme = theme;
141
+
142
+ if (me.dialog) {
143
+ me.dialog.theme = theme
144
+ }
145
+ }
117
146
  }
118
147
 
119
- export default Neo.setupClass(MainContainer);
148
+ export default Neo.setupClass(Viewport);