diff --git a/README.md b/README.md
index dbbd7c9..b09f85a 100644
--- a/README.md
+++ b/README.md
@@ -16,12 +16,11 @@ using REST API.
- 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
+- Easily configurable layout
- 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.
+- Easily styleable with styled-components and/or external css.
- Show or hide columns dynamically using the Columns item in the
toolbar.
@@ -49,146 +48,250 @@ import { reducer, epics } from '@flipbyte/redux-datatable';
```javascript
{
name: 'your_table_name', // this is the key used to set your table data inside the table reducer
- height: 500,
+ height: 400,
rowHeight: 50,
- pagination: {
- // visible: true, // or an object { top: true, bottom: false } default visible
- items: {
- limiter: {
- type: 'limiter',
- visible: true,
- position: 10,
- options: [10, 20, 50, 200, 2000],
- default: 200,
- },
- pages: {
- type: 'pages',
- visible: true,
- position: 20,
- right: true,
- },
- resultCount: {
- type: 'resultCount',
- visible: true,
- position: 30,
- right: true,
- },
- }
- },
+ editing: false,
+ primaryKey: 'id',
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'
- }
- },
- ...
+ 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.
- [{
+ layout: [
+ ['Editable'],
+ ['MassActions', 'SimpleButton', 'ResetFilters', 'Spacer', 'Print', 'Columns'],
+ ['Limiter', 'Spacer', 'ResultCount', 'Spacer', 'Pages'],
+ [{ id: 'Table', layout: [
+ ['Header'],
+ ['Filters'],
+ ['Body'],
+ ['Header']
+ ]}],
+ ['Limiter', 'Spacer', 'ResultCount', 'Spacer', 'Pages'],
+ ],
+ components: {
+ Loader: {
+ styles: {
+ mask: { ... },
+ spinner: { ... }
+ }
+ },
+ ResultCount: {
+ styles: { ... }
+ },
+ Pages: {
+ styles: {
+ first: { ... },
+ last: { ... },
+ previous: { ... },
+ next: { ... },
+ pageNumber: { ... },
+ }
+ },
+ Editable: {
+ type: 'editable',
+ labels: {
+ show: 'Make editable',
+ hide: 'Hide editable',
+ save: 'Save',
+ },
+ save: ( config ) => ( dispatch, getState ) => {
+ const tableState = getState()[config.reducerName][config.name];
+ console.log('toolbar save click with modified data', config, tableState.modified);
+ config.action(MODIFY_DATA)({ clear: true });
+ // Dispatch MODIFY_DATA action with clear: true, to reset the modified data
+ // Dispatch REQUEST_DATA action "config.action(REQUEST_DATA)" to refresh data.
+ },
+ styles: {
+ show: { ... },
+ hide: { ... },
+ save: { ... }
+ }
+ // renderer: ( props ) => { ... }
+ },
+ MassActions: {
+ name: 'actions',
+ label: 'Actions',
+ id: 'dropdown',
+ styles: {
+ button: { ... },
+ dropdownMenu: { ... },
+ dropdownItem: { ... }
+ }
+ options: [{
+ type: 'action',
+ name: 'delete',
+ label: 'Delete',
+ styles: { ... },
+ thunk: ( config ) => ( dispatch, getState ) => {
+ // Get current table state.
+ const tableState = getState()[config.reducerName][config.name];
+ console.log(config, tableState);
+ console.log(getItemIds(tableState.selection, tableState.items, config.primaryKey/*, config.entity.schema*/))
+ confirm('Are your sure you want to delete the selected items?')
+ ? console.log('delete items', config, getState(), tableState)
+ : console.log(false);
+
+ // Filter your selected item ids here for deletion
+ // You can find the selection data in the selection key of the tableState.
+ // When all:true, exclude the ids in the selected object with value false and vice versa.
+ }
+ }, {
+ type: 'action',
+ name: 'edit',
+ label: 'Edit this field',
+ }, ...]
+ },
+ SimpleButton: {
+ type: 'button',
+ label: 'Simple Button',
+ state: false,
+ thunk: ( config ) => ( dispatch, getState ) => { ... },
+ styles: { ... }
+ },
+ ResetFilters: {
type: 'reset-filters',
label: 'Reset Filters',
- visible: true,
state: false,
- }, {
+ styles: { ... }
+ },
+ Print: {
+ type: 'print',
+ label: 'Print Table',
+ state: false,
+ styles: { ... }
+ },
+ Columns: {
+ name: 'columns',
type: 'columns',
label: 'Columns',
visible: true,
state: false,
+ styles: {
+ button: { ... },
+ dropdownMenu: { ... },
+ dropdownItem: { ... }
+ }
},
- ...
- ]
- ...
- ],
- 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: "Status",
- type: "options",
- name: "entityData.data.status",
- sortable: true,
- filterable: true,
- textAlign: "center",
- options: {
- "published": {
- label: "Published"
+ Limiter: {
+ type: 'limiter',
+ options: [10, 20, 50, 200, 2000, 0],
+ default: 200,
+ styles: { ... }
+ },
+ Table: {
+ styles: {
+ table: { ... },
+ thead: { ... },
+ tbody: { ... },
+ filters: { ... },
+ tr: {
+ header: { ... },
+ filters: { ... },
+ body: { ... }
+ },
+ th: { ... },
+ td: {
+ filters: { ... },
+ body: { ... }
+ }
},
- "draft": {
- label: "Draft"
+ columns: [{
+ name: 'ids',
+ label: '',
+ sortable: false,
+ type: 'selection',
+ width: 50,
+ }, {
+ label: 'ID',
+ type: 'number',
+ name: 'id',
+ sortable: true,
+ width: 150,
+ filterable: true,
+ sortable: true,
+ }, {
+ label: "Status",
+ type: "options",
+ name: "status",
+ sortable: true,
+ filterable: true,
+ textAlign: "center",
+ options: {
+ "published": {
+ label: "Published"
+ },
+ "draft": {
+ label: "Draft"
+ },
+ "archived": {
+ label: "Archived"
+ },
+ ...
+ }
+ }, {
+ 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());
+ },
+ style: { ... }
+ }, {
+ 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);
+ },
+ style: { ... }
+ },
+ ...
+ ]
},
- "archived": {
- label: "Archived"
- }
+ ...
+ ]
}
- }, {
- 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);
- }
- },
- ...
- ]
- },
- ...
- ]
+ }
}
```
@@ -211,33 +314,11 @@ const YourComponent = () =>
| 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 |
-| styles | object | false | {} | Custom styles for your table |
-| editable | boolean | false | {} | Set whether the table is editable |
-| editing | boolean | false | {} | Set the default state of the table to be in editing mode |
-| primaryKey | string | true | {} | Set the primary key column of the table for actions like editing. |
-
-#### Pagination object
-
-| Key | Type | Required | Default | Description |
-| ------- | -------------- | -------- | ------- | ---------------------------------------------------------------------------------- |
-| items | object | false | {} | Items 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 object
-
-| Key | Type | Required | Default | Description |
-| ---------------------------- | ------- | -------- | ------- | ------------------------------------------------- |
-| type | string | true | - | One of the following: limiter, pages, resultCount |
-| visible | boolean | false | true | Whether the item is visible |
-| **Limiter specific options** | | | | |
-| options | array | true | - | Array of integers with limiter options |
-| default | array | true | - | One of the values in the limiter options key |
+| components | object | true | - | All the components required for your table |
+| layout | array | true | - | The layout of your table |
+| editing | boolean | false | { ... } | Set the default state of the table to be in editing mode |
+| primaryKey | string | true | { ... } | Set the primary key column of the table for actions like editing. |
#### Routes object
@@ -250,30 +331,169 @@ const YourComponent = () =>
| 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
+#### Components object
+
+Components can be defined within this object as key value pairs, where `key` is the id of the component and needs to be unique and `value` is a configuration object for the specific component.
+All available components are listed below with their configuration. Custom components can be added and existing components can be overridden by using the key `renderer` in the configuration object of the component.
+Please check the example table config object above.
+
+#### Layout array
+
+An array of arrays where each inner array represents a row in the layout, within which components can be specified, which will be displayed in the frontend.
+Please check the example table config object above.
+
+#### Available Components
+
+**_Common Properties_**
+
+| Key | Type | Required | Default | Description |
+| -------- | -------- | -------- | ------- | ------------------------------ |
+| styles | object | false | {} | styled-component styles object |
+| renderer | function | false | - | returns a react component |
+| type | string | true | - | the type of the object |
+
+##### Loader
+
+Note: This component cannot be added to the layout and does not have any other properties except styles.
+
+**_Styles object properties_**
-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 |
+| ------- | ------ | -------- | ------- | ------------------------------ |
+| mask | object | false | {} | styled-component styles object |
+| spinner | object | false | {} | styled-component styles object |
-| Key | Type | Required | Default | Description |
-| ---------------------- | -------- | -------- | ------------------------------------------------------------- | ------------------------------------------------------------------------- |
-| type | string | false | actions | Available values resetFilters, print, editable 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 |
-| **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. |
-| **For type: editable** | | | | |
-| labels | object | false | { show: 'Make editable', hide: 'Hide editable', save: 'Save'} | Labels for each of the buttons enabled when the table is editable |
+##### ResultCount
-Note: action of type "editable" is required when you set the table to be editable
+No unique properties
-#### Columns object
+##### Pages
+
+**_Styles object properties_**
+
+| Key | Type | Required | Default | Description |
+| ---------- | ------ | -------- | ------- | ------------------------------ |
+| first | object | false | {} | styled-component styles object |
+| last | object | false | {} | styled-component styles object |
+| previous | object | false | {} | styled-component styles object |
+| next | object | false | {} | styled-component styles object |
+| pageNumber | object | false | {} | styled-component styles object |
+
+##### Editable (type: editable)
+
+Toggles the table between editable and non-editable and shows a save button when the content of the table is modified
+
+**_Properties_**
+
+| Key | Type | Required | Default | Description |
+| ------ | -------- | -------- | ----------- | ----------------------------------------------- |
+| labels | object | false | check below | check below |
+| save | function | false | - | ( config ) => ( dispatch, getState ) => { ... } |
+
+**_Labels object properties_**
+
+| Key | Type | Required | Default | Description |
+| ---- | ------ | -------- | ------------- | ----------------------------------------------- |
+| show | string | false | Make editable | Label for the button to show editable table |
+| hide | string | false | Hide editable | Label for the button to hide editable table |
+| save | string | false | Save | Label for the button to save the modified table |
+
+**_Styles object properties_**
+
+| Key | Type | Required | Default | Description |
+| ---- | ------ | -------- | ------- | ------------------------------ |
+| show | object | false | {} | styled-component styles object |
+| hide | object | false | {} | styled-component styles object |
+| save | object | false | {} | styled-component styles object |
+
+##### Actions (type: mass-actions)
+
+**_Properties_**
+
+| Key | Type | Required | Default | Description |
+| ------- | ------ | -------- | ------- | ------------------------------------- |
+| options | array | required | \[] | array of actions objects |
+| label | string | required | - | Label for the actions dropdown button |
+
+**_Actions object properties_**
+
+| Key | Type | Required | Default | Description |
+| ----- | -------- | -------- | ------- | ----------------------------------------------- |
+| type | string | true | - | action |
+| name | string | true | - | Unique name |
+| label | string | true | - | Label for the action item |
+| thunk | function | true | - | ( config ) => ( dispatch, getState ) => { ... } |
+
+**_Styles object properties_**
+
+| Key | Type | Required | Default | Description |
+| ------------ | ------ | -------- | ------- | ------------------------------ |
+| button | object | false | {} | styled-component styles object |
+| dropdownMenu | object | false | {} | styled-component styles object |
+| dropdownItem | object | false | {} | styled-component styles object |
+
+##### Button (type: button)
+
+**_Properties_**
+
+| Key | Type | Required | Default | Description |
+| ----- | ------- | -------- | ------- | ------------------------------------- |
+| label | string | required | - | Label for the actions dropdown button |
+| state | boolean | false | false | Whether to pass state to this button |
+
+##### ResetFilters (type: reset-filters)
+
+**_Properties_**
+
+| Key | Type | Required | Default | Description |
+| ----- | ------- | -------- | ------- | ------------------------------------- |
+| label | string | required | - | Label for the actions dropdown button |
+| state | boolean | false | false | Whether to pass state to this button |
+
+##### Print (type: print)
+
+Makes the table printable.
+
+**_Properties_**
+
+| Key | Type | Required | Default | Description |
+| ----- | ------- | -------- | ------- | ------------------------------------- |
+| label | string | required | - | Label for the actions dropdown button |
+| state | boolean | false | false | Whether to pass state to this button |
+
+##### Columns (type: columns)
+
+Shows the columns toggling dropdown.
+
+**_Properties_**
+
+| Key | Type | Required | Default | Description |
+| ----- | ------- | -------- | ------- | ------------------------------------- |
+| label | string | required | - | Label for the actions dropdown button |
+| state | boolean | false | false | Whether to pass state to this button |
+
+**_Styles object properties_**
+
+| Key | Type | Required | Default | Description |
+| ------------ | ------ | -------- | ------- | ------------------------------ |
+| button | object | false | {} | styled-component styles object |
+| dropdownMenu | object | false | {} | styled-component styles object |
+| dropdownItem | object | false | {} | styled-component styles object |
+
+##### Limiter (type: limiter)
+
+| Key | Type | Required | Default | Description |
+| ------- | ------- | -------- | ------- | --------------------------------------------------------------------- |
+| options | array | required | \[] | array of limiter counts |
+| default | integer | required | \[] | default limiter option (should be a value in the options array above) |
+
+##### Table (type: table)
+
+| Key | Type | Required | Default | Description |
+| ------- | ----- | -------- | ------- | ----------------------------------------- |
+| columns | array | required | \[] | array of object with column configuration |
+
+**_Columns object properties_**
| Key | Type | Required | Default | Description |
| ------------------------------- | ------------ | -------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -297,24 +517,32 @@ Note: action of type "editable" is required when you set the table to be editabl
| label | string | true | - | Label for the action |
| thunk | function | true | - | An action creator which is dispatched on action click. Check demo schema. |
-#### Styles object
-
-Styles has the following properties available:
-
-| Key | Type | Required | Default | Description |
-| -------------- | ------------------------------ | -------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| tableContainer | styled-components style object | false | - | Outer table container |
-| table | styled-components style object | false | - | Table component |
-| thead | styled-components style object | false | - | Table header component |
-| tbody | styled-components style object | false | - | Table body component |
-| tr | object | false | - | Table rows - the object can contain the following keys `header`, `filter`, `body`, each of whose values is a styled-components style object |
-| th | styled-components style object | false | - | Table header columns |
-| td | object | false | - | Table columns - the object contain the following keys `filter`, `body` whose value is a styled-components style object |
-| toolbar | object | false | - | Keys `container` and `row` which are styled-components style object and `item` which is an object with keys that are the names of the respective items (as defined in the config) and the value is a styled-components style object |
-| pagination | object | false | - | Keys `container` - a styled-components style object and `item` - same as above toolbar item |
-| filter | object | false | - | Each key is the name of the column and the value is the styled-components style object |
-| body | object | false | - | Same as `filter` (above) |
-| loader | object | false | - | There are 2 style-able keys `mask` and `spinner` for the loader overlay and the loading spinner respectively |
+**_Styles object properties_**
+
+| Key | Type | Required | Default | Description |
+| ------- | ------ | -------- | ------- | ------------------------------ |
+| table | object | false | {} | styled-component styles object |
+| thead | object | false | {} | styled-component styles object |
+| tbody | object | false | {} | styled-component styles object |
+| filters | object | false | {} | styled-component styles object |
+| tr | object | false | {} | check below |
+| th | object | false | {} | styled-component styles object |
+| td | object | false | {} | check below |
+
+**_tr Styles object properties_**
+
+| Key | Type | Required | Default | Description |
+| ------- | ------ | -------- | ------- | ------------------------------ |
+| header | object | false | {} | styled-component styles object |
+| filters | object | false | {} | styled-component styles object |
+| body | object | false | {} | styled-component styles object |
+
+**_tr Styles object properties_**
+
+| Key | Type | Required | Default | Description |
+| ------- | ------ | -------- | ------- | ------------------------------ |
+| filters | object | false | {} | styled-component styles object |
+| body | object | false | {} | styled-component styles object |
## License
diff --git a/demo/src/schema/basic.js b/demo/src/schema/basic.js
index 2507824..0df46cc 100644
--- a/demo/src/schema/basic.js
+++ b/demo/src/schema/basic.js
@@ -1,132 +1,13 @@
import React from 'react';
import { MODIFY_DATA, REQUEST_DATA, IS_LOADING } from '../../../src/actions';
+import { getItemIds } from '../../../src/utils';
export default {
name: 'posts',
height: 400,
rowHeight: 50,
- filterable: true,
- headers: true,
- editable: true,
editing: false,
primaryKey: 'pageId',
- // styles: {
- // loader: {
- // mask: {
- // backgroundColor: 'red',
- // },
- // spinner: {
- // borderTopColor: 'black',
- // }
- // },
- // tableContainer: {
- // fontSize: '16px',
- // },
- // // table: {
- // // background: '#000',
- // // },
- // // thead: {
- // // background: '#000'
- // // },
- // // tbody: {
- // // background: '#000'
- // // },
- // // tr: {
- // // header: { fontWeight: 'normal' },
- // // filter: { },
- // // body: { },
- // // },
- // // th: { textAlign: 'center', ':last-child': {
- // // textAlign: 'right'
- // // }},
- // // td: {
- // // filter: {},
- // // body: { textAlign: 'center', ':last-child': {
- // // textAlign: 'right'
- // // }}
- // // },
- // toolbar: {
- // // item: {
- // // backgroundColor: '#000',
- // // },
- // // containr:
- // // row:
- // // item:
- // item: {
- // actions: {
- // marginRight: '5px'
- // },
- // columns: {
- // float: 'right',
- // 'button': {
- // background: '#fff',
- // color: '#6c757d'
- // },
- // '> div > div': {
- // right: 0,
- // left: ''
- // }
- // }
- // }
- // },
- // pagination: {
- // // container:
- // // item:
- // // items: {
- // // [name]:
- // // },
- // item: {
- // pages: {
- // float: 'right',
- // }
- // }
- // },
- // filter: {
- // // pageId: {' input': {
- // // border: '1px solid #000'
- // // }}
- // // [name]:
- // },
- // body: {
- // actions: {
- // ' button': {
- // fontSize: '12px',
- // ':last-child': {
- // background: '#dc3545',
- // color: '#fff',
- // ':hover': {
- // backgroundColor: '#c82333',
- // }
- // }
- // }
- // }
- // // [name]:
- // }
- // },
- pagination: {
- // visible: true, // or an object { top: true, bottom: false } default visible
- items: {
- limiter: {
- type: 'limiter',
- visible: true,
- position: 10,
- options: [10, 20, 50, 200, 2000, 0],
- default: 200,
- },
- pages: {
- type: 'pages',
- visible: true,
- position: 20,
- right: true,
- },
- resultCount: {
- type: 'resultCount',
- visible: true,
- position: 30,
- right: true,
- },
- }
- },
routes: {
get: {
route: '/page',
@@ -140,19 +21,93 @@ export default {
route: '/users/:id'
}
},
- toolbar: [
- [{
+ layout: [
+ ['Editable'],
+ ['MassActions', 'SimpleButton', 'ResetFilters', 'Spacer', 'Print', 'Columns'],
+ ['Limiter', 'Spacer', 'ResultCount', 'Spacer', 'Pages'],
+ [{ id: 'Table', layout: [
+ ['Header'],
+ ['Filters'],
+ ['Body'],
+ ['Header']
+ ]}],
+ ['Limiter', 'Spacer', 'ResultCount', 'Spacer', 'Pages'],
+ ],
+ components: {
+ // Loader: {
+ // // styles: {
+ // // mask: {
+ // // backgroundColor: 'red',
+ // // },
+ // // spinner: {
+ // // borderTopColor: 'black',
+ // // }
+ // // }
+ // },
+ ResultCount: {
+ styles: {
+ display: 'flex',
+ flexDirection: 'column',
+ justifyContent: 'center'
+ }
+ },
+ // Pages: {
+ // styles: {
+ // first: { backgroundColor: 'red' },
+ // previous: { backgroundColor: 'green' },
+ // pageNumber: { backgroundColor: 'yellow' },
+ // next: { backgroundColor: 'pink' },
+ // last: { backgroundColor: 'purple' },
+ // }
+ // },
+ Editable: {
+ type: 'editable',
+ labels: {
+ show: 'Make editable',
+ hide: 'Hide editable',
+ save: 'Save',
+ },
+ save: ( config ) => ( dispatch, getState ) => {
+ const tableState = getState()[config.reducerName][config.name];
+ console.log('toolbar save click with modified data', config, tableState.modified);
+ config.action(MODIFY_DATA)({ clear: true });
+ // Dispatch MODIFY_DATA action with clear: true, to reset the modified data
+ // Dispatch REQUEST_DATA action "config.action(REQUEST_DATA)" to refresh data.
+ },
+ // styles: {
+ // show: { backgroundColor: 'blue' },
+ // hide: { backgroundColor: 'black', color: 'white'},
+ // save: { backgroundColor: 'green' }
+ // }
+ // renderer: ( props ) => {}
+ },
+ MassActions: {
name: 'actions',
label: 'Actions',
id: 'dropdown',
+ // styles: {
+ // button: {
+ // backgroundColor: '#aaa'
+ // },
+ // dropdownMenu: {
+ // backgroundColor: 'magento'
+ // },
+ // dropdownItem: {
+ // backgroundColor: 'pink'
+ // }
+ // },
options: [{
type: 'action',
name: 'delete',
label: 'Delete',
- indexField: '@id',
+ styles: {
+ backgroundColor: 'red',
+ },
thunk: ( config ) => ( dispatch, getState ) => {
// Get current table state.
- const tableState = getState()[payload.reducerName][payload.name];
+ const tableState = getState()[config.reducerName][config.name];
+ console.log(config, tableState);
+ console.log(getItemIds(tableState.selection, tableState.items, config.primaryKey/*, config.entity.schema*/))
confirm('Are your sure you want to delete the selected items?')
? console.log('delete items', config, getState(), tableState)
: console.log(false);
@@ -165,13 +120,11 @@ export default {
type: 'action',
name: 'edit',
label: 'Edit this field',
- indexField: '@id'
- }],
- visible: true
- }, {
+ }]
+ },
+ SimpleButton: {
type: 'button',
label: 'Simple Button',
- visible: true,
state: false,
thunk: ( config ) => ( dispatch, getState ) => {
const tableState = getState()[config.reducerName][config.name];
@@ -181,128 +134,216 @@ export default {
setTimeout(function() {
config.action(IS_LOADING)({ value: false });
}, 1000);
- }
- }, {
- type: 'resetFilters',
+ },
+ // styles: {
+ // backgroundColor: 'green',
+ // color: 'white'
+ // }
+ },
+ ResetFilters: {
+ type: 'reset-filters',
label: 'Reset Filters',
- visible: true,
state: false,
- }, {
+ // styles: {
+ // backgroundColor: 'red',
+ // color: 'white'
+ // }
+ },
+ Print: {
type: 'print',
label: 'Print Table',
- visible: true,
state: false,
- }, {
+ // styles: {
+ // backgroundColor: 'yellow',
+ // }
+ },
+ Columns: {
name: 'columns',
type: 'columns',
label: 'Columns',
visible: true,
- state: false
- }, {
- name: 'editable',
- type: 'editable',
- labels: {
- show: 'Make editable',
- hide: 'Hide editable',
- save: 'Save',
- },
- save: ( config ) => ( dispatch, getState ) => {
- const tableState = getState()[config.reducerName][config.name];
- console.log('toolbar save click with modified data', config, tableState.modified);
- config.action(MODIFY_DATA)({ clear: true });
- // Dispatch MODIFY_DATA action with clear: true, to reset the modified data
- // Dispatch REQUEST_DATA action "config.action(REQUEST_DATA)" to refresh data.
- }
- }],
- ],
- columns: [{
- name: 'ids',
- label: '',
- sortable: false,
- type: 'selection',
- indexField: '@pageId',
- width: 50,
- extraData: 'selection'
- }, {
- label: 'ID',
- type: 'number',
- name: 'pageId',
- width: 150,
- filterable: true,
- sortable: true,
- // editable: true
- }, {
- label: "Status",
- type: "options",
- name: "entityData.data.status",
- sortable: true,
- filterable: true,
- textAlign: "center",
- width: 150,
- options: {
- "published": {
- "label": "Published"
- },
- "draft": {
- "label": "Draft"
- },
- "unpublished": {
- "label": "Unpublished"
- },
- "pending-review": {
- "label": "Pending Review"
- },
- "trashed": {
- "label": "Trashed"
- },
- "archived": {
- "label": "Archived"
- }
+ state: false,
+ // styles: {
+ // button: {
+ // backgroundColor: '#aaa'
+ // },
+ // dropdownMenu: {
+ // backgroundColor: 'magento'
+ // },
+ // dropdownItem: {
+ // backgroundColor: 'pink'
+ // }
+ // }
},
- editable: true
- // renderer: ({
- // data,
- // colConfig: { name, options }
- // }) =>
Not specified
- }, {
- label: 'Created at',
- type: 'date',
- name: 'createdAt',
- sortable: true,
- textAlign: 'left',
- width: 200,
- editable: true,
- filterable: true,
- }, {
- label: 'Actions',
- type: 'actions',
- name: 'actions',
- width: 100,
- items: [{
- type: 'action',
- name: 'edit',
- label: 'Edit',
- htmlClass: 'btn btn-secondary',
- params: {
- id: '@id',
- },
- thunk: ( config ) => ( dispatch, getState ) => {
- console.log('edit', config, getState());
- }
- }, {
- type: 'action',
- name: 'delete',
- label: 'Delete',
- icon: 'trash-alt',
- params: {
- id: '@id'
+ Limiter: {
+ type: 'limiter',
+ options: [10, 20, 50, 200, 2000, 0],
+ default: 200,
+ // styles: {}
+ },
+ Table: {
+ styles: {
+ // table: {
+ // background: '#000',
+ // },
+ // thead: {
+ // background: '#000'
+ // },
+ // filters: {
+ // background: 'blue'
+ // },
+ // // tbody: {
+ // // background: '#000'
+ // // },
+ // tr: {
+ // header: { fontWeight: 'normal' },
+ // filters: { background: 'green' },
+ // body: { },
+ // },
+ // // th: {
+ // // background: 'red',
+ // // textAlign: 'center',
+ // // ':last-child': {
+ // // textAlign: 'right'
+ // // }
+ // // },
+ // td: {
+ // filters: { backgroundColor: '#000' },
+ // // body: {
+ // // color: '#fff',
+ // // textAlign: 'center',
+ // // ':last-child': {
+ // // textAlign: 'right'
+ // // }
+ // // }
+ // },
},
- thunk: ( config ) => ( dispatch, getState ) => {
- confirm('Are your sure you want to delete this page?')
- ? console.log('delete', getState())
- : console.log(false);
+ columns: [{
+ name: 'ids',
+ label: '',
+ sortable: false,
+ type: 'selection',
+ // indexField: '@pageId',
+ width: 50,
+ extraData: 'selection',
+ }, {
+ label: 'ID',
+ type: 'number',
+ name: 'pageId',
+ width: 150,
+ filterable: true,
+ sortable: true,
+ // editable: true
+ }, {
+ label: 'ID',
+ type: 'number',
+ name: 'pageId',
+ width: 150,
+ filterable: true,
+ sortable: true,
+ // editable: true
+ }, {
+ label: 'ID',
+ type: 'number',
+ name: 'pageId',
+ width: 150,
+ filterable: true,
+ sortable: true,
+ // editable: true
+ }, {
+ label: 'ID',
+ type: 'number',
+ name: 'pageId',
+ width: 150,
+ filterable: true,
+ sortable: true,
+ // editable: true
+ }, {
+ label: 'ID',
+ type: 'number',
+ name: 'pageId',
+ width: 150,
+ filterable: true,
+ sortable: true,
+ // editable: true
+ }, {
+ label: "Status",
+ type: "options",
+ name: "entityData.data.status",
+ sortable: true,
+ filterable: true,
+ textAlign: "center",
+ width: 150,
+ options: {
+ "published": {
+ "label": "Published"
+ },
+ "draft": {
+ "label": "Draft"
+ },
+ "unpublished": {
+ "label": "Unpublished"
+ },
+ "pending-review": {
+ "label": "Pending Review"
+ },
+ "trashed": {
+ "label": "Trashed"
+ },
+ "archived": {
+ "label": "Archived"
+ }
+ },
+ editable: true
+ // renderer: ({
+ // data,
+ // colConfig: { name, options }
+ // }) => Not specified
+ }, {
+ label: 'Created at',
+ type: 'date',
+ name: 'createdAt',
+ sortable: true,
+ textAlign: 'left',
+ width: 200,
+ editable: true,
+ filterable: true,
+ }, {
+ label: 'Actions',
+ type: 'actions',
+ name: 'actions',
+ width: 100,
+ items: [{
+ type: 'action',
+ name: 'edit',
+ label: 'Edit',
+ htmlClass: 'btn btn-secondary',
+ params: {
+ id: '@id',
+ },
+ thunk: ( config ) => ( dispatch, getState ) => {
+ console.log('edit', config, getState());
+ }
+ }, {
+ type: 'action',
+ name: 'delete',
+ label: 'Delete',
+ icon: 'trash-alt',
+ params: {
+ id: '@id'
+ },
+ styles: {
+ backgroundColor: 'red',
+ color: 'white'
+ },
+ thunk: ( config ) => ( dispatch, getState ) => {
+ confirm('Are your sure you want to delete this page?')
+ ? console.log('delete', getState())
+ : console.log(false);
- }
- }]
- }]
+ }
+ }]
+ }]
+ }
+ }
}
diff --git a/package-lock.json b/package-lock.json
index c3fd29f..8bc2616 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "@flipbyte/redux-datatable",
- "version": "0.5.4",
+ "version": "0.6.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -748,9 +748,9 @@
}
},
"axios-observable": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/axios-observable/-/axios-observable-1.1.0.tgz",
- "integrity": "sha512-+dmhX0BgbNt27DvsyA9lRmOJpk4yNXK8k2pCjeiFbbVdaiUR9A9g0x49z8O97SI25m5otadmTIrSPUG8bKhDdQ==",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/axios-observable/-/axios-observable-1.1.1.tgz",
+ "integrity": "sha512-mjrRa8qJSLxTIKqThB7aWpMLZVDKrwYo/PwbbnnOzEo7u7s6n9sUjCYudCR9FVRZ8T3MexgnvXpSjwgdQTylXA==",
"dev": true
},
"babel-cli": {
diff --git a/package.json b/package.json
index cf9c74a..194f413 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@flipbyte/redux-datatable",
- "version": "0.5.4",
+ "version": "0.6.0",
"description": "React-Redux data table",
"main": "lib/index.js",
"module": "es/index.js",
@@ -40,7 +40,7 @@
},
"devDependencies": {
"axios": "^0.19.0",
- "axios-observable": "^1.1.0",
+ "axios-observable": "^1.1.1",
"bootstrap": "^4.3.1",
"lodash": ">=4.17.11",
"nwb": "0.23.x",
diff --git a/src/Renderer.js b/src/Renderer.js
deleted file mode 100644
index 858146c..0000000
--- a/src/Renderer.js
+++ /dev/null
@@ -1,71 +0,0 @@
-import React from 'react';
-
-import Limiter from './Renderer/Pagination/Limiter';
-import Pages from './Renderer/Pagination/Pages';
-import ResultCount from './Renderer/Pagination/ResultCount';
-
-import Button from './Renderer/Toolbar/Button';
-import Print from './Renderer/Toolbar/Print';
-import Columns from './Renderer/Toolbar/Columns';
-import MassActions from './Renderer/Toolbar/MassActions';
-import ResetFilters from './Renderer/Toolbar/ResetFilters';
-import EditableButtons from './Renderer/Toolbar/EditableButtons';
-
-import DateFilter from './Renderer/Filter/Date';
-import FilterOptions from './Renderer/Filter/Options';
-import String from './Renderer/Filter/String';
-import Number from './Renderer/Filter/Number';
-
-import Text from './Renderer/Body/Text';
-import Date from './Renderer/Body/Date';
-import Image from './Renderer/Body/Image';
-import Actions from './Renderer/Body/Actions';
-import Options from './Renderer/Body/Options';
-import Selection from './Renderer/Body/Selection';
-
-import HeaderCol from './Renderer/Header/Column';
-import SelectAll from './Renderer/Header/Selection';
-
-const renderers = {
- pagination: {
- limiter: Limiter,
- pages: Pages,
- resultCount: ResultCount
- },
- toolbar: {
- button: Button,
- print: Print,
- columns: Columns,
- resetFilters: ResetFilters,
- editable: EditableButtons,
- default: MassActions,
- },
- filter: {
- number: Number,
- date: DateFilter,
- options: FilterOptions,
- default: String
- },
- body: {
- date: Date,
- actions: Actions,
- selection: Selection,
- options: Options,
- image: Image,
- default: Text
- },
- header: {
- selection: SelectAll,
- default: HeaderCol
- }
-};
-
-const getRenderers = ( ofType ) => renderers[ofType] || {};
-
-const Renderer = ({ ofType, forItem, ...props }) => {
- const renderers = getRenderers(ofType);
- const Renderer = renderers[forItem] || renderers['default'];
- return ;
-};
-
-export default Renderer;
diff --git a/src/Renderer/Body/Selection.js b/src/Renderer/Body/Selection.js
deleted file mode 100644
index 899e20f..0000000
--- a/src/Renderer/Body/Selection.js
+++ /dev/null
@@ -1,35 +0,0 @@
-import _ from 'lodash';
-import React from 'react';
-import { SET_SELECTION } from '../../actions';
-import { getParam, getConfigParam } from '../../utils';
-import { SELECT_ALL } from '../../constants';
-import Field from '../../components/Field';
-
-const handleSelection = ({ data, indexField, action }, event ) => {
- let paramKey = getConfigParam(indexField);
- let key = getParam(indexField, data);
- action(SET_SELECTION)({ paramKey, key, value: event.target.checked });
-};
-
-const isChecked = ( selection, selected ) => selection.all === true && selected !== false ? true : !!selected;
-
-const Selection = ({
- action,
- data,
- extraData: { selection },
- colConfig: { indexField }
-}) => {
- const dataKey = getConfigParam(indexField);
- const key = _.get(data, dataKey);
- const value = isChecked(selection, _.get(selection.selected, [dataKey, key]));
- return (
-
-
-
- );
-};
-
-export default Selection;
diff --git a/src/Renderer/Header/Selection.js b/src/Renderer/Header/Selection.js
deleted file mode 100644
index 09f940e..0000000
--- a/src/Renderer/Header/Selection.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import React from 'react';
-import { SET_SELECTION } from '../../actions';
-import { SELECT_ALL } from '../../constants';
-import { getConfigParam } from '../../utils';
-import Field from '../../components/Field';
-
-const handleSelection = ( selector, indexField, event ) => {
- let paramKey = getConfigParam(indexField);
- if (!paramKey) {
- return false;
- }
-
- selector({ paramKey, type: SELECT_ALL, value: event.target.checked });
-};
-
-const Selection = ({ name, action, indexField }) => (
- handleSelection(action(SET_SELECTION), indexField, event)}
- />
-);
-
-export default Selection;
diff --git a/src/Renderer/Pagination/Pages.js b/src/Renderer/Pagination/Pages.js
deleted file mode 100644
index 4934a79..0000000
--- a/src/Renderer/Pagination/Pages.js
+++ /dev/null
@@ -1,53 +0,0 @@
-import React from 'react';
-import { SET_PAGE } from '../../actions';
-import styled from 'styled-components';
-import Button from '../../components/Button';
-
-export const List = styled.div `
- display: inline-flex;
- vertical-align: middle;
- background: ${props => props.background || '#fff'};
- border: ${props => props.border || '1px solid rgba(34,36,38,.15)'};
- border-radius: ${props => props.borderRadius || 'none'};
- height: ${props => props.height || '40px'};
-`;
-
-const NUM_LINKS = 5;
-
-const fillRange = ( start, end ) => {
- return Array(end - start + 1).fill().map((item, index) => start + index);
-};
-
-const getPages = ( currentPage, total ) => {
- var padding = Math.floor(NUM_LINKS / 2);
- var left = (currentPage - padding < padding) ? 1 : currentPage - padding;
- var right = (left + NUM_LINKS - 1 > total) ? total : left + NUM_LINKS - 1;
-
- left = (right === total) ?
- (right - NUM_LINKS < 1) ? 1 : right - NUM_LINKS + 1
- : left;
-
- return fillRange(left, right);
-};
-
-const Pages = ({ page, total, action, style }) => {
- const setPage = ( page ) => action(SET_PAGE)({ page });
- return (
-
-
-
- { getPages(page, total).map( (link, index) =>
-
- ) }
-
-
-
- );
-};
-
-export default Pages;
diff --git a/src/Renderer/Pagination/ResultCount.js b/src/Renderer/Pagination/ResultCount.js
deleted file mode 100644
index 0b3ac5f..0000000
--- a/src/Renderer/Pagination/ResultCount.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import React from 'react';
-
-const lowerLimit = ( page, limit ) => ((page - 1) * limit) + 1;
-const upperLimit = ( page, limit, count ) => (page * limit) > count || limit === 0 ? count : page * limit;
-
-const ResultCount = ({ page, limit, count }) => (
- !!count > 0 &&
- Showing { lowerLimit(page, limit) } to { upperLimit(page, limit, count) } of { count } entries
-);
-
-export default ResultCount;
diff --git a/src/Renderer/Toolbar/Button.js b/src/Renderer/Toolbar/Button.js
deleted file mode 100644
index a196fb5..0000000
--- a/src/Renderer/Toolbar/Button.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import React from 'react';
-import StyledButton from '../../components/Button';
-
-const Button = ({ itemConfig, thunk }) => {
- const { thunk: cb, label, name } = itemConfig;
- return (
-
- { label }
-
- );
-};
-
-export default Button;
diff --git a/src/Renderer/Toolbar/Columns.js b/src/Renderer/Toolbar/Columns.js
deleted file mode 100644
index 6350a1c..0000000
--- a/src/Renderer/Toolbar/Columns.js
+++ /dev/null
@@ -1,102 +0,0 @@
-import ReactDOM from 'react-dom';
-import React, { Component } from 'react';
-import Button from '../../components/Button';
-import Dropdown from '../../components/Dropdown';
-import { ADD_COLUMN, REMOVE_COLUMN } from '../../constants';
-
-class Columns extends Component {
- constructor(props) {
- super(props);
-
- this.state = { open: false };
- this.updateColumns = this.updateColumns.bind(this);
- this.toggle = this.toggle.bind(this);
- this.manageEvents = this.manageEvents.bind(this);
- this.handleDocumentClick = this.handleDocumentClick.bind(this);
- }
-
- updateColumns( value, event ) {
- const { internalStateUpdater } = this.props;
-
- if (event.target.checked) {
- internalStateUpdater({ type: ADD_COLUMN, value });
- } else {
- internalStateUpdater({ type: REMOVE_COLUMN, value });
- }
- }
-
- componentWillUnmount() {
- this.manageEvents(true);
- }
-
- componentDidMount() {
- this.manageEvents();
- }
-
- toggle( e ) {
- const { open } = this.state;
- // if(isOpen) {
- // this.manageEvents();
- // } else {
- // this.manageEvents(true);
- // }
- //
- this.setState({ open: !open });
- }
-
- manageEvents(remove = false) {
- var eventUpdater = remove ? document.removeEventListener : document.addEventListener;
-
- ['click', 'touchstart', 'keyup'].forEach( event =>
- eventUpdater(event, this.handleDocumentClick, true)
- );
- }
-
- handleDocumentClick(e) {
- if (e && (e.which === 3 || (e.type === 'keyup' && e.which !== 9))) {
- return;
- }
-
- const container = ReactDOM.findDOMNode(this);
- if (container.contains(e.target) && container !== e.target
- && (e.type !== 'keyup' || e.which === 9)
- ) {
- return;
- }
-
- if (this.state.open) {
- this.toggle(e);
- }
- }
-
- render() {
- const {
- itemConfig: { style = {} },
- columns = [],
- visibleColumns = []
- } = this.props;
- const { open } = this.state;
-
- return (
-
-
-
- { columns.map(({ name, label }, index) =>
-
-
-
-
- )}
-
-
- );
- }
-}
-
-export default Columns;
diff --git a/src/Renderer/Toolbar/EditableButtons.js b/src/Renderer/Toolbar/EditableButtons.js
deleted file mode 100644
index 4aad242..0000000
--- a/src/Renderer/Toolbar/EditableButtons.js
+++ /dev/null
@@ -1,32 +0,0 @@
-import React, { Fragment } from 'react';
-import Button from '../../components/Button';
-import { TOGGLE_EDITABLE } from '../../constants';
-
-const defaultLabels = {
- show: 'Make editable',
- hide: 'Hide editable',
- save: 'Save'
-};
-
-const EditableButtons = ({ itemConfig, isModified, isEditable, isEditing, internalStateUpdater, thunk }) => {
- const { save } = itemConfig;
- const labels = _.merge(defaultLabels, itemConfig.labels);
- const toggleEditable = () => internalStateUpdater({ type: TOGGLE_EDITABLE });
- const getEditableLabel = () => !isEditing ? labels.show : labels.hide;
- return isEditable && (
-
- { !isModified && (
-
- )}
- { isModified && (
-
- )}
-
- );
-};
-
-export default EditableButtons;
diff --git a/src/Renderer/Toolbar/MassActions.js b/src/Renderer/Toolbar/MassActions.js
deleted file mode 100644
index 672397f..0000000
--- a/src/Renderer/Toolbar/MassActions.js
+++ /dev/null
@@ -1,76 +0,0 @@
-import ReactDOM from 'react-dom';
-import React, { Component } from 'react';
-import Button from '../../components/Button';
-import Dropdown from '../../components/Dropdown';
-
-class MassActions extends Component {
- constructor( props ) {
- super(props);
- this.state = { open: false };
-
- this.toggle = this.toggle.bind(this);
- this.manageEvents = this.manageEvents.bind(this);
- this.handleDocumentClick = this.handleDocumentClick.bind(this);
- }
-
- componentWillUnmount() {
- this.manageEvents(true);
- }
-
- componentDidMount() {
- this.manageEvents();
- }
-
- toggle( e ) {
- const { open } = this.state;
- this.setState({ open: !open });
- }
-
- manageEvents(remove = false) {
- var eventUpdater = remove ? document.removeEventListener : document.addEventListener;
-
- ['click', 'touchstart', 'keyup'].forEach( event =>
- eventUpdater(event, this.handleDocumentClick, true)
- );
- }
-
- handleDocumentClick(e) {
- if (e && (e.which === 3 || (e.type === 'keyup' && e.which !== 9))) {
- return;
- }
-
- const container = ReactDOM.findDOMNode(this);
- if (container.contains(e.target) && container !== e.target
- && (e.type !== 'keyup' || e.which === 9)
- ) {
- return;
- }
-
- if (this.state.open) {
- this.toggle(e);
- }
- }
-
- render() {
- const { itemConfig, thunk } = this.props;
- const { label, name, options, style = {} } = itemConfig;
- const { open } = this.state;
-
- return (
-
-
-
- { options.map(({ thunk: cb, ...option }, index) =>
-
- { option.label }
-
- )}
-
-
- );
- }
-}
-
-export default MassActions;
diff --git a/src/Renderer/Toolbar/Print.js b/src/Renderer/Toolbar/Print.js
deleted file mode 100644
index 62948dc..0000000
--- a/src/Renderer/Toolbar/Print.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import React from 'react';
-import { SET_IS_PRINTING } from '../../constants';
-import Button from '../../components/Button';
-
-const Print = ({ itemConfig, internalStateUpdater }) => {
- const printTable = () => internalStateUpdater({ type: SET_IS_PRINTING, value: true });
- return (
-
- );
-};
-
-export default Print;
diff --git a/src/Renderer/Toolbar/ResetFilters.js b/src/Renderer/Toolbar/ResetFilters.js
deleted file mode 100644
index 37830d5..0000000
--- a/src/Renderer/Toolbar/ResetFilters.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import React from 'react';
-import { SET_FILTER } from '../../actions';
-import Button from '../../components/Button';
-
-const ResetFilters = ({ itemConfig, action }) => {
- const clearFilter = () => action(SET_FILTER)({ clear: true });
- return (
-
- );
-};
-
-export default ResetFilters;
diff --git a/src/TableProvider.js b/src/TableProvider.js
deleted file mode 100644
index 585136d..0000000
--- a/src/TableProvider.js
+++ /dev/null
@@ -1,42 +0,0 @@
-import React, { Component } from 'react';
-import _ from 'lodash';
-
-const ConfigContext = React.createContext({});
-
-const TableProvider = ({ config, children }) => (
- { children }
-);
-
-export const TableConsumer = ({ children }) => (
-
- { (config) => (
- children({ config })
- )}
-
-);
-
-export const withTableConfig = ( paths ) => ( WrappedComponent ) => {
- const ComponentWithConfig = ( props ) => (
-
- { (config) => {
- var tableConfig = {};
- if(_.isObject(paths)) {
- _.forEach(paths, ( value, key ) => {
- tableConfig = {
- ...tableConfig,
- [key]: _.get(config.config, value)
- };
- });
- } else {
- tableConfig = paths ? _.get(config.config, paths, false) : config;
- }
-
- return ;
- } }
-
- );
-
- return ComponentWithConfig;
-};
-
-export default TableProvider;
diff --git a/src/components/Body.js b/src/components/Body.js
new file mode 100644
index 0000000..8a7150e
--- /dev/null
+++ b/src/components/Body.js
@@ -0,0 +1,206 @@
+import React, { useRef, useEffect } from 'react';
+import { Loader, Tbody, Tr, Td, Div } from '../styled-components';
+import { Body as Renderers } from './Renderer';
+import { getStyles, getRenderer, prepareData, getExtraBodyRowProps, calculateWidth } from '../utils';
+import { MODIFY_DATA } from '../actions';
+
+const Body = ({
+ columns,
+ data = [],
+ rowHeight,
+ height,
+ isFetching,
+ scrollData: { top: startTop },
+ setScrollData,
+ overScanCount = 10,
+ isPrinting = false,
+ innerHeight,
+ visibleHeight = 500,
+ styles = {},
+ schema,
+ state,
+ primaryKey,
+ modified,
+ tableWidth: { width = '100%', widthAdjustment = 1 },
+ setTableWidth,
+ bodyExtraData = {},
+ action,
+ thunk,
+ isEditing,
+ minWidth,
+ loaderStyles = {},
+}) => {
+ let slicedData = [];
+ let startIndex = 0;
+ if (isPrinting === false) {
+ const visibleLower = startTop - overScanCount * rowHeight;
+ const visibleUpper = startTop + visibleHeight + overScanCount * rowHeight;
+
+ startIndex = Math.floor(visibleLower / rowHeight);
+ if(startIndex < 0) {
+ startIndex = 0;
+ }
+
+ const endIndex = Math.ceil(visibleUpper / rowHeight);
+ slicedData = data.slice(startIndex, endIndex);
+ } else {
+ slicedData = data.slice(0)
+ }
+
+ const ref = useRef(null);
+ const updateTableDimensions = () => {
+ const tableBodyEl = ref.current;
+ const computedTableWidth = minWidth > tableBodyEl.clientWidth || !tableBodyEl.clientWidth
+ ? minWidth
+ : tableBodyEl.clientWidth;
+
+ const percentage = computedTableWidth / calculateWidth(columns);
+ setTableWidth({
+ width: calculateWidth(columns, percentage),
+ widthAdjustment: percentage
+ })
+ }
+
+ const handleScroll = () => (
+ setScrollData({ pointerEvents: 'none', top: ref.current.scrollTop, left: -ref.current.scrollLeft })
+ );
+
+ useEffect(() => {
+ updateTableDimensions();
+ ref.current.addEventListener('scroll', handleScroll.bind(this), true);
+ window.addEventListener('resize', updateTableDimensions);
+ return () => {
+ ref.current.addEventListener('scroll', handleScroll.bind(this), true);
+ window.removeEventListener('resize', updateTableDimensions);
+ };
+ }, []);
+
+ return (
+
+ { isFetching && }
+
+ { slicedData.map((item, index) => {
+ const rowIndex = startIndex + index;
+ const top = rowIndex * rowHeight;
+ const data = prepareData(item, schema, state);
+ const primarKeyValue = _.get(data, primaryKey);
+ const modifiedData = modified[primarKeyValue] || {};
+ return (
+
+ {(column, index) => {
+ const { width, textAlign, name, type } = column;
+ const ColRenderer = getRenderer(column, Renderers);
+ const modifiedValue = _.get(modifiedData, name);
+ const origValue = _.get(data, name);
+ const value = modifiedValue || origValue;
+ return (
+ |
+
+ {
+ var newData = { ...modifiedData };
+ _.set(newData, primaryKey, primarKeyValue);
+ _.set(newData, event.target.name, event.target.value);
+ action(MODIFY_DATA)({ key: primarKeyValue, value: newData })
+ }}
+ />
+
+ |
+ );
+ }}
+
+ );
+ })}
+
+
+ );
+}
+
+Body.mapPropsToComponent = ({
+ config: {
+ rowHeight,
+ height,
+ primaryKey,
+ components: {
+ Loader = {},
+ Table = {}
+ }
+ },
+ tableData,
+ printing: [ isPrinting ],
+ editing: [ isEditing ],
+ scroller: [ scrollData, setScrollData ],
+ entity: { schema } = {},
+ state,
+ action,
+ thunk,
+ width: [ tableWidth, setTableWidth ],
+ minWidth,
+ visibleColumns
+}) => {
+ const { items: data, isFetching, modified } = tableData;
+ const totalHeight = rowHeight * (data || []).length;
+ const visibleHeight = height || totalHeight;
+
+ return ({
+ bodyExtraData: getExtraBodyRowProps(tableData, visibleColumns),
+ columns: visibleColumns,
+ data,
+ rowHeight,
+ scrollData,
+ setScrollData,
+ innerHeight: totalHeight,
+ height: totalHeight > visibleHeight ? visibleHeight : totalHeight,
+ visibleHeight: visibleHeight,
+ isFetching,
+ isPrinting,
+ schema,
+ state,
+ primaryKey,
+ modified,
+ action,
+ thunk,
+ isEditing,
+ setScrollData,
+ setTableWidth,
+ tableWidth,
+ minWidth,
+ loaderStyles: Loader.styles,
+ styles: Table.styles
+ });
+}
+
+export default Body;
diff --git a/src/components/Button.js b/src/components/Button.js
index 59102ff..56b2426 100644
--- a/src/components/Button.js
+++ b/src/components/Button.js
@@ -1,72 +1,19 @@
import React from 'react';
-import styled, { css } from 'styled-components';
-
-const Button = styled.button `
- display: inline-block;
- transition: color 0.15s ease-in-out,
- background-color 0.15s ease-in-out,
- border-color 0.15s ease-in-out,
- box-shadow 0.15s ease-in-out;
- vertical-align: middle;
- white-space: nowrap;
-
- ${({
- background = '#fff',
- border = '1px solid rgba(34,36,38,.15)',
- fontWeight = 400,
- lineHeight = 1.5,
- fontSize = '14px',
- borderRadius = 0,
- color = '#6c757d',
- padding = '0.375rem 0.75rem',
- dropdownToggle = false,
- hover = {},
- active,
- disabled = false,
- }) => css `
- background: ${background};
- border: ${border};
- font-weight: ${fontWeight};
- line-height: ${lineHeight};
- font-size: ${fontSize};
- border-radius: ${borderRadius};
- color: ${color};
- padding: ${padding};
-
- ${dropdownToggle && `
- &::after {
- display: inline-block;
- width: 0;
- height: 0;
- vertical-align: middle;
- content: '';
- border-top: 0.3em solid;
- border-right: 0.3rem solid transparent;
- border-bottom: 0;
- border-left: 0.3rem solid transparent;
- margin-left: 0.255rem;
- }
- `};
-
- ${!disabled && !active && css `
- cursor: pointer;
-
- &:hover {
- color: ${hover.color || '#fff'};
- background-color: ${hover.backgroundColor || '#5a6268'};
- }
- `};
-
- ${active && css `
- background: ${props => props.activeBackground || '#007bff'};
- color: ${props => props.activeColor || '#fff'};
- font-weight: ${props => props.activeFontWeight || 'normal'};
- `};
-
- ${disabled && css `
- opacity: 0.5;
- `};
- `}
-`;
+import { Button as StyledButton } from '../styled-components';
+
+const Button = ({ config, thunk }) => {
+ const { thunk: cb, label, name, styles = {} } = config;
+ return (
+
+ { label }
+
+ );
+};
+
+Button.mapPropsToComponent = ({ thunk }) => ({ thunk });
export default Button;
diff --git a/src/components/Columns.js b/src/components/Columns.js
new file mode 100644
index 0000000..19be3f7
--- /dev/null
+++ b/src/components/Columns.js
@@ -0,0 +1,70 @@
+import React, { useEffect } from 'react';
+import withDropdown from '../hoc/withDropdown';
+import { ADD_COLUMN, REMOVE_COLUMN } from '../constants';
+import { Button, Dropdown } from '../styled-components';
+
+const updateState = (type, index) => (state) => {
+ let visibleColumnIds = [ ...state ];
+ if (type === ADD_COLUMN) {
+ visibleColumnIds.push(index);
+ visibleColumnIds.sort();
+ } else {
+ visibleColumnIds.splice(visibleColumnIds.indexOf(index), 1).sort();
+ }
+
+ return visibleColumnIds;
+}
+
+const updateColumns = (setVisibleColumnIds, index, event) => {
+ if (event.target.checked) {
+ setVisibleColumnIds(updateState(ADD_COLUMN, index))
+ } else {
+ setVisibleColumnIds(updateState(REMOVE_COLUMN, index))
+ }
+};
+
+const Columns = withDropdown(({
+ open,
+ toggle,
+ columns,
+ visibleColumnIds,
+ setVisibleColumnIds,
+ config: {
+ styles = {}
+ }
+}) => (
+
+
+
+ { columns.map(({ name, label }, index) => (
+
+ updateColumns(setVisibleColumnIds, index, event)}
+ />
+
+
+ ))}
+
+
+));
+
+Columns.mapPropsToComponent = ({
+ config: {
+ components: {
+ Table: { columns }
+ }
+ },
+ columns: [ visibleColumnIds, setVisibleColumnIds ]
+}) => ({ columns, visibleColumnIds, setVisibleColumnIds });
+
+export default Columns;
diff --git a/src/components/Editable.js b/src/components/Editable.js
new file mode 100644
index 0000000..3dfbf6e
--- /dev/null
+++ b/src/components/Editable.js
@@ -0,0 +1,48 @@
+import React, { Fragment } from 'react';
+import { Button } from '../styled-components';
+import { TOGGLE_EDITABLE } from '../constants';
+
+const Editable = ({ config, isModified, isEditing, setIsEditing, thunk }) => {
+ const {
+ save,
+ labels: {
+ show = 'Make editable',
+ hide = 'Hide editable',
+ save: saveLabel = 'Save',
+ },
+ styles = {}
+ } = config;
+ const toggleEditable = () => setIsEditing(state => !state);
+ const getEditableLabel = () => !isEditing ? show : hide;
+ const getEditableStyles = () => !isEditing ? styles.show : styles.hide;
+ return (
+
+ { !isModified && (
+
+ )}
+ { isModified && (
+
+ )}
+
+ );
+};
+
+Editable.mapPropsToComponent = ({
+ thunk,
+ tableData,
+ editing: [ isEditing, setIsEditing ]
+}) => ({ isEditing, setIsEditing, thunk, isModified: tableData && !_.isEmpty(tableData.modified) });
+
+export default Editable;
diff --git a/src/components/Filters.js b/src/components/Filters.js
new file mode 100644
index 0000000..ef2b8c1
--- /dev/null
+++ b/src/components/Filters.js
@@ -0,0 +1,62 @@
+import React from 'react';
+import { Tr, Td, Thead, Div } from '../styled-components';
+import { getStyles, getRenderer } from '../utils';
+import { Filters as Renderers } from './Renderer';
+import { SET_FILTER } from '../actions';
+
+const Filters = ({
+ columns,
+ action,
+ query = {},
+ children,
+ styles = {},
+ scrollData: { left },
+ tableWidth: { width = '100%', widthAdjustment = 1 }
+}) => (
+
+
+
+ {(config, index) => {
+ const { filterable, type, width, ...rest } = config;
+ const { name } = rest;
+ const value = _.get(query, ['search', name, 'value']);
+ const Component = getRenderer(config, Renderers);
+ return (
+ |
+ { filterable && Component && (
+
+ action(SET_FILTER)({ key, filter })}
+ { ...rest }
+ />
+
+ )}
+ |
+ );
+ }}
+
+
+
+);
+
+Filters.mapPropsToComponent = ({
+ visibleColumns,
+ action,
+ tableData: { query },
+ width: [ tableWidth ],
+ scroller: [ scrollData ],
+ config: {
+ components: {
+ Table: { styles }
+ }
+ }
+}) => ({ columns: visibleColumns, action, query, tableWidth, scrollData, styles });
+
+export default Filters;
diff --git a/src/components/Header.js b/src/components/Header.js
new file mode 100644
index 0000000..35883b6
--- /dev/null
+++ b/src/components/Header.js
@@ -0,0 +1,87 @@
+import React from 'react';
+import { Tr, Th, Thead } from '../styled-components';
+import { getStyles, getRenderer } from '../utils';
+import { Header as Renderers } from './Renderer';
+import { SET_SORT } from '../actions';
+
+const changeSortOrder = ( query, colName, sorter ) => {
+ let dir = null;
+ if ( query.sort !== colName ) {
+ dir = 'asc';
+ } else {
+ if (query.dir === 'asc') {
+ dir = 'desc';
+ } else if (query.dir === 'desc') {
+ colName = '';
+ dir = '';
+ } else {
+ dir = 'asc';
+ }
+ }
+
+ sorter({ sort: colName, dir });
+};
+
+const Header = ({
+ columns,
+ action,
+ query = {},
+ selection,
+ children,
+ scrollData: { left },
+ styles = {},
+ primaryKey,
+ tableWidth: { width = '100%', widthAdjustment = 1 }
+}) => (
+
+
+
+ {(config, index) => {
+ const { sortable, width, textAlign, name, type, ...rest } = config;
+ const { sort, dir } = query;
+ const Component = getRenderer(config, Renderers);
+ return (
+ |
+ { Component && (
+
+ )}
+ |
+ );
+ }}
+
+
+
+);
+
+Header.mapPropsToComponent = ({
+ visibleColumns,
+ action,
+ tableData: { query, selection },
+ width: [ tableWidth ],
+ scroller: [ scrollData ],
+ config: {
+ primaryKey,
+ components: {
+ Table: { styles }
+ }
+ }
+}) => ({ columns: visibleColumns, action, query, tableWidth, scrollData, selection, styles, primaryKey });
+
+export default Header;
diff --git a/src/Renderer/Pagination/Limiter.js b/src/components/Limiter.js
similarity index 58%
rename from src/Renderer/Pagination/Limiter.js
rename to src/components/Limiter.js
index 99ff17d..f098cb6 100644
--- a/src/Renderer/Pagination/Limiter.js
+++ b/src/components/Limiter.js
@@ -1,13 +1,17 @@
import React from 'react';
-import { SET_LIMIT } from '../../actions';
-import Field from '../../components/Field';
-import Label from '../../components/Label';
-import { isUndefined } from '../../utils';
+import { SET_LIMIT } from '../actions';
+import { isUndefined } from '../utils';
+import { Field, Label } from '../styled-components';
-const Limiter = ({ options, limit, action, style }) => {
+const Limiter = ({
+ config: { options },
+ limit,
+ action,
+ config: { styles }
+}) => {
const setLimit = ( limit ) => action(SET_LIMIT)({ limit });
return (
-