├── .babelrc ├── .editorconfig ├── .eslintrc.js ├── .gitattributes ├── .gitignore ├── .npmignore ├── .prettierignore ├── .prettierrc ├── .storybook ├── addons.js ├── config.js └── preview-head.html ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── LICENSE.md ├── README.md ├── jest.config.js ├── package.json ├── scripts └── demos2Stories.js ├── src ├── components │ ├── Theme │ │ └── index.js │ ├── badges │ │ ├── Badge.js │ │ ├── Badge.style.js │ │ ├── demos │ │ │ ├── IconOverIcon.js │ │ │ ├── IconOverText.js │ │ │ ├── NumberOverIcon.js │ │ │ ├── NumberOverText.js │ │ │ └── TextOverButton.js │ │ └── index.js │ ├── buttons │ │ ├── Button.js │ │ ├── Button.style.js │ │ ├── demos │ │ │ ├── Fab.js │ │ │ ├── FabAccent.js │ │ │ ├── FabDisabled.js │ │ │ ├── FabMini.js │ │ │ ├── FabPrimary.js │ │ │ ├── Flat.js │ │ │ ├── FlatAccent.js │ │ │ ├── FlatDisabled.js │ │ │ ├── FlatPrimary.js │ │ │ ├── Icon.js │ │ │ ├── IconAccent.js │ │ │ ├── IconDisabled.js │ │ │ ├── IconMini.js │ │ │ ├── IconPrimary.js │ │ │ ├── Raised.js │ │ │ ├── RaisedAccent.js │ │ │ ├── RaisedDisabled.js │ │ │ └── RaisedPrimary.js │ │ └── index.js │ ├── cards │ │ ├── Card.style.js │ │ ├── CardActions.style.js │ │ ├── CardMedia.style.js │ │ ├── CardMenu.style.js │ │ ├── CardSubtitleText.style.js │ │ ├── CardSupportingText.style.js │ │ ├── CardTitle.style.js │ │ ├── CardTitleText.style.js │ │ ├── demos │ │ │ ├── Event.js │ │ │ ├── Image.js │ │ │ ├── Square.js │ │ │ └── Wide.js │ │ └── index.js │ ├── checkbox │ │ ├── Checkbox.js │ │ ├── Checkbox.style.js │ │ ├── demos │ │ │ └── Checkbox.js │ │ ├── images.js │ │ └── index.js │ ├── chips │ │ ├── ButtonChip.js │ │ ├── Chip.js │ │ ├── Chip.style.js │ │ ├── ChipContact.js │ │ ├── demos │ │ │ ├── Basic.js │ │ │ ├── Button.js │ │ │ ├── Contact.js │ │ │ ├── Deletable.js │ │ │ └── DeletableContact.js │ │ └── index.js │ ├── dialog │ │ ├── Dialog.js │ │ ├── Dialog.style.js │ │ ├── demos │ │ │ ├── Basic.js │ │ │ └── FullWidthActions.js │ │ └── index.js │ ├── fonts │ │ ├── MaterialIcons.js │ │ ├── Roboto.js │ │ └── index.js │ ├── icons │ │ ├── Icon.js │ │ ├── Icon.style.js │ │ └── index.js │ ├── layout │ │ ├── Spacer.style.js │ │ └── index.js │ ├── list │ │ ├── List.style.js │ │ ├── demos │ │ │ ├── AvatarsAndActions.js │ │ │ ├── AvatarsAndControls.js │ │ │ ├── Icons.js │ │ │ ├── Simple.js │ │ │ ├── ThreeLine.js │ │ │ └── TwoLine.js │ │ └── index.js │ ├── menu │ │ ├── Menu.js │ │ ├── Menu.style.js │ │ ├── MenuDivider.js │ │ ├── MenuItem.js │ │ ├── demos │ │ │ ├── LowerLeft.js │ │ │ ├── LowerRight.js │ │ │ ├── UpperLeft.js │ │ │ ├── UpperRight.js │ │ │ └── _shared.js │ │ ├── getRelativePosition.js │ │ └── index.js │ ├── progress │ │ ├── Progress.js │ │ ├── Progress.style.js │ │ ├── demos │ │ │ ├── Progress.js │ │ │ └── ProgressIndeterminate.js │ │ └── index.js │ ├── radio │ │ ├── Radio.js │ │ ├── Radio.style.js │ │ ├── demos │ │ │ └── Radio.js │ │ └── index.js │ ├── ripple │ │ ├── Ripple.js │ │ ├── Ripple.style.js │ │ ├── helpers.js │ │ └── index.js │ ├── slider │ │ ├── Slider.js │ │ ├── Slider.style.js │ │ ├── demos │ │ │ ├── default.js │ │ │ ├── disabled.js │ │ │ └── startingValue.js │ │ └── index.js │ ├── snackbar │ │ ├── Action.style.js │ │ ├── Message.style.js │ │ ├── Snackbar.js │ │ ├── Snackbar.style.js │ │ ├── demos │ │ │ ├── Snackbar.js │ │ │ └── SnackbarWithAction.js │ │ └── index.js │ ├── spinner │ │ ├── Spinner.js │ │ ├── Spinner.style.js │ │ ├── demos │ │ │ ├── Spinner.js │ │ │ └── SpinnerSingleColor.js │ │ └── index.js │ ├── switch │ │ ├── Switch.js │ │ ├── Switch.style.js │ │ ├── demos │ │ │ └── Switch.js │ │ └── index.js │ ├── tables │ │ ├── Table.style.js │ │ ├── demos │ │ │ └── DataTable.js │ │ └── index.js │ ├── textfield │ │ ├── Textfield.js │ │ ├── Textfield.style.js │ │ ├── demos │ │ │ ├── Error.js │ │ │ ├── HelperText.js │ │ │ ├── MultiLine.js │ │ │ └── SingleLine.js │ │ └── index.js │ ├── toast │ │ ├── Toast.js │ │ ├── Toast.style.js │ │ └── index.js │ └── tooltips │ │ ├── Tooltip.js │ │ ├── Tooltip.style.js │ │ ├── demos │ │ ├── Above.js │ │ ├── Below.js │ │ ├── Large.js │ │ ├── Left.js │ │ ├── MultiLine.js │ │ └── Right.js │ │ └── index.js ├── hocs │ ├── index.js │ └── proxyStyledStatics.js ├── index.js ├── initGlobals.js ├── input │ ├── Input.js │ └── index.js ├── mixins │ ├── animations.style.js │ ├── arrow.style.js │ ├── index.js │ ├── shadows.style.js │ └── type.style.js ├── theme │ ├── animation │ │ ├── defaults.js │ │ └── index.js │ ├── badge │ │ ├── defaults.js │ │ └── index.js │ ├── button │ │ ├── defaults.js │ │ └── index.js │ ├── card │ │ ├── defaults.js │ │ └── index.js │ ├── chip │ │ ├── defaults.js │ │ └── index.js │ ├── colorDefinitions.js │ ├── colors │ │ ├── defaults.js │ │ └── index.js │ ├── createTheme.js │ ├── createThemer.js │ ├── dataTable │ │ ├── defaults.js │ │ └── index.js │ ├── defaultTheme.js │ ├── dialog │ │ └── index.js │ ├── footer │ │ ├── defaults.js │ │ └── index.js │ ├── grid │ │ ├── defaults.js │ │ └── index.js │ ├── iconToggle │ │ ├── defaults.js │ │ └── index.js │ ├── index.js │ ├── layout │ │ ├── defaults.js │ │ └── index.js │ ├── list │ │ ├── defaults.js │ │ └── index.js │ ├── menu │ │ ├── defaults.js │ │ └── index.js │ ├── progress │ │ ├── defaults.js │ │ └── index.js │ ├── radio │ │ ├── defaults.js │ │ └── index.js │ ├── shadows │ │ ├── defaults.js │ │ └── index.js │ ├── snackbar │ │ └── index.js │ ├── spinner │ │ ├── defaults.js │ │ └── index.js │ ├── switch │ │ ├── defaults.js │ │ └── index.js │ ├── textField │ │ ├── defaults.js │ │ └── index.js │ ├── tooltip │ │ ├── defaults.js │ │ └── index.js │ └── typography │ │ ├── defaults.js │ │ └── index.js └── util │ ├── colors.js │ ├── getters.js │ ├── index.js │ ├── math.js │ └── units.js ├── stories ├── badges.stories.js ├── buttons.stories.js ├── cards.stories.js ├── checkbox.stories.js ├── chips.stories.js ├── decorators │ └── wrapStory.js ├── dialog.stories.js ├── list.stories.js ├── menu.stories.js ├── progress.stories.js ├── radio.stories.js ├── slider.stories.js ├── snackbar.stories.js ├── spinner.stories.js ├── switch.stories.js ├── tables.stories.js ├── textfield.stories.js └── tooltips.stories.js ├── tests ├── _setup │ ├── test-bundler.js │ ├── textOnly.js │ └── until.js ├── badges │ └── Badge.spec.js ├── buttons │ └── index.spec.js ├── checkbox │ └── Checkbox.spec.js ├── chips │ ├── ButtonChip.spec.js │ ├── Chip.spec.js │ └── ChipContact.spec.js ├── input │ └── Input.spec.js ├── menu │ ├── Divider.spec.js │ ├── Menu.spec.js │ ├── MenuItem.spec.js │ └── getRelativePosition.spec.js ├── progress │ └── Progress.spec.js ├── radio │ └── Radio.spec.js ├── ripple │ └── index.spec.js ├── slider │ └── Slider.spec.js ├── snackbar │ └── Snackbar.spec.js ├── spinner │ └── Spinner.spec.js ├── switch │ └── Switch.spec.js ├── textfield │ └── Textfield.spec.js ├── tooltips │ └── Tooltip.spec.js └── util │ ├── colors.spec.js │ ├── getters.spec.js │ ├── math.spec.js │ └── units.spec.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env", "react", "stage-0"], 3 | "env": { 4 | "production": { 5 | "only": ["app"], 6 | "plugins": [ 7 | "transform-react-remove-prop-types", 8 | "transform-react-constant-elements", 9 | "transform-react-inline-elements" 10 | ] 11 | }, 12 | "test": { 13 | "presets": ["env", "react", "stage-0"], 14 | "plugins": ["dynamic-import-node"] 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | insert_final_newline = true 9 | indent_style = space 10 | indent_size = 2 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: 'babel-eslint', 3 | extends: 'airbnb', 4 | env: { 5 | browser: true, 6 | node: true, 7 | jest: true, 8 | es6: true, 9 | }, 10 | plugins: ['react', 'jsx-a11y'], 11 | parserOptions: { 12 | ecmaVersion: 6, 13 | sourceType: 'module', 14 | ecmaFeatures: { 15 | jsx: true, 16 | }, 17 | }, 18 | rules: { 19 | semi: ['error', 'never'], 20 | 'arrow-parens': ['error', 'always'], 21 | 'arrow-body-style': [2, 'as-needed'], 22 | 'comma-dangle': [2, 'always-multiline'], 23 | 'function-paren-newline': 0, 24 | 'space-in-parens': 0, 25 | 'import/imports-first': 0, 26 | 'import/newline-after-import': 0, 27 | 'import/no-dynamic-require': 0, 28 | 'import/no-extraneous-dependencies': 0, 29 | 'import/no-named-as-default': 0, 30 | 'import/prefer-default-export': 0, 31 | 'import/extensions': 0, 32 | 'object-curly-newline': 0, 33 | indent: 0, 34 | 'jsx-a11y/aria-props': 2, 35 | 'jsx-a11y/heading-has-content': 0, 36 | 'jsx-a11y/label-has-for': 0, 37 | 'jsx-a11y/mouse-events-have-key-events': 2, 38 | 'jsx-a11y/role-has-required-aria-props': 2, 39 | 'jsx-a11y/role-supports-aria-props': 2, 40 | 'max-len': 0, 41 | 'newline-per-chained-call': 0, 42 | 'no-confusing-arrow': 0, 43 | 'no-console': 1, 44 | 'no-mixed-operators': 0, 45 | 'no-use-before-define': 0, 46 | 'prefer-template': 2, 47 | 'class-methods-use-this': 0, 48 | 'react/forbid-prop-types': 0, 49 | 'react/prop-types': 0, 50 | 'react/jsx-first-prop-new-line': [2, 'multiline'], 51 | 'react/jsx-filename-extension': 0, 52 | 'react/jsx-no-target-blank': 0, 53 | 'react/no-unescaped-entities': 0, 54 | 'react/jsx-uses-react': 2, 55 | 'react/react-in-jsx-scope': 2, 56 | 'react/require-extension': 0, 57 | 'react/self-closing-comp': 0, 58 | 'react/require-default-props': 0, 59 | 'require-yield': 0, 60 | 'import/no-webpack-loader-syntax': 0, 61 | }, 62 | globals: { 63 | shallowComponent: true, 64 | mountComponent: true, 65 | }, 66 | settings: { 67 | 'import/core-modules': ['material-components'], 68 | 'import/resolver': { 69 | webpack: { 70 | config: './internals/webpack/webpack.prod.babel.js', 71 | }, 72 | }, 73 | }, 74 | } 75 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # From https://github.com/Danimoth/gitattributes/blob/master/Web.gitattributes 2 | 3 | # Handle line endings automatically for files detected as text 4 | # and leave all files detected as binary untouched. 5 | * text=auto 6 | 7 | # 8 | # The above will handle all files NOT found below 9 | # 10 | 11 | # 12 | ## These files are text and should be normalized (Convert crlf => lf) 13 | # 14 | 15 | # source code 16 | *.php text 17 | *.css text 18 | *.sass text 19 | *.scss text 20 | *.less text 21 | *.styl text 22 | *.js text eol=lf 23 | *.coffee text 24 | *.json text 25 | *.htm text 26 | *.html text 27 | *.xml text 28 | *.svg text 29 | *.txt text 30 | *.ini text 31 | *.inc text 32 | *.pl text 33 | *.rb text 34 | *.py text 35 | *.scm text 36 | *.sql text 37 | *.sh text 38 | *.bat text 39 | 40 | # templates 41 | *.ejs text 42 | *.hbt text 43 | *.jade text 44 | *.haml text 45 | *.hbs text 46 | *.dot text 47 | *.tmpl text 48 | *.phtml text 49 | 50 | # server config 51 | .htaccess text 52 | .nginx.conf text 53 | 54 | # git config 55 | .gitattributes text 56 | .gitignore text 57 | .gitconfig text 58 | 59 | # code analysis config 60 | .jshintrc text 61 | .jscsrc text 62 | .jshintignore text 63 | .csslintrc text 64 | 65 | # misc config 66 | *.yaml text 67 | *.yml text 68 | .editorconfig text 69 | 70 | # build config 71 | *.npmignore text 72 | *.bowerrc text 73 | 74 | # Heroku 75 | Procfile text 76 | .slugignore text 77 | 78 | # Documentation 79 | *.md text 80 | LICENSE text 81 | AUTHORS text 82 | 83 | 84 | # 85 | ## These files are binary and should be left untouched 86 | # 87 | 88 | # (binary is a macro for -text -diff) 89 | *.png binary 90 | *.jpg binary 91 | *.jpeg binary 92 | *.gif binary 93 | *.ico binary 94 | *.mov binary 95 | *.mp4 binary 96 | *.mp3 binary 97 | *.flv binary 98 | *.fla binary 99 | *.swf binary 100 | *.gz binary 101 | *.zip binary 102 | *.7z binary 103 | *.ttf binary 104 | *.eot binary 105 | *.woff binary 106 | *.pyc binary 107 | *.pdf binary 108 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Don't check auto-generated stuff into git 2 | coverage 3 | build 4 | lib 5 | node_modules 6 | stats.json 7 | 8 | # Cruft 9 | .DS_Store 10 | npm-debug.log 11 | .idea 12 | yarn-error.log 13 | 14 | # Local scripts 15 | copy-local.sh 16 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .storybook 2 | coverage 3 | src 4 | stories 5 | tests 6 | .babelrc 7 | .editorconfig 8 | .eslintrc.js 9 | .prettierrc 10 | .travis.yml 11 | jest.config.js 12 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | src/**/*.style.js 2 | scripts/**/*.js 3 | .storybook/**/*.js 4 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | --- 2 | semi: false 3 | singleQuote: true 4 | trailingComma: all 5 | arrowParens: always 6 | -------------------------------------------------------------------------------- /.storybook/addons.js: -------------------------------------------------------------------------------- 1 | import '@storybook/addon-actions/register'; 2 | import '@storybook/addon-links/register'; 3 | -------------------------------------------------------------------------------- /.storybook/config.js: -------------------------------------------------------------------------------- 1 | import { configure } from '@storybook/react'; 2 | 3 | // automatically import all files ending in *.stories.js 4 | const req = require.context('../stories', true, /.stories.js$/); 5 | function loadStories() { 6 | req.keys().forEach((filename) => req(filename)); 7 | } 8 | 9 | configure(loadStories, module); 10 | -------------------------------------------------------------------------------- /.storybook/preview-head.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '6' 4 | before_install: 5 | - npm install -g npm@^4 6 | cache: yarn 7 | before_deploy: 8 | - yarn manualPrepublish 9 | deploy: 10 | skip_cleanup: true 11 | provider: npm 12 | email: kegan+isogon@keganmyers.com 13 | api_key: 14 | secure: OtRyB1Eovxs4ahZlVR0jEzmfutSHA0ldhpwE/1F1Ed2ZmBBzouqdSSxnY9urcb1QlM7D/RUXvMnDosUfHVesXkGpp+j3Oar/QmrBGhf2pKiwIvGqY23YKI1NKW8llYuWVgLyCKYF+vCw5lT2OoqRTEUqEV45T3BU3myU/greOzF7Q8M1Wl9DYXlGoIinYJl9Y9gW3YKFvAACaWye8QeCeSzVWDgPkx3EfUvZbhxq/sB3plm5yvNxLckIw9j1+PHjjjl9CVmihjFpleBX+Z9zUUltqBewrV8qappdA7DoXVbkwa69Y+npv+d52JqW2uZgRzi1RxKQK7r+wL7EAZxYq2XCFQUyvb8gAIpJW/1d9Y+W4N+WjuJNCz8dwsFNXOBvcElC+RNor6keBsdsYwIQvRanvBuoHf/oE6LjWerQ/jO+CLHZJ+WXdvSwAE9zxGUbpVz9ZfuAh5YVZkfHptVZgDoF4Ze8Gmjr/nLf7ISbuDL6X9oFAIqrtdaovoeyQddA+AR3tjjLdyJ7OQN8R3AIxVKmjA8xeYSQlW/7hBM3CZS/Wk1ruoXOGsUIb1CPqQdxEr028bn8JIFuqoh6U0rdb5coZaxixFZJT+1fjuz9np9xfUNbsq65i5xFUOu2dsyKgGTgAif1TaNeX2XF3SZPjN7dXRTQDv94Q3IJO73QTmo= 15 | on: 16 | tags: true 17 | repo: isogon/styled-mdl 18 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, and in the interest of 4 | fostering an open and welcoming community, we pledge to respect all people who 5 | contribute through reporting issues, posting feature requests, updating 6 | documentation, submitting pull requests or patches, and other activities. 7 | 8 | We are committed to making participation in this project a harassment-free 9 | experience for everyone, regardless of level of experience, gender, gender 10 | identity and expression, sexual orientation, disability, personal appearance, 11 | body size, race, ethnicity, age, religion, or nationality. 12 | 13 | Examples of unacceptable behavior by participants include: 14 | 15 | * The use of sexualized language or imagery 16 | * Personal attacks 17 | * Trolling or insulting/derogatory comments 18 | * Public or private harassment 19 | * Publishing other's private information, such as physical or electronic 20 | addresses, without explicit permission 21 | * Other unethical or unprofessional conduct 22 | 23 | Project maintainers have the right and responsibility to remove, edit, or 24 | reject comments, commits, code, wiki edits, issues, and other contributions 25 | that are not aligned to this Code of Conduct, or to ban temporarily or 26 | permanently any contributor for other behaviors that they deem inappropriate, 27 | threatening, offensive, or harmful. 28 | 29 | By adopting this Code of Conduct, project maintainers commit themselves to 30 | fairly and consistently applying these principles to every aspect of managing 31 | this project. Project maintainers who do not follow or enforce the Code of 32 | Conduct may be permanently removed from the project team. 33 | 34 | This Code of Conduct applies both within project spaces and in public spaces 35 | when an individual is representing the project or its community. 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 38 | reported by contacting the project maintainer at contact@mxstbr.com. All 39 | complaints will be reviewed and investigated and will result in a response that 40 | is deemed necessary and appropriate to the circumstances. Maintainers are 41 | obligated to maintain confidentiality with regard to the reporter of an 42 | incident. 43 | 44 | 45 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 46 | version 1.3.0, available at 47 | [http://contributor-covenant.org/version/1/3/0/][version] 48 | 49 | [homepage]: http://contributor-covenant.org 50 | [version]: http://contributor-covenant.org/version/1/3/0/ 51 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Maximilian Stoiber 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![npm version](https://img.shields.io/npm/v/styled-mdl.svg?style=flat-square)](https://www.npmjs.com/package/styled-mdl) 2 | [![Build Status](https://travis-ci.org/isogon/styled-mdl.svg?branch=master)](https://travis-ci.org/isogon/styled-mdl) 3 | 4 | To get started with development 5 | 6 | 1. clone this repo, 7 | 2. `yarn setup` 8 | 3. `yarn start` 9 | 10 | To use in your project 11 | 12 | 1. `yarn add styled-mdl` 13 | 2. `import { Button } from 'styled-mdl'` 14 | 3. read all the code to figure out how to use it because the scrub developers haven't bothered to write/publish any docs yet. 15 | 4. actually you can clone this repo down 16 | 5. `yarn setup` 17 | 6. `yarn start` 18 | 7. `localhost:3000` 19 | 8. and look at all the demonstrations 20 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | collectCoverageFrom: [ 3 | 'src/**/*.js', 4 | '!src/**/*.style.js', 5 | '!src/theme/**/*.js', 6 | '!src/globals/**/*.js', 7 | '!src/index.js', 8 | '!src/*/index.js', 9 | ], 10 | coverageThreshold: { 11 | global: { 12 | statements: 80, 13 | branches: 80, 14 | functions: 90, 15 | lines: 90, 16 | }, 17 | }, 18 | moduleDirectories: ['node_modules'], 19 | setupTestFrameworkScriptFile: '/tests/_setup/test-bundler.js', 20 | testRegex: 'tests/.*\\.spec\\.js$', 21 | } 22 | -------------------------------------------------------------------------------- /scripts/demos2Stories.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* eslint-disable no-console */ 4 | 5 | const fs = require('fs'); 6 | const path = require('path'); 7 | const glob = require('glob'); 8 | const startCase = require('lodash/startCase'); 9 | const dest = path.resolve(__dirname, '../stories'); 10 | const source = path.resolve(__dirname, '../src'); 11 | 12 | const components = glob.sync('/components/*', { root: source }); 13 | 14 | const createContents = (component, demos) => 15 | `import React from 'react'; 16 | import { storiesOf } from '@storybook/react'; 17 | import wrapStory from './decorators/wrapStory'; 18 | 19 | ${demos.map((demo) => `import ${demo.component} from '${demo.path}';`).join('\n')} 20 | 21 | storiesOf('${component}', module) 22 | .addDecorator(wrapStory) 23 | ${demos.map( 24 | (demo) => `.add('${demo.name}', () => <${demo.component} />)` 25 | ).join('\n ')}; 26 | `; 27 | 28 | const writeStoryForDemos = (demoDir) => { 29 | const componentName = path.basename(demoDir); 30 | const storyFileName = path.resolve(dest, `${componentName}.stories.js`); 31 | const demos = glob 32 | .sync(`/components/${componentName}/demos/*.js`, { root: source }) 33 | .filter((file) => !file.endsWith('_shared.js')) 34 | .map((demo) => ({ 35 | path: path.relative(dest, demo), 36 | component: startCase(path.basename(demo).replace(/.js$/, '')).replace(/\s/g, ''), 37 | name: startCase(path.basename(demo).replace(/.js$/, '')), 38 | })); 39 | 40 | if (demos.length) { 41 | fs.writeFileSync(storyFileName, createContents(startCase(componentName), demos)); 42 | } 43 | }; 44 | 45 | console.log(`==================== 46 | watching demos 47 | ====================`); 48 | 49 | components.forEach((component) => { 50 | writeStoryForDemos(component); 51 | 52 | if (fs.existsSync(`${component}/demos`)) { 53 | fs.watch(`${component}/demos`, (type) => { 54 | if (type === 'rename') { 55 | console.log('updating stories for', path.basename(component)); 56 | writeStoryForDemos(component); 57 | } 58 | }); 59 | } 60 | }); 61 | -------------------------------------------------------------------------------- /src/components/Theme/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { ThemeProvider } from 'styled-components' 4 | 5 | import defaultTheme from '../../theme/defaultTheme' 6 | 7 | export function Theme({ children, theme = defaultTheme }) { 8 | return {children} 9 | } 10 | 11 | Theme.propTypes = { 12 | children: PropTypes.node, 13 | theme: PropTypes.object, 14 | } 15 | -------------------------------------------------------------------------------- /src/components/badges/Badge.js: -------------------------------------------------------------------------------- 1 | import { setPropTypes, setDisplayName, defaultProps, compose } from 'recompose' 2 | import PropTypes from 'prop-types' 3 | import React from 'react' 4 | 5 | import { BadgeWrap, BadgeText } from './Badge.style' 6 | import { proxyStyledStatics } from '../../hocs' 7 | 8 | export const BadgeBase = ({ 9 | text, 10 | children, 11 | __StyledComponent__: Styled, 12 | ...props 13 | }) => ( 14 | 15 | {children} 16 | {text} 17 | 18 | ) 19 | 20 | const enhance = compose( 21 | proxyStyledStatics(BadgeWrap), 22 | setDisplayName('Badge'), 23 | setPropTypes({ 24 | text: PropTypes.node, 25 | children: PropTypes.node, 26 | className: PropTypes.string, 27 | }), 28 | defaultProps({ 29 | background: true, 30 | }), 31 | ) 32 | 33 | export default enhance(BadgeBase) 34 | -------------------------------------------------------------------------------- /src/components/badges/Badge.style.js: -------------------------------------------------------------------------------- 1 | import { add, divide, subtract, cond, T, always, compose } from 'lodash/fp' 2 | import { call, prop, ifProp } from 'styled-tools' 3 | import { setDisplayName } from 'recompose' 4 | import styled from 'styled-components' 5 | 6 | const negate = (n) => -n 7 | const overlap = prop('overlap') 8 | const forButton = prop('forButton') 9 | const badgeSize = prop('theme.badgeSize') 10 | const badgeFontSize = prop('theme.badgeFontSize') 11 | const badgeOverlap = prop('theme.badgeOverlap') 12 | const badgePadding = prop('theme.badgePadding') 13 | const preferredFont = prop('theme.preferredFont') 14 | const badgeBackground = prop('theme.badgeBackground') 15 | const badgeColor = prop('theme.badgeColor') 16 | const badgeColorInverse = prop('theme.badgeColorInverse') 17 | const badgeBackgroundInverse = prop('theme.badgeBackgroundInverse') 18 | 19 | export const BadgeWrap = setDisplayName('BadgeWrap')(styled.div` 20 | position: relative; 21 | white-space: nowrap; 22 | display: inline-block; 23 | margin-right: ${cond([ 24 | [overlap, call(subtract, badgeSize, badgeOverlap)], 25 | [forButton, call(subtract, badgeSize, always(14))], 26 | [T, call(add, badgeSize, badgePadding)], 27 | ])}px; 28 | `) 29 | 30 | export const BadgeText = setDisplayName('BadgeText')(styled.div` 31 | display: flex; 32 | flex-direction: row; 33 | flex-wrap: wrap; 34 | justify-content: center; 35 | align-content: center; 36 | align-items: center; 37 | position: absolute; 38 | top: ${ifProp('forButton', -10, call(divide, badgeSize, always(-2)))}px; 39 | right: ${cond([ 40 | [forButton, always(-10)], 41 | [overlap, call(compose(negate, subtract), badgeSize, badgeOverlap)], 42 | [T, call(compose(negate, add), badgeSize, badgePadding)], 43 | ])}px; 44 | font-family: ${preferredFont}; 45 | font-weight: 600; 46 | font-size: ${badgeFontSize}px; 47 | width: ${badgeSize}px; 48 | height: ${badgeSize}px; 49 | border-radius: 50%; 50 | color: ${ifProp('background', badgeColor, badgeColorInverse)}; 51 | background: ${ifProp('background', badgeBackground, badgeBackgroundInverse)}; 52 | box-shadow: ${ifProp('background', 'none', '0 0 1px gray')}; 53 | `) 54 | -------------------------------------------------------------------------------- /src/components/badges/demos/IconOverIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Badge, Icon } from '../../../' 3 | 4 | export default () => ( 5 | }> 6 | 7 | 8 | ) 9 | -------------------------------------------------------------------------------- /src/components/badges/demos/IconOverText.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Badge, Icon } from '../../../' 3 | 4 | export default () => }>Walk the dog 5 | -------------------------------------------------------------------------------- /src/components/badges/demos/NumberOverIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Badge, Icon } from '../../../' 3 | 4 | export default () => ( 5 | 6 | 7 | 8 | ) 9 | -------------------------------------------------------------------------------- /src/components/badges/demos/NumberOverText.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Badge } from '../../../' 3 | 4 | export default () => Inbox 5 | -------------------------------------------------------------------------------- /src/components/badges/demos/TextOverButton.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Badge, Button } from '../../../' 3 | 4 | export default () => ( 5 | 6 | 7 | 8 | ) 9 | -------------------------------------------------------------------------------- /src/components/badges/index.js: -------------------------------------------------------------------------------- 1 | export Badge from './Badge' 2 | -------------------------------------------------------------------------------- /src/components/buttons/Button.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { 4 | compose, 5 | setPropTypes, 6 | setDisplayName, 7 | defaultProps, 8 | withProps, 9 | } from 'recompose' 10 | 11 | import { Ripple } from '../ripple' 12 | 13 | import { StyledButton, ButtonInner } from './Button.style' 14 | import { proxyStyledStatics } from '../../hocs' 15 | 16 | export const ButtonBase = ({ 17 | children, 18 | text, 19 | shouldShowRipple, 20 | isDark, 21 | isRound, 22 | __StyledComponent__: Button, 23 | ...props 24 | }) => ( 25 | 31 | ) 32 | 33 | const enhance = compose( 34 | proxyStyledStatics(StyledButton), 35 | setDisplayName('Button'), 36 | setPropTypes({ 37 | text: PropTypes.string, 38 | children: PropTypes.node, 39 | ripple: PropTypes.bool, 40 | fab: PropTypes.bool, 41 | icon: PropTypes.bool, 42 | href: PropTypes.string, 43 | to: PropTypes.string, 44 | disabled: PropTypes.bool, 45 | }), 46 | defaultProps({ 47 | ripple: true, 48 | }), 49 | withProps((props) => ({ 50 | shouldShowRipple: props.ripple && !props.disabled && !props.icon, 51 | isDark: 52 | (props.raised || props.fab) && 53 | !(props.colored || props.accent || props.primary), 54 | isRound: props.fab || props.icon, 55 | })), 56 | ) 57 | 58 | export default enhance(ButtonBase) 59 | -------------------------------------------------------------------------------- /src/components/buttons/demos/Fab.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Button, Icon } from '../../../' 3 | 4 | export default () => ( 5 | 8 | ) 9 | -------------------------------------------------------------------------------- /src/components/buttons/demos/FabAccent.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Button, Icon } from '../../../' 3 | 4 | export default () => ( 5 | 8 | ) 9 | -------------------------------------------------------------------------------- /src/components/buttons/demos/FabDisabled.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Button, Icon } from '../../../' 3 | 4 | export default () => ( 5 | 8 | ) 9 | -------------------------------------------------------------------------------- /src/components/buttons/demos/FabMini.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Button, Icon } from '../../../' 3 | 4 | export default () => ( 5 | 8 | ) 9 | -------------------------------------------------------------------------------- /src/components/buttons/demos/FabPrimary.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Button, Icon } from '../../../' 3 | 4 | export default () => ( 5 | 8 | ) 9 | -------------------------------------------------------------------------------- /src/components/buttons/demos/Flat.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Button } from '../../../' 3 | 4 | export default () => 5 | -------------------------------------------------------------------------------- /src/components/buttons/demos/FlatAccent.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Button } from '../../../' 3 | 4 | export default () => 5 | -------------------------------------------------------------------------------- /src/components/buttons/demos/FlatDisabled.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Button } from '../../../' 3 | 4 | export default () => 5 | -------------------------------------------------------------------------------- /src/components/buttons/demos/FlatPrimary.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Button } from '../../../' 3 | 4 | export default () => 5 | -------------------------------------------------------------------------------- /src/components/buttons/demos/Icon.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Button, Icon } from '../../../' 3 | 4 | export default () => ( 5 | 8 | ) 9 | -------------------------------------------------------------------------------- /src/components/buttons/demos/IconAccent.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Button, Icon } from '../../../' 3 | 4 | export default () => ( 5 | 8 | ) 9 | -------------------------------------------------------------------------------- /src/components/buttons/demos/IconDisabled.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Button, Icon } from '../../../' 3 | 4 | export default () => ( 5 | 8 | ) 9 | -------------------------------------------------------------------------------- /src/components/buttons/demos/IconMini.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Button, Icon } from '../../../' 3 | 4 | export default () => ( 5 | 8 | ) 9 | -------------------------------------------------------------------------------- /src/components/buttons/demos/IconPrimary.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Button, Icon } from '../../../' 3 | 4 | export default () => ( 5 | 8 | ) 9 | -------------------------------------------------------------------------------- /src/components/buttons/demos/Raised.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Button } from '../../../' 3 | 4 | export default () => 5 | -------------------------------------------------------------------------------- /src/components/buttons/demos/RaisedAccent.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Button } from '../../../' 3 | 4 | export default () => ( 5 | 8 | ) 9 | -------------------------------------------------------------------------------- /src/components/buttons/demos/RaisedDisabled.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Button } from '../../../' 3 | 4 | export default () => ( 5 | 8 | ) 9 | -------------------------------------------------------------------------------- /src/components/buttons/demos/RaisedPrimary.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Button } from '../../../' 3 | 4 | export default () => ( 5 | 8 | ) 9 | -------------------------------------------------------------------------------- /src/components/buttons/index.js: -------------------------------------------------------------------------------- 1 | export Button from './Button' 2 | -------------------------------------------------------------------------------- /src/components/cards/Card.style.js: -------------------------------------------------------------------------------- 1 | import styled, { css } from 'styled-components' 2 | import { setDisplayName } from 'recompose' 3 | import { getters as g } from '../../util' 4 | 5 | export const Card = setDisplayName('Card')(styled.div` 6 | display: flex; 7 | flex-direction: column; 8 | font-size: ${g.cardFontSize}px; 9 | font-weight: 400; 10 | min-height: ${g.cardHeight}px; 11 | overflow: hidden; 12 | width: ${g.cardWidth}px; 13 | z-index: ${g.cardZIndex}; 14 | position: relative; 15 | background: ${g.cardBackgroundColor}; 16 | border-radius: 2px; 17 | box-sizing: border-box; 18 | ${({ expand }) => expand && css` 19 | flex-grow: 1; 20 | `} 21 | `) 22 | -------------------------------------------------------------------------------- /src/components/cards/CardActions.style.js: -------------------------------------------------------------------------------- 1 | import styled, { css } from 'styled-components' 2 | import { setDisplayName } from 'recompose' 3 | import { getters as g } from '../../util' 4 | 5 | export const CardActions = setDisplayName('CardActions')(styled.div` 6 | font-size: ${g.cardActionsFontSize}px; 7 | line-height: normal; 8 | width: 100%; 9 | background-color: rgba(0,0,0,0); 10 | padding: 8px; 11 | box-sizing: border-box; 12 | ${({ border }) => border && css` 13 | border-top: 1px solid ${g.cardBorderColor}; 14 | `} 15 | `) 16 | -------------------------------------------------------------------------------- /src/components/cards/CardMedia.style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | import { setDisplayName } from 'recompose' 3 | import { getters as g } from '../../util' 4 | 5 | export const CardMedia = setDisplayName('CardMedia')(styled.div` 6 | background-color: ${g.cardImagePlaceholderColor}; 7 | background-repeat: repeat; 8 | background-position: 50% 50%; 9 | background-size: cover; 10 | background-origin: padding-box; 11 | background-attachment: scroll; 12 | box-sizing: border-box; 13 | `) 14 | -------------------------------------------------------------------------------- /src/components/cards/CardMenu.style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | import { setDisplayName } from 'recompose' 3 | 4 | export const CardMenu = setDisplayName('CardMenu')(styled.div` 5 | position: absolute; 6 | right: 16px; 7 | top: 16px; 8 | `) 9 | -------------------------------------------------------------------------------- /src/components/cards/CardSubtitleText.style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | import { setDisplayName } from 'recompose' 3 | import { getters as g } from '../../util' 4 | 5 | export const CardSubtitleText = setDisplayName('CardSubtitleText')(styled.h4` 6 | font-size: ${g.cardSubtitleFontSize}px; 7 | color: ${g.cardSubtitleColor}px; 8 | margin: 0; 9 | `) 10 | -------------------------------------------------------------------------------- /src/components/cards/CardSupportingText.style.js: -------------------------------------------------------------------------------- 1 | import styled, { css } from 'styled-components' 2 | import { setDisplayName } from 'recompose' 3 | import { getters as g } from '../../util' 4 | 5 | export const CardSupportingText = setDisplayName('CardSupportingText')(styled.div` 6 | color: ${g.cardSupportingTextTextColor}; 7 | font-size: ${g.cardSupportingTextFontSize}px; 8 | line-height: ${g.cardSupportingTextLineHeight}px; 9 | overflow: hidden; 10 | padding: ${g.cardVerticalPadding}px ${g.cardHorizontalPadding}px; 11 | width: 90%; 12 | ${({ border }) => border && css` 13 | border-bottom: 1px solid ${g.cardBorderColor}; 14 | `} 15 | `) 16 | -------------------------------------------------------------------------------- /src/components/cards/CardTitle.style.js: -------------------------------------------------------------------------------- 1 | import styled, { css } from 'styled-components' 2 | import { setDisplayName } from 'recompose' 3 | import { getters as g } from '../../util' 4 | 5 | export const CardTitle = setDisplayName('CardTitle')(styled.header` 6 | align-items: center; 7 | color: ${g.cardTextColor}; 8 | display: block; 9 | display: flex; 10 | line-height: normal; 11 | padding: ${g.cardVerticalPadding}px ${g.cardHorizontalPadding}px; 12 | perspective-origin: ${g.cardTitlePerspectiveOriginX}px ${g.cardTitlePerspectiveOriginY}px; 13 | transform-origin: ${g.cardTitleTransformOriginX}px ${g.cardTitleTransformOriginY}px; 14 | box-sizing: border-box; 15 | ${({ border }) => border && css` 16 | border-bottom: 1px solid ${g.cardBorderColor}; 17 | `} 18 | ${({ expand }) => expand && css` 19 | flex-grow: 1; 20 | `} 21 | `) 22 | -------------------------------------------------------------------------------- /src/components/cards/CardTitleText.style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | import { setDisplayName } from 'recompose' 3 | import { getters as g } from '../../util' 4 | 5 | export const CardTitleText = setDisplayName('CardTitleText')(styled.h1` 6 | align-self: flex-end; 7 | color: inherit; 8 | display: block; 9 | display: flex; 10 | font-size: ${g.cardTitleFontSize}px; 11 | font-weight: ${g.cardTitleTextFontWeight}; 12 | line-height: normal; 13 | overflow: hidden; 14 | transform-origin: ${g.cardTitleTextTransformOriginX}px ${g.cardTitleTextTransformOriginY}px; 15 | margin: 0; 16 | `) 17 | -------------------------------------------------------------------------------- /src/components/cards/demos/Event.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { 4 | Card, 5 | CardActions, 6 | CardTitle, 7 | CardTitleText, 8 | shadow2dp, 9 | Button, 10 | Spacer, 11 | Icon, 12 | } from '../../../' 13 | 14 | const EventCard = Card.extend` 15 | ${shadow2dp()} width: 256px; 16 | height: 256px; 17 | background: #3e4eb8; 18 | align-self: flex-start; 19 | ` 20 | 21 | const EventInfo = CardTitleText.extend` 22 | margin-top: 0; 23 | align-self: flex-start; 24 | color: #fff; 25 | font-weight: normal; 26 | font-size: 24px; 27 | line-height: 32px; 28 | ` 29 | 30 | const EventActions = CardActions.extend` 31 | border-color: rgba(255, 255, 255, 0.2); 32 | display: flex; 33 | box-sizing: border-box; 34 | align-items: center; 35 | ` 36 | 37 | const EventIcon = Icon.extend` 38 | padding-right: 10px; 39 | color: #fff; 40 | ` 41 | 42 | const WhiteButton = Button.extend` 43 | color: #fff; 44 | ` 45 | 46 | export default () => ( 47 | 48 | 49 | 50 | Featured event:
51 | May 24, 2016
52 | 7-11pm 53 |
54 |
55 | 56 | Add to calenar 57 | 58 | 59 | 60 |
61 | ) 62 | -------------------------------------------------------------------------------- /src/components/cards/demos/Image.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | 4 | import { Card, CardActions, CardTitle, shadow2dp } from '../../../' 5 | 6 | const ImageCard = Card.extend` 7 | ${shadow2dp()} width: 256px; 8 | height: 256px; 9 | background: url('https://getmdl.io/assets/demos/image_card.jpg') center / 10 | cover; 11 | ` 12 | 13 | const Footer = CardActions.extend` 14 | height: 52px; 15 | padding: 16px; 16 | background: rgba(0, 0, 0, 0.2); 17 | ` 18 | 19 | const Filename = styled.span` 20 | color: #fff; 21 | font-size: 14px; 22 | font-weight: 500; 23 | ` 24 | 25 | export default () => ( 26 | 27 | 28 |
29 | Image.jpg 30 |
31 |
32 | ) 33 | -------------------------------------------------------------------------------- /src/components/cards/demos/Square.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { 4 | Button, 5 | Card, 6 | CardActions, 7 | CardSupportingText, 8 | CardTitle, 9 | CardTitleText, 10 | shadow2dp, 11 | } from '../../../' 12 | 13 | export const DemoCardSquare = Card.extend` 14 | ${shadow2dp()} width: 320px; 15 | height: 320px; 16 | ` 17 | 18 | export const DemoCardTitle = CardTitle.extend` 19 | color: #fff; 20 | height: 176px; 21 | background: url('https://getmdl.io/assets/demos/dog.png') bottom right 15% 22 | no-repeat #46b6ac; 23 | ` 24 | 25 | export default () => ( 26 | 27 | 28 | Update 29 | 30 | 31 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenan convallis. 32 | 33 | 34 | 35 | 36 | 37 | ) 38 | -------------------------------------------------------------------------------- /src/components/cards/demos/Wide.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { MdShare } from 'react-icons/lib/md' 3 | 4 | import { 5 | Button, 6 | Card, 7 | CardActions, 8 | CardMenu, 9 | CardSupportingText, 10 | CardTitle, 11 | CardTitleText, 12 | shadow2dp, 13 | } from '../../../' 14 | 15 | export const DemoCardWide = Card.extend` 16 | ${shadow2dp()} width: 512px; 17 | ` 18 | 19 | export const DemoCardTitle = CardTitle.extend` 20 | color: #fff; 21 | height: 176px; 22 | background: url('https://getmdl.io/assets/demos/welcome_card.jpg') center / 23 | cover; 24 | ` 25 | 26 | export const DemoCardMenu = CardMenu.extend` 27 | color: #fff; 28 | ` 29 | 30 | export default () => ( 31 | 32 | 33 | Welcome 34 | 35 | 36 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris sagittis 37 | pellentesque lacus eleifend lacinia... 38 | 39 | 40 | 41 | 42 | 43 | 46 | 47 | 48 | ) 49 | -------------------------------------------------------------------------------- /src/components/cards/index.js: -------------------------------------------------------------------------------- 1 | export { Card } from './Card.style' 2 | export { CardActions } from './CardActions.style' 3 | export { CardMedia } from './CardMedia.style' 4 | export { CardMenu } from './CardMenu.style' 5 | export { CardSubtitleText } from './CardSubtitleText.style' 6 | export { CardSupportingText } from './CardSupportingText.style' 7 | export { CardTitle } from './CardTitle.style' 8 | export { CardTitleText } from './CardTitleText.style' 9 | -------------------------------------------------------------------------------- /src/components/checkbox/Checkbox.js: -------------------------------------------------------------------------------- 1 | import { compose, setDisplayName, setPropTypes } from 'recompose' 2 | import PropTypes from 'prop-types' 3 | import React from 'react' 4 | 5 | import { 6 | CheckboxWrap, 7 | CheckboxButton, 8 | CheckboxLabel, 9 | BoxOutline, 10 | TickOutline, 11 | } from './Checkbox.style' 12 | import { proxyStyledStatics } from '../../hocs' 13 | 14 | export const CheckboxBase = ({ 15 | label, 16 | __StyledComponent__: Styled, 17 | ...props 18 | }) => ( 19 | 20 | 21 | {label && {label}} 22 | 23 | 24 | 25 | 26 | ) 27 | 28 | const enhance = compose( 29 | proxyStyledStatics(CheckboxWrap), 30 | setDisplayName('Checkbox'), 31 | setPropTypes({ 32 | label: PropTypes.string, 33 | disabled: PropTypes.bool, 34 | }), 35 | ) 36 | 37 | export default enhance(CheckboxBase) 38 | -------------------------------------------------------------------------------- /src/components/checkbox/Checkbox.style.js: -------------------------------------------------------------------------------- 1 | import { setDisplayName } from 'recompose' 2 | import styled, { css } from 'styled-components' 3 | 4 | import { getters as g } from '../../util' 5 | import { mask, bg } from './images' 6 | import { materialAnimationDefault } from '../../mixins' 7 | 8 | export const CheckboxWrap = setDisplayName('CheckboxWrap')(styled.label` 9 | position: relative; 10 | font-size: ${g.radioLabelFontSize}px; 11 | line-height: ${g.radioLabelHeight}px; 12 | display: inline-block; 13 | vertical-align: middle; 14 | box-sizing: border-box; 15 | height: ${g.radioLabelHeight}px; 16 | margin: 0; 17 | padding-left: ${({ theme }) => theme.radioButtonSize + theme.radioPadding}px; 18 | `) 19 | 20 | export const CheckboxButton = setDisplayName('CheckboxButton')(styled.input` 21 | line-height: ${g.radioLabelHeight}px; 22 | position: absolute; 23 | width: 0; 24 | height: 0; 25 | margin: 0; 26 | padding: 0; 27 | opacity: 0; 28 | -ms-appearance: none; 29 | -moz-appearance: none; 30 | -webkit-appearance: none; 31 | appearance: none; 32 | border: none; 33 | &:checked ~ div { 34 | border: 2px solid ${g.checkboxColor}; 35 | } 36 | &:checked ~ div > div { 37 | background-color: ${g.checkboxColor}; 38 | background-image: url(${bg}); 39 | } 40 | `) 41 | 42 | export const BoxOutline = setDisplayName('BoxOutline')(styled.div` 43 | position: absolute; 44 | top: ${g.radioTopOffset}px; 45 | left: 0; 46 | display: inline-block; 47 | box-sizing: border-box; 48 | width: ${g.radioButtonSize}px; 49 | height: ${g.radioButtonSize}px; 50 | margin: 0; 51 | cursor: pointer; 52 | overflow: hidden; 53 | border: 2px solid ${g.checkboxOffColor}; 54 | border-radius: 2px; 55 | z-index: 2; 56 | ${({ disabled }) => 57 | disabled && 58 | css` 59 | border: 2px solid ${g.checkboxDisabledColor}; 60 | cursor: auto; 61 | `}; 62 | `) 63 | 64 | export const TickOutline = setDisplayName('TickOutline')(styled.div` 65 | position: absolute; 66 | top: 0; 67 | left: 0; 68 | bottom: 0; 69 | right: 0; 70 | transform: scale(1.01); 71 | mask: url(${mask}); 72 | background: transparent; 73 | transition-property: background; 74 | ${materialAnimationDefault('0.28s')} 75 | ${({ disabled }) => 76 | disabled && 77 | css` 78 | background-color: ${g.checkboxDisabledColor}; 79 | `}; 80 | `) 81 | 82 | export const CheckboxLabel = setDisplayName('CheckboxLabel')(styled.span` 83 | cursor: pointer; 84 | 85 | ${({ disabled }) => 86 | disabled && 87 | css` 88 | color: ${g.checkboxDisabledColor}; 89 | cursor: auto; 90 | `}; 91 | `) 92 | -------------------------------------------------------------------------------- /src/components/checkbox/demos/Checkbox.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Checkbox } from '../../../' 3 | 4 | export default () => 5 | -------------------------------------------------------------------------------- /src/components/checkbox/images.js: -------------------------------------------------------------------------------- 1 | const mask = 2 | 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8ZGVmcz4KICAgIDxjbGlwUGF0aCBpZD0iY2xpcCI+CiAgICAgIDxwYXRoCiAgICAgICAgIGQ9Ik0gMCwwIDAsMSAxLDEgMSwwIDAsMCB6IE0gMC44NTM0Mzc1LDAuMTY3MTg3NSAwLjk1OTY4NzUsMC4yNzMxMjUgMC40MjkzNzUsMC44MDM0Mzc1IDAuMzIzMTI1LDAuOTA5Njg3NSAwLjIxNzE4NzUsMC44MDM0Mzc1IDAuMDQwMzEyNSwwLjYyNjg3NSAwLjE0NjU2MjUsMC41MjA2MjUgMC4zMjMxMjUsMC42OTc1IDAuODUzNDM3NSwwLjE2NzE4NzUgeiIKICAgICAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZjtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KICAgIDwvY2xpcFBhdGg+CiAgICA8bWFzayBpZD0ibWFzayIgbWFza1VuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgbWFza0NvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPgogICAgICA8cGF0aAogICAgICAgICBkPSJNIDAsMCAwLDEgMSwxIDEsMCAwLDAgeiBNIDAuODUzNDM3NSwwLjE2NzE4NzUgMC45NTk2ODc1LDAuMjczMTI1IDAuNDI5Mzc1LDAuODAzNDM3NSAwLjMyMzEyNSwwLjkwOTY4NzUgMC4yMTcxODc1LDAuODAzNDM3NSAwLjA0MDMxMjUsMC42MjY4NzUgMC4xNDY1NjI1LDAuNTIwNjI1IDAuMzIzMTI1LDAuNjk3NSAwLjg1MzQzNzUsMC4xNjcxODc1IHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmUiIC8+CiAgICA8L21hc2s+CiAgPC9kZWZzPgogIDxyZWN0CiAgICAgd2lkdGg9IjEiCiAgICAgaGVpZ2h0PSIxIgogICAgIHg9IjAiCiAgICAgeT0iMCIKICAgICBjbGlwLXBhdGg9InVybCgjY2xpcCkiCiAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KPC9zdmc+Cg==' 3 | 4 | const bg = 5 | 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8cGF0aAogICAgIGQ9Ik0gMC4wNDAzODA1OSwwLjYyNjc3NjcgMC4xNDY0NDY2MSwwLjUyMDcxMDY4IDAuNDI5Mjg5MzIsMC44MDM1NTMzOSAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IE0gMC4yMTcxNTcyOSwwLjgwMzU1MzM5IDAuODUzNTUzMzksMC4xNjcxNTcyOSAwLjk1OTYxOTQxLDAuMjczMjIzMyAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IgogICAgIGlkPSJyZWN0Mzc4MCIKICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lIiAvPgo8L3N2Zz4K' 6 | 7 | export { mask, bg } 8 | -------------------------------------------------------------------------------- /src/components/checkbox/index.js: -------------------------------------------------------------------------------- 1 | export Checkbox from './Checkbox' 2 | -------------------------------------------------------------------------------- /src/components/chips/ButtonChip.js: -------------------------------------------------------------------------------- 1 | import { compose, setPropTypes, setDisplayName } from 'recompose' 2 | import PropTypes from 'prop-types' 3 | import React from 'react' 4 | 5 | import { ButtonChipStyle, ChipText } from './Chip.style' 6 | import { proxyStyledStatics } from '../../hocs' 7 | import ChipContact from './ChipContact' 8 | 9 | export const ButtonChipBase = ({ 10 | contact, 11 | children, 12 | __StyledComponent__: Styled, 13 | ...props 14 | }) => ( 15 | 16 | {contact && } 17 | {children} 18 | 19 | ) 20 | 21 | const enhance = compose( 22 | proxyStyledStatics(ButtonChipStyle), 23 | setDisplayName('ButtonChip'), 24 | setPropTypes({ 25 | contact: PropTypes.shape({ 26 | color: PropTypes.string, 27 | textColor: PropTypes.string, 28 | text: PropTypes.string, 29 | src: PropTypes.string, 30 | }), 31 | children: PropTypes.node.isRequired, 32 | }), 33 | ) 34 | 35 | export default enhance(ButtonChipBase) 36 | -------------------------------------------------------------------------------- /src/components/chips/Chip.js: -------------------------------------------------------------------------------- 1 | import { compose, setPropTypes, setDisplayName } from 'recompose' 2 | import PropTypes from 'prop-types' 3 | import React from 'react' 4 | 5 | import { Icon } from '../icons' 6 | import { ChipStyle, ChipText, ChipAction } from './Chip.style' 7 | import { proxyStyledStatics } from '../../hocs' 8 | import ChipContact from './ChipContact' 9 | 10 | export const ChipBase = ({ 11 | deletable, 12 | contact, 13 | children, 14 | onClickDelete, 15 | __StyledComponent__: Styled, 16 | }) => ( 17 | 18 | {contact && } 19 | {children && {children}} 20 | {deletable && ( 21 | 22 | 23 | 24 | )} 25 | 26 | ) 27 | 28 | const enhance = compose( 29 | proxyStyledStatics(ChipStyle), 30 | setDisplayName('Chip'), 31 | setPropTypes({ 32 | deletable: PropTypes.bool, 33 | contact: PropTypes.shape({ 34 | color: PropTypes.string, 35 | textColor: PropTypes.string, 36 | text: PropTypes.string, 37 | src: PropTypes.string, 38 | }), 39 | children: PropTypes.node, 40 | onClickDelete: PropTypes.func, 41 | }), 42 | ) 43 | 44 | export default enhance(ChipBase) 45 | -------------------------------------------------------------------------------- /src/components/chips/Chip.style.js: -------------------------------------------------------------------------------- 1 | import { compose, defaultProps, setDisplayName } from 'recompose' 2 | import styled, { css } from 'styled-components' 3 | 4 | import { getters as g } from '../../util' 5 | import { shadow2dp } from '../../mixins' 6 | 7 | export const ChipStyle = setDisplayName('ChipStyle')(styled.span` 8 | height: ${g.chipHeight}px; 9 | font-family: ${g.preferredFont}; 10 | line-height: ${g.chipHeight}px; 11 | padding: 0 12px; 12 | border: 0; 13 | border-radius: ${({ theme }) => theme.chipHeight / 2}px; 14 | background-color: ${g.chipBgColor}; 15 | display: inline-block; 16 | color: ${g.textColorPrimary}; 17 | margin: 2px 0; 18 | font-size: 0; 19 | white-space: nowrap; 20 | &:focus { 21 | outline: 0; 22 | ${shadow2dp()} 23 | } 24 | &:active { 25 | background-color: ${g.chipBgActiveColor}; 26 | } 27 | ${({ deletable }) => deletable && css` 28 | padding-right: 4px; 29 | `} 30 | ${({ contact }) => contact && css` 31 | padding-left: 0px; 32 | `} 33 | `) 34 | 35 | export const ButtonChipStyle = ChipStyle.withComponent('button') 36 | 37 | 38 | export const ChipText = setDisplayName('ChipText')(styled.span` 39 | font-size: ${g.chipFontSize}px; 40 | vertical-align: middle; 41 | display: inline-block; 42 | `) 43 | 44 | export const ChipAction = compose( 45 | defaultProps({ 46 | type: 'button', 47 | }), 48 | setDisplayName('ChipAction'), 49 | )(styled.button` 50 | height: 24px; 51 | width: 24px; 52 | font-size: 24px; 53 | background: transparent; 54 | opacity: 0.54; 55 | display: inline-block; 56 | cursor: pointer; 57 | text-align: center; 58 | vertical-align: middle; 59 | padding: 0; 60 | margin: 0 0 0 4px; 61 | text-decoration: none; 62 | color: ${g.textColorPrimary}; 63 | border: none; 64 | outline: none; 65 | overflow: hidden; 66 | `) 67 | 68 | export const ChipContactImg = setDisplayName('ChipContactImg')(styled.img` 69 | height: ${g.chipHeight}px; 70 | width: ${g.chipHeight}px; 71 | border-radius: ${({ theme }) => theme.chipHeight / 2}px; 72 | display: inline-block; 73 | vertical-align: middle; 74 | margin-right: 8px; 75 | overflow: hidden; 76 | text-align: center; 77 | font-size: 18px; 78 | line-height: 32px; 79 | background-color: ${g.rgbFromProp('color', g.colorPrimary)}; 80 | color: ${({ textColor }) => textColor || '#FFF'}; 81 | `) 82 | 83 | export const ChipContactSpan = setDisplayName('ChipContactSpan')( 84 | ChipContactImg.withComponent('span') 85 | ) 86 | -------------------------------------------------------------------------------- /src/components/chips/ChipContact.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | import { ChipContactImg, ChipContactSpan } from './Chip.style' 5 | 6 | export default function ChipContact({ text, src, ...props }) { 7 | return src ? ( 8 | 9 | ) : ( 10 | {text} 11 | ) 12 | } 13 | 14 | ChipContact.propTypes = { 15 | src: PropTypes.string, 16 | text: PropTypes.string, 17 | } 18 | -------------------------------------------------------------------------------- /src/components/chips/demos/Basic.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Chip } from '../../../' 3 | 4 | export default () => Basic Chip 5 | -------------------------------------------------------------------------------- /src/components/chips/demos/Button.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { ButtonChip } from '../../../' 3 | 4 | export default () => Button Chip 5 | -------------------------------------------------------------------------------- /src/components/chips/demos/Contact.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Chip } from '../../../' 3 | 4 | /* eslint-disable no-alert */ 5 | export default () => ( 6 | 7 | Contact Chip 8 | 9 | ) 10 | -------------------------------------------------------------------------------- /src/components/chips/demos/Deletable.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Chip } from '../../../' 3 | 4 | /* eslint-disable no-alert */ 5 | export default () => ( 6 | alert('You clicked delete!')}> 7 | Basic Chip 8 | 9 | ) 10 | -------------------------------------------------------------------------------- /src/components/chips/demos/DeletableContact.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Chip } from '../../../' 3 | 4 | const avatarImg = 'https://getmdl.io/templates/dashboard/images/user.jpg' 5 | 6 | export default () => ( 7 | 8 | Deletable Contact Chip 9 | 10 | ) 11 | -------------------------------------------------------------------------------- /src/components/chips/index.js: -------------------------------------------------------------------------------- 1 | export Chip from './Chip' 2 | export ButtonChip from './ButtonChip' 3 | export ChipContact from './ChipContact' 4 | -------------------------------------------------------------------------------- /src/components/dialog/Dialog.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactModal from 'react-modal' 3 | 4 | import { DialogStyle, fadeIn } from './Dialog.style' 5 | 6 | const style = { 7 | overlay: { 8 | position: 'fixed', 9 | top: 0, 10 | left: 0, 11 | right: 0, 12 | bottom: 0, 13 | backgroundColor: 'rgba(100, 100, 100, 0.3)', 14 | zIndex: 999, 15 | display: 'flex', 16 | alignItems: 'center', 17 | justifyContent: 'center', 18 | animation: `${fadeIn} 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards`, 19 | }, 20 | content: { 21 | position: 'reltive', 22 | top: 0, 23 | left: 0, 24 | right: 0, 25 | bottom: 0, 26 | height: 'auto', 27 | width: 'auto', 28 | background: 'transparent', 29 | zIndex: 1000, 30 | padding: 0, 31 | margin: 0, 32 | border: 0, 33 | overflow: 'visible', 34 | }, 35 | } 36 | 37 | const DialogWrap = ({ className, children, ...props }) => ( 38 | 39 |
{children}
40 |
41 | ) 42 | 43 | const Dialog = DialogStyle.withComponent(DialogWrap) 44 | 45 | Dialog.displayName = 'Dialog' 46 | 47 | export default Dialog 48 | -------------------------------------------------------------------------------- /src/components/dialog/Dialog.style.js: -------------------------------------------------------------------------------- 1 | import styled, { css, keyframes } from 'styled-components' 2 | 3 | import { shadow24dp } from '../../mixins' 4 | import { getters as g } from '../../util' 5 | 6 | const flyIn = keyframes` 7 | from { 8 | transform: translate(0, 5vh); 9 | } 10 | to { 11 | transform: translate(0, 0); 12 | } 13 | ` 14 | 15 | export const fadeIn = keyframes` 16 | from { 17 | opacity: 0; 18 | } 19 | to { 20 | opacity: 1; 21 | } 22 | ` 23 | 24 | export const DialogStyle = styled.div.attrs({ 25 | width: ({ size }) => `${parseInt(size || 5, 10) * 56}px`, 26 | })` 27 | background: white; 28 | width: ${({ width }) => width}; 29 | max-width: calc(100vw - 144px); 30 | max-height: 80vh; 31 | margin-top: -15vh; 32 | border-radius: 3px; 33 | ${shadow24dp()} 34 | overflow: auto; 35 | animation: ${fadeIn} 0.3s ${g.animationCurveDefault} forwards, 36 | ${flyIn} 0.3s ${g.animationCurveLinearOutSlowIn} forwards; 37 | ` 38 | 39 | export const DialogTitle = styled.h1` 40 | padding: 24px 24px 0; 41 | margin: 0; 42 | font-size: 20px; 43 | line-height: 20px; 44 | font-weight: 500; 45 | ` 46 | 47 | export const DialogContent = styled.div` 48 | padding: 20px 24px 24px; 49 | font-size: 16px; 50 | line-height: 24px; 51 | color: ${g.dialogContentColor}; 52 | ` 53 | 54 | export const DialogActions = styled.div` 55 | position: relative; 56 | padding: 8px 0 8px 24px; 57 | display: flex; 58 | flex-direction: row-reverse; 59 | flex-wrap: wrap; 60 | > * { 61 | margin-right: 8px; 62 | height: 36px; 63 | } 64 | ${({ fullWidth }) => fullWidth && css` 65 | padding: 0 0 8px 0; 66 | right: 0; 67 | > * { 68 | height: 48px; 69 | flex: 0 0 auto; 70 | width: 100%; 71 | padding-right: 16px; 72 | margin-right: 0; 73 | text-align: right; 74 | justify-content: flex-end; 75 | border-radius: 0; 76 | } 77 | `} 78 | ` 79 | -------------------------------------------------------------------------------- /src/components/dialog/demos/Basic.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { withStateHandlers } from 'recompose' 3 | import { 4 | Dialog, 5 | DialogTitle, 6 | DialogActions, 7 | DialogContent, 8 | Button, 9 | } from '../../../' 10 | 11 | export default withStateHandlers( 12 | { isShowingDialog: false }, 13 | { 14 | showDialog: () => () => ({ isShowingDialog: true }), 15 | hideDialog: () => () => ({ isShowingDialog: false }), 16 | }, 17 | )(({ showDialog, hideDialog, isShowingDialog }) => ( 18 | 19 | 34 | 35 | 36 | 37 | 38 | )) 39 | -------------------------------------------------------------------------------- /src/components/dialog/demos/FullWidthActions.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { withStateHandlers } from 'recompose' 3 | import { 4 | Dialog, 5 | DialogTitle, 6 | DialogActions, 7 | DialogContent, 8 | Button, 9 | } from '../../../' 10 | 11 | const mdSpecLink = 12 | 'https://www.google.com/design/spec/components/dialogs.html#dialogs-specs' 13 | 14 | export default withStateHandlers( 15 | { isShowingDialog: false }, 16 | { 17 | showDialog: () => () => ({ isShowingDialog: true }), 18 | hideDialog: () => () => ({ isShowingDialog: false }), 19 | }, 20 | )(({ showDialog, hideDialog, isShowingDialog }) => ( 21 | 22 | 39 | 40 | 41 | 42 | 43 | )) 44 | -------------------------------------------------------------------------------- /src/components/dialog/index.js: -------------------------------------------------------------------------------- 1 | export { DialogTitle, DialogActions, DialogContent } from './Dialog.style' 2 | 3 | export Dialog from './Dialog' 4 | -------------------------------------------------------------------------------- /src/components/fonts/MaterialIcons.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const MaterialIconsFont = () => ( 4 | 8 | ) 9 | 10 | export default MaterialIconsFont 11 | -------------------------------------------------------------------------------- /src/components/fonts/Roboto.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | const RobotoFont = ({ variants = '300,400,400i,700' }) => ( 5 | 9 | ) 10 | 11 | RobotoFont.propTypes = { 12 | variants: PropTypes.string, 13 | } 14 | 15 | export default RobotoFont 16 | -------------------------------------------------------------------------------- /src/components/fonts/index.js: -------------------------------------------------------------------------------- 1 | export MaterialIcons from './MaterialIcons' 2 | export Roboto from './Roboto' 3 | -------------------------------------------------------------------------------- /src/components/icons/Icon.js: -------------------------------------------------------------------------------- 1 | import { compose, setDisplayName, setPropTypes, withProps } from 'recompose' 2 | import { prop, always, cond } from 'lodash/fp' 3 | import PropTypes from 'prop-types' 4 | import React from 'react' 5 | 6 | import { IconStyle } from './Icon.style' 7 | import { proxyStyledStatics } from '../../hocs' 8 | 9 | const Icon = ({ name, __StyledComponent__: Styled, ...props }) => ( 10 | {name} 11 | ) 12 | 13 | const enhance = compose( 14 | proxyStyledStatics(IconStyle), 15 | setDisplayName('Icon'), 16 | setPropTypes({ 17 | xs: PropTypes.bool, 18 | sm: PropTypes.bool, 19 | lg: PropTypes.bool, 20 | xl: PropTypes.bool, 21 | size: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 22 | name: PropTypes.string.isRequired, 23 | __StyledComponent__: PropTypes.func.isRequired, 24 | }), 25 | withProps((p) => ({ 26 | size: cond([ 27 | [prop('size'), prop('size')], 28 | [prop('xs'), always('12')], 29 | [prop('sm'), always('16')], 30 | [prop('lg'), always('32')], 31 | [prop('xl'), always('64')], 32 | ])(p), 33 | })), 34 | ) 35 | 36 | export default enhance(Icon) 37 | -------------------------------------------------------------------------------- /src/components/icons/Icon.style.js: -------------------------------------------------------------------------------- 1 | import { setDisplayName } from 'recompose' 2 | import styled from 'styled-components' 3 | import { ifProp } from 'styled-tools' 4 | 5 | import { typoIcon } from '../../mixins' 6 | 7 | export const IconStyle = setDisplayName('IconStyle')(styled.i` 8 | ${typoIcon()} 9 | font-size: ${ifProp('size', (p) => `${p.size}px`, 'inherit')}; 10 | line-height: ${ifProp('size', (p) => `${p.size}px`, 'inherit')};; 11 | `) 12 | -------------------------------------------------------------------------------- /src/components/icons/index.js: -------------------------------------------------------------------------------- 1 | export Icon from './Icon' 2 | -------------------------------------------------------------------------------- /src/components/layout/Spacer.style.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | 3 | export default styled.div` 4 | flex-grow: 1; 5 | ` 6 | -------------------------------------------------------------------------------- /src/components/layout/index.js: -------------------------------------------------------------------------------- 1 | export Spacer from './Spacer.style' 2 | -------------------------------------------------------------------------------- /src/components/list/demos/AvatarsAndActions.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { 3 | List, 4 | ListItem, 5 | LiPrimary, 6 | LiAction, 7 | LiSecondary, 8 | LiAvatar, 9 | Button, 10 | Icon, 11 | } from '../../../' 12 | 13 | export default () => ( 14 | 15 | 16 | 17 | 18 | Bryan Cranston 19 | 20 | 21 | 22 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | Aaron Paul 32 | 33 | 34 | 35 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | Bob Odenkirk 45 | 46 | 47 | 48 | 51 | 52 | 53 | 54 | 55 | ) 56 | -------------------------------------------------------------------------------- /src/components/list/demos/AvatarsAndControls.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { 3 | List, 4 | ListItem, 5 | LiAction, 6 | LiSecondary, 7 | LiPrimary, 8 | LiAvatar, 9 | Checkbox, 10 | Radio, 11 | Switch, 12 | Icon, 13 | } from '../../../' 14 | 15 | export default () => ( 16 | 17 | 18 | 19 | 20 | 21 | 22 | Bryan Cranston 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | Aaron Paul 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | Bob Odenkirk 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | ) 58 | -------------------------------------------------------------------------------- /src/components/list/demos/Icons.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { List, ListItem, LiIcon, Icon } from '../../../' 3 | 4 | export default () => ( 5 | 6 | 7 | 8 | 9 | 10 | Bryan Cranston 11 | 12 | 13 | 14 | 15 | 16 | Aaron Paul 17 | 18 | 19 | 20 | 21 | 22 | Bob Odenkirk 23 | 24 | 25 | ) 26 | -------------------------------------------------------------------------------- /src/components/list/demos/Simple.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { List, ListItem } from '../../../' 3 | 4 | export default () => ( 5 | 6 | Bryan Cranston 7 | Aaron Paul 8 | Bob Odenkirk 9 | 10 | ) 11 | -------------------------------------------------------------------------------- /src/components/list/demos/ThreeLine.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { 3 | List, 4 | ListItem, 5 | LiPrimary, 6 | LiSecondary, 7 | LiAvatar, 8 | LiAction, 9 | LiTextBody, 10 | Button, 11 | Icon, 12 | } from '../../../' 13 | 14 | export default () => ( 15 | 16 | 17 | 18 | 19 | 20 | 21 | Bryan Cranston 22 | 23 | Bryan Cranston played the role of Walter in Breaking Bad. He is also 24 | known for playing Hal in Malcom in the Middle. 25 | 26 | 27 | 28 | 29 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | Aaron Paul 41 | 42 | Aaron Paul played the role of Jesse in Breaking Bad. He also featured 43 | in the "Need For Speed" Movie. 44 | 45 | 46 | 47 | 48 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | Bob Odenkirk 60 | 61 | Bob Odinkrik played the role of Saul in Breaking Bad. Due to public 62 | fondness for the character, Bob stars in his own show now, called 63 | "Better Call Saul". 64 | 65 | 66 | 67 | 68 | 71 | 72 | 73 | 74 | 75 | ) 76 | -------------------------------------------------------------------------------- /src/components/list/demos/TwoLine.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { 3 | List, 4 | ListItem, 5 | LiPrimary, 6 | LiSecondary, 7 | LiAction, 8 | LiAvatar, 9 | LiSubTitle, 10 | LiInfo, 11 | Button, 12 | Icon, 13 | } from '../../../' 14 | 15 | export default () => ( 16 | 17 | 18 | 19 | 20 | 21 | 22 | Bryan Cranston 23 | 62 Episodes 24 | 25 | 26 | Actor 27 | 28 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | Aaron Paul 40 | 62 Episodes 41 | 42 | 43 | 44 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | Bob Odenkirk 56 | 62 Episodes 57 | 58 | 59 | 60 | 63 | 64 | 65 | 66 | 67 | ) 68 | -------------------------------------------------------------------------------- /src/components/list/index.js: -------------------------------------------------------------------------------- 1 | export { 2 | List, 3 | ListItem, 4 | LiAction, 5 | LiIcon, 6 | LiAvatar, 7 | LiInfo, 8 | LiPrimary, 9 | LiSecondary, 10 | LiSubTitle, 11 | LiTextBody, 12 | LiTitle, 13 | } from './List.style' 14 | -------------------------------------------------------------------------------- /src/components/menu/MenuDivider.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import MenuItem from './MenuItem' 4 | import { MenuDivider as MenuDividerBase } from './Menu.style' 5 | 6 | export default class MenuDivider extends MenuItem { 7 | render() { 8 | return ( 9 | { 13 | this.menuItem = menuItem 14 | }} 15 | /> 16 | ) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/components/menu/MenuItem.js: -------------------------------------------------------------------------------- 1 | import { compose, setDisplayName, setPropTypes } from 'recompose' 2 | import PropTypes from 'prop-types' 3 | import React, { Component } from 'react' 4 | 5 | import { MenuItem as MenuItemBase } from './Menu.style' 6 | import { Ripple } from '../ripple' 7 | import { proxyStyledStatics } from '../../hocs' 8 | 9 | export class MenuItem extends Component { 10 | state = { 11 | getTransitionDelay: () => 0, 12 | } 13 | 14 | componentWillReceiveProps(nextProps) { 15 | if (!this.props.isVisible && nextProps.isVisible) { 16 | const { 17 | height, 18 | } = this.menuItem.parentNode.parentNode.getBoundingClientRect() 19 | const { offsetTop } = this.menuItem 20 | const { height: itemHeight } = this.menuItem.getBoundingClientRect() 21 | const getTransitionDelay = nextProps.fadeDown 22 | ? (duration) => offsetTop / height * duration 23 | : (duration) => -(offsetTop + itemHeight - height) / height * duration 24 | 25 | this.setState({ getTransitionDelay }) 26 | } 27 | } 28 | 29 | render() { 30 | const { __StyledComponent__: Styled, ...props } = this.props 31 | 32 | return ( 33 | { 37 | this.menuItem = menuItem 38 | }} 39 | > 40 | {props.children} 41 | 42 | 43 | ) 44 | } 45 | } 46 | 47 | const enhance = compose( 48 | proxyStyledStatics(MenuItemBase), 49 | setPropTypes({ 50 | isVisible: PropTypes.bool, 51 | children: PropTypes.node, 52 | }), 53 | setDisplayName('MenuItem'), 54 | ) 55 | 56 | export default enhance(MenuItem) 57 | -------------------------------------------------------------------------------- /src/components/menu/demos/LowerLeft.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Menu, MenuItem, MenuDivider, Button, Icon } from '../../../' 3 | 4 | import { Container, Bar, Bg } from './_shared' 5 | 6 | export default () => ( 7 | 8 | 9 | 12 | 13 | 14 | } 15 | > 16 | Some Action 17 | Another Action 18 | 19 | Disabled Action 20 | Yet another action 21 | 22 | 23 | 24 | 25 | ) 26 | -------------------------------------------------------------------------------- /src/components/menu/demos/LowerRight.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Menu, MenuItem, MenuDivider, Button, Icon } from '../../../' 3 | 4 | import { Container, Bar, Bg } from './_shared' 5 | 6 | export default () => ( 7 | 8 | 9 | 13 | 14 | 15 | } 16 | > 17 | Some Action 18 | Another Action 19 | 20 | Disabled Action 21 | Yet another action 22 | 23 | 24 | 25 | 26 | ) 27 | -------------------------------------------------------------------------------- /src/components/menu/demos/UpperLeft.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Menu, MenuItem, MenuDivider, Button, Icon } from '../../../' 3 | 4 | import { Container, Bar, Bg } from './_shared' 5 | 6 | export default () => ( 7 | 8 | 9 | 10 | 14 | 15 | 16 | } 17 | > 18 | Some Action 19 | Another Action 20 | 21 | Disabled Action 22 | Yet another action 23 | 24 | 25 | 26 | ) 27 | -------------------------------------------------------------------------------- /src/components/menu/demos/UpperRight.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Menu, MenuItem, MenuDivider, Button, Icon } from '../../../' 3 | 4 | import { Container, Bar, Bg } from './_shared' 5 | 6 | export default () => ( 7 | 8 | 9 | 10 | 14 | 15 | 16 | } 17 | > 18 | Some Action 19 | Another Action 20 | 21 | Disabled Action 22 | Yet another action 23 | 24 | 25 | 26 | ) 27 | -------------------------------------------------------------------------------- /src/components/menu/demos/_shared.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | import { shadow2dp } from '../../../' 3 | 4 | export const Bar = styled.div` 5 | box-sizing: border-box; 6 | background: #3f51b5; 7 | color: white; 8 | width: 100%; 9 | padding: 16px; 10 | ` 11 | 12 | export const Bg = styled.div` 13 | background: white; 14 | height: 148px; 15 | width: 100%; 16 | ` 17 | 18 | export const Container = styled.div` 19 | position: relative; 20 | width: 200px; 21 | text-align: ${({ align }) => align}; 22 | ${shadow2dp()}; 23 | ` 24 | -------------------------------------------------------------------------------- /src/components/menu/getRelativePosition.js: -------------------------------------------------------------------------------- 1 | export default function getRelativePosition(element) { 2 | const rect = element.getBoundingClientRect() 3 | const sx = window.scrollX || window.pageXOffset 4 | const sy = window.scrollY || window.pageYOffset 5 | 6 | return { 7 | height: rect.height, 8 | width: rect.width, 9 | top: rect.top + sy, 10 | left: rect.left + sx, 11 | bottom: rect.bottom + sy, 12 | right: rect.right + sx, 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/components/menu/index.js: -------------------------------------------------------------------------------- 1 | export Menu from './Menu' 2 | export MenuItem from './MenuItem' 3 | export MenuDivider from './MenuDivider' 4 | -------------------------------------------------------------------------------- /src/components/progress/Progress.js: -------------------------------------------------------------------------------- 1 | import { compose, setDisplayName } from 'recompose' 2 | import React from 'react' 3 | 4 | import { 5 | ProgressBaseStyle, 6 | ProgressBar, 7 | BufferBar, 8 | AuxBar, 9 | } from './Progress.style' 10 | import { proxyStyledStatics } from '../../hocs' 11 | 12 | export const ProgressBase = ({ 13 | indeterminate, 14 | percent, 15 | width, 16 | __StyledComponent__: Styled, 17 | }) => ( 18 | 19 | 20 | 21 | 22 | 23 | ) 24 | 25 | const enhance = compose( 26 | proxyStyledStatics(ProgressBaseStyle), 27 | setDisplayName('Progress'), 28 | ) 29 | 30 | export default enhance(ProgressBase) 31 | -------------------------------------------------------------------------------- /src/components/progress/Progress.style.js: -------------------------------------------------------------------------------- 1 | import { setDisplayName } from 'recompose' 2 | import styled, { css, keyframes } from 'styled-components' 3 | 4 | import { getters as g } from '../../util' 5 | 6 | const indeterminate1 = keyframes` 7 | 0% { 8 | left: 0%; 9 | width: 0%; 10 | } 11 | 50% { 12 | left: 25%; 13 | width: 75%; 14 | } 15 | 75% { 16 | left: 100%; 17 | width: 0%; 18 | } 19 | ` 20 | 21 | const indeterminate2 = keyframes` 22 | 0% { 23 | left: 0%; 24 | width: 0%; 25 | } 26 | 50% { 27 | left: 0%; 28 | width: 0%; 29 | } 30 | 75% { 31 | left: 0%; 32 | width: 25%; 33 | } 34 | 100% { 35 | left: 100%; 36 | width: 0%; 37 | } 38 | ` 39 | 40 | export const ProgressBaseStyle = setDisplayName('ProgressBaseStyle')(styled.div` 41 | display: block; 42 | position: relative; 43 | height: ${g.barHeight}px; 44 | max-width: 100%; 45 | width: ${({ width }) => width || '500px'}; 46 | `) 47 | 48 | const Bar = styled.div` 49 | display: block; 50 | position: absolute; 51 | top: 0; 52 | bottom: 0; 53 | width: 0%; 54 | transition: width 0.2s ${g.animationCurveDefault}; 55 | ` 56 | 57 | export const ProgressBar = setDisplayName('ProgressBar')(Bar.extend` 58 | background-color: ${g.progressMainColor}; 59 | z-index: 1; 60 | left: 0; 61 | ${({ indeterminate }) => indeterminate && css` 62 | background-color: ${g.progressMainColor}; 63 | animation-name: ${indeterminate1}; 64 | animation-duration: 2s; 65 | animation-iteration-count: infinite; 66 | animation-timing-function: linear; 67 | `} 68 | ${({ percent }) => percent && css` 69 | width: ${percent}; 70 | `} 71 | `) 72 | 73 | export const BufferBar = setDisplayName('BufferBar')(Bar.extend` 74 | background-image: 75 | linear-gradient(to right, ${g.progressSecondaryColor}, ${g.progressSecondaryColor}), 76 | linear-gradient(to right, ${g.progressMainColor}, ${g.progressMainColor}); 77 | z-index: 0; 78 | left: 0; 79 | width: 100%; 80 | `) 81 | 82 | export const AuxBar = setDisplayName('AuxBar')(Bar.extend` 83 | right: 0; 84 | ${({ indeterminate }) => indeterminate && css` 85 | background-image: 86 | linear-gradient(to right, ${g.progressSecondaryColor}, ${g.progressSecondaryColor}), 87 | linear-gradient(to right,${g.progressMainColor}, ${g.progressMainColor}); 88 | animation-name: ${indeterminate2}; 89 | animation-duration: 2s; 90 | animation-iteration-count: infinite; 91 | animation-timing-function: linear; 92 | `} 93 | `) 94 | -------------------------------------------------------------------------------- /src/components/progress/demos/Progress.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Progress } from '../../../' 3 | 4 | export default () => 5 | -------------------------------------------------------------------------------- /src/components/progress/demos/ProgressIndeterminate.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Progress } from '../../../' 3 | 4 | export default () => 5 | -------------------------------------------------------------------------------- /src/components/progress/index.js: -------------------------------------------------------------------------------- 1 | export Progress from './Progress' 2 | -------------------------------------------------------------------------------- /src/components/radio/Radio.js: -------------------------------------------------------------------------------- 1 | import { compose, setDisplayName, setPropTypes } from 'recompose' 2 | import PropTypes from 'prop-types' 3 | import React from 'react' 4 | 5 | import { 6 | InnerCircle, 7 | OuterCircle, 8 | RadioButton, 9 | RadioLabel, 10 | RadioStyle, 11 | } from './Radio.style' 12 | import { proxyStyledStatics } from '../../hocs' 13 | 14 | export const RadioBase = ({ 15 | label, 16 | disabled, 17 | __StyledComponent__: Styled, 18 | ...props 19 | }) => ( 20 | 21 | 22 | {label && {label}} 23 | 24 | 25 | 26 | ) 27 | 28 | const enhance = compose( 29 | proxyStyledStatics(RadioStyle), 30 | setDisplayName('Radio'), 31 | setPropTypes({ 32 | label: PropTypes.string, 33 | disabled: PropTypes.bool, 34 | }), 35 | ) 36 | 37 | export default enhance(RadioBase) 38 | -------------------------------------------------------------------------------- /src/components/radio/Radio.style.js: -------------------------------------------------------------------------------- 1 | import { setDisplayName } from 'recompose' 2 | import styled, { css } from 'styled-components' 3 | 4 | import { getters as g } from '../../util' 5 | import { materialAnimationDefault } from '../../mixins' 6 | 7 | export const RadioStyle = setDisplayName('RadioStyle')(styled.label` 8 | position: relative; 9 | font-size: ${g.radioLabelFontSize}px; 10 | line-height: ${g.radioLabelHeight}px; 11 | display: inline-block; 12 | vertical-align: middle; 13 | box-sizing: border-box; 14 | min-height: ${g.radioLabelHeight}px; 15 | margin: 0; 16 | padding-left: ${({ theme }) => theme.radioButtonSize + theme.radioPadding}px; 17 | `) 18 | 19 | export const RadioButton = setDisplayName('RadioButton')(styled.input` 20 | line-height: ${g.radioLabelHeight}px; 21 | position: absolute; 22 | width: 0; 23 | height: 0; 24 | margin: 0; 25 | padding: 0; 26 | opacity: 0; 27 | -ms-appearance: none; 28 | -moz-appearance: none; 29 | -webkit-appearance: none; 30 | appearance: none; 31 | border: none; 32 | &:checked ~ div:first-of-type { 33 | border: 2px solid ${g.radioColor}; 34 | } 35 | &:checked ~ div:nth-of-type(2) { 36 | transform: scale(1, 1); 37 | } 38 | `) 39 | 40 | export const OuterCircle = setDisplayName('OuterCircle')(styled.div` 41 | position: absolute; 42 | top: ${g.radioTopOffset}px; 43 | left: 0; 44 | display: inline-block; 45 | box-sizing: border-box; 46 | width: ${g.radioButtonSize}px; 47 | height: ${g.radioButtonSize}px; 48 | margin: 0; 49 | cursor: pointer; 50 | border: 2px solid ${g.radioOffColor}; 51 | border-radius: 50%; 52 | z-index: 2; 53 | 54 | ${({ disabled }) => disabled && css` 55 | border: 2px solid ${g.radioDisabledColor}; 56 | cursor: auto; 57 | `} 58 | `) 59 | 60 | export const InnerCircle = setDisplayName('InnerCircle')(styled.div` 61 | position: absolute; 62 | z-index: 1; 63 | margin: 0; 64 | top: ${({ theme }) => theme.radioTopOffset + theme.radioInnerMargin}px; 65 | left: ${g.radioInnerMargin}px; 66 | box-sizing: border-box; 67 | width: ${({ theme }) => theme.radioButtonSize - theme.radioInnerMargin * 2}px; 68 | height: ${({ theme }) => theme.radioButtonSize - theme.radioInnerMargin * 2}px; 69 | cursor: pointer; 70 | ${materialAnimationDefault('0.28s')} 71 | transition-property: transform; 72 | transform: scale(0, 0); 73 | border-radius: 50%; 74 | background: ${g.radioColor}; 75 | 76 | ${({ disabled }) => disabled && css` 77 | background: ${g.radioDisabledColor}; 78 | cursor: auto; 79 | `} 80 | 81 | ${({ focused }) => focused && css` 82 | box-shadow: 0 0 0px 10px rgba(0, 0, 0, 0.1); 83 | `} 84 | `) 85 | 86 | export const RadioLabel = setDisplayName('RadioLabel')(styled.span` 87 | cursor: pointer; 88 | 89 | ${({ disabled }) => disabled && css` 90 | color: ${g.radioDisabledColor}; 91 | cursor: auto; 92 | `} 93 | `) 94 | -------------------------------------------------------------------------------- /src/components/radio/demos/Radio.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Radio } from '../../../' 3 | 4 | export default () => ( 5 | 6 | 7 | 8 | 9 | ) 10 | -------------------------------------------------------------------------------- /src/components/radio/index.js: -------------------------------------------------------------------------------- 1 | export Radio from './Radio' 2 | -------------------------------------------------------------------------------- /src/components/ripple/Ripple.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { compose, setDisplayName } from 'recompose' 3 | 4 | import { proxyStyledStatics } from '../../hocs' 5 | import { getRippleSize, getRippleCoords } from './helpers' 6 | import { RippleEffect, RippleWrap } from './Ripple.style' 7 | 8 | export class RippleBase extends Component { 9 | state = { 10 | opacity: 0, 11 | } 12 | 13 | componentDidMount() { 14 | this.size = getRippleSize(this.wrapper) 15 | } 16 | 17 | handleMouseDown = (e) => { 18 | const coords = getRippleCoords(e) 19 | const translate = `translate(-50%, -50%) translate(${coords})` 20 | const initalScale = ' scale3d(0.0001, 0.0001, 1)' 21 | const finalScale = ' scale3d(1, 1, 1)' 22 | this.setState({ 23 | size: this.size, 24 | shouldAnimate: false, 25 | transform: translate + initalScale, 26 | opacity: 0.3, 27 | }) 28 | requestAnimationFrame(() => { 29 | this.setState({ 30 | shouldAnimate: true, 31 | transform: translate + finalScale, 32 | }) 33 | }) 34 | } 35 | 36 | handleMouseUp = () => { 37 | this.setState({ opacity: 0 }) 38 | } 39 | 40 | render() { 41 | const { __StyledComponent__: Styled } = this.props 42 | 43 | return ( 44 | { 46 | this.wrapper = wrapper 47 | }} 48 | onMouseDown={this.handleMouseDown} 49 | onMouseUp={this.handleMouseUp} 50 | onMouseOut={this.handleMouseUp} 51 | onFocus={this.handleMouseDown} 52 | onBlur={this.handleMouseUp} 53 | className={this.props.className} 54 | > 55 | 67 | 68 | ) 69 | } 70 | } 71 | 72 | const enhance = compose( 73 | proxyStyledStatics(RippleWrap), 74 | setDisplayName('Ripple'), 75 | ) 76 | 77 | export default enhance(RippleBase) 78 | -------------------------------------------------------------------------------- /src/components/ripple/Ripple.style.js: -------------------------------------------------------------------------------- 1 | import { cond, prop, always, T } from 'lodash/fp' 2 | import { setDisplayName } from 'recompose' 3 | import styled, { css } from 'styled-components' 4 | 5 | import { getters as g, rgba } from '../../util' 6 | 7 | export const RippleEffect = setDisplayName('RippleEffect')(styled.div.attrs({ 8 | color: cond([ 9 | [prop('dark'), always('rgba(0,0,0,.3)')], 10 | [prop('colored'), ({ theme }) => rgba(theme.colorPrimary, 0.5)], 11 | [prop('accent'), ({ theme }) => rgba(theme.colorAccent, 0.5)], 12 | [T, always('white')], 13 | ]), 14 | })` 15 | background-color: ${prop('color')}; 16 | border-radius: 50%; 17 | transform: 'none'; 18 | pointer-events: none; 19 | position: absolute; 20 | top: 0; 21 | left: 0; 22 | overflow: hidden; 23 | transition-duration: 0.6s, 0.6s, 0.6s, 1.2s; 24 | transition-timing-function: ${g.animationCurveLinearOutSlowIn}; 25 | `) 26 | 27 | export const RippleWrap = setDisplayName('RippleWrap')(styled.div` 28 | display: block; 29 | position: absolute; 30 | top: 0; 31 | bottom: 0; 32 | left: 0; 33 | right: 0; 34 | z-index: 4; 35 | overflow: hidden; 36 | ${({ toggle }) => toggle && css` 37 | top: -6px; 38 | left: -10px; 39 | right: auto; 40 | bottom: auto; 41 | height: 36px; 42 | width: 36px; 43 | `} 44 | ${({ round }) => round && css` 45 | border-radius: 50%; 46 | -webkit-mask-image: -webkit-radial-gradient(circle, white, black); 47 | `} 48 | `) 49 | -------------------------------------------------------------------------------- /src/components/ripple/helpers.js: -------------------------------------------------------------------------------- 1 | import { round } from 'lodash' 2 | import { half, double, diagonalLength, px } from '../../util' 3 | 4 | function isKeyboardClick({ clientX, clientY }) { 5 | if (clientX === 0 && clientY === 0) { 6 | return true 7 | } 8 | return false 9 | } 10 | 11 | function getX({ clientX, touches }) { 12 | return clientX !== undefined ? clientX : touches[0].clientX 13 | } 14 | 15 | function getY({ clientY, touches }) { 16 | return clientY !== undefined ? clientY : touches[0].clientY 17 | } 18 | 19 | export function getRippleSize(e) { 20 | if (!e) { 21 | return '0px' 22 | } 23 | 24 | const { width, height } = e.getBoundingClientRect() 25 | return px(double(diagonalLength(width, height)) + 2) 26 | } 27 | 28 | export function getRippleCoords(e) { 29 | const rect = e.currentTarget.getBoundingClientRect() 30 | const { height, width, left, top } = rect 31 | const coords = isKeyboardClick(e) 32 | ? [width, height].map(half) 33 | : [getX(e) - left, getY(e) - top] 34 | 35 | return coords 36 | .map(round) 37 | .map(px) 38 | .join(', ') 39 | } 40 | -------------------------------------------------------------------------------- /src/components/ripple/index.js: -------------------------------------------------------------------------------- 1 | export Ripple from './Ripple' 2 | -------------------------------------------------------------------------------- /src/components/slider/Slider.js: -------------------------------------------------------------------------------- 1 | import { compose, setDisplayName, setPropTypes } from 'recompose' 2 | import PropTypes from 'prop-types' 3 | import React from 'react' 4 | 5 | import { Input } from '../../input' 6 | import { SliderInput, SliderContainer, SliderBackground } from './Slider.style' 7 | import { proxyStyledStatics } from '../../hocs' 8 | 9 | export class SliderBase extends Input { 10 | state = { 11 | ...this.state, 12 | value: this.props.value || this.props.defaultValue || this.props.min, 13 | active: this.props.active, 14 | } 15 | 16 | handleMouseDown = () => { 17 | this.setState({ active: true }) 18 | } 19 | 20 | handleMouseUp = (e) => { 21 | this.setState({ active: false }) 22 | this.handleBlur(e) 23 | } 24 | 25 | render() { 26 | const { disabled, max, min, __StyledComponent__: Styled } = this.props 27 | const { active, value, focused } = this.state 28 | 29 | const percent = (value - min) / (max - min) 30 | 31 | return ( 32 | 33 | 49 | 55 | 56 | ) 57 | } 58 | } 59 | 60 | const enhance = compose( 61 | proxyStyledStatics(SliderContainer), 62 | setDisplayName('Slider'), 63 | setPropTypes({ 64 | focused: PropTypes.bool, 65 | autoFocus: PropTypes.bool, 66 | active: PropTypes.bool, 67 | value: PropTypes.number, 68 | defaultValue: PropTypes.number, 69 | disabled: PropTypes.bool, 70 | min: PropTypes.number, 71 | max: PropTypes.number, 72 | onChange: PropTypes.func, 73 | onBlur: PropTypes.func, 74 | onFocus: PropTypes.func, 75 | }), 76 | ) 77 | 78 | export default enhance(SliderBase) 79 | -------------------------------------------------------------------------------- /src/components/slider/demos/default.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Slider } from '../../../' 3 | 4 | export default () => 5 | -------------------------------------------------------------------------------- /src/components/slider/demos/disabled.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Slider } from '../../../' 3 | 4 | export default () => 5 | -------------------------------------------------------------------------------- /src/components/slider/demos/startingValue.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Slider } from '../../../' 3 | 4 | export default () => 5 | -------------------------------------------------------------------------------- /src/components/slider/index.js: -------------------------------------------------------------------------------- 1 | export Slider from './Slider' 2 | -------------------------------------------------------------------------------- /src/components/snackbar/Action.style.js: -------------------------------------------------------------------------------- 1 | import { setDisplayName } from 'recompose' 2 | import styled from 'styled-components' 3 | 4 | import { getters as g } from '../../util' 5 | import { typoButton } from '../../mixins' 6 | 7 | export const SnackbarAction = setDisplayName('SnackbarAction')(styled.button` 8 | background: transparent; 9 | border: none; 10 | color: ${g.snackbarActionColor}; 11 | float: right; 12 | text-transform: uppercase; 13 | padding: 14px 24px 14px 12px; 14 | ${typoButton()} 15 | overflow: hidden; 16 | outline: none; 17 | opacity: 0; 18 | pointer-events: none; 19 | cursor: pointer; 20 | text-decoration: none; 21 | text-align: center; 22 | align-self: center; 23 | flex-shrink: 0; 24 | font-weight: 600; 25 | line-height: inherit; 26 | 27 | &::-moz-focus-inner { 28 | border: 0; 29 | } 30 | &:not([aria-hidden]) { 31 | opacity: 1; 32 | pointer-events: auto; 33 | } 34 | `) 35 | -------------------------------------------------------------------------------- /src/components/snackbar/Message.style.js: -------------------------------------------------------------------------------- 1 | import { setDisplayName } from 'recompose' 2 | import styled from 'styled-components' 3 | 4 | export const SnackbarMessage = setDisplayName('SnackbarMessage')(styled.div` 5 | padding: 14px 12px 14px 24px; 6 | vertical-align: middle; 7 | color: white; 8 | float: left; 9 | `) 10 | -------------------------------------------------------------------------------- /src/components/snackbar/Snackbar.js: -------------------------------------------------------------------------------- 1 | import { setPropTypes, setDisplayName, compose } from 'recompose' 2 | import PropTypes from 'prop-types' 3 | import React from 'react' 4 | 5 | import { SnackbarAction } from './Action.style' 6 | import { SnackbarMessage } from './Message.style' 7 | import { proxyStyledStatics } from '../../hocs' 8 | import { SnackbarStyle } from './Snackbar.style' 9 | 10 | export const SnackbarBase = ({ 11 | message, 12 | actionText, 13 | actionHandler, 14 | __StyledComponent__: Styled, 15 | ...props 16 | }) => ( 17 | 18 | {message && {message}} 19 | {actionText && ( 20 | {actionText} 21 | )} 22 | 23 | ) 24 | 25 | const enhance = compose( 26 | proxyStyledStatics(SnackbarStyle), 27 | setDisplayName('Snackbar'), 28 | setPropTypes({ 29 | message: PropTypes.node, 30 | actionText: PropTypes.string, 31 | actionHandler: PropTypes.func, 32 | }), 33 | ) 34 | 35 | export default enhance(SnackbarBase) 36 | -------------------------------------------------------------------------------- /src/components/snackbar/Snackbar.style.js: -------------------------------------------------------------------------------- 1 | import { setDisplayName } from 'recompose' 2 | import styled from 'styled-components' 3 | 4 | import { getters as g } from '../../util' 5 | 6 | export const SnackbarStyle = setDisplayName('SnacbarStyle')(styled.div` 7 | background-color: #323232; 8 | display: flex; 9 | justify-content: space-between; 10 | font-family: ${g.preferredFont}; 11 | @media(max-width: ${({ theme }) => theme.snackbarTabletBreakpoint - 1}px) { 12 | width: 100%; 13 | min-height: 48px; 14 | } 15 | @media(min-width: ${g.snackbarTabletBreakpoint}px) { 16 | min-width: 288px; 17 | max-width: 568px; 18 | border-radius: 2px; 19 | margin: 0 16px 16px 16px; 20 | } 21 | `) 22 | -------------------------------------------------------------------------------- /src/components/snackbar/demos/Snackbar.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { Toast, Snackbar, Button } from '../../../' 3 | 4 | export default class Demo extends Component { 5 | state = { 6 | messages: [], 7 | showMessage: false, 8 | } 9 | 10 | componentWillUpdate(nextProps, nextState) { 11 | if ( 12 | nextState.messages[0] && 13 | nextState.messages[0] !== this.state.messages[0] 14 | ) { 15 | requestAnimationFrame(() => this.setState({ showMessage: true })) 16 | this.timeout = setTimeout(() => { 17 | this.setState({ showMessage: false }) 18 | this.timeout = setTimeout(() => { 19 | this.setState({ messages: this.state.messages.slice(1) }) 20 | }, 300) 21 | }, 2000) 22 | } 23 | } 24 | 25 | componentWillUnmount() { 26 | clearTimeout(this.timeout) 27 | } 28 | 29 | counter = 0 30 | 31 | increment = () => { 32 | this.counter += 1 33 | this.setState({ 34 | messages: [ 35 | ...this.state.messages, 36 | { message: `Example message ${this.counter}` }, 37 | ], 38 | }) 39 | } 40 | 41 | render() { 42 | return ( 43 |
44 |
49 | ) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/components/snackbar/demos/SnackbarWithAction.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { Toast, Snackbar, Button } from '../../../' 3 | 4 | export default class Demo extends Component { 5 | state = { 6 | messages: [], 7 | showMessage: false, 8 | buttonColor: null, 9 | } 10 | 11 | componentWillUpdate(nextProps, nextState) { 12 | if ( 13 | nextState.messages[0] && 14 | nextState.messages[0] !== this.state.messages[0] 15 | ) { 16 | requestAnimationFrame(() => this.setState({ showMessage: true })) 17 | this.timeout = setTimeout(() => { 18 | this.setState({ showMessage: false }) 19 | this.timeout = setTimeout(() => { 20 | this.setState({ messages: this.state.messages.slice(1) }) 21 | }, 300) 22 | }, 2000) 23 | } 24 | } 25 | 26 | componentWillUnmount() { 27 | clearTimeout(this.timeout) 28 | } 29 | 30 | changeButtonColor = () => { 31 | this.counter += 1 32 | this.setState({ 33 | buttonColor: `#${Math.floor(Math.random() * 0xffffff).toString(16)}`, 34 | messages: [ 35 | ...this.state.messages, 36 | { 37 | message: 'Button color changed', 38 | actionText: 'Undo', 39 | actionHandler: () => this.setState({ buttonColor: null }), 40 | }, 41 | ], 42 | }) 43 | } 44 | 45 | render() { 46 | return ( 47 |
48 |
58 | ) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/components/snackbar/index.js: -------------------------------------------------------------------------------- 1 | export Snackbar from './Snackbar' 2 | -------------------------------------------------------------------------------- /src/components/spinner/Spinner.js: -------------------------------------------------------------------------------- 1 | import { compose, setPropTypes, setDisplayName } from 'recompose' 2 | import PropTypes from 'prop-types' 3 | import React from 'react' 4 | 5 | import { 6 | SpinnerStyle, 7 | LayerOne, 8 | LayerTwo, 9 | LayerThree, 10 | LayerFour, 11 | GapPatch, 12 | CircleClipper, 13 | Circle, 14 | } from './Spinner.style' 15 | import { proxyStyledStatics } from '../../hocs' 16 | 17 | const layers = [LayerOne, LayerTwo, LayerThree, LayerFour] 18 | 19 | export const SpinnerBase = ({ __StyledComponent__: Styled, ...props }) => ( 20 | 21 | {layers.map((Layer) => ( 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | ))} 34 | 35 | ) 36 | 37 | const enhance = compose( 38 | proxyStyledStatics(SpinnerStyle), 39 | setDisplayName('Spinner'), 40 | setPropTypes({ 41 | active: PropTypes.bool, 42 | singleColor: PropTypes.bool, 43 | }), 44 | ) 45 | 46 | export default enhance(SpinnerBase) 47 | -------------------------------------------------------------------------------- /src/components/spinner/demos/Spinner.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Spinner } from '../../../' 3 | 4 | export default () => 5 | -------------------------------------------------------------------------------- /src/components/spinner/demos/SpinnerSingleColor.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Spinner } from '../../../' 3 | 4 | export default () => 5 | -------------------------------------------------------------------------------- /src/components/spinner/index.js: -------------------------------------------------------------------------------- 1 | export Spinner from './Spinner' 2 | -------------------------------------------------------------------------------- /src/components/switch/Switch.js: -------------------------------------------------------------------------------- 1 | import { compose, setDisplayName, setPropTypes } from 'recompose' 2 | import PropTypes from 'prop-types' 3 | import React from 'react' 4 | 5 | import { proxyStyledStatics } from '../../hocs' 6 | import { 7 | SwitchWrapStyle, 8 | SwitchStyle, 9 | SwitchButton, 10 | SwitchLabel, 11 | Track, 12 | Thumb, 13 | } from './Switch.style' 14 | 15 | export const SwitchBase = ({ 16 | label, 17 | disabled, 18 | __StyledComponent__: Styled, 19 | ...props 20 | }) => ( 21 | 22 | 23 | 24 | {label && {label}} 25 | 26 | 27 | 28 | 29 | ) 30 | 31 | const enhance = compose( 32 | proxyStyledStatics(SwitchWrapStyle), 33 | setDisplayName('Switch'), 34 | setPropTypes({ 35 | label: PropTypes.string, 36 | disabled: PropTypes.bool, 37 | }), 38 | ) 39 | 40 | export default enhance(SwitchBase) 41 | -------------------------------------------------------------------------------- /src/components/switch/Switch.style.js: -------------------------------------------------------------------------------- 1 | import { setDisplayName } from 'recompose' 2 | import styled, { css } from 'styled-components' 3 | 4 | import { getters as g } from '../../util' 5 | import { materialAnimationDefault, shadow2dp, shadow3dp } from '../../mixins' 6 | 7 | export const SwitchWrapStyle = setDisplayName('SwitchWrapStyle')(styled.div` 8 | display: inline-block; 9 | `) 10 | 11 | export const SwitchStyle = setDisplayName('SwitchStyle')(styled.label` 12 | position: relative; 13 | z-index: 1; 14 | vertical-align: middle; 15 | display: inline-block; 16 | box-sizing: border-box; 17 | width: 100%; 18 | height: ${g.switchLabelHeight}px; 19 | margin: 0; 20 | padding: 0; 21 | overflow: visible; 22 | padding-left: ${({ theme }) => theme.switchTrackLength}px; 23 | -webkit-touch-callout: none; 24 | user-select: none; 25 | `) 26 | 27 | export const SwitchButton = setDisplayName('SwitchButton')(styled.input` 28 | line-height: ${g.switchLabelHeight}px; 29 | position: absolute; 30 | width: 0; 31 | height: 0; 32 | margin: 0; 33 | padding: 0; 34 | opacity: 0; 35 | -ms-appearance: none; 36 | -moz-appearance: none; 37 | -webkit-appearance: none; 38 | appearance: none; 39 | border: none; 40 | &:checked ~ div:first-of-type { 41 | background: ${g.switchTrackColor}; 42 | } 43 | &:checked ~ div:nth-of-type(2) { 44 | background: ${g.switchThumbColor}; 45 | left: ${({ theme }) => theme.switchTrackLength - theme.switchThumbSize}px; 46 | ${shadow3dp()}; 47 | } 48 | `) 49 | 50 | export const Track = setDisplayName('Track')(styled.div` 51 | background: ${g.switchOffTrackColor}; 52 | position: absolute; 53 | left: 0; 54 | top: ${g.switchTrackTop}px; 55 | height: ${g.switchTrackHeight}px; 56 | width: ${g.switchTrackLength}px; 57 | border-radius: ${g.switchTrackHeight}px; 58 | cursor: pointer; 59 | ${({ disabled }) => 60 | disabled && 61 | css` 62 | border: 2px solid ${g.switchDisabledTrackColor}; 63 | cursor: auto; 64 | `}; 65 | `) 66 | 67 | export const Thumb = setDisplayName('Thumb')(styled.div` 68 | background: ${g.switchOffThumbColor}; 69 | position: absolute; 70 | left: 0; 71 | top: ${g.switchThumbTop}px; 72 | height: ${g.switchThumbSize}px; 73 | width: ${g.switchThumbSize}px; 74 | border-radius: 50%; 75 | cursor: pointer; 76 | ${shadow2dp()} ${materialAnimationDefault('0.28s')} transition-property: left; 77 | `) 78 | 79 | export const SwitchLabel = setDisplayName('SwitchLabel')(styled.span` 80 | position: relative; 81 | cursor: pointer; 82 | font-size: ${g.switchLabelFontSize}px; 83 | line-height: ${g.switchLabelHeight}px; 84 | margin: 0; 85 | left: 24px; 86 | ${({ disabled }) => 87 | disabled && 88 | css` 89 | color: ${g.switchDisabledTumbColor}; 90 | cursor: auto; 91 | `}; 92 | `) 93 | -------------------------------------------------------------------------------- /src/components/switch/demos/Switch.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Switch } from '../../../' 3 | 4 | export default () => 5 | -------------------------------------------------------------------------------- /src/components/switch/index.js: -------------------------------------------------------------------------------- 1 | export Switch from './Switch' 2 | -------------------------------------------------------------------------------- /src/components/tables/Table.style.js: -------------------------------------------------------------------------------- 1 | import styled, { css } from 'styled-components' 2 | 3 | import { getters as g } from '../../util' 4 | import { Icon } from '../icons' 5 | import { materialAnimationDefault, typoBody2 } from '../../mixins' 6 | 7 | export const Table = styled.table` 8 | position: relative; 9 | border: ${g.dataTableDividers}; 10 | border-collapse: collapse; 11 | white-space: nowrap; 12 | font-size: ${g.dataTableFontSize}px; 13 | background-color: #fff; 14 | ` 15 | 16 | export const TableHeader = styled.thead` 17 | padding-bottom: 3px; 18 | 19 | ` 20 | 21 | export const TableBody = styled.tbody` 22 | tr:hover { 23 | background-color: ${g.dataTableHoverColor}; 24 | } 25 | ` 26 | 27 | export const Row = styled.tr` 28 | position: relative; 29 | height: ${g.dataTableRowHeight}px; 30 | ${materialAnimationDefault('0.28s')}; 31 | transition-property: background-color; 32 | ${({ isSelected }) => isSelected && css` 33 | background-color: ${g.dataTableSelectionColor}; 34 | `} 35 | ` 36 | 37 | const CellBase = styled.td` 38 | padding: 0 ${g.dataTableColumnPadding}px 12px ${g.dataTableColumnPadding}px; 39 | text-align: right; 40 | 41 | &:first-of-type { 42 | padding-left: 24px; 43 | } 44 | 45 | &:last-of-type { 46 | padding-right: 24px; 47 | } 48 | 49 | ${({ nonNumeric }) => nonNumeric && css` 50 | text-align: left; 51 | `} 52 | ` 53 | 54 | export const Cell = CellBase.extend` 55 | position: relative; 56 | vertical-align: middle; 57 | height: ${g.dataTableRowHeight}px; 58 | border-top: ${g.dataTableDividers}; 59 | border-bottom: ${g.dataTableDividers}; 60 | padding-top: ${g.dataTableCellTop}px; 61 | box-sizing: border-box; 62 | ` 63 | 64 | export const HeaderCell = CellBase.withComponent('th').extend` 65 | position: relative; 66 | vertical-align: bottom; 67 | text-overflow: ellipsis; 68 | ${typoBody2()} 69 | height: ${g.dataTableRowHeight}px; 70 | font-size: ${g.dataTableHeaderFontSize}px; 71 | color: ${g.dataTableHeaderColor}; 72 | padding-bottom: 8px; 73 | box-sizing: border-box; 74 | ` 75 | 76 | export const TableIcon = Icon.extend` 77 | font-size: ${g.dataTableHeaderSortIconSize}px; 78 | margin-right: 5px; 79 | vertical-align: bottom; 80 | ` 81 | -------------------------------------------------------------------------------- /src/components/tables/demos/DataTable.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { css } from 'styled-components' 3 | 4 | import { 5 | Table, 6 | TableHeader, 7 | TableBody, 8 | Row, 9 | HeaderCell, 10 | TableIcon, 11 | Cell, 12 | shadow2dp, 13 | Checkbox, 14 | theme, 15 | } from '../../../' 16 | 17 | export const DemoTable = Table.extend` 18 | ${shadow2dp()}; 19 | ` 20 | 21 | export const DemoTh = HeaderCell.extend` 22 | ${({ sorted }) => 23 | sorted && 24 | css` 25 | color: ${theme.textColorPrimary}; 26 | `}; 27 | ` 28 | 29 | export default () => ( 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | Material 39 | 40 | Quantity 41 | Unit price 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | Acrylic (Transparent) 50 | 25 51 | $2.90 52 | 53 | 54 | 55 | 56 | 57 | Laminate (Gold on Blue) 58 | 10 59 | $2.35 60 | 61 | 62 | 63 | 64 | 65 | Plywood (Birch) 66 | 50 67 | $1.25 68 | 69 | 70 | 71 | ) 72 | -------------------------------------------------------------------------------- /src/components/tables/index.js: -------------------------------------------------------------------------------- 1 | export * from './Table.style' 2 | -------------------------------------------------------------------------------- /src/components/textfield/Textfield.js: -------------------------------------------------------------------------------- 1 | import { compose, setPropTypes, setDisplayName, defaultProps } from 'recompose' 2 | import PropTypes from 'prop-types' 3 | import React from 'react' 4 | import omit from 'lodash/omit' 5 | 6 | import { Input as BaseInput } from '../../input' 7 | import { 8 | ErrorMessage, 9 | HelperText, 10 | Input, 11 | Label, 12 | Textarea, 13 | TextfieldStyle, 14 | } from './Textfield.style' 15 | import { proxyStyledStatics } from '../../hocs' 16 | 17 | export class TextfieldBase extends BaseInput { 18 | render() { 19 | const { __StyledComponent__: Styled, ...props } = this.props 20 | const Component = props.multiLine ? Textarea : Input 21 | return ( 22 | 23 | 26 | 33 | {props.error && {props.error}} 34 | {props.helperText && {props.helperText}} 35 | 36 | ) 37 | } 38 | } 39 | 40 | const enhance = compose( 41 | proxyStyledStatics(TextfieldStyle), 42 | setDisplayName('Textfield'), 43 | setPropTypes({ 44 | error: PropTypes.string, 45 | autoFocus: PropTypes.bool, 46 | value: PropTypes.string, 47 | label: PropTypes.string, 48 | multiLine: PropTypes.bool, 49 | }), 50 | defaultProps({ 51 | type: 'text', 52 | }), 53 | ) 54 | 55 | export default enhance(TextfieldBase) 56 | -------------------------------------------------------------------------------- /src/components/textfield/demos/Error.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Textfield } from '../../../' 3 | 4 | export default () => ( 5 | 6 | ) 7 | -------------------------------------------------------------------------------- /src/components/textfield/demos/HelperText.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Textfield } from '../../../' 3 | 4 | export default () => ( 5 | 10 | ) 11 | -------------------------------------------------------------------------------- /src/components/textfield/demos/MultiLine.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Textfield } from '../../../' 3 | 4 | export default () => 5 | -------------------------------------------------------------------------------- /src/components/textfield/demos/SingleLine.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Textfield } from '../../../' 3 | 4 | export default () => 5 | -------------------------------------------------------------------------------- /src/components/textfield/index.js: -------------------------------------------------------------------------------- 1 | export Textfield from './Textfield' 2 | -------------------------------------------------------------------------------- /src/components/toast/Toast.js: -------------------------------------------------------------------------------- 1 | import { setDisplayName, setPropTypes, defaultProps, compose } from 'recompose' 2 | import Portal from 'react-portal' 3 | import PropTypes from 'prop-types' 4 | import React, { Component } from 'react' 5 | 6 | import { ToastAnimation } from './Toast.style' 7 | import { proxyStyledStatics } from '../../hocs' 8 | 9 | export class ToastBase extends Component { 10 | constructor(props) { 11 | super(props) 12 | 13 | this.state = { 14 | isOpened: props.isActive, 15 | isActive: props.isActive, 16 | } 17 | } 18 | 19 | componentWillReceiveProps(nextProps) { 20 | if (!this.state.isActive && nextProps.isActive && !this.isAnimating) { 21 | this.setState({ isOpened: true }) 22 | this.animateIn = setTimeout(() => this.setState({ isActive: true })) 23 | } 24 | 25 | if (this.state.isActive && !nextProps.isActive) { 26 | this.setState({ isActive: false }) 27 | this.isAnimating = true 28 | 29 | this.animateOut = setTimeout(() => { 30 | this.isAnimating = false 31 | if (this.props.isActive) { 32 | this.setState({ isActive: true }) 33 | } else { 34 | this.setState({ isOpened: false }) 35 | } 36 | }, 300) 37 | } 38 | } 39 | 40 | componentWillUnmount() { 41 | clearTimeout(this.animateOut) 42 | clearTimeout(this.animateIn) 43 | } 44 | 45 | render() { 46 | const { __StyledComponent__: Styled, ...props } = this.props 47 | return ( 48 | 49 | 50 | {props.children} 51 | 52 | 53 | ) 54 | } 55 | } 56 | 57 | const enhance = compose( 58 | proxyStyledStatics(ToastAnimation), 59 | setDisplayName('Toast'), 60 | defaultProps({ 61 | position: 'left', 62 | }), 63 | setPropTypes({ 64 | isActive: PropTypes.bool, 65 | position: PropTypes.string, 66 | children: PropTypes.node, 67 | }), 68 | ) 69 | 70 | export default enhance(ToastBase) 71 | -------------------------------------------------------------------------------- /src/components/toast/Toast.style.js: -------------------------------------------------------------------------------- 1 | import { setDisplayName } from 'recompose' 2 | import styled, { css } from 'styled-components' 3 | 4 | import { getters as g } from '../../util' 5 | 6 | export const ToastAnimation = setDisplayName('ToastAnimation')(styled.div` 7 | position: fixed; 8 | bottom: 0; 9 | z-index: 3; 10 | display: block; 11 | will-change: transform; 12 | transform: translate(0, 100%); 13 | transition: transform 0.25s ${g.animationCurveFastOutLinearIn}; 14 | @media(max-width: ${({ theme }) => theme.snackbarTabletBreakpoint - 1}px) { 15 | width: 100%; 16 | left: 0; 17 | } 18 | 19 | @media(min-width: ${g.snackbarTabletBreakpoint}px) { 20 | ${({ position }) => { 21 | switch (position) { 22 | case 'left': 23 | return css` 24 | bottom: 0; 25 | left: 0; 26 | transform: translate(0, 100%); 27 | ` 28 | case 'right': 29 | return css` 30 | bottom: 0; 31 | right: 0; 32 | transform: translate(0, 100%); 33 | ` 34 | default: 35 | return css` 36 | left: 50%; 37 | bottom: 0; 38 | transform: translate(-50%, 100%); 39 | ` 40 | } 41 | }} 42 | } 43 | 44 | ${({ isActive }) => isActive && css` 45 | transform: translate(0, 0); 46 | transition: transform 0.25s ${g.animationCurveLinearOutSlowIn}; 47 | @media(min-width: ${g.snackbarTabletBreakpoint}px) { 48 | ${({ position }) => { 49 | switch (position) { 50 | case 'left': 51 | case 'right': 52 | return css` 53 | transform: translate(0, 0); 54 | ` 55 | default: 56 | return css` 57 | left: 50%; 58 | transform: translate(-50%, 0); 59 | ` 60 | } 61 | }} 62 | } 63 | `} 64 | `) 65 | -------------------------------------------------------------------------------- /src/components/toast/index.js: -------------------------------------------------------------------------------- 1 | export Toast from './Toast' 2 | -------------------------------------------------------------------------------- /src/components/tooltips/Tooltip.js: -------------------------------------------------------------------------------- 1 | import { compose, setDisplayName, setPropTypes, defaultProps } from 'recompose' 2 | import Portal from 'react-portal' 3 | import PropTypes from 'prop-types' 4 | import React, { Component } from 'react' 5 | 6 | import { TooltipStyle, TooltipPosition, TooltipWrapper } from './Tooltip.style' 7 | import { proxyStyledStatics } from '../../hocs' 8 | import getRelativePosition from '../menu/getRelativePosition' 9 | 10 | export class TooltipBase extends Component { 11 | state = { 12 | isVisible: false, 13 | } 14 | 15 | componentWillUnmount() { 16 | window.removeEventListener('scroll', this.setPosition, true) 17 | clearTimeout(this.open) 18 | } 19 | 20 | getPosition() { 21 | const control = getRelativePosition(this.control) 22 | const horizontalCenter = control.left + control.width / 2 23 | const verticalCenter = control.top + control.height / 2 24 | 25 | switch (this.props.position) { 26 | case 'below': 27 | return { x: horizontalCenter, y: control.bottom } 28 | case 'left': 29 | return { x: control.left, y: verticalCenter } 30 | case 'right': 31 | return { x: control.right, y: verticalCenter } 32 | default: 33 | return { x: horizontalCenter, y: control.top } 34 | } 35 | } 36 | 37 | setPosition = () => { 38 | this.setState({ ...this.getPosition() }) 39 | } 40 | 41 | setControl = (ctrl) => { 42 | this.control = ctrl 43 | } 44 | 45 | handleOpen = () => { 46 | this.setState({ isOpened: true }) 47 | this.setPosition() 48 | window.addEventListener('scroll', this.setPosition, true) 49 | this.open = setTimeout(() => { 50 | this.setState({ isVisible: true }) 51 | }, this.props.delay) 52 | } 53 | 54 | handleClose = () => { 55 | window.removeEventListener('scroll', this.setPosition, true) 56 | clearTimeout(this.open) 57 | this.setState({ isOpened: false, isVisible: false }) 58 | } 59 | 60 | render() { 61 | const { children, __StyledComponent__: Styled, ...props } = this.props 62 | 63 | return ( 64 | 70 | {children} 71 | 72 | 73 | 74 | {this.props.message} 75 | 76 | 77 | 78 | 79 | ) 80 | } 81 | } 82 | 83 | const enhance = compose( 84 | proxyStyledStatics(TooltipWrapper), 85 | setDisplayName('Tooltip'), 86 | setPropTypes({ 87 | message: PropTypes.node, 88 | children: PropTypes.node, 89 | position: PropTypes.string, 90 | delay: PropTypes.number, 91 | }), 92 | defaultProps({ 93 | position: 'above', 94 | delay: 0, 95 | }), 96 | ) 97 | 98 | export default enhance(TooltipBase) 99 | -------------------------------------------------------------------------------- /src/components/tooltips/Tooltip.style.js: -------------------------------------------------------------------------------- 1 | import { call, ifProp, switchProp, prop } from 'styled-tools' 2 | import { subtract, add, always } from 'lodash/fp' 3 | import { setDisplayName } from 'recompose' 4 | import styled, { css } from 'styled-components' 5 | 6 | import { arrowTop, arrowBottom, arrowLeft, arrowRight } from '../../mixins' 7 | import { getters as g } from '../../util' 8 | 9 | export const TooltipWrapper = setDisplayName('TooltipWrapper')(styled.div` 10 | display: inline-block; 11 | `) 12 | 13 | export const TooltipPosition = setDisplayName('TooltipPosition')(styled.div` 14 | position: fixed; 15 | transform: scale(0.6); 16 | opacity: 0; 17 | z-index: 999999; 18 | transition: opacity 0.2s ${g.animationCurveLinearOutSlowIn}, 19 | transform 0.2s ${g.animationCurveLinearOutSlowIn}; 20 | ${ifProp('isVisible', css` 21 | opacity: 1; 22 | transform: none; 23 | `)} 24 | ${switchProp('position', { 25 | above: css` 26 | top: ${call(subtract, prop('y'), always(7))}px; 27 | left: ${prop('x')}px; 28 | transform-origin: bottom center; 29 | `, 30 | below: css` 31 | top: ${call(add, prop('y'), always(7))}px; 32 | left: ${prop('x')}px; 33 | transform-origin: top center; 34 | `, 35 | left: css` 36 | top: ${prop('y')}px; 37 | left: ${call(subtract, prop('x'), always(7))}px; 38 | transform-origin: center right; 39 | `, 40 | right: css` 41 | top: ${prop('y')}px; 42 | left: ${call(add, prop('x'), always(7))}px; 43 | transform-origin: center left; 44 | `, 45 | })} 46 | `) 47 | 48 | export const TooltipStyle = setDisplayName('TooltipBase')(styled.div` 49 | display: block; 50 | background: ${g.tooltipBackgroundColor}; 51 | color: ${g.tooltipTextColor}; 52 | font-size: ${g.tooltipFontSize}px; 53 | line-height: 14px; 54 | padding: 8px; 55 | border-radius: 2px; 56 | user-select: none; 57 | pointer-events: none; 58 | position: absolute; 59 | white-space: nowrap; 60 | text-align: center; 61 | ${ifProp('large', css` 62 | font-size: ${g.tooltipFontSizeLarge}px; 63 | padding: 16px; 64 | `)} 65 | ${switchProp('position', { 66 | above: css` 67 | transform: translate(-50%, -100%); 68 | ${call(arrowBottom('5px'), g.tooltipBackgroundColor)} 69 | `, 70 | below: css` 71 | transform: translate(-50%, 0); 72 | ${call(arrowTop('5px'), g.tooltipBackgroundColor)} 73 | `, 74 | left: css` 75 | transform: translate(-100%, -50%); 76 | ${call(arrowRight('5px'), g.tooltipBackgroundColor)} 77 | `, 78 | right: css` 79 | transform: translate(0%, -50%); 80 | ${call(arrowLeft('5px'), g.tooltipBackgroundColor)} 81 | `, 82 | })} 83 | `) 84 | -------------------------------------------------------------------------------- /src/components/tooltips/demos/Above.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { Tooltip, Button, Icon } from '../../../' 4 | 5 | export default () => ( 6 | 7 | 10 | 11 | ) 12 | -------------------------------------------------------------------------------- /src/components/tooltips/demos/Below.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { Tooltip, Button, Icon } from '../../../' 4 | 5 | export default () => ( 6 | 7 | 10 | 11 | ) 12 | -------------------------------------------------------------------------------- /src/components/tooltips/demos/Large.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { Tooltip, Button, Icon } from '../../../' 4 | 5 | export default () => ( 6 | 7 | 10 | 11 | ) 12 | -------------------------------------------------------------------------------- /src/components/tooltips/demos/Left.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { Tooltip, Button, Icon } from '../../../' 4 | 5 | export default () => ( 6 | 7 | 10 | 11 | ) 12 | -------------------------------------------------------------------------------- /src/components/tooltips/demos/MultiLine.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { Tooltip, Button, Icon } from '../../../' 4 | 5 | export default () => ( 6 | 9 | Share content on
social media 10 | 11 | } 12 | > 13 | 16 |
17 | ) 18 | -------------------------------------------------------------------------------- /src/components/tooltips/demos/Right.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { Tooltip, Button, Icon } from '../../../' 4 | 5 | export default () => ( 6 | 7 | 10 | 11 | ) 12 | -------------------------------------------------------------------------------- /src/components/tooltips/index.js: -------------------------------------------------------------------------------- 1 | export Tooltip from './Tooltip' 2 | -------------------------------------------------------------------------------- /src/hocs/index.js: -------------------------------------------------------------------------------- 1 | export proxyStyledStatics from './proxyStyledStatics' 2 | -------------------------------------------------------------------------------- /src/hocs/proxyStyledStatics.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-param-reassign, no-underscore-dangle */ 2 | import { assignIn } from 'lodash' 3 | import { compose } from 'recompose' 4 | 5 | const cloneComponent = (component) => { 6 | const cloned = component.__clonedFrom || component 7 | 8 | function temp(...args) { 9 | return cloned.apply(this, args) 10 | } 11 | 12 | assignIn(temp, component) 13 | 14 | temp.__clonedFrom = cloned 15 | 16 | return temp 17 | } 18 | 19 | const proxyPropStatic = (propName, fnKey) => (Component) => { 20 | Component[fnKey] = function staticFn(...args) { 21 | const Cloned = cloneComponent(this) 22 | const NextStyledComponent = this.defaultProps[propName][fnKey](...args) 23 | Cloned.defaultProps = { 24 | ...this.defaultProps, 25 | [propName]: NextStyledComponent, 26 | } 27 | 28 | return Cloned 29 | } 30 | 31 | return Component 32 | } 33 | 34 | const passComponentAsProp = (propName, childComponent) => (Component) => { 35 | Component.defaultProps = { 36 | ...Component.defaultProps, 37 | [propName]: childComponent, 38 | } 39 | 40 | return Component 41 | } 42 | 43 | const proxyStyledStatics = ( 44 | StyledComponent, 45 | propName = '__StyledComponent__', 46 | ) => 47 | compose( 48 | proxyPropStatic(propName, 'withComponent'), 49 | proxyPropStatic(propName, 'extend'), 50 | passComponentAsProp(propName, StyledComponent), 51 | ) 52 | 53 | export default proxyStyledStatics 54 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | export * from './components/badges' 2 | export * from './components/buttons' 3 | export * from './components/cards' 4 | export * from './components/checkbox' 5 | export * from './components/chips' 6 | export * from './components/dialog' 7 | export * from './components/fonts' 8 | export * from './components/icons' 9 | export * from './components/layout' 10 | export * from './components/list' 11 | export * from './components/menu' 12 | export * from './components/progress' 13 | export * from './components/radio' 14 | export * from './components/ripple' 15 | export * from './components/slider' 16 | export * from './components/snackbar' 17 | export * from './components/spinner' 18 | export * from './components/switch' 19 | export * from './components/tables' 20 | export * from './components/textfield' 21 | export * from './components/toast' 22 | export * from './components/tooltips' 23 | export * from './components/Theme' 24 | export * from './theme' 25 | export * from './mixins' 26 | export * from './hocs' 27 | export * as util from './util' 28 | export initGlobals from './initGlobals' 29 | export theme from './util/getters' 30 | -------------------------------------------------------------------------------- /src/initGlobals.js: -------------------------------------------------------------------------------- 1 | import FontFaceObserver from 'fontfaceobserver' 2 | import { injectGlobal } from 'styled-components' 3 | 4 | const isBrowser = 5 | Object.prototype.toString.call( 6 | typeof process !== 'undefined' ? process : 0, 7 | ) !== '[object process]' 8 | 9 | export default ({ defaultFont: fontObserver = 'Roboto' } = {}) => { 10 | if (isBrowser && fontObserver) { 11 | const fontLoaded = () => document.body.classList.add('fontLoaded') 12 | const fontNotLoaded = () => document.body.classList.remove('fontLoaded') 13 | 14 | const observer = new FontFaceObserver(fontObserver, {}) 15 | observer.load().then(fontLoaded, fontNotLoaded) 16 | } 17 | 18 | // eslint-disable-next-line no-unused-expressions 19 | injectGlobal` 20 | *, *:before, *:after { 21 | box-sizing: border-box; 22 | } 23 | 24 | body { 25 | font-family: Helvetica, Arial, sans-serif; 26 | } 27 | 28 | body.fontLoaded { 29 | font-family: '${fontObserver}', Helvetica, Arial, sans-serif; 30 | } 31 | 32 | html { 33 | width: 100%; 34 | height: 100%; 35 | margin: 0; 36 | font-size: 14px; 37 | line-height: 1.4; 38 | -ms-touch-action: manipulation; 39 | touch-action: manipulation; 40 | } 41 | 42 | body { 43 | width: 100%; 44 | min-height: 100%; 45 | margin: 0; 46 | } 47 | ` 48 | } 49 | -------------------------------------------------------------------------------- /src/input/Input.js: -------------------------------------------------------------------------------- 1 | import { Component } from 'react' 2 | import PropTypes from 'prop-types' 3 | import warning from 'warning' 4 | 5 | import isUndefined from 'lodash/isUndefined' 6 | 7 | const MESSAGE = 8 | 'You provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultValue`. Otherwise, set either `onChange` or `readOnly`.' 9 | 10 | const warnIfReadOnly = (props) => { 11 | const isImmutable = props.value && !props.onChange && !props.readOnly 12 | warning(!isImmutable, MESSAGE) 13 | } 14 | 15 | export default class Input extends Component { 16 | constructor(props) { 17 | super(props) 18 | 19 | this.state = { 20 | value: props.value || props.defaultValue || '', 21 | focused: props.focused || props.autoFocus || false, 22 | } 23 | 24 | warnIfReadOnly(props) 25 | } 26 | 27 | componentWillReceiveProps(nextProps) { 28 | if (!isUndefined(nextProps.value) && nextProps.value !== this.state.value) { 29 | this.setState({ value: nextProps.value }) 30 | } 31 | 32 | if ( 33 | !isUndefined(nextProps.focused) && 34 | nextProps.focused !== this.state.focused 35 | ) { 36 | this.setState({ focused: nextProps.focused }) 37 | } 38 | } 39 | 40 | handleChange = (e) => { 41 | if (isUndefined(this.props.value)) { 42 | this.setState({ value: e.target.value }) 43 | } 44 | 45 | if (this.props.onChange) { 46 | this.props.onChange(e) 47 | } 48 | } 49 | 50 | handleFocus = (e) => { 51 | if (isUndefined(this.props.focused)) { 52 | this.setState({ focused: true }) 53 | } 54 | 55 | if (this.props.onFocus) { 56 | this.props.onFocus(e) 57 | } 58 | } 59 | 60 | handleBlur = (e) => { 61 | if (isUndefined(this.props.focused)) { 62 | this.setState({ focused: false }) 63 | } 64 | 65 | if (this.props.onBlur) { 66 | this.props.onBlur(e) 67 | } 68 | } 69 | 70 | render() { 71 | return null 72 | } 73 | } 74 | 75 | Input.propTypes = { 76 | focused: PropTypes.bool, 77 | autoFocus: PropTypes.bool, 78 | value: PropTypes.any, 79 | onChange: PropTypes.func, 80 | onBlur: PropTypes.func, 81 | onFocus: PropTypes.func, 82 | } 83 | -------------------------------------------------------------------------------- /src/input/index.js: -------------------------------------------------------------------------------- 1 | export Input from './Input' 2 | -------------------------------------------------------------------------------- /src/mixins/animations.style.js: -------------------------------------------------------------------------------- 1 | import { css } from 'styled-components' 2 | import { getters as g } from '../util' 3 | 4 | export function materialAnimationFastOutSlowIn(duration = '0.2s') { 5 | return css` 6 | transition-duration: ${duration}; 7 | transition-timing-function: ${g.animationCurveFastOutSlowIn}; 8 | ` 9 | } 10 | 11 | export function materialAnimationLinearOutSlowIn(duration = '0.2s') { 12 | return css` 13 | transition-duration: ${duration}; 14 | transition-timing-function: ${g.animationCurveLinearOutSlowIn}; 15 | ` 16 | } 17 | 18 | export function materialAnimationFastOutLinearIn(duration = '0.2s') { 19 | return css` 20 | transition-duration: ${duration}; 21 | transition-timing-function: ${g.animationCurveFastOutLinearIn}; 22 | ` 23 | } 24 | 25 | export function materialAnimationDefault(duration = '0.2s') { 26 | return css` 27 | transition-duration: ${duration}; 28 | transition-timing-function: ${g.animationCurveDefault}; 29 | ` 30 | } 31 | -------------------------------------------------------------------------------- /src/mixins/arrow.style.js: -------------------------------------------------------------------------------- 1 | import { css } from 'styled-components' 2 | import { curry } from 'lodash/fp' 3 | 4 | export const arrowTop = curry((size, color) => css` 5 | &:after { 6 | bottom: 100%; 7 | left: 50%; 8 | border: solid transparent; 9 | content: " "; 10 | height: 0; 11 | width: 0; 12 | position: absolute; 13 | pointer-events: none; 14 | border-color: rgba(0, 0, 0, 0); 15 | border-bottom-color: ${color}; 16 | border-width: ${size}; 17 | margin-left: -${size}; 18 | } 19 | `) 20 | 21 | export const arrowRight = curry((size, color) => css` 22 | &:after { 23 | left: 100%; 24 | top: 50%; 25 | border: solid transparent; 26 | content: " "; 27 | height: 0; 28 | width: 0; 29 | position: absolute; 30 | pointer-events: none; 31 | border-color: rgba(0, 0, 0, 0); 32 | border-left-color: ${color}; 33 | border-width: ${size}; 34 | margin-top: -${size}; 35 | } 36 | `) 37 | 38 | export const arrowBottom = curry((size, color) => css` 39 | &:after { 40 | top: 100%; 41 | left: 50%; 42 | border: solid transparent; 43 | content: " "; 44 | height: 0; 45 | width: 0; 46 | position: absolute; 47 | pointer-events: none; 48 | border-color: rgba(0, 0, 0, 0); 49 | border-top-color: ${color}; 50 | border-width: ${size}; 51 | margin-left: -${size}; 52 | } 53 | `) 54 | 55 | export const arrowLeft = curry((size, color) => css` 56 | &:after { 57 | right: 100%; 58 | top: 50%; 59 | border: solid transparent; 60 | content: " "; 61 | height: 0; 62 | width: 0; 63 | position: absolute; 64 | pointer-events: none; 65 | border-color: rgba(0, 0, 0, 0); 66 | border-right-color: ${color}; 67 | border-width: ${size}; 68 | margin-top: -${size}; 69 | } 70 | `) 71 | -------------------------------------------------------------------------------- /src/mixins/index.js: -------------------------------------------------------------------------------- 1 | export * from './type.style' 2 | export * from './shadows.style' 3 | export * from './animations.style' 4 | export * from './arrow.style' 5 | -------------------------------------------------------------------------------- /src/mixins/shadows.style.js: -------------------------------------------------------------------------------- 1 | import { css } from 'styled-components' 2 | import { getters as g } from '../util' 3 | 4 | export function focusShadow() { 5 | return css` 6 | box-shadow: 0 0 8px rgba(0,0,0,.18),0 8px 16px rgba(0,0,0,.36); 7 | ` 8 | } 9 | export function shadow2dp() { 10 | return css` 11 | box-shadow: 0 2px 2px 0 rgba(0, 0, 0, ${g.shadowKeyPenumbraOpacity}), 12 | 0 3px 1px -2px rgba(0, 0, 0, ${g.shadowKeyUmbraOpacity}), 13 | 0 1px 5px 0 rgba(0, 0, 0, ${g.shadowAmbientShadowOpacity}); 14 | ` 15 | } 16 | export function shadow3dp() { 17 | return css` 18 | box-shadow: 0 3px 4px 0 rgba(0, 0, 0, ${g.shadowKeyPenumbraOpacity}), 19 | 0 3px 3px -2px rgba(0, 0, 0, ${g.shadowKeyUmbraOpacity}), 20 | 0 1px 8px 0 rgba(0, 0, 0, ${g.shadowAmbientShadowOpacity}); 21 | ` 22 | } 23 | export function shadow4dp() { 24 | return css` 25 | box-shadow: 0 4px 5px 0 rgba(0, 0, 0, ${g.shadowKeyPenumbraOpacity}), 26 | 0 1px 10px 0 rgba(0, 0, 0, ${g.shadowAmbientShadowOpacity}), 27 | 0 2px 4px -1px rgba(0, 0, 0, ${g.shadowKeyUmbraOpacity}); 28 | ` 29 | } 30 | export function shadow6dp() { 31 | return css` 32 | box-shadow: 0 6px 10px 0 rgba(0, 0, 0, ${g.shadowKeyPenumbraOpacity}), 33 | 0 1px 18px 0 rgba(0, 0, 0, ${g.shadowAmbientShadowOpacity}), 34 | 0 3px 5px -1px rgba(0, 0, 0, ${g.shadowKeyUmbraOpacity}); 35 | ` 36 | } 37 | export function shadow8dp() { 38 | return css` 39 | box-shadow: 0 8px 10px 1px rgba(0, 0, 0, ${g.shadowKeyPenumbraOpacity}), 40 | 0 3px 14px 2px rgba(0, 0, 0, ${g.shadowAmbientShadowOpacity}), 41 | 0 5px 5px -3px rgba(0, 0, 0, ${g.shadowKeyUmbraOpacity}); 42 | ` 43 | } 44 | export function shadow16dp() { 45 | return css` 46 | box-shadow: 0 16px 24px 2px rgba(0, 0, 0, ${g.shadowKeyPenumbraOpacity}), 47 | 0 6px 30px 5px rgba(0, 0, 0, ${g.shadowAmbientShadowOpacity}), 48 | 0 8px 10px -5px rgba(0, 0, 0, ${g.shadowKeyUmbraOpacity}); 49 | ` 50 | } 51 | export function shadow24dp() { 52 | return css` 53 | box-shadow: 0 9px 46px 8px rgba(0, 0, 0, ${g.shadowKeyPenumbraOpacity}), 54 | 0 11px 15px -7px rgba(0, 0, 0, ${g.shadowAmbientShadowOpacity}), 55 | 0 24px 38px 3px rgba(0, 0, 0, ${g.shadowKeyUmbraOpacity}); 56 | ` 57 | } 58 | -------------------------------------------------------------------------------- /src/theme/animation/defaults.js: -------------------------------------------------------------------------------- 1 | export const animationCurveFastOutSlowIn = 'cubic-bezier(0.4, 0, 0.2, 1)' 2 | export const animationCurveLinearOutSlowIn = 'cubic-bezier(0, 0, 0.2, 1)' 3 | export const animationCurveFastOutLinearIn = 'cubic-bezier(0.4, 0, 1, 1)' 4 | export const animationCurveDefault = 'cubic-bezier(0.4, 0, 0.2, 1)' 5 | -------------------------------------------------------------------------------- /src/theme/animation/index.js: -------------------------------------------------------------------------------- 1 | import * as defaults from './defaults' 2 | import createThemer from '../createThemer' 3 | 4 | export default createThemer(defaults) 5 | -------------------------------------------------------------------------------- /src/theme/badge/defaults.js: -------------------------------------------------------------------------------- 1 | export const badgeFontSize = 12 2 | export const badgeSize = 22 3 | export const badgePadding = 2 4 | export const badgeOverlap = 12 5 | -------------------------------------------------------------------------------- /src/theme/badge/index.js: -------------------------------------------------------------------------------- 1 | import * as defaults from './defaults' 2 | import { colorAccentContrast, colorAccent } from '../colors/defaults' 3 | import createThemer from '../createThemer' 4 | import { rgba, rgb } from '../../util/colors' 5 | 6 | export default createThemer( 7 | { colorAccentContrast, colorAccent, ...defaults }, 8 | (theme) => ({ 9 | badgeColor: rgb(theme.colorAccentContrast), 10 | badgeColorInverse: rgb(theme.colorAccent), 11 | badgeBackground: rgb(theme.colorAccent), 12 | badgeBackgroundInverse: rgba(theme.colorAccentContrast, 0.2), 13 | }), 14 | ) 15 | -------------------------------------------------------------------------------- /src/theme/button/defaults.js: -------------------------------------------------------------------------------- 1 | export const buttonMinWidth = 64 2 | export const buttonHeight = 36 3 | export const buttonPadding = 16 4 | export const buttonMargin = 4 5 | export const buttonBorderRadius = 2 6 | 7 | export const buttonFabSize = 56 8 | export const buttonFabSizeMini = 40 9 | export const buttonFabFontSize = 24 10 | 11 | export const buttonIconSize = 32 12 | export const buttonIconSizeMini = 24 13 | -------------------------------------------------------------------------------- /src/theme/button/index.js: -------------------------------------------------------------------------------- 1 | import * as defaults from './defaults' 2 | import createThemer from '../createThemer' 3 | 4 | export default createThemer(defaults) 5 | -------------------------------------------------------------------------------- /src/theme/card/defaults.js: -------------------------------------------------------------------------------- 1 | /* Dimensions */ 2 | export const cardWidth = 330 3 | export const cardHeight = 200 4 | export const cardFontSize = 16 5 | export const cardTitleFontSize = 24 6 | export const cardSubtitleFontSize = 14 7 | export const cardHorizontalPadding = 16 8 | export const cardVerticalPadding = 16 9 | 10 | export const cardTitlePerspectiveOriginX = 165 11 | export const cardTitlePerspectiveOriginY = 56 12 | 13 | export const cardTitleTransformOriginX = 165 14 | export const cardTitleTransformOriginY = 56 15 | 16 | export const cardTitleTextTransformOriginX = 149 17 | export const cardTitleTextTransformOriginY = 48 18 | 19 | export const cardSupportingTextFontSize = 14 20 | export const cardSupportingTextLineHeight = 18 21 | 22 | export const cardActionsFontSize = 16 23 | 24 | export const cardTitleTextFontWeight = 300 25 | export const cardZIndex = 1 26 | 27 | /* Cover image */ 28 | export const cardCoverImageHeight = 186 29 | export const cardBackgroundImageUrl = '' 30 | -------------------------------------------------------------------------------- /src/theme/card/index.js: -------------------------------------------------------------------------------- 1 | import * as defaults from './defaults' 2 | import createThemer from '../createThemer' 3 | 4 | export default createThemer(defaults) 5 | -------------------------------------------------------------------------------- /src/theme/chip/defaults.js: -------------------------------------------------------------------------------- 1 | export const chipBgColor = 'rgb(222, 222, 222)' 2 | export const chipBgActiveColor = 'rgb(214, 214, 214)' 3 | export const chipHeight = 32 4 | export const chipFontSize = 13 5 | -------------------------------------------------------------------------------- /src/theme/chip/index.js: -------------------------------------------------------------------------------- 1 | import * as defaults from './defaults' 2 | import createThemer from '../createThemer' 3 | 4 | export default createThemer(defaults) 5 | -------------------------------------------------------------------------------- /src/theme/colors/index.js: -------------------------------------------------------------------------------- 1 | import * as defaults from './defaults' 2 | import createThemer from '../createThemer' 3 | import { rgba, rgb } from '../../util/colors' 4 | 5 | export default createThemer(defaults, (theme) => ({ 6 | /* ========== Buttons ========== */ 7 | 8 | // Default button colors. 9 | buttonHoverColor: theme.buttonPrimaryColor, 10 | 11 | // Colored button colors. 12 | buttonPrimaryColorAlt: rgb(theme.colorPrimary), 13 | buttonSecondaryColorAlt: rgb(theme.colorPrimaryContrast), 14 | buttonHoverColorAlt: rgb(theme.colorPrimary), 15 | buttonActiveColorAlt: rgb(theme.colorPrimary), 16 | buttonFocusColorAlt: theme.buttonFocusColor, 17 | 18 | // Ripple color for colored raised buttons. 19 | buttonRippleColorAlt: rgb(theme.colorPrimaryContrast), 20 | 21 | // FAB colors and sizes. 22 | buttonFabColorAlt: rgb(theme.colorAccent), 23 | buttonFabHoverColorAlt: rgb(theme.colorAccent), 24 | buttonFabActiveColorAlt: rgb(theme.colorAccent), 25 | buttonFabTextColorAlt: rgb(theme.colorAccentContrast), 26 | buttonFabRippleColorAlt: rgb(theme.colorAccentContrast), 27 | 28 | // Icon button colors and sizes. 29 | buttonIconFocusColor: theme.buttonFocusColor, 30 | 31 | /* ========== Icon Toggles ========== */ 32 | iconToggleFocusColor: theme.buttonFocusColor, 33 | iconToggleCheckedColor: rgb(theme.colorPrimary), 34 | iconToggleCheckedFocusColor: rgba(theme.colorPrimary, 0.26), 35 | 36 | /* ========== Radio Buttons ========== */ 37 | radioColor: rgb(theme.colorPrimary), 38 | 39 | /* ========== Header ========== */ 40 | layoutHeaderBgColor: rgb(theme.colorPrimary), 41 | layoutHeaderTextColor: rgb(theme.colorPrimaryContrast), 42 | layoutHeaderTabTextColor: rgba(theme.colorPrimaryContrast, 0.6), 43 | 44 | /* ========== Tabs ========== */ 45 | layoutHeaderTabHighlight: rgb(theme.colorAccent), 46 | tabHighlightColor: rgb(theme.colorPrimary), 47 | 48 | /* ========== Checkboxes ========== */ 49 | checkboxColor: rgb(theme.colorPrimary), 50 | checkboxFocusColor: rgba(theme.colorPrimary, 0.26), 51 | 52 | /* ========== Switches ========== */ 53 | switchColor: rgb(theme.colorPrimary), 54 | switchFadedColor: rgba(theme.colorPrimary, 0.26), 55 | switchThumbColor: rgb(theme.colorPrimary) || theme.switchColor, 56 | switchTrackColor: rgba(theme.colorPrimary, 0.5), 57 | 58 | /* ========== Spinner ========== */ 59 | spinnerSingleColor: rgb(theme.colorPrimary), 60 | 61 | /* ========== Text fields ========== */ 62 | inputTextHighlightColor: rgb(theme.colorPrimary), 63 | inputTextDisabledColor: theme.inputTextBottomBorderColor, 64 | inputTextDisabledTextColor: theme.inputTextLabelColor, 65 | 66 | /* ========== Card ========== */ 67 | cardImagePlaceholderColor: rgb(theme.colorAccent), 68 | 69 | /* ========== Sliders ========== */ 70 | rangeColor: rgb(theme.colorPrimary), 71 | rangeFadedColor: rgba(theme.colorPrimary, 0.26), 72 | 73 | /* ========== Progress ========== */ 74 | progressMainColor: rgb(theme.colorPrimary), 75 | progressSecondaryColor: rgba(theme.colorPrimaryContrast, 0.7), 76 | progressFallbackBufferColor: rgba(theme.colorPrimaryContrast, 0.9), 77 | 78 | /* ========== Footer ========== */ 79 | footerButtonFillColor: theme.footerColor, 80 | footerUnderlineColor: theme.footerColor, 81 | })) 82 | -------------------------------------------------------------------------------- /src/theme/createTheme.js: -------------------------------------------------------------------------------- 1 | import reduce from 'lodash/reduce' 2 | import assign from 'lodash/assign' 3 | 4 | import animation from './animation' 5 | import badge from './badge' 6 | import button from './button' 7 | import card from './card' 8 | import chip from './chip' 9 | import * as colorDefinitions from './colorDefinitions' 10 | import colors from './colors' 11 | import dataTable from './dataTable' 12 | import dialog from './dialog' 13 | import footer from './footer' 14 | import grid from './grid' 15 | import iconToggle from './iconToggle' 16 | import layout from './layout' 17 | import list from './list' 18 | import menu from './menu' 19 | import progress from './progress' 20 | import radio from './radio' 21 | import shadows from './shadows' 22 | import snackbar from './snackbar' 23 | import spinner from './spinner' 24 | import switcher from './switch' 25 | import textField from './textField' 26 | import tooltip from './tooltip' 27 | import typography from './typography' 28 | 29 | const themers = [ 30 | animation, 31 | badge, 32 | button, 33 | card, 34 | chip, 35 | colors, 36 | dataTable, 37 | dialog, 38 | footer, 39 | grid, 40 | iconToggle, 41 | layout, 42 | list, 43 | menu, 44 | progress, 45 | radio, 46 | shadows, 47 | snackbar, 48 | spinner, 49 | switcher, 50 | textField, 51 | tooltip, 52 | typography, 53 | ] 54 | 55 | export default function createTheme(overrides = {}) { 56 | const compiled = reduce( 57 | themers, 58 | (theme, themer) => assign({}, theme, themer(overrides)), 59 | {}, 60 | ) 61 | 62 | return assign({}, colorDefinitions, compiled, overrides) 63 | } 64 | -------------------------------------------------------------------------------- /src/theme/createThemer.js: -------------------------------------------------------------------------------- 1 | import mapValues from 'lodash/mapValues' 2 | 3 | export default (defaults, compute) => (overrides) => { 4 | const base = mapValues(defaults, (value, key) => overrides[key] || value) 5 | 6 | return compute ? { ...compute(base), ...base } : base 7 | } 8 | -------------------------------------------------------------------------------- /src/theme/dataTable/defaults.js: -------------------------------------------------------------------------------- 1 | export const dataTableFontSize = 13 2 | export const dataTableHeaderFontSize = 12 3 | export const dataTableHeaderSortIconSize = 16 4 | 5 | export const dataTableHeaderColor = 'rgba(0, 0, 0, 0.54)' 6 | export const dataTableHeaderSortedColor = 'rgba(0, 0, 0, 0.87)' 7 | export const dataTableHeaderSortedIconHoverColor = 'rgba(0, 0, 0, 0.26)' 8 | export const dataTableDividerColor = 'rgba(0, 0, 0, 0.12)' 9 | 10 | export const dataTableHoverColor = '#eeeeee' 11 | export const dataTableSelectionColor = '#e0e0e0' 12 | 13 | export const dataTableRowHeight = 48 14 | export const dataTableLastRowHeight = 56 15 | export const dataTableHeaderHeight = 56 16 | 17 | export const dataTableColumnSpacing = 36 18 | 19 | export const dataTableCardHeaderHeight = 64 20 | export const dataTableCardTitleTop = 20 21 | export const dataTableCardPadding = 24 22 | export const dataTableButtonPaddingRight = 16 23 | -------------------------------------------------------------------------------- /src/theme/dataTable/index.js: -------------------------------------------------------------------------------- 1 | import * as defaults from './defaults' 2 | import createThemer from '../createThemer' 3 | 4 | export default createThemer(defaults, (theme) => ({ 5 | dataTableDividers: `1px solid ${theme.dataTableDividerColor}`, 6 | dataTableColumnPadding: theme.dataTableColumnSpacing / 2, 7 | dataTableCellTop: theme.dataTableCardPadding / 2, 8 | })) 9 | -------------------------------------------------------------------------------- /src/theme/defaultTheme.js: -------------------------------------------------------------------------------- 1 | import createTheme from './createTheme' 2 | 3 | const defaultTheme = createTheme() 4 | 5 | export default defaultTheme 6 | -------------------------------------------------------------------------------- /src/theme/dialog/index.js: -------------------------------------------------------------------------------- 1 | import createThemer from '../createThemer' 2 | 3 | export default createThemer({}, (theme) => ({ 4 | dialogContentColor: theme.cardSupportingTextColor, 5 | })) 6 | -------------------------------------------------------------------------------- /src/theme/footer/defaults.js: -------------------------------------------------------------------------------- 1 | /* megaFooter */ 2 | export const footerMinPadding = 16 3 | export const footerPaddingSides = 40 4 | export const footerHeadingFontSize = 14 5 | export const footerBtnSize = 36 6 | 7 | /* miniFooter */ 8 | export const miniFooterPadding = 16 9 | export const miniFooterHeadingFontSize = 24 10 | export const miniFooterBtnSize = 36 11 | -------------------------------------------------------------------------------- /src/theme/footer/index.js: -------------------------------------------------------------------------------- 1 | import * as defaults from './defaults' 2 | import createThemer from '../createThemer' 3 | 4 | export default createThemer(defaults, (theme) => ({ 5 | footerHeadingLineHeight: 1.7 * theme.footerHeadingFontSize, 6 | miniFooterHeadingLineHeight: 1.5 * theme.miniFooterHeadingFontSize, 7 | })) 8 | -------------------------------------------------------------------------------- /src/theme/grid/defaults.js: -------------------------------------------------------------------------------- 1 | export const gridDesktopColumns = 12 2 | export const gridDesktopGutter = 16 3 | export const gridDesktopMargin = 16 4 | 5 | export const gridDesktopBreakpoint = 840 6 | 7 | export const gridTabletColumns = 8 8 | 9 | export const gridTabletBreakpoint = 480 10 | 11 | export const gridPhoneColumns = 4 12 | -------------------------------------------------------------------------------- /src/theme/grid/index.js: -------------------------------------------------------------------------------- 1 | import * as defaults from './defaults' 2 | import createThemer from '../createThemer' 3 | 4 | export default createThemer(defaults, (theme) => ({ 5 | gridTabletGutter: theme.gridDesktopGutter, 6 | gridTabletMargin: theme.gridDesktopMargin, 7 | gridPhoneGutter: theme.gridDesktopGutter, 8 | gridPhoneMargin: theme.gridDesktopMargin, 9 | gridCellDefaultColumns: theme.gridPhoneColumns, 10 | gridMaxColumns: theme.gridDesktopColumns, 11 | })) 12 | -------------------------------------------------------------------------------- /src/theme/iconToggle/defaults.js: -------------------------------------------------------------------------------- 1 | export const iconToggleSize = 32 2 | export const iconToggleFontSize = 24 3 | export const iconToggleRippleSize = 36 4 | -------------------------------------------------------------------------------- /src/theme/iconToggle/index.js: -------------------------------------------------------------------------------- 1 | import * as defaults from './defaults' 2 | import createThemer from '../createThemer' 3 | 4 | export default createThemer(defaults) 5 | -------------------------------------------------------------------------------- /src/theme/index.js: -------------------------------------------------------------------------------- 1 | export defaultTheme from './defaultTheme' 2 | export createTheme from './createTheme' 3 | export * as colors from './colorDefinitions' 4 | -------------------------------------------------------------------------------- /src/theme/layout/defaults.js: -------------------------------------------------------------------------------- 1 | export const layoutDrawerNarrow = 240 2 | export const layoutDrawerWide = 456 3 | 4 | export const layoutHeaderIconSize = 32 5 | export const layoutScreenSizeThreshold = 1024 6 | export const layoutHeaderIconMargin = 24 7 | export const layoutDrawerButtonMobileSize = 32 8 | export const layoutDrawerButtonDesktopSize = 48 9 | 10 | export const layoutHeaderMobileRowHeight = 56 11 | export const layoutHeaderDesktopRowHeight = 64 12 | 13 | export const layoutHeaderDesktopBaseline = 80 14 | export const layoutHeaderMobileBaseline = 72 15 | export const layoutHeaderMobileIndent = 16 16 | export const layoutHeaderDesktopIndent = 40 17 | 18 | export const layoutTabFontSize = 14 19 | export const layoutTabBarHeight = 48 20 | export const layoutTabMobilePadding = 12 21 | export const layoutTabDesktopPadding = 24 22 | export const layoutTabHighlightThickness = 2 23 | -------------------------------------------------------------------------------- /src/theme/layout/index.js: -------------------------------------------------------------------------------- 1 | import * as defaults from './defaults' 2 | import createThemer from '../createThemer' 3 | 4 | export default createThemer(defaults, (theme) => ({ 5 | layoutDrawerWidth: theme.layoutDrawerNarrow, 6 | layoutMobileHeaderHeight: theme.layoutHeaderMobileRowHeight, 7 | layoutDesktopHeaderHeight: theme.layoutHeaderDesktopRowHeight, 8 | })) 9 | -------------------------------------------------------------------------------- /src/theme/list/defaults.js: -------------------------------------------------------------------------------- 1 | export const listBorder = 8 2 | export const listMinHeight = 48 3 | export const listMinPadding = 16 4 | export const listBottomPadding = 20 5 | export const listAvatarTextLeftDistance = 72 6 | export const listIconTextLeftDistance = 72 7 | 8 | export const listAvatarSize = 40 9 | export const listIconSize = 24 10 | 11 | export const listTwoLineHeight = 72 12 | export const listThreeLineHeight = 88 13 | -------------------------------------------------------------------------------- /src/theme/list/index.js: -------------------------------------------------------------------------------- 1 | import * as defaults from './defaults' 2 | import createThemer from '../createThemer' 3 | 4 | export default createThemer(defaults) 5 | -------------------------------------------------------------------------------- /src/theme/menu/defaults.js: -------------------------------------------------------------------------------- 1 | export const menuExpandDuration = 0.3 2 | export const menuFadeDuration = 0.2 3 | -------------------------------------------------------------------------------- /src/theme/menu/index.js: -------------------------------------------------------------------------------- 1 | import * as defaults from './defaults' 2 | import createThemer from '../createThemer' 3 | 4 | export default createThemer(defaults) 5 | -------------------------------------------------------------------------------- /src/theme/progress/defaults.js: -------------------------------------------------------------------------------- 1 | export const barHeight = 4 2 | -------------------------------------------------------------------------------- /src/theme/progress/index.js: -------------------------------------------------------------------------------- 1 | import * as defaults from './defaults' 2 | import createThemer from '../createThemer' 3 | 4 | export default createThemer(defaults) 5 | -------------------------------------------------------------------------------- /src/theme/radio/defaults.js: -------------------------------------------------------------------------------- 1 | export const radioLabelFontSize = 16 2 | export const radioLabelHeight = 24 3 | export const radioButtonSize = 16 4 | export const radioPadding = 8 5 | export const radioRippleSize = 42 6 | -------------------------------------------------------------------------------- /src/theme/radio/index.js: -------------------------------------------------------------------------------- 1 | import * as defaults from './defaults' 2 | import createThemer from '../createThemer' 3 | 4 | export default createThemer(defaults, (theme) => ({ 5 | radioInnerMargin: theme.radioButtonSize / 4, 6 | radioTopOffset: (theme.radioLabelHeight - theme.radioButtonSize) / 2, 7 | })) 8 | -------------------------------------------------------------------------------- /src/theme/shadows/defaults.js: -------------------------------------------------------------------------------- 1 | export const shadowKeyUmbraOpacity = 0.2 2 | export const shadowKeyPenumbraOpacity = 0.14 3 | export const shadowAmbientShadowOpacity = 0.12 4 | -------------------------------------------------------------------------------- /src/theme/shadows/index.js: -------------------------------------------------------------------------------- 1 | import * as defaults from './defaults' 2 | import createThemer from '../createThemer' 3 | 4 | export default createThemer(defaults) 5 | -------------------------------------------------------------------------------- /src/theme/snackbar/index.js: -------------------------------------------------------------------------------- 1 | import createThemer from '../createThemer' 2 | import { colorAccent } from '../colors/defaults' 3 | import { gridTabletBreakpoint } from '../grid/defaults' 4 | import { rgb } from '../../util/colors' 5 | 6 | export default createThemer({ colorAccent, gridTabletBreakpoint }, (theme) => ({ 7 | snackbarTabletBreakpoint: theme.gridTabletBreakpoint, 8 | snackbarActionColor: rgb(theme.colorAccent), 9 | })) 10 | -------------------------------------------------------------------------------- /src/theme/spinner/defaults.js: -------------------------------------------------------------------------------- 1 | export const spinnerSize = 28 2 | export const spinnerStrokeWidth = 3 3 | 4 | // Amount of circle the arc takes up. 5 | export const spinnerArcSize = 270 6 | // Time it takes to expand and contract arc. 7 | export const spinnerArcTime = 1333 8 | // How much the start location of the arc should rotate each time. 9 | export const spinnerArcStartRot = 216 10 | -------------------------------------------------------------------------------- /src/theme/spinner/index.js: -------------------------------------------------------------------------------- 1 | import * as defaults from './defaults' 2 | import createThemer from '../createThemer' 3 | 4 | export default createThemer(defaults, (theme) => ({ 5 | spinnerDuration: 6 | 360 * (theme.spinnerArcTime / theme.spinnerArcStartRot) + 7 | (360 - theme.spinnerArcSize), 8 | })) 9 | -------------------------------------------------------------------------------- /src/theme/switch/defaults.js: -------------------------------------------------------------------------------- 1 | export const switchLabelFontSize = 16 2 | export const switchLabelHeight = 24 3 | export const switchTrackHeight = 14 4 | export const switchTrackLength = 36 5 | export const switchThumbSize = 20 6 | export const switchHelperSize = 8 7 | -------------------------------------------------------------------------------- /src/theme/switch/index.js: -------------------------------------------------------------------------------- 1 | import * as defaults from './defaults' 2 | import createThemer from '../createThemer' 3 | 4 | export default createThemer(defaults, (theme) => ({ 5 | switchTrackTop: (theme.switchLabelHeight - theme.switchTrackHeight) / 2, 6 | switchThumbTop: (theme.switchLabelHeight - theme.switchThumbSize) / 2, 7 | switchRippleSize: theme.switchLabelHeight * 2, 8 | })) 9 | -------------------------------------------------------------------------------- /src/theme/textField/defaults.js: -------------------------------------------------------------------------------- 1 | export const inputTextFontSize = 16 2 | export const inputTextWidth = '100%' 3 | export const inputTextPadding = 4 4 | export const inputTextVerticalSpacing = 20 5 | 6 | export const inputTextButtonSize = 32 7 | export const inputTextFloatingLabelFontsize = 12 8 | export const inputTextExpandableIconTop = 16 9 | -------------------------------------------------------------------------------- /src/theme/textField/index.js: -------------------------------------------------------------------------------- 1 | import * as defaults from './defaults' 2 | import createThemer from '../createThemer' 3 | 4 | export default createThemer(defaults) 5 | -------------------------------------------------------------------------------- /src/theme/tooltip/defaults.js: -------------------------------------------------------------------------------- 1 | export const tooltipFontSize = 10 2 | export const tooltipFontSizeLarge = 14 3 | -------------------------------------------------------------------------------- /src/theme/tooltip/index.js: -------------------------------------------------------------------------------- 1 | import * as defaults from './defaults' 2 | import createThemer from '../createThemer' 3 | 4 | export default createThemer(defaults) 5 | -------------------------------------------------------------------------------- /src/theme/typography/defaults.js: -------------------------------------------------------------------------------- 1 | export const preferredFont = "'Roboto', 'Helvetica', 'Arial', sansSerif" 2 | export const performanceFont = "'Helvetica', 'Arial', sansSerif" 3 | -------------------------------------------------------------------------------- /src/theme/typography/index.js: -------------------------------------------------------------------------------- 1 | import * as defaults from './defaults' 2 | import createThemer from '../createThemer' 3 | 4 | export default createThemer(defaults) 5 | -------------------------------------------------------------------------------- /src/util/colors.js: -------------------------------------------------------------------------------- 1 | export const rgb = (values) => `rgb(${values})` 2 | export const rgba = (values, alpha) => `rgba(${values},${alpha})` 3 | -------------------------------------------------------------------------------- /src/util/getters.js: -------------------------------------------------------------------------------- 1 | import { mapValues } from 'lodash' 2 | import { defaultTheme } from '../theme' 3 | import { rgb, rgba } from './colors' 4 | 5 | const getters = mapValues(defaultTheme, (v, k) => (p) => p.theme[k]) 6 | 7 | getters.fg = (p) => p.theme.fg || p.theme.textColorPrimary 8 | getters.bg = (p) => p.theme.bg || `rgb(${p.theme.white})` 9 | 10 | getters.rgb = (base, value) => (p) => rgb(p.theme[base][value]) 11 | getters.rgba = (base, value, alpha) => (p) => rgba(p.theme[base][value], alpha) 12 | getters.rgbFromProp = (prop, fallback) => (p) => { 13 | const color = p[prop] 14 | 15 | if (!color) { 16 | return fallback ? fallback(p) : 'none' 17 | } 18 | 19 | const [base, value = 500] = color.split('|') 20 | 21 | return rgb(p.theme[base][parseInt(value, 10)]) 22 | } 23 | 24 | export default getters 25 | -------------------------------------------------------------------------------- /src/util/index.js: -------------------------------------------------------------------------------- 1 | export getters from './getters' 2 | export * from './colors' 3 | export * from './math' 4 | export * from './units' 5 | -------------------------------------------------------------------------------- /src/util/math.js: -------------------------------------------------------------------------------- 1 | import { round } from 'lodash' 2 | 3 | export const half = (n) => round(n / 2) 4 | export const double = (n) => n * 2 5 | export const diagonalLength = (width, height) => 6 | Math.sqrt(width * width + height * height) 7 | -------------------------------------------------------------------------------- /src/util/units.js: -------------------------------------------------------------------------------- 1 | export const px = (n) => `${n}px` 2 | -------------------------------------------------------------------------------- /stories/badges.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { storiesOf } from '@storybook/react' 3 | import wrapStory from './decorators/wrapStory' 4 | 5 | import IconOverIcon from '../src/components/badges/demos/IconOverIcon.js' 6 | import IconOverText from '../src/components/badges/demos/IconOverText.js' 7 | import NumberOverIcon from '../src/components/badges/demos/NumberOverIcon.js' 8 | import NumberOverText from '../src/components/badges/demos/NumberOverText.js' 9 | import TextOverButton from '../src/components/badges/demos/TextOverButton.js' 10 | 11 | storiesOf('Badges', module) 12 | .addDecorator(wrapStory) 13 | .add('Icon Over Icon', () => ) 14 | .add('Icon Over Text', () => ) 15 | .add('Number Over Icon', () => ) 16 | .add('Number Over Text', () => ) 17 | .add('Text Over Button', () => ) 18 | -------------------------------------------------------------------------------- /stories/buttons.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { storiesOf } from '@storybook/react' 3 | import wrapStory from './decorators/wrapStory' 4 | 5 | import Fab from '../src/components/buttons/demos/Fab.js' 6 | import FabAccent from '../src/components/buttons/demos/FabAccent.js' 7 | import FabDisabled from '../src/components/buttons/demos/FabDisabled.js' 8 | import FabMini from '../src/components/buttons/demos/FabMini.js' 9 | import FabPrimary from '../src/components/buttons/demos/FabPrimary.js' 10 | import Flat from '../src/components/buttons/demos/Flat.js' 11 | import FlatAccent from '../src/components/buttons/demos/FlatAccent.js' 12 | import FlatDisabled from '../src/components/buttons/demos/FlatDisabled.js' 13 | import FlatPrimary from '../src/components/buttons/demos/FlatPrimary.js' 14 | import Icon from '../src/components/buttons/demos/Icon.js' 15 | import IconAccent from '../src/components/buttons/demos/IconAccent.js' 16 | import IconDisabled from '../src/components/buttons/demos/IconDisabled.js' 17 | import IconMini from '../src/components/buttons/demos/IconMini.js' 18 | import IconPrimary from '../src/components/buttons/demos/IconPrimary.js' 19 | import Raised from '../src/components/buttons/demos/Raised.js' 20 | import RaisedAccent from '../src/components/buttons/demos/RaisedAccent.js' 21 | import RaisedDisabled from '../src/components/buttons/demos/RaisedDisabled.js' 22 | import RaisedPrimary from '../src/components/buttons/demos/RaisedPrimary.js' 23 | 24 | storiesOf('Buttons', module) 25 | .addDecorator(wrapStory) 26 | .add('Fab', () => ) 27 | .add('Fab Accent', () => ) 28 | .add('Fab Disabled', () => ) 29 | .add('Fab Mini', () => ) 30 | .add('Fab Primary', () => ) 31 | .add('Flat', () => ) 32 | .add('Flat Accent', () => ) 33 | .add('Flat Disabled', () => ) 34 | .add('Flat Primary', () => ) 35 | .add('Icon', () => ) 36 | .add('Icon Accent', () => ) 37 | .add('Icon Disabled', () => ) 38 | .add('Icon Mini', () => ) 39 | .add('Icon Primary', () => ) 40 | .add('Raised', () => ) 41 | .add('Raised Accent', () => ) 42 | .add('Raised Disabled', () => ) 43 | .add('Raised Primary', () => ) 44 | -------------------------------------------------------------------------------- /stories/cards.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { storiesOf } from '@storybook/react' 3 | import wrapStory from './decorators/wrapStory' 4 | 5 | import Event from '../src/components/cards/demos/Event.js' 6 | import Image from '../src/components/cards/demos/Image.js' 7 | import Square from '../src/components/cards/demos/Square.js' 8 | import Wide from '../src/components/cards/demos/Wide.js' 9 | 10 | storiesOf('Cards', module) 11 | .addDecorator(wrapStory) 12 | .add('Event', () => ) 13 | .add('Image', () => ) 14 | .add('Square', () => ) 15 | .add('Wide', () => ) 16 | -------------------------------------------------------------------------------- /stories/checkbox.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { storiesOf } from '@storybook/react' 3 | import wrapStory from './decorators/wrapStory' 4 | 5 | import Checkbox from '../src/components/checkbox/demos/Checkbox.js' 6 | 7 | storiesOf('Checkbox', module) 8 | .addDecorator(wrapStory) 9 | .add('Checkbox', () => ) 10 | -------------------------------------------------------------------------------- /stories/chips.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { storiesOf } from '@storybook/react' 3 | import wrapStory from './decorators/wrapStory' 4 | 5 | import Basic from '../src/components/chips/demos/Basic.js' 6 | import Button from '../src/components/chips/demos/Button.js' 7 | import Contact from '../src/components/chips/demos/Contact.js' 8 | import Deletable from '../src/components/chips/demos/Deletable.js' 9 | import DeletableContact from '../src/components/chips/demos/DeletableContact.js' 10 | 11 | storiesOf('Chips', module) 12 | .addDecorator(wrapStory) 13 | .add('Basic', () => ) 14 | .add('Button', () =>