neo.mjs 6.9.3 → 6.9.5

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='6.9.3'
23
+ * @member {String} version='6.9.5'
24
24
  */
25
- version: '6.9.3'
25
+ version: '6.9.5'
26
26
  }
27
27
 
28
28
  /**
@@ -45,7 +45,7 @@ class MainContainerController extends Component {
45
45
  */
46
46
 
47
47
 
48
- doPrehandling(value, oldValue, params = null) {
48
+ doPrehandling(params = null, value, oldValue) {
49
49
  const userId = parseInt(params.userId);
50
50
  if (userId > 0 && userId === this.data.activeUser) {
51
51
  return true;
@@ -137,27 +137,27 @@ class MainContainerController extends Component {
137
137
  this.#removeMetaButtonSelection();
138
138
  }
139
139
 
140
- handleHomeRoute(value, oldValue, params = null) {
140
+ handleHomeRoute(params = null, value, oldValue) {
141
141
  const centerContainer = this.getReference('center-container');
142
142
  centerContainer.layout.activeIndex = 4;
143
143
  }
144
144
 
145
- handleSection1Route(value, oldValue, params = null) {
145
+ handleSection1Route(params = null, value, oldValue) {
146
146
  const centerContainer = this.getReference('center-container');
147
147
  centerContainer.layout.activeIndex = 2;
148
148
  }
149
149
 
150
- handleSection2Route(value, oldValue, params = null) {
150
+ handleSection2Route(params = null, value, oldValue) {
151
151
  const centerContainer = this.getReference('center-container');
152
152
  centerContainer.layout.activeIndex = 3;
153
153
  }
154
154
 
155
- handleContactRoute(value, oldValue, params = null) {
155
+ handleContactRoute(params = null, value, oldValue) {
156
156
  const centerContainer = this.getReference('center-container');
157
157
  centerContainer.layout.activeIndex = 0;
158
158
  }
159
159
 
160
- handleUserRoute(value, oldValue, params = null) {
160
+ handleUserRoute(params = null, value, oldValue) {
161
161
  const centerContainer = this.getReference('center-container');
162
162
  centerContainer.layout.activeIndex = 1
163
163
  }
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='6.9.3'
23
+ * @member {String} version='6.9.5'
24
24
  */
25
- version: '6.9.3'
25
+ version: '6.9.5'
26
26
  }
27
27
 
28
28
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neo.mjs",
3
- "version": "6.9.3",
3
+ "version": "6.9.5",
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.31",
59
- "sass": "^1.69.4",
59
+ "sass": "^1.69.5",
60
60
  "showdown": "^2.1.0",
61
61
  "webpack": "^5.89.0",
62
62
  "webpack-cli": "^5.1.4",
@@ -236,12 +236,12 @@ const DefaultConfig = {
236
236
  useVdomWorker: true,
237
237
  /**
238
238
  * buildScripts/injectPackageVersion.mjs will update this value
239
- * @default '6.9.3'
239
+ * @default '6.9.5'
240
240
  * @memberOf! module:Neo
241
241
  * @name config.version
242
242
  * @type String
243
243
  */
244
- version: '6.9.3'
244
+ version: '6.9.5'
245
245
  };
246
246
 
247
247
  Object.assign(DefaultConfig, {
package/src/Neo.mjs CHANGED
@@ -380,20 +380,25 @@ Neo = globalThis.Neo = Object.assign({
380
380
  * @memberOf module:Neo
381
381
  * @param {Object} target
382
382
  * @param {Object} source
383
+ * @param {Object} defaults
383
384
  * @returns {Object} target
384
385
  */
385
- merge(target, source) {
386
+ merge(target, source, defaults) {
387
+ if (defaults) {
388
+ return Neo.merge(Neo.merge(target, defaults), source)
389
+ }
390
+
386
391
  for (const key in source) {
387
392
  const value = source[key];
388
393
 
389
394
  if (Neo.typeOf(value) === 'Object') {
390
- target[key] = Neo.merge(target[key] || {}, value);
395
+ target[key] = Neo.merge(target[key] || {}, value)
391
396
  } else {
392
- target[key] = value;
397
+ target[key] = value
393
398
  }
394
399
  }
395
400
 
396
- return target;
401
+ return target
397
402
  },
398
403
 
399
404
  /**
@@ -625,7 +625,8 @@ class Base extends CoreBase {
625
625
  me[state]()
626
626
  }
627
627
 
628
- me.fire(state, {id: me.id})
628
+ me.fire(state, {id: me.id});
629
+ me.fire('hiddenChange', {id: me.id, oldValue, value})
629
630
  }
630
631
 
631
632
  /**
@@ -995,8 +996,9 @@ class Base extends CoreBase {
995
996
  edgeAlign: value
996
997
  }
997
998
  }
999
+
998
1000
  // merge the incoming alignment specification into the configured default
999
- return Neo.merge(Neo.merge({}, me.constructor.config.align), value)
1001
+ return Neo.merge({}, value, me.constructor.config.align)
1000
1002
  }
1001
1003
 
1002
1004
  /**
@@ -1,6 +1,10 @@
1
1
  import CoreBase from '../core/Base.mjs';
2
2
  import HashHistory from '../util/HashHistory.mjs';
3
3
 
4
+ const
5
+ amountSlashesRegex = /\//g,
6
+ routeParamRegex = /{[^\s/]+}/g
7
+
4
8
  /**
5
9
  * @class Neo.controller.Base
6
10
  * @extends Neo.core.Base
@@ -17,22 +21,25 @@ class Base extends CoreBase {
17
21
  * @protected
18
22
  */
19
23
  ntype: 'controller',
20
-
21
24
  /**
22
- * @member {Object} routes={}
25
+ * @member {String} defaultRoute=undefined
23
26
  */
24
- routes_: {},
25
-
27
+ defaultRoute: null,
26
28
  /**
27
29
  * @member {Object} handleRoutes={}
28
30
  */
29
31
  handleRoutes: {},
30
-
31
32
  /**
32
- * @member {String} defaultRoute=undefined
33
+ * @example
34
+ * routes: {
35
+ * '/home' : 'handleHomeRoute',
36
+ * '/users/{userId}' : {handler: 'handleUserRoute', preHandler: 'preHandleUserRoute'},
37
+ * '/users/{userId}/posts/{postId}': 'handlePostRoute',
38
+ * 'default' : 'handleOtherRoutes'
39
+ * }
40
+ * @member {Object} routes={}
33
41
  */
34
- defaultRoute: null
35
-
42
+ routes_: {}
36
43
  }
37
44
 
38
45
  /**
@@ -41,43 +48,33 @@ class Base extends CoreBase {
41
48
  construct(config) {
42
49
  super.construct(config);
43
50
 
44
- const me = this;
45
- HashHistory.on('change', me.onHashChange, me);
51
+ HashHistory.on('change', this.onHashChange, this)
46
52
  }
47
53
 
48
54
  /**
49
- * Triggered after the badgePosition config got changed
50
- * @param {String} value
51
- * @param {String} oldValue
55
+ * Triggered after the routes config got changed
56
+ * @param {Object} value
57
+ * @param {Object} oldValue
52
58
  * @protected
53
59
  */
54
60
  afterSetRoutes(value, oldValue){
55
- const me = this;
61
+ let me = this,
62
+ routeKeys = Object.keys(value);
56
63
 
57
- const functionSort = (a,b) => {
58
- const usedRegex = new RegExp("/", "g");
59
- return a.match(usedRegex).length - b.match(usedRegex).length;
60
- }
61
- me.routes = Object.keys(value).sort(functionSort).reduce(
62
- (obj, key) => {
63
- obj[key] = value[key];
64
- return obj;
65
- },
66
- {}
67
- );
68
-
64
+ me.routes = routeKeys.sort(me.#sortRoutes).reduce((obj, key) => {
65
+ obj[key] = value[key];
66
+ return obj
67
+ }, {});
69
68
 
70
69
  me.handleRoutes = {};
71
- if (Object.keys(me.routes).length > 0) {
72
- Object.keys(me.routes).forEach(key => {
73
- if (key.toLowerCase() === 'default'){
74
- me.defaultRoute = value[key];
75
- } else {
76
- me.handleRoutes[key] = new RegExp(key.replace(/{[^\s/]+}/g, '([\\w-]+)')+'$');
77
- }
78
70
 
79
- });
80
- }
71
+ routeKeys.forEach(key => {
72
+ if (key.toLowerCase() === 'default'){
73
+ me.defaultRoute = value[key]
74
+ } else {
75
+ me.handleRoutes[key] = new RegExp(key.replace(routeParamRegex, '([\\w-]+)')+'$')
76
+ }
77
+ })
81
78
  }
82
79
 
83
80
  /**
@@ -90,12 +87,12 @@ class Base extends CoreBase {
90
87
  }
91
88
 
92
89
  /**
93
- * Placeholder method which gets triggered when an invalid route is called
94
- * @param {Object} value
95
- * @param {Object} oldValue
90
+ *
96
91
  */
97
- onNoRouteFound(value, oldValue) {
92
+ onConstructed() {
93
+ let currentHash = HashHistory.first();
98
94
 
95
+ currentHash && this.onHashChange(currentHash, null)
99
96
  }
100
97
 
101
98
  /**
@@ -104,73 +101,85 @@ class Base extends CoreBase {
104
101
  * @param {Object} oldValue
105
102
  */
106
103
  onHashChange(value, oldValue) {
104
+ let me = this,
105
+ hasRouteBeenFound = false,
106
+ handleRoutes = me.handleRoutes,
107
+ routeKeys = Object.keys(handleRoutes),
108
+ routes = me.routes,
109
+ handler, preHandler, responsePreHandler, result, route;
110
+
111
+ routeKeys.forEach(key => {
112
+ handler = null;
113
+ preHandler = null;
114
+ responsePreHandler = null;
115
+
116
+ result = value.hashString.match(handleRoutes[key]);
117
+
118
+ if (result) {
119
+ const
120
+ arrayParamIds = key.match(routeParamRegex),
121
+ arrayParamValues = result.splice(1, result.length - 1),
122
+ paramObject = {};
123
+
124
+ if (arrayParamIds && arrayParamIds.length !== arrayParamValues.length) {
125
+ throw 'Number of IDs and number of Values do not match';
126
+ }
107
127
 
108
- const me = this;
109
- let hasRouteBeenFound = false;
110
- Object.keys(me.handleRoutes).forEach( key => {
111
- let preHandler = undefined;
112
- let executeHandler = undefined;
113
- let responsePreHandler = undefined;
114
-
115
- const result = value.hashString.match(me.handleRoutes[key]);
116
- if (result){
117
-
118
- const paramsRegex = /{[^\s/]+}/g;
119
- const arrayParamIds = key.match(paramsRegex);
120
- const arrayParamValues = result.splice(1,result.length - 1);
121
- if (arrayParamIds && arrayParamIds.length !== arrayParamValues.length){
122
- throw "Number of IDs and number of Values do not match";
123
- }
124
-
125
- const paramObject = {};
126
- for(let i=0; arrayParamIds && i< arrayParamIds.length; i++){
127
- paramObject[ arrayParamIds[i].substring(1,arrayParamIds[i].length -1)] = arrayParamValues[i];
128
+ for (let i = 0; arrayParamIds && i < arrayParamIds.length; i++) {
129
+ paramObject[arrayParamIds[i].substring(1, arrayParamIds[i].length - 1)] = arrayParamValues[i];
128
130
  }
129
131
 
130
- const target = me.routes[key];
131
- if (Neo.isString(target)){
132
- executeHandler = this.routes[key];
132
+ route = routes[key];
133
+
134
+ if (Neo.isString(route)) {
135
+ handler = route;
133
136
  responsePreHandler = true;
134
- }
135
- if (Neo.isObject(target)){
136
- executeHandler = this.routes[key].handler;
137
- preHandler = this.routes[key].preHandler;
137
+ } else if (Neo.isObject(route)) {
138
+ handler = route.handler;
139
+ preHandler = route.preHandler;
140
+
138
141
  if (preHandler) {
139
- responsePreHandler = me[preHandler]?.call(this, value, oldValue, paramObject);
142
+ responsePreHandler = me[preHandler]?.call(me, paramObject, value, oldValue);
140
143
  } else {
141
144
  responsePreHandler = true;
142
- console.warn('No preHandler defined for routes -> todo it better');
143
- }
145
+ }
144
146
  }
145
147
 
146
148
  hasRouteBeenFound = true;
147
149
 
148
150
  if (responsePreHandler) {
149
- this[executeHandler]?.call(this, value, oldValue, paramObject);
151
+ me[handler]?.call(me, paramObject, value, oldValue)
150
152
  }
151
153
  }
152
154
  });
153
155
 
154
- if (Object.keys(me.handleRoutes).length > 0 && !hasRouteBeenFound) {
156
+ if (routeKeys.length > 0 && !hasRouteBeenFound) {
155
157
  if (me.defaultRoute) {
156
- this[me.defaultRoute]?.call(this, value, oldValue);
158
+ me[me.defaultRoute]?.(value, oldValue)
157
159
  } else {
158
- this.onNoRouteFound(value, oldValue);
160
+ me.onNoRouteFound(value, oldValue)
159
161
  }
160
162
  }
161
163
  }
162
164
 
163
165
  /**
164
- *
166
+ * Placeholder method which gets triggered when an invalid route is called
167
+ * @param {Object} value
168
+ * @param {Object} oldValue
165
169
  */
166
- onConstructed() {
167
- let currentHash = HashHistory.first();
170
+ onNoRouteFound(value, oldValue) {
168
171
 
169
- currentHash && this.onHashChange(currentHash, null);
170
172
  }
171
173
 
172
-
173
-
174
+ /**
175
+ * Internal helper method to sort routes by their amount of slashes
176
+ * @param {String} route1
177
+ * @param {String} route2
178
+ * @returns {Number}
179
+ */
180
+ #sortRoutes(route1, route2) {
181
+ return route1.match(amountSlashesRegex).length - route2.match(amountSlashesRegex).length
182
+ }
174
183
  }
175
184
 
176
185
  Neo.applyClassConfig(Base);
package/src/core/Base.mjs CHANGED
@@ -71,6 +71,12 @@ class Base {
71
71
  * @protected
72
72
  */
73
73
  ntype: 'base',
74
+ /**
75
+ * While it is recommended to change the static delayable configs on class level,
76
+ * you can change it on instance level too. If not null, we will do a deep merge.
77
+ * @member {Object} delayable=null
78
+ */
79
+ delayable: null,
74
80
  /**
75
81
  * The unique component id
76
82
  * @member {String|null} id_=null
@@ -178,9 +184,11 @@ class Base {
178
184
  * Adjusts all methods inside static delayable
179
185
  */
180
186
  applyDelayable() {
181
- let me = this;
187
+ let me = this,
188
+ ctorDelayable = me.constructor.delayable,
189
+ delayable = me.delayable ? Neo.merge({}, me.delayable, ctorDelayable) : ctorDelayable;
182
190
 
183
- Object.entries(me.constructor.delayable).forEach(([key, value]) => {
191
+ Object.entries(delayable).forEach(([key, value]) => {
184
192
  let map = {
185
193
  debounce() {me[key] = new debounce(me[key], me, value.timer)},
186
194
  throttle() {me[key] = new throttle(me[key], me, value.timer)}
@@ -85,13 +85,6 @@ class Picker extends Text {
85
85
  }]
86
86
  }
87
87
 
88
- /**
89
- * Internal flag to prevent showing a picker multiple times
90
- * @member {Boolean} pickerIsMounting=false
91
- * @protected
92
- */
93
- pickerIsMounting = false
94
-
95
88
  /**
96
89
  * @param {Object} config
97
90
  */
@@ -183,6 +176,8 @@ class Picker extends Text {
183
176
  }
184
177
  });
185
178
 
179
+ me.picker.on('hiddenChange', me.onPickerHiddenChange, me);
180
+
186
181
  return me.picker
187
182
  }
188
183
 
@@ -291,6 +286,17 @@ class Picker extends Text {
291
286
  this.pickerIsMounted && this.hidePicker()
292
287
  }
293
288
 
289
+ /**
290
+ * @param {Object} data
291
+ * @param {String} data.id
292
+ * @param {Boolean} data.oldValue
293
+ * @param {Boolean} data.value
294
+ * @protected
295
+ */
296
+ onPickerHiddenChange(data) {
297
+ this.pickerIsMounted = !data.value
298
+ }
299
+
294
300
  /**
295
301
  * Called by form.field.trigger.Picker
296
302
  * @protected
@@ -303,8 +309,7 @@ class Picker extends Text {
303
309
  *
304
310
  */
305
311
  showPicker() {
306
- let picker = this.getPicker();
307
- picker.hidden = false
312
+ this.getPicker().hidden = false
308
313
  }
309
314
 
310
315
  /**
@@ -20,6 +20,11 @@ class Button extends BaseButton {
20
20
  * @member {String[]} baseCls=['neo-button','neo-tab-button']
21
21
  */
22
22
  baseCls: ['neo-tab-header-button', 'neo-button'],
23
+ /**
24
+ * Specify a role tag attribute for the vdom root.
25
+ * @member {String|null} role='tab'
26
+ */
27
+ role: 'tab',
23
28
  /**
24
29
  * @member {Boolean} useActiveTabIndicator_=true
25
30
  */
@@ -28,7 +33,7 @@ class Button extends BaseButton {
28
33
  * @member {Object} _vdom
29
34
  */
30
35
  _vdom:
31
- {tag: 'button', type: 'button', cn: [
36
+ {tag: 'button', cn: [
32
37
  {tag: 'span', cls: ['neo-button-glyph']},
33
38
  {tag: 'span', cls: ['neo-button-text']},
34
39
  {cls: ['neo-button-badge']},
@@ -39,6 +44,26 @@ class Button extends BaseButton {
39
44
  ]}
40
45
  }
41
46
 
47
+ /**
48
+ * Triggered after the pressed config got changed
49
+ * @param {Boolean} value
50
+ * @param {Boolean} oldValue
51
+ * @protected
52
+ */
53
+ afterSetPressed(value, oldValue) {
54
+ super.afterSetPressed(value, oldValue);
55
+
56
+ let me = this;
57
+
58
+ if (value) {
59
+ me.vdom['aria-selected'] = true
60
+ } else {
61
+ delete me.vdom['aria-selected']
62
+ }
63
+
64
+ me.update()
65
+ }
66
+
42
67
  /**
43
68
  * Triggered after the useActiveTabIndicator config got changed
44
69
  * @param {Boolean} value