neo.mjs 6.8.3 → 6.9.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.
- package/apps/ServiceWorker.mjs +2 -2
- package/apps/learnneo/view/home/MainContainer.mjs +4 -3
- package/apps/learnneo/view/home/MainContainerController.mjs +24 -2
- package/apps/route/app.mjs +6 -0
- package/apps/route/index.html +11 -0
- package/apps/route/neo-config.json +6 -0
- package/apps/route/view/ButtonBar.mjs +57 -0
- package/apps/route/view/CenterContainer.mjs +37 -0
- package/apps/route/view/FooterContainer.mjs +47 -0
- package/apps/route/view/HeaderContainer.mjs +47 -0
- package/apps/route/view/MainView.mjs +66 -0
- package/apps/route/view/MainViewController.mjs +210 -0
- package/apps/route/view/MetaContainer.mjs +52 -0
- package/apps/route/view/Viewport.mjs +15 -0
- package/apps/route/view/center/CardAdministration.mjs +36 -0
- package/apps/route/view/center/CardAdministrationDenied.mjs +26 -0
- package/apps/route/view/center/CardContact.mjs +29 -0
- package/apps/route/view/center/CardHome.mjs +26 -0
- package/apps/route/view/center/CardSection1.mjs +26 -0
- package/apps/route/view/center/CardSection2.mjs +27 -0
- package/examples/ConfigurationViewport.mjs +1 -1
- package/examples/ServiceWorker.mjs +2 -2
- package/examples/form/field/select/MainContainer.mjs +1 -2
- package/examples/table/container/MainContainer.mjs +4 -2
- package/examples/table/container/MainModel.mjs +3 -0
- package/examples/table/container/MainStore.mjs +10 -10
- package/examples/toolbar/paging/view/MainContainer.mjs +38 -3
- package/package.json +4 -4
- package/resources/data/learnneo/content.json +9 -9
- package/resources/data/learnneo/pages/whyneo.md +76 -0
- package/resources/scss/src/apps/route/CenterContainer.scss +29 -0
- package/resources/scss/src/apps/route/HeaderContainer.scss +122 -0
- package/resources/scss/src/apps/route/MainView.scss +3 -0
- package/resources/scss/src/apps/route/MetaContainer.scss +44 -0
- package/resources/scss/src/apps/route/_all.scss +1 -0
- package/resources/scss/src/component/Toast.scss +2 -2
- package/src/DefaultConfig.mjs +2 -2
- package/src/Neo.mjs +15 -14
- package/src/button/Base.mjs +2 -2
- package/src/collection/Filter.mjs +13 -2
- package/src/component/Base.mjs +41 -50
- package/src/component/Toast.mjs +2 -1
- package/src/container/Base.mjs +59 -2
- package/src/controller/Base.mjs +84 -4
- package/src/controller/Component.mjs +22 -7
- package/src/core/Observable.mjs +50 -9
- package/src/form/field/Range.mjs +8 -0
- package/src/form/field/Select.mjs +9 -10
- package/src/form/field/Text.mjs +13 -3
- package/src/form/field/trigger/Picker.mjs +1 -8
- package/src/main/DomEvents.mjs +9 -3
- package/src/manager/DomEvent.mjs +37 -24
- package/src/menu/List.mjs +1 -1
- package/src/table/View.mjs +78 -53
- package/src/toolbar/Paging.mjs +68 -76
- package/src/tooltip/Base.mjs +123 -11
- package/src/vdom/Helper.mjs +7 -0
- package/src/worker/App.mjs +4 -0
- package/test/components/app.mjs +8 -0
- package/test/components/files/button/Base.mjs +17 -0
- package/test/components/files/component/DateSelector.mjs +18 -0
- package/test/components/files/form/field/Select.mjs +35 -0
- package/test/components/index.html +17 -0
- package/test/components/neo-config.json +6 -0
- package/test/components/siesta.js +14 -0
package/src/Neo.mjs
CHANGED
@@ -1,7 +1,19 @@
|
|
1
1
|
import DefaultConfig from './DefaultConfig.mjs';
|
2
2
|
|
3
3
|
const configSymbol = Symbol.for('configSymbol'),
|
4
|
-
getSetCache = Symbol('getSetCache')
|
4
|
+
getSetCache = Symbol('getSetCache'),
|
5
|
+
typeDetector = {
|
6
|
+
function: (item) => {
|
7
|
+
if (item.prototype?.constructor.isClass) {
|
8
|
+
return 'NeoClass'
|
9
|
+
}
|
10
|
+
},
|
11
|
+
object: (item) => {
|
12
|
+
if (item.constructor.isClass && item instanceof Neo.core.Base) {
|
13
|
+
return 'NeoInstance'
|
14
|
+
}
|
15
|
+
}
|
16
|
+
};
|
5
17
|
|
6
18
|
/**
|
7
19
|
* The base module to enhance classes, create instances and the Neo namespace
|
@@ -375,7 +387,7 @@ Neo = globalThis.Neo = Object.assign({
|
|
375
387
|
const value = source[key];
|
376
388
|
|
377
389
|
if (Neo.typeOf(value) === 'Object') {
|
378
|
-
target[key] = Neo.merge(target[key], value);
|
390
|
+
target[key] = Neo.merge(target[key] || {}, value);
|
379
391
|
} else {
|
380
392
|
target[key] = value;
|
381
393
|
}
|
@@ -492,18 +504,7 @@ Neo = globalThis.Neo = Object.assign({
|
|
492
504
|
return null
|
493
505
|
}
|
494
506
|
|
495
|
-
return
|
496
|
-
function: () => {
|
497
|
-
if (item.prototype?.constructor.isClass) {
|
498
|
-
return 'NeoClass'
|
499
|
-
}
|
500
|
-
},
|
501
|
-
object: () => {
|
502
|
-
if (item.constructor.isClass && item instanceof Neo.core.Base) {
|
503
|
-
return 'NeoInstance'
|
504
|
-
}
|
505
|
-
}
|
506
|
-
}[typeof item]?.() || item.constructor.name
|
507
|
+
return typeDetector[typeof item]?.(item) || item.constructor.name
|
507
508
|
}
|
508
509
|
}, Neo);
|
509
510
|
|
package/src/button/Base.mjs
CHANGED
@@ -51,7 +51,7 @@ class Base extends Component {
|
|
51
51
|
editRoute: true,
|
52
52
|
/**
|
53
53
|
* Shortcut for domListeners={click:handler}
|
54
|
-
* A string based value assumes that the handlerFn lives inside a
|
54
|
+
* A string based value assumes that the handlerFn lives inside a controller.Component
|
55
55
|
* @member {Function|String|null} handler_=null
|
56
56
|
*/
|
57
57
|
handler_: null,
|
@@ -487,7 +487,7 @@ class Base extends Component {
|
|
487
487
|
onClick(data) {
|
488
488
|
let me = this;
|
489
489
|
|
490
|
-
me.
|
490
|
+
me.callback(me.handler, me.handlerScope || me, [data]);
|
491
491
|
|
492
492
|
me.menu && me.toggleMenu();
|
493
493
|
me.route && me.changeRoute();
|
@@ -14,12 +14,15 @@ class Filter extends Base {
|
|
14
14
|
static observable = true
|
15
15
|
/**
|
16
16
|
* Valid values for the operator config:<br>
|
17
|
-
* ['==',
|
17
|
+
* ['==','===','!=','!==','<','<=','>','>=','endsWith','excluded','included','isDefined','isUndefined','like','startsWith']
|
18
18
|
* @member {String[]} operators
|
19
19
|
* @protected
|
20
20
|
* @static
|
21
21
|
*/
|
22
|
-
static operators = [
|
22
|
+
static operators = [
|
23
|
+
'==', '===', '!=', '!==', '<', '<=', '>', '>=', 'endsWith', 'excluded', 'included',
|
24
|
+
'isDefined', 'isUndefined', 'like', 'startsWith'
|
25
|
+
]
|
23
26
|
|
24
27
|
static config = {
|
25
28
|
/**
|
@@ -220,6 +223,10 @@ class Filter extends Base {
|
|
220
223
|
static ['>'] (a, b) {return a > b;}
|
221
224
|
static ['>='] (a, b) {return a >= b;}
|
222
225
|
|
226
|
+
static ['endsWith'](a, b) {
|
227
|
+
return a?.toLowerCase().endsWith(b?.toLowerCase()) || false;
|
228
|
+
}
|
229
|
+
|
223
230
|
static ['excluded'](a, b) {
|
224
231
|
return b.indexOf(a) < 0;
|
225
232
|
}
|
@@ -239,6 +246,10 @@ class Filter extends Base {
|
|
239
246
|
static ['like'](a, b) {
|
240
247
|
return a?.toLowerCase().includes(b?.toLowerCase()) || false;
|
241
248
|
}
|
249
|
+
|
250
|
+
static ['startsWith'](a, b) {
|
251
|
+
return a?.toLowerCase().startsWith(b?.toLowerCase()) || false;
|
252
|
+
}
|
242
253
|
}
|
243
254
|
|
244
255
|
Neo.applyClassConfig(Filter);
|
package/src/component/Base.mjs
CHANGED
@@ -302,11 +302,17 @@ class Base extends CoreBase {
|
|
302
302
|
*/
|
303
303
|
theme_: null,
|
304
304
|
/**
|
305
|
-
* Add tooltip config
|
305
|
+
* Add tooltip config object or a string containing the display text
|
306
306
|
* See tooltip/Base.mjs
|
307
|
-
*
|
307
|
+
*
|
308
|
+
* By default, a single, shared Tooltip instance is used for all widgets which request
|
309
|
+
* a tooltip. It reconfigures itself from the widget's definition just before showing.
|
310
|
+
*
|
311
|
+
* If a widget needs its own instance for any reason, inslude the property `ownInstance : true`
|
312
|
+
* in the tooltip config object.
|
313
|
+
* @member {Object|String} tooltip_=null
|
308
314
|
*/
|
309
|
-
|
315
|
+
tooltip_: null,
|
310
316
|
/**
|
311
317
|
* Add 'primary' and other attributes to make it an outstanding design
|
312
318
|
* @member {String|null} ui_=null
|
@@ -771,21 +777,21 @@ class Base extends CoreBase {
|
|
771
777
|
}
|
772
778
|
|
773
779
|
/**
|
774
|
-
* Triggered after the
|
775
|
-
* @param {
|
776
|
-
* @param {
|
780
|
+
* Triggered after the tooltip config got changed
|
781
|
+
* @param {Object|String} value
|
782
|
+
* @param {Object|String} oldValue
|
777
783
|
* @protected
|
778
784
|
*/
|
779
|
-
|
780
|
-
|
781
|
-
let me = this;
|
785
|
+
afterSetTooltip(value, oldValue) {
|
786
|
+
oldValue?.destroy();
|
782
787
|
|
788
|
+
if (value) {
|
783
789
|
if (Neo.ns('Neo.tooltip.Base')) {
|
784
|
-
|
790
|
+
this.createTooltip(value);
|
785
791
|
} else {
|
786
|
-
import('../tooltip/Base.mjs').then((
|
787
|
-
|
788
|
-
})
|
792
|
+
import('../tooltip/Base.mjs').then(() => {
|
793
|
+
this.createTooltip(value);
|
794
|
+
});
|
789
795
|
}
|
790
796
|
}
|
791
797
|
}
|
@@ -1129,24 +1135,6 @@ class Base extends CoreBase {
|
|
1129
1135
|
return (Neo.isNumber(oldValue) && oldValue > 0) ? (oldValue - 1) : 0
|
1130
1136
|
}
|
1131
1137
|
|
1132
|
-
/**
|
1133
|
-
* Triggered before the style config gets changed.
|
1134
|
-
* @param {Object} value
|
1135
|
-
* @param {Object} oldValue
|
1136
|
-
* @returns {Object}
|
1137
|
-
* @protected
|
1138
|
-
*/
|
1139
|
-
beforeSetStyle(value, oldValue) {
|
1140
|
-
let me = this;
|
1141
|
-
|
1142
|
-
if (typeof value === 'object') {
|
1143
|
-
// merge the incoming style specification into the configured default
|
1144
|
-
value = Neo.merge(Neo.merge({}, me.constructor.config.style), value)
|
1145
|
-
}
|
1146
|
-
|
1147
|
-
return value
|
1148
|
-
}
|
1149
|
-
|
1150
1138
|
/**
|
1151
1139
|
* Changes the value of a vdom object attribute or removes it in case it has no value
|
1152
1140
|
* @param {String} key
|
@@ -1167,31 +1155,31 @@ class Base extends CoreBase {
|
|
1167
1155
|
|
1168
1156
|
/**
|
1169
1157
|
* Creates the tooltip instances
|
1170
|
-
* @param {
|
1158
|
+
* @param {Object|String} value
|
1171
1159
|
* @protected
|
1172
1160
|
*/
|
1173
|
-
|
1174
|
-
if (
|
1175
|
-
value =
|
1161
|
+
createTooltip(value) {
|
1162
|
+
if (typeof value === 'string') {
|
1163
|
+
value = {
|
1164
|
+
text : value
|
1165
|
+
};
|
1176
1166
|
}
|
1177
1167
|
|
1178
|
-
let me
|
1179
|
-
tooltips = [],
|
1180
|
-
tip;
|
1181
|
-
|
1182
|
-
value.forEach(item => {
|
1183
|
-
// todo: check for existing tooltips
|
1168
|
+
let me = this;
|
1184
1169
|
|
1185
|
-
|
1170
|
+
if (value.ownInstance) {
|
1171
|
+
me._tooltip = Neo.create('Neo.tooltip.Base', {
|
1172
|
+
...value,
|
1186
1173
|
appName : me.appName,
|
1187
|
-
componentId: me.id
|
1188
|
-
...item
|
1174
|
+
componentId: me.id
|
1189
1175
|
});
|
1190
|
-
|
1191
|
-
|
1192
|
-
|
1193
|
-
|
1194
|
-
|
1176
|
+
}
|
1177
|
+
else {
|
1178
|
+
me._tooltip = value;
|
1179
|
+
Neo.tooltip.Base.createSingleton(me.app);
|
1180
|
+
me.addCls('neo-uses-shared-tooltip');
|
1181
|
+
me.update();
|
1182
|
+
}
|
1195
1183
|
}
|
1196
1184
|
|
1197
1185
|
/**
|
@@ -1665,7 +1653,10 @@ class Base extends CoreBase {
|
|
1665
1653
|
// does not clone existing Neo instances
|
1666
1654
|
me._vdom = Neo.clone(vdom, true, true);
|
1667
1655
|
|
1668
|
-
|
1656
|
+
if (config.style) {
|
1657
|
+
// If we are passed an object, merge it with the class's own style
|
1658
|
+
me.style = Neo.typeOf(config.style) === 'Object' ? { ...config.style, ...me.constructor.config.style } : config.style;
|
1659
|
+
}
|
1669
1660
|
|
1670
1661
|
me.wrapperStyle = Neo.clone(config.wrapperStyle, false);
|
1671
1662
|
|
package/src/component/Toast.mjs
CHANGED
package/src/container/Base.mjs
CHANGED
@@ -8,6 +8,8 @@ import LayoutVBox from '../layout/VBox.mjs';
|
|
8
8
|
import Logger from '../util/Logger.mjs';
|
9
9
|
import NeoArray from '../util/Array.mjs';
|
10
10
|
|
11
|
+
const byWeight = ({ weight : lhs = 0 }, { weight : rhs = 0 }) => lhs - rhs;
|
12
|
+
|
11
13
|
/**
|
12
14
|
* @class Neo.container.Base
|
13
15
|
* @extends Neo.component.Base
|
@@ -33,10 +35,33 @@ class Base extends Component {
|
|
33
35
|
*/
|
34
36
|
itemDefaults_: null,
|
35
37
|
/**
|
36
|
-
* An array of config objects|instances|modules for each child component
|
38
|
+
* An array or an object of config objects|instances|modules for each child component
|
37
39
|
* @member {Object[]} items_=[]
|
38
40
|
* @example
|
39
41
|
* import Button from '../button/Base.mjs';
|
42
|
+
* import Toolbar from '../toolbar/Base.mjs';
|
43
|
+
*
|
44
|
+
* let myButton = Neo.create(Button, {
|
45
|
+
* text: 'Button1'
|
46
|
+
* });
|
47
|
+
*
|
48
|
+
* Neo.create(Toolbar, {
|
49
|
+
* //...
|
50
|
+
* items: {
|
51
|
+
* buttonRef : {
|
52
|
+
* ntype: 'button', // by ntype
|
53
|
+
* text : 'Button 2'
|
54
|
+
* },
|
55
|
+
* secondRef : {
|
56
|
+
* module: Button, // by imported module
|
57
|
+
* text : 'Button 3'
|
58
|
+
* }
|
59
|
+
* }
|
60
|
+
* });
|
61
|
+
*
|
62
|
+
* or
|
63
|
+
* @example
|
64
|
+
* import Button from '../button/Base.mjs';
|
40
65
|
* import MyRedButton from 'myapp/MyRedButton.mjs';
|
41
66
|
* import Toolbar from '../toolbar/Base.mjs';
|
42
67
|
*
|
@@ -192,6 +217,36 @@ class Base extends Component {
|
|
192
217
|
}
|
193
218
|
}
|
194
219
|
|
220
|
+
/**
|
221
|
+
* Convert items object to an array for onward storage as _items
|
222
|
+
* @param {Object|Object[]} value
|
223
|
+
* @param {Object|Object[]} oldValue
|
224
|
+
* @returns {Object[]}
|
225
|
+
* @protected
|
226
|
+
*/
|
227
|
+
beforeSetItems(value, oldValue) {
|
228
|
+
if (Neo.typeOf(value) === 'Object') {
|
229
|
+
const result = [];
|
230
|
+
|
231
|
+
let hasWeight;
|
232
|
+
|
233
|
+
for (const ref in value) {
|
234
|
+
const item = value[ref]
|
235
|
+
|
236
|
+
item.reference = ref;
|
237
|
+
result.push(item);
|
238
|
+
hasWeight ||= ('weight' in item);
|
239
|
+
}
|
240
|
+
|
241
|
+
if (hasWeight) {
|
242
|
+
result.sort(byWeight);
|
243
|
+
}
|
244
|
+
value = result;
|
245
|
+
}
|
246
|
+
|
247
|
+
return value;
|
248
|
+
}
|
249
|
+
|
195
250
|
/**
|
196
251
|
* @param {Object|String} value
|
197
252
|
* @param {Object|String|Neo.layout.Base} oldValue
|
@@ -446,7 +501,9 @@ class Base extends Component {
|
|
446
501
|
}
|
447
502
|
|
448
503
|
if (config.items) {
|
449
|
-
|
504
|
+
// If we are passed an object, merge the class's own items object into it
|
505
|
+
me.items = Neo.typeOf(config.items) === 'Object' ?
|
506
|
+
Neo.merge(Neo.clone(me.constructor.config.items), config.items) : config.items;
|
450
507
|
delete config.items
|
451
508
|
}
|
452
509
|
|
package/src/controller/Base.mjs
CHANGED
@@ -16,7 +16,23 @@ class Base extends CoreBase {
|
|
16
16
|
* @member {String} ntype='controller'
|
17
17
|
* @protected
|
18
18
|
*/
|
19
|
-
ntype: 'controller'
|
19
|
+
ntype: 'controller',
|
20
|
+
|
21
|
+
/**
|
22
|
+
* @member {Object} routes={}
|
23
|
+
*/
|
24
|
+
routes: {},
|
25
|
+
|
26
|
+
/**
|
27
|
+
* @member {Object} handleRoutes={}
|
28
|
+
*/
|
29
|
+
handleRoutes: {},
|
30
|
+
|
31
|
+
/**
|
32
|
+
* @member {String} defaultRoute=undefined
|
33
|
+
*/
|
34
|
+
defaultRoute: null
|
35
|
+
|
20
36
|
}
|
21
37
|
|
22
38
|
/**
|
@@ -25,7 +41,21 @@ class Base extends CoreBase {
|
|
25
41
|
construct(config) {
|
26
42
|
super.construct(config);
|
27
43
|
|
28
|
-
|
44
|
+
const me = this;
|
45
|
+
|
46
|
+
me.handleRoutes = {};
|
47
|
+
if (Object.keys(me.routes).length > 0) {
|
48
|
+
Object.keys(me.routes).forEach(key => {
|
49
|
+
if (key.toLowerCase() === 'default'){
|
50
|
+
me.defaultRoute = me.routes[key];
|
51
|
+
} else {
|
52
|
+
me.handleRoutes[key] = new RegExp(key.replace(/{[^\s/]+}/g, '([\\w-]+)')+'$');
|
53
|
+
}
|
54
|
+
|
55
|
+
});
|
56
|
+
}
|
57
|
+
|
58
|
+
HashHistory.on('change', me.onHashChange, me);
|
29
59
|
}
|
30
60
|
|
31
61
|
/**
|
@@ -37,6 +67,15 @@ class Base extends CoreBase {
|
|
37
67
|
super.destroy(...args);
|
38
68
|
}
|
39
69
|
|
70
|
+
/**
|
71
|
+
* Placeholder method which gets triggered when an invalid route is called
|
72
|
+
* @param {Object} value
|
73
|
+
* @param {Object} oldValue
|
74
|
+
*/
|
75
|
+
onNoRouteFound(value, oldValue) {
|
76
|
+
|
77
|
+
}
|
78
|
+
|
40
79
|
/**
|
41
80
|
* Placeholder method which gets triggered when the hash inside the browser url changes
|
42
81
|
* @param {Object} value
|
@@ -44,6 +83,46 @@ class Base extends CoreBase {
|
|
44
83
|
*/
|
45
84
|
onHashChange(value, oldValue) {
|
46
85
|
|
86
|
+
const me = this;
|
87
|
+
let hasRouteBeenFound = false;
|
88
|
+
Object.keys(me.handleRoutes).every( key => {
|
89
|
+
let preHandler = undefined;
|
90
|
+
let executeHandler = undefined;
|
91
|
+
let responsePreHandler = undefined;
|
92
|
+
|
93
|
+
const result = value.hashString.match(me.handleRoutes[key]);
|
94
|
+
if (result){
|
95
|
+
const target = me.routes[key];
|
96
|
+
if (Neo.isString(target)){
|
97
|
+
executeHandler = this.routes[key];
|
98
|
+
responsePreHandler = true;
|
99
|
+
}
|
100
|
+
if (Neo.isObject(target)){
|
101
|
+
executeHandler = this.routes[key].handler;
|
102
|
+
preHandler = this.routes[key].preHandler;
|
103
|
+
responsePreHandler = preHandler ? me[preHandler]?.call(this, value, oldValue, result.splice(1,result.length - 1)) : true;
|
104
|
+
}
|
105
|
+
|
106
|
+
hasRouteBeenFound = true;
|
107
|
+
|
108
|
+
if (responsePreHandler) {
|
109
|
+
this[executeHandler]?.call(this, value, oldValue, result.splice(1,result.length - 1));
|
110
|
+
} else {
|
111
|
+
console.warn('No preHandler defined for routes -> todo it better');
|
112
|
+
}
|
113
|
+
return false;
|
114
|
+
|
115
|
+
}
|
116
|
+
return true;
|
117
|
+
});
|
118
|
+
|
119
|
+
if (Object.keys(me.handleRoutes).length > 0 && !hasRouteBeenFound) {
|
120
|
+
if (me.defaultRoute) {
|
121
|
+
this[me.defaultRoute]?.call(this, value, oldValue);
|
122
|
+
} else {
|
123
|
+
this.onNoRouteFound(value, oldValue);
|
124
|
+
}
|
125
|
+
}
|
47
126
|
}
|
48
127
|
|
49
128
|
/**
|
@@ -53,9 +132,10 @@ class Base extends CoreBase {
|
|
53
132
|
let currentHash = HashHistory.first();
|
54
133
|
|
55
134
|
currentHash && this.onHashChange(currentHash, null);
|
56
|
-
|
57
|
-
super.onConstructed();
|
58
135
|
}
|
136
|
+
|
137
|
+
|
138
|
+
|
59
139
|
}
|
60
140
|
|
61
141
|
Neo.applyClassConfig(Base);
|
@@ -69,10 +69,22 @@ class Component extends Base {
|
|
69
69
|
* @param {String} handlerName
|
70
70
|
* @returns {Neo.controller.Component|null}
|
71
71
|
*/
|
72
|
-
getHandlerScope(handlerName) {
|
72
|
+
getHandlerScope(handlerName, component) {
|
73
73
|
let me = this,
|
74
74
|
parent = me.parent;
|
75
75
|
|
76
|
+
if (component) {
|
77
|
+
// Look for ths function *name* first in the Component itself.
|
78
|
+
// If we find it, return true so calling code knows not to continue to search.
|
79
|
+
const handlerCb = component.resolveCallback(handlerName, component);
|
80
|
+
|
81
|
+
// Handler fn is resolved in the Component or its own parent chain.
|
82
|
+
// Return a status indicating that we do not need an erly binding
|
83
|
+
if (handlerCb.fn) {
|
84
|
+
return true;
|
85
|
+
}
|
86
|
+
}
|
87
|
+
|
76
88
|
return Neo.isFunction(me[handlerName]) ?
|
77
89
|
me : parent ?
|
78
90
|
parent.getHandlerScope(handlerName) : null;
|
@@ -140,9 +152,12 @@ class Component extends Base {
|
|
140
152
|
eventHandler, handlerScope;
|
141
153
|
|
142
154
|
if (handler && typeof handler === 'string') {
|
143
|
-
handlerScope = me.getHandlerScope(handler);
|
155
|
+
handlerScope = me.getHandlerScope(handler, component);
|
144
156
|
|
145
|
-
|
157
|
+
// If the handler name was not resolved in the Component itself, bind it
|
158
|
+
if (handlerScope !== true) {
|
159
|
+
component.handler = handlerScope[handler].bind(component.handlerScope || handlerScope);
|
160
|
+
}
|
146
161
|
}
|
147
162
|
|
148
163
|
if (listeners) {
|
@@ -150,11 +165,11 @@ class Component extends Base {
|
|
150
165
|
if (key !== 'scope' && key !== 'delegate') {
|
151
166
|
if (Neo.isString(value)) {
|
152
167
|
eventHandler = value;
|
153
|
-
handlerScope = me.getHandlerScope(eventHandler);
|
168
|
+
handlerScope = me.getHandlerScope(eventHandler, component);
|
154
169
|
|
155
170
|
if (!handlerScope) {
|
156
171
|
Logger.logError('Unknown event handler for', eventHandler, component)
|
157
|
-
} else {
|
172
|
+
} else if (handlerScope !== true) {
|
158
173
|
listeners[key] = {};
|
159
174
|
listeners[key].fn = handlerScope[eventHandler].bind(handlerScope)
|
160
175
|
}
|
@@ -162,11 +177,11 @@ class Component extends Base {
|
|
162
177
|
value.forEach(listener => {
|
163
178
|
if (Neo.isObject(listener) && listener.hasOwnProperty('fn') && Neo.isString(listener.fn)) {
|
164
179
|
eventHandler = listener.fn;
|
165
|
-
handlerScope = me.getHandlerScope(eventHandler);
|
180
|
+
handlerScope = me.getHandlerScope(eventHandler, component);
|
166
181
|
|
167
182
|
if (!handlerScope) {
|
168
183
|
Logger.logError('Unknown event handler for', eventHandler, component)
|
169
|
-
} else {
|
184
|
+
} else if (handlerScope !== true) {
|
170
185
|
listener.fn = handlerScope[eventHandler].bind(handlerScope)
|
171
186
|
}
|
172
187
|
}
|
package/src/core/Observable.mjs
CHANGED
@@ -98,31 +98,50 @@ class Observable extends Base {
|
|
98
98
|
let me = this,
|
99
99
|
args = [].slice.call(arguments, 1),
|
100
100
|
listeners = me.listeners,
|
101
|
-
|
101
|
+
handler, handlers, i, len;
|
102
102
|
|
103
103
|
if (listeners && listeners[name]) {
|
104
|
-
|
105
|
-
len =
|
104
|
+
handlers = [...listeners[name]];
|
105
|
+
len = handlers.length;
|
106
106
|
|
107
107
|
for (i = 0; i < len; i++) {
|
108
|
-
|
108
|
+
handler = handlers[i];
|
109
109
|
|
110
|
-
|
111
|
-
|
112
|
-
|
110
|
+
// Resolve function name on the scope (oe me), or, if it starts with 'up.'
|
111
|
+
// look in the ownership hierarchy from me.
|
112
|
+
const cb = me.resolveCallback(handler.fn, handler.scope || me);
|
113
113
|
|
114
114
|
// remove the listener, in case the scope no longer exists
|
115
|
-
if (
|
115
|
+
if (cb.scope && !cb.scope.id) {
|
116
116
|
listeners[name].splice(i, 1);
|
117
117
|
} else {
|
118
118
|
if (!me.suspendEvents) {
|
119
|
-
|
119
|
+
// Object event format. Inject firer reference in as 'source'
|
120
|
+
if (args.length === 1 && typeof(args[0]) === 'object') {
|
121
|
+
args[0].source = me.id;
|
122
|
+
}
|
123
|
+
cb.fn.apply(cb.scope, handler.data ? args.concat(handler.data) : args);
|
120
124
|
}
|
121
125
|
}
|
122
126
|
}
|
123
127
|
}
|
124
128
|
}
|
125
129
|
|
130
|
+
/**
|
131
|
+
* Call the passed function, or a function by *name* which exists in the passed scope's
|
132
|
+
* or this component's ownership chain.
|
133
|
+
* @param {Function|String} fn A function, or the name of a function to find in the passed scope object/
|
134
|
+
* @param {Object} scope The scope to find the function in if it is specified as a string.
|
135
|
+
* @param {Array} args Arguments to pass to the callback.
|
136
|
+
*/
|
137
|
+
callback(fn, scope=this, args) {
|
138
|
+
if (fn) {
|
139
|
+
const handler = this.resolveCallback(fn, scope);
|
140
|
+
|
141
|
+
handler.fn.apply(handler.scope, args);
|
142
|
+
}
|
143
|
+
}
|
144
|
+
|
126
145
|
/**
|
127
146
|
* @param {Object} config
|
128
147
|
*/
|
@@ -240,6 +259,28 @@ class Observable extends Base {
|
|
240
259
|
|
241
260
|
// }
|
242
261
|
|
262
|
+
/**
|
263
|
+
* Locate a callable function by name in the passed scope.
|
264
|
+
*
|
265
|
+
* If the name starts with 'up.', the parent Component chain is searched.
|
266
|
+
*
|
267
|
+
* This is used by Observable.fire and by 'handler' function calls to resolve
|
268
|
+
* string function names in the Component's own hierarchy.
|
269
|
+
* @param {Function|String} fn A function, or the name of a function to find in the passed scope object/
|
270
|
+
* @param {Object} scope The scope to find the function in if it is specified as a string.
|
271
|
+
* @returns {Object}
|
272
|
+
*/
|
273
|
+
resolveCallback(fn, scope=this) {
|
274
|
+
if (typeof fn === 'string') {
|
275
|
+
if (!scope[fn] && fn.startsWith('up.')) {
|
276
|
+
fn = fn.slice(3);
|
277
|
+
while (!scope[fn] && (scope = scope.parent));
|
278
|
+
}
|
279
|
+
fn = scope[fn];
|
280
|
+
}
|
281
|
+
return { scope, fn };
|
282
|
+
}
|
283
|
+
|
243
284
|
/**
|
244
285
|
* Alias for removeListener
|
245
286
|
* @param {Object|String} name
|
package/src/form/field/Range.mjs
CHANGED
@@ -5,6 +5,14 @@ import Number from './Number.mjs';
|
|
5
5
|
* @extends Neo.form.field.Number
|
6
6
|
*/
|
7
7
|
class Range extends Number {
|
8
|
+
/**
|
9
|
+
* Removing the debounce for range fields
|
10
|
+
* @member {Object} delayable
|
11
|
+
* @protected
|
12
|
+
* @static
|
13
|
+
*/
|
14
|
+
static delayable = {}
|
15
|
+
|
8
16
|
static config = {
|
9
17
|
/**
|
10
18
|
* @member {String} className='Neo.form.field.Range'
|
@@ -38,6 +38,10 @@ class Select extends Picker {
|
|
38
38
|
* @member {String} displayField='name'
|
39
39
|
*/
|
40
40
|
displayField: 'name',
|
41
|
+
/**
|
42
|
+
* @member {String} filterOperator='like'
|
43
|
+
*/
|
44
|
+
filterOperator: 'like',
|
41
45
|
/**
|
42
46
|
* True will only fire a change event, in case the TextField input value matches a record.
|
43
47
|
* onFocusLeave() will try to select a hint record, if needed and possible.
|
@@ -103,6 +107,11 @@ class Select extends Picker {
|
|
103
107
|
* @member {Boolean} typeAhead_=true
|
104
108
|
*/
|
105
109
|
typeAhead_: true,
|
110
|
+
/**
|
111
|
+
* Set this config to false, in case typing into the input field should not filter list items
|
112
|
+
* @member {Boolean} useFilter=true
|
113
|
+
*/
|
114
|
+
useFilter: true,
|
106
115
|
/**
|
107
116
|
* This config should point to the store keyProperty or a different model field,
|
108
117
|
* which you want to submit instead
|
@@ -111,16 +120,6 @@ class Select extends Picker {
|
|
111
120
|
valueField: 'id'
|
112
121
|
}
|
113
122
|
|
114
|
-
/**
|
115
|
-
* @member {String} filterOperator='like'
|
116
|
-
*/
|
117
|
-
filterOperator = 'like'
|
118
|
-
/**
|
119
|
-
* Set this config to false, in case typing into the input field should not filter list items
|
120
|
-
* @member {Boolean} useFilter=true
|
121
|
-
*/
|
122
|
-
useFilter = true
|
123
|
-
|
124
123
|
/**
|
125
124
|
* @param {Object} config
|
126
125
|
*/
|