neo.mjs 6.9.0 → 6.9.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.
- package/apps/ServiceWorker.mjs +2 -2
- package/apps/route/view/MainViewController.mjs +2 -3
- package/examples/ServiceWorker.mjs +2 -2
- package/examples/toolbar/paging/view/MainContainer.mjs +9 -1
- package/examples/treeAccordion/MainContainer.mjs +23 -4
- package/package.json +4 -4
- package/resources/scss/src/component/Toast.scss +2 -2
- package/src/DefaultConfig.mjs +2 -2
- package/src/collection/Filter.mjs +51 -31
- package/src/component/Toast.mjs +2 -1
- package/src/container/Base.mjs +6 -3
- package/src/controller/Base.mjs +47 -12
- package/src/form/field/FileUpload.mjs +2 -2
- 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 +1 -3
- package/src/main/mixin/DeltaUpdates.mjs +3 -1
- package/src/manager/DomEvent.mjs +34 -24
- package/src/table/View.mjs +1 -1
- package/src/tooltip/Base.mjs +18 -6
- 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 +34 -0
package/apps/ServiceWorker.mjs
CHANGED
@@ -20,7 +20,7 @@ class MainContainerController extends Component {
|
|
20
20
|
'/contact': 'handleContactRoute',
|
21
21
|
'/users/{userId}': { handler: 'handleUserRoute', preHandler: 'doPrehandling' },
|
22
22
|
// '/users/{userId}' : {handler: 'handleUserRoute', preHandler: 'doPrehandling'}, //example
|
23
|
-
//
|
23
|
+
//'/users/{userId}/posts/{postId}' : {handler:'handleUserPostsRoute', preHandler: 'doPrehandlingFalse'}, //example
|
24
24
|
// default: 'doDefaultHandling' //optional - example
|
25
25
|
},
|
26
26
|
|
@@ -46,7 +46,7 @@ class MainContainerController extends Component {
|
|
46
46
|
|
47
47
|
|
48
48
|
doPrehandling(value, oldValue, params = null) {
|
49
|
-
const userId = parseInt(params);
|
49
|
+
const userId = parseInt(params.userId);
|
50
50
|
if (userId > 0 && userId === this.data.activeUser) {
|
51
51
|
return true;
|
52
52
|
}
|
@@ -132,7 +132,6 @@ class MainContainerController extends Component {
|
|
132
132
|
* @param {Object} data
|
133
133
|
*/
|
134
134
|
onSwitchButtonMetaReset(data) {
|
135
|
-
const currentUser = this.data.activeUser;
|
136
135
|
this.data.activeUser = 0;
|
137
136
|
this.#setUsername();
|
138
137
|
this.#removeMetaButtonSelection();
|
@@ -66,9 +66,17 @@ class MainContainer extends Viewport {
|
|
66
66
|
// These two have been moved to the start of the Toolbar by their weights
|
67
67
|
label: {
|
68
68
|
style : {marginLeft: 0},
|
69
|
-
weight: -10000
|
69
|
+
weight: -10000,
|
70
|
+
|
71
|
+
// Embed a tooltip request into the DOM
|
72
|
+
vdom: {
|
73
|
+
data: {
|
74
|
+
neoTooltip: 'The Label'
|
75
|
+
}
|
76
|
+
}
|
70
77
|
},
|
71
78
|
rowsPerPage: {
|
79
|
+
style : {margin: '0 .5em'},
|
72
80
|
tooltip: 'Set the number of rows to display in a page',
|
73
81
|
weight : -999
|
74
82
|
}
|
@@ -23,10 +23,6 @@ class MainContainer extends ConfigurationViewport {
|
|
23
23
|
cls : ['examples-container-accordion']
|
24
24
|
}
|
25
25
|
|
26
|
-
onConfigChange(config, opts) {
|
27
|
-
this.exampleComponent.items[0][config] = opts.value;
|
28
|
-
}
|
29
|
-
|
30
26
|
createConfigurationComponents() {
|
31
27
|
let me = this,
|
32
28
|
treeList = me.exampleComponent.items[0];
|
@@ -74,6 +70,12 @@ class MainContainer extends ConfigurationViewport {
|
|
74
70
|
stepSize : 3,
|
75
71
|
style : {marginTop: '10px'},
|
76
72
|
value : 400
|
73
|
+
}, {
|
74
|
+
ntype : 'button',
|
75
|
+
handler: me.onRemoveDomButtonClick.bind(me),
|
76
|
+
style : {marginTop: '20px'},
|
77
|
+
text : 'Remove DOM',
|
78
|
+
width : 100
|
77
79
|
}];
|
78
80
|
}
|
79
81
|
|
@@ -169,6 +171,23 @@ class MainContainer extends ConfigurationViewport {
|
|
169
171
|
}]
|
170
172
|
});
|
171
173
|
}
|
174
|
+
|
175
|
+
/**
|
176
|
+
* @param {String} config
|
177
|
+
* @param {Object} opts
|
178
|
+
*/
|
179
|
+
onConfigChange(config, opts) {
|
180
|
+
this.exampleComponent.items[0][config] = opts.value;
|
181
|
+
}
|
182
|
+
|
183
|
+
/**
|
184
|
+
*
|
185
|
+
* @param data
|
186
|
+
*/
|
187
|
+
onRemoveDomButtonClick(data) {
|
188
|
+
let accordion = this.exampleComponent.items[0];
|
189
|
+
accordion.hidden = !accordion.hidden
|
190
|
+
}
|
172
191
|
}
|
173
192
|
|
174
193
|
Neo.applyClassConfig(MainContainer);
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "neo.mjs",
|
3
|
-
"version": "6.9.
|
3
|
+
"version": "6.9.2",
|
4
4
|
"description": "The webworkers driven UI framework",
|
5
5
|
"type": "module",
|
6
6
|
"repository": {
|
@@ -47,7 +47,7 @@
|
|
47
47
|
"autoprefixer": "^10.4.16",
|
48
48
|
"chalk": "^5.3.0",
|
49
49
|
"clean-webpack-plugin": "^4.0.0",
|
50
|
-
"commander": "^11.
|
50
|
+
"commander": "^11.1.0",
|
51
51
|
"cssnano": "^6.0.1",
|
52
52
|
"envinfo": "^7.10.0",
|
53
53
|
"fs-extra": "^11.1.1",
|
@@ -56,9 +56,9 @@
|
|
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.
|
59
|
+
"sass": "^1.69.4",
|
60
60
|
"showdown": "^2.1.0",
|
61
|
-
"webpack": "^5.
|
61
|
+
"webpack": "^5.89.0",
|
62
62
|
"webpack-cli": "^5.1.4",
|
63
63
|
"webpack-dev-server": "4.15.1",
|
64
64
|
"webpack-hook-plugin": "^1.0.7",
|
@@ -1,12 +1,10 @@
|
|
1
1
|
.neo-toast {
|
2
|
-
z-index : 20; // ensure to be on top of table headers
|
3
2
|
align-items : center;
|
4
3
|
background-color: var(--toast-background-color);
|
5
4
|
border-radius : 0.5rem;
|
6
5
|
box-shadow : 0 0.25rem 0.5rem var(--toast-shadow-color);
|
7
6
|
color : var(--toast-color);
|
8
7
|
display : flex;
|
9
|
-
position : fixed;
|
10
8
|
transition : bottom .3s ease-out, top .3s ease-out;
|
11
9
|
|
12
10
|
.neo-toast-inner {
|
@@ -90,6 +88,7 @@
|
|
90
88
|
/* Toast positions */
|
91
89
|
.neo-toast-tr {
|
92
90
|
right: 1rem;
|
91
|
+
left : auto;
|
93
92
|
top : 1rem;
|
94
93
|
}
|
95
94
|
|
@@ -107,6 +106,7 @@
|
|
107
106
|
.neo-toast-br {
|
108
107
|
bottom: 1rem;
|
109
108
|
right : 1rem;
|
109
|
+
left : auto;
|
110
110
|
}
|
111
111
|
|
112
112
|
.neo-toast-bc {
|
package/src/DefaultConfig.mjs
CHANGED
@@ -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.
|
239
|
+
* @default '6.9.2'
|
240
240
|
* @memberOf! module:Neo
|
241
241
|
* @name config.version
|
242
242
|
* @type String
|
243
243
|
*/
|
244
|
-
version: '6.9.
|
244
|
+
version: '6.9.2'
|
245
245
|
};
|
246
246
|
|
247
247
|
Object.assign(DefaultConfig, {
|
@@ -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
|
/**
|
@@ -95,7 +98,7 @@ class Filter extends Base {
|
|
95
98
|
}
|
96
99
|
|
97
100
|
afterSetDisabled(...args) {
|
98
|
-
this.fireChangeEvent(...args)
|
101
|
+
this.fireChangeEvent(...args)
|
99
102
|
}
|
100
103
|
|
101
104
|
afterSetFilterBy(value, oldValue) {
|
@@ -103,28 +106,28 @@ class Filter extends Base {
|
|
103
106
|
}
|
104
107
|
|
105
108
|
afterSetIsUpdating(value, oldValue) {
|
106
|
-
value === false && this.fireChangeEvent(value, oldValue)
|
109
|
+
value === false && this.fireChangeEvent(value, oldValue)
|
107
110
|
}
|
108
111
|
|
109
112
|
afterSetOperator(...args) {
|
110
|
-
this.fireChangeEvent(...args)
|
113
|
+
this.fireChangeEvent(...args)
|
111
114
|
}
|
112
115
|
|
113
116
|
afterSetProperty(...args) {
|
114
|
-
this.fireChangeEvent(...args)
|
117
|
+
this.fireChangeEvent(...args)
|
115
118
|
}
|
116
119
|
|
117
120
|
afterSetValue(...args) {
|
118
|
-
this.fireChangeEvent(...args)
|
121
|
+
this.fireChangeEvent(...args)
|
119
122
|
}
|
120
123
|
|
121
124
|
beforeSetFilterBy(value, oldValue) {
|
122
125
|
if (value && typeof value !== 'function') {
|
123
126
|
Neo.logError('filterBy has to be a function', this);
|
124
|
-
return oldValue
|
127
|
+
return oldValue
|
125
128
|
}
|
126
129
|
|
127
|
-
return value
|
130
|
+
return value
|
128
131
|
}
|
129
132
|
|
130
133
|
/**
|
@@ -135,7 +138,7 @@ class Filter extends Base {
|
|
135
138
|
* @protected
|
136
139
|
*/
|
137
140
|
beforeSetOperator(value, oldValue) {
|
138
|
-
return this.beforeSetEnumValue(value, oldValue, 'operator')
|
141
|
+
return this.beforeSetEnumValue(value, oldValue, 'operator')
|
139
142
|
}
|
140
143
|
|
141
144
|
/**
|
@@ -149,10 +152,10 @@ class Filter extends Base {
|
|
149
152
|
value = me.value;
|
150
153
|
|
151
154
|
if (!me.filterBy) {
|
152
|
-
return {operator, property, value}
|
155
|
+
return {operator, property, value}
|
153
156
|
}
|
154
157
|
|
155
|
-
return null
|
158
|
+
return null
|
156
159
|
}
|
157
160
|
|
158
161
|
/**
|
@@ -167,7 +170,7 @@ class Filter extends Base {
|
|
167
170
|
operator: me.operator,
|
168
171
|
property: me.property,
|
169
172
|
value : me.value
|
170
|
-
})
|
173
|
+
})
|
171
174
|
}
|
172
175
|
}
|
173
176
|
|
@@ -184,7 +187,7 @@ class Filter extends Base {
|
|
184
187
|
filterValue, recordValue;
|
185
188
|
|
186
189
|
if (me._disabled) {
|
187
|
-
return false
|
190
|
+
return false
|
188
191
|
}
|
189
192
|
|
190
193
|
if (me._filterBy) {
|
@@ -193,11 +196,11 @@ class Filter extends Base {
|
|
193
196
|
filteredItems,
|
194
197
|
item,
|
195
198
|
value: me._value
|
196
|
-
})
|
199
|
+
})
|
197
200
|
}
|
198
201
|
|
199
202
|
if (me.includeEmptyValues && (me._value === null || Neo.isEmpty(me._value))) {
|
200
|
-
return false
|
203
|
+
return false
|
201
204
|
}
|
202
205
|
|
203
206
|
filterValue = me._value;
|
@@ -205,39 +208,56 @@ class Filter extends Base {
|
|
205
208
|
|
206
209
|
if (filterValue instanceof Date && recordValue instanceof Date) {
|
207
210
|
filterValue = filterValue.valueOf();
|
208
|
-
recordValue = recordValue.valueOf()
|
211
|
+
recordValue = recordValue.valueOf()
|
209
212
|
}
|
210
213
|
|
211
|
-
return !Filter[me._operator](recordValue, filterValue)
|
214
|
+
return !Filter[me._operator](recordValue, filterValue)
|
212
215
|
}
|
213
216
|
|
214
|
-
static ['=='] (a, b) {return a == b
|
215
|
-
static ['==='](a, b) {return a === b
|
216
|
-
static ['!='] (a, b) {return a != b
|
217
|
-
static ['!=='](a, b) {return a !== b
|
218
|
-
static ['<'] (a, b) {return a < b
|
219
|
-
static ['<='] (a, b) {return a <= b
|
220
|
-
static ['>'] (a, b) {return a > b
|
221
|
-
static ['>='] (a, b) {return a >= b
|
217
|
+
static ['=='] (a, b) {return a == b}
|
218
|
+
static ['==='](a, b) {return a === b}
|
219
|
+
static ['!='] (a, b) {return a != b}
|
220
|
+
static ['!=='](a, b) {return a !== b}
|
221
|
+
static ['<'] (a, b) {return a < b}
|
222
|
+
static ['<='] (a, b) {return a <= b}
|
223
|
+
static ['>'] (a, b) {return a > b}
|
224
|
+
static ['>='] (a, b) {return a >= b}
|
225
|
+
|
226
|
+
static ['endsWith'](a, b) {
|
227
|
+
if (!Neo.isString(a)) {a = String(a)}
|
228
|
+
if (!Neo.isString(b)) {b = String(b)}
|
229
|
+
|
230
|
+
return a?.toLowerCase().endsWith(b?.toLowerCase()) || false
|
231
|
+
}
|
222
232
|
|
223
233
|
static ['excluded'](a, b) {
|
224
|
-
return b.indexOf(a) < 0
|
234
|
+
return b.indexOf(a) < 0
|
225
235
|
}
|
226
236
|
|
227
237
|
static ['included'](a, b) {
|
228
|
-
return b.indexOf(a) > -1
|
238
|
+
return b.indexOf(a) > -1
|
229
239
|
}
|
230
240
|
|
231
241
|
static ['isDefined'](a, b) {
|
232
|
-
return a !== undefined
|
242
|
+
return a !== undefined
|
233
243
|
}
|
234
244
|
|
235
245
|
static ['isUndefined'](a, b) {
|
236
|
-
return a === undefined
|
246
|
+
return a === undefined
|
237
247
|
}
|
238
248
|
|
239
249
|
static ['like'](a, b) {
|
240
|
-
|
250
|
+
if (!Neo.isString(a)) {a = String(a)}
|
251
|
+
if (!Neo.isString(b)) {b = String(b)}
|
252
|
+
|
253
|
+
return a?.toLowerCase().includes(b?.toLowerCase()) || false
|
254
|
+
}
|
255
|
+
|
256
|
+
static ['startsWith'](a, b) {
|
257
|
+
if (!Neo.isString(a)) {a = String(a)}
|
258
|
+
if (!Neo.isString(b)) {b = String(b)}
|
259
|
+
|
260
|
+
return a?.toLowerCase().startsWith(b?.toLowerCase()) || false
|
241
261
|
}
|
242
262
|
}
|
243
263
|
|
package/src/component/Toast.mjs
CHANGED
package/src/container/Base.mjs
CHANGED
@@ -401,7 +401,7 @@ class Base extends Component {
|
|
401
401
|
* @param {Boolean} [silent=false] true to update the vdom silently (useful for destroying multiple child items in a row)
|
402
402
|
*/
|
403
403
|
destroy(updateParentVdom=false, silent=false) {
|
404
|
-
this.items
|
404
|
+
this.items?.forEach(item => {
|
405
405
|
item.destroy?.(false, true)
|
406
406
|
});
|
407
407
|
|
@@ -502,8 +502,11 @@ class Base extends Component {
|
|
502
502
|
|
503
503
|
if (config.items) {
|
504
504
|
// If we are passed an object, merge the class's own items object into it
|
505
|
-
|
506
|
-
Neo.merge(Neo.clone(me.constructor.config.items), config.items)
|
505
|
+
if (Neo.typeOf(config.items) === 'Object') {
|
506
|
+
me.items = Neo.merge(Neo.clone(me.constructor.config.items), config.items)
|
507
|
+
} else {
|
508
|
+
me._items = Neo.clone(config.items, true, true)
|
509
|
+
}
|
507
510
|
delete config.items
|
508
511
|
}
|
509
512
|
|
package/src/controller/Base.mjs
CHANGED
@@ -21,7 +21,7 @@ class Base extends CoreBase {
|
|
21
21
|
/**
|
22
22
|
* @member {Object} routes={}
|
23
23
|
*/
|
24
|
-
|
24
|
+
routes_: {},
|
25
25
|
|
26
26
|
/**
|
27
27
|
* @member {Object} handleRoutes={}
|
@@ -42,20 +42,42 @@ class Base extends CoreBase {
|
|
42
42
|
super.construct(config);
|
43
43
|
|
44
44
|
const me = this;
|
45
|
+
HashHistory.on('change', me.onHashChange, me);
|
46
|
+
}
|
47
|
+
|
48
|
+
/**
|
49
|
+
* Triggered after the badgePosition config got changed
|
50
|
+
* @param {String} value
|
51
|
+
* @param {String} oldValue
|
52
|
+
* @protected
|
53
|
+
*/
|
54
|
+
afterSetRoutes(value, oldValue){
|
55
|
+
const me = this;
|
56
|
+
|
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
|
+
|
45
69
|
|
46
70
|
me.handleRoutes = {};
|
47
71
|
if (Object.keys(me.routes).length > 0) {
|
48
72
|
Object.keys(me.routes).forEach(key => {
|
49
73
|
if (key.toLowerCase() === 'default'){
|
50
|
-
me.defaultRoute =
|
74
|
+
me.defaultRoute = value[key];
|
51
75
|
} else {
|
52
76
|
me.handleRoutes[key] = new RegExp(key.replace(/{[^\s/]+}/g, '([\\w-]+)')+'$');
|
53
77
|
}
|
54
78
|
|
55
79
|
});
|
56
80
|
}
|
57
|
-
|
58
|
-
HashHistory.on('change', me.onHashChange, me);
|
59
81
|
}
|
60
82
|
|
61
83
|
/**
|
@@ -85,13 +107,26 @@ class Base extends CoreBase {
|
|
85
107
|
|
86
108
|
const me = this;
|
87
109
|
let hasRouteBeenFound = false;
|
88
|
-
Object.keys(me.handleRoutes).
|
110
|
+
Object.keys(me.handleRoutes).forEach( key => {
|
89
111
|
let preHandler = undefined;
|
90
112
|
let executeHandler = undefined;
|
91
113
|
let responsePreHandler = undefined;
|
92
114
|
|
93
115
|
const result = value.hashString.match(me.handleRoutes[key]);
|
94
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
|
+
}
|
129
|
+
|
95
130
|
const target = me.routes[key];
|
96
131
|
if (Neo.isString(target)){
|
97
132
|
executeHandler = this.routes[key];
|
@@ -100,20 +135,20 @@ class Base extends CoreBase {
|
|
100
135
|
if (Neo.isObject(target)){
|
101
136
|
executeHandler = this.routes[key].handler;
|
102
137
|
preHandler = this.routes[key].preHandler;
|
103
|
-
|
138
|
+
if (preHandler) {
|
139
|
+
responsePreHandler = me[preHandler]?.call(this, value, oldValue, paramObject);
|
140
|
+
} else {
|
141
|
+
responsePreHandler = true;
|
142
|
+
console.warn('No preHandler defined for routes -> todo it better');
|
143
|
+
}
|
104
144
|
}
|
105
145
|
|
106
146
|
hasRouteBeenFound = true;
|
107
147
|
|
108
148
|
if (responsePreHandler) {
|
109
|
-
this[executeHandler]?.call(this, value, oldValue,
|
110
|
-
} else {
|
111
|
-
console.warn('No preHandler defined for routes -> todo it better');
|
149
|
+
this[executeHandler]?.call(this, value, oldValue, paramObject);
|
112
150
|
}
|
113
|
-
return false;
|
114
|
-
|
115
151
|
}
|
116
|
-
return true;
|
117
152
|
});
|
118
153
|
|
119
154
|
if (Object.keys(me.handleRoutes).length > 0 && !hasRouteBeenFound) {
|
@@ -285,7 +285,7 @@ class FileUpload extends Base {
|
|
285
285
|
documentDeleteMethod: 'DELETE',
|
286
286
|
|
287
287
|
/**
|
288
|
-
* @member {String} state_=
|
288
|
+
* @member {String} state_=ready
|
289
289
|
*/
|
290
290
|
state_: 'ready',
|
291
291
|
|
@@ -729,7 +729,7 @@ class FileUpload extends Base {
|
|
729
729
|
isChangeEventNeeded = true;
|
730
730
|
}
|
731
731
|
|
732
|
-
if (isChangeEventNeeded) {
|
732
|
+
if (isChangeEventNeeded && oldValue !== undefined) {
|
733
733
|
me.fireChangeEvent(me.file)
|
734
734
|
}
|
735
735
|
me.validate();
|
@@ -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
|
*/
|
package/src/form/field/Text.mjs
CHANGED
@@ -367,8 +367,9 @@ class Text extends Base {
|
|
367
367
|
{ cls } = me;
|
368
368
|
|
369
369
|
NeoArray.toggle(cls, 'neo-not-editable', !value);
|
370
|
-
me.cls = cls
|
371
|
-
|
370
|
+
me.cls = cls;
|
371
|
+
|
372
|
+
me.updateReadOnlyState()
|
372
373
|
}
|
373
374
|
|
374
375
|
/**
|
@@ -675,7 +676,7 @@ class Text extends Base {
|
|
675
676
|
NeoArray[value ? 'add' : 'remove'](cls, 'neo-readonly');
|
676
677
|
me.cls = cls;
|
677
678
|
|
678
|
-
me.
|
679
|
+
me.updateReadOnlyState();
|
679
680
|
|
680
681
|
me.triggers?.forEach(trigger => {
|
681
682
|
trigger.hidden = value ? true : trigger.getHiddenState?.() || false
|
@@ -1440,6 +1441,15 @@ class Text extends Base {
|
|
1440
1441
|
me.update()
|
1441
1442
|
}
|
1442
1443
|
|
1444
|
+
/**
|
1445
|
+
* The DOM based readonly attribute needs to honor the editable & readOnly configs
|
1446
|
+
*/
|
1447
|
+
updateReadOnlyState() {
|
1448
|
+
let me = this;
|
1449
|
+
|
1450
|
+
me.changeInputElKey('readonly', !me.editable || me.readOnly || null);
|
1451
|
+
}
|
1452
|
+
|
1443
1453
|
/**
|
1444
1454
|
* Since triggers do not get rendered, assign the relevant props
|
1445
1455
|
* todo: this could be handled by component.Base
|
@@ -34,14 +34,7 @@ class Picker extends Base {
|
|
34
34
|
*/
|
35
35
|
onTriggerClick(data) {
|
36
36
|
this.field.onPickerTriggerClick();
|
37
|
-
}
|
38
|
-
|
39
|
-
/**
|
40
|
-
* @returns {Boolean} true in case the trigger should be hidden
|
41
|
-
*/
|
42
|
-
getHiddenState() {
|
43
|
-
return !this.field.editable;
|
44
|
-
}
|
37
|
+
}
|
45
38
|
}
|
46
39
|
|
47
40
|
Neo.applyClassConfig(Picker);
|
package/src/main/DomEvents.mjs
CHANGED
@@ -420,13 +420,11 @@ class DomEvents extends Base {
|
|
420
420
|
onChange(event) {
|
421
421
|
let me = this,
|
422
422
|
target = event.target,
|
423
|
-
tagName = target.tagName,
|
424
|
-
value = target.value,
|
425
423
|
|
426
424
|
data = {
|
427
425
|
...me.getEventData(event),
|
428
426
|
valid: target.checkValidity(),
|
429
|
-
value:
|
427
|
+
value: target.value
|
430
428
|
};
|
431
429
|
|
432
430
|
// input and change events can pass a FileList for input type file
|
@@ -196,7 +196,9 @@ class DeltaUpdates extends Base {
|
|
196
196
|
node = me.getElementOrBody(delta.id);
|
197
197
|
|
198
198
|
if (!node) {
|
199
|
-
|
199
|
+
if (Neo.config.environment === 'development') {
|
200
|
+
console.warn('du_updateNode: node not found for id', delta.id)
|
201
|
+
}
|
200
202
|
} else {
|
201
203
|
Object.entries(delta).forEach(([prop, value]) => {
|
202
204
|
switch(prop) {
|
package/src/manager/DomEvent.mjs
CHANGED
@@ -432,34 +432,44 @@ class DomEvent extends Base {
|
|
432
432
|
* @returns {Boolean|String} true in case the delegation string matches the event path
|
433
433
|
*/
|
434
434
|
verifyDelegationPath(listener, path) {
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
item = delegationArray[i];
|
444
|
-
isId = item.startsWith('#');
|
445
|
-
|
446
|
-
if (isId || item.startsWith('.')) {
|
447
|
-
item = item.substr(1);
|
435
|
+
const { delegate } = listener;
|
436
|
+
|
437
|
+
let j = 0, pathLen = path.length, targetId;
|
438
|
+
|
439
|
+
if (typeof delegate === 'function') {
|
440
|
+
j = delegate(path);
|
441
|
+
if (j != null) {
|
442
|
+
targetId = path[j].id;
|
448
443
|
}
|
444
|
+
}
|
445
|
+
else {
|
446
|
+
let delegationArray = delegate.split(' '),
|
447
|
+
len = delegationArray.length,
|
448
|
+
hasMatch, i, item, isId;
|
449
|
+
|
450
|
+
for (i=len-1; i >= 0; i--) {
|
451
|
+
hasMatch = false;
|
452
|
+
item = delegationArray[i];
|
453
|
+
isId = item.startsWith('#');
|
454
|
+
|
455
|
+
if (isId || item.startsWith('.')) {
|
456
|
+
item = item.substr(1);
|
457
|
+
}
|
449
458
|
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
459
|
+
for (; j < pathLen; j++) {
|
460
|
+
if (
|
461
|
+
(isId && path[j].id === item) ||
|
462
|
+
path[j].cls.includes(item)
|
463
|
+
) {
|
464
|
+
hasMatch = true;
|
465
|
+
targetId = path[j].id;
|
466
|
+
break
|
467
|
+
}
|
458
468
|
}
|
459
|
-
}
|
460
469
|
|
461
|
-
|
462
|
-
|
470
|
+
if (!hasMatch) {
|
471
|
+
return false
|
472
|
+
}
|
463
473
|
}
|
464
474
|
}
|
465
475
|
|
package/src/table/View.mjs
CHANGED
@@ -237,7 +237,7 @@ class View extends Component {
|
|
237
237
|
me.promiseUpdate().then(() => {
|
238
238
|
if (selectedRows?.length > 0) {
|
239
239
|
// this logic only works for selection.table.RowModel
|
240
|
-
Neo.main.DomAccess.scrollToTableRow({id: selectedRows[0]})
|
240
|
+
Neo.main.DomAccess.scrollToTableRow({appName: me.appName, id: selectedRows[0]})
|
241
241
|
}
|
242
242
|
})
|
243
243
|
}
|
package/src/tooltip/Base.mjs
CHANGED
@@ -196,17 +196,18 @@ class Base extends Container {
|
|
196
196
|
componentId : app.mainView.id,
|
197
197
|
resetCfg : {},
|
198
198
|
isShared : true,
|
199
|
-
delegate :
|
199
|
+
delegate : this.delegateFilter,
|
200
200
|
listeners : {
|
201
201
|
// Reconfigure on over a target
|
202
|
-
async targetOver({ target }) {
|
202
|
+
async targetOver({ target, data }) {
|
203
203
|
// Revert last pointerOver config set to initial setting.
|
204
204
|
this.set(this.resetCfg);
|
205
205
|
this.resetCfg = {};
|
206
206
|
|
207
207
|
// Use the tooltip config block that the target was configured with
|
208
|
-
// to
|
209
|
-
|
208
|
+
// to reconfigure this instance, or if there was none, check the
|
209
|
+
// data-neo-tooltip property for a text string.
|
210
|
+
const config = target?._tooltip || { text : data.target.data.neoTooltip };
|
210
211
|
|
211
212
|
// Cache things we have to reset
|
212
213
|
for (const key in config) {
|
@@ -264,7 +265,8 @@ class Base extends Container {
|
|
264
265
|
me.align.targetMargin = 10;
|
265
266
|
|
266
267
|
me.fire('targetOver', {
|
267
|
-
target : me.activeTarget
|
268
|
+
target : me.activeTarget,
|
269
|
+
data
|
268
270
|
});
|
269
271
|
|
270
272
|
// Still visible, just realign
|
@@ -278,6 +280,15 @@ class Base extends Container {
|
|
278
280
|
}
|
279
281
|
}
|
280
282
|
|
283
|
+
// Used as a delegate filter to activate on targets which have a tooltip configuration
|
284
|
+
static delegateFilter(path) {
|
285
|
+
for (let i = 0, { length } = path; i < length; i++) {
|
286
|
+
if (path[i].cls.includes('neo-uses-shared-tooltip') || path[i].data['neoTooltip']) {
|
287
|
+
return i;
|
288
|
+
}
|
289
|
+
}
|
290
|
+
}
|
291
|
+
|
281
292
|
/**
|
282
293
|
* @param {Object} data
|
283
294
|
*/
|
@@ -287,7 +298,8 @@ class Base extends Container {
|
|
287
298
|
// If it's an internal move within the delegate, do nothing
|
288
299
|
if (data.currentTarget === me.activeTarget?.id) {
|
289
300
|
me.fire('targetOut', {
|
290
|
-
target :
|
301
|
+
target : me.activeTarget,
|
302
|
+
data
|
291
303
|
});
|
292
304
|
me.activeTarget = null;
|
293
305
|
me.hideDelayed(data);
|
package/src/vdom/Helper.mjs
CHANGED
@@ -750,6 +750,13 @@ class Helper extends Base {
|
|
750
750
|
node.className = value;
|
751
751
|
}
|
752
752
|
break;
|
753
|
+
case 'data':
|
754
|
+
if (value && Neo.typeOf(value) === 'Object') {
|
755
|
+
Object.entries(value).forEach(([key, val]) => {
|
756
|
+
node.attributes[`data-${Neo.decamel(key)}`] = val
|
757
|
+
})
|
758
|
+
}
|
759
|
+
break;
|
753
760
|
case 'height':
|
754
761
|
case 'maxHeight':
|
755
762
|
case 'maxWidth':
|
package/src/worker/App.mjs
CHANGED
@@ -134,6 +134,10 @@ class App extends Base {
|
|
134
134
|
}
|
135
135
|
}
|
136
136
|
} else {
|
137
|
+
// default parentId='document.body' => we want it to get shown
|
138
|
+
config.autoMount = true;
|
139
|
+
config.autoRender = true;
|
140
|
+
|
137
141
|
instance = Neo[config.ntype ? 'ntype' : 'create'](config)
|
138
142
|
}
|
139
143
|
|
@@ -0,0 +1,8 @@
|
|
1
|
+
// Important: You need to import all classes which you want to use inside tests here
|
2
|
+
// (excluding base classes (prototypes) of already imported classes)
|
3
|
+
|
4
|
+
import Button from '../../src/button/Base.mjs';
|
5
|
+
import DateSelector from '../../src/component/DateSelector.mjs';
|
6
|
+
import SelectField from '../../src/form/field/Select.mjs';
|
7
|
+
|
8
|
+
export const onStart = () => Neo.app({name: 'AppEmpty'})
|
@@ -0,0 +1,17 @@
|
|
1
|
+
StartTest(t => {
|
2
|
+
t.it('Checking if neo.mjs got started', async t => {
|
3
|
+
if (!globalThis.Neo?.Main) {
|
4
|
+
console.log('Starting the neo.mjs workers setup');
|
5
|
+
|
6
|
+
await import('../../../../src/MicroLoader.mjs');
|
7
|
+
}
|
8
|
+
|
9
|
+
setTimeout(() => {
|
10
|
+
Neo.worker.App.createNeoInstance({
|
11
|
+
ntype : 'button',
|
12
|
+
iconCls: 'fa fa-home',
|
13
|
+
text : 'Hello Siesta'
|
14
|
+
})
|
15
|
+
}, 300)
|
16
|
+
});
|
17
|
+
});
|
@@ -0,0 +1,18 @@
|
|
1
|
+
StartTest(t => {
|
2
|
+
t.it('Checking if neo.mjs got started', async t => {
|
3
|
+
if (!globalThis.Neo?.Main) {
|
4
|
+
console.log('Starting the neo.mjs workers setup');
|
5
|
+
|
6
|
+
await import('../../../../src/MicroLoader.mjs');
|
7
|
+
}
|
8
|
+
|
9
|
+
setTimeout(() => {
|
10
|
+
Neo.worker.App.createNeoInstance({
|
11
|
+
ntype : 'dateselector',
|
12
|
+
height: 250,
|
13
|
+
value : '2023-10-15',
|
14
|
+
width : 300
|
15
|
+
})
|
16
|
+
}, 300)
|
17
|
+
});
|
18
|
+
});
|
@@ -0,0 +1,35 @@
|
|
1
|
+
StartTest(t => {
|
2
|
+
t.it('Checking if neo.mjs got started', async t => {
|
3
|
+
if (!globalThis.Neo?.Main) {
|
4
|
+
console.log('Starting the neo.mjs workers setup');
|
5
|
+
|
6
|
+
await import('../../../../../src/MicroLoader.mjs');
|
7
|
+
}
|
8
|
+
|
9
|
+
setTimeout(() => {
|
10
|
+
Neo.worker.App.createNeoInstance({
|
11
|
+
ntype : 'selectfield',
|
12
|
+
labelPosition: 'inline',
|
13
|
+
labelText : 'US States',
|
14
|
+
labelWidth : 80,
|
15
|
+
width : 300,
|
16
|
+
|
17
|
+
store : {
|
18
|
+
autoLoad : true,
|
19
|
+
keyProperty: 'abbreviation',
|
20
|
+
url : '../../resources/examples/data/us_states.json',
|
21
|
+
|
22
|
+
model: {
|
23
|
+
fields: [{
|
24
|
+
name: 'abbreviation',
|
25
|
+
type: 'string'
|
26
|
+
}, {
|
27
|
+
name: 'name',
|
28
|
+
type: 'string'
|
29
|
+
}]
|
30
|
+
}
|
31
|
+
}
|
32
|
+
})
|
33
|
+
}, 300)
|
34
|
+
});
|
35
|
+
});
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
6
|
+
|
7
|
+
<!-- Web interface -->
|
8
|
+
<link rel="stylesheet" type="text/css" href="../../node_modules/siesta-lite/resources/css/siesta-all.css">
|
9
|
+
<script type="text/javascript" src="../../node_modules/siesta-lite/siesta-all.js"></script>
|
10
|
+
|
11
|
+
<!-- Project file -->
|
12
|
+
<script type="text/javascript" src="siesta.js"></script>
|
13
|
+
</head>
|
14
|
+
|
15
|
+
<body>
|
16
|
+
</body>
|
17
|
+
</html>
|
@@ -0,0 +1,34 @@
|
|
1
|
+
const project = new Siesta.Project.Browser();
|
2
|
+
|
3
|
+
project.configure({
|
4
|
+
title : 'Neo Component Tests',
|
5
|
+
isEcmaModule: true
|
6
|
+
});
|
7
|
+
|
8
|
+
project.plan(
|
9
|
+
{
|
10
|
+
group: 'button',
|
11
|
+
items: [
|
12
|
+
'files/button/Base.mjs'
|
13
|
+
]
|
14
|
+
},
|
15
|
+
{
|
16
|
+
group: 'component',
|
17
|
+
items: [
|
18
|
+
'files/component/DateSelector.mjs'
|
19
|
+
]
|
20
|
+
},
|
21
|
+
{
|
22
|
+
group: 'form',
|
23
|
+
items: [
|
24
|
+
{
|
25
|
+
group: 'field',
|
26
|
+
items: [
|
27
|
+
'files/form/field/Select.mjs'
|
28
|
+
]
|
29
|
+
}
|
30
|
+
]
|
31
|
+
}
|
32
|
+
);
|
33
|
+
|
34
|
+
project.start();
|