neo.mjs 8.0.0 → 8.0.1

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 (55) 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/table/cellEditing/MainContainer.mjs +174 -0
  19. package/examples/table/cellEditing/MainContainerStateProvider.mjs +62 -0
  20. package/examples/table/cellEditing/MainModel.mjs +30 -0
  21. package/examples/table/cellEditing/MainStore.mjs +54 -0
  22. package/examples/table/cellEditing/app.mjs +6 -0
  23. package/examples/table/cellEditing/index.html +11 -0
  24. package/examples/table/cellEditing/neo-config.json +6 -0
  25. package/examples/table/nestedRecordFields/MainContainerStateProvider.mjs +2 -1
  26. package/package.json +5 -5
  27. package/resources/scss/src/apps/portal/home/FooterContainer.scss +11 -2
  28. package/resources/scss/src/grid/Container.scss +0 -13
  29. package/resources/scss/src/grid/plugin/CellEditing.scss +11 -0
  30. package/resources/scss/src/table/plugin/CellEditing.scss +11 -0
  31. package/src/DefaultConfig.mjs +2 -2
  32. package/src/component/DateSelector.mjs +15 -0
  33. package/src/form/field/Base.mjs +1 -4
  34. package/src/form/field/ComboBox.mjs +18 -2
  35. package/src/form/field/Date.mjs +10 -4
  36. package/src/grid/Container.mjs +242 -39
  37. package/src/grid/README.md +1 -1
  38. package/src/grid/View.mjs +282 -129
  39. package/src/grid/header/Button.mjs +327 -36
  40. package/src/grid/header/Toolbar.mjs +68 -4
  41. package/src/grid/plugin/CellEditing.mjs +30 -0
  42. package/src/main/DomEvents.mjs +12 -3
  43. package/src/manager/Focus.mjs +2 -2
  44. package/src/plugin/Base.mjs +15 -1
  45. package/src/plugin/Resizable.mjs +1 -5
  46. package/src/selection/Model.mjs +5 -1
  47. package/src/selection/grid/CellColumnModel.mjs +1 -1
  48. package/src/selection/grid/CellColumnRowModel.mjs +1 -1
  49. package/src/selection/grid/CellModel.mjs +1 -1
  50. package/src/selection/grid/ColumnModel.mjs +2 -2
  51. package/src/table/Container.mjs +32 -3
  52. package/src/table/View.mjs +9 -4
  53. package/src/table/header/Toolbar.mjs +15 -1
  54. package/src/table/plugin/CellEditing.mjs +330 -0
  55. package/src/util/KeyNavigation.mjs +14 -8
@@ -165,7 +165,7 @@ class ComboBox extends Picker {
165
165
  */
166
166
  afterSetStore(value, oldValue) {
167
167
  let me = this,
168
- filters;
168
+ filters, val;
169
169
 
170
170
  if (value) {
171
171
  if (me.useFilter) {
@@ -185,7 +185,14 @@ class ComboBox extends Picker {
185
185
  me.list.store = value
186
186
  }
187
187
 
188
- value.on('load', me.onStoreLoad, me)
188
+ value.on('load', me.onStoreLoad, me);
189
+
190
+ if (me.value) {
191
+ val = me.value;
192
+
193
+ me._value = null; // silent reset to trigger a change event
194
+ me.value = val
195
+ }
189
196
  }
190
197
  }
191
198
 
@@ -248,6 +255,11 @@ class ComboBox extends Picker {
248
255
  let me = this,
249
256
  {displayField, valueField} = me;
250
257
 
258
+ // Do not create a default store instance, in case there is a bound store to be created
259
+ if (!value && me.bind?.store) {
260
+ return null
261
+ }
262
+
251
263
  oldValue?.destroy();
252
264
 
253
265
  // Promote an array of items to be a Store
@@ -312,6 +324,10 @@ class ComboBox extends Picker {
312
324
  return null
313
325
  }
314
326
 
327
+ if (!store) { // We will (re)set the value once the store is created
328
+ return value
329
+ }
330
+
315
331
  // we can only match record ids or display values in case the store is loaded
316
332
  if (store.getCount() > 0) {
317
333
  record = store.isFiltered() ? store.allItems.get(value) : store.get(value);
@@ -142,7 +142,7 @@ class DateField extends Picker {
142
142
  afterSetMinValue(value, oldValue) {
143
143
  let me = this;
144
144
 
145
- me.changeInputElKey('max', value);
145
+ me.changeInputElKey('min', value);
146
146
 
147
147
  if (me.dateSelector) {
148
148
  me.dateSelector.minValue = value
@@ -170,8 +170,14 @@ class DateField extends Picker {
170
170
  * @protected
171
171
  */
172
172
  beforeSetValue(value, oldValue) {
173
- const val = super.beforeSetValue(value, oldValue);
174
- return (this.isoDate && val) ? val.substring(0, 10) : val;
173
+ if (Neo.typeOf(value) === 'Date') {
174
+ value = DateUtil.convertToyyyymmdd(value);
175
+ this.originalConfig.value = value
176
+ }
177
+
178
+ value = super.beforeSetValue(value, oldValue);
179
+
180
+ return (this.isoDate && value) ? value.substring(0, 10) : value
175
181
  }
176
182
 
177
183
  /**
@@ -244,7 +250,7 @@ class DateField extends Picker {
244
250
  onInputValueChange(data) {
245
251
  this.invalidInput = !data.valid;
246
252
 
247
- if (data.valid === true) {
253
+ if (data.valid) {
248
254
  super.onInputValueChange(data)
249
255
  } else {
250
256
  this.validate(false)
@@ -1,5 +1,7 @@
1
1
  import BaseContainer from '../container/Base.mjs';
2
2
  import ClassSystemUtil from '../util/ClassSystem.mjs';
3
+ import CssUtil from '../util/Css.mjs';
4
+ import NeoArray from '../util/Array.mjs';
3
5
  import RowModel from '../selection/grid/RowModel.mjs';
4
6
  import Store from '../data/Store.mjs';
5
7
  import View from './View.mjs';
@@ -25,27 +27,79 @@ class Container extends BaseContainer {
25
27
  * @member {String[]} baseCls=['neo-grid-container']
26
28
  */
27
29
  baseCls: ['neo-grid-container'],
30
+ /**
31
+ * true uses table.plugin.CellEditing
32
+ * @member {Boolean} cellEditing_=false
33
+ */
34
+ cellEditing_: false,
35
+ /**
36
+ * Default configs for each column
37
+ * @member {Object} columnDefaults=null
38
+ */
39
+ columnDefaults: null,
28
40
  /**
29
41
  * @member {Object[]} columns_=[]
30
42
  */
31
43
  columns_: [],
44
+ /**
45
+ * Configs for Neo.table.header.Toolbar
46
+ * @member {Object|null} [headerToolbarConfig=null]
47
+ */
48
+ headerToolbarConfig: null,
49
+ /**
50
+ * @member {String|null} headerToolbarId_=null
51
+ */
52
+ headerToolbarId_: null,
32
53
  /**
33
54
  * Additional used keys for the selection model
34
- * @member {Object} keys={}
55
+ * @member {Object} keys
35
56
  */
36
57
  keys: {},
37
58
  /**
38
59
  * @member {String} layout='base'
39
60
  */
40
61
  layout: 'base',
62
+ /**
63
+ * @member {Boolean} scrollbarsCssApplied=false
64
+ * @protected
65
+ */
66
+ scrollbarsCssApplied: false,
41
67
  /**
42
68
  * @member {Neo.selection.Model} selectionModel_=null
43
69
  */
44
70
  selectionModel_: null,
71
+ /**
72
+ * @member {Boolean} showHeaderFilters_=false
73
+ */
74
+ showHeaderFilters_: false,
75
+ /**
76
+ * @member {Boolean} sortable_=true
77
+ */
78
+ sortable_: true,
45
79
  /**
46
80
  * @member {Neo.data.Store} store_=null
47
81
  */
48
82
  store_: null,
83
+ /**
84
+ * todo: only works for chrome & safari -> add a check
85
+ * @member {Boolean} useCustomScrollbars_=true
86
+ */
87
+ useCustomScrollbars_: true,
88
+ /**
89
+ * Configs for Neo.table.View
90
+ * @member {Object|null} [viewConfig=null]
91
+ */
92
+ viewConfig: null,
93
+ /**
94
+ * @member {String|null} viewId_=null
95
+ * @protected
96
+ */
97
+ viewId_: null,
98
+ /**
99
+ * @member {Array|null} items=null
100
+ * @protected
101
+ */
102
+ items: null,
49
103
  /**
50
104
  * @member {Object} _vdom={cls:['neo-grid-wrapper'],cn:[{cn:[]}]}
51
105
  */
@@ -56,24 +110,20 @@ class Container extends BaseContainer {
56
110
  }
57
111
 
58
112
  /**
59
- * Configs for Neo.grid.header.Toolbar
60
- * @member {Object|null} [headerToolbarConfig=null]
61
- */
62
- headerToolbarConfig = null
63
- /**
64
- * @member {String|null} headerToolbarId_=null
65
- */
66
- headerToolbarId = null
67
- /**
68
- * Configs for Neo.grid.View
69
- * @member {Object|null} [viewConfig=null]
113
+ * Convenience method to access the Neo.grid.header.Toolbar
114
+ * @returns {Neo.table.header.Toolbar|null}
70
115
  */
71
- viewConfig = null
116
+ get headerToolbar() {
117
+ return Neo.getComponent(this.headerToolbarId) || Neo.get(this.headerToolbarId)
118
+ }
119
+
72
120
  /**
73
- * @member {String|null} viewId_=null
74
- * @protected
121
+ * Convenience method to access the Neo.grid.View
122
+ * @returns {Neo.table.View|null}
75
123
  */
76
- viewId = null
124
+ get view() {
125
+ return Neo.getComponent(this.viewId) || Neo.get(this.viewId)
126
+ }
77
127
 
78
128
  /**
79
129
  * @param {Object} config
@@ -90,6 +140,7 @@ class Container extends BaseContainer {
90
140
  module : header.Toolbar,
91
141
  id : me.headerToolbarId,
92
142
  showHeaderFilters: me.showHeaderFilters,
143
+ sortable : me.sortable,
93
144
  ...me.headerToolbarConfig
94
145
  }, {
95
146
  module : View,
@@ -99,11 +150,55 @@ class Container extends BaseContainer {
99
150
  ...me.viewConfig
100
151
  }];
101
152
 
102
- me.vdom.id = me.id + 'wrapper';
153
+ me.vdom.id = me.getWrapperId();
103
154
 
104
155
  me.createColumns(me.columns)
105
156
  }
106
157
 
158
+ /**
159
+ * Triggered after the cellEditing config got changed
160
+ * @param {Boolean} value
161
+ * @param {Boolean} oldValue
162
+ * @protected
163
+ */
164
+ afterSetCellEditing(value, oldValue) {
165
+ if (value) {
166
+ import('./plugin/CellEditing.mjs').then(module => {
167
+ let me = this,
168
+ {appName, windowId} = me,
169
+ plugins = me.plugins || [];
170
+
171
+ plugins.push({
172
+ module : module.default,
173
+ appName,
174
+ windowId
175
+ });
176
+
177
+ me.plugins = plugins
178
+ })
179
+ }
180
+ }
181
+
182
+ /**
183
+ * Triggered after the columns config got changed
184
+ * @param {Object[]|null} value
185
+ * @param {Object[]|null} oldValue
186
+ * @protected
187
+ */
188
+ afterSetColumns(value, oldValue) {
189
+ if (Array.isArray(oldValue) && oldValue.length > 0) {
190
+ let me = this,
191
+ {headerToolbar} = me;
192
+
193
+ if (headerToolbar) {
194
+ headerToolbar.items = value;
195
+ headerToolbar.createItems()
196
+ }
197
+
198
+ me.view?.createViewData(me.store.items)
199
+ }
200
+ }
201
+
107
202
  /**
108
203
  * Triggered after the selectionModel config got changed
109
204
  * @param {Neo.selection.Model} value
@@ -114,6 +209,65 @@ class Container extends BaseContainer {
114
209
  this.rendered && value.register(this)
115
210
  }
116
211
 
212
+ /**
213
+ * Triggered after the showHeaderFilters config got changed
214
+ * @param {Boolean} value
215
+ * @param {Boolean} oldValue
216
+ * @protected
217
+ */
218
+ afterSetShowHeaderFilters(value, oldValue) {
219
+ if (oldValue !== undefined) {
220
+ this.headerToolbar.showHeaderFilters = value
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Triggered after the sortable config got changed
226
+ * @param {Boolean} value
227
+ * @param {Boolean} oldValue
228
+ * @protected
229
+ */
230
+ afterSetSortable(value, oldValue) {
231
+ if (oldValue !== undefined) {
232
+ this.headerToolbar.sortable = value
233
+ }
234
+ }
235
+
236
+ /**
237
+ * Triggered after the useCustomScrollbars config got changed
238
+ * @param {Boolean} value
239
+ * @param {Boolean} oldValue
240
+ * @protected
241
+ */
242
+ afterSetUseCustomScrollbars(value, oldValue) {
243
+ if (value === true) {
244
+ this.vdom.cls = NeoArray.union(this.vdom.cls, ['neo-use-custom-scrollbar'])
245
+ }
246
+ }
247
+
248
+ /**
249
+ * @protected
250
+ */
251
+ applyCustomScrollbarsCss() {
252
+ let me = this,
253
+ id = me.getWrapperId(),
254
+ cssRules = [];
255
+
256
+ if (me.dockLeftMargin) {
257
+ cssRules.push('#' + id + '::-webkit-scrollbar-track:horizontal {margin-left: ' + me.dockLeftMargin + 'px;}')
258
+ }
259
+
260
+ if (me.dockRightMargin) {
261
+ cssRules.push('#' + id + '::-webkit-scrollbar-track:horizontal {margin-right: ' + me.dockRightMargin + 'px;}')
262
+ }
263
+
264
+ if (cssRules.length > 0) {
265
+ CssUtil.insertRules(me.appName, cssRules)
266
+ }
267
+
268
+ me.scrollbarsCssApplied = true
269
+ }
270
+
117
271
  /**
118
272
  * Triggered before the columns config gets changed.
119
273
  * @param {Object[]} value
@@ -128,6 +282,16 @@ class Container extends BaseContainer {
128
282
  return value
129
283
  }
130
284
 
285
+ /**
286
+ * Triggered before the headerToolbarId config gets changed.
287
+ * @param {String} value
288
+ * @param {String} oldValue
289
+ * @protected
290
+ */
291
+ beforeSetHeaderToolbarId(value, oldValue) {
292
+ return value || oldValue
293
+ }
294
+
131
295
  /**
132
296
  * Triggered before the selectionModel config gets changed.
133
297
  * @param {Neo.selection.Model} value
@@ -177,6 +341,39 @@ class Container extends BaseContainer {
177
341
  return value
178
342
  }
179
343
 
344
+ /**
345
+ * Triggered before the viewId config gets changed.
346
+ * @param {String} value
347
+ * @param {String} oldValue
348
+ * @protected
349
+ */
350
+ beforeSetViewId(value, oldValue) {
351
+ return value || oldValue
352
+ }
353
+
354
+ /**
355
+ * In case you want to update multiple existing records in parallel,
356
+ * using this method is faster than updating each record one by one.
357
+ * At least until we introduce row based vdom updates.
358
+ * @param {Object[]} records
359
+ */
360
+ bulkUpdateRecords(records) {
361
+ let {store, view} = this,
362
+ {keyProperty} = store;
363
+
364
+ if (view) {
365
+ view.silentVdomUpdate = true;
366
+
367
+ records.forEach(item => {
368
+ store.get(item[keyProperty])?.set(item)
369
+ });
370
+
371
+ view.silentVdomUpdate = false;
372
+
373
+ view.update()
374
+ }
375
+ }
376
+
180
377
  /**
181
378
  * @param {Object[]} columns
182
379
  * @returns {*}
@@ -188,7 +385,7 @@ class Container extends BaseContainer {
188
385
  renderer;
189
386
 
190
387
  if (!columns || !columns.length) {
191
- Neo.logError('Attempting to create a grid.Container without defined columns', me.id)
388
+ Neo.logError('Attempting to create a table.Container without defined columns', me.id);
192
389
  }
193
390
 
194
391
  columns.forEach(column => {
@@ -197,7 +394,7 @@ class Container extends BaseContainer {
197
394
  columnDefaults && Neo.assignDefaults(column, columnDefaults);
198
395
 
199
396
  if (column.dock && !column.width) {
200
- Neo.logError('Attempting to create a docked column without a defined width', column, me.id)
397
+ Neo.logError('Attempting to create a docked column without a defined width', column, me.id);
201
398
  }
202
399
 
203
400
  if (renderer && Neo.isString(renderer) && me[renderer]) {
@@ -205,7 +402,7 @@ class Container extends BaseContainer {
205
402
  }
206
403
 
207
404
  if (sorters?.[0]) {
208
- if (column.field === sorters[0].property) {
405
+ if (column.dataField === sorters[0].property) {
209
406
  column.isSorted = sorters[0].direction
210
407
  }
211
408
  }
@@ -222,10 +419,16 @@ class Container extends BaseContainer {
222
419
  }
223
420
 
224
421
  /**
225
- * @param {Object[]} inputData
422
+ * @param {Array} inputData
226
423
  */
227
424
  createViewData(inputData) {
228
- this.getView().createViewData(inputData)
425
+ let me = this;
426
+
427
+ me.view.createViewData(inputData);
428
+
429
+ if (me.useCustomScrollbars && me.scrollbarsCssApplied === false) {
430
+ me.applyCustomScrollbarsCss()
431
+ }
229
432
  }
230
433
 
231
434
  /**
@@ -243,13 +446,6 @@ class Container extends BaseContainer {
243
446
  return this.vdom.cn[0]
244
447
  }
245
448
 
246
- /**
247
- * @returns {Neo.grid.View}
248
- */
249
- getView() {
250
- return Neo.getComponent(this.viewId) || Neo.get(this.viewId)
251
- }
252
-
253
449
  /**
254
450
  * @override
255
451
  * @returns {Neo.vdom.VNode}
@@ -258,6 +454,13 @@ class Container extends BaseContainer {
258
454
  return this.vnode.childNodes[0]
259
455
  }
260
456
 
457
+ /**
458
+ * @returns {String}
459
+ */
460
+ getWrapperId() {
461
+ return `${this.id}__wrapper`
462
+ }
463
+
261
464
  /**
262
465
  *
263
466
  */
@@ -292,8 +495,7 @@ class Container extends BaseContainer {
292
495
  * @protected
293
496
  */
294
497
  onStoreLoad(data) {
295
- let me = this,
296
- listenerId;
498
+ let me = this;
297
499
 
298
500
  if (me.rendered) {
299
501
  me.createViewData(data);
@@ -302,12 +504,11 @@ class Container extends BaseContainer {
302
504
  me.removeSortingCss()
303
505
  }
304
506
  } else {
305
- listenerId = me.on('rendered', () => {
306
- me.un('rendered', listenerId);
507
+ me.on('rendered', () => {
307
508
  me.timeout(50).then(() => {
308
509
  me.createViewData(data)
309
510
  })
310
- })
511
+ }, me, {once: true})
311
512
  }
312
513
  }
313
514
 
@@ -322,16 +523,18 @@ class Container extends BaseContainer {
322
523
  * @param {*} opts.value
323
524
  */
324
525
  onStoreRecordChange(opts) {
325
- this.getView().onStoreRecordChange(opts)
526
+ this.view.onStoreRecordChange(opts)
326
527
  }
327
528
 
328
529
  /**
329
- * @param {String} field
530
+ * @param {String} dataField
330
531
  * @protected
331
532
  */
332
- removeSortingCss(field) {
333
- this.items[0].items.forEach(column => {
334
- column.field !== field && column.removeSortingCss()
533
+ removeSortingCss(dataField) {
534
+ this.headerToolbar.items.forEach(column => {
535
+ if (column.dataField !== dataField) {
536
+ column.removeSortingCss()
537
+ }
335
538
  })
336
539
  }
337
540
  }
@@ -1,3 +1,3 @@
1
1
  The grid implementation using div tags is currently on hold in favor for the table implementation using a table tag.
2
2
 
3
- The BufferedGrid implementation is currently scoped for neo v5.
3
+ The BufferedGrid implementation is currently scoped for neo v9.