neo.mjs 8.30.0 → 8.31.0
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/BACKERS.md +15 -15
- package/CONTRIBUTING.md +9 -9
- package/apps/ServiceWorker.mjs +2 -2
- package/apps/portal/index.html +1 -1
- package/apps/portal/view/home/FooterContainer.mjs +1 -1
- package/examples/ServiceWorker.mjs +2 -2
- package/examples/grid/animatedRowSorting/MainModel.mjs +40 -0
- package/examples/grid/animatedRowSorting/MainStore.mjs +36 -0
- package/examples/grid/animatedRowSorting/Viewport.mjs +87 -0
- package/examples/grid/animatedRowSorting/app.mjs +6 -0
- package/examples/grid/animatedRowSorting/index.html +11 -0
- package/examples/grid/animatedRowSorting/neo-config.json +8 -0
- package/package.json +3 -3
- package/src/DefaultConfig.mjs +2 -2
- package/src/collection/Base.mjs +2 -1
- package/src/data/Store.mjs +12 -9
- package/src/grid/Container.mjs +43 -67
- package/src/grid/View.mjs +81 -1
- package/src/grid/column/Component.mjs +0 -1
- package/src/grid/header/Button.mjs +1 -2
- package/src/grid/plugin/AnimateRows.mjs +234 -0
- package/src/list/plugin/Animate.mjs +16 -7
- package/src/worker/Manager.mjs +6 -8
package/BACKERS.md
CHANGED
@@ -1,22 +1,22 @@
|
|
1
1
|
# Sponsors & Backers
|
2
2
|
|
3
|
-
Truth to be told, the
|
3
|
+
Truth to be told, the Neo.mjs project is in need of sponsors & backers.
|
4
4
|
|
5
5
|
As an <a href="./LICENSE">MIT-licensed</a> open source project,
|
6
|
-
you can use
|
6
|
+
you can use Neo.mjs for free (as long as you stick to the license & copyright notice).
|
7
7
|
|
8
8
|
Prior to the public release, the project was already at <a href=".github/NEOMJS_HISTORY.md">3720 commits</a>.
|
9
9
|
|
10
|
-
To get
|
10
|
+
To get Neo.mjs to this point, I spent 3 years of my full and unpaid working time.
|
11
11
|
This does not only include many 100h+ weeks and several sleepless nights,
|
12
12
|
but I also burned most of my personal savings to get here.
|
13
13
|
|
14
|
-
I did this, since I truly and deeply believe that
|
14
|
+
I did this, since I truly and deeply believe that Neo.mjs has the potential to start a new era of UI development.
|
15
15
|
|
16
16
|
The current version is just a fraction of a much bigger picture I have in mind.
|
17
17
|
Trust me, it is beautiful :)
|
18
18
|
|
19
|
-
I can definitely say that I did use several other UI frameworks in the past and that
|
19
|
+
I can definitely say that I did use several other UI frameworks in the past and that Neo.mjs boosted my productivity
|
20
20
|
to an entire new level.
|
21
21
|
|
22
22
|
This is something I would love to share with you!
|
@@ -29,35 +29,35 @@ By default, the main Readme will only include sponsors starting at the Gold tier
|
|
29
29
|
(*) Only valid as long as the sponsorship stays active.
|
30
30
|
|
31
31
|
# What will I sponsor?
|
32
|
-
Obviously the continuing of the development for the
|
32
|
+
Obviously the continuing of the development for the Neo.mjs framework.
|
33
33
|
To get the idea of what this means in detail, please take a look at the: <a href=".github/VISION.md">Project Vision</a>.
|
34
34
|
|
35
|
-
# Why should I sponsor
|
35
|
+
# Why should I sponsor Neo.mjs?
|
36
36
|
I am in need to make up for the invested time, which means that going forward,
|
37
37
|
I have to spend a lot of my time on billable projects.
|
38
|
-
Of course I will do my best to continue pushing
|
38
|
+
Of course I will do my best to continue pushing Neo.mjs in my free time.
|
39
39
|
|
40
|
-
With active sponsors I could invest more time into
|
40
|
+
With active sponsors I could invest more time into Neo.mjs,
|
41
41
|
which means that you will get new features, guides and bug-fixes a lot faster.
|
42
42
|
|
43
43
|
# For developers:
|
44
44
|
You can select a Backer tier ($10 / month), which would equal a cup of coffee for me every month (after taxes).
|
45
|
-
This will not cover my living costs (unless
|
45
|
+
This will not cover my living costs (unless Neo.mjs goes viral), but it does have an impact on
|
46
46
|
my motivation for sure.
|
47
47
|
|
48
48
|
# For companies:
|
49
49
|
Starting at the bronze tier (special offer), you will be able to place your company logo on the main repository landing page (Readme.md).
|
50
|
-
This will have a high impact on the
|
51
|
-
to find
|
50
|
+
This will have a high impact on the Neo.mjs community and make it easier for you
|
51
|
+
to find Neo.mjs developers in the future.
|
52
52
|
|
53
53
|
Obviously, sponsoring open source projects in general will increase your companies reputation.
|
54
54
|
|
55
|
-
As soon as the
|
55
|
+
As soon as the Neo.mjs community grows, it will also increase your company visibility in a good way.
|
56
56
|
|
57
|
-
Please make sure to create a feature request ticket inside the
|
57
|
+
Please make sure to create a feature request ticket inside the Neo.mjs <a href="../../issues">Issues Tracker</a>
|
58
58
|
after signing a sponsor program to provide your company logo!
|
59
59
|
|
60
|
-
### Thanks a lot for your support of the
|
60
|
+
### Thanks a lot for your support of the Neo.mjs project!<br/>
|
61
61
|
<a href="https://github.com/sponsors/tobiu">Sponsor Tobias Uhlig</a>
|
62
62
|
|
63
63
|
<br><br>
|
package/CONTRIBUTING.md
CHANGED
@@ -1,15 +1,15 @@
|
|
1
|
-
#
|
2
|
-
We are very excited that you are interested in contributing to
|
1
|
+
# Neo.mjs Contributing Guide
|
2
|
+
We are very excited that you are interested in contributing to Neo.mjs.<br>
|
3
3
|
No worries, you don't need to be a guru, ninja or rockstar to support the project.
|
4
4
|
|
5
5
|
## What you can do to help:
|
6
6
|
|
7
|
-
### 1.
|
7
|
+
### 1. Neo.mjs just got released, so the most important thing right now is to get more eyes on the project.
|
8
8
|
1. Add a star to this repository. Ok, this one was easy. Thank you!
|
9
|
-
2. Tell your friends about
|
9
|
+
2. Tell your friends about Neo.mjs.
|
10
10
|
3. Write blog posts or post on social media (Facebook, LinkedIn, Twitter, etc.)
|
11
11
|
4. Please stick to our <a href=".github/CODE_OF_CONDUCT.md">Code of Conduct</a>
|
12
|
-
5. Interested to see a
|
12
|
+
5. Interested to see a Neo.mjs session at a developer conference? Definitely possible. Just reach out!
|
13
13
|
|
14
14
|
### 2. Use the issues tracker
|
15
15
|
1. In case you got an idea for a new feature
|
@@ -17,9 +17,9 @@ No worries, you don't need to be a guru, ninja or rockstar to support the projec
|
|
17
17
|
1. Ideally, you create a new breaking test inside the tests folder.
|
18
18
|
This saves a lot of time and ensures the bug will stay fixed once the ticket is resolved.
|
19
19
|
3. Please like or comment on current tickets.
|
20
|
-
This is a great help to figure out which tickets are the most important ones for the
|
20
|
+
This is a great help to figure out which tickets are the most important ones for the Neo.mjs community.
|
21
21
|
|
22
|
-
### 3. Contribute to the
|
22
|
+
### 3. Contribute to the Neo.mjs code base
|
23
23
|
1. Please ***always*** create a new issue inside our <a href="../../issues">Issues Tracker</a> first and wait for approval.
|
24
24
|
This ensures that your idea fits the scope of the project and makes it less likely to get a rejected PR.
|
25
25
|
We will do our best to reply to new tickets within 7d max. In case we don't, feel free to bump the ticket.
|
@@ -27,12 +27,12 @@ No worries, you don't need to be a guru, ninja or rockstar to support the projec
|
|
27
27
|
Otherwise multiple contributors could work on the same item in parallel.
|
28
28
|
3. Please make sure that pull requests are always related to an issue inside our <a href="../../issues">Issues Tracker</a>.
|
29
29
|
4. Please create PRs for the dev branch and not for master.
|
30
|
-
5. Please do not copy code from other frameworks / libraries. So far, the entire
|
30
|
+
5. Please do not copy code from other frameworks / libraries. So far, the entire Neo.mjs code base is written from scratch.
|
31
31
|
1. It is simply impossible to verify if contributed code is self-written.
|
32
32
|
2. Adding new npm packages => dependencies / devDependencies is fine, in case there are no licensing conflicts.
|
33
33
|
6. Refer to the <a href="./.github/CODEBASE_OVERVIEW.md">codebase overview</a> to understand how our repository is structured.
|
34
34
|
|
35
|
-
### 4. In case you created a nice app or component using
|
35
|
+
### 4. In case you created a nice app or component using Neo.mjs, please let us know about it.
|
36
36
|
1. We are always interested to feature client projects in blog posts or on social media.
|
37
37
|
|
38
38
|
<br><br>
|
package/apps/ServiceWorker.mjs
CHANGED
package/apps/portal/index.html
CHANGED
@@ -0,0 +1,40 @@
|
|
1
|
+
import Model from '../../../src/data/Model.mjs';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* @class Neo.examples.grid.animatedRowSorting.MainModel
|
5
|
+
* @extends Neo.data.Model
|
6
|
+
*/
|
7
|
+
class MainModel extends Model {
|
8
|
+
static config = {
|
9
|
+
/**
|
10
|
+
* @member {String} className='Neo.examples.grid.animatedRowSorting.MainModel'
|
11
|
+
* @protected
|
12
|
+
*/
|
13
|
+
className: 'Neo.examples.grid.animatedRowSorting.MainModel',
|
14
|
+
/**
|
15
|
+
* @member {Object[]} fields
|
16
|
+
*/
|
17
|
+
fields: [{
|
18
|
+
name: 'country',
|
19
|
+
type: 'String'
|
20
|
+
}, {
|
21
|
+
name: 'firstname',
|
22
|
+
type: 'String'
|
23
|
+
}, {
|
24
|
+
name: 'id',
|
25
|
+
type: 'Int'
|
26
|
+
}, {
|
27
|
+
name: 'lastname',
|
28
|
+
type: 'String'
|
29
|
+
}, {
|
30
|
+
name: 'progress',
|
31
|
+
type: 'Int'
|
32
|
+
}],
|
33
|
+
/**
|
34
|
+
* @member {Boolean} trackModifiedFields=true
|
35
|
+
*/
|
36
|
+
trackModifiedFields: true
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
export default Neo.setupClass(MainModel);
|
@@ -0,0 +1,36 @@
|
|
1
|
+
import Model from './MainModel.mjs';
|
2
|
+
import Store from '../../../src/data/Store.mjs';
|
3
|
+
|
4
|
+
/**
|
5
|
+
* @class Neo.examples.grid.animatedRowSorting.MainStore
|
6
|
+
* @extends Neo.data.Store
|
7
|
+
*/
|
8
|
+
class MainStore extends Store {
|
9
|
+
static config = {
|
10
|
+
className: 'Neo.examples.grid.animatedRowSorting.MainStore',
|
11
|
+
model : Model,
|
12
|
+
|
13
|
+
data: [
|
14
|
+
{country : 'DE', firstname: 'Max', id: 1, lastname : 'Johnson', progress : 50},
|
15
|
+
{country : 'UK', firstname: 'Paul', id: 2, lastname : 'Walker', progress : 10},
|
16
|
+
{country : 'US', firstname: 'Sam', id: 3, lastname : 'Anderson', progress : 90},
|
17
|
+
{country : 'GR', firstname: 'William', id: 4, lastname : 'Wilson', progress : 30},
|
18
|
+
{country : 'AT', firstname: 'Carol', id: 5, lastname : 'Jackson', progress : 70},
|
19
|
+
{country : 'NL', firstname: 'Amanda', id: 6, lastname : 'King', progress : 100},
|
20
|
+
{country : 'FR', firstname: 'Sarah', id: 7, lastname : 'Scott', progress : 35}
|
21
|
+
],
|
22
|
+
|
23
|
+
filters: [{
|
24
|
+
property: 'firstname',
|
25
|
+
operator: 'like',
|
26
|
+
value : null
|
27
|
+
}],
|
28
|
+
|
29
|
+
sorters: [{
|
30
|
+
property : 'id',
|
31
|
+
direction: 'ASC'
|
32
|
+
}]
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
export default Neo.setupClass(MainStore);
|
@@ -0,0 +1,87 @@
|
|
1
|
+
import BaseViewport from '../../../src/container/Viewport.mjs';
|
2
|
+
import GridContainer from '../../../src/grid/Container.mjs';
|
3
|
+
import MainStore from './MainStore.mjs';
|
4
|
+
import NumberField from '../../../src/form/field/Number.mjs';
|
5
|
+
import Toolbar from '../../../src/toolbar/Base.mjs';
|
6
|
+
|
7
|
+
/**
|
8
|
+
* @class Neo.examples.grid.animatedRowSorting.Viewport
|
9
|
+
* @extends Neo.container.Viewport
|
10
|
+
*/
|
11
|
+
class Viewport extends BaseViewport {
|
12
|
+
static config = {
|
13
|
+
/**
|
14
|
+
* @member {String} className='Neo.examples.grid.animatedRowSorting.Viewport'
|
15
|
+
* @protected
|
16
|
+
*/
|
17
|
+
className: 'Neo.examples.grid.animatedRowSorting.Viewport',
|
18
|
+
/**
|
19
|
+
* @member {Object} style={padding:'1em'}
|
20
|
+
*/
|
21
|
+
style: {padding: '1em'},
|
22
|
+
/**
|
23
|
+
* @member {Object[]} items
|
24
|
+
*/
|
25
|
+
items: [{
|
26
|
+
module: Toolbar,
|
27
|
+
flex : 'none',
|
28
|
+
style : {marginBottom: '1em', maxWidth: 'fit-content'},
|
29
|
+
|
30
|
+
items : [{
|
31
|
+
ntype : 'textfield',
|
32
|
+
labelPosition: 'inline',
|
33
|
+
labelText : 'Firstname',
|
34
|
+
listeners : {change: 'up.onFirstnameFieldChange'},
|
35
|
+
width : 120
|
36
|
+
},{
|
37
|
+
module : NumberField,
|
38
|
+
clearable : false,
|
39
|
+
labelPosition: 'inline',
|
40
|
+
labelText : 'Transition Duration',
|
41
|
+
listeners : {change: 'up.onTransitionDurationFieldChange'},
|
42
|
+
maxValue : 1000,
|
43
|
+
minValue : 200,
|
44
|
+
stepSize : 100,
|
45
|
+
style : {marginLeft: '1em'},
|
46
|
+
value : 500,
|
47
|
+
width : 180
|
48
|
+
}]
|
49
|
+
}, {
|
50
|
+
module : GridContainer,
|
51
|
+
reference: 'grid',
|
52
|
+
store : MainStore,
|
53
|
+
|
54
|
+
columnDefaults: {
|
55
|
+
width: 200
|
56
|
+
},
|
57
|
+
|
58
|
+
viewConfig: {
|
59
|
+
animatedRowSorting: true
|
60
|
+
},
|
61
|
+
|
62
|
+
columns: [
|
63
|
+
{dataField: 'id', text: 'Id', width: 100},
|
64
|
+
{dataField: 'firstname', text: 'Firstname'},
|
65
|
+
{dataField: 'lastname', text: 'Lastname'},
|
66
|
+
{dataField: 'progress', text: 'Progress', type: 'progress'},
|
67
|
+
{dataField: 'country', text: 'Country'}
|
68
|
+
]
|
69
|
+
}]
|
70
|
+
}
|
71
|
+
|
72
|
+
/**
|
73
|
+
* @param {Object} data
|
74
|
+
*/
|
75
|
+
onFirstnameFieldChange(data) {
|
76
|
+
this.getReference('grid').store.getFilter('firstname').value = data.value
|
77
|
+
}
|
78
|
+
|
79
|
+
/**
|
80
|
+
* @param {Object} data
|
81
|
+
*/
|
82
|
+
onTransitionDurationFieldChange(data) {
|
83
|
+
this.getReference('grid').view.getPlugin('grid-animate-rows').transitionDuration = data.value
|
84
|
+
}
|
85
|
+
}
|
86
|
+
|
87
|
+
export default Neo.setupClass(Viewport);
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<!DOCTYPE HTML>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
5
|
+
<meta charset="UTF-8">
|
6
|
+
<title>Neo Grid: Animated Row Sorting</title>
|
7
|
+
</head>
|
8
|
+
<body>
|
9
|
+
<script src="../../../src/MicroLoader.mjs" type="module"></script>
|
10
|
+
</body>
|
11
|
+
</html>
|
@@ -0,0 +1,8 @@
|
|
1
|
+
{
|
2
|
+
"appPath" : "examples/grid/animatedRowSorting/app.mjs",
|
3
|
+
"basePath" : "../../../",
|
4
|
+
"environment" : "development",
|
5
|
+
"mainPath" : "./Main.mjs",
|
6
|
+
"mainThreadAddons": ["DragDrop", "Navigator", "ResizeObserver", "Stylesheet"],
|
7
|
+
"themes" : ["neo-theme-dark", "neo-theme-light"]
|
8
|
+
}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "neo.mjs",
|
3
|
-
"version": "8.
|
3
|
+
"version": "8.31.0",
|
4
4
|
"description": "The webworkers driven UI framework",
|
5
5
|
"type": "module",
|
6
6
|
"repository": {
|
@@ -48,7 +48,7 @@
|
|
48
48
|
"homepage": "https://neomjs.com/",
|
49
49
|
"devDependencies": {
|
50
50
|
"@fortawesome/fontawesome-free": "^6.7.2",
|
51
|
-
"autoprefixer": "^10.4.
|
51
|
+
"autoprefixer": "^10.4.21",
|
52
52
|
"chalk": "^5.4.1",
|
53
53
|
"clean-webpack-plugin": "^4.0.0",
|
54
54
|
"commander": "^13.1.0",
|
@@ -56,7 +56,7 @@
|
|
56
56
|
"envinfo": "^7.14.0",
|
57
57
|
"fs-extra": "^11.3.0",
|
58
58
|
"highlightjs-line-numbers.js": "^2.9.0",
|
59
|
-
"inquirer": "^12.4.
|
59
|
+
"inquirer": "^12.4.3",
|
60
60
|
"marked": "^15.0.7",
|
61
61
|
"monaco-editor": "0.50.0",
|
62
62
|
"neo-jsdoc": "1.0.1",
|
package/src/DefaultConfig.mjs
CHANGED
@@ -263,12 +263,12 @@ const DefaultConfig = {
|
|
263
263
|
useVdomWorker: true,
|
264
264
|
/**
|
265
265
|
* buildScripts/injectPackageVersion.mjs will update this value
|
266
|
-
* @default '8.
|
266
|
+
* @default '8.31.0'
|
267
267
|
* @memberOf! module:Neo
|
268
268
|
* @name config.version
|
269
269
|
* @type String
|
270
270
|
*/
|
271
|
-
version: '8.
|
271
|
+
version: '8.31.0'
|
272
272
|
};
|
273
273
|
|
274
274
|
Object.assign(DefaultConfig, {
|
package/src/collection/Base.mjs
CHANGED
@@ -360,7 +360,8 @@ class Collection extends Base {
|
|
360
360
|
* @protected
|
361
361
|
*/
|
362
362
|
cacheUpdate(opts) {
|
363
|
-
|
363
|
+
// disabled for now
|
364
|
+
// console.log('cacheUpdate', opts, this[toAddArray]);
|
364
365
|
return;
|
365
366
|
|
366
367
|
let me = this,
|
package/src/data/Store.mjs
CHANGED
@@ -400,7 +400,7 @@ class Store extends Base {
|
|
400
400
|
onCollectionMutate(opts) {
|
401
401
|
let me = this;
|
402
402
|
|
403
|
-
if (me.
|
403
|
+
if (me.isConstructed && !me.isLoading) {
|
404
404
|
me.fire('load', me.items)
|
405
405
|
}
|
406
406
|
}
|
@@ -411,9 +411,8 @@ class Store extends Base {
|
|
411
411
|
onCollectionSort() {
|
412
412
|
let me = this;
|
413
413
|
|
414
|
-
if (me.
|
415
|
-
//
|
416
|
-
// me.fire('load', me.items);
|
414
|
+
if (me.isConstructed) {
|
415
|
+
//me.fire('load', me.items)
|
417
416
|
}
|
418
417
|
}
|
419
418
|
|
@@ -426,14 +425,18 @@ class Store extends Base {
|
|
426
425
|
let me = this;
|
427
426
|
|
428
427
|
if (me.data) {
|
429
|
-
me.afterSetData(me.data)
|
428
|
+
me.afterSetData(me.data)
|
430
429
|
}
|
431
430
|
|
432
|
-
|
433
|
-
|
431
|
+
// Being constructed does not mean that related afterSetStore() methods got executed
|
432
|
+
// => break the sync flow to ensure potential listeners got applied
|
433
|
+
Promise.resolve().then(() => {
|
434
|
+
if (me.getCount() > 0) {
|
435
|
+
me.fire('load', me.items)
|
436
|
+
} else if (me.autoLoad) {
|
434
437
|
me.load()
|
435
|
-
}
|
436
|
-
}
|
438
|
+
}
|
439
|
+
})
|
437
440
|
}
|
438
441
|
|
439
442
|
/**
|
package/src/grid/Container.mjs
CHANGED
@@ -322,6 +322,29 @@ class GridContainer extends BaseContainer {
|
|
322
322
|
}
|
323
323
|
}
|
324
324
|
|
325
|
+
/**
|
326
|
+
* Triggered after the store config got changed
|
327
|
+
* @param {Number} value
|
328
|
+
* @param {Number} oldValue
|
329
|
+
* @protected
|
330
|
+
*/
|
331
|
+
afterSetStore(value, oldValue) {
|
332
|
+
let me = this,
|
333
|
+
listeners = {
|
334
|
+
filter: me.onStoreFilter,
|
335
|
+
load : me.onStoreLoad,
|
336
|
+
scope : me
|
337
|
+
};
|
338
|
+
|
339
|
+
value ?.on(listeners);
|
340
|
+
oldValue?.un(listeners);
|
341
|
+
|
342
|
+
// in case we dynamically change the store, the view needs to get the new reference
|
343
|
+
if (me.view) {
|
344
|
+
me.view.store = value
|
345
|
+
}
|
346
|
+
}
|
347
|
+
|
325
348
|
/**
|
326
349
|
* Triggered before the columns config gets changed.
|
327
350
|
* @param {Object[]} value
|
@@ -348,36 +371,13 @@ class GridContainer extends BaseContainer {
|
|
348
371
|
|
349
372
|
/**
|
350
373
|
* Triggered before the store config gets changed.
|
351
|
-
* @param {Neo.data.Store} value
|
352
|
-
* @param {Neo.data.Store}
|
374
|
+
* @param {Object|Neo.data.Store|null} value
|
375
|
+
* @param {Neo.data.Store} oldValue
|
353
376
|
* @protected
|
354
377
|
*/
|
355
378
|
beforeSetStore(value, oldValue) {
|
356
|
-
oldValue?.destroy();
|
357
|
-
|
358
379
|
if (value) {
|
359
|
-
|
360
|
-
|
361
|
-
listeners = {
|
362
|
-
filter : me.onStoreFilter,
|
363
|
-
load : me.onStoreLoad,
|
364
|
-
recordChange: me.onStoreRecordChange,
|
365
|
-
scope : me
|
366
|
-
};
|
367
|
-
|
368
|
-
if (value instanceof Store) {
|
369
|
-
value.on(listeners);
|
370
|
-
value.getCount() > 0 && me.onStoreLoad(value.items)
|
371
|
-
} else {
|
372
|
-
value = ClassSystemUtil.beforeSetInstance(value, Store, {
|
373
|
-
listeners
|
374
|
-
})
|
375
|
-
}
|
376
|
-
|
377
|
-
// in case we dynamically change the store, the view needs to get the new reference
|
378
|
-
if (me.view) {
|
379
|
-
me.view.store = value
|
380
|
-
}
|
380
|
+
value = ClassSystemUtil.beforeSetInstance(value, Store)
|
381
381
|
}
|
382
382
|
|
383
383
|
return value
|
@@ -476,24 +476,14 @@ class GridContainer extends BaseContainer {
|
|
476
476
|
})
|
477
477
|
}
|
478
478
|
|
479
|
-
/**
|
480
|
-
* @param {Array} inputData
|
481
|
-
*/
|
482
|
-
createViewData(inputData) {
|
483
|
-
let me = this;
|
484
|
-
|
485
|
-
me.getVdomRoot()['aria-rowcount'] = inputData.length + 2; // 1 based & the header row counts as well
|
486
|
-
me.update();
|
487
|
-
|
488
|
-
me.view.createViewData()
|
489
|
-
}
|
490
|
-
|
491
479
|
/**
|
492
480
|
* @param args
|
493
481
|
*/
|
494
482
|
destroy(...args) {
|
495
483
|
let me = this;
|
496
484
|
|
485
|
+
me.store = null; // remove the listeners
|
486
|
+
|
497
487
|
me.scrollManager.destroy();
|
498
488
|
|
499
489
|
me.mounted && Neo.main.addon.ResizeObserver.unregister({
|
@@ -570,14 +560,14 @@ class GridContainer extends BaseContainer {
|
|
570
560
|
|
571
561
|
me.store.sort(opts);
|
572
562
|
me.removeSortingCss(opts.property);
|
573
|
-
me.onStoreLoad(
|
563
|
+
me.view.onStoreLoad()
|
574
564
|
}
|
575
565
|
|
576
566
|
/**
|
577
567
|
*
|
578
568
|
*/
|
579
569
|
onStoreFilter() {
|
580
|
-
this.
|
570
|
+
this.updateRowCount()
|
581
571
|
}
|
582
572
|
|
583
573
|
/**
|
@@ -587,37 +577,13 @@ class GridContainer extends BaseContainer {
|
|
587
577
|
onStoreLoad(data) {
|
588
578
|
let me = this;
|
589
579
|
|
590
|
-
|
591
|
-
me.createViewData(data);
|
592
|
-
|
593
|
-
me.timeout(50).then(() => {
|
594
|
-
Neo.main.DomAccess.scrollTo({
|
595
|
-
direction: 'top',
|
596
|
-
id : me.view.vdom.id,
|
597
|
-
value : 0
|
598
|
-
})
|
599
|
-
})
|
580
|
+
me.updateRowCount();
|
600
581
|
|
601
|
-
|
602
|
-
|
603
|
-
}
|
582
|
+
if (me.store.sorters?.length < 1) {
|
583
|
+
me.removeSortingCss()
|
604
584
|
}
|
605
585
|
}
|
606
586
|
|
607
|
-
/**
|
608
|
-
* Gets triggered after changing the value of a record field.
|
609
|
-
* E.g. myRecord.foo = 'bar';
|
610
|
-
* @param {Object} opts
|
611
|
-
* @param {String} opts.field The name of the field which got changed
|
612
|
-
* @param {Neo.data.Model} opts.model The model instance of the changed record
|
613
|
-
* @param {*} opts.oldValue
|
614
|
-
* @param {Object} opts.record
|
615
|
-
* @param {*} opts.value
|
616
|
-
*/
|
617
|
-
onStoreRecordChange(opts) {
|
618
|
-
this.view.onStoreRecordChange(opts)
|
619
|
-
}
|
620
|
-
|
621
587
|
/**
|
622
588
|
* @param {Boolean} silent=false
|
623
589
|
* @returns {Promise<void>}
|
@@ -701,6 +667,16 @@ class GridContainer extends BaseContainer {
|
|
701
667
|
})
|
702
668
|
}
|
703
669
|
}
|
670
|
+
|
671
|
+
/**
|
672
|
+
* @param {Boolean} silent=false
|
673
|
+
*/
|
674
|
+
updateRowCount(silent=false) {
|
675
|
+
let me = this;
|
676
|
+
|
677
|
+
this.getVdomRoot()['aria-rowcount'] = me.store.getCount() + 2; // 1 based & the header row counts as well
|
678
|
+
!silent && this.update()
|
679
|
+
}
|
704
680
|
}
|
705
681
|
|
706
682
|
export default Neo.setupClass(GridContainer);
|
package/src/grid/View.mjs
CHANGED
@@ -21,6 +21,10 @@ class GridView extends Component {
|
|
21
21
|
* @protected
|
22
22
|
*/
|
23
23
|
ntype: 'grid-view',
|
24
|
+
/**
|
25
|
+
* @member {Boolean} animatedRowSorting_=false
|
26
|
+
*/
|
27
|
+
animatedRowSorting_: false,
|
24
28
|
/**
|
25
29
|
* Internal flag. Gets calculated when mounting the grid.Container
|
26
30
|
* @member {Number} availableHeight_=0
|
@@ -93,6 +97,11 @@ class GridView extends Component {
|
|
93
97
|
* @protected
|
94
98
|
*/
|
95
99
|
mountedRows: [0, 0],
|
100
|
+
/**
|
101
|
+
* Optional config values for Neo.grid.plugin.AnimateRows
|
102
|
+
* @member {Object} pluginAnimateRowsConfig=null
|
103
|
+
*/
|
104
|
+
pluginAnimateRowsConfig: null,
|
96
105
|
/**
|
97
106
|
* @member {String} role='rowgroup'
|
98
107
|
*/
|
@@ -204,6 +213,30 @@ class GridView extends Component {
|
|
204
213
|
}])
|
205
214
|
}
|
206
215
|
|
216
|
+
/**
|
217
|
+
* Triggered after the animatedRowSorting config got changed
|
218
|
+
* @param {Boolean} value
|
219
|
+
* @param {Boolean} oldValue
|
220
|
+
* @protected
|
221
|
+
*/
|
222
|
+
afterSetAnimatedRowSorting(value, oldValue) {
|
223
|
+
if (value && !this.getPlugin('grid-animate-rows')) {
|
224
|
+
import('./plugin/AnimateRows.mjs').then(module => {
|
225
|
+
let me = this,
|
226
|
+
plugins = me.plugins || [];
|
227
|
+
|
228
|
+
plugins.push({
|
229
|
+
module : module.default,
|
230
|
+
appName : me.appName,
|
231
|
+
windowId: me.windowId,
|
232
|
+
...me.pluginAnimateRowsConfig
|
233
|
+
});
|
234
|
+
|
235
|
+
me.plugins = plugins
|
236
|
+
})
|
237
|
+
}
|
238
|
+
}
|
239
|
+
|
207
240
|
/**
|
208
241
|
* Triggered after the availableHeight config got changed
|
209
242
|
* @param {Number} value
|
@@ -364,6 +397,25 @@ class GridView extends Component {
|
|
364
397
|
oldValue !== undefined && this.createViewData()
|
365
398
|
}
|
366
399
|
|
400
|
+
/**
|
401
|
+
* Triggered after the store config got changed
|
402
|
+
* @param {Number} value
|
403
|
+
* @param {Number} oldValue
|
404
|
+
* @protected
|
405
|
+
*/
|
406
|
+
afterSetStore(value, oldValue) {
|
407
|
+
let me = this,
|
408
|
+
listeners = {
|
409
|
+
filter : me.onStoreFilter,
|
410
|
+
load : me.onStoreLoad,
|
411
|
+
recordChange: me.onStoreRecordChange,
|
412
|
+
scope : me
|
413
|
+
};
|
414
|
+
|
415
|
+
oldValue?.un(listeners);
|
416
|
+
value ?.on(listeners);
|
417
|
+
}
|
418
|
+
|
367
419
|
/**
|
368
420
|
* @param {Object} data
|
369
421
|
* @param {String} [data.cellId]
|
@@ -608,7 +660,8 @@ class GridView extends Component {
|
|
608
660
|
* @param args
|
609
661
|
*/
|
610
662
|
destroy(...args) {
|
611
|
-
this.store = null;
|
663
|
+
this.store = null; // remove the listeners
|
664
|
+
|
612
665
|
super.destroy(...args)
|
613
666
|
}
|
614
667
|
|
@@ -849,6 +902,33 @@ class GridView extends Component {
|
|
849
902
|
this.fireRowEvent(data, 'rowDoubleClick')
|
850
903
|
}
|
851
904
|
|
905
|
+
/**
|
906
|
+
*
|
907
|
+
*/
|
908
|
+
onStoreFilter() {
|
909
|
+
this.onStoreLoad()
|
910
|
+
}
|
911
|
+
|
912
|
+
/**
|
913
|
+
* @param {Object[]} data
|
914
|
+
* @protected
|
915
|
+
*/
|
916
|
+
onStoreLoad(data) {
|
917
|
+
let me = this;
|
918
|
+
|
919
|
+
me.createViewData();
|
920
|
+
|
921
|
+
if (me.mounted) {
|
922
|
+
me.timeout(50).then(() => {
|
923
|
+
Neo.main.DomAccess.scrollTo({
|
924
|
+
direction: 'top',
|
925
|
+
id : me.vdom.id,
|
926
|
+
value : 0
|
927
|
+
})
|
928
|
+
})
|
929
|
+
}
|
930
|
+
}
|
931
|
+
|
852
932
|
/**
|
853
933
|
* @param {Object} data
|
854
934
|
* @param {Object[]} data.fields Each field object contains the keys: name, oldValue, value
|
@@ -176,7 +176,7 @@ class Button extends BaseButton {
|
|
176
176
|
let me = this,
|
177
177
|
{cls} = me;
|
178
178
|
|
179
|
-
if (value
|
179
|
+
if (value) {
|
180
180
|
NeoArray.remove(cls, 'neo-sort-hidden');
|
181
181
|
|
182
182
|
me.addDomListeners({
|
@@ -211,7 +211,6 @@ class Button extends BaseButton {
|
|
211
211
|
*/
|
212
212
|
destroy(...args) {
|
213
213
|
this.filterField?.destroy();
|
214
|
-
|
215
214
|
super.destroy(...args)
|
216
215
|
}
|
217
216
|
|
@@ -0,0 +1,234 @@
|
|
1
|
+
import Base from '../../plugin/Base.mjs';
|
2
|
+
import CssUtil from '../../util/Css.mjs';
|
3
|
+
import NeoArray from '../../util/Array.mjs';
|
4
|
+
|
5
|
+
/**
|
6
|
+
* @class Neo.grid.plugin.AnimateRows
|
7
|
+
* @extends Neo.plugin.Base
|
8
|
+
*/
|
9
|
+
class AnimateRows extends Base {
|
10
|
+
/**
|
11
|
+
* Valid values for transitionEasing
|
12
|
+
* @member {String[]} transitionEasings=['ease','ease-in','ease-out','ease-in-out','linear']
|
13
|
+
* @protected
|
14
|
+
* @static
|
15
|
+
*/
|
16
|
+
static transitionEasings = ['ease', 'ease-in', 'ease-out', 'ease-in-out', 'linear']
|
17
|
+
|
18
|
+
static config = {
|
19
|
+
/**
|
20
|
+
* @member {String} className='Neo.grid.plugin.AnimateRows'
|
21
|
+
* @protected
|
22
|
+
*/
|
23
|
+
className: 'Neo.grid.plugin.AnimateRows',
|
24
|
+
/**
|
25
|
+
* @member {String} ntype='plugin-grid-animate-rows'
|
26
|
+
* @protected
|
27
|
+
*/
|
28
|
+
ntype: 'plugin-grid-animate-rows',
|
29
|
+
/**
|
30
|
+
* Time in ms. Please ensure to match the CSS based value, in case you change the default.
|
31
|
+
* @member {Number} transitionDuration_=500
|
32
|
+
*/
|
33
|
+
transitionDuration_: 500,
|
34
|
+
/**
|
35
|
+
* The easing used for fadeIn, fadeOut and position changes.
|
36
|
+
* Valid values: 'ease','ease-in','ease-out','ease-in-out','linear'
|
37
|
+
* @member {String} transitionEasing_='ease-out'
|
38
|
+
*/
|
39
|
+
transitionEasing_: 'ease-out'
|
40
|
+
}
|
41
|
+
|
42
|
+
/**
|
43
|
+
* The id of the setTimeout() call which gets triggered after a transition is done.
|
44
|
+
* @member {Number|null} transitionTimeoutId=null
|
45
|
+
* @protected
|
46
|
+
*/
|
47
|
+
transitionTimeoutId = null
|
48
|
+
|
49
|
+
/**
|
50
|
+
* @param {Object} config
|
51
|
+
*/
|
52
|
+
construct(config) {
|
53
|
+
super.construct(config);
|
54
|
+
|
55
|
+
let me = this,
|
56
|
+
{owner} = me,
|
57
|
+
{store} = owner;
|
58
|
+
|
59
|
+
// Remove the previous view listeners
|
60
|
+
owner.store = null;
|
61
|
+
|
62
|
+
owner.onStoreFilter = me.onStoreFilter.bind(me);
|
63
|
+
owner.onStoreLoad = me.onStoreLoad .bind(me);
|
64
|
+
|
65
|
+
// Add the re-bound listeners
|
66
|
+
owner.store = store;
|
67
|
+
|
68
|
+
me.updateTransitionDetails()
|
69
|
+
}
|
70
|
+
|
71
|
+
/**
|
72
|
+
* Triggered after the transitionDuration config got changed.
|
73
|
+
* @param {Number} value
|
74
|
+
* @param {Number} oldValue
|
75
|
+
* @protected
|
76
|
+
*/
|
77
|
+
afterSetTransitionDuration(value, oldValue) {
|
78
|
+
this.isConstructed && this.updateTransitionDetails(Neo.isNumber(oldValue))
|
79
|
+
}
|
80
|
+
|
81
|
+
/**
|
82
|
+
* Triggered after the transitionEasing config got changed.
|
83
|
+
* @param {String} value
|
84
|
+
* @param {String} oldValue
|
85
|
+
* @protected
|
86
|
+
*/
|
87
|
+
afterSetTransitionEasing(value, oldValue) {
|
88
|
+
this.isConstructed && this.updateTransitionDetails(!!oldValue)
|
89
|
+
}
|
90
|
+
|
91
|
+
/**
|
92
|
+
* Triggered before the transitionEasing config gets changed
|
93
|
+
* @param {String} value
|
94
|
+
* @param {String} oldValue
|
95
|
+
* @protected
|
96
|
+
*/
|
97
|
+
beforeSetTransitionEasing(value, oldValue) {
|
98
|
+
return this.beforeSetEnumValue(value, oldValue, 'transitionEasing')
|
99
|
+
}
|
100
|
+
|
101
|
+
/**
|
102
|
+
* @param {Object} args
|
103
|
+
*/
|
104
|
+
destroy(...args) {
|
105
|
+
CssUtil.deleteRules(this.appName, `#${this.owner.id} .neo-grid-row`);
|
106
|
+
super.destroy(...args)
|
107
|
+
}
|
108
|
+
|
109
|
+
/**
|
110
|
+
* @param {Object} data
|
111
|
+
* @param {Boolean} data.isFiltered
|
112
|
+
* @param {Object[]} data.items
|
113
|
+
* @param {Object[]} data.oldItems
|
114
|
+
* @param {Neo.data.Store} data.scope
|
115
|
+
*/
|
116
|
+
onStoreFilter(data) {
|
117
|
+
this.updateView()
|
118
|
+
}
|
119
|
+
|
120
|
+
/**
|
121
|
+
* @param {Object[]} data
|
122
|
+
* @protected
|
123
|
+
*/
|
124
|
+
onStoreLoad(data) {
|
125
|
+
this.updateView()
|
126
|
+
}
|
127
|
+
|
128
|
+
/**
|
129
|
+
* We do not want to apply the style to each list item itself,
|
130
|
+
* so we are using Neo.util.Css
|
131
|
+
* @param {Boolean} deleteRule=false
|
132
|
+
* @protected
|
133
|
+
*/
|
134
|
+
async updateTransitionDetails(deleteRule=false) {
|
135
|
+
let me = this,
|
136
|
+
duration = me.transitionDuration,
|
137
|
+
easing = me.transitionEasing,
|
138
|
+
{id} = me.owner;
|
139
|
+
|
140
|
+
if (deleteRule) {
|
141
|
+
await CssUtil.deleteRules(me.appName, `#${id} .neo-grid-row`)
|
142
|
+
}
|
143
|
+
|
144
|
+
CssUtil.insertRules(me.appName, [
|
145
|
+
`#${id} .neo-grid-row {`,
|
146
|
+
'transition:',
|
147
|
+
`background-color ${duration}ms ${easing},`,
|
148
|
+
`opacity ${duration}ms ${easing},`,
|
149
|
+
`transform ${duration}ms ${easing}`,
|
150
|
+
'}'
|
151
|
+
].join(''))
|
152
|
+
}
|
153
|
+
|
154
|
+
/**
|
155
|
+
*
|
156
|
+
*/
|
157
|
+
updateView() {
|
158
|
+
let me = this,
|
159
|
+
{owner} = me,
|
160
|
+
{mountedRows} = owner,
|
161
|
+
addedRows = [],
|
162
|
+
hasChange = false,
|
163
|
+
map = {},
|
164
|
+
rowsContainer = owner.getVdomRoot().cn,
|
165
|
+
id, mapItem, record, row, rowIndex, transform;
|
166
|
+
|
167
|
+
rowsContainer.forEach(row => {
|
168
|
+
map[row.id] = row
|
169
|
+
});
|
170
|
+
|
171
|
+
// Creates the new start & end indexes inside mountedRows
|
172
|
+
owner.updateMountedAndVisibleRows();
|
173
|
+
|
174
|
+
for (rowIndex=mountedRows[0]; rowIndex < mountedRows[1]; rowIndex++) {
|
175
|
+
record = owner.store.getAt(rowIndex);
|
176
|
+
id = owner.getRowId(record, rowIndex)
|
177
|
+
mapItem = map[id];
|
178
|
+
|
179
|
+
if (mapItem) {
|
180
|
+
// Inside the map (previous state) & vdom => move OP
|
181
|
+
transform = `translate(0px, ${rowIndex * owner.rowHeight}px)`;
|
182
|
+
|
183
|
+
if (mapItem.style.transform !== transform) {
|
184
|
+
mapItem.style.opacity = .9; // slightly less than 1 to see visual overlays while moving
|
185
|
+
mapItem.style.transform = transform;
|
186
|
+
NeoArray.toggle(mapItem.cls, 'neo-even', rowIndex % 2 !== 0);
|
187
|
+
hasChange = true
|
188
|
+
}
|
189
|
+
|
190
|
+
delete map[id]
|
191
|
+
} else {
|
192
|
+
// Inside the vdom, but not the map => insert OP
|
193
|
+
row = owner.createRow({record, rowIndex});
|
194
|
+
|
195
|
+
row.style.opacity = 0;
|
196
|
+
|
197
|
+
addedRows .push(row);
|
198
|
+
rowsContainer.push(row);
|
199
|
+
|
200
|
+
owner.updateDepth = -1; // Added rows might contain components
|
201
|
+
hasChange = true
|
202
|
+
}
|
203
|
+
}
|
204
|
+
|
205
|
+
// Only rows which need to get removed are still inside the map
|
206
|
+
Object.values(map).forEach(row => {
|
207
|
+
row.style.opacity = 0;
|
208
|
+
hasChange = true
|
209
|
+
});
|
210
|
+
|
211
|
+
if (hasChange) {
|
212
|
+
clearTimeout(me.transitionTimeoutId);
|
213
|
+
|
214
|
+
owner.promiseUpdate().then(() => {
|
215
|
+
if (addedRows.length > 0) {
|
216
|
+
// Added rows need a 2nd DOM update to change the opacity from 0 to 1.
|
217
|
+
// If we added them with 1 directly, there would not be a fade-in transition.
|
218
|
+
addedRows.forEach(row => {
|
219
|
+
row.style.opacity = 1
|
220
|
+
});
|
221
|
+
|
222
|
+
owner.update()
|
223
|
+
}
|
224
|
+
});
|
225
|
+
|
226
|
+
me.transitionTimeoutId = setTimeout(() => {
|
227
|
+
me.transitionTimeoutId = null;
|
228
|
+
owner.createViewData()
|
229
|
+
}, me.transitionDuration)
|
230
|
+
}
|
231
|
+
}
|
232
|
+
}
|
233
|
+
|
234
|
+
export default Neo.setupClass(AnimateRows);
|
@@ -80,7 +80,7 @@ class Animate extends Base {
|
|
80
80
|
owner.onStoreFilter = me.onStoreFilter.bind(me);
|
81
81
|
owner.onStoreSort = me.onStoreSort .bind(me);
|
82
82
|
|
83
|
-
this.updateTransitionDetails(
|
83
|
+
this.updateTransitionDetails()
|
84
84
|
}
|
85
85
|
|
86
86
|
/**
|
@@ -152,6 +152,14 @@ class Animate extends Base {
|
|
152
152
|
return item
|
153
153
|
}
|
154
154
|
|
155
|
+
/**
|
156
|
+
* @param {Object} args
|
157
|
+
*/
|
158
|
+
destroy(...args) {
|
159
|
+
CssUtil.deleteRules(this.appName, `#${this.owner.id} .neo-list-item`);
|
160
|
+
super.destroy(...args)
|
161
|
+
}
|
162
|
+
|
155
163
|
/**
|
156
164
|
* @param {Object} record
|
157
165
|
* @param {Number} index
|
@@ -412,18 +420,19 @@ class Animate extends Base {
|
|
412
420
|
}
|
413
421
|
|
414
422
|
/**
|
415
|
-
* We do not want to apply the style to each list item itself,
|
416
|
-
*
|
417
|
-
* @param {Boolean} deleteRule
|
423
|
+
* We do not want to apply the style to each list item itself, so we are using Neo.util.Css
|
424
|
+
* @param {Boolean} deleteRule=false
|
418
425
|
* @protected
|
419
426
|
*/
|
420
|
-
updateTransitionDetails(deleteRule) {
|
427
|
+
async updateTransitionDetails(deleteRule=false) {
|
421
428
|
let me = this,
|
422
429
|
duration = me.transitionDuration,
|
423
430
|
easing = me.transitionEasing,
|
424
|
-
id
|
431
|
+
{id} = me.owner;
|
425
432
|
|
426
|
-
|
433
|
+
if (deleteRule) {
|
434
|
+
await CssUtil.deleteRules(me.appName, `#${id} .neo-list-item`)
|
435
|
+
}
|
427
436
|
|
428
437
|
CssUtil.insertRules(me.appName, [
|
429
438
|
`#${id} .neo-list-item {`,
|
package/src/worker/Manager.mjs
CHANGED
@@ -295,18 +295,16 @@ class Manager extends Base {
|
|
295
295
|
|
296
296
|
/**
|
297
297
|
* Handler method for worker message events
|
298
|
-
* @param {Object}
|
298
|
+
* @param {Object} event
|
299
299
|
*/
|
300
|
-
onWorkerMessage(
|
300
|
+
onWorkerMessage(event) {
|
301
301
|
let me = this,
|
302
|
-
{data} =
|
302
|
+
{data} = event,
|
303
303
|
transfer = null,
|
304
304
|
promise;
|
305
305
|
|
306
306
|
const {action, destination: dest, replyId} = data;
|
307
307
|
|
308
|
-
// console.log('Main: Incoming Worker message: ' + data.origin + ':' + action, data);
|
309
|
-
|
310
308
|
me.fire('message:'+action, data);
|
311
309
|
|
312
310
|
if (action === 'reply') {
|
@@ -317,14 +315,14 @@ class Manager extends Base {
|
|
317
315
|
data.data.autoMount && me.fire('automount', data);
|
318
316
|
data.data.updateVdom && me.fire('updateVdom', data);
|
319
317
|
|
320
|
-
//
|
321
|
-
//
|
318
|
+
// We want to delay the message until the rendering queue has processed it
|
319
|
+
// See: https://github.com/neomjs/neo/issues/2864
|
322
320
|
me.promiseForwardMessage(data).then(msgData => {
|
323
321
|
me.sendMessage(msgData.destination, msgData)
|
324
322
|
})
|
325
323
|
}
|
326
324
|
} else {
|
327
|
-
if (
|
325
|
+
if (dest === 'main') {
|
328
326
|
data = data.data
|
329
327
|
}
|
330
328
|
|