neo.mjs 8.32.0 → 8.34.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/apps/ServiceWorker.mjs +2 -2
- package/apps/finance/app.mjs +6 -0
- package/apps/finance/index.html +12 -0
- package/apps/finance/model/Company.mjs +37 -0
- package/apps/finance/neo-config.json +7 -0
- package/apps/finance/resources/data/companies.json +103 -0
- package/apps/finance/resources/images/neo_logo_favicon.svg +16 -0
- package/apps/finance/store/Companies.mjs +37 -0
- package/apps/finance/view/GridContainer.mjs +59 -0
- package/apps/finance/view/Viewport.mjs +43 -0
- package/apps/finance/view/ViewportController.mjs +50 -0
- package/apps/finance/view/ViewportStateProvider.mjs +32 -0
- package/apps/portal/index.html +1 -1
- package/apps/portal/view/home/FooterContainer.mjs +1 -1
- package/examples/ServiceWorker.mjs +2 -2
- package/package.json +1 -1
- package/resources/scss/src/grid/column/AnimatedChange.scss +1 -1
- package/resources/scss/src/grid/column/AnimatedCurrency.scss +25 -0
- package/src/DefaultConfig.mjs +2 -2
- package/src/component/MagicMoveText.mjs +13 -6
- package/src/grid/Container.mjs +7 -5
- package/src/grid/View.mjs +0 -4
- package/src/grid/column/AnimatedCurrency.mjs +87 -0
- package/src/grid/column/Base.mjs +6 -2
- package/src/grid/column/Currency.mjs +72 -0
- package/src/grid/column/_export.mjs +8 -6
- package/src/main/addon/ServiceWorker.mjs +29 -19
- package/src/manager/Component.mjs +0 -2
- package/src/worker/App.mjs +0 -11
- package/src/worker/Manager.mjs +4 -0
- package/src/worker/ServiceBase.mjs +40 -30
package/apps/ServiceWorker.mjs
CHANGED
@@ -0,0 +1,12 @@
|
|
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.mjs Finance Dashboard</title>
|
7
|
+
<link rel="icon" href="./resources/images/neo_logo_favicon.svg">
|
8
|
+
</head>
|
9
|
+
<body>
|
10
|
+
<script src="../../src/MicroLoader.mjs" type="module"></script>
|
11
|
+
</body>
|
12
|
+
</html>
|
@@ -0,0 +1,37 @@
|
|
1
|
+
import Model from '../../../src/data/Model.mjs';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* @class Finance.model.Company
|
5
|
+
* @extends Neo.data.Model
|
6
|
+
*/
|
7
|
+
class Company extends Model {
|
8
|
+
static config = {
|
9
|
+
/**
|
10
|
+
* @member {String} className='Finance.model.Company'
|
11
|
+
* @protected
|
12
|
+
*/
|
13
|
+
className: 'Finance.model.Company',
|
14
|
+
/**
|
15
|
+
* @member {Object[]} fields
|
16
|
+
*/
|
17
|
+
fields: [{
|
18
|
+
name : 'change',
|
19
|
+
defaultValue: null,
|
20
|
+
type : 'Float'
|
21
|
+
}, {
|
22
|
+
name: 'name',
|
23
|
+
type: 'String'
|
24
|
+
}, {
|
25
|
+
name: 'sector',
|
26
|
+
type: 'String'
|
27
|
+
}, {
|
28
|
+
name: 'symbol',
|
29
|
+
type: 'String'
|
30
|
+
}, {
|
31
|
+
name: 'value',
|
32
|
+
type: 'Float'
|
33
|
+
}]
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
export default Neo.setupClass(Company);
|
@@ -0,0 +1,103 @@
|
|
1
|
+
[
|
2
|
+
{"symbol": "ADBE", "name": "Adobe Inc.", "sector": "Information Technology"},
|
3
|
+
{"symbol": "AMD", "name": "Advanced Micro Devices", "sector": "Information Technology"},
|
4
|
+
{"symbol": "ABNB", "name": "Airbnb", "sector": "Consumer Discretionary"},
|
5
|
+
{"symbol": "GOOGL", "name": "Alphabet Inc. (Class A)", "sector": "Communication Services"},
|
6
|
+
{"symbol": "GOOG", "name": "Alphabet Inc. (Class C)", "sector": "Communication Services"},
|
7
|
+
{"symbol": "AMZN", "name": "Amazon", "sector": "Consumer Discretionary"},
|
8
|
+
{"symbol": "AEP", "name": "American Electric Power", "sector": "Utilities"},
|
9
|
+
{"symbol": "AMGN", "name": "Amgen", "sector": "Health Care"},
|
10
|
+
{"symbol": "ADI", "name": "Analog Devices", "sector": "Information Technology"},
|
11
|
+
{"symbol": "ANSS", "name": "Ansys", "sector": "Information Technology"},
|
12
|
+
{"symbol": "AAPL", "name": "Apple Inc.", "sector": "Information Technology"},
|
13
|
+
{"symbol": "AMAT", "name": "Applied Materials", "sector": "Information Technology"},
|
14
|
+
{"symbol": "APP", "name": "AppLovin", "sector": "Information Technology"},
|
15
|
+
{"symbol": "ARM", "name": "Arm Holdings", "sector": "Information Technology"},
|
16
|
+
{"symbol": "ASML", "name": "ASML Holding", "sector": "Information Technology"},
|
17
|
+
{"symbol": "AZN", "name": "AstraZeneca", "sector": "Health Care"},
|
18
|
+
{"symbol": "TEAM", "name": "Atlassian", "sector": "Information Technology"},
|
19
|
+
{"symbol": "ADSK", "name": "Autodesk", "sector": "Information Technology"},
|
20
|
+
{"symbol": "ADP", "name": "Automatic Data Processing", "sector": "Industrials"},
|
21
|
+
{"symbol": "AXON", "name": "Axon Enterprise", "sector": "Industrials"},
|
22
|
+
{"symbol": "BKR", "name": "Baker Hughes", "sector": "Energy"},
|
23
|
+
{"symbol": "BIIB", "name": "Biogen", "sector": "Health Care"},
|
24
|
+
{"symbol": "BKNG", "name": "Booking Holdings", "sector": "Consumer Discretionary"},
|
25
|
+
{"symbol": "AVGO", "name": "Broadcom", "sector": "Information Technology"},
|
26
|
+
{"symbol": "CDNS", "name": "Cadence Design Systems", "sector": "Information Technology"},
|
27
|
+
{"symbol": "CDW", "name": "CDW Corporation", "sector": "Information Technology"},
|
28
|
+
{"symbol": "CHTR", "name": "Charter Communications", "sector": "Communication Services"},
|
29
|
+
{"symbol": "CTAS", "name": "Cintas", "sector": "Industrials"},
|
30
|
+
{"symbol": "CSCO", "name": "Cisco", "sector": "Information Technology"},
|
31
|
+
{"symbol": "CCEP", "name": "Coca-Cola Europacific Partners", "sector": "Consumer Staples"},
|
32
|
+
{"symbol": "CTSH", "name": "Cognizant", "sector": "Information Technology"},
|
33
|
+
{"symbol": "CMCSA", "name": "Comcast", "sector": "Communication Services"},
|
34
|
+
{"symbol": "CEG", "name": "Constellation Energy", "sector": "Utilities"},
|
35
|
+
{"symbol": "CPRT", "name": "Copart", "sector": "Industrials"},
|
36
|
+
{"symbol": "CSGP", "name": "CoStar Group", "sector": "Real Estate"},
|
37
|
+
{"symbol": "COST", "name": "Costco", "sector": "Consumer Staples"},
|
38
|
+
{"symbol": "CRWD", "name": "CrowdStrike", "sector": "Information Technology"},
|
39
|
+
{"symbol": "CSX", "name": "CSX Corporation", "sector": "Industrials"},
|
40
|
+
{"symbol": "DDOG", "name": "Datadog", "sector": "Information Technology"},
|
41
|
+
{"symbol": "DXCM", "name": "DexCom", "sector": "Health Care"},
|
42
|
+
{"symbol": "FANG", "name": "Diamondback Energy", "sector": "Energy"},
|
43
|
+
{"symbol": "DASH", "name": "DoorDash", "sector": "Consumer Discretionary"},
|
44
|
+
{"symbol": "EA", "name": "Electronic Arts", "sector": "Communication Services"},
|
45
|
+
{"symbol": "EXC", "name": "Exelon", "sector": "Utilities"},
|
46
|
+
{"symbol": "FAST", "name": "Fastenal", "sector": "Industrials"},
|
47
|
+
{"symbol": "FTNT", "name": "Fortinet", "sector": "Information Technology"},
|
48
|
+
{"symbol": "GEHC", "name": "GE HealthCare", "sector": "Health Care"},
|
49
|
+
{"symbol": "GILD", "name": "Gilead Sciences", "sector": "Health Care"},
|
50
|
+
{"symbol": "GFS", "name": "GlobalFoundries", "sector": "Information Technology"},
|
51
|
+
{"symbol": "HON", "name": "Honeywell", "sector": "Industrials"},
|
52
|
+
{"symbol": "IDXX", "name": "Idexx Laboratories", "sector": "Health Care"},
|
53
|
+
{"symbol": "INTC", "name": "Intel", "sector": "Information Technology"},
|
54
|
+
{"symbol": "INTU", "name": "Intuit", "sector": "Information Technology"},
|
55
|
+
{"symbol": "ISRG", "name": "Intuitive Surgical", "sector": "Health Care"},
|
56
|
+
{"symbol": "KDP", "name": "Keurig Dr Pepper", "sector": "Keurig Dr Pepper"},
|
57
|
+
{"symbol": "KLAC", "name": "KLA Corporation", "sector": "Information Technology"},
|
58
|
+
{"symbol": "KHC", "name": "Kraft Heinz", "sector": "Consumer Staples"},
|
59
|
+
{"symbol": "LRCX", "name": "Lam Research", "sector": "Information Technology"},
|
60
|
+
{"symbol": "LIN", "name": "Linde plc", "sector": "Materials"},
|
61
|
+
{"symbol": "LULU", "name": "Lululemon Athletica", "sector": "Consumer Discretionary"},
|
62
|
+
{"symbol": "MAR", "name": "Marriott International", "sector": "Consumer Discretionary"},
|
63
|
+
{"symbol": "MRVL", "name": "Marvell Technology", "sector": "Information Technology"},
|
64
|
+
{"symbol": "MELI", "name": "MercadoLibre", "sector": "Consumer Discretionary"},
|
65
|
+
{"symbol": "META", "name": "Meta Platforms", "sector": "Communication Services"},
|
66
|
+
{"symbol": "MCHP", "name": "Microchip Technology", "sector": "Information Technology"},
|
67
|
+
{"symbol": "MU", "name": "Micron Technology", "sector": "Information Technology"},
|
68
|
+
{"symbol": "MSFT", "name": "Microsoft", "sector": "Information Technology"},
|
69
|
+
{"symbol": "MSTR", "name": "MicroStrategy", "sector": "Information Technology"},
|
70
|
+
{"symbol": "MDLZ", "name": "Mondelez International", "sector": "Consumer Staples"},
|
71
|
+
{"symbol": "MDB", "name": "MongoDB Inc.", "sector": "Information Technology"},
|
72
|
+
{"symbol": "MNST", "name": "Monster Beverage", "sector": "Consumer Staples"},
|
73
|
+
{"symbol": "NFLX", "name": "Netflix, Inc.", "sector": "Communication Services"},
|
74
|
+
{"symbol": "NVDA", "name": "Nvidia", "sector": "Information Technology"},
|
75
|
+
{"symbol": "NXPI", "name": "NXP Semiconductors", "sector": "Information Technology"},
|
76
|
+
{"symbol": "ORLY", "name": "O'Reilly Automotive", "sector": "Consumer Discretionary"},
|
77
|
+
{"symbol": "ODFL", "name": "Old Dominion Freight Line", "sector": "Industrials"},
|
78
|
+
{"symbol": "ON", "name": "Onsemi", "sector": "Information Technology"},
|
79
|
+
{"symbol": "PCAR", "name": "Paccar", "sector": "Industrials"},
|
80
|
+
{"symbol": "PLTR", "name": "Palantir Technologies", "sector": "Information Technology"},
|
81
|
+
{"symbol": "PANW", "name": "Palo Alto Networks", "sector": "Information Technology"},
|
82
|
+
{"symbol": "PAYX", "name": "Paychex", "sector": "Industrials"},
|
83
|
+
{"symbol": "PYPL", "name": "PayPal", "sector": "Financials"},
|
84
|
+
{"symbol": "PDD", "name": "PDD Holdings", "sector": "Consumer Discretionary"},
|
85
|
+
{"symbol": "PEP", "name": "PepsiCo", "sector": "Consumer Staples"},
|
86
|
+
{"symbol": "QCOM", "name": "Qualcomm", "sector": "Information Technology"},
|
87
|
+
{"symbol": "REGN", "name": "Regeneron Pharmaceuticals", "sector": "Health Care"},
|
88
|
+
{"symbol": "ROP", "name": "Roper Technologies", "sector": "Information Technology"},
|
89
|
+
{"symbol": "ROST", "name": "Ross Stores", "sector": "Consumer Discretionary"},
|
90
|
+
{"symbol": "SBUX", "name": "Starbucks", "sector": "Consumer Discretionary"},
|
91
|
+
{"symbol": "SNPS", "name": "Synopsys", "sector": "Information Technology"},
|
92
|
+
{"symbol": "TTWO", "name": "Take-Two Interactive", "sector": "Communication Services"},
|
93
|
+
{"symbol": "TMUS", "name": "T-Mobile US", "sector": "Communication Services"},
|
94
|
+
{"symbol": "TSLA", "name": "Tesla, Inc.", "sector": "Consumer Discretionary"},
|
95
|
+
{"symbol": "TXN", "name": "Texas Instruments", "sector": "Information Technology"},
|
96
|
+
{"symbol": "TTD", "name": "Trade Desk (The)", "sector": "Communication Services"},
|
97
|
+
{"symbol": "VRSK", "name": "Verisk Analytics", "sector": "Industrials"},
|
98
|
+
{"symbol": "VRTX", "name": "Vertex Pharmaceuticals", "sector": "Health Care"},
|
99
|
+
{"symbol": "WBD", "name": "Warner Bros. Discovery", "sector": "Communication Services"},
|
100
|
+
{"symbol": "WDAY", "name": "Workday, Inc.", "sector": "Information Technology"},
|
101
|
+
{"symbol": "XEL", "name": "Xcel Energy", "sector": "Utilities"},
|
102
|
+
{"symbol": "ZS", "name": "Zscaler", "sector": "Information Technology"}
|
103
|
+
]
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
2
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
3
|
+
<svg width="100%" height="100%" viewBox="0 0 157 157" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;">
|
4
|
+
<style>
|
5
|
+
path {
|
6
|
+
stroke: #3E63DD;
|
7
|
+
}
|
8
|
+
@media (prefers-color-scheme: dark) {
|
9
|
+
path {
|
10
|
+
stroke: #fff;
|
11
|
+
}
|
12
|
+
}
|
13
|
+
</style>
|
14
|
+
<path d="M8.841,127.508L33.676,144.928L33.675,105.744L33.675,94.343L43.023,100.871L112.335,149.272L135.026,135.569L8.841,46.576L8.841,127.508Z" style="fill:white;fill-opacity:0;fill-rule:nonzero;stroke-width:11.89px;"/>
|
15
|
+
<path d="M144.867,30.661L120.032,13.241L120.033,52.425L120.034,63.826L110.686,57.298L41.374,8.897L18.682,22.6L144.867,111.593L144.867,30.661Z" style="fill:white;fill-opacity:0;fill-rule:nonzero;stroke-width:11.89px;"/>
|
16
|
+
</svg>
|
@@ -0,0 +1,37 @@
|
|
1
|
+
import CompanyModel from '../model/Company.mjs';
|
2
|
+
import Store from '../../../src/data/Store.mjs';
|
3
|
+
|
4
|
+
/**
|
5
|
+
* @class Finance.store.Companies
|
6
|
+
* @extends Neo.data.Store
|
7
|
+
*/
|
8
|
+
class Companies extends Store {
|
9
|
+
static config = {
|
10
|
+
/**
|
11
|
+
* @member {String} className='Finance.store.Companies'
|
12
|
+
* @protected
|
13
|
+
*/
|
14
|
+
className: 'Finance.store.Companies',
|
15
|
+
/**
|
16
|
+
* @member {String} keyProperty='symbol'
|
17
|
+
*/
|
18
|
+
keyProperty: 'symbol',
|
19
|
+
/**
|
20
|
+
* @member {Neo.data.model} model=CompanyModel
|
21
|
+
*/
|
22
|
+
model: CompanyModel,
|
23
|
+
/**
|
24
|
+
* @member {Object[]} sorters=[{property:'name',direction:'ASC'}]
|
25
|
+
*/
|
26
|
+
sorters: [{
|
27
|
+
property : 'name',
|
28
|
+
direction: 'ASC'
|
29
|
+
}],
|
30
|
+
/**
|
31
|
+
* @member {String} url='../../apps/finance/resources/data/companies.json'
|
32
|
+
*/
|
33
|
+
url: '../../apps/finance/resources/data/companies.json'
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
export default Neo.setupClass(Companies);
|
@@ -0,0 +1,59 @@
|
|
1
|
+
import BaseGridContainer from '../../../src/grid/Container.mjs';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* @class Finance.view.GridContainer
|
5
|
+
* @extends Neo.table.Container
|
6
|
+
*/
|
7
|
+
class GridContainer extends BaseGridContainer {
|
8
|
+
static config = {
|
9
|
+
/**
|
10
|
+
* @member {String} className='Finance.view.GridContainer'
|
11
|
+
* @protected
|
12
|
+
*/
|
13
|
+
className: 'Finance.view.GridContainer',
|
14
|
+
/**
|
15
|
+
* @member {Object} bind
|
16
|
+
*/
|
17
|
+
bind: {
|
18
|
+
store: 'stores.companies'
|
19
|
+
},
|
20
|
+
/**
|
21
|
+
* @member {Object[]} columns
|
22
|
+
*/
|
23
|
+
columns: [{
|
24
|
+
dataField: 'id',
|
25
|
+
text : '#',
|
26
|
+
type : 'index',
|
27
|
+
width : 40
|
28
|
+
}, {
|
29
|
+
dataField: 'symbol',
|
30
|
+
text : 'Symbol',
|
31
|
+
width : 100
|
32
|
+
}, {
|
33
|
+
dataField: 'name',
|
34
|
+
text : 'Name',
|
35
|
+
width : 250
|
36
|
+
}, {
|
37
|
+
dataField: 'sector',
|
38
|
+
text : 'Sector',
|
39
|
+
width : 200
|
40
|
+
}, {
|
41
|
+
cellAlign: 'right',
|
42
|
+
dataField: 'change',
|
43
|
+
locale : 'en-US',
|
44
|
+
text : 'Change',
|
45
|
+
type : 'animatedCurrency',
|
46
|
+
width : 120
|
47
|
+
}, {
|
48
|
+
cellAlign : 'right',
|
49
|
+
compareField: 'change',
|
50
|
+
dataField : 'value',
|
51
|
+
locale : 'en-US',
|
52
|
+
text : 'Value',
|
53
|
+
type : 'animatedCurrency',
|
54
|
+
width : 120
|
55
|
+
}]
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
export default Neo.setupClass(GridContainer);
|
@@ -0,0 +1,43 @@
|
|
1
|
+
import BaseViewport from '../../../src/container/Viewport.mjs';
|
2
|
+
import GridContainer from './GridContainer.mjs';
|
3
|
+
import ViewportController from './ViewportController.mjs';
|
4
|
+
import ViewportStateProvider from './ViewportStateProvider.mjs';
|
5
|
+
|
6
|
+
/**
|
7
|
+
* @class Finance.view.Viewport
|
8
|
+
* @extends Neo.container.Viewport
|
9
|
+
*/
|
10
|
+
class Viewport extends BaseViewport {
|
11
|
+
static config = {
|
12
|
+
/**
|
13
|
+
* @member {String} className='Finance.view.Viewport'
|
14
|
+
* @protected
|
15
|
+
*/
|
16
|
+
className: 'Finance.view.Viewport',
|
17
|
+
/**
|
18
|
+
* @member {Neo.controller.Component} controller=ViewportController
|
19
|
+
*/
|
20
|
+
controller: ViewportController,
|
21
|
+
/**
|
22
|
+
* @member {Object[]} items
|
23
|
+
*/
|
24
|
+
items: [{
|
25
|
+
module : GridContainer,
|
26
|
+
reference: 'grid'
|
27
|
+
}],
|
28
|
+
/*
|
29
|
+
* @member {Object} layout={ntype:'fit'}
|
30
|
+
*/
|
31
|
+
layout: {ntype: 'fit'},
|
32
|
+
/**
|
33
|
+
* @member {Neo.state.Provider} stateProvider=ViewportStateProvider
|
34
|
+
*/
|
35
|
+
stateProvider: ViewportStateProvider,
|
36
|
+
/**
|
37
|
+
* @member {Object} style
|
38
|
+
*/
|
39
|
+
style: {padding: '2em'}
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
export default Neo.setupClass(Viewport);
|
@@ -0,0 +1,50 @@
|
|
1
|
+
import Controller from '../../../src/controller/Component.mjs';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* @class Finance.view.ViewportController
|
5
|
+
* @extends Neo.controller.Component
|
6
|
+
*/
|
7
|
+
class ViewportController extends Controller {
|
8
|
+
static config = {
|
9
|
+
/**
|
10
|
+
* @member {String} className='Finance.view.ViewportController'
|
11
|
+
* @protected
|
12
|
+
*/
|
13
|
+
className: 'Finance.view.ViewportController'
|
14
|
+
}
|
15
|
+
|
16
|
+
generateData() {
|
17
|
+
let me = this,
|
18
|
+
store = me.getStore('companies'),
|
19
|
+
change, index, record;
|
20
|
+
|
21
|
+
setInterval(() => {
|
22
|
+
index = Math.round(Math.random() * 100); // 0 - 100
|
23
|
+
change = Math.random() * 10 - 5;
|
24
|
+
record = store.getAt(index);
|
25
|
+
|
26
|
+
record.set({change, value: record.value + change})
|
27
|
+
}, 1)
|
28
|
+
}
|
29
|
+
|
30
|
+
/**
|
31
|
+
*
|
32
|
+
*/
|
33
|
+
onCompaniesStoreLoad() {
|
34
|
+
let me = this,
|
35
|
+
companiesStore = me.getStore('companies'),
|
36
|
+
items = [];
|
37
|
+
|
38
|
+
companiesStore.items.forEach(record => {
|
39
|
+
items.push({
|
40
|
+
symbol: record.symbol,
|
41
|
+
value : Math.random() * 1000
|
42
|
+
})
|
43
|
+
});
|
44
|
+
|
45
|
+
me.getReference('grid').bulkUpdateRecords(items);
|
46
|
+
me.generateData()
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
export default Neo.setupClass(ViewportController);
|
@@ -0,0 +1,32 @@
|
|
1
|
+
import CompanyStore from '../store/Companies.mjs';
|
2
|
+
import StateProvider from '../../../src/state/Provider.mjs';
|
3
|
+
|
4
|
+
/**
|
5
|
+
* @class Finance.view.ViewportStateProvider
|
6
|
+
* @extends Neo.state.Provider
|
7
|
+
*/
|
8
|
+
class ViewportStateProvider extends StateProvider {
|
9
|
+
static config = {
|
10
|
+
/**
|
11
|
+
* @member {String} className='Finance.view.ViewportStateProvider'
|
12
|
+
* @protected
|
13
|
+
*/
|
14
|
+
className: 'Finance.view.ViewportStateProvider',
|
15
|
+
/**
|
16
|
+
* @member {Object} data
|
17
|
+
*/
|
18
|
+
data: {},
|
19
|
+
/**
|
20
|
+
* @member {Object} stores
|
21
|
+
*/
|
22
|
+
stores: {
|
23
|
+
companies: {
|
24
|
+
module : CompanyStore,
|
25
|
+
autoLoad : true,
|
26
|
+
listeners: {load: 'onCompaniesStoreLoad'}
|
27
|
+
}
|
28
|
+
}
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
export default Neo.setupClass(ViewportStateProvider);
|
package/apps/portal/index.html
CHANGED
package/package.json
CHANGED
@@ -0,0 +1,25 @@
|
|
1
|
+
@keyframes grid-animated-cell-negative {
|
2
|
+
50% {background-color: darkred;}
|
3
|
+
}
|
4
|
+
|
5
|
+
@keyframes grid-animated-cell-positive {
|
6
|
+
50% {background-color: darkgreen}
|
7
|
+
}
|
8
|
+
|
9
|
+
.neo-grid-container {
|
10
|
+
.neo-grid-row, .neo-grid-row.neo-even {
|
11
|
+
.neo-grid-cell {
|
12
|
+
&.neo-animated-negative {
|
13
|
+
animation-name : grid-animated-cell-negative;
|
14
|
+
animation-duration : .4s;
|
15
|
+
animation-timing-function: ease-in-out;
|
16
|
+
}
|
17
|
+
|
18
|
+
&.neo-animated-positive {
|
19
|
+
animation-name : grid-animated-cell-positive;
|
20
|
+
animation-duration : .4s;
|
21
|
+
animation-timing-function: ease-in-out;
|
22
|
+
}
|
23
|
+
}
|
24
|
+
}
|
25
|
+
}
|
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.34.0'
|
267
267
|
* @memberOf! module:Neo
|
268
268
|
* @name config.version
|
269
269
|
* @type String
|
270
270
|
*/
|
271
|
-
version: '8.
|
271
|
+
version: '8.34.0'
|
272
272
|
};
|
273
273
|
|
274
274
|
Object.assign(DefaultConfig, {
|
@@ -219,14 +219,21 @@ class MagicMoveText extends Component {
|
|
219
219
|
|
220
220
|
let me = this;
|
221
221
|
|
222
|
-
|
223
|
-
me.
|
224
|
-
|
222
|
+
if (value) {
|
223
|
+
me.getDomRect().then(rect => {
|
224
|
+
me.contentHeight = rect.height;
|
225
|
+
me.contentWidth = rect.width;
|
226
|
+
})
|
227
|
+
} else {
|
228
|
+
me.measureCache = {};
|
229
|
+
me.previousChars = []
|
230
|
+
}
|
225
231
|
|
226
|
-
|
227
|
-
|
232
|
+
if(oldValue !== undefined) {
|
233
|
+
me.addResizeObserver(value);
|
228
234
|
|
229
|
-
|
235
|
+
me.autoCycle && me.startAutoCycle(value)
|
236
|
+
}
|
230
237
|
}
|
231
238
|
|
232
239
|
/**
|
package/src/grid/Container.mjs
CHANGED
@@ -19,11 +19,13 @@ class GridContainer extends BaseContainer {
|
|
19
19
|
* @static
|
20
20
|
*/
|
21
21
|
static columnTypes = {
|
22
|
-
animatedChange: column.AnimatedChange,
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
22
|
+
animatedChange : column.AnimatedChange,
|
23
|
+
animatedCurrency: column.AnimatedCurrency,
|
24
|
+
column : column.Base,
|
25
|
+
component : column.Component,
|
26
|
+
currency : column.Currency,
|
27
|
+
index : column.Index,
|
28
|
+
progress : column.Progress
|
27
29
|
}
|
28
30
|
/**
|
29
31
|
* @member {Object} delayable
|
package/src/grid/View.mjs
CHANGED
@@ -434,10 +434,6 @@ class GridView extends Component {
|
|
434
434
|
fieldValue = record[dataField],
|
435
435
|
cellConfig, rendererOutput;
|
436
436
|
|
437
|
-
if (fieldValue === null || fieldValue === undefined) {
|
438
|
-
fieldValue = ''
|
439
|
-
}
|
440
|
-
|
441
437
|
rendererOutput = column.renderer.call(column.rendererScope || column, {
|
442
438
|
column,
|
443
439
|
columnIndex,
|
@@ -0,0 +1,87 @@
|
|
1
|
+
import AnimatedChange from './AnimatedChange.mjs';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* @class Neo.grid.column.AnimatedCurrency
|
5
|
+
* @extends Neo.grid.column.AnimatedChange
|
6
|
+
*/
|
7
|
+
class AnimatedCurrency extends AnimatedChange {
|
8
|
+
static config = {
|
9
|
+
/**
|
10
|
+
* @member {String} className='Neo.grid.column.AnimatedCurrency'
|
11
|
+
* @protected
|
12
|
+
*/
|
13
|
+
className: 'Neo.grid.column.AnimatedCurrency',
|
14
|
+
/**
|
15
|
+
* @member {String} type='animatedCurrency'
|
16
|
+
* @protected
|
17
|
+
*/
|
18
|
+
type: 'animatedCurrency',
|
19
|
+
/**
|
20
|
+
* Set a different record field to base the change on.
|
21
|
+
* Defaults this.dataField
|
22
|
+
* @member {String|null} compareField=null
|
23
|
+
*/
|
24
|
+
compareField: null,
|
25
|
+
/**
|
26
|
+
* @member {String} currency='USD'
|
27
|
+
*/
|
28
|
+
currency: 'USD',
|
29
|
+
/**
|
30
|
+
* @member {String} locale='default'
|
31
|
+
*/
|
32
|
+
locale: 'default'
|
33
|
+
}
|
34
|
+
|
35
|
+
/**
|
36
|
+
* @member {Intl.NumberFormat|null} formatter=null
|
37
|
+
*/
|
38
|
+
formatter = null
|
39
|
+
|
40
|
+
/**
|
41
|
+
* @param {Object} config
|
42
|
+
*/
|
43
|
+
construct(config) {
|
44
|
+
super.construct(config);
|
45
|
+
this.createFormatter()
|
46
|
+
}
|
47
|
+
|
48
|
+
/**
|
49
|
+
* @param {Object} data
|
50
|
+
* @param {Neo.button.Base} data.column
|
51
|
+
* @param {Number} data.columnIndex
|
52
|
+
* @param {String} data.dataField
|
53
|
+
* @param {Neo.grid.Container} data.gridContainer
|
54
|
+
* @param {Object} data.record
|
55
|
+
* @param {Number} data.rowIndex
|
56
|
+
* @param {Neo.data.Store} data.store
|
57
|
+
* @param {Number|String} data.value
|
58
|
+
* @returns {*}
|
59
|
+
*/
|
60
|
+
cellRenderer({value}) {
|
61
|
+
if (value === null || value === undefined) {
|
62
|
+
return ''
|
63
|
+
}
|
64
|
+
|
65
|
+
return this.formatter.format(value)
|
66
|
+
}
|
67
|
+
|
68
|
+
/**
|
69
|
+
*
|
70
|
+
*/
|
71
|
+
createFormatter() {
|
72
|
+
let me = this;
|
73
|
+
|
74
|
+
me.formatter = new Intl.NumberFormat(me.locale, {style: 'currency', currency: me.currency})
|
75
|
+
}
|
76
|
+
|
77
|
+
/**
|
78
|
+
* Override as needed for dynamic record-based animation classes
|
79
|
+
* @param {Record} record
|
80
|
+
* @returns {String}
|
81
|
+
*/
|
82
|
+
getAnimationCls(record) {
|
83
|
+
return record[this.compareField || this.dataField] < 0 ? 'neo-animated-negative' : 'neo-animated-positive'
|
84
|
+
}
|
85
|
+
}
|
86
|
+
|
87
|
+
export default Neo.setupClass(AnimatedCurrency);
|
package/src/grid/column/Base.mjs
CHANGED
@@ -73,8 +73,12 @@ class Column extends Base {
|
|
73
73
|
* @param {Number|String} data.value
|
74
74
|
* @returns {*}
|
75
75
|
*/
|
76
|
-
cellRenderer(
|
77
|
-
|
76
|
+
cellRenderer({value}) {
|
77
|
+
if (value === null || value === undefined) {
|
78
|
+
return ''
|
79
|
+
}
|
80
|
+
|
81
|
+
return value
|
78
82
|
}
|
79
83
|
}
|
80
84
|
|
@@ -0,0 +1,72 @@
|
|
1
|
+
import Column from './Base.mjs';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* @class Neo.grid.column.Currency
|
5
|
+
* @extends Neo.grid.column.Base
|
6
|
+
*/
|
7
|
+
class Currency extends Column {
|
8
|
+
static config = {
|
9
|
+
/**
|
10
|
+
* @member {String} className='Neo.grid.column.Currency'
|
11
|
+
* @protected
|
12
|
+
*/
|
13
|
+
className: 'Neo.grid.column.Currency',
|
14
|
+
/**
|
15
|
+
* @member {String} type='currency'
|
16
|
+
* @protected
|
17
|
+
*/
|
18
|
+
type: 'currency',
|
19
|
+
/**
|
20
|
+
* @member {String} currency='USD'
|
21
|
+
*/
|
22
|
+
currency: 'USD',
|
23
|
+
/**
|
24
|
+
* @member {String} locale='default'
|
25
|
+
*/
|
26
|
+
locale: 'default'
|
27
|
+
}
|
28
|
+
|
29
|
+
/**
|
30
|
+
* @member {Intl.NumberFormat|null} formatter=null
|
31
|
+
*/
|
32
|
+
formatter = null
|
33
|
+
|
34
|
+
/**
|
35
|
+
* @param {Object} config
|
36
|
+
*/
|
37
|
+
construct(config) {
|
38
|
+
super.construct(config);
|
39
|
+
this.createFormatter()
|
40
|
+
}
|
41
|
+
|
42
|
+
/**
|
43
|
+
* @param {Object} data
|
44
|
+
* @param {Neo.button.Base} data.column
|
45
|
+
* @param {Number} data.columnIndex
|
46
|
+
* @param {String} data.dataField
|
47
|
+
* @param {Neo.grid.Container} data.gridContainer
|
48
|
+
* @param {Object} data.record
|
49
|
+
* @param {Number} data.rowIndex
|
50
|
+
* @param {Neo.data.Store} data.store
|
51
|
+
* @param {Number|String} data.value
|
52
|
+
* @returns {*}
|
53
|
+
*/
|
54
|
+
cellRenderer({value}) {
|
55
|
+
if (value === null || value === undefined) {
|
56
|
+
return ''
|
57
|
+
}
|
58
|
+
|
59
|
+
return this.formatter.format(value)
|
60
|
+
}
|
61
|
+
|
62
|
+
/**
|
63
|
+
*
|
64
|
+
*/
|
65
|
+
createFormatter() {
|
66
|
+
let me = this;
|
67
|
+
|
68
|
+
me.formatter = new Intl.NumberFormat(me.locale, {style: 'currency', currency: me.currency})
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
export default Neo.setupClass(Currency);
|
@@ -1,7 +1,9 @@
|
|
1
|
-
import AnimatedChange
|
2
|
-
import
|
3
|
-
import
|
4
|
-
import
|
5
|
-
import
|
1
|
+
import AnimatedChange from './AnimatedChange.mjs';
|
2
|
+
import AnimatedCurrency from './AnimatedCurrency.mjs';
|
3
|
+
import Base from './Base.mjs';
|
4
|
+
import Component from './Component.mjs';
|
5
|
+
import Currency from './Currency.mjs';
|
6
|
+
import Index from './Index.mjs';
|
7
|
+
import Progress from './Progress.mjs';
|
6
8
|
|
7
|
-
export {AnimatedChange, Base, Component, Index, Progress};
|
9
|
+
export {AnimatedChange, AnimatedCurrency, Base, Component, Currency, Index, Progress};
|
@@ -19,6 +19,14 @@ class ServiceWorker extends Base {
|
|
19
19
|
* @param {Object} config
|
20
20
|
*/
|
21
21
|
construct(config) {
|
22
|
+
super.construct(config);
|
23
|
+
this.registerServiceWorker()
|
24
|
+
}
|
25
|
+
|
26
|
+
/**
|
27
|
+
* @returns {Promise<void>}
|
28
|
+
*/
|
29
|
+
async registerServiceWorker() {
|
22
30
|
if ('serviceWorker' in navigator) {
|
23
31
|
let me = this,
|
24
32
|
{config} = Neo,
|
@@ -27,30 +35,32 @@ class ServiceWorker extends Base {
|
|
27
35
|
folder = window.location.pathname.includes('/examples/') ? 'examples/' : 'apps/',
|
28
36
|
opts = devMode ? {type: 'module'} : {},
|
29
37
|
path = (devMode ? config.basePath : config.workerBasePath) + (devMode ? folder : '') + fileName,
|
30
|
-
{serviceWorker} = navigator
|
38
|
+
{serviceWorker} = navigator,
|
39
|
+
registration = await serviceWorker.register(path, opts);
|
31
40
|
|
32
41
|
window.addEventListener('beforeunload', me.onBeforeUnload.bind(me));
|
33
42
|
|
34
|
-
|
35
|
-
.
|
36
|
-
|
37
|
-
|
43
|
+
registration.addEventListener('updatefound', () => {
|
44
|
+
window.location.reload()
|
45
|
+
})
|
46
|
+
|
47
|
+
await serviceWorker.ready;
|
48
|
+
|
49
|
+
serviceWorker.onmessage = WorkerManager.onWorkerMessage.bind(WorkerManager);
|
38
50
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
51
|
+
if (!WorkerManager.getWorker('service')) {
|
52
|
+
/*
|
53
|
+
* navigator.serviceWorker.controller can be null in case we load a page for the first time
|
54
|
+
* or in case of a force refresh.
|
55
|
+
* See: https://www.w3.org/TR/service-workers/#navigator-service-worker-controller
|
56
|
+
*/
|
57
|
+
WorkerManager.serviceWorker = registration.active
|
58
|
+
}
|
47
59
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
})
|
53
|
-
})
|
60
|
+
WorkerManager.sendMessage('service', {
|
61
|
+
action: 'registerNeoConfig',
|
62
|
+
data : config
|
63
|
+
})
|
54
64
|
}
|
55
65
|
}
|
56
66
|
|
package/src/worker/App.mjs
CHANGED
@@ -413,17 +413,6 @@ class App extends Base {
|
|
413
413
|
})
|
414
414
|
})
|
415
415
|
}
|
416
|
-
/**
|
417
|
-
* Triggered in case a connected ServiceWorker receives a new version.
|
418
|
-
* Especially inside dist envs, a reload of the connecting window is required,
|
419
|
-
* since the SW will clear its caches and the app can receive conflicting bundle versions.
|
420
|
-
* @param {Object} data
|
421
|
-
* @param {String} data.newVersion
|
422
|
-
* @param {String} data.oldVersion
|
423
|
-
*/
|
424
|
-
onNewVersion(data) {
|
425
|
-
Neo.Main.reloadWindow({});
|
426
|
-
}
|
427
416
|
|
428
417
|
/**
|
429
418
|
* Fire event on all apps
|
package/src/worker/Manager.mjs
CHANGED
@@ -8,6 +8,10 @@ import RemoteMethodAccess from './mixin/RemoteMethodAccess.mjs';
|
|
8
8
|
const NeoConfig = Neo.config,
|
9
9
|
devMode = NeoConfig.environment === 'development';
|
10
10
|
|
11
|
+
navigator.serviceWorker.addEventListener('controllerchange', function() {
|
12
|
+
window.location.reload()
|
13
|
+
}, {once: true});
|
14
|
+
|
11
15
|
/**
|
12
16
|
* The worker manager lives inside the main thread and creates the App, Data & VDom worker.
|
13
17
|
* Also, responsible for sending messages from the main thread to the different workers.
|
@@ -14,6 +14,10 @@ class ServiceBase extends Base {
|
|
14
14
|
* @protected
|
15
15
|
*/
|
16
16
|
className: 'Neo.worker.ServiceBase',
|
17
|
+
/**
|
18
|
+
* @member {String} cacheName_='neo-runtime'
|
19
|
+
*/
|
20
|
+
cacheName_: 'neo-runtime',
|
17
21
|
/**
|
18
22
|
* @member {String[]|Neo.core.Base[]|null} mixins=[RemoteMethodAccess]
|
19
23
|
*/
|
@@ -33,10 +37,6 @@ class ServiceBase extends Base {
|
|
33
37
|
}
|
34
38
|
}
|
35
39
|
|
36
|
-
/**
|
37
|
-
* @member {String} cacheName='neo-runtime'
|
38
|
-
*/
|
39
|
-
cacheName = 'neo-runtime'
|
40
40
|
/**
|
41
41
|
* @member {String[]} cachePaths
|
42
42
|
*/
|
@@ -94,6 +94,15 @@ class ServiceBase extends Base {
|
|
94
94
|
Neo.workerId = me.workerId
|
95
95
|
}
|
96
96
|
|
97
|
+
/**
|
98
|
+
* Triggered when accessing the cacheName config
|
99
|
+
* @param {String} value
|
100
|
+
* @protected
|
101
|
+
*/
|
102
|
+
beforeGetCacheName(value) {
|
103
|
+
return value + '-' + this.version
|
104
|
+
}
|
105
|
+
|
97
106
|
/**
|
98
107
|
* @param {String} name=this.cacheName
|
99
108
|
* @returns {Promise<Object>}
|
@@ -163,14 +172,26 @@ class ServiceBase extends Base {
|
|
163
172
|
/**
|
164
173
|
* @param {ExtendableMessageEvent} event
|
165
174
|
*/
|
166
|
-
onActivate(event) {
|
167
|
-
console.log('onActivate', event)
|
175
|
+
async onActivate(event) {
|
176
|
+
console.log('onActivate', event);
|
177
|
+
|
178
|
+
let me = this,
|
179
|
+
keys = await caches.keys(),
|
180
|
+
key;
|
181
|
+
|
182
|
+
for (key of keys) {
|
183
|
+
// Clear caches for prior SW versions, without touching non-related caches
|
184
|
+
if (key.startsWith(me._cacheName) && key !== me.cacheName) {
|
185
|
+
// No need to await the method execution
|
186
|
+
me.clearCache(key)
|
187
|
+
}
|
188
|
+
}
|
168
189
|
}
|
169
190
|
|
170
191
|
/**
|
171
192
|
* @param {Client} source
|
172
193
|
*/
|
173
|
-
onConnect(source) {
|
194
|
+
async onConnect(source) {
|
174
195
|
console.log('onConnect', source);
|
175
196
|
|
176
197
|
this.createMessageChannel(source);
|
@@ -181,8 +202,8 @@ class ServiceBase extends Base {
|
|
181
202
|
* @param {ExtendableMessageEvent} event
|
182
203
|
*/
|
183
204
|
onFetch(event) {
|
184
|
-
let hasMatch
|
185
|
-
{request}
|
205
|
+
let hasMatch = false,
|
206
|
+
{request} = event,
|
186
207
|
key;
|
187
208
|
|
188
209
|
for (key of this.cachePaths) {
|
@@ -192,7 +213,7 @@ class ServiceBase extends Base {
|
|
192
213
|
}
|
193
214
|
}
|
194
215
|
|
195
|
-
hasMatch && event.respondWith(
|
216
|
+
hasMatch && request.method === 'GET' && event.respondWith(
|
196
217
|
caches.match(request)
|
197
218
|
.then(cachedResponse => cachedResponse || caches.open(this.cacheName)
|
198
219
|
.then(cache => fetch(request)
|
@@ -206,8 +227,7 @@ class ServiceBase extends Base {
|
|
206
227
|
* @param {ExtendableMessageEvent} event
|
207
228
|
*/
|
208
229
|
onInstall(event) {
|
209
|
-
console.log('onInstall', event)
|
210
|
-
globalThis.skipWaiting()
|
230
|
+
console.log('onInstall', event)
|
211
231
|
}
|
212
232
|
|
213
233
|
/**
|
@@ -250,25 +270,15 @@ class ServiceBase extends Base {
|
|
250
270
|
* @param {ExtendableMessageEvent} event
|
251
271
|
*/
|
252
272
|
async onRegisterNeoConfig(msg, event) {
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
Neo.config = Neo.config || {};
|
257
|
-
Object.assign(Neo.config, msg.data);
|
258
|
-
|
259
|
-
if (version !== Neo.config.version) {
|
260
|
-
await me.clearCaches();
|
261
|
-
|
262
|
-
me.version = Neo.config.version;
|
273
|
+
this.onConnect(event.source)
|
274
|
+
}
|
263
275
|
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
me.onConnect(event.source)
|
271
|
-
}
|
276
|
+
/**
|
277
|
+
* @param {Object} msg
|
278
|
+
* @param {ExtendableMessageEvent} event
|
279
|
+
*/
|
280
|
+
async onSkipWaiting(msg, event) {
|
281
|
+
await globalThis.skipWaiting()
|
272
282
|
}
|
273
283
|
|
274
284
|
/**
|