neo.mjs 4.6.11 → 4.6.12

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.
@@ -181,8 +181,9 @@ Pros:
181
181
 
182
182
  Cons:
183
183
  1. neo.mjs is not using TypeScript (you could do it for your own app code, in case you want to use a build process)
184
- 2. Firefox & Safari do not support JS modules inside workers yet, so the development mode only runs in Chrome v80+.
185
- Of course the dist (dev&prod) versions do run fine in FF & Safari as well.
184
+ 2. Firefox does not support JS modules inside workers yet, so the development mode only runs in Chromium (Chrome & Edge),
185
+ as well as Safari. Mozilla is actively working on it.
186
+ Of course the dist (dev&prod) versions do run fine in Firefox as well.
186
187
  3. Several npm dependencies can not easily get used, since they do not use a correct ES6 import syntax (e.g. missing file names)
187
188
 
188
189
  ## No string based pseudo XML templates
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neo.mjs",
3
- "version": "4.6.11",
3
+ "version": "4.6.12",
4
4
  "description": "The webworkers driven UI framework",
5
5
  "type": "module",
6
6
  "repository": {
@@ -47,7 +47,7 @@
47
47
  "autoprefixer": "^10.4.13",
48
48
  "chalk": "^5.2.0",
49
49
  "clean-webpack-plugin": "^4.0.0",
50
- "commander": "^9.5.0",
50
+ "commander": "^10.0.0",
51
51
  "cssnano": "^5.1.14",
52
52
  "envinfo": "^7.8.1",
53
53
  "fs-extra": "^11.1.0",
@@ -1,30 +1,27 @@
1
1
  .neo-checkboxfield {
2
2
  .neo-checkbox-input {
3
- appearance: none;
3
+ display: none;
4
4
 
5
5
  &:checked {
6
- &::after {
7
- color : v(checkboxfield-checked-color);
8
- content : "\f00c";
9
- font-weight: 900;
6
+ +.neo-checkbox-icon {
7
+ color: v(checkboxfield-color-checked);
10
8
  }
11
9
  }
12
10
 
13
- &::after {
14
- color : v(checkboxfield-color);
15
- content : "\f0c8";
16
- cursor : pointer;
17
- display : inline-block;
18
- font-family: "Font Awesome 5 Free";
19
- font-size : 14px;
20
- width : 20px;
21
- }
22
-
23
11
  &:focus {
24
12
  outline : 0;
25
13
  }
26
14
  }
27
15
 
16
+ .neo-checkbox-icon {
17
+ color : v(checkboxfield-color);
18
+ cursor : pointer;
19
+ display : inline-block;
20
+ font-family: v(checkboxfield-icon-font-family);
21
+ font-size : v(checkboxfield-icon-font-size);
22
+ width : 20px;
23
+ }
24
+
28
25
  .neo-checkbox-label {
29
26
  color : v(textfield-label-color);
30
27
  display : inline-block;
@@ -1,49 +1,3 @@
1
1
  .neo-radiofield {
2
- .neo-radio-input {
3
- appearance: none;
4
2
 
5
- &:checked {
6
- &::after {
7
- color : v(radiofield-checked-color);
8
- content : "\f00c";
9
- font-weight: 900;
10
- }
11
- }
12
-
13
- &::after {
14
- color : v(radiofield-color);
15
- content : "\f111";
16
- cursor : pointer;
17
- display : inline-block;
18
- font-family: "Font Awesome 5 Free";
19
- font-size : 14px;
20
- width : 20px;
21
- }
22
-
23
- &:focus {
24
- outline : 0;
25
- }
26
- }
27
-
28
- .neo-radio-label {
29
- color : v(textfield-label-color);
30
- display : inline-block;
31
- user-select: none;
32
- }
33
-
34
- .neo-radio-value-label {
35
- color : v(textfield-label-color);
36
- user-select: none;
37
- }
38
-
39
- &.neo-label-top {
40
- .neo-radio-input {
41
- margin-left: 0;
42
- }
43
-
44
- .neo-radio-label {
45
- display: block;
46
- margin : v(radiofield-label-top-margin);
47
- }
48
- }
49
3
  }
@@ -1,7 +1,6 @@
1
1
  @import "field/trigger/all";
2
2
  @import "field/CheckBox";
3
3
  @import "field/Picker";
4
- @import "field/Radio";
5
4
  @import "field/Range";
6
5
  @import "field/Search";
7
6
  @import "field/Select";
@@ -1,13 +1,17 @@
1
1
  $neoMap: map-merge($neoMap, (
2
2
  'checkboxfield-color' : #eee,
3
- 'checkboxfield-checked-color' : #64B5F6,
3
+ 'checkboxfield-color-checked' : #64B5F6,
4
+ 'checkboxfield-icon-font-family': "Font Awesome 5 Free",
5
+ 'checkboxfield-icon-font-size' : 14px,
4
6
  'checkboxfield-label-top-margin': 0 0 3px
5
7
  ));
6
8
 
7
9
  @if $useCssVars == true {
8
10
  :root .neo-theme-dark { // .neo-checkboxfield
9
11
  --checkboxfield-color : #{neo(checkboxfield-color)};
10
- --checkboxfield-checked-color : #{neo(checkboxfield-checked-color)};
12
+ --checkboxfield-color-checked : #{neo(checkboxfield-color-checked)};
13
+ --checkboxfield-icon-font-family: #{neo(checkboxfield-icon-font-family)};
14
+ --checkboxfield-icon-font-size : #{neo(checkboxfield-icon-font-size)};
11
15
  --checkboxfield-label-top-margin: #{neo(checkboxfield-label-top-margin)};
12
16
  }
13
17
  }
@@ -1,7 +1,6 @@
1
1
  @import "field/trigger/all";
2
2
  @import "field/CheckBox";
3
3
  @import "field/Picker";
4
- @import "field/Radio";
5
4
  @import "field/Range";
6
5
  @import "field/Search";
7
6
  @import "field/Select";
@@ -1,13 +1,17 @@
1
1
  $neoMap: map-merge($neoMap, (
2
2
  'checkboxfield-color' : #aaa,
3
- 'checkboxfield-checked-color' : #1c60a0,
3
+ 'checkboxfield-color-checked' : #1c60a0,
4
+ 'checkboxfield-icon-font-family': '"Font Awesome 5 Free"',
5
+ 'checkboxfield-icon-font-size' : 14px,
4
6
  'checkboxfield-label-top-margin': 0 0 3px
5
7
  ));
6
8
 
7
9
  @if $useCssVars == true {
8
10
  :root .neo-theme-light { // .neo-checkboxfield
9
11
  --checkboxfield-color : #{neo(checkboxfield-color)};
10
- --checkboxfield-checked-color : #{neo(checkboxfield-checked-color)};
12
+ --checkboxfield-color-checked : #{neo(checkboxfield-color-checked)};
13
+ --checkboxfield-icon-font-family: #{neo(checkboxfield-icon-font-family)};
14
+ --checkboxfield-icon-font-size : #{neo(checkboxfield-icon-font-size)};
11
15
  --checkboxfield-label-top-margin: #{neo(checkboxfield-label-top-margin)};
12
16
  }
13
17
  }
package/src/Neo.mjs CHANGED
@@ -214,48 +214,30 @@ Neo = globalThis.Neo = Object.assign({
214
214
  /**
215
215
  * @memberOf module:Neo
216
216
  * @param {Object|Array|*} obj
217
- * @param {Boolean} [deep=false] Set this to true in case you want to clone nested objects as well
218
- * @param {Boolean} [ignoreNeoInstances=false] returns existing instances if set to true
217
+ * @param {Boolean} deep=false Set this to true in case you want to clone nested objects as well
218
+ * @param {Boolean} ignoreNeoInstances=false returns existing instances if set to true
219
219
  * @returns {Object|Array|*} the cloned input
220
220
  */
221
221
  clone(obj, deep=false, ignoreNeoInstances=false) {
222
222
  let out;
223
223
 
224
- switch (Neo.typeOf(obj)) {
225
- case 'Array': {
226
- return !deep ? [...obj] : [...obj.map(val => Neo.clone(val, deep, ignoreNeoInstances))];
227
- }
228
-
229
- case 'Date': {
230
- return new Date(obj.valueOf());
231
- }
232
-
233
- case 'Map': {
234
- return new Map(obj); // shallow copy
235
- }
224
+ return {
225
+ Array : () => !deep ? [...obj] : [...obj.map(val => Neo.clone(val, deep, ignoreNeoInstances))],
226
+ Date : () => new Date(obj.valueOf()),
227
+ Map : () => new Map(obj), // shallow copy
228
+ NeoInstance: () => ignoreNeoInstances ? obj : this.cloneNeoInstance(obj),
229
+ Set : () => new Set(obj),
236
230
 
237
- case 'NeoInstance': {
238
- return ignoreNeoInstances ? obj : this.cloneNeoInstance(obj);
239
- }
240
-
241
- case 'Object': {
231
+ Object: () => {
242
232
  out = {};
243
233
 
244
234
  Object.entries(obj).forEach(([key, value]) => {
245
235
  out[key] = !deep ? value : Neo.clone(value, deep, ignoreNeoInstances);
246
236
  });
247
237
 
248
- return out;
238
+ return out
249
239
  }
250
-
251
- case 'Set': {
252
- return new Set(obj); // shallow copy
253
- }
254
-
255
- default: {
256
- return obj; // return all other data types
257
- }
258
- }
240
+ }[Neo.typeOf(obj)]?.() || obj
259
241
  },
260
242
 
261
243
  /**
@@ -469,23 +451,18 @@ Neo = globalThis.Neo = Object.assign({
469
451
  return null;
470
452
  }
471
453
 
472
- switch (typeof item) {
473
- case 'function': {
454
+ return {
455
+ function: () => {
474
456
  if (item.prototype?.constructor.isClass) {
475
- return 'NeoClass';
457
+ return 'NeoClass'
476
458
  }
477
-
478
- break;
479
- }
480
-
481
- case 'object': {
459
+ },
460
+ object: () => {
482
461
  if (item.constructor.isClass && item instanceof Neo.core.Base) {
483
- return 'NeoInstance';
462
+ return 'NeoInstance'
484
463
  }
485
464
  }
486
- }
487
-
488
- return item.constructor.name;
465
+ }[typeof item]?.() || item.constructor.name
489
466
  }
490
467
  }, Neo);
491
468
 
@@ -1730,11 +1730,12 @@ class Base extends CoreBase {
1730
1730
  /**
1731
1731
  * Toggle a cls inside the vdomRoot of the component
1732
1732
  * @param {String} value
1733
+ * @param {Boolean} [add] Use this param to enforce an add() or remove() operation.
1733
1734
  */
1734
- toggleCls(value) {
1735
+ toggleCls(value, add) {
1735
1736
  let cls = this.cls;
1736
1737
 
1737
- NeoArray.toggle(cls, value);
1738
+ NeoArray.toggle(cls, value, add);
1738
1739
  this.cls = cls;
1739
1740
  }
1740
1741
 
@@ -1841,10 +1842,7 @@ class Base extends CoreBase {
1841
1842
 
1842
1843
  opts = {
1843
1844
  action: 'updateDom',
1844
- deltas: [{
1845
- id : id,
1846
- style: delta
1847
- }]
1845
+ deltas: [{id, style: delta}]
1848
1846
  };
1849
1847
 
1850
1848
  if (Neo.currentWorker.isSharedWorker) {
@@ -35,15 +35,18 @@ class CheckBox extends Base {
35
35
  * @member {Boolean} checked_=false
36
36
  */
37
37
  checked_: false,
38
- /**
39
- * True to change the checked state when clicking on the value label
40
- * @member {Boolean} enableLabelClicks_=true
41
- */
42
- enableLabelClicks_: true,
43
38
  /**
44
39
  * @member {Boolean} hideLabel_=false
45
40
  */
46
41
  hideLabel_: false,
42
+ /**
43
+ * @member {String[]} iconCls=['far','fa-square']
44
+ */
45
+ iconCls: ['far', 'fa-square'],
46
+ /**
47
+ * @member {String[]} iconClsChecked=['fas','fa-check']
48
+ */
49
+ iconClsChecked: ['fas', 'fa-check'],
47
50
  /**
48
51
  * @member {String} inputType_='checkbox'
49
52
  */
@@ -82,10 +85,11 @@ class CheckBox extends Base {
82
85
  * @member {Object} _vdom
83
86
  */
84
87
  _vdom:
85
- {cn: [
86
- {tag: 'label', cls: []},
88
+ {tag: 'label', cn: [
89
+ {tag: 'span', cls: []},
87
90
  {tag: 'input', cls: ['neo-checkbox-input']},
88
- {tag: 'label', cls: ['neo-checkbox-value-label']}
91
+ {tag: 'i', cls: ['neo-checkbox-icon']},
92
+ {tag: 'span', cls: ['neo-checkbox-value-label']}
89
93
  ]}
90
94
  }}
91
95
 
@@ -108,9 +112,14 @@ class CheckBox extends Base {
108
112
  * @protected
109
113
  */
110
114
  afterSetChecked(value, oldValue) {
111
- let me = this;
115
+ let me = this,
116
+ iconCls = me.vdom.cn[2].cls;
112
117
 
113
118
  me.vdom.cn[1].checked = value;
119
+
120
+ NeoArray.toggle(iconCls, me.iconClsChecked, value);
121
+ NeoArray.toggle(iconCls, me.iconCls, !value);
122
+
114
123
  me.update();
115
124
 
116
125
  if (oldValue !== undefined) {
@@ -122,25 +131,6 @@ class CheckBox extends Base {
122
131
  }
123
132
  }
124
133
 
125
- /**
126
- * Triggered after the enableLabelClicks config got changed
127
- * @param {Boolean} value
128
- * @param {Boolean} oldValue
129
- * @protected
130
- */
131
- afterSetEnableLabelClicks(value, oldValue) {
132
- let me = this,
133
- label = me.vdom.cn[2];
134
-
135
- if (value) {
136
- label.for = me.getInputElId();
137
- } else {
138
- delete label.for;
139
- }
140
-
141
- me.update();
142
- }
143
-
144
134
  /**
145
135
  * Triggered after the hideLabel config got changed
146
136
  * @param {String} value
@@ -164,7 +154,8 @@ class CheckBox extends Base {
164
154
 
165
155
  vdom.cn[0].id = me.getLabelId();
166
156
  vdom.cn[1].id = me.getInputElId();
167
- vdom.cn[2].id = me.getValueLabelId();
157
+ vdom.cn[2].id = me.getIconElId();
158
+ vdom.cn[3].id = me.getValueLabelId();
168
159
 
169
160
  // silent vdom update, the super call will trigger the engine
170
161
  super.afterSetId(value, oldValue);
@@ -272,7 +263,7 @@ class CheckBox extends Base {
272
263
  */
273
264
  afterSetValueLabelText(value, oldValue) {
274
265
  let me = this,
275
- valueLabel = me.vdom.cn[2],
266
+ valueLabel = me.vdom.cn[3],
276
267
  showLabel = !!value; // hide the label, in case value === null || value === ''
277
268
 
278
269
  if (showLabel) {
@@ -304,6 +295,13 @@ class CheckBox extends Base {
304
295
  return this.beforeSetEnumValue(value, oldValue, 'labelPosition');
305
296
  }
306
297
 
298
+ /**
299
+ * @returns {String}
300
+ */
301
+ getIconElId() {
302
+ return `${this.id}__icon`;
303
+ }
304
+
307
305
  /**
308
306
  * @returns {String}
309
307
  */
@@ -333,17 +331,10 @@ class CheckBox extends Base {
333
331
  let me = this,
334
332
  checked = data.target.checked;
335
333
 
336
- me._checked = checked; // silent update
337
-
338
334
  // keep the vdom & vnode in sync for future updates
339
- me.vdom.cn[1].checked = checked;
340
335
  me.vnode.childNodes[me.hideLabel ? 0 : 1].attributes.checked = `${checked}`;
341
336
 
342
- me.fire('change', {
343
- component: me,
344
- oldValue : !checked,
345
- value : checked
346
- });
337
+ me.checked = checked;
347
338
  }
348
339
  }
349
340
 
@@ -18,26 +18,17 @@ class Radio extends CheckBox {
18
18
  */
19
19
  ntype: 'radiofield',
20
20
  /**
21
- * @member {String[]} baseCls=['neo-radiofield']
21
+ * @member {String[]} baseCls=['neo-radiofield','neo-checkboxfield']
22
22
  */
23
- baseCls: ['neo-radiofield'],
23
+ baseCls: ['neo-radiofield', 'neo-checkboxfield'],
24
24
  /**
25
- * @member {String} inputType='radio'
26
- */
27
- inputType: 'radio',
28
- /**
29
- * @member {String[]} labelBaseCls=['neo-radio-label']
25
+ * @member {String[]} iconCls=['far','fa-circle']
30
26
  */
31
- labelBaseCls: ['neo-radio-label'],
27
+ iconCls: ['far', 'fa-circle'],
32
28
  /**
33
- * @member {Object} _vdom
29
+ * @member {String} inputType='radio'
34
30
  */
35
- _vdom:
36
- {cn: [
37
- {tag: 'label', cls: []},
38
- {tag: 'input', cls: ['neo-radio-input']},
39
- {tag: 'label', cls: ['neo-radio-value-label']}
40
- ]}
31
+ inputType: 'radio'
41
32
  }}
42
33
 
43
34
  /**
@@ -50,18 +41,7 @@ class Radio extends CheckBox {
50
41
  super.afterSetChecked(value, oldValue);
51
42
 
52
43
  // update radios with the same name to be unchecked
53
- if (value) {
54
- this.uncheckGroupItems();
55
- }
56
- }
57
-
58
- /**
59
- * Gets triggered when a user checks a radio input.
60
- * @param {Object} data
61
- */
62
- onInputValueChange(data) {
63
- super.onInputValueChange(data);
64
- this.uncheckGroupItems();
44
+ value && this.uncheckGroupItems()
65
45
  }
66
46
 
67
47
  /**
@@ -79,22 +59,9 @@ class Radio extends CheckBox {
79
59
 
80
60
  radios.forEach(item => {
81
61
  if (item.id !== me.id && item._checked) {
82
- item._checked = false; // silent update
83
-
84
- // keep the vdom & vnode in sync for future updates
85
- item.vdom.cn[1].checked = false;
86
-
87
- if (item.vnode) {
88
- item.vnode.childNodes[me.hideLabel ? 0 : 1].attributes.checked = 'false';
89
- }
90
-
91
- item.fire('change', {
92
- component: me,
93
- oldValue : true,
94
- value : false
95
- });
62
+ item.checked = false;
96
63
  }
97
- });
64
+ })
98
65
  }
99
66
  }
100
67
 
@@ -171,18 +171,6 @@ class DomEvents extends Base {
171
171
  });
172
172
  }
173
173
 
174
- /**
175
- * Returns the distance between two points
176
- * @param {Number} x1 The X position of the first point
177
- * @param {Number} y1 The Y position of the first point
178
- * @param {Number} x2 The X position of the second point
179
- * @param {Number} y2 The Y position of the second point
180
- * @returns {Number}
181
- */
182
- getDistance(x1, y1, x2, y2) {
183
- return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
184
- }
185
-
186
174
  /**
187
175
  * Local domEvent listener
188
176
  * @param {Object} event
@@ -246,6 +234,18 @@ class DomEvents extends Base {
246
234
  Neo.worker.Manager.sendMessage('app', config);
247
235
  }
248
236
 
237
+ /**
238
+ * Returns the distance between two points
239
+ * @param {Number} x1 The X position of the first point
240
+ * @param {Number} y1 The Y position of the first point
241
+ * @param {Number} x2 The X position of the second point
242
+ * @param {Number} y2 The Y position of the second point
243
+ * @returns {Number}
244
+ */
245
+ getDistance(x1, y1, x2, y2) {
246
+ return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
247
+ }
248
+
249
249
  /**
250
250
  * @param {Object} event
251
251
  * @returns {Object}
@@ -123,7 +123,7 @@ class DomEvent extends Base {
123
123
  if (eventName === 'focusin' || eventName === 'focusout') {
124
124
  FocusManager['on' + Neo.capitalize(eventName)]({
125
125
  componentPath: path,
126
- data : data
126
+ data
127
127
  });
128
128
 
129
129
  break;
@@ -303,14 +303,14 @@ class DomEvent extends Base {
303
303
  listenerConfig = {
304
304
  bubble : config.hasOwnProperty('bubble') ? config.bubble : opts.hasOwnProperty('bubble') ? opts.bubble : true,
305
305
  delegate : config.delegate,
306
- eventName : eventName,
307
- fn : fn,
306
+ eventName,
307
+ fn,
308
308
  id : listenerId,
309
309
  mounted : !config.local && globalDomEvents.includes(eventName),
310
310
  originalConfig: config.originalConfig,
311
311
  ownerId : config.ownerId,
312
312
  priority : config.priority || 1,
313
- scope : scope,
313
+ scope,
314
314
  vnodeId : config.vnodeId
315
315
  };
316
316
 
@@ -115,9 +115,20 @@ class NeoArray extends Base {
115
115
  * Removes an item from an array in case it does exist, otherwise adds it
116
116
  * @param {Array} arr
117
117
  * @param {*} item
118
+ * @param {Boolean} [add]
118
119
  */
119
- static toggle(arr, item) {
120
- this[this.hasItem(arr, item) ? 'remove' : 'add'](arr, item);
120
+ static toggle(arr, item, add) {
121
+ let operation;
122
+
123
+ if (add === true) {
124
+ operation = 'add';
125
+ } else if (add === false) {
126
+ operation = 'remove';
127
+ } else {
128
+ operation = this.hasItem(arr, item) ? 'remove' : 'add';
129
+ }
130
+
131
+ this[operation](arr, item);
121
132
  }
122
133
 
123
134
  /**
@@ -1,13 +0,0 @@
1
- $neoMap: map-merge($neoMap, (
2
- 'radiofield-color' : #eee,
3
- 'radiofield-checked-color' : #64B5F6,
4
- 'radiofield-label-top-margin': 0 0 3px
5
- ));
6
-
7
- @if $useCssVars == true {
8
- :root .neo-theme-dark { // .neo-radiofield
9
- --radiofield-color : #{neo(radiofield-color)};
10
- --radiofield-checked-color : #{neo(radiofield-checked-color)};
11
- --radiofield-label-top-margin: #{neo(radiofield-label-top-margin)};
12
- }
13
- }
@@ -1,13 +0,0 @@
1
- $neoMap: map-merge($neoMap, (
2
- 'radiofield-color' : #aaa,
3
- 'radiofield-checked-color' : #1c60a0,
4
- 'radiofield-label-top-margin': 0 0 3px
5
- ));
6
-
7
- @if $useCssVars == true {
8
- :root .neo-theme-light { // .neo-radiofield
9
- --radiofield-color : #{neo(radiofield-color)};
10
- --radiofield-checked-color : #{neo(radiofield-checked-color)};
11
- --radiofield-label-top-margin: #{neo(radiofield-label-top-margin)};
12
- }
13
- }