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.
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='5.4.12'
23
+ * @member {String} version='5.5.1'
24
24
  */
25
- version: '5.4.12'
25
+ version: '5.5.1'
26
26
  }
27
27
 
28
28
  /**
@@ -16,10 +16,12 @@ class Page4 extends FormPageContainer {
16
16
  * @member {Object} itemDefaults
17
17
  */
18
18
  itemDefaults: {
19
- module : CheckBox,
20
- labelText : null,
21
- labelWidth: 70,
22
- name : 'fruits'
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
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='5.4.12'
23
+ * @member {String} version='5.5.1'
24
24
  */
25
- version: '5.4.12'
25
+ version: '5.5.1'
26
26
  }
27
27
 
28
28
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neo.mjs",
3
- "version": "5.4.12",
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.0",
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",
@@ -113,17 +113,16 @@ const DefaultConfig = {
113
113
  logDeltaUpdates: false,
114
114
  /**
115
115
  * Add addons for the main thread
116
- * Possible values: AmCharts, AnalyticsByGoogle, DragDrop, HighlightJS, LocalStorage, MapboxGL, Markdown, Siesta, Stylesheet, WindowPosition
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.4.12'
239
+ * @default '5.5.1'
241
240
  * @memberOf! module:Neo
242
241
  * @name config.version
243
242
  * @type String
244
243
  */
245
- version: '5.4.12'
244
+ version: '5.5.1'
246
245
  };
247
246
 
248
247
  Object.assign(DefaultConfig, {
@@ -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.parentIndex
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 : undefined,
1630
- parentIndex: autoMount ? me.parentIndex : undefined,
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 from './Base.mjs';
2
- import NeoArray from '../../util/Array.mjs';
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 = this,
411
- cls = me.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 (value) {
476
+ if (showError) {
423
477
  errorNode.html = value;
424
478
  } else {
425
479
  delete errorNode.html;
426
480
  }
427
481
 
428
- errorNode.removeDom = !value;
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
- returnValue = true;
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.required && !me.checked) {
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 = true,
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
- me._error = null;
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 !returnValue ? false : super.validate(silent);
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) {console.log('onFocusEnter')
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) {console.log('onFocusLeave')
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) {console.log('onInputClick')
309
+ onInputClick(data) {
311
310
  let me = this;
312
311
 
313
312
  if (!me.editable) {
@@ -1,5 +1,5 @@
1
1
  import CheckBox from './CheckBox.mjs';
2
- import ComponentManager from '../../manager/Component.mjs'
2
+ import ComponentManager from '../../manager/Component.mjs';
3
3
 
4
4
  /**
5
5
  * @class Neo.form.field.Radio
@@ -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;