neo.mjs 4.1.0 → 4.2.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/buildScripts/addConfig.mjs +398 -0
  2. package/buildScripts/createApp.mjs +3 -3
  3. package/buildScripts/createClass.mjs +7 -7
  4. package/examples/grid/covid/GridContainer.mjs +113 -0
  5. package/examples/grid/covid/GridContainerController.mjs +62 -0
  6. package/examples/grid/covid/MainContainer.mjs +36 -0
  7. package/examples/grid/covid/Model.mjs +65 -0
  8. package/examples/grid/covid/Store.mjs +35 -0
  9. package/examples/grid/covid/Util.mjs +165 -0
  10. package/examples/grid/covid/app.mjs +6 -0
  11. package/examples/grid/covid/index.html +11 -0
  12. package/examples/grid/covid/neo-config.json +8 -0
  13. package/package.json +5 -4
  14. package/resources/scss/src/examples/grid/covid/GridContainer.scss +21 -0
  15. package/resources/scss/src/grid/Container.scss +6 -34
  16. package/resources/scss/src/grid/View.scss +73 -1
  17. package/resources/scss/src/grid/header/Button.scss +67 -7
  18. package/resources/scss/src/grid/header/Toolbar.scss +6 -2
  19. package/resources/scss/src/table/View.scss +2 -2
  20. package/resources/scss/theme-dark/grid/Container.scss +17 -0
  21. package/resources/scss/theme-dark/grid/View.scss +29 -0
  22. package/resources/scss/theme-dark/grid/_all.scss +3 -0
  23. package/resources/scss/theme-dark/grid/header/Button.scss +15 -0
  24. package/resources/scss/theme-dark/grid/header/Toolbar.scss +0 -0
  25. package/resources/scss/theme-dark/grid/header/_all.scss +2 -0
  26. package/resources/scss/theme-light/grid/Container.scss +17 -0
  27. package/resources/scss/theme-light/grid/View.scss +29 -0
  28. package/resources/scss/theme-light/grid/_all.scss +3 -0
  29. package/resources/scss/theme-light/grid/header/Button.scss +15 -0
  30. package/resources/scss/theme-light/grid/header/Toolbar.scss +0 -0
  31. package/resources/scss/theme-light/grid/header/_all.scss +2 -0
  32. package/src/grid/Container.mjs +252 -83
  33. package/src/grid/View.mjs +206 -11
  34. package/src/grid/header/Button.mjs +127 -2
  35. package/src/grid/header/Toolbar.mjs +42 -54
  36. package/src/selection/grid/CellColumnModel.mjs +122 -0
  37. package/src/selection/grid/CellColumnRowModel.mjs +122 -0
  38. package/src/selection/grid/CellModel.mjs +184 -0
  39. package/src/selection/grid/CellRowModel.mjs +164 -0
  40. package/src/selection/grid/ColumnModel.mjs +185 -0
  41. package/src/selection/grid/RowModel.mjs +188 -0
  42. package/src/selection/table/RowModel.mjs +26 -32
  43. package/src/table/Container.mjs +9 -13
  44. package/src/table/header/Toolbar.mjs +1 -1
package/src/grid/View.mjs CHANGED
@@ -19,21 +19,216 @@ class View extends Component {
19
19
  /**
20
20
  * @member {Array} cls=['neo-grid-view']
21
21
  */
22
- cls: ['neo-grid-view'],
23
- /**
24
- * @member {Object} _vdom
25
- */
26
- _vdom:
27
- {cn: [
28
- {cls: ['neo-grid-row'], cn: []}
29
- ]}
22
+ cls: ['neo-grid-view']
30
23
  }}
31
24
 
32
25
  /**
33
- * vdom.Helper should not compare ids for table rows by default
34
- * @override
26
+ * @param {Array} inputData
27
+ */
28
+ createViewData(inputData) {
29
+ let me = this,
30
+ amountRows = inputData.length,
31
+ container = Neo.getComponent(me.parentId),
32
+ columns = container.items[0].items,
33
+ colCount = columns.length,
34
+ data = [],
35
+ i = 0,
36
+ vdom = me.vdom,
37
+ cellCls, cellStyle, config, column, dockLeftMargin, dockRightMargin, id, index, j, rendererOutput,
38
+ record, rendererValue, selectedRows, trCls;
39
+
40
+ me.recordVnodeMap = {}; // remove old data
41
+
42
+ // console.log('createViewData', me.id, inputData);
43
+
44
+ if (container.selectionModel?.ntype === 'selection-table-rowmodel') {
45
+ selectedRows = container.selectionModel.items || [];
46
+ }
47
+
48
+ for (; i < amountRows; i++) {
49
+ record = inputData[i];
50
+ id = me.getRowId(record, i);
51
+
52
+ me.recordVnodeMap[id] = i;
53
+
54
+ trCls = me.getTrClass(record, i);
55
+
56
+ if (selectedRows?.includes(id)) {
57
+ trCls.push('neo-selected');
58
+
59
+ Neo.getComponent(me.containerId).fire('select', {
60
+ record
61
+ });
62
+ }
63
+
64
+ data.push({
65
+ id,
66
+ cls : trCls,
67
+ cn : [],
68
+ tabIndex: '-1'
69
+ });
70
+
71
+ dockLeftMargin = 0;
72
+ dockRightMargin = 0;
73
+
74
+ j = 0;
75
+
76
+ for (; j < colCount; j++) {
77
+ column = columns[j];
78
+ rendererValue = record[column.field];
79
+
80
+ if (rendererValue === undefined) {
81
+ rendererValue = '';
82
+ }
83
+
84
+ rendererOutput = column.renderer.call(column.rendererScope || container, {
85
+ field: column.field,
86
+ index: i,
87
+ record,
88
+ value: rendererValue
89
+ });
90
+
91
+ cellCls = rendererOutput?.cls || ['neo-grid-cell'];
92
+
93
+ if (column.align !== 'left') {
94
+ cellCls.push('neo-' + column.align);
95
+ }
96
+
97
+ if (!Neo.isObject(rendererOutput)) {
98
+ rendererOutput = {
99
+ cls : cellCls,
100
+ html: rendererOutput?.toString()
101
+ };
102
+ }
103
+
104
+ cellStyle = rendererOutput.style || {};
105
+
106
+ if (column.width) {
107
+ cellStyle.minWidth = `${column.width}px`;
108
+ }
109
+
110
+ config = {
111
+ id : me.getCellId(record, column.field),
112
+ cls : rendererOutput.cls || ['neo-grid-cell'],
113
+ innerHTML: rendererOutput.html || '',
114
+ style : cellStyle,
115
+ tabIndex : '-1'
116
+ };
117
+
118
+ if (column.dock) {
119
+ config.cls = ['neo-locked', ...config.cls || []];
120
+
121
+ if (column.dock === 'left') {
122
+ config.style.left = dockLeftMargin + 'px';
123
+ dockLeftMargin += column.width;
124
+ }
125
+ }
126
+
127
+ if (column.flex) {
128
+ config.style.width = '100%';
129
+ }
130
+
131
+ data[i].cn.push(config);
132
+ }
133
+
134
+ j = 0;
135
+
136
+ for (; j < colCount; j++) {
137
+ index = colCount - j -1;
138
+ column = columns[index];
139
+
140
+ if (column.dock === 'right') {
141
+ data[i].cn[index].style.right = dockRightMargin + 'px';
142
+ dockRightMargin += (column.width + 1); // todo: borders fix
143
+ }
144
+ }
145
+ }
146
+
147
+ vdom.cn = data;
148
+
149
+ container.dockLeftMargin = dockLeftMargin;
150
+ container.dockRightMargin = dockRightMargin;
151
+
152
+ me.promiseVdomUpdate().then(() => {
153
+ if (selectedRows?.length > 0) {
154
+ // this logic only works for selection.table.RowModel
155
+ Neo.main.DomAccess.scrollToTableRow({id: selectedRows[0]});
156
+ }
157
+ });
158
+ }
159
+
160
+ /**
161
+ * @param {Boolean} updateParentVdom
162
+ * @param {Boolean} silent
163
+ */
164
+ destroy(updateParentVdom, silent) {
165
+ this.store = null;
166
+ super.destroy(updateParentVdom, silent);
167
+ }
168
+
169
+ /**
170
+ * @param {Object} record
171
+ * @param {String} field
172
+ * @returns {String}
173
+ */
174
+ getCellId(record, field) {
175
+ return this.id + '__' + record[this.store.keyProperty] + '__' + field;
176
+ }
177
+
178
+ /**
179
+ * @param {String} rowId
180
+ * @returns {Object}
35
181
  */
36
- syncVdomIds() {}
182
+ getRecordByRowId(rowId) {
183
+ return this.store.getAt(this.recordVnodeMap[rowId]);
184
+ }
185
+
186
+ /**
187
+ * @param {Object} record
188
+ * @param {Number} [index]
189
+ * @returns {String}
190
+ */
191
+ getRowId(record, index) {
192
+ return `${this.id}__tr__${record[this.store.keyProperty]}`;
193
+ }
194
+
195
+ /**
196
+ * Override this method to apply custom CSS rules to grid rows
197
+ * @param {Object} record
198
+ * @param {Number} rowIndex
199
+ * @returns {String[]}
200
+ */
201
+ getTrClass(record, rowIndex) {
202
+ return ['neo-grid-row'];
203
+ }
204
+
205
+ /**
206
+ * Gets triggered after changing the value of a record field.
207
+ * E.g. myRecord.foo = 'bar';
208
+ * @param {Object} opts
209
+ * @param {Object[]} opts.fields Each field object contains the keys: name, oldValue, value
210
+ * @param {Neo.data.Model} opts.model The model instance of the changed record
211
+ * @param {Object} opts.record
212
+ */
213
+ onStoreRecordChange(opts) {
214
+ let me = this,
215
+ deltas = [],
216
+ cellId, cellNode;
217
+
218
+ opts.fields.forEach(field => {
219
+ cellId = me.getCellId(opts.record, field.name);
220
+ cellNode = me.getVdomChild(cellId);
221
+
222
+ cellNode.innerHTML = field.value; // keep the vdom in sync
223
+
224
+ deltas.push({
225
+ id : cellId,
226
+ innerHTML: field.value
227
+ })
228
+ });
229
+
230
+ deltas.length > 0 && Neo.applyDeltas(me.appName, deltas);
231
+ }
37
232
  }
38
233
 
39
234
  Neo.applyClassConfig(View);
@@ -1,10 +1,21 @@
1
1
  import BaseButton from '../../button/Base.mjs';
2
+ import NeoArray from '../../util/Array.mjs';
2
3
 
3
4
  /**
4
5
  * @class Neo.grid.header.Button
5
6
  * @extends Neo.button.Base
6
7
  */
7
8
  class Button extends BaseButton {
9
+ /**
10
+ * Sort direction when clicking on an unsorted button
11
+ * @member {String} defaultSortDirection='ASC'
12
+ */
13
+ defaultSortDirection = 'ASC'
14
+ /**
15
+ * @member {String|null} field=null
16
+ */
17
+ field = null
18
+
8
19
  static getConfig() {return {
9
20
  /**
10
21
  * @member {String} className='Neo.grid.header.Button'
@@ -17,10 +28,124 @@ class Button extends BaseButton {
17
28
  */
18
29
  ntype: 'grid-header-button',
19
30
  /**
20
- * @member {Array} cls=['neo-grid-header-button']
31
+ * @member {String[]} cls=['neo-grid-header-button']
32
+ */
33
+ cls: ['neo-grid-header-button'],
34
+ /**
35
+ * @member {String} iconCls='fa fa-arrow-circle-up'
36
+ */
37
+ iconCls: 'fa fa-arrow-circle-up',
38
+ /**
39
+ * @member {String} iconPosition='right'
40
+ */
41
+ iconPosition: 'right',
42
+ /**
43
+ * 'ASC', 'DESC' or null
44
+ * @member {String|null} isSorted_=null
45
+ * @protected
21
46
  */
22
- cls: ['neo-grid-header-button']
47
+ isSorted_: null
23
48
  }}
49
+
50
+ /**
51
+ * @param {Object} config
52
+ */
53
+ construct(config) {
54
+ super.construct(config);
55
+
56
+ let me = this,
57
+ domListeners = me.domListeners;
58
+
59
+ domListeners.push({
60
+ click: me.onButtonClick,
61
+ scope: me
62
+ })
63
+
64
+ me.domListeners = domListeners;
65
+ }
66
+
67
+ /**
68
+ * Triggered after the isSorted config got changed
69
+ * @param {Boolean} value
70
+ * @param {Boolean} oldValue
71
+ * @protected
72
+ */
73
+ afterSetIsSorted(value, oldValue) {
74
+ let me = this,
75
+ cls = me.cls;
76
+
77
+ switch(value) {
78
+ case null:
79
+ NeoArray.add(cls, 'neo-sort-hidden');
80
+ break;
81
+ case 'ASC':
82
+ NeoArray.remove(cls, 'neo-sort-desc');
83
+ NeoArray.remove(cls, 'neo-sort-hidden');
84
+ NeoArray.add(cls, 'neo-sort-asc');
85
+ break;
86
+ case 'DESC':
87
+ NeoArray.remove(cls, 'neo-sort-asc');
88
+ NeoArray.remove(cls, 'neo-sort-hidden');
89
+ NeoArray.add(cls, 'neo-sort-desc');
90
+ break;
91
+ }
92
+
93
+ me.cls = cls;
94
+
95
+ me.mounted && me.fire('sort', {
96
+ direction: value,
97
+ property : me.field
98
+ });
99
+ }
100
+
101
+ /**
102
+ * @protected
103
+ */
104
+ onButtonClick() {
105
+ let me = this,
106
+ map;
107
+
108
+ if (me.defaultSortDirection === 'DESC') {
109
+ map = {
110
+ ASC : null,
111
+ DESC: 'ASC',
112
+ null: 'DESC'
113
+ };
114
+ } else {
115
+ map = {
116
+ ASC : 'DESC',
117
+ DESC: null,
118
+ null: 'ASC'
119
+ };
120
+ }
121
+
122
+ me.isSorted = map[me.isSorted + ''];
123
+ }
124
+
125
+ /**
126
+ * @protected
127
+ */
128
+ removeSortingCss() {
129
+ let me = this,
130
+ cls = me.cls;
131
+
132
+ NeoArray.add(cls, 'neo-sort-hidden');
133
+
134
+ me.cls = cls;
135
+ me._isSorted = null;
136
+ }
137
+
138
+ /**
139
+ * @param {Object} data
140
+ * @param {String} data.field
141
+ * @param {Number} data.index
142
+ * @param {Object} data.record
143
+ * @param {Number|String} data.value
144
+ * @returns {*}
145
+ */
146
+ renderer(data) {
147
+ return data.value;
148
+ }
24
149
  }
25
150
 
26
151
  Neo.applyClassConfig(Button);
@@ -17,81 +17,69 @@ class Toolbar extends BaseToolbar {
17
17
  */
18
18
  ntype: 'grid-header-toolbar',
19
19
  /**
20
- * @member {Array} cls=['neo-grid-header-toolbar']
20
+ * @member {Array} cls=['neo-grid-header-toolbar','neo-toolbar']
21
21
  */
22
- cls: ['grid-header-toolbar'],
23
- /**
24
- * @member {String} _layout='base'
25
- * @protected
26
- */
27
- _layout : 'base',
22
+ cls: ['neo-grid-header-toolbar', 'neo-toolbar'],
28
23
  /**
29
24
  * @member {Object} itemDefaults={ntype:'grid-header-button'}
30
25
  * @protected
31
26
  */
32
27
  itemDefaults: {
33
28
  ntype: 'grid-header-button'
34
- },
35
- /**
36
- * @member {Object} _vdom
37
- * @protected
38
- */
39
- _vdom:
40
- {cn: [
41
- {cls: ['neo-grid-row'], cn: []}
42
- ]}
29
+ }
43
30
  }}
44
31
 
45
32
  /**
46
- * @param {Array} items
33
+ *
47
34
  */
48
- createItems(items) {
49
- let me = this,
50
- cn = [],
51
- i = 0,
52
- len, vdom;
35
+ createItems() {
36
+ let me = this;
37
+
38
+ me.itemDefaults.showHeaderFilter = me.showHeaderFilters;
53
39
 
54
40
  super.createItems();
55
41
 
56
- items = me.items;
57
- len = items.length;
58
- vdom = me.vdom;
42
+ let dockLeftWidth = 0,
43
+ dockRightWidth = 0,
44
+ items = me.items,
45
+ len = items.length,
46
+ vdom = me.vdom,
47
+ style;
59
48
 
60
- for (; i < len; i++) {
61
- cn.push(items[i]._vdom = {
62
- cls: ['neo-grid-header-cell'],
63
- cn : items[i].vdom
64
- });
49
+ items.forEach((item, index) => {
50
+ style = item.wrapperStyle;
65
51
 
66
- //items[i].vdom.cls = []; // remove the button cls from the th tag
67
- }
52
+ // todo: only add px if number
53
+ if (item.maxWidth) {style.maxWidth = item.maxWidth + 'px'}
54
+ if (item.minWidth) {style.minWidth = item.minWidth + 'px'}
55
+ if (item.width) {style.width = item.width + 'px'}
68
56
 
69
- vdom.cn[0].cn = cn;
57
+ if (item.dock) {
58
+ item.vdom.cls.push('neo-locked');
70
59
 
71
- me.vdom = vdom;
72
- }
60
+ if (item.dock === 'left') {
61
+ style.left = dockLeftWidth + 'px';
62
+ }
73
63
 
74
- /**
75
- * @param dock
76
- * @returns {String} layoutConfig
77
- * @override
78
- */
79
- getLayoutConfig(dock) {
80
- return 'base';
81
- }
64
+ dockLeftWidth += (item.width + 1); // todo: borders fix
65
+ }
82
66
 
83
- /**
84
- * @returns {Object}
85
- */
86
- getVdomRoot() {
87
- return this.vdom.cn[0];
88
- }
67
+ item.wrapperStyle = style;
89
68
 
90
- /**
91
- * @returns {Object}
92
- */
93
- getVnodeRoot() {
94
- return this.vnode.childNodes[0];
69
+ // inverse loop direction
70
+ item = items[len - index -1];
71
+
72
+ if (item.dock === 'right') {
73
+ style = item.wrapperStyle;
74
+ style.right = dockRightWidth + 'px';
75
+
76
+ item.wrapperStyle = style;
77
+
78
+ dockRightWidth += (item.width + 1); // todo: borders fix
79
+ }
80
+ });
81
+
82
+ me.vdom = vdom;
95
83
  }
96
84
  }
97
85
 
@@ -0,0 +1,122 @@
1
+ import CellModel from './CellModel.mjs';
2
+ import ColumnModel from './ColumnModel.mjs';
3
+ import VDomUtil from '../../util/VDom.mjs';
4
+
5
+ /**
6
+ * @class Neo.selection.grid.CellColumnModel
7
+ * @extends Neo.selection.grid.CellModel
8
+ */
9
+ class CellColumnModel extends CellModel {
10
+ static getConfig() {return {
11
+ /**
12
+ * @member {String} className='Neo.selection.grid.CellColumnModel'
13
+ * @protected
14
+ */
15
+ className: 'Neo.selection.grid.CellColumnModel',
16
+ /**
17
+ * @member {String} ntype='selection-grid-cellcolumnmodel'
18
+ * @protected
19
+ */
20
+ ntype: 'selection-grid-cellcolumnmodel',
21
+ /**
22
+ * @member {String} cls='neo-selection-cellcolumnmodel'
23
+ * @protected
24
+ */
25
+ cls: 'neo-selection-cellcolumnmodel',
26
+ /**
27
+ * @member {String} selectedColumnCellCls='selected-column-cell'
28
+ * @protected
29
+ */
30
+ selectedColumnCellCls: 'selected-column-cell',
31
+ /**
32
+ * @member {Array|null} selectedColumnCellIds=null
33
+ * @protected
34
+ */
35
+ selectedColumnCellIds: null
36
+ }}
37
+
38
+ /**
39
+ * @param {Object} config
40
+ */
41
+ construct(config) {
42
+ super.construct(config);
43
+
44
+ this.selectedColumnCellIds = [];
45
+ }
46
+
47
+ /**
48
+ * @param {Boolean} [silent] true to prevent a vdom update
49
+ */
50
+ deselectAllCells(silent) {
51
+ let me = this,
52
+ cellIds = [...me.selectedColumnCellIds],
53
+ view = me.view,
54
+ vdom = view.vdom;
55
+
56
+ cellIds.forEach(cellId => {
57
+ me.deselect(cellId, true, me.selectedColumnCellIds, me.selectedColumnCellCls);
58
+ });
59
+
60
+ if (!silent) {
61
+ view.vdom = vdom;
62
+ }
63
+ }
64
+
65
+ /**
66
+ * @param {Object} data
67
+ */
68
+ onCellClick(data) {
69
+ let me = this,
70
+ id = ColumnModel.getCellId(data.path),
71
+ columnNodeIds, index, tbodyNode;
72
+
73
+ if (id) {
74
+ index = ColumnModel.getColumnIndex(id, me.view.items[0].items);
75
+ tbodyNode = VDomUtil.findVdomChild(me.view.vdom, {tag: 'tbody'}).vdom;
76
+ columnNodeIds = VDomUtil.getColumnNodesIds(tbodyNode, index);
77
+
78
+ me.deselectAllCells(true);
79
+ me.select(columnNodeIds, me.selectedColumnCellIds, me.selectedColumnCellCls);
80
+ }
81
+
82
+ super.onCellClick(data);
83
+ }
84
+
85
+ /**
86
+ * @param {Object} data
87
+ * @param {Number} step
88
+ */
89
+ onNavKeyColumn(data, step) {
90
+ let me = this,
91
+ idArray = ColumnModel.getCellId(data.path).split('__'),
92
+ currentColumn = idArray[2],
93
+ view = me.view,
94
+ dataFields = view.columns.map(c => c.dataField),
95
+ newIndex = (dataFields.indexOf(currentColumn) + step) % dataFields.length,
96
+ columnNodeIds, tbodyNode;
97
+
98
+ while (newIndex < 0) {
99
+ newIndex += dataFields.length;
100
+ }
101
+
102
+ tbodyNode = VDomUtil.findVdomChild(me.view.vdom, {tag: 'tbody'}).vdom;
103
+ columnNodeIds = VDomUtil.getColumnNodesIds(tbodyNode, newIndex);
104
+
105
+ me.deselectAllCells(true);
106
+ me.select(columnNodeIds, me.selectedColumnCellIds, me.selectedColumnCellCls);
107
+
108
+ super.onNavKeyColumn(data, step);
109
+ }
110
+
111
+ /**
112
+ *
113
+ */
114
+ unregister() {
115
+ this.deselectAllCells();
116
+ super.unregister();
117
+ }
118
+ }
119
+
120
+ Neo.applyClassConfig(CellColumnModel);
121
+
122
+ export default CellColumnModel;