neo.mjs 6.9.0 → 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/examples/ServiceWorker.mjs +2 -2
- package/examples/toolbar/paging/view/MainContainer.mjs +8 -1
- 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 +13 -2
- package/src/component/Toast.mjs +2 -1
- 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/manager/DomEvent.mjs +34 -24
- 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 +14 -0
package/apps/ServiceWorker.mjs
CHANGED
@@ -66,7 +66,14 @@ 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
|
+
'neo-tooltip' : 'The Label'
|
75
|
+
}
|
76
|
+
}
|
70
77
|
},
|
71
78
|
rowsPerPage: {
|
72
79
|
tooltip: 'Set the number of rows to display in a page',
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "neo.mjs",
|
3
|
-
"version": "6.9.
|
3
|
+
"version": "6.9.1",
|
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.3",
|
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.1'
|
240
240
|
* @memberOf! module:Neo
|
241
241
|
* @name config.version
|
242
242
|
* @type String
|
243
243
|
*/
|
244
|
-
version: '6.9.
|
244
|
+
version: '6.9.1'
|
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
|
/**
|
@@ -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/Toast.mjs
CHANGED
@@ -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/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/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,14 @@
|
|
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
|
+
'files/button/Base.mjs',
|
10
|
+
'files/component/DateSelector.mjs',
|
11
|
+
'files/form/field/Select.mjs'
|
12
|
+
);
|
13
|
+
|
14
|
+
project.start();
|