neo.mjs 8.20.1 → 8.21.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.
- package/apps/ServiceWorker.mjs +2 -2
- package/apps/portal/index.html +1 -1
- package/apps/portal/view/home/FooterContainer.mjs +1 -1
- package/examples/ServiceWorker.mjs +2 -2
- package/examples/grid/bigData/ControlsContainer.mjs +57 -0
- package/examples/grid/bigData/GridContainer.mjs +1 -1
- package/examples/grid/bigData/MainStore.mjs +12 -0
- package/examples/grid/cellEditing/MainContainer.mjs +21 -9
- package/package.json +3 -3
- package/resources/scss/src/grid/View.scss +5 -3
- package/resources/scss/src/grid/plugin/CellEditing.scss +1 -0
- package/src/DefaultConfig.mjs +11 -2
- package/src/collection/Base.mjs +6 -3
- package/src/data/Store.mjs +32 -20
- package/src/form/field/Base.mjs +4 -0
- package/src/grid/Scrollbar.mjs +3 -2
- package/src/grid/View.mjs +16 -33
- package/src/grid/header/Button.mjs +1 -0
- package/src/grid/plugin/CellEditing.mjs +5 -1
- package/src/main/DomAccess.mjs +55 -42
- package/src/main/DomEvents.mjs +1 -1
- package/src/main/mixin/DeltaUpdates.mjs +14 -14
- package/src/selection/grid/BaseModel.mjs +16 -0
- package/src/selection/grid/CellModel.mjs +4 -4
- package/src/selection/grid/ColumnModel.mjs +2 -2
- package/src/selection/grid/RowModel.mjs +2 -2
- package/src/table/plugin/CellEditing.mjs +33 -22
package/apps/ServiceWorker.mjs
CHANGED
package/apps/portal/index.html
CHANGED
@@ -84,6 +84,33 @@ class ControlsContainer extends Container {
|
|
84
84
|
style : {marginTop: '.3em'},
|
85
85
|
value : 'neo-theme-light',
|
86
86
|
valueLabelText: 'Light'
|
87
|
+
}, {
|
88
|
+
ntype: 'label',
|
89
|
+
style: {marginTop: '2em'},
|
90
|
+
text : 'Filters'
|
91
|
+
}, {
|
92
|
+
ntype : 'textfield',
|
93
|
+
clearable : true,
|
94
|
+
editable : true,
|
95
|
+
labelText : 'Firstname',
|
96
|
+
labelWidth: 90,
|
97
|
+
listeners : {change: 'up.onFilterFieldChange'},
|
98
|
+
name : 'firstname',
|
99
|
+
style : {marginTop: '.3em'},
|
100
|
+
width : 200
|
101
|
+
}, {
|
102
|
+
ntype : 'textfield',
|
103
|
+
clearable : true,
|
104
|
+
editable : true,
|
105
|
+
labelText : 'Lastname',
|
106
|
+
labelWidth: 90,
|
107
|
+
listeners : {change: 'up.onFilterFieldChange'},
|
108
|
+
name : 'lastname',
|
109
|
+
width : 200
|
110
|
+
}, {
|
111
|
+
ntype : 'label',
|
112
|
+
reference: 'count-rows-label',
|
113
|
+
style : {marginTop: '1em'}
|
87
114
|
}]
|
88
115
|
}],
|
89
116
|
/**
|
@@ -154,6 +181,36 @@ class ControlsContainer extends Container {
|
|
154
181
|
|
155
182
|
me.grid.toggleCls('neo-extend-margin-right');
|
156
183
|
}
|
184
|
+
|
185
|
+
onConstructed() {
|
186
|
+
super.onConstructed();
|
187
|
+
|
188
|
+
let me = this,
|
189
|
+
{store} = me.grid;
|
190
|
+
|
191
|
+
store.on({
|
192
|
+
filter: me.updateRowsLabel,
|
193
|
+
load : me.updateRowsLabel,
|
194
|
+
scope : me
|
195
|
+
});
|
196
|
+
|
197
|
+
store.getCount() > 0 && me.updateRowsLabel()
|
198
|
+
}
|
199
|
+
|
200
|
+
/**
|
201
|
+
* @param {Object} data
|
202
|
+
*/
|
203
|
+
onFilterFieldChange(data) {
|
204
|
+
this.grid.store.getFilter(data.component.name).value = data.value
|
205
|
+
}
|
206
|
+
|
207
|
+
updateRowsLabel() {
|
208
|
+
let {store} = this.grid;
|
209
|
+
|
210
|
+
if (!store.isLoading) {
|
211
|
+
this.getItem('count-rows-label').text = 'Filtered rows: ' + store.getCount()
|
212
|
+
}
|
213
|
+
}
|
157
214
|
}
|
158
215
|
|
159
216
|
export default Neo.setupClass(ControlsContainer);
|
@@ -47,7 +47,7 @@ class GridContainer extends BaseGridContainer {
|
|
47
47
|
afterSetAmountColumns(value, oldValue) {
|
48
48
|
let i = 4,
|
49
49
|
columns = [
|
50
|
-
{dataField: 'id', text: '#', width: 60},
|
50
|
+
{dataField: 'id', text: '#', width: 60, renderer({record, store}) {return store.indexOf(record) + 1}},
|
51
51
|
{cellAlign: 'left', dataField: 'firstname', defaultSortDirection: 'ASC', text: 'Firstname', width: 150},
|
52
52
|
{cellAlign: 'left', dataField: 'lastname', defaultSortDirection: 'ASC', text: 'Lastname', width: 150}
|
53
53
|
];
|
@@ -20,6 +20,18 @@ class MainStore extends Store {
|
|
20
20
|
* @member {Number} amountRows_=1000
|
21
21
|
*/
|
22
22
|
amountRows_: 1000,
|
23
|
+
/**
|
24
|
+
* @member {Object[]} filters
|
25
|
+
*/
|
26
|
+
filters: [{
|
27
|
+
property: 'firstname',
|
28
|
+
operator: 'like',
|
29
|
+
value : null
|
30
|
+
}, {
|
31
|
+
property: 'lastname',
|
32
|
+
operator: 'like',
|
33
|
+
value : null
|
34
|
+
}],
|
23
35
|
/**
|
24
36
|
* @member {Neo.data.Model} model=Model
|
25
37
|
*/
|
@@ -44,7 +44,8 @@ class MainContainer extends ConfigurationViewport {
|
|
44
44
|
* @returns {Object[]}
|
45
45
|
*/
|
46
46
|
createConfigurationComponents() {
|
47
|
-
let me
|
47
|
+
let me = this,
|
48
|
+
{selectionModel} = me.exampleComponent.view;
|
48
49
|
|
49
50
|
const selectionModelRadioDefaults = {
|
50
51
|
module : Radio,
|
@@ -64,25 +65,25 @@ class MainContainer extends ConfigurationViewport {
|
|
64
65
|
value : me.exampleComponent.height
|
65
66
|
}, {
|
66
67
|
...selectionModelRadioDefaults,
|
67
|
-
checked :
|
68
|
+
checked : selectionModel.ntype === 'selection-grid-cellmodel',
|
68
69
|
labelText : 'selectionModel',
|
69
|
-
listeners : {change: me.
|
70
|
+
listeners : {change: me.onRadioViewChange.bind(me, 'selectionModel', CellModel)},
|
70
71
|
style : {marginTop: '10px'},
|
71
72
|
valueLabelText: 'Cell'
|
72
73
|
}, {
|
73
74
|
...selectionModelRadioDefaults,
|
74
|
-
checked :
|
75
|
-
listeners : {change: me.
|
75
|
+
checked : selectionModel.ntype === 'selection-grid-cellcolumnmodel',
|
76
|
+
listeners : {change: me.onRadioViewChange.bind(me, 'selectionModel', CellColumnModel)},
|
76
77
|
valueLabelText: 'Cell & Column'
|
77
78
|
}, {
|
78
79
|
...selectionModelRadioDefaults,
|
79
|
-
checked :
|
80
|
-
listeners : {change: me.
|
80
|
+
checked : selectionModel.ntype === 'selection-grid-cellrowmodel',
|
81
|
+
listeners : {change: me.onRadioViewChange.bind(me, 'selectionModel', CellRowModel)},
|
81
82
|
valueLabelText: 'Cell & Row'
|
82
83
|
}, {
|
83
84
|
...selectionModelRadioDefaults,
|
84
|
-
checked :
|
85
|
-
listeners : {change: me.
|
85
|
+
checked : selectionModel.ntype === 'selection-grid-cellcolumnrowmodel',
|
86
|
+
listeners : {change: me.onRadioViewChange.bind(me, 'selectionModel', CellColumnRowModel)},
|
86
87
|
valueLabelText: 'Cell & Column & Row'
|
87
88
|
}, {
|
88
89
|
module : CheckBox,
|
@@ -173,6 +174,17 @@ class MainContainer extends ConfigurationViewport {
|
|
173
174
|
onPluginConfigChange(config, opts) {
|
174
175
|
this.exampleComponent.getPlugin('grid-cell-editing')[config] = opts.value
|
175
176
|
}
|
177
|
+
|
178
|
+
/**
|
179
|
+
* @param {String} config
|
180
|
+
* @param {String} value
|
181
|
+
* @param {Object} opts
|
182
|
+
*/
|
183
|
+
onRadioViewChange(config, value, opts) {
|
184
|
+
if (opts.value === true) { // we only want to listen to check events, not uncheck
|
185
|
+
this.exampleComponent.view[config] = value
|
186
|
+
}
|
187
|
+
}
|
176
188
|
}
|
177
189
|
|
178
190
|
export default Neo.setupClass(MainContainer);
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "neo.mjs",
|
3
|
-
"version": "8.
|
3
|
+
"version": "8.21.0",
|
4
4
|
"description": "The webworkers driven UI framework",
|
5
5
|
"type": "module",
|
6
6
|
"repository": {
|
@@ -55,11 +55,11 @@
|
|
55
55
|
"fs-extra": "^11.3.0",
|
56
56
|
"highlightjs-line-numbers.js": "^2.9.0",
|
57
57
|
"inquirer": "^12.4.1",
|
58
|
-
"marked": "^15.0.
|
58
|
+
"marked": "^15.0.7",
|
59
59
|
"monaco-editor": "0.50.0",
|
60
60
|
"neo-jsdoc": "1.0.1",
|
61
61
|
"neo-jsdoc-x": "1.0.5",
|
62
|
-
"postcss": "^8.5.
|
62
|
+
"postcss": "^8.5.2",
|
63
63
|
"sass": "^1.84.0",
|
64
64
|
"siesta-lite": "5.5.2",
|
65
65
|
"url": "^0.11.4",
|
@@ -98,9 +98,11 @@
|
|
98
98
|
|
99
99
|
.neo-mouse {
|
100
100
|
.neo-grid-row {
|
101
|
-
&:
|
102
|
-
|
103
|
-
|
101
|
+
&:not(.neo-selected) {
|
102
|
+
&:hover {
|
103
|
+
.neo-grid-cell {
|
104
|
+
background-color: var(--grid-cell-background-color-hover);
|
105
|
+
}
|
104
106
|
}
|
105
107
|
}
|
106
108
|
}
|
package/src/DefaultConfig.mjs
CHANGED
@@ -18,6 +18,15 @@ const DefaultConfig = {
|
|
18
18
|
* @type Boolean
|
19
19
|
*/
|
20
20
|
applyBodyCls: true,
|
21
|
+
/**
|
22
|
+
* true will apply 'position: fixed' to the html tag itself
|
23
|
+
* See: https://github.com/neomjs/neo/issues/6429
|
24
|
+
* @default true
|
25
|
+
* @memberOf! module:Neo
|
26
|
+
* @name config.applyFixedPositionToHtmlTag
|
27
|
+
* @type Boolean
|
28
|
+
*/
|
29
|
+
applyFixedPositionToHtmlTag: true,
|
21
30
|
/**
|
22
31
|
* Path to your app.mjs file. You can create multiple apps there if needed.
|
23
32
|
* @default null
|
@@ -262,12 +271,12 @@ const DefaultConfig = {
|
|
262
271
|
useVdomWorker: true,
|
263
272
|
/**
|
264
273
|
* buildScripts/injectPackageVersion.mjs will update this value
|
265
|
-
* @default '8.
|
274
|
+
* @default '8.21.0'
|
266
275
|
* @memberOf! module:Neo
|
267
276
|
* @name config.version
|
268
277
|
* @type String
|
269
278
|
*/
|
270
|
-
version: '8.
|
279
|
+
version: '8.21.0'
|
271
280
|
};
|
272
281
|
|
273
282
|
Object.assign(DefaultConfig, {
|
package/src/collection/Base.mjs
CHANGED
@@ -234,7 +234,7 @@ class Collection extends Base {
|
|
234
234
|
me.sortDirections = [];
|
235
235
|
me.sortProperties = [];
|
236
236
|
|
237
|
-
me.sorters.forEach(sorter => {
|
237
|
+
me.sorters.forEach(sorter => {
|
238
238
|
me.sortDirections.push(sorter.directionMultiplier);
|
239
239
|
me.sortProperties.push(sorter.property)
|
240
240
|
})
|
@@ -1050,9 +1050,12 @@ class Collection extends Base {
|
|
1050
1050
|
onMutate(opts) {
|
1051
1051
|
let me = this;
|
1052
1052
|
|
1053
|
-
|
1053
|
+
// todo: inspect the bubbling chain
|
1054
|
+
/*if (opts.preventBubbleUp) {
|
1054
1055
|
me.preventBubbleUp = true
|
1055
|
-
}
|
1056
|
+
}*/
|
1057
|
+
|
1058
|
+
me.preventBubbleUp = true;
|
1056
1059
|
|
1057
1060
|
me.splice(null, opts.removedItems, opts.addedItems)
|
1058
1061
|
}
|
package/src/data/Store.mjs
CHANGED
@@ -132,7 +132,7 @@ class Store extends Base {
|
|
132
132
|
* @returns {Number} the collection count
|
133
133
|
*/
|
134
134
|
add(item) {
|
135
|
-
return super.add(this.
|
135
|
+
return super.add(this.createRecord(item))
|
136
136
|
}
|
137
137
|
|
138
138
|
/**
|
@@ -161,6 +161,8 @@ class Store extends Base {
|
|
161
161
|
me.initialData = [...value]
|
162
162
|
}
|
163
163
|
|
164
|
+
me.isLoading = false;
|
165
|
+
|
164
166
|
me.add(value)
|
165
167
|
}
|
166
168
|
}
|
@@ -256,22 +258,9 @@ class Store extends Base {
|
|
256
258
|
*/
|
257
259
|
beforeSetData(value, oldValue) {
|
258
260
|
if (value) {
|
259
|
-
|
260
|
-
value = [value]
|
261
|
-
}
|
262
|
-
|
263
|
-
let me = this,
|
264
|
-
i = 0,
|
265
|
-
len = value.length,
|
266
|
-
item;
|
267
|
-
|
268
|
-
for (; i < len; i++) {
|
269
|
-
item = value[i]
|
261
|
+
this.isLoading = true;
|
270
262
|
|
271
|
-
|
272
|
-
value[i] = RecordFactory.createRecord(me.model, item)
|
273
|
-
}
|
274
|
-
}
|
263
|
+
value = this.createRecord(value)
|
275
264
|
}
|
276
265
|
|
277
266
|
return value
|
@@ -304,10 +293,34 @@ class Store extends Base {
|
|
304
293
|
}
|
305
294
|
|
306
295
|
/**
|
307
|
-
*
|
296
|
+
* Converts an object or array of objects into records
|
297
|
+
* @param {Object|Object[]} config
|
298
|
+
* @returns {Object|Object[]} Array in case an array was passed
|
308
299
|
*/
|
309
300
|
createRecord(config) {
|
310
|
-
|
301
|
+
let isArray = true;
|
302
|
+
|
303
|
+
if (config) {
|
304
|
+
if (!Array.isArray(config)) {
|
305
|
+
isArray = false;
|
306
|
+
config = [config]
|
307
|
+
}
|
308
|
+
|
309
|
+
let me = this,
|
310
|
+
i = 0,
|
311
|
+
len = config.length,
|
312
|
+
item;
|
313
|
+
|
314
|
+
for (; i < len; i++) {
|
315
|
+
item = config[i]
|
316
|
+
|
317
|
+
if (!RecordFactory.isRecord(item)) {
|
318
|
+
config[i] = RecordFactory.createRecord(me.model, item)
|
319
|
+
}
|
320
|
+
}
|
321
|
+
}
|
322
|
+
|
323
|
+
return isArray ? config : config[0]
|
311
324
|
}
|
312
325
|
|
313
326
|
/**
|
@@ -375,8 +388,7 @@ class Store extends Base {
|
|
375
388
|
onCollectionMutate(opts) {
|
376
389
|
let me = this;
|
377
390
|
|
378
|
-
if (me.configsApplied) {
|
379
|
-
// console.log('onCollectionMutate', opts);
|
391
|
+
if (me.configsApplied && !me.isLoading) {
|
380
392
|
me.fire('load', me.items)
|
381
393
|
}
|
382
394
|
}
|
package/src/form/field/Base.mjs
CHANGED
@@ -33,6 +33,10 @@ class Field extends Component {
|
|
33
33
|
* @member {String|null} formGroup_=null
|
34
34
|
*/
|
35
35
|
formGroup_: null,
|
36
|
+
/**
|
37
|
+
* @member {String|null} keys={}
|
38
|
+
*/
|
39
|
+
keys: {},
|
36
40
|
/**
|
37
41
|
* True indicates that a user has interacted with the form field
|
38
42
|
* @member {Boolean} isTouched_=false
|
package/src/grid/Scrollbar.mjs
CHANGED
@@ -93,8 +93,9 @@ class GridScrollbar extends Component {
|
|
93
93
|
let me = this;
|
94
94
|
|
95
95
|
value.on({
|
96
|
-
|
97
|
-
|
96
|
+
filter: me.updateScrollHeight,
|
97
|
+
load : me.updateScrollHeight,
|
98
|
+
scope : me
|
98
99
|
});
|
99
100
|
|
100
101
|
value.getCount() > 0 && me.updateScrollHeight()
|
package/src/grid/View.mjs
CHANGED
@@ -336,25 +336,6 @@ class GridView extends Component {
|
|
336
336
|
oldValue !== undefined && this.createViewData()
|
337
337
|
}
|
338
338
|
|
339
|
-
/**
|
340
|
-
* Triggered after the store config got changed
|
341
|
-
* @param {Neo.data.Store|null} value
|
342
|
-
* @param {Neo.data.Store|null} oldValue
|
343
|
-
* @protected
|
344
|
-
*/
|
345
|
-
afterSetStore(value, oldValue) {
|
346
|
-
if (value) {
|
347
|
-
let me = this;
|
348
|
-
|
349
|
-
value.on({
|
350
|
-
load : me.updateScrollHeight,
|
351
|
-
scope: me
|
352
|
-
});
|
353
|
-
|
354
|
-
value.getCount() > 0 && me.updateScrollHeight()
|
355
|
-
}
|
356
|
-
}
|
357
|
-
|
358
339
|
/**
|
359
340
|
* Triggered after the visibleColumns config got changed
|
360
341
|
* @param {Number[]} value
|
@@ -372,18 +353,19 @@ class GridView extends Component {
|
|
372
353
|
* @param {String} [data.cellId]
|
373
354
|
* @param {Object} data.column
|
374
355
|
* @param {Number} data.columnIndex
|
375
|
-
* @param {Neo.grid.Container} data.gridContainer
|
376
356
|
* @param {Object} data.record
|
377
357
|
* @param {Number} data.rowIndex
|
378
358
|
* @returns {Object}
|
379
359
|
*/
|
380
360
|
applyRendererOutput(data) {
|
381
|
-
let {cellId, column, columnIndex,
|
382
|
-
me
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
361
|
+
let {cellId, column, columnIndex, record, rowIndex} = data,
|
362
|
+
me = this,
|
363
|
+
gridContainer = me.parent,
|
364
|
+
{store} = me,
|
365
|
+
cellCls = ['neo-grid-cell'],
|
366
|
+
colspan = record[me.colspanField],
|
367
|
+
{dataField} = column,
|
368
|
+
fieldValue = record[dataField],
|
387
369
|
cellConfig, rendererOutput;
|
388
370
|
|
389
371
|
if (fieldValue === null || fieldValue === undefined) {
|
@@ -397,6 +379,7 @@ class GridView extends Component {
|
|
397
379
|
gridContainer,
|
398
380
|
record,
|
399
381
|
rowIndex,
|
382
|
+
store,
|
400
383
|
value: fieldValue
|
401
384
|
});
|
402
385
|
|
@@ -541,7 +524,7 @@ class GridView extends Component {
|
|
541
524
|
|
542
525
|
for (i=startIndex; i <= endIndex; i++) {
|
543
526
|
column = columns[i];
|
544
|
-
config = me.applyRendererOutput({column, columnIndex: i,
|
527
|
+
config = me.applyRendererOutput({column, columnIndex: i, record, rowIndex});
|
545
528
|
|
546
529
|
if (column.dock) {
|
547
530
|
config.cls = ['neo-locked', ...config.cls || []]
|
@@ -577,7 +560,7 @@ class GridView extends Component {
|
|
577
560
|
endIndex, i;
|
578
561
|
|
579
562
|
if (
|
580
|
-
|
563
|
+
store.isLoading ||
|
581
564
|
me.availableRows < 1 ||
|
582
565
|
me._containerWidth < 1 || // we are not checking me.containerWidth, since we want to ignore the config symbol
|
583
566
|
me.columnPositions.getCount() < 1 ||
|
@@ -597,6 +580,7 @@ class GridView extends Component {
|
|
597
580
|
|
598
581
|
me.parent.isLoading = false;
|
599
582
|
|
583
|
+
me.updateScrollHeight(true); // silent
|
600
584
|
me.update()
|
601
585
|
}
|
602
586
|
|
@@ -887,7 +871,6 @@ class GridView extends Component {
|
|
887
871
|
let me = this,
|
888
872
|
fieldNames = fields.map(field => field.name),
|
889
873
|
needsUpdate = false,
|
890
|
-
gridContainer = me.parent,
|
891
874
|
rowIndex = me.store.indexOf(record),
|
892
875
|
{selectionModel, vdom} = me,
|
893
876
|
cellId, cellNode, cellStyle, cellVdom, column, columnIndex;
|
@@ -910,7 +893,7 @@ class GridView extends Component {
|
|
910
893
|
cellStyle = cellNode.vdom.style;
|
911
894
|
column = me.getColumn(field.name);
|
912
895
|
columnIndex = cellNode.index;
|
913
|
-
cellVdom = me.applyRendererOutput({cellId, column, columnIndex,
|
896
|
+
cellVdom = me.applyRendererOutput({cellId, column, columnIndex, record, rowIndex});
|
914
897
|
needsUpdate = true;
|
915
898
|
|
916
899
|
// The cell-positioning logic happens outside applyRendererOutput()
|
@@ -958,16 +941,16 @@ class GridView extends Component {
|
|
958
941
|
}
|
959
942
|
|
960
943
|
/**
|
961
|
-
*
|
944
|
+
* @param {Boolean} silent=false
|
962
945
|
*/
|
963
|
-
updateScrollHeight() {
|
946
|
+
updateScrollHeight(silent=false) {
|
964
947
|
let me = this,
|
965
948
|
countRecords = me.store.getCount(),
|
966
949
|
{rowHeight} = me;
|
967
950
|
|
968
951
|
if (countRecords > 0 && rowHeight > 0) {
|
969
952
|
me.vdom.cn[0].height = `${(countRecords + 1) * rowHeight}px`;
|
970
|
-
me.update()
|
953
|
+
!silent && me.update()
|
971
954
|
}
|
972
955
|
}
|
973
956
|
|
@@ -235,6 +235,7 @@ class Button extends BaseButton {
|
|
235
235
|
* @param {Neo.grid.Container} data.gridContainer
|
236
236
|
* @param {Object} data.record
|
237
237
|
* @param {Number} data.rowIndex
|
238
|
+
* @param {Neo.data.Store} data.store
|
238
239
|
* @param {Number|String} data.value
|
239
240
|
* @returns {*}
|
240
241
|
*/
|
@@ -23,7 +23,11 @@ class CellEditing extends BaseCellEditing {
|
|
23
23
|
/**
|
24
24
|
* @member {String[]} editorCls=['neo-grid-editor']
|
25
25
|
*/
|
26
|
-
editorCls: ['neo-grid-editor']
|
26
|
+
editorCls: ['neo-grid-editor'],
|
27
|
+
/**
|
28
|
+
* @member {Boolean} focusCells=true
|
29
|
+
*/
|
30
|
+
focusCells: true
|
27
31
|
}
|
28
32
|
}
|
29
33
|
|
package/src/main/DomAccess.mjs
CHANGED
@@ -165,47 +165,6 @@ class DomAccess extends Base {
|
|
165
165
|
me.syncAligns = me.syncAligns.bind(me)
|
166
166
|
}
|
167
167
|
|
168
|
-
/**
|
169
|
-
*
|
170
|
-
*/
|
171
|
-
initGlobalListeners() {
|
172
|
-
let me = this;
|
173
|
-
|
174
|
-
document.addEventListener('blur', me.onDocumentBlur .bind(me), capturePassive);
|
175
|
-
document.addEventListener('keydown', me.onDocumentKeyDown .bind(me), capturePassive);
|
176
|
-
document.addEventListener('keyup', me.onDocumentKeyUp .bind(me), capturePassive);
|
177
|
-
document.addEventListener('mousedown', me.onDocumentMouseDown.bind(me), {capture : true})
|
178
|
-
}
|
179
|
-
|
180
|
-
onDocumentMouseDown(e) {
|
181
|
-
let focusController = e.target?.closest('[data-focus]');
|
182
|
-
|
183
|
-
// data-focus on an element means reject mousedown gestures, and move focus
|
184
|
-
// to the referenced element.
|
185
|
-
if (focusController) {
|
186
|
-
e.preventDefault();
|
187
|
-
document.getElementById(focusController.dataset.focus)?.focus()
|
188
|
-
}
|
189
|
-
}
|
190
|
-
|
191
|
-
onDocumentKeyDown(keyEvent) {
|
192
|
-
if (modifierKeys[keyEvent.key]) {
|
193
|
-
// e.g. Neo.isShiftKeyDown = true or Neo.isControlKeyDown = true.
|
194
|
-
// Selection can consult this value
|
195
|
-
Neo[`${StringUtil.uncapitalize(keyEvent.key)}KeyDown`] = true;
|
196
|
-
}
|
197
|
-
}
|
198
|
-
|
199
|
-
onDocumentKeyUp(keyEvent) {
|
200
|
-
if (modifierKeys[keyEvent.key]) {
|
201
|
-
Neo[`${StringUtil.uncapitalize(keyEvent.key)}KeyDown`] = false;
|
202
|
-
}
|
203
|
-
}
|
204
|
-
|
205
|
-
onDocumentBlur() {
|
206
|
-
Neo.altKeyDown = Neo.controlKeyDown = Neo.metaKeyDown = Neo.shiftKeyDown = false;
|
207
|
-
}
|
208
|
-
|
209
168
|
/**
|
210
169
|
* @param {Object} alignSpec
|
211
170
|
*/
|
@@ -564,6 +523,18 @@ class DomAccess extends Base {
|
|
564
523
|
}
|
565
524
|
}
|
566
525
|
|
526
|
+
/**
|
527
|
+
*
|
528
|
+
*/
|
529
|
+
initGlobalListeners() {
|
530
|
+
let me = this;
|
531
|
+
|
532
|
+
document.addEventListener('blur', me.onDocumentBlur .bind(me), capturePassive);
|
533
|
+
document.addEventListener('keydown', me.onDocumentKeyDown .bind(me), capturePassive);
|
534
|
+
document.addEventListener('keyup', me.onDocumentKeyUp .bind(me), capturePassive);
|
535
|
+
document.addEventListener('mousedown', me.onDocumentMouseDown.bind(me), {capture : true})
|
536
|
+
}
|
537
|
+
|
567
538
|
/**
|
568
539
|
* @param {HTMLElement} el
|
569
540
|
* @returns {Boolean}
|
@@ -715,6 +686,33 @@ class DomAccess extends Base {
|
|
715
686
|
}
|
716
687
|
}
|
717
688
|
|
689
|
+
/**
|
690
|
+
*
|
691
|
+
*/
|
692
|
+
onDocumentBlur() {
|
693
|
+
Neo.altKeyDown = Neo.controlKeyDown = Neo.metaKeyDown = Neo.shiftKeyDown = false
|
694
|
+
}
|
695
|
+
|
696
|
+
/**
|
697
|
+
* @param {KeyboardEvent} keyEvent
|
698
|
+
*/
|
699
|
+
onDocumentKeyDown(keyEvent) {
|
700
|
+
if (modifierKeys[keyEvent.key]) {
|
701
|
+
// e.g. Neo.isShiftKeyDown = true or Neo.isControlKeyDown = true.
|
702
|
+
// Selection can consult this value
|
703
|
+
Neo[`${StringUtil.uncapitalize(keyEvent.key)}KeyDown`] = true
|
704
|
+
}
|
705
|
+
}
|
706
|
+
|
707
|
+
/**
|
708
|
+
* @param {KeyboardEvent} keyEvent
|
709
|
+
*/
|
710
|
+
onDocumentKeyUp(keyEvent) {
|
711
|
+
if (modifierKeys[keyEvent.key]) {
|
712
|
+
Neo[`${StringUtil.uncapitalize(keyEvent.key)}KeyDown`] = false
|
713
|
+
}
|
714
|
+
}
|
715
|
+
|
718
716
|
/**
|
719
717
|
* @param {Array} mutations
|
720
718
|
*/
|
@@ -733,11 +731,26 @@ class DomAccess extends Base {
|
|
733
731
|
}
|
734
732
|
}
|
735
733
|
|
734
|
+
/**
|
735
|
+
* @param {MouseEvent} e
|
736
|
+
*/
|
737
|
+
onDocumentMouseDown(e) {
|
738
|
+
let focusController = e.target?.closest('[data-focus]');
|
739
|
+
|
740
|
+
// data-focus on an element means reject mousedown gestures, and move focus
|
741
|
+
// to the referenced element.
|
742
|
+
if (focusController) {
|
743
|
+
e.preventDefault();
|
744
|
+
document.getElementById(focusController.dataset.focus)?.focus()
|
745
|
+
}
|
746
|
+
}
|
747
|
+
|
736
748
|
/**
|
737
749
|
*
|
738
750
|
*/
|
739
751
|
onDomContentLoaded() {
|
740
|
-
Neo.config.applyBodyCls && this.applyBodyCls({cls: ['neo-body']})
|
752
|
+
Neo.config.applyBodyCls && this.applyBodyCls({cls: ['neo-body']});
|
753
|
+
Neo.config.applyFixedPositionToHtmlTag && document.documentElement.style.setProperty('position', 'fixed')
|
741
754
|
}
|
742
755
|
|
743
756
|
/**
|
package/src/main/DomEvents.mjs
CHANGED
@@ -514,7 +514,7 @@ class DomEvents extends Base {
|
|
514
514
|
if (
|
515
515
|
isInput &&
|
516
516
|
event.key === 'Tab' &&
|
517
|
-
me.testPathInclusion(event, ['neo-table-editor'], true)
|
517
|
+
me.testPathInclusion(event, ['neo-grid-editor', 'neo-table-editor'], true)
|
518
518
|
) {
|
519
519
|
event.preventDefault()
|
520
520
|
}
|
@@ -26,14 +26,16 @@ class DeltaUpdates extends Base {
|
|
26
26
|
len = attributes.length,
|
27
27
|
attribute;
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
29
|
+
if (node) {
|
30
|
+
for (; i < len; i++) {
|
31
|
+
attribute = attributes.item(i);
|
32
|
+
clone.setAttribute(attribute.nodeName, attribute.nodeValue)
|
33
|
+
}
|
33
34
|
|
34
|
-
|
35
|
+
clone.innerHTML= node.innerHTML;
|
35
36
|
|
36
|
-
|
37
|
+
node.parentNode.replaceChild(clone, node)
|
38
|
+
}
|
37
39
|
}
|
38
40
|
|
39
41
|
/**
|
@@ -41,7 +43,7 @@ class DeltaUpdates extends Base {
|
|
41
43
|
* @param {String} delta.id
|
42
44
|
*/
|
43
45
|
du_focusNode(delta) {
|
44
|
-
this.getElement(delta.id)
|
46
|
+
this.getElement(delta.id)?.focus()
|
45
47
|
}
|
46
48
|
|
47
49
|
/**
|
@@ -183,7 +185,7 @@ class DeltaUpdates extends Base {
|
|
183
185
|
let me = this,
|
184
186
|
node = me.getElement(delta.parentId);
|
185
187
|
|
186
|
-
node
|
188
|
+
node?.replaceChild(me.getElement(delta.toId), me.getElement(delta.fromId))
|
187
189
|
}
|
188
190
|
|
189
191
|
/**
|
@@ -195,7 +197,9 @@ class DeltaUpdates extends Base {
|
|
195
197
|
let me = this,
|
196
198
|
node = me.getElement(delta.id);
|
197
199
|
|
198
|
-
node
|
200
|
+
if (node) {
|
201
|
+
node.textContent = delta.value
|
202
|
+
}
|
199
203
|
}
|
200
204
|
|
201
205
|
/**
|
@@ -211,11 +215,7 @@ class DeltaUpdates extends Base {
|
|
211
215
|
let me = this,
|
212
216
|
node = me.getElementOrBody(delta.id);
|
213
217
|
|
214
|
-
if (
|
215
|
-
if (Neo.config.environment === 'development') {
|
216
|
-
console.warn('du_updateNode: node not found for id', delta.id)
|
217
|
-
}
|
218
|
-
} else {
|
218
|
+
if (node) {
|
219
219
|
Object.entries(delta).forEach(([prop, value]) => {
|
220
220
|
switch(prop) {
|
221
221
|
case 'attributes':
|
@@ -22,6 +22,22 @@ class BaseModel extends Model {
|
|
22
22
|
get dataFields() {
|
23
23
|
return this.view.parent.columns.map(column => column.dataField)
|
24
24
|
}
|
25
|
+
|
26
|
+
/**
|
27
|
+
* Checks if an event path contains a grid cell editor
|
28
|
+
* @param {Object} data
|
29
|
+
* @param {Object[]} data.path
|
30
|
+
* @returns {Boolean}
|
31
|
+
*/
|
32
|
+
hasEditorFocus({path}) {
|
33
|
+
for (const node of path) {
|
34
|
+
if (node.cls?.includes('neo-grid-editor')) {
|
35
|
+
return true
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
return false
|
40
|
+
}
|
25
41
|
}
|
26
42
|
|
27
43
|
export default Neo.setupClass(BaseModel);
|
@@ -54,28 +54,28 @@ class CellModel extends BaseModel {
|
|
54
54
|
* @param {Object} data
|
55
55
|
*/
|
56
56
|
onKeyDownDown(data) {
|
57
|
-
this.onNavKeyRow(1)
|
57
|
+
!this.hasEditorFocus(data) && this.onNavKeyRow(1)
|
58
58
|
}
|
59
59
|
|
60
60
|
/**
|
61
61
|
* @param {Object} data
|
62
62
|
*/
|
63
63
|
onKeyDownLeft(data) {
|
64
|
-
this.onNavKeyColumn(-1)
|
64
|
+
!this.hasEditorFocus(data) && this.onNavKeyColumn(-1)
|
65
65
|
}
|
66
66
|
|
67
67
|
/**
|
68
68
|
* @param {Object} data
|
69
69
|
*/
|
70
70
|
onKeyDownRight(data) {
|
71
|
-
this.onNavKeyColumn(1)
|
71
|
+
!this.hasEditorFocus(data) && this.onNavKeyColumn(1)
|
72
72
|
}
|
73
73
|
|
74
74
|
/**
|
75
75
|
* @param {Object} data
|
76
76
|
*/
|
77
77
|
onKeyDownUp(data) {
|
78
|
-
this.onNavKeyRow(-1)
|
78
|
+
!this.hasEditorFocus(data) && this.onNavKeyRow(-1)
|
79
79
|
}
|
80
80
|
|
81
81
|
/**
|
@@ -66,14 +66,14 @@ class ColumnModel extends BaseModel {
|
|
66
66
|
* @param {Object} data
|
67
67
|
*/
|
68
68
|
onKeyDownLeft(data) {
|
69
|
-
this.onNavKeyColumn(-1)
|
69
|
+
!this.hasEditorFocus(data) && this.onNavKeyColumn(-1)
|
70
70
|
}
|
71
71
|
|
72
72
|
/**
|
73
73
|
* @param {Object} data
|
74
74
|
*/
|
75
75
|
onKeyDownRight(data) {
|
76
|
-
this.onNavKeyColumn(1)
|
76
|
+
!this.hasEditorFocus(data) && this.onNavKeyColumn(1)
|
77
77
|
}
|
78
78
|
|
79
79
|
/**
|
@@ -55,14 +55,14 @@ class RowModel extends BaseModel {
|
|
55
55
|
* @param {Object} data
|
56
56
|
*/
|
57
57
|
onKeyDownDown(data) {
|
58
|
-
this.onNavKeyRow(1)
|
58
|
+
!this.hasEditorFocus(data) && this.onNavKeyRow(1)
|
59
59
|
}
|
60
60
|
|
61
61
|
/**
|
62
62
|
* @param {Object} data
|
63
63
|
*/
|
64
64
|
onKeyDownUp(data) {
|
65
|
-
this.onNavKeyRow(-1)
|
65
|
+
!this.hasEditorFocus(data) && this.onNavKeyRow(-1)
|
66
66
|
}
|
67
67
|
|
68
68
|
/**
|
@@ -29,7 +29,11 @@ class CellEditing extends Plugin {
|
|
29
29
|
/**
|
30
30
|
* @member {String[]} editorCls=['neo-table-editor']
|
31
31
|
*/
|
32
|
-
editorCls: ['neo-table-editor']
|
32
|
+
editorCls: ['neo-table-editor'],
|
33
|
+
/**
|
34
|
+
* @member {Boolean} focusCells=true
|
35
|
+
*/
|
36
|
+
focusCells: true
|
33
37
|
}
|
34
38
|
|
35
39
|
/**
|
@@ -110,7 +114,8 @@ class CellEditing extends Plugin {
|
|
110
114
|
cellNode = VdomUtil.find(view.vdom, cellId).vdom,
|
111
115
|
column = me.owner.headerToolbar.getColumn(dataField),
|
112
116
|
editor = me.editors[dataField],
|
113
|
-
value = record[dataField]
|
117
|
+
value = record[dataField],
|
118
|
+
keys;
|
114
119
|
|
115
120
|
if (me.mountedEditor) {
|
116
121
|
await me.unmountEditor();
|
@@ -133,15 +138,21 @@ class CellEditing extends Plugin {
|
|
133
138
|
value,
|
134
139
|
windowId,
|
135
140
|
|
136
|
-
keys: {
|
137
|
-
Enter : 'onEditorKeyEnter',
|
138
|
-
Escape: 'onEditorKeyEscape',
|
139
|
-
Tab : 'onEditorKeyTab',
|
140
|
-
scope : me
|
141
|
-
},
|
142
|
-
|
143
141
|
...column.editor
|
144
|
-
})
|
142
|
+
});
|
143
|
+
|
144
|
+
keys = {
|
145
|
+
Enter : 'onEditorKeyEnter',
|
146
|
+
Escape: 'onEditorKeyEscape',
|
147
|
+
Tab : 'onEditorKeyTab',
|
148
|
+
scope : me
|
149
|
+
};
|
150
|
+
|
151
|
+
if (editor.keys) {
|
152
|
+
editor.keys.add(keys)
|
153
|
+
} else {
|
154
|
+
editor.keys = keys
|
155
|
+
}
|
145
156
|
} else {
|
146
157
|
editor.originalConfig.value = value;
|
147
158
|
editor.setSilent({record, value})
|
@@ -249,14 +260,14 @@ class CellEditing extends Plugin {
|
|
249
260
|
* @returns {Promise<void>}
|
250
261
|
*/
|
251
262
|
async onTableKeyDown(data) {
|
252
|
-
let me
|
253
|
-
{target}
|
254
|
-
|
263
|
+
let me = this,
|
264
|
+
{target} = data,
|
265
|
+
{view} = me.owner,
|
255
266
|
dataField, record;
|
256
267
|
|
257
268
|
if (!me.mountedEditor && target.cls?.includes('neo-selected')) {
|
258
|
-
dataField =
|
259
|
-
record =
|
269
|
+
dataField = view.getCellDataField(target.id);
|
270
|
+
record = view.getRecord(target.id);
|
260
271
|
|
261
272
|
await me.mountEditor(record, dataField)
|
262
273
|
}
|
@@ -283,7 +294,7 @@ class CellEditing extends Plugin {
|
|
283
294
|
if (cellId) {
|
284
295
|
selectionModel?.deselect(cellId, true); // the cell might still count as selected => silent deselect first
|
285
296
|
selectionModel?.select(cellId);
|
286
|
-
me.owner.focus(cellId)
|
297
|
+
me.focusCells && me.owner.focus(cellId)
|
287
298
|
}
|
288
299
|
}
|
289
300
|
|
@@ -315,15 +326,15 @@ class CellEditing extends Plugin {
|
|
315
326
|
return
|
316
327
|
}
|
317
328
|
|
318
|
-
let me
|
319
|
-
record
|
320
|
-
|
321
|
-
rowIndex
|
329
|
+
let me = this,
|
330
|
+
record = me.mountedEditor.record,
|
331
|
+
{view} = me.owner,
|
332
|
+
rowIndex = view.store.indexOf(record);
|
322
333
|
|
323
334
|
me.mountedEditor = null;
|
324
335
|
|
325
|
-
|
326
|
-
await
|
336
|
+
view.getVdomRoot().cn[rowIndex] = view.createRow({record, rowIndex});
|
337
|
+
await view.promiseUpdate()
|
327
338
|
}
|
328
339
|
}
|
329
340
|
|