├── .babelrc ├── .eslintrc ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── changelog.md ├── example ├── components │ ├── accordion.js │ ├── filepicker.js │ └── pager.js ├── index.html ├── index.js ├── root.js └── style.scss ├── index.d.ts ├── lib ├── _config.scss ├── accordion │ ├── _config.scss │ ├── accordion.js │ ├── chord.js │ ├── index.d.ts │ ├── index.js │ └── theme.scss ├── filepicker │ ├── _config.scss │ ├── filepicker.js │ ├── index.d.ts │ ├── index.js │ └── theme.scss ├── identifiers.js ├── index.js └── pager │ ├── _config.scss │ ├── index.d.ts │ ├── index.js │ ├── page.js │ ├── pager.js │ └── theme.scss ├── package.json ├── screenshots └── rta.gif ├── server.js ├── src ├── _config.scss ├── accordion │ ├── _config.scss │ ├── accordion.js │ ├── chord.js │ ├── index.d.ts │ ├── index.js │ ├── readme.md │ └── theme.scss ├── filepicker │ ├── _config.scss │ ├── filepicker.js │ ├── index.d.ts │ ├── index.js │ ├── readme.md │ └── theme.scss ├── identifiers.js ├── index.js └── pager │ ├── _config.scss │ ├── index.d.ts │ ├── index.js │ ├── page.js │ ├── pager.js │ ├── readme.md │ └── theme.scss └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-0", "react"] 3 | } -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "node": true, 5 | "mocha": true, 6 | "es6": true 7 | }, 8 | 9 | "ecmaFeatures": { 10 | "jsx": true, 11 | "templateStrings": true, 12 | "superInFunctions": false, 13 | "classes": true, 14 | "modules": true 15 | }, 16 | 17 | "parser": "babel-eslint", 18 | 19 | "plugins": [ 20 | "babel", 21 | "react" 22 | ], 23 | 24 | "rules": { 25 | "block-scoped-var": [0], 26 | "brace-style": [2, "1tbs", { 27 | "allowSingleLine": true 28 | }], 29 | "camelcase": [0], 30 | "comma-dangle": [2, "never"], 31 | "comma-spacing": [2], 32 | "comma-style": [2, "last"], 33 | "complexity": [0, 11], 34 | "constructor-super": [2], 35 | "consistent-return": [0], 36 | "consistent-this": [0, "that"], 37 | "curly": [2, "multi-line"], 38 | "default-case": [2], 39 | "dot-notation": [2, { 40 | "allowKeywords": true 41 | }], 42 | "eol-last": [2], 43 | "eqeqeq": [2], 44 | "func-names": [0], 45 | "func-style": [0, "declaration"], 46 | "generator-star-spacing": [2, "after"], 47 | "guard-for-in": [0], 48 | "handle-callback-err": [0], 49 | "key-spacing": [2, { 50 | "beforeColon": false, 51 | "afterColon": true 52 | }], 53 | "quotes": [2, "single", "avoid-escape"], 54 | "max-depth": [0, 4], 55 | "max-len": [0, 80, 4], 56 | "max-nested-callbacks": [0, 2], 57 | "max-params": [0, 3], 58 | "max-statements": [0, 10], 59 | "new-parens": [2], 60 | "new-cap": [0], 61 | "newline-after-var": [0], 62 | "no-alert": [2], 63 | "no-array-constructor": [2], 64 | "no-bitwise": [0], 65 | "no-caller": [2], 66 | "no-catch-shadow": [2], 67 | "no-cond-assign": [2], 68 | "no-console": [0], 69 | "no-constant-condition": [1], 70 | "no-continue": [2], 71 | "no-control-regex": [2], 72 | "no-debugger": [2], 73 | "no-delete-var": [2], 74 | "no-div-regex": [0], 75 | "no-dupe-args": [2], 76 | "no-dupe-keys": [2], 77 | "no-duplicate-case": [2], 78 | "no-else-return": [0], 79 | "no-empty": [2], 80 | "no-empty-character-class": [2], 81 | "no-eq-null": [0], 82 | "no-eval": [2], 83 | "no-ex-assign": [2], 84 | "no-extend-native": [1], 85 | "no-extra-bind": [2], 86 | "no-extra-boolean-cast": [2], 87 | "no-extra-parens": [0], 88 | "no-extra-semi": [1], 89 | "no-fallthrough": [2], 90 | "no-floating-decimal": [2], 91 | "no-func-assign": [2], 92 | "no-implied-eval": [2], 93 | "no-inline-comments": [0], 94 | "no-inner-declarations": [2, "functions"], 95 | "no-invalid-regexp": [2], 96 | "no-irregular-whitespace": [2], 97 | "no-iterator": [2], 98 | "no-label-var": [2], 99 | "no-labels": [2], 100 | "no-lone-blocks": [2], 101 | "no-lonely-if": [2], 102 | "no-loop-func": [2], 103 | "no-mixed-requires": [0, false], 104 | "no-mixed-spaces-and-tabs": [2, false], 105 | "no-multi-spaces": [2], 106 | "no-multi-str": [2], 107 | "no-multiple-empty-lines": [2, { 108 | "max": 2 109 | }], 110 | "no-native-reassign": [1], 111 | "no-negated-in-lhs": [2], 112 | "no-nested-ternary": [0], 113 | "no-new": [2], 114 | "no-new-func": [2], 115 | "no-new-object": [2], 116 | "no-new-require": [0], 117 | "no-new-wrappers": [2], 118 | "no-obj-calls": [2], 119 | "no-octal": [2], 120 | "no-octal-escape": [2], 121 | "no-path-concat": [0], 122 | "no-param-reassign": [2], 123 | "no-plusplus": [0], 124 | "no-process-env": [0], 125 | "no-process-exit": [2], 126 | "no-proto": [2], 127 | "no-redeclare": [2], 128 | "no-regex-spaces": [2], 129 | "no-reserved-keys": [0], 130 | "no-restricted-modules": [0], 131 | "no-return-assign": [2], 132 | "no-script-url": [2], 133 | "no-self-compare": [0], 134 | "no-sequences": [2], 135 | "no-shadow": [2], 136 | "no-shadow-restricted-names": [2], 137 | "semi-spacing": [2], 138 | "no-spaced-func": [2], 139 | "no-sparse-arrays": [2], 140 | "no-sync": [0], 141 | "no-ternary": [0], 142 | "no-this-before-super": [2], 143 | "no-throw-literal": [2], 144 | "no-trailing-spaces": [2], 145 | "no-undef": [2], 146 | "no-undef-init": [2], 147 | "no-undefined": [0], 148 | "no-underscore-dangle": [0], 149 | "no-unreachable": [2], 150 | "no-unused-expressions": [2, { 151 | "allowShortCircuit": true 152 | }], 153 | "no-unused-vars": [1, { 154 | "vars": "all", 155 | "args": "after-used" 156 | }], 157 | "no-use-before-define": [2, "nofunc"], 158 | "no-var": [2], 159 | "no-void": [0], 160 | "no-warning-comments": [0, { 161 | "terms": ["todo", "fixme", "xxx"], 162 | "location": "start" 163 | }], 164 | "no-with": [2], 165 | "one-var": [0], 166 | "operator-assignment": [0, "always"], 167 | "operator-linebreak": [2, "before"], 168 | "padded-blocks": [0], 169 | "prefer-const": [2], 170 | "prefer-spread": [2], 171 | "quote-props": [0], 172 | "radix": [0], 173 | "semi": [2], 174 | "sort-vars": [0], 175 | "keyword-spacing": [2, {"after": true}], 176 | "space-before-function-paren": [2, { "anonymous": "always", "named": "always" }], 177 | "space-before-blocks": [0, "always"], 178 | "space-in-brackets": [0, "never", { 179 | "singleValue": true, 180 | "arraysInArrays": false, 181 | "arraysInObjects": false, 182 | "objectsInArrays": true, 183 | "objectsInObjects": true, 184 | "propertyName": false 185 | }], 186 | "space-in-parens": [2, "never"], 187 | "space-infix-ops": [2], 188 | "space-unary-ops": [2, { 189 | "words": true, 190 | "nonwords": false 191 | }], 192 | "spaced-line-comment": [0, "always"], 193 | "strict": [1], 194 | "use-isnan": [2], 195 | "valid-jsdoc": [0], 196 | "valid-typeof": [2], 197 | "vars-on-top": [0], 198 | "wrap-iife": [2], 199 | "wrap-regex": [2], 200 | "yoda": [2, "never", { 201 | "exceptRange": true 202 | }], 203 | "babel/object-shorthand": [2], 204 | "react/display-name": 0, 205 | "react/jsx-boolean-value": 1, 206 | "react/jsx-closing-bracket-location": 0, 207 | "react/jsx-curly-spacing": 1, 208 | "react/jsx-max-props-per-line": 0, 209 | "react/jsx-indent-props": 0, 210 | "react/jsx-no-duplicate-props": 1, 211 | "react/jsx-no-undef": 1, 212 | "react/jsx-pascal-case": 1, 213 | "react/sort-prop-types": 1, 214 | "react/jsx-sort-props": 0, 215 | "react/jsx-uses-react": 1, 216 | "react/jsx-uses-vars": 1, 217 | "react/no-danger": 0, 218 | "react/no-did-mount-set-state": 0, 219 | "react/no-did-update-set-state": 1, 220 | "react/no-multi-comp": 0, 221 | "react/no-unknown-property": 1, 222 | "react/prop-types": [2, {"ignore": ["onMouseDown", "onTouchStart"]}], 223 | "react/react-in-jsx-scope": 1, 224 | "react/self-closing-comp": 1, 225 | "react/sort-comp": 1 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | node_modules 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .*rc 2 | .gitignore 3 | server.js 4 | webpack.config.* 5 | example 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 react-toolbox-additions author 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Additional components for react-toolbox components library 2 | 3 | [![npm version](https://img.shields.io/badge/npm-v1.3.9-blue.svg?style=flat-square)](https://www.npmjs.org/package/react-toolbox-additions) 4 | 5 | The project provides additions for react-toolbox library (http://react-toolbox.com/), 6 | which are not defined at material design and therefore were not included to original react-toolbox components library, 7 | but it could be useful to have. 8 | 9 | ## Installation 10 | 11 | React Toolbox can be installed as an [npm package](https://www.npmjs.org/package/react-toolbox-additions): 12 | 13 | ```bash 14 | $ npm install react-toolbox-additions 15 | ``` 16 | ## Prerequisites 17 | 18 | React Toolbox Additions require uses [react-toolbox](https://www.npmjs.com/package/react-toolbox) components library and [react](https://www.npmjs.com/package/react). 19 | 20 | Like react-toolbox authors I also would recommend [webpack](https://webpack.github.io/) to use module bundler. 21 | 22 | The components can be customized via themes by using [react-css-themr](https://github.com/javivelasco/react-css-themr) which is also used by React Toolbox to make component easily themeable. 23 | 24 | ## Basic usage 25 | 26 | Examples how to use. 27 | 28 | Pager: 29 | 30 | ```js 31 | import Pager from 'react-toolbox-additions/lib/pager'; 32 | import FontIcon from 'react-toolbox-additions/lib/font_icon'; 33 | 34 | const PagerTest = () => { 35 | 36 | onPageChange = (newPage, oldPage) => { 37 | console.info('Selected page : ' + newPage + ', Previous page: ' + oldPage); 38 | } 39 | 40 | return ( 41 | )} 43 | nextButtonLabel={()} 44 | rangeLeftButtonLabel={()} 45 | rangeRightButtonLabel={()} 46 | totalPages={29} 47 | currentPage={5} 48 | visiblePagesBlockSize={3} 49 | onPageChange={onPageChange} 50 | /> 51 | ); 52 | }; 53 | ``` 54 | 55 | Accordion: 56 | 57 | ```jsx 58 | import React, { PropTypes } from 'react'; 59 | import FontIcon from 'react-toolbox/lib/font_icon'; 60 | import Input from 'react-toolbox/lib/input'; 61 | import {Button} from 'react-toolbox/lib/button'; 62 | import { ListCheckbox, ListSubHeader, List, ListItem, ListDivider, ListItemText, ListItemContent } from 'react-toolbox/lib/list'; 63 | 64 | import { Accordion, Chord } from 'react-toolbox-additions/lib/accordion'; 65 | import style from '../style'; 66 | 67 | class AccordionTest extends React.Component { 68 | 69 | static propTypes = { 70 | }; 71 | 72 | static defaultProps = { 73 | } 74 | 75 | state = { 76 | index: 0 77 | } 78 | 79 | onChange = (idx) => { 80 | this.setState({ 81 | index: idx 82 | }); 83 | } 84 | 85 | onActive1 = () => { 86 | console.info('Selected chord: ' + this.state.index); 87 | } 88 | 89 | onActive2 = () => { 90 | console.info('Selected chord: ' + this.state.index); 91 | } 92 | 93 | onActive3 = () => { 94 | console.info('Selected chord: ' + this.state.index); 95 | } 96 | 97 | render () { 98 | 99 | return ( 100 | 105 | 109 |
110 | The project provides additions for react-toolbox which are not defined at material design and therefore were not included to original react-toolbox components library but it could be useful to have. 111 |
112 |
113 | } 115 | label='Number Two' 116 | onActive={this.onActive2} 117 | > 118 |
119 | 124 | 125 |
129 | 130 |
131 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 |
146 | ); 147 | } 148 | } 149 | ``` 150 | 151 | Also important notice from authors of react-toolbox: 152 | "Take into account that any required style will be included in the final CSS so your final would include `Button` styles in this case. It's more convenient to import components this way (or with raw imports) because if you require from the project root, every stylesheet of React Toolbox will be included, even if you don't use it." 153 | 154 | ## Importing components 155 | 156 | The project inherits the same style of component structures that is in react-toolbox project, what makes it easy to use for everybody who is familiar with react-toolbox components structure. 157 | ``` 158 | |- /pager 159 | |---- pager.js 160 | |---- _config.scss 161 | |---- index.js 162 | |---- readme.md 163 | |---- theme.scss 164 | ``` 165 | 166 | ### Bundled component 167 | 168 | ```js 169 | import { Pager } from 'react-toolbox-additions/lib/pager'; 170 | import { Accordion, Chord } from 'react-toolbox-additions/lib/accordion'; 171 | import { FilePicker } from 'react-toolbox-additions/lib/filepicker'; 172 | ``` 173 | 174 | ## Screenshots 175 | 176 |

177 | 178 |

179 | 180 | ## Authors and Contributors 181 | 182 | The project is being initially developed and maintained by [Max Komlev](https://github.com/MaximKomlev). 183 | 184 | Steps to build project and run example locally. 185 | 186 | ``` 187 | $ git clone https://github.com/MaximKomlev/react-toolbox-additions.git 188 | $ npm install 189 | $ npm run build 190 | $ npm start 191 | ``` 192 | 193 | Local example will be available at `http://localhost:8000/`. 194 | 195 | ## Components in bundle 196 | 197 | Currently the library implements Pager, FilePicker and Accordion components. 198 | 199 | ## Changes 200 | 201 | [The log of changes](https://github.com/MaximKomlev/react-toolbox-additions/blob/master/changelog.md). 202 | 203 | ## License 204 | 205 | This project is licensed under the terms of the [MIT license](https://github.com/MaximKomlev/react-toolbox-additions/blob/master/LICENSE). 206 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | ## [Release 1.3.9] - 01/05/2016 5 | ### Updated 6 | - Fixed typing at readme. 7 | 8 | ## [Release 1.3.8] - 01/04/2016 9 | ### Updated 10 | - Fixed misversioning. 11 | 12 | ## [Release 1.3.7] - 02/04/2016 13 | ### Updated 14 | - To follow base project (react-toolbox). 15 | 16 | ## [Release 1.3.6] - 12/14/2016 17 | ### Updated 18 | - Exported components. 19 | 20 | ## [Release 1.3.5] - 12/14/2016 21 | ### Updated 22 | - Dependencies. 23 | 24 | ## [Release 1.3.4] - 12/14/2016 25 | ### Added 26 | - Chord icon and postIcon customization. 27 | 28 | ## [Release 1.3.3] - 12/06/2016 29 | ### Fixed 30 | - Chord resizing and content growing. 31 | 32 | ## [Release 1.3.2] - 12/05/2016 33 | ### Fixed 34 | - Chord icon styling. 35 | 36 | ## [Release 1.3.1] - 12/05/2016 37 | ### Fixed 38 | - Component resizing. 39 | 40 | ### Updated 41 | - Sample. 42 | 43 | ### Changed 44 | - labelIcon and labelPostIcon properties to take value as component or string. 45 | 46 | ## [Release 1.3.0] - 12/05/2016 47 | ### Added 48 | - Initial version of Accordion component. 49 | 50 | ## [Release 1.2.4] - 11/29/2016 51 | ### Added 52 | - Change log at readme. 53 | 54 | ## [Release 1.2.3] - 11/29/2016 55 | ### Added 56 | - Styling for first and last pages buttons. 57 | 58 | ### Fixed 59 | - Adjasment of buttons number according to visibleBlockSize. 60 | 61 | ## [Release 1.2.2] - 11/28/2016 62 | ### Added 63 | - Missed file. 64 | 65 | ## [Release 1.2.1] - 11/28/2016 66 | ### Added 67 | - More examples of pager to use. 68 | 69 | ### Fixed 70 | - Fixed runtime react warning on filepicker component. 71 | - Fixed ripple appearing on page button click. 72 | 73 | ### Changed 74 | - Updated default layout of Pager component from flex to inline-block. 75 | - Updated default layout of FilePicker component from flex to inline-block. 76 | 77 | ## [Release 1.1.2] - 11/16/2016 78 | ### Changed 79 | - Changed FilePicker Public Interface to provide more flexible way to customize/change behaviour or view of component. 80 | - Updated Sample of use. 81 | 82 | ## [Release 1.0.1] - 10/30/2016 83 | ### Added 84 | - Initial Version. 85 | -------------------------------------------------------------------------------- /example/components/accordion.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | import FontIcon from 'react-toolbox/lib/font_icon'; 3 | import Input from 'react-toolbox/lib/input'; 4 | import {Button} from 'react-toolbox/lib/button'; 5 | import { ListCheckbox, ListSubHeader, List, ListItem, ListDivider, ListItemText, ListItemContent } from 'react-toolbox/lib/list'; 6 | 7 | import {Accordion, Chord} from '../../lib/accordion'; 8 | import style from '../style'; 9 | 10 | class AccordionTest extends React.Component { 11 | 12 | static propTypes = { 13 | }; 14 | 15 | static defaultProps = { 16 | } 17 | 18 | state = { 19 | index1: 0, 20 | index2: 0 21 | } 22 | 23 | onChange1 = (idx) => { 24 | this.setState({ 25 | index1: idx 26 | }); 27 | } 28 | 29 | onChange2 = (idx) => { 30 | this.setState({ 31 | index2: idx 32 | }); 33 | } 34 | 35 | onActive1 = () => { 36 | console.info('Selected chord: ' + this.state.index1); 37 | } 38 | 39 | onActive2 = () => { 40 | console.info('Selected chord: ' + this.state.index1); 41 | } 42 | 43 | onActive3 = () => { 44 | console.info('Selected chord: ' + this.state.index1); 45 | } 46 | 47 | render () { 48 | 49 | return ( 50 |
51 |
Accordion
52 |

Accordion component.

53 | 54 |
55 | 60 | 64 |
65 | The project provides additions for react-toolbox which are not defined at material design and therefore were not included to original react-toolbox components library but it could be useful to have. 66 |
67 |
68 | } 70 | label='Number Two' 71 | onActive={this.onActive2} 72 | > 73 |
74 | 79 | 80 |
84 | 85 |
86 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 |
101 |
102 | 103 | 104 |

Themed accordion component.

105 | 106 |
107 | 113 | 116 |
117 | The project provides additions for react-toolbox which are not defined at material design and therefore were not included to original react-toolbox components library but it could be useful to have. 118 |
119 |
120 | } 122 | label='Number Two' 123 | > 124 | 125 | 130 | 131 |
151 |
152 | ); 153 | } 154 | } 155 | 156 | export default AccordionTest; 157 | -------------------------------------------------------------------------------- /example/components/filepicker.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | 3 | import FilePicker from '../../lib/filepicker'; 4 | import style from '../style'; 5 | 6 | class FilePickerTest extends React.Component { 7 | 8 | state = { 9 | } 10 | 11 | onChange1 (fobj, fname) { 12 | console.info('Selected file1 : ' + fname); 13 | } 14 | 15 | onChange2 (fobj, fname) { 16 | console.info('Selected file2 : ' + fname); 17 | } 18 | 19 | onChange3 (fobj, fname) { 20 | console.info('Selected file3 : ' + fname); 21 | } 22 | 23 | onChange4 (fobj, fname) { 24 | console.info('Selected file4 : ' + fname); 25 | } 26 | 27 | render () { 28 | 29 | return ( 30 |
31 |
FilePicker
32 |

FilePicker based on ToolBox components.

33 | 34 |
35 | 36 | 41 | 42 | 47 | 48 | 54 | 55 | 59 | 60 |
61 | 62 |
63 | ); 64 | } 65 | } 66 | 67 | export default FilePickerTest; 68 | -------------------------------------------------------------------------------- /example/components/pager.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | import FontIcon from 'react-toolbox/lib/font_icon'; 3 | import Input from 'react-toolbox/lib/input'; 4 | 5 | import Pager from '../../lib/pager'; 6 | import style from '../style'; 7 | 8 | class PagerTest extends React.Component { 9 | 10 | static propTypes = { 11 | visiblePagesBlockSize: PropTypes.number 12 | }; 13 | 14 | static defaultProps = { 15 | visiblePagesBlockSize: 3 16 | } 17 | 18 | state = { 19 | totalPages: 29, 20 | currentPage: 5 21 | } 22 | 23 | onInputChange = (name, value) => { 24 | const state = this.state; 25 | 26 | state[name] = value; 27 | 28 | this.setState({ 29 | ...state 30 | }); 31 | } 32 | 33 | onPageChange1 = (newPage, oldPage) => { 34 | console.info('Selected page : ' + newPage + ', Previous page: ' + oldPage); 35 | } 36 | 37 | onPageChange2 = (newPage, oldPage) => { 38 | console.info('Selected page : ' + newPage + ', Previous page: ' + oldPage); 39 | } 40 | 41 | render () { 42 | 43 | return ( 44 |
45 |
Pager
46 |

Pager based on ToolBox components with default button styles. Example: 1.

47 | 48 |
49 | } 51 | nextButtonLabel={} 52 | rangeLeftButtonLabel={} 53 | rangeRightButtonLabel={} 54 | totalPages={this.state.totalPages} 55 | currentPage={this.state.currentPage} 56 | visiblePagesBlockSize={this.props.visiblePagesBlockSize} 57 | onPageChange={this.onPageChange1} 58 | /> 59 |
60 | 61 | 62 | 63 |

Pager based on ToolBox components with custom button styles. Example: 2.

64 | 65 |
66 | } 68 | nextButtonLabel={} 69 | rangeLeftButtonLabel={} 70 | rangeRightButtonLabel={} 71 | leftRightArrowButtonStyles={{flat: true, primary: true}} 72 | leftRightRangeButtonStyles={{primary: true}} 73 | totalPages={this.state.totalPages} 74 | currentPage={this.state.currentPage} 75 | visiblePagesBlockSize={this.props.visiblePagesBlockSize} 76 | onPageChange={this.onPageChange2} 77 | /> 78 |
79 | 80 | 81 | 82 |

Pager based on ToolBox components with custom button styles. Example: 3.

83 | 84 |
85 | } 87 | nextButtonLabel={} 88 | rangeLeftButtonLabel={} 89 | rangeRightButtonLabel={} 90 | leftRightArrowButtonStyles={{ primary: true, raised: true }} 91 | leftRightRangeButtonStyles={{ raised: true}} 92 | pagesButtonStyles={{raised: true }} 93 | firstLastPagesButtonStyles={{ raised: true}} 94 | totalPages={this.state.totalPages} 95 | currentPage={this.state.currentPage} 96 | visiblePagesBlockSize={this.props.visiblePagesBlockSize} 97 | onPageChange={this.onPageChange2} 98 | /> 99 |
100 | 101 | 102 | 103 |

Pager based on ToolBox components with custom theme. Example: 4.

104 | 105 |
106 | } 108 | nextButtonLabel={} 109 | rangeLeftButtonLabel={} 110 | rangeRightButtonLabel={} 111 | leftRightArrowButtonStyles={{ primary: true, raised: true }} 112 | leftRightRangeButtonStyles={{ raised: true}} 113 | pagesButtonStyles={{raised: true }} 114 | firstLastPagesButtonStyles={{ raised: true}} 115 | totalPages={this.state.totalPages} 116 | currentPage={this.state.currentPage} 117 | visiblePagesBlockSize={this.props.visiblePagesBlockSize} 118 | onPageChange={this.onPageChange2} 119 | theme={style} 120 | /> 121 |
122 | 123 | 124 | 125 | 126 |

Pager based on ToolBox components with custom theme. Example: 5.

127 | 128 |
129 | } 131 | nextButtonLabel={} 132 | rangeLeftButtonLabel={} 133 | rangeRightButtonLabel={} 134 | leftRightArrowButtonStyles={{ primary: true}} 135 | firstLastPagesButtonStyles={{ raised: true}} 136 | totalPages={this.state.totalPages} 137 | currentPage={this.state.currentPage} 138 | visiblePagesBlockSize={this.props.visiblePagesBlockSize} 139 | onPageChange={this.onPageChange2} 140 | theme={style} 141 | /> 142 |
143 | 144 | 145 | 150 | 151 |
152 | ); 153 | } 154 | } 155 | 156 | export default PagerTest; 157 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Additions for React Toolbox library 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | import 'babel-polyfill'; 2 | 3 | import React from 'react'; 4 | import ReactDOM from 'react-dom'; 5 | import Root from './root'; 6 | 7 | ReactDOM.render(, document.getElementById('app-root')); 8 | -------------------------------------------------------------------------------- /example/root.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import {Card} from 'react-toolbox/lib/card'; 4 | 5 | import Pager from './components/pager'; 6 | import FilePicker from './components/filepicker'; 7 | import Accordion from './components/accordion'; 8 | import style from './style'; 9 | 10 | const Root = () => ( 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | ); 23 | 24 | export default Root; 25 | -------------------------------------------------------------------------------- /example/style.scss: -------------------------------------------------------------------------------- 1 | $unit: 1rem !default; 2 | 3 | .card { 4 | 5 | box-sizing: border-box; 6 | padding: $unit * 2; 7 | margin-top: $unit * 0.75; 8 | background-color: #fff; 9 | border: 1px solid #f4f4f4; 10 | 11 | .pager { 12 | display: flex; 13 | flex-flow: row nowrap; 14 | justify-content: center; 15 | margin: 1rem 0 1.5rem 0; 16 | 17 | .active { 18 | text-decoration: none; 19 | color: #3f51b5; 20 | } 21 | 22 | .leftRightArrowButton{ 23 | display: none; 24 | } 25 | } 26 | 27 | .accordion { 28 | 29 | .chord { 30 | 31 | border: 1px solid #f4f4f4; 32 | 33 | .label { 34 | border: none; 35 | } 36 | 37 | &.active { 38 | .label { 39 | border-bottom: 1px solid #f4f4f4; 40 | } 41 | } 42 | 43 | .content { 44 | margin-left: 10; 45 | margin-right: 10; 46 | } 47 | } 48 | 49 | } 50 | 51 | .accordion-body1 { 52 | display: flex; 53 | flex-flow: row nowrap; 54 | justify-content: space-around; 55 | 56 | > * { 57 | flex: 1; 58 | } 59 | } 60 | 61 | .accordion-body2 { 62 | padding: 1rem; 63 | padding-top: 0; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for react-toolbox-additions 2 | 3 | import { Pager } from "react-toolbox-additions/lib/pager"; 4 | import { FilePicker } from "react-toolbox-additions/lib/filepicker"; 5 | 6 | import { PagerTheme } from "react-toolbox-additions/lib/pager"; 7 | import { FilePickerTheme } from "react-toolbox-additions/lib/filepicker"; 8 | 9 | export { 10 | Peger, 11 | Autocomplete, 12 | 13 | PagerTheme, 14 | FilePickerTheme 15 | } 16 | -------------------------------------------------------------------------------- /lib/_config.scss: -------------------------------------------------------------------------------- 1 | @import "~react-toolbox/lib/commons"; 2 | 3 | $indent: $unit *.5 !default; 4 | $button-margin: 0 $indent 0 0 !default; 5 | 6 | @mixin space-between { 7 | margin-right: $indent; 8 | } 9 | -------------------------------------------------------------------------------- /lib/accordion/_config.scss: -------------------------------------------------------------------------------- 1 | @import "~react-toolbox/lib/button/theme"; 2 | @import "../config"; 3 | 4 | $chord-content-padding: 1 * $unit !default; 5 | $chord-label-disabled-opacity: .2 !default; 6 | $chord-label-v-padding: 0.2 * $unit !default; 7 | $chord-label-height: 4.8 * $unit !default; 8 | $chord-icon-height: 2.4 * $unit !default; 9 | $chord-text-height: 1.4 * $unit !default; 10 | $chord-pointer-color: $color-primary !default; 11 | $chord-text: $color-black !default; 12 | $chord-text-color: $chord-text !default; 13 | $chord-text-inactive-color: rgba($chord-text, .7) !default; 14 | -------------------------------------------------------------------------------- /lib/accordion/accordion.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.Accordion = exports.accordionFactory = undefined; 7 | 8 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 9 | 10 | var _react = require('react'); 11 | 12 | var _react2 = _interopRequireDefault(_react); 13 | 14 | var _classnames2 = require('classnames'); 15 | 16 | var _classnames3 = _interopRequireDefault(_classnames2); 17 | 18 | var _reactCssThemr = require('react-css-themr'); 19 | 20 | var _identifiers = require('../identifiers.js'); 21 | 22 | var _chord = require('./chord.js'); 23 | 24 | var _chord2 = _interopRequireDefault(_chord); 25 | 26 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 27 | 28 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 29 | 30 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 31 | 32 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 33 | 34 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 35 | 36 | var factory = function factory(Chord) { 37 | var Accordion = function (_Component) { 38 | _inherits(Accordion, _Component); 39 | 40 | function Accordion() { 41 | _classCallCheck(this, Accordion); 42 | 43 | return _possibleConstructorReturn(this, (Accordion.__proto__ || Object.getPrototypeOf(Accordion)).apply(this, arguments)); 44 | } 45 | 46 | _createClass(Accordion, [{ 47 | key: 'handleHeaderClick', 48 | 49 | 50 | //events handler 51 | value: function handleHeaderClick(event, idx, item) { 52 | idx = parseInt(idx); 53 | if (this.props.onChange) { 54 | this.props.onChange(idx); 55 | } 56 | if (item.props.onClick) { 57 | item.props.onClick(event); 58 | } 59 | } 60 | }, { 61 | key: 'renderChords', 62 | 63 | 64 | //private methods 65 | value: function renderChords() { 66 | var _this2 = this; 67 | 68 | var chords = []; 69 | var idx = 0; 70 | 71 | _react2.default.Children.forEach(this.props.children, function (item) { 72 | if (item.type === Chord) { 73 | if (item.props.children) { 74 | chords.push(_react2.default.cloneElement(item, { 75 | id: idx, 76 | key: idx, 77 | theme: _this2.props.theme, 78 | active: _this2.props.index === idx, 79 | onClick: _this2.handleHeaderClick.bind(_this2, event, idx, item) 80 | })); 81 | ++idx; 82 | } 83 | } 84 | }); 85 | 86 | return chords; 87 | } 88 | }, { 89 | key: 'render', 90 | value: function render() { 91 | var _props = this.props, 92 | className = _props.className, 93 | disableAnimation = _props.disableAnimation, 94 | theme = _props.theme; 95 | 96 | var classNames = (0, _classnames3.default)(theme.accordion, className, _defineProperty({}, theme.disableAnimation, disableAnimation)); 97 | 98 | return _react2.default.createElement( 99 | 'div', 100 | { 'data-react-toolbox': 'accordion', className: classNames }, 101 | _react2.default.createElement( 102 | 'div', 103 | { className: theme.accordion }, 104 | this.renderChords() 105 | ) 106 | ); 107 | } 108 | }]); 109 | 110 | return Accordion; 111 | }(_react.Component); 112 | 113 | Accordion.propTypes = { 114 | children: _react.PropTypes.node, 115 | className: _react.PropTypes.string, 116 | disableAnimation: _react.PropTypes.bool, 117 | index: _react.PropTypes.number, 118 | onChange: _react.PropTypes.func, 119 | theme: _react.PropTypes.shape({ 120 | accordion: _react.PropTypes.string 121 | }) 122 | }; 123 | Accordion.defaultProps = { 124 | index: 0, 125 | disableAnimation: false 126 | }; 127 | 128 | 129 | return Accordion; 130 | }; 131 | 132 | var Accordion = factory(_chord2.default); 133 | exports.default = (0, _reactCssThemr.themr)(_identifiers.ACCORDION)(Accordion); 134 | exports.accordionFactory = factory; 135 | exports.Accordion = Accordion; -------------------------------------------------------------------------------- /lib/accordion/chord.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.Chord = undefined; 7 | 8 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 9 | 10 | var _react = require('react'); 11 | 12 | var _react2 = _interopRequireDefault(_react); 13 | 14 | var _reactDom = require('react-dom'); 15 | 16 | var _reactDom2 = _interopRequireDefault(_reactDom); 17 | 18 | var _classnames5 = require('classnames'); 19 | 20 | var _classnames6 = _interopRequireDefault(_classnames5); 21 | 22 | var _font_icon = require('react-toolbox/lib/font_icon'); 23 | 24 | var _reactCssThemr = require('react-css-themr'); 25 | 26 | var _identifiers = require('../identifiers.js'); 27 | 28 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 29 | 30 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 31 | 32 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 33 | 34 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 35 | 36 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 37 | 38 | var contentId = 'contentId'; 39 | 40 | var Chord = function (_Component) { 41 | _inherits(Chord, _Component); 42 | 43 | function Chord(props) { 44 | _classCallCheck(this, Chord); 45 | 46 | var _this = _possibleConstructorReturn(this, (Chord.__proto__ || Object.getPrototypeOf(Chord)).call(this)); 47 | 48 | _this.state = { 49 | active: false 50 | }; 51 | 52 | _this.handleClick = function (event) { 53 | if (!_this.props.disabled && _this.props.onClick) { 54 | _this.props.onClick(event); 55 | } 56 | }; 57 | 58 | _this.handleResize = function () { 59 | if (_this.state.active) { 60 | _this.expand(); 61 | } else { 62 | _this.collapse(); 63 | } 64 | }; 65 | 66 | _this.state.active = props.active; 67 | return _this; 68 | } 69 | 70 | _createClass(Chord, [{ 71 | key: 'transitionEndEventName', 72 | 73 | 74 | //private methods 75 | value: function transitionEndEventName() { 76 | var i = void 0, 77 | undefined = void 0, 78 | el = document.createElement('div'), 79 | transitions = { 80 | 'transition': 'transitionend', 81 | 'OTransition': 'otransitionend', 82 | 'MozTransition': 'transitionend', 83 | 'WebkitTransition': 'webkitTransitionEnd' 84 | }; 85 | 86 | for (i in transitions) { 87 | if (transitions.hasOwnProperty(i) && el.style[i] !== undefined) { 88 | return transitions[i]; 89 | } 90 | } 91 | } 92 | }, { 93 | key: 'collapse', 94 | value: function collapse() { 95 | var contentEl = _reactDom2.default.findDOMNode(this.refs[contentId]); 96 | if (contentEl) { 97 | contentEl.style.height = getComputedStyle(contentEl).height; 98 | contentEl.offsetHeight; 99 | contentEl.style.height = 0; 100 | } 101 | } 102 | }, { 103 | key: 'expand', 104 | value: function expand() { 105 | var _this2 = this; 106 | 107 | var contentEl = _reactDom2.default.findDOMNode(this.refs[contentId]); 108 | if (contentEl) { 109 | (function () { 110 | var initialHeight = getComputedStyle(contentEl).height; 111 | contentEl.style.height = 'auto'; 112 | var newHeight = getComputedStyle(contentEl).height; 113 | contentEl.style.height = initialHeight; 114 | contentEl.offsetHeight; 115 | contentEl.style.height = newHeight; 116 | 117 | var self = _this2; 118 | contentEl.addEventListener(_this2.transitionEndEventName(), function transitionEnd(event) { 119 | if (self.state.active) { 120 | contentEl.style.height = 'auto'; 121 | } 122 | contentEl.removeEventListener(self.transitionEndEventName(), transitionEnd, false); 123 | }, false); 124 | })(); 125 | } 126 | } 127 | }, { 128 | key: 'componentDidMount', 129 | value: function componentDidMount() { 130 | window.addEventListener('resize', this.handleResize); 131 | if (this.props.active) { 132 | this.expand(); 133 | } else { 134 | this.collapse(); 135 | } 136 | } 137 | }, { 138 | key: 'componentDidUpdate', 139 | value: function componentDidUpdate(prevProps) { 140 | if (!prevProps.active && this.props.active && this.props.onActive) { 141 | this.props.onActive(); 142 | } 143 | } 144 | }, { 145 | key: 'componentWillReceiveProps', 146 | value: function componentWillReceiveProps(nextProps) { 147 | this.state.active = nextProps.active; 148 | if (this.state.active) { 149 | this.expand(); 150 | } else { 151 | this.collapse(); 152 | } 153 | } 154 | }, { 155 | key: 'componentWillUnmount', 156 | value: function componentWillUnmount() { 157 | window.removeEventListener('resize', this.handleResize); 158 | } 159 | 160 | //events handler 161 | 162 | }, { 163 | key: 'render', 164 | value: function render() { 165 | var _classnames; 166 | 167 | var _props = this.props, 168 | children = _props.children, 169 | id = _props.id, 170 | key = _props.key, 171 | active = _props.active, 172 | activeClassName = _props.activeClassName, 173 | className = _props.className, 174 | disabled = _props.disabled, 175 | hidden = _props.hidden, 176 | label = _props.label, 177 | labelIcon = _props.labelIcon, 178 | labelPostIcon = _props.labelPostIcon, 179 | theme = _props.theme; 180 | 181 | var _className = (0, _classnames6.default)(theme.label, (_classnames = {}, _defineProperty(_classnames, theme.withText, label), _defineProperty(_classnames, theme.withIcon, labelIcon), _defineProperty(_classnames, theme.withPostIcon, labelPostIcon), _classnames)); 182 | 183 | var lIcon = labelIcon; 184 | if (lIcon && Object.prototype.toString.call(lIcon) !== '[object String]') { 185 | lIcon = _react2.default.cloneElement(labelIcon, { 186 | className: (0, _classnames6.default)(labelIcon.props.className, theme.icon) 187 | }); 188 | } else if (labelIcon) { 189 | lIcon = _react2.default.createElement(_font_icon.FontIcon, { className: theme.icon, value: labelIcon }); 190 | } 191 | 192 | var lpIcon = labelPostIcon; 193 | if (lpIcon && Object.prototype.toString.call(lpIcon) !== '[object String]') { 194 | lpIcon = _react2.default.cloneElement(labelPostIcon, { 195 | className: (0, _classnames6.default)(labelPostIcon.props.className, theme.postIcon) 196 | }); 197 | } else if (labelPostIcon) { 198 | lpIcon = _react2.default.createElement(_font_icon.FontIcon, { className: theme.postIcon, value: labelPostIcon }); 199 | } 200 | 201 | return _react2.default.createElement( 202 | 'div', 203 | { id: id, key: key, 'data-react-toolbox': 'chord', className: (0, _classnames6.default)(className, theme.chord, _defineProperty({}, theme.active, active), _defineProperty({}, theme.hidden, hidden), _defineProperty({}, theme.disabled, disabled)) }, 204 | _react2.default.createElement( 205 | 'label', 206 | { className: _className, onClick: this.handleClick }, 207 | lIcon, 208 | _react2.default.createElement( 209 | 'div', 210 | { className: theme.text }, 211 | label 212 | ), 213 | lpIcon 214 | ), 215 | _react2.default.createElement( 216 | 'div', 217 | { ref: contentId, className: (0, _classnames6.default)(theme.content, !active ? theme.hidden : null) }, 218 | children 219 | ) 220 | ); 221 | } 222 | }]); 223 | 224 | return Chord; 225 | }(_react.Component); 226 | 227 | Chord.propTypes = { 228 | active: _react.PropTypes.bool, 229 | activeClassName: _react.PropTypes.string, 230 | className: _react.PropTypes.string, 231 | disabled: _react.PropTypes.bool, 232 | hidden: _react.PropTypes.bool, 233 | label: _react.PropTypes.node, 234 | labelIcon: _react.PropTypes.node, 235 | labelPostIcon: _react.PropTypes.node, 236 | onActive: _react.PropTypes.func, 237 | onClick: _react.PropTypes.func, 238 | theme: _react.PropTypes.shape({ 239 | active: _react.PropTypes.string, 240 | disabled: _react.PropTypes.string, 241 | hidden: _react.PropTypes.string, 242 | label: _react.PropTypes.string, 243 | content: _react.PropTypes.string 244 | }) 245 | }; 246 | Chord.defaultProps = { 247 | active: false, 248 | className: '', 249 | disabled: false, 250 | hidden: false 251 | }; 252 | exports.default = (0, _reactCssThemr.themr)(_identifiers.CHORD)(Chord); 253 | exports.Chord = Chord; -------------------------------------------------------------------------------- /lib/accordion/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import ReactToolbox from "../index"; 3 | 4 | export interface AccordionTheme { 5 | /** 6 | * Used for the root element. 7 | */ 8 | accordion?: string; 9 | } 10 | 11 | interface AccordionProps extends ReactToolbox.Props { 12 | /** 13 | * The class(s) will be applied to the root elemt. 14 | */ 15 | className 16 | /** 17 | * Children to pass through the component. 18 | */ 19 | children?: React.ReactNode; 20 | /** 21 | * Disable the animation on children expanding/collapsing. 22 | * @default false 23 | */ 24 | disableAnimation?: boolean; 25 | /** 26 | * Expanded element 27 | * @default 0 28 | */ 29 | index?: number; 30 | /** 31 | * Callback function that is fired when the Chord changes. 32 | */ 33 | onChange?: Function; 34 | /** 35 | * Classnames object defining the component style. 36 | */ 37 | theme?: AccordionTheme; 38 | } 39 | 40 | export class Accordion extends React.Component { } 41 | 42 | export interface ChordTheme { 43 | /** 44 | * Added to the chord element (label and content) in case it's active. 45 | */ 46 | active?: string; 47 | /** 48 | * Added to the chord element in case it's disabled. 49 | */ 50 | disabled?: string; 51 | /** 52 | * Added to the chord element in case it's hidden. 53 | */ 54 | hidden?: string; 55 | /** 56 | * Base style of header of chord element. 57 | */ 58 | label?: string; 59 | /** 60 | * Base style of content of chord element. 61 | */ 62 | content?: string; 63 | } 64 | 65 | interface ChordProps extends ReactToolbox.Props { 66 | /** 67 | * If true, the current component is visible. 68 | */ 69 | active?: boolean; 70 | /** 71 | * Additional class name to provide custom styling for the active Chord. 72 | */ 73 | activeClassName?: string; 74 | /** 75 | * If true, the current component is not clickable. 76 | * @default false 77 | */ 78 | disabled?: boolean; 79 | /** 80 | * If true, the current component is not visible. 81 | * @default false 82 | */ 83 | hidden?: boolean; 84 | /** 85 | * Label text for the chord header. Required. 86 | */ 87 | label: string; 88 | /** 89 | * Icon for chord header. Required. 90 | */ 91 | labelIcon: string; 92 | /** 93 | * Icon behind of the label for chord header. 94 | */ 95 | labelPostIcon: string; 96 | /** 97 | * Callback function that is fired when the Chord is activated. 98 | */ 99 | onActive?: Function; 100 | /** 101 | * Callback function that is fired when the chord label is clicked. 102 | */ 103 | onClick?: Function; 104 | /** 105 | * Classnames object defining the component style. 106 | */ 107 | theme?: ChordTheme; 108 | } 109 | 110 | export class Chord extends React.Component { } 111 | -------------------------------------------------------------------------------- /lib/accordion/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.Accordion = exports.Chord = undefined; 7 | 8 | var _reactCssThemr = require('react-css-themr'); 9 | 10 | var _identifiers = require('../identifiers.js'); 11 | 12 | var _accordion = require('./accordion.js'); 13 | 14 | var _chord = require('./chord.js'); 15 | 16 | var _theme = require('./theme.scss'); 17 | 18 | var _theme2 = _interopRequireDefault(_theme); 19 | 20 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 21 | 22 | var applyTheme = function applyTheme(Component) { 23 | return (0, _reactCssThemr.themr)(_identifiers.ACCORDION, _theme2.default)(Component); 24 | }; 25 | var ThemedChord = applyTheme(_chord.Chord); 26 | var ThemedAccordion = applyTheme((0, _accordion.accordionFactory)(ThemedChord)); 27 | 28 | exports.Chord = ThemedChord; 29 | exports.Accordion = ThemedAccordion; -------------------------------------------------------------------------------- /lib/accordion/theme.scss: -------------------------------------------------------------------------------- 1 | @import "./config"; 2 | 3 | .accordion { 4 | position: relative; 5 | height: 100%; 6 | width: 100%; 7 | display: block; 8 | 9 | &.disableAnimation { 10 | .chord { 11 | .content { 12 | transition: none !important; 13 | } 14 | } 15 | } 16 | 17 | .chord { 18 | position: relative; 19 | width: 100%; 20 | display: block; 21 | 22 | &.hidden { 23 | display: none !important; 24 | } 25 | 26 | &.disabled { 27 | opacity: $chord-label-disabled-opacity; 28 | .label { 29 | cursor: default; 30 | } 31 | } 32 | 33 | .content { 34 | position: relative; 35 | height: 0; 36 | box-sizing: border-box; 37 | overflow: hidden; 38 | opacity: 0; 39 | transition: height $animation-duration ease-in-out, opacity $animation-duration linear; 40 | } 41 | 42 | &.active { 43 | margin-bottom: 2 * $chord-content-padding; 44 | 45 | .content { 46 | opacity: 1; 47 | height: auto; 48 | transition: height $animation-duration ease-in-out, opacity $animation-duration linear; 49 | } 50 | 51 | .label { 52 | color: $chord-pointer-color; 53 | margin-bottom: $chord-content-padding; 54 | } 55 | } 56 | 57 | .label { 58 | display: block; 59 | width: 100%; 60 | font-size: $chord-text-height; 61 | font-weight: $font-weight-semi-bold; 62 | padding-top: $chord-label-v-padding; 63 | padding-bottom: $chord-label-v-padding; 64 | margin-bottom: 0; 65 | color: $chord-text-color; 66 | text-transform: uppercase; 67 | transition-timing-function: $animation-curve-default; 68 | transition-duration: $animation-duration; 69 | transition-property: box-shadow, color; 70 | cursor: pointer; 71 | 72 | .text { 73 | position: relative; 74 | display: inline-block; 75 | height: $chord-icon-height; 76 | line-height: $chord-icon-height; 77 | vertical-align: top; 78 | margin-right: $chord-content-padding; 79 | } 80 | 81 | .icon, 82 | .postIcon { 83 | position: relative; 84 | display: inline-block; 85 | height: $chord-icon-height; 86 | width: $chord-icon-height; 87 | line-height: $chord-icon-height; 88 | } 89 | 90 | &.withIcon { 91 | } 92 | 93 | &.withPostButton { 94 | } 95 | 96 | &.withText { 97 | .icon { 98 | margin-right: $chord-content-padding; 99 | } 100 | } 101 | 102 | .postIcon { 103 | } 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /lib/filepicker/_config.scss: -------------------------------------------------------------------------------- 1 | @import "../config"; 2 | 3 | $input-min-width: $unit * 4 !default; 4 | -------------------------------------------------------------------------------- /lib/filepicker/filepicker.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.FilePicker = exports.filePickerFactory = undefined; 7 | 8 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; 9 | 10 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 11 | 12 | var _react = require('react'); 13 | 14 | var _react2 = _interopRequireDefault(_react); 15 | 16 | var _classnames = require('classnames'); 17 | 18 | var _classnames2 = _interopRequireDefault(_classnames); 19 | 20 | var _reactCssThemr = require('react-css-themr'); 21 | 22 | var _button = require('react-toolbox/lib/button'); 23 | 24 | var _input = require('react-toolbox/lib/input'); 25 | 26 | var _input2 = _interopRequireDefault(_input); 27 | 28 | var _identifiers = require('../identifiers.js'); 29 | 30 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 31 | 32 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 33 | 34 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 35 | 36 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 37 | 38 | var factory = function factory(Input, Button) { 39 | var FilePicker = function (_Component) { 40 | _inherits(FilePicker, _Component); 41 | 42 | function FilePicker(props) { 43 | _classCallCheck(this, FilePicker); 44 | 45 | var _this = _possibleConstructorReturn(this, (FilePicker.__proto__ || Object.getPrototypeOf(FilePicker)).call(this, props)); 46 | 47 | _this.state = { 48 | value: _this.props.value 49 | }; 50 | 51 | _this.handlerBrowse = function (e) { 52 | var files = e.target.files; 53 | 54 | if (files && files.length) { 55 | if (_this.state.value !== files[0].name) { 56 | if (_this.props.onChange) { 57 | _this.props.onChange(files[0], files[0].name); 58 | } 59 | } 60 | 61 | _this.setState({ 62 | value: files[0].name 63 | }); 64 | } 65 | }; 66 | 67 | _this.state = { value: props.value }; 68 | return _this; 69 | } 70 | 71 | _createClass(FilePicker, [{ 72 | key: 'componentWillReceiveProps', 73 | value: function componentWillReceiveProps(nextProps) { 74 | if (this.state.value !== nextProps.value) { 75 | this.setState({ 76 | value: nextProps.value 77 | }); 78 | } 79 | } 80 | }, { 81 | key: 'render', 82 | value: function render() { 83 | var _props = this.props, 84 | className = _props.className, 85 | buttonProperties = _props.buttonProperties, 86 | inputProperties = _props.inputProperties, 87 | value = _props.value, 88 | inline = _props.inline, 89 | theme = _props.theme; 90 | 91 | var css = inline ? 'inline' : null; 92 | 93 | return _react2.default.createElement( 94 | 'div', 95 | { 'data-ext-react-toolbox': 'filepicker', className: (0, _classnames2.default)(theme.filepicker, className, [theme[css]]) }, 96 | _react2.default.createElement(Input, _extends({ 97 | className: (0, _classnames2.default)(theme.input), 98 | value: this.state.value 99 | }, inputProperties)), 100 | _react2.default.createElement(Button, _extends({ 101 | className: (0, _classnames2.default)(theme.button, theme.iefix), 102 | onChange: this.handlerBrowse 103 | }, buttonProperties)) 104 | ); 105 | } 106 | }]); 107 | 108 | return FilePicker; 109 | }(_react.Component); 110 | 111 | FilePicker.propTypes = { 112 | buttonProperties: _react2.default.PropTypes.object, 113 | className: _react.PropTypes.string, 114 | inline: _react.PropTypes.bool, 115 | inputProperties: _react2.default.PropTypes.object, 116 | onChange: _react.PropTypes.func, 117 | theme: _react.PropTypes.shape({ 118 | button: _react.PropTypes.string, 119 | filepicker: _react.PropTypes.string, 120 | inline: _react.PropTypes.string, 121 | input: _react.PropTypes.string 122 | }), 123 | value: _react.PropTypes.string 124 | }; 125 | FilePicker.defaultProps = { 126 | buttonProperties: { label: 'BROWSE' }, 127 | className: '', 128 | inline: false, 129 | inputProperties: { label: 'SELECT FILE' }, 130 | value: undefined 131 | }; 132 | 133 | return FilePicker; 134 | }; 135 | 136 | var FilePicker = factory(_input2.default, _button.BrowseButton); 137 | 138 | exports.default = (0, _reactCssThemr.themr)(_identifiers.FILEPICKER)(FilePicker); 139 | exports.filePickerFactory = factory; 140 | exports.FilePicker = FilePicker; -------------------------------------------------------------------------------- /lib/filepicker/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import ReactToolbox from "react-toolbox/lib/index"; 3 | 4 | export interface FilePickerTheme { 5 | /** 6 | * Used for the root element. 7 | */ 8 | filepicker?: string; 9 | /** 10 | * Used for the input component. 11 | */ 12 | input?: string; 13 | /** 14 | * Used for inline style of component. 15 | */ 16 | inline?: string; 17 | /** 18 | * Used for the button component. 19 | */ 20 | button?: string; 21 | } 22 | 23 | interface FilePickerProps extends ReactToolbox.Props { 24 | /** 25 | * This class will be applied to the root elemt. 26 | */ 27 | className?: string; 28 | /** 29 | * This is button base properties like: accent, disabled, icon, ... 30 | */ 31 | buttonProperties?: object 32 | /** 33 | * This is input base properties like: hint, disabled, icon, ... 34 | */ 35 | inputProperties?: object 36 | /** 37 | * If true, the component will apear inline. 38 | * @default false 39 | */ 40 | inline?: boolean 41 | /** 42 | * Callback called when the input is changing. 43 | */ 44 | onChange?: Function; 45 | /** 46 | * Classnames object defining the component style. 47 | */ 48 | theme?: FilePickerTheme; 49 | /** 50 | * This is initial value of input component. 51 | */ 52 | value?: string 53 | } 54 | 55 | export class FilePicker extends React.Component { } 56 | 57 | export default FilePicker; 58 | -------------------------------------------------------------------------------- /lib/filepicker/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.FilePicker = undefined; 7 | 8 | var _reactCssThemr = require('react-css-themr'); 9 | 10 | var _identifiers = require('../identifiers.js'); 11 | 12 | var _filepicker = require('./filepicker.js'); 13 | 14 | var _button = require('react-toolbox/lib/button'); 15 | 16 | var _input = require('react-toolbox/lib/input'); 17 | 18 | var _theme = require('./theme.scss'); 19 | 20 | var _theme2 = _interopRequireDefault(_theme); 21 | 22 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 23 | 24 | var FilePicker = (0, _filepicker.filePickerFactory)(_input.Input, _button.BrowseButton); 25 | 26 | var ThemedFilePicker = (0, _reactCssThemr.themr)(_identifiers.FILEPICKER, _theme2.default)(FilePicker); 27 | exports.default = ThemedFilePicker; 28 | exports.FilePicker = ThemedFilePicker; -------------------------------------------------------------------------------- /lib/filepicker/theme.scss: -------------------------------------------------------------------------------- 1 | @import "./config"; 2 | 3 | .filepicker { 4 | 5 | > * { 6 | display: inline-block; 7 | vertical-align: middle; 8 | } 9 | 10 | &.inline { 11 | display: inline-block; 12 | vertical-align: middle; 13 | } 14 | 15 | .input { 16 | @include space-between; 17 | min-width: $input-min-width; 18 | 19 | & span:last-child { 20 | opacity: 1 !important; 21 | } 22 | } 23 | 24 | /*ie fix*/ 25 | .iefix { 26 | input { 27 | top: 0 !important; 28 | left: 0 !important; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/identifiers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | var ACCORDION = exports.ACCORDION = 'ERTAccordion'; 7 | var CHORD = exports.CHORD = 'ERTChord'; 8 | var PAGER = exports.PAGER = 'ERTPager'; 9 | var FILEPICKER = exports.FILEPICKER = 'ERTFilePicker'; -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.FilePicker = exports.Pager = undefined; 7 | 8 | var _accordion = require('./accordion'); 9 | 10 | Object.keys(_accordion).forEach(function (key) { 11 | if (key === "default" || key === "__esModule") return; 12 | Object.defineProperty(exports, key, { 13 | enumerable: true, 14 | get: function get() { 15 | return _accordion[key]; 16 | } 17 | }); 18 | }); 19 | 20 | var _pager = require('./pager'); 21 | 22 | var _pager2 = _interopRequireDefault(_pager); 23 | 24 | var _filepicker = require('./filepicker'); 25 | 26 | var _filepicker2 = _interopRequireDefault(_filepicker); 27 | 28 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 29 | 30 | exports.Pager = _pager2.default; 31 | exports.FilePicker = _filepicker2.default; -------------------------------------------------------------------------------- /lib/pager/_config.scss: -------------------------------------------------------------------------------- 1 | @import "~react-toolbox/lib/button/theme"; 2 | @import "../config"; 3 | -------------------------------------------------------------------------------- /lib/pager/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import ReactToolbox from "react-toolbox/lib/index"; 3 | 4 | export interface PagerTheme { 5 | /** 6 | * Used for the root element. 7 | */ 8 | pager?: string; 9 | /** 10 | * Used for the active page. 11 | */ 12 | active?: string; 13 | /** 14 | * Used for the first and last page buttons. 15 | */ 16 | firstLastPagesButton?: string; 17 | /** 18 | * Used for the next and previous arrow buttons. 19 | */ 20 | leftRightArrowButton?: string; 21 | /** 22 | * Used for the next and previous range pages buttons. 23 | */ 24 | leftRightRangeButton?: string; 25 | /** 26 | * Used for the regular page buttons. 27 | */ 28 | pagesButton?: string; 29 | } 30 | 31 | interface PagerProps extends ReactToolbox.Props { 32 | /** 33 | * Used for the previous button content. 34 | */ 35 | prevButtonLabel?: string; 36 | /** 37 | * Used for the left range button content 38 | */ 39 | rangeLeftButtonLabel?: string; 40 | /** 41 | * Used for the right range button content 42 | */ 43 | rangeRightButtonLabel?: string; 44 | /** 45 | * Used for the next button content 46 | */ 47 | nextButtonLabel?: string; 48 | /** 49 | * A Number with the currently selected page 50 | */ 51 | currentPage?: number; 52 | /** 53 | * A Number of total pages. 54 | */ 55 | totalPages?: number; 56 | /** 57 | * A Number of pages visible in control except next, previous and ranges buttons, the minimum value is 2. 58 | */ 59 | visiblePagesBlockSize?: number; 60 | /** 61 | * Callback called when the page is changing. 62 | */ 63 | onPageChange?: Function; 64 | /** 65 | * Classnames object defining the component style. 66 | */ 67 | theme?: PagerTheme; 68 | /** 69 | * This class will be applied to the root elemt. 70 | */ 71 | pagerClassName?: string; 72 | /** 73 | * Defining default style of first, last page buttons. 74 | * it can have following styles: 75 | * accent, 76 | * flat, 77 | * inverse, 78 | * mini, 79 | * neutral, 80 | * primary, 81 | * raised, 82 | * toggle 83 | */ 84 | firstLastPagesButtonStyles?: object; 85 | /** 86 | * Defining default style of next, previous buttons. 87 | * it can have following styles: 88 | * accent, 89 | * flat, 90 | * inverse, 91 | * mini, 92 | * neutral, 93 | * primary, 94 | * raised, 95 | * toggle 96 | */ 97 | leftRightArrowButtonStyles?: object; 98 | /** 99 | * Defining default style of left, right range buttons. 100 | * it can have following styles: 101 | * accent, 102 | * flat, 103 | * inverse, 104 | * mini, 105 | * neutral, 106 | * primary, 107 | * raised, 108 | * toggle 109 | */ 110 | leftRightRangeButtonStyles?: object; 111 | /** 112 | * Defining default style of regular page buttons. 113 | * it can have following styles: 114 | * accent, 115 | * flat, 116 | * inverse, 117 | * mini, 118 | * neutral, 119 | * primary, 120 | * raised, 121 | * toggle 122 | */ 123 | pagesButtonStyles?: object; 124 | } 125 | 126 | export class Pager extends React.Component { } 127 | 128 | export default Pager; 129 | -------------------------------------------------------------------------------- /lib/pager/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.Pager = undefined; 7 | 8 | var _reactCssThemr = require('react-css-themr'); 9 | 10 | var _identifiers = require('../identifiers.js'); 11 | 12 | var _pager = require('./pager.js'); 13 | 14 | var _page = require('./page.js'); 15 | 16 | var _page2 = _interopRequireDefault(_page); 17 | 18 | var _theme = require('./theme.scss'); 19 | 20 | var _theme2 = _interopRequireDefault(_theme); 21 | 22 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 23 | 24 | var Pager = (0, _pager.pagerFactory)(_page2.default); 25 | 26 | var ThemedPager = (0, _reactCssThemr.themr)(_identifiers.PAGER, _theme2.default)(Pager); 27 | exports.default = ThemedPager; 28 | exports.Pager = ThemedPager; -------------------------------------------------------------------------------- /lib/pager/page.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.BUTTON_TYPE = undefined; 7 | 8 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; 9 | 10 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 11 | 12 | var _react = require('react'); 13 | 14 | var _react2 = _interopRequireDefault(_react); 15 | 16 | var _classnames = require('classnames'); 17 | 18 | var _classnames2 = _interopRequireDefault(_classnames); 19 | 20 | var _button = require('react-toolbox/lib/button'); 21 | 22 | var _button2 = _interopRequireDefault(_button); 23 | 24 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 25 | 26 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 27 | 28 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 29 | 30 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 31 | 32 | var BUTTON_TYPE = exports.BUTTON_TYPE = { 33 | PAGE: 0, 34 | RANGE: 1, 35 | ARROW: 2 36 | }; 37 | 38 | var Page = function (_Component) { 39 | _inherits(Page, _Component); 40 | 41 | function Page(props) { 42 | _classCallCheck(this, Page); 43 | 44 | var _this = _possibleConstructorReturn(this, (Page.__proto__ || Object.getPrototypeOf(Page)).call(this)); 45 | 46 | _this.state = { 47 | disabled: false, 48 | active: false, 49 | data: 0, 50 | buttonType: 0, 51 | label: null 52 | }; 53 | 54 | 55 | _this.state.data = props.data; 56 | _this.state.buttonType = props.buttonType; 57 | _this.state.label = props.children; 58 | return _this; 59 | } 60 | 61 | _createClass(Page, [{ 62 | key: 'componentWillReceiveProps', 63 | value: function componentWillReceiveProps(nextProps) { 64 | if (nextProps.children && !isNaN(nextProps.children) && this.state.label !== nextProps.children) { 65 | this.setState({ 66 | label: nextProps.children 67 | }); 68 | } 69 | if (nextProps.buttonType && !isNaN(nextProps.buttonType) && this.state.buttonType !== nextProps.buttonType) { 70 | this.setState({ 71 | buttonType: nextProps.buttonType 72 | }); 73 | } 74 | } 75 | 76 | //events handlers 77 | 78 | }, { 79 | key: 'handlerClick', 80 | value: function handlerClick() { 81 | if (this.props.onPageClick) { 82 | this.props.onPageClick(this.state.data, this.state.buttonType); 83 | } 84 | } 85 | 86 | //rendering 87 | 88 | }, { 89 | key: 'render', 90 | value: function render() { 91 | var styles = this.props.buttonStyles[this.state.buttonType]; 92 | var className = this.props.buttonClassNames[this.state.buttonType]; 93 | 94 | if (styles['disabled'] === undefined) { 95 | styles = _extends({}, styles, { disabled: this.state.disabled }); 96 | } else { 97 | styles['disabled'] = this.state.disabled; 98 | } 99 | 100 | return _react2.default.createElement( 101 | _button2.default, 102 | _extends({ 103 | className: (0, _classnames2.default)(className, this.state.active ? this.props.activeClassName : null), 104 | onClick: this.handlerClick.bind(this) 105 | }, styles), 106 | this.state.label 107 | ); 108 | } 109 | 110 | //public methods 111 | 112 | }, { 113 | key: 'update', 114 | value: function update(active, data, label, type) { 115 | this.setState({ 116 | active: active, 117 | data: data, 118 | buttonType: type, 119 | label: label 120 | }); 121 | } 122 | }, { 123 | key: 'disable', 124 | value: function disable(v) { 125 | this.setState({ 126 | disabled: v 127 | }); 128 | } 129 | }]); 130 | 131 | return Page; 132 | }(_react.Component); 133 | 134 | Page.propTypes = { 135 | activeClassName: _react.PropTypes.string, 136 | buttonClassNames: _react.PropTypes.object, 137 | buttonStyles: _react.PropTypes.object, 138 | buttonType: _react.PropTypes.oneOf([BUTTON_TYPE.PAGE, BUTTON_TYPE.RANGE, BUTTON_TYPE.ARROW]), 139 | onPageClick: _react.PropTypes.func 140 | }; 141 | Page.defaultProps = { 142 | buttonType: 0, 143 | buttonStyles: { 0: { flat: true } }, 144 | buttonClassNames: { 0: null } 145 | }; 146 | exports.default = Page; -------------------------------------------------------------------------------- /lib/pager/pager.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.Pager = exports.pagerFactory = undefined; 7 | 8 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 9 | 10 | var _react = require('react'); 11 | 12 | var _react2 = _interopRequireDefault(_react); 13 | 14 | var _classnames = require('classnames'); 15 | 16 | var _classnames2 = _interopRequireDefault(_classnames); 17 | 18 | var _reactCssThemr = require('react-css-themr'); 19 | 20 | var _page = require('./page.js'); 21 | 22 | var _page2 = _interopRequireDefault(_page); 23 | 24 | var _identifiers = require('../identifiers.js'); 25 | 26 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 27 | 28 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 29 | 30 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 31 | 32 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 33 | 34 | var isOnePage = function isOnePage(first, last) { 35 | return first === last || !last; 36 | }; 37 | 38 | var isBorderPage = function isBorderPage(curr, border) { 39 | return curr === border; 40 | }; 41 | 42 | var clone = function clone(src) { 43 | return JSON.parse(JSON.stringify(src)); 44 | }; 45 | 46 | var prevPageId = 'prev'; 47 | var nextPageId = 'next'; 48 | 49 | var initialVisiblePagesBlockSize = 3; 50 | var initialCurrentPage = 1; 51 | var first = 1; 52 | /* 53 | * adjustment of start and end elements at range to have equal elements during navigation 54 | * 2 - because we already have the rendered first and last button 55 | */ 56 | var adjustment = 2; 57 | 58 | var factory = function factory(Page) { 59 | var Pager = function (_Component) { 60 | _inherits(Pager, _Component); 61 | 62 | function Pager(props) { 63 | _classCallCheck(this, Pager); 64 | 65 | var _this = _possibleConstructorReturn(this, (Pager.__proto__ || Object.getPrototypeOf(Pager)).call(this)); 66 | 67 | _this._ranges = { 68 | leftEnd: null, 69 | rightStart: null 70 | }; 71 | _this._pages = []; 72 | _this.state = { 73 | currentPage: initialCurrentPage, 74 | totalPages: initialCurrentPage 75 | }; 76 | 77 | 78 | if (props) { 79 | _this.state.currentPage = props.currentPage; 80 | _this.state.totalPages = props.totalPages; 81 | } 82 | return _this; 83 | } 84 | 85 | //fields 86 | 87 | 88 | _createClass(Pager, [{ 89 | key: 'componentWillReceiveProps', 90 | value: function componentWillReceiveProps(nextProps) { 91 | if (nextProps.currentPage && !isNaN(Number(nextProps.currentPage)) && this.state.currentPage !== nextProps.currentPage) { 92 | this._pages.splice(0); 93 | this.setState({ 94 | currentPage: Number(nextProps.currentPage < first ? first : nextProps.currentPage) 95 | }); 96 | } 97 | if (nextProps.totalPages && !isNaN(Number(nextProps.totalPages)) && this.state.totalPages !== nextProps.totalPages) { 98 | this._pages.splice(0); 99 | this.setState({ 100 | totalPages: Number(nextProps.totalPages < first ? first : nextProps.totalPages) 101 | }); 102 | } 103 | if (nextProps.visiblePagesBlockSize && !isNaN(Number(nextProps.visiblePagesBlockSize)) && this.state.visiblePagesBlockSize !== nextProps.visiblePagesBlockSize) { 104 | this._pages.splice(0); 105 | this.setState({ 106 | visiblePagesBlockSize: Number(nextProps.visiblePagesBlockSize < nextProps.totalPages ? nextProps.visiblePagesBlockSize : nextProps.totalPages) 107 | }); 108 | } 109 | } 110 | }, { 111 | key: 'componentWillUnmount', 112 | value: function componentWillUnmount() { 113 | this._pages.splice(0); 114 | } 115 | }, { 116 | key: 'componentDidMount', 117 | value: function componentDidMount() { 118 | this.updateState(this.state.currentPage, this.state.totalPages); 119 | } 120 | }, { 121 | key: 'componentDidUpdate', 122 | value: function componentDidUpdate() { 123 | this.updatePages(this.state.currentPage, this.state.totalPages); 124 | } 125 | 126 | //events handlers 127 | 128 | }, { 129 | key: 'handlerPageClick', 130 | value: function handlerPageClick(page, type) { 131 | if (type === _page.BUTTON_TYPE.RANGE) { 132 | this.handlerRangeClick(page); 133 | return; 134 | } 135 | 136 | var oldValue = this.state.currentPage; 137 | var newValue = page; 138 | 139 | this.updateState(newValue); 140 | 141 | if (this.props.onPageChange) { 142 | this.props.onPageChange(newValue, oldValue); 143 | } 144 | } 145 | }, { 146 | key: 'handlerRangeClick', 147 | value: function handlerRangeClick(key) { 148 | var curr = this.state.currentPage; 149 | var last = this.state.totalPages; 150 | 151 | var right = !this._ranges.rightStart ? last : this._ranges.rightStart; 152 | var left = !this._ranges.leftEnd ? first : this._ranges.leftEnd; 153 | 154 | var newValue = curr; 155 | var sum = first + left; 156 | if (key === prevPageId) { 157 | newValue = sum >> 1; //rounding to left 158 | } else { 159 | sum = last + right; 160 | newValue = (sum >> 1) + sum % 2; //rounding to right 161 | } 162 | 163 | this.updateState(newValue); 164 | 165 | if (this.props.onPageChange) { 166 | this.props.onPageChange(newValue, curr); 167 | } 168 | } 169 | }, { 170 | key: 'handlerPrevNextClick', 171 | value: function handlerPrevNextClick(key, type) { 172 | var oldValue = this.state.currentPage; 173 | var newValue = key === prevPageId ? oldValue - 1 : oldValue + 1; 174 | 175 | this.updateState(newValue); 176 | 177 | if (this.props.onPageChange) { 178 | this.props.onPageChange(newValue, oldValue); 179 | } 180 | } 181 | 182 | //private methods 183 | 184 | }, { 185 | key: 'updateState', 186 | value: function updateState(newValue) { 187 | this.state.currentPage = newValue; 188 | 189 | if (this._pages.length > 0) { 190 | this.updatePages(newValue, this.state.totalPages); 191 | } else { 192 | this.forceUpdate(); 193 | } 194 | } 195 | }, { 196 | key: 'updatePages', 197 | value: function updatePages(currPage, totalPages) { 198 | var curr = currPage; 199 | var last = totalPages; 200 | 201 | var blockSize = this.props.visiblePagesBlockSize === 1 ? adjustment : this.props.visiblePagesBlockSize; 202 | 203 | var padding = blockSize >> 1; 204 | var left = curr - padding * (blockSize % 2); //in case of even visiblePagesBlockSize 205 | var right = curr + padding; 206 | 207 | var blocksNumber = Math.ceil(last / blockSize); 208 | var currentBlock = Math.ceil(curr / blockSize); 209 | 210 | var start = (currentBlock - 1) * blockSize + first; 211 | var end = start + blockSize - first; 212 | 213 | if (currentBlock === 1) { 214 | //adjust set of buttons if current is on the left boundary 215 | end += adjustment; 216 | end = last - first === end ? last : end; 217 | } else if (currentBlock < blocksNumber) { 218 | //adjustment set of buttons if current is between boundaries 219 | start = left; 220 | end = right; 221 | 222 | currentBlock = Math.ceil((totalPages - end === 1 ? totalPages : end) / blockSize); 223 | } 224 | 225 | if (currentBlock === blocksNumber) { 226 | //adjustment set of buttons if current is on the right boundary 227 | start = last - (blockSize + adjustment - first); 228 | start = start - 1 <= first ? first : start; 229 | end = last; 230 | } 231 | 232 | this.refs[prevPageId].disable(curr === first); 233 | 234 | var buttonIdx = 0; 235 | 236 | if (currentBlock > 1 && start - 1 > first) { 237 | this.refs[buttonIdx].update(curr === first, first, String(first), _page.BUTTON_TYPE.FLPAGE); 238 | ++buttonIdx; 239 | 240 | this.refs[buttonIdx].update(false, prevPageId, this.props.rangeLeftButtonLabel, _page.BUTTON_TYPE.RANGE); 241 | ++buttonIdx; 242 | } 243 | 244 | for (var i = start; i <= last && i <= end; ++i) { 245 | var bType = _page.BUTTON_TYPE.PAGE; 246 | if (i === first || i === last) { 247 | bType = _page.BUTTON_TYPE.FLPAGE; 248 | } 249 | this.refs[buttonIdx].update(curr === i, i, String(i), bType); 250 | ++buttonIdx; 251 | } 252 | 253 | if (currentBlock < blocksNumber && end < last) { 254 | this.refs[buttonIdx].update(false, nextPageId, this.props.rangeRightButtonLabel, _page.BUTTON_TYPE.RANGE); 255 | ++buttonIdx; 256 | 257 | this.refs[buttonIdx].update(curr === last, last, String(last), _page.BUTTON_TYPE.FLPAGE); 258 | ++buttonIdx; 259 | } 260 | 261 | this.refs[nextPageId].disable(curr === last); 262 | 263 | // keep range boundaries to calculate correct navigation through the range 264 | this._ranges.leftEnd = start; 265 | this._ranges.rightStart = end - 1; 266 | } 267 | 268 | //rendering 269 | 270 | }, { 271 | key: 'renderPages', 272 | value: function renderPages(currPage, totalPages) { 273 | this._pages.splice(0); 274 | 275 | var curr = currPage; 276 | var last = totalPages < first ? first : totalPages; 277 | if (curr < first || curr > last) { 278 | curr = curr < first ? first : curr > last ? last : curr; 279 | this.setState({ 280 | currentPage: curr 281 | }); 282 | } 283 | 284 | var content = []; 285 | 286 | var buttonsCount = this.props.visiblePagesBlockSize + adjustment + 2; 287 | buttonsCount = buttonsCount > last ? last : buttonsCount; 288 | 289 | var possibleButtonClassNames = {}; 290 | possibleButtonClassNames[_page.BUTTON_TYPE.FLPAGE] = (0, _classnames2.default)(this.props.theme.firstLastPagesButton); 291 | possibleButtonClassNames[_page.BUTTON_TYPE.PAGE] = (0, _classnames2.default)(this.props.theme.pagesButton); 292 | possibleButtonClassNames[_page.BUTTON_TYPE.RANGE] = (0, _classnames2.default)(this.props.theme.leftRightRangeButton); 293 | possibleButtonClassNames[_page.BUTTON_TYPE.ARROW] = (0, _classnames2.default)(this.props.theme.leftRightArrowButton); 294 | 295 | var possibleLeftButtoStyles = {}; 296 | possibleLeftButtoStyles[_page.BUTTON_TYPE.ARROW] = clone(this.props.leftRightArrowButtonStyles); 297 | possibleLeftButtoStyles[_page.BUTTON_TYPE.ARROW].disabled = isOnePage(first, this.state.totalPages) || isBorderPage(this.state.currentPage, first); 298 | content.push(_react2.default.createElement( 299 | Page, 300 | { 301 | key: prevPageId, 302 | ref: prevPageId, 303 | data: prevPageId, 304 | buttonType: _page.BUTTON_TYPE.ARROW, 305 | buttonStyles: possibleLeftButtoStyles, 306 | buttonClassNames: possibleButtonClassNames, 307 | onPageClick: this.handlerPrevNextClick.bind(this) }, 308 | this.props.prevButtonLabel 309 | )); 310 | 311 | var possibleButtoStyles = {}; 312 | possibleButtoStyles[_page.BUTTON_TYPE.FLPAGE] = this.props.firstLastPagesButtonStyles; 313 | possibleButtoStyles[_page.BUTTON_TYPE.PAGE] = this.props.pagesButtonStyles; 314 | possibleButtoStyles[_page.BUTTON_TYPE.RANGE] = this.props.leftRightRangeButtonStyles; 315 | for (var i = 0; i < buttonsCount; ++i) { 316 | content.push(_react2.default.createElement( 317 | Page, 318 | { 319 | key: i, 320 | ref: i, 321 | data: i, 322 | buttonType: _page.BUTTON_TYPE.PAGE, 323 | activeClassName: (0, _classnames2.default)(this.props.theme.active), 324 | buttonClassNames: possibleButtonClassNames, 325 | onPageClick: this.handlerPageClick.bind(this), 326 | buttonStyles: possibleButtoStyles 327 | }, 328 | String(i) 329 | )); 330 | this._pages.push(i); 331 | } 332 | 333 | var possibleRightButtoStyles = {}; 334 | possibleRightButtoStyles[_page.BUTTON_TYPE.ARROW] = clone(this.props.leftRightArrowButtonStyles); 335 | possibleRightButtoStyles[_page.BUTTON_TYPE.ARROW].disabled = isOnePage(first, this.state.totalPages) || isBorderPage(this.state.currentPage, this.state.totalPages); 336 | content.push(_react2.default.createElement( 337 | Page, 338 | { 339 | key: nextPageId, 340 | ref: nextPageId, 341 | data: nextPageId, 342 | buttonType: _page.BUTTON_TYPE.ARROW, 343 | buttonStyles: possibleLeftButtoStyles, 344 | buttonClassNames: possibleButtonClassNames, 345 | onPageClick: this.handlerPrevNextClick.bind(this) }, 346 | this.props.nextButtonLabel 347 | )); 348 | 349 | return content; 350 | } 351 | }, { 352 | key: 'render', 353 | value: function render() { 354 | return _react2.default.createElement( 355 | 'div', 356 | { 'data-ext-react-toolbox': 'pager', className: (0, _classnames2.default)(this.props.theme.pager, this.props.pagerClassName) }, 357 | this.renderPages(this.state.currentPage, this.state.totalPages) 358 | ); 359 | } 360 | }]); 361 | 362 | return Pager; 363 | }(_react.Component); 364 | 365 | Pager.propTypes = { 366 | currentPage: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.number]).isRequired, 367 | firstLastPagesButtonStyles: _react.PropTypes.object, 368 | leftRightArrowButtonStyles: _react.PropTypes.object, 369 | leftRightRangeButtonStyles: _react.PropTypes.object, 370 | nextButtonLabel: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.element]), 371 | onPageChange: _react.PropTypes.func, 372 | pagerClassName: _react.PropTypes.string, 373 | pagesButtonStyles: _react.PropTypes.object, 374 | prevButtonLabel: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.element]), 375 | rangeLeftButtonLabel: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.element]), 376 | rangeRightButtonLabel: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.element]), 377 | theme: _react.PropTypes.shape({ 378 | pager: _react.PropTypes.string, 379 | active: _react.PropTypes.string, 380 | firstLastPagesButton: _react.PropTypes.string, 381 | leftRightArrowButton: _react.PropTypes.string, 382 | leftRightRangeButton: _react.PropTypes.string, 383 | pagesButton: _react.PropTypes.string 384 | }), 385 | totalPages: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.number]).isRequired, 386 | visiblePagesBlockSize: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.number]).isRequired 387 | }; 388 | Pager.defaultProps = { 389 | currentPage: initialCurrentPage, 390 | totalPages: initialCurrentPage, 391 | firstLastPagesButtonStyles: { flat: true }, 392 | leftRightArrowButtonStyles: { raised: true }, 393 | leftRightRangeButtonStyles: { flat: true }, 394 | nextButtonLabel: '>', 395 | pagesButtonStyles: { flat: true }, 396 | prevButtonLabel: '<', 397 | rangeLeftButtonLabel: '...', 398 | rangeRightButtonLabel: '...', 399 | visiblePagesBlockSize: initialVisiblePagesBlockSize 400 | }; 401 | 402 | 403 | return Pager; 404 | }; 405 | 406 | var Pager = factory(_page2.default); 407 | 408 | exports.default = (0, _reactCssThemr.themr)(_identifiers.PAGER)(Pager); 409 | exports.pagerFactory = factory; 410 | exports.Pager = Pager; -------------------------------------------------------------------------------- /lib/pager/theme.scss: -------------------------------------------------------------------------------- 1 | @import "./config"; 2 | 3 | .pager { 4 | display: inline-block; 5 | vertical-align: middle; 6 | 7 | .firstLastPagesButton, 8 | .leftRightArrowButton, 9 | .leftRightRangeButton, 10 | .pagesButton { 11 | min-width: $button-height; 12 | margin: $button-margin; 13 | } 14 | 15 | .active { 16 | text-decoration: underline; 17 | } 18 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-toolbox-additions", 3 | "description": "The project provides additions for react-toolbox which are not defined at material design and therefore were not included to original react-toolbox components library.", 4 | "homepage": "https://github.com/MaximKomlev/react-toolbox-additions", 5 | "version": "1.3.9", 6 | "main": "./lib", 7 | "author": { 8 | "name": "Maksim Komleu", 9 | "url": "https://github.com/MaximKomlev/react-toolbox-additions" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/MaximKomlev/react-toolbox-additions.git" 14 | }, 15 | "bugs": { 16 | "email": "max_komlev@msn.com" 17 | }, 18 | "keywords": [ 19 | "components", 20 | "react-toolbox", 21 | "react", 22 | "react-component", 23 | "toolkit", 24 | "addons", 25 | "pager", 26 | "filepicker", 27 | "accordion" 28 | ], 29 | "dependencies": { 30 | "classnames": "~2.2.5", 31 | "core-js": "~2.4.0", 32 | "react": "~15.4.0", 33 | "react-css-themr": "^1.6.0", 34 | "react-dom": "^15.4.0", 35 | "react-toolbox": "^1.3.2" 36 | }, 37 | "devDependencies": { 38 | "autoprefixer": "~6.4.0", 39 | "babel-cli": "~6.8.0", 40 | "babel-core": "~6.13.2", 41 | "babel-eslint": "~6.0.4", 42 | "babel-loader": "~6.2.4", 43 | "babel-plugin-react-transform": "~2.0.2", 44 | "babel-polyfill": "~6.13.0", 45 | "babel-preset-es2015": "~6.13.2", 46 | "babel-preset-react": "~6.5.0", 47 | "babel-preset-stage-0": "~6.5.0", 48 | "bluebird": "~3.3.5", 49 | "cpx": "~1.3.1", 50 | "cross-env": "~2.0.0", 51 | "css-loader": "~0.23.1", 52 | "eslint": "~3.2.2", 53 | "eslint-plugin-babel": "~3.2.0", 54 | "eslint-plugin-react": "~6.0.0", 55 | "expect": "~1.20.1", 56 | "express": "~4.13.4", 57 | "extract-text-webpack-plugin": "~1.0.1", 58 | "git-dirty": "~1.0.2", 59 | "glob": "~7.0.3", 60 | "immutability-helper": "~2.0.0", 61 | "karma": "~1.1.2", 62 | "karma-chrome-launcher": "~1.0.1", 63 | "karma-cli": "~1.0.0", 64 | "karma-mocha": "~1.0.1", 65 | "karma-phantomjs-launcher": "~1.0.0", 66 | "karma-webpack": "~1.7.0", 67 | "mocha": "~3.0.1", 68 | "node-sass": "~3.7.0", 69 | "phantomjs": "~2.1.7", 70 | "phantomjs-polyfill": "0.0.2", 71 | "phantomjs-prebuilt": "~2.1.7", 72 | "postcss-loader": "~0.9.1", 73 | "react": "~15.3.0", 74 | "react-addons-css-transition-group": "~15.3.0", 75 | "react-addons-test-utils": "~15.3.0", 76 | "react-docgen": "~2.8.2", 77 | "react-dom": "~15.3.0", 78 | "react-transform-catch-errors": "~1.0.2", 79 | "react-transform-hmr": "~1.0.4", 80 | "redbox-react": "~1.2.4", 81 | "rimraf": "~2.5.2", 82 | "sass-lint": "1.8.2", 83 | "sass-loader": "~4.0.0", 84 | "sinon": "git://github.com/sinonjs/sinon.git#b672042043517b9f84e14ed0fb8265126168778a", 85 | "style-loader": "~0.13.1", 86 | "webpack": "~1.13.0", 87 | "html-webpack-plugin": "~2.18.0", 88 | "webpack-dev-server": "~1.14.1" 89 | }, 90 | "scripts": { 91 | "build": "cross-env NODE_ENV=production npm run babel && npm run sass && npm run tsd", 92 | "start": "node server.js", 93 | "babel": "babel ./src --out-dir ./lib", 94 | "sass": "cpx \"./src/**/*.scss\" ./lib", 95 | "tsd": "cpx \"./src/**/*.d.ts\" ./lib" 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /screenshots/rta.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaximKomlev/react-toolbox-additions/0e68b68de8a3bd2fc8e976665f6d87ad8a276255/screenshots/rta.gif -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var WebpackDevServer = require('webpack-dev-server'); 3 | var config = require('./webpack.config'); 4 | 5 | config.entry.push('webpack-dev-server/client?http://localhost:8000'); 6 | 7 | new WebpackDevServer(webpack(config), { 8 | contentBase: config.output.path 9 | }).listen(8000, '', function (err, result) { 10 | if (err) { 11 | return console.log(err); 12 | } 13 | 14 | console.log('Listening at http://localhost:8000/'); 15 | }); -------------------------------------------------------------------------------- /src/_config.scss: -------------------------------------------------------------------------------- 1 | @import "~react-toolbox/lib/commons"; 2 | 3 | $indent: $unit *.5 !default; 4 | $button-margin: 0 $indent 0 0 !default; 5 | 6 | @mixin space-between { 7 | margin-right: $indent; 8 | } 9 | -------------------------------------------------------------------------------- /src/accordion/_config.scss: -------------------------------------------------------------------------------- 1 | @import "~react-toolbox/lib/button/theme"; 2 | @import "../config"; 3 | 4 | $chord-content-padding: 1 * $unit !default; 5 | $chord-label-disabled-opacity: .2 !default; 6 | $chord-label-v-padding: 0.2 * $unit !default; 7 | $chord-label-height: 4.8 * $unit !default; 8 | $chord-icon-height: 2.4 * $unit !default; 9 | $chord-text-height: 1.4 * $unit !default; 10 | $chord-pointer-color: $color-primary !default; 11 | $chord-text: $color-black !default; 12 | $chord-text-color: $chord-text !default; 13 | $chord-text-inactive-color: rgba($chord-text, .7) !default; 14 | -------------------------------------------------------------------------------- /src/accordion/accordion.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import classnames from 'classnames'; 3 | import { themr } from 'react-css-themr'; 4 | import { ACCORDION } from '../identifiers.js'; 5 | import InjectChord from './chord.js'; 6 | 7 | const factory = (Chord) => { 8 | class Accordion extends Component { 9 | 10 | static propTypes = { 11 | children: PropTypes.node, 12 | className: PropTypes.string, 13 | disableAnimation: PropTypes.bool, 14 | index: PropTypes.number, 15 | onChange: PropTypes.func, 16 | theme: PropTypes.shape({ 17 | accordion: PropTypes.string 18 | }) 19 | }; 20 | 21 | static defaultProps = { 22 | index: 0, 23 | disableAnimation: false 24 | }; 25 | 26 | //events handler 27 | handleHeaderClick (event, idx, item) { 28 | idx = parseInt(idx); 29 | if (this.props.onChange) { 30 | this.props.onChange(idx); 31 | } 32 | if (item.props.onClick) { 33 | item.props.onClick(event); 34 | } 35 | }; 36 | 37 | //private methods 38 | renderChords () { 39 | const chords = []; 40 | let idx = 0; 41 | 42 | React.Children.forEach(this.props.children, (item) => { 43 | if (item.type === Chord) { 44 | if (item.props.children) { 45 | chords.push(React.cloneElement(item, { 46 | id: idx, 47 | key: idx, 48 | theme: this.props.theme, 49 | active: this.props.index === idx, 50 | onClick: this.handleHeaderClick.bind(this, event, idx, item) 51 | })); 52 | ++idx; 53 | } 54 | } 55 | }); 56 | 57 | return chords; 58 | } 59 | 60 | render () { 61 | const { className, disableAnimation, theme} = this.props; 62 | const classNames = classnames(theme.accordion, className, { 63 | [theme.disableAnimation]: disableAnimation 64 | }); 65 | 66 | return ( 67 |
68 |
69 | {this.renderChords()} 70 |
71 |
72 | ); 73 | } 74 | } 75 | 76 | return Accordion; 77 | }; 78 | 79 | const Accordion = factory(InjectChord); 80 | export default themr(ACCORDION)(Accordion); 81 | export { factory as accordionFactory }; 82 | export { Accordion }; 83 | -------------------------------------------------------------------------------- /src/accordion/chord.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import classnames from 'classnames'; 4 | import { FontIcon } from 'react-toolbox/lib/font_icon'; 5 | import { themr } from 'react-css-themr'; 6 | import { CHORD } from '../identifiers.js'; 7 | 8 | const contentId = 'contentId'; 9 | 10 | class Chord extends Component { 11 | 12 | constructor(props) { 13 | super(); 14 | 15 | this.state.active = props.active; 16 | } 17 | 18 | static propTypes = { 19 | active: PropTypes.bool, 20 | activeClassName: PropTypes.string, 21 | className: PropTypes.string, 22 | disabled: PropTypes.bool, 23 | hidden: PropTypes.bool, 24 | label: PropTypes.node, 25 | labelIcon: PropTypes.node, 26 | labelPostIcon: PropTypes.node, 27 | onActive: PropTypes.func, 28 | onClick: PropTypes.func, 29 | theme: PropTypes.shape({ 30 | active: PropTypes.string, 31 | disabled: PropTypes.string, 32 | hidden: PropTypes.string, 33 | label: PropTypes.string, 34 | content: PropTypes.string 35 | }) 36 | }; 37 | 38 | static defaultProps = { 39 | active: false, 40 | className: '', 41 | disabled: false, 42 | hidden: false 43 | }; 44 | 45 | state = { 46 | active: false 47 | }; 48 | 49 | //private methods 50 | transitionEndEventName () { 51 | let i, 52 | undefined, 53 | el = document.createElement('div'), 54 | transitions = { 55 | 'transition':'transitionend', 56 | 'OTransition':'otransitionend', 57 | 'MozTransition':'transitionend', 58 | 'WebkitTransition':'webkitTransitionEnd' 59 | }; 60 | 61 | for (i in transitions) { 62 | if (transitions.hasOwnProperty(i) && el.style[i] !== undefined) { 63 | return transitions[i]; 64 | } 65 | } 66 | } 67 | 68 | collapse() { 69 | let contentEl = ReactDOM.findDOMNode(this.refs[contentId]); 70 | if (contentEl) { 71 | contentEl.style.height = getComputedStyle(contentEl).height; 72 | contentEl.offsetHeight; 73 | contentEl.style.height = 0; 74 | } 75 | } 76 | 77 | expand() { 78 | let contentEl = ReactDOM.findDOMNode(this.refs[contentId]); 79 | if (contentEl) { 80 | let initialHeight = getComputedStyle(contentEl).height; 81 | contentEl.style.height = 'auto'; 82 | let newHeight = getComputedStyle(contentEl).height; 83 | contentEl.style.height = initialHeight; 84 | contentEl.offsetHeight; 85 | contentEl.style.height = newHeight; 86 | 87 | let self = this; 88 | contentEl.addEventListener(this.transitionEndEventName(), function transitionEnd(event) { 89 | if (self.state.active) { 90 | contentEl.style.height = 'auto'; 91 | } 92 | contentEl.removeEventListener(self.transitionEndEventName(), transitionEnd, false); 93 | }, false) 94 | } 95 | } 96 | 97 | componentDidMount() { 98 | window.addEventListener('resize', this.handleResize); 99 | if (this.props.active) { 100 | this.expand(); 101 | } else { 102 | this.collapse(); 103 | } 104 | } 105 | 106 | componentDidUpdate (prevProps) { 107 | if (!prevProps.active && this.props.active && this.props.onActive) { 108 | this.props.onActive(); 109 | } 110 | } 111 | 112 | componentWillReceiveProps (nextProps) { 113 | this.state.active = nextProps.active; 114 | if (this.state.active) { 115 | this.expand(); 116 | } else { 117 | this.collapse(); 118 | } 119 | } 120 | 121 | componentWillUnmount () { 122 | window.removeEventListener('resize', this.handleResize); 123 | } 124 | 125 | //events handler 126 | handleClick = (event) => { 127 | if (!this.props.disabled && this.props.onClick) { 128 | this.props.onClick(event); 129 | } 130 | }; 131 | 132 | handleResize = () => { 133 | if (this.state.active) { 134 | this.expand(); 135 | } else { 136 | this.collapse(); 137 | } 138 | }; 139 | 140 | render () { 141 | const { 142 | children, id, key, 143 | active, activeClassName, className, disabled, hidden, 144 | label, labelIcon, labelPostIcon, theme 145 | } = this.props; 146 | const _className = classnames(theme.label, { 147 | [theme.withText]: label, 148 | [theme.withIcon]: labelIcon, 149 | [theme.withPostIcon]: labelPostIcon 150 | }); 151 | 152 | let lIcon = labelIcon; 153 | if (lIcon && Object.prototype.toString.call(lIcon) !== '[object String]') { 154 | lIcon = React.cloneElement(labelIcon, { 155 | className: classnames(labelIcon.props.className, theme.icon) 156 | }); 157 | } else if (labelIcon) { 158 | lIcon = 159 | } 160 | 161 | let lpIcon = labelPostIcon; 162 | if (lpIcon && Object.prototype.toString.call(lpIcon) !== '[object String]') { 163 | lpIcon = React.cloneElement(labelPostIcon, { 164 | className: classnames(labelPostIcon.props.className, theme.postIcon) 165 | }); 166 | } else if (labelPostIcon) { 167 | lpIcon = 168 | } 169 | 170 | return ( 171 |
172 | 177 |
178 | {children} 179 |
180 |
181 | ); 182 | } 183 | } 184 | 185 | export default themr(CHORD)(Chord); 186 | export { Chord }; 187 | -------------------------------------------------------------------------------- /src/accordion/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import ReactToolbox from "../index"; 3 | 4 | export interface AccordionTheme { 5 | /** 6 | * Used for the root element. 7 | */ 8 | accordion?: string; 9 | } 10 | 11 | interface AccordionProps extends ReactToolbox.Props { 12 | /** 13 | * The class(s) will be applied to the root elemt. 14 | */ 15 | className 16 | /** 17 | * Children to pass through the component. 18 | */ 19 | children?: React.ReactNode; 20 | /** 21 | * Disable the animation on children expanding/collapsing. 22 | * @default false 23 | */ 24 | disableAnimation?: boolean; 25 | /** 26 | * Expanded element 27 | * @default 0 28 | */ 29 | index?: number; 30 | /** 31 | * Callback function that is fired when the Chord changes. 32 | */ 33 | onChange?: Function; 34 | /** 35 | * Classnames object defining the component style. 36 | */ 37 | theme?: AccordionTheme; 38 | } 39 | 40 | export class Accordion extends React.Component { } 41 | 42 | export interface ChordTheme { 43 | /** 44 | * Added to the chord element (label and content) in case it's active. 45 | */ 46 | active?: string; 47 | /** 48 | * Added to the chord element in case it's disabled. 49 | */ 50 | disabled?: string; 51 | /** 52 | * Added to the chord element in case it's hidden. 53 | */ 54 | hidden?: string; 55 | /** 56 | * Base style of header of chord element. 57 | */ 58 | label?: string; 59 | /** 60 | * Base style of content of chord element. 61 | */ 62 | content?: string; 63 | } 64 | 65 | interface ChordProps extends ReactToolbox.Props { 66 | /** 67 | * If true, the current component is visible. 68 | */ 69 | active?: boolean; 70 | /** 71 | * Additional class name to provide custom styling for the active Chord. 72 | */ 73 | activeClassName?: string; 74 | /** 75 | * If true, the current component is not clickable. 76 | * @default false 77 | */ 78 | disabled?: boolean; 79 | /** 80 | * If true, the current component is not visible. 81 | * @default false 82 | */ 83 | hidden?: boolean; 84 | /** 85 | * Label text for the chord header. Required. 86 | */ 87 | label: string; 88 | /** 89 | * Icon for chord header. Required. 90 | */ 91 | labelIcon: string; 92 | /** 93 | * Icon behind of the label for chord header. 94 | */ 95 | labelPostIcon: string; 96 | /** 97 | * Callback function that is fired when the Chord is activated. 98 | */ 99 | onActive?: Function; 100 | /** 101 | * Callback function that is fired when the chord label is clicked. 102 | */ 103 | onClick?: Function; 104 | /** 105 | * Classnames object defining the component style. 106 | */ 107 | theme?: ChordTheme; 108 | } 109 | 110 | export class Chord extends React.Component { } 111 | -------------------------------------------------------------------------------- /src/accordion/index.js: -------------------------------------------------------------------------------- 1 | import { themr } from 'react-css-themr'; 2 | import { ACCORDION } from '../identifiers.js'; 3 | import { accordionFactory } from './accordion.js'; 4 | import { Chord } from './chord.js'; 5 | import theme from './theme.scss'; 6 | 7 | const applyTheme = (Component) => themr(ACCORDION, theme)(Component); 8 | const ThemedChord = applyTheme(Chord); 9 | const ThemedAccordion = applyTheme(accordionFactory(ThemedChord)); 10 | 11 | export { ThemedChord as Chord }; 12 | export { ThemedAccordion as Accordion }; 13 | -------------------------------------------------------------------------------- /src/accordion/readme.md: -------------------------------------------------------------------------------- 1 | # Accordion 2 | 3 | Provides functionality of Accordion control, actually the component is not defined at(https://www.google.com/design/spec/components), 4 | but designed by using standard components from material design utilizing react-toolbox components library (http://react-toolbox.com/). 5 | It is easy to use and configure, also it is highly customizable. 6 | 7 | 8 | ```jsx 9 | import React, { PropTypes } from 'react'; 10 | import FontIcon from 'react-toolbox/lib/font_icon'; 11 | import Input from 'react-toolbox/lib/input'; 12 | import {Button} from 'react-toolbox/lib/button'; 13 | import { ListCheckbox, ListSubHeader, List, ListItem, ListDivider, ListItemText, ListItemContent } from 'react-toolbox/lib/list'; 14 | 15 | import {Accordion, Chord} from '../../lib/accordion'; 16 | import style from '../style'; 17 | 18 | class AccordionTest extends React.Component { 19 | 20 | static propTypes = { 21 | }; 22 | 23 | static defaultProps = { 24 | } 25 | 26 | state = { 27 | index: 0 28 | } 29 | 30 | onChange = (idx) => { 31 | this.setState({ 32 | index: idx 33 | }); 34 | } 35 | 36 | onActive1 = () => { 37 | console.info('Selected chord: ' + this.state.index); 38 | } 39 | 40 | onActive2 = () => { 41 | console.info('Selected chord: ' + this.state.index); 42 | } 43 | 44 | onActive3 = () => { 45 | console.info('Selected chord: ' + this.state.index); 46 | } 47 | 48 | render () { 49 | 50 | return ( 51 |
52 |
Accordion
53 |

Accordion component.

54 | 55 |
56 | 61 | 65 |
66 | The project provides additions for react-toolbox which are not defined at material design and therefore were not included to original react-toolbox components library but it could be useful to have. 67 |
68 |
69 | } 71 | label='Number Two' 72 | onActive={this.onActive2} 73 | > 74 |
75 | 80 | 81 |
85 | 86 |
87 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 |
102 |
103 | 104 |
105 | ); 106 | } 107 | } 108 | ``` 109 | 110 | If you want to provide a theme via context, the component key is `ERTAccordion`. 111 | 112 | ## Accordion 113 | 114 | This component is a wrapper and the main controller of the content that is being displayed as his children. 115 | 116 | ### Properties 117 | 118 | | Name | Type | Default | Description| 119 | |:-----|:-----|:-----|:-----| 120 | | `className` | `String` | `''` | Additional class name to provide custom styling.| 121 | | `disableAnimation` | `Boolean` | `false` | Disable the animation on children expanding/collapsing.| 122 | | `index` | `Number` | `0` | Expanded child | 123 | | `children` | `Elements` | [] | Collection of children .| 124 | | `onChange` | `Function` | | Callback function that is fired when the chord changes.| 125 | 126 | ### Theming 127 | 128 | | Name | Description| 129 | |:---------|:-----------| 130 | | `accordion` | Used for the root element.| 131 | 132 | ## Chord 133 | 134 | Represent a single child element with some properties to describe the chors itself and get children elements as content. 135 | 136 | ### Properties 137 | 138 | | Name | Type | Default | Description| 139 | |:-----|:-----|:-----|:-----| 140 | | `active` | `Boolean` | `false` | If true, the current component is expanded.| 141 | | `activeClassName` | `String` | `''` | Additional class name to provide custom styling for the expanded chord.| 142 | | `className` | `String` | `''` | Additional class name to provide custom styling for each chord.| 143 | | `disabled` | `Boolean` | `false` | If true, the current chord component is not clickable.| 144 | | `hidden` | `Boolean` | `false` | If true, the current chord component is not visible.| 145 | | `label` | `String` | | Label text for the chord header. | 146 | | `labelIcon` | `String` | | Icon for chord header. | 147 | | `labelPostIcon` | `String` | | Icon behind of the label for chord header. | 148 | | `onActive` | `Function` | | Callback function that is fired when the chord is expanded. | 149 | | `onClick` | `Function` | | Callback function that is fired when the chord label is clicked. | 150 | 151 | It is required to provide either a label or an icon (or both). 152 | 153 | ### Theme 154 | 155 | | Name | Description| 156 | |:---------|:-----------| 157 | | `active` | Added to the chord element (label and content) in case it's active.| 158 | | `disabled` | Added to the chord element in case it's disabled.| 159 | | `hidden` | Added to the chord element in case it's hidden.| 160 | | `label` | Base style of header of chord element.| 161 | | `content` | Base style of content of chord element.| 162 | -------------------------------------------------------------------------------- /src/accordion/theme.scss: -------------------------------------------------------------------------------- 1 | @import "./config"; 2 | 3 | .accordion { 4 | position: relative; 5 | height: 100%; 6 | width: 100%; 7 | display: block; 8 | 9 | &.disableAnimation { 10 | .chord { 11 | .content { 12 | transition: none !important; 13 | } 14 | } 15 | } 16 | 17 | .chord { 18 | position: relative; 19 | width: 100%; 20 | display: block; 21 | 22 | &.hidden { 23 | display: none !important; 24 | } 25 | 26 | &.disabled { 27 | opacity: $chord-label-disabled-opacity; 28 | .label { 29 | cursor: default; 30 | } 31 | } 32 | 33 | .content { 34 | position: relative; 35 | height: 0; 36 | box-sizing: border-box; 37 | overflow: hidden; 38 | opacity: 0; 39 | transition: height $animation-duration ease-in-out, opacity $animation-duration linear; 40 | } 41 | 42 | &.active { 43 | margin-bottom: 2 * $chord-content-padding; 44 | 45 | .content { 46 | opacity: 1; 47 | height: auto; 48 | transition: height $animation-duration ease-in-out, opacity $animation-duration linear; 49 | } 50 | 51 | .label { 52 | color: $chord-pointer-color; 53 | margin-bottom: $chord-content-padding; 54 | } 55 | } 56 | 57 | .label { 58 | display: block; 59 | width: 100%; 60 | font-size: $chord-text-height; 61 | font-weight: $font-weight-semi-bold; 62 | padding-top: $chord-label-v-padding; 63 | padding-bottom: $chord-label-v-padding; 64 | margin-bottom: 0; 65 | color: $chord-text-color; 66 | text-transform: uppercase; 67 | transition-timing-function: $animation-curve-default; 68 | transition-duration: $animation-duration; 69 | transition-property: box-shadow, color; 70 | cursor: pointer; 71 | 72 | .text { 73 | position: relative; 74 | display: inline-block; 75 | height: $chord-icon-height; 76 | line-height: $chord-icon-height; 77 | vertical-align: top; 78 | margin-right: $chord-content-padding; 79 | } 80 | 81 | .icon, 82 | .postIcon { 83 | position: relative; 84 | display: inline-block; 85 | height: $chord-icon-height; 86 | width: $chord-icon-height; 87 | line-height: $chord-icon-height; 88 | } 89 | 90 | &.withIcon { 91 | } 92 | 93 | &.withPostButton { 94 | } 95 | 96 | &.withText { 97 | .icon { 98 | margin-right: $chord-content-padding; 99 | } 100 | } 101 | 102 | .postIcon { 103 | } 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/filepicker/_config.scss: -------------------------------------------------------------------------------- 1 | @import "../config"; 2 | 3 | $input-min-width: $unit * 4 !default; 4 | -------------------------------------------------------------------------------- /src/filepicker/filepicker.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import classnames from 'classnames'; 3 | import { themr } from 'react-css-themr'; 4 | 5 | import {BrowseButton as InjectButton} from 'react-toolbox/lib/button'; 6 | import InjectInput from 'react-toolbox/lib/input'; 7 | import { FILEPICKER } from '../identifiers.js'; 8 | 9 | const factory = (Input, Button) => { 10 | class FilePicker extends Component { 11 | 12 | constructor(props) { 13 | super(props); 14 | this.state = {value: props.value}; 15 | } 16 | 17 | static propTypes = { 18 | buttonProperties: React.PropTypes.object, 19 | className: PropTypes.string, 20 | inline: PropTypes.bool, 21 | inputProperties: React.PropTypes.object, 22 | onChange: PropTypes.func, 23 | theme: PropTypes.shape({ 24 | button: PropTypes.string, 25 | filepicker: PropTypes.string, 26 | inline: PropTypes.string, 27 | input: PropTypes.string 28 | }), 29 | value: PropTypes.string 30 | }; 31 | 32 | static defaultProps = { 33 | buttonProperties: {label: 'BROWSE'}, 34 | className: '', 35 | inline: false, 36 | inputProperties: {label: 'SELECT FILE'}, 37 | value: undefined 38 | } 39 | 40 | state = { 41 | value: this.props.value 42 | } 43 | 44 | componentWillReceiveProps (nextProps) { 45 | if (this.state.value !== nextProps.value) { 46 | this.setState({ 47 | value: nextProps.value 48 | }); 49 | } 50 | } 51 | 52 | handlerBrowse = (e) => { 53 | let files = e.target.files; 54 | 55 | if (files && files.length) { 56 | if (this.state.value !== files[0].name) { 57 | if (this.props.onChange) { 58 | this.props.onChange(files[0], files[0].name); 59 | } 60 | } 61 | 62 | this.setState({ 63 | value: files[0].name 64 | }); 65 | } 66 | } 67 | 68 | render() { 69 | const { className, buttonProperties, inputProperties, value, inline, theme } = this.props; 70 | const css = inline ? 'inline' : null; 71 | 72 | return ( 73 |
74 | 79 |
85 | );} 86 | } 87 | return FilePicker; 88 | }; 89 | 90 | const FilePicker = factory(InjectInput, InjectButton); 91 | 92 | export default themr(FILEPICKER)(FilePicker); 93 | export { 94 | factory as filePickerFactory 95 | }; 96 | export { FilePicker }; 97 | -------------------------------------------------------------------------------- /src/filepicker/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import ReactToolbox from "react-toolbox/lib/index"; 3 | 4 | export interface FilePickerTheme { 5 | /** 6 | * Used for the root element. 7 | */ 8 | filepicker?: string; 9 | /** 10 | * Used for the input component. 11 | */ 12 | input?: string; 13 | /** 14 | * Used for inline style of component. 15 | */ 16 | inline?: string; 17 | /** 18 | * Used for the button component. 19 | */ 20 | button?: string; 21 | } 22 | 23 | interface FilePickerProps extends ReactToolbox.Props { 24 | /** 25 | * This class will be applied to the root elemt. 26 | */ 27 | className?: string; 28 | /** 29 | * This is button base properties like: accent, disabled, icon, ... 30 | */ 31 | buttonProperties?: object 32 | /** 33 | * This is input base properties like: hint, disabled, icon, ... 34 | */ 35 | inputProperties?: object 36 | /** 37 | * If true, the component will apear inline. 38 | * @default false 39 | */ 40 | inline?: boolean 41 | /** 42 | * Callback called when the input is changing. 43 | */ 44 | onChange?: Function; 45 | /** 46 | * Classnames object defining the component style. 47 | */ 48 | theme?: FilePickerTheme; 49 | /** 50 | * This is initial value of input component. 51 | */ 52 | value?: string 53 | } 54 | 55 | export class FilePicker extends React.Component { } 56 | 57 | export default FilePicker; 58 | -------------------------------------------------------------------------------- /src/filepicker/index.js: -------------------------------------------------------------------------------- 1 | import { themr } from 'react-css-themr'; 2 | import { FILEPICKER } from '../identifiers.js'; 3 | import { filePickerFactory } from './filepicker.js'; 4 | import { BrowseButton } from 'react-toolbox/lib/button'; 5 | import { Input } from 'react-toolbox/lib/input'; 6 | import theme from './theme.scss'; 7 | 8 | const FilePicker = filePickerFactory(Input, BrowseButton); 9 | 10 | const ThemedFilePicker = themr(FILEPICKER, theme)(FilePicker); 11 | export default ThemedFilePicker; 12 | export { ThemedFilePicker as FilePicker }; 13 | -------------------------------------------------------------------------------- /src/filepicker/readme.md: -------------------------------------------------------------------------------- 1 | # FilePicker 2 | 3 | Provides functionality of filepicker control, actually the component is not defined at(https://www.google.com/design/spec/components), 4 | but designed by using standard components from material design utilizing react-toolbox components library (http://react-toolbox.com/). 5 | It is easy to use and configure, also it is highly customizable. 6 | 7 | 8 | ```jsx 9 | import FilePicker from 'react-toolbox-additions/lib/filepicker'; 10 | 11 | const FilePickerTest = () => { 12 | 13 | onChange (fobj, fname) { 14 | console.info('Selected file : ' + fname); 15 | } 16 | 17 | return ( 18 | 23 | ); 24 | }; 25 | ``` 26 | 27 | If you want to provide a theme via context, the component key is `ERTFilePicker`. 28 | 29 | ## Properties 30 | 31 | | Name | Type | Default | Description| 32 | |:-----|:-----|:-----|:-----| 33 | | `className` | `String` | `` | This class will be applied to the root elemt.| 34 | | `buttonProperties` | `String` | {label: 'BROWSE'} | This is button base properties like: accent, disabled, icon, ...| 35 | | `inputProperties` | `String` | {label: 'SELECT FILE'} | This is input base properties like: hint, disabled, icon, ...| 36 | | `inline` | `boolean` | false | If true, the component will apear inline.| 37 | | `onChange` | `Function` | | Callback called when the input is changing.| 38 | | `theme` | `String` | | Classnames object defining the component style.| 39 | | `value` | `String` | `` | This is initial value of input component.| 40 | 41 | 42 | ## Theme 43 | 44 | | Name | Description| 45 | |:---------|:-----------| 46 | | `filepicker` | Used for the root element.| 47 | | `input` | Used for the input element.| 48 | | `inline` | Used for inline style of component.| 49 | | `button` | Used for the button element.| 50 | -------------------------------------------------------------------------------- /src/filepicker/theme.scss: -------------------------------------------------------------------------------- 1 | @import "./config"; 2 | 3 | .filepicker { 4 | 5 | > * { 6 | display: inline-block; 7 | vertical-align: middle; 8 | } 9 | 10 | &.inline { 11 | display: inline-block; 12 | vertical-align: middle; 13 | } 14 | 15 | .input { 16 | @include space-between; 17 | min-width: $input-min-width; 18 | 19 | & span:last-child { 20 | opacity: 1 !important; 21 | } 22 | } 23 | 24 | /*ie fix*/ 25 | .iefix { 26 | input { 27 | top: 0 !important; 28 | left: 0 !important; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/identifiers.js: -------------------------------------------------------------------------------- 1 | export const ACCORDION = 'ERTAccordion'; 2 | export const CHORD = 'ERTChord'; 3 | export const PAGER = 'ERTPager'; 4 | export const FILEPICKER = 'ERTFilePicker'; 5 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | export Pager from './pager'; 2 | export FilePicker from './filepicker'; 3 | export * from './accordion'; 4 | -------------------------------------------------------------------------------- /src/pager/_config.scss: -------------------------------------------------------------------------------- 1 | @import "~react-toolbox/lib/button/theme"; 2 | @import "../config"; 3 | -------------------------------------------------------------------------------- /src/pager/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import ReactToolbox from "react-toolbox/lib/index"; 3 | 4 | export interface PagerTheme { 5 | /** 6 | * Used for the root element. 7 | */ 8 | pager?: string; 9 | /** 10 | * Used for the active page. 11 | */ 12 | active?: string; 13 | /** 14 | * Used for the first and last page buttons. 15 | */ 16 | firstLastPagesButton?: string; 17 | /** 18 | * Used for the next and previous arrow buttons. 19 | */ 20 | leftRightArrowButton?: string; 21 | /** 22 | * Used for the next and previous range pages buttons. 23 | */ 24 | leftRightRangeButton?: string; 25 | /** 26 | * Used for the regular page buttons. 27 | */ 28 | pagesButton?: string; 29 | } 30 | 31 | interface PagerProps extends ReactToolbox.Props { 32 | /** 33 | * Used for the previous button content. 34 | */ 35 | prevButtonLabel?: string; 36 | /** 37 | * Used for the left range button content 38 | */ 39 | rangeLeftButtonLabel?: string; 40 | /** 41 | * Used for the right range button content 42 | */ 43 | rangeRightButtonLabel?: string; 44 | /** 45 | * Used for the next button content 46 | */ 47 | nextButtonLabel?: string; 48 | /** 49 | * A Number with the currently selected page 50 | */ 51 | currentPage?: number; 52 | /** 53 | * A Number of total pages. 54 | */ 55 | totalPages?: number; 56 | /** 57 | * A Number of pages visible in control except next, previous and ranges buttons, the minimum value is 2. 58 | */ 59 | visiblePagesBlockSize?: number; 60 | /** 61 | * Callback called when the page is changing. 62 | */ 63 | onPageChange?: Function; 64 | /** 65 | * Classnames object defining the component style. 66 | */ 67 | theme?: PagerTheme; 68 | /** 69 | * This class will be applied to the root elemt. 70 | */ 71 | pagerClassName?: string; 72 | /** 73 | * Defining default style of first, last page buttons. 74 | * it can have following styles: 75 | * accent, 76 | * flat, 77 | * inverse, 78 | * mini, 79 | * neutral, 80 | * primary, 81 | * raised, 82 | * toggle 83 | */ 84 | firstLastPagesButtonStyles?: object; 85 | /** 86 | * Defining default style of next, previous buttons. 87 | * it can have following styles: 88 | * accent, 89 | * flat, 90 | * inverse, 91 | * mini, 92 | * neutral, 93 | * primary, 94 | * raised, 95 | * toggle 96 | */ 97 | leftRightArrowButtonStyles?: object; 98 | /** 99 | * Defining default style of left, right range buttons. 100 | * it can have following styles: 101 | * accent, 102 | * flat, 103 | * inverse, 104 | * mini, 105 | * neutral, 106 | * primary, 107 | * raised, 108 | * toggle 109 | */ 110 | leftRightRangeButtonStyles?: object; 111 | /** 112 | * Defining default style of regular page buttons. 113 | * it can have following styles: 114 | * accent, 115 | * flat, 116 | * inverse, 117 | * mini, 118 | * neutral, 119 | * primary, 120 | * raised, 121 | * toggle 122 | */ 123 | pagesButtonStyles?: object; 124 | } 125 | 126 | export class Pager extends React.Component { } 127 | 128 | export default Pager; 129 | -------------------------------------------------------------------------------- /src/pager/index.js: -------------------------------------------------------------------------------- 1 | import { themr } from 'react-css-themr'; 2 | import { PAGER } from '../identifiers.js'; 3 | import { pagerFactory } from './pager.js'; 4 | import Page from './page.js'; 5 | import theme from './theme.scss'; 6 | 7 | const Pager = pagerFactory(Page); 8 | 9 | const ThemedPager = themr(PAGER, theme)(Pager); 10 | export default ThemedPager; 11 | export { ThemedPager as Pager }; 12 | -------------------------------------------------------------------------------- /src/pager/page.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import classnames from 'classnames'; 3 | 4 | import Button from 'react-toolbox/lib/button'; 5 | 6 | export const BUTTON_TYPE = { 7 | PAGE: 0, 8 | RANGE: 1, 9 | ARROW: 2 10 | } 11 | 12 | class Page extends Component { 13 | 14 | constructor(props) { 15 | super(); 16 | 17 | this.state.data = props.data; 18 | this.state.buttonType = props.buttonType; 19 | this.state.label = props.children; 20 | } 21 | 22 | static propTypes = { 23 | activeClassName: PropTypes.string, 24 | buttonClassNames: PropTypes.object, 25 | buttonStyles: PropTypes.object, 26 | buttonType: PropTypes.oneOf([BUTTON_TYPE.PAGE, BUTTON_TYPE.RANGE, BUTTON_TYPE.ARROW]), 27 | onPageClick: PropTypes.func 28 | } 29 | 30 | static defaultProps = { 31 | buttonType: 0, 32 | buttonStyles: {0: {flat: true}}, 33 | buttonClassNames: {0: null} 34 | } 35 | 36 | state = { 37 | disabled: false, 38 | active: false, 39 | data: 0, 40 | buttonType: 0, 41 | label: null 42 | } 43 | 44 | componentWillReceiveProps (nextProps) { 45 | if (nextProps.children && !isNaN(nextProps.children) 46 | && this.state.label !== nextProps.children) { 47 | this.setState({ 48 | label: nextProps.children 49 | }); 50 | } 51 | if (nextProps.buttonType && !isNaN(nextProps.buttonType) 52 | && this.state.buttonType !== nextProps.buttonType) { 53 | this.setState({ 54 | buttonType: nextProps.buttonType 55 | }); 56 | } 57 | } 58 | 59 | //events handlers 60 | handlerClick() { 61 | if (this.props.onPageClick){ 62 | this.props.onPageClick(this.state.data, this.state.buttonType); 63 | } 64 | } 65 | 66 | //rendering 67 | render () { 68 | let styles = this.props.buttonStyles[this.state.buttonType]; 69 | let className = this.props.buttonClassNames[this.state.buttonType]; 70 | 71 | if (styles['disabled'] === undefined) { 72 | styles = {...styles, disabled: this.state.disabled} 73 | } else { 74 | styles['disabled'] = this.state.disabled; 75 | } 76 | 77 | return ( 78 | 84 | ); 85 | } 86 | 87 | //public methods 88 | update(active, data, label, type) { 89 | this.setState({ 90 | active: active, 91 | data: data, 92 | buttonType: type, 93 | label: label 94 | }); 95 | } 96 | 97 | disable(v) { 98 | this.setState({ 99 | disabled: v 100 | }); 101 | } 102 | } 103 | 104 | export default Page; -------------------------------------------------------------------------------- /src/pager/pager.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import classnames from 'classnames'; 3 | import { themr } from 'react-css-themr'; 4 | 5 | import InjectPage, {BUTTON_TYPE} from './page.js'; 6 | import { PAGER } from '../identifiers.js'; 7 | 8 | const isOnePage = (first, last) => { 9 | return first === last || !last; 10 | }; 11 | 12 | const isBorderPage = (curr, border) => { 13 | return curr === border; 14 | }; 15 | 16 | const clone = (src) => { 17 | return JSON.parse(JSON.stringify(src)); 18 | } 19 | 20 | const prevPageId = 'prev'; 21 | const nextPageId = 'next'; 22 | 23 | const initialVisiblePagesBlockSize = 3; 24 | const initialCurrentPage = 1; 25 | const first = 1; 26 | /* 27 | * adjustment of start and end elements at range to have equal elements during navigation 28 | * 2 - because we already have the rendered first and last button 29 | */ 30 | const adjustment = 2; 31 | 32 | const factory = (Page) => { 33 | class Pager extends Component { 34 | 35 | constructor(props) { 36 | super(); 37 | 38 | if (props) { 39 | this.state.currentPage = props.currentPage; 40 | this.state.totalPages = props.totalPages; 41 | } 42 | } 43 | 44 | static propTypes = { 45 | currentPage: PropTypes.oneOfType([ 46 | PropTypes.string, 47 | PropTypes.number 48 | ]).isRequired, 49 | firstLastPagesButtonStyles: PropTypes.object, 50 | leftRightArrowButtonStyles: PropTypes.object, 51 | leftRightRangeButtonStyles: PropTypes.object, 52 | nextButtonLabel: PropTypes.oneOfType([ 53 | PropTypes.string, 54 | PropTypes.element 55 | ]), 56 | onPageChange: PropTypes.func, 57 | pagerClassName: PropTypes.string, 58 | pagesButtonStyles: PropTypes.object, 59 | prevButtonLabel: PropTypes.oneOfType([ 60 | PropTypes.string, 61 | PropTypes.element 62 | ]), 63 | rangeLeftButtonLabel: PropTypes.oneOfType([ 64 | PropTypes.string, 65 | PropTypes.element 66 | ]), 67 | rangeRightButtonLabel: PropTypes.oneOfType([ 68 | PropTypes.string, 69 | PropTypes.element 70 | ]), 71 | theme: PropTypes.shape({ 72 | pager: PropTypes.string, 73 | active: PropTypes.string, 74 | firstLastPagesButton: PropTypes.string, 75 | leftRightArrowButton: PropTypes.string, 76 | leftRightRangeButton: PropTypes.string, 77 | pagesButton: PropTypes.string 78 | }), 79 | totalPages: PropTypes.oneOfType([ 80 | PropTypes.string, 81 | PropTypes.number 82 | ]).isRequired, 83 | visiblePagesBlockSize: PropTypes.oneOfType([ 84 | PropTypes.string, 85 | PropTypes.number 86 | ]).isRequired 87 | } 88 | 89 | static defaultProps = { 90 | currentPage: initialCurrentPage, 91 | totalPages: initialCurrentPage, 92 | firstLastPagesButtonStyles: {flat: true}, 93 | leftRightArrowButtonStyles: {raised: true}, 94 | leftRightRangeButtonStyles: {flat: true}, 95 | nextButtonLabel: '\u003E', 96 | pagesButtonStyles: {flat: true}, 97 | prevButtonLabel: '\u003C', 98 | rangeLeftButtonLabel: '...', 99 | rangeRightButtonLabel: '...', 100 | visiblePagesBlockSize: initialVisiblePagesBlockSize 101 | } 102 | 103 | //fields 104 | _ranges = { 105 | leftEnd: null, 106 | rightStart: null 107 | } 108 | 109 | _pages = []; 110 | 111 | state = { 112 | currentPage: initialCurrentPage, 113 | totalPages: initialCurrentPage 114 | } 115 | 116 | componentWillReceiveProps (nextProps) { 117 | if (nextProps.currentPage && !isNaN(Number(nextProps.currentPage)) 118 | && this.state.currentPage !== nextProps.currentPage) { 119 | this._pages.splice(0); 120 | this.setState({ 121 | currentPage: Number(nextProps.currentPage < first ? first : nextProps.currentPage) 122 | }); 123 | } 124 | if (nextProps.totalPages && !isNaN(Number(nextProps.totalPages)) 125 | && this.state.totalPages !== nextProps.totalPages) { 126 | this._pages.splice(0); 127 | this.setState({ 128 | totalPages: Number(nextProps.totalPages < first ? first : nextProps.totalPages) 129 | }); 130 | } 131 | if (nextProps.visiblePagesBlockSize && !isNaN(Number(nextProps.visiblePagesBlockSize)) 132 | && this.state.visiblePagesBlockSize !== nextProps.visiblePagesBlockSize) { 133 | this._pages.splice(0); 134 | this.setState({ 135 | visiblePagesBlockSize: Number( nextProps.visiblePagesBlockSize < nextProps.totalPages ? nextProps.visiblePagesBlockSize : nextProps.totalPages) 136 | }); 137 | } 138 | } 139 | 140 | componentWillUnmount() { 141 | this._pages.splice(0); 142 | } 143 | 144 | componentDidMount() { 145 | this.updateState(this.state.currentPage, this.state.totalPages); 146 | } 147 | 148 | componentDidUpdate() { 149 | this.updatePages(this.state.currentPage, this.state.totalPages); 150 | } 151 | 152 | //events handlers 153 | handlerPageClick (page, type) { 154 | if (type === BUTTON_TYPE.RANGE) { 155 | this.handlerRangeClick(page); 156 | return; 157 | } 158 | 159 | const oldValue = this.state.currentPage; 160 | const newValue = page; 161 | 162 | this.updateState(newValue); 163 | 164 | if (this.props.onPageChange) { 165 | this.props.onPageChange(newValue, oldValue); 166 | } 167 | } 168 | 169 | handlerRangeClick (key) { 170 | const curr = this.state.currentPage; 171 | const last = this.state.totalPages; 172 | 173 | const right = !this._ranges.rightStart ? last : this._ranges.rightStart; 174 | const left = !this._ranges.leftEnd ? first : this._ranges.leftEnd; 175 | 176 | let newValue = curr; 177 | let sum = first + left; 178 | if (key === prevPageId) { 179 | newValue = (sum >> 1); //rounding to left 180 | } else { 181 | sum = last + right; 182 | newValue = (sum >> 1) + (sum % 2); //rounding to right 183 | } 184 | 185 | this.updateState(newValue); 186 | 187 | if (this.props.onPageChange) { 188 | this.props.onPageChange(newValue, curr); 189 | } 190 | } 191 | 192 | handlerPrevNextClick (key, type) { 193 | const oldValue = this.state.currentPage; 194 | const newValue = key === prevPageId ? oldValue - 1 : oldValue + 1; 195 | 196 | this.updateState(newValue); 197 | 198 | if (this.props.onPageChange) { 199 | this.props.onPageChange(newValue, oldValue); 200 | } 201 | } 202 | 203 | //private methods 204 | updateState(newValue) { 205 | this.state.currentPage = newValue; 206 | 207 | if (this._pages.length > 0) { 208 | this.updatePages(newValue, this.state.totalPages); 209 | } else { 210 | this.forceUpdate(); 211 | } 212 | } 213 | 214 | updatePages (currPage, totalPages) { 215 | let curr = currPage; 216 | let last = totalPages; 217 | 218 | const blockSize = this.props.visiblePagesBlockSize === 1 ? adjustment : this.props.visiblePagesBlockSize; 219 | 220 | const padding = blockSize >> 1; 221 | const left = curr - padding * (blockSize % 2); //in case of even visiblePagesBlockSize 222 | const right = curr + padding; 223 | 224 | const blocksNumber = Math.ceil(last / blockSize); 225 | let currentBlock = Math.ceil(curr / blockSize); 226 | 227 | let start = ((currentBlock - 1) * blockSize) + first; 228 | let end = start + blockSize - first; 229 | 230 | if (currentBlock === 1) { //adjust set of buttons if current is on the left boundary 231 | end += adjustment; 232 | end = (last - first) === end ? last : end; 233 | } else if (currentBlock < blocksNumber) { //adjustment set of buttons if current is between boundaries 234 | start = left; 235 | end = right; 236 | 237 | currentBlock = Math.ceil((totalPages - end === 1 ? totalPages : end ) / blockSize); 238 | } 239 | 240 | if (currentBlock === blocksNumber) { //adjustment set of buttons if current is on the right boundary 241 | start = last - (blockSize + adjustment - first); 242 | start = start - 1 <= first ? first : start; 243 | end = last; 244 | } 245 | 246 | this.refs[prevPageId].disable(curr === first); 247 | 248 | let buttonIdx = 0; 249 | 250 | if (currentBlock > 1 && (start - 1) > first) { 251 | this.refs[buttonIdx].update(curr === first, first, String(first), BUTTON_TYPE.FLPAGE); 252 | ++buttonIdx; 253 | 254 | this.refs[buttonIdx].update(false, prevPageId, this.props.rangeLeftButtonLabel, BUTTON_TYPE.RANGE); 255 | ++buttonIdx; 256 | } 257 | 258 | for (let i = start; i <= last && i <= end; ++i) { 259 | let bType = BUTTON_TYPE.PAGE; 260 | if (i === first || i === last) { 261 | bType = BUTTON_TYPE.FLPAGE; 262 | } 263 | this.refs[buttonIdx].update(curr === i, i, String(i), bType); 264 | ++buttonIdx; 265 | } 266 | 267 | if (currentBlock < blocksNumber && end < last) { 268 | this.refs[buttonIdx].update(false, nextPageId, this.props.rangeRightButtonLabel, BUTTON_TYPE.RANGE); 269 | ++buttonIdx; 270 | 271 | this.refs[buttonIdx].update(curr === last, last, String(last), BUTTON_TYPE.FLPAGE); 272 | ++buttonIdx; 273 | } 274 | 275 | this.refs[nextPageId].disable(curr === last); 276 | 277 | // keep range boundaries to calculate correct navigation through the range 278 | this._ranges.leftEnd = start; 279 | this._ranges.rightStart = end - 1; 280 | } 281 | 282 | //rendering 283 | renderPages (currPage, totalPages) { 284 | this._pages.splice(0); 285 | 286 | let curr = currPage; 287 | let last = totalPages < first ? first : totalPages; 288 | if (curr < first || curr > last) { 289 | curr = curr < first ? first : curr > last ? last : curr; 290 | this.setState({ 291 | currentPage: curr 292 | }); 293 | } 294 | 295 | const content = []; 296 | 297 | let buttonsCount = this.props.visiblePagesBlockSize + adjustment + 2; 298 | buttonsCount = buttonsCount > last ? last : buttonsCount; 299 | 300 | let possibleButtonClassNames = {}; 301 | possibleButtonClassNames[BUTTON_TYPE.FLPAGE] = classnames(this.props.theme.firstLastPagesButton); 302 | possibleButtonClassNames[BUTTON_TYPE.PAGE] = classnames(this.props.theme.pagesButton); 303 | possibleButtonClassNames[BUTTON_TYPE.RANGE] = classnames(this.props.theme.leftRightRangeButton); 304 | possibleButtonClassNames[BUTTON_TYPE.ARROW] = classnames(this.props.theme.leftRightArrowButton); 305 | 306 | let possibleLeftButtoStyles = {}; 307 | possibleLeftButtoStyles[BUTTON_TYPE.ARROW] = clone(this.props.leftRightArrowButtonStyles); 308 | possibleLeftButtoStyles[BUTTON_TYPE.ARROW].disabled = isOnePage(first, this.state.totalPages) || isBorderPage(this.state.currentPage, first); 309 | content.push( 310 | 318 | { this.props.prevButtonLabel } 319 | 320 | ); 321 | 322 | let possibleButtoStyles = {}; 323 | possibleButtoStyles[BUTTON_TYPE.FLPAGE] = this.props.firstLastPagesButtonStyles; 324 | possibleButtoStyles[BUTTON_TYPE.PAGE] = this.props.pagesButtonStyles; 325 | possibleButtoStyles[BUTTON_TYPE.RANGE] = this.props.leftRightRangeButtonStyles; 326 | for (let i = 0; i < buttonsCount; ++i) { 327 | content.push( 328 | 338 | { String(i) } 339 | 340 | ); 341 | this._pages.push(i); 342 | } 343 | 344 | let possibleRightButtoStyles = {}; 345 | possibleRightButtoStyles[BUTTON_TYPE.ARROW] = clone(this.props.leftRightArrowButtonStyles); 346 | possibleRightButtoStyles[BUTTON_TYPE.ARROW].disabled = isOnePage(first, this.state.totalPages) || isBorderPage(this.state.currentPage, this.state.totalPages); 347 | content.push( 348 | 356 | { this.props.nextButtonLabel } 357 | 358 | ); 359 | 360 | return content; 361 | } 362 | 363 | render () { 364 | return ( 365 |
366 | { 367 | this.renderPages(this.state.currentPage, this.state.totalPages) 368 | } 369 |
); 370 | } 371 | } 372 | 373 | return Pager; 374 | }; 375 | 376 | const Pager = factory(InjectPage); 377 | 378 | export default themr(PAGER)(Pager); 379 | export { 380 | factory as pagerFactory 381 | }; 382 | export { Pager }; 383 | -------------------------------------------------------------------------------- /src/pager/readme.md: -------------------------------------------------------------------------------- 1 | # Pager 2 | 3 | Provides functionality of pagination control, actually the component is not defined at(https://www.google.com/design/spec/components), 4 | but designed by using standard components from material design utilizing react-toolbox components library (http://react-toolbox.com/). 5 | It is easy to use and configure, also it is highly customizable. 6 | 7 | 8 | ```jsx 9 | import Pager from 'react-toolbox-additions/lib/pager'; 10 | import FontIcon from 'react-toolbox-additions/lib/font_icon'; 11 | 12 | const PagerTest = () => { 13 | 14 | onPageChange = (newPage, oldPage) => { 15 | console.info('Selected page : ' + newPage + ', Previous page: ' + oldPage); 16 | } 17 | 18 | return ( 19 | ) } 21 | nextButtonLabel={ () } 22 | rangeLeftButtonLabel={()} 23 | rangeRightButtonLabel={()} 24 | totalPages={29} 25 | currentPage={5} 26 | visiblePagesBlockSize={3} 27 | onPageChange={onPageChange} 28 | /> 29 | ); 30 | }; 31 | ``` 32 | 33 | If you want to provide a theme via context, the component key is `ERTPager`. 34 | 35 | ## Properties 36 | 37 | | Name | Type | Default | Description| 38 | |:-----|:-----|:-----|:-----| 39 | | `prevButtonLabel` | `String` | `\u003C` | Used for the previous button label content, it can be text or FontIcon element.| 40 | | `nextButtonLabel` | `String` | `\u003E` | Used for the next button label content, it can be text or FontIcon element.| 41 | | `rangeLeftButtonLabel` | `String` | `...` | Used for the left range button label content, it can be text or FontIcon element.| 42 | | `rangeRightButtonLabel` | `String` | `...` | Used for the right range button label content, it can be text or FontIcon element.| 43 | | `currentPage` | `Number` | `1` | A Number with the currently selected page.| 44 | | `totalPages` | `Number` | `1` | A Number of total pages.| 45 | | `visiblePagesBlockSize` | `Number` | `3` | A Number of pages visible in control except next, previous and ranges buttons, the minimum value is 2.| 46 | | `onPageChange` | `Function` | | Callback called when the page is changing.| 47 | | `theme` | `String` | | Classnames object defining the component style.| 48 | | `pagerClassName` | `String` | | This class will be applied to the root elemt.| 49 | | `firstLastPagesButtonStyles` | `Object` | {raised: true} | Defining default style of first and last page buttons.| 50 | | `leftRightArrowButtonStyles` | `Object` | {raised: true} | Defining default style of next and previous arrow buttons.| 51 | | `leftRightRangeButtonStyles` | `Object` | {flat: true} | Defining default style of left and right range buttons.| 52 | | `pagesButtonStyles` | `Object` | {flat: true} | Defining default style of regular pages buttons.| 53 | 54 | 55 | ## Theme 56 | 57 | | Name | Description| 58 | |:---------|:-----------| 59 | | `pager` | Used for the root element.| 60 | | `active` | Used for the active page.| 61 | | `firstLastPagesButton` | Used for the first and last arrow buttons.| 62 | | `leftRightArrowButton` | Used for the next and previous arrow buttons.| 63 | | `leftRightRangeButton` | Used for the next and previous range pages buttons.| 64 | | `pagesButton` | Used for the regular pages buttons.| -------------------------------------------------------------------------------- /src/pager/theme.scss: -------------------------------------------------------------------------------- 1 | @import "./config"; 2 | 3 | .pager { 4 | display: inline-block; 5 | vertical-align: middle; 6 | 7 | .firstLastPagesButton, 8 | .leftRightArrowButton, 9 | .leftRightRangeButton, 10 | .pagesButton { 11 | min-width: $button-height; 12 | margin: $button-margin; 13 | } 14 | 15 | .active { 16 | text-decoration: underline; 17 | } 18 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const pkg = require('./package'); 2 | const webpack = require('webpack'); 3 | const path = require('path'); 4 | const autoprefixer = require('autoprefixer'); 5 | var ExtractTextPlugin = require('extract-text-webpack-plugin'); 6 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 7 | 8 | module.exports = { 9 | context: __dirname, 10 | devtool: 'source-map', 11 | entry: [ 12 | 'babel-polyfill', 13 | './example' 14 | ], 15 | output: { 16 | path: path.join(__dirname, 'dist'), 17 | filename: 'bundle.js', 18 | publicPath: './' 19 | }, 20 | resolve: { 21 | extensions: ['', '.scss', '.js', '.jsx', '.json'], 22 | modulesDirectories: [ 23 | 'node_modules', 24 | path.resolve(__dirname, './node_modules') 25 | ], 26 | packageMains: ['browser', 'web', 'browserify', 'main', 'style'] 27 | }, 28 | resolveLoader: { fallback: path.join(__dirname, "node_modules") }, 29 | module: { 30 | loaders: [ 31 | { 32 | test: /\.(js|jsx)$/, 33 | loader: 'babel', 34 | exclude: [/(node_modules)/], 35 | query: { 36 | presets: ['es2015', 'stage-0', 'react'] 37 | } 38 | }, { 39 | test: /\.(scss|css)$/, 40 | loader: ExtractTextPlugin.extract('style', 'css?sourceMap&modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!postcss!sass?sourceMap') 41 | } 42 | ] 43 | }, 44 | postcss: [autoprefixer], 45 | plugins: [ 46 | new ExtractTextPlugin('styles.css', { allChunks: true }), 47 | new webpack.NoErrorsPlugin(), 48 | new webpack.DefinePlugin({ 49 | 'process.env.NODE_ENV': JSON.stringify('development'), 50 | VERSION: JSON.stringify(pkg.version) 51 | }), 52 | new HtmlWebpackPlugin({ 53 | template: path.join(__dirname, 'example/index.html') 54 | }) 55 | ] 56 | }; 57 | --------------------------------------------------------------------------------