diff --git a/README.md b/README.md
index 0221c5d..6c4fdb7 100644
--- a/README.md
+++ b/README.md
@@ -1,16 +1,283 @@
# @flipbyte/redux-datatable
-[![Travis][build-badge]][build]
+[Developed by Flipbyte](https://www.flipbyte.com/)
+
[![npm package][npm-badge]][npm]
-[![Coveralls][coveralls-badge]][coveralls]
-Describe @flipbyte/redux-datatable here.
+Datatable built using React and Redux to fetch JSON data asynchronously using REST API.
+
+- Filterable columns by date ranges, numeric ranges and text.
+- Pagination
+- Sortable columns
+- Configurable column widths
+- Built in windowing to handle large dataset with thousands of rows
+- Customizable limiter options
+- Customizable toolbar with the ability to add custom renderers
+- Completely configurable headers, filters, toolbar and pagination with options to enable/disable them individual
+- Custom row level actions
+- Thunks to handle custom mass or row actions externally.
+- Compatible with normalizr to handle externally managed states
+- Easily stylable with styled-components.
+- Show or hide columns dynamically using the Columns item in the toolbar.
+
+## Installation
+
+Run the following command to install using npm
+
+```sh
+npm i @flipbyte/redux-datatable
+```
+
+## Usage
+
+#### Add the table reducer and epics to your store.
+
+```javascript
+// Get the table reducer and epics as follows
+import { reducer, epics } from '@flipbyte/redux-datatable';
+
+// Add the above reducer and epics to your store.
+```
+
+#### Preparing your table config object
+
+```javascript
+{
+ name: 'your_table_name', // this is the key used to set your table data inside the table reducer
+ height: 500,
+ rowHeight: 50,
+ pagination: {
+ items: [{
+ type: 'limiter',
+ visible: true,
+ position: 10,
+ options: [10, 20, 50, 200, 2000],
+ default: 200,
+ style: {
+ right: false,
+ }
+ }, {
+ type: 'pages',
+ visible: true,
+ position: 20,
+ style: {
+ right: true,
+ }
+ }, {
+ type: 'resultCount',
+ visible: true,
+ position: 30,
+ style: {
+ width: '350px',
+ textAlign: 'center',
+ }
+ }]
+ },
+ routes: { // You can add other routes and handle them using custom actions.
+ get: { // The route used to fetch data and it's params
+ route: '/{your_route}',
+ sort: 'id',
+ dir: 'asc',
+ resultPath: {
+ data: 'data'
+ }
+ },
+ ...
+ },
+ toolbar: [ // Each toolbar array of objects below is a separate row in the toolbar section. You can add your own renderers and toolbar items or use some of the in-built ones.
+ [{
+ type: 'reset-filters',
+ label: 'Reset Filters',
+ visible: true,
+ state: false,
+ }, {
+ type: 'columns',
+ label: 'Columns',
+ visible: true,
+ state: false,
+ style: {
+ right: true
+ }
+ },
+ ...
+ ]
+ ...
+ ],
+ columns: [{
+ name: 'ids',
+ label: '',
+ sortable: false,
+ type: 'selection',
+ indexField: '@pageId',
+ width: 50,
+ extraData: 'selection'
+ }, {
+ label: 'ID',
+ type: 'number',
+ name: 'pageId',
+ sortable: true,
+ width: 150,
+ filterable: true,
+ sortable: true,
+ }, {
+ label: 'Avatar',
+ type: 'image',
+ name: 'avatar',
+ sortable: false,
+ textAlign: 'center',
+ width: 200,
+ filterable: false,
+ imgHeight: 50
+ }, {
+ label: 'First Name',
+ type: 'string',
+ name: 'first_name',
+ sortable: true,
+ textAlign: 'text-left',
+ width: 200,
+ filterable: true,
+ }, {
+ label: 'Actions',
+ type: 'actions',
+ name: 'actions',
+ width: 100,
+ items: [{
+ type: 'action',
+ name: 'edit',
+ label: 'Edit',
+ btnClass: 'btn btn-secondary',
+ icon: 'edit',
+ params: {
+ id: '@id',
+ },
+ thunk: ( payload ) => ( dispatch, getState ) => {
+ console.log('edit', payload, getState());
+ }
+ }, {
+ type: 'action',
+ name: 'delete',
+ label: 'Delete',
+ icon: 'trash-alt',
+ params: {
+ id: '@id'
+ },
+ thunk: ( payload ) => ( dispatch, getState ) => {
+ confirm("Are your sure you want to delete this page?")
+ ? console.log('delete', getState())
+ : console.log(false);
+ }
+ },
+ ...
+ ]
+ },
+ ...
+ ]
+}
+```
+
+#### Render table component
+```javascript
+import ReduxDatatable from '@flipbyte/redux-datatable';
+
+const YourComponent = () =>
+
+```
+
+### Table config props
+
+| Key | Type | Required | Default | Description |
+| ------------ | ------------ | ------------ | ------------ | ------------ |
+| name | string | true | - | A unique key where the data for the table is saved in the table state object |
+| height | integer | true | - | The maximum height of the table |
+| rowHeight | integer | true | - | The maximum height of each table body row |
+| filterable | boolean | false | true | Whether to show/hide filters row |
+| headers | boolean | false | true | Whether to show/hide headers row |
+| pagination | object | false | {} | Pagination bar configuration (Check below) |
+| routes | object | true | - | Routes definition to fetch data and other custom routes config for custom handling (Check below) |
+| toolbar | array | false | [] | Toolbar definition (Check below) |
+| columns | array | true | - | Columns to display |
+
+#### Pagination object
+
+| Key | Type | Required | Default | Description |
+| ------------ | ------------ | ------------ | ------------ | ------------ |
+| items | array | false | [] | Array of item objects available for display in the pagination bar. Check below for items available |
+| visible | boolean/object | false | true | Whether the pagination is visible or not |
+
+##### Pagination items array:
+
+| Key | Type | Required | Default | Description |
+| ------------ | ------------ | ------------ | ------------ | ------------ |
+| type | string | true | - | One of the following: limiter, pages, resultCount |
+| visible | boolean | false | true | Whether the item is visible |
+| style | object | false | {} | Stylable properties |
+| **Limiter specific options** | | | | |
+| options | array | true | - | Array of integers with limiter options |
+| default | array | true | - | One of the values in the limiter options key |
+| **- style** | | | | |
+| right | boolean | false | false | Align the item to the right |
+| width | integer | false | 200 | Width of the item |
+| textAlign | string | false | left | Align the text of the item to the right, left or center|
+| fontSize | string | false | 14 | The font size of the text in the item |
+
+#### Routes object
+
+| Key | Type | Required | Default | Description |
+| ------------ | ------------ | ------------ | ------------ | ------------ |
+| get | object | true | - | The configuration for fetching data |
+| **- get** | | | | |
+| route | string | true | - | Your data fetching route |
+| sort | string | true | - | Your key to sort with |
+| dir | string | true | - | Sort by 'asc' or 'desc' order |
+| resultPath | object | true | - | The keys object to your data. Required { data: '{your data path in json response. Ex: result.data}'} |
+
+#### Toolbar
+
+Toolbar config is an array of array of object where objects are the toolbar items. Each inner array represents a different row.
+
+| Key | Type | Required | Default | Description |
+| ------------ | ------------ | ------------ | ------------ | ------------ |
+| type | string | false | actions | Available values reset-filters and columns |
+| label | string | true | - | Label for the toolbar item |
+| visible | boolean | false | true | Whether the item is visible |
+| state | boolean | false | false | Whether to pass the state object as item prop |
+| style | object | false | {} | Define toolbar item styles |
+| **For type: actions** | | | | |
+| options | array | true | - | Array of option objects |
+| **-- options** | | | | |
+| type | string | true | action | Available option: action |
+| name | string | true | - | Unique name for the action |
+| label | string | true | - | Label for the action |
+| thunk | function | true | - | An action creator which is dispatched on action click. Check demo schema. |
+| **- styles** | | | | |
+| right | boolean | false | false | Align the item to the right |
+| width | integer | false | 200 | Width of the item |
+| fontSize | string | false | 14 | The font size of the text in the item |
+
+#### Columns object
-[build-badge]: https://img.shields.io/travis/user/repo/master.png?style=flat-square
-[build]: https://travis-ci.org/user/repo
+| Key | Type | Required | Default | Description |
+| ------------ | ------------ | ------------ | ------------ | ------------ |
+| name | string | true | - | Unique name for the column |
+| label | string | true | - | Label for the column |
+| sortable | boolean | false | true | Whether the column is sortable |
+| filterable | boolean | false | true | Whether the column is filterable |
+| type | string | true | string | Available types: selection, number, date, string, image, actions |
+| width | integer | true | - | Width of the column |
+| extraData | string/array | fasle | - | properties from the state to pass as in the extra object |
+| textAlign | string | false | left | Text alignment in the column |
+| **type: actions** | | | | |
+| items | array | true | - | array of item configuration object |
+| **- item configuration object ** | | | | |
+| name | string | true | - | Unique name for the action |
+| label | string | true | - | Label for the action |
+| thunk | function | true | - | An action creator which is dispatched on action click. Check demo schema. |
-[npm-badge]: https://img.shields.io/npm/v/npm-package.png?style=flat-square
-[npm]: https://www.npmjs.org/package/npm-package
+## License
+The MIT License (MIT)
-[coveralls-badge]: https://img.shields.io/coveralls/user/repo/master.png?style=flat-square
-[coveralls]: https://coveralls.io/github/user/repo
+[npm-badge]: https://img.shields.io/npm/v/@flipbyte/redux-datatable.svg
+[npm]: https://www.npmjs.com/package/@flipbyte/redux-datatable
diff --git a/demo/src/schema/basic.js b/demo/src/schema/basic.js
index 11d2ee8..d6dedb7 100644
--- a/demo/src/schema/basic.js
+++ b/demo/src/schema/basic.js
@@ -2,15 +2,16 @@ export default {
name: 'posts',
height: 400,
rowHeight: 50,
- filterable: false,
+ filterable: true,
+ headers: true,
pagination: {
// visible: true, // or an object { top: true, bottom: false } default visible
items: [{
type: 'limiter',
visible: true,
position: 10,
- options: [10, 20, 50],
- default: 10,
+ options: [10, 20, 50, 200, 2000],
+ default: 200,
style: {
right: false,
}
@@ -26,19 +27,18 @@ export default {
visible: true,
position: 30,
style: {
- width: '400px',
+ width: '350px',
textAlign: 'center',
}
}]
},
routes: {
get: {
- route: '/users',
+ route: '/page',
sort: 'id',
dir: 'asc',
resultPath: {
- data: 'data',
- total: 'total'
+ data: 'data'
}
},
delete: {
@@ -51,14 +51,15 @@ export default {
id: 'dropdown',
options: [{
type: 'action',
- onClick: (params, action) => (
- confirm("Are your sure you want to delete these item(s)?")
- ? dispatch(action('DELETE_DATA')(params))
- : false
- ),
name: 'delete',
label: 'Delete',
- indexField: '@id'
+ indexField: '@id',
+ thunk: ( payload ) => ( dispatch, getState ) => {
+ confirm("Are your sure you want to delete the selected items?")
+ ? console.log('delete items', getState())
+ : console.log(false);
+
+ }
}, {
type: 'action',
name: 'edit',
@@ -176,18 +177,18 @@ export default {
label: '',
sortable: false,
type: 'selection',
- indexField: '@id',
+ indexField: '@pageId',
width: 50,
- selection: true,
+ extraData: 'selection'
}, {
label: 'ID',
type: 'number',
- name: 'id',
+ name: 'pageId',
sortable: true,
width: 150,
filterable: true,
sortable: true,
- }, {
+ }, /*{
label: 'Avatar',
type: 'image',
name: 'avatar',
@@ -212,34 +213,45 @@ export default {
textAlign: 'text-left',
width: 200,
filterable: true,
+ },*/{
+ label: 'Created at',
+ type: 'date',
+ name: 'createdAt',
+ sortable: true,
+ textAlign: 'left',
+ width: 200,
+ filterable: true,
}, {
label: 'Actions',
type: 'actions',
name: 'actions',
width: 100,
- children: {
- edit: {
- type: 'action',
- name: 'route',
- action: 'EDIT_PAGE',
- label: 'Edit',
- btnClass: 'btn btn-secondary',
- icon: 'edit',
- params: {
- id: '@id',
- }
+ items: [{
+ type: 'action',
+ name: 'edit',
+ label: 'Edit',
+ btnClass: 'btn btn-secondary',
+ icon: 'edit',
+ params: {
+ id: '@id',
},
- delete: {
- type: 'action',
- name: 'delete',
- label: 'Delete',
- action: 'DATA_DELETE',
- btnClass: 'btn btn-danger',
- icon: 'trash-alt',
- params: {
- id: '@id'
- }
+ thunk: ( payload ) => ( dispatch, getState ) => {
+ console.log('edit', payload, getState());
}
- }
+ }, {
+ type: 'action',
+ name: 'delete',
+ label: 'Delete',
+ icon: 'trash-alt',
+ params: {
+ id: '@id'
+ },
+ thunk: ( payload ) => ( dispatch, getState ) => {
+ confirm("Are your sure you want to delete this page?")
+ ? console.log('delete', getState())
+ : console.log(false);
+
+ }
+ }]
}]
}
diff --git a/demo/src/store.js b/demo/src/store.js
index 1af01ee..a1891b8 100644
--- a/demo/src/store.js
+++ b/demo/src/store.js
@@ -6,6 +6,7 @@ import { api, request } from './request-handlers';
import { createEpicMiddleware, combineEpics } from 'redux-observable';
import { combineReducers, createStore, applyMiddleware, compose } from 'redux';
import { createLogger } from 'redux-logger';
+import thunk from 'redux-thunk';
const requestHandlers = { api, request };
@@ -35,7 +36,7 @@ const prepareEpics = ( epics ) => {
}
const configureStore = ( config ) => {
- let middlewares = [];
+ let middlewares = [thunk];
const logger = createLogger({ diff: true, duration: true, collapsed: true });
middlewares.push(logger);
diff --git a/src/Datatable/Pagination.js b/src/Datatable/Pagination.js
index 7aeffe1..ab9fbd5 100644
--- a/src/Datatable/Pagination.js
+++ b/src/Datatable/Pagination.js
@@ -23,7 +23,7 @@ const Pagination = ({
position,
margin,
componentConfig: {
- items,
+ items = [],
visible = true
}
}) => {
diff --git a/src/Datatable/Renderer/Body/Actions.js b/src/Datatable/Renderer/Body/Actions.js
index 197e178..2b8873c 100644
--- a/src/Datatable/Renderer/Body/Actions.js
+++ b/src/Datatable/Renderer/Body/Actions.js
@@ -6,40 +6,23 @@ import { withTableConfig } from '../../../TableProvider';
import { paramsResolver, prepareActionPayload } from '../../../utils'
import { Button } from 'styled-button-component';
-class Actions extends Component {
- constructor( props ) {
- super(props);
-
- this.handleAction = this.handleAction.bind(this);
- }
-
- handleAction( action ) {
- const { data, actions } = this.props;
- const params = paramsResolver(action.params, data);
- actions[action.name](params.get(), action.action);
- }
-
- render() {
- const {
- colConfig: { items },
- extra,
- thunk
- } = this.props;
- return (
-
- { items.map((item, index) =>
-
- )}
-
- )
- }
-}
+const Actions = ({
+ extra,
+ thunk,
+ colConfig: { items }
+}) => (
+
+ { items.map(({ thunk: cb, ...item }, index) =>
+
+ )}
+
+);
export default Actions;
diff --git a/src/Datatable/Table.js b/src/Datatable/Table.js
index 690a138..908b7f6 100644
--- a/src/Datatable/Table.js
+++ b/src/Datatable/Table.js
@@ -126,6 +126,8 @@ class Table extends Component {
action,
thunk,
data,
+ filterable,
+ headers,
config: { columns, height },
} = this.props;
const { items = [], query = {} } = data;
@@ -137,7 +139,7 @@ class Table extends Component {
- { columns.map((column, index) =>
+ { headers !== false && columns.map((column, index) =>
- { columns.map((column, index) =>
+ { filterable !== false && columns.map((column, index) =>
renderers[type] || renderers['default']
export const toolbarItem = ( render, item, index ) => {
- const { style, type } = item;
+ const { style, type, visible } = item;
return (
- { render(getRenderer(item.type), item, index) }
+ { visible !== false && render(getRenderer(item.type), item, index) }
);
}
diff --git a/src/Datatable/Toolbar/MassActions.js b/src/Datatable/Toolbar/MassActions.js
index 47bef43..9120d7d 100644
--- a/src/Datatable/Toolbar/MassActions.js
+++ b/src/Datatable/Toolbar/MassActions.js
@@ -56,42 +56,11 @@ class MassActions extends Component {
}
}
- handleAction( action, event ) {
- const {
- data: { selection },
- massActions
- } = this.props;
-
- let dataKey = getConfigParam(action.indexField);
- let selectedItems = getSelectedKeys(selection, dataKey);
-
- if( !selectedItems || !selectedItems[dataKey] || selectedItems[dataKey].length == 0 ) {
- alert("No item(s) selected");
- return false;
- }
-
- switch( action.type ) {
- // case 'route':
- // context.router.history.push({
- // pathname: action.route,
- // search: '?' + selectedItems.toString()
- // });
- // break;
-
- case 'action':
- massActions[action.name](selectedItems.get());
- break;
-
- default:
- break;
- }
- }
-
render() {
const { itemConfig, thunk } = this.props;
const { label, options } = itemConfig;
const { open } = this.state;
- console.log('thunk', thunk);
+
return (
{ options.map(({ thunk: cb, ...option }, index) =>
-
+
{ option.label }
)}
diff --git a/src/createTable.js b/src/createTable.js
index 5f46e74..ef3680c 100755
--- a/src/createTable.js
+++ b/src/createTable.js
@@ -60,7 +60,7 @@ class ReduxDatatable extends Component {
render() {
const { name, config, tableData, reducerName, action, thunk } = this.props;
const { columns: allColumns, ...otherConfig } = config;
- const { toolbar, pagination } = config;
+ const { toolbar = [], pagination = {}, filterable, headers } = config;
const tableConfig = {
...otherConfig,
allColumns,
@@ -84,7 +84,14 @@ class ReduxDatatable extends Component {
-
+
} />
}
/>
-
+