├── .babelrc ├── .editorconfig ├── .eslintrc ├── .flowconfig ├── .gitignore ├── .storybook ├── .babelrc ├── addons.js ├── config.js ├── preview-head.html └── stories │ ├── Navbar.js │ ├── Pagination.js │ └── Progress.js ├── .stylelintrc ├── LICENSE ├── README.md ├── components └── Navbar │ ├── Navbar.js │ ├── NavbarToggler.js │ └── index.js ├── docs-examples ├── doc2.md ├── doc3.md ├── exampledoc4.md ├── exampledoc5.md └── installation.md ├── docs ├── CNAME ├── css │ └── main.css ├── docs │ ├── doc2.html │ ├── doc3.html │ ├── doc4.html │ ├── doc5.html │ └── installation.html ├── en │ ├── help.html │ ├── index.html │ └── users.html ├── help.html ├── img │ ├── docusaurus.svg │ ├── favicon.png │ ├── favicon │ │ └── favicon.ico │ ├── language.svg │ └── styled-logo.png ├── index.html ├── storybook │ ├── favicon.ico │ ├── iframe.html │ ├── index.html │ └── static │ │ ├── manager.8248110a2e5e2824379b.bundle.js │ │ └── preview.32ac8310be26e188fd6b.bundle.js └── users.html ├── package-lock.json ├── package.json ├── rollup.config.js ├── src ├── Alert │ ├── Alert.js │ ├── DismissableAlert.js │ ├── default-theme.js │ ├── index.js │ ├── stories │ │ ├── Alert.story.js │ │ ├── Custom.story.js │ │ └── DismissableAlert.story.js │ └── utils │ │ └── alert-variant.js ├── Badge │ ├── Badge.js │ ├── default-theme.js │ ├── index.js │ ├── stories │ │ └── Badge.story.js │ └── utils │ │ └── badge-variant.js ├── Breadcrumbs │ ├── default-theme.js │ ├── index.js │ └── stories │ │ └── Breadcrumb.story.js ├── Button │ ├── Button.js │ ├── __tests__ │ │ ├── Button.test.js │ │ └── __snapshots__ │ │ │ └── Button.test.js.snap │ ├── defaultTheme.js │ ├── index.js │ ├── stories │ │ └── Button.story.js │ └── utils │ │ ├── button-outline-variant.js │ │ ├── button-size.js │ │ └── button-variant.js ├── Card │ ├── Card.js │ ├── CardBody.js │ ├── CardFooter.js │ ├── CardHeader.js │ ├── CardImage.js │ ├── default-theme.js │ ├── index.js │ └── stories │ │ └── Card.story.js ├── CloseIcon │ ├── default-theme.js │ ├── index.js │ └── stories │ │ └── CloseIcon.story.js ├── Collapse │ ├── default-theme.js │ ├── index.js │ └── stories │ │ └── Collapse.story.js ├── Dropdown │ ├── Dropdown.js │ ├── DropdownDivider.js │ ├── DropdownHeader.js │ ├── DropdownItem.js │ ├── DropdownMenu.js │ ├── default-theme.js │ ├── index.js │ └── stories │ │ └── Dropdown.story.js ├── Forms │ ├── CustomSelect.js │ ├── FormGroup.js │ ├── Input.js │ ├── Select.js │ ├── Textarea.js │ ├── default-theme.js │ ├── index.js │ └── stories │ │ ├── CustomSelect.story.js │ │ ├── Full-form.story.js │ │ ├── Input.story.js │ │ ├── Select.story.js │ │ └── Textarea.story.js ├── Heading │ ├── default-theme.js │ ├── index.js │ └── stories │ │ └── Heading.story.js ├── Jumbotron │ ├── default-theme.js │ ├── index.js │ └── stories │ │ └── Jumbotron.story.js ├── ListGroup │ ├── ListGroup.js │ ├── ListGroupButton.js │ ├── ListGroupItem.js │ ├── ListGroupLink.js │ ├── default-theme.js │ ├── index.js │ ├── stories │ │ └── ListGroup.story.js │ └── utils │ │ └── list-group-item-variant.js ├── Pagination │ ├── Pagination.js │ ├── PaginationItem.js │ ├── PaginationLink.js │ ├── default-theme.js │ ├── index.js │ ├── pagination-size.js │ └── stories │ │ └── Pagination.story.js ├── Popovers │ ├── StyledPopover.js │ ├── default-theme.js │ ├── index.js │ ├── placementFunctions.js │ └── stories │ │ ├── Custom.story.js │ │ └── Popover.story.js ├── Progress │ ├── Progress.js │ ├── ProgressBar.js │ ├── default-theme.js │ ├── index.js │ └── stories │ │ └── Progress.story.js ├── Tooltip │ ├── StyledTooltip.js │ ├── arrowPlacement.js │ ├── default-theme.js │ ├── index.js │ └── stories │ │ └── Tooltip.story.js ├── index.js └── utils │ ├── border-radius.js │ ├── box-shadow.js │ ├── color-functions.js │ ├── config │ ├── colors.js │ ├── components.js │ ├── fonts.js │ ├── options.js │ └── spacing.js │ ├── defaultTheme.js │ ├── forms.js │ ├── gradients.js │ ├── hover.js │ ├── index.js │ ├── lists.js │ ├── nav-divider.js │ ├── sassRgba.js │ ├── static-bootstrap-variables.json │ ├── theme.js │ └── transition.js ├── styled-logo-small.png ├── styled-logo.png └── website ├── .gitignore ├── core └── Footer.js ├── i18n └── en.json ├── package-lock.json ├── package.json ├── pages └── en │ ├── help.js │ ├── index.js │ └── users.js ├── sidebars.json ├── siteConfig.js ├── static ├── css │ └── custom.css └── img │ ├── docusaurus.svg │ ├── favicon.png │ ├── favicon │ └── favicon.ico │ └── styled-logo.png └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["@babel/preset-env", { 4 | "modules": false 5 | }], 6 | "@babel/preset-react", 7 | "@babel/preset-flow" 8 | ], 9 | "plugins": ["@babel/plugin-proposal-class-properties"] 10 | } 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [{package.json,*.yml}] 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": [ 4 | "airbnb", 5 | "prettier", 6 | "prettier/flowtype", 7 | "prettier/react" 8 | ], 9 | "plugins": [ 10 | "prettier" 11 | ], 12 | "env": { 13 | "browser": true, 14 | "node": true 15 | }, 16 | "rules": { 17 | "react/jsx-filename-extension": [1, { 18 | "extensions": [".js", ".jsx"] 19 | }], 20 | "prettier/prettier": ["error", { 21 | "singleQuote": true 22 | }], 23 | "import/no-extraneous-dependencies": ["error", { 24 | "devDependencies": true, 25 | "optionalDependencies": false, 26 | "peerDependencies": false 27 | }] 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | /node_modules/styled-components/* 3 | /node_modules/polished/* 4 | /node_modules/* 5 | /lib/* 6 | 7 | [include] 8 | 9 | [libs] 10 | 11 | [lints] 12 | all=warn 13 | 14 | [options] 15 | include_warnings=true 16 | suppress_comment= \\(.\\|\n\\)*\\$FlowIssue 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /lib 11 | /dist 12 | 13 | # flow definitions 14 | /flow-typed 15 | 16 | # misc 17 | .DS_Store 18 | .env.local 19 | .env.development.local 20 | .env.test.local 21 | .env.production.local 22 | stats.html 23 | 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | *.log 28 | package-lock.json 29 | -------------------------------------------------------------------------------- /.storybook/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env", 4 | "@babel/preset-react", 5 | "@babel/preset-flow" 6 | ], 7 | "plugins": ["@babel/plugin-proposal-class-properties"] 8 | } 9 | -------------------------------------------------------------------------------- /.storybook/addons.js: -------------------------------------------------------------------------------- 1 | import '@storybook/addon-knobs/register'; 2 | import '@storybook/addon-actions/register'; 3 | import '@storybook/addon-options/register'; 4 | import '@storybook/addon-links/register'; 5 | -------------------------------------------------------------------------------- /.storybook/config.js: -------------------------------------------------------------------------------- 1 | import { configure, addDecorator } from '@storybook/react'; 2 | import { setOptions } from '@storybook/addon-options'; 3 | 4 | import React from 'react'; 5 | import { ThemeProvider } from 'styled-components'; 6 | // import defaultTheme from '../src/utils/default-theme'; 7 | 8 | // addDecorator(story => ( 9 | // {story()} 10 | // )); 11 | 12 | setOptions({ 13 | name: 'styled-bootstrap', 14 | url: 'https://github.com/xDae/styled-bootstrap' 15 | }); 16 | 17 | const req = require.context('../src/', true, /.story.js$/); 18 | 19 | function loadStories() { 20 | req.keys().forEach((filename) => req(filename)) 21 | } 22 | 23 | configure(loadStories, module); 24 | -------------------------------------------------------------------------------- /.storybook/preview-head.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.storybook/stories/Navbar.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | import styled from 'styled-components'; 4 | 5 | import Navbar, { NavbarToggler } from '../../src/components/Navbar'; 6 | 7 | export default storiesOf('Navbar', module) 8 | .add('Navbar', () => 9 | 10 | Navbar 11 | 12 | {/**/} 13 | 14 | 27 | 28 | ) 29 | .add('Navbar Dark', () => 30 | 31 | {/**/} 32 | 33 | Navbar 34 | 35 | 48 | 49 | ) 50 | .add('NavbarToggler', () => ( 51 |
52 | 53 | 54 |
55 | 56 |
57 |
58 | )); 59 | -------------------------------------------------------------------------------- /.storybook/stories/Pagination.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | import styled from 'styled-components'; 4 | 5 | import Pagination from '../../src/components/Pagination'; 6 | 7 | 8 | export default storiesOf('Pagination', module) 9 | .add('Pagination', () => 10 |
11 | 12 | Previous 13 | 1 14 | 2 15 | 3 16 | Next 17 | 18 |
19 | ) 20 | .add('Pagination large', () => 21 |
22 | 23 | Previous 24 | 1 25 | 2 26 | 3 27 | Next 28 | 29 |
30 | ) 31 | .add('Pagination small', () => 32 |
33 | 34 | Previous 35 | 1 36 | 2 37 | 3 38 | Next 39 | 40 |
41 | ); 42 | -------------------------------------------------------------------------------- /.storybook/stories/Progress.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | 4 | import styled from 'styled-components'; 5 | import { green } from '../../src/defaultTheme'; 6 | 7 | import Progress from '../../src/components/Progress'; 8 | 9 | const GreenProgressBar = styled(Progress.Bar)` 10 | background-color: ${green}; 11 | `; 12 | 13 | export default storiesOf('Progress', module) 14 | .add('Progress', () => ( 15 |
16 | 17 | 18 | 19 |
20 | )) 21 | .add('Progress with label', () => ( 22 |
23 | 24 | 60% 25 | 26 |
27 | )) 28 | .add('Striped', () => ( 29 |
30 | 31 | 32 | 33 |
34 | )) 35 | .add('Animated', () => ( 36 |
37 | 38 | 39 | 40 |
41 | )) 42 | .add('Multiple bars ', () => ( 43 |
44 | 45 | 46 | 47 | 48 |
49 | )); 50 | -------------------------------------------------------------------------------- /.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "processors": ["stylelint-processor-styled-components"], 3 | "extends": "stylelint-config-standard", 4 | "syntax": "scss" 5 | } 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Jose Miguel Bejarano (josemiguel.org) 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | styled-components 3 |
4 | 5 | # styled-bootstrap 6 | > Bootstrap 4 Components for react powered by styled-components 7 | 8 | [![npm](https://img.shields.io/npm/dt/styled-bootstrap.svg)](https://www.npmjs.com/package/styled-bootstrap) 9 | [![npm](https://img.shields.io/npm/v/styled-bootstrap.svg)](https://www.npmjs.com/package/styled-bootstrap) 10 | [![David](https://img.shields.io/david/xDae/styled-bootstrap.svg)](https://david-dm.org/xDae/styled-bootstrap) 11 | [![Travis](https://img.shields.io/travis/xDae/styled-bootstrap.svg)](https://travis-ci.org/xDae/styled-bootstrap) 12 | [![gitter](https://badges.gitter.im/rollup/rollup.svg)](https://gitter.im/styled-bootstrap) 13 | [![license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/xDae/styled-bootstrap/blob/master/LICENSE) 14 | 15 | 16 | ## 🚀 Installation 17 | 18 | ``` 19 | npm install styled-bootstrap --save 20 | ``` 21 | 22 | ## ⚙️ Usage 23 | 24 | ⚠️ Work in progress. 25 | 26 | ## 📦 Components 27 | 💻 = Demo 28 | 29 | - [x] [[💻](https://xdae.github.io/styled-bootstrap/storybook?selectedKind=Alert)] Alert 30 | - [x] [[💻](https://xdae.github.io/styled-bootstrap/storybook?selectedKind=Badge)] Barge 31 | - [x] [[💻](https://xdae.github.io/styled-bootstrap/storybook?selectedKind=Breadcrumb)] Breadcrumbs 32 | - [x] [[💻](https://xdae.github.io/styled-bootstrap/storybook?selectedKind=Buttons)] Button 33 | - [x] [[💻](https://xdae.github.io/styled-bootstrap/storybook?selectedKind=Card)] Card 34 | - [x] [[💻](https://xdae.github.io/styled-bootstrap/storybook?selectedKind=Collapse)] Collapse 35 | - [x] [[💻](https://xdae.github.io/styled-bootstrap/storybook?selectedKind=Dropdown)] Dropdown 36 | - [x] [[💻](https://xdae.github.io/styled-bootstrap/storybook?selectedKind=Forms)] Forms 37 | - [x] [[💻](https://xdae.github.io/styled-bootstrap/storybook?selectedKind=Headings)] Heading 38 | - [ ] Input group 39 | - [ ] Jumbotron 40 | - [x] [[💻](https://xdae.github.io/styled-bootstrap/storybook?selectedKind=ListGroup)] List group 41 | - [ ] Modal 42 | - [ ] Navs 43 | - [ ] Navbar 44 | - [x] [[💻](https://xdae.github.io/styled-bootstrap/storybook?selectedKind=Pagination)] Pagination 45 | - [x] Popovers 46 | - [x] [[💻](https://xdae.github.io/styled-bootstrap/storybook?selectedKind=Progress)] Progress 47 | - [ ] Scrollspy 48 | - [x] Tooltips 49 | - Utilities 50 | - [x] [[💻](https://xdae.github.io/styled-bootstrap/storybook?selectedKind=CLoseIcon)] Close icon 51 | 52 | ## 📖 Support 53 | 54 | Please [open an issue](https://github.com/xDae/styled-bootstrap/issues/new) for support. 55 | 56 | ## 🛎 Contributing 57 | 58 | Please contribute using [Github Flow](https://guides.github.com/introduction/flow/). Create a branch, add commits, and [open a pull request](https://github.com/xDae/styled-bootstrap/compare). 59 | 60 | ## Backers 61 | 62 | Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/parcel#backer)] 63 | 64 | 65 | 66 | ## Sponsors 67 | 68 | Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/styled-bootstrap#sponsor)] 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /components/Navbar/Navbar.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import styled, { css } from 'styled-components'; 4 | import PropTypes from 'prop-types'; 5 | 6 | import { hoverFocus } from '../../utils/hover'; 7 | 8 | import { 9 | navbarPaddingY, 10 | navbarPaddingX, 11 | navbarBrandPaddingY, 12 | navbarBrandFontSize, 13 | navbarLightColor, 14 | navbarLightDisabledColor, 15 | navbarLightHoverColor, 16 | navbarLightActiveColor, 17 | navbarDarkActiveColor, 18 | navbarDarkColor, 19 | navbarDarkHoverColor 20 | } from '../../defaultTheme'; 21 | 22 | const Navbar = styled.nav` 23 | position: relative; 24 | display: flex; 25 | flex-wrap: wrap; 26 | align-items: center; 27 | // justify-content: space-between; 28 | ${props => 29 | `padding: ${props.theme.navbarPaddingY} ${props.theme.navbarPaddingX};`}; 30 | 31 | > .container, 32 | > .container-fluid { 33 | display: flex; 34 | flex-wrap: wrap; 35 | align-items: center; 36 | justify-content: space-between; 37 | 38 | // @include media-breakpoint-down(nth(map-keys($grid-breakpoints), 1)) { 39 | // width: 100%; 40 | // margin-right: 0; 41 | // margin-left: 0; 42 | // } 43 | } 44 | `; 45 | 46 | Navbar.defaultProps = { 47 | theme: { 48 | navbarPaddingY, 49 | navbarPaddingX 50 | } 51 | }; 52 | 53 | Navbar.propTypes = { 54 | theme: PropTypes.object 55 | }; 56 | 57 | Navbar.Brand = styled.a` 58 | display: inline-block; 59 | padding-top: ${props => props.theme.navbarBrandPaddingY}; 60 | padding-bottom: ${props => props.theme.navbarBrandPaddingY}; 61 | margin-right: ${props => props.theme.navbarPaddingX}; 62 | font-size: ${props => props.theme.navbarBrandFontSize}; 63 | line-height: inherit; 64 | white-space: nowrap; 65 | 66 | ${hoverFocus(css` 67 | text-decoration: none; 68 | `)}; 69 | 70 | ${props => { 71 | const colors = { 72 | light: props.theme.navbarLightActiveColor, 73 | dark: props.theme.navbarDarkActiveColor 74 | }; 75 | 76 | return css` 77 | color: ${colors[props.color]}; 78 | 79 | ${hoverFocus(css` 80 | color: ${colors[props.color]} 81 | `)}; 82 | `; 83 | }}; 84 | `; 85 | 86 | Navbar.Brand.defaultProps = { 87 | theme: { 88 | navbarBrandPaddingY, 89 | navbarPaddingX, 90 | navbarBrandFontSize, 91 | navbarLightActiveColor, 92 | navbarDarkActiveColor 93 | }, 94 | color: 'light' 95 | }; 96 | 97 | Navbar.Nav = styled.ul` 98 | display: flex; 99 | // flex-direction: column; 100 | padding-left: 0; 101 | margin-bottom: 0; 102 | list-style: none; 103 | 104 | // .dropdown-menu { 105 | // position: static; 106 | // float: none; 107 | // } 108 | `; 109 | 110 | Navbar.NavItem = styled.li``; 111 | 112 | Navbar.NavLink = styled.a` 113 | padding-right: .5rem; 114 | padding-left: .5rem; 115 | // color: ${props => props.theme.navbarLightColor}; 116 | 117 | ${props => { 118 | const colors = { 119 | light: { 120 | color: props.theme.navbarLightActiveColor, 121 | hover: props.theme.navbarLightHoverColor 122 | }, 123 | dark: { 124 | color: props.theme.navbarDarkColor, 125 | hover: props.theme.navbarDarkHoverColor 126 | } 127 | }; 128 | 129 | return css` 130 | color: ${colors[props.color].color}; 131 | 132 | ${hoverFocus(css` 133 | color: ${colors[props.color].hover}; 134 | text-decoration: none; 135 | `)}; 136 | `; 137 | }}; 138 | 139 | &.disabled { 140 | color: ${props => props.theme.navbarLightDisabledColor}; 141 | } 142 | `; 143 | 144 | Navbar.NavLink.defaultProps = { 145 | theme: { 146 | navbarLightColor, 147 | navbarLightDisabledColor, 148 | navbarLightHoverColor, 149 | navbarLightActiveColor, 150 | navbarDarkColor, 151 | navbarDarkHoverColor 152 | }, 153 | color: 'light' 154 | }; 155 | 156 | export default Navbar; 157 | -------------------------------------------------------------------------------- /components/Navbar/NavbarToggler.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react'; 4 | 5 | import styled, { css } from 'styled-components'; 6 | import PropTypes from 'prop-types'; 7 | 8 | import { borderRadius } from '../../utils/border-radius'; 9 | 10 | import { 11 | navbarTogglerPaddingY, 12 | navbarTogglerPaddingX, 13 | navbarTogglerFontSize, 14 | navbarTogglerBorderRadius, 15 | navbarLightTogglerIconBg, 16 | navbarDarkTogglerIconBg, 17 | navbarLightColor, 18 | navbarDarkColor, 19 | navbarLightTogglerBorderColor, 20 | navbarDarkTogglerBorderColor, 21 | borderWidth 22 | } from '../../defaultTheme'; 23 | 24 | export const StyledNavbarToggler = styled.button` 25 | ${props => 26 | `padding: ${props.theme.navbarTogglerPaddingY} ${props.theme 27 | .navbarTogglerPaddingX};`}; 28 | font-size: ${props => props.theme.navbarTogglerFontSize}; 29 | line-height: 1; 30 | background: transparent; // remove default button style 31 | border: ${props => props.theme.borderWidth} solid transparent; // remove default button style 32 | ${props => borderRadius(props.theme.navbarTogglerBorderRadius)}; 33 | 34 | ${props => { 35 | const colors = { 36 | light: { 37 | color: props.theme.navbarLightColor, 38 | borderColor: props.theme.navbarLightTogglerBorderColor 39 | }, 40 | dark: { 41 | color: props.theme.navbarDarkColor, 42 | borderColor: props.theme.navbarDarkTogglerBorderColor 43 | } 44 | }; 45 | 46 | return css` 47 | color: ${colors[props.color].color}; 48 | border-color: ${colors[props.color].borderColor}; 49 | `; 50 | }}; 51 | 52 | // @include hover-focus { 53 | // text-decoration: none; 54 | // } 55 | `; 56 | 57 | StyledNavbarToggler.defaultProps = { 58 | theme: { 59 | navbarTogglerPaddingY, 60 | navbarTogglerPaddingX, 61 | navbarTogglerFontSize, 62 | navbarTogglerBorderRadius, 63 | navbarLightColor, 64 | navbarDarkColor, 65 | navbarLightTogglerBorderColor, 66 | navbarDarkTogglerBorderColor, 67 | borderWidth 68 | }, 69 | color: 'light' 70 | }; 71 | 72 | export const NavbarTogglerIcon = styled.span` 73 | display: inline-block; 74 | width: 1.5em; 75 | height: 1.5em; 76 | vertical-align: middle; 77 | content: ""; 78 | background: no-repeat center center; 79 | background-size: 100% 100%; 80 | 81 | ${props => { 82 | const colors = { 83 | light: { 84 | backgroundImage: props.theme.navbarLightTogglerIconBg 85 | }, 86 | dark: { 87 | backgroundImage: props.theme.navbarDarkTogglerIconBg 88 | } 89 | }; 90 | 91 | return css`background-image: ${colors[props.color].backgroundImage};`; 92 | }}; 93 | `; 94 | 95 | NavbarTogglerIcon.defaultProps = { 96 | theme: { 97 | navbarLightTogglerIconBg, 98 | navbarDarkTogglerIconBg 99 | }, 100 | color: 'light' 101 | }; 102 | 103 | const NavbarToggler = props => 104 | 105 | {props.icon ? props.icon : } 106 | ; 107 | 108 | export default NavbarToggler; 109 | -------------------------------------------------------------------------------- /components/Navbar/index.js: -------------------------------------------------------------------------------- 1 | import Navbar from './Navbar'; 2 | import NavbarToggler from './NavbarToggler'; 3 | 4 | export { NavbarToggler }; 5 | export default Navbar; 6 | -------------------------------------------------------------------------------- /docs-examples/doc2.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: doc2 3 | title: document number 2 4 | --- 5 | 6 | This is a link to [another document.](/docs/en/doc3.md) 7 | This is a link to an [external page.](http://www.example.com) 8 | -------------------------------------------------------------------------------- /docs-examples/doc3.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: doc3 3 | title: This is document number 3 4 | --- 5 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. In ac euismod odio, eu consequat dui. Nullam molestie consectetur risus id imperdiet. Proin sodales ornare turpis, non mollis massa ultricies id. Nam at nibh scelerisque, feugiat ante non, dapibus tortor. Vivamus volutpat diam quis tellus elementum bibendum. Praesent semper gravida velit quis aliquam. Etiam in cursus neque. Nam lectus ligula, malesuada et mauris a, bibendum faucibus mi. Phasellus ut interdum felis. Phasellus in odio pulvinar, porttitor urna eget, fringilla lectus. Aliquam sollicitudin est eros. Mauris consectetur quam vitae mauris interdum hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. 6 | 7 | Duis et egestas libero, imperdiet faucibus ipsum. Sed posuere eget urna vel feugiat. Vivamus a arcu sagittis, fermentum urna dapibus, congue lectus. Fusce vulputate porttitor nisl, ac cursus elit volutpat vitae. Nullam vitae ipsum egestas, convallis quam non, porta nibh. Morbi gravida erat nec neque bibendum, eu pellentesque velit posuere. Fusce aliquam erat eu massa eleifend tristique. 8 | 9 | Sed consequat sollicitudin ipsum eget tempus. Integer a aliquet velit. In justo nibh, pellentesque non suscipit eget, gravida vel lacus. Donec odio ante, malesuada in massa quis, pharetra tristique ligula. Donec eros est, tristique eget finibus quis, semper non nisl. Vivamus et elit nec enim ornare placerat. Sed posuere odio a elit cursus sagittis. 10 | 11 | Phasellus feugiat purus eu tortor ultrices finibus. Ut libero nibh, lobortis et libero nec, dapibus posuere eros. Sed sagittis euismod justo at consectetur. Nulla finibus libero placerat, cursus sapien at, eleifend ligula. Vivamus elit nisl, hendrerit ac nibh eu, ultrices tempus dui. Nam tellus neque, commodo non rhoncus eu, gravida in risus. Nullam id iaculis tortor. 12 | 13 | Nullam at odio in sem varius tempor sit amet vel lorem. Etiam eu hendrerit nisl. Fusce nibh mauris, vulputate sit amet ex vitae, congue rhoncus nisl. Sed eget tellus purus. Nullam tempus commodo erat ut tristique. Cras accumsan massa sit amet justo consequat eleifend. Integer scelerisque vitae tellus id consectetur. 14 | -------------------------------------------------------------------------------- /docs-examples/exampledoc4.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: doc4 3 | title: Other Document 4 | --- 5 | 6 | this is another document 7 | -------------------------------------------------------------------------------- /docs-examples/exampledoc5.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: doc5 3 | title: Fifth Document 4 | --- 5 | 6 | Another one 7 | -------------------------------------------------------------------------------- /docs-examples/installation.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: installation 3 | title: Installation 4 | # sidebar_label: Example Page 5 | --- 6 | 7 | Check the [documentation](https://docusaurus.io) for how to use Docusaurus. 8 | 9 | ## Lorem 10 | 11 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus elementum massa eget nulla aliquet sagittis. Proin odio tortor, vulputate ut odio in, ultrices ultricies augue. Cras ornare ultrices lorem malesuada iaculis. Etiam sit amet libero tempor, pulvinar mauris sed, sollicitudin sapien. 12 | 13 | ## Mauris In Code 14 | Install with npm 15 | ``` 16 | npm install styled-bootstrap --save 17 | ``` 18 | 19 | Or using Yarn 20 | ``` 21 | yarn add styled-bootstrap 22 | ``` 23 | 24 | ## Nulla 25 | 26 | Nulla facilisi. Maecenas sodales nec purus eget posuere. Sed sapien quam, pretium a risus in, porttitor dapibus erat. Sed sit amet fringilla ipsum, eget iaculis augue. Integer sollicitudin tortor quis ultricies aliquam. Suspendisse fringilla nunc in tellus cursus, at placerat tellus scelerisque. Sed tempus elit a sollicitudin rhoncus. Nulla facilisi. Morbi nec dolor dolor. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Cras et aliquet lectus. Pellentesque sit amet eros nisi. Quisque ac sapien in sapien congue accumsan. Nullam in posuere ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Proin lacinia leo a nibh fringilla pharetra. 27 | 28 | ## Orci 29 | 30 | Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin venenatis lectus dui, vel ultrices ante bibendum hendrerit. Aenean egestas feugiat dui id hendrerit. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur in tellus laoreet, eleifend nunc id, viverra leo. Proin vulputate non dolor vel vulputate. Curabitur pretium lobortis felis, sit amet finibus lorem suscipit ut. Sed non mollis risus. Duis sagittis, mi in euismod tincidunt, nunc mauris vestibulum urna, at euismod est elit quis erat. Phasellus accumsan vitae neque eu placerat. In elementum arcu nec tellus imperdiet, eget maximus nulla sodales. Curabitur eu sapien eget nisl sodales fermentum. 31 | 32 | ## Phasellus 33 | 34 | Phasellus pulvinar ex id commodo imperdiet. Praesent odio nibh, sollicitudin sit amet faucibus id, placerat at metus. Donec vitae eros vitae tortor hendrerit finibus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Quisque vitae purus dolor. Duis suscipit ac nulla et finibus. Phasellus ac sem sed dui dictum gravida. Phasellus eleifend vestibulum facilisis. Integer pharetra nec enim vitae mattis. Duis auctor, lectus quis condimentum bibendum, nunc dolor aliquam massa, id bibendum orci velit quis magna. Ut volutpat nulla nunc, sed interdum magna condimentum non. Sed urna metus, scelerisque vitae consectetur a, feugiat quis magna. Donec dignissim ornare nisl, eget tempor risus malesuada quis. 35 | -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | styled-bootstrap.js.org -------------------------------------------------------------------------------- /docs/docs/doc2.html: -------------------------------------------------------------------------------- 1 | document number 2 · styled-bootstrap -------------------------------------------------------------------------------- /docs/docs/doc4.html: -------------------------------------------------------------------------------- 1 | Other Document · styled-bootstrap -------------------------------------------------------------------------------- /docs/docs/doc5.html: -------------------------------------------------------------------------------- 1 | Fifth Document · styled-bootstrap -------------------------------------------------------------------------------- /docs/en/help.html: -------------------------------------------------------------------------------- 1 | styled-bootstrap · A styled-component implementation of Bootstrap -------------------------------------------------------------------------------- /docs/en/index.html: -------------------------------------------------------------------------------- 1 | styled-bootstrap · A styled-component implementation of Bootstrap -------------------------------------------------------------------------------- /docs/en/users.html: -------------------------------------------------------------------------------- 1 | styled-bootstrap · A styled-component implementation of Bootstrap -------------------------------------------------------------------------------- /docs/help.html: -------------------------------------------------------------------------------- 1 | styled-bootstrap · A styled-component implementation of Bootstrap -------------------------------------------------------------------------------- /docs/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xDae/styled-bootstrap/03f8e65a12f5663c4f519d0e7e377bf688b4df5f/docs/img/favicon.png -------------------------------------------------------------------------------- /docs/img/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xDae/styled-bootstrap/03f8e65a12f5663c4f519d0e7e377bf688b4df5f/docs/img/favicon/favicon.ico -------------------------------------------------------------------------------- /docs/img/language.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/img/styled-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xDae/styled-bootstrap/03f8e65a12f5663c4f519d0e7e377bf688b4df5f/docs/img/styled-logo.png -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | styled-bootstrap · A styled-component implementation of Bootstrap -------------------------------------------------------------------------------- /docs/storybook/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xDae/styled-bootstrap/03f8e65a12f5663c4f519d0e7e377bf688b4df5f/docs/storybook/favicon.ico -------------------------------------------------------------------------------- /docs/storybook/iframe.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 13 | Storybook 14 | 15 | 16 | 17 | 18 |
19 |
20 | 21 | 22 | -------------------------------------------------------------------------------- /docs/storybook/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Storybook 9 | 38 | 39 | 40 | 41 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/users.html: -------------------------------------------------------------------------------- 1 | styled-bootstrap · A styled-component implementation of Bootstrap -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from 'rollup-plugin-node-resolve'; 2 | import commonjs from 'rollup-plugin-commonjs'; 3 | import babel from 'rollup-plugin-babel'; 4 | import flow from 'rollup-plugin-flow'; 5 | import filesize from 'rollup-plugin-filesize'; 6 | import replace from 'rollup-plugin-replace'; 7 | import cleanup from 'rollup-plugin-cleanup'; 8 | import visualizer from 'rollup-plugin-visualizer'; 9 | 10 | import pkg from './package.json'; 11 | 12 | export default [ 13 | { 14 | input: 'src/index.js', 15 | external: ['react', 'styled-components'], 16 | output: [ 17 | { file: pkg.main, format: 'cjs' }, 18 | { file: pkg.module, format: 'es' } 19 | ], 20 | plugins: [ 21 | replace({ 22 | 'process.env.NODE_ENV': JSON.stringify('production') 23 | }), 24 | flow(), 25 | babel({ 26 | exclude: 'node_modules/**' 27 | }), 28 | resolve({ 29 | extensions: ['.js', '.jsx'], 30 | jsnext: true, 31 | main: true 32 | }), 33 | commonjs({ 34 | namedExports: { 35 | react: ['Component', 'createElement'] 36 | } 37 | }), 38 | filesize(), 39 | cleanup(), 40 | visualizer() 41 | ] 42 | } 43 | ]; 44 | -------------------------------------------------------------------------------- /src/Alert/Alert.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import PropTypes from 'prop-types'; 4 | import styled from 'styled-components'; 5 | // $FlowIssue 6 | import get from 'lodash.get'; 7 | import themeProp from '../utils/theme'; 8 | 9 | import { borderRadius } from '../utils/border-radius'; 10 | import { themeColorLevel } from '../utils/color-functions'; 11 | 12 | import { alertVariant } from './utils/alert-variant'; 13 | 14 | import * as defaultTheme from './default-theme'; 15 | 16 | const { 17 | alertPaddingY, 18 | alertPaddingX, 19 | alertMarginBottom, 20 | alertBorderWidth, 21 | alertLinkFontWeight, 22 | alertBorderRadius, 23 | alertBgLevel, 24 | alertBorderLevel, 25 | alertColorLevel 26 | } = defaultTheme; 27 | 28 | const Alert = styled.div` 29 | position: relative; 30 | padding: ${themeProp('alertPaddingY', alertPaddingY)} 31 | ${themeProp('alertPaddingX,', alertPaddingX)}; 32 | 33 | margin-bottom: ${themeProp('alertMarginBottom', alertMarginBottom)}; 34 | 35 | border: ${themeProp('alertBorderWidth', alertBorderWidth)} solid transparent; 36 | 37 | a { 38 | font-weight: ${themeProp('alertLinkFontWeight', alertLinkFontWeight)}; 39 | } 40 | 41 | ${borderRadius(themeProp('alertBorderRadius', alertBorderRadius))}; 42 | 43 | ${({ type, theme }) => 44 | alertVariant( 45 | themeColorLevel( 46 | defaultTheme[type], 47 | get(theme, 'alertBgLevel', alertBgLevel) 48 | ), 49 | themeColorLevel( 50 | defaultTheme[type], 51 | get(theme, 'alertBorderLevel', alertBorderLevel) 52 | ), 53 | themeColorLevel( 54 | defaultTheme[type], 55 | get(theme, 'alertColorLevel', alertColorLevel) 56 | ) 57 | )}}; 58 | `; 59 | 60 | Alert.defaultProps = { 61 | type: 'primary' 62 | }; 63 | 64 | Alert.propTypes = { 65 | type: PropTypes.oneOf([ 66 | 'primary', 67 | 'secondary', 68 | 'success', 69 | 'danger', 70 | 'warning', 71 | 'info', 72 | 'light', 73 | 'dark' 74 | ]) 75 | }; 76 | 77 | export default Alert; 78 | -------------------------------------------------------------------------------- /src/Alert/DismissableAlert.js: -------------------------------------------------------------------------------- 1 | import React, { cloneElement, isValidElement } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import omit from 'lodash.omit'; 4 | import styled, { css } from 'styled-components'; 5 | import themeProp from '../utils/theme'; 6 | import { hoverFocus } from '../utils/hover'; 7 | 8 | import Alert from './Alert'; 9 | import CloseIcon from '../CloseIcon'; 10 | 11 | import { alertPaddingY, alertPaddingX } from './default-theme'; 12 | 13 | const closeIconStyles = css` 14 | position: absolute; 15 | top: 0; 16 | right: 0; 17 | `; 18 | 19 | export const StyledCloseIcon = styled(CloseIcon)` 20 | padding: ${themeProp('alertPaddingY', alertPaddingY)} 21 | ${themeProp('alertPaddingX', alertPaddingX)}; 22 | color: ${props => !props.theme.closeColor && 'inherit'}; 23 | ${closeIconStyles}; 24 | `; 25 | 26 | const CloseString = styled.button` 27 | ${closeIconStyles}; 28 | padding: ${themeProp('alertPaddingY', alertPaddingY)} 29 | ${themeProp('alertPaddingX', alertPaddingX)}; 30 | cursor: pointer; 31 | background: transparent; 32 | border: 0; 33 | -webkit-appearance: none; 34 | opacity: 0.5; 35 | 36 | ${hoverFocus(` 37 | color: #000; 38 | text-decoration: none; 39 | opacity: .75; 40 | `)}; 41 | `; 42 | 43 | const DismissableAlert = props => { 44 | const filteredProps = omit(props, ['onClose', 'closeIcon']); 45 | return ( 46 | 47 | {props.children} 48 | 49 | {props.closeIcon && isValidElement(props.closeIcon) ? ( 50 | cloneElement(props.closeIcon, { onClick: props.onClose }) 51 | ) : ( 52 | {props.closeIcon} 53 | )} 54 | 55 | {!props.closeIcon && } 56 | 57 | ); 58 | }; 59 | 60 | DismissableAlert.propTypes = { 61 | closeIcon: PropTypes.oneOf([PropTypes.string, PropTypes.node]) 62 | }; 63 | 64 | DismissableAlert.defaultProps = { 65 | closeIcon: null 66 | }; 67 | 68 | export default DismissableAlert; 69 | -------------------------------------------------------------------------------- /src/Alert/default-theme.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export const primary = '#007bff'; 4 | export const secondary = '#6c757d'; 5 | export const success = '#28a745'; 6 | export const info = '#17a2b8'; 7 | export const warning = '#ffc107'; 8 | export const danger = '#dc3545'; 9 | export const light = '#f8f9fa'; 10 | export const dark = '#343a40'; 11 | 12 | export const alertPaddingY = '.75rem'; 13 | export const alertPaddingX = '1.25rem'; 14 | export const alertMarginBottom = '1rem'; 15 | export const alertBorderRadius = '0.25rem'; 16 | export const alertLinkFontWeight = 'bold'; 17 | export const alertBorderWidth = '1px'; 18 | 19 | export const alertBgLevel = -10; 20 | export const alertBorderLevel = -9; 21 | export const alertColorLevel = 6; 22 | -------------------------------------------------------------------------------- /src/Alert/index.js: -------------------------------------------------------------------------------- 1 | import DismissableAlert, { 2 | StyledCloseIcon as CloseIcon 3 | } from './DismissableAlert'; 4 | 5 | export { alertVariant } from './utils/alert-variant'; 6 | 7 | export { DismissableAlert, CloseIcon }; 8 | 9 | export { default } from './Alert'; 10 | -------------------------------------------------------------------------------- /src/Alert/stories/Alert.story.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | import { host } from 'storybook-host'; 4 | 5 | import Alert from '../index'; 6 | 7 | export default storiesOf('Alert', module) 8 | .addDecorator( 9 | host({ 10 | title: 11 | 'Provide contextual feedback messages for typical user actions with the handful of available and flexible alert messages.', 12 | align: 'center', 13 | width: 700 14 | }) 15 | ) 16 | .add('Default Alert', () => ( 17 | 18 | This is a primary alert—check it out! 19 | This is a secondary alert—check it out! 20 | This is a success alert—check it out! 21 | This is a danger alert—check it out! 22 | This is a warning alert—check it out! 23 | This is a info alert—check it out! 24 | This is a light alert—check it out! 25 | This is a dark alert—check it out! 26 | 27 | )); 28 | -------------------------------------------------------------------------------- /src/Alert/stories/Custom.story.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | import { host } from 'storybook-host'; 4 | import { action } from '@storybook/addon-actions'; 5 | 6 | import styled, { ThemeProvider } from 'styled-components'; 7 | 8 | import Alert, { DismissableAlert } from '../index'; 9 | import CloseIcon from '../../CloseIcon'; 10 | 11 | const StyledCloseIcon = styled(CloseIcon)` 12 | position: absolute; 13 | color: green; 14 | top: 0; 15 | right: 0; 16 | padding: 0.75rem 1.25rem; 17 | `; 18 | 19 | const customTheme = { 20 | alertPaddingY: '.5rem', 21 | alertPaddingX: '1rem', 22 | alertMarginBottom: '1rem', 23 | alertBorderRadius: '0.5rem', 24 | alertLinkFontWeight: 'normal', 25 | alertBorderWidth: '2px', 26 | 27 | alertBgLevel: -9, 28 | alertBorderLevel: -5, 29 | alertColorLevel: 3 30 | }; 31 | 32 | const customButtonTheme = { 33 | closeFontWeight: 700, 34 | closeColor: 'red', 35 | closeTextShadow: '0 1px 0 blue' 36 | }; 37 | 38 | export default storiesOf('Alert', module) 39 | .addDecorator( 40 | host({ 41 | align: 'center', 42 | width: 700 43 | }) 44 | ) 45 | .add('With custom Close Icon', () => ( 46 | 47 | 48 | } 52 | > 53 | This is a dark alert with an example link. Give it a 54 | click if you like. 55 | 56 | 57 | This is a dark alert with an example link. Give it a 58 | click if you like. 59 | 60 | 61 | 62 | )) 63 | .add('With ThemeProvider', () => ( 64 | 65 | 66 | This is a primary alert—check it out! 67 | This is a secondary alert—check it out! 68 | This is a success alert—check it out! 69 | This is a danger alert—check it out! 70 | This is a warning alert—check it out! 71 | This is a info alert—check it out! 72 | This is a light alert—check it out! 73 | This is a dark alert—check it out! 74 | 75 | 76 | )); 77 | -------------------------------------------------------------------------------- /src/Alert/utils/alert-variant.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { css } from 'styled-components'; 4 | 5 | // $FlowIssue 6 | import darken from 'polished/lib/color/darken'; 7 | 8 | export function alertVariant( 9 | background: string, 10 | border: string, 11 | bodyColor: string 12 | ) { 13 | return css` 14 | color: ${bodyColor}; 15 | background-color: ${background}; 16 | border-color: ${border}; 17 | 18 | hr { 19 | border-top-color: ${darken(0.05, border)}; 20 | } 21 | 22 | a { 23 | color: ${darken(0.1, bodyColor)}; 24 | } 25 | `; 26 | } 27 | 28 | export default alertVariant; 29 | -------------------------------------------------------------------------------- /src/Badge/Badge.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import PropTypes from 'prop-types'; 4 | import styled, { css } from 'styled-components'; 5 | import themeProp from '../utils/theme'; 6 | 7 | import { borderRadius } from '../utils/border-radius'; 8 | import { badgeVariant } from './utils/badge-variant'; 9 | 10 | import * as defaultTheme from './default-theme'; 11 | 12 | const { 13 | badgeFontSize, 14 | badgeFontWeight, 15 | badgePaddingY, 16 | badgePaddingX, 17 | badgeBorderRadius, 18 | badgePillPaddingX, 19 | badgePillBorderRadius 20 | } = defaultTheme; 21 | 22 | const Badge = styled.span` 23 | display: inline-block; 24 | padding: ${themeProp('badgePaddingY', badgePaddingY)} 25 | ${themeProp('badgePaddingX', badgePaddingX)}; 26 | font-size: ${themeProp('badgeFontSize', badgeFontSize)}; 27 | font-weight: ${themeProp('badgeFontWeight', badgeFontWeight)}; 28 | line-height: 1; 29 | text-align: center; 30 | white-space: nowrap; 31 | vertical-align: baseline; 32 | ${borderRadius(themeProp('badgeBorderRadius', badgeBorderRadius))}; 33 | 34 | /* Empty badges collapse automatically */ 35 | &:empty { 36 | display: none; 37 | } 38 | 39 | ${({ theme, color }) => badgeVariant(theme[color] || defaultTheme[color])}; 40 | 41 | ${({ pill }) => 42 | pill && 43 | css` 44 | padding-right: ${themeProp('badgePillPaddingX', badgePillPaddingX)}; 45 | padding-left: ${themeProp('badgePillPaddingX', badgePillPaddingX)}; 46 | ${borderRadius( 47 | themeProp('badgePillBorderRadius', badgePillBorderRadius) 48 | )}; 49 | `}; 50 | `; 51 | 52 | Badge.Link = Badge.withComponent('a'); 53 | 54 | Badge.propTypes = { 55 | color: PropTypes.oneOf([ 56 | 'primary', 57 | 'secondary', 58 | 'success', 59 | 'danger', 60 | 'warning', 61 | 'info', 62 | 'light', 63 | 'dark' 64 | ]), 65 | pill: PropTypes.bool 66 | }; 67 | 68 | Badge.defaultProps = { 69 | color: 'primary', 70 | pill: false 71 | }; 72 | 73 | export default Badge; 74 | -------------------------------------------------------------------------------- /src/Badge/default-theme.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export const primary = '#007bff'; 4 | export const secondary = '#6c757d'; 5 | export const success = '#28a745'; 6 | export const info = '#17a2b8'; 7 | export const warning = '#ffc107'; 8 | export const danger = '#dc3545'; 9 | export const light = '#f8f9fa'; 10 | export const dark = '#343a40'; 11 | 12 | export const badgeFontSize = '75%'; 13 | export const badgeFontWeight = 700; 14 | export const badgePaddingY = '.25em'; 15 | export const badgePaddingX = '.4em'; 16 | export const badgeBorderRadius = '.25rem'; 17 | export const badgePillPaddingX = '.6em'; 18 | export const badgePillBorderRadius = '10rem'; 19 | -------------------------------------------------------------------------------- /src/Badge/index.js: -------------------------------------------------------------------------------- 1 | import Badge from './Badge'; 2 | import { badgeVariant } from './utils/badge-variant'; 3 | 4 | export { badgeVariant }; 5 | export default Badge; 6 | -------------------------------------------------------------------------------- /src/Badge/stories/Badge.story.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | import { host } from 'storybook-host'; 4 | 5 | import { ThemeProvider } from 'styled-components'; 6 | 7 | import Badge from '../Badge'; 8 | 9 | const customTheme = { 10 | badgeFontWeight: 400, 11 | badgePaddingY: '.23em', 12 | badgePaddingX: '.5em', 13 | badgeBorderRadius: '0' 14 | }; 15 | 16 | const margin = { marginRight: 6, marginBottom: 6 }; 17 | 18 | export default storiesOf('Badge', module) 19 | .addDecorator( 20 | host({ 21 | title: 'Small count and labeling component.', 22 | align: 'center' 23 | }) 24 | ) 25 | .add('Badges', () => ( 26 | 27 |

28 | Example heading New 29 |

30 |

31 | Example heading New 32 |

33 |

34 | Example heading New 35 |

36 |

37 | Example heading New 38 |

39 |
40 | Example heading New 41 |
42 |
43 | Example heading New 44 |
45 |
46 | )) 47 | .add('Color Badges', () => ( 48 | 49 | 50 | Primary 51 | 52 | 53 | Secondary 54 | 55 | 56 | Success 57 | 58 | 59 | Danger 60 | 61 | 62 | Warning 63 | 64 | 65 | Info 66 | 67 | 68 | Light 69 | 70 | 71 | Dark 72 | 73 | 74 | )) 75 | .add('Link Badges', () => ( 76 | 77 | 78 | Primary 79 | 80 | 81 | Secondary 82 | 83 | 84 | Success 85 | 86 | 87 | Danger 88 | 89 | 90 | Warning 91 | 92 | 93 | Info 94 | 95 | 96 | Light 97 | 98 | 99 | Dark 100 | 101 | 102 | )) 103 | .add('Pill Badges', () => ( 104 | 105 | 106 | Primary 107 | 108 | 109 | Secondary 110 | 111 | 112 | Success 113 | 114 | 115 | Info 116 | 117 | 118 | Warning 119 | 120 | 121 | Danger 122 | 123 | 124 | Light 125 | 126 | 127 | Dark 128 | 129 | 130 | )) 131 | .add('Squared badges', () => ( 132 | 133 | 134 | 135 | Primary 136 | 137 | 138 | Secondary 139 | 140 | 141 | Success 142 | 143 | 144 | Info 145 | 146 | 147 | Warning 148 | 149 | 150 | Danger 151 | 152 | 153 | Light 154 | 155 | 156 | Dark 157 | 158 | 159 | 160 | )); 161 | -------------------------------------------------------------------------------- /src/Badge/utils/badge-variant.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { css } from 'styled-components'; 4 | // $FlowIssue 5 | import darken from 'polished/lib/color/darken'; 6 | 7 | import { colorYiq } from '../../utils/color-functions'; 8 | 9 | export function badgeVariant(color: string) { 10 | return css` 11 | color: ${colorYiq(color)}; 12 | background-color: ${color}; 13 | 14 | &[href] { 15 | &:hover { 16 | color: ${colorYiq(color)}; 17 | text-decoration: none; 18 | background-color: ${darken(0.1, color)}; 19 | } 20 | } 21 | `; 22 | } 23 | 24 | export default badgeVariant; 25 | -------------------------------------------------------------------------------- /src/Breadcrumbs/default-theme.js: -------------------------------------------------------------------------------- 1 | export const breadcrumbPaddingY = '.75rem'; 2 | export const breadcrumbPaddingX = '1rem'; 3 | export const breadcrumbItemPadding = '.5rem'; 4 | export const breadcrumbMarginBottom = '1rem'; 5 | export const breadcrumbBg = '#e9ecef'; 6 | export const breadcrumbDividerColor = '#6c757d'; 7 | export const breadcrumbActiveColor = '#6c757d'; 8 | export const breadcrumbDivider = '"/"'; 9 | -------------------------------------------------------------------------------- /src/Breadcrumbs/index.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import PropTypes from 'prop-types'; 3 | import themeProp from '../utils/theme'; 4 | 5 | import { borderRadius } from '../utils/border-radius'; 6 | 7 | import { 8 | breadcrumbPaddingY, 9 | breadcrumbPaddingX, 10 | breadcrumbItemPadding, 11 | breadcrumbMarginBottom, 12 | breadcrumbBg, 13 | breadcrumbDividerColor, 14 | breadcrumbActiveColor, 15 | breadcrumbDivider 16 | } from './default-theme'; 17 | 18 | const Breadcrumb = styled.ol` 19 | display: flex; 20 | flex-wrap: wrap; 21 | padding: ${themeProp('breadcrumbPaddingY', breadcrumbPaddingY)} 22 | ${themeProp('breadcrumbPaddingX', breadcrumbPaddingX)}; 23 | margin-bottom: ${themeProp('breadcrumbMarginBottom', breadcrumbMarginBottom)}; 24 | list-style: none; 25 | background-color: ${themeProp('breadcrumbBg', breadcrumbBg)}; 26 | ${borderRadius(themeProp('bordeRadius', '.25rem'))}; 27 | `; 28 | 29 | Breadcrumb.Item = styled.li.attrs({ 30 | 'aria-current': props => props.active && 'page' 31 | })` 32 | :not(:first-child)::before { 33 | display: inline-block; 34 | padding-right: ${themeProp('breadcrumbItemPadding', breadcrumbItemPadding)}; 35 | padding-left: ${themeProp('breadcrumbItemPadding', breadcrumbItemPadding)}; 36 | color: ${themeProp('breadcrumbDividerColor', breadcrumbDividerColor)}; 37 | content: ${themeProp('breadcrumbDivider', breadcrumbDivider)}; 38 | } 39 | 40 | :not(:first-child)&:hover::before { 41 | text-decoration: underline; 42 | } 43 | :not(:first-child)&:hover::before { 44 | text-decoration: none; 45 | } 46 | 47 | ${({ active }) => 48 | active && 49 | `color: ${themeProp('readcrumbActiveColor', breadcrumbActiveColor)};`}; 50 | `; 51 | 52 | Breadcrumb.defaultProps = {}; 53 | 54 | Breadcrumb.Item.defaultProps = { 55 | active: false 56 | }; 57 | 58 | Breadcrumb.Item.Proptypes = { 59 | active: PropTypes.bool 60 | }; 61 | 62 | export default Breadcrumb; 63 | -------------------------------------------------------------------------------- /src/Breadcrumbs/stories/Breadcrumb.story.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | import { host } from 'storybook-host'; 4 | 5 | // import extend, { ThemeProvider } from 'styled-components'; 6 | 7 | import Breadcrumb from '../index'; 8 | 9 | const customTheme = { 10 | closeFontSize: '1.75rem', 11 | closeFontWeight: 700, 12 | closeColor: 'red', 13 | closeTextShadow: '0 1px 0 blue' 14 | }; 15 | 16 | export default storiesOf('Breadcrumb', module) 17 | .addDecorator( 18 | host({ 19 | title: 20 | 'Indicate the current pages location within a navigational hierarchy.', 21 | align: 'center' 22 | }) 23 | ) 24 | .add('Breadcrumb', () => ( 25 | 26 | 27 | Home 28 | 29 | 30 | Library 31 | 32 | Data 33 | 34 | )); 35 | -------------------------------------------------------------------------------- /src/Button/__tests__/Button.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { shallow } from 'enzyme'; 4 | 5 | import Button from './Button'; 6 | 7 | test('it works', () => { 8 | const wrapper = shallow( 29 | 30 | 31 | )) 32 | .add('With Image', () => ( 33 | 34 | 35 | 36 | Card title 37 |

38 | Some quick example text to build on the card title and make up the 39 | bulk of the card's content. 40 |

41 | 44 |
45 |
46 | )) 47 | .add('Card with Header and Footer', () => ( 48 | 49 | Featured 50 | 51 | Card title 52 |

53 | Some quick example text to build on the card title and make up the 54 | bulk of the card's content. 55 |

56 | 59 |
60 | 2 days ago 61 |
62 | )); 63 | -------------------------------------------------------------------------------- /src/CloseIcon/default-theme.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export const closeFontSize = '1.5rem'; 4 | export const closeFontWeight = 700; 5 | export const closeColor = '#000'; 6 | export const closeTextShadow = '0 1px 0 #fff'; 7 | -------------------------------------------------------------------------------- /src/CloseIcon/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react'; 4 | import styled from 'styled-components'; 5 | import themeProp from '../utils/theme'; 6 | 7 | import { hoverFocus } from '../../src/utils/hover'; 8 | 9 | import { 10 | closeFontSize, 11 | closeFontWeight, 12 | closeColor, 13 | closeTextShadow 14 | } from './default-theme'; 15 | 16 | export const StyledCloseIcon = styled.button` 17 | font-size: ${themeProp('closeFontSize', closeFontSize)}; 18 | font-weight: ${themeProp('closeFontWeight', closeFontWeight)}; 19 | line-height: 1; 20 | color: ${themeProp('closeColor', closeColor)}; 21 | text-shadow: ${themeProp('closeTextShadow', closeTextShadow)}; 22 | opacity: 0.5; 23 | 24 | ${hoverFocus(` 25 | color: ${themeProp('closeColor', closeColor)}; 26 | text-decoration: none; 27 | opacity: .75; 28 | `)}; 29 | 30 | /* Opinionated: add "hand" cursor to non-disabled .close elements */ 31 | &:not(:disabled):not(.disabled) { 32 | cursor: pointer; 33 | } 34 | 35 | padding: 0; 36 | background: transparent; 37 | border: 0; 38 | -webkit-appearance: none; 39 | `; 40 | 41 | const CloseIcon = props => ( 42 | 43 | 44 | 45 | ); 46 | 47 | export default CloseIcon; 48 | -------------------------------------------------------------------------------- /src/CloseIcon/stories/CloseIcon.story.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | import { host } from 'storybook-host'; 4 | 5 | import { ThemeProvider } from 'styled-components'; 6 | 7 | import CloseIcon from '../index'; 8 | 9 | const customTheme = { 10 | closeFontSize: '1.75rem', 11 | closeFontWeight: 700, 12 | closeColor: 'red', 13 | closeTextShadow: '0 1px 0 blue' 14 | }; 15 | 16 | export default storiesOf('CloseIcon', module) 17 | .addDecorator( 18 | host({ 19 | align: 'center' 20 | }) 21 | ) 22 | .add('Default', () => ) 23 | .add('With custom theme', () => ( 24 | 25 | 26 | 27 | )); 28 | -------------------------------------------------------------------------------- /src/Collapse/default-theme.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | const transitionCollapse = 'height 0.35s ease'; 4 | 5 | export default transitionCollapse; 6 | -------------------------------------------------------------------------------- /src/Collapse/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import Transition from 'react-transition-group/Transition'; 4 | import omit from 'lodash.omit'; 5 | import styled, { css } from 'styled-components'; 6 | 7 | import themeProp from '../utils/theme'; 8 | import transitionCollapse from './default-theme'; 9 | 10 | const Wrapper = styled.div` 11 | ${({ status }) => status === 'exited' && 'display: none;'}; 12 | ${({ status }) => status === 'entered' && 'display: block;'}; 13 | 14 | ${({ status }) => 15 | (status === 'exiting' || status === 'entering') && 16 | css` 17 | height: 0; 18 | overflow: hidden; 19 | transition: ${themeProp('transitionCollapse', transitionCollapse)}; 20 | `}; 21 | `; 22 | 23 | const TransitionTimeouts = { 24 | Fade: 150, // $transition-fade 25 | Collapse: 350, // $transition-collapse 26 | Modal: 300, // $modal-transition 27 | Carousel: 600 // $carousel-transition 28 | }; 29 | 30 | // Duplicated Transition.propType keys to ensure that Reactstrap builds 31 | // for distribution properly exclude these keys for nested child HTML attributes 32 | // since `react-transition-group` removes propTypes in production builds. 33 | const TransitionPropTypeKeys = [ 34 | 'in', 35 | 'mountOnEnter', 36 | 'unmountOnExit', 37 | 'appear', 38 | 'enter', 39 | 'exit', 40 | 'timeout', 41 | 'onEnter', 42 | 'onEntering', 43 | 'onEntered', 44 | 'onExit', 45 | 'onExiting', 46 | 'onExited' 47 | ]; 48 | 49 | function getHeight(node) { 50 | return node.scrollHeight; 51 | } 52 | 53 | class Collapse extends Component { 54 | constructor(props) { 55 | super(props); 56 | 57 | this.state = { 58 | height: null 59 | }; 60 | } 61 | 62 | onEntering = node => { 63 | this.setState({ height: getHeight(node) }); 64 | this.props.onEntering(); 65 | }; 66 | 67 | onEntered = () => { 68 | this.setState({ height: null }); 69 | this.props.onEntered(); 70 | }; 71 | 72 | onExit = node => { 73 | this.setState({ height: getHeight(node) }); 74 | this.props.onExit(); 75 | }; 76 | 77 | onExiting = node => { 78 | const unused = node.offsetHeight; // eslint-disable-line no-unused-vars 79 | this.setState({ height: 0 }); 80 | this.props.onExiting(); 81 | }; 82 | 83 | onExited = () => { 84 | this.setState({ height: null }); 85 | this.props.onExited(); 86 | }; 87 | 88 | render() { 89 | const { isOpen, children, ...otherProps } = this.props; 90 | const { height } = this.state; 91 | 92 | const childProps = omit(otherProps, TransitionPropTypeKeys); 93 | 94 | return ( 95 | 104 | {status => { 105 | const style = height === null ? null : { height }; 106 | return ( 107 | 108 | {children} 109 | 110 | ); 111 | }} 112 | 113 | ); 114 | } 115 | } 116 | 117 | Collapse.defaultProps = { 118 | ...Transition.defaultProps, 119 | isOpen: false, 120 | timeout: TransitionTimeouts.Collapse 121 | }; 122 | 123 | Collapse.propTypes = { 124 | ...Transition.propTypes, 125 | isOpen: PropTypes.bool, 126 | children: PropTypes.oneOfType([ 127 | PropTypes.arrayOf(PropTypes.node), 128 | PropTypes.node 129 | ]) 130 | }; 131 | 132 | export default Collapse; 133 | -------------------------------------------------------------------------------- /src/Collapse/stories/Collapse.story.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment, Component } from 'react'; 2 | 3 | import { storiesOf } from '@storybook/react'; 4 | import { host } from 'storybook-host'; 5 | import { withKnobs, boolean } from '@storybook/addon-knobs/react'; 6 | import { action } from '@storybook/addon-actions'; 7 | 8 | import { Collapse, Button, CardBody, Card } from '../../index'; 9 | 10 | class Example extends Component { 11 | constructor(props) { 12 | super(props); 13 | this.state = { collapse: false }; 14 | } 15 | 16 | toggle = () => { 17 | this.setState({ collapse: !this.state.collapse }); 18 | }; 19 | 20 | render() { 21 | return ( 22 |
23 | 30 | 38 | 39 | 40 | Anim pariatur cliche reprehenderit, enim eiusmod high life 41 | accusamus terry richardson ad squid. Nihil anim keffiyeh 42 | helvetica, craft beer labore wes anderson cred nesciunt sapiente 43 | ea proident. 44 | 45 | 46 | 47 |
48 | ); 49 | } 50 | } 51 | 52 | export default storiesOf('Collapse', module) 53 | .addDecorator(withKnobs) 54 | .addDecorator( 55 | host({ 56 | title: 'Toggle the visibility of content across your project.', 57 | align: 'center', 58 | width: 420 59 | }) 60 | ) 61 | .add('Default Collapse', () => ( 62 | 63 | 64 | 65 | )); 66 | -------------------------------------------------------------------------------- /src/Dropdown/Dropdown.js: -------------------------------------------------------------------------------- 1 | import styled, { css } from 'styled-components'; 2 | 3 | import DropdownHeader from './DropdownHeader'; 4 | import DropdownItem from './DropdownItem'; 5 | import DropdownMenu from './DropdownMenu'; 6 | import DropdownDivider from './DropdownDivider'; 7 | 8 | const Dropdown = styled.div` 9 | position: relative; 10 | 11 | ${props => 12 | props.isOpen && 13 | css` 14 | ${DropdownMenu} { 15 | display: block; 16 | ${props.align === 'right' ? 'right: 0;' : 'left: 0;'}; 17 | } 18 | `}; 19 | `; 20 | 21 | Dropdown.Header = DropdownHeader; 22 | Dropdown.Item = DropdownItem; 23 | Dropdown.Menu = DropdownMenu; 24 | Dropdown.Divider = DropdownDivider; 25 | 26 | Dropdown.defaultProps = { 27 | align: 'left' 28 | }; 29 | 30 | export default Dropdown; 31 | -------------------------------------------------------------------------------- /src/Dropdown/DropdownDivider.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import themeProp from '../utils/theme'; 3 | 4 | import { navDivider } from '../utils/nav-divider'; 5 | 6 | import { dropdownDividerBg } from './default-theme'; 7 | 8 | const DropdownDivider = styled.div` 9 | ${navDivider(themeProp('dropdownDividerBg', dropdownDividerBg))}; 10 | `; 11 | 12 | DropdownDivider.defaultProps = {}; 13 | 14 | export default DropdownDivider; 15 | -------------------------------------------------------------------------------- /src/Dropdown/DropdownHeader.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | import themeProp from '../utils/theme'; 4 | 5 | import { 6 | dropdownPaddingY, 7 | dropdownItemPaddingX, 8 | fontSizeSm, 9 | dropdownHeaderColor 10 | } from './default-theme'; 11 | 12 | import Heading from '../Heading'; 13 | 14 | const DropdownHeader = styled(Heading)` 15 | display: block; 16 | padding: ${themeProp('dropdownPaddingY', dropdownPaddingY)} 17 | ${themeProp('dropdownItemPaddingX', dropdownItemPaddingX)}; 18 | margin-bottom: 0; 19 | font-size: ${themeProp('fontSizeSm', fontSizeSm)}; 20 | color: ${themeProp('dropdownHeaderColor', dropdownHeaderColor)}; 21 | white-space: nowrap; 22 | `; 23 | 24 | DropdownHeader.defaultProps = {}; 25 | 26 | export default DropdownHeader; 27 | -------------------------------------------------------------------------------- /src/Dropdown/DropdownItem.js: -------------------------------------------------------------------------------- 1 | import styled, { css } from 'styled-components'; 2 | 3 | import { hoverFocus } from '../utils/hover'; 4 | import themeProp from '../utils/theme'; 5 | 6 | import { 7 | dropdownItemPaddingY, 8 | dropdownItemPaddingX, 9 | fontWeightNormal, 10 | dropdownLinkColor, 11 | dropdownLinkHoverColor, 12 | dropdownLinkHoverBg, 13 | dropdownLinkActiveColor, 14 | dropdownLinkActiveBg, 15 | dropdownLinkDisabledColor, 16 | enableGradients 17 | } from './default-theme'; 18 | 19 | const DropdownItem = styled.a` 20 | display: block; 21 | width: 100%; 22 | padding: ${themeProp('dropdownItemPaddingY', dropdownItemPaddingY)} 23 | ${themeProp('dropdownItemPaddingX', dropdownItemPaddingX)}; 24 | clear: both; 25 | font-weight: ${themeProp('fontWeightNormal', fontWeightNormal)}; 26 | color: ${themeProp('dropdownLinkColor', dropdownLinkColor)}; 27 | text-align: inherit; 28 | white-space: nowrap; 29 | background: none; 30 | border: 0; 31 | 32 | ${hoverFocus(css` 33 | color: ${themeProp('dropdownLinkHoverColor', dropdownLinkHoverColor)}; 34 | text-decoration: none; 35 | background-color: ${themeProp('dropdownLinkHoverBg', dropdownLinkHoverBg)}; 36 | `)}; 37 | 38 | ${props => 39 | props.active && 40 | css` 41 | color: ${themeProp( 42 | 'dropdownLinkActiveColor', 43 | dropdownLinkActiveColor 44 | )}!important; 45 | text-decoration: none; 46 | background-color: ${themeProp( 47 | 'dropdownLinkActiveBg', 48 | dropdownLinkActiveBg 49 | )}!important; 50 | `}; 51 | 52 | ${props => 53 | props.disabled && 54 | css` 55 | color: ${themeProp( 56 | 'dropdownLinkDisabledColor', 57 | dropdownLinkDisabledColor 58 | )}!important; 59 | background-color: transparent !important; 60 | 61 | ${themeProp('enableGradients', enableGradients) && 62 | 'background-image: none;'}; 63 | `}; 64 | `; 65 | 66 | DropdownItem.defaultProps = {}; 67 | 68 | export default DropdownItem; 69 | -------------------------------------------------------------------------------- /src/Dropdown/DropdownMenu.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | import themeProp from '../utils/theme'; 4 | 5 | import { borderRadius } from '../utils/border-radius'; 6 | import { boxShadow } from '../utils/box-shadow'; 7 | 8 | import { 9 | dropdownMinWidth, 10 | dropdownPaddingY, 11 | dropdownSpacer, 12 | fontSizeBase, 13 | bodyColor, 14 | dropdownBg, 15 | dropdownBorderWidth, 16 | dropdownBorderColor, 17 | dropdownBorderRadius, 18 | dropdownBoxShadow, 19 | zIndexDropdown 20 | } from './default-theme'; 21 | 22 | const DropdownMenu = styled.div` 23 | position: absolute; 24 | top: 100%; 25 | z-index: ${themeProp('zIndexDropdown', zIndexDropdown)}; 26 | display: none; 27 | float: left; 28 | min-width: ${themeProp('dropdownMinWidth', dropdownMinWidth)}; 29 | padding: ${themeProp('dropdownPaddingY', dropdownPaddingY)} 0; 30 | margin: ${themeProp('dropdownSpacer', dropdownSpacer)} 0 0; 31 | font-size: ${themeProp('fontSizeBase', fontSizeBase)}; 32 | color: ${themeProp('bodyColor', bodyColor)}; 33 | text-align: left; 34 | list-style: none; 35 | background-color: ${themeProp('dropdownBg', dropdownBg)}; 36 | background-clip: padding-box; 37 | border: ${themeProp('dropdownBorderWidth', dropdownBorderWidth)} solid 38 | ${themeProp('dropdownBorderColor', dropdownBorderColor)}; 39 | ${borderRadius(themeProp('dropdownBorderRadius', dropdownBorderRadius))}; 40 | ${boxShadow(themeProp('dropdownBoxShadow', dropdownBoxShadow))}; 41 | `; 42 | 43 | DropdownMenu.defaultProps = {}; 44 | 45 | export default DropdownMenu; 46 | -------------------------------------------------------------------------------- /src/Dropdown/default-theme.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export const fontSizeBase = '1rem'; 4 | export const fontSizeSm = '.875rem'; 5 | export const fontWeightNormal = '400'; 6 | export const bodyColor = '#212529'; 7 | export const enableGradients = 'false'; 8 | 9 | export const dropdownMinWidth = '10rem'; 10 | export const dropdownPaddingY = '.5rem'; 11 | export const dropdownSpacer = '.125rem'; 12 | export const dropdownBg = '#fff'; 13 | export const dropdownBorderColor = 'rgba(0,0,0,0.15)'; 14 | export const dropdownBorderRadius = '.25rem'; 15 | export const dropdownBorderWidth = '1px'; 16 | export const dropdownDividerBg = '#e9ecef'; 17 | export const dropdownBoxShadow = '0 .5rem 1rem rgba(0,0,0,0.175)'; 18 | export const dropdownLinkColor = '#212529'; 19 | export const dropdownLinkHoverColor = '#16181b'; 20 | export const dropdownLinkHoverBg = '#f8f9fa'; 21 | export const dropdownLinkActiveColor = '#fff'; 22 | export const dropdownLinkActiveBg = '#007bff'; 23 | export const dropdownLinkDisabledColor = '#6c757d'; 24 | export const dropdownItemPaddingY = '.25rem'; 25 | export const dropdownItemPaddingX = '1.5rem'; 26 | export const dropdownHeaderColor = '#6c757d'; 27 | export const zIndexDropdown = 1000; 28 | -------------------------------------------------------------------------------- /src/Dropdown/index.js: -------------------------------------------------------------------------------- 1 | export { default as DropdownHeader } from './DropdownHeader'; 2 | export { default as DropdownItem } from './DropdownItem'; 3 | export { default as DropdownMenu } from './DropdownMenu'; 4 | export { default as DropdownDivider } from './DropdownDivider'; 5 | 6 | export { default } from './Dropdown'; 7 | -------------------------------------------------------------------------------- /src/Forms/CustomSelect.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import PropTypes from 'prop-types'; 4 | 5 | // $FlowIssue 6 | import get from 'lodash.get'; 7 | 8 | import styled, { css } from 'styled-components'; 9 | import themeProp from '../utils/theme'; 10 | 11 | import { 12 | customSelectHeight, 13 | customSelectPaddingY, 14 | customSelectPaddingX, 15 | // customSelectIndicatorPadding, 16 | customSelectLineHeight, 17 | customSelectBg, 18 | customSelectBgSize, 19 | customSelectBorderWidth, 20 | customSelectFocusBorderColor, 21 | customSelectFocusBoxShadow, 22 | inputColor, 23 | inputBg, 24 | customSelectDisabledColor, 25 | customSelectDisabledBg, 26 | customSelectColor, 27 | customSelectIndicator, 28 | customSelectBorderColor, 29 | customSelectBorderRadius, 30 | customSelectHeightSm, 31 | customSelectFontSizeSm, 32 | customSelectHeightLg, 33 | customSelectFontSizeLg 34 | } from './default-theme'; 35 | 36 | const CustomSelect = styled.select` 37 | display: inline-block; 38 | width: 100%; 39 | /* height: ${themeProp('customSelectHeight', customSelectHeight)}; */ 40 | padding: ${themeProp('customSelectPaddingY', customSelectPaddingY)} 1.75rem 41 | ${themeProp('customSelectPaddingY', customSelectPaddingY)} 42 | ${themeProp('customSelectPaddingX', customSelectPaddingX)}; 43 | line-height: ${themeProp('customSelectLineHeight', customSelectLineHeight)}; 44 | color: ${themeProp('customSelectColor', customSelectColor)}; 45 | vertical-align: middle; 46 | background: ${themeProp('customSelectBg', customSelectBg)} 47 | ${themeProp('customSelectIndicator', customSelectIndicator)} no-repeat right 48 | ${themeProp('customSelectPaddingX', customSelectPaddingX)} center; 49 | background-size: ${themeProp('customSelectBgSize', customSelectBgSize)}; 50 | border: ${themeProp('customSelectBorderWidth', customSelectBorderWidth)} solid 51 | ${themeProp('customSelectBorderColor', customSelectBorderColor)}; 52 | 53 | ${({ theme }) => 54 | typeof theme.enableRounded === 'undefined' || theme.enableRounded 55 | ? `border-radius: ${get( 56 | theme, 57 | 'customSelectBorderRadius', 58 | customSelectBorderRadius 59 | )};` 60 | : 'border-radius: 0;'}; 61 | 62 | appearance: none; 63 | 64 | &:focus { 65 | border-color: ${themeProp( 66 | 'customSelectFocusBorderColor', 67 | customSelectFocusBorderColor 68 | )}; 69 | outline: 0; 70 | box-shadow: ${themeProp( 71 | 'customSelectFocusBoxShadow', 72 | customSelectFocusBoxShadow 73 | )}; 74 | 75 | &::-ms-value { 76 | color: ${themeProp('inputColor', inputColor)}; 77 | backgroundcolor: ${themeProp('inputBg', inputBg)}; 78 | } 79 | } 80 | 81 | &[multiple], 82 | &[size]:not([size='1']) { 83 | height: auto; 84 | padding-right: ${themeProp('customSelectPaddingX', customSelectPaddingX)}; 85 | background-image: none; 86 | } 87 | 88 | &:disabled { 89 | color: ${themeProp('customSelectDisabledColor', customSelectDisabledColor)}; 90 | background-color: ${themeProp( 91 | 'customSelectDisabledBg', 92 | customSelectDisabledBg 93 | )}; 94 | } 95 | 96 | // Hides the default caret in IE11 97 | &::-ms-expand { 98 | opacity: 0; 99 | } 100 | 101 | ${({ size, theme }) => { 102 | switch (size) { 103 | case 'small': 104 | return css` 105 | height: ${get(theme, 'customSelectHeightSm', customSelectHeightSm)}; 106 | font-size: ${get( 107 | theme, 108 | 'customSelectFontSizeSm', 109 | customSelectFontSizeSm 110 | )}; 111 | `; 112 | case 'large': 113 | return css` 114 | height: ${get(theme, 'customSelectHeightLg', customSelectHeightLg)}; 115 | font-size: ${get( 116 | theme, 117 | 'customSelectFontSizeLg', 118 | customSelectFontSizeLg 119 | )}; 120 | `; 121 | default: 122 | return css` 123 | height: ${get(theme, 'customSelectHeight', customSelectHeight)}; 124 | `; 125 | } 126 | }}; 127 | `; 128 | 129 | CustomSelect.defaultProps = { 130 | size: 'normal' 131 | }; 132 | 133 | CustomSelect.defaultProps = { 134 | size: PropTypes.oneOf(['normal', 'small', 'large']) 135 | }; 136 | 137 | export default CustomSelect; 138 | -------------------------------------------------------------------------------- /src/Forms/FormGroup.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import styled from 'styled-components'; 4 | 5 | import themeProp from '../utils/theme'; 6 | 7 | import { formGroupMarginBottom } from './default-theme'; 8 | 9 | const FormGroup = styled.div` 10 | margin-bottom: ${themeProp('formGroupMarginBottom', formGroupMarginBottom)}; 11 | `; 12 | 13 | export default FormGroup; 14 | -------------------------------------------------------------------------------- /src/Forms/Input.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import PropTypes from 'prop-types'; 4 | import styled, { css } from 'styled-components'; 5 | 6 | // $FlowIssue 7 | import get from 'lodash.get'; 8 | 9 | import themeProp from '../utils/theme'; 10 | 11 | import { borderRadius } from '../utils/border-radius'; 12 | 13 | import { transition } from '../utils/transition'; 14 | import { formControlFocus } from '../utils/forms'; 15 | 16 | import { 17 | inputPaddingY, 18 | inputPaddingX, 19 | fontSizeBase, 20 | fontSizeSm, 21 | fontSizeLg, 22 | inputLineHeight, 23 | inputPaddingYsm, 24 | inputPaddingXsm, 25 | inputLineHeightSm, 26 | inputPaddingYlg, 27 | inputPaddingXlg, 28 | inputLineHeightLg, 29 | inputBg, 30 | inputDisabledBg, 31 | inputColor, 32 | inputBorderColor, 33 | inputBorderWidth, 34 | inputBoxShadow, 35 | inputBorderRadius, 36 | inputBorderRadiusLg, 37 | inputBorderRadiusSm, 38 | inputPlaceholderColor, 39 | inputTransition, 40 | inputFocusColor, 41 | inputFocusBg, 42 | inputFocusBorderColor, 43 | inputFocusBoxShadow 44 | } from './default-theme'; 45 | 46 | const Input = styled.input` 47 | display: block; 48 | width: 100%; 49 | padding: ${themeProp('inputPaddingY', inputPaddingY)} 50 | ${themeProp('inputPaddingX', inputPaddingX)}; 51 | font-size: ${themeProp('fontSizeBase', fontSizeBase)}; 52 | line-height: ${themeProp('inputLineHeight', inputLineHeight)}; 53 | color: ${themeProp('inputColor', inputColor)}; 54 | background-color: ${themeProp('inputBg', inputBg)}; 55 | background-image: none; 56 | background-clip: padding-box; 57 | border: ${themeProp('inputBorderWidth', inputBorderWidth)} solid 58 | ${themeProp('inputBorderColor', inputBorderColor)}; 59 | 60 | ${({ theme }) => 61 | typeof theme.enableRounded === 'undefined' || theme.enableRounded 62 | ? `border-radius: ${get(theme, 'inputBorderRadius', inputBorderRadius)};` 63 | : 'border-radius: 0;'} 64 | 65 | ${({ theme }) => 66 | (typeof theme.enableShadows === 'undefined' || theme.enableShadows) && 67 | `box-shadow: ${get(theme, 'inputBoxShadow', inputBoxShadow)};`}; 68 | 69 | ${transition(themeProp('inputTransition', inputTransition))} 70 | 71 | &::-ms-expand { 72 | background-color: transparent; 73 | border: 0; 74 | } 75 | 76 | ${formControlFocus( 77 | themeProp('inputFocusColor', inputFocusColor), 78 | themeProp('inputFocusBg', inputFocusBg), 79 | themeProp('inputFocusBorderColor', inputFocusBorderColor) 80 | )}; 81 | 82 | ${({ theme }) => css` 83 | &:focus { 84 | ${typeof theme.enableShadows === 'undefined' || theme.enableShadows 85 | ? `box-shadow: ${get(theme, 'inputBoxShadow', inputBoxShadow)}, ${get( 86 | theme, 87 | 'inputFocusBoxShadow', 88 | inputFocusBoxShadow 89 | )};` 90 | : `box-shadow: ${get( 91 | theme, 92 | 'inputFocusBoxShadow', 93 | inputFocusBoxShadow 94 | )};`}; 95 | } 96 | `}; 97 | 98 | &::placeholder { 99 | color: ${themeProp('inputPlaceholderColor', inputPlaceholderColor)}; 100 | opacity: 1; 101 | } 102 | 103 | ${props => 104 | props.disabled && 105 | css` 106 | background-color: ${themeProp('inputDisabledBg', inputDisabledBg)}; 107 | opacity: 1; 108 | `} 109 | 110 | ${({ size, theme }) => 111 | size === 'small' && 112 | css` 113 | padding: ${themeProp('inputPaddingYsm', inputPaddingYsm)} 114 | ${themeProp('inputPaddingXsm', inputPaddingXsm)}; 115 | font-size: ${themeProp('fontSizeSm', fontSizeSm)}; 116 | line-height: ${themeProp('inputLineHeightSm', inputLineHeightSm)}; 117 | ${(typeof theme.enableRounded === 'undefined' || theme.enableRounded) && 118 | borderRadius(themeProp('inputBorderRadiusSm', inputBorderRadiusSm))}; 119 | `} 120 | 121 | ${({ size, theme }) => 122 | size === 'large' && 123 | css` 124 | padding: ${themeProp('inputPaddingYlg', inputPaddingYlg)} 125 | ${themeProp('inputPaddingXlg', inputPaddingXlg)}; 126 | font-size: ${themeProp('fontSizeLg', fontSizeLg)}; 127 | line-height: ${themeProp('inputLineHeightLg', inputLineHeightLg)}; 128 | ${(typeof theme.enableRounded === 'undefined' || theme.enableRounded) && 129 | borderRadius(themeProp('inputBorderRadiusLg', inputBorderRadiusLg))}; 130 | `}; 131 | `; 132 | 133 | Input.defaultProps = { 134 | size: 'normal' 135 | }; 136 | 137 | Input.defaultProps = { 138 | size: PropTypes.oneOf(['normal', 'small', 'large']) 139 | }; 140 | 141 | export default Input; 142 | -------------------------------------------------------------------------------- /src/Forms/Select.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import styled from 'styled-components'; 3 | 4 | import themeProp from '../utils/theme'; 5 | 6 | import Input from './Input'; 7 | 8 | import { 9 | inputColor, 10 | inputBg, 11 | inputHeight, 12 | inputHeightSm, 13 | inputHeightLg 14 | } from './default-theme'; 15 | 16 | // TODO: it has a .extend before 17 | const Select = styled(Input)` 18 | &:not([size]):not([multiple]) { 19 | ${props => { 20 | switch (props.size) { 21 | case 'small': 22 | return `height: ${themeProp('inputHeight', inputHeightSm)};`; 23 | case 'large': 24 | return `height: ${themeProp('inputHeight', inputHeightLg)};`; 25 | default: 26 | return `height: ${themeProp('inputHeight', inputHeight)};`; 27 | } 28 | }} 29 | 30 | } 31 | 32 | &:focus::-ms-value { 33 | color: ${themeProp('inputColor', inputColor)}; 34 | background-color: ${themeProp('inputBg', inputBg)}; 35 | }; 36 | `; 37 | 38 | export default Select; 39 | -------------------------------------------------------------------------------- /src/Forms/Textarea.js: -------------------------------------------------------------------------------- 1 | import Input from './Input'; 2 | 3 | const Textarea = Input.withComponent('textarea'); 4 | 5 | export default Textarea; 6 | -------------------------------------------------------------------------------- /src/Forms/index.js: -------------------------------------------------------------------------------- 1 | import Input from './Input'; 2 | import Select from './Select'; 3 | import Textarea from './Textarea'; 4 | import FormGroup from './FormGroup'; 5 | import CustomSelect from './CustomSelect'; 6 | 7 | const Form = { Input, Select, Textarea, FormGroup, CustomSelect }; 8 | 9 | export { Input, Select, Textarea, FormGroup, CustomSelect }; 10 | export default Form; 11 | -------------------------------------------------------------------------------- /src/Forms/stories/CustomSelect.story.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | import { host } from 'storybook-host'; 4 | import { withKnobs, select } from '@storybook/addon-knobs/react'; 5 | 6 | import CustomSelect from '../CustomSelect'; 7 | 8 | export default storiesOf('Forms', module) 9 | .addDecorator(withKnobs) 10 | .addDecorator( 11 | host({ 12 | align: 'center', 13 | width: 400 14 | }) 15 | ) 16 | .add('CustomSelect', () => ( 17 | 18 | 19 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | )); 39 | -------------------------------------------------------------------------------- /src/Forms/stories/Full-form.story.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | import { host } from 'storybook-host'; 4 | 5 | import Input from '../Input'; 6 | import Select from '../Select'; 7 | import Textarea from '../Textarea'; 8 | import FormGroup from '../FormGroup'; 9 | 10 | export default storiesOf('Forms', module) 11 | .addDecorator( 12 | host({ 13 | align: 'center', 14 | width: 620 15 | }) 16 | ) 17 | .add('Full form', () => ( 18 | 19 | 20 | 21 | 26 | 27 | 28 | 29 | 36 | 37 | 38 | 41 | 48 | 49 | 50 | 51 |