neo.mjs 8.0.0 → 8.1.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 (89) hide show
  1. package/apps/ServiceWorker.mjs +2 -2
  2. package/apps/portal/index.html +1 -1
  3. package/apps/portal/view/about/Container.mjs +0 -2
  4. package/apps/portal/view/about/MemberContainer.mjs +1 -20
  5. package/apps/portal/view/home/FooterContainer.mjs +1 -5
  6. package/examples/ConfigurationViewport.mjs +37 -32
  7. package/examples/ServiceWorker.mjs +2 -2
  8. package/examples/grid/cellEditing/MainContainer.mjs +175 -0
  9. package/examples/grid/cellEditing/MainContainerStateProvider.mjs +62 -0
  10. package/examples/grid/cellEditing/MainModel.mjs +30 -0
  11. package/examples/grid/cellEditing/MainStore.mjs +54 -0
  12. package/examples/grid/cellEditing/app.mjs +6 -0
  13. package/examples/grid/cellEditing/index.html +11 -0
  14. package/examples/grid/cellEditing/neo-config.json +6 -0
  15. package/examples/grid/container/MainContainer.mjs +7 -6
  16. package/examples/grid/covid/GridContainer.mjs +36 -36
  17. package/examples/grid/covid/Util.mjs +1 -1
  18. package/examples/grid/covid/neo-config.json +6 -5
  19. package/examples/table/cellEditing/MainContainer.mjs +174 -0
  20. package/examples/table/cellEditing/MainContainerStateProvider.mjs +62 -0
  21. package/examples/table/cellEditing/MainModel.mjs +30 -0
  22. package/examples/table/cellEditing/MainStore.mjs +54 -0
  23. package/examples/table/cellEditing/app.mjs +6 -0
  24. package/examples/table/cellEditing/index.html +11 -0
  25. package/examples/table/cellEditing/neo-config.json +6 -0
  26. package/examples/table/nestedRecordFields/MainContainerStateProvider.mjs +2 -1
  27. package/package.json +8 -8
  28. package/resources/scss/src/apps/portal/home/FooterContainer.scss +11 -2
  29. package/resources/scss/src/examples/grid/covid/GridContainer.scss +1 -1
  30. package/resources/scss/src/grid/Container.scss +13 -23
  31. package/resources/scss/src/grid/View.scss +45 -19
  32. package/resources/scss/src/grid/header/Button.scss +2 -4
  33. package/resources/scss/src/grid/header/Toolbar.scss +1 -2
  34. package/resources/scss/src/grid/plugin/CellEditing.scss +11 -0
  35. package/resources/scss/src/table/plugin/CellEditing.scss +11 -0
  36. package/src/DefaultConfig.mjs +2 -2
  37. package/src/Xhr.mjs +1 -1
  38. package/src/button/Base.mjs +2 -2
  39. package/src/collection/Base.mjs +5 -5
  40. package/src/component/Base.mjs +3 -3
  41. package/src/component/DateSelector.mjs +15 -0
  42. package/src/container/Base.mjs +2 -2
  43. package/src/controller/Base.mjs +3 -3
  44. package/src/dialog/Base.mjs +2 -2
  45. package/src/form/field/Base.mjs +3 -6
  46. package/src/form/field/CheckBox.mjs +2 -2
  47. package/src/form/field/ComboBox.mjs +18 -2
  48. package/src/form/field/Date.mjs +10 -4
  49. package/src/form/field/FileUpload.mjs +4 -4
  50. package/src/form/field/Hidden.mjs +2 -2
  51. package/src/form/field/Text.mjs +2 -2
  52. package/src/grid/Container.mjs +340 -43
  53. package/src/grid/View.mjs +599 -124
  54. package/src/grid/header/Button.mjs +331 -36
  55. package/src/grid/header/Toolbar.mjs +111 -4
  56. package/src/grid/plugin/CellEditing.mjs +30 -0
  57. package/src/layout/Base.mjs +3 -3
  58. package/src/list/Base.mjs +2 -2
  59. package/src/list/Circle.mjs +2 -2
  60. package/src/list/Color.mjs +2 -2
  61. package/src/list/Component.mjs +2 -2
  62. package/src/main/DomEvents.mjs +12 -3
  63. package/src/manager/Base.mjs +3 -3
  64. package/src/manager/Component.mjs +20 -11
  65. package/src/manager/DomEvent.mjs +5 -6
  66. package/src/manager/Focus.mjs +2 -2
  67. package/src/manager/Instance.mjs +4 -4
  68. package/src/manager/Task.mjs +2 -2
  69. package/src/manager/Toast.mjs +3 -3
  70. package/src/plugin/Base.mjs +18 -4
  71. package/src/plugin/Popover.mjs +3 -3
  72. package/src/plugin/PrefixField.mjs +2 -2
  73. package/src/plugin/Resizable.mjs +3 -7
  74. package/src/plugin/Responsive.mjs +2 -2
  75. package/src/selection/Model.mjs +17 -2
  76. package/src/selection/grid/CellColumnModel.mjs +1 -1
  77. package/src/selection/grid/CellColumnRowModel.mjs +1 -1
  78. package/src/selection/grid/CellModel.mjs +1 -1
  79. package/src/selection/grid/ColumnModel.mjs +2 -2
  80. package/src/table/Container.mjs +32 -3
  81. package/src/table/View.mjs +9 -4
  82. package/src/table/header/Toolbar.mjs +15 -1
  83. package/src/table/plugin/CellEditing.mjs +330 -0
  84. package/src/toolbar/Base.mjs +2 -2
  85. package/src/toolbar/Breadcrumb.mjs +1 -1
  86. package/src/tooltip/Base.mjs +2 -2
  87. package/src/util/KeyNavigation.mjs +14 -8
  88. package/src/worker/Base.mjs +3 -3
  89. package/src/grid/README.md +0 -3
@@ -1,15 +1,24 @@
1
1
  import BaseContainer from '../container/Base.mjs';
2
2
  import ClassSystemUtil from '../util/ClassSystem.mjs';
3
+ import GridView from './View.mjs';
3
4
  import RowModel from '../selection/grid/RowModel.mjs';
4
5
  import Store from '../data/Store.mjs';
5
- import View from './View.mjs';
6
6
  import * as header from './header/_export.mjs';
7
7
 
8
8
  /**
9
9
  * @class Neo.grid.Container
10
10
  * @extends Neo.container.Base
11
11
  */
12
- class Container extends BaseContainer {
12
+ class GridContainer extends BaseContainer {
13
+ /**
14
+ * @member {Object} delayable
15
+ * @protected
16
+ * @static
17
+ */
18
+ static delayable = {
19
+ onResize: {type: 'buffer', timer: 300}
20
+ }
21
+
13
22
  static config = {
14
23
  /**
15
24
  * @member {String} className='Neo.grid.Container'
@@ -23,57 +32,124 @@ class Container extends BaseContainer {
23
32
  ntype: 'grid-container',
24
33
  /**
25
34
  * @member {String[]} baseCls=['neo-grid-container']
35
+ * @protected
26
36
  */
27
37
  baseCls: ['neo-grid-container'],
38
+ /**
39
+ * true uses grid.plugin.CellEditing
40
+ * @member {Boolean} cellEditing_=false
41
+ */
42
+ cellEditing_: false,
43
+ /**
44
+ * Default configs for each column
45
+ * @member {Object} columnDefaults=null
46
+ */
47
+ columnDefaults: null,
28
48
  /**
29
49
  * @member {Object[]} columns_=[]
30
50
  */
31
51
  columns_: [],
52
+ /**
53
+ * Configs for Neo.grid.header.Toolbar
54
+ * @member {Object|null} [headerToolbarConfig=null]
55
+ */
56
+ headerToolbarConfig: null,
57
+ /**
58
+ * @member {String|null} headerToolbarId_=null
59
+ */
60
+ headerToolbarId_: null,
32
61
  /**
33
62
  * Additional used keys for the selection model
34
- * @member {Object} keys={}
63
+ * @member {Object} keys
35
64
  */
36
65
  keys: {},
37
66
  /**
38
67
  * @member {String} layout='base'
39
68
  */
40
69
  layout: 'base',
70
+ /**
71
+ * @member {String} role='grid'
72
+ */
73
+ role: 'grid',
74
+ /**
75
+ * Number in px
76
+ * @member {Number} rowHeight_=32
77
+ */
78
+ rowHeight_: 32,
79
+ /**
80
+ * @member {String|null} scrollbarId_=null
81
+ * @protected
82
+ */
83
+ scrollbarId_: null,
41
84
  /**
42
85
  * @member {Neo.selection.Model} selectionModel_=null
43
86
  */
44
87
  selectionModel_: null,
88
+ /**
89
+ * @member {Boolean} showHeaderFilters_=false
90
+ */
91
+ showHeaderFilters_: false,
92
+ /**
93
+ * @member {Boolean} sortable_=true
94
+ */
95
+ sortable_: true,
45
96
  /**
46
97
  * @member {Neo.data.Store} store_=null
47
98
  */
48
99
  store_: null,
100
+ /**
101
+ * Configs for Neo.grid.View
102
+ * @member {Object|null} [viewConfig=null]
103
+ */
104
+ viewConfig: null,
105
+ /**
106
+ * @member {String|null} viewId_=null
107
+ * @protected
108
+ */
109
+ viewId_: null,
110
+ /**
111
+ * @member {Array|null} items=null
112
+ * @protected
113
+ */
114
+ items: null,
49
115
  /**
50
116
  * @member {Object} _vdom={cls:['neo-grid-wrapper'],cn:[{cn:[]}]}
51
117
  */
52
118
  _vdom:
53
119
  {cls: ['neo-grid-wrapper'], cn: [
54
- {cn: []}
120
+ {'aria-rowcount': 1, cn: []} // aria-rowcount includes the column headers
55
121
  ]}
56
122
  }
57
123
 
58
124
  /**
59
- * Configs for Neo.grid.header.Toolbar
60
- * @member {Object|null} [headerToolbarConfig=null]
125
+ * We do not need the first event to trigger logic, since afterSetMounted() handles this
126
+ * @member {Boolean}} initialResizeEvent=true
61
127
  */
62
- headerToolbarConfig = null
128
+ initialResizeEvent = true
129
+
63
130
  /**
64
- * @member {String|null} headerToolbarId_=null
131
+ * Convenience method to access the Neo.grid.header.Toolbar
132
+ * @returns {Neo.grid.header.Toolbar|null}
65
133
  */
66
- headerToolbarId = null
134
+ get headerToolbar() {
135
+ return Neo.getComponent(this.headerToolbarId) || Neo.get(this.headerToolbarId)
136
+ }
137
+
67
138
  /**
68
- * Configs for Neo.grid.View
69
- * @member {Object|null} [viewConfig=null]
139
+ * Convenience method to access the Neo.grid.Scrollbar
140
+ * @returns {Neo.grid.Scrollbar|null}
70
141
  */
71
- viewConfig = null
142
+ get scrollbar() {
143
+ return Neo.getComponent(this.scrollbarId) || Neo.get(this.scrollbarId)
144
+ }
145
+
72
146
  /**
73
- * @member {String|null} viewId_=null
74
- * @protected
147
+ * Convenience method to access the Neo.grid.View
148
+ * @returns {Neo.grid.View|null}
75
149
  */
76
- viewId = null
150
+ get view() {
151
+ return Neo.getComponent(this.viewId) || Neo.get(this.viewId)
152
+ }
77
153
 
78
154
  /**
79
155
  * @param {Object} config
@@ -81,27 +157,124 @@ class Container extends BaseContainer {
81
157
  construct(config) {
82
158
  super.construct(config);
83
159
 
84
- let me = this;
160
+ let me = this,
161
+ {rowHeight, store} = me;
85
162
 
86
163
  me.headerToolbarId = Neo.getId('grid-header-toolbar');
164
+ me.scrollbarId = Neo.getId('grid-scrollbar');
87
165
  me.viewId = Neo.getId('grid-view');
88
166
 
89
167
  me.items = [{
90
168
  module : header.Toolbar,
169
+ gridContainer : me,
91
170
  id : me.headerToolbarId,
92
171
  showHeaderFilters: me.showHeaderFilters,
172
+ sortable : me.sortable,
93
173
  ...me.headerToolbarConfig
94
174
  }, {
95
- module : View,
175
+ module : GridView,
96
176
  containerId: me.id,
97
177
  id : me.viewId,
98
- store : me.store,
178
+ rowHeight,
179
+ store,
99
180
  ...me.viewConfig
100
181
  }];
101
182
 
102
- me.vdom.id = me.id + 'wrapper';
183
+ me.vdom.id = me.getWrapperId();
184
+
185
+ me.createColumns(me.columns);
186
+
187
+ me.addDomListeners({
188
+ resize: me.onResize,
189
+ scroll: me.onScroll,
190
+ scope : me
191
+ })
192
+ }
193
+
194
+ /**
195
+ * Triggered after the cellEditing config got changed
196
+ * @param {Boolean} value
197
+ * @param {Boolean} oldValue
198
+ * @protected
199
+ */
200
+ afterSetCellEditing(value, oldValue) {
201
+ if (value) {
202
+ import('./plugin/CellEditing.mjs').then(module => {
203
+ let me = this,
204
+ {appName, windowId} = me,
205
+ plugins = me.plugins || [];
206
+
207
+ plugins.push({
208
+ module : module.default,
209
+ appName,
210
+ windowId
211
+ });
212
+
213
+ me.plugins = plugins
214
+ })
215
+ }
216
+ }
217
+
218
+ /**
219
+ * Triggered after the columns config got changed
220
+ * @param {Object[]|null} value
221
+ * @param {Object[]|null} oldValue
222
+ * @protected
223
+ */
224
+ afterSetColumns(value, oldValue) {
225
+ if (Array.isArray(oldValue) && oldValue.length > 0) {
226
+ let me = this,
227
+ {headerToolbar} = me;
228
+
229
+ if (headerToolbar) {
230
+ headerToolbar.items = value;
231
+ headerToolbar.createItems()
232
+ }
233
+
234
+ me.view?.createViewData(me.store.items)
235
+ }
236
+ }
237
+
238
+ /**
239
+ * Triggered after the mounted config got changed
240
+ * @param {Boolean} value
241
+ * @param {Boolean} oldValue
242
+ * @protected
243
+ */
244
+ afterSetMounted(value, oldValue) {
245
+ super.afterSetMounted(value, oldValue);
246
+
247
+ let me = this,
248
+ {ResizeObserver} = Neo.main.addon,
249
+ resizeParams = {id: me.id, windowId: me.windowId};
250
+
251
+ if (value) {
252
+ ResizeObserver.register(resizeParams);
253
+ me.passSizeToView()
254
+ } else if (!value && oldValue) { // unmount
255
+ me.initialResizeEvent = true;
256
+ ResizeObserver.unregister(resizeParams)
257
+ }
258
+ }
259
+
260
+ /**
261
+ * Triggered after the rowHeight config got changed
262
+ * @param {Number} value
263
+ * @param {Number} oldValue
264
+ * @protected
265
+ */
266
+ afterSetRowHeight(value, oldValue) {
267
+ if (value > 0) {
268
+ let {scrollbar, view} = this;
269
+
270
+ if (scrollbar) {
271
+ scrollbar.rowHeight = value
272
+ }
103
273
 
104
- me.createColumns(me.columns)
274
+ if (view) {
275
+ view.rowHeight = value
276
+ }
277
+ }
105
278
  }
106
279
 
107
280
  /**
@@ -114,6 +287,30 @@ class Container extends BaseContainer {
114
287
  this.rendered && value.register(this)
115
288
  }
116
289
 
290
+ /**
291
+ * Triggered after the showHeaderFilters config got changed
292
+ * @param {Boolean} value
293
+ * @param {Boolean} oldValue
294
+ * @protected
295
+ */
296
+ afterSetShowHeaderFilters(value, oldValue) {
297
+ if (oldValue !== undefined) {
298
+ this.headerToolbar.showHeaderFilters = value
299
+ }
300
+ }
301
+
302
+ /**
303
+ * Triggered after the sortable config got changed
304
+ * @param {Boolean} value
305
+ * @param {Boolean} oldValue
306
+ * @protected
307
+ */
308
+ afterSetSortable(value, oldValue) {
309
+ if (oldValue !== undefined) {
310
+ this.headerToolbar.sortable = value
311
+ }
312
+ }
313
+
117
314
  /**
118
315
  * Triggered before the columns config gets changed.
119
316
  * @param {Object[]} value
@@ -128,6 +325,16 @@ class Container extends BaseContainer {
128
325
  return value
129
326
  }
130
327
 
328
+ /**
329
+ * Triggered before the headerToolbarId config gets changed.
330
+ * @param {String} value
331
+ * @param {String} oldValue
332
+ * @protected
333
+ */
334
+ beforeSetHeaderToolbarId(value, oldValue) {
335
+ return value || oldValue
336
+ }
337
+
131
338
  /**
132
339
  * Triggered before the selectionModel config gets changed.
133
340
  * @param {Neo.selection.Model} value
@@ -177,6 +384,39 @@ class Container extends BaseContainer {
177
384
  return value
178
385
  }
179
386
 
387
+ /**
388
+ * Triggered before the viewId config gets changed.
389
+ * @param {String} value
390
+ * @param {String} oldValue
391
+ * @protected
392
+ */
393
+ beforeSetViewId(value, oldValue) {
394
+ return value || oldValue
395
+ }
396
+
397
+ /**
398
+ * In case you want to update multiple existing records in parallel,
399
+ * using this method is faster than updating each record one by one.
400
+ * At least until we introduce row based vdom updates.
401
+ * @param {Object[]} records
402
+ */
403
+ bulkUpdateRecords(records) {
404
+ let {store, view} = this,
405
+ {keyProperty} = store;
406
+
407
+ if (view) {
408
+ view.silentVdomUpdate = true;
409
+
410
+ records.forEach(item => {
411
+ store.get(item[keyProperty])?.set(item)
412
+ });
413
+
414
+ view.silentVdomUpdate = false;
415
+
416
+ view.update()
417
+ }
418
+ }
419
+
180
420
  /**
181
421
  * @param {Object[]} columns
182
422
  * @returns {*}
@@ -188,7 +428,7 @@ class Container extends BaseContainer {
188
428
  renderer;
189
429
 
190
430
  if (!columns || !columns.length) {
191
- Neo.logError('Attempting to create a grid.Container without defined columns', me.id)
431
+ Neo.logError('Attempting to create a grid.Container without defined columns', me.id);
192
432
  }
193
433
 
194
434
  columns.forEach(column => {
@@ -197,7 +437,7 @@ class Container extends BaseContainer {
197
437
  columnDefaults && Neo.assignDefaults(column, columnDefaults);
198
438
 
199
439
  if (column.dock && !column.width) {
200
- Neo.logError('Attempting to create a docked column without a defined width', column, me.id)
440
+ Neo.logError('Attempting to create a docked column without a defined width', column, me.id);
201
441
  }
202
442
 
203
443
  if (renderer && Neo.isString(renderer) && me[renderer]) {
@@ -205,7 +445,7 @@ class Container extends BaseContainer {
205
445
  }
206
446
 
207
447
  if (sorters?.[0]) {
208
- if (column.field === sorters[0].property) {
448
+ if (column.dataField === sorters[0].property) {
209
449
  column.isSorted = sorters[0].direction
210
450
  }
211
451
  }
@@ -222,10 +462,29 @@ class Container extends BaseContainer {
222
462
  }
223
463
 
224
464
  /**
225
- * @param {Object[]} inputData
465
+ * @param {Array} inputData
226
466
  */
227
467
  createViewData(inputData) {
228
- this.getView().createViewData(inputData)
468
+ let me = this;
469
+
470
+ me.getVdomRoot()['aria-rowcount'] = inputData.length + 2; // 1 based & the header row counts as well
471
+ me.update();
472
+
473
+ me.view.createViewData()
474
+ }
475
+
476
+ /**
477
+ * @param args
478
+ */
479
+ destroy(...args) {
480
+ let me = this;
481
+
482
+ me.mounted && Neo.main.addon.ResizeObserver.unregister({
483
+ id : me.id,
484
+ windowId: me.windowId
485
+ });
486
+
487
+ super.destroy(...args)
229
488
  }
230
489
 
231
490
  /**
@@ -243,13 +502,6 @@ class Container extends BaseContainer {
243
502
  return this.vdom.cn[0]
244
503
  }
245
504
 
246
- /**
247
- * @returns {Neo.grid.View}
248
- */
249
- getView() {
250
- return Neo.getComponent(this.viewId) || Neo.get(this.viewId)
251
- }
252
-
253
505
  /**
254
506
  * @override
255
507
  * @returns {Neo.vdom.VNode}
@@ -258,6 +510,13 @@ class Container extends BaseContainer {
258
510
  return this.vnode.childNodes[0]
259
511
  }
260
512
 
513
+ /**
514
+ * @returns {String}
515
+ */
516
+ getWrapperId() {
517
+ return `${this.id}__wrapper`
518
+ }
519
+
261
520
  /**
262
521
  *
263
522
  */
@@ -266,6 +525,30 @@ class Container extends BaseContainer {
266
525
  this.selectionModel?.register(this)
267
526
  }
268
527
 
528
+ /**
529
+ * @param {Object} data
530
+ */
531
+ async onResize(data) {
532
+ let me = this;
533
+
534
+ if (!me.initialResizeEvent) {
535
+ await me.passSizeToView(true);
536
+
537
+ me.view.updateVisibleColumns();
538
+
539
+ await me.headerToolbar.passSizeToView()
540
+ } else {
541
+ me.initialResizeEvent = false
542
+ }
543
+ }
544
+
545
+ /**
546
+ * @param {Object} data
547
+ */
548
+ onScroll(data) {
549
+ this.view.scrollPosition = {x: data.scrollLeft, y: this.view.scrollPosition.y}
550
+ }
551
+
269
552
  /**
270
553
  * @param {Object} opts
271
554
  * @param {String} opts.direction
@@ -292,8 +575,7 @@ class Container extends BaseContainer {
292
575
  * @protected
293
576
  */
294
577
  onStoreLoad(data) {
295
- let me = this,
296
- listenerId;
578
+ let me = this;
297
579
 
298
580
  if (me.rendered) {
299
581
  me.createViewData(data);
@@ -302,12 +584,11 @@ class Container extends BaseContainer {
302
584
  me.removeSortingCss()
303
585
  }
304
586
  } else {
305
- listenerId = me.on('rendered', () => {
306
- me.un('rendered', listenerId);
587
+ me.on('rendered', () => {
307
588
  me.timeout(50).then(() => {
308
589
  me.createViewData(data)
309
590
  })
310
- })
591
+ }, me, {once: true})
311
592
  }
312
593
  }
313
594
 
@@ -322,18 +603,34 @@ class Container extends BaseContainer {
322
603
  * @param {*} opts.value
323
604
  */
324
605
  onStoreRecordChange(opts) {
325
- this.getView().onStoreRecordChange(opts)
606
+ this.view.onStoreRecordChange(opts)
326
607
  }
327
608
 
328
609
  /**
329
- * @param {String} field
610
+ * @param {Boolean} silent=false
611
+ * @returns {Promise<void>}
612
+ */
613
+ async passSizeToView(silent=false) {
614
+ let me = this,
615
+ [containerRect, headerRect] = await me.getDomRect([me.id, me.headerToolbarId]);
616
+
617
+ me.view[silent ? 'setSilent' : 'set']({
618
+ availableHeight: containerRect.height - headerRect.height,
619
+ containerWidth : containerRect.width
620
+ })
621
+ }
622
+
623
+ /**
624
+ * @param {String} dataField
330
625
  * @protected
331
626
  */
332
- removeSortingCss(field) {
333
- this.items[0].items.forEach(column => {
334
- column.field !== field && column.removeSortingCss()
627
+ removeSortingCss(dataField) {
628
+ this.headerToolbar.items.forEach(column => {
629
+ if (column.dataField !== dataField) {
630
+ column.removeSortingCss()
631
+ }
335
632
  })
336
633
  }
337
634
  }
338
635
 
339
- export default Neo.setupClass(Container);
636
+ export default Neo.setupClass(GridContainer);