neo.mjs 5.4.12 → 5.5.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/form/view/pages/Page4.mjs +9 -4
- package/examples/ServiceWorker.mjs +2 -2
- package/package.json +2 -2
- package/src/DefaultConfig.mjs +5 -6
- package/src/component/Base.mjs +28 -3
- package/src/form/field/CheckBox.mjs +95 -8
- package/src/form/field/Number.mjs +41 -20
- package/src/form/field/Picker.mjs +3 -4
- package/src/form/field/Radio.mjs +1 -1
- package/src/main/addon/ScrollSync.mjs +58 -0
package/apps/ServiceWorker.mjs
CHANGED
@@ -16,10 +16,12 @@ class Page4 extends FormPageContainer {
|
|
16
16
|
* @member {Object} itemDefaults
|
17
17
|
*/
|
18
18
|
itemDefaults: {
|
19
|
-
module
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
module : CheckBox,
|
20
|
+
groupRequired : true,
|
21
|
+
labelText : null,
|
22
|
+
labelWidth : 70,
|
23
|
+
name : 'fruits',
|
24
|
+
showErrorTexts: false
|
23
25
|
},
|
24
26
|
/**
|
25
27
|
* @member {Object[]} items
|
@@ -40,11 +42,14 @@ class Page4 extends FormPageContainer {
|
|
40
42
|
value : 'orange',
|
41
43
|
valueLabelText: 'Orange'
|
42
44
|
}, {
|
45
|
+
showErrorTexts: true, // overwriting the itemDefaults value
|
43
46
|
value : 'strawberry',
|
44
47
|
valueLabelText: 'Strawberry'
|
45
48
|
}, {
|
46
49
|
labelText : 'Boolean',
|
50
|
+
groupRequired : false, // overwriting the itemDefaults value
|
47
51
|
name : 'boolean',
|
52
|
+
showErrorTexts: true, // overwriting the itemDefaults value
|
48
53
|
style : {marginTop: '50px'},
|
49
54
|
uncheckedValue: false,
|
50
55
|
value : true
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "neo.mjs",
|
3
|
-
"version": "5.
|
3
|
+
"version": "5.5.1",
|
4
4
|
"description": "The webworkers driven UI framework",
|
5
5
|
"type": "module",
|
6
6
|
"repository": {
|
@@ -56,7 +56,7 @@
|
|
56
56
|
"neo-jsdoc": "^1.0.1",
|
57
57
|
"neo-jsdoc-x": "^1.0.5",
|
58
58
|
"postcss": "^8.4.23",
|
59
|
-
"sass": "^1.62.
|
59
|
+
"sass": "^1.62.1",
|
60
60
|
"webpack": "^5.80.0",
|
61
61
|
"webpack-cli": "^5.0.2",
|
62
62
|
"webpack-dev-server": "4.13.3",
|
package/src/DefaultConfig.mjs
CHANGED
@@ -113,17 +113,16 @@ const DefaultConfig = {
|
|
113
113
|
logDeltaUpdates: false,
|
114
114
|
/**
|
115
115
|
* Add addons for the main thread
|
116
|
-
*
|
117
|
-
* (src/main/addon) contains all framework related options.
|
116
|
+
* ./src/main/addon/ contains all framework related options.
|
118
117
|
* You can also create your own addons within your workspace scope. Make sure to put them inside 'src/main/addon/'
|
119
118
|
* and prefix them with 'WS/' inside your neo-config.json file.
|
120
119
|
* Example: ['DragDrop', 'Stylesheet', 'WS/MyAddon']
|
121
|
-
* @default ['DragDrop','Stylesheet']
|
120
|
+
* @default ['DragDrop','ScrollSync','Stylesheet']
|
122
121
|
* @memberOf! module:Neo
|
123
122
|
* @name config.mainThreadAddons
|
124
123
|
* @type String[]
|
125
124
|
*/
|
126
|
-
mainThreadAddons: ['DragDrop', 'Stylesheet'],
|
125
|
+
mainThreadAddons: ['DragDrop', 'ScrollSync', 'Stylesheet'],
|
127
126
|
/**
|
128
127
|
* Pass the URL of a JSON-file, which contains the services and methods from your backend,
|
129
128
|
* which you want to expose to the client.
|
@@ -237,12 +236,12 @@ const DefaultConfig = {
|
|
237
236
|
useVdomWorker: true,
|
238
237
|
/**
|
239
238
|
* buildScripts/injectPackageVersion.mjs will update this value
|
240
|
-
* @default '5.
|
239
|
+
* @default '5.5.1'
|
241
240
|
* @memberOf! module:Neo
|
242
241
|
* @name config.version
|
243
242
|
* @type String
|
244
243
|
*/
|
245
|
-
version: '5.
|
244
|
+
version: '5.5.1'
|
246
245
|
};
|
247
246
|
|
248
247
|
Object.assign(DefaultConfig, {
|
package/src/component/Base.mjs
CHANGED
@@ -1180,6 +1180,31 @@ class Base extends CoreBase {
|
|
1180
1180
|
return this.getConfigInstanceByNtype('model', ntype);
|
1181
1181
|
}
|
1182
1182
|
|
1183
|
+
/**
|
1184
|
+
* Calculate the real parentIndex inside the DOM
|
1185
|
+
* @returns {Number|undefined}
|
1186
|
+
*/
|
1187
|
+
getMountedParentIndex() {
|
1188
|
+
let parent = Neo.getComponent(this.parentId),
|
1189
|
+
items = parent?.items || [],
|
1190
|
+
i = 0,
|
1191
|
+
index = 0,
|
1192
|
+
len = items.length,
|
1193
|
+
item;
|
1194
|
+
|
1195
|
+
for (; i < len; i++) {
|
1196
|
+
item = items[i];
|
1197
|
+
|
1198
|
+
if (item === this) {
|
1199
|
+
return index
|
1200
|
+
}
|
1201
|
+
|
1202
|
+
if (!item.hidden && item.hideMode === 'removeDom') {
|
1203
|
+
index++
|
1204
|
+
}
|
1205
|
+
}
|
1206
|
+
}
|
1207
|
+
|
1183
1208
|
/**
|
1184
1209
|
* Get the parent components as an array
|
1185
1210
|
* @returns {Neo.component.Base[]}
|
@@ -1404,7 +1429,7 @@ class Base extends CoreBase {
|
|
1404
1429
|
id : me.id,
|
1405
1430
|
html : me.vnode.outerHTML,
|
1406
1431
|
parentId : me.parentId,
|
1407
|
-
parentIndex: me.
|
1432
|
+
parentIndex: me.getMountedParentIndex()
|
1408
1433
|
});
|
1409
1434
|
|
1410
1435
|
delete me.vdom.removeDom;
|
@@ -1626,8 +1651,8 @@ class Base extends CoreBase {
|
|
1626
1651
|
Neo.vdom.Helper.create({
|
1627
1652
|
appName : me.appName,
|
1628
1653
|
autoMount,
|
1629
|
-
parentId : autoMount ? me.parentId
|
1630
|
-
parentIndex: autoMount ? me.
|
1654
|
+
parentId : autoMount ? me.parentId : undefined,
|
1655
|
+
parentIndex: autoMount ? me.getMountedParentIndex() : undefined,
|
1631
1656
|
...me.vdom
|
1632
1657
|
}).then(data => {
|
1633
1658
|
me.onRender(data, useVdomWorker ? autoMount : false);
|
@@ -1,5 +1,6 @@
|
|
1
|
-
import Base
|
2
|
-
import
|
1
|
+
import Base from './Base.mjs';
|
2
|
+
import ComponentManager from '../../manager/Component.mjs';
|
3
|
+
import NeoArray from '../../util/Array.mjs';
|
3
4
|
|
4
5
|
/**
|
5
6
|
* @class Neo.form.field.CheckBox
|
@@ -37,10 +38,18 @@ class CheckBox extends Base {
|
|
37
38
|
* @member {String|null} error_=null
|
38
39
|
*/
|
39
40
|
error_: null,
|
41
|
+
/**
|
42
|
+
* @member {Function} errorTextGroupRequired='Required'
|
43
|
+
*/
|
44
|
+
errorTextGroupRequired: data => `Please check at least one item of the group: ${data.name}`,
|
40
45
|
/**
|
41
46
|
* @member {String} errorTextRequired='Required'
|
42
47
|
*/
|
43
48
|
errorTextRequired: 'Required',
|
49
|
+
/**
|
50
|
+
* @member {Boolean} groupRequired_=false
|
51
|
+
*/
|
52
|
+
groupRequired_: false,
|
44
53
|
/**
|
45
54
|
* @member {Boolean} hideLabel_=false
|
46
55
|
*/
|
@@ -87,6 +96,11 @@ class CheckBox extends Base {
|
|
87
96
|
* @member {Boolean} required_=false
|
88
97
|
*/
|
89
98
|
required_: false,
|
99
|
+
/**
|
100
|
+
* Use case: Set this config to false for all but one items with the same name.
|
101
|
+
* @member {Boolean} showErrorTexts_=true
|
102
|
+
*/
|
103
|
+
showErrorTexts_: true,
|
90
104
|
/**
|
91
105
|
* In case the CheckBox does not belong to a group (multiple fields with the same name),
|
92
106
|
* you can pass a custom value for the unchecked state.
|
@@ -148,6 +162,10 @@ class CheckBox extends Base {
|
|
148
162
|
newCls = value ? me.iconClsChecked : me.iconCls,
|
149
163
|
oldCls = value ? me.iconCls : me.iconClsChecked;
|
150
164
|
|
165
|
+
if (oldValue) {
|
166
|
+
me.clean = false;
|
167
|
+
}
|
168
|
+
|
151
169
|
me.validate(); // silent
|
152
170
|
|
153
171
|
labelEl.cn[1].checked = value;
|
@@ -171,6 +189,16 @@ class CheckBox extends Base {
|
|
171
189
|
this.updateError(value)
|
172
190
|
}
|
173
191
|
|
192
|
+
/**
|
193
|
+
* Triggered after the required groupRequired got changed
|
194
|
+
* @param {Boolean} value
|
195
|
+
* @param {Boolean} oldValue
|
196
|
+
* @protected
|
197
|
+
*/
|
198
|
+
afterSetGroupRequired(value, oldValue) {
|
199
|
+
oldValue !== undefined && this.validate(false)
|
200
|
+
}
|
201
|
+
|
174
202
|
/**
|
175
203
|
* Triggered after the hideLabel config got changed
|
176
204
|
* @param {String} value
|
@@ -291,6 +319,16 @@ class CheckBox extends Base {
|
|
291
319
|
oldValue !== undefined && this.validate(false)
|
292
320
|
}
|
293
321
|
|
322
|
+
/**
|
323
|
+
* Triggered after the showErrorTexts config got changed
|
324
|
+
* @param {Boolean} value
|
325
|
+
* @param {Boolean} oldValue
|
326
|
+
* @protected
|
327
|
+
*/
|
328
|
+
afterSetShowErrorTexts(value, oldValue) {
|
329
|
+
oldValue !== undefined && this.validate(false)
|
330
|
+
}
|
331
|
+
|
294
332
|
/**
|
295
333
|
* Triggered after the value config got changed
|
296
334
|
* @param {String} value
|
@@ -323,6 +361,21 @@ class CheckBox extends Base {
|
|
323
361
|
me.update()
|
324
362
|
}
|
325
363
|
|
364
|
+
/**
|
365
|
+
* Triggered before the groupRequired config gets changed.
|
366
|
+
* @param {Boolean} value
|
367
|
+
* @param {Boolean} oldValue
|
368
|
+
* @protected
|
369
|
+
*/
|
370
|
+
beforeSetGroupRequired(value, oldValue) {
|
371
|
+
if (value && this.required) {
|
372
|
+
console.warn('Do not use groupRequired & required at the same time. Switching to required.', this);
|
373
|
+
return false
|
374
|
+
}
|
375
|
+
|
376
|
+
return value
|
377
|
+
}
|
378
|
+
|
326
379
|
/**
|
327
380
|
* Triggered before the labelCls config gets changed.
|
328
381
|
* @param {String[]} value
|
@@ -407,8 +460,9 @@ class CheckBox extends Base {
|
|
407
460
|
@param {Boolean} silent=false
|
408
461
|
*/
|
409
462
|
updateError(value, silent=false) {
|
410
|
-
let me
|
411
|
-
cls
|
463
|
+
let me = this,
|
464
|
+
cls = me.cls,
|
465
|
+
showError = value && me.showErrorTexts,
|
412
466
|
errorNode;
|
413
467
|
|
414
468
|
if (!(me.clean && !me.mounted)) {
|
@@ -419,13 +473,13 @@ class CheckBox extends Base {
|
|
419
473
|
|
420
474
|
errorNode = me.vdom.cn[1];
|
421
475
|
|
422
|
-
if (
|
476
|
+
if (showError) {
|
423
477
|
errorNode.html = value;
|
424
478
|
} else {
|
425
479
|
delete errorNode.html;
|
426
480
|
}
|
427
481
|
|
428
|
-
errorNode.removeDom = !
|
482
|
+
errorNode.removeDom = !showError;
|
429
483
|
|
430
484
|
!silent && me.update()
|
431
485
|
}
|
@@ -438,14 +492,47 @@ class CheckBox extends Base {
|
|
438
492
|
*/
|
439
493
|
validate(silent=true) {
|
440
494
|
let me = this,
|
441
|
-
|
495
|
+
name = me.name,
|
496
|
+
returnValue = true,
|
497
|
+
checkBox, checkBoxes;
|
442
498
|
|
443
499
|
if (!silent) {
|
444
500
|
// in case we manually call validate(false) on a form or field before it is mounted, we do want to see errors.
|
445
501
|
me.clean = false;
|
446
502
|
}
|
447
503
|
|
448
|
-
if (me.
|
504
|
+
if (me.groupRequired) {
|
505
|
+
returnValue = false;
|
506
|
+
|
507
|
+
// discuss: we could limit this to checkBoxes / radios inside the same form, IF a top level form is used
|
508
|
+
checkBoxes = ComponentManager.find({
|
509
|
+
ntype: me.ntype,
|
510
|
+
name : me.name
|
511
|
+
});
|
512
|
+
|
513
|
+
// get the group validity state first
|
514
|
+
for (checkBox of checkBoxes) {
|
515
|
+
if (checkBox.checked) {
|
516
|
+
returnValue = true;
|
517
|
+
break;
|
518
|
+
}
|
519
|
+
}
|
520
|
+
|
521
|
+
// update all group items
|
522
|
+
for (checkBox of checkBoxes) {
|
523
|
+
if (checkBox.id !== me.id) {
|
524
|
+
if (!me.clean) {
|
525
|
+
checkBox.clean = false;
|
526
|
+
}
|
527
|
+
|
528
|
+
checkBox[me.clean ? '_error' : 'error'] = returnValue ? null : checkBox.errorTextGroupRequired({name})
|
529
|
+
}
|
530
|
+
}
|
531
|
+
|
532
|
+
if (!returnValue) {
|
533
|
+
me._error = me.errorTextGroupRequired({name});
|
534
|
+
}
|
535
|
+
} else if (me.required && !me.checked) {
|
449
536
|
me._error = me.errorTextRequired;
|
450
537
|
returnValue = false;
|
451
538
|
}
|
@@ -162,6 +162,34 @@ class Number extends Text {
|
|
162
162
|
}
|
163
163
|
}
|
164
164
|
|
165
|
+
/**
|
166
|
+
* Triggered before the maxLength config gets changed
|
167
|
+
* @param {Number|null} value
|
168
|
+
* @param {Number|null} oldValue
|
169
|
+
* @protected
|
170
|
+
*/
|
171
|
+
beforeSetMaxLength(value, oldValue) {
|
172
|
+
if (value !== null) {
|
173
|
+
console.warn('input type number does not support maxLength. use maxValue instead.', this)
|
174
|
+
}
|
175
|
+
|
176
|
+
return null;
|
177
|
+
}
|
178
|
+
|
179
|
+
/**
|
180
|
+
* Triggered before the minLength config gets changed
|
181
|
+
* @param {Number|null} value
|
182
|
+
* @param {Number|null} oldValue
|
183
|
+
* @protected
|
184
|
+
*/
|
185
|
+
beforeSetMinLength(value, oldValue) {
|
186
|
+
if (value !== null) {
|
187
|
+
console.warn('input type number does not support minLength. use minValue instead.', this)
|
188
|
+
}
|
189
|
+
|
190
|
+
return null;
|
191
|
+
}
|
192
|
+
|
165
193
|
/**
|
166
194
|
* Triggered after the triggerPosition config got changed
|
167
195
|
* @param {String} value
|
@@ -351,32 +379,25 @@ class Number extends Text {
|
|
351
379
|
minValue = me.minValue,
|
352
380
|
stepSize = me.stepSize,
|
353
381
|
stepSizePow = Math.pow(10, me.stepSizeDigits),
|
354
|
-
returnValue =
|
382
|
+
returnValue = super.validate(silent),
|
355
383
|
errorParam = {maxValue, minValue, stepSize, value};
|
356
384
|
|
357
|
-
if (!silent) {
|
358
|
-
// in case we manually call validate(false) on a form or field before it is mounted, we do want to see errors.
|
359
|
-
me.clean = false;
|
360
|
-
}
|
361
|
-
|
362
|
-
if (Neo.isNumber(maxValue) && isNumber && value > maxValue) {
|
363
|
-
me._error = me.errorTextMaxValue(errorParam);
|
364
|
-
returnValue = false;
|
365
|
-
} else if (Neo.isNumber(minValue) && isNumber && value < minValue) {
|
366
|
-
me._error = me.errorTextMinValue(errorParam);
|
367
|
-
returnValue = false;
|
368
|
-
} else if ((Math.round((value % me.stepSize) * stepSizePow) / stepSizePow) !== 0) {
|
369
|
-
me._error = me.errorTextStepSize(errorParam);
|
370
|
-
returnValue = false;
|
371
|
-
}
|
372
|
-
|
373
385
|
if (returnValue) {
|
374
|
-
|
386
|
+
if (Neo.isNumber(maxValue) && isNumber && value > maxValue) {
|
387
|
+
me._error = me.errorTextMaxValue(errorParam);
|
388
|
+
returnValue = false;
|
389
|
+
} else if (Neo.isNumber(minValue) && isNumber && value < minValue) {
|
390
|
+
me._error = me.errorTextMinValue(errorParam);
|
391
|
+
returnValue = false;
|
392
|
+
} else if ((Math.round((value % me.stepSize) * stepSizePow) / stepSizePow) !== 0) {
|
393
|
+
me._error = me.errorTextStepSize(errorParam);
|
394
|
+
returnValue = false;
|
395
|
+
}
|
375
396
|
}
|
376
397
|
|
377
|
-
!me.clean && me.updateError(me._error, silent);
|
398
|
+
!returnValue && !me.clean && me.updateError(me._error, silent);
|
378
399
|
|
379
|
-
return
|
400
|
+
return returnValue
|
380
401
|
}
|
381
402
|
}
|
382
403
|
|
@@ -2,7 +2,6 @@ import Container from '../../container/Base.mjs';
|
|
2
2
|
import NeoArray from '../../util/Array.mjs';
|
3
3
|
import PickerTrigger from './trigger/Picker.mjs';
|
4
4
|
import Text from './Text.mjs';
|
5
|
-
import VDomUtil from '../../util/VDom.mjs';
|
6
5
|
|
7
6
|
/**
|
8
7
|
* The abstract picker field provides an arrow down trigger which opens a floating container to provide
|
@@ -274,7 +273,7 @@ class Picker extends Text {
|
|
274
273
|
* @param {Object} data
|
275
274
|
* @protected
|
276
275
|
*/
|
277
|
-
onFocusEnter(data) {
|
276
|
+
onFocusEnter(data) {
|
278
277
|
super.onFocusEnter(data);
|
279
278
|
|
280
279
|
let me = this;
|
@@ -286,7 +285,7 @@ class Picker extends Text {
|
|
286
285
|
* @param {Object} data
|
287
286
|
* @protected
|
288
287
|
*/
|
289
|
-
onFocusLeave(data) {
|
288
|
+
onFocusLeave(data) {
|
290
289
|
let me = this,
|
291
290
|
insidePicker = false,
|
292
291
|
item;
|
@@ -307,7 +306,7 @@ class Picker extends Text {
|
|
307
306
|
/**
|
308
307
|
* @param {Object} data
|
309
308
|
*/
|
310
|
-
onInputClick(data) {
|
309
|
+
onInputClick(data) {
|
311
310
|
let me = this;
|
312
311
|
|
313
312
|
if (!me.editable) {
|
package/src/form/field/Radio.mjs
CHANGED
@@ -0,0 +1,58 @@
|
|
1
|
+
import Base from '../../core/Base.mjs';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* @class Neo.main.addon.ScrollSync
|
5
|
+
* @extends Neo.core.Base
|
6
|
+
* @singleton
|
7
|
+
*/
|
8
|
+
class ScrollSync extends Base {
|
9
|
+
static config = {
|
10
|
+
/**
|
11
|
+
* @member {String} className='Neo.main.addon.ScrollSync'
|
12
|
+
* @protected
|
13
|
+
*/
|
14
|
+
className: 'Neo.main.addon.ScrollSync',
|
15
|
+
/**
|
16
|
+
* Remote method access for other workers
|
17
|
+
* @member {Object} remote={app: [//...]}
|
18
|
+
* @protected
|
19
|
+
*/
|
20
|
+
remote: {
|
21
|
+
app: [
|
22
|
+
'register',
|
23
|
+
'unregister'
|
24
|
+
]
|
25
|
+
},
|
26
|
+
/**
|
27
|
+
* @member {Boolean} singleton=true
|
28
|
+
* @protected
|
29
|
+
*/
|
30
|
+
singleton: true
|
31
|
+
}
|
32
|
+
|
33
|
+
/**
|
34
|
+
* @param {Object} data
|
35
|
+
* @param {String} data.sourceId
|
36
|
+
* @param {String} data.targetId
|
37
|
+
*/
|
38
|
+
register(data) {
|
39
|
+
console.log('register', data)
|
40
|
+
}
|
41
|
+
|
42
|
+
/**
|
43
|
+
* @param {Object} data
|
44
|
+
* @param {String} data.sourceId
|
45
|
+
* @param {String} data.targetId
|
46
|
+
*/
|
47
|
+
unregister(data) {
|
48
|
+
console.log('unregister', data)
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
Neo.applyClassConfig(ScrollSync);
|
53
|
+
|
54
|
+
let instance = Neo.create(ScrollSync);
|
55
|
+
|
56
|
+
Neo.applyToGlobalNs(instance);
|
57
|
+
|
58
|
+
export default instance;
|