neo.mjs 5.10.2 → 5.10.4

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.
Files changed (44) hide show
  1. package/apps/ServiceWorker.mjs +2 -2
  2. package/buildScripts/createClass.mjs +10 -2
  3. package/examples/ConfigurationViewport.mjs +6 -2
  4. package/examples/ServiceWorker.mjs +2 -2
  5. package/examples/component/process/MainContainer.mjs +90 -0
  6. package/examples/component/process/app.mjs +6 -0
  7. package/examples/component/process/index.html +11 -0
  8. package/examples/component/process/neo-config.json +6 -0
  9. package/examples/component/process/realWorldExample/MainContainer.mjs +334 -0
  10. package/examples/component/process/realWorldExample/RangeHeader.jpg +0 -0
  11. package/examples/component/process/realWorldExample/app.mjs +6 -0
  12. package/examples/component/process/realWorldExample/index.html +11 -0
  13. package/examples/component/process/realWorldExample/neo-config.json +6 -0
  14. package/examples/component/toast/MainContainer.mjs +9 -6
  15. package/examples/component/toast/MainContainerController.mjs +5 -5
  16. package/examples/component/toast/neo-config.json +1 -1
  17. package/examples/container/accordion/MainContainer.mjs +123 -0
  18. package/examples/container/accordion/app.mjs +6 -0
  19. package/examples/container/accordion/index.html +11 -0
  20. package/examples/container/accordion/neo-config.json +6 -0
  21. package/package.json +1 -1
  22. package/resources/scss/src/component/Process.scss +2 -4
  23. package/resources/scss/src/container/Accordion.scss +25 -0
  24. package/resources/scss/src/container/AccordionItem.scss +83 -0
  25. package/resources/scss/src/form/field/CheckBox.scss +1 -0
  26. package/resources/scss/src/form/field/Range.scss +1 -0
  27. package/resources/scss/theme-dark/component/Process.scss +8 -8
  28. package/resources/scss/theme-dark/container/Accordion.scss +13 -0
  29. package/resources/scss/theme-dark/container/AccordionItem.scss +23 -0
  30. package/resources/scss/theme-light/component/Process.scss +7 -7
  31. package/resources/scss/theme-light/container/Accordion.scss +13 -0
  32. package/resources/scss/theme-light/container/AccordionItem.scss +23 -0
  33. package/src/DefaultConfig.mjs +2 -2
  34. package/src/component/Process.mjs +11 -7
  35. package/src/component/Splitter.mjs +5 -4
  36. package/src/component/Toast.mjs +2 -1
  37. package/src/container/Accordion.mjs +130 -0
  38. package/src/container/AccordionItem.mjs +178 -0
  39. package/src/form/Container.mjs +42 -25
  40. package/src/form/field/CheckBox.mjs +7 -2
  41. package/src/form/field/Number.mjs +49 -37
  42. package/src/form/field/Range.mjs +31 -4
  43. package/src/form/field/Text.mjs +6 -1
  44. package/src/manager/Toast.mjs +5 -1
@@ -0,0 +1,130 @@
1
+ import AccordionItem from './AccordionItem.mjs';
2
+ import NeoArray from '../util/Array.mjs';
3
+ import Panel from './Panel.mjs';
4
+
5
+ /**
6
+ * @class Neo.container.Accordion
7
+ * @extends Neo.container.Panel
8
+ */
9
+ class Accordion extends Panel {
10
+ static config = {
11
+ /**
12
+ * @member {String} className='Neo.container.Accordion'
13
+ * @protected
14
+ */
15
+ className: 'Neo.container.Accordion',
16
+ /**
17
+ * @member {String} ntype='accordion'
18
+ * @protected
19
+ */
20
+ ntype: 'accordion',
21
+ /**
22
+ * @member {String[]} baseCls=['neo-accordion']
23
+ */
24
+ baseCls: ['neo-accordion'],
25
+ /**
26
+ * Add zero based numbers, which accordion items you want initially expanded
27
+ * @member {Number[]} initialOpen=[]
28
+ */
29
+ initialOpen_: [],
30
+ /**
31
+ * @member {Object} itemDefaults={ntype:'accordionitem'}
32
+ */
33
+ itemDefaults: {ntype: 'accordionitem'},
34
+ /**
35
+ * @member {Object[]} items=[]
36
+ */
37
+ items: [],
38
+ /**
39
+ * Max number of accordion items, which can be expanded at the same time
40
+ * @member {Number} maxExpandedItems=1
41
+ */
42
+ maxExpandedItems: 1,
43
+ /**
44
+ * Keep track of currently open items
45
+ * @member {String[]} expandedItems=[]
46
+ * @private
47
+ */
48
+ expandedItems_: [],
49
+ /**
50
+ * Creates a top header
51
+ * @memeber {String|null} title=null
52
+ */
53
+ title_: null
54
+ }
55
+
56
+ /**
57
+ * @param {Number[]} value
58
+ * @param {Number[]} oldValue
59
+ */
60
+ afterSetInitialOpen(value, oldValue) {
61
+ const me = this,
62
+ items = me.items,
63
+ expandedItems = me.expandedItems;
64
+
65
+ value.forEach((itemNo) => {
66
+ const id = Neo.getId(me.itemDefaults.ntype),
67
+ item = items[itemNo];
68
+
69
+ item.expanded = true;
70
+ item.id = id;
71
+ NeoArray.add(expandedItems, id);
72
+ });
73
+
74
+ me.expandedItems = expandedItems;
75
+ }
76
+
77
+ /**
78
+ * After changes to title config, we add a header
79
+ * @param {String|null} value
80
+ * @param {String|null} oldValue
81
+ */
82
+ afterSetTitle(value, oldValue) {
83
+ const me = this,
84
+ titleEl = me.down({flag: 'titleEl'});
85
+
86
+ if (value && !titleEl) {
87
+ me.headers = [{
88
+ baseCls: 'neo-accordion-title',
89
+ cls : ['neo-accordion-title'],
90
+ dock : 'top',
91
+ text : value
92
+ }];
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Called by accordion items.
98
+ * Checks for maxExpandedItems
99
+ * @param {Object} data
100
+ * @param {Boolean} data.expanded newState
101
+ * @param {Neo.container.AccordionItem} data.target accordion item
102
+ * @protected
103
+ */
104
+ childExpandChange(data) {
105
+ const me = this,
106
+ maxExpandedItems = me.maxExpandedItems,
107
+ expandedItems = me.expandedItems,
108
+ curNoOpenItems = expandedItems.length,
109
+ target = data.target,
110
+ targetId = target.id,
111
+ expanded = data.expanded;
112
+
113
+ if (expanded
114
+ && maxExpandedItems !== 0
115
+ && curNoOpenItems === maxExpandedItems
116
+ ) {
117
+ Neo.get(expandedItems[0]).expanded = false;
118
+ NeoArray.remove(expandedItems, expandedItems[0]);
119
+ }
120
+
121
+ target.expanded = expanded;
122
+ NeoArray.toggle(expandedItems, targetId);
123
+
124
+ me.expandedItems = expandedItems;
125
+ }
126
+ }
127
+
128
+ Neo.applyClassConfig(Accordion);
129
+
130
+ export default Accordion;
@@ -0,0 +1,178 @@
1
+ import Base from './Base.mjs';
2
+ import Toolbar from '../toolbar/Base.mjs';
3
+ import NeoArray from '../util/Array.mjs';
4
+
5
+ /**
6
+ * @class Neo.container.AccordionItem
7
+ * @extends Neo.container.Base
8
+ */
9
+ class AccordionContainer extends Base {
10
+ static config = {
11
+ /**
12
+ * @member {String} className='Neo.container.AccordionItem'
13
+ * @protected
14
+ */
15
+ className: 'Neo.container.AccordionItem',
16
+ /**
17
+ * @member {String} ntype='accordionitem'
18
+ * @protected
19
+ */
20
+ ntype: 'accordionitem',
21
+ /**
22
+ * @member {String[]} baseCls=['neo-accordion-component']
23
+ */
24
+ baseCls: ['neo-accordion-item'],
25
+
26
+ /**
27
+ * Additional cls for the arrow, in case you need to customize it
28
+ * @member {String[]|String|null} arrowCls=null
29
+ */
30
+ arrowCls: null,
31
+ /**
32
+ * Expand or collapse (true/false) this value
33
+ * @member {Boolean} expanded=false
34
+ */
35
+ expanded_: false,
36
+ /**
37
+ * cls for the icon in front of the title
38
+ * May be empty. 'fa' is preset,
39
+ * so if you use fontawesome you do not have to add it.
40
+ *
41
+ * @member {String[]|String|null} iconCls=null
42
+ */
43
+ iconCls_: null,
44
+ /**
45
+ * Title for Headerbar
46
+ * @member {String|null} title=null
47
+ */
48
+ title_: null
49
+ }
50
+
51
+ /**
52
+ * Runs after afterSetTitle, so that the title component does not exist prior.
53
+ * @protected
54
+ */
55
+ createItems() {
56
+ const me = this,
57
+ items = me.items,
58
+ title = me.title;
59
+ let iconCls = me.iconCls || ['no-icon'],
60
+ arrowCls = me.arrowCls || 'fa-caret-down',
61
+ header, content;
62
+
63
+ if (!Neo.isArray(iconCls)) {
64
+ iconCls = iconCls.split(' ');
65
+ }
66
+ if (!Neo.isArray(arrowCls)) {
67
+ arrowCls = arrowCls.split(' ');
68
+ }
69
+
70
+ header = Neo.create({
71
+ module: Toolbar,
72
+ items : [{
73
+ flag : 'iconEl',
74
+ ntype : 'component',
75
+ baseCls: ['neo-accordion-header-icon'],
76
+ cls : ['fa', ...iconCls],
77
+ }, {
78
+ flag : 'titleEl',
79
+ ntype : 'component',
80
+ baseCls: ['neo-accordion-header-title'],
81
+ html : title
82
+ }, {
83
+ ntype : 'component',
84
+ baseCls: ['neo-accordion-header-arrow'],
85
+ cls : ['fa', ...arrowCls]
86
+ }]
87
+ });
88
+ content = {
89
+ ntype : 'container',
90
+ flag : 'content',
91
+ baseCls: ['neo-accordion-content', 'neo-container'],
92
+ items : items
93
+ };
94
+
95
+ me.items = [header, content];
96
+
97
+ super.createItems();
98
+
99
+ me.addDomListeners([
100
+ {click: me.onExpandClick, delegate: 'neo-accordion-header-arrow'}
101
+ ]);
102
+ }
103
+
104
+ /**
105
+ * After changing expanded, we expand/collapse via CSS
106
+ * @param {Boolean} isExpanded
107
+ */
108
+ afterSetExpanded(isExpanded) {
109
+ const me = this,
110
+ cls = me.cls,
111
+ fn = isExpanded ? 'add' : 'remove';
112
+
113
+ NeoArray[fn](cls, 'neo-expanded');
114
+ me.cls = cls;
115
+
116
+ // Ensure scrollbars are not flipping in and out
117
+ Neo.timeout(450).then(() => {
118
+ NeoArray[fn](cls, 'neo-scrollable');
119
+ me.cls = cls;
120
+ })
121
+ }
122
+
123
+ /**
124
+ * The initial title is set before the component is created,
125
+ * so we have to check if it exists.
126
+ *
127
+ * @param {String[]|String} newValue
128
+ * @param {String[]} oldValue
129
+ */
130
+ afterSetIconCls(newValue, oldValue) {
131
+ let iconEl = this.down({flag: 'iconEl'});
132
+
133
+ if (iconEl) {
134
+ let cls = iconEl.cls;
135
+
136
+ if (!Neo.isArray(newValue)) newValue = newValue.split(' ');
137
+
138
+ NeoArray.remove(cls, oldValue);
139
+ NeoArray.add(cls, newValue);
140
+ iconEl.cls = cls;
141
+ }
142
+ }
143
+
144
+ /**
145
+ * The initial title is set before the component is created,
146
+ * so we have to check if it exists.
147
+ *
148
+ * @param {String} newValue
149
+ */
150
+ afterSetTitle(newValue) {
151
+ let titleEl = this.down({flag: 'titleEl'});
152
+
153
+ if (titleEl) titleEl.html = newValue;
154
+ }
155
+
156
+ /**
157
+ * If the parent is an AccordionContainer, we run the parent function.
158
+ * Otherwise we set this.expanded to the new value.
159
+ */
160
+ onExpandClick() {
161
+ const me = this,
162
+ currentState = me.expanded;
163
+ let parent = me.up('accordion');
164
+
165
+ if (parent.ntype === 'accordion') {
166
+ parent.childExpandChange({
167
+ expanded: !currentState,
168
+ target : me
169
+ });
170
+ } else {
171
+ me.expanded = !currentState;
172
+ }
173
+ }
174
+ }
175
+
176
+ Neo.applyClassConfig(AccordionContainer);
177
+
178
+ export default AccordionContainer;
@@ -198,46 +198,63 @@ class Container extends BaseContainer {
198
198
  }
199
199
 
200
200
  /**
201
- * Set field values by field name or field id
202
- * @param {Object} values={}
201
+ * Set field configs by field name or field id
202
+ * @param {Object} configs={}
203
203
  * @param {Boolean} suspendEvents=false
204
204
  */
205
- async setValues(values={}, suspendEvents=false) {
205
+ async setConfigs(configs={}, suspendEvents=false) {
206
206
  let me = this,
207
207
  fields = await me.getFields(),
208
- isCheckBox, isRadio, path, value;
208
+ fieldConfigs, isCheckBox, isRadio, path, value;
209
209
 
210
210
  fields.forEach(item => {
211
- if (suspendEvents) {
212
- item.suspendEvents = true;
213
- }
211
+ path = me.getFieldPath(item);
212
+ fieldConfigs = Neo.nsWithArrays(path, false, configs);
213
+
214
+ if (fieldConfigs) {
215
+ if (suspendEvents) {
216
+ item.suspendEvents = true;
217
+ }
218
+
219
+ isCheckBox = Neo.form.field?.CheckBox && item instanceof Neo.form.field.CheckBox;
220
+ value = fieldConfigs.value;
214
221
 
215
- isCheckBox = Neo.form.field?.CheckBox && item instanceof Neo.form.field.CheckBox;
216
- path = me.getFieldPath(item);
217
- value = Neo.nsWithArrays(path, false, values);
222
+ if (isCheckBox) {
223
+ if (Neo.typeOf(value) === 'Array') {
224
+ if (value.includes(item.value)) {
225
+ fieldConfigs.checked = true
226
+ }
227
+ } else {
228
+ fieldConfigs.checked = item.value === value
229
+ }
230
+ } else if (value !== undefined) {
231
+ isRadio = Neo.form.field?.Radio && item instanceof Neo.form.field.Radio;
218
232
 
219
- if (isCheckBox) {
220
- if (Neo.typeOf(value) === 'Array') {
221
- if (value.includes(item.value)) {
222
- item.checked = true
233
+ if (isRadio) {
234
+ fieldConfigs.checked = item.value === value
223
235
  }
224
- } else {
225
- item.checked = item.value === value
226
236
  }
227
- } else if (value !== undefined) {
228
- isRadio = Neo.form.field?.Radio && item instanceof Neo.form.field.Radio;
229
237
 
230
- if (isRadio) {
231
- item.checked = item.value === value
232
- } else {
233
- item.value = value
238
+ item.set(fieldConfigs)
239
+
240
+ if (suspendEvents) {
241
+ delete item.suspendEvents;
234
242
  }
235
243
  }
244
+ })
245
+ }
236
246
 
237
- if (suspendEvents) {
238
- delete item.suspendEvents;
239
- }
247
+ /**
248
+ * Set field values by field name or field id
249
+ * @param {Object} values={}
250
+ * @param {Boolean} suspendEvents=false
251
+ */
252
+ async setValues(values={}, suspendEvents=false) {
253
+ Object.entries(values).forEach(([key, value]) => {
254
+ values[key] = {value}
240
255
  })
256
+
257
+ await this.setConfigs(values, suspendEvents)
241
258
  }
242
259
 
243
260
  /**
@@ -81,7 +81,7 @@ class CheckBox extends Base {
81
81
  labelCls_: [],
82
82
  /**
83
83
  * Edge-case config in case we want to render leading content with their own selectors like:
84
- * <span class="my-label-id-cls">E10</span> Firstname
84
+ * <span class="my-label-id-cls">E10</span> · Firstname
85
85
  * @member {String|null} labelId_=null
86
86
  */
87
87
  labelId_: null,
@@ -90,6 +90,11 @@ class CheckBox extends Base {
90
90
  * @member {String[]} labelIdCls_=[]
91
91
  */
92
92
  labelIdCls_: [],
93
+ /**
94
+ * Separator between labelId & labelText
95
+ * @member {String} labelIdSeparator_=' · '
96
+ */
97
+ labelIdSeparator_: ' · ',
93
98
  /**
94
99
  * Valid values: 'left', 'top'
95
100
  * @member {String} labelPosition_='left'
@@ -310,7 +315,7 @@ class CheckBox extends Base {
310
315
  let me = this;
311
316
 
312
317
  if (me.labelId) {
313
- value = `<span class="${me.labelIdCls.join(',')}">${me.labelId}</span> ${value}`
318
+ value = `<span class="${me.labelIdCls.join(',')}">${me.labelId}</span>${me.labelIdSeparator + value}`
314
319
  }
315
320
 
316
321
  me.vdom.cn[0].cn[0].innerHTML = value;
@@ -148,7 +148,7 @@ class Number extends Text {
148
148
 
149
149
  me.changeInputElKey('step', value);
150
150
 
151
- stepSizeString = String(this.stepSize);
151
+ stepSizeString = String(value);
152
152
 
153
153
  me.stepSizeDigits = stepSizeString.includes('.') ? stepSizeString.split('.')[1].length : 0;
154
154
 
@@ -157,16 +157,44 @@ class Number extends Text {
157
157
 
158
158
  if (modulo !== 0) { // find the closest valid value
159
159
  if (modulo / value > 0.5) {
160
- if (val + value - modulo < me.maxValue) {me.value = val + value - modulo;}
161
- else if (val - modulo > me.minValue) {me.value = val - modulo;}
160
+ if (val + value - modulo < me.maxValue) {
161
+ me.value = val + value - modulo;
162
+ } else if (val - modulo > me.minValue) {
163
+ me.value = val - modulo;
164
+ }
162
165
  } else {
163
- if (val - modulo > me.minValue) {me.value = val - modulo;}
164
- else if (val + value - modulo < me.maxValue) {me.value = val + value - modulo;}
166
+ if (val - modulo > me.minValue) {
167
+ me.value = val - modulo;
168
+ } else if (val + value - modulo < me.maxValue) {
169
+ me.value = val + value - modulo;
170
+ }
165
171
  }
166
172
  }
167
173
  }
168
174
  }
169
175
 
176
+ /**
177
+ * Triggered after the triggerPosition config got changed
178
+ * @param {String} value
179
+ * @param {String} oldValue
180
+ * @protected
181
+ */
182
+ afterSetTriggerPosition(value, oldValue) {
183
+ oldValue && this.updateTriggers();
184
+ }
185
+
186
+ /**
187
+ * Triggered after the useSpinButtons config got changed
188
+ * @param {Boolean} value
189
+ * @param {Boolean} oldValue
190
+ * @protected
191
+ */
192
+ afterSetUseSpinButtons(value, oldValue) {
193
+ if (typeof oldValue === 'boolean') {
194
+ this.updateTriggers();
195
+ }
196
+ }
197
+
170
198
  /**
171
199
  * Triggered before the maxLength config gets changed
172
200
  * @param {Number|null} value
@@ -195,28 +223,6 @@ class Number extends Text {
195
223
  return null;
196
224
  }
197
225
 
198
- /**
199
- * Triggered after the triggerPosition config got changed
200
- * @param {String} value
201
- * @param {String} oldValue
202
- * @protected
203
- */
204
- afterSetTriggerPosition(value, oldValue) {
205
- oldValue && this.updateTriggers();
206
- }
207
-
208
- /**
209
- * Triggered after the useSpinButtons config got changed
210
- * @param {Boolean} value
211
- * @param {Boolean} oldValue
212
- * @protected
213
- */
214
- afterSetUseSpinButtons(value, oldValue) {
215
- if (typeof oldValue === 'boolean') {
216
- this.updateTriggers();
217
- }
218
- }
219
-
220
226
  /**
221
227
  * Triggered before the triggerPosition config gets changed
222
228
  * @param {String} value
@@ -229,18 +235,24 @@ class Number extends Text {
229
235
 
230
236
  /**
231
237
  * Triggered before the value config gets changed
232
- * @param {Number} value
238
+ * @param {Number|String} value
233
239
  * @param {Number} oldValue
234
240
  * @protected
235
241
  */
236
242
  beforeSetValue(value, oldValue) {
237
- if (Neo.isNumber(value) && this.stepSizeDigits > 0) {
238
- return +value.toFixed(this.stepSizeDigits)
239
- } else if (value === '') {
240
- return null
243
+ if (value === null || value === '') {
244
+ return null;
245
+ }
246
+
247
+ if (!Neo.isNumber(value)) {
248
+ value = +value;
249
+ }
250
+
251
+ if (this.stepSizeDigits > 0) {
252
+ value = +value.toFixed(this.stepSizeDigits);
241
253
  }
242
254
 
243
- return value
255
+ return value;
244
256
  }
245
257
 
246
258
  /**
@@ -366,7 +378,7 @@ class Number extends Text {
366
378
  }
367
379
 
368
380
  me.removeTrigger('spindown', true, triggers);
369
- me.removeTrigger('spinup', true, triggers);
381
+ me.removeTrigger('spinup', true, triggers);
370
382
  } else {
371
383
  if (!me.hasTrigger('spindown')) {
372
384
  triggers.push(SpinDownTrigger);
@@ -379,8 +391,8 @@ class Number extends Text {
379
391
  me.removeTrigger('spinupdown', true, triggers);
380
392
  }
381
393
  } else {
382
- me.removeTrigger('spindown', true, triggers);
383
- me.removeTrigger('spinup', true, triggers);
394
+ me.removeTrigger('spindown', true, triggers);
395
+ me.removeTrigger('spinup', true, triggers);
384
396
  me.removeTrigger('spinupdown', true, triggers);
385
397
  }
386
398
 
@@ -392,7 +404,7 @@ class Number extends Text {
392
404
  * @param {Boolean} silent=true
393
405
  * @returns {Boolean} Returns true in case there are no client-side errors
394
406
  */
395
- validate(silent=true) {
407
+ validate(silent = true) {
396
408
  let me = this,
397
409
  value = me.value,
398
410
  isNumber = Neo.isNumber(value),
@@ -30,6 +30,11 @@ class Range extends Number {
30
30
  * @member {String} inputType='range'
31
31
  */
32
32
  inputType: 'range',
33
+ /**
34
+ * If true, shows the result of the slider in the label
35
+ * @member {Boolean} showResultInLabel=false
36
+ */
37
+ showResultInLabel: false,
33
38
  /**
34
39
  * @member {Array} tickmarks_=[]
35
40
  */
@@ -37,7 +42,7 @@ class Range extends Number {
37
42
  /**
38
43
  * @member {Boolean} useInputEvent=false
39
44
  */
40
- useInputEvent : false,
45
+ useInputEvent: false,
41
46
  /**
42
47
  * Disables the field.Number buttons
43
48
  * @member {Boolean} useInputEvent=false
@@ -57,14 +62,16 @@ class Range extends Number {
57
62
  if (me.useInputEvent) {
58
63
  me.addDomListeners({
59
64
  input: {
60
- fn : me.onInputValueChange,
61
- id : me.vdom.cn[2].id,
62
- scope : me
65
+ fn : me.onInputValueChange,
66
+ id : me.vdom.cn[2].id,
67
+ scope: me
63
68
  }
64
69
  });
65
70
  }
66
71
 
67
72
  inputEl.cls = ['neo-rangefield-input']; // replace neo-textfield-input
73
+
74
+ me.addValueToLabel();
68
75
  }
69
76
 
70
77
  /**
@@ -76,6 +83,26 @@ class Range extends Number {
76
83
  afterSetTickmarks(value, oldValue) {
77
84
  // todo
78
85
  }
86
+
87
+ /**
88
+ * Override the NumberField implementation
89
+ * @param {Object} data
90
+ */
91
+ afterSetValue(value, oldValue) {
92
+ this.addValueToLabel();
93
+ super.afterSetValue(value, oldValue);
94
+ }
95
+
96
+ /**
97
+ * Update label with value
98
+ */
99
+ addValueToLabel() {
100
+ const me = this;
101
+
102
+ if (me.showResultInLabel) {
103
+ me.getLabelEl().innerHTML = `[${me.value}] ` + me.labelText;
104
+ }
105
+ }
79
106
  }
80
107
 
81
108
  Neo.applyClassConfig(Range);
@@ -141,6 +141,11 @@ class Text extends Base {
141
141
  * @member {String[]} labelIdCls_=[]
142
142
  */
143
143
  labelIdCls_: [],
144
+ /**
145
+ * Separator between labelId & labelText
146
+ * @member {String} labelIdSeparator_=' · '
147
+ */
148
+ labelIdSeparator_: ' · ',
144
149
  /**
145
150
  * @member {String} labelOptionalText_=' (Optional)'
146
151
  */
@@ -514,7 +519,7 @@ class Text extends Base {
514
519
  isEmpty = me.isEmpty();
515
520
 
516
521
  if (me.labelId) {
517
- value = `<span class="${me.labelIdCls.join(',')}">${me.labelId}</span> ${value}`
522
+ value = `<span class="${me.labelIdCls.join(',')}">${me.labelId}</span>${me.labelIdSeparator + value}`
518
523
  }
519
524
 
520
525
  me.getLabelEl().innerHTML = value;
@@ -204,7 +204,11 @@ class Toast extends Base {
204
204
  component.style = moveObj;
205
205
  component.update();
206
206
 
207
- acc = acc + rects[index].height;
207
+ // Sometimes the index is already reduced
208
+ // so the last index might not be available
209
+ if(rects[index]) {
210
+ acc = acc + rects[index].height;
211
+ }
208
212
  }
209
213
  }
210
214
  }