neo.mjs 6.10.2 → 6.10.3
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/package.json +2 -2
- package/resources/data/deck/learnneo/t.json +3 -3
- package/resources/scss/src/component/Base.scss +26 -0
- package/src/DefaultConfig.mjs +2 -2
- package/src/component/Base.mjs +58 -14
- package/src/util/Array.mjs +2 -12
- package/src/worker/App.mjs +8 -2
- package/test/components/files/button/Base.mjs +41 -14
- package/test/components/files/form/field/Select.mjs +21 -29
- package/test/components/siesta.js +32 -2
package/apps/ServiceWorker.mjs
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "neo.mjs",
|
3
|
-
"version": "6.10.
|
3
|
+
"version": "6.10.3",
|
4
4
|
"description": "The webworkers driven UI framework",
|
5
5
|
"type": "module",
|
6
6
|
"repository": {
|
@@ -42,7 +42,7 @@
|
|
42
42
|
},
|
43
43
|
"homepage": "https://neomjs.github.io/pages/",
|
44
44
|
"devDependencies": {
|
45
|
-
"@fortawesome/fontawesome-free": "^6.5.
|
45
|
+
"@fortawesome/fontawesome-free": "^6.5.1",
|
46
46
|
"autoprefixer": "^10.4.16",
|
47
47
|
"chalk": "^5.3.0",
|
48
48
|
"clean-webpack-plugin": "^4.0.0",
|
@@ -143,13 +143,13 @@
|
|
143
143
|
"name": "Main Thread Addons"
|
144
144
|
},
|
145
145
|
{
|
146
|
-
"id": "
|
146
|
+
"id": "MainThreadAddonIntro",
|
147
147
|
"parentId": "MainThreadAddons",
|
148
148
|
"isLeaf": true,
|
149
149
|
"name": "Introduction"
|
150
150
|
},
|
151
151
|
{
|
152
|
-
"id": "
|
152
|
+
"id": "MainThreadAddonExample",
|
153
153
|
"parentId": "MainThreadAddons",
|
154
154
|
"isLeaf": true,
|
155
155
|
"name": "Example"
|
@@ -214,4 +214,4 @@
|
|
214
214
|
"isLeaf": true
|
215
215
|
}
|
216
216
|
]
|
217
|
-
}
|
217
|
+
}
|
@@ -25,3 +25,29 @@
|
|
25
25
|
top : -10000px;
|
26
26
|
z-index : 1000;
|
27
27
|
}
|
28
|
+
|
29
|
+
.neo-masked {
|
30
|
+
position : relative;
|
31
|
+
|
32
|
+
.neo-load-mask {
|
33
|
+
position : absolute;
|
34
|
+
inset : 0;
|
35
|
+
background-color : inherit;
|
36
|
+
z-index : 4;
|
37
|
+
display : grid;
|
38
|
+
justify-content : center;
|
39
|
+
align-content : center;
|
40
|
+
overflow : clip;
|
41
|
+
|
42
|
+
.neo-load-mask-body {
|
43
|
+
display : flex;
|
44
|
+
flex-flow : row nowrap;
|
45
|
+
gap : 0.7em;
|
46
|
+
|
47
|
+
.fa-spinner {
|
48
|
+
width : 1em;
|
49
|
+
height : 1em;
|
50
|
+
}
|
51
|
+
}
|
52
|
+
}
|
53
|
+
}
|
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.10.
|
239
|
+
* @default '6.10.3'
|
240
240
|
* @memberOf! module:Neo
|
241
241
|
* @name config.version
|
242
242
|
* @type String
|
243
243
|
*/
|
244
|
-
version: '6.10.
|
244
|
+
version: '6.10.3'
|
245
245
|
};
|
246
246
|
|
247
247
|
Object.assign(DefaultConfig, {
|
package/src/component/Base.mjs
CHANGED
@@ -199,6 +199,12 @@ class Base extends CoreBase {
|
|
199
199
|
* @member {String|null} html_=null
|
200
200
|
*/
|
201
201
|
html_: null,
|
202
|
+
/**
|
203
|
+
* Set to `true` to show a spinner centered in the component.
|
204
|
+
* Set to a string to show a message next to a spinner centered in the component.
|
205
|
+
* @member {Boolean|String} isLoading=false
|
206
|
+
*/
|
207
|
+
isLoading_: false,
|
202
208
|
/**
|
203
209
|
* Internal flag which will get set to true while an update request (worker messages) is in progress
|
204
210
|
* @member {Boolean} isVdomUpdating=false
|
@@ -653,6 +659,48 @@ class Base extends CoreBase {
|
|
653
659
|
ComponentManager.register(this)
|
654
660
|
}
|
655
661
|
|
662
|
+
/**
|
663
|
+
* Triggered after the isLoading config got changed
|
664
|
+
* @param {Boolean|String} value
|
665
|
+
* @param {Boolean|String} oldValue
|
666
|
+
* @protected
|
667
|
+
*/
|
668
|
+
afterSetIsLoading(value, oldValue) {
|
669
|
+
if (!(value === false && oldValue === undefined)) {
|
670
|
+
let me = this,
|
671
|
+
{ cls, vdom } = me,
|
672
|
+
maskIndex;
|
673
|
+
|
674
|
+
if (oldValue !== undefined && vdom.cn) {
|
675
|
+
maskIndex = vdom.cn.findIndex(c => c.cls.includes('neo-load-mask'));
|
676
|
+
|
677
|
+
// Remove the load mask
|
678
|
+
if (maskIndex !== -1) {
|
679
|
+
vdom.cn.splice(maskIndex, 1)
|
680
|
+
}
|
681
|
+
}
|
682
|
+
|
683
|
+
if (value) {
|
684
|
+
vdom.cn.push(me.loadMask = {
|
685
|
+
cls: ['neo-load-mask'],
|
686
|
+
cn : [{
|
687
|
+
cls: ['neo-load-mask-body'],
|
688
|
+
cn : [{
|
689
|
+
cls: ['fa', 'fa-spinner', 'fa-spin']
|
690
|
+
}, {
|
691
|
+
cls : ['neo-loading-message'],
|
692
|
+
html : value,
|
693
|
+
removeDom: !Neo.isString(value)
|
694
|
+
}]
|
695
|
+
}]
|
696
|
+
})
|
697
|
+
}
|
698
|
+
|
699
|
+
NeoArray.toggle(cls, 'neo-masked', value);
|
700
|
+
me.set({cls, vdom})
|
701
|
+
}
|
702
|
+
}
|
703
|
+
|
656
704
|
/**
|
657
705
|
* Triggered after the maxHeight config got changed
|
658
706
|
* @param {Number|String|null} value
|
@@ -1231,8 +1279,12 @@ class Base extends CoreBase {
|
|
1231
1279
|
* Triggers all stored resolve() callbacks
|
1232
1280
|
*/
|
1233
1281
|
doResolveUpdateCache() {
|
1234
|
-
|
1235
|
-
|
1282
|
+
let me = this;
|
1283
|
+
|
1284
|
+
if (me.resolveUpdateCache) {
|
1285
|
+
me.resolveUpdateCache.forEach(item => item());
|
1286
|
+
me.resolveUpdateCache = []
|
1287
|
+
}
|
1236
1288
|
}
|
1237
1289
|
|
1238
1290
|
/**
|
@@ -1959,10 +2011,11 @@ class Base extends CoreBase {
|
|
1959
2011
|
resolve?.();
|
1960
2012
|
|
1961
2013
|
if (me.needsVdomUpdate) {
|
1962
|
-
|
2014
|
+
// if a new update is scheduled, we can clear the cache => these updates are included
|
2015
|
+
me.childUpdateCache = [];
|
1963
2016
|
|
1964
2017
|
me.update()
|
1965
|
-
} else {
|
2018
|
+
} else if (me.childUpdateCache) {
|
1966
2019
|
[...me.childUpdateCache].forEach(id => {
|
1967
2020
|
Neo.getComponent(id)?.update();
|
1968
2021
|
NeoArray.remove(me.childUpdateCache, id)
|
@@ -2133,16 +2186,7 @@ class Base extends CoreBase {
|
|
2133
2186
|
me._hidden = true; // silent update
|
2134
2187
|
me.mounted = false;
|
2135
2188
|
|
2136
|
-
Neo.
|
2137
|
-
action : 'updateDom',
|
2138
|
-
appName: me.appName,
|
2139
|
-
deltas : [{
|
2140
|
-
action: 'removeNode',
|
2141
|
-
id : me.vdom.id
|
2142
|
-
}]
|
2143
|
-
}).catch(err => {
|
2144
|
-
console.log('Error attempting to unmount component', err, me)
|
2145
|
-
})
|
2189
|
+
Neo.applyDeltas(me.appName, {action: 'removeNode', id: me.vdom.id})
|
2146
2190
|
}
|
2147
2191
|
|
2148
2192
|
/**
|
package/src/util/Array.mjs
CHANGED
@@ -117,18 +117,8 @@ class NeoArray extends Base {
|
|
117
117
|
* @param {*} item
|
118
118
|
* @param {Boolean} [add]
|
119
119
|
*/
|
120
|
-
static toggle(arr, item, add) {
|
121
|
-
|
122
|
-
|
123
|
-
if (add === true) {
|
124
|
-
operation = 'add';
|
125
|
-
} else if (add === false) {
|
126
|
-
operation = 'remove';
|
127
|
-
} else {
|
128
|
-
operation = this.hasItem(arr, item) ? 'remove' : 'add';
|
129
|
-
}
|
130
|
-
|
131
|
-
this[operation](arr, item);
|
120
|
+
static toggle(arr, item, add = !this.hasItem(arr, item)) {
|
121
|
+
this[add ? 'add' : 'remove'](arr, item);
|
132
122
|
}
|
133
123
|
|
134
124
|
/**
|
package/src/worker/App.mjs
CHANGED
@@ -107,16 +107,22 @@ class App extends Base {
|
|
107
107
|
* }).then(id => console.log(id))
|
108
108
|
*
|
109
109
|
* @param {Object} config
|
110
|
+
* @param {String} [config.importPath] you can lazy load missing classes via this config. dev mode only.
|
110
111
|
* @param {String} [config.parentId] passing a parentId will put your instance into a container
|
111
112
|
* @param {Number} [config.parentIndex] if a parentId is passed, but no index, neo will use add()
|
112
113
|
* @returns {String} the instance id
|
113
114
|
*/
|
114
|
-
createNeoInstance(config) {
|
115
|
+
async createNeoInstance(config) {
|
116
|
+
if (config.importPath) {
|
117
|
+
await import(/* webpackIgnore: true */ config.importPath);
|
118
|
+
delete config.importPath
|
119
|
+
}
|
120
|
+
|
115
121
|
let appName = Object.keys(Neo.apps)[0], // fallback in case no appName was provided
|
116
122
|
Container = Neo.container?.Base,
|
117
123
|
index, instance, parent;
|
118
124
|
|
119
|
-
config = {appName
|
125
|
+
config = {appName, ...config};
|
120
126
|
|
121
127
|
if (config.parentId) {
|
122
128
|
parent = Neo.getComponent(config.parentId);
|
@@ -1,17 +1,44 @@
|
|
1
1
|
StartTest(t => {
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
2
|
+
let button;
|
3
|
+
|
4
|
+
t.beforeEach(async t => {
|
5
|
+
button && await Neo.worker.App.destroyNeoInstance(button);
|
6
|
+
});
|
7
|
+
|
8
|
+
t.it('Sanity', async t => {
|
9
|
+
button = await Neo.worker.App.createNeoInstance({
|
10
|
+
ntype : 'button',
|
11
|
+
iconCls: 'fa fa-home',
|
12
|
+
text : 'Hello Siesta'
|
13
|
+
});
|
14
|
+
});
|
15
|
+
|
16
|
+
t.it('Should show isLoading UI', async t => {
|
17
|
+
button = await Neo.worker.App.createNeoInstance({
|
18
|
+
ntype : 'button',
|
19
|
+
iconCls : 'fa fa-home',
|
20
|
+
text : 'Hello Siesta',
|
21
|
+
isLoading : 'Loading...'
|
22
|
+
});
|
23
|
+
|
24
|
+
// Spinner and text exist
|
25
|
+
await t.waitForSelector('button .fa-spinner.fa-spin');
|
26
|
+
t.selectorExists('button .neo-loading-message:contains(Loading...)');
|
27
|
+
|
28
|
+
await Neo.worker.App.setConfigs({ id: button, isLoading : true });
|
29
|
+
|
30
|
+
// Just a spinner now, no text
|
31
|
+
await t.waitForSelectorNotFound('button .neo-loading-message:contains(Loading...)');
|
32
|
+
t.selectorExists('button .fa-spinner');
|
33
|
+
|
34
|
+
await Neo.worker.App.setConfigs({ id: button, isLoading : 'New loading message' });
|
35
|
+
|
36
|
+
await t.waitForSelector('button .neo-loading-message:contains(New loading message)');
|
37
|
+
t.selectorExists('button .fa-spinner.fa-spin');
|
38
|
+
|
39
|
+
await Neo.worker.App.setConfigs({ id: button, isLoading : false });
|
40
|
+
|
41
|
+
// Not loading now
|
42
|
+
await t.waitForSelectorNotFound('button .fa-spinner');
|
16
43
|
});
|
17
44
|
});
|
@@ -1,35 +1,27 @@
|
|
1
1
|
StartTest(t => {
|
2
|
-
t.it('
|
3
|
-
|
4
|
-
|
2
|
+
t.it('Sanity', async t => {
|
3
|
+
Neo.worker.App.createNeoInstance({
|
4
|
+
ntype : 'selectfield',
|
5
|
+
labelPosition: 'inline',
|
6
|
+
labelText : 'US States',
|
7
|
+
labelWidth : 80,
|
8
|
+
width : 300,
|
5
9
|
|
6
|
-
|
7
|
-
|
10
|
+
store : {
|
11
|
+
autoLoad : true,
|
12
|
+
keyProperty: 'abbreviation',
|
13
|
+
url : '../../resources/examples/data/us_states.json',
|
8
14
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
-
}
|
15
|
+
model: {
|
16
|
+
fields: [{
|
17
|
+
name: 'abbreviation',
|
18
|
+
type: 'string'
|
19
|
+
}, {
|
20
|
+
name: 'name',
|
21
|
+
type: 'string'
|
22
|
+
}]
|
31
23
|
}
|
32
|
-
}
|
33
|
-
}
|
24
|
+
}
|
25
|
+
});
|
34
26
|
});
|
35
27
|
});
|
@@ -1,8 +1,38 @@
|
|
1
1
|
const project = new Siesta.Project.Browser();
|
2
2
|
|
3
3
|
project.configure({
|
4
|
-
title
|
5
|
-
isEcmaModule: true
|
4
|
+
title : 'Neo Component Tests',
|
5
|
+
isEcmaModule : true,
|
6
|
+
preload : [{
|
7
|
+
type : 'js',
|
8
|
+
url : '../../src/MicroLoader.mjs',
|
9
|
+
isEcmaModule : true
|
10
|
+
}],
|
11
|
+
testClass : Class('My.Test.Class', {
|
12
|
+
isa : Siesta.Test.Browser,
|
13
|
+
override : {
|
14
|
+
setup(callback, errback) {
|
15
|
+
this.SUPER(function() {
|
16
|
+
// We need to call the startup callback only when we know we are
|
17
|
+
// ready to start testing.
|
18
|
+
const
|
19
|
+
{ global } = this,
|
20
|
+
startupTimer = setInterval(() => {
|
21
|
+
if (global.Neo?.worker?.App && global.Neo.worker.Manager && global.Neo.Main) {
|
22
|
+
clearInterval(startupTimer);
|
23
|
+
|
24
|
+
// TODO: Find what we actually need to wait for
|
25
|
+
setTimeout(callback, 300);
|
26
|
+
}
|
27
|
+
}, 100);
|
28
|
+
}, errback);
|
29
|
+
},
|
30
|
+
async beforeEach() {
|
31
|
+
this.SUPER(...arguments);
|
32
|
+
this.SUPER(t => t.waitFor(50));
|
33
|
+
}
|
34
|
+
}
|
35
|
+
})
|
6
36
|
});
|
7
37
|
|
8
38
|
project.plan(
|