neo.mjs 5.1.1 → 5.1.2

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.
@@ -419,18 +419,21 @@ fields: [{
419
419
  * (39) `if (/**/) {` if, blank char, parenthesis, blank char, curly bracket
420
420
  * (40) `for (/**/) {` for, blank char, parenthesis, blank char, curly bracket
421
421
  * (41) `switch(/**/) {` switch, parenthesis, blank char, curly bracket `// could get changed to use a blank char as well
422
- * (42) Use optional chaining => `?.` where it makes sense
422
+ * (42) `while (/**/) {` while, blank char, parenthesis, blank char, curly bracket
423
+ * (43) Use optional chaining => `?.` where it makes sense
423
424
  + Bad: `myView && myView.myFn && myView.myFn();`
424
425
  + Good: `myView?.myFn?.();`
425
426
  + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining
426
- * (43) Use method definitions (meaning avoid using the term `function`)
427
+ * (44) Use method definitions (meaning avoid using the term `function`)
427
428
  + Bad: `let obj = {a: function() {/**/}};`
428
429
  + Good: `let obj = {a() {/**/}};`
429
430
  + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions
430
- * (44) Use shorthand property names when possible
431
+ * (45) Use shorthand property names when possible
431
432
  + Bad: `let obj = {record: record}`
432
433
  + Good: `let obj = {record};`
433
434
  + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#property_definitions
434
- * (45) Do not use killing commas (while IE6 is luckily no longer an issue => file size)
435
- + Bad: `let obj = {a: 1,}`
435
+ * (46) Do not use killing commas (while IE6 is luckily no longer an issue => file size)
436
+ + Bad: `let obj = {a: 1,};`
437
+ + Bad: `let arr = [1,];`
436
438
  + Good: `let obj = {a: 1};`
439
+ + Good: `let arr = [1];`
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='5.1.1'
23
+ * @member {String} version='5.1.2'
24
24
  */
25
- version: '5.1.1'
25
+ version: '5.1.2'
26
26
  }
27
27
 
28
28
  /**
@@ -1,4 +1,17 @@
1
1
  [
2
+ {
3
+ "author" : "Torsten Dinkheller",
4
+ "authorImage" : "author_TorstenDinkheller.jpg",
5
+ "date" : "Feb 03, 2023",
6
+ "id" : 57,
7
+ "image" : "leveraging-overrides.jpg",
8
+ "name" : "Leveraging Overrides for Resilient Customizations and Effective Bugfixes in JavaScript Frameworks",
9
+ "provider" : "Medium",
10
+ "publisher" : "ITNEXT",
11
+ "selectedInto": [],
12
+ "type" : "Blog Post",
13
+ "url" : "https://itnext.io/leveraging-overrides-for-resilient-customizations-and-effective-bugfixes-in-javascript-frameworks-5b8ccfec0f8?source=friends_link&sk=790e868aac8b8570f0eee093adc30c00"
14
+ },
2
15
  {
3
16
  "author" : "Torsten Dinkheller",
4
17
  "authorImage" : "author_TorstenDinkheller.jpg",
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='5.1.1'
23
+ * @member {String} version='5.1.2'
24
24
  */
25
- version: '5.1.1'
25
+ version: '5.1.2'
26
26
  }
27
27
 
28
28
  /**
@@ -1,4 +1,5 @@
1
1
  import BreadcrumbToolbar from '../../../../src/toolbar/Breadcrumb.mjs';
2
+ import Store from '../../../../src/data/Store.mjs';
2
3
  import Viewport from '../../../../src/container/Viewport.mjs';
3
4
 
4
5
  /**
@@ -16,8 +17,36 @@ class MainContainer extends Viewport {
16
17
  * @member {Object[]} items
17
18
  */
18
19
  items: [{
19
- module: BreadcrumbToolbar,
20
- flex : 'none'
20
+ module : BreadcrumbToolbar,
21
+ activeKey: 2,
22
+ flex : 'none',
23
+
24
+ store: {
25
+ module: Store,
26
+
27
+ model: {
28
+ fields: [{
29
+ name: 'id',
30
+ type: 'Integer'
31
+ }, {
32
+ name: 'name',
33
+ type: 'String'
34
+ }, {
35
+ name: 'parentId',
36
+ type: 'Integer'
37
+ }, {
38
+ name: 'route',
39
+ type: 'String'
40
+ }]
41
+ },
42
+
43
+ data: [
44
+ {id: 1, name: 'Home', parentId: null, route: '/home/'},
45
+ {id: 2, name: 'Accessibility', parentId: 1, route: '/home/accessibility/'},
46
+ {id: 3, name: 'Imprint', parentId: 1, route: '/home/imprint/'},
47
+ {id: 4, name: 'News', parentId: 1, route: '/home/news/'},
48
+ ]
49
+ }
21
50
  }],
22
51
  /**
23
52
  * @member {Object} layout={ntype:'vbox',align:'stretch'}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neo.mjs",
3
- "version": "5.1.1",
3
+ "version": "5.1.2",
4
4
  "description": "The webworkers driven UI framework",
5
5
  "type": "module",
6
6
  "repository": {
@@ -18,6 +18,7 @@
18
18
  .neo-typeahead-input {
19
19
  border-color: transparent;
20
20
  color : v(selectfield-input-hint-color);
21
+ font : v(textfield-input-font);
21
22
  min-height : 25px;
22
23
  opacity : v(selectfield-input-hint-opacity);
23
24
  }
@@ -309,7 +309,6 @@
309
309
  flex-shrink : 1;
310
310
  margin : 0; // important for Safari => #1125
311
311
  min-height : v(textfield-input-height);
312
- width : 30px;
313
312
  }
314
313
  }
315
314
 
@@ -331,6 +330,7 @@
331
330
  min-width : 50px;
332
331
  padding : v(textfield-input-padding);
333
332
  transition : 250ms border-color cubic-bezier(0.4, 0, 0.2, 1);
333
+ width : 100%;
334
334
 
335
335
  &:focus {
336
336
  outline: none;
@@ -0,0 +1,17 @@
1
+ .neo-breadcrumb-toolbar {
2
+ .neo-button {
3
+ &:not(:last-child) {
4
+ margin-right: 30px;
5
+
6
+ &::after {
7
+ color : v(button-text-color);
8
+ content : "\f105";
9
+ display : block;
10
+ font-family : "Font Awesome 5 Free";
11
+ font-weight : 600;
12
+ position : absolute;
13
+ right : -20px;
14
+ }
15
+ }
16
+ }
17
+ }
@@ -237,12 +237,12 @@ const DefaultConfig = {
237
237
  useVdomWorker: true,
238
238
  /**
239
239
  * buildScripts/injectPackageVersion.mjs will update this value
240
- * @default '5.1.1'
240
+ * @default '5.1.2'
241
241
  * @memberOf! module:Neo
242
242
  * @name config.version
243
243
  * @type String
244
244
  */
245
- version: '5.1.1'
245
+ version: '5.1.2'
246
246
  };
247
247
 
248
248
  Object.assign(DefaultConfig, {
@@ -215,7 +215,11 @@ class Base extends Component {
215
215
 
216
216
  case 'NeoInstance': {
217
217
  item.set(config);
218
+
219
+ // In case an item got created outside a VC or VM based hierarchy, there might be bindings or string
220
+ // based listeners which still need to get resolved.
218
221
  item.getController()?.parseConfig(item);
222
+ item.getModel() ?.parseConfig(item);
219
223
  break;
220
224
  }
221
225
 
@@ -242,7 +242,7 @@ class Number extends Text {
242
242
  value = (oldValue - stepSize) < me.minValue ? me.maxValue : (oldValue - stepSize);
243
243
 
244
244
  if (me.excludedValues) {
245
- while(me.excludedValues.includes(value)) {
245
+ while (me.excludedValues.includes(value)) {
246
246
  value = Math.max(me.minValue, value - stepSize);
247
247
  }
248
248
  }
@@ -262,7 +262,7 @@ class Number extends Text {
262
262
  value = (oldValue + stepSize) > me.maxValue ? me.minValue : (oldValue + stepSize);
263
263
 
264
264
  if (me.excludedValues) {
265
- while(me.excludedValues.includes(value)) {
265
+ while (me.excludedValues.includes(value)) {
266
266
  value = Math.min(me.maxValue, value + stepSize);
267
267
  }
268
268
  }
@@ -115,6 +115,20 @@ class Picker extends Text {
115
115
  this.cls = cls;
116
116
  }
117
117
 
118
+ /**
119
+ * Triggered after the mounted config got changed
120
+ * @param {Boolean} value
121
+ * @param {Boolean} oldValue
122
+ * @protected
123
+ */
124
+ afterSetMounted(value, oldValue) {
125
+ if (value === false && oldValue && this.pickerIsMounted) {
126
+ this.picker.hide();
127
+ }
128
+
129
+ super.afterSetMounted(value, oldValue);
130
+ }
131
+
118
132
  /**
119
133
  * @param {Boolean} silent
120
134
  */
@@ -186,6 +200,14 @@ class Picker extends Text {
186
200
  return null;
187
201
  }
188
202
 
203
+ /**
204
+ * @param args
205
+ */
206
+ destroy(...args) {
207
+ this.picker?.destroy();
208
+ super.destroy(...args);
209
+ }
210
+
189
211
  /**
190
212
  * @param {Function} [callback]
191
213
  * @param {Object} [callbackScope]
@@ -640,7 +640,7 @@ class Select extends Picker {
640
640
  }
641
641
 
642
642
  if (!hasMatch && inputHintEl) {
643
- inputHintEl.value = '';
643
+ inputHintEl.value = null;
644
644
  me.hintRecordId = null;
645
645
  }
646
646
 
@@ -530,7 +530,17 @@ class Text extends Base {
530
530
  * @protected
531
531
  */
532
532
  afterSetPlaceholderText(value, oldValue) {
533
- this.changeInputElKey('placeholder', value === '' ? null : value);
533
+ let me = this,
534
+ cls = me.cls;
535
+
536
+ me.changeInputElKey('placeholder', value === '' ? null : value);
537
+
538
+ // a non-empty placeholder needs to keep the 'neo-has-content' rule
539
+ // => labelPosition: 'inline' should keep the label at the top
540
+ if (Neo.isEmpty(value) !== Neo.isEmpty(oldValue)) {
541
+ NeoArray[value !== null && value.toString().length > 0 ? 'add' : 'remove'](cls, 'neo-has-content');
542
+ me.cls = cls;
543
+ }
534
544
  }
535
545
 
536
546
  /**
@@ -686,21 +696,19 @@ class Text extends Base {
686
696
  * @protected
687
697
  */
688
698
  afterSetValue(value, oldValue) {
689
- let me = this,
690
- cls = me.cls,
691
- emptyValue = Neo.isEmpty(value),
692
- originalValue = me.originalConfig.value,
693
- isDirty = value !== originalValue && emptyValue !== Neo.isEmpty(originalValue);
699
+ let me = this,
700
+ cls = me.cls,
701
+ placeholderText = me.placeholderText,
702
+ hasContent = placeholderText?.length > 0 || value !== null && value.toString().length > 0,
703
+ originalValue = me.originalConfig.value,
704
+ isDirty = value !== originalValue && Neo.isEmpty(value) !== Neo.isEmpty(originalValue);
694
705
 
695
706
  me.silentVdomUpdate = true;
696
707
 
697
708
  me.getInputEl().value = value;
698
709
 
699
- if (emptyValue !== Neo.isEmpty(oldValue)) {
700
- NeoArray[value !== null && value.toString().length > 0 ? 'add' : 'remove'](cls, 'neo-has-content');
701
- }
702
-
703
- NeoArray[isDirty ? 'add' : 'remove'](cls, 'neo-is-dirty');
710
+ NeoArray[hasContent ? 'add' : 'remove'](cls, 'neo-has-content');
711
+ NeoArray[isDirty ? 'add' : 'remove'](cls, 'neo-is-dirty');
704
712
  me.cls = cls;
705
713
 
706
714
  me.validate(); // silent
@@ -407,11 +407,20 @@ class DomEvents extends Base {
407
407
  * @param {Object} event
408
408
  */
409
409
  onChange(event) {
410
- this.sendMessageToApp({
410
+ let target = event.target,
411
+
412
+ data = {
411
413
  ...this.getEventData(event),
412
- valid: event.target.checkValidity(),
413
- value: event.target.value
414
- });
414
+ valid: target.checkValidity(),
415
+ value: target.value
416
+ };
417
+
418
+ // input and change events can pass a FileList for input type file
419
+ if (target.files) {
420
+ data.files = target.files;
421
+ }
422
+
423
+ this.sendMessageToApp(data);
415
424
  }
416
425
 
417
426
  /**
@@ -13,6 +13,19 @@ class Breadcrumb extends Toolbar {
13
13
  * @protected
14
14
  */
15
15
  className: 'Neo.toolbar.Breadcrumb',
16
+ /**
17
+ * @member {String} ntype='breadcrumb-toolbar'
18
+ * @protected
19
+ */
20
+ ntype: 'breadcrumb-toolbar',
21
+ /**
22
+ * @member {String[]} baseCls=['neo-breadcrumb-toolbar','neo-toolbar']
23
+ */
24
+ baseCls: ['neo-breadcrumb-toolbar', 'neo-toolbar'],
25
+ /**
26
+ * @member {Number|String|null} activeKey_=null
27
+ */
28
+ activeKey_: null,
16
29
  /**
17
30
  * @member {Object[]} items
18
31
  */
@@ -23,6 +36,16 @@ class Breadcrumb extends Toolbar {
23
36
  store_: null
24
37
  }
25
38
 
39
+ /**
40
+ * Triggered after the activeKey config got changed
41
+ * @param {Number|String|null} value
42
+ * @param {Number|String|null} oldValue
43
+ * @protected
44
+ */
45
+ afterSetActiveKey(value, oldValue) {
46
+ this.store.getCount?.() > 0 && this.updateItems()
47
+ }
48
+
26
49
  /**
27
50
  * Triggered after the store config got changed
28
51
  * @param {Neo.data.Store|Object} value
@@ -30,7 +53,14 @@ class Breadcrumb extends Toolbar {
30
53
  * @protected
31
54
  */
32
55
  afterSetStore(value, oldValue) {
56
+ let me = this;
57
+
58
+ value.on({
59
+ load: this.onStoreLoad,
60
+ scope: me
61
+ });
33
62
 
63
+ value?.getCount() > 0 && me.onStoreLoad(value.items)
34
64
  }
35
65
 
36
66
  /**
@@ -44,6 +74,64 @@ class Breadcrumb extends Toolbar {
44
74
  oldValue?.destroy();
45
75
  return ClassSystemUtil.beforeSetInstance(value, Store);
46
76
  }
77
+
78
+ /**
79
+ * @returns {Object[]}
80
+ */
81
+ getPathItems() {
82
+ let items = [],
83
+ parentId = this.activeKey,
84
+ store = this.store,
85
+ item;
86
+
87
+ while (parentId !== null) {
88
+ item = store.get(parentId);
89
+
90
+ items.unshift(item);
91
+
92
+ parentId = item.parentId;
93
+ }
94
+
95
+ return items;
96
+ }
97
+
98
+ /**
99
+ * @param {Object[]} items
100
+ */
101
+ onStoreLoad(items) {
102
+ this.activeKey !== null && this.updateItems()
103
+ }
104
+
105
+ /**
106
+ *
107
+ */
108
+ updateItems() {
109
+ let me = this,
110
+ items = me.items,
111
+ pathItems = me.getPathItems(),
112
+ i = 0,
113
+ len = pathItems.length,
114
+ newItems = [],
115
+ config, item
116
+
117
+ for (; i < len; i++) {
118
+ item = pathItems[i];
119
+
120
+ config = {
121
+ editRoute: false,
122
+ route : item.route,
123
+ text : item.name
124
+ };
125
+
126
+ if (items[i]) {
127
+ items[i].set(config);
128
+ } else {
129
+ newItems.push(config);
130
+ }
131
+ }
132
+
133
+ newItems.length > 0 && me.add(newItems);
134
+ }
47
135
  }
48
136
 
49
137
  Neo.applyClassConfig(Breadcrumb);