Compare commits

..

126 Commits

Author SHA1 Message Date
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
ceolter
eee08a9284 release 5.3.2 2016-09-02 16:43:50 +01:00
ceolter
68ba430896 release 5.3.2 2016-09-02 16:43:20 +01:00
134 changed files with 6779 additions and 537 deletions

View File

@@ -2,10 +2,22 @@
ag-Grid React Example
==============
Example of running ag-Grid inside React application.
Examples of running ag-Grid inside React application.
See the [www.ag-grid.com](http://www.ag-grid.com).
See [www.ag-grid.com](http://www.ag-grid.com) for an overview and full documentation.
Frameworks Supported
====================
Framework specific Getting Started guides:
[Angular 1](https://www.ag-grid.com/best-angularjs-data-grid/) | [Angular 2](https://www.ag-grid.com/best-angular-2-data-grid/) | [Aurelia](https://www.ag-grid.com/best-aurelia-data-grid/)
[Javascript](https://www.ag-grid.com/best-javascript-data-grid/) | [React](https://www.ag-grid.com/best-react-data-grid/) | [TypeScript](https://www.ag-grid.com/ag-grid-typescript-webpack-2/)
[VueJS](https://www.ag-grid.com/best-vuejs-data-grid/) | [Web Components](https://www.ag-grid.com/best-web-component-data-grid/)
There are two examples:
1. standard - shows a typical grid demonstrating many ag-Grid features
2. large - shows a very large grid (767 columns and 1,000 rows) using React cell renderers
Building
==============
@@ -13,6 +25,5 @@ Building
To build:
- `npm install`
- `npm install webpack -g`
- `npm start`
- `npm run examples`, `npm run large` or `npm run trader`
- navigate to localhost:8080

View File

@@ -0,0 +1,44 @@
const path = require('path');
const SRC_DIR = path.resolve(__dirname, '../src-examples');
module.exports = {
entry: SRC_DIR + "/index.js",
output: {
path: path.resolve(__dirname, '../'),
filename: "dist/react-examples.js"
},
module: {
loaders: [
{
test: /\.css$/,
loader: "style!css"
},
{
test: /\.js$|\.jsx$/,
include: SRC_DIR,
loader: 'babel-loader',
query: {
presets: ['react', 'es2015', 'stage-0']
}
},
{
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
loader: 'file?name=[path]/[name].[ext]'
}
]
},
resolve: {
alias: {
"ag-grid": path.resolve('./node_modules/ag-grid'),
"ag-grid-enterprise": path.resolve('./node_modules/ag-grid-enterprise'),
react: path.resolve('./node_modules/react')
},
extensions: ['', '.js', '.jsx']
},
devServer: {
historyApiFallback: true,
contentBase: './',
hot: true
}
};

View File

@@ -0,0 +1,35 @@
const path = require('path');
const SRC_DIR = path.resolve(__dirname, '../src-large-data');
module.exports = {
entry: SRC_DIR + "/index.js",
output: {
path: path.resolve(__dirname, '../'),
filename: "dist/react-large.js"
},
module: {
loaders: [
{
test: /\.css$/,
loader: "style!css"
},
{
test: /\.js$|\.jsx$/,
include: SRC_DIR,
loader: 'babel-loader',
query: {
presets: ['react', 'es2015', 'stage-0']
}
}
]
},
resolve: {
alias: {
"ag-grid": path.resolve('./node_modules/ag-grid'),
"ag-grid-enterprise": path.resolve('./node_modules/ag-grid-enterprise'),
react: path.resolve('./node_modules/react')
},
extensions: ['', '.js', '.jsx']
}
};

View File

@@ -0,0 +1,35 @@
const path = require('path');
const SRC_DIR = path.resolve(__dirname, '../src-trader-dashboard');
module.exports = {
entry: SRC_DIR + "/index.js",
output: {
path: path.resolve(__dirname, '../'),
filename: "dist/react-trader.js"
},
module: {
loaders: [
{
test: /\.css$/,
loader: "style!css"
},
{
test: /\.js$|\.jsx$/,
include: SRC_DIR,
loader: 'babel-loader',
query: {
presets: ['react', 'es2015', 'stage-0']
}
}
]
},
resolve: {
alias: {
"ag-grid": path.resolve('./node_modules/ag-grid'),
"ag-grid-enterprise": path.resolve('./node_modules/ag-grid-enterprise'),
react: path.resolve('./node_modules/react')
},
extensions: ['', '.js', '.jsx']
}
};

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,22 +0,0 @@
<html>
<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,14 +1,29 @@
{
"name": "ag-grid-react-example",
"version": "5.3.0",
"version": "14.2.0",
"description": "Example Reach applicaiton using ag-Grid.",
"main": "dist/ag-grid-react-example.js",
"scripts": {
"start": "webpack-dev-server --progress --colors --hot --inline"
"trader": "webpack-dev-server --content-base src-trader-dashboard/ --config config/webpack.config.trader.js --progress --colors --hot --inline",
"examples": "webpack-dev-server --content-base src-examples/ --config config/webpack.config.examples.js --progress --colors --hot --inline",
"large": "webpack-dev-server --content-base src-large-data/ --config config/webpack.config.large.js --progress --colors --hot --inline",
"clean": "rimraf dist",
"mkdirs": "mkdirp dist/trader/dist dist/examples/dist",
"copy-examples": "ncp images dist/examples/images && ncp src/index.html dist/examples/index.html && ncp dist/react-examples.js dist/examples/dist/react-examples.js && ncp src 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",
"copy-to-docs": "ncp dist/examples ../ag-grid-docs/src/framework-examples/react-examples/examples && ncp dist/trader ../ag-grid-docs/src/framework-examples/react-examples/trader",
"build-to-docs": "npm run build && npm run copy-to-docs",
"start": "npm run examples"
},
"repository": {
"type": "git",
"url": "https://github.com/ceolter/ag-grid-react-example.git"
"url": "https://github.com/ag-grid/ag-grid-react-example.git"
},
"keywords": [
"react",
@@ -19,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"
"babel-core": "6.24.x",
"babel-loader": "6.4.x",
"babel-preset-es2015": "6.24.x",
"babel-preset-react": "6.24.x",
"babel-preset-stage-0": "6.24.x",
"babel-preset-stage-1": "6.24.x",
"css-loader": "0.23.x",
"mkdirp": "0.5.1",
"ncp": "2.0.0",
"prop-types": "15.6.0",
"rimraf": "2.5.x",
"style-loader": "0.13.x",
"webpack": "1.12.x",
"webpack-dev-server": "1.14.x",
"gulp": "3.9.x",
"gulp-typescript": "3.1.x",
"merge2": "1.0.x",
"typescript": "2.3.x"
},
"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.0"
"ag-grid": "^14.0.0",
"ag-grid-enterprise": "^14.0.0",
"ag-grid-react": "^14.0.0",
"bootstrap": "3.3.7",
"d3": "4.9.1",
"file-loader": "0.11.1",
"lodash": "4.17.4",
"react": "16.0.0",
"react-dom": "16.0.0",
"react-dom-factories": "1.0.2",
"react-redux": "5.0.x",
"react-router-dom": "4.2.x",
"redux": "3.6.x",
"url-search-params-polyfill": "1.2.0"
}
}

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

@@ -0,0 +1,65 @@
import React, {Component} from "react";
import {Redirect, Route, Switch} from "react-router-dom";
import NavItem from "./NavItem";
import DynamicComponentsExample from "./dynamicComponentExample/DynamicComponentsExample";
import RichGridExample from "./richGridExample/RichGridExample";
import RichGridDeclarativeExample from "./richGridDeclarativeExample/RichGridDeclarativeExample";
import RichComponentsExample from "./richComponentExample/RichComponentsExample";
import EditorComponentsExample from "./editorComponentExample/EditorComponentsExample";
import PinnedRowComponentExample from "./pinnedRowExample/PinnedRowComponentExample";
import FullWidthComponentExample from "./fullWidthExample/FullWidthComponentExample";
import GroupedRowInnerRendererComponentExample from "./groupedRowInnerRendererExample/GroupedRowInnerRendererComponentExample";
import FilterComponentExample from "./filterComponentExample/FilterComponentExample";
import SimpleReduxExample from "./simpleReduxExample/SimpleReduxExample";
import FloatingFilterGridExample from "./floatingFilter/FloatingFilterGridExample";
import SimpleReduxDynamicExample from "./simpleReduxDynamicComponentExample/SimpleReduxExample";
const SideBar = () => (
<div style={{float: "left", width: 335, marginRight: 25}}>
<ul className="nav nav-pills nav-stacked">
<NavItem to='/rich-grid'>Rich Grid Example</NavItem>
<NavItem to='/rich-grid-declarative'>Rich Grid with Declarative Markup</NavItem>
<NavItem to='/dynamic'>Dynamic React Component Example</NavItem>
<NavItem to='/rich-dynamic'>Dynamic React Components - Richer Example</NavItem>
<NavItem to='/editor'>Cell Editor Component Example</NavItem>
<NavItem to='/floating-row'>Floating Row Renderer Example</NavItem>
<NavItem to='/full-width'>Full Width Renderer Example</NavItem>
<NavItem to='/group-row'>Grouped Row Inner Renderer Example</NavItem>
<NavItem to='/filter'>Filters Component Example</NavItem>
<NavItem to='/floating-filter'>Floating Filters</NavItem>
<NavItem to='/simple-redux'>Simple Redux Example</NavItem>
<NavItem to='/simple-redux-dynamic'>Simple Redux Dynamic 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"/>
<Route exact path='/rich-grid' component={RichGridExample}/>
<Route exact path='/rich-grid-declarative' component={RichGridDeclarativeExample}/>
<Route exact path='/dynamic' component={DynamicComponentsExample}/>
<Route exact path='/rich-dynamic' component={RichComponentsExample}/>
<Route exact path='/editor' component={EditorComponentsExample}/>
<Route exact path='/floating-row' component={PinnedRowComponentExample}/>
<Route exact path='/full-width' component={FullWidthComponentExample}/>
<Route exact path='/group-row' component={GroupedRowInnerRendererComponentExample}/>
<Route exact path='/filter' component={FilterComponentExample}/>
<Route exact path='/floating-filter' component={FloatingFilterGridExample}/>
<Route exact path='/simple-redux' component={SimpleReduxExample}/>
<Route exact path='/simple-redux-dynamic' component={SimpleReduxDynamicExample}/>
</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 {Route, Link} 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={match ? 'active' : null}>
<Link to={to}>{children}</Link>
</li>
)}/>
)
}
NavItem.propTypes = {
to: PropTypes.string.isRequired,
exact: PropTypes.bool,
children: PropTypes.node.isRequired,
};

View File

@@ -0,0 +1,20 @@
import React, {Component} from "react";
export default class ChildMessageRenderer extends Component {
constructor(props) {
super(props);
}
invokeParentMethod = () => {
let rowIndex = this.props.node.rowIndex;
let headerName = this.props.colDef.headerName;
this.props.context.componentParent.methodFromParent(`Row: ${rowIndex}, Col: ${headerName}`)
};
render() {
return (
<span><button style={{height: 20, lineHeight: 0.5}} onClick={this.invokeParentMethod} className="btn btn-info">Invoke Parent</button></span>
);
}
};

View File

@@ -0,0 +1,21 @@
import React, {Component} from "react";
export default class CubeRenderer extends Component {
constructor(props) {
super(props);
this.state = {
value: this.valueSquared()
};
}
valueSquared() {
return this.props.value * this.props.value * this.props.value;
}
render() {
return (
<span>{this.state.value}</span>
);
}
};

View File

@@ -0,0 +1,31 @@
import React, {Component} from "react";
export default class CurrencyRenderer extends Component {
constructor(props) {
super(props);
this.state = {
value: props.value
}
}
static formatValueToCurrency(currency, value) {
return `${currency}${value}`
}
// noinspection JSUnusedGlobalSymbols
refresh(params) {
if(params.value !== this.state.value) {
this.setState({
value: params.value.toFixed(2)
})
}
return true;
}
render() {
return (
<span>{CurrencyRenderer.formatValueToCurrency('EUR', this.state.value)}</span>
);
}
};

View File

@@ -0,0 +1,162 @@
import React, {Component} from "react";
import {AgGridReact} from "ag-grid-react";
import SquareRenderer from "./SquareRenderer";
import CubeRenderer from "./CubeRenderer";
import ParamsRenderer from "./ParamsRenderer";
import CurrencyRenderer from "./CurrencyRenderer";
import ChildMessageRenderer from "./ChildMessageRenderer";
export default class DynamicComponentsExample extends Component {
constructor(props) {
super(props);
this.state = {
gridOptions: {
context: {
componentParent: this
}
},
rowData: DynamicComponentsExample.createRowData(),
columnDefs: DynamicComponentsExample.createColumnDefs()
};
}
onGridReady = (params) => {
this.gridApi = params.api;
this.columnApi = params.columnApi;
this.gridApi.sizeColumnsToFit();
};
methodFromParent(cell) {
alert(`Parent Component Method from ${cell}!`);
}
refreshEvenRowsCurrencyData = () => {
this.gridApi.forEachNode(rowNode => {
if (rowNode.data.value % 2 === 0) {
rowNode.setDataValue('currency', rowNode.data.value + Number(Math.random().toFixed(2)))
}
});
this.gridApi.refreshCells({
columns: ['currency']
})
};
static createColumnDefs() {
return [
{headerName: "Row", field: "row", width: 100},
{
headerName: "Square",
field: "value",
cellRendererFramework: SquareRenderer,
editable: true,
colId: "square",
width: 100
},
{
headerName: "Cube",
field: "value",
cellRendererFramework: CubeRenderer,
colId: "cube",
width: 100
},
{
headerName: "Row Params",
field: "row",
cellRendererFramework: ParamsRenderer,
colId: "params",
width: 215
},
{
headerName: "Currency",
field: "currency",
cellRendererFramework: CurrencyRenderer,
colId: "currency",
width: 135
},
{
headerName: "Child/Parent",
field: "value",
cellRendererFramework: ChildMessageRenderer,
colId: "params",
width: 120
}
];
}
static createRowData() {
let rowData = [];
for (let i = 0; i < 15; i++) {
rowData.push({
row: "Row " + i,
value: i,
currency: 1 + Number(Math.random()).toFixed(2)
});
}
return rowData;
}
render() {
return (
<div style={{height: 400, width: 900}}
className="ag-fresh">
<h1>Dynamic React Component Example</h1>
<button onClick={this.refreshEvenRowsCurrencyData} style={{marginBottom: 10}}
className="btn btn-primary">Refresh Even Row Currency Data
</button>
<AgGridReact
// properties
columnDefs={this.state.columnDefs}
rowData={this.state.rowData}
gridOptions={this.state.gridOptions}
// events
onGridReady={this.onGridReady}>
</AgGridReact>
<div className="row">
<div className="col-sm-12"><h1>Dynamic React Component Example</h1></div>
</div>
<div className="row">
<div className="col-sm-12">
<h5>This example demonstrates Dynamic React Components with ag-Grid, Parent/Child Communication
(cell component to parent grid component), as well as
dynamic data updates of the <code>Currency</code> column.</h5>
<p><span style={{fontWeight: "bold"}}>Square, Cube, Row Params, Currency and Child/Parent</span>:
React Components within the Grid</p>
<p><span style={{fontWeight: "bold"}}>Currency (Pipe)</span>: An React Component mimicking a
Currency Pipe, dynamically updated with the button above.</p>
<p><span style={{fontWeight: "bold"}}>Child/Parent</span>: Demonstrates the Child Cell Component
communicating with the Parent Grid Component.</p>
<p><span style={{fontWeight: "bold"}}>Refresh Even Row Currency Data</span>: Dynamically Updates
Event Rows Currency Value. Only the Currency column will be re-rendered.</p>
</div>
</div>
<div className="row">
<div className="col-sm-12">
<div className="card">
<div className="card-body">
<h4 className="card-title">React Functionality</h4>
<p className="card-text">Utilise React Components within ag-Grid</p>
<a target="_blank" href="https://www.ag-grid.com/best-react-data-grid/?framework=react"
className="btn btn-primary">React with ag-Grid</a>
<a target="_blank"
href="https://www.ag-grid.com/javascript-grid-cell-rendering-components/?framework=react"
className="btn btn-primary">React Renderers</a>
<a target="_blank" href="https://www.ag-grid.com/javascript-grid-data-update/"
className="btn btn-primary">Updating Data</a>
</div>
</div>
</div>
</div>
</div>
);
}
};

View File

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

View File

@@ -0,0 +1,21 @@
import React, {Component} from "react";
export default class SquareRenderer extends Component {
constructor(props) {
super(props);
this.state = {
value: this.valueSquared()
};
}
valueSquared() {
return this.props.value * this.props.value;
}
render() {
return (
<span>{this.state.value}</span>
);
}
};

View File

@@ -0,0 +1,132 @@
import React, {Component} from "react";
import {AgGridReact} from "ag-grid-react";
import MoodRenderer from "./MoodRenderer";
import MoodEditor from "./MoodEditor";
import NumericEditor from "./NumericEditor";
export default class EditorComponentsExample extends Component {
constructor(props) {
super(props);
this.state = {
rowData: EditorComponentsExample.createRowData(),
columnDefs: EditorComponentsExample.createColumnDefs()
};
this.onGridReady = this.onGridReady.bind(this);
}
onGridReady(params) {
this.gridApi = params.api;
this.columnApi = params.columnApi;
this.gridApi.sizeColumnsToFit();
}
static createColumnDefs() {
return [
{
headerName: "Name",
field: "name",
width: 300,
editable: true,
cellEditor: 'richSelect',
cellEditorParams: {
values: [
"Bob",
"Harry",
"Sally",
"Mary",
"John",
"Jack",
"Sue",
"Sean",
"Niall",
"Albert",
"Fred",
"Jenny",
"Larry"
]
}
},
{
headerName: "Mood",
field: "mood",
cellRendererFramework: MoodRenderer,
cellEditorFramework: MoodEditor,
editable: true,
width: 250
},
{
headerName: "Numeric",
field: "number",
cellEditorFramework: NumericEditor,
editable: true,
width: 250
}
];
}
static createRowData() {
return [
{name: "Bob", mood: "Happy", number: 10},
{name: "Harry", mood: "Sad", number: 3},
{name: "Sally", mood: "Happy", number: 20},
{name: "Mary", mood: "Sad", number: 5},
{name: "John", mood: "Happy", number: 15},
{name: "Jack", mood: "Happy", number: 25},
{name: "Sue", mood: "Sad", number: 43},
{name: "Sean", mood: "Sad", number: 1335},
{name: "Niall", mood: "Happy", number: 2},
{name: "Alberto", mood: "Happy", number: 123},
{name: "Fred", mood: "Sad", number: 532},
{name: "Jenny", mood: "Happy", number: 34},
{name: "Larry", mood: "Happy", number: 13},
];
}
render() {
return (
<div style={{height: 370, width: 900}}
className="ag-fresh">
<h1>Cell Editor Component Example</h1>
<AgGridReact
// properties
columnDefs={this.state.columnDefs}
rowData={this.state.rowData}
// events
onGridReady={this.onGridReady}>
</AgGridReact>
<div className="row">
<div className="col-sm-12"><h1>Cell Editor Component Example</h1></div>
</div>
<div className="row">
<div className="col-sm-12">
<h5>This example demonstrates React Editor Components within ag-Grid, as well as an example using the built in Rich Select editor.</h5>
<p><span style={{fontWeight: "bold"}}>Name</span>: Utilises the built in <code>RichSelect</code> editor</p>
<p><span style={{fontWeight: "bold"}}>Mood</span>: A Custom React Editor demonstrating popup functionality, with full keyboard control.</p>
<p><span style={{fontWeight: "bold"}}>Numeric</span>: A Custom React Editor demonstrating pre & post validation. Only numeric characters are allowed,
and numbers greater than 1000000 will be rejected.</p>
</div>
</div>
<div className="row">
<div className="col-sm-12">
<div className="card">
<div className="card-body">
<h4 className="card-title">React Functionality</h4>
<p className="card-text">Utilise React Components within ag-Grid</p>
<a target="_blank" href="https://www.ag-grid.com/javascript-grid-cell-editing/?framework=react" className="btn btn-primary">Cell Editing</a>
<a target="_blank" href="https://www.ag-grid.com/javascript-grid-cell-editor/?framework=react" className="btn btn-primary">Editor Components</a>
<a target="_blank" href="https://www.ag-grid.com/best-react-data-grid/?framework=react" className="btn btn-primary">React with ag-Grid</a>
<a target="_blank" href="https://www.ag-grid.com/javascript-grid-cell-editor/?framework=react#reactCellEditing" className="btn btn-primary">React Editor Components</a>
</div>
</div>
</div>
</div>
</div>
);
}
};

View File

@@ -0,0 +1,120 @@
import React, {Component} from "react";
import ReactDOM from "react-dom";
export default class MoodEditor extends Component {
constructor(props) {
super(props);
this.onHappyClick = this.onHappyClick.bind(this);
this.onSadClick = this.onSadClick.bind(this);
this.state = {
happy: false
}
}
componentWillMount() {
this.setHappy(this.props.value === "Happy");
}
componentDidMount() {
this.refs.container.addEventListener('keydown', this.checkAndToggleMoodIfLeftRight);
this.focus();
}
componentWillUnmount() {
this.refs.container.removeEventListener('keydown', this.checkAndToggleMoodIfLeftRight);
}
checkAndToggleMoodIfLeftRight = (event) => {
if ([37, 39].indexOf(event.keyCode) > -1) { // left and right
this.toggleMood();
event.stopPropagation();
}
}
componentDidUpdate() {
this.focus();
}
focus() {
setTimeout(() => {
let container = ReactDOM.findDOMNode(this.refs.container);
if (container) {
container.focus();
}
})
}
getValue() {
return this.state.happy ? "Happy" : "Sad";
}
isPopup() {
return true;
}
setHappy(happy) {
this.setState({
happy
});
}
onHappyClick() {
this.setState({
happy: true
},
() => this.props.api.stopEditing()
);
}
onSadClick() {
this.setState({
happy: false
},
() => this.props.api.stopEditing()
);
}
toggleMood() {
this.setHappy(!this.state.happy);
}
render() {
let mood = {
borderRadius: 15,
border: "1px solid grey",
background: "#e6e6e6",
padding: 15,
textAlign: "center",
display: "inline-block"
};
let unselected = {
paddingLeft: 10,
paddingRight: 10,
border: "1px solid transparent",
padding: 4
};
let selected = {
paddingLeft: 10,
paddingRight: 10,
border: "1px solid lightgreen",
padding: 4
};
let happyStyle = this.state.happy ? selected : unselected;
let sadStyle = !this.state.happy ? selected : unselected;
return (
<div ref="container"
style={mood}
tabIndex={1} // important - without this the keypresses wont be caught
>
<img src="images/smiley.png" onClick={this.onHappyClick} style={happyStyle}/>
<img src="images/smiley-sad.png" onClick={this.onSadClick} style={sadStyle}/>
</div>
);
}
}

View File

@@ -0,0 +1,27 @@
import React, {Component} from "react";
export default class MoodRenderer extends Component {
constructor(props) {
super(props);
}
componentWillMount() {
this.setMood(this.props.value)
}
refresh(params) {
this.setMood(params.value);
}
setMood(mood) {
this.setState({
imgForMood: mood === 'Happy' ? 'images/smiley.png' : 'images/smiley-sad.png'
})
};
render() {
return (
<img width="20px" src={this.state.imgForMood}/>
);
}
}

View File

@@ -0,0 +1,102 @@
import React, {Component} from "react";
export default class NumericEditor extends Component {
constructor(props) {
super(props);
// if the character pressed to start editing isn't numeric we set this flag, which will mean editing wont be initiated
this.cancelBeforeStart = this.props.charPress && ('1234567890'.indexOf(this.props.charPress) < 0);
let value = this.props.value;
if (!this.cancelBeforeStart && this.props.charPress) {
value = value + this.props.charPress;
}
this.state = {
value
};
}
componentDidMount() {
this.refs.input.addEventListener('keydown', this.onKeyDown);
this.focus();
}
componentWillUnmount() {
this.refs.input.removeEventListener('keydown', this.onKeyDown);
}
// componentDidUpdate() {
// this.focus();
// }
focus() {
if (!this.cancelBeforeStart) {
setTimeout(() => {
this.refs.input.focus();
this.refs.input.setSelectionRange(this.refs.input.value.length, this.refs.input.value.length);
})
}
}
getValue() {
return this.state.value;
}
isCancelBeforeStart() {
return this.cancelBeforeStart;
}
// will reject the number if it greater than 1,000,000
// not very practical, but demonstrates the method.
isCancelAfterEnd() {
return this.state.value > 1000000;
};
handleChange = (event) => {
this.setState({value: event.target.value});
};
onKeyDown = (event) => {
// allow back and forth navigation, as well as editing
if (NumericEditor.isAllowableKeypress(event)) {
event.stopPropagation();
return;
}
// if not a numeral, then reject the keypress
if (!NumericEditor.isKeyPressedNumeric(event)) {
if (event.preventDefault) event.preventDefault();
}
};
static isAllowableKeypress(event) {
// left, right, backspace & delete
return [37, 39, 46, 8].indexOf(event.keyCode) > -1;
}
static getCharCodeFromEvent(event) {
event = event || window.event;
return (typeof event.which === "undefined") ? event.keyCode : event.which;
}
static isCharNumeric(charStr) {
return !!/\d/.test(charStr);
}
static isKeyPressedNumeric(event) {
const charCode = NumericEditor.getCharCodeFromEvent(event);
const charStr = event.key ? event.key : String.fromCharCode(charCode);
return this.isCharNumeric(charStr);
}
render() {
return (
<input ref="input"
value={this.state.value}
onChange={this.handleChange}
style={{width: "100%",padding:0,margin:-1}}
/>
);
}
}

View File

@@ -0,0 +1,90 @@
import React, {Component} from "react";
import {AgGridReact} from "ag-grid-react";
import "ag-grid-enterprise";
import PartialMatchFilter from "./PartialMatchFilter";
export default class FilterComponentExample extends Component {
constructor(props) {
super(props);
this.state = {
rowData: FilterComponentExample.createRowData(),
columnDefs: FilterComponentExample.createColumnDefs(),
};
}
onGridReady = (params) => {
this.gridApi = params.api;
this.columnApi = params.columnApi;
this.gridApi.sizeColumnsToFit();
};
onClicked = () => {
this.gridApi.getFilterInstance("name").getFrameworkComponentInstance().componentMethod("Hello World!");
};
static createColumnDefs() {
return [
{
field: "row",
width: 400,
suppressMenu: true
},
{
headerName: "Filter Component",
field: "name",
filterFramework: PartialMatchFilter,
width: 400,
menuTabs: ['filterMenuTab']
}
];
}
static createRowData() {
return [
{"row": "Row 1", "name": "Michael Phelps"},
{"row": "Row 2", "name": "Natalie Coughlin"},
{"row": "Row 3", "name": "Aleksey Nemov"},
{"row": "Row 4", "name": "Alicia Coutts"},
{"row": "Row 5", "name": "Missy Franklin"},
{"row": "Row 6", "name": "Ryan Lochte"},
{"row": "Row 7", "name": "Allison Schmitt"},
{"row": "Row 8", "name": "Natalie Coughlin"},
{"row": "Row 9", "name": "Ian Thorpe"},
{"row": "Row 10", "name": "Bob Mill"},
{"row": "Row 11", "name": "Willy Walsh"},
{"row": "Row 12", "name": "Sarah McCoy"},
{"row": "Row 13", "name": "Jane Jack"},
{"row": "Row 14", "name": "Tina Wills"}
];
}
render() {
return (
<div style={{height: 400, width: 900}}
className="ag-fresh">
<h1>Filter Component Example</h1>
<button style={{marginBottom: 10}} onClick={this.onClicked} className="btn btn-primary">Filter Instance
Method
</button>
<AgGridReact
// properties
columnDefs={this.state.columnDefs}
rowData={this.state.rowData}
enableFilter
suppressMenuHide
// events
onGridReady={this.onGridReady}>
</AgGridReact>
<h4>The filter on the Filter Component column uses a Custom React Filter Component</h4>
<p>This filter will allow partial matches - for example enter "m p" which will match Michael Phelps.</p>
</div>
);
}
};

View File

@@ -0,0 +1,83 @@
import React, {Component} from "react";
export default class PartialMatchFilter extends Component {
constructor(props) {
super(props);
this.state = {
text: ''
};
this.valueGetter = this.props.valueGetter;
}
isFilterActive() {
return this.state.text !== null && this.state.text !== undefined && this.state.text !== '';
}
doesFilterPass(params) {
return this.state.text.toLowerCase()
.split(" ")
.every((filterWord) => {
return this.valueGetter(params.node).toString().toLowerCase().indexOf(filterWord) >= 0;
});
}
getModel() {
return {value: this.state.text};
}
setModel(model) {
this.state.text = model ? model.value : '';
}
componentDidMount() {
this.focus();
}
focus() {
setTimeout(() => {
if (this.refs.input) {
this.refs.input.focus();
}
})
}
componentMethod(message) {
alert(`Alert from PartialMatchFilterComponent ${message}`);
}
onChange = (event) => {
let newValue = event.target.value;
if (this.state.text !== newValue) {
this.setState({
text: newValue
}, () => {
// wait for the state to be applied, then let the grid know about the change
this.props.filterChangedCallback();
});
}
};
render() {
console.log("render");
let style = {
border: "2px solid #22ff22",
borderRadius: "5px",
backgroundColor: "#bbffbb",
width: "200px",
height: "50px"
};
return (
<div style={style}>
Filter: <input style={{height: "20px"}}
ref="input"
value={this.state.text}
onChange={this.onChange}
className="form-control"/>
</div>
);
}
};

View File

@@ -0,0 +1,134 @@
import React, {Component} from "react";
import {AgGridReact} from "ag-grid-react";
import SlidingFloatingFilter from "./SlidingFloatingFilter";
export default class FloatingFilterGridExample extends Component {
constructor(props) {
super(props);
this.state = {
columnDefs: FloatingFilterGridExample.createColumnDefs(),
rowData: FloatingFilterGridExample.createRowData()
}
}
onGridReady(params) {
this.gridApi = params.api;
this.columnApi = params.columnApi;
this.gridApi.sizeColumnsToFit();
}
static createColumnDefs() {
return [
{
field: "country",
suppressFilter: true
},
{
field: "language",
suppressFilter: true
},
{
field: "name",
suppressFilter: true
},
{
field: "gold",
floatingFilterComponentFramework: SlidingFloatingFilter,
floatingFilterComponentParams: {
maxValue: 7,
suppressFilterButton: true
},
filter: 'number',
suppressMenu: false
},
{
field: "silver",
filter: 'number',
floatingFilterComponentFramework: SlidingFloatingFilter,
floatingFilterComponentParams: {
maxValue: 5,
suppressFilterButton: true
},
suppressMenu: false
},
{
field: "bronze",
filter: 'number',
floatingFilterComponentFramework: SlidingFloatingFilter,
floatingFilterComponentParams: {
maxValue: 10,
suppressFilterButton: true
},
suppressMenu: false
}
];
}
static createRowData() {
return [
{country: "United States", language: "English", name: "Bob", gold: 1, silver: 3, bronze: 0},
{country: "United States", language: "English", name: "Jack", gold: 0, silver: 1, bronze: 1},
{country: "United States", language: "English", name: "Sue", gold: 3, silver: 0, bronze: 1},
{country: "United Kingdom", language: "English", name: "Mary", gold: 1, silver: 1, bronze: 2},
{country: "United Kingdom", language: "English", name: "Tess", gold: 2, silver: 1, bronze: 1},
{country: "United Kingdom", language: "English", name: "John", gold: 0, silver: 2, bronze: 1},
{country: "Jamaica", language: "English", name: "Bob", gold: 4, silver: 1, bronze: 2},
{country: "Jamaica", language: "English", name: "Jack", gold: 3, silver: 1, bronze: 2},
{country: "Jamaica", language: "English", name: "Mary", gold: 1, silver: 1, bronze: 2},
{country: "South Africa", language: "English", name: "Bob", gold: 4, silver: 0, bronze: 1},
{country: "South Africa", language: "English", name: "Jack", gold: 3, silver: 0, bronze: 1},
{country: "South Africa", language: "English", name: "Mary", gold: 5, silver: 3, bronze: 1},
{country: "South Africa", language: "English", name: "John", gold: 2, silver: 3, bronze: 1},
{country: "New Zealand", language: "English", name: "Bob", gold: 5, silver: 3, bronze: 10},
{country: "New Zealand", language: "English", name: "Jack", gold: 1, silver: 1, bronze: 1},
{country: "New Zealand", language: "English", name: "Sue", gold: 1, silver: 3, bronze: 1},
{country: "Australia", language: "English", name: "Mary", gold: 1, silver: 1, bronze: 3},
{country: "Australia", language: "English", name: "Tess", gold: 2, silver: 1, bronze: 1},
{country: "Australia", language: "English", name: "John", gold: 3, silver: 2, bronze: 1},
{country: "Canada", language: "English", name: "Bob", gold: 7, silver: 5, bronze: 3},
{country: "Canada", language: "English", name: "Jack", gold: 2, silver: 1, bronze: 3},
{country: "Canada", language: "English", name: "Mary", gold: 0, silver: 1, bronze: 3},
{country: "Switzerland", language: "French", name: "Bob", gold: 2, silver: 3, bronze: 1},
{country: "Switzerland", language: "French", name: "Jack", gold: 2, silver: 3, bronze: 1},
{country: "Switzerland", language: "French", name: "Mary", gold: 1, silver: 3, bronze: 1},
{country: "Switzerland", language: "French", name: "John", gold: 1, silver: 4, bronze: 1},
{country: "Spain", language: "Spanish", name: "Bob", gold: 1, silver: 4, bronze: 0},
{country: "Spain", language: "Spanish", name: "Jack", gold: 0, silver: 1, bronze: 1},
{country: "Spain", language: "Spanish", name: "Sue", gold: 1, silver: 4, bronze: 1},
{country: "Portugal", language: "Portuguese", name: "Mary", gold: 2, silver: 1, bronze: 4},
{country: "Portugal", language: "Portuguese", name: "Tess", gold: 3, silver: 1, bronze: 1},
{country: "Portugal", language: "Portuguese", name: "John", gold: 5, silver: 2, bronze: 1},
{country: "Zimbabwe", language: "English", name: "Bob", gold: 3, silver: 1, bronze: 4},
{country: "Zimbabwe", language: "English", name: "Jack", gold: 4, silver: 1, bronze: 4},
{country: "Zimbabwe", language: "English", name: "Mary", gold: 2, silver: 1, bronze: 4},
{country: "Brazil", language: "Brazillian", name: "Bob", gold: 3, silver: 4, bronze: 1},
{country: "Brazil", language: "Brazillian", name: "Jack", gold: 2, silver: 4, bronze: 1},
{country: "Brazil", language: "Brazillian", name: "Mary", gold: 4, silver: 0, bronze: 1},
{country: "Brazil", language: "Brazillian", name: "John", gold: 1, silver: 0, bronze: 1}];
}
render() {
let divStyle = {
height: 400,
width: 900
};
return (
<div style={divStyle} className="ag-fresh">
<h1>Floating Filter Example</h1>
<AgGridReact
// properties
columnDefs={this.state.columnDefs}
rowData={this.state.rowData}
floatingFilter
// events
onGridReady={this.onGridReady}>
</AgGridReact>
</div>
)
}
};

View File

@@ -0,0 +1,53 @@
import React, {Component} from "react";
export default class SlidingFloatingFilter extends Component {
constructor(props) {
super(props);
this.state = {
maxValue: props.maxValue,
currentValue: 0
}
}
valueChanged = (event) => {
this.setState({
currentValue: event.target.value
},
() => {
this.props.onFloatingFilterChanged({model: this.buildModel()});
})
};
onParentModelChanged(parentModel) {
// note that the filter could be anything here, but our purposes we're assuming a greater than filter only,
// so just read off the value and use that
this.setState({
currentValue: !parentModel ? 0 : parentModel.filter
});
}
buildModel() {
if (this.state.currentValue === 0) {
return null;
}
return {
filterType: 'number',
type: 'greaterThan',
filter: this.state.currentValue,
filterTo: null
};
}
render() {
return (
<input type="range"
value={this.state.currentValue}
min={0}
max={this.state.maxValue}
step={1}
onChange={this.valueChanged}/>
)
}
}

View File

@@ -0,0 +1,88 @@
import React, {Component} from "react";
import {AgGridReact} from "ag-grid-react";
import NameAndAgeRenderer from "./NameAndAgeRenderer";
export default class FullWidthComponentExample extends Component {
constructor(props) {
super(props);
this.state = {
gridOptions: {},
rowData: FullWidthComponentExample.createRowData(),
columnDefs: FullWidthComponentExample.createColumnDefs()
};
this.onGridReady = this.onGridReady.bind(this);
}
onGridReady(params) {
this.gridApi = params.api;
this.columnApi = params.columnApi;
this.gridApi.sizeColumnsToFit();
}
isFullWidthCell(rowNode) {
return (rowNode.id === "0") || (parseInt(rowNode.id) % 2 === 0);
}
static createColumnDefs() {
return [
{
headerName: "Name",
field: "name",
width: 400
},
{
headerName: "Age",
field: "age",
width: 399
},
];
}
static createRowData() {
return [
{name: "Bob", age: 10},
{name: "Harry", age: 3},
{name: "Sally", age: 20},
{name: "Mary", age: 5},
{name: "John", age: 15},
{name: "Bob", age: 10},
{name: "Harry", age: 3},
{name: "Sally", age: 20},
{name: "Mary", age: 5},
{name: "John", age: 15},
{name: "Jack", age: 25},
{name: "Sue", age: 43},
{name: "Sean", age: 44},
{name: "Niall", age: 2},
{name: "Alberto", age: 32},
{name: "Fred", age: 53},
{name: "Jenny", age: 34},
{name: "Larry", age: 13},
];
}
render() {
return (
<div style={{height: 400, width: 900}}
className="ag-fresh">
<h1>Full Width Renderer Example</h1>
<AgGridReact
// properties
columnDefs={this.state.columnDefs}
rowData={this.state.rowData}
isFullWidthCell={this.isFullWidthCell}
fullWidthCellRendererFramework={NameAndAgeRenderer}
// events
onGridReady={this.onGridReady}>
</AgGridReact>
</div>
);
}
};

View File

@@ -0,0 +1,21 @@
import React, {Component} from "react";
export default class NameAndAgeRenderer extends Component {
constructor(props) {
super(props);
this.values = `Name: ${this.props.data.name}, Age: ${this.props.data.age}`;
}
render() {
let style = {
border: "2px solid #22ff22",
borderRadius: "5px",
backgroundColor: "#bbffbb"
};
return (
<div style={style}>Full Width Column! { this.values }</div>
);
}
};

View File

@@ -0,0 +1,126 @@
import React, {Component} from "react";
import {AgGridReact} from "ag-grid-react";
import MedalRenderer from "./MedalRenderer";
import "ag-grid-enterprise";
export default class GroupedRowInnerRendererComponentExample extends Component {
constructor(props) {
super(props);
this.state = {
gridOptions: {},
rowData: this.createRowData(),
columnDefs: this.createColumnDefs(),
};
this.onGridReady = this.onGridReady.bind(this);
}
onGridReady(params) {
this.gridApi = params.api;
this.columnApi = params.columnApi;
this.gridApi.sizeColumnsToFit();
}
createColumnDefs() {
return [
{
headerName: "Country",
field: "country",
width: 100,
rowGroupIndex: 0
},
{
headerName: "Name",
field: "name",
width: 100
},
{
headerName: "Gold",
field: "gold",
width: 100,
aggFunc: 'sum'
},
{
headerName: "Silver",
field: "silver",
width: 100,
aggFunc: 'sum'
},
{
headerName: "Bronze",
field: "bronze",
width: 100,
aggFunc: 'sum'
},
];
}
createRowData() {
return [
{country: "United States", name: "Bob", gold: 1, silver: 0, bronze: 0},
{country: "United States", name: "Jack", gold: 0, silver: 1, bronze: 1},
{country: "United States", name: "Sue", gold: 1, silver: 0, bronze: 1},
{country: "United Kingdom", name: "Mary", gold: 1, silver: 1, bronze: 0},
{country: "United Kingdom", name: "Tess", gold: 0, silver: 1, bronze: 1},
{country: "United Kingdom", name: "John", gold: 0, silver: 2, bronze: 1},
{country: "Jamaica", name: "Bob", gold: 1, silver: 1, bronze: 0},
{country: "Jamaica", name: "Jack", gold: 1, silver: 1, bronze: 0},
{country: "Jamaica", name: "Mary", gold: 1, silver: 1, bronze: 0},
{country: "South Africa", name: "Bob", gold: 1, silver: 0, bronze: 1},
{country: "South Africa", name: "Jack", gold: 1, silver: 0, bronze: 1},
{country: "South Africa", name: "Mary", gold: 1, silver: 0, bronze: 1},
{country: "South Africa", name: "John", gold: 1, silver: 0, bronze: 1},
{country: "New Zealand", name: "Bob", gold: 1, silver: 0, bronze: 0},
{country: "New Zealand", name: "Jack", gold: 0, silver: 1, bronze: 1},
{country: "New Zealand", name: "Sue", gold: 1, silver: 0, bronze: 1},
{country: "Australia", name: "Mary", gold: 1, silver: 1, bronze: 0},
{country: "Australia", name: "Tess", gold: 0, silver: 1, bronze: 1},
{country: "Australia", name: "John", gold: 0, silver: 2, bronze: 1},
{country: "Canada", name: "Bob", gold: 1, silver: 1, bronze: 0},
{country: "Canada", name: "Jack", gold: 1, silver: 1, bronze: 0},
{country: "Canada", name: "Mary", gold: 1, silver: 1, bronze: 0},
{country: "Switzerland", name: "Bob", gold: 1, silver: 0, bronze: 1},
{country: "Switzerland", name: "Jack", gold: 1, silver: 0, bronze: 1},
{country: "Switzerland", name: "Mary", gold: 1, silver: 0, bronze: 1},
{country: "Switzerland", name: "John", gold: 1, silver: 0, bronze: 1},
{country: "Spain", name: "Bob", gold: 1, silver: 0, bronze: 0},
{country: "Spain", name: "Jack", gold: 0, silver: 1, bronze: 1},
{country: "Spain", name: "Sue", gold: 1, silver: 0, bronze: 1},
{country: "Portugal", name: "Mary", gold: 1, silver: 1, bronze: 0},
{country: "Portugal", name: "Tess", gold: 0, silver: 1, bronze: 1},
{country: "Portugal", name: "John", gold: 0, silver: 2, bronze: 1},
{country: "Zimbabwe", name: "Bob", gold: 1, silver: 1, bronze: 0},
{country: "Zimbabwe", name: "Jack", gold: 1, silver: 1, bronze: 0},
{country: "Zimbabwe", name: "Mary", gold: 1, silver: 1, bronze: 0},
{country: "Brazil", name: "Bob", gold: 1, silver: 0, bronze: 1},
{country: "Brazil", name: "Jack", gold: 1, silver: 0, bronze: 1},
{country: "Brazil", name: "Mary", gold: 1, silver: 0, bronze: 1},
{country: "Brazil", name: "John", gold: 1, silver: 0, bronze: 1}
];
}
render() {
return (
<div style={{height: 400, width: 900}}
className="ag-fresh">
<h1>Group Row Renderer Example</h1>
<AgGridReact
// properties
columnDefs={this.state.columnDefs}
rowData={this.state.rowData}
groupUseEntireRow
groupRowInnerRendererFramework={MedalRenderer}
// events
onGridReady={this.onGridReady}>
</AgGridReact>
</div>
);
}
};

View File

@@ -0,0 +1,21 @@
import React, {Component} from "react";
export default class MedalRenderer extends Component {
constructor(props) {
super(props);
this.country = this.props.node.key;
this.gold = this.props.node.aggData.gold;
this.silver = this.props.node.aggData.silver;
this.bronze = this.props.node.aggData.bronze;
// override the containing div so that the +/- and label are inline
this.props.reactContainer.style.display = "inline-block";
}
render() {
return (
<span>{this.country} Gold: {this.gold}, Silver: {this.silver}, Bronze: {this.bronze}</span>
);
}
};

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

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

@@ -0,0 +1,132 @@
<!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
}
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/dist/styles/ag-grid.css";
import "ag-grid/dist/styles/theme-fresh.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/main";
// LicenseManager.setLicenseKey("<your license key>");
document.addEventListener('DOMContentLoaded', () => {
render(
<BrowserRouter>
<App/>
</BrowserRouter>,
document.querySelector('#app')
);
});

View File

@@ -0,0 +1,85 @@
import React, {Component} from "react";
import {AgGridReact} from "ag-grid-react";
import StyledRenderer from "./StyledRenderer";
export default class PinnedRowComponentExample extends Component {
constructor(props) {
super(props);
this.state = {
gridOptions: {},
rowData: this.createRowData(),
columnDefs: this.createColumnDefs(),
pinnedTopRowData: [{row: "Top Row", number: "Top Number"}],
pinnedBottomRowData: [{row: "Bottom Row", number: "Bottom Number"}]
};
this.onGridReady = this.onGridReady.bind(this);
}
onGridReady(params) {
this.gridApi = params.api;
this.columnApi = params.columnApi;
this.gridApi.sizeColumnsToFit();
}
createColumnDefs() {
return [
{
headerName: "Row",
field: "row",
width: 400,
pinnedRowCellRendererFramework: StyledRenderer,
pinnedRowCellRendererParams: {
style: {'fontWeight': 'bold'}
}
},
{
headerName: "Number",
field: "number",
width: 399,
pinnedRowCellRendererFramework: StyledRenderer,
pinnedRowCellRendererParams: {
style: {'fontStyle': 'italic'}
}
},
];
}
createRowData() {
let rowData = [];
for (let i = 0; i < 15; i++) {
rowData.push({
row: "Row " + i,
number: Math.round(Math.random() * 100)
});
}
return rowData;
}
render() {
return (
<div style={{height: 400, width: 900}}
className="ag-fresh">
<h1>Pinned Row Renderer Example</h1>
<AgGridReact
// properties
columnDefs={this.state.columnDefs}
rowData={this.state.rowData}
pinnedTopRowData={this.state.pinnedTopRowData}
pinnedBottomRowData={this.state.pinnedBottomRowData}
// events
onGridReady={this.onGridReady}>
</AgGridReact>
</div>
);
}
};

View File

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

View File

@@ -0,0 +1,28 @@
import React, {Component} from "react";
export default class ClickableRenderer extends Component {
constructor(props) {
super(props);
this.state = {
cell: {
row: this.props.value,
col: this.props.colDef.headerName
}
};
}
clicked = () => {
console.log("Child Cell Clicked: " + JSON.stringify(this.state.cell));
};
render() {
let buttonStyle = {
lineHeight: 0.5,
width: "98%"
};
return (
<button style={buttonStyle} onClick={this.clicked} className="btn btn-info">Click Me</button>
);
}
}

View File

@@ -0,0 +1,41 @@
import React, {Component} from "react";
export default class RatioRenderer extends Component {
constructor(props) {
super(props);
}
render() {
let agRatioStyle = {
display: "block",
overflow: "hidden",
border: "1px solid #ccc",
borderRadius: "6px",
background: "#fff",
height: 20
};
let svg = {
width: "100%",
height: "100%",
pointerEvents: "none"
};
let topBar = {
fill: "#ff9933"
};
let bottomBar = {
fill: "#6699ff"
};
return (
<ag-ratio style={agRatioStyle}>
<svg style={svg} viewBox="0 0 300 100" preserveAspectRatio="none">
<rect x="0" y="0" width={this.props.value.top * 300} height="50" rx="4" ry="4" style={topBar}/>
<rect x="0" y="50" width={this.props.value.bottom * 300} height="50" rx="4" ry="4" style={bottomBar}/>
</svg>
</ag-ratio>
);
}
}

View File

@@ -0,0 +1,97 @@
import React, {Component} from "react";
import {AgGridReact} from "ag-grid-react";
import RatioRenderer from "./RatioRenderer";
import ClickableRenderer from "./ClickableRenderer";
export default class RichComponentsExample extends Component {
constructor(props) {
super(props);
this.state = {
rowData: RichComponentsExample.createRowData(),
columnDefs: RichComponentsExample.createColumnDefs()
};
}
onGridReady = (params) => {
this.gridApi = params.api;
this.columnApi = params.columnApi;
this.gridApi.sizeColumnsToFit();
};
static createColumnDefs() {
return [
{
headerName: "Name",
field: "name",
width: 200
},
{
headerName: "Ratio Component",
field: "ratios",
cellRendererFramework: RatioRenderer,
width: 350
},
{
headerName: "Clickable Component",
field: "name",
cellRendererFramework: ClickableRenderer,
width: 250
}
]; }
static createRowData() {
return [
{name: 'Homer Simpson', ratios: {top: 0.25, bottom: 0.75}},
{name: 'Marge Simpson', ratios: {top: 0.67, bottom: 0.39}},
{name: 'Bart Simpson', ratios: {top: 0.82, bottom: 0.47}},
{name: 'Lisa Simpson', ratios: {top: 0.39, bottom: 1}},
{name: 'Barney', ratios: {top: 0.22, bottom: 0.78}},
{name: 'Sideshow Bob', ratios: {top: 0.13, bottom: 0.87}},
{name: 'Ned Flanders', ratios: {top: 0.49, bottom: 0.51}},
{name: 'Milhouse', ratios: {top: 0.69, bottom: 0.31}},
{name: 'Apu', ratios: {top: 0.89, bottom: 0.11}},
{name: 'Moe', ratios: {top: 0.64, bottom: 0.36}},
{name: 'Smithers', ratios: {top: 0.09, bottom: 0.91}},
{name: 'Edna Krabappel', ratios: {top: 0.39, bottom: 0.61}},
{name: 'Krusty', ratios: {top: 0.74, bottom: 0.26}}
];
}
render() {
return (
<div style={{height: 370, width: 900}} className="ag-fresh">
<h1>Dynamic React Components - Richer Example</h1>
<AgGridReact
// properties
columnDefs={this.state.columnDefs}
rowData={this.state.rowData}
gridOptions={this.state.gridOptions}
// events
onGridReady={this.onGridReady}>
</AgGridReact>
<div className="row">
<div className="col-sm-12">
<h5>This example demonstrates Dynamic React Components with ag-Grid. Functionally similar
to the <a href="/dynamic">Dynamic React Components Example</a> but with slightly richer components.</h5>
</div>
</div>
<div className="row">
<div className="col-sm-12">
<div className="card">
<div className="card-body">
<h4 className="card-title">React Functionality</h4>
<p className="card-text">Utilise React Components within ag-Grid</p>
<a target="_blank" href="https://www.ag-grid.com/best-react-data-grid/?framework=react" className="btn btn-primary">React with ag-Grid</a>
<a target="_blank" href="https://www.ag-grid.com/javascript-grid-cell-rendering-components/?framework=react" className="btn btn-primary">React Renderers</a>
</div>
</div>
</div>
</div>
</div>
);
}
};

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 onInput={this.onDateChanged.bind(this)} ref="dd" placeholder="dd" style={ddStyle}
value={this.state.textBoxes.dd} maxLength="2"/>/
<input onInput={this.onDateChanged.bind(this)} ref="mm" placeholder="mm" style={mmStyle}
value={this.state.textBoxes.mm} maxLength="2"/>/
<input onInput={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

@@ -1,35 +1,32 @@
import React from 'react';
import RefData from './RefData';
var PROFICIENCY_NAMES = ['No Filter', 'Above 40%', 'Above 60%', 'Above 80%'];
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() {
super();
constructor(props) {
super(props);
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);
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;
case PROFICIENCY_NAMES[1] :
return valueAsNumber >= 40;
case PROFICIENCY_NAMES[2] :
return valueAsNumber >= 60;
case PROFICIENCY_NAMES[3] :
return valueAsNumber >= 80;
default :
return true;
}
};
@@ -39,23 +36,28 @@ export default class ProficiencyFilter extends React.Component {
};
onButtonPressed(name) {
console.log(name);
this.setState({
selected: name
}, ()=> {
this.filterChangedCallback();
});
console.log(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() {
var rows = [];
PROFICIENCY_NAMES.forEach( (name)=> {
var selected = this.state.selected === name;
const rows = [];
PROFICIENCY_NAMES.forEach((name) => {
const 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)}/>
<input type="radio" checked={selected} name={Math.random()}
onChange={this.onButtonPressed.bind(this, name)}/>
{name}
</label>
</div>
@@ -64,7 +66,13 @@ export default class ProficiencyFilter extends React.Component {
return (
<div>
<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 Proficiency Filter</b>
</div>
{rows}

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,338 @@
import React, {Component} from "react";
import {AgGridColumn, AgGridReact} from "ag-grid-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";
// take this line out if you do not want to use ag-Grid-Enterprise
import "ag-grid-enterprise";
export default class RichGridDeclarativeExample extends Component {
constructor() {
super();
this.state = {
quickFilterText: null,
showToolPanel: false,
rowData: new RowDataFactory().createRowData(),
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"/>'
}
};
}
/* Grid Events we're listening to */
onGridReady = (params) => {
this.api = params.api;
this.columnApi = params.columnApi;
};
onCellClicked = (event) => {
console.log('onCellClicked: ' + event.data.name + ', col ' + event.colIndex);
};
onRowSelected = (event) => {
console.log('onRowSelected: ' + event.node.data.name);
};
/* Demo related methods */
onToggleToolPanel = (event) => {
this.setState({showToolPanel: event.target.checked});
};
deselectAll() {
this.api.deselectAll();
}
onQuickFilterText = (event) => {
this.setState({quickFilterText: event.target.value});
};
onRefreshData = () => {
this.setState({
rowData: new RowDataFactory().createRowData()
});
};
invokeSkillsFilterMethod = () => {
let skillsFilter = this.api.getFilterInstance('skills');
let componentInstance = skillsFilter.getFrameworkComponentInstance();
componentInstance.helloFromSkillsFilter();
};
dobFilter = () => {
let dateFilterComponent = this.api.getFilterInstance('dob');
dateFilterComponent.setFilterType('equals');
dateFilterComponent.setDateFrom('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();
}, 0)
};
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><span id="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"}}>
<label>
<input type="checkbox" onChange={this.onToggleToolPanel} style={{marginRight: 5}}/>
Show Tool Panel
</label>
</div>
<div style={{float: "left", marginLeft: 20}}>
<button onClick={this.onRefreshData} className="btn btn-primary">Refresh Data</button>
</div>
<div style={{float: "left", marginLeft: 20}}>
<input type="text" onChange={this.onQuickFilterText} placeholder="Type text to filter..."/>
</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={{height: 400, width: 900}} className="ag-fresh">
<AgGridReact
// listening for events
onGridReady={this.onGridReady}
onRowSelected={this.onRowSelected}
onCellClicked={this.onCellClicked}
// 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
rowData={this.state.rowData}
// no binding, just providing hard coded strings for the properties
// boolean properties will default to true if provided (ie enableColResize => enableColResize="true")
suppressRowClickSelection
rowSelection="multiple"
enableColResize
enableSorting
enableFilter
floatingFilter
groupHeaders
rowHeight="22"
// setting grid wide date component
dateComponentFramework={DateComponent}
// setting default column properties
defaultColDef={{
headerComponentFramework: SortableHeaderComponent,
headerComponentParams: {
menuIcon: 'fa-bars'
}
}}
>
<AgGridColumn headerName="#" width={30} checkboxSelection suppressSorting suppressMenu suppressFilter pinned></AgGridColumn>
<AgGridColumn headerName="Employee" headerGroupComponentFramework={HeaderGroupComponent}>
<AgGridColumn field="name" width={150} enableRowGroup enablePivot pinned editable cellEditorFramework={NameCellEditor}></AgGridColumn>
<AgGridColumn field="country" width={150} enableRowGroup enablePivot pinned editable cellRenderer={RichGridDeclarativeExample.countryCellRenderer} filterParams={{cellRenderer: RichGridDeclarativeExample.countryCellRenderer, cellHeight:20}}></AgGridColumn>
<AgGridColumn field="dob" width={145} headerName="DOB" filter="date" pinned columnGroupShow="open" cellRenderer={RichGridDeclarativeExample.dateCellRenderer}></AgGridColumn>
</AgGridColumn>
<AgGridColumn headerName="IT Skills">
<AgGridColumn field="skills" width={120} enableRowGroup enablePivot suppressSorting cellRendererFramework={SkillsCellRenderer} filterFramework={SkillsFilter}></AgGridColumn>
<AgGridColumn field="proficiency" width={135} enableValue cellRendererFramework={ProficiencyCellRenderer} filterFramework={ProficiencyFilter}></AgGridColumn>
</AgGridColumn>
<AgGridColumn headerName="Contact">
<AgGridColumn field="mobile" width={150} filter="text"></AgGridColumn>
<AgGridColumn field="landline" width={150} filter="text"></AgGridColumn>
<AgGridColumn field="address" width={500} filter="text"></AgGridColumn>
</AgGridColumn>
</AgGridReact>
</div>
<div>
<div className="row">
<div className="col-sm-12"><h1>Rich Grid with Declarative Markup Example</h1></div>
</div>
<div className="row">
<div className="col-sm-12">
<h5>This example demonstrates many features of ag-Grid, with Grid and Column Definition defined declaratively (i.e. with markup).</h5>
<p><span style={{fontWeight: 500}}>Select All/Clear Selection</span>: Select or Deselect
All
Rows</p>
<p><span style={{fontWeight: 500}}>Hide/Show Country Column</span>: Select or Deselect
All
Rows
(expand the Employee column to show the Country column first)</p>
<p><span style={{fontWeight: 500}}>Toggle The Tool Panel</span>: Let your users Pivot,
Group
and
Aggregate using the Tool Panel</p>
<p><span style={{fontWeight: 500}}>Refresh Data</span>: Dynamically Update Grid Data</p>
<p><span style={{fontWeight: 500}}>Quick Filter</span>: Perform Quick Grid Wide
Filtering
with
the Quick Filter</p>
<p><span style={{fontWeight: 500}}>DOB Filter</span>: Set the DOB filter to 01/01/2000
using
the
Filter API (expand the Employee column to show the DOB column)</p>
<p><span style={{fontWeight: 500}}>Custom Headers</span>: Sort, Filter and Render
Headers
using
Header Components</p>
</div>
</div>
<div className="row">
<div className="col-sm-4">
<div className="card">
<div className="card-body">
<h4 className="card-title">Grid & Column API</h4>
<p className="card-text">Utilise Grid Features Programmatically Using the APIs
Available</p>
<a target="_blank" href="https://www.ag-grid.com/javascript-grid-api/"
className="btn btn-primary">Grid API</a>
<a target="_blank" href="https://www.ag-grid.com//javascript-grid-column-api/"
className="btn btn-primary">Column API</a>
</div>
</div>
</div>
<div className="col-sm-4">
<div className="card">
<div className="card-body">
<h4 className="card-title">Header Components</h4>
<p className="card-text">Customer the Header with React Components</p>
<a target="_blank"
href="https://www.ag-grid.com//javascript-grid-header-rendering/#headerComponent"
className="btn btn-primary">Header Component</a>
<a target="_blank"
href="https://www.ag-grid.com//javascript-grid-header-rendering/#headerGroupComponent"
className="btn btn-primary">Header Group Component</a>
</div>
</div>
</div>
<div className="col-sm-4">
<div className="card">
<div className="card-body">
<h4 className="card-title">Filters</h4>
<p className="card-text">Filter with Quick Filters, Floating Filters, Built In
Filters
or Using the
Filter API</p>
<a target="_blank" href="https://www.ag-grid.com//javascript-grid-filter-quick/"
className="btn btn-primary">Quick
Filter</a>
<a target="_blank"
href="https://www.ag-grid.com//javascript-grid-floating-filter-component/"
className="btn btn-primary">Floating Filters</a>
<a target="_blank" href="https://www.ag-grid.com//javascript-grid-filtering/"
className="btn btn-primary">Built in
Filters</a>
<a target="_blank"
href="https://www.ag-grid.com//javascript-grid-filtering/#accessing-filter-component-instances"
className="btn btn-primary">Filter API</a>
</div>
</div>
</div>
</div>
<div className="row">
<div className="col-sm-4">
<div className="card">
<div className="card-body">
<h4 className="card-title">Tool Panel</h4>
<p className="card-text">Let your users Pivot, Group and Aggregate using the
Tool
Panel</p>
<a target="_blank" href="https://www.ag-grid.com//javascript-grid-tool-panel/"
className="btn btn-primary">Tool Panel</a>
</div>
</div>
</div>
<div className="col-sm-8">
<div className="card">
<div className="card-body">
<h4 className="card-title">The Rest</h4>
<p className="card-text">Pinned Columns, Checkbox Selection, Customer Renderers,
Data
Updates</p>
<a target="_blank"
href="https://www.ag-grid.com/best-react-data-grid/?framework=react"
className="btn btn-primary">React with ag-Grid</a>
<a target="_blank" href="https://www.ag-grid.com//javascript-grid-pinning/"
className="btn btn-primary">Pinned Column</a>
<a target="_blank"
href="https://www.ag-grid.com//javascript-grid-selection/#checkboxSelection"
className="btn btn-primary">Checkbox Selection</a>
<a target="_blank"
href="https://www.ag-grid.com//javascript-grid-cell-rendering/"
className="btn btn-primary">Cell
Renderers</a>
<a target="_blank" href="https://www.ag-grid.com/javascript-grid-data-update/"
className="btn btn-primary">Updating Data</a>
</div>
</div>
</div>
</div>
</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,43 @@ export default class SkillsFilter extends React.Component {
return passed;
};
getModel() {
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) => {
const skillsTemplates = [];
RefData.IT_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 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 +103,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 +120,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,124 @@
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';
export default class ColDefFactory {
createColDefs() {
return [
{
headerName: '#',
width: 30,
checkboxSelection: true,
suppressSorting: true,
suppressMenu: true,
suppressFilter: true,
pinned: true
},
{
headerName: 'Employee',
headerGroupComponentFramework: HeaderGroupComponent,
children: [
{
headerName: "Name",
field: "name",
enableRowGroup: true,
enablePivot: true,
width: 150,
pinned: true,
editable: true,
// use a React cellEditor
cellEditorFramework: NameCellEditor
},
{
headerName: "Country",
field: "country",
width: 150,
enableRowGroup: true,
enablePivot: true,
// an example of using a non-React cell renderer
cellRenderer: countryCellRenderer,
pinned: true,
filterParams: {
cellRenderer: countryCellRenderer,
cellHeight: 20
}
},
{
headerName: "DOB",
field: "dob",
width: 145,
enableRowGroup: true,
enablePivot: true,
filter: 'date',
pinned: true,
cellRenderer: function (params) {
return pad(params.value.getDate(), 2) + '/' +
pad(params.value.getMonth() + 1, 2) + '/' +
params.value.getFullYear();
},
columnGroupShow: 'open'
}
]
},
{
headerName: 'IT Skills',
children: [
{
headerName: "Skills",
width: 125,
suppressSorting: true,
field: 'skills',
enableRowGroup: true,
enablePivot: true,
// supply a React component
cellRendererFramework: SkillsCellRenderer,
// supply a React component
filterFramework: SkillsFilter
},
{
headerName: "Proficiency",
field: "proficiency",
width: 135,
enableValue: true,
// supply a React component
cellRendererFramework: ProficiencyCellRenderer,
// supply a React component
filterFramework: ProficiencyFilter
}
]
},
{
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'}
]
}
];
}
}
// this is a simple cell renderer, putting together static html, no
// need to use React for it.
function countryCellRenderer(params) {
if (params.value) {
const 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;
}
}
//Utility function used to pad the date formatting.
function pad(num, totalStringSize) {
let asString = num + "";
while (asString.length < totalStringSize) asString = "0" + asString;
return asString;
}

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 onInput={this.onDateChanged.bind(this)} ref="dd" placeholder="dd" style={ddStyle}
value={this.state.textBoxes.dd} maxLength="2"/>/
<input onInput={this.onDateChanged.bind(this)} ref="mm" placeholder="mm" style={mmStyle}
value={this.state.textBoxes.mm} maxLength="2"/>/
<input onInput={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}>
<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

@@ -0,0 +1,27 @@
.ag-cell {
padding-top: 2px !important;
padding-bottom: 2px !important;
}
label {
font-weight: normal !important;
}
.div-percent-bar {
display: inline-block;
height: 20px;
position: relative;
}
.div-percent-value {
position: absolute;
padding-left: 4px;
font-weight: bold;
font-size: 13px;
}
.div-outer-div {
display: inline-block;
height: 100%;
width: 100%;
}

View File

@@ -0,0 +1,316 @@
import React, {Component} from "react";
import {AgGridReact} from "ag-grid-react";
import RowDataFactory from "./RowDataFactory";
import ColDefFactory from "./ColDefFactory.jsx";
import DateComponent from "./DateComponent.jsx";
import SortableHeaderComponent from "./SortableHeaderComponent";
import "./RichGridExample.css";
// take this line out if you do not want to use ag-Grid-Enterprise
import "ag-grid-enterprise";
export default class RichGridExample extends Component {
constructor() {
super();
this.state = {
quickFilterText: null,
showToolPanel: false,
columnDefs: new ColDefFactory().createColDefs(),
rowData: new RowDataFactory().createRowData(),
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 = {
//We register the react date component that ag-grid will use to render
dateComponentFramework: DateComponent,
// this is how you listen for events using gridOptions
onModelUpdated: function () {
console.log('event onModelUpdated received');
},
defaultColDef: {
headerComponentFramework: SortableHeaderComponent,
headerComponentParams: {
menuIcon: 'fa-bars'
}
},
// this is a simple property
rowBuffer: 10, // no need to set this, the default is fine for almost all scenarios,
floatingFilter: true
};
}
/* Grid Events we're listening to */
onGridReady = (params) => {
this.api = params.api;
this.columnApi = params.columnApi;
};
onCellClicked = (event) => {
console.log('onCellClicked: ' + event.data.name + ', col ' + event.colIndex);
};
onRowSelected = (event) => {
console.log('onRowSelected: ' + event.node.data.name);
};
/* Demo related methods */
onToggleToolPanel = (event) => {
this.setState({showToolPanel: event.target.checked});
};
deselectAll() {
this.api.deselectAll();
}
onQuickFilterText = (event) => {
this.setState({quickFilterText: event.target.value});
};
onRefreshData = () => {
this.setState({
rowData: new RowDataFactory().createRowData()
});
};
invokeSkillsFilterMethod = () => {
let skillsFilter = this.api.getFilterInstance('skills');
let componentInstance = skillsFilter.getFrameworkComponentInstance();
componentInstance.helloFromSkillsFilter();
};
dobFilter = () => {
let dateFilterComponent = this.api.getFilterInstance('dob');
dateFilterComponent.setFilterType('equals');
dateFilterComponent.setDateFrom('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();
}, 0)
};
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><span id="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"}}>
<label>
<input type="checkbox" onChange={this.onToggleToolPanel} style={{marginRight: 5}}/>
Show Tool Panel
</label>
</div>
<div style={{float: "left", marginLeft: 20}}>
<button onClick={this.onRefreshData} className="btn btn-primary">Refresh Data</button>
</div>
<div style={{float: "left", marginLeft: 20}}>
<input type="text" onChange={this.onQuickFilterText} placeholder="Type text to filter..."/>
</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={{height: 400, width: 900}} className="ag-fresh">
<AgGridReact
ref="myGrid"
// gridOptions is optional - it's possible to provide
// all values as React props
gridOptions={this.gridOptions}
// listening for events
onGridReady={this.onGridReady}
onRowSelected={this.onRowSelected}
onCellClicked={this.onCellClicked}
// 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 hard coded strings for the properties
// boolean properties will default to true if provided (ie enableColResize => enableColResize="true")
suppressRowClickSelection
rowSelection="multiple"
enableColResize
enableSorting
enableFilter
groupHeaders
rowHeight="22"/>
</div>
<div>
<div className="row">
<div className="col-sm-12"><h1>Rich Grid Example</h1></div>
</div>
<div className="row">
<div className="col-sm-12">
<h5>This example demonstrates many features of ag-Grid.</h5>
<p><span style={{fontWeight: 500}}>Select All/Clear Selection</span>: Select or Deselect
All
Rows</p>
<p><span style={{fontWeight: 500}}>Hide/Show Country Column</span>: Select or Deselect
All
Rows
(expand the Employee column to show the Country column first)</p>
<p><span style={{fontWeight: 500}}>Toggle The Tool Panel</span>: Let your users Pivot,
Group
and
Aggregate using the Tool Panel</p>
<p><span style={{fontWeight: 500}}>Refresh Data</span>: Dynamically Update Grid Data</p>
<p><span style={{fontWeight: 500}}>Quick Filter</span>: Perform Quick Grid Wide
Filtering
with
the Quick Filter</p>
<p><span style={{fontWeight: 500}}>DOB Filter</span>: Set the DOB filter to 01/01/2000
using
the
Filter API (expand the Employee column to show the DOB column)</p>
<p><span style={{fontWeight: 500}}>Custom Headers</span>: Sort, Filter and Render
Headers
using
Header Components</p>
</div>
</div>
<div className="row">
<div className="col-sm-4">
<div className="card">
<div className="card-body">
<h4 className="card-title">Grid & Column API</h4>
<p className="card-text">Utilise Grid Features Programmatically Using the APIs
Available</p>
<a target="_blank" href="https://www.ag-grid.com/javascript-grid-api/"
className="btn btn-primary">Grid API</a>
<a target="_blank" href="https://www.ag-grid.com//javascript-grid-column-api/"
className="btn btn-primary">Column API</a>
</div>
</div>
</div>
<div className="col-sm-4">
<div className="card">
<div className="card-body">
<h4 className="card-title">Header Components</h4>
<p className="card-text">Customer the Header with React Components</p>
<a target="_blank"
href="https://www.ag-grid.com//javascript-grid-header-rendering/#headerComponent"
className="btn btn-primary">Header Component</a>
<a target="_blank"
href="https://www.ag-grid.com//javascript-grid-header-rendering/#headerGroupComponent"
className="btn btn-primary">Header Group Component</a>
</div>
</div>
</div>
<div className="col-sm-4">
<div className="card">
<div className="card-body">
<h4 className="card-title">Filters</h4>
<p className="card-text">Filter with Quick Filters, Floating Filters, Built In
Filters
or Using the
Filter API</p>
<a target="_blank" href="https://www.ag-grid.com//javascript-grid-filter-quick/"
className="btn btn-primary">Quick
Filter</a>
<a target="_blank"
href="https://www.ag-grid.com//javascript-grid-floating-filter-component/"
className="btn btn-primary">Floating Filters</a>
<a target="_blank" href="https://www.ag-grid.com//javascript-grid-filtering/"
className="btn btn-primary">Built in
Filters</a>
<a target="_blank"
href="https://www.ag-grid.com//javascript-grid-filtering/#accessing-filter-component-instances"
className="btn btn-primary">Filter API</a>
</div>
</div>
</div>
</div>
<div className="row">
<div className="col-sm-4">
<div className="card">
<div className="card-body">
<h4 className="card-title">Tool Panel</h4>
<p className="card-text">Let your users Pivot, Group and Aggregate using the
Tool
Panel</p>
<a target="_blank" href="https://www.ag-grid.com//javascript-grid-tool-panel/"
className="btn btn-primary">Tool Panel</a>
</div>
</div>
</div>
<div className="col-sm-8">
<div className="card">
<div className="card-body">
<h4 className="card-title">The Rest</h4>
<p className="card-text">Pinned Columns, Checkbox Selection, Customer Renderers,
Data
Updates</p>
<a target="_blank"
href="https://www.ag-grid.com/best-react-data-grid/?framework=react"
className="btn btn-primary">React with ag-Grid</a>
<a target="_blank" href="https://www.ag-grid.com//javascript-grid-pinning/"
className="btn btn-primary">Pinned Column</a>
<a target="_blank"
href="https://www.ag-grid.com//javascript-grid-selection/#checkboxSelection"
className="btn btn-primary">Checkbox Selection</a>
<a target="_blank"
href="https://www.ag-grid.com//javascript-grid-cell-rendering/"
className="btn btn-primary">Cell
Renderers</a>
<a target="_blank" href="https://www.ag-grid.com/javascript-grid-data-update/"
className="btn btn-primary">Updating Data</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
}

View File

@@ -0,0 +1,45 @@
import RefData from './RefData';
export default class RowDataFactory {
createRowData() {
const rowData = [];
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: {
android: Math.random() < 0.4,
html5: Math.random() < 0.4,
mac: Math.random() < 0.4,
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),
country: countryData.country,
continent: countryData.continent,
language: countryData.language,
mobile: this.createRandomPhoneNumber(),
landline: this.createRandomPhoneNumber()
});
}
return rowData;
}
createRandomPhoneNumber() {
let result = '+';
for (let i = 0; i < 12; i++) {
result += Math.round(Math.random() * 10);
if (i === 2 || i === 5 || i === 8) {
result += ' ';
}
}
return result;
}
}

View File

@@ -0,0 +1,27 @@
import React from 'react';
import * as PropTypes from 'prop-types';
import RefData from './RefData';
export default class SkillsCellRenderer extends React.Component {
render() {
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}/>);
}
});
return <span>{skills}</span>;
}
}
// 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.
SkillsCellRenderer.propTypes = {
params: PropTypes.object
};

View File

@@ -0,0 +1,126 @@
import React from 'react';
import RefData from './RefData';
// the skills filter component. this can be laid out much better in a 'React'
// way. there are design patterns you can apply to layout out your React classes.
// however, i'm not worried, as the intention here is to show you ag-Grid
// working with React, and that's all. i'm not looking for any awards for my
// React design skills.
export default class SkillsFilter extends React.Component {
constructor(props) {
super(props);
this.state = {
android: false,
css: false,
html5: false,
mac: false,
windows: false
};
}
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) {
const rowSkills = params.data.skills;
let passed = true;
RefData.IT_SKILLS.forEach((skill) => {
if (this.state[skill]) {
if (!rowSkills[skill]) {
passed = false;
}
}
});
return passed;
};
getModel() {
return ''
}
// called by agGrid
isFilterActive() {
const somethingSelected = this.state.android || this.state.css ||
this.state.html5 || this.state.mac || this.state.windows;
return somethingSelected;
};
onSkillChanged(skill, event) {
const newValue = event.target.checked;
const newModel = {};
newModel[skill] = newValue;
// 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() {
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}/>
</div>
</span>
</label>
);
skillsTemplates.push(template);
});
return (
<div style={{width: 380}}>
<div style={{
textAlign: 'center',
background: 'lightgray',
width: '100%',
display: 'block',
borderBottom: '1px solid grey'
}}>
<b>Custom Skills Filter</b>
</div>
{skillsTemplates}
</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.
//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,63 @@
import React, {Component} from "react";
import {AgGridReact} from "ag-grid-react";
import {connect} from "react-redux";
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-fresh">
<AgGridReact
// properties
columnDefs={this.state.columnDefs}
rowData={this.props.rowData}
// 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,86 @@
import React, {Component} from "react";
import {connect} from "react-redux";
// take this line out if you do not want to use ag-Grid-Enterprise
import "ag-grid-enterprise";
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)}>Set Currency to GBP</button>
<button onClick={this.setCurrency.bind(this, '$', 1.29)}>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,37 @@
import React, {Component} from "react";
import {connect} from "react-redux";
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 (
<span>{this.props.currencySymbol}{this.state.convertedValue}</span>
);
}
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
}
}
)(PriceRenderer);

View File

@@ -0,0 +1,35 @@
import React, {Component} from "react";
import {Provider} from "react-redux";
import {createStore} from "redux";
// take this line out if you do not want to use ag-Grid-Enterprise
import "ag-grid-enterprise";
import HeaderComponent from "./HeaderComponent";
import GridComponent from "./GridComponent";
import gridData from "./gridDataReducer";
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 />
<GridComponent />
</div>
</Provider>
)
}
};

View File

@@ -0,0 +1,13 @@
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;
}
};

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