From 47150020503e52e28677df77b0d41067be4c3dad Mon Sep 17 00:00:00 2001 From: Catalin Vasile Date: Fri, 28 Dec 2018 14:56:45 +0200 Subject: [PATCH] Initial release --- .eslintrc | 25 + .gitignore | 8 + LICENSE.md | 7 + README.md | 89 + build/rollup.config.js | 132 + components/alert/Alert.js | 120 + components/alert/docs/01-basic-alert.js | 62 + components/alert/docs/02-dismissible-alert.js | 27 + .../alert/docs/03-self-dismissing-alert.js | 60 + components/alert/index.js | 3 + components/badge/Badge.js | 58 + components/badge/docs/01-basic-badges.js | 22 + components/badge/docs/02-pill-badges.js | 36 + components/badge/docs/03-outline-badges.js | 36 + components/badge/docs/04-mixed-badges.js | 38 + components/badge/docs/05-link-badges.js | 36 + components/badge/index.js | 3 + components/breadcrumb/Breadcrumb.js | 63 + components/breadcrumb/BreadcrumbItem.js | 38 + .../breadcrumb/docs/01-basic-breadcrumbs.js | 18 + components/breadcrumb/index.js | 4 + components/button-group/ButtonGroup.js | 39 + .../docs/1-basic-button-groups.js | 17 + .../button-group/docs/2-size-button-groups.js | 28 + .../docs/3-vertical-button-groups.js | 16 + components/button-group/index.js | 3 + components/button-toolbar/ButtonToolbar.js | 29 + .../docs/01-basic-button-toolbar.js | 23 + components/button-toolbar/index.js | 3 + components/button/Button.js | 130 + components/button/docs/01-basic-buttons.js | 22 + components/button/docs/02-outline-buttons.js | 36 + components/button/docs/03-pill-buttons.js | 36 + components/button/docs/04-mixed-buttons.js | 38 + components/button/docs/05-squared-buttons.js | 38 + .../button/docs/06-outline-squared-buttons.js | 38 + components/button/docs/07-sizes-buttons.js | 17 + .../button/docs/08-active-state-buttons.js | 20 + .../button/docs/09-disabled-state-buttons.js | 20 + components/button/docs/10-block-buttons.js | 18 + components/button/index.js | 3 + components/card/Card.js | 64 + components/card/CardBody.js | 38 + components/card/CardColumns.js | 27 + components/card/CardDeck.js | 27 + components/card/CardFooter.js | 27 + components/card/CardGroup.js | 27 + components/card/CardHeader.js | 27 + components/card/CardImg.js | 45 + components/card/CardImgOverlay.js | 27 + components/card/CardLink.js | 35 + components/card/CardSubtitle.js | 27 + components/card/CardText.js | 27 + components/card/CardTitle.js | 27 + components/card/docs/01-basic-card.js | 30 + components/card/docs/02-card-body.js | 17 + .../card/docs/03-card-body-title-subtitle.js | 19 + components/card/docs/04-card-image.js | 33 + components/card/index.js | 29 + components/collapse/Collapse.js | 147 + components/collapse/docs/01-basic-collapse.js | 36 + .../collapse/docs/02-initial-visibility.js | 38 + components/collapse/index.js | 3 + components/constants.js | 103 + components/container/Col.js | 103 + components/container/Container.js | 37 + components/container/Row.js | 49 + components/container/docs/01-basic-layout.js | 52 + .../container/docs/02-layout-explained.js | 50 + components/container/index.js | 5 + components/datepicker/DatePicker.css | 132 + components/datepicker/DatePicker.js | 36 + .../datepicker/docs/01-basic-datepicker.js | 40 + .../docs/02-datepicker-disabled-dates.js | 44 + .../docs/03-datepicker-highlighted-dates.js | 45 + components/datepicker/index.js | 3 + components/dropdown/Dropdown.js | 197 + components/dropdown/DropdownContext.js | 8 + components/dropdown/DropdownItem.js | 128 + components/dropdown/DropdownMenu.js | 116 + components/dropdown/DropdownToggle.js | 143 + components/dropdown/docs/01-basic-dropdown.js | 39 + .../dropdown/docs/02-dropdown-themes.js | 39 + .../dropdown/docs/03-dropdown-positioning.js | 39 + components/dropdown/docs/04-dropup.js | 39 + components/dropdown/docs/05-split-buttons.js | 41 + .../dropdown/docs/06-dropdown-sizing.js | 79 + components/dropdown/index.js | 6 + components/fade/Fade.js | 75 + components/fade/docs/01-basic-fade.js | 34 + components/fade/index.js | 3 + components/form-checkbox/FormCheckbox.js | 111 + .../docs/01-basic-form-checkbox.js | 54 + .../form-checkbox/docs/02-basic-toggles.js | 11 + .../form-checkbox/docs/03-small-toggles.js | 15 + .../docs/04-inline-checkboxes.js | 17 + components/form-checkbox/index.js | 3 + components/form-group/FormGroup.js | 56 + .../form-group/docs/01-basic-form-group.js | 16 + components/form-group/index.js | 3 + components/form-input/FormInput.js | 101 + .../form-input/docs/01-basic-form-input.js | 11 + .../form-input/docs/02-form-input-sizing.js | 17 + .../docs/03-form-input-validation.js | 17 + components/form-input/index.js | 3 + components/form-radio/FormRadio.js | 101 + .../form-radio/docs/01-basic-form-radio.js | 57 + .../form-radio/docs/02-inline-form-radio.js | 24 + components/form-radio/index.js | 3 + components/form-select/FormSelect.js | 83 + .../form-select/docs/01-basic-form-select.js | 19 + .../form-select/docs/02-form-select-sizing.js | 28 + .../docs/03-form-select-validation.js | 23 + components/form-select/index.js | 3 + components/form-textarea/FormTextarea.js | 84 + .../docs/01-basic-form-textarea.js | 29 + components/form-textarea/index.js | 3 + components/form/Form.js | 72 + components/form/FormFeedback.js | 45 + components/form/docs/01-basic-form.js | 22 + components/form/index.js | 4 + components/index.js | 116 + components/input-group/InputGroup.js | 47 + components/input-group/InputGroupAddon.js | 50 + components/input-group/InputGroupText.js | 27 + .../input-group/docs/01-basic-input-group.js | 41 + .../input-group/docs/02-input-group-addons.js | 26 + .../docs/03-input-group-dropdowns.js | 48 + .../input-group/docs/04-input-group-sizing.js | 48 + .../docs/05-seamless-input-groups.js | 26 + components/input-group/index.js | 5 + components/list-group/ListGroup.js | 43 + components/list-group/ListGroupItem.js | 65 + components/list-group/ListGroupItemHeading.js | 27 + components/list-group/ListGroupItemText.js | 27 + .../list-group/docs/01-basic-list-group.js | 19 + components/list-group/index.js | 6 + components/modal/Modal.js | 258 + components/modal/ModalBody.js | 30 + components/modal/ModalFooter.js | 30 + components/modal/ModalHeader.js | 76 + components/modal/docs/01-basic-modal.js | 29 + components/modal/docs/02-modal-size.js | 34 + components/modal/index.js | 6 + components/nav/Nav.js | 97 + components/nav/NavItem.js | 40 + components/nav/NavLink.js | 94 + components/nav/docs/01-basic-nav.js | 30 + components/nav/docs/02-nav-tabs-style.js | 30 + components/nav/docs/03-nav-pills-style.js | 30 + components/nav/docs/04-nav-fill-style.js | 28 + components/nav/docs/05-nav-justified-style.js | 28 + components/nav/index.js | 5 + components/navbar/Navbar.js | 88 + components/navbar/NavbarBrand.js | 27 + components/navbar/NavbarToggler.js | 40 + components/navbar/docs/01-basic-navbar.js | 103 + components/navbar/index.js | 5 + components/popover/Popover.js | 266 + components/popover/PopoverBody.js | 27 + components/popover/PopoverHeader.js | 27 + components/popover/docs/01-basic-popover.js | 46 + .../popover/docs/02-popover-placement.js | 42 + components/popover/index.js | 5 + components/progress/Progress.js | 115 + components/progress/docs/01-basic-progress.js | 11 + .../progress/docs/02-progress-labels.js | 11 + .../docs/03-multiple-progress-bars.js | 16 + components/progress/index.js | 3 + components/slider/Slider.js | 115 + components/slider/docs/01-basic-slider.js | 13 + .../slider/docs/02-slider-custom-range.js | 35 + components/slider/docs/02-slider-themes.js | 18 + .../slider/docs/03-slider-custom-range.js | 35 + .../slider/docs/03-slider-multiple-values.js | 35 + .../slider/docs/04-slider-multiple-values.js | 35 + .../slider/docs/04-slider-using-pips.js | 36 + .../slider/docs/05-slider-using-pips.js | 36 + components/slider/index.js | 3 + components/tooltip/Tooltip.js | 383 + components/tooltip/docs/01-basic-tooltip.js | 36 + .../tooltip/docs/02-tooltip-placement.js | 79 + components/tooltip/docs/03-tooltip-trigger.js | 37 + components/tooltip/index.js | 3 + components/utils.js | 71 + dist/shards-react.common.js | 6735 ++++++++++++++++ dist/shards-react.common.js.map | 1 + dist/shards-react.es.js | 6674 ++++++++++++++++ dist/shards-react.es.js.map | 1 + dist/shards-react.umd.js | 6742 ++++++++++++++++ dist/shards-react.umd.js.map | 1 + dist/shards-react.umd.min.js | 8 + dist/shards-react.umd.min.js.map | 1 + docs/getting-started.md | 34 + docs/roadmap.md | 6 + logo.jpg | Bin 0 -> 34737 bytes package-lock.json | 6980 +++++++++++++++++ package.json | 76 + utils/PopperManager.js | 236 + 199 files changed, 35719 insertions(+) create mode 100644 .eslintrc create mode 100644 .gitignore create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 build/rollup.config.js create mode 100644 components/alert/Alert.js create mode 100644 components/alert/docs/01-basic-alert.js create mode 100644 components/alert/docs/02-dismissible-alert.js create mode 100644 components/alert/docs/03-self-dismissing-alert.js create mode 100644 components/alert/index.js create mode 100644 components/badge/Badge.js create mode 100644 components/badge/docs/01-basic-badges.js create mode 100644 components/badge/docs/02-pill-badges.js create mode 100644 components/badge/docs/03-outline-badges.js create mode 100644 components/badge/docs/04-mixed-badges.js create mode 100644 components/badge/docs/05-link-badges.js create mode 100644 components/badge/index.js create mode 100644 components/breadcrumb/Breadcrumb.js create mode 100644 components/breadcrumb/BreadcrumbItem.js create mode 100644 components/breadcrumb/docs/01-basic-breadcrumbs.js create mode 100644 components/breadcrumb/index.js create mode 100644 components/button-group/ButtonGroup.js create mode 100644 components/button-group/docs/1-basic-button-groups.js create mode 100644 components/button-group/docs/2-size-button-groups.js create mode 100644 components/button-group/docs/3-vertical-button-groups.js create mode 100644 components/button-group/index.js create mode 100644 components/button-toolbar/ButtonToolbar.js create mode 100644 components/button-toolbar/docs/01-basic-button-toolbar.js create mode 100644 components/button-toolbar/index.js create mode 100644 components/button/Button.js create mode 100644 components/button/docs/01-basic-buttons.js create mode 100644 components/button/docs/02-outline-buttons.js create mode 100644 components/button/docs/03-pill-buttons.js create mode 100644 components/button/docs/04-mixed-buttons.js create mode 100644 components/button/docs/05-squared-buttons.js create mode 100644 components/button/docs/06-outline-squared-buttons.js create mode 100644 components/button/docs/07-sizes-buttons.js create mode 100644 components/button/docs/08-active-state-buttons.js create mode 100644 components/button/docs/09-disabled-state-buttons.js create mode 100644 components/button/docs/10-block-buttons.js create mode 100644 components/button/index.js create mode 100644 components/card/Card.js create mode 100644 components/card/CardBody.js create mode 100644 components/card/CardColumns.js create mode 100644 components/card/CardDeck.js create mode 100644 components/card/CardFooter.js create mode 100644 components/card/CardGroup.js create mode 100644 components/card/CardHeader.js create mode 100644 components/card/CardImg.js create mode 100644 components/card/CardImgOverlay.js create mode 100644 components/card/CardLink.js create mode 100644 components/card/CardSubtitle.js create mode 100644 components/card/CardText.js create mode 100644 components/card/CardTitle.js create mode 100644 components/card/docs/01-basic-card.js create mode 100644 components/card/docs/02-card-body.js create mode 100644 components/card/docs/03-card-body-title-subtitle.js create mode 100644 components/card/docs/04-card-image.js create mode 100644 components/card/index.js create mode 100644 components/collapse/Collapse.js create mode 100644 components/collapse/docs/01-basic-collapse.js create mode 100644 components/collapse/docs/02-initial-visibility.js create mode 100644 components/collapse/index.js create mode 100644 components/constants.js create mode 100644 components/container/Col.js create mode 100644 components/container/Container.js create mode 100644 components/container/Row.js create mode 100644 components/container/docs/01-basic-layout.js create mode 100644 components/container/docs/02-layout-explained.js create mode 100644 components/container/index.js create mode 100644 components/datepicker/DatePicker.css create mode 100644 components/datepicker/DatePicker.js create mode 100644 components/datepicker/docs/01-basic-datepicker.js create mode 100644 components/datepicker/docs/02-datepicker-disabled-dates.js create mode 100644 components/datepicker/docs/03-datepicker-highlighted-dates.js create mode 100644 components/datepicker/index.js create mode 100644 components/dropdown/Dropdown.js create mode 100644 components/dropdown/DropdownContext.js create mode 100644 components/dropdown/DropdownItem.js create mode 100644 components/dropdown/DropdownMenu.js create mode 100644 components/dropdown/DropdownToggle.js create mode 100644 components/dropdown/docs/01-basic-dropdown.js create mode 100644 components/dropdown/docs/02-dropdown-themes.js create mode 100644 components/dropdown/docs/03-dropdown-positioning.js create mode 100644 components/dropdown/docs/04-dropup.js create mode 100644 components/dropdown/docs/05-split-buttons.js create mode 100644 components/dropdown/docs/06-dropdown-sizing.js create mode 100644 components/dropdown/index.js create mode 100644 components/fade/Fade.js create mode 100644 components/fade/docs/01-basic-fade.js create mode 100644 components/fade/index.js create mode 100644 components/form-checkbox/FormCheckbox.js create mode 100644 components/form-checkbox/docs/01-basic-form-checkbox.js create mode 100644 components/form-checkbox/docs/02-basic-toggles.js create mode 100644 components/form-checkbox/docs/03-small-toggles.js create mode 100644 components/form-checkbox/docs/04-inline-checkboxes.js create mode 100644 components/form-checkbox/index.js create mode 100644 components/form-group/FormGroup.js create mode 100644 components/form-group/docs/01-basic-form-group.js create mode 100644 components/form-group/index.js create mode 100644 components/form-input/FormInput.js create mode 100644 components/form-input/docs/01-basic-form-input.js create mode 100644 components/form-input/docs/02-form-input-sizing.js create mode 100644 components/form-input/docs/03-form-input-validation.js create mode 100644 components/form-input/index.js create mode 100644 components/form-radio/FormRadio.js create mode 100644 components/form-radio/docs/01-basic-form-radio.js create mode 100644 components/form-radio/docs/02-inline-form-radio.js create mode 100644 components/form-radio/index.js create mode 100644 components/form-select/FormSelect.js create mode 100644 components/form-select/docs/01-basic-form-select.js create mode 100644 components/form-select/docs/02-form-select-sizing.js create mode 100644 components/form-select/docs/03-form-select-validation.js create mode 100644 components/form-select/index.js create mode 100644 components/form-textarea/FormTextarea.js create mode 100644 components/form-textarea/docs/01-basic-form-textarea.js create mode 100644 components/form-textarea/index.js create mode 100644 components/form/Form.js create mode 100644 components/form/FormFeedback.js create mode 100644 components/form/docs/01-basic-form.js create mode 100644 components/form/index.js create mode 100644 components/index.js create mode 100644 components/input-group/InputGroup.js create mode 100644 components/input-group/InputGroupAddon.js create mode 100644 components/input-group/InputGroupText.js create mode 100644 components/input-group/docs/01-basic-input-group.js create mode 100644 components/input-group/docs/02-input-group-addons.js create mode 100644 components/input-group/docs/03-input-group-dropdowns.js create mode 100644 components/input-group/docs/04-input-group-sizing.js create mode 100644 components/input-group/docs/05-seamless-input-groups.js create mode 100644 components/input-group/index.js create mode 100644 components/list-group/ListGroup.js create mode 100644 components/list-group/ListGroupItem.js create mode 100644 components/list-group/ListGroupItemHeading.js create mode 100644 components/list-group/ListGroupItemText.js create mode 100644 components/list-group/docs/01-basic-list-group.js create mode 100644 components/list-group/index.js create mode 100644 components/modal/Modal.js create mode 100644 components/modal/ModalBody.js create mode 100644 components/modal/ModalFooter.js create mode 100644 components/modal/ModalHeader.js create mode 100644 components/modal/docs/01-basic-modal.js create mode 100644 components/modal/docs/02-modal-size.js create mode 100644 components/modal/index.js create mode 100644 components/nav/Nav.js create mode 100644 components/nav/NavItem.js create mode 100644 components/nav/NavLink.js create mode 100644 components/nav/docs/01-basic-nav.js create mode 100644 components/nav/docs/02-nav-tabs-style.js create mode 100644 components/nav/docs/03-nav-pills-style.js create mode 100644 components/nav/docs/04-nav-fill-style.js create mode 100644 components/nav/docs/05-nav-justified-style.js create mode 100644 components/nav/index.js create mode 100644 components/navbar/Navbar.js create mode 100644 components/navbar/NavbarBrand.js create mode 100644 components/navbar/NavbarToggler.js create mode 100644 components/navbar/docs/01-basic-navbar.js create mode 100644 components/navbar/index.js create mode 100644 components/popover/Popover.js create mode 100644 components/popover/PopoverBody.js create mode 100644 components/popover/PopoverHeader.js create mode 100644 components/popover/docs/01-basic-popover.js create mode 100644 components/popover/docs/02-popover-placement.js create mode 100644 components/popover/index.js create mode 100644 components/progress/Progress.js create mode 100644 components/progress/docs/01-basic-progress.js create mode 100644 components/progress/docs/02-progress-labels.js create mode 100644 components/progress/docs/03-multiple-progress-bars.js create mode 100644 components/progress/index.js create mode 100644 components/slider/Slider.js create mode 100644 components/slider/docs/01-basic-slider.js create mode 100644 components/slider/docs/02-slider-custom-range.js create mode 100644 components/slider/docs/02-slider-themes.js create mode 100644 components/slider/docs/03-slider-custom-range.js create mode 100644 components/slider/docs/03-slider-multiple-values.js create mode 100644 components/slider/docs/04-slider-multiple-values.js create mode 100644 components/slider/docs/04-slider-using-pips.js create mode 100644 components/slider/docs/05-slider-using-pips.js create mode 100644 components/slider/index.js create mode 100644 components/tooltip/Tooltip.js create mode 100644 components/tooltip/docs/01-basic-tooltip.js create mode 100644 components/tooltip/docs/02-tooltip-placement.js create mode 100644 components/tooltip/docs/03-tooltip-trigger.js create mode 100644 components/tooltip/index.js create mode 100644 components/utils.js create mode 100644 dist/shards-react.common.js create mode 100644 dist/shards-react.common.js.map create mode 100644 dist/shards-react.es.js create mode 100644 dist/shards-react.es.js.map create mode 100644 dist/shards-react.umd.js create mode 100644 dist/shards-react.umd.js.map create mode 100644 dist/shards-react.umd.min.js create mode 100644 dist/shards-react.umd.min.js.map create mode 100644 docs/getting-started.md create mode 100644 docs/roadmap.md create mode 100644 logo.jpg create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 utils/PopperManager.js diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..5c9b2b7 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,25 @@ +{ + "plugins": [ + "react" + ], + "parserOptions": { + "ecmaVersion": 6, + "sourceType": "module", + "ecmaFeatures": { + "jsx": true, + "experimentalObjectRestSpread": true + } + }, + "env": { + "es6": true, + "browser": true, + "node": true, + "mocha": true + }, + "extends": [ + "eslint:recommended", + "plugin:react/recommended" + ], + "rules": { + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d6ef27d --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +.git + +# OS +.DS_Store + +# Project dependencies +node_modules +yarn-error.log diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..010c44b --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,7 @@ +Copyright 2018 DesignRevision + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..c453f9f --- /dev/null +++ b/README.md @@ -0,0 +1,89 @@ +

+ +

+ +

+Shards React is a free, beautiful and modern React UI kit
based on Shards. +

+ +
+ +

+ + + + + + +

+ +
+ +

+ Documentation & Demo • + Official Page +

+ + +### Getting Started + +Getting started with Shards React is fairly simple. You can download Shards React via the official website, here on GitHub as a release package or by using a package manager such as Yarn or NPM. + +* [Official Website](https://designrevision.com/downloads/shards-react) +* [Release Package](https://github.com/DesignRevision/shards-react/releases) + +
+ +### Quick Start + +You can install Shards React via Yarn or NPM. + +```bash +# Yarn +yarn add shards-react + +# NPM +npm i shards-react +``` + +
+ +### Module Bundlers + +If you are using a module bundler such as [Webpack](https://webpack.js.org/) or [Rollup](https://rollupjs.org/), you can import components as follows: + +```javascript +import { Alert } from "shards-react"; + +import "bootstrap/dist/css/bootstrap.min.css"; +import "shards-ui/dist/css/shards.min.css" + +// ... +``` + +
+ +### Built Using + +* [Shards UI Kit](https://designrevision.com/downloads/shards/) +* [React Datepicker](https://reactdatepicker.com/) +* [React Popper](https://github.com/FezVrasta/react-popper) +* [noUiSlider by Léon Gersen (WTFPL License)](https://refreshless.com/nouislider/download/) + +
+ +### Contributing + +Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us. + +
+ +### Roadmap + +[View the roadmap.](http://designrevision.com/docs/shards-react/roadmap) + +
+ +### Changelog + +[View notable changes.](CHANGELOG.md) diff --git a/build/rollup.config.js b/build/rollup.config.js new file mode 100644 index 0000000..ffd1bbd --- /dev/null +++ b/build/rollup.config.js @@ -0,0 +1,132 @@ +'use strict' + +import path from 'path' +import nodeResolve from 'rollup-plugin-node-resolve' +import commonjs from 'rollup-plugin-commonjs' +import babel from 'rollup-plugin-babel' +import minify from 'rollup-plugin-babel-minify' +import postcss from 'rollup-plugin-postcss' +import { name, version, dependencies, peerDependencies } from '../package.json' + +const PATHS = { + INPUT: path.resolve(__dirname, '..', 'components', 'index.js'), + DIST: path.resolve(__dirname, '..', 'dist') +} + +// Converts strings into camelCase +function camelize(str) { + return str.replace(/(?:^\w|[A-Z]|\b\w|\s+)/g, function(match, index) { + if (+match === 0) { + return '' + } + + return index == 0 ? match.toLowerCase() : match.toUpperCase() + }) +} + +const year = new Date().getFullYear() +const banner = `/* +* Shards React v${version} (https://designrevision.com/downloads/shards-react/) +* Based on: Bootstrap ${dependencies.bootstrap} (https://getbootstrap.com) +* Based on: Shards ${dependencies['shards-ui']} (https://designrevision.com/downloads/shards/) +* Copyright 2017-${year} DesignRevision (https://designrevision.com) +* Copyright 2017-${year} Catalin Vasile (http://catalin.me) +*/` + +const globals = { + react: 'React', + 'react-dom': 'ReactDOM', + 'react-transition-group': 'ReactTransitionGroup', + 'react-popper': 'ReactPopper', + 'prop-types': 'PropTypes', + 'lodash.pick': 'pick', + 'lodash.omit': 'omit', + 'lodash.isfunction': 'isFunction', + 'react-datepicker': 'ReactDatePicker', + 'shortid': 'shortid', + 'lodash.tonumber': 'toNumber', + 'nouislider': 'nouislider', + 'classnames': 'classNames' +} + +function createBaseConfig(callback) { + const baseConfig = { + input: PATHS.INPUT, + plugins: [ + nodeResolve(), + commonjs({ + include: 'node_modules/**' // Workaround for: https://github.com/rollup/rollup-plugin-commonjs/issues/247 + }), + postcss(), + babel({ + runtimeHelpers: true, + presets: [ + "@babel/env", + "@babel/react" + ] + }) + ], + external: Object.keys(Object.assign({}, peerDependencies, dependencies)) + } + + return callback(baseConfig) +} + +const CommonJSConfig = createBaseConfig(function(config) { + return Object.assign({}, config, { + output: { + banner, + name: camelize(name), + sourcemap: true, + format: 'cjs', + file: path.resolve(PATHS.DIST, `${name}.common.js`) + } + }) +}) + +const ESModulesConfig = createBaseConfig(function(config) { + return Object.assign({}, config, { + output: { + banner, + name: camelize(name), + sourcemap: true, + format: 'es', + file: path.resolve(PATHS.DIST, `${name}.es.js`) + } + }) +}) + +const UMDConfig = createBaseConfig(function(config) { + return Object.assign({}, config, { + output: { + banner, + name: camelize(name), + sourcemap: true, + globals: globals, + format: 'umd', + file: path.resolve(PATHS.DIST, `${name}.umd.js`) + } + }) +}) + +const MinifiedUMDConfig = createBaseConfig(function(config) { + config.plugins.push(minify({ comments: false })) + + return Object.assign({}, config, { + output: { + banner, + name: camelize(name), + sourcemap: true, + globals: globals, + format: 'umd', + file: path.resolve(PATHS.DIST, `${name}.umd.min.js`) + } + }) +}) + +module.exports = [ + CommonJSConfig, + ESModulesConfig, + UMDConfig, + MinifiedUMDConfig +] diff --git a/components/alert/Alert.js b/components/alert/Alert.js new file mode 100644 index 0000000..5afa0bb --- /dev/null +++ b/components/alert/Alert.js @@ -0,0 +1,120 @@ +import React from "react"; +import PropTypes from "prop-types"; +import classNames from "classnames"; + +import Fade from "../fade"; + +/** + * The alert component can be used to display contextual user messages. + */ +const Alert = props => { + const { + className, + closeClassName, + closeAriaLabel, + tag: Tag, + theme, + open, + dismissible, + children, + transition, + fade, + ...attrs + } = props; + + const classes = classNames( + className, + "alert", + `alert-${theme}`, + dismissible && "alert-dismissible" + ); + + const closeClasses = classNames("close", closeClassName); + + const alertTransition = { + ...Fade.defaultProps, + ...transition, + baseClass: fade ? transition.baseClass : "", + timeout: fade ? transition.timeout : 0 + }; + + return ( + + {dismissible ? ( + + ) : null} + {children} + + ); +}; + +Alert.propTypes = { + /** + * The children nodes. + */ + children: PropTypes.node, + /** + * The class name. + */ + className: PropTypes.string, + /** + * The close button's class name. + */ + closeClassName: PropTypes.string, + /** + * The close button's aria label. + */ + closeAriaLabel: PropTypes.string, + /** + * The theme color. + */ + theme: PropTypes.string, + /** + * Whether it should fade, or not. + */ + fade: PropTypes.bool, + /** + * Whether is open, or not. + */ + open: PropTypes.bool, + /** + * Whether is dismissible, or not. + */ + dismissible: PropTypes.func, + /** + * The transition config. See `Fade` component for more details. + */ + transition: PropTypes.shape(Fade.propTypes), + /** + * The component tag type. + */ + tag: PropTypes.oneOfType([PropTypes.func, PropTypes.string]) +}; + +Alert.defaultProps = { + theme: "primary", + open: true, + tag: "div", + closeAriaLabel: "Close", + fade: true, + transition: { + ...Fade.defaultProps, + unmountOnExit: true + } +}; + +export default Alert; diff --git a/components/alert/docs/01-basic-alert.js b/components/alert/docs/01-basic-alert.js new file mode 100644 index 0000000..7d2068a --- /dev/null +++ b/components/alert/docs/01-basic-alert.js @@ -0,0 +1,62 @@ +import React from "react"; +import { Alert } from "shards-react"; + +/** + * ## Basic Alerts + * + * Alerts come in various contextual theme colors. + */ +export default function AlertExample() { + return ( +
+ + Alert - Primary Theme (default) -{" "} + + Example Link + + + + Alert - Secondary Theme -{" "} + + Example Link + + + + Alert - Success Theme -{" "} + + Example Link + + + + Alert - Danger Theme -{" "} + + Example Link + + + + Alert - Warning Theme -{" "} + + Example Link + + + + Alert - Info Theme -{" "} + + Example Link + + + + Alert - Light Theme -{" "} + + Example Link + + + + Alert - Dark Theme -{" "} + + Example Link + + +
+ ); +} diff --git a/components/alert/docs/02-dismissible-alert.js b/components/alert/docs/02-dismissible-alert.js new file mode 100644 index 0000000..4c59f1d --- /dev/null +++ b/components/alert/docs/02-dismissible-alert.js @@ -0,0 +1,27 @@ +import React from "react"; +import { Alert } from "shards-react"; + +/** + * ## Dismissible Alerts + * + * Dismissible alerts allow you to hide them using an `X` button. + */ +export default class DismissibleAlertExample extends React.Component { + constructor(props) { + super(props); + this.dismiss = this.dismiss.bind(this); + this.state = { visible: true }; + } + + render() { + return ( + + You can easily dismiss me using the close button → + + ); + } + + dismiss() { + this.setState({ visible: false }); + } +} diff --git a/components/alert/docs/03-self-dismissing-alert.js b/components/alert/docs/03-self-dismissing-alert.js new file mode 100644 index 0000000..7dafc8b --- /dev/null +++ b/components/alert/docs/03-self-dismissing-alert.js @@ -0,0 +1,60 @@ +import React from "react"; +import { Alert, Button } from "shards-react"; + +/** + * ## Self Dismissing Alert + * + * Self-dismissible alerts can also be created using a few state tricks. + */ +export default class SelfDismissingAlertExample extends React.Component { + constructor(props) { + super(props); + + this.interval = null; + this.state = { + visible: false, + countdown: 0, + timeUntilDismissed: 5 + }; + + this.showAlert = this.showAlert.bind(this); + this.handleTimeChange = this.handleTimeChange.bind(this); + this.clearInterval = this.clearInterval.bind(this); + } + + showAlert() { + this.clearInterval(); + this.setState({ visible: true, countdown: 0, timeUntilDismissed: 5 }); + this.interval = setInterval(this.handleTimeChange, 1000); + } + + handleTimeChange() { + if (this.state.countdown < this.state.timeUntilDismissed - 1) { + this.setState({ + ...this.state, + ...{ countdown: this.state.countdown + 1 } + }); + return; + } + + this.setState({ ...this.state, ...{ visible: false } }); + this.clearInterval(); + } + + clearInterval() { + clearInterval(this.interval); + this.interval = null; + } + + render() { + return ( +
+ + Success! This alert will will be dismissed in{" "} + {this.state.timeUntilDismissed - this.state.countdown} seconds! + + +
+ ); + } +} diff --git a/components/alert/index.js b/components/alert/index.js new file mode 100644 index 0000000..f1a2cf4 --- /dev/null +++ b/components/alert/index.js @@ -0,0 +1,3 @@ +import Alert from "./Alert"; + +export default Alert; diff --git a/components/badge/Badge.js b/components/badge/Badge.js new file mode 100644 index 0000000..5e34566 --- /dev/null +++ b/components/badge/Badge.js @@ -0,0 +1,58 @@ +import React from "react"; +import PropTypes from "prop-types"; +import classNames from "classnames"; + +/** + * Badges are really great for labels and count values. + */ +const Badge = props => { + let { tag: Tag, className, theme, pill, outline, ...attrs } = props; + + const classes = classNames( + className, + "badge", + theme && !outline && `badge-${theme}`, + outline && `badge-outline-${theme}`, + pill && "badge-pill" + ); + + Tag = attrs.href && Tag === "span" ? "a" : Tag; + + return ; +}; + +Badge.propTypes = { + /** + * The children nodes. + */ + children: PropTypes.node, + /** + * The class name. + */ + className: PropTypes.string, + /** + * The theme color. + */ + theme: PropTypes.string, + /** + * Whether it should be outlined, or not. + */ + outline: PropTypes.bool, + /** + * Whether it should be a pill, or not. + */ + pill: PropTypes.bool, + /** + * The component tag. + */ + tag: PropTypes.oneOfType([PropTypes.func, PropTypes.string]) +}; + +Badge.defaultProps = { + theme: "primary", + pill: false, + outline: false, + tag: "span" +}; + +export default Badge; diff --git a/components/badge/docs/01-basic-badges.js b/components/badge/docs/01-basic-badges.js new file mode 100644 index 0000000..e4644bd --- /dev/null +++ b/components/badge/docs/01-basic-badges.js @@ -0,0 +1,22 @@ +import React from "react"; +import { Badge } from "shards-react"; + +/** + * ## Contextual Variations + * + * The `theme` prop allows you to easily change the appearance of your badge using the main theme colors: `primary, secondary, success, danger, warning, info, light` and `dark`. + */ +export default function ContextualBadgesExamples() { + return ( +
+ Primary + Secondary + Success + Info + Warning + Danger + Light + Dark +
+ ); +} diff --git a/components/badge/docs/02-pill-badges.js b/components/badge/docs/02-pill-badges.js new file mode 100644 index 0000000..ec2efc1 --- /dev/null +++ b/components/badge/docs/02-pill-badges.js @@ -0,0 +1,36 @@ +import React from "react"; +import { Badge } from "shards-react"; + +/** + * ## Pill-Shaped Badges + * + * The pill `prop` applies a larger border radius that make your badges to look rounded. + */ +export default function PillBadgesExamples() { + return ( +
+ Primary + + Secondary + + + Success + + + Info + + + Warning + + + Danger + + + Light + + + Dark + +
+ ); +} diff --git a/components/badge/docs/03-outline-badges.js b/components/badge/docs/03-outline-badges.js new file mode 100644 index 0000000..e347617 --- /dev/null +++ b/components/badge/docs/03-outline-badges.js @@ -0,0 +1,36 @@ +import React from "react"; +import { Badge } from "shards-react"; + +/** + * ## Outline Badges + * + * The `outline` prop removes the background color and applies a thin border that make your badges to look outlined. + */ +export default function OutlineBadgesExamples() { + return ( +
+ Primary + + Secondary + + + Success + + + Info + + + Warning + + + Danger + + + Light + + + Dark + +
+ ); +} diff --git a/components/badge/docs/04-mixed-badges.js b/components/badge/docs/04-mixed-badges.js new file mode 100644 index 0000000..4c60bc1 --- /dev/null +++ b/components/badge/docs/04-mixed-badges.js @@ -0,0 +1,38 @@ +import React from "react"; +import { Badge } from "shards-react"; + +/** + * ## Mixed Effects + * + * You can also mix both the `pill` and `outline` props to get mixed results. + */ +export default function MixedBadgesExamples() { + return ( +
+ + Primary + + + Secondary + + + Success + + + Info + + + Warning + + + Danger + + + Light + + + Dark + +
+ ); +} diff --git a/components/badge/docs/05-link-badges.js b/components/badge/docs/05-link-badges.js new file mode 100644 index 0000000..30dc049 --- /dev/null +++ b/components/badge/docs/05-link-badges.js @@ -0,0 +1,36 @@ +import React from "react"; +import { Badge } from "shards-react"; + +/** + * ## Link Badges + * + * Using the `href` prop you can turn your badges into regular links. + */ +export default function LinkBadgesExamples() { + return ( +
+ Primary + + Secondary + + + Success + + + Info + + + Warning + + + Danger + + + Light + + + Dark + +
+ ); +} diff --git a/components/badge/index.js b/components/badge/index.js new file mode 100644 index 0000000..902994f --- /dev/null +++ b/components/badge/index.js @@ -0,0 +1,3 @@ +import Badge from "./Badge"; + +export default Badge; diff --git a/components/breadcrumb/Breadcrumb.js b/components/breadcrumb/Breadcrumb.js new file mode 100644 index 0000000..8d6bb68 --- /dev/null +++ b/components/breadcrumb/Breadcrumb.js @@ -0,0 +1,63 @@ +import React from "react"; +import PropTypes from "prop-types"; +import classNames from "classnames"; + +/** + * The breadcrumb component is great for indicating the current page's location within a navigational hierarchy. + */ +const Breadcrumb = props => { + const { + className, + listClassName, + children, + tag: Tag, + listTag: ListTag, + "aria-label": label, + ...attrs + } = props; + + const classes = classNames(className); + + const listClasses = classNames("breadcrumb", listClassName); + + return ( + + {children} + + ); +}; + +Breadcrumb.propTypes = { + /** + * The breadcrumb list class name. + */ + listClassName: PropTypes.string, + /** + * The class name. + */ + className: PropTypes.string, + /** + * The aria label value. + */ + "aria-label": PropTypes.string, + /** + * The children nodes. + */ + children: PropTypes.node, + /** + * The component tag name. + */ + tag: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), + /** + * The breadcrumb list tag. + */ + listTag: PropTypes.oneOfType([PropTypes.func, PropTypes.string]) +}; + +Breadcrumb.defaultProps = { + "aria-label": "breadcrumb", + tag: "nav", + listTag: "ol" +}; + +export default Breadcrumb; diff --git a/components/breadcrumb/BreadcrumbItem.js b/components/breadcrumb/BreadcrumbItem.js new file mode 100644 index 0000000..1c5a537 --- /dev/null +++ b/components/breadcrumb/BreadcrumbItem.js @@ -0,0 +1,38 @@ +import React from "react"; +import PropTypes from "prop-types"; +import classNames from "classnames"; + +const BreadcrumbItem = props => { + const { className, active, tag: Tag, ...attrs } = props; + + const classes = classNames(className, active && "active", "breadcrumb-item"); + + return ( + + ); +}; + +BreadcrumbItem.propTypes = { + /** + * Whether it is active, or not. + */ + active: PropTypes.bool, + /** + * The class name. + */ + className: PropTypes.string, + /** + * The component tag. + */ + tag: PropTypes.oneOfType([PropTypes.func, PropTypes.string]) +}; + +BreadcrumbItem.defaultProps = { + tag: "li" +}; + +export default BreadcrumbItem; diff --git a/components/breadcrumb/docs/01-basic-breadcrumbs.js b/components/breadcrumb/docs/01-basic-breadcrumbs.js new file mode 100644 index 0000000..793373c --- /dev/null +++ b/components/breadcrumb/docs/01-basic-breadcrumbs.js @@ -0,0 +1,18 @@ +import React from "react"; +import { Breadcrumb, BreadcrumbItem } from "shards-react"; + +/** + * ## Basic Example + * + * Breadcrumbs can be composed using both the `BreadCrumb` and `BreadcrumbItem` components. + */ +export default function BadgesExamples() { + return ( + + + Home + + Library + + ); +} diff --git a/components/breadcrumb/index.js b/components/breadcrumb/index.js new file mode 100644 index 0000000..de84c48 --- /dev/null +++ b/components/breadcrumb/index.js @@ -0,0 +1,4 @@ +import Breadcrumb from "./Breadcrumb"; +import BreadcrumbItem from "./BreadcrumbItem"; + +export { Breadcrumb, BreadcrumbItem }; diff --git a/components/button-group/ButtonGroup.js b/components/button-group/ButtonGroup.js new file mode 100644 index 0000000..2119907 --- /dev/null +++ b/components/button-group/ButtonGroup.js @@ -0,0 +1,39 @@ +import React from "react"; +import PropTypes from "prop-types"; +import classNames from "classnames"; + +/** + * Button groups allow you to group buttons together on a single line. + */ +const ButtonGroup = props => { + const { className, vertical, size, ...attrs } = props; + + const classes = classNames( + className, + size && `btn-group-${size}`, + vertical ? "btn-group-vertical" : "btn-group" + ); + + return
; +}; + +ButtonGroup.propTypes = { + /** + * The children nodes. + */ + children: PropTypes.node, + /** + * The class name. + */ + className: PropTypes.string, + /** + * The size. + */ + size: PropTypes.string, + /** + * Whether it is vertical, or not. + */ + vertical: PropTypes.bool +}; + +export default ButtonGroup; diff --git a/components/button-group/docs/1-basic-button-groups.js b/components/button-group/docs/1-basic-button-groups.js new file mode 100644 index 0000000..2f2b6d0 --- /dev/null +++ b/components/button-group/docs/1-basic-button-groups.js @@ -0,0 +1,17 @@ +import React from "react"; +import { Button, ButtonGroup } from "shards-react"; + +/** + * ## Basic Example + * + * You can create button groups using the `ButtonGroup` component. + */ +export default function ButtonGroupExample() { + return ( + + + + + + ); +} diff --git a/components/button-group/docs/2-size-button-groups.js b/components/button-group/docs/2-size-button-groups.js new file mode 100644 index 0000000..b832187 --- /dev/null +++ b/components/button-group/docs/2-size-button-groups.js @@ -0,0 +1,28 @@ +import React from "react"; +import { Button, ButtonGroup } from "shards-react"; + +/** + * ## Button Group Size + * + * The button group's size can be controlled via the `size` prop. The button group can be normal (default), `small` or `large`. + */ +export default function SizeButtonGroupExample() { + return ( +
+ + + + + + + + + + + + + + +
+ ); +} diff --git a/components/button-group/docs/3-vertical-button-groups.js b/components/button-group/docs/3-vertical-button-groups.js new file mode 100644 index 0000000..961ddbc --- /dev/null +++ b/components/button-group/docs/3-vertical-button-groups.js @@ -0,0 +1,16 @@ +import React from "react"; +import { Button, ButtonGroup } from "shards-react"; + +/** + * ## Vertical Button Groups + * + * You can stack button groups vertically using the `vertical` prop. + */ +export default function VerticalButtonGroupExample() { + return ( + + + + + ); +} diff --git a/components/button-group/index.js b/components/button-group/index.js new file mode 100644 index 0000000..1b7c4de --- /dev/null +++ b/components/button-group/index.js @@ -0,0 +1,3 @@ +import ButtonGroup from "./ButtonGroup"; + +export default ButtonGroup; diff --git a/components/button-toolbar/ButtonToolbar.js b/components/button-toolbar/ButtonToolbar.js new file mode 100644 index 0000000..b39d9f1 --- /dev/null +++ b/components/button-toolbar/ButtonToolbar.js @@ -0,0 +1,29 @@ +import React from "react"; +import PropTypes from "prop-types"; +import classNames from "classnames"; + +/** + * Button toolbars allow you to group a series of button or input groups on a single line. + */ +const ButtonToolbar = props => { + const { className, ...attrs } = props; + const classes = classNames(className, "btn-toolbar"); + + return
; +}; + +ButtonToolbar.propTypes = { + /** + * The class name. + */ + className: PropTypes.string, + /** + * The children nodes. + */ + children: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.node), + PropTypes.node + ]) +}; + +export default ButtonToolbar; diff --git a/components/button-toolbar/docs/01-basic-button-toolbar.js b/components/button-toolbar/docs/01-basic-button-toolbar.js new file mode 100644 index 0000000..6479f46 --- /dev/null +++ b/components/button-toolbar/docs/01-basic-button-toolbar.js @@ -0,0 +1,23 @@ +import React from "react"; +import { + Button, + ButtonGroup, + ButtonToolbar, + FormInput, + InputGroup +} from "shards-react"; + +export default function ButtonToolbarExample() { + return ( + + + + + + + + + + + ); +} diff --git a/components/button-toolbar/index.js b/components/button-toolbar/index.js new file mode 100644 index 0000000..1b9fe72 --- /dev/null +++ b/components/button-toolbar/index.js @@ -0,0 +1,3 @@ +import ButtonToolbar from "./ButtonToolbar"; + +export default ButtonToolbar; diff --git a/components/button/Button.js b/components/button/Button.js new file mode 100644 index 0000000..01ad6e0 --- /dev/null +++ b/components/button/Button.js @@ -0,0 +1,130 @@ +import React from "react"; +import PropTypes from "prop-types"; +import classNames from "classnames"; + +/** + * Buttons are Bootstrap's core component for triggering various actions. In Shards, they're very flxible, support multiple sizes, styles, states and many more. + */ +class Button extends React.Component { + constructor(props) { + super(props); + + this.onClick = this.onClick.bind(this); + } + + onClick(e) { + if (this.props.disabled) { + e.preventDefault(); + return; + } + + if (this.props.onClick) { + this.props.onClick(e); + } + } + + render() { + let { + className, + theme, + size, + pill, + outline, + squared, + active, + disabled, + innerRef, + tag: Tag, + block, + ...attrs + } = this.props; + + const classes = classNames( + className, + "btn", + theme && `btn-${outline ? "outline-" : ""}${theme}`, + size && `btn-${size}`, + pill && "btn-pill", + squared && "btn-squared", + block && "btn-block", + active && "active" + ); + + Tag = attrs.href && Tag === "button" ? "a" : Tag; + const tagType = Tag === "button" && attrs.onClick ? "button" : undefined; + + return ( + + ); + } +} + +Button.propTypes = { + /** + * The class name. + */ + className: PropTypes.string, + /** + * The children nodes. + */ + children: PropTypes.node, + /** + * The theme color. + */ + theme: PropTypes.string, + /** + * The size. + */ + size: PropTypes.string, + /** + * Whether it is outline, or not. + */ + outline: PropTypes.bool, + /** + * Whether it is pill, or not. + */ + pill: PropTypes.bool, + /** + * Whether it is squared, or not. + */ + squared: PropTypes.bool, + /** + * Whether it is active, or not. + */ + active: PropTypes.bool, + /** + * Whether it should be displayed as a block (full-width), or not. + */ + block: PropTypes.bool, + /** + * Whether it is disabled, or not. + */ + disabled: PropTypes.bool, + /** + * The component tag. + */ + tag: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), + /** + * The inner ref. + * @type {[type]} + */ + innerRef: PropTypes.oneOfType([ + PropTypes.object, + PropTypes.func, + PropTypes.string + ]) +}; + +Button.defaultProps = { + theme: "primary", + tag: "button" +}; + +export default Button; diff --git a/components/button/docs/01-basic-buttons.js b/components/button/docs/01-basic-buttons.js new file mode 100644 index 0000000..58f71d8 --- /dev/null +++ b/components/button/docs/01-basic-buttons.js @@ -0,0 +1,22 @@ +import React from "react"; +import { Button } from "shards-react"; + +/** + * ## Button Themes + * + * Using the theme prop you can easily change the appearance of your button using one the main theme colors: `primary, secondary, success, danger, warning, info, light` and `dark`. The default theme value is `primary`. + */ +export default function ButtonsExample() { + return ( +
+ + + + + + + + +
+ ); +} diff --git a/components/button/docs/02-outline-buttons.js b/components/button/docs/02-outline-buttons.js new file mode 100644 index 0000000..9d35991 --- /dev/null +++ b/components/button/docs/02-outline-buttons.js @@ -0,0 +1,36 @@ +import React from "react"; +import { Button } from "shards-react"; + +/** + * ## Outline Styled Buttons + * + * You can use the `outline` prop to remove the background color and apply a thin border that make your buttons look outlined. + */ +export default function OutlineButtonsExample() { + return ( +
+ + + + + + + + +
+ ); +} diff --git a/components/button/docs/03-pill-buttons.js b/components/button/docs/03-pill-buttons.js new file mode 100644 index 0000000..3b8a90d --- /dev/null +++ b/components/button/docs/03-pill-buttons.js @@ -0,0 +1,36 @@ +import React from "react"; +import { Button } from "shards-react"; + +/** + * ## Pill Shaped Buttons + * + * The `pill` prop applies a larger border radius that make your buttons look more rounded and pill-like. + */ +export default function PillButtonsExample() { + return ( +
+ + + + + + + + +
+ ); +} diff --git a/components/button/docs/04-mixed-buttons.js b/components/button/docs/04-mixed-buttons.js new file mode 100644 index 0000000..36fdaf8 --- /dev/null +++ b/components/button/docs/04-mixed-buttons.js @@ -0,0 +1,38 @@ +import React from "react"; +import { Button } from "shards-react"; + +/** + * ## Mixed Styles + * + * Similarly to Badges you can also mix both the `pill` and `outline` props to get a mixed "outline-pill" result. + */ +export default function OutlinePillButtonsExample() { + return ( +
+ + + + + + + + +
+ ); +} diff --git a/components/button/docs/05-squared-buttons.js b/components/button/docs/05-squared-buttons.js new file mode 100644 index 0000000..1eda161 --- /dev/null +++ b/components/button/docs/05-squared-buttons.js @@ -0,0 +1,38 @@ +import React from "react"; +import { Button } from "shards-react"; + +/** + * ## Squared Style + * + * Using the `squared` prop you can style your buttons to look, well, squared. + * + * > **Note**: The `pill` prop has priority over the `squared` prop. + */ +export default function SquaredButtonsExample() { + return ( +
+ + + + + + + + +
+ ); +} diff --git a/components/button/docs/06-outline-squared-buttons.js b/components/button/docs/06-outline-squared-buttons.js new file mode 100644 index 0000000..1c02ae7 --- /dev/null +++ b/components/button/docs/06-outline-squared-buttons.js @@ -0,0 +1,38 @@ +import React from "react"; +import { Button } from "shards-react"; + +/** + * ## Mixed Outline-Squared Style + * + * Mixing the `outline` and `squared` prop is also possible and it will render an outlined and squared button. + */ +export default function OutlineSquaredPillButtonsExample() { + return ( +
+ + + + + + + + +
+ ); +} diff --git a/components/button/docs/07-sizes-buttons.js b/components/button/docs/07-sizes-buttons.js new file mode 100644 index 0000000..a3296c8 --- /dev/null +++ b/components/button/docs/07-sizes-buttons.js @@ -0,0 +1,17 @@ +import React from "react"; +import { Button } from "shards-react"; + +/** + * ## Button Sizes + * + * Using the `size` prop you can control the size of your buttons. There are three sizes available: normal (default), `lg` for large buttons and `sm` for small buttons. + */ +export default function ButtonsSizesExample() { + return ( +
+ + + +
+ ); +} diff --git a/components/button/docs/08-active-state-buttons.js b/components/button/docs/08-active-state-buttons.js new file mode 100644 index 0000000..5da3894 --- /dev/null +++ b/components/button/docs/08-active-state-buttons.js @@ -0,0 +1,20 @@ +import React from "react"; +import { Button } from "shards-react"; + +/** + * ## Active State + * + * Controlling the active state and appearance of your buttons can be achieved via the `active` prop. + */ +export default function ActiveButtonsExample() { + return ( +
+ + +
+ ); +} diff --git a/components/button/docs/09-disabled-state-buttons.js b/components/button/docs/09-disabled-state-buttons.js new file mode 100644 index 0000000..a04b1c1 --- /dev/null +++ b/components/button/docs/09-disabled-state-buttons.js @@ -0,0 +1,20 @@ +import React from "react"; +import { Button } from "shards-react"; + +/** + * ## Disabled State + * + * Similarly to the active state, the disabled state can also be controlled via the `disabled` prop. + */ +export default function DisabledButtonsExample() { + return ( +
+ + +
+ ); +} diff --git a/components/button/docs/10-block-buttons.js b/components/button/docs/10-block-buttons.js new file mode 100644 index 0000000..2c8c906 --- /dev/null +++ b/components/button/docs/10-block-buttons.js @@ -0,0 +1,18 @@ +import React from "react"; +import { Button } from "shards-react"; + +/** + * ## Block Level Buttons + * + * Using the `block` prop you can make buttons display using the full-width of their parent element. + */ +export default function BlockButtonsExample() { + return ( +
+ + +
+ ); +} diff --git a/components/button/index.js b/components/button/index.js new file mode 100644 index 0000000..150e111 --- /dev/null +++ b/components/button/index.js @@ -0,0 +1,3 @@ +import Button from "./Button"; + +export default Button; diff --git a/components/card/Card.js b/components/card/Card.js new file mode 100644 index 0000000..9351e04 --- /dev/null +++ b/components/card/Card.js @@ -0,0 +1,64 @@ +import React from "react"; +import PropTypes from "prop-types"; +import classNames from "classnames"; + +/** + * Cards provide a flexible content container that you can use to display a variety of content using contextual background colors, headers and footers. + */ +const Card = props => { + const { + className, + innerRef, + tag: Tag, + theme, + outline, + small, + ...attrs + } = props; + + const classes = classNames( + className, + "card", + small && "card-small", + theme && `${outline ? "border" : "bg"}-${theme}` + ); + + return ; +}; + +Card.propTypes = { + /** + * The class name. + */ + className: PropTypes.string, + /** + * The theme color. + */ + theme: PropTypes.string, + /** + * Whether it is outline, or not. + */ + outline: PropTypes.bool, + /** + * The component tag. + */ + tag: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), + /** + * Whether the card is small, or not. + */ + small: PropTypes.bool, + /** + * The inner ref. + */ + innerRef: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.object, + PropTypes.func + ]) +}; + +Card.defaultProps = { + tag: "div" +}; + +export default Card; diff --git a/components/card/CardBody.js b/components/card/CardBody.js new file mode 100644 index 0000000..0c98c56 --- /dev/null +++ b/components/card/CardBody.js @@ -0,0 +1,38 @@ +import React from "react"; +import PropTypes from "prop-types"; +import classNames from "classnames"; + +const CardBody = props => { + const { className, tag: Tag, children, ...attrs } = props; + const classes = classNames(className, "card-body"); + + return ( + + {children} + + ); +}; + +CardBody.propTypes = { + /** + * The class name. + */ + className: PropTypes.string, + /** + * The children nodes. + */ + children: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.node), + PropTypes.node + ]), + /** + * The component tag. + */ + tag: PropTypes.oneOfType([PropTypes.func, PropTypes.string]) +}; + +CardBody.defaultProps = { + tag: "div" +}; + +export default CardBody; diff --git a/components/card/CardColumns.js b/components/card/CardColumns.js new file mode 100644 index 0000000..9669a9e --- /dev/null +++ b/components/card/CardColumns.js @@ -0,0 +1,27 @@ +import React from "react"; +import PropTypes from "prop-types"; +import classNames from "classnames"; + +const CardColumns = props => { + const { className, tag: Tag, ...attrs } = props; + const classes = classNames(className, "card-columns"); + + return ; +}; + +CardColumns.propTypes = { + /** + * The class name. + */ + className: PropTypes.string, + /** + * The component tag type. + */ + tag: PropTypes.oneOfType([PropTypes.func, PropTypes.string]) +}; + +CardColumns.defaultProps = { + tag: "div" +}; + +export default CardColumns; diff --git a/components/card/CardDeck.js b/components/card/CardDeck.js new file mode 100644 index 0000000..7214764 --- /dev/null +++ b/components/card/CardDeck.js @@ -0,0 +1,27 @@ +import React from "react"; +import PropTypes from "prop-types"; +import classNames from "classnames"; + +const CardDeck = props => { + const { className, tag: Tag, ...attrs } = props; + const classes = classNames(className, "card-deck"); + + return ; +}; + +CardDeck.propTypes = { + /** + * The component tag type. + */ + tag: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), + /** + * The class name. + */ + className: PropTypes.string +}; + +CardDeck.defaultProps = { + tag: "div" +}; + +export default CardDeck; diff --git a/components/card/CardFooter.js b/components/card/CardFooter.js new file mode 100644 index 0000000..f023279 --- /dev/null +++ b/components/card/CardFooter.js @@ -0,0 +1,27 @@ +import React from "react"; +import PropTypes from "prop-types"; +import classNames from "classnames"; + +const CardFooter = props => { + const { className, tag: Tag, ...attrs } = props; + const classes = classNames(className, "card-footer"); + + return ; +}; + +CardFooter.propTypes = { + /** + * The component tag type. + */ + tag: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), + /** + * The class name. + */ + className: PropTypes.string +}; + +CardFooter.defaultProps = { + tag: "div" +}; + +export default CardFooter; diff --git a/components/card/CardGroup.js b/components/card/CardGroup.js new file mode 100644 index 0000000..bb90709 --- /dev/null +++ b/components/card/CardGroup.js @@ -0,0 +1,27 @@ +import React from "react"; +import PropTypes from "prop-types"; +import classNames from "classnames"; + +const CardGroup = props => { + const { className, tag: Tag, ...attrs } = props; + const classes = classNames(className, "card-group"); + + return ; +}; + +CardGroup.propTypes = { + /** + * The class name. + */ + className: PropTypes.string, + /** + * The component tag type. + */ + tag: PropTypes.oneOfType([PropTypes.func, PropTypes.string]) +}; + +CardGroup.defaultProps = { + tag: "div" +}; + +export default CardGroup; diff --git a/components/card/CardHeader.js b/components/card/CardHeader.js new file mode 100644 index 0000000..5e0dc01 --- /dev/null +++ b/components/card/CardHeader.js @@ -0,0 +1,27 @@ +import React from "react"; +import PropTypes from "prop-types"; +import classNames from "classnames"; + +const CardHeader = props => { + const { className, tag: Tag, ...attrs } = props; + const classes = classNames(className, "card-header"); + + return ; +}; + +CardHeader.propTypes = { + /** + * The class name. + */ + className: PropTypes.string, + /** + * The component tag type. + */ + tag: PropTypes.oneOfType([PropTypes.func, PropTypes.string]) +}; + +CardHeader.defaultProps = { + tag: "div" +}; + +export default CardHeader; diff --git a/components/card/CardImg.js b/components/card/CardImg.js new file mode 100644 index 0000000..b57db7f --- /dev/null +++ b/components/card/CardImg.js @@ -0,0 +1,45 @@ +import React from "react"; +import PropTypes from "prop-types"; +import classNames from "classnames"; + +const CardImg = props => { + const { className, top, bottom, tag: Tag, ...attrs } = props; + let cardImgClass = ""; + + if (top) { + cardImgClass = "card-img-top"; + } + + if (bottom) { + cardImgClass = "card-img-bottom"; + } + + cardImgClass = classNames(className, cardImgClass); + + return ; +}; + +CardImg.propTypes = { + /** + * Whether the image is positioned at the top of the card, or not. + */ + top: PropTypes.bool, + /** + * Whether the image is positioned at the bottom of the card, or not. + */ + bottom: PropTypes.bool, + /** + * The class name. + */ + className: PropTypes.string, + /** + * The component's tag type. + */ + tag: PropTypes.oneOfType([PropTypes.func, PropTypes.string]) +}; + +CardImg.defaultProps = { + tag: "img" +}; + +export default CardImg; diff --git a/components/card/CardImgOverlay.js b/components/card/CardImgOverlay.js new file mode 100644 index 0000000..e7d46f6 --- /dev/null +++ b/components/card/CardImgOverlay.js @@ -0,0 +1,27 @@ +import React from "react"; +import PropTypes from "prop-types"; +import classNames from "classnames"; + +const CardImgOverlay = props => { + const { className, tag: Tag, ...attrs } = props; + const classes = classNames(className, "card-img-overlay"); + + return ; +}; + +CardImgOverlay.propTypes = { + /** + * The class name. + */ + className: PropTypes.string, + /** + * The component's tag type. + */ + tag: PropTypes.oneOfType([PropTypes.func, PropTypes.string]) +}; + +CardImgOverlay.defaultProps = { + tag: "div" +}; + +export default CardImgOverlay; diff --git a/components/card/CardLink.js b/components/card/CardLink.js new file mode 100644 index 0000000..9688e3d --- /dev/null +++ b/components/card/CardLink.js @@ -0,0 +1,35 @@ +import React from "react"; +import PropTypes from "prop-types"; +import classNames from "classnames"; + +const CardLink = props => { + const { className, tag: Tag, innerRef, ...attrs } = props; + const classes = classNames(className, "card-link"); + + return ; +}; + +CardLink.propTypes = { + /** + * The class name. + */ + className: PropTypes.string, + /** + * The component's tag type. + */ + tag: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), + /** + * The inner ref. + */ + innerRef: PropTypes.oneOfType([ + PropTypes.object, + PropTypes.func, + PropTypes.string + ]) +}; + +CardLink.defaultProps = { + tag: "a" +}; + +export default CardLink; diff --git a/components/card/CardSubtitle.js b/components/card/CardSubtitle.js new file mode 100644 index 0000000..218327e --- /dev/null +++ b/components/card/CardSubtitle.js @@ -0,0 +1,27 @@ +import React from "react"; +import PropTypes from "prop-types"; +import classNames from "classnames"; + +const CardSubtitle = props => { + const { className, tag: Tag, ...attrs } = props; + const classes = classNames(className, "card-subtitle", "text-muted"); + + return ; +}; + +CardSubtitle.propTypes = { + /** + * The class name. + */ + className: PropTypes.string, + /** + * The component's tag type. + */ + tag: PropTypes.oneOfType([PropTypes.func, PropTypes.string]) +}; + +CardSubtitle.defaultProps = { + tag: "h6" +}; + +export default CardSubtitle; diff --git a/components/card/CardText.js b/components/card/CardText.js new file mode 100644 index 0000000..2a026dc --- /dev/null +++ b/components/card/CardText.js @@ -0,0 +1,27 @@ +import React from "react"; +import PropTypes from "prop-types"; +import classNames from "classnames"; + +const CardText = props => { + const { className, tag: Tag, ...attrs } = props; + const classes = classNames(className, "card-text"); + + return ; +}; + +CardText.propTypes = { + /** + * The class name. + */ + className: PropTypes.string, + /** + * The tag type. + */ + tag: PropTypes.oneOfType([PropTypes.func, PropTypes.string]) +}; + +CardText.defaultProps = { + tag: "p" +}; + +export default CardText; diff --git a/components/card/CardTitle.js b/components/card/CardTitle.js new file mode 100644 index 0000000..85abb98 --- /dev/null +++ b/components/card/CardTitle.js @@ -0,0 +1,27 @@ +import React from "react"; +import PropTypes from "prop-types"; +import classNames from "classnames"; + +const CardTitle = props => { + const { className, tag: Tag, ...attributes } = props; + const classes = classNames(className, "card-title"); + + return ; +}; + +CardTitle.propTypes = { + /** + * The class name. + */ + className: PropTypes.string, + /** + * The component's tag type. + */ + tag: PropTypes.oneOfType([PropTypes.func, PropTypes.string]) +}; + +CardTitle.defaultProps = { + tag: "h5" +}; + +export default CardTitle; diff --git a/components/card/docs/01-basic-card.js b/components/card/docs/01-basic-card.js new file mode 100644 index 0000000..05c7eb5 --- /dev/null +++ b/components/card/docs/01-basic-card.js @@ -0,0 +1,30 @@ +import React from "react"; +import { + Card, + CardHeader, + CardTitle, + CardImg, + CardBody, + CardFooter, + Button +} from "shards-react"; + +/** + * ## Basic Example + * + * By default, cards fill in the full width of their parent element, however this can be customized via styling. Cards also support a large variety of content including images, links, text and more. Make sure to check out all the examples below. + */ +export default function BasicCardExample() { + return ( + + Card header + + + Lorem Ipsum +

Lorem ipsum dolor sit amet.

+ +
+ Card footer +
+ ); +} diff --git a/components/card/docs/02-card-body.js b/components/card/docs/02-card-body.js new file mode 100644 index 0000000..49da3ab --- /dev/null +++ b/components/card/docs/02-card-body.js @@ -0,0 +1,17 @@ +import React from 'react' +import { Card, CardBody } from 'shards-react' + +/** + * ## Card Body + * + * The core building block of a card is the `CardBody`. You can use it whenever you'd like to add a padded section within a card. + */ +export default function CardBodyExample() { + return ( + + + Nunc quis nisl ac justo elementum sagittis in quis justo. + + + ) +} diff --git a/components/card/docs/03-card-body-title-subtitle.js b/components/card/docs/03-card-body-title-subtitle.js new file mode 100644 index 0000000..8810392 --- /dev/null +++ b/components/card/docs/03-card-body-title-subtitle.js @@ -0,0 +1,19 @@ +import React from "react"; +import { Card, CardBody, CardTitle, CardSubtitle } from "shards-react"; + +/** + * ## Card Body Title and Subtitle + * + * You can display a title and subtitle for your card using the `CardTitle` and `CardSubtitle` components. + */ +export default function CardBodyTitleSubtitleExample() { + return ( + + + Card Title + Card subtitle + Nunc quis nisl ac justo elementum sagittis in quis justo. + + + ); +} diff --git a/components/card/docs/04-card-image.js b/components/card/docs/04-card-image.js new file mode 100644 index 0000000..99abbaa --- /dev/null +++ b/components/card/docs/04-card-image.js @@ -0,0 +1,33 @@ +import React from "react"; +import { Container, Row, Col, Card, CardBody, CardImg } from "shards-react"; + +/** + * ## Card Image + * + * Using the `CardImg` component you can place a responsive image either on top of bottom of the card using the `top` or `bottom` props. + */ +export default function CardBodyExample() { + return ( + + + + + + +

This is the body of the card.

+
+
+ + + + + +

This is the body of the card.

+
+ +
+ +
+
+ ); +} diff --git a/components/card/index.js b/components/card/index.js new file mode 100644 index 0000000..9816a14 --- /dev/null +++ b/components/card/index.js @@ -0,0 +1,29 @@ +import Card from "./Card"; +import CardBody from "./CardBody"; +import CardColumns from "./CardColumns"; +import CardFooter from "./CardFooter"; +import CardGroup from "./CardGroup"; +import CardDeck from "./CardDeck"; +import CardHeader from "./CardHeader"; +import CardImg from "./CardImg"; +import CardImgOverlay from "./CardImgOverlay"; +import CardLink from "./CardLink"; +import CardSubtitle from "./CardSubtitle"; +import CardText from "./CardText"; +import CardTitle from "./CardTitle"; + +export { + Card, + CardBody, + CardColumns, + CardFooter, + CardGroup, + CardDeck, + CardHeader, + CardImg, + CardImgOverlay, + CardLink, + CardSubtitle, + CardText, + CardTitle +}; diff --git a/components/collapse/Collapse.js b/components/collapse/Collapse.js new file mode 100644 index 0000000..1540c8a --- /dev/null +++ b/components/collapse/Collapse.js @@ -0,0 +1,147 @@ +import React from "react"; +import PropTypes from "prop-types"; +import classNames from "classnames"; +import Transition from "react-transition-group/Transition"; +import pick from "lodash.pick"; +import omit from "lodash.omit"; + +import { TIMEOUT, TRANSITION_KEYS, TRANSITION_CLASS_MAP } from "../constants"; +import { reflow, getNodeHeight } from "../utils"; + +/** + * The `Collapse` component allows you to easily toggle the visibility of your content. + */ +class Collapse extends React.Component { + constructor(props) { + super(props); + + this.state = { + height: null + }; + } + + render() { + const { + tag: Tag, + open, + className, + navbar, + children, + innerRef, + ...attrs + } = this.props; + + const { height } = this.state; + const transitionProps = pick(attrs, TRANSITION_KEYS); + const childProps = omit(attrs, TRANSITION_KEYS); + + return ( + + {status => { + const style = { + height: height || null, + display: status !== "exited" && "block" + }; + + const classes = classNames( + className, + TRANSITION_CLASS_MAP[status] || "collapse", + navbar && "navbar-collapse" + ); + + return ( + + {children} + + ); + }} + + ); + } + + onEntering(node, isAppearing) { + this.setState({ height: getNodeHeight(node) }); + this.props.onEntering(node, isAppearing); + } + + onEntered(node, isAppearing) { + this.setState({ height: null }); + this.props.onEntered(node, isAppearing); + } + + onExit(node) { + this.setState({ height: getNodeHeight(node) }); + this.props.onExit(node); + } + + onExiting(node) { + reflow(node); + this.setState({ height: 0 }); + this.props.onExiting(node); + } + + onExited(node) { + this.setState({ height: null }); + this.props.onExited(node); + } +} + +Collapse.propTypes = { + ...Transition.propTypes, + /** + * Whether it is open, or not. + */ + open: PropTypes.bool, + /** + * The children components. + */ + children: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.node), + PropTypes.node + ]), + /** + * The element tag type. + */ + tag: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), + /** + * The class name. + */ + className: PropTypes.node, + /** + * Whether it is located inside a navbar, or not. + */ + navbar: PropTypes.bool, + /** + * The inner ref. + */ + innerRef: PropTypes.oneOfType([ + PropTypes.func, + PropTypes.string, + PropTypes.object + ]) +}; + +Collapse.defaultProps = { + ...Transition.defaultProps, + open: false, + appear: false, + enter: true, + exit: true, + tag: "div", + timeout: TIMEOUT.COLLAPSE +}; + +export default Collapse; diff --git a/components/collapse/docs/01-basic-collapse.js b/components/collapse/docs/01-basic-collapse.js new file mode 100644 index 0000000..71414b2 --- /dev/null +++ b/components/collapse/docs/01-basic-collapse.js @@ -0,0 +1,36 @@ +import React from "react"; +import Button from "shards-react/button"; +import Collapse from "shards-react/collapse"; + +/** + * ## Basic Example + */ +export default class BasicCollapseExample extends React.Component { + constructor(props) { + super(props); + this.toggle = this.toggle.bind(this); + this.state = { collapse: false }; + } + + toggle() { + this.setState({ collapse: !this.state.collapse }); + } + + render() { + return ( +
+ + +
+
😍 Now you see me!
+ + In sagittis nibh non arcu viverra, nec imperdiet quam suscipit. + Sed porta eleifend scelerisque. Vestibulum dapibus quis arcu a + facilisis. + +
+
+
+ ); + } +} diff --git a/components/collapse/docs/02-initial-visibility.js b/components/collapse/docs/02-initial-visibility.js new file mode 100644 index 0000000..282b71e --- /dev/null +++ b/components/collapse/docs/02-initial-visibility.js @@ -0,0 +1,38 @@ +import React from "react"; +import Button from "shards-react/button"; +import Collapse from "shards-react/collapse"; + +/** + * ## Initial Visibility + * + * You can control the visibility of your collapsed element via the `open` prop. + */ +export default class CollapseInitialVisibilityExample extends React.Component { + constructor(props) { + super(props); + this.toggle = this.toggle.bind(this); + this.state = { collapse: true }; + } + + toggle() { + this.setState({ collapse: !this.state.collapse }); + } + + render() { + return ( +
+ + +
+
😁 I am already visible!
+ + In sagittis nibh non arcu viverra, nec imperdiet quam suscipit. + Sed porta eleifend scelerisque. Vestibulum dapibus quis arcu a + facilisis. + +
+
+
+ ); + } +} diff --git a/components/collapse/index.js b/components/collapse/index.js new file mode 100644 index 0000000..bf5c453 --- /dev/null +++ b/components/collapse/index.js @@ -0,0 +1,3 @@ +import Collapse from "./Collapse"; + +export default Collapse; diff --git a/components/constants.js b/components/constants.js new file mode 100644 index 0000000..b5949f7 --- /dev/null +++ b/components/constants.js @@ -0,0 +1,103 @@ +export const TIMEOUT = { + FADE: 150, + COLLAPSE: 350, + SHOW: 0, + HIDE: 0 +}; + +export const EVENTS = { + CLICK: ["click", "touchstart", "keyup"], + MOUSE: ["mouseenter", "mouseleave"], + FOCUS: ["focusin", "focusout"] +}; + +export const KEYCODES = { + ESC: 27, + SPACE: 32, + ENTER: 13, + TAB: 9, + UP: 38, + DOWN: 40 +}; + +export const TRANSITION_KEYS = [ + "in", + "mountOnEnter", + "unmountOnExit", + "appear", + "enter", + "exit", + "timeout", + "onEnter", + "onEntering", + "onEntered", + "onExit", + "onExiting", + "onExited" +]; + +export const TRANSITION_STATUS = { + ENTERING: "entering", + ENTERED: "entered", + EXITING: "exiting", + EXITED: "exited" +}; + +export const TRANSITION_CLASS_MAP = { + [TRANSITION_STATUS.ENTERING]: "collapsing", + [TRANSITION_STATUS.ENTERED]: "collapse show", + [TRANSITION_STATUS.EXITING]: "collapsing", + [TRANSITION_STATUS.EXITED]: "collapse" +}; + +export const POPPER_PLACEMENTS = [ + "top-start", + "top", + "top-end", + "right-start", + "right", + "right-end", + "bottom-end", + "bottom", + "bottom-start", + "left-end", + "left", + "left-start", + "auto-start", + "auto", + "auto-end" +]; + +export const DROPDOWN_POSITION_MAP = { + UP: "top", + LEFT: "left", + RIGHT: "right", + DOWN: "bottom" +}; + +export const BREAKPOINTS = ["xs", "sm", "md", "lg", "xl"]; + +/** + * FORMS + */ + +export const INPUT_TYPES = [ + "text", + "password", + "email", + "number", + "tel", + "url", + "search", + "range", + "color", + "date", + "time", + "datetime", + "datetime-local", + "month", + "week", + "file" +]; + +export const INPUT_GROUP_ADDON_TYPES = ["prepend", "append"]; diff --git a/components/container/Col.js b/components/container/Col.js new file mode 100644 index 0000000..32f8638 --- /dev/null +++ b/components/container/Col.js @@ -0,0 +1,103 @@ +import React from "react"; +import PropTypes from "prop-types"; +import classNames from "classnames"; + +import { BREAKPOINTS } from "../constants"; +import { CustomPropTypes } from "../utils"; + +const makeColumnClass = function(isXs, breakpoint, colSize) { + if (colSize === true || colSize === "") { + return isXs ? "col" : `col-${breakpoint}`; + } else if (colSize === "auto") { + return isXs ? "col-auto" : `col-${breakpoint}-auto`; + } + + return isXs ? `col-${colSize}` : `col-${breakpoint}-${colSize}`; +}; + +const Col = props => { + const { className, breakpoints, tag: Tag, ...attrs } = props; + + const columnClasses = []; + + breakpoints.forEach((breakpoint, idx) => { + let columnProp = props[breakpoint]; + + delete attrs[breakpoint]; + + if (!columnProp && columnProp !== "") { + return; + } + + const isXs = idx === 0; + + if (typeof columnProp !== "object") { + const colClass = makeColumnClass(isXs, breakpoint, columnProp); + columnClasses.push(colClass); + return; + } + + const colSizeInterfix = isXs ? "-" : `-${breakpoint}-`; + const colClass = makeColumnClass(isXs, breakpoint, columnProp.size); + + columnClasses.push( + classNames({ + [colClass]: columnProp.size || columnProp.size === "", + [`order${colSizeInterfix}${columnProp.order}`]: + columnProp.order || columnProp.order === 0, + [`offset${colSizeInterfix}${columnProp.offset}`]: + columnProp.offset || columnProp.offset === 0 + }) + ); + }); + + if (!columnClasses.length) { + columnClasses.push("col"); + } + + const classes = classNames(className, columnClasses); + + return ; +}; + +Col.propTypes = { + /** + * Col number or config object for xs viewports. + */ + xs: CustomPropTypes.column, + /** + * Col number or config object for sm viewports. + */ + sm: CustomPropTypes.column, + /** + * Col number or config object for md viewports. + */ + md: CustomPropTypes.column, + /** + * Col number or config object for lg viewports. + */ + lg: CustomPropTypes.column, + /** + * Col number or config object for xl viewports. + */ + xl: CustomPropTypes.column, + /** + * The class name. + */ + className: PropTypes.string, + /** + * The available breakpoints. + */ + breakpoints: PropTypes.array, + /** + * The component tag type. + */ + tag: PropTypes.oneOfType([PropTypes.func, PropTypes.string]) +}; + +Col.defaultProps = { + tag: "div", + breakpoints: BREAKPOINTS +}; + +export default Col; diff --git a/components/container/Container.js b/components/container/Container.js new file mode 100644 index 0000000..c271b5f --- /dev/null +++ b/components/container/Container.js @@ -0,0 +1,37 @@ +import PropTypes from "prop-types"; +import React from "react"; +import classNames from "classnames"; + +/** + * Shards React provides support for all native Bootstrap 4 layout elements including **containers**, **rows**, **columns** and **form rows** so you can use its full power while building your project's responsive layout powered by flexbox. + */ +const Container = props => { + const { className, fluid, tag: Tag, ...attrs } = props; + const classes = classNames( + className, + fluid ? "container-fluid" : "container" + ); + + return ; +}; + +Container.propTypes = { + /** + * The class name. + */ + className: PropTypes.string, + /** + * Whether it is fluid, or not. + */ + fluid: PropTypes.bool, + /** + * The component's tag type. + */ + tag: PropTypes.oneOfType([PropTypes.func, PropTypes.string]) +}; + +Container.defaultProps = { + tag: "div" +}; + +export default Container; diff --git a/components/container/Row.js b/components/container/Row.js new file mode 100644 index 0000000..3dead6a --- /dev/null +++ b/components/container/Row.js @@ -0,0 +1,49 @@ +import PropTypes from 'prop-types' +import React from 'react' +import classNames from 'classnames' + +const Row = (props) => { + const { + noGutters, + form, + className, + tag: Tag, + ...attrs + } = props + + const classes = classNames( + className, + noGutters ? 'no-gutters' : null, + form ? 'form-row' : 'row' + ) + + return () +} + +Row.propTypes = { + /** + * The class name. + */ + className: PropTypes.string, + /** + * Whether it has gutters, or not. + */ + noGutters: PropTypes.bool, + /** + * Whether it is located inside a form, or not. + */ + form: PropTypes.bool, + /** + * The component's tag type. + */ + tag: PropTypes.oneOfType([ + PropTypes.func, + PropTypes.string + ]) +} + +Row.defaultProps = { + tag: 'div' +} + +export default Row diff --git a/components/container/docs/01-basic-layout.js b/components/container/docs/01-basic-layout.js new file mode 100644 index 0000000..3bbedd1 --- /dev/null +++ b/components/container/docs/01-basic-layout.js @@ -0,0 +1,52 @@ +import React from "react"; +import { Container, Row, Col } from "shards-react"; + +/** + * ## Basic Example + */ +export default class Example extends React.Component { + render() { + return ( + + + 1 / 12 + 2 / 12 + 3 / 12 + 4 / 12 + 5 / 12 + 6 / 12 + 7 / 12 + 8 / 12 + 9 / 12 + 10 / 12 + 11 / 12 + 12 / 12 + + + + 1 / 6 + 2 / 6 + 3 / 6 + 4 / 6 + 5 / 6 + 6 / 6 + + + + 1 / 3 + 2 / 3 + 3 / 3 + + + + 1 / 2 + 2 / 2 + + + + 1 / 1 + + + ); + } +} diff --git a/components/container/docs/02-layout-explained.js b/components/container/docs/02-layout-explained.js new file mode 100644 index 0000000..cb1c7ad --- /dev/null +++ b/components/container/docs/02-layout-explained.js @@ -0,0 +1,50 @@ +import React from "react"; +import { Container, Row, Col } from "shards-react"; + +/** + * ## Containers + * + * Containers are the most fundamental and required layout element for your application or website's layout. You can use the `Container` component for a fixed container, and apply the `fluid` prop for a fluid container. + * + * ## Rows + * + * The `Row` component must be placed inside a `Container` component and it's used to define a row of columns. You can also apply the `form` prop onto a `Row` component to turn it into a `form-row` better suited for building form layouts that help you create a layout with more compact margins. + * + * ## Columns + * + * The `Col` component is used to represent a column and must be placed inside a `Row` component. + */ +export default class Example extends React.Component { + render() { + return ( + + + + sm=12 lg=6 + + + sm=12 lg=6 + + + + + + sm=12 md=4 lg=3 + + + sm=12 md=4 lg=6 + + + sm=12 md=4 lg=3 + + + + + + .col-sm-8 .order-sm-2 .offset-sm-2 + + + + ); + } +} diff --git a/components/container/index.js b/components/container/index.js new file mode 100644 index 0000000..402d13f --- /dev/null +++ b/components/container/index.js @@ -0,0 +1,5 @@ +import Container from "./Container"; +import Row from "./Row"; +import Col from "./Col"; + +export { Container, Row, Col }; diff --git a/components/datepicker/DatePicker.css b/components/datepicker/DatePicker.css new file mode 100644 index 0000000..2d1700a --- /dev/null +++ b/components/datepicker/DatePicker.css @@ -0,0 +1,132 @@ +/** + * Datepicker Styles + */ + +.react-datepicker { + border: none; +} + +.react-datepicker-popper, +.react-datepicker { + z-index: 1000; +} + +.react-datepicker__month-container { + border: none; + box-shadow: 0 0.5rem 4rem rgba(0, 0, 0, 0.11), 0 10px 20px rgba(0, 0, 0, 0.05), + 0 2px 3px rgba(0, 0, 0, 0.06); +} + +.react-datepicker__header { + border: none; + background: #fff; + padding-top: 20px; +} + +.react-datepicker__day-name, +.react-datepicker__day, +.react-datepicker__time-name, +.react-datepicker__current-month { + font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, + Helvetica Neue, Arial, sans-serif; + color: #5a6169; +} + +.react-datepicker__day--disabled { + color: #ddd; +} + +.react-datepicker__day--disabled:hover { + background: transparent !important; +} + +.react-datepicker__day { + transition: all 0.25s cubic-bezier(0.27, 0.01, 0.38, 1.06); +} + +.react-datepicker__day:hover { + background-color: #eceeef; +} + +.react-datepicker__current-month { + font-weight: 600; +} + +.react-datepicker__day, +.react-datepicker__day:hover, +.react-datepicker__day--keyboard-selected { + border-radius: 50%; +} + +.react-datepicker__day--highlighted { + background: #e6f2ff; +} + +.react-datepicker__day--keyboard-selected, +.react-datepicker__day--selected { + color: #fff; + background: #007bff; +} + +.react-datepicker__day--keyboard-selected:hover, +.react-datepicker__day--selected:hover { + background: #006fe6; +} + +.react-datepicker__header, +.react-datepicker__month-container { + border-bottom-left-radius: 0.375rem; + border-bottom-right-radius: 0.375rem; +} + +.react-datepicker__header { + border-top-left-radius: 0.375rem; + border-top-right-radius: 0.375rem; +} + +.react-datepicker { + border-radius: 0.375rem; +} + +.react-datepicker__navigation { + top: 25px; +} + +.react-datepicker__triangle:before { + border-bottom-color: #e3e3e3 !important; +} + +.react-datepicker__month { + padding: 10px 15px; +} + +/* Datepicker & Input Groups */ + +.input-group > .react-datepicker-wrapper .form-control { + position: relative; +} + +.input-group > .react-datepicker-wrapper:not(:first-child) .form-control { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.input-group > .react-datepicker-wrapper:not(:last-child) .form-control { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.input-group > .react-datepicker-wrapper ~ .react-datepicker-wrapper input { + margin-left: -1px; +} + +.input-group + > .react-datepicker-wrapper + ~ .react-datepicker-wrapper + ~ .input-group-append { + margin-left: -2px; +} + +.input-group > .react-datepicker-wrapper .form-control:focus { + z-index: 3; +} diff --git a/components/datepicker/DatePicker.js b/components/datepicker/DatePicker.js new file mode 100644 index 0000000..4fde3e2 --- /dev/null +++ b/components/datepicker/DatePicker.js @@ -0,0 +1,36 @@ +import React from "react"; +import PropTypes from "prop-types"; +import ReactDatePicker from "react-datepicker"; +import classNames from "classnames"; + +import "react-datepicker/dist/react-datepicker.css"; +import "./DatePicker.css"; + +/** + * The `DatePicker` component is a wrapper for the [react-datepicker](https://github.com/Hacker0x01/react-datepicker) component. + * + * > **Note:** Make sure to check out its official documentation for the complete list of available props. + */ +const DatePicker = props => { + const { className, size, ...attrs } = props; + const classes = classNames( + className, + "form-control", + size && `form-control-${size}` + ); + + if (!attrs.dropdownMode) { + attrs.dropdownMode = "select"; + } + + return ; +}; + +DatePicker.propTypes = { + ...ReactDatePicker.propTypes, + ...{ + size: PropTypes.string + } +}; + +export default DatePicker; diff --git a/components/datepicker/docs/01-basic-datepicker.js b/components/datepicker/docs/01-basic-datepicker.js new file mode 100644 index 0000000..0acd949 --- /dev/null +++ b/components/datepicker/docs/01-basic-datepicker.js @@ -0,0 +1,40 @@ +import React from "react"; +import { DatePicker } from "shards-react"; + +/** + * ## Basic Example + * + * You can create a datepicker using the `DatePicker` component. + */ +export default class DatepickerExample extends React.Component { + constructor(props) { + super(props); + + this.handleOnChange = this.handleOnChange.bind(this); + this.state = { + startDate: new Date() + }; + } + + handleOnChange(val) { + this.setState({ + startDate: new Date(val) + }); + } + + render() { + return ( +
+

+ Selected date: {this.state.startDate.toString()} +

+ +
+ ); + } +} diff --git a/components/datepicker/docs/02-datepicker-disabled-dates.js b/components/datepicker/docs/02-datepicker-disabled-dates.js new file mode 100644 index 0000000..8cf064f --- /dev/null +++ b/components/datepicker/docs/02-datepicker-disabled-dates.js @@ -0,0 +1,44 @@ +import React from "react"; +import { DatePicker } from "shards-react"; + +/** + * ## Excluded Dates + * + * You can disable certain dates using the `excludeDates` prop. + */ +export default class DatepickerExcludedDatesExample extends React.Component { + constructor(props) { + super(props); + + this.handleOnChange = this.handleOnChange.bind(this); + this.state = { + startDate: new Date() + }; + } + + handleOnChange(val) { + this.setState({ + startDate: new Date(val) + }); + } + + render() { + const today = new Date(); + const yesterday = today.setDate(today.getDate() - 1); + + return ( +
+

+ Selected date: {this.state.startDate.toString()} +

+ +
+ ); + } +} diff --git a/components/datepicker/docs/03-datepicker-highlighted-dates.js b/components/datepicker/docs/03-datepicker-highlighted-dates.js new file mode 100644 index 0000000..8700ea0 --- /dev/null +++ b/components/datepicker/docs/03-datepicker-highlighted-dates.js @@ -0,0 +1,45 @@ +import React from "react"; +import { DatePicker } from "shards-react"; + +/** + * ## Highlighted Dates + * + * You can highlight certain dates using the `highlightDates` prop. + */ +export default class DatepickerHighlightedDatesExample extends React.Component { + constructor(props) { + super(props); + + this.handleOnChange = this.handleOnChange.bind(this); + this.state = { + startDate: new Date() + }; + } + + handleOnChange(val) { + this.setState({ + startDate: new Date(val) + }); + } + + render() { + const today = new Date(); + const yesterday = (d => new Date(d.setDate(d.getDate() - 1)))(new Date()); + const twoDaysAgo = (d => new Date(d.setDate(d.getDate() - 2)))(new Date()); + + return ( +
+

+ Selected date: {this.state.startDate.toString()} +

+ +
+ ); + } +} diff --git a/components/datepicker/index.js b/components/datepicker/index.js new file mode 100644 index 0000000..48afa5c --- /dev/null +++ b/components/datepicker/index.js @@ -0,0 +1,3 @@ +import DatePicker from "./Datepicker"; + +export default DatePicker; diff --git a/components/dropdown/Dropdown.js b/components/dropdown/Dropdown.js new file mode 100644 index 0000000..def4ce6 --- /dev/null +++ b/components/dropdown/Dropdown.js @@ -0,0 +1,197 @@ +import React from "react"; +import ReactDOM from "react-dom"; +import PropTypes from "prop-types"; +import classNames from "classnames"; +import { Manager } from "react-popper"; +import omit from "lodash.omit"; + +import { DropdownContext } from "./DropdownContext"; +import { KEYCODES, EVENTS } from "./../constants"; + +/** + * You can use dropdowns to display accessible contextual overlays for displaying lists of links and more. + */ +class Dropdown extends React.Component { + constructor(props) { + super(props); + + this.handleListeners = this.handleListeners.bind(this); + this.addListeners = this.addListeners.bind(this); + this.removeListeners = this.removeListeners.bind(this); + this.handleDocumentClick = this.handleDocumentClick.bind(this); + this.getContainer = this.getContainer.bind(this); + this.toggle = this.toggle.bind(this); + } + + componentDidMount() { + this.handleListeners(); + } + + componentWillUnmount() { + this.removeListeners(); + } + + componentDidUpdate(prevProps) { + if (this.props.open !== prevProps.open) { + this.handleListeners(); + } + } + + handleListeners() { + if (this.props.open) { + this.addListeners(); + return; + } + + this.removeListeners(); + } + + addListeners() { + EVENTS.CLICK.forEach(e => + document.addEventListener(e, this.handleDocumentClick, true) + ); + } + + removeListeners() { + EVENTS.CLICK.forEach(e => + document.removeEventListener(e, this.handleDocumentClick, true) + ); + } + + getContainer() { + return ReactDOM.findDOMNode(this); // eslint-disable-line react/no-find-dom-node + } + + handleDocumentClick(e) { + if ( + e && + (e.which === 3 || (e.type === "keyup" && e.which !== KEYCODES.TAB)) + ) + return; + const container = this.getContainer(); + + if ( + container.contains(e.target) && + container !== e.target && + (e.type !== "keyup" || e.which === KEYCODES.TAB) + ) { + return; + } + + this.toggle(e); + } + + toggle(e) { + if (this.props.disabled) { + return e && e.preventDefault(); + } + + return this.props.toggle(e); + } + + render() { + const props = omit(this.props, [ + "toggle", + "disabled", + "inNavbar", + "direction" + ]); + + const { + className, + children, + dropup, + open, + group, + size, + nav, + setActiveFromChild, + active, + addonType, + ...attrs + } = props; + + const direction = + this.props.direction === "down" && dropup ? "up" : this.props.direction; + + attrs.tag = attrs.tag || (nav ? "li" : "div"); + + let subItemIsActive = false; + if (setActiveFromChild) { + React.Children.map( + this.props.children[1].props.children, + dropdownItem => { + if (dropdownItem && dropdownItem.props.active) subItemIsActive = true; + } + ); + } + + const classes = classNames( + className, + direction !== "down" && `drop${direction}`, + nav && active && "active", + setActiveFromChild && subItemIsActive && "active", + addonType && `input-group-${addonType}`, + group && "btn-group", + !!size && `btn-group-${size}`, + !group && !addonType && "dropdown", + open && "show", + nav && "nav-item" + ); + + const toggle = this.toggle; + + return ( + + + + {() =>
{children}
} +
+
+
+ ); + } +} + +Dropdown.propTypes = { + /** + * Whether it is open, or not. + */ + open: PropTypes.bool, + /** + * Whether it is disabled, or not. + */ + disabled: PropTypes.bool, + /** + * The toggle function. + */ + toggle: PropTypes.func, + /** + * Whether it is located inside a navbar, or not. + */ + inNavbar: PropTypes.bool, + /** + * Whether it is a drop-up, or not. + */ + dropup: PropTypes.bool, + /** + * The component's tag type. + */ + tag: PropTypes.string, + /** + * Whether it is located inside a Nav, or not. + */ + nav: PropTypes.bool, + /** + * The direction. + */ + direction: PropTypes.oneOf(["up", "down", "left", "right"]) +}; + +Dropdown.defaultProps = { + open: false, + direction: "down", + nav: false +}; + +export default Dropdown; diff --git a/components/dropdown/DropdownContext.js b/components/dropdown/DropdownContext.js new file mode 100644 index 0000000..dd73691 --- /dev/null +++ b/components/dropdown/DropdownContext.js @@ -0,0 +1,8 @@ +import React from "react"; + +export const DropdownContext = React.createContext({ + toggle: function() {}, + open: false, + direction: "down", + nav: false +}); diff --git a/components/dropdown/DropdownItem.js b/components/dropdown/DropdownItem.js new file mode 100644 index 0000000..2379f92 --- /dev/null +++ b/components/dropdown/DropdownItem.js @@ -0,0 +1,128 @@ +import React from "react"; +import PropTypes from "prop-types"; +import classNames from "classnames"; +import omit from "lodash.omit"; + +import { DropdownContext } from "./DropdownContext"; + +class DropdownItem extends React.Component { + constructor(props) { + super(props); + + this.onClick = this.onClick.bind(this); + this.getTabIndex = this.getTabIndex.bind(this); + } + + onClick(e) { + if (this.props.disabled || this.props.header || this.props.divider) { + e.preventDefault(); + return; + } + + if (this.props.onClick) { + this.props.onClick(e); + } + + if (this.props.toggle) { + this.context.toggle(e); + } + } + + getTabIndex() { + if (this.props.disabled || this.props.header || this.props.divider) { + return "-1"; + } + + return "0"; + } + + render() { + let { className, divider, tag: Tag, header, active, ...attrs } = omit( + this.props, + ["toggle"] + ); + + const tabIndex = this.getTabIndex(); + + const classes = classNames( + className, + attrs.disabled && "disabled", + !divider && !header && "dropdown-item", + header && "dropdown-header", + divider && "dropdown-divider", + active && "active" + ); + + if (Tag === "button") { + if (header) { + Tag = "h6"; + } else if (divider) { + Tag = "div"; + } else if (attrs.href) { + Tag = "a"; + } + } + + return ( + + ); + } +} + +DropdownItem.propTypes = { + /** + * The children nodes. + */ + children: PropTypes.node, + /** + * Whether it is active, or not. + */ + active: PropTypes.bool, + /** + * Whether it is disabled, or not. + */ + disabled: PropTypes.bool, + /** + * Whether it is a divider, or not. + */ + divider: PropTypes.bool, + /** + * Whether it is a dropdown header item, or not. + */ + header: PropTypes.bool, + /** + * The function that should be triggered on click. + */ + onClick: PropTypes.func, + /** + * The class name. + */ + className: PropTypes.string, + /** + * Whether it should toggle the dropdown, or not. + */ + toggle: PropTypes.bool, + /** + * The component's tag type. + */ + tag: PropTypes.oneOfType([PropTypes.func, PropTypes.string]) +}; + +DropdownItem.defaultProps = { + tag: "button", + toggle: true +}; + +DropdownItem.contextType = DropdownContext; + +export default DropdownItem; diff --git a/components/dropdown/DropdownMenu.js b/components/dropdown/DropdownMenu.js new file mode 100644 index 0000000..aade2c2 --- /dev/null +++ b/components/dropdown/DropdownMenu.js @@ -0,0 +1,116 @@ +import React from "react"; +import PropTypes from "prop-types"; +import classNames from "classnames"; +import { Popper } from "react-popper"; + +import { DropdownContext } from "./DropdownContext"; +import { DROPDOWN_POSITION_MAP } from "../constants"; + +class DropdownMenu extends React.Component { + render() { + const { + className, + children, + right, + tag: Tag, + flip, + small, + modifiers, + persist, + ...attrs + } = this.props; + + const classes = classNames( + className, + "dropdown-menu", + small && "dropdown-menu-small", + right && "dropdown-menu-right", + this.context.open && "show" + ); + + if (persist || (this.context.open && !this.context.inNavbar)) { + const pos1 = + DROPDOWN_POSITION_MAP[this.context.direction.toUpperCase()] || "bottom"; + const pos2 = right ? "end" : "start"; + attrs.placement = `${pos1}-${pos2}`; + attrs.component = Tag; + attrs.modifiers = !flip + ? { + ...modifiers, + ...{ + flip: { + enabled: false + } + } + } + : modifiers; + + return ( + + {({ ref, placement }) => ( +
+ {children} +
+ )} +
+ ); + } + + return ( + + {children} + + ); + } +} + +DropdownMenu.propTypes = { + /** + * The component tag. + */ + tag: PropTypes.string, + /** + * The children nodes. + */ + children: PropTypes.node.isRequired, + /** + * Whether it is positioned on the right side, or not. + */ + right: PropTypes.bool, + /** + * Whether it should flip, or not. + */ + flip: PropTypes.bool, + /** + * Whether the dropdown is small, or not. + */ + small: PropTypes.bool, + /** + * The modifiers config object. + */ + modifiers: PropTypes.object, + /** + * The class name. + */ + className: PropTypes.string, + /** + * Whether it should persist, or not. + */ + persist: PropTypes.bool +}; + +DropdownMenu.defaultProps = { + tag: "div", + flip: true +}; + +DropdownMenu.contextType = DropdownContext; + +export default DropdownMenu; diff --git a/components/dropdown/DropdownToggle.js b/components/dropdown/DropdownToggle.js new file mode 100644 index 0000000..daaadfd --- /dev/null +++ b/components/dropdown/DropdownToggle.js @@ -0,0 +1,143 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { Reference } from "react-popper"; +import classNames from "classnames"; +import Button from "../button"; + +import { DropdownContext } from "./DropdownContext"; + +class DropdownToggle extends React.Component { + constructor(props) { + super(props); + + this.onClick = this.onClick.bind(this); + } + + onClick(e) { + if (this.props.disabled) { + e.preventDefault(); + return; + } + + if (this.props.nav && !this.props.tag) { + e.preventDefault(); + } + + if (this.props.onClick) { + this.props.onClick(e); + } + + this.context.toggle(e); + } + + render() { + const { className, theme, caret, split, nav, tag, ...attrs } = this.props; + const ariaLabel = attrs["aria-label"] || "Toggle Dropdown"; + const classes = classNames( + className, + (caret || split) && "dropdown-toggle", + split && "dropdown-toggle-split", + nav && "nav-link" + ); + + const children = attrs.children || ( + {ariaLabel} + ); + + let Tag; + + if (nav && !tag) { + Tag = "a"; + attrs.href = "#"; + } else if (!tag) { + Tag = Button; + attrs.theme = theme; + } else { + Tag = tag; + } + + if (this.context.inNavbar) { + return ( + + {() => ( + + {children} + + )} + + ); + } + + return ( + + {() => ( + + {children} + + )} + + ); + } +} + +DropdownToggle.propTypes = { + /** + * Whether it should display a caret, or not. + */ + caret: PropTypes.bool, + /** + * The theme color. + */ + theme: PropTypes.string, + /** + * The children nodes. + */ + children: PropTypes.node, + /** + * The class name. + */ + className: PropTypes.string, + /** + * Whether it is disabled, or not. + */ + disabled: PropTypes.bool, + /** + * The function that should be triggered on click. + */ + onClick: PropTypes.func, + /** + * The aria-haspopup attribute. + */ + "aria-haspopup": PropTypes.bool, + /** + * Whether it is split, or not. + */ + split: PropTypes.bool, + /** + * Whether it is located inside a nav, or not. + */ + nav: PropTypes.bool, + /** + * The component's tag type. + */ + tag: PropTypes.oneOfType([PropTypes.func, PropTypes.string]) +}; + +DropdownToggle.defaultProps = { + "aria-haspopup": true, + theme: "primary" +}; + +DropdownToggle.contextType = DropdownContext; + +export default DropdownToggle; diff --git a/components/dropdown/docs/01-basic-dropdown.js b/components/dropdown/docs/01-basic-dropdown.js new file mode 100644 index 0000000..9e727e7 --- /dev/null +++ b/components/dropdown/docs/01-basic-dropdown.js @@ -0,0 +1,39 @@ +import React from "react"; +import { + Dropdown, + DropdownToggle, + DropdownMenu, + DropdownItem +} from "shards-react"; + +/** + * ## Basic Example + * + * You can create dropdowns using the `Dropdown` component. + */ +export default class DropdownExample extends React.Component { + constructor(props) { + super(props); + this.toggle = this.toggle.bind(this); + this.state = { open: false }; + } + + toggle() { + this.setState(prevState => { + return { open: !prevState.open }; + }); + } + + render() { + return ( + + Dropdown + + Action + Another action + Something else here + + + ); + } +} diff --git a/components/dropdown/docs/02-dropdown-themes.js b/components/dropdown/docs/02-dropdown-themes.js new file mode 100644 index 0000000..d789e40 --- /dev/null +++ b/components/dropdown/docs/02-dropdown-themes.js @@ -0,0 +1,39 @@ +import React from "react"; +import { + Dropdown, + DropdownToggle, + DropdownMenu, + DropdownItem +} from "shards-react"; + +/** + * ## Theme Color + * + * Changing the theme color for the `DropdownToggle` component can be achieved via the `theme` prop. + */ +export default class DropdownThemeExample extends React.Component { + constructor(props) { + super(props); + this.toggle = this.toggle.bind(this); + this.state = { open: false }; + } + + toggle() { + this.setState(prevState => { + return { open: !prevState.open }; + }); + } + + render() { + return ( + + Dropdown + + Action + Another action + Something else here + + + ); + } +} diff --git a/components/dropdown/docs/03-dropdown-positioning.js b/components/dropdown/docs/03-dropdown-positioning.js new file mode 100644 index 0000000..c6773e6 --- /dev/null +++ b/components/dropdown/docs/03-dropdown-positioning.js @@ -0,0 +1,39 @@ +import React from "react"; +import { + Dropdown, + DropdownToggle, + DropdownMenu, + DropdownItem +} from "shards-react"; + +/** + * ## Positioning + * + * By default `DropdownMenu`s are left aligned. However, you can change this by applying a right alignment using the `right` prop. + */ +export default class DropdownPositionExample extends React.Component { + constructor(props) { + super(props); + this.toggle = this.toggle.bind(this); + this.state = { open: false }; + } + + toggle() { + this.setState(prevState => { + return { open: !prevState.open }; + }); + } + + render() { + return ( + + Right Align + + Action + Another action + Something else here + + + ); + } +} diff --git a/components/dropdown/docs/04-dropup.js b/components/dropdown/docs/04-dropup.js new file mode 100644 index 0000000..e34b43f --- /dev/null +++ b/components/dropdown/docs/04-dropup.js @@ -0,0 +1,39 @@ +import React from "react"; +import { + Dropdown, + DropdownToggle, + DropdownMenu, + DropdownItem +} from "shards-react"; + +/** + * ## Drop-up + * + * Turning dropdown menus into drop-up menus can be easily achieved using the `dropup` prop applied on the `Dropdown` component. + */ +export default class DropupExample extends React.Component { + constructor(props) { + super(props); + this.toggle = this.toggle.bind(this); + this.state = { open: false }; + } + + toggle() { + this.setState(prevState => { + return { open: !prevState.open }; + }); + } + + render() { + return ( + + Dropup + + Action + Another action + Something else here + + + ); + } +} diff --git a/components/dropdown/docs/05-split-buttons.js b/components/dropdown/docs/05-split-buttons.js new file mode 100644 index 0000000..9e5fcef --- /dev/null +++ b/components/dropdown/docs/05-split-buttons.js @@ -0,0 +1,41 @@ +import React from "react"; +import { + Dropdown, + DropdownToggle, + DropdownMenu, + DropdownItem, + Button +} from "shards-react"; + +/** + * ## Split Buttons + * + * Using the `group` prop applied on the `Dropdown` component and the `split` prop applied on the `DropdownToggle` component you can create split-type dropdowns. + */ +export default class DropdownSplitExample extends React.Component { + constructor(props) { + super(props); + this.toggle = this.toggle.bind(this); + this.state = { open: false }; + } + + toggle() { + this.setState(prevState => { + return { open: !prevState.open }; + }); + } + + render() { + return ( + + + + + Action + Another action + Something else here + + + ); + } +} diff --git a/components/dropdown/docs/06-dropdown-sizing.js b/components/dropdown/docs/06-dropdown-sizing.js new file mode 100644 index 0000000..0fce7c4 --- /dev/null +++ b/components/dropdown/docs/06-dropdown-sizing.js @@ -0,0 +1,79 @@ +import React from "react"; +import { + Dropdown, + DropdownToggle, + DropdownMenu, + DropdownItem +} from "shards-react"; + +/** + * ## Sizing + * + * Using the `size` prop on the `Dropdown` component you can control the dropdown button's size. + */ +export default class DropdownSizeExample extends React.Component { + constructor(props) { + super(props); + this.toggle = this.toggle.bind(this); + this.state = { + dropdown1: false, + dropdown2: false, + dropdown3: false + }; + } + + toggle(nr) { + this.setState(prevState => { + const newState = {}; + newState[`dropdown${nr}`] = !prevState[`dropdown${nr}`]; + return { ...prevState, ...newState }; + }); + } + + render() { + return ( +
+ this.toggle(1)} + size="lg" + className="mr-2" + > + Large + + Action + Another action + Something else here + + + + this.toggle(2)} + className="mr-2" + > + Normal + + Action + Another action + Something else here + + + + this.toggle(3)} + size="sm" + className="mr-2" + > + Small + + Action + Another action + Something else here + + +
+ ); + } +} diff --git a/components/dropdown/index.js b/components/dropdown/index.js new file mode 100644 index 0000000..c118ec5 --- /dev/null +++ b/components/dropdown/index.js @@ -0,0 +1,6 @@ +import Dropdown from "./Dropdown"; +import DropdownToggle from "./DropdownToggle"; +import DropdownMenu from "./DropdownMenu"; +import DropdownItem from "./DropdownItem"; + +export { Dropdown, DropdownToggle, DropdownMenu, DropdownItem }; diff --git a/components/fade/Fade.js b/components/fade/Fade.js new file mode 100644 index 0000000..bc2999b --- /dev/null +++ b/components/fade/Fade.js @@ -0,0 +1,75 @@ +import React from "react"; +import PropTypes from "prop-types"; +import classNames from "classnames"; +import Transition from "react-transition-group/Transition"; +import omit from "lodash.omit"; +import pick from "lodash.pick"; + +import { TIMEOUT, TRANSITION_KEYS } from "./../constants"; + +/** + * The `Fade` component allows you to easily fade in and out content and is powered by [react-transition-group](https://github.com/reactjs/react-transition-group). + */ +const Fade = props => { + const { + tag: Tag, + baseClass, + baseClassActive, + className, + children, + innerRef, + ...attrs + } = props; + + const transitionProps = pick(attrs, TRANSITION_KEYS); + const childProps = omit(attrs, TRANSITION_KEYS); + + return ( + + {status => { + const isActive = status === "entered"; + const classes = classNames( + className, + baseClass, + isActive && baseClassActive + ); + return ( + + {children} + + ); + }} + + ); +}; + +Fade.propTypes = { + ...Transition.propTypes, + tag: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), + baseClass: PropTypes.string, + baseClassActive: PropTypes.string, + className: PropTypes.string, + innerRef: PropTypes.oneOfType([ + PropTypes.object, + PropTypes.string, + PropTypes.func + ]), + children: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.node), + PropTypes.node + ]) +}; + +Fade.defaultProps = { + ...Transition.defaultProps, + tag: "div", + baseClass: "fade", + baseClassActive: "show", + timeout: TIMEOUT.FADE, + appear: true, + enter: true, + exit: true, + in: true +}; + +export default Fade; diff --git a/components/fade/docs/01-basic-fade.js b/components/fade/docs/01-basic-fade.js new file mode 100644 index 0000000..9a5550c --- /dev/null +++ b/components/fade/docs/01-basic-fade.js @@ -0,0 +1,34 @@ +import React from "react"; +import { Fade, Button } from "shards-react"; + +/** + * ## Basic Example + */ +export default class BasicFadeExample extends React.Component { + constructor(props) { + super(props); + this.toggle = this.toggle.bind(this); + this.state = { + visible: true + }; + } + + toggle() { + this.setState({ + visible: !this.state.visible + }); + } + + render() { + return ( +
+ + + Etiam semper convallis tortor, in euismod orci vehicula sit amet. + +
+ ); + } +} diff --git a/components/fade/index.js b/components/fade/index.js new file mode 100644 index 0000000..09f726e --- /dev/null +++ b/components/fade/index.js @@ -0,0 +1,3 @@ +import Fade from "./Fade"; + +export default Fade; diff --git a/components/form-checkbox/FormCheckbox.js b/components/form-checkbox/FormCheckbox.js new file mode 100644 index 0000000..9382c58 --- /dev/null +++ b/components/form-checkbox/FormCheckbox.js @@ -0,0 +1,111 @@ +import React from "react"; +import PropTypes from "prop-types"; +import classNames from "classnames"; +import shortid from "shortid"; + +/** + * The `FormCheckbox` component is a wrapper over Bootstrap's [custom checkbox component](https://getbootstrap.com/docs/4.1/components/forms/#checkboxes-and-radios-1). + */ +class FormCheckbox extends React.Component { + constructor(props) { + super(props); + + this.getRef = this.getRef.bind(this); + } + + getRef(ref) { + if (this.props.innerRef) { + this.props.innerRef(ref); + } + + this.ref = ref; + } + + render() { + const { + className, + children, + inline, + valid, + invalid, + innerRef, + toggle, + small, + id: _id, + ...attrs + } = this.props; + + const labelClasses = classNames( + className, + "custom-control", + !toggle ? "custom-checkbox" : "custom-toggle", + toggle && small && "custom-toggle-sm", + inline && "custom-control-inline", + valid && "is-valid", + invalid && "is-invalid" + ); + + const inputClasses = classNames( + "custom-control-input", + valid && "is-valid", + invalid && "is-invalid" + ); + + const id = _id || `dr-checkbox-${shortid.generate()}`; + + return ( +