diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..94826a2 --- /dev/null +++ b/.babelrc @@ -0,0 +1,36 @@ +{ + "presets": [ + [ + "/Users/eon/Documents/GitHub/redux-datatable/node_modules/babel-preset-env/lib/index.js", + { + "loose": true, + "modules": false + } + ], + "/Users/eon/Documents/GitHub/redux-datatable/node_modules/babel-preset-react/lib/index.js", + "/Users/eon/Documents/GitHub/redux-datatable/node_modules/babel-preset-stage-1/lib/index.js" + ], + "plugins": [ + "/Users/eon/Documents/GitHub/redux-datatable/node_modules/babel-plugin-transform-decorators-legacy/lib/index.js", + [ + "/Users/eon/Documents/GitHub/redux-datatable/node_modules/babel-plugin-transform-react-remove-prop-types/lib/index.js", + { + "mode": "wrap" + } + ], + [ + "/Users/eon/Documents/GitHub/redux-datatable/node_modules/babel-plugin-transform-runtime/lib/index.js", + { + "helpers": false, + "polyfill": false, + "regenerator": true + } + ] + ], + "ignore": [ + ".spec.js", + ".test.js", + "-test.js", + "/__tests__/" + ] +} \ No newline at end of file diff --git a/README.md b/README.md index c4df947..4b194e9 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [Developed by Flipbyte](https://www.flipbyte.com/) [![npm package][npm-badge]][npm] +[![Codacy Badge][codacy-badge]][codacy] Datatable built using React and Redux to fetch JSON data asynchronously using REST API. @@ -46,33 +47,30 @@ import { reducer, epics } from '@flipbyte/redux-datatable'; 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: { + 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, - } - }, { - type: 'resultCount', - visible: true, - position: 30, - style: { - width: '350px', - textAlign: 'center', - } - }] - }, + }, + resultCount: { + type: 'resultCount', + visible: true, + position: 30, + right: true, + }, + } + }, 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}', @@ -95,9 +93,6 @@ import { reducer, epics } from '@flipbyte/redux-datatable'; label: 'Columns', visible: true, state: false, - style: { - right: true - } }, ... ] @@ -199,29 +194,24 @@ const YourComponent = () => | 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 | #### 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 | +| 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 array: +##### 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 | -| 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 @@ -244,7 +234,6 @@ Toolbar config is an array of array of object where objects are the toolbar item | 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** | | | | | @@ -252,10 +241,6 @@ Toolbar config is an array of array of object where objects are the toolbar item | 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 @@ -276,8 +261,29 @@ Toolbar config is an array of array of object where objects are the toolbar item | 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 avaailable: + +| 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) | + ## License The MIT License (MIT) [npm-badge]: https://img.shields.io/npm/v/@flipbyte/redux-datatable.svg [npm]: https://www.npmjs.com/package/@flipbyte/redux-datatable + +[codacy-badge]: https://api.codacy.com/project/badge/Grade/67274650b4874f5db55ede76156ab4d2 +[codacy]: https://www.codacy.com/app/flipbyte/redux-datatable?utm_source=github.com&utm_medium=referral&utm_content=flipbyte/redux-datatable&utm_campaign=Badge_Grade diff --git a/demo/src/css/simple-sidebar.css b/demo/src/css/simple-sidebar.css index 69d3ef5..00bcb8d 100644 --- a/demo/src/css/simple-sidebar.css +++ b/demo/src/css/simple-sidebar.css @@ -38,7 +38,7 @@ body { .content { position: fixed; - overflow: scroll; + overflow: hidden; height: 100%; top: 60px; } @@ -54,7 +54,7 @@ body { #page-content-wrapper { min-width: 0; - width: 100%; + /* width: 100%; */ margin-left: 15rem; } diff --git a/demo/src/index.js b/demo/src/index.js index 8e47fad..22b41c8 100644 --- a/demo/src/index.js +++ b/demo/src/index.js @@ -6,7 +6,7 @@ import { Provider } from 'react-redux'; import configureStore from './store'; import config from './config'; -import '../../node_modules/bootstrap/dist/css/bootstrap.css'; +// import '../../node_modules/bootstrap/dist/css/bootstrap.css'; import './css/simple-sidebar.css'; import './css/styles.css'; diff --git a/demo/src/schema/basic.js b/demo/src/schema/basic.js index d6dedb7..d3dd6ba 100644 --- a/demo/src/schema/basic.js +++ b/demo/src/schema/basic.js @@ -4,33 +4,114 @@ export default { rowHeight: 50, filterable: true, headers: true, + // styles: { + // 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: [{ - type: 'limiter', - visible: true, - position: 10, - options: [10, 20, 50, 200, 2000], - default: 200, - style: { - right: false, - } - }, { - type: 'pages', - visible: true, - position: 20, - style: { + 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, - } - }, { - type: 'resultCount', - visible: true, - position: 30, - style: { - width: '350px', - textAlign: 'center', - } - }] + }, + resultCount: { + type: 'resultCount', + visible: true, + position: 30, + right: true, + }, + } }, routes: { get: { @@ -47,6 +128,7 @@ export default { }, toolbar: [ [{ + name: 'actions', label: 'Actions', id: 'dropdown', options: [{ @@ -66,112 +148,20 @@ export default { label: 'Edit this field', indexField: '@id' }], - visible: true, - style: { - right: false, - width: '100px' - } + visible: true }, { - type: 'reset-filters', + type: 'resetFilters', label: 'Reset Filters', visible: true, state: false, }, { + name: 'columns', type: 'columns', label: 'Columns', visible: true, - state: false, - style: { - right: true - } - }/*, { - label: 'NACTIONS', - id: 'massActions', - delete: { - type: 'action', - name: 'delete', - label: 'Delete', - indexField: '@id' - }, - visible: true, - style: { - right: false, - width: '200px' - } - }, { - label: '2Actions', - id: 'massActions', - delete: { - type: 'action', - name: 'delete', - label: 'Delete', - indexField: '@id' - }, - visible: true, - style: { - right: true, - width: '100px' - } - }*/], - // [{ - // label: 'Actions', - // id: 'massActions', - // delete: { - // type: 'action', - // name: 'delete', - // label: 'Delete', - // indexField: '@id' - // }, - // visible: true, - // style: { - // right: false, - // width: '200px' - // } - // }, { - // type: 'columns', - // label: 'Columns', - // visible: true, - // }, { - // label: 'NACTIONS', - // id: 'massActions', - // delete: { - // type: 'action', - // name: 'delete', - // label: 'Delete', - // indexField: '@id' - // }, - // visible: true, - // style: { - // width: '200px' - // } - // }, { - // label: '2Actions', - // id: 'massActions', - // delete: { - // type: 'action', - // name: 'delete', - // label: 'Delete', - // indexField: '@id' - // }, - // visible: true, - // style: { - // right: true, - // width: '100px' - // } - // }] + state: false + }], ], - // toolbar: { - // massActions: { - // label: 'Actions', - // delete: { - // type: 'action', - // name: 'delete', - // label: 'Delete', - // indexField: '@id' - // } - // }, - // columns: true - // }, columns: [{ name: 'ids', label: '', @@ -184,36 +174,10 @@ export default { 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: 'Last Name', - type: 'string', - name: 'last_name', - sortable: true, - textAlign: 'text-left', - width: 200, - filterable: true, - },*/{ label: 'Created at', type: 'date', name: 'createdAt', diff --git a/package-lock.json b/package-lock.json index 15f2745..91a8c09 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@flipbyte/redux-datatable", - "version": "0.1.0", + "version": "0.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -8,6 +8,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz", "integrity": "sha512-3UYcJUj9kvSLbLbUIfQTqzcy5VX7GRZ/CCDrnOaZorFFM01aXp1+GJwuFGV4NDDoAS+mOUyHcO6UD/RfqOks3Q==", + "dev": true, "requires": { "@babel/types": "^7.0.0" } @@ -16,6 +17,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz", "integrity": "sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==", + "dev": true, "requires": { "@babel/types": "^7.0.0" } @@ -32,6 +34,7 @@ "version": "7.3.4", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.3.4.tgz", "integrity": "sha512-WEkp8MsLftM7O/ty580wAmZzN1nDmCACc5+jFzUt+GUFNNIi3LdRlueYz0YIlmJhlZx1QYDMZL5vdWCL0fNjFQ==", + "dev": true, "requires": { "esutils": "^2.0.2", "lodash": "^4.17.11", @@ -41,7 +44,8 @@ "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true } } }, @@ -49,6 +53,7 @@ "version": "0.7.3", "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.7.3.tgz", "integrity": "sha512-uxJqm/sqwXw3YPA5GXX365OBcJGFtxUVkB6WyezqFHlNe9jqUWH5ur2O2M8dGBz61kn1g3ZBlzUunFQXQIClhA==", + "dev": true, "requires": { "@emotion/memoize": "0.7.1" } @@ -56,12 +61,14 @@ "@emotion/memoize": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.1.tgz", - "integrity": "sha512-Qv4LTqO11jepd5Qmlp3M1YEjBumoTHcHFdgPTQ+sFlIL5myi/7xu/POwP7IRu6odBdmLXdtIs1D6TuW6kbwbbg==" + "integrity": "sha512-Qv4LTqO11jepd5Qmlp3M1YEjBumoTHcHFdgPTQ+sFlIL5myi/7xu/POwP7IRu6odBdmLXdtIs1D6TuW6kbwbbg==", + "dev": true }, "@emotion/unitless": { "version": "0.7.3", "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.3.tgz", - "integrity": "sha512-4zAPlpDEh2VwXswwr/t8xGNDGg8RQiPxtxZ3qQEXyQsBV39ptTdESCjuBvGze1nLMVrxmTIKmnO/nAV8Tqjjzg==" + "integrity": "sha512-4zAPlpDEh2VwXswwr/t8xGNDGg8RQiPxtxZ3qQEXyQsBV39ptTdESCjuBvGze1nLMVrxmTIKmnO/nAV8Tqjjzg==", + "dev": true }, "@insin/npm-install-webpack-plugin": { "version": "5.0.0", @@ -1122,6 +1129,7 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-1.10.0.tgz", "integrity": "sha512-sQVKG8irFXx14ZfaK1bBePirfkacl3j8nZwSZK+ZjsbnadRHKQTbhXbe/RB1vT6Vgkz45E+V95LBq4KqdhZUNw==", + "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.0.0", "@babel/helper-module-imports": "^7.0.0", @@ -1198,7 +1206,8 @@ "babel-plugin-syntax-jsx": { "version": "6.18.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", - "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=" + "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=", + "dev": true }, "babel-plugin-syntax-object-rest-spread": { "version": "6.13.0", @@ -2471,7 +2480,8 @@ "camelize": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", - "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" + "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=", + "dev": true }, "caniuse-lite": { "version": "1.0.30000943", @@ -3081,7 +3091,8 @@ "css-color-keywords": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", - "integrity": "sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU=" + "integrity": "sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU=", + "dev": true }, "css-loader": { "version": "1.0.0", @@ -3165,6 +3176,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-2.3.0.tgz", "integrity": "sha512-IhR7bNIrCFwbJbKZOAjNDZdwpsbjTN6f1agXeELHDqg1wHPA8c2QLruttKOW7hgMGetkfraRJCIEMrptifBfVw==", + "dev": true, "requires": { "camelize": "^1.0.0", "css-color-keywords": "^1.0.0", @@ -3934,7 +3946,8 @@ "esutils": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true }, "etag": { "version": "1.8.1", @@ -5386,7 +5399,8 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true }, "has-symbols": { "version": "1.0.0", @@ -7952,7 +7966,8 @@ "memoize-one": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-4.0.3.tgz", - "integrity": "sha512-QmpUu4KqDmX0plH4u+tf0riMc1KHE1+lw95cMrLlXQAFOx/xnBtwhZ52XJxd9X2O6kwKBqX32kmhbhlobD0cuw==" + "integrity": "sha512-QmpUu4KqDmX0plH4u+tf0riMc1KHE1+lw95cMrLlXQAFOx/xnBtwhZ52XJxd9X2O6kwKBqX32kmhbhlobD0cuw==", + "dev": true }, "memory-fs": { "version": "0.4.1", @@ -9565,7 +9580,8 @@ "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true }, "prelude-ls": { "version": "1.1.2", @@ -11585,18 +11601,11 @@ "schema-utils": "^0.4.5" } }, - "styled-button-component": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/styled-button-component/-/styled-button-component-3.0.1.tgz", - "integrity": "sha512-Y88+H5GgL9ZXvu4BY2hxRmLp/wDJhcrVpf3fBjay+wBgAPK6JocBAPq8X+/PXYhg8Fzi0GcOilSo0DVh4L06LA==", - "requires": { - "styled-config": "^3.0.1" - } - }, "styled-components": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-4.1.3.tgz", "integrity": "sha512-0quV4KnSfvq5iMtT0RzpMGl/Dg3XIxIxOl9eJpiqiq4SrAmR1l1DLzNpMzoy3DyzdXVDMJS2HzROnXscWA3SEw==", + "dev": true, "requires": { "@babel/helper-module-imports": "^7.0.0", "@emotion/is-prop-valid": "^0.7.3", @@ -11615,34 +11624,24 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "requires": { "has-flag": "^3.0.0" } } } }, - "styled-config": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/styled-config/-/styled-config-3.0.1.tgz", - "integrity": "sha512-dzXbqRgJPTEbm30T2FYx3JyWCLAXI2B7zoyJcukFLB2WqGZlx640/4TqIq8sgpUlkkLZW5ufeX09i79sREwYFw==" - }, - "styled-dropdown-component": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/styled-dropdown-component/-/styled-dropdown-component-3.0.1.tgz", - "integrity": "sha512-pjZME8BBAIXdysHMJPMoIaMsDM43RJE9Upb2yqaAzk2sWA08bUXUURNzckTlcj1wLuxp68B9ocUtFXk4DMGxYg==", - "requires": { - "styled-config": "^3.0.1" - } - }, "stylis": { "version": "3.5.4", "resolved": "https://registry.npmjs.org/stylis/-/stylis-3.5.4.tgz", - "integrity": "sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q==" + "integrity": "sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q==", + "dev": true }, "stylis-rule-sheet": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz", - "integrity": "sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw==" + "integrity": "sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw==", + "dev": true }, "supports-color": { "version": "6.1.0", diff --git a/package.json b/package.json index 9a1bd96..23c9b37 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@flipbyte/redux-datatable", - "version": "0.1.0", + "version": "0.2.0", "description": "React-Redux data table", "main": "lib/index.js", "module": "es/index.js", @@ -25,7 +25,8 @@ "rxjs": "^6.4.0", "prop-types": "^15.6.2", "react-redux": "^6.0.1", - "redux": "^4.0.1" + "redux": "^4.0.1", + "styled-components": "^4.1.3" }, "dependencies": { "lodash": ">=4.17.10", @@ -35,10 +36,7 @@ "react-pure-time": "^0.2.2", "react-redux-notify": "^4.1.0", "redux-observable": "^1.0.0", - "redux-thunk": "^2.3.0", - "styled-button-component": "^3.0.1", - "styled-components": "^4.1.3", - "styled-dropdown-component": "^3.0.1" + "redux-thunk": "^2.3.0" }, "devDependencies": { "axios": "^0.18.0", @@ -51,7 +49,8 @@ "react-redux": "^6.0.1", "redux": "^4.0.1", "redux-logger": "^3.0.6", - "rxjs": "^6.4.0" + "rxjs": "^6.4.0", + "styled-components": "^4.1.3" }, "author": "", "homepage": "", diff --git a/src/Datatable/Container.js b/src/Datatable/Container.js deleted file mode 100644 index 14b6774..0000000 --- a/src/Datatable/Container.js +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; -import Styles from './Styles'; - -const Container = ({ children }) => - - { React.Children.map(children, (child, i) => - { child } - ) } - - -export default Container; diff --git a/src/Datatable/Pagination.js b/src/Datatable/Pagination.js deleted file mode 100644 index ab9fbd5..0000000 --- a/src/Datatable/Pagination.js +++ /dev/null @@ -1,47 +0,0 @@ -import React from 'react'; -import PropTypes from "prop-types"; -import Styles from './Styles'; -import Limiter, { TYPE_LIMITER } from './Pagination/Limiter'; -import ResultCount from './Pagination/ResultCount'; -import Pages from './Pagination/Pages'; -import { calculatePaginationProps, getLimiter } from '../utils'; - -const renderers = { - 'limiter': Limiter, - 'pages': Pages, - 'resultCount': ResultCount, -} - -const isVisible = (visible, position) => visible === true - || (typeof visible === 'object' - && (visible[position] === true || visible[position] === undefined)); - -const getRenderer = ( type ) => renderers[type]; -const Pagination = ({ - query, - render, - position, - margin, - componentConfig: { - items = [], - visible = true - } -}) => { - const limiter = getLimiter(items); - const defaultLimit = limiter.default || 10; - const paginationProps = calculatePaginationProps(query, defaultLimit); - return( - isVisible(visible, position) && - { items.map((item, index) => { - const { visible: itemVisible, style, type } = item; - return ( - itemVisible && - { render(getRenderer(type), item, paginationProps, index) } - - ) - })} - - ) -} - -export default Pagination; diff --git a/src/Datatable/Pagination/Pages.js b/src/Datatable/Pagination/Pages.js deleted file mode 100644 index 7fa93dd..0000000 --- a/src/Datatable/Pagination/Pages.js +++ /dev/null @@ -1,47 +0,0 @@ -import React from 'react'; -import Styles from '../Styles'; -import { SET_PAGE } from '../../actions'; - -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 }) => { - const setPage = ( page ) => action(SET_PAGE)({ page }); - return ( - - First - Previous - { getPages(page, total).map( (link, index) => - { link } - ) } - = total }>Next - Last - - ); -} - -export default Pages diff --git a/src/Datatable/Renderer.js b/src/Datatable/Renderer.js deleted file mode 100644 index c6d4d72..0000000 --- a/src/Datatable/Renderer.js +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; -import Styles from './Styles'; -import Filter from './Renderer/Filter'; -import Header from './Renderer/Header'; -import Body from './Renderer/Body'; - -const getRenderer = (ofType) => { - const renderers = { - header: Header, - filter: Filter, - default: Body, - } - - return renderers[ofType] || renderers['default']; -} - -const Renderer = ({ ofType, ...props }) => { - const Renderer = getRenderer(ofType); - return -} - -export default Renderer; diff --git a/src/Datatable/Renderer/Body.js b/src/Datatable/Renderer/Body.js deleted file mode 100644 index 727a92c..0000000 --- a/src/Datatable/Renderer/Body.js +++ /dev/null @@ -1,31 +0,0 @@ -import React from 'react'; -import Styles from '../Styles'; - -import Text from './Body/Text'; -import Date from './Body/Date'; -import Image from './Body/Image'; -import Actions from './Body/Actions'; -import Options from './Body/Options'; -import Selection from './Body/Selection'; - -const renderers = { - date: Date, - actions: Actions, - selection: Selection, - options: Options, - image: Image, - default: Text -}; - -const ColumnRenderer = ( props ) => { - const { renderer, type } = props.colConfig; - const Component = renderer || renderers[type] || renderers['default']; - return -}; - -const Body = ({ index, item, top, action, thunk, extra, ...column }) => - - - - -export default Body; diff --git a/src/Datatable/Renderer/Body/Date.js b/src/Datatable/Renderer/Body/Date.js deleted file mode 100644 index d413247..0000000 --- a/src/Datatable/Renderer/Body/Date.js +++ /dev/null @@ -1,21 +0,0 @@ -import _ from 'lodash'; -import Time from 'react-pure-time'; -import React, { Component } from 'react'; -import { shouldUpdate } from '../../../utils'; - -class Date extends Component { - shouldComponentUpdate({ data, colConfig: { name } }) { - return shouldUpdate(this.props.data, data, name); - } - - render() { - const { - data, - index, - colConfig: { name, textAlign, format } - } = this.props; - return - } -} - -export default Date; diff --git a/src/Datatable/Renderer/Body/Image.js b/src/Datatable/Renderer/Body/Image.js deleted file mode 100644 index 5e95ee8..0000000 --- a/src/Datatable/Renderer/Body/Image.js +++ /dev/null @@ -1,16 +0,0 @@ -import _ from 'lodash'; -import { shouldUpdate } from '../../../utils'; -import React, { Fragment, Component } from 'react'; - -class Image extends Component { - shouldComponentUpdate({ data, colConfig: { name } }) { - return shouldUpdate(this.props.data, data, name); - } - - render() { - const { data, index, colConfig: { name, imgWidth, imgHeight } } = this.props; - return - } -} - -export default Image; diff --git a/src/Datatable/Renderer/Body/Options.js b/src/Datatable/Renderer/Body/Options.js deleted file mode 100644 index 99fc9a3..0000000 --- a/src/Datatable/Renderer/Body/Options.js +++ /dev/null @@ -1,27 +0,0 @@ -import _ from 'lodash'; -import Time from 'react-pure-time'; -import { shouldUpdate } from '../../../utils'; -import React, { Fragment, Component } from 'react'; - -class Options extends Component { - shouldComponentUpdate({ data, colConfig: { name } }) { - return shouldUpdate(this.props.data, data, name); - } - - render() { - const { data, colConfig: { name, options } } = this.props; - const value = _.get(data, name); - - if(!options || !options[value]) { - return { value } - } - - return ( - - { options[value].value } - - ); - } -} - -export default Options; diff --git a/src/Datatable/Renderer/Body/Text.js b/src/Datatable/Renderer/Body/Text.js deleted file mode 100644 index 8473800..0000000 --- a/src/Datatable/Renderer/Body/Text.js +++ /dev/null @@ -1,16 +0,0 @@ -import _ from 'lodash'; -import { shouldUpdate } from '../../../utils'; -import React, { Fragment, Component } from 'react'; - -class Text extends Component { - shouldComponentUpdate({ data, colConfig: { name } }) { - return shouldUpdate(this.props.data, data, name); - } - - render() { - const { data, index, colConfig: { name } } = this.props; - return { _.get(data, name, '') } - } -} - -export default Text; diff --git a/src/Datatable/Renderer/Filter.js b/src/Datatable/Renderer/Filter.js deleted file mode 100644 index a733f4f..0000000 --- a/src/Datatable/Renderer/Filter.js +++ /dev/null @@ -1,45 +0,0 @@ -import _ from 'lodash'; -import React from 'react'; -import Styles from '../Styles'; -import { SET_FILTER } from '../../actions'; - -import Date from './Filter/Date'; -import String from './Filter/String'; -import Number from './Filter/Number'; - -export const OPERATOR = { - IS: 'is', - IN: 'in', - NOT_IN: 'not_in', - BETWEEN: 'between', - CONTAINS: 'contains' -} - -export const TYPE = { - DATE: 'date' -} - -const filterers = { - number: Number, - date: Date, - default: String -}; - -const Filterer = ( props ) => { - const Component = filterers[props.type] || filterers['default']; - return -}; - -const Filter = ({ filterable, query, width, action, ...rest }) => { - const { name } = rest; - const filterer = ( key, filter ) => action(SET_FILTER)({ key, filter }); - const value = _.get(query, ['search', name, 'value']); - - return ( - - { filterable && } - - ) -} - -export default Filter; diff --git a/src/Datatable/Renderer/Header.js b/src/Datatable/Renderer/Header.js deleted file mode 100644 index 568dcfa..0000000 --- a/src/Datatable/Renderer/Header.js +++ /dev/null @@ -1,40 +0,0 @@ -import React from 'react'; -import Styles from '../Styles'; -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 = ({ name, label, sortable, action, width, textAlign, query }) => { - const { sort, dir } = query; - return sortable ? - - { label } - { name === sort && } - : - { label } -} - -export default Header; diff --git a/src/Datatable/Styles/Container.js b/src/Datatable/Styles/Container.js deleted file mode 100644 index f2002b5..0000000 --- a/src/Datatable/Styles/Container.js +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; -import styled from 'styled-components'; - -const Container = styled.div ` - width: 100%; - margin-right: auto; - margin-left: auto; -` - -export default Container; diff --git a/src/Datatable/Styles/Pagination.js b/src/Datatable/Styles/Pagination.js deleted file mode 100644 index 114bcc9..0000000 --- a/src/Datatable/Styles/Pagination.js +++ /dev/null @@ -1,68 +0,0 @@ -import React from 'react'; -import styled, { css } from 'styled-components'; - -export const PaginationItem = styled.div ` - display: block; - min-width: ${props => props.width || '200px'}; - float: ${props => props.right ? 'right' : 'left'}; - text-align: ${props => props.textAlign || 'left'}; - font-size: ${props => props.fontSize || '14px'}; -` - -export const PaginationList = 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'} -` - -export const PaginationListItem = styled.button ` - min-width: ${props => props.width || '42px'}; - display: flex; - flex: 0 0 auto; - align-items: center; - text-align: center; - padding: ${props => props.padding || '13px 16px'}; - line-height: 1; - cursor: pointer; - transition: background .1s ease,box-shadow .1s ease,color .1s ease,-webkit-box-shadow .1s ease; - background: ${props => props.background || '#fff'} - border: ${props => props.border || 'none'}; - position: relative; - - &::before { - position: absolute; - content: ''; - top: 0; - right: 0; - height: 100%; - width: 1px; - background: #ebebeb; - } - - &:last-child::before { - height: 0; - } - - &:hover { - background: ${props => !props.disabled && (props.hoverBackground || '#ebebeb')} - } - - ${props => props.active && css ` - background: ${props => props.activeBackground || '#007bff'}; - color: ${props => props.activeColor || '#fff'}; - font-weight: ${props => props.activeFontWeight || 'normal'} - `} -` - -const Pagination = styled.div ` - display: block; - width: 100%; - ${props => props.margin && css ` - margin: ${props.margin} - `} -` - -export default Pagination; diff --git a/src/Datatable/Styles/Table/Body.js b/src/Datatable/Styles/Table/Body.js deleted file mode 100644 index dcd04be..0000000 --- a/src/Datatable/Styles/Table/Body.js +++ /dev/null @@ -1,23 +0,0 @@ -import React from 'react'; -import styled, { css } from 'styled-components'; - -const TableBody = styled.div ` - max-width: 100%; - margin-right: auto; - margin-left: auto; - ${props => props.height && css ` - height: ${props.height} - `}; - overflow-y: scroll; -` - -export const TableBodyInner = styled.div ` - position: relative; - width: ${props => props.width || '100%'}; - border-bottom: ${props => props.borderBottom || '1px solid #ddd'}; - ${props => props.height && css ` - height: ${props.height} - `}; -` - -export default TableBody; diff --git a/src/Datatable/Styles/Table/Cell.js b/src/Datatable/Styles/Table/Cell.js deleted file mode 100644 index 81fdd92..0000000 --- a/src/Datatable/Styles/Table/Cell.js +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import styled, { css } from 'styled-components'; - -const TableCell = styled.div ` - float: left; - padding: 10px 5px; - overflow: hidden; - text-align: ${props => props.textAlign || 'center'}; - text-overflow: ellipsis; - white-space: nowrap; - vertical-align: middle; - width: ${props => props.width || '0px'}; - font-size: ${props => props.fontSize || '14px'}; - - ${props => props.header && css ` - font-weight: ${props => props.headerFontWeight || 'bold'}; - background: ${props => props.headerBackground || '#f9fafb'}; - border-bottom: ${props => props.headerBorderBottom || '1px solid rgba(34,36,38,.1)'}; - `} - - ${props => props.sortable && css ` - cursor: pointer; - `} - - ${props => props.search && css ` - & input { - border-radius: ${props => props.inputRadius || 0}; - color: ${props => props.inputColor || '#495057'}; - border: ${props => props.inputBorder || '1px solid #ebebeb'}; - padding: ${props => props.inputPadding || '10px 14px'}; - width: ${props => props.inputWidth || '100%'}; - - &:focus { - box-shadow: ${props => props.inputFocusShadow || 'none'}; - border-color: ${props => props.inputFocusBorderColor || '#80bdff'} - } - } - `} -` - -export default TableCell; diff --git a/src/Datatable/Styles/Table/Header.js b/src/Datatable/Styles/Table/Header.js deleted file mode 100644 index 1af96a1..0000000 --- a/src/Datatable/Styles/Table/Header.js +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; -import styled from 'styled-components'; - -const TableHeader = styled.div ` - max-width: 100%; - margin-right: auto; - margin-left: auto; - overflow: hidden; -` - -export const TableHeaderInner = styled.div ` - width: ${props => props.width || '100%'}; - border-bottom: ${props => props.borderBottom || '1px solid #ddd'}; -` - -export default TableHeader; diff --git a/src/Datatable/Styles/Toolbar.js b/src/Datatable/Styles/Toolbar.js deleted file mode 100644 index cbc2f94..0000000 --- a/src/Datatable/Styles/Toolbar.js +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react'; -import styled from 'styled-components'; - -export const ToolbarRow = styled.div ` - display: inline-block; - width: 100%; -` - -export const ToolbarItem = styled.div ` - display: block; - width: ${props => props.width || '100px'}; - float: ${props => props.right ? 'right' : 'left'}; - font-size: ${props => props.fontSize || '14px'}; -` - -const Toolbar = styled.div ` - display: block; - width: 100%; -` - -export default Toolbar; diff --git a/src/Datatable/Styles/index.js b/src/Datatable/Styles/index.js deleted file mode 100644 index aadbb96..0000000 --- a/src/Datatable/Styles/index.js +++ /dev/null @@ -1,28 +0,0 @@ -import Container from './Container'; -import Row from './Row'; -import Toolbar, { ToolbarRow, ToolbarItem } from './Toolbar'; -import Pagination, { PaginationItem, PaginationList, PaginationListItem } from './Pagination'; -import Table from './Table'; -import TableHeader, { TableHeaderInner } from './Table/Header'; -import TableBody, { TableBodyInner } from './Table/Body'; -import TableCell from './Table/Cell'; -import SortCaret from './SortCaret'; - -export default { - Container, - Row, - Toolbar, - ToolbarRow, - ToolbarItem, - Pagination, - PaginationItem, - PaginationList, - PaginationListItem, - Table, - TableHeader, - TableHeaderInner, - TableCell, - TableBody, - TableBodyInner, - SortCaret -} diff --git a/src/Datatable/Table.js b/src/Datatable/Table.js deleted file mode 100644 index 908b7f6..0000000 --- a/src/Datatable/Table.js +++ /dev/null @@ -1,215 +0,0 @@ -import _ from 'lodash'; -import ReactDOM from 'react-dom'; -import PropTypes from "prop-types"; -import { connect } from 'react-redux'; -import React, { Component } from 'react'; -import { withTableConfig } from '../TableProvider'; -import TableHeader from './Table/Header'; -import TableBody from './Table/Body'; -import Styles from './Styles'; -import Renderer from './Renderer'; -import { isArray } from '../utils'; - -class Table extends Component { - constructor(props) { - super(props); - - this.width = this.calculateWidth(this.props.config.columns); - this.minWidth = this.width; - this.rowHeight = this.props.config.rowHeight || 39; - this.state = { top: 0 }; - this.handleScroll = this.handleScroll.bind(this); - this.updateTableDimensions = this.updateTableDimensions.bind(this); - this.scrollEnded = _.debounce(this.scrollEnded, 150); - - this.tableBody = React.createRef(); - this.tableHeader = React.createRef(); - } - - get height() { - return this.props.data.items.length * this.rowHeight - } - - scrollEnded() { - this.setState({ pointerEvents: '' }) - } - - scrollUpdate() { - this.tableHeader.current.scrollLeft = this.tableBody.current.scrollLeft; - this.setState({ - pointerEvents: 'none', - top: this.tableBody.current.scrollTop - }) - } - - handleScroll() { - this.scrollEnded(); - this.scrollUpdate(); - } - - calculateWidth(columns) { - return columns.reduce((result, column) => result + (column.width || 0), 0); - } - - updateTableDimensions() { - const tableBodyEl = this.tableBody.current; - this.computedTableWidth = this.minWidth > tableBodyEl.clientWidth - ? this.minWidth - : tableBodyEl.clientWidth; - - const { columns, allColumns, checkedColumns, stateUpdater } = this.props.config; - const totalColumnsWidth = checkedColumns.reduce((result, columnIndex) => ( - result + (allColumns[columnIndex].width || 0) - ), 0); - const percentage = this.computedTableWidth / totalColumnsWidth; - - let updatedColumns = checkedColumns.reduce((result, columnIndex) => ( - result.concat([{ - ...allColumns[columnIndex], - width: Math.round(percentage * allColumns[columnIndex].width) - }]) - ), []) - - this.width = this.calculateWidth(updatedColumns); - stateUpdater({ columns: [ ...updatedColumns ] }); - } - - updateTableOnColumnVisibilityChange(prevColumns, currentColumns) { - let prevKeys = _.keys(prevColumns); - let currentKeys = _.keys(currentColumns); - if(!_.isEqual(prevKeys, currentKeys)) { - this.updateTableDimensions(); - } - } - - componentDidMount() { - this.updateTableDimensions(); - this.tableBody.current.addEventListener('scroll', this.handleScroll, true); - window.addEventListener('resize', this.updateTableDimensions); - } - - componentDidUpdate(prevProps) { - this.updateTableOnColumnVisibilityChange(prevProps.config.columns, this.props.config.columns) - } - - componentWillUnmount() { - this.tableBody.current.removeEventListener('scroll', this.handleScroll, true); - window.removeEventListener('resize', this.updateTableDimensions); - } - - getExtraBodyRowProps() { - const { - data, - config: { columns } - } = this.props; - return columns.reduce((result = {}, { name, extraData }) => { - result[name] = extraData - ? isArray(extraData) - ? extraData.reduce((edResult, dataKey) => { - if(isArray(dataKey)) { - edResult[dataKey[1] || dataKey[0]] = _.get(data, dataKey[0]); - } else { - edResult[dataKey] = _.get(data, dataKey); - } - - return edResult; - }, {}) - : { [extraData]: _.get(data, extraData) } - : {} - return result; - }, {}) - } - - render() { - const { - children, - action, - thunk, - data, - filterable, - headers, - config: { columns, height }, - } = this.props; - const { items = [], query = {} } = data; - const { pointerEvents, top } = this.state; - const bodyExtraData = this.getExtraBodyRowProps(); - - return ( - - - - - { headers !== false && columns.map((column, index) => - - )} - - - { filterable !== false && columns.map((column, index) => - - )} - - - ( - - { columns.map((column, index) => { - const props = { - key: index, - ofType: 'body', - extra: bodyExtraData[column.name], - action, - thunk, - top, - item, - ...column - }; - return - })} - - )} - /> - - - ); - } -} - -export default withTableConfig({ - name: 'name', - height: 'height', - columns: 'columns', - minWidth: 'minWidth', - rowHeight: 'rowHeight', - allColumns: 'allColumns', - checkedColumns: 'checkedColumns', - reducerName: 'reducerName', - stateUpdater: 'updateTableState' -})(Table); diff --git a/src/Datatable/Table/Body.js b/src/Datatable/Table/Body.js deleted file mode 100644 index 0e3e9ad..0000000 --- a/src/Datatable/Table/Body.js +++ /dev/null @@ -1,39 +0,0 @@ -import React, { Component } from 'react'; -import Styles from '../Styles'; - -const TableBody = React.forwardRef(({ - children, - data, - width, - height, - startTop, - rowHeight, - rowWidth, - renderItem, - visibleHeight, - overScanCount = 10 -}, ref) => { - const visibleLower = startTop - overScanCount * rowHeight; - const visibleUpper = startTop + visibleHeight + overScanCount * rowHeight; - - let startIndex = Math.floor(visibleLower / rowHeight); - if(startIndex < 0) { - startIndex = 0; - } - - const endIndex = Math.ceil(visibleUpper / rowHeight); - const slicedData = data.slice(startIndex, endIndex); - - return ( - visibleHeight ? visibleHeight : height}px` }> - - { slicedData.map((item, index) => { - let currentIndex = startIndex + index; - return renderItem({ index: currentIndex, item, top: currentIndex * rowHeight }) - })} - - - ); -}); - -export default TableBody; diff --git a/src/Datatable/Table/Cell.js b/src/Datatable/Table/Cell.js deleted file mode 100644 index 18957fa..0000000 --- a/src/Datatable/Table/Cell.js +++ /dev/null @@ -1,9 +0,0 @@ -import React, { Component } from 'react'; -import Styles from '../Styles'; - -const TableCell = ({ children }) => - - { children } - - -export default TableCell; diff --git a/src/Datatable/Table/Header.js b/src/Datatable/Table/Header.js deleted file mode 100644 index 13d9fb4..0000000 --- a/src/Datatable/Table/Header.js +++ /dev/null @@ -1,12 +0,0 @@ -import React, { Component } from 'react'; -import Styles from '../Styles'; - -const TableHeader = React.forwardRef(({ children, width }, ref) => ( - - - { children } - - -)); - -export default TableHeader; diff --git a/src/Datatable/Toolbar.js b/src/Datatable/Toolbar.js deleted file mode 100644 index babd2f2..0000000 --- a/src/Datatable/Toolbar.js +++ /dev/null @@ -1,33 +0,0 @@ -import React from 'react'; -import PropTypes from "prop-types"; -import MassActions from './Toolbar/MassActions'; -import ResetFilters from './Toolbar/ResetFilters'; -import Columns from './Toolbar/Columns'; -import Styles from './Styles' - -const renderers = { - 'reset-filters': ResetFilters, - 'columns': Columns, - 'default': MassActions -} - -const getRenderer = ( type ) => renderers[type] || renderers['default'] -export const toolbarItem = ( render, item, index ) => { - const { style, type, visible } = item; - return ( - - { visible !== false && render(getRenderer(item.type), item, index) } - - ); -} - -const Toolbar = ({ items, render }) => - - { items.map((row, rowIndex) => - - { row.map((item, itemIndex) => toolbarItem(render, item, itemIndex) )} - - )} - - -export default Toolbar; diff --git a/src/Datatable/index.js b/src/Datatable/index.js deleted file mode 100644 index 4fa995d..0000000 --- a/src/Datatable/index.js +++ /dev/null @@ -1,11 +0,0 @@ -import Container from './Container'; -import Toolbar from './Toolbar'; -import Pagination from './Pagination'; -import Table from './Table' - -export default { - Container, - Toolbar, - Pagination, - Table -} diff --git a/src/Renderer.js b/src/Renderer.js new file mode 100644 index 0000000..42ebb51 --- /dev/null +++ b/src/Renderer.js @@ -0,0 +1,56 @@ +import React from 'react'; + +import Limiter from './Renderer/Pagination/Limiter'; +import Pages from './Renderer/Pagination/Pages'; +import ResultCount from './Renderer/Pagination/ResultCount'; + +import Columns from './Renderer/Toolbar/Columns'; +import MassActions from './Renderer/Toolbar/MassActions'; +import ResetFilters from './Renderer/Toolbar/ResetFilters'; + +import DateFilter from './Renderer/Filter/Date'; +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'; + +const renderers = { + pagination: { + limiter: Limiter, + pages: Pages, + resultCount: ResultCount + }, + toolbar: { + columns: Columns, + resetFilters: ResetFilters, + default: MassActions, + }, + filter: { + number: Number, + date: DateFilter, + default: String + }, + body: { + date: Date, + actions: Actions, + selection: Selection, + options: Options, + image: Image, + default: Text + } +}; + +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/Datatable/Renderer/Body/Actions.js b/src/Renderer/Body/Actions.js similarity index 65% rename from src/Datatable/Renderer/Body/Actions.js rename to src/Renderer/Body/Actions.js index 2b8873c..b186f7a 100644 --- a/src/Datatable/Renderer/Body/Actions.js +++ b/src/Renderer/Body/Actions.js @@ -1,10 +1,10 @@ import _ from 'lodash'; import { connect } from 'react-redux'; import React, { Component } from 'react'; -import { deleteData } from '../../../actions'; -import { withTableConfig } from '../../../TableProvider'; -import { paramsResolver, prepareActionPayload } from '../../../utils' -import { Button } from 'styled-button-component'; +import { deleteData } from '../../actions'; +import { withTableConfig } from '../../TableProvider'; +import { paramsResolver, prepareActionPayload } from '../../utils' +import Button from '../../components/Button'; const Actions = ({ extra, @@ -15,9 +15,8 @@ const Actions = ({ { items.map(({ thunk: cb, ...item }, index) => { item.label } diff --git a/src/Renderer/Body/Date.js b/src/Renderer/Body/Date.js new file mode 100644 index 0000000..27f05cc --- /dev/null +++ b/src/Renderer/Body/Date.js @@ -0,0 +1,14 @@ +import _ from 'lodash'; +import Time from 'react-pure-time'; +import React, { Component } from 'react'; +import { shouldUpdate } from '../../utils'; + +const Date = ({ + data, + index, + colConfig: { name, textAlign, format } +}) => ( + +) + +export default Date; diff --git a/src/Renderer/Body/Image.js b/src/Renderer/Body/Image.js new file mode 100644 index 0000000..f68c716 --- /dev/null +++ b/src/Renderer/Body/Image.js @@ -0,0 +1,13 @@ +import _ from 'lodash'; +import { shouldUpdate } from '../../utils'; +import React, { Fragment, Component } from 'react'; + +const Image = ({ + data, + index, + colConfig: { name, imgWidth, imgHeight } +}) => ( + +) + +export default Image; diff --git a/src/Renderer/Body/Options.js b/src/Renderer/Body/Options.js new file mode 100644 index 0000000..f27e71e --- /dev/null +++ b/src/Renderer/Body/Options.js @@ -0,0 +1,23 @@ +import _ from 'lodash'; +import Time from 'react-pure-time'; +import { shouldUpdate } from '../../utils'; +import React, { Fragment, Component } from 'react'; + +const Options = ({ + data, + colConfig: { name, options } +}) => { + const value = _.get(data, name); + + if(!options || !options[value]) { + return { value } + } + + return ( + + { options[value].value } + + ); +} + +export default Options; diff --git a/src/Datatable/Renderer/Body/Selection.js b/src/Renderer/Body/Selection.js similarity index 51% rename from src/Datatable/Renderer/Body/Selection.js rename to src/Renderer/Body/Selection.js index ebd8c14..a679f5a 100644 --- a/src/Datatable/Renderer/Body/Selection.js +++ b/src/Renderer/Body/Selection.js @@ -2,23 +2,23 @@ import _ from 'lodash'; import PropTypes from "prop-types"; import { connect } from 'react-redux'; import React, { Component } from 'react'; -import { SET_SELECTION } from '../../../actions'; -import { withTableConfig } from '../../../TableProvider'; -import { getParam, getConfigParam, prepareActionPayload } from '../../../utils'; -import { memoizedGetSelection } from '../../../selectors'; +import { SET_SELECTION } from '../../actions'; +import { withTableConfig } from '../../TableProvider'; +import { getParam, getConfigParam, prepareActionPayload } from '../../utils'; +import { memoizedGetSelection } from '../../selectors'; -const handleSelection = ({ data, colConfig, action }, event ) => { - let paramKey = getConfigParam(colConfig.indexField); - let key = getParam(colConfig.indexField, data); +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 Selection = ( props ) => { - const { - data, - extraData: { selection }, - colConfig: { indexField } - } = props; +const Selection = ({ + action, + data, + extraData: { selection }, + colConfig: { indexField } +}) => { const dataKey = getConfigParam(indexField); const key = _.get(data, dataKey); const value = _.get(selection, [dataKey, key], false); @@ -28,7 +28,7 @@ const Selection = ( props ) => { + onChange={ handleSelection.bind(this, { data, indexField, action }) } /> ); } diff --git a/src/Renderer/Body/Text.js b/src/Renderer/Body/Text.js new file mode 100644 index 0000000..481dd54 --- /dev/null +++ b/src/Renderer/Body/Text.js @@ -0,0 +1,13 @@ +import _ from 'lodash'; +import { shouldUpdate } from '../../utils'; +import React, { Fragment, Component } from 'react'; + +const Text = ({ + data, + index, + colConfig: { name } +}) => ( + { _.get(data, name, '') } +); + +export default Text; diff --git a/src/Datatable/Renderer/Filter/Date.js b/src/Renderer/Filter/Date.js similarity index 64% rename from src/Datatable/Renderer/Filter/Date.js rename to src/Renderer/Filter/Date.js index c06d783..d44e224 100644 --- a/src/Datatable/Renderer/Filter/Date.js +++ b/src/Renderer/Filter/Date.js @@ -1,7 +1,7 @@ -import PropTypes from "prop-types"; import React, { Fragment } from 'react'; -import { OPERATOR } from '../Filter'; -import Styles from '../../Styles'; +import { OPERATOR } from '../../constants'; +import Row from '../../components/Row'; +import Field from '../../components/Field'; var dateFrom = null; var dateTo = null; @@ -9,13 +9,13 @@ var dateTo = null; const applyFilter = ( key, filterer, event ) => { let filter = {}; - if(key == 0) { + if (key == 0) { dateFrom = event.target.value; } else { dateTo = event.target.value; } - if( dateFrom || dateTo ) { + if ( dateFrom || dateTo ) { filter = { operator: OPERATOR.BETWEEN, field: event.target.name, @@ -28,24 +28,27 @@ const applyFilter = ( key, filterer, event ) => { filterer(event.target.name, filter); }; -const Date = ({ name, value = [], filterer }) => +const Date = ({ name, value = [], filterer }) => ( - - + - - - + + + - - ; + placeholder="To" + /> + + +); export default Date; diff --git a/src/Datatable/Renderer/Filter/Number.js b/src/Renderer/Filter/Number.js similarity index 65% rename from src/Datatable/Renderer/Filter/Number.js rename to src/Renderer/Filter/Number.js index 47f2ee2..f88be69 100644 --- a/src/Datatable/Renderer/Filter/Number.js +++ b/src/Renderer/Filter/Number.js @@ -1,7 +1,7 @@ -import PropTypes from "prop-types"; import React, { Fragment } from 'react'; -import { OPERATOR } from '../Filter'; -import Styles from '../../Styles'; +import { OPERATOR } from '../../constants'; +import Row from '../../components/Row'; +import Field from '../../components/Field'; var valFrom = null; var valTo = null; @@ -9,13 +9,13 @@ var valTo = null; const applyFilter = ( key, filterer, event ) => { let filter = {}; - if(key == 0) { + if (key == 0) { valFrom = event.target.value; } else { valTo = event.target.value; } - if( valFrom || valTo ) { + if ( valFrom || valTo ) { filter = { operator: OPERATOR.BETWEEN, field: event.target.name, @@ -28,24 +28,27 @@ const applyFilter = ( key, filterer, event ) => { filterer(event.target.name, filter); }; -const Number = ({ name, value = [], filterer }) => +const Number = ({ name, value = [], filterer }) => ( - - + - - - + + + - + placeholder="To" + /> + +); export default Number; diff --git a/src/Datatable/Renderer/Filter/String.js b/src/Renderer/Filter/String.js similarity index 60% rename from src/Datatable/Renderer/Filter/String.js rename to src/Renderer/Filter/String.js index 0f75b77..7f334ff 100644 --- a/src/Datatable/Renderer/Filter/String.js +++ b/src/Renderer/Filter/String.js @@ -1,10 +1,10 @@ -import PropTypes from "prop-types"; import React from 'react'; -import { OPERATOR } from '../Filter'; +import { OPERATOR } from '../../constants'; +import Field from '../../components/Field'; const applyFilter = ( filterer, event ) => { let filter = {}; - if(event.target.value) { + if (event.target.value) { filter = { operator: OPERATOR.CONTAINS, field: event.target.name, @@ -16,11 +16,12 @@ const applyFilter = ( filterer, event ) => { filterer(event.target.name, filter); }; -const String = ({ name, value = '', filterer }) => - ( + - + onChange={ applyFilter.bind(this, filterer) } + /> +); export default String; diff --git a/src/Datatable/Pagination/Limiter.js b/src/Renderer/Pagination/Limiter.js similarity index 60% rename from src/Datatable/Pagination/Limiter.js rename to src/Renderer/Pagination/Limiter.js index 41f820f..056e40b 100644 --- a/src/Datatable/Pagination/Limiter.js +++ b/src/Renderer/Pagination/Limiter.js @@ -1,23 +1,21 @@ import React from 'react'; -import PropTypes from "prop-types"; import { SET_LIMIT } from '../../actions'; +import Field from '../../components/Field'; +import Label from '../../components/Label'; -export const TYPE_LIMITER = 'limiter'; - -const Limiter = ({ options, limit, action, default: defaultLimit }) => { +const Limiter = ({ options, limit, action, style, default: defaultLimit }) => { const setLimit = ( limit ) => action(SET_LIMIT)({ limit }); return ( - - + setLimit(event.target.value) }> { options.map( (option, index) => { option !== 0 ? option : 'All' } ) } - per page - + per page + ); } diff --git a/src/Renderer/Pagination/Pages.js b/src/Renderer/Pagination/Pages.js new file mode 100644 index 0000000..257758b --- /dev/null +++ b/src/Renderer/Pagination/Pages.js @@ -0,0 +1,52 @@ +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 ( + + First + Previous + { getPages(page, total).map( (link, index) => + { link } + ) } + = total }>Next + Last + + ); +} + +export default Pages diff --git a/src/Datatable/Pagination/ResultCount.js b/src/Renderer/Pagination/ResultCount.js similarity index 100% rename from src/Datatable/Pagination/ResultCount.js rename to src/Renderer/Pagination/ResultCount.js diff --git a/src/Datatable/Toolbar/Columns.js b/src/Renderer/Toolbar/Columns.js similarity index 62% rename from src/Datatable/Toolbar/Columns.js rename to src/Renderer/Toolbar/Columns.js index 5dbda90..d5de94d 100644 --- a/src/Datatable/Toolbar/Columns.js +++ b/src/Renderer/Toolbar/Columns.js @@ -1,7 +1,7 @@ -import React, { Component } from 'react'; import ReactDOM from 'react-dom'; -import { Button } from 'styled-button-component'; -import { Dropdown, DropdownItem, DropdownMenu } from 'styled-dropdown-component'; +import React, { Component } from 'react'; +import Button from '../../components/Button'; +import Dropdown from '../../components/Dropdown'; class Columns extends Component { constructor(props) { @@ -15,23 +15,13 @@ class Columns extends Component { } updateColumns( index, event ) { - const { checkedColumns, allColumns, updateTableState } = this.props.config; + const { columnUpdater } = this.props; - var activeColumns = []; if(event.target.checked) { - activeColumns = [ ...checkedColumns, index ]; + columnUpdater({ type: 'add', index }) } else { - const itemIndex = checkedColumns.indexOf(index); - activeColumns = [ ...checkedColumns ]; - activeColumns.splice(itemIndex, 1); + columnUpdater({ type: 'remove', index }) } - activeColumns.sort(); - - const columns = activeColumns.reduce((result, currentIndex) => ( - result.concat([allColumns[currentIndex]]) - ), []); - - updateTableState({ columns, checkedColumns: activeColumns }); } componentWillUnmount() { @@ -55,7 +45,7 @@ class Columns extends Component { manageEvents(remove = false) { var eventUpdater = remove ? document.removeEventListener : document.addEventListener; - + ['click', 'touchstart', 'keyup'].forEach( event => eventUpdater(event, this.handleDocumentClick, true) ); @@ -79,26 +69,30 @@ class Columns extends Component { } render() { - const { config: { allColumns, checkedColumns }} = this.props; + const { + itemConfig: { style = {} }, + columns = [], + visibleColumns = [] + } = this.props; const { open } = this.state; return ( - - + + Columns - - { allColumns.map(({ name, label }, index) => - + + { columns.map(({ name, label }, index) => + { label } - + )} - - + + ); } } diff --git a/src/Datatable/Toolbar/MassActions.js b/src/Renderer/Toolbar/MassActions.js similarity index 68% rename from src/Datatable/Toolbar/MassActions.js rename to src/Renderer/Toolbar/MassActions.js index 9120d7d..b37164e 100644 --- a/src/Datatable/Toolbar/MassActions.js +++ b/src/Renderer/Toolbar/MassActions.js @@ -1,12 +1,7 @@ import ReactDOM from 'react-dom'; import React, { Component } from 'react'; -import PropTypes from "prop-types"; -import { connect } from 'react-redux'; -import { deleteData } from '../../actions'; -import { prepareActionPayload } from '../../utils' -import { getSelectedKeys, getConfigParam } from '../../utils'; -import { Dropdown, DropdownItem, DropdownMenu } from 'styled-dropdown-component'; -import { Button } from 'styled-button-component'; +import Button from '../../components/Button'; +import Dropdown from '../../components/Dropdown'; class MassActions extends Component { constructor( props ) { @@ -58,22 +53,22 @@ class MassActions extends Component { render() { const { itemConfig, thunk } = this.props; - const { label, options } = itemConfig; + const { label, options, style = {} } = itemConfig; const { open } = this.state; return ( - - + + { label } - + { options.map(({ thunk: cb, ...option }, index) => - + { option.label } - + )} - - + + ) } } diff --git a/src/Datatable/Toolbar/ResetFilters.js b/src/Renderer/Toolbar/ResetFilters.js similarity index 66% rename from src/Datatable/Toolbar/ResetFilters.js rename to src/Renderer/Toolbar/ResetFilters.js index d046850..625d254 100644 --- a/src/Datatable/Toolbar/ResetFilters.js +++ b/src/Renderer/Toolbar/ResetFilters.js @@ -1,12 +1,11 @@ import React from 'react'; -import PropTypes from "prop-types"; -import { Button } from 'styled-button-component'; import { SET_FILTER } from '../../actions'; +import Button from '../../components/Button'; const ResetFilters = ({ itemConfig, action }) => { const clearFilter = () => action(SET_FILTER)({ clear: true }) return ( - + { itemConfig.label || 'Reset Filters' } ) diff --git a/src/components/Button.js b/src/components/Button.js new file mode 100644 index 0000000..d4e03d4 --- /dev/null +++ b/src/components/Button.js @@ -0,0 +1,72 @@ +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; + `} + `} +`; + +export default Button; diff --git a/src/components/Container.js b/src/components/Container.js new file mode 100644 index 0000000..b0f66b7 --- /dev/null +++ b/src/components/Container.js @@ -0,0 +1,16 @@ +import React from 'react'; +import styled from 'styled-components'; +import { getExtendedStyles } from '../utils'; + +const Container = styled.div ` + width: 100%; + margin-right: auto; + margin-left: auto; + font-size: 14px; + clear: both; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, + sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; +` + +const ExtendedContainer = styled(Container)(getExtendedStyles()); +export default ExtendedContainer; diff --git a/src/components/Dropdown.js b/src/components/Dropdown.js new file mode 100644 index 0000000..f136804 --- /dev/null +++ b/src/components/Dropdown.js @@ -0,0 +1,88 @@ +import React from 'react'; +import styled, { css } from 'styled-components'; + +const Container = styled.div ` + position: relative; +` + +export const Menu = styled.div ` + position: absolute; + top: 100%; + z-index: 1000; + float: left; + min-width: 10rem; + text-align: left; + list-style: none; + background-clip: padding-box; + + ${({ + padding = '0.5rem 0', + margin = '0.125rem 0 0', + fontSize = '14px', + color = '#6c757d', + border = '1px solid #ebebeb', + backgroundColor = '#fff', + borderRadius, + hidden + }) => css ` + padding: ${padding}; + margin: ${margin}; + font-size: ${fontSize}; + color: ${color}; + background-color: ${backgroundColor}; + border: ${border}; + border-radius: ${borderRadius}; + ${hidden + ? `display: none` + : `display: block` + } + `} +`; + +export const Item = styled.div ` + clear: both; + text-align: inherit; + white-space: nowrap; + background-color: transparent; + border: 0; + cursor: pointer; + + ${({ + padding = '0.5rem 1.5rem', + color = '#6c757d', + fontWeight, + hover = {}, + isActive, + active, + isDisabled, + disabled + }) => css ` + padding: ${padding}; + color: ${color}; + font-weight: ${fontWeight}; + + &:hover, &:focus { + color: ${hover.color || '#16181b'}; + text-decoration: none; + background-color: ${hover.backgroundColor || '#f8f9fa'}; + } + + ${isActive && active && ` + text-decoration: none; + color: ${active.color || '#fff'}; + background-color: ${active.backgroundColor || '#007bff'}; + `} + + ${isDisabled && disabled && ` + color: ${disabled.color || '#6c757d'}; + background-color: transparent; + `} + `} +`; + + +export default { + Container, + Menu, + Item +}; diff --git a/src/components/ExtendedDiv.js b/src/components/ExtendedDiv.js new file mode 100644 index 0000000..15ed0e5 --- /dev/null +++ b/src/components/ExtendedDiv.js @@ -0,0 +1,6 @@ +import React from 'react'; +import styled from 'styled-components'; +import { getExtendedStyles } from '../utils'; + +const ExtendedDiv = styled.div(getExtendedStyles()) +export default ExtendedDiv; diff --git a/src/components/Field.js b/src/components/Field.js new file mode 100644 index 0000000..6e99eb9 --- /dev/null +++ b/src/components/Field.js @@ -0,0 +1,44 @@ +import React from 'react'; +import styled, { css } from 'styled-components'; + +const formControl = ({ + backgroundColor, + border, + borderRadius, + fontSize, + color, + padding, + focusShadow, + focusBorderColor +}) => css ` + display: block; + width: 100%; + font-weight: 400; + line-height: 1.5; + background-color: ${backgroundColor || '#fff'}; + background-clip: padding-box; + border: ${border || '1px solid #ebebeb'}; + border-radius: ${borderRadius || 0}; + font-size: ${fontSize || '14px'}; + color: ${color || '#495057'}; + padding: ${padding || '10px 14px'}; + + &:focus { + box-shadow: ${focusShadow || 'none'}; + border-color: ${focusBorderColor || '#80bdff'} + } +` + +const Input = styled.input ` + ${props => formControl(props)} +` + +const Select = styled.select ` + ${props => formControl(props)} + width: ${props => props.width || '100%'}; +` + +export default { + Select, + Input +} diff --git a/src/components/Label.js b/src/components/Label.js new file mode 100644 index 0000000..18c0ccd --- /dev/null +++ b/src/components/Label.js @@ -0,0 +1,13 @@ +import React from 'react'; +import styled, { css } from 'styled-components'; + +const Label = styled.label ` + display: inline-block; + line-height: 2.5; + ${({ flex, noWrap }) => css ` + ${flex && `display: flex`}; + ${noWrap && `white-space: nowrap`}; + `} +` + +export default Label; diff --git a/src/components/Pagination.js b/src/components/Pagination.js new file mode 100644 index 0000000..ddd655b --- /dev/null +++ b/src/components/Pagination.js @@ -0,0 +1,87 @@ +import React from 'react'; +import styled, { css } from 'styled-components'; +import { getExtendedStyles, getStyles } from '../utils'; + +const Item = styled.div ` + display: flex; + min-width: auto; + float: ${props => props.right ? 'right' : 'left'}; + text-align: left; + flex-direction: column; + justify-content: center; + height: 100%; + margin-left: 10px; + + ${props => props.first && css ` + margin-left: 0; + `} +` + +const ExtendedItem = styled(Item)(getExtendedStyles()); + +const isVisible = (visible, position) => ( + visible === true || ( + typeof visible === 'object' && ( + visible[position] === true || visible[position] === undefined + ) + ) +); + +export const calculatePaginationProps = ( + { page, limit = 0, count = 0 }, + defaultLimit = 10 +) => { + page = page > 1 ? page : 1 + limit = limit != 0 ? limit : defaultLimit; + + let start = (page - 1) * limit + let end = start + limit - 1 + + return { + page: page, + start: start, + end: (count > end && end >= 0) ? end : count, + count: count, + limit: limit, + total: Math.ceil(count / limit) + } +} + +const Pagination = ({ + children, + className, + config: { items = {}, visible = true }, + margin, + position, + query = {}, + styles: { item } +}) => { + const defaultLimit = items.limiter.default || 10; + const paginationProps = calculatePaginationProps(query, defaultLimit); + return( + isVisible(visible, position) && + { Object.keys(items).map((key, index) => { + const { visible: itemVisible, type, right } = items[key]; + return ( + itemVisible && + { children(items[key], paginationProps, index) } + + ) + })} + + ) +} + +const StyledPagination = styled(Pagination) ` + display: block; + width: 100%; +` +const ExtendedStyledPagination = styled(StyledPagination)(getExtendedStyles('container')); +export default ExtendedStyledPagination; diff --git a/src/Datatable/Styles/Row.js b/src/components/Row.js similarity index 83% rename from src/Datatable/Styles/Row.js rename to src/components/Row.js index 89d37ac..815de64 100644 --- a/src/Datatable/Styles/Row.js +++ b/src/components/Row.js @@ -4,11 +4,8 @@ import styled, { css } from 'styled-components'; const Row = styled.div.attrs(({ top }) => ({ style: { top } })) ` - box-sizing: border-box; display: flex; - flex: 1 0 auto; - flex-direction: row; - flex-wrap: wrap; + width: 100%; padding: ${props => props.padding || 0}; height: ${props => props.height || 'auto'}; position: ${props => props.position || 'relative'}; diff --git a/src/Datatable/Styles/SortCaret.js b/src/components/SortCaret.js similarity index 100% rename from src/Datatable/Styles/SortCaret.js rename to src/components/SortCaret.js diff --git a/src/Datatable/Styles/Table.js b/src/components/Table.js similarity index 57% rename from src/Datatable/Styles/Table.js rename to src/components/Table.js index 9d8c4c5..0c027fb 100644 --- a/src/Datatable/Styles/Table.js +++ b/src/components/Table.js @@ -1,13 +1,6 @@ import React from 'react'; import styled from 'styled-components'; - -const TableContainer = styled.div ` - position: relative; - display: block; - width: 100%; - overflow: auto; - border: 0; -` +import { getExtendedStyles } from '../utils'; const Table = styled.div ` overflow: hidden; @@ -15,6 +8,9 @@ const Table = styled.div ` width: 100%; background-color: transparent; border-collapse: collapse; + clear: both; + margin: 10px 0; ` -export default Table; +const ExtendedTable = styled(Table)(getExtendedStyles()); +export default ExtendedTable; diff --git a/src/components/Tbody.js b/src/components/Tbody.js new file mode 100644 index 0000000..7dafddd --- /dev/null +++ b/src/components/Tbody.js @@ -0,0 +1,54 @@ +import React from 'react'; +import styled, { css } from 'styled-components'; +import { getExtendedStyles } from '../utils'; + +const Tbody = React.forwardRef(({ + className, + style, + children, + data, + height, + innerHeight, + overScanCount = 10, + rowHeight, + rowWidth, + startTop, + visibleHeight, + width = '100%' +}, ref) => { + const visibleLower = startTop - overScanCount * rowHeight; + const visibleUpper = startTop + visibleHeight + overScanCount * rowHeight; + + let startIndex = Math.floor(visibleLower / rowHeight); + if(startIndex < 0) { + startIndex = 0; + } + + const endIndex = Math.ceil(visibleUpper / rowHeight); + const slicedData = data.slice(startIndex, endIndex); + + return ( + + + { slicedData.map((item, index) => { + let currentIndex = startIndex + index; + return children({ item, top: currentIndex * rowHeight, index: currentIndex }) + })} + + + ); +}); + + +const StyledTbody = styled(Tbody).attrs(({ height }) => ({ + style: { height } +})) ` + max-width: 100%; + margin-right: auto; + margin-left: auto; + overflow-y: scroll; + overflow-x: scroll; + border-bottom: 1px solid #ddd; +` +const ExtendedStyledTbody = styled(StyledTbody)(getExtendedStyles()) +export default ExtendedStyledTbody; diff --git a/src/components/Td.js b/src/components/Td.js new file mode 100644 index 0000000..9c16f8d --- /dev/null +++ b/src/components/Td.js @@ -0,0 +1,24 @@ +import React from 'react'; +import styled from 'styled-components'; +import { getExtendedStyles } from '../utils'; + +const Td = styled.div.attrs(({ width: maxWidth }) => ({ + style: { maxWidth } +})) ` + flex-direction: column; + justify-content: center; + flex: 1; + display: flex; + padding: 10px 5px; + text-align: left; + text-overflow: ellipsis; + white-space: nowrap; + vertical-align: middle; + width: 100%; + position: relative; + overflow: hidden; + font-size: 14px; +` + +const ExtendedTd = styled(Td)(getExtendedStyles()) +export default ExtendedTd; diff --git a/src/components/Th.js b/src/components/Th.js new file mode 100644 index 0000000..5583774 --- /dev/null +++ b/src/components/Th.js @@ -0,0 +1,14 @@ +import React from 'react'; +import styled from 'styled-components'; +import { getExtendedStyles } from '../utils'; + +import Td from './Td'; + +const Th = styled(Td) ` + font-weight: bold; + background: #f9fafb; + border-bottom: 1px solid rgba(34, 36, 38, .1); + cursor: ${props => props.sortable ? 'pointer': 'default'}; +` +const ExtendedTh = styled(Th)(getExtendedStyles()) +export default ExtendedTh; diff --git a/src/components/Thead.js b/src/components/Thead.js new file mode 100644 index 0000000..ecaa635 --- /dev/null +++ b/src/components/Thead.js @@ -0,0 +1,22 @@ +import React from 'react'; +import styled from 'styled-components'; +import { getExtendedStyles } from '../utils'; + +const Thead = React.forwardRef(({ className, children, width = '100%' }, ref) => ( + + + { children } + + +)); + +const StyledThead = styled(Thead) ` + max-width: 100%; + margin-right: auto; + margin-left: auto; + overflow: hidden; + border-bottom: 1px solid #ddd; +` + +const ExtendedStyledThead = styled(StyledThead)(getExtendedStyles()); +export default ExtendedStyledThead; diff --git a/src/components/Toolbar.js b/src/components/Toolbar.js new file mode 100644 index 0000000..fc6e82a --- /dev/null +++ b/src/components/Toolbar.js @@ -0,0 +1,64 @@ +import React from 'react'; +import styled from 'styled-components'; +import { getExtendedStyles, getStyles } from '../utils'; + +const Row = styled.div ` + display: inline-block; + width: 100%; +` +const ExtendedRow = styled(Row)(getExtendedStyles()) + +const Item = styled.div ` + display: block; + width: auto; + float: left; +` +const ExtendedItem = styled(Item)(getExtendedStyles()) + +const toolbarItem = ( children, item, index, first, last, styles ) => { + const { visible } = item; + return ( + + { visible !== false && children(item, index) } + + ); +} + +const Toolbar = ({ + className, + items, + children, + styles: { item: itemStyles, row } +}) => ( + + { items.map((row, rowIndex) => ( + + { row.map((item, itemIndex) => ( + + { item.visible !== false && children(item, itemIndex) } + + )) } + + ))} + +); + +const StyledToolbar = styled(Toolbar) ` + display: block; + width: 100%; +`; + +const ExtendedStyledToolbar = styled(StyledToolbar)(getExtendedStyles('container')); +export default ExtendedStyledToolbar; diff --git a/src/components/Tr.js b/src/components/Tr.js new file mode 100644 index 0000000..b6896ab --- /dev/null +++ b/src/components/Tr.js @@ -0,0 +1,26 @@ +import React from 'react'; +import styled from 'styled-components'; +import { getExtendedStyles } from '../utils'; + +const Tr = ({ className, children, style, columns }) => ( + + { columns.map((column, index) => ( + children(column, index) + )) } + +); + +const StyledTr = styled(Tr).attrs(({ top }) => ({ + style: { top } +})) ` + display: flex; + width: 100%; + padding: 0; + height: auto; + position: relative; + background: none; + position: ${props => props.position || 'relative'}; + +` +const ExtendedStyledTr = styled(StyledTr)(getExtendedStyles()) +export default ExtendedStyledTr; diff --git a/src/constants.js b/src/constants.js new file mode 100644 index 0000000..cb5f77d --- /dev/null +++ b/src/constants.js @@ -0,0 +1,7 @@ +export const OPERATOR = { + IS: 'is', + IN: 'in', + NOT_IN: 'not_in', + BETWEEN: 'between', + CONTAINS: 'contains' +} diff --git a/src/createTable.js b/src/createTable.js index ef3680c..5641ed3 100755 --- a/src/createTable.js +++ b/src/createTable.js @@ -1,127 +1,340 @@ import _ from 'lodash'; +import React, { useState, useEffect, useReducer, useRef } from 'react'; +import ReactDOM from 'react-dom'; import { connect } from "react-redux"; -import React, { Component } from 'react' -import Datatable from './Datatable'; import TableProvider from './TableProvider'; -import { setPage, setLimit, setSort } from './actions'; -import { createActionCreator, getLimiter } from './utils'; +import Renderer from './Renderer'; +import Row from './components/Row'; +import Table from './components/Table'; +import Thead from './components/Thead'; +import Tbody from './components/Tbody'; +import Tr from './components/Tr'; +import Th from './components/Th'; +import Td from './components/Td'; +import Toolbar from './components/Toolbar'; +import Pagination from './components/Pagination'; +import Container from './components/Container'; +import SortCaret from './components/SortCaret'; +import ExtendedDiv from './components/ExtendedDiv'; +import { createActionCreator, isArray, getStyles } from './utils'; +import { setPage, setLimit, setSort, SET_SORT, SET_FILTER } from './actions'; -class ReduxDatatable extends Component { - constructor(props) { - super(props); +const calculateWidth = ( columns, adjustment = 1 ) => ( + columns.reduce((result, column) => ( + result + ((column.width * adjustment) || 0) + ), 0) +); - this.state = { columns: null, checkedColumns: null }; - this.updateState = this.updateState.bind(this); - } +const useTableScroll = ( tableBody, tableHeader ) => { + const [ pointerEvents, setPointerEvents ] = useState(''); + const [ top, setTop ] = useState(0); - updateState(updatedObj) { - this.setState(updatedObj); + const scrollEnded = _.debounce(() => setPointerEvents(''), 150); + const handleScroll = () => { + tableHeader.current.scrollLeft = tableBody.current.scrollLeft; + setPointerEvents('none'); + setTop(tableBody.current.scrollTop); } - componentDidMount() { - const { tableData, loadData } = this.props; - if(!_.isEmpty(tableData) && !_.isEmpty(tableData.items)) { - return; + useEffect(() => { + if(typeof tableBody.current.addEventListener === 'function') { + tableBody.current.addEventListener('scroll', handleScroll, true); + } + return () => { + tableBody.current.removeEventListener('scroll', handleScroll, true); } + }, [ tableBody.current ]) - loadData(); + return [ top, pointerEvents ]; +} + +const columnsReducer = (state, { type, index }) => { + var visibleColumns = [ ...state ]; + if(type === 'add') { + visibleColumns.push(index); + visibleColumns.sort(); + } else { + visibleColumns.splice(visibleColumns.indexOf(index), 1).sort(); } - setValidPage(nextProps) { - var nextQuery = nextProps.tableData.query; - if(!nextQuery || nextQuery.count <= 0) { - return true; - } + return visibleColumns; +} - let totalEntries = parseInt(nextQuery.count); - let limit = nextQuery.limit != 0 ? nextQuery.limit : totalEntries; - let totalPages = Math.ceil(totalEntries / limit); - if(totalPages < this.props.tableData.query.page) { - this.props.setPage(totalPages); - return false - } +const useTableWidth = ( tableBody, minWidth, visibleColumns ) => { + const [ width, setWidth ] = useState(minWidth); + const [ widthAdjustment, setWidthAdjustment ] = useState(1); - return true; - } + const updateTableDimensions = ( ) => { + const tableBodyEl = tableBody.current; + const computedTableWidth = minWidth > tableBodyEl.clientWidth || !tableBodyEl.clientWidth + ? minWidth + : tableBodyEl.clientWidth; - shouldComponentUpdate(nextProps, nextState) { - return this.setValidPage(nextProps); + const percentage = computedTableWidth / calculateWidth(visibleColumns); + setWidth(calculateWidth(visibleColumns, percentage)) + setWidthAdjustment(percentage); } - getPropsWithState(props, addState) { - if(addState !== false) { - return { ...props, data: this.props.tableData } + useEffect(() => { + window.addEventListener('resize', updateTableDimensions); + return () => { + window.removeEventListener('resize', updateTableDimensions); } + }, []); - return props; - } + useEffect(() => { + updateTableDimensions(); + }, [ visibleColumns ]); - render() { - const { name, config, tableData, reducerName, action, thunk } = this.props; - const { columns: allColumns, ...otherConfig } = config; - const { toolbar = [], pagination = {}, filterable, headers } = config; - const tableConfig = { - ...otherConfig, - allColumns, - columns: this.state.columns || [ ...allColumns ], - checkedColumns: this.state.checkedColumns || _.range(allColumns.length), - updateTableState: this.updateState - }; - - if(!tableData) { - return ( - - - The table failed to initialise. Please check you are connected to the internet and try again. - - - ); + return [ width, widthAdjustment ]; +} + +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'; } + } - const { query } = tableData; + sorter({ sort: colName, dir }); +} + +const renderToolbar = ( config = [], action, thunk, columnUpdater, columns, visibleColumns, styles = {} ) => ( + + + {({ state, ...itemConfig }) => ( + + )} + + +); + +const renderPagination = ( position, query, config = {}, action, thunk, styles = {} ) => ( + + + {(item, paginationProps) => ( + + )} + + +); + +const getExtraBodyRowProps = (data, columns) => ( + columns.reduce((result = {}, { name, extraData }) => { + result[name] = extraData + ? isArray(extraData) + ? extraData.reduce((edResult, dataKey) => { + if(isArray(dataKey)) { + edResult[dataKey[1] || dataKey[0]] = _.get(data, dataKey[0]); + } else { + edResult[dataKey] = _.get(data, dataKey); + } + + return edResult; + }, {}) + : { [extraData]: _.get(data, extraData) } + : {} + return result; + }, {}) +) + +const renderTable = ({ + action, + bodyExtraData = {}, + columns = [], + height, + items, + query = {}, + rowHeight, + tableHeader, + tableBody, + thunk, + top, + visibleHeight, + width, + widthAdjustment = 1, + styles = {} +}) => ( + + + + + {({ sortable, width, textAlign, name, label }, index) => { + const { sort, dir } = query; + return ( + + { label } + { sortable && sort === name && } + + ) + }} + + + {({ filterable, type, width, ...rest }, index) => { + const { name } = rest; + const value = _.get(query, ['search', name, 'value']); + return ( + + { filterable && + action(SET_FILTER)({ key, filter })} + { ...rest } + /> + } + + ) + }} + + + visibleHeight ? visibleHeight : height}px` } + innerHeight={ height } + data={ items } + startTop={ top } + visibleHeight={ visibleHeight } + rowHeight={ rowHeight } + styles={ getStyles(styles, 'tbody') } + > + {({ item, top, index: rowIndex }) => ( + + {(column, index) => { + const { width, textAlign, name, type } = column; + return ( + + + + + + ); + }} + + )} + + + +) + +const ReduxDatatable = ( props ) => { + const { config = {}, reducerName, tableData, action, thunk, loadData } = props; + const [ visibleColumns, dispatch ] = useReducer(columnsReducer, _.range(config.columns.length)); + const { toolbar = [], pagination = {}, filterable, headers, height, rowHeight, styles = {}, columns } = config; + const tableConfig = { + ...config, + updateTableState: dispatch, + visibleColumns: visibleColumns.reduce((result, currentIndex) => [ ...result, columns[currentIndex]], []) + }; + + useEffect(() => { + loadData(); + }, []); + + // Handle Table scrolling + let tableHeader = useRef({}); + let tableBody = useRef({}); + const [ top, pointerEvents ] = useTableScroll(tableBody, tableHeader); + // Set min-width of the table + const [ minWidth ] = useState(calculateWidth(columns)); + // Handle table columns and width + const [ width, widthAdjustment ] = useTableWidth(tableBody, minWidth, tableConfig.visibleColumns); + + if(!tableData) { return ( - - - - - } /> - - - } - /> - - - - } - /> - - - ) + + + The table failed to initialise. Please check you are connected to the internet and try again. + + + ); } + + const { query } = tableData; + return ( + + + { renderToolbar(toolbar, action, thunk, dispatch, columns, visibleColumns, styles.toolbar) } + { renderPagination('top', query, pagination, action, thunk, styles.pagination) } + { renderTable({ + action, + bodyExtraData: getExtraBodyRowProps(tableData, tableConfig.visibleColumns), + columns: tableConfig.visibleColumns, + height: rowHeight * ( tableData.items || [] ).length, + items: tableData.items || [], + pointerEvents, + query, + rowHeight, + tableHeader, + tableBody, + thunk, + top, + visibleHeight: height, + width, + widthAdjustment, + styles + }) } + { renderPagination('bottom', query, pagination, action, thunk, styles.pagination) } + + + ); } const prepareActionPayload = ({ reducerName, config: { name, routes, entity }}) => @@ -136,7 +349,7 @@ const mapDispatchToProps = ( dispatch, ownProps ) => { return { loadData: ( ) => { dispatch(setPage(preparePayload({ page: 1 }))) - dispatch(setLimit(preparePayload({ limit: getLimiter(ownProps.config.pagination.items).default || 10 }))) + dispatch(setLimit(preparePayload({ limit: ownProps.config.pagination.items.limiter.default || 10 }))) dispatch(setSort(preparePayload({ dir: 'desc' }))) }, action: ( type ) => ( payload ) => dispatch(createActionCreator(type)(preparePayload(payload))), diff --git a/src/css/table.css b/src/css/table.css deleted file mode 100755 index 2ac28d4..0000000 --- a/src/css/table.css +++ /dev/null @@ -1,196 +0,0 @@ -/* - flutter-react-data-table -*/ - -label.limiter { - display: inline-flex; - line-height: 30px; -} - -label.limiter select { - width: 50%; - margin-right: 10px; -} -.flutter-table-loader { - position: absolute; - top: 0; - bottom: 0; - height: 100%; - width: 100%; - opacity: 0.7; - background: #fff; - display: none; -} - -.flutter-table-loader.show { - display: block; -} - -.flutter-table-container { - position: relative; - display: block; - width: 100%; - overflow: auto; - border: 0; -} - - -/**** new table css ******/ -.flutter-table { - overflow: hidden; - table-layout: fixed; - width: 100%; - /* max-width: 100%; */ - background-color: transparent; - border-collapse: collapse; -} - -.flutter-table-head-container-inner { - border-bottom: 1px solid #ddd; -} - -.flutter-table-head-container { - overflow: hidden; - background: #f4f5f8; -} - -.flutter-table-body-container { - overflow: auto; - position: relative; -} - -.flutter-table-head-row, -.flutter-table-body-row, -.flutter-table-body-container.col-xs-12 { - clear: both; -} - -.flutter-table-body-row { - position: absolute; - left: 0; - border-bottom: 1px solid #ddd; -} - -.flutter-table-row-item { - float: left; - padding: 10px 5px; - overflow: hidden; - text-align: left; - text-overflow: ellipsis; - white-space: nowrap; - vertical-align: middle; -} - -.flutter-table-body-row .flutter-table-row-item { - height: 40px; -} - -.flutter-table-head-row.headers .flutter-table-row-item { - font-weight: bold; - text-align: center; - border-bottom: none !important; - cursor: pointer; - color: #167495; - border-top: none; - vertical-align: bottom; -} - -.flutter-table-header-item.sortable a { - white-space: nowrap; - cursor: pointer; - text-decoration: none; -} - -.flutter-table-head-row.headers .flutter-table-row-item button { - display: block; - border: none; - background: none; - padding: 0; -} - -.flutter-table.table-striped .flutter-table-body-row.odd { - background-color: #f9f9f9; -} - -.flutter-table .sort-caret { - display: inline-block; - width: 0; - height: 0; - margin-left: 0.3em; - border: 0; - content: ""; - border-right: 4px solid transparent; - border-left: 4px solid transparent; -} - -.flutter-table .asc .sort-caret { - vertical-align: baseline; - border-top: none; - border-bottom: 4px solid #000000; -} - -.flutter-table .desc .sort-caret { - vertical-align: super; - border-top: 4px solid #000000; - border-bottom: none; -} - -.flutter-table .string-cell, -.flutter-table .uri-cell, -.flutter-table .email-cell { - text-align: left; -} - -.flutter-table .date-cell, -.flutter-table .time-cell, -.flutter-table .datetime-cell, -.flutter-table .number-cell, -.flutter-table .integer-cell, -.flutter-table .percent-cell { - text-align: right; -} - -.flutter-table .boolean-cell { - text-align: center; -} - -.flutter-table .select-cell { - text-align: center; -} - -.flutter-table .flutter-table-body-row .flutter-table-row-item .badge { - width: 100% -} - -/* .flutter-table .flutter-table-row-item > input { - min-width: 100px; -} */ - -.flutter-table-body thead .headers { - visibility: collapse; -} - -/* .flutter-table-body { - overflow-y: scroll; -} */ - -.flutter-table-body-inner { - position: relative; -} - -.flutter-table-body-inner > table { - position: absolute; -} - -.flutter-table-outer-container .active-columns .dropdown-menu .dropdown-item { - padding: 5px 10px; -} - -.flutter-table-outer-container .active-columns .dropdown-menu .dropdown-item > input { - margin-right: 10px; -} - -.page-item.active .page-link { - background-color: #167495; - border-color: #167495; -} diff --git a/src/index.js b/src/index.js index cb84270..5026327 100755 --- a/src/index.js +++ b/src/index.js @@ -4,7 +4,6 @@ import * as epics from './epics'; import TableProvider, { TableConsumer, withTableConfig } from './TableProvider'; import * as actions from './actions'; import * as utils from './utils'; -import './css/table.css'; export { TableProvider, TableConsumer, withTableConfig, actions, utils, reducer, epics } export default ReduxDatatable; diff --git a/src/utils.js b/src/utils.js index 3bcd465..c8d561c 100755 --- a/src/utils.js +++ b/src/utils.js @@ -1,6 +1,5 @@ import qs from 'query-string'; import _ from 'lodash'; -import { TYPE_LIMITER } from './Datatable/Pagination/Limiter'; export const defaultLimiterCongig = { options: [10, 20, 50, 100, 200], @@ -9,44 +8,8 @@ export const defaultLimiterCongig = { export const isArray = (value) => Array.isArray(value); -export const getLimiter = ( items ) => items.find(({ type }) => type === TYPE_LIMITER); - -export const calculatePaginationProps = ( - { page, limit = 0, count = 0 }, - defaultLimit = 10 -) => { - page = page > 1 ? page : 1 - limit = limit != 0 ? limit : defaultLimit; - - let start = (page - 1) * limit - let end = start + limit - 1 - - return { - page: page, - start: start, - end: (count > end && end >= 0) ? end : count, - count: count, - limit: limit, - total: Math.ceil(count / limit) - } -} - -// export const isSelectionEmpty = (obj) => { -// for(var key in obj) { -// if(obj.hasOwnProperty(key)) -// return false; -// } -// -// return true; -// } - export const getUrl = ( baseUrl, endpoint ) => baseUrl + endpoint; -// export const pushRoute = ( action, params, context ) => context.router.history.push({ -// pathname: action.route, -// search: '?' + params.toString() -// }); - export const getSelectedKeys = ( data, dataKey ) => { if( !data[dataKey] ) { return false; @@ -103,9 +66,6 @@ export const paramsResolver = ( params, data ) => { return paramsObject; } -// export const getValueByPath = ( obj, path ) => -// path.reduce((acc, currVal) => (acc && acc[currVal]) ? acc[currVal] : null, obj); - export const createActionCreator = ( type ) => ( data ) => { const { name, reducerName, routes, entity, payload } = data; let action = ({ type, meta: { name, routes, reducerName, entity }, payload }); @@ -114,12 +74,22 @@ export const createActionCreator = ( type ) => ( data ) => { return action; } -export const createReducer = (reducer, predicate) => (state, action) => +export const createReducer = (reducer, predicate) => (state, action) => ( predicate(action) || state === undefined ? reducer(state, action) : state +); export const prepareActionPayload = ({ name, reducerName, routes, entity }) => ( payload = {} ) => ({ name, reducerName, routes, entity, payload }) -export const shouldUpdate = ( currentData, nextData, index ) => +export const shouldUpdate = ( currentData, nextData, index ) => ( _.get(currentData, index, '') != _.get(nextData, index, '') +) + +export const getStyles = ( styles, name ) => ( + name && styles ? styles[name] : undefined +) + +export const getExtendedStyles = ( name ) => ({ styles }) => ( + name ? styles ? styles[name] : undefined : styles +)
- The table failed to initialise. Please check you are connected to the internet and try again. -
+ The table failed to initialise. Please check you are connected to the internet and try again. +