neo.mjs 8.6.2 → 8.7.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.
- 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/table/nestedRecordFields/MainContainer.mjs +5 -1
- package/examples/table/nestedRecordFields/MainModel.mjs +16 -3
- package/examples/table/nestedRecordFields/MainStore.mjs +1 -1
- package/package.json +1 -1
- package/resources/scss/src/dialog/Base.scss +4 -0
- package/resources/scss/src/table/View.scss +18 -0
- package/src/DefaultConfig.mjs +2 -2
- package/src/data/RecordFactory.mjs +141 -70
- package/src/dialog/Base.mjs +1 -2
- package/src/table/View.mjs +10 -0
package/apps/ServiceWorker.mjs
CHANGED
package/apps/portal/index.html
CHANGED
@@ -39,7 +39,11 @@ class MainContainer extends Viewport {
|
|
39
39
|
{dataField: 'githubId', text: 'Github Id'},
|
40
40
|
{dataField: 'country', text: 'Country', renderer: 'up.countryRenderer'},
|
41
41
|
{dataField: 'edit', text: 'Edit Action', renderer: 'up.editRenderer'}
|
42
|
-
]
|
42
|
+
],
|
43
|
+
|
44
|
+
viewConfig: {
|
45
|
+
highlightModifiedCells: true
|
46
|
+
}
|
43
47
|
}]
|
44
48
|
}
|
45
49
|
|
@@ -6,8 +6,14 @@ import Model from '../../../src/data/Model.mjs';
|
|
6
6
|
*/
|
7
7
|
class MainModel extends Model {
|
8
8
|
static config = {
|
9
|
-
|
10
|
-
|
9
|
+
/**
|
10
|
+
* @member {String} className='Neo.examples.table.nestedRecordFields.MainModel'
|
11
|
+
* @protected
|
12
|
+
*/
|
13
|
+
className: 'Neo.examples.table.nestedRecordFields.MainModel',
|
14
|
+
/**
|
15
|
+
* @member {Object[]} fields
|
16
|
+
*/
|
11
17
|
fields: [{
|
12
18
|
name: 'annotations',
|
13
19
|
type: 'Object',
|
@@ -20,6 +26,9 @@ class MainModel extends Model {
|
|
20
26
|
}, {
|
21
27
|
name: 'country',
|
22
28
|
type: 'String'
|
29
|
+
}, {
|
30
|
+
name: 'edit',
|
31
|
+
type: 'String'
|
23
32
|
}, {
|
24
33
|
name: 'githubId',
|
25
34
|
type: 'String'
|
@@ -34,7 +43,11 @@ class MainModel extends Model {
|
|
34
43
|
name: 'lastname',
|
35
44
|
type: 'String'
|
36
45
|
}]
|
37
|
-
}]
|
46
|
+
}],
|
47
|
+
/**
|
48
|
+
* @member {Boolean} trackModifiedFields=true
|
49
|
+
*/
|
50
|
+
trackModifiedFields: true
|
38
51
|
}
|
39
52
|
}
|
40
53
|
|
@@ -7,7 +7,7 @@ import Store from '../../../src/data/Store.mjs';
|
|
7
7
|
*/
|
8
8
|
class MainStore extends Store {
|
9
9
|
static config = {
|
10
|
-
className : 'Neo.examples.table.
|
10
|
+
className : 'Neo.examples.table.nestedRecordFields.MainStore',
|
11
11
|
keyProperty: 'githubId',
|
12
12
|
model : Model,
|
13
13
|
|
package/package.json
CHANGED
@@ -8,6 +8,24 @@
|
|
8
8
|
}
|
9
9
|
|
10
10
|
.neo-table-row {
|
11
|
+
.neo-table-cell {
|
12
|
+
position: relative;
|
13
|
+
|
14
|
+
&.neo-is-modified {
|
15
|
+
&:after {
|
16
|
+
border-color: transparent orange transparent transparent;
|
17
|
+
border-style: solid;
|
18
|
+
border-width: 0 10px 10px 0;
|
19
|
+
content : '';
|
20
|
+
height : 0;
|
21
|
+
position : absolute;
|
22
|
+
right : 0;
|
23
|
+
top : -1px;
|
24
|
+
width : 0;
|
25
|
+
}
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
11
29
|
&:hover {
|
12
30
|
.neo-table-cell {
|
13
31
|
background-color: var(--table-cell-background-color-hover);
|
package/src/DefaultConfig.mjs
CHANGED
@@ -262,12 +262,12 @@ const DefaultConfig = {
|
|
262
262
|
useVdomWorker: true,
|
263
263
|
/**
|
264
264
|
* buildScripts/injectPackageVersion.mjs will update this value
|
265
|
-
* @default '8.
|
265
|
+
* @default '8.7.1'
|
266
266
|
* @memberOf! module:Neo
|
267
267
|
* @name config.version
|
268
268
|
* @type String
|
269
269
|
*/
|
270
|
-
version: '8.
|
270
|
+
version: '8.7.1'
|
271
271
|
};
|
272
272
|
|
273
273
|
Object.assign(DefaultConfig, {
|
@@ -2,7 +2,9 @@ import Base from '../core/Base.mjs';
|
|
2
2
|
import Logger from '../util/Logger.mjs';
|
3
3
|
import Model from './Model.mjs';
|
4
4
|
|
5
|
-
const
|
5
|
+
const
|
6
|
+
dataSymbol = Symbol.for('data'),
|
7
|
+
originalDataSymbol = Symbol.for('originalData');
|
6
8
|
|
7
9
|
let instance;
|
8
10
|
|
@@ -18,12 +20,6 @@ class RecordFactory extends Base {
|
|
18
20
|
* @protected
|
19
21
|
*/
|
20
22
|
className: 'Neo.data.RecordFactory',
|
21
|
-
/**
|
22
|
-
* The internal record prefix for original field values.
|
23
|
-
* Only used in case the model has trackModifiedFields set to true.
|
24
|
-
* @member {String} ovPrefix='ov_'
|
25
|
-
*/
|
26
|
-
ovPrefix: 'ov_',
|
27
23
|
/**
|
28
24
|
* @member {String} recordNamespace='Neo.data.record'
|
29
25
|
*/
|
@@ -35,6 +31,37 @@ class RecordFactory extends Base {
|
|
35
31
|
singleton: true
|
36
32
|
}
|
37
33
|
|
34
|
+
/**
|
35
|
+
* Assigns model based default values to a data object
|
36
|
+
* @param {Object} data
|
37
|
+
* @param {Record} record
|
38
|
+
* @param {Neo.data.Model} model
|
39
|
+
* @returns {Object}
|
40
|
+
*/
|
41
|
+
assignDefaultValues(data, record, model) {
|
42
|
+
let {hasNestedFields} = model,
|
43
|
+
scope;
|
44
|
+
|
45
|
+
model.fieldsMap.forEach((field, fieldName) => {
|
46
|
+
if (Object.hasOwn(field, 'defaultValue')) {
|
47
|
+
if (hasNestedFields && fieldName.includes('.')) {
|
48
|
+
let nsArray = fieldName.split('.');
|
49
|
+
|
50
|
+
fieldName = nsArray.pop();
|
51
|
+
scope = Neo.ns(nsArray, true, data)
|
52
|
+
} else {
|
53
|
+
scope = data
|
54
|
+
}
|
55
|
+
|
56
|
+
if (scope[fieldName] === undefined) {
|
57
|
+
scope[fieldName] = field.defaultValue
|
58
|
+
}
|
59
|
+
}
|
60
|
+
});
|
61
|
+
|
62
|
+
return data
|
63
|
+
}
|
64
|
+
|
38
65
|
/**
|
39
66
|
* @param {Object} data
|
40
67
|
* @param {Object} data.field
|
@@ -70,10 +97,9 @@ class RecordFactory extends Base {
|
|
70
97
|
value = instance.parseRecordValue(me, field, value);
|
71
98
|
|
72
99
|
if (!Neo.isEqual(value, oldValue)) {
|
73
|
-
instance.setRecordData(fieldPath, model, me, value);
|
100
|
+
instance.setRecordData({fieldName: fieldPath, model, record: me, value});
|
74
101
|
|
75
|
-
me._isModified =
|
76
|
-
me._isModified = instance.isModified(me, model.trackModifiedFields);
|
102
|
+
me._isModified = me.isModified;
|
77
103
|
|
78
104
|
instance.onRecordChange({
|
79
105
|
fields: [{name: fieldPath, oldValue, value}],
|
@@ -85,13 +111,6 @@ class RecordFactory extends Base {
|
|
85
111
|
}
|
86
112
|
};
|
87
113
|
|
88
|
-
// adding the original value of each field
|
89
|
-
if (model.trackModifiedFields) {
|
90
|
-
properties[instance.ovPrefix + field.name] = {
|
91
|
-
value
|
92
|
-
}
|
93
|
-
}
|
94
|
-
|
95
114
|
Object.defineProperties(me, properties)
|
96
115
|
}
|
97
116
|
}
|
@@ -132,9 +151,63 @@ class RecordFactory extends Base {
|
|
132
151
|
|
133
152
|
[dataSymbol] = {}
|
134
153
|
|
154
|
+
get isModified() {
|
155
|
+
let me = this;
|
156
|
+
|
157
|
+
if (model.trackModifiedFields) {
|
158
|
+
return Neo.isEqual(me[dataSymbol], me[originalDataSymbol])
|
159
|
+
}
|
160
|
+
|
161
|
+
return me._isModified
|
162
|
+
}
|
163
|
+
|
164
|
+
/**
|
165
|
+
* @param {Object} config
|
166
|
+
*/
|
135
167
|
constructor(config) {
|
136
|
-
this
|
137
|
-
|
168
|
+
let me = this;
|
169
|
+
|
170
|
+
config = instance.assignDefaultValues(config, me, model);
|
171
|
+
|
172
|
+
if (model.trackModifiedFields) {
|
173
|
+
me[originalDataSymbol] = {};
|
174
|
+
me.setOriginal(config)
|
175
|
+
}
|
176
|
+
|
177
|
+
me.setSilent(config); // We do not want to fire change events when constructing
|
178
|
+
me._isModified = false
|
179
|
+
}
|
180
|
+
|
181
|
+
/**
|
182
|
+
* @param {String} fieldName
|
183
|
+
* @returns {Boolean|null} null in case the model does not use trackModifiedFields, true in case a change was found
|
184
|
+
*/
|
185
|
+
isModifiedField(fieldName) {
|
186
|
+
let me = this;
|
187
|
+
|
188
|
+
// Check if the field getter does exist
|
189
|
+
if (!me.__proto__.hasOwnProperty(fieldName)) {
|
190
|
+
Logger.logError('The record does not contain the field', fieldName, me)
|
191
|
+
}
|
192
|
+
|
193
|
+
if (model.trackModifiedFields) {
|
194
|
+
let dataScope, originalDataScope;
|
195
|
+
|
196
|
+
if (model.hasNestedFields && fieldName.includes('.')) {
|
197
|
+
let nsArray = fieldName.split('.');
|
198
|
+
|
199
|
+
fieldName = nsArray.pop();
|
200
|
+
dataScope = Neo.ns(nsArray, false, me[dataSymbol]);
|
201
|
+
originalDataScope = Neo.ns(nsArray, false, me[originalDataSymbol])
|
202
|
+
} else {
|
203
|
+
dataScope = me[dataSymbol];
|
204
|
+
originalDataScope = me[originalDataSymbol]
|
205
|
+
}
|
206
|
+
|
207
|
+
return !Neo.isEqual(dataScope[fieldName], originalDataScope[fieldName])
|
208
|
+
}
|
209
|
+
|
210
|
+
return null
|
138
211
|
}
|
139
212
|
|
140
213
|
/**
|
@@ -142,7 +215,17 @@ class RecordFactory extends Base {
|
|
142
215
|
* @param {Object} fields
|
143
216
|
*/
|
144
217
|
set(fields) {
|
145
|
-
instance.setRecordFields(model, this
|
218
|
+
instance.setRecordFields({fields, model, record: this})
|
219
|
+
}
|
220
|
+
|
221
|
+
/**
|
222
|
+
* If the model uses trackModifiedFields, we will store the original data
|
223
|
+
* for tracking the dirty state (changed fields)
|
224
|
+
* @param {Object} fields
|
225
|
+
* @protected
|
226
|
+
*/
|
227
|
+
setOriginal(fields) {
|
228
|
+
instance.setRecordFields({fields, model, record: this, silent: true, useOriginalData: true})
|
146
229
|
}
|
147
230
|
|
148
231
|
/**
|
@@ -150,7 +233,7 @@ class RecordFactory extends Base {
|
|
150
233
|
* @param {Object} fields
|
151
234
|
*/
|
152
235
|
setSilent(fields) {
|
153
|
-
instance.setRecordFields(model, this,
|
236
|
+
instance.setRecordFields({fields, model, record: this, silent: true})
|
154
237
|
}
|
155
238
|
|
156
239
|
/**
|
@@ -180,28 +263,10 @@ class RecordFactory extends Base {
|
|
180
263
|
|
181
264
|
/**
|
182
265
|
* @param {Object} record
|
183
|
-
* @param {Boolean} trackModifiedFields
|
184
266
|
* @returns {Boolean} true in case a change was found
|
185
267
|
*/
|
186
|
-
isModified(record
|
187
|
-
|
188
|
-
let fields = Object.keys(record),
|
189
|
-
i = 0,
|
190
|
-
len = fields.length,
|
191
|
-
field;
|
192
|
-
|
193
|
-
for (; i < len; i++) {
|
194
|
-
field = fields[i];
|
195
|
-
|
196
|
-
if (!Neo.isEqual(record[field], record[this.ovPrefix + field])) {
|
197
|
-
return true
|
198
|
-
}
|
199
|
-
}
|
200
|
-
|
201
|
-
return false
|
202
|
-
}
|
203
|
-
|
204
|
-
return record._isModified
|
268
|
+
isModified(record) {
|
269
|
+
return record.isModified
|
205
270
|
}
|
206
271
|
|
207
272
|
/**
|
@@ -210,17 +275,7 @@ class RecordFactory extends Base {
|
|
210
275
|
* @returns {Boolean|null} null in case the model does not use trackModifiedFields, true in case a change was found
|
211
276
|
*/
|
212
277
|
isModifiedField(record, fieldName) {
|
213
|
-
|
214
|
-
Logger.logError('The record does not contain the field', fieldName, record)
|
215
|
-
}
|
216
|
-
|
217
|
-
let modifiedField = this.ovPrefix + fieldName;
|
218
|
-
|
219
|
-
if (record.hasOwnProperty(modifiedField)) {
|
220
|
-
return !Neo.isEqual(record[fieldName], record[modifiedField])
|
221
|
-
}
|
222
|
-
|
223
|
-
return null
|
278
|
+
return record.isModifiedField(fieldName)
|
224
279
|
}
|
225
280
|
|
226
281
|
/**
|
@@ -229,7 +284,7 @@ class RecordFactory extends Base {
|
|
229
284
|
* @returns {Boolean}
|
230
285
|
*/
|
231
286
|
isRecord(record) {
|
232
|
-
return record?.isRecord
|
287
|
+
return record?.isRecord || false
|
233
288
|
}
|
234
289
|
|
235
290
|
/**
|
@@ -252,7 +307,7 @@ class RecordFactory extends Base {
|
|
252
307
|
* @param {Object} recordConfig=null
|
253
308
|
* @returns {*}
|
254
309
|
*/
|
255
|
-
parseRecordValue(record, field, value, recordConfig=null) {
|
310
|
+
parseRecordValue(record, field, value, recordConfig=null) {
|
256
311
|
if (field.calculate) {
|
257
312
|
return field.calculate(record, field, recordConfig)
|
258
313
|
}
|
@@ -322,34 +377,40 @@ class RecordFactory extends Base {
|
|
322
377
|
}
|
323
378
|
|
324
379
|
/**
|
325
|
-
* @param {
|
326
|
-
* @param {
|
327
|
-
* @param {
|
328
|
-
* @param {
|
380
|
+
* @param {Object} data
|
381
|
+
* @param {String} data.fieldName
|
382
|
+
* @param {Neo.data.Model} data.model
|
383
|
+
* @param {Record} data.record
|
384
|
+
* @param {Boolean} data.useOriginalData=false true will apply changes to the originalData symbol
|
385
|
+
* @param {*} data.value
|
329
386
|
* @protected
|
330
387
|
*/
|
331
|
-
setRecordData(fieldName, model, record, value) {
|
388
|
+
setRecordData({fieldName, model, record, useOriginalData=false, value}) {
|
389
|
+
let scope = useOriginalData ? originalDataSymbol : dataSymbol;
|
390
|
+
|
332
391
|
if (model.hasNestedFields && fieldName.includes('.')) {
|
333
392
|
let ns, nsArray;
|
334
393
|
|
335
394
|
nsArray = fieldName.split('.');
|
336
395
|
fieldName = nsArray.pop();
|
337
|
-
ns = Neo.ns(nsArray, true, record[
|
396
|
+
ns = Neo.ns(nsArray, true, record[scope]);
|
338
397
|
|
339
398
|
ns[fieldName] = value
|
340
399
|
} else {
|
341
|
-
record[
|
400
|
+
record[scope][fieldName] = value
|
342
401
|
}
|
343
402
|
}
|
344
403
|
|
345
404
|
/**
|
346
|
-
* @param {
|
347
|
-
* @param {Object}
|
348
|
-
* @param {Object}
|
349
|
-
* @param {
|
350
|
-
* @param {Object
|
405
|
+
* @param {Object} data
|
406
|
+
* @param {Object[]} data.changedFields=[] Internal flag
|
407
|
+
* @param {Object} data.fields
|
408
|
+
* @param {Neo.data.Model} data.model
|
409
|
+
* @param {Object} data.record
|
410
|
+
* @param {Boolean} data.silent=false
|
411
|
+
* @param {Boolean} data.useOriginalData=false true will apply changes to the originalData symbol
|
351
412
|
*/
|
352
|
-
setRecordFields(model, record,
|
413
|
+
setRecordFields({changedFields=[], fields, model, record, silent=false, useOriginalData=false}) {
|
353
414
|
let {fieldsMap} = model,
|
354
415
|
fieldExists, oldValue;
|
355
416
|
|
@@ -358,16 +419,26 @@ class RecordFactory extends Base {
|
|
358
419
|
|
359
420
|
if (Neo.isObject(value) && !fieldExists) {
|
360
421
|
Object.entries(value).forEach(([childKey, childValue]) => {
|
361
|
-
this.setRecordFields(
|
422
|
+
this.setRecordFields({
|
423
|
+
changedFields,
|
424
|
+
fields: {[`${key}.${childKey}`]: childValue},
|
425
|
+
model,
|
426
|
+
record,
|
427
|
+
silent: true,
|
428
|
+
useOriginalData
|
429
|
+
})
|
362
430
|
})
|
363
431
|
} else if (fieldExists) {
|
364
432
|
oldValue = record[key];
|
365
433
|
value = instance.parseRecordValue(record, model.getField(key), value);
|
366
434
|
|
367
435
|
if (!Neo.isEqual(oldValue, value)) {
|
368
|
-
instance.setRecordData(key, model, record, value);
|
436
|
+
instance.setRecordData({fieldName: key, model, record, useOriginalData, value});
|
437
|
+
|
438
|
+
if (!useOriginalData) {
|
439
|
+
record._isModified = true
|
440
|
+
}
|
369
441
|
|
370
|
-
record._isModified = true;
|
371
442
|
changedFields.push({name: key, oldValue, value})
|
372
443
|
}
|
373
444
|
}
|
package/src/dialog/Base.mjs
CHANGED
package/src/table/View.mjs
CHANGED
@@ -32,6 +32,10 @@ class View extends Component {
|
|
32
32
|
* @protected
|
33
33
|
*/
|
34
34
|
containerId: null,
|
35
|
+
/**
|
36
|
+
* @member {Boolean} highlightModifiedCells_=false
|
37
|
+
*/
|
38
|
+
highlightModifiedCells_: false,
|
35
39
|
/**
|
36
40
|
* @member {Object} recordVnodeMap={}
|
37
41
|
*/
|
@@ -150,6 +154,12 @@ class View extends Component {
|
|
150
154
|
cellCls.push('neo-' + column.cellAlign)
|
151
155
|
}
|
152
156
|
|
157
|
+
if (me.highlightModifiedCells && me.store.model.trackModifiedFields) {
|
158
|
+
if (record.isModifiedField(dataField)) {
|
159
|
+
cellCls.push('neo-is-modified')
|
160
|
+
}
|
161
|
+
}
|
162
|
+
|
153
163
|
if (!cellId) {
|
154
164
|
// todo: remove the else part as soon as all tables use stores (examples table)
|
155
165
|
if (hasStore) {
|