Compare commits

...

223 Commits

Author SHA1 Message Date
Sean Landsman
7a5e15c8cd Release 23.0.2 - Initial Creation and Setup 2020-03-20 08:22:33 +00:00
Sean Landsman
bf18305c9a AG-3200 - add callback parameter to api.getFilterInstance 2020-03-19 10:35:16 +00:00
Sean Landsman
aef9c596b6 Merge branch 'b23.0.0' into latest 2020-03-17 14:38:43 +00:00
Sean Landsman
2d5fea4790 Release 23.0.0 2020-03-11 08:16:58 +00:00
Adrian Lowdon
c680bffd5a AG-3390 - fixed icon 2020-02-18 15:48:10 +00:00
Guilherme Lopes Portela
917277bfe1 updated boostrap 2020-02-03 22:38:26 +00:00
Sean Landsman
d2cb2149cc AG-3790 Vue component not found if not within immediate parent 2020-01-29 10:28:03 +00:00
Sean Landsman
0954bec3ee Utility script 2020-01-14 09:44:17 +00:00
Bernie Sumption
89d76cd1ac Moved from fresh theme to balham 2019-12-11 15:53:40 +00:00
Sean Landsman
52cf4512a9 AG-3600 React - memoized functions don't render 2019-11-21 14:55:31 +00:00
Sean Landsman
611d668981 Merge branch 'b22.0.0' into latest 2019-11-11 15:02:22 +00:00
Sean Landsman
5ad3592fed AG-1329 Update examples 2019-11-11 11:22:13 +00:00
Sean Landsman
ff7c46e553 AG-1329 Add licenses etc to new packages 2019-11-08 10:54:47 +00:00
Sean Landsman
1c1e1bf555 AG-1329 Rename packages to new scopes (@ag-grid-community & @ag-grid-enterprise) 2019-11-05 09:18:50 +00:00
Sean Landsman
6ad4493357 AG-1329 Rename packages to new scopes (@ag-grid-community & @ag-grid-enterprise) 2019-11-04 20:25:39 +00:00
Sean Landsman
1cf70a551e Merge branch 'latest' into b22.0.0 2019-10-25 12:41:54 +01:00
Sean Landsman
fcc2bfffcd Merge branch 'latest' into b22.0.0 2019-10-25 11:11:08 +01:00
Sean Landsman
3e8b1b7979 Merge branch 'latest' into b22.0.0 2019-10-24 09:35:46 +01:00
Sean Landsman
61420e0d67 AG-1329 Move ag-grid-react and ag-grid-vue to modules 2019-10-23 20:25:40 +01:00
Sean Landsman
bd63d49c52 Release 22.0.0 2019-10-23 11:23:35 +01:00
Sean Landsman
36eb9c8a18 AG-1329 Fix source map issue, change versions, clean modules 2019-10-22 16:58:54 +01:00
Sean Landsman
b4c3ebc248 AG-1329 Update framework examples and tests 2019-10-18 20:46:09 +01:00
Sean Landsman
51621200da AG-1329 Modularisation work - update examples 2019-10-18 18:40:14 +01:00
Sean Landsman
46c26f074e AG-1329 Add modularisation mechanism 2019-10-09 18:41:11 +01:00
Sean Landsman
894d971f0e AG-3316 agGridReact needs to be updated to use the updated react lifecycle hooks 2019-09-30 15:00:08 +01:00
Sean Landsman
0584234be7 AG-3316 agGridReact needs to be updated to use the updated react lifecycle hooks 2019-09-25 12:22:09 +01:00
Sean Landsman
b9b2b03a6b AG-2914 DOCS - Add docs around react hooks with editors. 2019-09-23 14:29:21 +01:00
Sean Landsman
5a26863aff AG-3316 agGridReact needs to be updated to use the updated react lifecycle hooks 2019-09-16 15:45:07 +01:00
Sean Landsman
3223b82616 AG-3294 Update and improve build process 2019-09-10 12:32:05 +01:00
Sean Landsman
70f747e9f3 Update example versions 2019-06-04 15:39:31 +01:00
Sean Landsman
29152268bc Update example versions 2019-06-04 15:35:54 +01:00
Sean Landsman
cda61736cf Framework sweep (react) 2019-05-18 15:47:50 +01:00
Sean Landsman
e55950227f AG-2750 Pre-release framework sweep 2019-03-19 16:30:35 +00:00
Sean Landsman
940cc314aa AG-2750 Pre-release framework sweep 2019-03-19 12:15:40 +00:00
Sean Landsman
5e4a94d865 AG-2750 Pre-release framework sweep 2019-03-19 08:17:15 +00:00
Sean Landsman
efa5167c54 Minor updates to deps 2019-03-18 15:08:58 +00:00
Sean Landsman
6b06b9fa01 AG-2741 Functional components don't work with reactNext 2019-03-14 12:01:33 +00:00
Sean Landsman
eecd2a6bb2 AG-1520 React wrapper causes styling issues for components 2019-03-06 14:13:10 +00:00
Sean Landsman
ec4f7dea27 Test CI Breaks on test failure 2019-03-06 09:36:31 +00:00
Sean Landsman
3d7774e7c7 AG-2656 Allow equality checking for rowData to be configurable (ie use equality checking, deep checking and so on) 2019-03-05 14:57:53 +00:00
Sean Landsman
92ad56ed49 AG-2656 Allow equality checking for rowData to be configurable (ie use equality checking, deep checking and so on) 2019-02-27 15:26:04 +00:00
Sean Landsman
7ff41a6966 Update fw examples 2019-02-20 15:35:12 +00:00
Sean Landsman
74ccfa6b26 AG-2653 Put React Hook example together, update docs 2019-02-12 14:45:24 +00:00
Sean Landsman
e07c66c95d AG-2264 Configuring AggregationPanel in status bar does not work if any other status panel is enabled 2019-02-08 11:00:27 +00:00
Sean Landsman
cb438ce67b Update versions 2019-01-11 11:06:50 +00:00
Sean Landsman
3f0fe9f899 AG-644 Refactor of sorting, filtering and resizing properties 2019-01-08 16:46:07 +00:00
Sean Landsman
a3a1d74b88 Framework sweep pre-release testing 2019-01-03 12:36:17 +00:00
Sean Landsman
2b49fb1b01 AG-2114 React Components are rendered to the root of the DOM tree
AG-2417 Issue with React 6: withRef is removed. To access the wrapped instance, use a ref on the connected component
AG-2439 Add support for react-redux v6

h/t @hrgui - thanks!
2018-12-17 13:18:28 +00:00
Sean Landsman
c338f851e0 Merge branch 'master' into latest 2018-11-09 16:22:29 +00:00
Sean Landsman
58df719d45 Merge branch 'b19.1.2' 2018-11-09 15:31:00 +00:00
Sean Landsman
2b6e3e8f41 Release 19.1.2 dist files added, package.json updated, license key updated 2018-11-09 08:32:07 +00:00
Sean Landsman
f470a4c33d Merge 19.1.1 2018-10-31 16:22:47 +00:00
rmc-software
b51caf4746 Merge branch 'b19.0.0' into Master 2018-10-31 15:42:56 +00:00
rmc-software
30746397ef Updated npm and bower versions for b19.1.1 2018-10-31 13:10:02 +00:00
Sean Landsman
4998a51817 AG-2178 Add docs and example on how to use Context API with React 2018-10-16 17:42:29 +01:00
Sean Landsman
518a95ee89 AG-2177 React Example - allow force refresh (routing breaks) 2018-10-16 16:07:35 +01:00
Sean Landsman
3debbabe28 Allow more flexibility with prop-types, fix trader example 2018-10-16 15:50:21 +01:00
Guilherme Lopes
550f6c4077 updated dependencies and webpack config 2018-10-16 12:09:53 +01:00
Robert Clarke
99bc479b60 Release 19.0.0 dist files created 2018-09-05 13:11:46 +01:00
Sean Landsman
f5c1a4ba17 Update fw examples 2018-09-04 17:07:32 +01:00
Sean Landsman
168a127e51 AG-2018 Rename ag-Grid to ag-Grid-Community 2018-08-21 16:07:22 +01:00
Sean Landsman
f9b1f8527a retiring deprecated themes 2018-08-14 10:23:54 +01:00
MrSafferty
eee938e0e7 AG-1922 Framework Sweep 2018-07-18 16:03:44 +01:00
MrSafferty
449a88e832 AG-1905 Future proof ag-grid-react by removing dependency on unstable_renderSubtreeIntoContainer 2018-07-16 14:49:45 +01:00
Alberto
60181add9a Merge branch 'master' into latest
Merge branch 'master' into latest

# Conflicts:
#	package-lock.json
#	packages/ag-grid-angular/package-lock.json
#	packages/ag-grid-aurelia/package-lock.json
#	packages/ag-grid-docs/package.json
#	packages/ag-grid-enterprise/package.json
#	packages/ag-grid-react/package-lock.json
#	packages/ag-grid-vue/package-lock.json
2018-07-05 11:14:57 +01:00
Alberto
847005c758 Merge branch 'b18.1.0'
# Conflicts:
#	package.json

Merge branch 'b18.1.0'

Merge branch 'b18.1.0'

# Conflicts:
#	package-lock.json
#	package.json
#	vue.config.js
2018-07-05 11:06:51 +01:00
Alberto
ca64dae64c Release 18.1.0 dist files created 2018-07-03 11:21:25 +01:00
MrSafferty
3591b78e4b Set example repos to private 2018-07-02 11:12:58 +01:00
MrSafferty
2fedd32efd Test, Fix and Clean React Examples 2018-06-29 11:11:35 +01:00
MrSafferty
849472a031 Test, Fix and Clean React Examples 2018-06-29 10:58:22 +01:00
Minh Nguyen
a2743f9076 AG-1868 Remove dependency on react-dom-factories in ag-grid-react 2018-06-27 16:01:27 +01:00
MrSafferty
d9724f7ab5 Remove an ignore package-lock.json files 2018-06-20 17:27:35 +01:00
MrSafferty
c0443ba6ef Remove an ignore package-lock.json files 2018-06-20 17:23:06 +01:00
MrSafferty
88752be90b Further monorepo work 2018-06-14 15:42:39 +01:00
MrSafferty
2080ebb054 Bring changes from master over 2018-06-13 16:57:40 +01:00
MrSafferty
172c2a05b0 Add Vue and Aurelia Examples 2018-06-13 12:15:03 +01:00
Alberto
badb5ea485 Merge remote-tracking branch 'origin/b18.0.0'
Merge remote-tracking branch 'origin/b18.0.1'
2018-06-12 16:34:06 +01:00
Alberto
c3af000642 Release 18.0.0 setting new license key 2018-06-11 11:48:53 +01:00
Alberto
7b6b609cdf Release 18.0.0 dist files created 2018-06-11 11:29:34 +01:00
Alberto
5ffb682014 Merge remote-tracking branch 'origin/master' into latest 2018-04-17 10:09:22 +02:00
Alberto
de18262157 Merge remote-tracking branch 'origin/b17.1.0' 2018-04-17 10:05:12 +02:00
Alberto
1f6c1e41a1 Release 17.1.0 dist files created 2018-04-12 15:50:19 +01:00
Alberto
ba3762bbd3 Release 17.1.0 dist files created 2018-04-12 15:48:39 +01:00
Alberto
455f982d6c Merge remote-tracking branch 'origin/master' into latest
Merge remote-tracking branch 'origin/master' into latest

# Conflicts:
#	src/index.php
2018-03-08 12:02:45 +01:00
Alberto
0c927ae3c5 Merge remote-tracking branch 'origin/b17.0.0' 2018-03-08 11:54:52 +01:00
Alberto
2bd8d4c987 Release 17.0.0 dist files created 2018-03-05 12:59:50 +01:00
Alberto
318f78de84 Release 17.0.0 dist files created 2018-03-05 12:57:11 +01:00
Alberto
9ca52a727d Merge branch 'master' into latest 2018-01-29 11:08:55 +00:00
Alberto
56ccdd028c Merge branch 'b16.0.0' 2018-01-29 10:35:22 +00:00
Alberto
a5ccb6d622 Release 16.0.0 dist files created 2018-01-24 11:56:55 +00:00
seanlandsman
25e1f1c184 Merge pull request #34 from ag-grid/latest
AG-1294 Sweep framework examples & update versions Sprint IV
2017-12-18 11:02:15 +00:00
Alberto
8dd0f96b32 Merge branch 'master' into latest
# Conflicts:
#	angular-cli/package.json
#	ngtools_webpack/package.json
#	systemjs_aot/package.json
#	webpack/package.json
#	webpack2/package.json

Merge branch 'master' into latest

Merge branch 'master' into latest

# Conflicts:
#	package.json
2017-12-18 11:15:57 +01:00
Alberto
cf0796eb84 Merge branch 'b15.0.0' 2017-12-18 10:55:49 +01:00
Sean Landsman
a64626e67e AG-1294 Sweep framework examples & update versions Sprint IV 2017-12-15 14:29:20 +00:00
Alberto
95445e558c Release 15.0.0 dist files created 2017-12-11 21:42:59 +00:00
Alberto
79a6206026 Release 15.0.0 creating branch 2017-12-11 21:29:22 +00:00
Sean Landsman
f8ac0e5c98 Pre 15x smoke test - minor tweaks 2017-12-08 12:34:24 +00:00
Alberto
06d9e88e88 Merge master into latest post 14.2.0 2017-11-16 16:26:14 +00:00
Alberto
f860a740f3 Merge branch 'b14.2.0' 2017-11-16 16:02:35 +00:00
Sean Landsman
90ec337474 Release 14.2.0 dist/lib files created 2017-11-15 11:15:38 +00:00
Sean Landsman
05d4341dfc Release 14.2.0 creating branch 2017-11-15 10:53:49 +00:00
Sean Landsman
df77361fd4 AG-1075 Remove legacy master/detail from FW examples 2017-11-13 09:55:06 +00:00
Sean Landsman
983b5974cf AG-1053 Tidy react examples, make more idiomatic 2017-11-08 14:45:28 +00:00
Sean Landsman
b030235bbc AG-530 Implement React declarative offering 2017-11-07 16:32:03 +00:00
Sean Landsman
351d93b8fb AG-530 Implement React declarative offering 2017-11-07 16:22:19 +00:00
Alberto
43af1237bd Merge branch 'master' into latest 2017-11-03 11:23:13 +00:00
Alberto
fc76e85d77 Release 14.0.0 2017-10-31 17:03:55 +00:00
Sean Landsman
6979c51eaf v14 fixes 2017-10-31 12:07:38 +00:00
Sean Landsman
f035ee7a9d v14 changes 2017-10-31 10:16:42 +00:00
Sean Landsman
a982e0f2a7 Revert versions 2017-10-30 15:18:53 +00:00
Sean Landsman
cbb4a31be6 Merge branch 'latest' of /Users/seanlandsman/IdeaProjects/ag/ag-grid/react/ag-grid-react-example with conflicts. 2017-10-05 13:02:49 +01:00
Alberto
d2b92b4c9a Release 13.3.0 2017-10-02 14:55:11 +02:00
Sean Landsman
c8267730bf Update code to match latest changes 2017-09-28 13:56:46 +01:00
Alberto
3992e3e928 v 13.2.0 2017-09-19 10:17:51 +01:00
Alberto
8409183d6b v 13.2.0 2017-09-19 09:00:11 +01:00
Sean Landsman
7075f57d88 Tidy examples 2017-09-18 16:03:27 +01:00
Sean Landsman
ad6bc068b2 Improve react examples 2017-09-15 16:56:08 +01:00
seanlandsman
c4310c2768 Merge pull request #32 from unformatt/patch-1
Fixes typo in README
2017-09-13 14:35:08 +01:00
Alberto
2228a8d0c9 Merge branch 'master' into latest 2017-09-08 09:40:53 +01:00
Matt
966fa0fe69 Update README.md 2017-09-07 18:37:15 -04:00
Sean Landsman
9528d5a5bc Move per col icons to grid based 2017-09-04 10:39:17 +01:00
Alberto
3b5eaac21a Merge branch 'latest' into 13.1.0
# Conflicts:
#	aot/dist/agGridColumn.ngfactory.ts
#	aot/dist/agGridColumn.ngsummary.json
#	aot/dist/agGridNg2.ngfactory.ts
#	aot/dist/agGridNg2.ngsummary.json
#	aot/dist/aggrid.module.ngfactory.ts
#	aot/dist/aggrid.module.ngsummary.json
#	aot/dist/baseComponentFactory.ngsummary.json
#	aot/dist/interfaces.ngsummary.json
#	aot/dist/ng2ComponentFactory.ngsummary.json
#	aot/dist/ng2FrameworkComponentWrapper.ngsummary.json
#	aot/dist/ng2FrameworkFactory.ngsummary.json
#	aot/main.ngsummary.json
#	aot/src/agGridColumn.ngfactory.ts
#	aot/src/agGridColumn.ngsummary.json
#	aot/src/agGridNg2.ngfactory.ts
#	aot/src/agGridNg2.ngsummary.json
#	aot/src/aggrid.module.ngfactory.ts
#	aot/src/aggrid.module.ngsummary.json
#	aot/src/baseComponentFactory.ngsummary.json
#	aot/src/interfaces.ngsummary.json
#	aot/src/ng2ComponentFactory.ngsummary.json
#	aot/src/ng2FrameworkComponentWrapper.ngsummary.json
#	aot/src/ng2FrameworkFactory.ngsummary.json
2017-09-01 14:02:24 +01:00
Sean Landsman
73cc989512 AG-730 New example for redux 2017-08-31 17:17:26 +01:00
Alberto
5a45846cb7 Release 13.0.1 2017-08-29 15:27:33 +02:00
Petyo Ivanov
2d8d5b125e Removed legacy padding 2017-08-29 13:34:18 +03:00
Alberto
818b7798ee Merge branch 'latest' into 13.0.0
# Conflicts:
#	package.json
2017-08-25 16:59:25 +02:00
Sean Landsman
15ba8d7e60 Rename cellFormatter to valueFormatter 2017-08-23 14:04:13 +01:00
Alberto
eab958c3e9 Releasing 13.0.0 2017-08-23 11:35:39 +01:00
Sean Landsman
3a17b7f638 AG-668 Update all README.md to point to appropriate plunker/seed 2017-08-21 11:45:09 +01:00
Sean Landsman
734d39e2d7 AG-676 Move framework examples into parent folder for easier exclusion 2017-08-17 11:04:12 +01:00
Sean Landsman
0d7cace354 Add example for keypress in editors
https://github.com/ag-grid/ag-grid-react/issues/60
2017-08-10 16:02:03 +01:00
Sean Landsman
fa0881bfcf Merge remote-tracking branch 'origin/latest' into latest 2017-08-10 15:51:05 +01:00
Sean Landsman
62b9e3582a Add example for keypress in editors
https://github.com/ag-grid/ag-grid-react/issues/60
2017-08-10 15:50:54 +01:00
Petyo Ivanov
949797d4be rename repo from ceolter to ag-grid 2017-08-04 12:23:47 +01:00
Sean Landsman
a869fe3819 CI Work 2017-08-02 13:48:39 +01:00
Sean Landsman
c611af4f39 CI Work 2017-08-02 13:15:55 +01:00
Sean Landsman
7cab3190ae CI Work 2017-08-02 12:19:07 +01:00
Sean Landsman
71fe653fcb CI Work 2017-08-02 11:55:18 +01:00
Sean Landsman
ba1c213ce0 Change selectAll to use refs to expand on example 2017-07-27 08:17:15 +01:00
Sean Landsman
46217c9ad7 Fix sorting issue 2017-07-26 12:10:12 +01:00
Sean Landsman
017642e4d7 AG-643 Tidy React examples 2017-07-25 12:47:47 +01:00
Sean Landsman
79b0eda61b AG-643 Tidy React examples 2017-07-25 12:47:07 +01:00
Sean Landsman
a70f248af5 AG-642 Remove deprecation warning in preparation for 16.x 2017-07-25 12:46:21 +01:00
Alberto
32142b435b v12 Hot fixes 2017-07-21 12:10:00 +02:00
Alberto
d71781899d AG-618 Fixing floating filter issue. Also allowing for gulp watch of Angular and React 2017-07-20 13:59:16 +02:00
Sean Landsman
9560f86bf5 Updated readme 2017-07-19 10:30:28 +01:00
Alberto
9f4b270884 New release branch, everything compiles 2017-07-13 11:25:34 +01:00
John Masterson
e5627d8b9f Added links to ReadMe 2017-07-11 17:07:57 +01:00
Alberto
f8f0c21af2 Version 11.0.0 2017-06-26 16:40:27 +02:00
Sean Landsman
b54d549959 AG-528 Document and use in examples overriding of style in child (react component) divs 2017-06-14 15:32:09 +01:00
Sean Landsman
464a0ea9f4 AG-529 Implement improvements - esp around property changes 2017-06-14 13:15:54 +01:00
Sean Landsman
675cbe497c AG-538 Upgrade to React 15x
AG-539 React rich grid example - fix bugs
2017-06-13 18:15:05 +01:00
Sean Landsman
a4dbbf529d AG-534 Make use of new data retrieval & updating in trader dashboard 2017-06-13 15:17:30 +01:00
Alberto
89d36b0bf4 version 10.1.0 2017-06-08 10:56:03 +01:00
Alberto
9ff1a54e57 Merge branch 'latest' into 10.1.0
# Conflicts:
#	package.json
2017-06-08 10:31:06 +01:00
Sean Landsman
7ed2666aaf AG-420 Improve React implementation 2017-06-06 09:42:17 +01:00
Sean Landsman
76258a8ee4 AG-420 Improve React implementation 2017-06-06 09:39:10 +01:00
Sean Landsman
7564f1597a AG-420 Improve React implementation 2017-06-06 08:52:37 +01:00
Sean Landsman
5f42824c5e Fix dep issue 2017-06-01 20:52:37 +02:00
Sean Landsman
ef44ebc166 AG-420 Improve React implementation 2017-05-26 14:50:49 +01:00
Sean Landsman
9c3c7a77c0 AG-420 Improve React implementation 2017-05-26 14:49:34 +01:00
Sean Landsman
f8b8a0b066 AG-420 Improve React implementation 2017-05-26 11:12:43 +01:00
Sean Landsman
e1871affe3 AG-420 Improve React implementation 2017-05-26 10:31:12 +01:00
Sean Landsman
3226c0d6ba AG-420 Improve React implementation 2017-05-25 22:15:56 +01:00
Sean Landsman
1b3fc21107 AG-420 Improve React implementation 2017-05-25 19:02:14 +01:00
Sean Landsman
587af2e888 AG-420 Improve React implementation 2017-05-25 12:21:02 +01:00
Sean Landsman
4ef1959a52 AG-420 Improve React implementation 2017-05-24 14:28:07 +01:00
Sean Landsman
ddefba039a AG-420 Improve React implementation 2017-05-23 17:51:52 +01:00
Sean Landsman
06ebb8bf18 AG-420 Improve React implementation 2017-05-23 17:51:33 +01:00
Sean Landsman
e183a0503a AG-420 Improve React implementation 2017-05-23 15:10:27 +01:00
Sean Landsman
19771949cb AG-420 Improve React implementation 2017-05-23 11:43:35 +01:00
Alberto
5fbdb690af There is a dependency (to be eliminated) in the release script and the order of the projects in the package.json 2017-05-22 15:06:18 +01:00
Sean Landsman
93fd0fe112 AG-420 Improve React implementation 2017-05-22 15:03:35 +01:00
Sean Landsman
a8163cad16 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	package.json
2017-05-22 15:02:25 +01:00
Sean Landsman
3f54c23b4d AG-420 Improve React implementation 2017-05-22 15:01:21 +01:00
Alberto
00dd86f4c8 Merge branch '10.0.0' 2017-05-22 10:37:03 +01:00
Alberto
e8ba0413ae Version 10.0.0 2017-05-22 10:32:54 +01:00
Sean Landsman
a3a0336f6b AG-420 Improve React implementation 2017-05-19 15:58:12 +01:00
Sean Landsman
a438cb6169 AG-420 Improve React implementation 2017-05-19 11:28:58 +01:00
Sean Landsman
8677118a27 AG-420 Improve React implementation 2017-05-17 13:22:59 +01:00
Alberto
4069f6bc10 Merge branch '9.1.0' 2017-04-27 15:20:38 +02:00
Alberto
575edb5b85 Release 9.1.0 2017-04-27 15:18:12 +02:00
Alberto
042dfc62a6 Merge branch '9.0.0' 2017-03-29 17:13:06 +01:00
Alberto
d59b5e8071 Version 9.0.0 2017-03-29 17:11:37 +01:00
Alberto
afa03227e4 Full width renderer with react 2017-03-09 11:03:20 +01:00
Alberto
a2952230d0 Starting work of full width Renderer with React 2017-03-08 16:46:50 +01:00
Alberto
7f8d1bb3a7 Starting work of full width Renderer with React 2017-03-08 16:36:05 +01:00
Alberto
801bf16339 Merge branch '8.2.0' 2017-03-08 15:12:51 +01:00
Alberto
cf1be920ee Release 8.2.0 2017-03-08 14:43:47 +01:00
Sean Landsman
f8ad31c2e0 more docs work 2017-02-24 15:09:48 +00:00
Alberto
e026fb65e3 Merge branch '8.1.0' 2017-02-20 13:11:50 +00:00
Alberto
05a7371467 8.1.0 2017-02-20 12:49:05 +00:00
Alberto
4ea0b7f8b2 Merge branch '8.0.0' 2017-02-09 15:48:33 +00:00
Alberto
5904f47d55 8.0.0 2017-02-09 14:14:07 +00:00
Alberto
25b416b71d Getting ready for releasing 8.0.0 2017-02-09 13:59:26 +00:00
Alberto
abb7828921 Getting ready for releasing 8.0.0 2017-02-09 13:58:19 +00:00
Alberto
ad5fd2d1ce ag-49 Ng2 Header component 2017-02-07 10:00:52 +00:00
Alberto
6d686f1c02 ag-49 Custom Group Header example completed 2017-02-03 11:16:17 +00:00
Alberto
6376a24e3a ag-49 Custom Header example completed 2017-02-03 10:14:20 +00:00
Sean Landsman
9c8cf08d2a AG-195 Create react example using grouped data 2017-01-26 11:40:46 +00:00
Alberto
a8c70c0ae8 Adding the cellRenderer so the example and updating the docs to reflect the example changes 2017-01-23 14:58:26 +00:00
Alberto
3390411e8a React date component, polishing the UI and adding documentation 2017-01-23 12:30:15 +00:00
Alberto
a280e97763 Creating React component to manage the dates in the DateFilter 2017-01-20 15:58:01 +00:00
Alberto
5451b02acd Creating the scaffolding for the ag-grid-react DateComponent 2017-01-20 15:29:42 +00:00
Alberto
3613a26f48 Creating the scaffolding for the ag-grid-react DateComponent 2017-01-20 14:47:11 +00:00
Sean Landsman
4acdee4b72 AG-24 Add getFilterInstance to React Examples Project 2017-01-19 10:58:32 +00:00
Alberto
72dae24ebe 7.2.0 2017-01-17 17:05:38 +00:00
ceolter
0aa2f7a2be Merge remote-tracking branch 'origin/master' 2017-01-03 14:13:12 +00:00
ceolter
6a94aee204 fixed example, the percent bar was not correct height 2017-01-03 14:13:03 +00:00
Sean Landsman
e0dfa732c7 7.1.1 2016-12-22 15:16:58 +00:00
Sean Landsman
d26876876f 7.0.0 2016-11-30 16:41:47 +00:00
Sean Landsman
3148c047b6 6.4.0 2016-11-11 13:42:22 +00:00
seanlandsman
33b006b783 6.3.0 2016-11-04 15:45:15 +00:00
seanlandsman
3b5ee65790 Release of 6.2.0 2016-10-11 16:13:26 +01:00
Sean Landsman
4829b2f6cd Release of 6.1.0 2016-10-03 13:02:15 +01:00
Sean Landsman
67af64ea9d Release of 6.0.1 2016-09-15 15:05:56 +01:00
ceolter
99dfa1e4f9 new component model 2016-09-14 15:58:15 +01:00
ceolter
1e9ef53316 new component model 2016-09-14 15:02:35 +01:00
ceolter
b5949d8d5d new component model 2016-09-14 12:15:30 +01:00
ceolter
b4b8c85172 new component model 2016-09-12 22:20:20 +01:00
ceolter
c703eaf89c new component model 2016-09-12 13:45:37 +01:00
ceolter
8804a01de3 release 5.4.0 2016-09-09 14:21:19 +01:00
ceolter
0dc55bd393 release 5.3.2 2016-09-02 16:45:32 +01:00
109 changed files with 3792 additions and 723 deletions

2
.gitignore vendored
View File

@@ -1,2 +1,4 @@
/node_modules
/dist
package-lock.json
*.nosync

View File

@@ -1,20 +1,18 @@
ag-Grid React Example
==============
Examples of running ag-Grid inside React application.
## Examples
See the [www.ag-grid.com](http://www.ag-grid.com).
1. standard - shows a typical grid demonstrating many ag-Grid features
There are two examples:
standard - shows a typical grid demonstrating many ag-Grid features
large - shows a very large grid (767 columns and 1,000 rows) using React cell renderers
2. large - shows a very large grid (767 columns and 1,000 rows) using React cell renderers
See [www.ag-grid.com](http://www.ag-grid.com) for full documentation and examples.
Building
==============
To build:
- `npm install`
- `npm install webpack -g`
- `npm run standard` or `npm run large`
- `npm run examples`, `npm run large` or `npm run trader`
- navigate to localhost:8080

View File

@@ -0,0 +1,52 @@
const path = require('path');
const SRC_DIR = path.resolve(__dirname, '../src-examples');
module.exports = {
mode: 'production',
entry: SRC_DIR + "/index.js",
output: {
path: path.resolve(__dirname, '../'),
filename: "dist/react-examples.js"
},
module: {
rules: [
{
test: /\.css$/,
loader: "style-loader!css-loader"
},
{
test: /\.js$|\.jsx$/,
include: SRC_DIR,
loader: 'babel-loader',
query: {
presets: ['@babel/preset-react', '@babel/preset-env'],
plugins: [
require('@babel/plugin-proposal-function-bind'),
require('@babel/plugin-proposal-class-properties')
]
}
},
{
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
loader: 'file-loader?name=[path]/[name].[ext]'
}
]
},
resolve: {
alias: {
// "@ag-grid-community/core/modules": path.resolve('./node_modules/@ag-grid-community/core/dist/es2015/modules'),
"@ag-grid-community/core": path.resolve('./node_modules/@ag-grid-community/core'),
// "ag-grid-enterprise": path.resolve('./node_modules/ag-grid-enterprise'),
react: path.resolve('./node_modules/react')
},
extensions: ['.js', '.jsx']
},
performance: {
hints: false
},
devServer: {
port: 8080,
historyApiFallback: true
}
};

View File

@@ -0,0 +1,46 @@
const path = require('path');
const SRC_DIR = path.resolve(__dirname, '../src-large-data');
module.exports = {
mode: 'production',
entry: SRC_DIR + "/index.js",
output: {
path: path.resolve(__dirname, '../'),
filename: "dist/react-large.js"
},
module: {
rules: [
{
test: /\.css$/,
loader: "style-loader!css-loader"
},
{
test: /\.js$|\.jsx$/,
include: SRC_DIR,
loader: 'babel-loader',
query: {
presets: ['@babel/preset-react', '@babel/preset-env'],
plugins: [
require('@babel/plugin-proposal-function-bind'),
require('@babel/plugin-proposal-class-properties')
]
}
}
]
},
resolve: {
alias: {
"@ag-grid-community/core": path.resolve('./node_modules/@ag-grid-community/core'),
"ag-grid-enterprise": path.resolve('./node_modules/ag-grid-enterprise'),
react: path.resolve('./node_modules/react')
},
extensions: ['.js', '.jsx']
},
performance: {
hints: false
},
devServer: {
port: 8080,
}
};

View File

@@ -0,0 +1,46 @@
const path = require('path');
const SRC_DIR = path.resolve(__dirname, '../src-trader-dashboard');
module.exports = {
mode: 'production',
entry: SRC_DIR + "/index.js",
output: {
path: path.resolve(__dirname, '../'),
filename: "dist/react-trader.js"
},
module: {
rules: [
{
test: /\.css$/,
loader: "style-loader!css-loader"
},
{
test: /\.js$|\.jsx$/,
include: SRC_DIR,
loader: 'babel-loader',
query: {
presets: ['@babel/preset-react', '@babel/preset-env'],
plugins: [
require('@babel/plugin-proposal-function-bind'),
require('@babel/plugin-proposal-class-properties')
]
}
}
]
},
resolve: {
alias: {
"@ag-grid-community/core": path.resolve('./node_modules/@ag-grid-community/core'),
"ag-grid-enterprise": path.resolve('./node_modules/ag-grid-enterprise'),
react: path.resolve('./node_modules/react')
},
extensions: ['.js', '.jsx']
},
performance: {
hints: false
},
devServer: {
port: 8080,
}
};

26
gulpfile.js Normal file
View File

@@ -0,0 +1,26 @@
const gulp = require('gulp');
const gulpTypescript = require('gulp-typescript');
const merge = require('merge2');
gulp.task('watch', watchTask);
gulp.task('rebuild-ag-grid-react', rebuildAgGridReact);
const tsConfig = '../ag-grid-react/tsconfig.json';
const tsProject = gulpTypescript.createProject(tsConfig);
function rebuildAgGridReact() {
const tsResult = gulp
.src('../ag-grid-react/src/**/*.ts')
.pipe(tsProject());
return merge([
tsResult.dts
.pipe(gulp.dest('node_modules/ag-grid-react/lib')),
tsResult.js
.pipe(gulp.dest('node_modules/ag-grid-react/lib'))
]);
}
function watchTask() {
gulp.watch(['../ag-grid-react/src/**/*'], rebuildAgGridReact);
}

View File

@@ -1,25 +0,0 @@
<html>
<!-- This index file is common for both the standard and large examples. As to which project you are running
depends on whether you use 'npm run standard' or 'npm run large' -->
<head>
<meta charset="utf-8">
<script type="text/javascript" src="dist/bundle.js" charset="utf-8"></script>
<!-- Example uses font awesome icons -->
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
</head>
<!-- Put some spacing around the buttons - not needed, just makes it look nice -->
<style>
button {
margin-left: 4px;
margin-right: 4px;
}
</style>
<body>
<div id="myAppContainer"></div>
</body>
</html>

View File

@@ -1,16 +1,30 @@
{
"name": "ag-grid-react-example",
"version": "5.3.2",
"version": "23.0.2",
"description": "Example Reach applicaiton using ag-Grid.",
"main": "dist/ag-grid-react-example.js",
"scripts": {
"standard": "webpack-dev-server --config webpack.config.standard.js --progress --colors --hot --inline",
"large": "webpack-dev-server --config webpack.config.large.js --progress --colors --hot --inline"
"trader": "webpack-dev-server --content-base src-trader-dashboard/ --config config/webpack.config.trader.js --mode development --open",
"examples": "webpack-dev-server --content-base src-examples/ --config config/webpack.config.examples.js --mode development --open",
"large": "webpack-dev-server --content-base src-large-data/ --config config/webpack.config.large.js --mode development --open",
"clean": "rimraf dist",
"mkdirs": "mkdirp dist/trader/dist dist/examples/dist",
"copy-examples": "ncp src-examples/images dist/examples/images && ncp src-examples/index.html dist/examples/index.html && ncp dist/react-examples.js dist/examples/dist/react-examples.js && ncp src-examples/ dist/examples/src",
"copy-trader": "ncp src-trader-dashboard/index.html dist/trader/index.html && ncp dist/react-trader.js dist/trader/dist/react-trader.js",
"copy": "npm run copy-examples && npm run copy-trader",
"build-large": "webpack --config config/webpack.config.large.js --progress --profile --bail",
"build-examples": "webpack --config config/webpack.config.examples.js --progress --profile --bail",
"build-dashboard": "webpack --config config/webpack.config.trader.js --progress --profile --bail",
"build-all": "npm run build-examples && npm run build-dashboard",
"build": "npm run clean && npm run mkdirs && npm run build-all && npm run copy",
"start": "npm run examples",
"test": "./ts-tests/runTsTests.sh"
},
"repository": {
"type": "git",
"url": "https://github.com/ceolter/ag-grid-react-example.git"
"url": "https://github.com/ag-grid/ag-grid-react-example.git"
},
"private": true,
"keywords": [
"react",
"grid",
@@ -20,23 +34,43 @@
"author": "Niall Crosby <niall.crosby@gmail.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/ceolter/ag-grid-react-example/issues"
"url": "https://github.com/ag-grid/ag-grid-react-example/issues"
},
"homepage": "http://www.ag-grid.com/",
"devDependencies": {
"babel-loader": "6.2.1",
"babel-preset-es2015": "6.3.13",
"babel-preset-react": "6.3.13",
"css-loader": "0.23.1",
"style-loader": "0.13.0",
"webpack": "1.12.11",
"webpack-dev-server": "^1.14.1"
"@ag-grid-community/core": "~23.0.0",
"@babel/core": "7.4.4",
"@babel/plugin-proposal-class-properties": "7.4.4",
"@babel/plugin-proposal-function-bind": "7.2.0",
"@babel/preset-env": "7.4.4",
"@babel/preset-react": "7.0.0",
"@types/react": "16.9.2",
"@types/react-dom": "16.9.0",
"babel-loader": "8.0.6",
"css-loader": "2.1.1",
"file-loader": "3.0.1",
"gulp": "3.9.1",
"merge2": "1.2.3",
"mkdirp": "0.5.1",
"ncp": "2.0.0",
"prop-types": "15.7.2",
"rimraf": "2.6.3",
"style-loader": "0.23.1",
"typescript": "3.4.5",
"webpack": "4.31.0",
"webpack-cli": "3.3.2",
"webpack-dev-server": "3.4.1"
},
"dependencies": {
"react": "0.14.6",
"react-dom": "0.14.6",
"ag-grid": "~5.3.0",
"ag-grid-enterprise": "~5.3.0",
"ag-grid-react": "~5.3.2"
"@ag-grid-enterprise/all-modules": "~23.0.0",
"@ag-grid-community/react": "~23.0.0",
"bootstrap": "4.4.1",
"d3": "4.9.1",
"lodash": "4.17.11",
"react": "16.8.6",
"react-dom": "16.8.6",
"react-redux": "7.0.3",
"react-router-dom": "5.0.0",
"redux": "4.0.1"
}
}
}

37
src-examples/App.jsx Normal file
View File

@@ -0,0 +1,37 @@
import React, {Component} from "react";
import {Redirect, Route, Switch} from "react-router-dom";
import NavItem from "./NavItem";
import RichGridDeclarativeExample from "./richGridDeclarativeExample/RichGridDeclarativeExample";
import SimpleReduxDynamicExample from "./simpleReduxDynamicComponentExample/SimpleReduxExample";
import SimpleReduxHookExample from "./simpleReduxHooksExample/SimpleReduxHookExample";
const SideBar = () => (
<div style={{float: "left", width: 335, marginRight: 25}}>
<ul className="nav nav-pills">
<NavItem to='/rich-grid-declarative'>Rich Grid with Declarative Markup</NavItem>
<NavItem to='/simple-redux-dynamic'>Simple Redux Dynamic Component Example</NavItem>
<NavItem to='/simple-redux-hook'>Simple React Hook Component Example</NavItem>
</ul>
</div>
);
class App extends Component {
render() {
return (
<div style={{display: "inline-block", width: "100%"}}>
<SideBar/>
<div style={{float: "left"}}>
<Switch>
<Redirect from="/" exact to="/rich-grid-declarative"/>
<Route exact path='/rich-grid-declarative' component={RichGridDeclarativeExample}/>
<Route exact path='/simple-redux-dynamic' component={SimpleReduxDynamicExample}/>
<Route exact path='/simple-redux-hook' component={SimpleReduxHookExample}/>
</Switch>
</div>
</div>
)
}
}
export default App

20
src-examples/NavItem.jsx Normal file
View File

@@ -0,0 +1,20 @@
import React from 'react'
import * as PropTypes from 'prop-types';
import {Link, Route} from 'react-router-dom'
// for bootstrap li active functionality
export default function NavItem({children, to, exact}) {
return (
<Route path={to} exact={exact} children={({match}) => (
<li className="nav-item">
<Link className={match ? 'nav-link active' : 'nav-link'} to={to}>{children}</Link>
</li>
)}/>
)
}
NavItem.propTypes = {
to: PropTypes.string.isRequired,
exact: PropTypes.bool,
children: PropTypes.node.isRequired,
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 606 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 739 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

View File

Before

Width:  |  Height:  |  Size: 883 B

After

Width:  |  Height:  |  Size: 883 B

View File

Before

Width:  |  Height:  |  Size: 902 B

After

Width:  |  Height:  |  Size: 902 B

View File

Before

Width:  |  Height:  |  Size: 953 B

After

Width:  |  Height:  |  Size: 953 B

View File

Before

Width:  |  Height:  |  Size: 394 B

After

Width:  |  Height:  |  Size: 394 B

View File

Before

Width:  |  Height:  |  Size: 893 B

After

Width:  |  Height:  |  Size: 893 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

BIN
src-examples/images/sun.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 570 B

136
src-examples/index.html Normal file
View File

@@ -0,0 +1,136 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script type="text/javascript" src="../dist/react-examples.js" charset="utf-8"></script>
<!-- Example uses font awesome icons -->
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
<style>
html, body {
height: 100%;
}
body {
line-height: 1
}
h1 {
font-size: 1.5rem;
}
div.card-body > a {
margin-top: 5px
}
.btn-primary {
margin-right: 5px;
}
button {
margin-left: 4px;
margin-right: 4px;
}
.align-right {
text-align: right
}
.customHeaderMenuButton {
margin-left: 4px;
float: left;
}
.customHeaderLabel {
margin-left: 5px;
float: left;
}
.customSortDownLabel {
float: left;
margin-left: 10px;
}
.customSortUpLabel {
float: left;
margin-left: 3px;
}
.customSortRemoveLabel {
float: left;
font-size: 11px;
margin-left: 3px;
}
.active {
color: cornflowerblue;
}
.hidden {
display: none;
}
.customHeaderLabel {
margin-left: 5px;
float: left;
}
.customExpandButton {
float: right;
margin-left: 3px;
}
.expanded {
animation-name: toExpanded;
animation-duration: 1s;
-ms-transform: rotate(180deg); /* IE 9 */
-webkit-transform: rotate(180deg); /* Chrome, Safari, Opera */
transform: rotate(180deg);
}
.collapsed {
color: cornflowerblue;
animation-name: toCollapsed;
animation-duration: 1s;
-ms-transform: rotate(0deg); /* IE 9 */
-webkit-transform: rotate(0deg); /* Chrome, Safari, Opera */
transform: rotate(0deg);
}
@keyframes toExpanded {
from {
color: cornflowerblue;
-ms-transform: rotate(0deg); /* IE 9 */
-webkit-transform: rotate(0deg); /* Chrome, Safari, Opera */
transform: rotate(0deg);
}
to {
color: black;
-ms-transform: rotate(180deg); /* IE 9 */
-webkit-transform: rotate(180deg); /* Chrome, Safari, Opera */
transform: rotate(180deg);
}
}
@keyframes toCollapsed {
from {
color: black;
-ms-transform: rotate(180deg); /* IE 9 */
-webkit-transform: rotate(180deg); /* Chrome, Safari, Opera */
transform: rotate(180deg);
}
to {
color: cornflowerblue;
-ms-transform: rotate(0deg); /* IE 9 */
-webkit-transform: rotate(0deg); /* Chrome, Safari, Opera */
transform: rotate(0deg);
}
}
</style>
</head>
<body>
<div id="app"></div>
</body>
</html>

25
src-examples/index.js Normal file
View File

@@ -0,0 +1,25 @@
'use strict';
import React from "react";
import {render} from "react-dom";
import {BrowserRouter} from "react-router-dom";
import "@ag-grid-enterprise/all-modules/dist/styles/ag-grid.css";
import "@ag-grid-enterprise/all-modules/dist/styles/ag-theme-balham.css";
import "../node_modules/bootstrap/dist/css/bootstrap.css";
import App from "./App";
// only required when using enterprise features
// import {LicenseManager} from "@ag-grid-enterprise/all-modules";
// LicenseManager.setLicenseKey("<your license key>");
document.addEventListener('DOMContentLoaded', () => {
render(
<BrowserRouter>
<App/>
</BrowserRouter>,
document.querySelector('#app')
);
});

View File

@@ -0,0 +1,171 @@
import React from "react";
import * as PropTypes from "prop-types";
// Date Component to be used in the date filter.
// This is a very simple example of how a React component can be plugged as a DateComponentFramework
// as you can see, the only requirement is that the React component implements the required methods
// getDate and setDate and that it calls back into props.onDateChanged every time that the date changes.
export default class DateComponent extends React.Component {
constructor(props) {
super(props);
//The state of this component is represented of:
// The current date it holds, null by default, null if the date typed by the user is not valid or fields are blank
// The current values that the user types in the input boxes, by default ''
//The textBoxes state is necessary since it can be set from ag-Grid. This can be seen in this example through
// the usage of the button DOB equals to 01/01/2000 in the example page.
this.state = {
date: null,
textBoxes: {
dd: '',
mm: '',
yyyy: ''
}
}
}
render() {
//Inlining styles to make simpler the component
let filterStyle = {
margin: '2px'
};
let ddStyle = {
width: '30px'
};
let mmStyle = {
width: '30px'
};
let yyyyStyle = {
width: '40px'
};
let resetStyle = {
padding: '2px',
backgroundColor: 'red',
borderRadius: '3px',
fontSize: '10px',
marginRight: '5px',
color: 'white'
};
return (
<div style={filterStyle}>
<span style={resetStyle} onClick={this.resetDate.bind(this)}>x</span>
<input onChange={this.onDateChanged.bind(this)} ref="dd" placeholder="dd" style={ddStyle}
value={this.state.textBoxes.dd} maxLength="2"/>/
<input onChange={this.onDateChanged.bind(this)} ref="mm" placeholder="mm" style={mmStyle}
value={this.state.textBoxes.mm} maxLength="2"/>/
<input onChange={this.onDateChanged.bind(this)} ref="yyyy" placeholder="yyyy" style={yyyyStyle}
value={this.state.textBoxes.yyyy} maxLength="4"/>
</div>
);
}
//*********************************************************************************
// METHODS REQUIRED BY AG-GRID
//*********************************************************************************
getDate() {
//ag-grid will call us here when in need to check what the current date value is hold by this
//component.
return this.state.date;
}
setDate(date) {
//ag-grid will call us here when it needs this component to update the date that it holds.
this.setState({
date,
textBoxes: {
dd: date ? date.getDate() : '',
mm: date ? date.getMonth() + 1 : '',
yyyy: date ? date.getFullYear() : ''
}
})
}
//*********************************************************************************
// LINKS THE INTERNAL STATE AND AG-GRID
//*********************************************************************************
updateAndNotifyAgGrid(date, textBoxes) {
this.setState({
date: date,
textBoxes: textBoxes
},
//Callback after the state is set. This is where we tell ag-grid that the date has changed so
//it will proceed with the filtering and we can then expect ag-Grid to call us back to getDate
this.props.onDateChanged
);
}
//*********************************************************************************
// LINKING THE UI, THE STATE AND AG-GRID
//*********************************************************************************
resetDate() {
let date = null;
let textBoxes = {
dd: '',
mm: '',
yyyy: '',
};
this.updateAndNotifyAgGrid(date, textBoxes)
}
onDateChanged() {
let date = this.parseDate(this.refs.dd.value, this.refs.mm.value, this.refs.yyyy.value);
let textBoxes = {
dd: this.refs.dd.value,
mm: this.refs.mm.value,
yyyy: this.refs.yyyy.value,
};
this.updateAndNotifyAgGrid(date, textBoxes)
}
//*********************************************************************************
// INTERNAL LOGIC
//*********************************************************************************
parseDate(dd, mm, yyyy) {
//If any of the three input date fields are empty, stop and return null
if (dd.trim() === '' || mm.trim() === '' || yyyy.trim() === '') {
return null;
}
let day = Number(dd);
let month = Number(mm);
let year = Number(yyyy);
let date = new Date(year, month - 1, day);
//If the date is not valid
if (isNaN(date.getTime())) {
return null;
}
//Given that new Date takes any garbage in, it is possible for the user to specify a new Date
//like this (-1, 35, 1) and it will return a valid javascript date. In this example, it will
//return Sat Dec 01 1 00:00:00 GMT+0000 (GMT) - Go figure...
//To ensure that we are not letting non sensical dates to go through we check that the resultant
//javascript date parts (month, year and day) match the given date fields provided as parameters.
//If the javascript date parts don't match the provided fields, we assume that the input is non
//sensical... ie: Day=-1 or month=14, if this is the case, we return null
//This also protects us from non sensical dates like dd=31, mm=2 of any year
if (date.getDate() != day || date.getMonth() + 1 != month || date.getFullYear() != year) {
return null;
}
return date;
}
}
// the grid will always pass in one props called 'params',
// which is the grid passing you the params for the cellRenderer.
// this piece is optional. the grid will always pass the 'params'
// props, so little need for adding this validation meta-data.
DateComponent.propTypes = {
params: PropTypes.object
};

View File

@@ -0,0 +1,46 @@
import React from 'react';
import * as PropTypes from 'prop-types';
// Header component to be used as default for all the columns.
export default class HeaderGroupComponent extends React.Component {
constructor(props) {
super(props);
this.props.columnGroup.getOriginalColumnGroup().addEventListener('expandedChanged', this.onExpandChanged.bind(this));
this.state = {
expanded: null
};
}
componentDidMount() {
this.onExpandChanged();
}
render() {
let arrowClassName = "customExpandButton " + (this.state.expanded ? " expanded" : " collapsed");
return <div>
<div className="customHeaderLabel"> {this.props.displayName}</div>
<div onClick={this.expandOrCollapse.bind(this)} className={arrowClassName}><i
className="fa fa-arrow-right"/></div>
</div>
}
expandOrCollapse() {
this.props.setExpanded(!this.state.expanded);
};
onExpandChanged() {
this.setState({
expanded: this.props.columnGroup.getOriginalColumnGroup().isExpanded()
})
}
}
// the grid will always pass in one props called 'params',
// which is the grid passing you the params for the cellRenderer.
// this piece is optional. the grid will always pass the 'params'
// props, so little need for adding this validation meta-data.
HeaderGroupComponent.propTypes = {
params: PropTypes.object
};

View File

@@ -0,0 +1,117 @@
import React from 'react';
import * as PropTypes from 'prop-types';
const KEY_BACKSPACE = 8;
const KEY_DELETE = 46;
const KEY_F2 = 113;
// cell renderer for the proficiency column. this is a very basic cell editor,
export default class NameCellEditor extends React.Component {
constructor(props) {
super(props);
// the entire ag-Grid properties are passed as one single object inside the params
this.state = this.createInitialState(props);
}
// work out how to present the data based on what the user hit. you don't need to do any of
// this for your ag-Grid cellEditor to work, however it makes sense to do this so the user
// experience is similar to Excel
createInitialState(props) {
let startValue;
const putCursorAtEndOnFocus = false;
const highlightAllOnFocus = false;
if (props.keyPress === KEY_BACKSPACE || props.keyPress === KEY_DELETE) {
// if backspace or delete pressed, we clear the cell
startValue = '';
} else if (props.charPress) {
// if a letter was pressed, we start with the letter
startValue = props.charPress;
} else {
// otherwise we start with the current value
startValue = props.value;
if (props.keyPress === KEY_F2) {
this.putCursorAtEndOnFocus = true;
} else {
this.highlightAllOnFocus = true;
}
}
return {
value: startValue,
putCursorAtEndOnFocus: putCursorAtEndOnFocus,
highlightAllOnFocus: highlightAllOnFocus
}
}
render() {
return (
<input ref="textField" value={this.state.value} onChange={this.onChangeListener.bind(this)}/>
);
}
onChangeListener(event) {
// if doing React, you will probably be using a library for managing immutable
// objects better. to keep this example simple, we don't use one.
const newState = {
value: event.target.value,
putCursorAtEndOnFocus: this.state.putCursorAtEndOnFocus,
highlightAllOnFocus: this.state.highlightAllOnFocus
};
this.setState(newState);
}
// called by ag-Grid, to get the final value
getValue() {
return this.state.value;
}
// cannot use componentDidMount because although the component might be ready from React's point of
// view, it may not yet be in the browser (put in by ag-Grid) so focus will not work
afterGuiAttached() {
// get ref from React component
const eInput = this.refs.textField;
eInput.focus();
if (this.highlightAllOnFocus) {
eInput.select();
} else {
// when we started editing, we want the carot at the end, not the start.
// this comes into play in two scenarios: a) when user hits F2 and b)
// when user hits a printable character, then on IE (and only IE) the carot
// was placed after the first character, thus 'apply' would end up as 'pplea'
const length = eInput.value ? eInput.value.length : 0;
if (length > 0) {
eInput.setSelectionRange(length, length);
}
}
}
// if we want the editor to appear in a popup, then return true.
isPopup() {
return false;
}
// return true here if you don't want to allow editing on the cell.
isCancelBeforeStart() {
return false;
}
// just to demonstrate, if you type in 'cancel' then the edit will not take effect
isCancelAfterEnd() {
if (this.state.value && this.state.value.toUpperCase() === 'CANCEL') {
return true;
} else {
return false;
}
}
}
// the grid will always pass in one props called 'params',
// which is the grid passing you the params for the cellRenderer.
// this piece is optional. the grid will always pass the 'params'
// props, so little need for adding this validation meta-data.
NameCellEditor.propTypes = {
params: PropTypes.object
};

View File

@@ -0,0 +1,24 @@
import React from 'react';
// cell renderer for the proficiency column. this is a very basic cell renderer,
// it is arguable that we should not of used React and just returned a string of
// html as a normal ag-Grid cellRenderer.
export default class ProficiencyCellRenderer extends React.Component {
render() {
let backgroundColor;
if (this.props.value < 20) {
backgroundColor = 'red';
} else if (this.props.value < 60) {
backgroundColor = '#ff9900';
} else {
backgroundColor = '#00A000';
}
return (
<div className="div-percent-bar" style={{width: this.props.value + '%', backgroundColor: backgroundColor}}>
<div className="div-percent-value">{this.props.value}%</div>
</div>
);
}
}

View File

@@ -0,0 +1,90 @@
import React from 'react';
const PROFICIENCY_NAMES = ['No Filter', 'Above 40%', 'Above 60%', 'Above 80%'];
// the proficiency filter component. this demonstrates how to integrate
// a React filter component with ag-Grid.
export default class ProficiencyFilter extends React.Component {
constructor(props) {
super(props);
this.state = {
selected: PROFICIENCY_NAMES[0]
};
}
// called by agGrid
doesFilterPass(params) {
const value = this.props.valueGetter(params);
const valueAsNumber = parseFloat(value);
switch (this.state.selected) {
case PROFICIENCY_NAMES[1] :
return valueAsNumber >= 40;
case PROFICIENCY_NAMES[2] :
return valueAsNumber >= 60;
case PROFICIENCY_NAMES[3] :
return valueAsNumber >= 80;
default :
return true;
}
};
// called by agGrid
isFilterActive() {
return this.state.selected !== PROFICIENCY_NAMES[0];
};
onButtonPressed(name) {
const newState = {selected: name};
// set the state, and once it is done, then call filterChangedCallback
this.setState(newState, this.props.filterChangedCallback);
}
getModel() {
return ''
}
setModel(model) {
}
render() {
const rows = [];
PROFICIENCY_NAMES.forEach((name) => {
const selected = this.state.selected === name;
rows.push(
<div key={name} style={{marginTop: 3}}>
<label style={{paddingLeft: 4}}>
<input type="radio" checked={selected} name={Math.random()}
onChange={this.onButtonPressed.bind(this, name)}/>
{name}
</label>
</div>
);
});
return (
<div>
<div style={{
textAlign: 'center',
background: 'lightgray',
width: '100%',
display: 'block',
borderBottom: '1px solid grey'
}}>
<b>Custom Proficiency Filter</b>
</div>
{rows}
</div>
);
}
// these are other method that agGrid calls that we
// could of implemented, but they are optional and
// we have no use for them in this particular filter.
//getApi() {}
//afterGuiAttached(params) {}
//onNewRowsLoaded() {}
//onAnyFilterChanged() {}
}

View File

@@ -0,0 +1,129 @@
export default class RefData {
}
RefData.FIRST_NAMES = [
"Sophie", "Isabelle", "Emily", "Olivia", "Lily", "Chloe", "Isabella",
"Amelia", "Jessica", "Sophia", "Ava", "Charlotte", "Mia", "Lucy", "Grace", "Ruby",
"Ella", "Evie", "Freya", "Isla", "Poppy", "Daisy", "Layla"
];
RefData.LAST_NAMES = [
"Beckham", "Black", "Braxton", "Brennan", "Brock", "Bryson", "Cadwell",
"Cage", "Carson", "Chandler", "Cohen", "Cole", "Corbin", "Dallas", "Dalton", "Dane",
"Donovan", "Easton", "Fisher", "Fletcher", "Grady", "Greyson", "Griffin", "Gunner",
"Hayden", "Hudson", "Hunter", "Jacoby", "Jagger", "Jaxon", "Jett", "Kade", "Kane",
"Keating", "Keegan", "Kingston", "Kobe"
];
RefData.COUNTRY_CODES = {
Ireland: "ie",
Spain: "es",
"United Kingdom": "gb",
France: "fr",
Germany: "de",
Sweden: "se",
Italy: "it",
Greece: "gr",
Iceland: "is",
Portugal: "pt",
Malta: "mt",
Norway: "no",
Brazil: "br",
Argentina: "ar",
Colombia: "co",
Peru: "pe",
Venezuela: "ve",
Uruguay: "uy"
};
RefData.COUNTRIES = [
{country: "Ireland", continent: "Europe", language: "English"},
{country: "Spain", continent: "Europe", language: "Spanish"},
{country: "United Kingdom", continent: "Europe", language: "English"},
{country: "France", continent: "Europe", language: "French"},
{country: "Germany", continent: "Europe", language: "(other)"},
{country: "Sweden", continent: "Europe", language: "(other)"},
{country: "Norway", continent: "Europe", language: "(other)"},
{country: "Italy", continent: "Europe", language: "(other)"},
{country: "Greece", continent: "Europe", language: "(other)"},
{country: "Iceland", continent: "Europe", language: "(other)"},
{country: "Portugal", continent: "Europe", language: "Portuguese"},
{country: "Malta", continent: "Europe", language: "(other)"},
{country: "Brazil", continent: "South America", language: "Portuguese"},
{country: "Argentina", continent: "South America", language: "Spanish"},
{country: "Colombia", continent: "South America", language: "Spanish"},
{country: "Peru", continent: "South America", language: "Spanish"},
{country: "Venezuela", continent: "South America", language: "Spanish"},
{country: "Uruguay", continent: "South America", language: "Spanish"}
];
RefData.DOB = [
new Date(2000, 0, 1),
new Date(2001, 1, 2),
new Date(2002, 2, 3),
new Date(2003, 3, 4),
new Date(2004, 4, 5),
new Date(2005, 5, 6),
new Date(2006, 6, 7),
new Date(2007, 7, 8),
new Date(2008, 8, 9),
new Date(2009, 9, 10),
new Date(2010, 10, 11),
new Date(2011, 11, 12)
];
RefData.ADDRESSES = [
'1197 Thunder Wagon Common, Cataract, RI, 02987-1016, US, (401) 747-0763',
'3685 Rocky Glade, Showtucket, NU, X1E-9I0, CA, (867) 371-4215',
'3235 High Forest, Glen Campbell, MS, 39035-6845, US, (601) 638-8186',
'2234 Sleepy Pony Mall , Drain, DC, 20078-4243, US, (202) 948-3634',
'2722 Hazy Turnabout, Burnt Cabins, NY, 14120-5642, US, (917) 604-6597',
'6686 Lazy Ledge, Two Rock, CA, 92639-3020, US, (619) 901-9911',
'2000 Dewy Limits, Wacahoota, NF, A4L-2V9, CA, (709) 065-3959',
'7710 Noble Pond Avenue, Bolivia, RI, 02931-1842, US, (401) 865-2160',
'3452 Sunny Vale, Pyro, ON, M8V-4Z0, CA, (519) 072-8609',
'4402 Dusty Cove, Many Farms, UT, 84853-8223, US, (435) 518-0673',
'5198 Silent Parade, Round Bottom, MD, 21542-9798, US, (301) 060-7245',
'8550 Shady Moor, Kitty Fork, CO, 80941-6207, US, (303) 502-3767',
'2131 Old Dell, Merry Midnight, AK, 99906-8842, US, (907) 369-2206',
'7390 Harvest Crest, Mosquito Crossing, RI, 02957-6116, US, (401) 463-6348',
'874 Little Point, Hot Coffee, BC, V3U-2P6, CA, (250) 706-9207',
'8834 Stony Pioneer Heights, Newlove, OR, 97419-8670, US, (541) 408-2213',
'9829 Grand Beach, Flint, UT, 84965-9900, US, (435) 700-5161',
'3799 Cozy Blossom Ramp, Ptarmigan, MS, 38715-0313, US, (769) 740-1526',
'3254 Silver Island Loop, Maunaloa, DE, 19869-3169, US, (302) 667-7671',
'1081 Middle Wood, Taylors Gut Landing, OR, 97266-2873, US, (541) 357-6310',
'1137 Umber Trail, Shacktown, NW, X3U-5Y8, CA, (867) 702-6883',
'9914 Hidden Bank, Wyoming, MO, 64635-9665, US, (636) 280-4192',
'7080 Misty Nectar Townline, Coward, AB, T9U-3N4, CA, (403) 623-2838',
'1184 Wishing Grounds, Vibank, NW, X7D-0V9, CA, (867) 531-2730',
'126 Easy Pointe, Grandview Beach, KY, 40928-9539, US, (502) 548-0956',
'6683 Colonial Street, Swan River, BC, V1A-9I8, CA, (778) 014-4257',
'960 Gentle Oak Lane, Shakopee, ND, 58618-6277, US, (701) 327-1219',
'6918 Cotton Pine Corner, Kenaston, IA, 52165-3975, US, (515) 906-7427',
'2368 Burning Woods, Ernfold, NY, 11879-9186, US, (646) 819-0355',
'5646 Quiet Shadow Chase, Tiger Tail, IA, 52283-5537, US, (712) 375-9225',
'5466 Foggy Mountain Dale, Sweet Home, MT, 59738-0251, US, (406) 881-1706',
'5313 Clear Willow Route, Amazon, BC, V0S-2S6, CA, (604) 340-7596',
'7000 Pleasant Autoroute, Spaceport City, UT, 84749-2448, US, (435) 154-3360',
'8359 Quaking Anchor Road, Gross, BC, V9O-0H5, CA, (250) 985-3859',
'5143 Amber Deer Hollow, New Deal, ND, 58446-0853, US, (701) 927-0322',
'6230 Jagged Bear Key, Young, AR, 72337-3811, US, (501) 805-7239',
'7207 Heather Vista, Devon, WY, 82520-1771, US, (307) 358-7092',
'9416 Red Rise Place, Spraytown, OK, 73809-4766, US, (580) 867-1973',
'3770 Golden Horse Diversion, Yelland, IL, 60471-1487, US, (224) 717-9349',
'4819 Honey Treasure Park, Alaska, NB, E1U-3I0, CA, (506) 656-9138',
'6187 Round Front, Land O Lakes, AK, 99873-6403, US, (907) 853-9063',
'9218 Crystal Highway, Pickelville, MT, 59847-9299, US, (406) 076-0024',
'6737 Bright Quay, Lazy Mountain, KY, 42390-4772, US, (606) 256-7288',
'237 Merry Campus, Twentysix, SC, 29330-4909, US, (864) 945-0157',
'446 Fallen Gate Rise, Petrolia, SC, 29959-9527, US, (864) 826-0553',
'2347 Indian Boulevard, Frisbee, VA, 23797-6458, US, (703) 656-8445',
'365 Emerald Grove Line, Level, NC, 28381-1514, US, (919) 976-7958',
'1207 Iron Extension, Klickitat, SC, 29197-8571, US, (803) 535-7888',
'6770 Cinder Glen, Caronport, OH, 45053-5002, US, (440) 369-4018',
'7619 Tawny Carrefour, Senlac, NV, 89529-9876, US, (775) 901-6433'];
RefData.IT_SKILLS = ['android', 'css', 'html5', 'mac', 'windows'];
RefData.IT_SKILLS_NAMES = ['Android', 'CSS', 'HTML 5', 'Mac', 'Windows'];

View File

@@ -1,4 +1,3 @@
.ag-cell {
padding-top: 2px !important;
padding-bottom: 2px !important;
@@ -10,7 +9,7 @@ label {
.div-percent-bar {
display: inline-block;
height: 100%;
height: 20px;
position: relative;
}

View File

@@ -0,0 +1,261 @@
import React, { Component } from "react";
import { AgGridColumn, AgGridReact } from "@ag-grid-community/react";
import RowDataFactory from "./RowDataFactory";
import DateComponent from "./DateComponent.jsx";
import SkillsCellRenderer from './SkillsCellRenderer.jsx';
import NameCellEditor from './NameCellEditor.jsx';
import ProficiencyCellRenderer from './ProficiencyCellRenderer.jsx';
import RefData from './RefData';
import SkillsFilter from './SkillsFilter.jsx';
import ProficiencyFilter from './ProficiencyFilter.jsx';
import HeaderGroupComponent from './HeaderGroupComponent.jsx';
import SortableHeaderComponent from './SortableHeaderComponent.jsx';
import "./RichGridDeclarativeExample.css";
// for community features
// import {AllCommunityModules} from "@ag-grid-community/all-modules";
// for enterprise features
import { AllModules } from "@ag-grid-enterprise/all-modules";
export default class RichGridDeclarativeExample extends Component {
constructor(props) {
super(props);
this.state = {
quickFilterText: null,
sideBar: false,
rowData: new RowDataFactory().createRowData(),
rowCount: null,
icons: {
columnRemoveFromGroup: '<i class="fa fa-times"/>',
filter: '<i class="fa fa-filter"/>',
sortAscending: '<i class="fa fa-long-arrow-down"/>',
sortDescending: '<i class="fa fa-long-arrow-up"/>',
groupExpanded: '<i class="fa fa-minus-square-o"/>',
groupContracted: '<i class="fa fa-plus-square-o"/>'
}
};
}
/* Grid Events we're listening to */
onGridReady = (params) => {
this.api = params.api;
this.columnApi = params.columnApi;
this.calculateRowCount();
};
onCellClicked = (event) => {
console.log('onCellClicked: ' + event.data.name + ', col ' + event.colIndex);
};
onRowSelected = (event) => {
console.log('onRowSelected: ' + event.node.data.name);
};
/* Demo related methods */
onToggleSidebar = (event) => {
this.setState({ sideBar: event.target.checked });
};
deselectAll() {
this.api.deselectAll();
}
onQuickFilterText = (event) => {
this.setState({ quickFilterText: event.target.value });
};
onRefreshData = () => {
this.setState({
rowData: new RowDataFactory().createRowData()
});
};
invokeSkillsFilterMethod = () => {
this.api.getFilterInstance('skills', (instance) => {
let componentInstance = instance.getFrameworkComponentInstance();
componentInstance.helloFromSkillsFilter();
});
};
dobFilter = () => {
this.api.getFilterInstance('dob', (dateFilterComponent) => {
dateFilterComponent.setModel({
type: 'equals',
dateFrom: '2000-01-01'
});
// as the date filter is a React component, and its using setState internally, we need
// to allow time for the state to be set (as setState is an async operation)
// simply wait for the next tick
setTimeout(() => {
this.api.onFilterChanged();
});
});
};
calculateRowCount = () => {
if (this.api && this.state.rowData) {
const model = this.api.getModel();
const totalRows = this.state.rowData.length;
const processedRows = model.getRowCount();
this.setState({
rowCount: processedRows.toLocaleString() + ' / ' + totalRows.toLocaleString()
});
}
};
static countryCellRenderer(params) {
if (params.value) {
return `<img border='0' width='15' height='10' style='margin-bottom: 2px' src='http://flags.fmcdn.net/data/flags/mini/${RefData.COUNTRY_CODES[params.value]}.png'> ${params.value}`;
} else {
return null;
}
}
static dateCellRenderer(params) {
return RichGridDeclarativeExample.pad(params.value.getDate(), 2) + '/' +
RichGridDeclarativeExample.pad(params.value.getMonth() + 1, 2) + '/' +
params.value.getFullYear();
}
static pad(num, totalStringSize) {
let asString = num + "";
while (asString.length < totalStringSize) asString = "0" + asString;
return asString;
}
render() {
return (
<div style={{ width: '900px' }}>
<h1>Rich Grid with Declarative Markup Example</h1>
<div style={{ display: "inline-block", width: "100%" }}>
<div style={{ float: "left" }}>
<b>Employees Skills and Contact Details: </b>{this.state.rowCount}
</div>
</div>
<div style={{ marginTop: 10 }}>
<div>
<span>
Grid API:
<button onClick={() => {
this.api.selectAll();
}} className="btn btn-primary">Select All</button>
<button onClick={() => {
this.api.deselectAll();
}} className="btn btn-primary">Clear Selection</button>
</span>
<span style={{ float: "right" }}>
Column API:
<button onClick={() => {
this.columnApi.setColumnVisible('country', false);
}} className="btn btn-primary">Hide Country Column</button>
<button onClick={() => {
this.columnApi.setColumnVisible('country', true);
}} className="btn btn-primary">Show Country Column</button>
</span>
</div>
<div style={{ display: "inline-block", width: "100%", marginTop: 10, marginBottom: 10 }}>
<div style={{ float: "left" }}>
<button onClick={this.onRefreshData} className="btn btn-primary">Refresh Data</button>
</div>
<div style={{ float: "right" }}>
Filter API:
<button onClick={this.invokeSkillsFilterMethod}
className="btn btn-primary">Invoke Skills Filter Method
</button>
<button onClick={this.dobFilter} className="btn btn-primary">DOB equals to 01/01/2000
</button>
</div>
</div>
<div style={{ display: "inline-block", width: "100%", marginTop: 10, marginBottom: 10 }}>
<div style={{ float: "left" }}>
<label htmlFor="sideBarToggle">Show Side Bar&nbsp;</label>
<input type="checkbox" id="sideBarToggle" onChange={this.onToggleSidebar} style={{ marginRight: 5 }} />
</div>
<div style={{ float: "right", marginLeft: 20 }}>
<label htmlFor="quickFilter">Quick Filter:&nbsp;</label>
<input type="text" id="quickFilter" onChange={this.onQuickFilterText} placeholder="Type text to filter..." />
</div>
</div>
<div style={{ height: 400, width: 900 }} className="ag-theme-balham">
<AgGridReact
// listening for events
onGridReady={this.onGridReady}
onRowSelected={this.onRowSelected}
onCellClicked={this.onCellClicked}
onModelUpdated={this.calculateRowCount}
// binding to simple properties
sideBar={this.state.sideBar}
quickFilterText={this.state.quickFilterText}
// binding to an object property
icons={this.state.icons}
// binding to array properties
rowData={this.state.rowData}
// register all modules (row model, csv/excel, row grouping etc)
modules={AllModules}
// no binding, just providing hard coded strings for the properties
// boolean properties will default to true if provided (ie suppressRowClickSelection => suppressRowClickSelection="true")
suppressRowClickSelection
rowSelection="multiple"
groupHeaders
// setting grid wide date component
dateComponentFramework={DateComponent}
// setting default column properties
defaultColDef={{
resizable: true,
sortable: true,
filter: true,
headerComponentFramework: SortableHeaderComponent,
headerComponentParams: {
menuIcon: 'fa-bars'
}
}}>
<AgGridColumn headerName="#" width={40}
checkboxSelection sortable={false} suppressMenu filter={false} pinned>
</AgGridColumn>
<AgGridColumn headerName="Employee" headerGroupComponentFramework={HeaderGroupComponent}>
<AgGridColumn field="name" width={150}
cellEditorFramework={NameCellEditor}
enableRowGroup enablePivot pinned editable />
<AgGridColumn field="country" width={150}
cellRenderer={RichGridDeclarativeExample.countryCellRenderer}
filterParams={{
cellRenderer: RichGridDeclarativeExample.countryCellRenderer,
cellHeight: 20
}}
enableRowGroup enablePivot pinned editable />
<AgGridColumn field="dob" width={175} headerName="DOB" filter="agDateColumnFilter"
pinned columnGroupShow="open"
cellRenderer={RichGridDeclarativeExample.dateCellRenderer} />
</AgGridColumn>
<AgGridColumn headerName="IT Skills">
<AgGridColumn field="skills" width={120} enableRowGroup enablePivot sortable={false}
cellRendererFramework={SkillsCellRenderer}
filterFramework={SkillsFilter} />
<AgGridColumn field="proficiency" width={160} enableValue
cellRendererFramework={ProficiencyCellRenderer}
filterFramework={ProficiencyFilter} />
</AgGridColumn>
<AgGridColumn headerName="Contact">
<AgGridColumn field="mobile" width={150} filter="text" />
<AgGridColumn field="landline" width={150} filter="text" />
<AgGridColumn field="address" width={500} filter="text" />
</AgGridColumn>
</AgGridReact>
</div>
</div>
</div>
);
}
}

View File

@@ -3,10 +3,10 @@ import RefData from './RefData';
export default class RowDataFactory {
createRowData() {
var rowData = [];
const rowData = [];
for (var i = 0; i < 1000; i++) {
var countryData = RefData.COUNTRIES[i % RefData.COUNTRIES.length];
for (let i = 0; i < 200; i++) {
const countryData = RefData.COUNTRIES[i % RefData.COUNTRIES.length];
rowData.push({
name: RefData.FIRST_NAMES[i % RefData.FIRST_NAMES.length] + ' ' + RefData.LAST_NAMES[i % RefData.LAST_NAMES.length],
skills: {
@@ -16,6 +16,7 @@ export default class RowDataFactory {
windows: Math.random() < 0.4,
css: Math.random() < 0.4
},
dob: RefData.DOB[i % RefData.DOB.length],
address: RefData.ADDRESSES[i % RefData.ADDRESSES.length],
years: Math.round(Math.random() * 100),
proficiency: Math.round(Math.random() * 100),
@@ -31,8 +32,8 @@ export default class RowDataFactory {
}
createRandomPhoneNumber() {
var result = '+';
for (var i = 0; i < 12; i++) {
let result = '+';
for (let i = 0; i < 12; i++) {
result += Math.round(Math.random() * 10);
if (i === 2 || i === 5 || i === 8) {
result += ' ';

View File

@@ -1,14 +1,15 @@
import React from 'react';
import * as PropTypes from 'prop-types';
import RefData from './RefData';
export default class SkillsCellRenderer extends React.Component {
render() {
var skills = [];
var rowData = this.props.params.data;
RefData.IT_SKILLS.forEach( (skill) => {
const skills = [];
const rowData = this.props.data;
RefData.IT_SKILLS.forEach((skill) => {
if (rowData && rowData.skills && rowData.skills[skill]) {
skills.push(<img key={skill} src={'images/skills/' + skill + '.png'} width={16} title={skill} />);
skills.push(<img key={skill} src={'images/skills/' + skill + '.png'} width={16} title={skill}/>);
}
});
@@ -22,5 +23,5 @@ export default class SkillsCellRenderer extends React.Component {
// this piece is optional. the grid will always pass the 'params'
// props, so little need for adding this validation meta-data.
SkillsCellRenderer.propTypes = {
params: React.PropTypes.object
params: PropTypes.object
};

View File

@@ -8,11 +8,9 @@ import RefData from './RefData';
// React design skills.
export default class SkillsFilter extends React.Component {
constructor() {
super();
constructor(props) {
super(props);
this.state = {
skills: RefData.IT_SKILLS,
skillNames: RefData.IT_SKILLS_NAMES,
android: false,
css: false,
html5: false,
@@ -21,18 +19,33 @@ export default class SkillsFilter extends React.Component {
};
}
// called by agGrid
init(params) {
this.filterChangedCallback = params.filterChangedCallback;
getModel() {
return {
android: this.state.android,
css: this.state.css,
html5: this.state.html5,
mac: this.state.mac,
windows: this.state.windows
}
}
setModel(model) {
this.setState({
android: model ? model.android : null,
css: model ? model.css : null,
html5: model ? model.html5 : null,
mac: model ? model.mac : null,
windows: model ? model.windows : null
});
}
// called by agGrid
doesFilterPass(params) {
var rowSkills = params.data.skills;
var passed = true;
const rowSkills = params.data.skills;
let passed = true;
this.state.skills.forEach( (skill) => {
RefData.IT_SKILLS.forEach((skill) => {
if (this.state[skill]) {
if (!rowSkills[skill]) {
passed = false;
@@ -43,33 +56,42 @@ export default class SkillsFilter extends React.Component {
return passed;
};
getModelAsString() {
return ''
}
// called by agGrid
isFilterActive() {
var somethingSelected = this.state.android || this.state.css ||
const somethingSelected = this.state.android || this.state.css ||
this.state.html5 || this.state.mac || this.state.windows;
return somethingSelected;
};
onSkillChanged(skill, event) {
var newValue = event.target.checked;
var newModel = {};
const newValue = event.target.checked;
const newModel = {};
newModel[skill] = newValue;
this.setState(newModel, ()=> {this.filterChangedCallback();} );
// set the state, and once it is done, then call filterChangedCallback
this.setState(newModel, this.props.filterChangedCallback);
}
helloFromSkillsFilter() {
alert("Hello From The Skills Filter!");
}
render() {
var skillsTemplates = [];
this.state.skills.forEach( (skill, index) => {
var skillName = this.state.skillNames[index];
var template = (
<label key={skill} style={{border: '1px solid lightgrey', margin: 4, padding: 4, display: 'inline-block'}}>
const skillsTemplates = [];
RefData.IT_SKILLS.forEach((skill, index) => {
const skillName = RefData.IT_SKILLS_NAMES[index];
const template = (
<label key={skill}
style={{border: '1px solid lightgrey', margin: 4, padding: 4, display: 'inline-block'}}>
<span>
<div style={{textAlign: 'center'}}>{skillName}</div>
<div>
<input type="checkbox" onClick={this.onSkillChanged.bind(this, skill)}/>
<img src={'images/skills/'+skill+'.png'} width={30}/>
<img src={'images/skills/' + skill + '.png'} width={30}/>
</div>
</span>
</label>
@@ -80,7 +102,13 @@ export default class SkillsFilter extends React.Component {
return (
<div style={{width: 380}}>
<div style={{textAlign: 'center', background: 'lightgray', width: '100%', display: 'block', borderBottom: '1px solid grey'}}>
<div style={{
textAlign: 'center',
background: 'lightgray',
width: '100%',
display: 'block',
borderBottom: '1px solid grey'
}}>
<b>Custom Skills Filter</b>
</div>
{skillsTemplates}
@@ -91,7 +119,6 @@ export default class SkillsFilter extends React.Component {
// these are other method that agGrid calls that we
// could of implemented, but they are optional and
// we have no use for them in this particular filter.
//getApi() {}
//afterGuiAttached(params) {}
//onNewRowsLoaded() {}
//onAnyFilterChanged() {}

View File

@@ -0,0 +1,89 @@
import React from "react";
import * as PropTypes from "prop-types";
// Header component to be used as default for all the columns.
export default class SortableHeaderComponent extends React.Component {
constructor(props) {
super(props);
// this.sortChanged = this.onSortChanged.bind(this);
this.props.column.addEventListener('sortChanged', this.onSortChanged);
//The state of this component contains the current sort state of this column
//The possible values are: 'asc', 'desc' and ''
this.state = {
sorted: ''
}
}
componentWillUnmount() {
this.props.column.removeEventListener('sortChanged', this.onSortChanged);
}
render() {
let sortElements = [];
if (this.props.enableSorting) {
let downArrowClass = "customSortDownLabel " + (this.state.sorted === 'desc' ? " active" : "");
let upArrowClass = "customSortUpLabel " + (this.state.sorted === 'asc' ? " active" : "");
let removeArrowClass = "customSortRemoveLabel " + (this.state.sorted === '' ? " active" : "");
sortElements.push(<div key={`up${this.props.displayName}`} className={downArrowClass}
onClick={this.onSortRequested.bind(this, 'desc')}><i
className="fa fa-long-arrow-down"/></div>);
sortElements.push(<div key={`down${this.props.displayName}`} className={upArrowClass}
onClick={this.onSortRequested.bind(this, 'asc')}><i
className="fa fa-long-arrow-up"/></div>);
sortElements.push(<div key={`minus${this.props.displayName}`} className={removeArrowClass}
onClick={this.onSortRequested.bind(this, '')}><i
className="fa fa-times"/></div>)
}
let menuButton = null;
if (this.props.enableMenu) {
menuButton =
<div ref="menuButton" className="customHeaderMenuButton" onClick={this.onMenuClick.bind(this)}><i
className={"fa " + this.props.menuIcon}/></div>
}
return <div>
{menuButton}
<div className="customHeaderLabel">{this.props.displayName}</div>
{sortElements}
</div>
}
onSortRequested(order, event) {
this.props.setSort(order, event.shiftKey);
};
onSortChanged = () => {
if (this.props.column.isSortAscending()) {
this.setState({
sorted: 'asc'
})
} else if (this.props.column.isSortDescending()) {
this.setState({
sorted: 'desc'
})
} else {
this.setState({
sorted: ''
})
}
};
onMenuClick() {
this.props.showColumnMenu(this.refs.menuButton);
};
}
// the grid will always pass in one props called 'params',
// which is the grid passing you the params for the cellRenderer.
// this piece is optional. the grid will always pass the 'params'
// props, so little need for adding this validation meta-data.
SortableHeaderComponent.propTypes = {
params: PropTypes.object
};

View File

@@ -0,0 +1,64 @@
import React, {Component} from "react";
import {connect} from "react-redux";
import {AgGridReact} from "@ag-grid-community/react";
import {AllModules} from "@ag-grid-enterprise/all-modules";
import PriceRenderer from "./PriceRenderer";
/*
* This component serves to display the row data (provided by redux)
*/
class GridComponent extends Component {
constructor(props) {
super(props);
this.state = {
columnDefs: [
{
field: 'symbol'
},
{
field: 'price',
cellClass: 'align-right',
cellRendererFramework: PriceRenderer
}
]
};
this.onGridReady = this.onGridReady.bind(this);
}
onGridReady(params) {
this.gridApi = params.api;
this.columnApi = params.columnApi;
this.gridApi.sizeColumnsToFit();
}
// row data will be provided via redux on this.props.rowData
render() {
return (
<div style={{height: 400, width: 900, marginTop: 15}}
className="ag-theme-balham">
<AgGridReact
// properties
columnDefs={this.state.columnDefs}
rowData={this.props.rowData}
modules={AllModules}
// events
onGridReady={this.onGridReady}>
</AgGridReact>
</div>
)
}
}
// pull off row data changes
export default connect(
(state) => {
return {
rowData: state.rowData
}
}
)(GridComponent);

View File

@@ -0,0 +1,83 @@
import React, {Component} from "react";
import {connect} from "react-redux";
import {setCurrency, updateRowData} from "./gridDataActions";
/*
* This component serves both to host the demo controls, which in turn will drive row data state changes
*/
class HeaderComponent extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
// provide the initial data to the store (which in turn will populate the grid)
this.props.dispatch(updateRowData(this.createRowData()));
}
setCurrency(currencySymbol, exchangeRate) {
this.props.dispatch(setCurrency(currencySymbol, exchangeRate));
}
render() {
return (
<div style={{marginTop: 15}}>
<button onClick={this.setCurrency.bind(this, '£', 1)} className="btn btn-primary">Set Currency to GBP</button>
<button onClick={this.setCurrency.bind(this, '$', 1.29)} className="btn btn-primary">Set Currency to USD</button>
</div>
)
}
// the following methods are for creating dummy row data
createRowData() {
let rowData = [];
for (let i = 0; i < 14; i++) {
let newItem = this.createItem();
rowData.push(newItem);
}
return rowData;
}
createItem() {
return {
symbol: this.createUniqueRandomSymbol(),
price: Math.floor(Math.random() * 100)
};
}
// creates a unique symbol, eg 'ADG' or 'ZJD'
createUniqueRandomSymbol() {
let symbol;
let possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
let isUnique = false;
while (!isUnique) {
symbol = '';
// create symbol
for (let i = 0; i < 3; i++) {
symbol += possible.charAt(Math.floor(Math.random() * possible.length));
}
// check uniqueness
isUnique = true;
this.props.rowData.forEach(function (oldItem) {
if (oldItem.symbol === symbol) {
isUnique = false;
}
});
}
return symbol;
}
}
// pull off row data
export default connect(
(state) => {
return {
rowData: state.rowData
}
}
)(HeaderComponent);

View File

@@ -0,0 +1,45 @@
import React, {Component} from "react";
import {connect} from "react-redux";
import FontContext from './fontContext'
class PriceRenderer extends Component {
constructor(props) {
super(props);
this.state = {
convertedValue: this.applyExchangeRate(props.exchangeRate, props.value)
};
}
componentWillReceiveProps(nextProps) {
this.setState({
convertedValue: this.applyExchangeRate(nextProps.exchangeRate, nextProps.value)
})
}
render() {
return (
<FontContext.Consumer>
{fontWeight => <span
style={{fontWeight}}> {this.props.currencySymbol}{this.state.convertedValue}</span>}
</FontContext.Consumer>
);
}
applyExchangeRate = (exchangeRate, value) => {
return parseFloat(value * exchangeRate).toFixed(2); // simplified/naive exchange rate implementation!
}
}
export default connect(
(state) => {
return {
currencySymbol: state.currencySymbol,
exchangeRate: state.exchangeRate
}
},
null,
null,
{forwardRef: true} // must be supplied for react/redux when using AgGridReact
)(PriceRenderer);

View File

@@ -0,0 +1,36 @@
import React, {Component} from "react";
import {Provider} from "react-redux";
import {createStore} from "redux";
import HeaderComponent from "./HeaderComponent";
import GridComponent from "./GridComponent";
import gridData from "./gridDataReducer";
import FontContext from './fontContext'
let store = createStore(gridData);
/*
* This component serves as a container for both the header and grid components. It's primarily here to act as a container
* for the redux Provider
*/
export default class SimpleReduxExample extends Component {
constructor(props) {
super(props);
}
render() {
return (
<Provider store={store}>
<div>
<h1>Simple Redux Example using Connected React Components</h1>
<HeaderComponent/>
<FontContext.Provider value="bold">
<GridComponent/>
</FontContext.Provider>
</div>
</Provider>
)
}
};

View File

@@ -0,0 +1,3 @@
import React from "react";
export default React.createContext('normal');

View File

@@ -0,0 +1,14 @@
export function updateRowData(rowData) {
return {
type: 'ROW_DATA_CHANGED',
rowData
}
}
export function setCurrency(currencySymbol, exchangeRate) {
return {
type: 'CURRENCY_CHANGED',
currencySymbol,
exchangeRate
}
}

View File

@@ -0,0 +1,17 @@
export default (state = {rowData: [], currencySymbol: '£', exchangeRate: 1}, action) => {
switch (action.type) {
case 'ROW_DATA_CHANGED':
return {
...state,
rowData: action.rowData,
};
case 'CURRENCY_CHANGED':
return {
...state,
currencySymbol: action.currencySymbol,
exchangeRate: action.exchangeRate
};
default:
return state;
}
};

View File

@@ -0,0 +1,34 @@
import React, {useContext} from "react";
import {Context} from "./store";
import {AgGridReact} from "@ag-grid-community/react";
import {AllModules} from "@ag-grid-enterprise/all-modules";
/*
* This component serves to display the row data (provided by redux)
*/
export default function GridComponent() {
const {store, dispatch} = useContext(Context);
const {columnDefs, rowData} = store;
const onGridReady = (params) => {
params.api.sizeColumnsToFit();
};
// row data will be provided via redux on this.props.rowData
return (
<div style={{height: 400, width: 900, marginTop: 15}}
className="ag-theme-balham">
<AgGridReact
// properties
columnDefs={columnDefs}
rowData={rowData}
modules={AllModules}
defaultColDef={{filter: true}}
// events
onGridReady={onGridReady}>
</AgGridReact>
</div>
)
}

View File

@@ -0,0 +1,18 @@
import React, {useEffect, forwardRef, useImperativeHandle, useRef} from "react";
export default forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => {
return {
getValue: () => {
return inputRef.current.value;
}
};
});
useEffect(() => {
// https://github.com/facebook/react/issues/7835#issuecomment-395504863
setTimeout(() => inputRef.current.focus(), 10)
}, []);
return <input type="text" ref={inputRef} defaultValue={props.value}/>;
})

View File

@@ -0,0 +1,18 @@
import React, {forwardRef, useImperativeHandle, useRef} from "react";
export default forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => {
return {
isFilterActive() {
return inputRef.current.value !== '';
},
doesFilterPass: (params) => {
return params.data.price.toString() === inputRef.current.value;
}
};
});
return <input type="text" ref={inputRef} onChange={() => props.filterChangedCallback()}/>;
})

View File

@@ -0,0 +1,13 @@
import React, {Component} from "react";
export default class PriceRenderer extends Component {
constructor(props) {
super(props);
}
render() {
return (
<span>{this.props.value}</span>
);
}
}

View File

@@ -0,0 +1,18 @@
import React, {useReducer} from "react";
import GridComponent from "./GridComponent";
import {Context, initialState, reducer} from "./store";
export default function SimpleReduxHookExample() {
const [store, dispatch] = useReducer(reducer, initialState);
return (
<Context.Provider value={{store, dispatch}}>
<div>
<h1>Simple Example using Hooks (with useContext and useReducer)</h1>
<button onClick={() => dispatch({type: "SET_ROW_DATA"})} className="btn btn-primary">Populate Row Data</button>
<GridComponent/>
</div>
</Context.Provider>
)
}

View File

@@ -0,0 +1,14 @@
export function updateRowData(rowData) {
return {
type: 'ROW_DATA_CHANGED',
rowData
}
}
export function setCurrency(currencySymbol, exchangeRate) {
return {
type: 'CURRENCY_CHANGED',
currencySymbol,
exchangeRate
}
}

View File

@@ -0,0 +1,81 @@
import React from "react";
import PriceRenderer from "./PriceRenderer";
import PriceEditor from "./PriceEditor";
import PriceFilter from "./PriceFilter";
export const initialState = {
rowData: [],
columnDefs: [
{
field: 'symbol',
editable: true
},
{
field: 'price',
cellClass: 'align-right',
editable: true,
cellEditorFramework: PriceEditor,
filterFramework: PriceFilter,
cellRendererFramework: PriceRenderer
}
]
};
export const reducer = (state = {rowData: []}, action) => {
switch (action.type) {
case 'SET_ROW_DATA':
return {
...state,
rowData: createRowData()
};
default:
return state;
}
};
export const Context = React.createContext();
// for test data
// the following methods are for creating dummy row data
const createRowData = () => {
let rowData = [];
for (let i = 0; i < 14; i++) {
let newItem = createItem(rowData);
rowData.push(newItem);
}
return rowData;
};
const createItem = (rowData) => {
return {
symbol: createUniqueRandomSymbol(rowData),
price: Math.floor(Math.random() * 100)
};
};
// creates a unique symbol, eg 'ADG' or 'ZJD'
const createUniqueRandomSymbol = (rowData) => {
let symbol;
let possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
let isUnique = false;
while (!isUnique) {
symbol = '';
// create symbol
for (let i = 0; i < 3; i++) {
symbol += possible.charAt(Math.floor(Math.random() * possible.length));
}
// check uniqueness
isUnique = true;
rowData.forEach(function (oldItem) {
if (oldItem.symbol === symbol) {
isUnique = false;
}
});
}
return symbol;
};

124
src-large-data/index.html Normal file
View File

@@ -0,0 +1,124 @@
<html>
<!-- This index file is common for both the standard and large examples. As to which project you are running
depends on whether you use 'npm run standard' or 'npm run large' -->
<head>
<meta charset="utf-8">
<script type="text/javascript" src="../dist/react-large.js" charset="utf-8"></script>
<!-- Example uses font awesome icons -->
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
</head>
<!-- Put some spacing around the buttons - not needed, just makes it look nice -->
<style>
button {
margin-left: 4px;
margin-right: 4px;
}
.customHeaderMenuButton{
margin-top: 5px;
margin-left: 4px;
float: left;
}
.customHeaderLabel{
margin-left: 5px;
margin-top: 3px;
float: left;
}
.customSortDownLabel{
float: left;
margin-left: 10px;
margin-top: 5px;
}
.customSortUpLabel{
float: left;
margin-left: 3px;
margin-top: 4px;
}
.customSortRemoveLabel{
float: left;
font-size: 11px;
margin-left: 3px;
margin-top: 6px;
}
.active {
color: cornflowerblue;
}
.hidden { display:none; }
.customHeaderLabel{
margin-left: 5px;
margin-top: 3px;
float: left;
}
.customExpandButton{
float:right;
margin-top: 5px;
margin-left: 3px;
}
.expanded {
animation-name: toExpanded;
animation-duration: 1s;
-ms-transform: rotate(180deg); /* IE 9 */
-webkit-transform: rotate(180deg); /* Chrome, Safari, Opera */
transform: rotate(180deg);
}
.collapsed {
color: cornflowerblue;
animation-name: toCollapsed;
animation-duration: 1s;
-ms-transform: rotate(0deg); /* IE 9 */
-webkit-transform: rotate(0deg); /* Chrome, Safari, Opera */
transform: rotate(0deg);
}
@keyframes toExpanded{
from {
color: cornflowerblue;
-ms-transform: rotate(0deg); /* IE 9 */
-webkit-transform: rotate(0deg); /* Chrome, Safari, Opera */
transform: rotate(0deg);
}
to {
color: black;
-ms-transform: rotate(180deg); /* IE 9 */
-webkit-transform: rotate(180deg); /* Chrome, Safari, Opera */
transform: rotate(180deg);
}
}
@keyframes toCollapsed{
from {
color: black;
-ms-transform: rotate(180deg); /* IE 9 */
-webkit-transform: rotate(180deg); /* Chrome, Safari, Opera */
transform: rotate(180deg);
}
to {
color: cornflowerblue;
-ms-transform: rotate(0deg); /* IE 9 */
-webkit-transform: rotate(0deg); /* Chrome, Safari, Opera */
transform: rotate(0deg);
}
}
</style>
<body>
<div id="myAppContainer"></div>
</body>
</html>

View File

@@ -3,9 +3,9 @@
import ReactDOM from 'react-dom';
import React from 'react';
import LargeGrid from './largeGrid.jsx';
// is there a better way of doing this?
import 'ag-grid-root/dist/styles/ag-grid.css';
import 'ag-grid-root/dist/styles/theme-fresh.css';
import '@ag-grid-community/core/dist/styles/ag-grid.css';
import '@ag-grid-community/core/dist/styles/ag-theme-balham.css';
// waiting for dom to load before booting react. we could alternatively
// put the index.js reference at the end fo the index.html, but i prefer this way.

View File

@@ -0,0 +1,72 @@
import React, {Component} from 'react';
import SimpleCellRenderer from './simpleCellRenderer.jsx';
import {AgGridReact} from '@ag-grid-community/react';
// for community features
import {AllModules} from "@ag-grid-enterprise/all-modules";
// for enterprise features
// import {AllModules} from "@ag-grid-enterprise/all-modules";
export default class MyApp extends Component {
constructor() {
super();
this.createColumnNames();
this.state = {
columnDefs: this.createColumnDefs(),
rowData: this.createRowData()
};
}
createColumnNames() {
// creates column names by iterating the alphabet twice, eg {'aa','ab','ac',.....'zz'}
const alphabet = 'abcdefghijklmnopqrstuvwxyz'.split('');
this.columnNames = [];
alphabet.forEach(letter1 => {
alphabet.forEach(letter2 => {
this.columnNames.push(letter1 + letter2);
});
});
}
createRowData() {
const rowData = [];
for (let i = 0; i < 1000; i++) {
const item = {};
this.columnNames.forEach(colName => {
item[colName] = '(' + colName.toUpperCase() + ',' + i + ')'
});
rowData.push(item);
}
return rowData;
}
createColumnDefs() {
const columnDefs = [];
this.columnNames.forEach(colName => {
columnDefs.push({
headerName: colName.toUpperCase(),
field: colName,
cellRendererFramework: SimpleCellRenderer,
width: 100
});
});
return columnDefs;
}
render() {
return (
<div style={{height: '100%'}} className="ag-theme-balham">
<AgGridReact columnDefs={this.state.columnDefs} rowData={this.state.rowData} modules={AllModules}/>
</div>
);
}
}

View File

@@ -1,17 +1,16 @@
import React from 'react';
import * as PropTypes from 'prop-types';
export default class SimpleCellRenderer extends React.Component {
render() {
var params = this.props.params;
// the class below does nothing, it's just for testing, so we can inspect the dom of
// the result and looking for it, to validate that this cellRenderer is actually getting used.
return (
<span className="simple-cell-renderer">{params.value}</span>
<span className="simple-cell-renderer">{this.props.value}</span>
);
}
}
SimpleCellRenderer.propTypes = {
params: React.PropTypes.object
params: PropTypes.object
};

View File

@@ -1,75 +0,0 @@
import React from 'react';
import {reactCellRendererFactory} from 'ag-grid-react';
import SimpleCellRenderer from './simpleCellRenderer.jsx';
import {AgGridReact} from 'ag-grid-react';
// put this line in to use ag-Grid enterprise
// import 'ag-grid-enterprise';
export default class MyApp extends React.Component {
constructor() {
super();
this.createColumnNames();
this.state = {
columnDefs: this.createColumnDefs(),
rowData: this.createRowData()
};
}
createColumnNames() {
// creates column names by iterating the alphabet twice, eg {'aa','ab','ac',.....'zz'}
var alphabet = 'abcdefghijklmnopqrstuvwxyz'.split('');
this.columnNames = [];
alphabet.forEach( letter1 => {
alphabet.forEach( letter2 => {
this.columnNames.push(letter1 + letter2);
});
});
}
createRowData() {
var rowData = [];
for (var i = 0; i<1000; i++) {
var item = {};
this.columnNames.forEach( colName => {
item[colName] = '('+colName.toUpperCase()+','+i+')'
});
rowData.push(item);
}
return rowData;
}
createColumnDefs() {
var columnDefs = [];
// pass in the parent component to use for React component to the cellRenderer.
// this is optional. if missing, then react router will not work.
var cellRenderer = reactCellRendererFactory(SimpleCellRenderer, this);
this.columnNames.forEach( colName => {
columnDefs.push({
headerName: colName.toUpperCase(),
field: colName,
cellRenderer: cellRenderer,
width: 100
});
});
return columnDefs;
}
render() {
return (
<div style={{height: '100%'}} className="ag-fresh">
<AgGridReact columnDefs={this.state.columnDefs} rowData={this.state.rowData} />
</div>
);
}
}

View File

@@ -1,67 +0,0 @@
import SkillsCellRenderer from './SkillsCellRenderer.jsx';
import ProficiencyCellRenderer from './ProficiencyCellRenderer.jsx';
import RefData from './RefData';
import {reactCellRendererFactory} from 'ag-grid-react';
import {reactFilterFactory} from 'ag-grid-react';
import SkillsFilter from './SkillsFilter.jsx';
import ProficiencyFilter from './ProficiencyFilter.jsx';
export default class ColDefFactory {
createColDefs(parentReactComponent) {
var columnDefs = [
{headerName: '#', width: 30, checkboxSelection: true, suppressSorting: true,
suppressMenu: true, pinned: true},
{
headerName: 'Employee',
children: [
{headerName: "Name", field: "name", enableRowGroup: true, enablePivot: true,
width: 150, pinned: true},
{headerName: "Country", field: "country", width: 150, enableRowGroup: true, enablePivot: true,
// not bothering with React for country, as it's a simple HTML string
cellRenderer: countryCellRenderer, pinned: true,
filterParams: {cellRenderer: countryCellRenderer, cellHeight: 20}},
]
},
{
headerName: 'IT Skills',
children: [
{headerName: "Skills", width: 125, suppressSorting: true, field: 'skills', enableRowGroup: true, enablePivot: true,
// using ag-Grid's React cellRenderer factory
cellRenderer: reactCellRendererFactory(SkillsCellRenderer, parentReactComponent),
// using ag-Grid's React filter factory
filter: reactFilterFactory(SkillsFilter, parentReactComponent)
},
{headerName: "Proficiency", field: "proficiency", filter: 'number', width: 120, enableValue: true,
// using ag-Grid's React cellRenderer factory
cellRenderer: reactCellRendererFactory(ProficiencyCellRenderer, parentReactComponent),
// using ag-Grid's React filter factory
filter: reactFilterFactory(ProficiencyFilter, parentReactComponent)}
]
},
{
headerName: 'Contact',
children: [
{headerName: "Mobile", field: "mobile", width: 150, filter: 'text'},
{headerName: "Land-line", field: "landline", width: 150, filter: 'text'},
{headerName: "Address", field: "address", width: 500, filter: 'text'}
]
}
];
return columnDefs;
}
}
// this is a simple cell renderer, putting together static html, no
// need to use React for it.
function countryCellRenderer(params) {
if (params.value) {
var flag = "<img border='0' width='15' height='10' " +
"style='margin-bottom: 2px' src='http://flags.fmcdn.net/data/flags/mini/"
+ RefData.COUNTRY_CODES[params.value] + ".png'>";
return flag + " " + params.value;
} else {
return null;
}
}

View File

@@ -1,34 +0,0 @@
import React from 'react';
import RefData from './RefData';
// cell renderer for the proficiency column. this is a very basic cell renderer,
// it is arguable that we should not of used React and just returned a string of
// html as a normal ag-Grid cellRenderer.
export default class ProficiencyCellRenderer extends React.Component {
render() {
var params = this.props.params;
var backgroundColor;
if (params.value < 20) {
backgroundColor = 'red';
} else if (params.value < 60) {
backgroundColor = '#ff9900';
} else {
backgroundColor = '#00A000';
}
return (
<div className="div-percent-bar" style={{ width: params.value + '%', backgroundColor: backgroundColor }}>
<div className="div-percent-value">{params.value}%</div>
</div>
);
}
}
// the grid will always pass in one props called 'params',
// which is the grid passing you the params for the cellRenderer.
// this piece is optional. the grid will always pass the 'params'
// props, so little need for adding this validation meta-data.
ProficiencyCellRenderer.propTypes = {
params: React.PropTypes.object
};

View File

@@ -1,82 +0,0 @@
import React from 'react';
import RefData from './RefData';
var PROFICIENCY_NAMES = ['No Filter', 'Above 40%', 'Above 60%', 'Above 80%'];
// the proficiency filter component. this demonstrates how to integrate
// a React filter component with ag-Grid.
export default class ProficiencyFilter extends React.Component {
constructor() {
super();
this.state = {
selected: PROFICIENCY_NAMES[0]
};
}
// called by agGrid
init(params) {
this.filterChangedCallback = params.filterChangedCallback;
this.valueGetter = params.valueGetter;
}
// called by agGrid
doesFilterPass(params) {
var value = this.valueGetter(params);
var valueAsNumber = parseFloat(value);
switch (this.state.selected) {
case PROFICIENCY_NAMES[1] : return valueAsNumber >= 40;
case PROFICIENCY_NAMES[2] : return valueAsNumber >= 60;
case PROFICIENCY_NAMES[3] : return valueAsNumber >= 80;
default : return true;
}
};
// called by agGrid
isFilterActive() {
return this.state.selected !== PROFICIENCY_NAMES[0];
};
onButtonPressed(name) {
console.log(name);
this.setState({
selected: name
}, ()=> {
this.filterChangedCallback();
});
console.log(name);
}
render() {
var rows = [];
PROFICIENCY_NAMES.forEach( (name)=> {
var selected = this.state.selected === name;
rows.push(
<div key={name}>
<label style={{paddingLeft: 4}}>
<input type="radio" checked={selected} name={Math.random()} onChange={this.onButtonPressed.bind(this, name)}/>
{name}
</label>
</div>
);
});
return (
<div>
<div style={{textAlign: 'center', background: 'lightgray', width: '100%', display: 'block', borderBottom: '1px solid grey'}}>
<b>Custom Proficiency Filter</b>
</div>
{rows}
</div>
);
}
// these are other method that agGrid calls that we
// could of implemented, but they are optional and
// we have no use for them in this particular filter.
//getApi() {}
//afterGuiAttached(params) {}
//onNewRowsLoaded() {}
//onAnyFilterChanged() {}
}

View File

@@ -1,113 +0,0 @@
export default class RefData {}
RefData.FIRST_NAMES = [
"Sophie", "Isabelle", "Emily", "Olivia", "Lily", "Chloe", "Isabella",
"Amelia", "Jessica", "Sophia", "Ava", "Charlotte", "Mia", "Lucy", "Grace", "Ruby",
"Ella", "Evie", "Freya", "Isla", "Poppy", "Daisy", "Layla"
];
RefData.LAST_NAMES = [
"Beckham", "Black", "Braxton", "Brennan", "Brock", "Bryson", "Cadwell",
"Cage", "Carson", "Chandler", "Cohen", "Cole", "Corbin", "Dallas", "Dalton", "Dane",
"Donovan", "Easton", "Fisher", "Fletcher", "Grady", "Greyson", "Griffin", "Gunner",
"Hayden", "Hudson", "Hunter", "Jacoby", "Jagger", "Jaxon", "Jett", "Kade", "Kane",
"Keating", "Keegan", "Kingston", "Kobe"
];
RefData.COUNTRY_CODES = {
Ireland: "ie",
Spain: "es",
"United Kingdom": "gb",
France: "fr",
Germany: "de",
Sweden: "se",
Italy: "it",
Greece: "gr",
Iceland: "is",
Portugal: "pt",
Malta: "mt",
Norway: "no",
Brazil: "br",
Argentina: "ar",
Colombia: "co",
Peru: "pe",
Venezuela: "ve",
Uruguay: "uy"
};
RefData.COUNTRIES = [
{country: "Ireland", continent: "Europe", language: "English"},
{country: "Spain", continent: "Europe", language: "Spanish"},
{country: "United Kingdom", continent: "Europe", language: "English"},
{country: "France", continent: "Europe", language: "French"},
{country: "Germany", continent: "Europe", language: "(other)"},
{country: "Sweden", continent: "Europe", language: "(other)"},
{country: "Norway", continent: "Europe", language: "(other)"},
{country: "Italy", continent: "Europe", language: "(other)"},
{country: "Greece", continent: "Europe", language: "(other)"},
{country: "Iceland", continent: "Europe", language: "(other)"},
{country: "Portugal", continent: "Europe", language: "Portuguese"},
{country: "Malta", continent: "Europe", language: "(other)"},
{country: "Brazil", continent: "South America", language: "Portuguese"},
{country: "Argentina", continent: "South America", language: "Spanish"},
{country: "Colombia", continent: "South America", language: "Spanish"},
{country: "Peru", continent: "South America", language: "Spanish"},
{country: "Venezuela", continent: "South America", language: "Spanish"},
{country: "Uruguay", continent: "South America", language: "Spanish"}
];
RefData.ADDRESSES = [
'1197 Thunder Wagon Common, Cataract, RI, 02987-1016, US, (401) 747-0763',
'3685 Rocky Glade, Showtucket, NU, X1E-9I0, CA, (867) 371-4215',
'3235 High Forest, Glen Campbell, MS, 39035-6845, US, (601) 638-8186',
'2234 Sleepy Pony Mall , Drain, DC, 20078-4243, US, (202) 948-3634',
'2722 Hazy Turnabout, Burnt Cabins, NY, 14120-5642, US, (917) 604-6597',
'6686 Lazy Ledge, Two Rock, CA, 92639-3020, US, (619) 901-9911',
'2000 Dewy Limits, Wacahoota, NF, A4L-2V9, CA, (709) 065-3959',
'7710 Noble Pond Avenue, Bolivia, RI, 02931-1842, US, (401) 865-2160',
'3452 Sunny Vale, Pyro, ON, M8V-4Z0, CA, (519) 072-8609',
'4402 Dusty Cove, Many Farms, UT, 84853-8223, US, (435) 518-0673',
'5198 Silent Parade, Round Bottom, MD, 21542-9798, US, (301) 060-7245',
'8550 Shady Moor, Kitty Fork, CO, 80941-6207, US, (303) 502-3767',
'2131 Old Dell, Merry Midnight, AK, 99906-8842, US, (907) 369-2206',
'7390 Harvest Crest, Mosquito Crossing, RI, 02957-6116, US, (401) 463-6348',
'874 Little Point, Hot Coffee, BC, V3U-2P6, CA, (250) 706-9207',
'8834 Stony Pioneer Heights, Newlove, OR, 97419-8670, US, (541) 408-2213',
'9829 Grand Beach, Flint, UT, 84965-9900, US, (435) 700-5161',
'3799 Cozy Blossom Ramp, Ptarmigan, MS, 38715-0313, US, (769) 740-1526',
'3254 Silver Island Loop, Maunaloa, DE, 19869-3169, US, (302) 667-7671',
'1081 Middle Wood, Taylors Gut Landing, OR, 97266-2873, US, (541) 357-6310',
'1137 Umber Trail, Shacktown, NW, X3U-5Y8, CA, (867) 702-6883',
'9914 Hidden Bank, Wyoming, MO, 64635-9665, US, (636) 280-4192',
'7080 Misty Nectar Townline, Coward, AB, T9U-3N4, CA, (403) 623-2838',
'1184 Wishing Grounds, Vibank, NW, X7D-0V9, CA, (867) 531-2730',
'126 Easy Pointe, Grandview Beach, KY, 40928-9539, US, (502) 548-0956',
'6683 Colonial Street, Swan River, BC, V1A-9I8, CA, (778) 014-4257',
'960 Gentle Oak Lane, Shakopee, ND, 58618-6277, US, (701) 327-1219',
'6918 Cotton Pine Corner, Kenaston, IA, 52165-3975, US, (515) 906-7427',
'2368 Burning Woods, Ernfold, NY, 11879-9186, US, (646) 819-0355',
'5646 Quiet Shadow Chase, Tiger Tail, IA, 52283-5537, US, (712) 375-9225',
'5466 Foggy Mountain Dale, Sweet Home, MT, 59738-0251, US, (406) 881-1706',
'5313 Clear Willow Route, Amazon, BC, V0S-2S6, CA, (604) 340-7596',
'7000 Pleasant Autoroute, Spaceport City, UT, 84749-2448, US, (435) 154-3360',
'8359 Quaking Anchor Road, Gross, BC, V9O-0H5, CA, (250) 985-3859',
'5143 Amber Deer Hollow, New Deal, ND, 58446-0853, US, (701) 927-0322',
'6230 Jagged Bear Key, Young, AR, 72337-3811, US, (501) 805-7239',
'7207 Heather Vista, Devon, WY, 82520-1771, US, (307) 358-7092',
'9416 Red Rise Place, Spraytown, OK, 73809-4766, US, (580) 867-1973',
'3770 Golden Horse Diversion, Yelland, IL, 60471-1487, US, (224) 717-9349',
'4819 Honey Treasure Park, Alaska, NB, E1U-3I0, CA, (506) 656-9138',
'6187 Round Front, Land O Lakes, AK, 99873-6403, US, (907) 853-9063',
'9218 Crystal Highway, Pickelville, MT, 59847-9299, US, (406) 076-0024',
'6737 Bright Quay, Lazy Mountain, KY, 42390-4772, US, (606) 256-7288',
'237 Merry Campus, Twentysix, SC, 29330-4909, US, (864) 945-0157',
'446 Fallen Gate Rise, Petrolia, SC, 29959-9527, US, (864) 826-0553',
'2347 Indian Boulevard, Frisbee, VA, 23797-6458, US, (703) 656-8445',
'365 Emerald Grove Line, Level, NC, 28381-1514, US, (919) 976-7958',
'1207 Iron Extension, Klickitat, SC, 29197-8571, US, (803) 535-7888',
'6770 Cinder Glen, Caronport, OH, 45053-5002, US, (440) 369-4018',
'7619 Tawny Carrefour, Senlac, NV, 89529-9876, US, (775) 901-6433'];
RefData.IT_SKILLS = ['android', 'css', 'html5', 'mac', 'windows'];
RefData.IT_SKILLS_NAMES = ['Android', 'CSS', 'HTML 5', 'Mac', 'Windows'];

View File

@@ -1,18 +0,0 @@
'use strict';
import ReactDOM from 'react-dom';
import React from 'react';
import MyApp from './myApp.jsx';
// is there a better way of doing this?
import 'ag-grid-root/dist/styles/ag-grid.css';
import 'ag-grid-root/dist/styles/theme-fresh.css';
// waiting for dom to load before booting react. we could alternatively
// put the index.js reference at the end fo the index.html, but i prefer this way.
document.addEventListener('DOMContentLoaded', ()=> {
var container = document.getElementById('myAppContainer');
ReactDOM.render(
React.createElement(MyApp),
container
);
});

View File

@@ -1,187 +0,0 @@
import ReactDOM from 'react-dom';
import React from 'react';
import {AgGridReact} from 'ag-grid-react';
import RefData from './RefData';
import RowDataFactory from './RowDataFactory';
import ColDefFactory from './ColDefFactory.jsx';
import './myApp.css';
// take this line out if you do not want to use ag-Grid-Enterprise
import 'ag-grid-enterprise';
export default class MyApp extends React.Component {
constructor() {
super();
this.state = {
quickFilterText: null,
showGrid: true,
showToolPanel: false,
columnDefs: new ColDefFactory().createColDefs(this),
rowData: new RowDataFactory().createRowData(this),
icons: {
columnRemoveFromGroup: '<i class="fa fa-remove"/>',
filter: '<i class="fa fa-filter"/>',
sortAscending: '<i class="fa fa-long-arrow-down"/>',
sortDescending: '<i class="fa fa-long-arrow-up"/>',
groupExpanded: '<i class="fa fa-minus-square-o"/>',
groupContracted: '<i class="fa fa-plus-square-o"/>',
columnGroupOpened: '<i class="fa fa-minus-square-o"/>',
columnGroupClosed: '<i class="fa fa-plus-square-o"/>'
}
};
// the grid options are optional, because you can provide every property
// to the grid via standard React properties. however, the react interface
// doesn't block you from using the standard JavaScript interface if you
// wish. Maybe you have the gridOptions stored as JSON on your server? If
// you do, the providing the gridOptions as a standalone object is just
// what you want!
this.gridOptions = {
// this is how you listen for events using gridOptions
onModelUpdated: function() {
console.log('event onModelUpdated received');
},
// this is a simple property
rowBuffer: 10 // no need to set this, the default is fine for almost all scenarios
};
}
onShowGrid(show) {
this.setState({
showGrid: show
});
}
onToggleToolPanel(event) {
this.setState({showToolPanel: event.target.checked});
}
onGridReady(params) {
this.api = params.api;
this.columnApi = params.columnApi;
}
selectAll() {
this.api.selectAll();
}
deselectAll() {
this.api.deselectAll();
}
setCountryVisible(visible) {
this.columnApi.setColumnVisible('country', visible);
}
onQuickFilterText(event) {
this.setState({quickFilterText: event.target.value});
}
onCellClicked(event) {
console.log('onCellClicked: ' + event.data.name + ', col ' + event.colIndex);
}
onRowSelected(event) {
console.log('onRowSelected: ' + event.node.data.name);
}
onRefreshData() {
var newRowData = new RowDataFactory().createRowData();
this.setState({
rowData: newRowData
});
}
render() {
var gridTemplate;
var bottomHeaderTemplate;
var topHeaderTemplate;
topHeaderTemplate = (
<div>
<div style={{float: 'right'}}>
<input type="text" onChange={this.onQuickFilterText.bind(this)} placeholder="Type text to filter..."/>
<button id="btDestroyGrid" disabled={!this.state.showGrid} onClick={this.onShowGrid.bind(this, false)}>Destroy Grid</button>
<button id="btCreateGrid" disabled={this.state.showGrid} onClick={this.onShowGrid.bind(this, true)}>Create Grid</button>
</div>
<div style={{padding: '4px'}}>
<b>Employees Skills and Contact Details</b> <span id="rowCount"/>
</div>
</div>
);
// showing the bottom header and grid is optional, so we put in a switch
if (this.state.showGrid) {
bottomHeaderTemplate = (
<div>
<div style={{padding: 4}} className={'toolbar'}>
<span>
Grid API:
<button onClick={this.selectAll.bind(this)}>Select All</button>
<button onClick={this.deselectAll.bind(this)}>Clear Selection</button>
</span>
<span style={{marginLeft: 20}}>
Column API:
<button onClick={this.setCountryVisible.bind(this, false)}>Hide Country Column</button>
<button onClick={this.setCountryVisible.bind(this, true)}>Show Country Column</button>
</span>
</div>
<div style={{clear: 'both'}}></div>
<div style={{padding: 4}} className={'toolbar'}>
<label>
<input type="checkbox" onChange={this.onToggleToolPanel.bind(this)}/>
Show Tool Panel
</label>
<button onClick={this.onRefreshData.bind(this)}>Refresh Data</button>
</div>
<div style={{clear: 'both'}}></div>
</div>
);
gridTemplate = (
<div style={{height: 400}} className="ag-fresh">
<AgGridReact
// gridOptions is optional - it's possible to provide
// all values as React props
gridOptions={this.gridOptions}
// listening for events
onGridReady={this.onGridReady.bind(this)}
onRowSelected={this.onRowSelected.bind(this)}
onCellClicked={this.onCellClicked.bind(this)}
// binding to simple properties
showToolPanel={this.state.showToolPanel}
quickFilterText={this.state.quickFilterText}
// binding to an object property
icons={this.state.icons}
// binding to array properties
columnDefs={this.state.columnDefs}
rowData={this.state.rowData}
// no binding, just providing harde coded strings for the properties
rowSelection="multiple"
enableColResize="true"
enableSorting="true"
enableFilter="true"
groupHeaders="true"
rowHeight="22"
debug="true"
/>
</div>
);
}
return <div style={{width: '800px'}}>
<div style={{padding: '4px'}}>
{topHeaderTemplate}
{bottomHeaderTemplate}
{gridTemplate}
</div>
</div>;
}
}

View File

@@ -0,0 +1,16 @@
import cloneDeep from "lodash/cloneDeep";
export function fxDataUpdated(fxData) {
return {
type: 'FX_DATA_CHANGED',
fxData: cloneDeep(fxData)
};
}
export function fxTopMoversUpdated(fxTopMovers) {
return {
type: 'FX_TOP_MOVERS_CHANGED',
fxTopMovers: cloneDeep(fxTopMovers)
};
}

View File

@@ -0,0 +1,28 @@
import React, {Component} from "react";
export default class extends Component {
constructor(props) {
super(props);
this.onExchangeChanged = this.onExchangeChanged.bind(this);
}
onExchangeChanged(event) {
this.props.onExchangeChanged(event.target.value);
}
render() {
return (
<div>
<span style={{marginRight: 15}}>Control Panel</span>
<select value={this.props.selectedExchange.symbol} onChange={this.onExchangeChanged}>
{
this.props.exchanges.map((exchange) => {
return <option key={exchange.symbol} value={exchange.symbol}>{exchange.name}</option>
})
}
</select>
</div>
);
}
};

View File

@@ -0,0 +1,26 @@
import React, {Component} from "react";
import FxDataService from "../services/FxDataService.jsx";
import FxQuoteMatrix from "./FxQuoteMatrix.jsx";
import TopMoversGrid from "./TopMoversGrid.jsx";
export default class extends Component {
constructor(props) {
super(props);
this.fxDataService = new FxDataService();
}
render() {
return (
<div>
<div style={{float: "left", marginRight: 25}}>
<FxQuoteMatrix fxDataService={this.fxDataService}/>
</div>
<div style={{float: "left"}}>
<TopMoversGrid fxDataService={this.fxDataService}/>
</div>
</div>
);
}
};

View File

@@ -0,0 +1,92 @@
import React, {Component} from "react";
import {connect} from "react-redux";
import {AgGridReact} from "@ag-grid-community/react";
import {ClientSideRowModelModule} from "@ag-grid-enterprise/all-modules";
class FxQuoteMatrix extends Component {
constructor(props) {
super(props);
this.state = {
columnDefs: this.props.fxDataService.getFxMatrixHeaderNames()
};
// grid events
this.onGridReady = this.onGridReady.bind(this);
// grid callbacks
this.getRowNodeId = this.getRowNodeId.bind(this);
}
onGridReady(params) {
this.gridApi = params.api;
this.columnApi = params.columnApi;
if (this.props.rowData) {
this.gridApi.setRowData(this.props.rowData)
}
}
getRowNodeId(data) {
return data.symbol;
}
componentWillReceiveProps(nextProps) {
if (this.gridApi) {
const newRowData = nextProps.rowData;
const updatedRows = [];
for (let i = 0; i < newRowData.length; i++) {
let newRow = newRowData[i];
let currentRowNode = this.gridApi.getRowNode(newRow.symbol);
const {data} = currentRowNode;
for (const def of this.state.columnDefs) {
if (data[def.field] !== newRow[def.field]) {
updatedRows.push(newRow);
break;
}
}
}
this.gridApi.updateRowData({update: updatedRows});
}
}
render() {
return (
<div style={{height: 410, width: 800}}
className="ag-theme-balham">
<AgGridReact
// properties
columnDefs={this.state.columnDefs}
defaultColDef={{
sortable: false,
filter: false
}}
// callbacks
getRowNodeId={this.getRowNodeId}
modules={[ClientSideRowModelModule]}
// events
onGridReady={this.onGridReady}>
</AgGridReact>
</div>
);
}
}
export default connect(
(state) => {
return {
rowData: state ? state.fxData : null
}
}
)(FxQuoteMatrix);

View File

@@ -0,0 +1,176 @@
import React, {Component} from "react";
import {AgGridReact} from "@ag-grid-community/react";
import {ClientSideRowModelModule} from "@ag-grid-enterprise/all-modules";
import map from "lodash/map";
import difference from "lodash/difference";
import forEach from "lodash/forEach";
import includes from "lodash/includes";
import ExchangeService from "../services/ExchangeService.jsx";
export default class extends Component {
constructor(props) {
super(props);
this.state = {
columnDefs: [
{
field: 'symbol',
headerName: 'Symbol',
sort: 'asc'
},
{
field: 'price',
headerName: 'Price',
valueFormatter: this.numberFormatter,
cellRenderer: 'agAnimateShowChangeCellRenderer',
cellStyle: {'text-align': 'right'}
},
{
field: 'bid',
headerName: 'Bid',
valueFormatter: this.numberFormatter,
cellRenderer: 'agAnimateShowChangeCellRenderer',
cellStyle: {'text-align': 'right'}
},
{
field: 'ask',
headerName: 'Ask',
valueFormatter: this.numberFormatter,
cellRenderer: 'agAnimateShowChangeCellRenderer',
cellStyle: {'text-align': 'right'}
}
]
};
this.exchangeService = new ExchangeService();
// grid events
this.onGridReady = this.onGridReady.bind(this);
this.onSelectionChanged = this.onSelectionChanged.bind(this);
// grid callbacks
this.getRowNodeId = this.getRowNodeId.bind(this);
// component events
this.updateSymbol = this.updateSymbol.bind(this);
}
numberFormatter(params) {
if (typeof params.value === 'number') {
return params.value.toFixed(2);
} else {
return params.value;
}
}
onGridReady(params) {
this.gridApi = params.api;
this.columnApi = params.columnApi;
// make realistic - call in a batch
let rowData = map(this.props.selectedExchange.supportedStocks, symbol => this.exchangeService.getTicker(symbol));
this.gridApi.updateRowData({add: rowData});
// select the first symbol to show the chart
this.gridApi.getModel().getRow(0).setSelected(true);
this.gridApi.sizeColumnsToFit();
}
getRowNodeId(data) {
return data.symbol;
}
onSelectionChanged() {
let selectedNode = this.gridApi.getSelectedNodes()[0];
this.props.onSelectionChanged(selectedNode ? selectedNode.data.symbol : null);
}
componentWillMount() {
this.props.selectedExchange.supportedStocks.forEach(symbol => {
this.exchangeService.addSubscriber(this.updateSymbol, symbol);
});
}
componentWillUnmount() {
this.exchangeService.removeSubscribers();
}
componentWillReceiveProps(nextProps) {
if (nextProps.selectedExchange.supportedStocks !== this.props.selectedExchange.supportedStocks) {
if (!this.gridApi) {
return;
}
this.gridApi.deselectAll();
const currentSymbols = this.props.selectedExchange.supportedStocks;
const nextSymbols = nextProps.selectedExchange.supportedStocks;
// Unsubscribe to current ones that will be removed
const symbolsRemoved = difference(currentSymbols, nextSymbols);
forEach(symbolsRemoved, symbol => {
this.exchangeService.removeSubscriber(this.updateSymbol, symbol);
});
// Remove ag-grid nodes as necessary
const rowsToRemove = [];
this.gridApi.forEachNode(node => {
const {data} = node;
if (includes(symbolsRemoved, data.symbol)) {
rowsToRemove.push(data);
}
});
this.gridApi.updateRowData({remove: rowsToRemove});
// Subscribe to new ones that need to be added
const symbolsAdded = difference(nextSymbols, currentSymbols);
forEach(symbolsAdded, symbol => {
this.exchangeService.addSubscriber(this.updateSymbol, symbol);
});
// Insert new ag-grid nodes as necessary
let rowData = map(symbolsAdded, symbol => this.exchangeService.getTicker(symbol));
this.gridApi.updateRowData({add: rowData});
// select the first symbol to show the chart
this.gridApi.getModel().getRow(0).setSelected(true);
}
}
updateSymbol(symbol) {
if (!this.gridApi) {
// the grid isn't ready yet
return;
}
this.gridApi.updateRowData({update: [symbol]});
}
render() {
return (
<div style={{height: 410, width: 800}}
className="ag-theme-balham">
<AgGridReact
// properties
columnDefs={this.state.columnDefs}
defaultColDef={{
sortable: true
}}
rowSelection="single"
modules={[ClientSideRowModelModule]}
// callbacks
getRowNodeId={this.getRowNodeId}
// events
onGridReady={this.onGridReady}
onSelectionChanged={this.onSelectionChanged}>
</AgGridReact>
</div>
);
}
}

View File

@@ -0,0 +1,57 @@
import React, {Component} from "react";
import ExchangeService from "../services/ExchangeService.jsx";
import StockPriceDeltaPanel from "./StockPriceDeltaPanel.jsx";
import StockTimestampPanel from "./StockTimestampPanel.jsx";
import StockSummaryPanel from "./StockSummaryPanel.jsx";
import StockHistoricalChartPanel from "./StockHistoricalChartPanel.jsx";
export default class extends Component {
constructor(props) {
super(props);
this.exchangeService = new ExchangeService();
this.state = {
priceDelta: null,
timestamp: null,
tickerSummary: null,
historicalData: null
}
}
componentWillReceiveProps(nextProps) {
if (nextProps.selectedSymbol &&
nextProps.selectedSymbol !== this.props.selectedSymbol) {
let stockDetail = this.exchangeService.getTickerDetail(nextProps.selectedSymbol);
this.setState({
pricingDelta: stockDetail.pricingDelta,
timestamp: stockDetail.timestamp,
tickerSummary: stockDetail.tickerSummary,
historicalData: stockDetail.historicalData
})
}
}
shouldComponentUpdate(nextProps) {
return nextProps.selectedSymbol !== this.props.selectedSymbol;
}
render() {
if (!this.props.selectedSymbol) {
return null;
} else {
return (
<div>
<StockPriceDeltaPanel pricingDelta={this.state.pricingDelta}/>
<StockTimestampPanel timestamp={this.state.timestamp}
exchangeName={this.props.exchangeName}/>
<StockSummaryPanel tickerSummary={this.state.tickerSummary}/>
<StockHistoricalChartPanel historicalData={this.state.historicalData}/>
</div>
);
}
}
}

View File

@@ -0,0 +1,128 @@
import React, {Component} from "react";
import * as d3 from "d3";
export default class extends Component {
constructor(props) {
super(props);
this.margin = {
top: 20,
right: 20,
bottom: 50,
left: 0
};
this.renderingWidth = this.props.graphWidth - this.margin.left - this.margin.right;
this.renderingheight = this.props.graphHeight - this.margin.top - this.margin.bottom;
this.x = d3.scaleTime()
.range([0, this.renderingWidth]);
this.y = d3.scaleLinear()
.rangeRound([this.renderingheight, 0]);
this.line = d3.line()
.x(d => this.x(d.date))
.y(d => this.y(d.price));
}
static get defaultProps() {
return {
graphHeight: 230,
graphWidth: 400
}
}
componentDidMount() {
this.renderGraph()
}
componentDidUpdate() {
this.renderGraph()
}
renderGraph() {
if (!this.props.historicalData) {
return;
}
let data = this.props.historicalData;
let parseTime = d3.timeParse("%d-%m-%Y");
data.forEach((datum) => {
datum.date = parseTime(datum.date);
datum.price = +datum.price;
});
// clear out any previous graph
d3.select(this.refs.historyGraph)
.selectAll("svg")
.remove();
// create a new one
let svg = d3.select(this.refs.historyGraph)
.append('svg')
.attr('height', this.props.graphHeight)
.attr('width', this.props.graphWidth);
let g = svg.append("g")
.attr("transform", "translate(" + this.margin.left + "," + this.margin.top + ")");
this.x.domain(d3.extent(data, d => d.date));
this.y.domain(d3.extent(data, d => d.price));
let scale = d3.scaleTime()
.domain(d3.extent(data, d => d.date))
.range([0, this.renderingWidth]);
// Add the x Axis
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + (this.renderingheight + 20 ) + ")")
.call(d3.axisBottom(scale).tickFormat(d3.timeFormat("%Y-%m-%d")))
.selectAll("text")
.attr("y", 0)
.attr("x", 9)
.attr("dy", ".35em")
.attr("transform", "rotate(55)")
.style("text-anchor", "start");
// Add the y Axis
svg.append("g")
.call(d3.axisLeft(this.y));
g.append("g")
.call(d3.axisLeft(this.y))
.append("text")
.attr("fill", "#000")
.attr("transform", "rotate(-90)")
.attr("y", 9)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.text("Price ($)");
g.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.attr("d", this.line);
}
render() {
let containerStyle = {
marginTop: 5
};
if (!this.props.historicalData) {
return null;
} else {
return (
<div style={containerStyle} ref="historyGraph"></div>
);
}
}
}

View File

@@ -0,0 +1,56 @@
import React, {Component} from "react";
import PriceChangesGrid from "./PriceChangesGrid.jsx";
import StockDetailPanel from "./StockDetailPanel.jsx";
import FxPanel from "./FxPanel.jsx";
export default class extends Component {
constructor(props) {
super(props);
this.state = {
selectedSymbol: null
};
this.onSelectionChanged = this.onSelectionChanged.bind(this);
}
componentWillReceiveProps(nextProps) {
if (nextProps.selectedExchange !== this.props.selectedExchange) {
this.setState({
selectedSymbol: null
})
}
}
shouldComponentUpdate(nextProps, nextState) {
return nextProps.selectedExchange !== this.props.selectedExchange ||
nextState.selectedSymbol !== this.state.selectedSymbol;
}
onSelectionChanged(selectedSymbol) {
this.setState({
selectedSymbol
})
}
render() {
return (
<div style={{width: 1250}}>
<div>
<div style={{float: "left", marginRight: 25}}>
<PriceChangesGrid selectedExchange={this.props.selectedExchange}
onSelectionChanged={this.onSelectionChanged}/>
</div>
<div style={{float: "left"}}>
<StockDetailPanel selectedSymbol={this.state.selectedSymbol}
exchangeName={this.props.selectedExchange.name}/>
</div>
</div>
<div style={{width: "100%", clear: "both", paddingTop: 25}}>
<FxPanel/>
</div>
</div>
);
}
};

View File

@@ -0,0 +1,95 @@
import React, {Component} from "react";
import isEqual from "lodash/isEqual";
export default class extends Component {
constructor(props) {
super(props);
this.state = {
currentPrice: null,
delta: null,
deltaPercentage: null
};
}
componentDidMount() {
this.updatePriceDelta(this.props.pricingDelta);
}
componentWillReceiveProps(nextProps) {
if (!isEqual(this.props.pricingDelta, nextProps.pricingDelta)) {
this.updatePriceDelta(nextProps.pricingDelta);
}
}
updatePriceDelta(pricingDelta) {
let delta = pricingDelta.currentPrice - pricingDelta.previousPrice;
let deltaPercentage = (pricingDelta.currentPrice - pricingDelta.previousPrice) / pricingDelta.currentPrice;
this.setState({
currentPrice: pricingDelta.currentPrice,
delta,
deltaPercentage
})
}
shouldComponentUpdate(nextProps, nextState) {
return !isEqual(this.props.pricingDelta, nextProps.pricingDelta) ||
!isEqual(this.state, nextState);
}
numberFormatter(input) {
return input ? input.toFixed(2) : null;
}
render() {
let containerStyle = {
display: "inline-block"
};
let priceStyle = {
fontSize: "2.6em",
fontWeight: "bold",
marginRight: 10
};
let deltaStyle = {
fontWeight: "normal",
fontSize: "1.8em",
verticalAlign: "bottom"
};
let negativeSwingStyle = {
color: "#d14836",
marginRight: 5
};
let positiveSwingStyle = {
color: "#093",
marginRight: 5
};
let swingStyle = this.state.delta >= 0 ? positiveSwingStyle : negativeSwingStyle;
if (!this.props.pricingDelta) {
return null;
} else {
return (
<div>
<span style={priceStyle}>
{this.numberFormatter(this.state.currentPrice)}
</span>
<div style={containerStyle}>
<span style={deltaStyle}>
<span style={swingStyle}>{this.numberFormatter(this.state.delta)}</span>
<span
style={swingStyle}>({this.numberFormatter(this.state.deltaPercentage)}%)</span>
</span>
</div>
</div>
);
}
}
}

View File

@@ -0,0 +1,78 @@
import React, {Component} from "react";
export default class extends Component {
constructor(props) {
super(props);
}
render() {
let containerStyle = {
fontSize: 13
};
let tableStyle = {
display: "inline-block",
verticalAlign: "top",
borderCollapse: "collapse"
};
let keyStyle = {
color: "#666"
};
let valueStyle = {
textAlign: "right"
};
if (!this.props.tickerSummary) {
return null;
} else {
return (
<div style={containerStyle}>
<table style={tableStyle}>
<tbody>
<tr>
<td style={keyStyle}>Range</td>
<td style={valueStyle}>{this.props.tickerSummary.range}</td>
</tr>
<tr>
<td style={keyStyle}>52 week</td>
<td style={valueStyle}>{this.props.tickerSummary.fiftyTwoWeek}</td>
</tr>
<tr>
<td style={keyStyle}>Open</td>
<td style={valueStyle}>{this.props.tickerSummary.open}</td>
</tr>
<tr>
<td style={keyStyle}>Vol / Avg.</td>
<td style={valueStyle}>{this.props.tickerSummary.vol}/{this.props.tickerSummary.avg}
</td>
</tr>
</tbody>
</table>
<table style={tableStyle}>
<tbody>
<tr>
<td style={keyStyle}>Div/yield</td>
<td style={valueStyle}>{this.props.tickerSummary.dividend}/{this.props.tickerSummary.yld}</td>
</tr>
<tr>
<td style={keyStyle}>EPS</td>
<td style={valueStyle}>{this.props.tickerSummary.eps}</td>
</tr>
<tr>
<td style={keyStyle}>Shares</td>
<td style={valueStyle}>{this.props.tickerSummary.shares}</td>
</tr>
<tr>
<td style={keyStyle}>Market Cap</td>
<td style={valueStyle}>{this.props.tickerSummary.marketCap}</td>
</tr>
</tbody>
</table>
</div>
);
}
}
}

View File

@@ -0,0 +1,30 @@
import React, {Component} from "react";
export default class extends Component {
constructor(props) {
super(props);
}
render() {
let minorStyle = {
fontSize: 11,
color: "#6F6F6F"
};
if (!this.props.timestamp) {
return null;
} else {
return (
<div>
<div>
{this.props.timestamp}
<div style={minorStyle}>
<span>{this.props.exchangeName}</span>
<div>Currency in USD</div>
</div>
</div>
</div>
);
}
}
}

View File

@@ -0,0 +1,95 @@
import React, {Component} from "react";
import {connect} from "react-redux";
import {AgGridReact} from "@ag-grid-community/react";
import {ClientSideRowModelModule} from "@ag-grid-enterprise/all-modules";
class TopMoversGrid extends Component {
constructor(props) {
super(props);
this.state = {
columnDefs: [
{
field: 'symbol',
headerName: 'Symbol'
},
{
field: 'last',
headerName: 'Last',
headerClass: 'align-right',
cellRenderer: 'agAnimateShowChangeCellRenderer',
cellClass: 'align-right'
},
{
field: 'net',
headerName: 'Net',
headerClass: 'align-right',
cellRenderer: 'agAnimateShowChangeCellRenderer',
cellClass: 'align-right'
},
{
field: 'pct_net_change',
headerName: '% NC',
headerClass: 'align-right',
cellRenderer: 'agAnimateShowChangeCellRenderer',
cellClass: 'align-right',
sort: 'desc',
valueFormatter(params) {
return params.value.toFixed(2)
}
},
]
};
// grid events
this.onGridReady = this.onGridReady.bind(this);
// grid callbacks
this.getRowNodeId = this.getRowNodeId.bind(this);
}
getRowNodeId(data) {
return data.symbol;
}
onGridReady(params) {
this.gridApi = params.api;
this.columnApi = params.columnApi;
this.gridApi.sizeColumnsToFit();
}
render() {
return (
<div style={{height: 410, width: 400}}
className="ag-theme-balham">
<AgGridReact
// properties
columnDefs={this.state.columnDefs}
rowData={this.props.rowData}
defaultColDef={{
sortable: true,
filter: false
}}
animateRows
deltaRowDataMode
getRowNodeId={this.getRowNodeId}
modules={[ClientSideRowModelModule]}
// events
onGridReady={this.onGridReady}>
</AgGridReact>
</div>
);
}
}
export default connect(
(state) => {
return {
rowData: state ? state.fxTopMovers : null
}
}
)(TopMoversGrid);

View File

@@ -0,0 +1,43 @@
import React, {Component} from "react";
import ExchangeService from "../services/ExchangeService.jsx";
import ControlPanel from "./ControlPanel.jsx";
import StockPanel from "./StockPanel.jsx";
export default class TraderDashboard extends Component {
constructor(props) {
super(props);
this.exchangeService = new ExchangeService();
this.state = {
selectedExchange: this.exchangeService.getExchangeInformation("NASDAQ"),
exchanges: this.exchangeService.getExchanges()
};
this.onExchangeChanged = this.onExchangeChanged.bind(this);
}
onExchangeChanged(selectedExchange) {
this.setState({
selectedExchange: this.exchangeService.getExchangeInformation(selectedExchange)
});
}
render() {
return (
<div>
<div style={{marginTop: 25, marginBottom: 25}}>
<ControlPanel
exchanges={this.state.exchanges}
selectedExchange={this.state.selectedExchange}
onExchangeChanged={this.onExchangeChanged}>
</ControlPanel>
</div>
<StockPanel selectedExchange={this.state.selectedExchange}/>
</div>
);
}
};

View File

@@ -0,0 +1,58 @@
import React, {Component} from "react";
import * as PropTypes from 'prop-types';
export default class HorizontalBarComponent extends Component {
constructor(props) {
super(props);
this.state = {
value: this.props.value
}
}
render() {
let positiveChange = {
fill: "green"
};
let negativeChange = {
fill: "red"
};
let text = {
position: "absolute",
top: 0,
width: "100%",
textAlign: "right"
};
let pctNetChange = this.state.value;
let pctNetChangeBar = Math.min(Math.abs(pctNetChange) * 100, 100) / 2;
let barWidth = `${pctNetChangeBar}%`;
let barStyle = pctNetChange >= 0 ? positiveChange : negativeChange;
let barX = `${pctNetChange >= 0 ? '50' : 50 - pctNetChangeBar}%`;
return (
<div style={{position: 'relative'}}>
<div style={{width: "50%"}}>
<svg width="100%" preserveAspectRatio="none">
<rect x={barX} y="0" width={barWidth} height="20px" rx="4" ry="4" style={barStyle}/>
</svg>
</div>
<div style={text}>{pctNetChange}</div>
</div>
)
}
refresh(params) {
this.setState({
value: params.value
});
return true;
}
}
HorizontalBarComponent.propTypes = {
params: PropTypes.object
};

View File

@@ -0,0 +1,92 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script type="text/javascript" src="../dist/react-trader.js" charset="utf-8"></script>
<style>
html, body {
height: 100%;
}
.ag-fresh .ag-value-change-value-highlight {
background-color: #afbcff;
}
.align-right {
text-align: right
}
.pct-change-green {
background-color: lightgreen;
}
.pct-change-amber {
background-color: lightgoldenrodyellow;
}
.pct-change-red {
background-color: red;
}
.fx-null {
border-top: 20px solid #3ACFD5;
border-right: 20px solid #3a4ed5;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
background-position: 0 0, 0 100%;
background-repeat: no-repeat;
-webkit-background-size: 100% 20px;
-moz-background-size: 100% 20px;
background-size: 100% 20px;
/*background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj48bGluZWFyR3JhZGllbnQgaWQ9Imxlc3NoYXQtZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiMzYWNmZDUiIHN0b3Atb3BhY2l0eT0iMSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzNhNGVkNSIgc3RvcC1vcGFjaXR5PSIxIi8+PC9saW5lYXJHcmFkaWVudD48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2xlc3NoYXQtZ2VuZXJhdGVkKSIgLz48L3N2Zz4=),url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj48bGluZWFyR3JhZGllbnQgaWQ9Imxlc3NoYXQtZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiMzYWNmZDUiIHN0b3Atb3BhY2l0eT0iMSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzNhNGVkNSIgc3RvcC1vcGFjaXR5PSIxIi8+PC9saW5lYXJHcmFkaWVudD48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2xlc3NoYXQtZ2VuZXJhdGVkKSIgLz48L3N2Zz4=);*/
background-image: -webkit-linear-gradient(top, #afbcff 0%, #c1d5c9 100%), -webkit-linear-gradient(top, #afbcff 0%, #c1d5c9 100%);
background-image: -moz-linear-gradient(top, #afbcff 0%, #c1d5c9 100%), -moz-linear-gradient(top, #afbcff 0%, #c1d5c9 100%);
background-image: -o-linear-gradient(top, #afbcff 0%, #c1d5c9 100%), -o-linear-gradient(top, #afbcff 0%, #c1d5c9 100%);
background-image: linear-gradient(to bottom, #afbcff 0%, #c1d5c9 100%), linear-gradient(to bottom, #afbcff 0%, #c1d5c9 100%);
}
.fx-positive {
border-top: 20px solid #3ACFD5;
border-right: 20px solid #3a4ed5;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
background-position: 0 0, 0 100%;
background-repeat: no-repeat;
-webkit-background-size: 100% 20px;
-moz-background-size: 100% 20px;
background-size: 100% 20px;
/*background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj48bGluZWFyR3JhZGllbnQgaWQ9Imxlc3NoYXQtZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiMzYWNmZDUiIHN0b3Atb3BhY2l0eT0iMSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzNhNGVkNSIgc3RvcC1vcGFjaXR5PSIxIi8+PC9saW5lYXJHcmFkaWVudD48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2xlc3NoYXQtZ2VuZXJhdGVkKSIgLz48L3N2Zz4=),url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj48bGluZWFyR3JhZGllbnQgaWQ9Imxlc3NoYXQtZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiMzYWNmZDUiIHN0b3Atb3BhY2l0eT0iMSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzNhNGVkNSIgc3RvcC1vcGFjaXR5PSIxIi8+PC9saW5lYXJHcmFkaWVudD48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2xlc3NoYXQtZ2VuZXJhdGVkKSIgLz48L3N2Zz4=);*/
background-image: -webkit-linear-gradient(top, #00FF00 0%, #c1d5c9 100%), -webkit-linear-gradient(top, #00FF00 0%, #c1d5c9 100%);
background-image: -moz-linear-gradient(top, #00FF00 0%, #c1d5c9 100%), -moz-linear-gradient(top, #00FF00 0%, #c1d5c9 100%);
background-image: -o-linear-gradient(top, #00FF00 0%, #c1d5c9 100%), -o-linear-gradient(top, #00FF00 0%, #c1d5c9 100%);
background-image: linear-gradient(to bottom, #00FF00 0%, #c1d5c9 100%), linear-gradient(to bottom, #00FF00 0%, #c1d5c9 100%);
}
.fx-negative {
border-top: 20px solid #3ACFD5;
border-right: 20px solid #3a4ed5;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
background-position: 0 0, 0 100%;
background-repeat: no-repeat;
-webkit-background-size: 100% 20px;
-moz-background-size: 100% 20px;
background-size: 100% 20px;
/*background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj48bGluZWFyR3JhZGllbnQgaWQ9Imxlc3NoYXQtZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiMzYWNmZDUiIHN0b3Atb3BhY2l0eT0iMSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzNhNGVkNSIgc3RvcC1vcGFjaXR5PSIxIi8+PC9saW5lYXJHcmFkaWVudD48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2xlc3NoYXQtZ2VuZXJhdGVkKSIgLz48L3N2Zz4=),url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj48bGluZWFyR3JhZGllbnQgaWQ9Imxlc3NoYXQtZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiMzYWNmZDUiIHN0b3Atb3BhY2l0eT0iMSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzNhNGVkNSIgc3RvcC1vcGFjaXR5PSIxIi8+PC9saW5lYXJHcmFkaWVudD48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2xlc3NoYXQtZ2VuZXJhdGVkKSIgLz48L3N2Zz4=);*/
background-image: -webkit-linear-gradient(top, #FF0000 0%, #d5b3af 100%), -webkit-linear-gradient(top, #FF0000 0%, #d5b3af 100%);
background-image: -moz-linear-gradient(top, #FF0000 0%, #d5b3af 100%), -moz-linear-gradient(top, #FF0000 0%, #d5b3af 100%);
background-image: -o-linear-gradient(top, #FF0000 0%, #d5b3af 100%), -o-linear-gradient(top, #FF0000 0%, #d5b3af 100%);
background-image: linear-gradient(to bottom, #FF0000 0%, #d5b3af 100%), linear-gradient(to bottom, #FF0000 0%, #d5b3af 100%);
}
</style>
</head>
<body>
<div id="traderDashboard"></div>
</body>
</html>

View File

@@ -0,0 +1,24 @@
'use strict';
import React from "react";
import {render} from "react-dom";
import {Provider} from "react-redux";
import StoreService from './services/StoreService';
import TraderDashboard from "./components/TraderDashboard.jsx";
import "@ag-grid-enterprise/all-modules/dist/styles/ag-grid.css";
import "@ag-grid-enterprise/all-modules/dist/styles/ag-theme-balham.css";
let store = StoreService.STORE;
document.addEventListener('DOMContentLoaded', () => {
render(
<Provider store={store}>
<TraderDashboard />
</Provider>,
document.querySelector('#traderDashboard')
);
});

Some files were not shown because too many files have changed in this diff Show More