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
@@ -0,0 +1,330 @@
1
+ import Plugin from '../../plugin/Base.mjs';
2
+ import TextField from '../../form/field/Text.mjs';
3
+ import VdomUtil from '../../util/VDom.mjs';
4
+
5
+ /**
6
+ * @class Neo.table.plugin.CellEditing
7
+ * @extends Neo.plugin.Base
8
+ */
9
+ class CellEditing extends Plugin {
10
+ static config = {
11
+ /**
12
+ * @member {String} className='Neo.table.plugin.CellEditing'
13
+ * @protected
14
+ */
15
+ className: 'Neo.table.plugin.CellEditing',
16
+ /**
17
+ * @member {String} ntype='plugin-table-cell-editing'
18
+ * @protected
19
+ */
20
+ ntype: 'plugin-table-cell-editing',
21
+ /**
22
+ * @member {String} cellCls='neo-table-cell'
23
+ */
24
+ cellCls: 'neo-table-cell',
25
+ /**
26
+ * @member {Boolean} disabled_=false
27
+ */
28
+ disabled_: false,
29
+ /**
30
+ * @member {String[]} editorCls=['neo-table-editor']
31
+ */
32
+ editorCls: ['neo-table-editor']
33
+ }
34
+
35
+ /**
36
+ * Storing editor instances per column
37
+ * @member {Object} editors={}
38
+ */
39
+ editors = {}
40
+ /**
41
+ * Storing the currently mounted editor
42
+ * @member {Neo.form.field.Base|null} mountedEditor=null
43
+ */
44
+ mountedEditor = null
45
+
46
+ /**
47
+ * @param {Object} config
48
+ */
49
+ construct(config) {
50
+ super.construct(config);
51
+
52
+ let me = this,
53
+ {owner} = me,
54
+ {selectionModel} = owner;
55
+
56
+ owner.on({
57
+ cellDoubleClick : me.onCellDoubleClick,
58
+ focusLeave : me.onFocusLeave,
59
+ selectionModelChange: me.onSelectionModelChange,
60
+ scope : me
61
+ });
62
+
63
+ // Connect an already registered selectionModel instance
64
+ if (Neo.typeOf(selectionModel) === 'NeoInstance') {
65
+ me.onSelectionModelChange({value: selectionModel})
66
+ }
67
+
68
+ owner.keys.add({
69
+ Enter: 'onTableKeyDown',
70
+ Space: 'onTableKeyDown',
71
+ scope: me
72
+ })
73
+ }
74
+
75
+ /**
76
+ * Triggered after the disabled config got changed
77
+ * @param {Boolean} value
78
+ * @param {Boolean} oldValue
79
+ * @protected
80
+ */
81
+ afterSetDisabled(value, oldValue) {
82
+ oldValue && this.unmountEditor()
83
+ }
84
+
85
+ /**
86
+ * @param {args} args
87
+ */
88
+ destroy(...args) {
89
+ Object.values(this.editors).forEach(editor => {
90
+ editor.destroy(false, true)
91
+ });
92
+
93
+ super.destroy(...args)
94
+ }
95
+
96
+ /**
97
+ * @param {Object} record
98
+ * @param {String} dataField
99
+ * @returns {Promise<void>}
100
+ */
101
+ async mountEditor(record, dataField) {
102
+ if (this.disabled) {
103
+ return
104
+ }
105
+
106
+ let me = this,
107
+ {appName, windowId} = me,
108
+ {view} = me.owner,
109
+ cellId = view.getCellId(record, dataField),
110
+ cellNode = VdomUtil.find(view.vdom, cellId).vdom,
111
+ column = me.owner.headerToolbar.getColumn(dataField),
112
+ editor = me.editors[dataField],
113
+ value = record[dataField];
114
+
115
+ if (me.mountedEditor) {
116
+ await me.unmountEditor();
117
+ await me.timeout(10)
118
+ }
119
+
120
+ if (!column.editable) {
121
+ return
122
+ }
123
+
124
+ if (!editor) {
125
+ me.editors[dataField] = editor = Neo.create({
126
+ module : TextField,
127
+ appName,
128
+ cls : me.editorCls,
129
+ dataField,
130
+ hideLabel: true,
131
+ parentId : view.id,
132
+ record,
133
+ value,
134
+ windowId,
135
+
136
+ keys: {
137
+ Enter : 'onEditorKeyEnter',
138
+ Escape: 'onEditorKeyEscape',
139
+ Tab : 'onEditorKeyTab',
140
+ scope : me
141
+ },
142
+
143
+ ...column.editor
144
+ })
145
+ } else {
146
+ editor.originalConfig.value = value;
147
+ editor.setSilent({record, value})
148
+ }
149
+
150
+ me.mountedEditor = editor;
151
+
152
+ cellNode.cn = [editor.createVdomReference()];
153
+ delete cellNode.innerHTML;
154
+
155
+ view.updateDepth = -1;
156
+
157
+ await view.promiseUpdate();
158
+
159
+ await me.timeout(10);
160
+
161
+ editor.focus()
162
+ }
163
+
164
+ /**
165
+ *
166
+ * @param {Object} data
167
+ * @param {Object} data.data
168
+ * @param {String} data.dataField
169
+ * @param {Object} data.record
170
+ * @param {Neo.table.View} data.view
171
+ * @returns {Promise<void>}
172
+ */
173
+ async onCellDoubleClick({data, dataField, record, view}) {
174
+ await this.mountEditor(record, dataField)
175
+ }
176
+
177
+ /**
178
+ * @param {Object} data
179
+ * @param {Neo.form.field.Base} field
180
+ * @returns {Promise<void>}
181
+ */
182
+ async onEditorKeyEnter(data, field) {
183
+ let me = this;
184
+
185
+ await me.submitEditor();
186
+ await me.timeout(20);
187
+ me.selectCell(data)
188
+ }
189
+
190
+ /**
191
+ * @param {Object} data
192
+ * @param {Neo.form.field.Base} field
193
+ * @returns {Promise<void>}
194
+ */
195
+ async onEditorKeyEscape(data, field) {
196
+ let me = this;
197
+
198
+ await me.unmountEditor();
199
+ await me.timeout(20);
200
+ me.selectCell(data)
201
+ }
202
+
203
+ /**
204
+ * @param {Object} event
205
+ * @param {Neo.form.field.Base} field
206
+ * @returns {Promise<void>}
207
+ */
208
+ async onEditorKeyTab(event, field) {
209
+ let me = this,
210
+ {store} = me.owner,
211
+ oldIndex = store.indexOf(field.record),
212
+ countRecords = store.getCount(),
213
+ index = (oldIndex + (event.altKey ? -1 : 1) + countRecords) % countRecords,
214
+ record = store.getAt(index);
215
+
216
+ await me.submitEditor();
217
+ await me.mountEditor(record, field.dataField)
218
+ }
219
+
220
+ /**
221
+ * @param {Object} data
222
+ * @returns {Promise<void>}
223
+ */
224
+ async onFocusLeave(data) {
225
+ await this.unmountEditor()
226
+ }
227
+
228
+ /**
229
+ * @param {Object} data
230
+ */
231
+ onSelectionChange(data) {
232
+ // todo: Once we separate cell selections & focus, we can use this event to mount editors
233
+ // console.log('onSelectionChange', data);
234
+ }
235
+
236
+ /**
237
+ * @param {Object} data
238
+ */
239
+ onSelectionModelChange(data) {
240
+ let selectionModel = data.value;
241
+
242
+ if (selectionModel.ntype.includes('cell')) {
243
+ selectionModel.on('selectionChange', this.onSelectionChange, this)
244
+ }
245
+ }
246
+
247
+ /**
248
+ * @param {Object} data
249
+ * @returns {Promise<void>}
250
+ */
251
+ async onTableKeyDown(data) {
252
+ let me = this,
253
+ {target} = data,
254
+ tableView = me.owner.view,
255
+ dataField, record;
256
+
257
+ if (!me.mountedEditor && target.cls?.includes('neo-selected')) {
258
+ dataField = tableView.getCellDataField(target.id);
259
+ record = tableView.getRecord(target.id);
260
+
261
+ await me.mountEditor(record, dataField)
262
+ }
263
+ }
264
+
265
+ /**
266
+ * @param {Object} data
267
+ * @param {Object[]} data.path
268
+ */
269
+ selectCell({path}) {
270
+ let me = this,
271
+ {selectionModel} = me.owner,
272
+ i = 0,
273
+ len = path.length,
274
+ cellId;
275
+
276
+ for (; i < len; i++) {
277
+ if (path[i].cls?.includes(me.cellCls)) {
278
+ cellId = path[i].id;
279
+ break
280
+ }
281
+ }
282
+
283
+ if (cellId) {
284
+ selectionModel?.deselect(cellId, true); // the cell might still count as selected => silent deselect first
285
+ selectionModel?.select(cellId);
286
+ me.owner.focus(cellId)
287
+ }
288
+ }
289
+
290
+ /**
291
+ * If the field is valid:
292
+ * Updates the record field, in case the value of the editor changed,
293
+ * otherwise unmounts the editor
294
+ * @returns {Promise<void>}
295
+ */
296
+ async submitEditor() {
297
+ let me = this,
298
+ field = me.mountedEditor;
299
+
300
+ if (field?.isValid()) {
301
+ if (field.isDirty) {
302
+ me.mountedEditor = null;
303
+ field.record[field.dataField] = field.getSubmitValue()
304
+ } else {
305
+ await me.unmountEditor()
306
+ }
307
+ }
308
+ }
309
+
310
+ /**
311
+ * @returns {Promise<void>}
312
+ */
313
+ async unmountEditor() {
314
+ if (!this.mountedEditor) {
315
+ return
316
+ }
317
+
318
+ let me = this,
319
+ record = me.mountedEditor.record,
320
+ tableView = me.owner.view,
321
+ rowIndex = tableView.store.indexOf(record);
322
+
323
+ me.mountedEditor = null;
324
+
325
+ tableView.vdom.cn[rowIndex] = tableView.createRow({record, rowIndex});
326
+ await tableView.promiseUpdate()
327
+ }
328
+ }
329
+
330
+ export default Neo.setupClass(CellEditing);
@@ -8,7 +8,7 @@ import NeoArray from '../util/Array.mjs';
8
8
  * @class Neo.toolbar.Base
9
9
  * @extends Neo.container.Base
10
10
  */
11
- class Base extends Container {
11
+ class Toolbar extends Container {
12
12
  /**
13
13
  * Valid values for dock
14
14
  * @member {String[]} dockPositions=['top','right','bottom','left', null]
@@ -223,4 +223,4 @@ class Base extends Container {
223
223
  }
224
224
  }
225
225
 
226
- export default Neo.setupClass(Base);
226
+ export default Neo.setupClass(Toolbar);
@@ -1,7 +1,7 @@
1
1
  import ClassSystemUtil from '../util/ClassSystem.mjs';
2
2
  import HashHistory from '../util/HashHistory.mjs';
3
3
  import Store from '../data/Store.mjs';
4
- import Toolbar from '../toolbar/Base.mjs';
4
+ import Toolbar from './Base.mjs';
5
5
 
6
6
  /**
7
7
  * @class Neo.toolbar.Breadcrumb
@@ -8,7 +8,7 @@ let singletons = {};
8
8
  * @class Neo.tooltip.Base
9
9
  * @extends Neo.container.Base
10
10
  */
11
- class Base extends Container {
11
+ class Tooltip extends Container {
12
12
  static config = {
13
13
  /**
14
14
  * @member {String} className='Neo.tooltip.Base'
@@ -368,4 +368,4 @@ class Base extends Container {
368
368
  }
369
369
  }
370
370
 
371
- export default Neo.setupClass(Base);
371
+ export default Neo.setupClass(Tooltip);
@@ -75,10 +75,14 @@ class KeyNavigation extends Base {
75
75
  upperCaseKey = me.parseUpperCaseKey(upperCaseKey);
76
76
 
77
77
  me.keys.forEach(key => {
78
- scope = Neo.get(key.scope);
78
+ scope = Neo.isString(key.scope) ? Neo.get(key.scope) : key.scope;
79
79
 
80
80
  if (key.key.toUpperCase() === upperCaseKey) {
81
- scope[key.fn]?.apply(scope, [data])
81
+ if (Neo.isFunction(key.fn)) {
82
+ key.fn.apply(scope, [data, me.component])
83
+ } else {
84
+ scope[key.fn]?.apply(scope, [data, me.component])
85
+ }
82
86
  }
83
87
  })
84
88
  }
@@ -94,12 +98,14 @@ class KeyNavigation extends Base {
94
98
  keyArray = [];
95
99
 
96
100
  if (componentId) {
97
- Object.entries(value).forEach(([key, value]) => {
98
- keyArray.push({
99
- fn : value,
100
- key,
101
- scope: componentId // todo: support VCs later on
102
- })
101
+ Object.entries(value).forEach(([key, val]) => {
102
+ if (key !== 'scope') {
103
+ keyArray.push({
104
+ fn : val,
105
+ key,
106
+ scope: value.scope || componentId // todo: support VCs later on
107
+ })
108
+ }
103
109
  });
104
110
 
105
111
  value = keyArray
@@ -1,4 +1,4 @@
1
- import CoreBase from '../core/Base.mjs';
1
+ import Base from '../core/Base.mjs';
2
2
  import Observable from '../core/Observable.mjs';
3
3
  import Message from './Message.mjs';
4
4
  import RemoteMethodAccess from './mixin/RemoteMethodAccess.mjs';
@@ -9,7 +9,7 @@ import RemoteMethodAccess from './mixin/RemoteMethodAccess.mjs';
9
9
  * @extends Neo.core.Base
10
10
  * @abstract
11
11
  */
12
- class Base extends CoreBase {
12
+ class Worker extends Base {
13
13
  static config = {
14
14
  /**
15
15
  * @member {String} className='Neo.worker.Base'
@@ -309,4 +309,4 @@ class Base extends CoreBase {
309
309
  }
310
310
  }
311
311
 
312
- export default Neo.setupClass(Base);
312
+ export default Neo.setupClass(Worker);
@@ -1,3 +0,0 @@
1
- The grid implementation using div tags is currently on hold in favor for the table implementation using a table tag.
2
-
3
- The BufferedGrid implementation is currently scoped for neo v5.