├── .gitignore
├── .babelrc
├── dist
├── css
│ ├── ProgressBar.css.map
│ ├── Pagination.css.map
│ ├── ProgressBar.css
│ ├── ButtonGroup.css.map
│ ├── Panel.css.map
│ ├── Tabs.css.map
│ ├── ColorPicker.css.map
│ ├── Pagination.css
│ ├── LoadingSpinner.css.map
│ ├── ButtonGroup.css
│ ├── Switch.css.map
│ ├── Button.css.map
│ ├── Panel.css
│ ├── ColorPicker.css
│ ├── Tabs.css
│ ├── LoadingSpinner.css
│ ├── Switch.css
│ └── Button.css
├── ButtonGroup.js
├── ProgressBar.js
├── LoadingSpinner.js
├── Switch.js
├── Tabs.js
├── VimeoVideo.js
├── Button.js
├── YoutubeVideo.js
├── Panel.js
├── Pagination.js
└── ColorPicker.js
├── .scss-lint.yml
├── src
├── stories
│ ├── ColorPicker.js
│ ├── YoutubeVideo.js
│ ├── ProgressBar.js
│ ├── VimeoVideo.js
│ ├── Tabs.js
│ ├── ButtonGroup.js
│ ├── Switch.js
│ ├── LoadingSpinner.js
│ ├── Button.js
│ ├── Panel.js
│ └── Pagination.js
├── ProgressBar.jsx
├── ButtonGroup.jsx
├── LoadingSpinner.jsx
├── Switch.jsx
├── Button.jsx
├── Tabs.jsx
├── VimeoVideo.jsx
├── YoutubeVideo.jsx
├── Panel.jsx
├── Pagination.jsx
└── ColorPicker.jsx
├── index.js
├── .storybook
├── webpack.config.js
└── config.js
├── styles
├── ProgressBar.scss
├── mixins.scss
├── ButtonGroup.scss
├── Pagination.scss
├── Button.scss
├── Tabs.scss
├── Panel.scss
├── LoadingSpinner.scss
├── Switch.scss
└── ColorPicker.scss
├── .eslintrc
├── README.md
├── Gruntfile.js
├── CHANGELOG.md
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .sass-cache
3 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | { "presets": ["es2015", "react"] }
2 |
--------------------------------------------------------------------------------
/dist/css/ProgressBar.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../../styles/ProgressBar.scss"],"names":[],"mappings":"AAAA;EAQE,+BAPoD;EAQpD,mBAJmC;EAKnC,aAN6B;EAO7B,iBAAgB,EAAA;EAEhB;IACE,oBAZkC;IAalC,aAAY;IACZ,iCAVgD,EAAA;EAalD;IACE,oBAjB2C;IAkB3C,iBAAgB,EAAA","file":"ProgressBar.css","sourcesContent":[null]}
--------------------------------------------------------------------------------
/.scss-lint.yml:
--------------------------------------------------------------------------------
1 | linters:
2 |
3 | SelectorFormat:
4 | enabled: true
5 | convention: hyphenated_BEM
6 |
7 | Indentation:
8 | enabled: true
9 | allow_non_nested_indentation: true
10 |
11 | NameFormat:
12 | enabled: false
13 |
14 | PropertySortOrder:
15 | enabled: false
16 |
17 | ColorVariable:
18 | enabled: false
19 |
20 | ImportantRule:
21 | enabled: false
22 |
--------------------------------------------------------------------------------
/dist/css/Pagination.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../../styles/Pagination.scss"],"names":[],"mappings":"AAAA;EAUE,iBAAgB;EAChB,UAAS;EACT,WAAU;EACV,mBAAkB,EAAA;EAElB;IACE,sBAAqB;IACrB,kBAhBoC,EAAA;IAkBpC;MACE,gBAAe,EAAA;EAInB;IACE,iBArBuC;IAsBvC,0BAxBiD;IAyBjD,YArBkC;IAsBlC,eAAc;IACd,iBA1BwC;IA2BxC,0BAAyB;IACzB,sBAAqB;IACrB,gBAxBsC,EAAA;EA2BxC;IACE,oBA/BiD;IAgCjD,YA9ByC,EAAA","file":"Pagination.css","sourcesContent":[null]}
--------------------------------------------------------------------------------
/dist/css/ProgressBar.css:
--------------------------------------------------------------------------------
1 | .re-progress-bar {
2 | background: rgba(0, 0, 0, 0.1);
3 | border-radius: 5px;
4 | height: 10px;
5 | overflow: hidden; }
6 | .re-progress-bar__bar {
7 | background: #337ab7;
8 | height: 100%;
9 | transition: width 350ms ease-out; }
10 | .re-progress-bar--complete .re-progress-bar__bar {
11 | background: #9dda83;
12 | transition: none; }
13 |
14 | /*# sourceMappingURL=ProgressBar.css.map */
--------------------------------------------------------------------------------
/src/stories/ColorPicker.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { storiesOf, action } from '@kadira/storybook';
3 | import ColorPicker from '../ColorPicker';
4 |
5 |
6 | storiesOf('ColorPicker', module)
7 | .add('default', () => (
8 |
9 | ))
10 | .add('with initial color', () => (
11 |
13 | ));
14 |
--------------------------------------------------------------------------------
/dist/css/ButtonGroup.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../../styles/ButtonGroup.scss"],"names":[],"mappings":"AAAA;EAGE,iBAAgB;EAChB,UAAS;EACT,WAAU,EAAA;EAEV;IACE,sBAAqB,EAAA;IAErB;MACE,iBAAgB,EAAA;EAKlB;IACE,6BAhB4B;IAiB5B,gCAjB4B,EAAA;EAsB9B;IACE,4BAvB4B;IAwB5B,+BAxB4B,EAAA;EA4BhC;IACE,eAAc,EAAA;IAEd;MACE,iBAAgB;MAChB,eAAc;MACd,YAAW,EAAA;EAKb;IACE,+BAxC4B;IAyC5B,gCAzC4B,EAAA;EA8C9B;IACE,4BA/C4B;IAgD5B,6BAhD4B,EAAA","file":"ButtonGroup.css","sourcesContent":[null]}
--------------------------------------------------------------------------------
/src/stories/YoutubeVideo.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { storiesOf } from '@kadira/storybook';
3 | import YoutubeVideo from '../YoutubeVideo';
4 |
5 |
6 | storiesOf('YoutubeVideo', module)
7 | .add('default', () => (
8 |
9 | ))
10 | .add('no controls', () => (
11 |
13 | ));
14 |
--------------------------------------------------------------------------------
/src/stories/ProgressBar.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { storiesOf } from '@kadira/storybook';
3 | import ProgressBar from '../ProgressBar';
4 |
5 |
6 | storiesOf('ProgressBar', module)
7 | .add('25%', () => (
8 |
9 | ))
10 | .add('50%', () => (
11 |
12 | ))
13 | .add('75%', () => (
14 |
15 | ))
16 | .add('100%', () => (
17 |
18 | ))
19 | ;
20 |
--------------------------------------------------------------------------------
/src/stories/VimeoVideo.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { storiesOf } from '@kadira/storybook';
3 | import VimeoVideo from '../VimeoVideo';
4 |
5 |
6 | storiesOf('VimeoVideo', module)
7 | .add('default', () => (
8 |
9 | ))
10 | .add('custom colour', () => (
11 |
13 | ))
14 | .add('autoplay', () => (
15 |
17 | ));
18 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | exports.Button = require('./dist/Button');
2 | exports.ButtonGroup = require('./dist/ButtonGroup');
3 | exports.ColorPicker = require('./dist/ColorPicker');
4 | exports.LoadingSpinner = require('./dist/LoadingSpinner');
5 | exports.Pagination = require('./dist/Pagination');
6 | exports.Panel = require('./dist/Panel');
7 | exports.ProgressBar = require('./dist/ProgressBar');
8 | exports.Switch = require('./dist/Switch');
9 | exports.Tabs = require('./dist/Tabs');
10 | exports.YoutubeVideo = require('./dist/YoutubeVideo');
11 | exports.VimeoVideo = require('./dist/VimeoVideo');
12 |
--------------------------------------------------------------------------------
/dist/css/Panel.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../../styles/Panel.scss"],"names":[],"mappings":"AAAA;EAYE,iBAX0B;EAY1B,uBAXiC;EAYjC,mBAV4B;EAW5B,oBAL6B;EAM7B,0BAPmC,EAAA;EASnC;IACE,eAd6B;IAe7B,cAjBqB;IAkBrB,4BAjB0B;IAkB1B,6BAlB0B;IAmB1B,oBAjBkC,EAAA;IAmBlC;MACE,iBAAgB,EAAA;IAGlB;MACE,mBAAkB,EAAA;IAGpB;MACE,kBAAiB,EAAA;EAIrB;IACE,UAAS;IACT,WAAU,EAAA;EAGZ;IACE,cAzCqB,EAAA;IA2CrB;MACE,cAAa,EAAA;IAGf;MACE,iBAAgB,EAAA;EAIpB;IACE,eAhD6B;IAiD7B,cAtDqB;IAuDrB,+BAtD0B;IAuD1B,gCAvD0B;IAwD1B,oBArDkC,EAAA;IAuDlC;MACE,iBAAgB,EAAA;IAGlB;MACE,mBAAkB,EAAA;IAGpB;MACE,kBAAiB,EAAA","file":"Panel.css","sourcesContent":[null]}
--------------------------------------------------------------------------------
/dist/css/Tabs.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../../styles/Tabs.scss"],"names":[],"mappings":"AAAA;EAUE,iBAAgB;EAChB,UAAS;EACT,WAAU;EACV,0BALiC,EAAA;EAOjC;IACE,eAAc;IACd,iBAAgB;IAChB,UAAS,EAAA;IAET;MACE,6BAnBsB;MAoBtB,4BApBsB,EAAA;IAuBxB;MACE,gCAxBsB;MAyBtB,+BAzBsB;MA0BtB,8BAA6C,EAAA;EAIjD;IACE,sBAAqB;IACrB,4BAA2C;IAC3C,6BAA4C;IAC5C,2BAA0C;IAC1C,6BAnCwB;IAoCxB,4BApCwB;IAqCxB,gBAAe;IACf,cAjCoB;IAkCpB,mBAnCwB;IAoCxB,0BAAiB;OAAjB,uBAAiB;QAAjB,sBAAiB;YAAjB,kBAAiB;IACjB,iBAxCsB,EAAA;IA0CtB;MACE,eAAc,EAAA;IAGhB;MACE,gBAAe,EAAA;IAGjB;MACE,oBAjD6B,EAAA;EAqDjC;IACE,oBAvDkC,EAAA;IAyDlC;MACE,oBA1DgC,EAAA","file":"Tabs.css","sourcesContent":[null]}
--------------------------------------------------------------------------------
/src/stories/Tabs.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { storiesOf, action } from '@kadira/storybook';
3 | import Tabs from '../Tabs';
4 |
5 |
6 | storiesOf('Tabs', module)
7 | .add('default', () => (
8 |
9 | Foo
10 | Bar
11 | Eggs
12 | Spam
13 |
14 | ))
15 | .add('block', () => (
16 |
18 | Foo
19 | Bar
20 | Eggs
21 | Spam
22 |
23 | ))
24 | ;
25 |
--------------------------------------------------------------------------------
/dist/css/ColorPicker.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../../styles/ColorPicker.scss"],"names":[],"mappings":"AAWE;EACE,sBAAqB;EACrB,mBAAkB;EAClB,aAZ8B;EAa9B,cAZmD;EAanD,iBAAgB;EAChB,kBAAiB,EAAA;AAGnB;EACE,gBAAe,EAAA;AAGjB;EACE,mBAAkB;EAClB,OAAM;EACN,QAAO;EACP,aA1B8B;EA2B9B,cA1BmD,EAAA;AA8BrD;EACE,mBAAkB;EAClB,YA/BoC;EAgCpC,aA/BiE;EAgCjE,wBAAuB;EACvB,OAAM;EACN,QAAO;EACP,qBAAoB;EACpB,mBAAkB;EAClB,uBAAsB;EACtB,2BAA0B,EAAA;AAG5B;EACE,mBAAkB;EAClB,sBAAqB;EACrB,YA1C4B;EA2C5B,cA5C8B;EA6C9B,uKAYC,EAAA;AAGH;EACE,gBAAe,EAAA;AAGjB;EACE,YAhE4B;EAiE5B,YAhEmC;EAiEnC,uBAhE+C;EAiE/C,wBAAuB;EACvB,mBAAkB;EAClB,OAAM;EACN,QAAO;EACP,qBAAoB,EAAA","file":"ColorPicker.css","sourcesContent":[null]}
--------------------------------------------------------------------------------
/.storybook/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | plugins: [
5 | // your custom plugins
6 | ],
7 | resolve: {
8 | extensions: ['', '.js', '.jsx']
9 | },
10 | module: {
11 | loaders: [
12 | {
13 | test: /\.(js|jsx)$/,
14 | exclude: /node_modules/,
15 | loader: 'babel-loader'
16 | },
17 | {
18 | test: /\.scss/,
19 | loader: 'style-loader!css-loader!autoprefixer-loader!sass-loader?outputStyle=expanded'
20 | },
21 | {
22 | test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
23 | loader: 'file-loader'
24 | }
25 | ]
26 | }
27 | };
28 |
--------------------------------------------------------------------------------
/dist/css/Pagination.css:
--------------------------------------------------------------------------------
1 | .re-pagination {
2 | list-style: none;
3 | margin: 0;
4 | padding: 0;
5 | text-align: center; }
6 | .re-pagination__item {
7 | display: inline-block;
8 | margin: 0 5px 0 0; }
9 | .re-pagination__item:last-child {
10 | margin-right: 0; }
11 | .re-pagination__item-link {
12 | background: #fff;
13 | border: 1px solid #efefef;
14 | color: #999;
15 | display: block;
16 | padding: 4px 8px;
17 | font-family: Arial, serif;
18 | text-decoration: none;
19 | font-size: 14px; }
20 | .re-pagination__item--active .re-pagination__item-link {
21 | background: #337ab7;
22 | color: #fff; }
23 |
24 | /*# sourceMappingURL=Pagination.css.map */
--------------------------------------------------------------------------------
/src/stories/ButtonGroup.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { storiesOf } from '@kadira/storybook';
3 | import ButtonGroup from '../ButtonGroup';
4 | import Button from '../Button';
5 |
6 |
7 | storiesOf('ButtonGroup', module)
8 | .add('default', () => (
9 |
10 | Button 1
11 | Button 2
12 | Button 3
13 | Button 4
14 |
15 | ))
16 | .add('stacked', () => (
17 |
18 | Button 1
19 | Button 2
20 | Button 3
21 | Button 4
22 |
23 | ))
24 | ;
25 |
--------------------------------------------------------------------------------
/src/stories/Switch.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { storiesOf, action } from '@kadira/storybook';
3 | import Switch from '../Switch';
4 |
5 |
6 | storiesOf('Switch', module)
7 | .add('default', () => (
8 | getComponent()
9 | ))
10 | .add('on', () => (
11 | getComponent(null, true)
12 | ))
13 | .add('extra small', () => (
14 | getComponent('xs')
15 | ))
16 | .add('small', () => (
17 | getComponent('sm')
18 | ))
19 | .add('large', () => (
20 | getComponent('lg')
21 | ));
22 |
23 |
24 | function getComponent(size = null, value = false) {
25 | return (
26 |
30 | );
31 | }
32 |
--------------------------------------------------------------------------------
/src/stories/LoadingSpinner.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { storiesOf } from '@kadira/storybook';
3 | import LoadingSpinner from '../LoadingSpinner';
4 |
5 |
6 | storiesOf('LoadingSpinner', module)
7 | .add('default', () => (
8 |
9 | ))
10 | .add('small', () => (
11 |
12 | ))
13 | .add('mini', () => (
14 |
15 | ))
16 | .add('slow', () => (
17 |
18 | ))
19 | .add('fast', () => (
20 |
21 | ))
22 | .add('custom color', () => (
23 |
24 | ))
25 | .add('custom background color', () => (
26 |
27 | ));
28 |
--------------------------------------------------------------------------------
/src/ProgressBar.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import classnames from 'classnames';
3 |
4 |
5 | const ProgressBar = React.createClass({
6 |
7 | propTypes: {
8 | progress: PropTypes.number.isRequired
9 | },
10 |
11 | render: function() {
12 | let percentage = this.props.progress * 100;
13 | let innerStyle = { width: `${percentage}%` };
14 | let className = classnames(
15 | 're-progress-bar',
16 | this.props.className,
17 | {
18 | 're-progress-bar--complete': percentage === 100
19 | }
20 | );
21 | return (
22 |
25 | );
26 | }
27 |
28 | });
29 |
30 |
31 | export default ProgressBar;
32 |
--------------------------------------------------------------------------------
/src/ButtonGroup.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import classnames from 'classnames';
3 |
4 |
5 | const ButtonGroup = React.createClass({
6 |
7 | propTypes: {
8 | stacked: PropTypes.bool
9 | },
10 |
11 | render: function() {
12 | let className = classnames(
13 | 're-btn-group',
14 | { 're-btn-group--stacked': this.props.stacked },
15 | this.props.className
16 | );
17 | return (
18 |
19 | {
20 | this.props.children.map((child, i) => {
21 | return (
22 | {child}
23 | );
24 | })
25 | }
26 |
27 | );
28 | }
29 |
30 | });
31 |
32 |
33 | export default ButtonGroup;
34 |
--------------------------------------------------------------------------------
/dist/css/LoadingSpinner.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../../styles/LoadingSpinner.scss"],"names":[],"mappings":"AAAA;EAME,6CAAoC;UAApC,qCAAoC;EACpC,+BAAsB;UAAtB,uBAAsB;EACtB,0CAAiC;UAAjC,kCAAiC;EACjC,4CAAmC;UAAnC,oCAAmC;EAEnC,uBAAsB;EAEtB,8CAVoD;EAWpD,2BAV6C;EAW7C,6CAZoD;EAapD,2CAboD;EAcpD,mBAf+D;EAgB/D,oBAAmB;EAEnB,gBAAe;EACf,qBAAoB;EACpB,iCAAwB;UAAxB,yBAAwB;EACxB,iBAAgB,EAAA;EAEhB;IAEE,mBAAkB;IAClB,aA3B4B;IA4B5B,YA5B4B,EAAA;EA+B9B;IAGE,kBAD+D,EAAA;IAG/D;MAEE,aAN4B;MAO5B,YAP4B,EAAA;EAWhC;IAGE,kBAD+D,EAAA;IAG/D;MAEE,aAN4B;MAO5B,YAP4B,EAAA;EAWhC;IACE,+BAAsB;YAAtB,uBAAsB,EAAA;EAGxB;IACE,gCAAuB;YAAvB,wBAAuB,EAAA;;AAI3B;EACE;IACE,gCAAuB;YAAvB,wBAAuB,EAAA;EAGzB;IACE,kCAAyB;YAAzB,0BAAyB,EAAA,EAAA;;AAN7B;EACE;IACE,gCAAuB;YAAvB,wBAAuB,EAAA;EAGzB;IACE,kCAAyB;YAAzB,0BAAyB,EAAA,EAAA","file":"LoadingSpinner.css","sourcesContent":[null]}
--------------------------------------------------------------------------------
/styles/ProgressBar.scss:
--------------------------------------------------------------------------------
1 | .re-progress-bar {
2 | $re-progress-bar-track-background: rgba(0, 0, 0, .1) !default;
3 | $re-progress-bar-background: #337ab7 !default;
4 | $re-progress-bar-background-complete: #9dda83 !default;
5 | $re-progress-bar-height: 10px !default;
6 | $re-progress-bar-border-radius: 5px !default;
7 | $re-progress-bar-transition: width 350ms ease-out !default;
8 |
9 | background: $re-progress-bar-track-background;
10 | border-radius: $re-progress-bar-border-radius;
11 | height: $re-progress-bar-height;
12 | overflow: hidden;
13 |
14 | &__bar {
15 | background: $re-progress-bar-background;
16 | height: 100%;
17 | transition: $re-progress-bar-transition;
18 | }
19 |
20 | &--complete &__bar {
21 | background: $re-progress-bar-background-complete;
22 | transition: none;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/.storybook/config.js:
--------------------------------------------------------------------------------
1 | import { configure } from '@kadira/storybook';
2 | import '../styles/ColorPicker.scss';
3 | import '../styles/Switch.scss';
4 | import '../styles/LoadingSpinner.scss';
5 | import '../styles/Button.scss';
6 | import '../styles/ButtonGroup.scss';
7 | import '../styles/Pagination.scss';
8 | import '../styles/Panel.scss';
9 | import '../styles/ProgressBar.scss';
10 | import '../styles/Tabs.scss';
11 |
12 | function loadStories() {
13 | require('../src/stories/Switch');
14 | require('../src/stories/YoutubeVideo');
15 | require('../src/stories/VimeoVideo');
16 | require('../src/stories/ColorPicker');
17 | require('../src/stories/LoadingSpinner');
18 | require('../src/stories/Button');
19 | require('../src/stories/ButtonGroup');
20 | require('../src/stories/Pagination');
21 | require('../src/stories/Panel');
22 | require('../src/stories/ProgressBar');
23 | require('../src/stories/Tabs');
24 | }
25 |
26 | configure(loadStories, module);
27 |
--------------------------------------------------------------------------------
/styles/mixins.scss:
--------------------------------------------------------------------------------
1 | @mixin button-background($bg: #666, $color: #fff) {
2 | background: $bg;
3 | border-color: darken($bg, 5%);
4 | color: $color;
5 |
6 | &:hover {
7 | background: lighten($bg, 5%);
8 | border-color: $bg;
9 | }
10 |
11 | &:disabled {
12 | color: rgba($color, .55);
13 |
14 | &:hover {
15 | background: $bg;
16 | border-color: darken($bg, 5%);
17 | }
18 | }
19 | }
20 |
21 | @mixin re-switch($modifier, $width, $height, $border-width) {
22 | &--#{$modifier} {
23 | width: $width;
24 | height: $height;
25 | border-radius: $width * .5;
26 |
27 | .re-switch__switch {
28 | width: $height - ($border-width * 2);
29 | height: $height - ($border-width * 2);
30 | }
31 |
32 | .re-switch__checkbox {
33 | width: $width;
34 | height: $height;
35 | }
36 |
37 | &.re-switch--on {
38 | .re-switch__switch {
39 | left: $width - $height;
40 | }
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["eslint:recommended", "plugin:react/recommended"],
3 | "rules": {
4 | "strict": 0,
5 | "indent": [
6 | 2,
7 | 2
8 | ],
9 | "quotes": [
10 | 2,
11 | "single"
12 | ],
13 | "linebreak-style": [
14 | 2,
15 | "unix"
16 | ],
17 | "semi": [
18 | 2,
19 | "always"
20 | ],
21 | "react/prop-types": [
22 | 2,
23 | {
24 | "ignore": ["children", "className"]
25 | }
26 | ]
27 | },
28 | "env": {
29 | "es6": true,
30 | "browser": true,
31 | "node": true,
32 | "phantomjs": true,
33 | "mocha": true
34 | },
35 | "parser": "babel-eslint",
36 | "parserOptions": {
37 | "sourceType": "module",
38 | "ecmaVersion": 6,
39 | "ecmaFeatures": {
40 | "jsx": true,
41 | "modules": true,
42 | "arrowFunctions": true,
43 | "experimentalObjectRestSpread": true
44 | }
45 | },
46 | "plugins": [
47 | "react"
48 | ]
49 | }
50 |
--------------------------------------------------------------------------------
/dist/css/ButtonGroup.css:
--------------------------------------------------------------------------------
1 | .re-btn-group {
2 | list-style: none;
3 | margin: 0;
4 | padding: 0; }
5 | .re-btn-group__item {
6 | display: inline-block; }
7 | .re-btn-group__item .re-btn {
8 | border-radius: 0; }
9 | .re-btn-group__item:last-child .re-btn {
10 | border-top-right-radius: 4px;
11 | border-bottom-right-radius: 4px; }
12 | .re-btn-group__item:first-child .re-btn {
13 | border-top-left-radius: 4px;
14 | border-bottom-left-radius: 4px; }
15 | .re-btn-group--stacked .re-btn-group__item {
16 | display: block; }
17 | .re-btn-group--stacked .re-btn-group__item .re-btn {
18 | border-radius: 0;
19 | display: block;
20 | width: 100%; }
21 | .re-btn-group--stacked .re-btn-group__item:last-child .re-btn {
22 | border-bottom-left-radius: 4px;
23 | border-bottom-right-radius: 4px; }
24 | .re-btn-group--stacked .re-btn-group__item:first-child .re-btn {
25 | border-top-left-radius: 4px;
26 | border-top-right-radius: 4px; }
27 |
28 | /*# sourceMappingURL=ButtonGroup.css.map */
--------------------------------------------------------------------------------
/dist/css/Switch.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../../styles/Switch.scss","../../styles/mixins.scss"],"names":[],"mappings":"AAEA;EAqBE,sBAAqB;EACrB,YAhByC;EAiBzC,aAlBuB;EAmBvB,sBAAoC;EACpC,iBAvB0B;EAwB1B,mBAAkB;EAClB,mBAzB0B;EA0B1B,kBA3B4B;EA4B5B,oBAAmB;EACnB,6EAA4E;EAC5E,uBAAsB,EAAA;ECZtB;IACE,YDX6C;ICY7C,aDbwB;ICcxB,oBAA0B,EAAA;IAE1B;MACE,YAAoC;MACpC,aAAqC,EAAA;IAGvC;MACE,YDrB2C;MCsB3C,aDvBsB,EAAA;IC2BtB;MACE,WAAsB,EAAA;EAjB5B;IACE,YDR6C;ICS7C,aDVwB;ICWxB,sBAA0B,EAAA;IAE1B;MACE,YAAoC;MACpC,aAAqC,EAAA;IAGvC;MACE,YDlB2C;MCmB3C,aDpBsB,EAAA;ICwBtB;MACE,WAAsB,EAAA;EAjB5B;IACE,YDL6C;ICM7C,aDPwB;ICQxB,sBAA0B,EAAA;IAE1B;MACE,YAAoC;MACpC,aAAqC,EAAA;IAGvC;MACE,YDf2C;MCgB3C,aDjBsB,EAAA;ICqBtB;MACE,WAAsB,EAAA;EDH5B;IACE,eAAc;IACd,YAAwD;IACxD,aAAyD;IACzD,mBAAkB;IAClB,iBAAgB;IAChB,WAAU;IACV,mBAAkB;IAClB,QAAO;IACP,kCAAiC;IACjC,yCAAuC;IACvC,uBAAsB,EAAA;EAGxB;IACE,UAAS;IACT,WAAU;IACV,mBAAkB;IAClB,WAAU;IACV,YA9CuC;IA+CvC,aAhDqB;IAiDrB,UAA6B;IAC7B,WAA8B;IAC9B,uBAAsB;IACtB,WAAU;IACV,gBAAe,EAAA;EAGjB;IACE,sBA3D0B;IA4D1B,0BA5D0B,EAAA;IA8D1B;MACE,WAA0C,EAAA","file":"Switch.css","sourcesContent":[null,null]}
--------------------------------------------------------------------------------
/dist/css/Button.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../../styles/Button.scss","../../styles/mixins.scss"],"names":[],"mappings":"AAEA;EAaE,uBAAsB;EACtB,8BAA6B;EAC7B,mBAZ0B;EAa1B,gBAAe;EACf,sBAAqB;EACrB,gBAAe;EACf,iBAAgB;EAChB,wBAAuB;EACvB,iBAAgB;EAChB,kBApB0B;EAqB1B,mBAAkB;EAClB,mBAAkB;EAClB,+BAA0B;MAA1B,2BAA0B;EAC1B,0BAAiB;KAAjB,uBAAiB;MAAjB,sBAAiB;UAAjB,kBAAiB;EACjB,uBAAsB;EACtB,oBAAmB,EAAA;EAEnB;IACE,cA9ByB,EAAA;EAiC3B;IACE,UAAS;IACT,mBAAkB;IAClB,kBAAiB;IACjB,8BAA6B;IAC7B,SAAQ,EAAA;EAGV;IC3CA,oBDK2B;ICJ3B,sBAA6B;IAC7B,YDI2B,EAAA;ICF3B;MACE,kBAA4B;MAC5B,sBDDyB,EAAA;ICI3B;MACE,8BAAwB,EAAA;MAExB;QACE,oBDRuB;QCSvB,sBAA6B,EAAA;EDiCjC;IC/CA,oBDO2B;ICN3B,sBAA6B;IAC7B,YDM2B,EAAA;ICJ3B;MACE,oBAA4B;MAC5B,sBDCyB,EAAA;ICE3B;MACE,iCAAwB,EAAA;MAExB;QACE,oBDNuB;QCOvB,sBAA6B,EAAA;EDqCjC;ICnDA,oBDS0B;ICR1B,sBAA6B;IAC7B,YDQ0B,EAAA;ICN1B;MACE,oBAA4B;MAC5B,sBDGwB,EAAA;ICA1B;MACE,iCAAwB,EAAA;MAExB;QACE,oBDJsB;QCKtB,sBAA6B,EAAA;EDyCjC;ICvDA,wBDW4B;ICV5B,0BAA6B;IAC7B,YDUwB,EAAA;ICRxB;MACE,gCAA4B;MAC5B,0BDK0B,EAAA;ICF5B;MACE,2BAAwB,EAAA;MAExB;QACE,wBDFwB;QCGxB,0BAA6B,EAAA;ED6CjC;IACE,eAAc;IACd,YAAW,EAAA;EAGb;IACE,mBAAkB,EAAA","file":"Button.css","sourcesContent":[null,null]}
--------------------------------------------------------------------------------
/dist/ButtonGroup.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _react = require('react');
8 |
9 | var _react2 = _interopRequireDefault(_react);
10 |
11 | var _classnames = require('classnames');
12 |
13 | var _classnames2 = _interopRequireDefault(_classnames);
14 |
15 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
16 |
17 | var ButtonGroup = _react2.default.createClass({
18 | displayName: 'ButtonGroup',
19 |
20 |
21 | propTypes: {
22 | stacked: _react.PropTypes.bool
23 | },
24 |
25 | render: function render() {
26 | var className = (0, _classnames2.default)('re-btn-group', { 're-btn-group--stacked': this.props.stacked }, this.props.className);
27 | return _react2.default.createElement(
28 | 'ul',
29 | { className: className },
30 | this.props.children.map(function (child, i) {
31 | return _react2.default.createElement(
32 | 'li',
33 | { className: 're-btn-group__item', key: 'item-' + i },
34 | child
35 | );
36 | })
37 | );
38 | }
39 |
40 | });
41 |
42 | exports.default = ButtonGroup;
43 |
--------------------------------------------------------------------------------
/dist/ProgressBar.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _react = require('react');
8 |
9 | var _react2 = _interopRequireDefault(_react);
10 |
11 | var _classnames = require('classnames');
12 |
13 | var _classnames2 = _interopRequireDefault(_classnames);
14 |
15 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
16 |
17 | var ProgressBar = _react2.default.createClass({
18 | displayName: 'ProgressBar',
19 |
20 |
21 | propTypes: {
22 | progress: _react.PropTypes.number.isRequired
23 | },
24 |
25 | render: function render() {
26 | var percentage = this.props.progress * 100;
27 | var innerStyle = { width: percentage + '%' };
28 | var className = (0, _classnames2.default)('re-progress-bar', this.props.className, {
29 | 're-progress-bar--complete': percentage === 100
30 | });
31 | return _react2.default.createElement(
32 | 'div',
33 | { className: className },
34 | _react2.default.createElement('div', { className: 're-progress-bar__bar', style: innerStyle })
35 | );
36 | }
37 |
38 | });
39 |
40 | exports.default = ProgressBar;
41 |
--------------------------------------------------------------------------------
/src/stories/Button.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { storiesOf } from '@kadira/storybook';
3 | import Button from '../Button';
4 |
5 |
6 | storiesOf('Button', module)
7 | .add('default', () => (
8 | Click me
9 | ))
10 | .add('disabled', () => (
11 | Click me
12 | ))
13 | .add('primary', () => (
14 | Click me
15 | ))
16 | .add('danger', () => (
17 | Click me
18 | ))
19 | .add('link', () => (
20 | Click me
21 | ))
22 | .add('block', () => (
23 | Click me
24 | ))
25 | .add('default - processing', () => (
26 | Click me
28 | ))
29 | .add('primary - processing', () => (
30 | Click me
32 | ))
33 | .add('danger - processing', () => (
34 | Click me
36 | ))
37 | .add('block - processing', () => (
38 | Click me
41 | ))
42 | ;
43 |
--------------------------------------------------------------------------------
/dist/css/Panel.css:
--------------------------------------------------------------------------------
1 | .re-panel {
2 | background: #fff;
3 | border: 1px solid #eee;
4 | border-radius: 4px;
5 | margin-bottom: 20px;
6 | font-family: Arial, serif; }
7 | .re-panel__header {
8 | color: inherit;
9 | padding: 15px;
10 | border-top-left-radius: 4px;
11 | border-top-right-radius: 4px;
12 | background: #e7e7e7; }
13 | .re-panel__header--text-left {
14 | text-align: left; }
15 | .re-panel__header--text-center {
16 | text-align: center; }
17 | .re-panel__header--text-right {
18 | text-align: right; }
19 | .re-panel__header-heading {
20 | margin: 0;
21 | padding: 0; }
22 | .re-panel__content {
23 | padding: 15px; }
24 | .re-panel__content p {
25 | margin-top: 0; }
26 | .re-panel__content p:last-child {
27 | margin-bottom: 0; }
28 | .re-panel__footer {
29 | color: inherit;
30 | padding: 15px;
31 | border-bottom-left-radius: 4px;
32 | border-bottom-right-radius: 4px;
33 | background: #e7e7e7; }
34 | .re-panel__footer--text-left {
35 | text-align: left; }
36 | .re-panel__footer--text-center {
37 | text-align: center; }
38 | .re-panel__footer--text-right {
39 | text-align: right; }
40 |
41 | /*# sourceMappingURL=Panel.css.map */
--------------------------------------------------------------------------------
/src/LoadingSpinner.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import classnames from 'classnames';
3 |
4 |
5 | const LoadingSpinner = React.createClass({
6 |
7 | propTypes: {
8 | mini: PropTypes.bool,
9 | small: PropTypes.bool,
10 | slow: PropTypes.bool,
11 | fast: PropTypes.bool,
12 | color: PropTypes.string,
13 | backgroundColor: PropTypes.string
14 | },
15 |
16 | render: function() {
17 | let className = classnames(
18 | 're-loading-spinner',
19 | {
20 | 're-loading-spinner--mini': this.props.mini,
21 | 're-loading-spinner--small': this.props.small,
22 | 're-loading-spinner--slow': this.props.slow,
23 | 're-loading-spinner--fast': this.props.fast
24 | },
25 | this.props.className
26 | );
27 | let style = {};
28 | if (this.props.color) style.borderLeftColor = this.props.color;
29 | if (this.props.backgroundColor) {
30 | style.borderBottomColor = this.props.backgroundColor;
31 | style.borderRightColor = this.props.backgroundColor;
32 | style.borderTopColor = this.props.backgroundColor;
33 | }
34 | return(
35 | Loading
36 | );
37 | }
38 |
39 | });
40 |
41 |
42 | export default LoadingSpinner;
43 |
--------------------------------------------------------------------------------
/styles/ButtonGroup.scss:
--------------------------------------------------------------------------------
1 | .re-btn-group {
2 | $re-btn-group-border-radius: 4px !default;
3 |
4 | list-style: none;
5 | margin: 0;
6 | padding: 0;
7 |
8 | &__item {
9 | display: inline-block;
10 |
11 | .re-btn {
12 | border-radius: 0;
13 | }
14 | }
15 |
16 | &__item:last-child {
17 | .re-btn {
18 | border-top-right-radius: $re-btn-group-border-radius;
19 | border-bottom-right-radius: $re-btn-group-border-radius;
20 | }
21 | }
22 |
23 | &__item:first-child {
24 | .re-btn {
25 | border-top-left-radius: $re-btn-group-border-radius;
26 | border-bottom-left-radius: $re-btn-group-border-radius;
27 | }
28 | }
29 |
30 | &--stacked &__item {
31 | display: block;
32 |
33 | .re-btn {
34 | border-radius: 0;
35 | display: block;
36 | width: 100%;
37 | }
38 | }
39 |
40 | &--stacked &__item:last-child {
41 | .re-btn {
42 | border-bottom-left-radius: $re-btn-group-border-radius;
43 | border-bottom-right-radius: $re-btn-group-border-radius;
44 | }
45 | }
46 |
47 | &--stacked &__item:first-child {
48 | .re-btn {
49 | border-top-left-radius: $re-btn-group-border-radius;
50 | border-top-right-radius: $re-btn-group-border-radius;
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/styles/Pagination.scss:
--------------------------------------------------------------------------------
1 | .re-pagination {
2 | $re-pagination-item-margin: 0 5px 0 0 !default;
3 | $re-pagination-item-link-border: 1px solid #efefef !default;
4 | $re-pagination-item-link-padding: 4px 8px !default;
5 | $re-pagination-item-link-background: #fff !default;
6 | $re-pagination-item-link-background-active: #337ab7 !default;
7 | $re-pagination-item-link-color: #999 !default;
8 | $re-pagination-item-link-color-active: #fff !default;
9 | $re-pagination-item-link-font-size: 14px !default;
10 |
11 | list-style: none;
12 | margin: 0;
13 | padding: 0;
14 | text-align: center;
15 |
16 | &__item {
17 | display: inline-block;
18 | margin: $re-pagination-item-margin;
19 |
20 | &:last-child {
21 | margin-right: 0;
22 | }
23 | }
24 |
25 | &__item-link {
26 | background: $re-pagination-item-link-background;
27 | border: $re-pagination-item-link-border;
28 | color: $re-pagination-item-link-color;
29 | display: block;
30 | padding: $re-pagination-item-link-padding;
31 | font-family: Arial, serif;
32 | text-decoration: none;
33 | font-size: $re-pagination-item-link-font-size;
34 | }
35 |
36 | &__item--active &__item-link {
37 | background: $re-pagination-item-link-background-active;
38 | color: $re-pagination-item-link-color-active;
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/dist/css/ColorPicker.css:
--------------------------------------------------------------------------------
1 | .re-color-picker__sb-picker {
2 | display: inline-block;
3 | position: relative;
4 | width: 128px;
5 | height: 128px;
6 | overflow: hidden;
7 | margin-right: 3px; }
8 | .re-color-picker__sb-picker--dragging {
9 | cursor: pointer; }
10 | .re-color-picker__sb-picker-layer {
11 | position: absolute;
12 | top: 0;
13 | left: 0;
14 | width: 128px;
15 | height: 128px; }
16 | .re-color-picker__sb-picker-cursor {
17 | position: absolute;
18 | width: 14px;
19 | height: 14px;
20 | background: transparent;
21 | top: 0;
22 | left: 0;
23 | pointer-events: none;
24 | border-radius: 50%;
25 | border: 1px solid #fff;
26 | box-shadow: 0 0 0 1px #000; }
27 | .re-color-picker__h-picker {
28 | position: relative;
29 | display: inline-block;
30 | width: 26px;
31 | height: 128px;
32 | background: linear-gradient(red 0%, #ff9500 10%, #d0ff00 20%, #37ff00 30%, #00ff62 40%, #00fffb 50%, #006aff 60%, #3300ff 70%, #cc00ff 80%, #ff0099 90%, #ff0004 100%); }
33 | .re-color-picker__h-picker--dragging {
34 | cursor: pointer; }
35 | .re-color-picker__h-picker-cursor {
36 | width: 26px;
37 | height: 3px;
38 | border: 1px solid #fff;
39 | background: transparent;
40 | position: absolute;
41 | top: 0;
42 | left: 0;
43 | pointer-events: none; }
44 |
45 | /*# sourceMappingURL=ColorPicker.css.map */
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React Elements
2 |
3 | A library of reusable React components consisting of the following:
4 |
5 | ```HTML
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | ```
18 |
19 | **DEMO:** http://willdady.github.io/react-elements/
20 |
21 | ## Installation
22 |
23 | ```bash
24 | npm install --save react-elements
25 | ```
26 |
27 | ## Usage
28 |
29 | To include a component in your project simply import the component.
30 |
31 | ```javascript
32 | import Switch from 'react-elements/dist/Switch';
33 | ```
34 |
35 | ### Component stylesheets
36 |
37 | Most components have an accompanying stylesheet. When using a component be sure
38 | to include the component's stylesheet in your build otherwise the component
39 | will fail to render correctly.
40 |
41 | Stylesheets are written using [BEM syntax](https://css-tricks.com/bem-101/) and all classes are prefixed
42 | with ```re-```.
43 |
44 | Pre-built CSS files can be found in `dist/css`. Source SCSS can be found in
45 | the `styles` directory in the project's root.
46 |
47 | #### Button
48 |
49 | Please note the ` ` component composes the ` ` component so be sure to include both component's stylesheets.
50 |
--------------------------------------------------------------------------------
/dist/css/Tabs.css:
--------------------------------------------------------------------------------
1 | .re-tabs {
2 | list-style: none;
3 | margin: 0;
4 | padding: 0;
5 | font-family: Arial, serif; }
6 | .re-tabs--block .re-tabs__tab {
7 | display: block;
8 | border-radius: 0;
9 | margin: 0; }
10 | .re-tabs--block .re-tabs__tab:first-child {
11 | border-top-right-radius: 4px;
12 | border-top-left-radius: 4px; }
13 | .re-tabs--block .re-tabs__tab:last-child {
14 | border-bottom-right-radius: 4px;
15 | border-bottom-left-radius: 4px;
16 | border-bottom: 1px solid #eee; }
17 | .re-tabs__tab {
18 | display: inline-block;
19 | border-left: 1px solid #eee;
20 | border-right: 1px solid #eee;
21 | border-top: 1px solid #eee;
22 | border-top-right-radius: 4px;
23 | border-top-left-radius: 4px;
24 | cursor: pointer;
25 | margin: 0 2px;
26 | padding: 0.5em 1em;
27 | -webkit-user-select: none;
28 | -moz-user-select: none;
29 | -ms-user-select: none;
30 | user-select: none;
31 | background: #fff; }
32 | .re-tabs__tab:first-child {
33 | margin-left: 0; }
34 | .re-tabs__tab:last-child {
35 | margin-right: 0; }
36 | .re-tabs__tab:hover {
37 | background: #f7f7f7; }
38 | .re-tabs__tab--selected {
39 | background: #e7e7e7; }
40 | .re-tabs__tab--selected:hover {
41 | background: #e7e7e7; }
42 |
43 | /*# sourceMappingURL=Tabs.css.map */
--------------------------------------------------------------------------------
/src/Switch.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import classnames from 'classnames';
3 | import values from 'lodash/values';
4 |
5 |
6 | const SIZES = {
7 | EXTRA_SMALL: 'xs',
8 | SMALL: 'sm',
9 | LARGE: 'lg'
10 | };
11 |
12 |
13 | const Switch = React.createClass({
14 |
15 | propTypes: {
16 | name: PropTypes.string,
17 | value: PropTypes.bool.isRequired,
18 | size: PropTypes.oneOf(values(SIZES)),
19 | onClick: PropTypes.func
20 | },
21 |
22 | onClick: function () {
23 | if (!this.props.onClick) return;
24 | this.props.onClick(this.props.value, this.props.name);
25 | },
26 |
27 | render: function() {
28 | let className = classnames({
29 | 're-switch': true,
30 | 're-switch--on': this.props.value,
31 | 're-switch--xs': this.props.size === SIZES.EXTRA_SMALL,
32 | 're-switch--sm': this.props.size === SIZES.SMALL,
33 | 're-switch--lg': this.props.size === SIZES.LARGE
34 | });
35 | return (
36 |
37 |
45 |
46 |
47 | );
48 | }
49 | });
50 |
51 |
52 | export default Switch;
53 |
--------------------------------------------------------------------------------
/src/stories/Panel.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { storiesOf } from '@kadira/storybook';
3 | import Panel from '../Panel';
4 |
5 |
6 | const TEXT = (
7 |
8 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi sit amet cursus mauris. Nunc ac risus mi. Curabitur vitae posuere risus. Sed porta finibus neque sed dignissim.
9 | Pellentesque luctus mi a lectus bibendum euismod. Ut laoreet ipsum non ex tempor, eu gravida est viverra. Nam in viverra sapien. Suspendisse a augue auctor, luctus leo sit amet, tempus turpis.
10 |
11 | );
12 |
13 |
14 | storiesOf('Panel', module)
15 | .add('default', () => (
16 | {TEXT}
17 | ))
18 | .add('with header', () => (
19 | {TEXT}
20 | ))
21 | .add('with center aligned header', () => (
22 | {TEXT}
23 | ))
24 | .add('with right aligned header', () => (
25 | {TEXT}
26 | ))
27 | .add('with footer', () => (
28 | {TEXT}
29 | ))
30 | .add('with center aligned footer', () => (
31 | {TEXT}
32 | ))
33 | .add('with right aligned footer', () => (
34 | {TEXT}
35 | ))
36 | .add('with header and footer', () => (
37 | {TEXT}
39 | ));
40 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = function(grunt) {
3 |
4 | require('load-grunt-tasks')(grunt);
5 |
6 | grunt.initConfig({
7 |
8 | clean: [
9 | 'dist/'
10 | ],
11 |
12 | babel: {
13 | options: {
14 | babelrc: '.babelrc'
15 | },
16 | dist: {
17 | files: [{
18 | 'expand': true,
19 | 'cwd': 'src/',
20 | 'src': ['**/*.js', '**/*.jsx', '!**/main.js', '!**/stories/*.js'],
21 | 'dest': 'dist/',
22 | 'ext': '.js'
23 | }]
24 | }
25 | },
26 |
27 | sass: {
28 | dist: {
29 | files: [{
30 | expand: true,
31 | cwd: 'styles',
32 | src: ['*.scss', '!mixins.scss'],
33 | dest: 'dist/css',
34 | ext: '.css'
35 | }]
36 | }
37 | },
38 |
39 | postcss: {
40 | options: {
41 | map: {
42 | inline: false,
43 | annotation: 'dist/css/'
44 | },
45 |
46 | processors: [
47 | require('autoprefixer')({browsers: 'last 3 versions'})
48 | ]
49 | },
50 | dist: {
51 | src: 'dist/css/*.css'
52 | }
53 | },
54 |
55 | copy: {
56 | main: {
57 | files: [
58 | {
59 | expand: true,
60 | cwd: 'styles/img/',
61 | src: ['**'],
62 | dest: 'dist/css/img/',
63 | filter: 'isFile'
64 | }
65 | ]
66 | }
67 | }
68 |
69 | });
70 |
71 | grunt.registerTask('default', ['clean', 'babel', 'sass:dist', 'postcss', 'copy']);
72 | grunt.registerTask('build', ['default']);
73 | };
74 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## v1.3.0
2 | * Added ButtonGroup component.
3 | * Buttons are no longer outlined when focused.
4 |
5 | ## v1.2.1
6 | * No longer creating shrinkwrap.json with -dev option.
7 |
8 | ## v1.2.0
9 | * Panel component now takes optional footerTextAlign and headerTextAlign properties. Valid values for are 'left', 'center' or 'right'.
10 |
11 | ## v1.1.1
12 | * Now deploying storybook to gh-pages with storybook-deployer.
13 | * Updated README.
14 |
15 | ## v1.1.0
16 | * Renamed React component file extensions from .js to .jsx.
17 | * Updated libraries.
18 | * Code linting.
19 | * Updated README.
20 | * Updated/Added component PropTypes.
21 | * Added more scss variables.
22 |
23 | ## v1.0.4
24 | * Removed css position entirely from LoadingSpinner.
25 |
26 | ## v1.0.3
27 | * Added more variables to LoadingSpinner scss.
28 |
29 | ## v1.0.2
30 | * Added more variables to Panel's scss.
31 |
32 | ## v1.0.1
33 | * Tweaked README
34 |
35 | ## v1.0.0
36 |
37 | * MAJOR UPDATE. This release is not backwards compatible with older versions.
38 | * Removed MediaObject and FloatingLabelInput components.
39 | * Switched CSS pre-processor from Less to Sass.
40 | * Switched from grunt-react to grunt-babel.
41 | * Updated React to v15.0.1.
42 | * Switch is now a controlled component. Previously it was uncontrolled, maintaining it's own state.
43 | * Added LoadingSpinner component
44 | * Removed TagsInput. [React Select](http://jedwatson.github.io/react-select/) is a better alternative.
45 | * Changed css prefix from `rui-` to `re-`.
46 | * Added Button component.
47 | * Added Pagination component.
48 | * Added Panel component.
49 | * Added ProgressBar component.
50 | * Added Tabs component.
51 |
--------------------------------------------------------------------------------
/dist/LoadingSpinner.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _react = require('react');
8 |
9 | var _react2 = _interopRequireDefault(_react);
10 |
11 | var _classnames = require('classnames');
12 |
13 | var _classnames2 = _interopRequireDefault(_classnames);
14 |
15 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
16 |
17 | var LoadingSpinner = _react2.default.createClass({
18 | displayName: 'LoadingSpinner',
19 |
20 |
21 | propTypes: {
22 | mini: _react.PropTypes.bool,
23 | small: _react.PropTypes.bool,
24 | slow: _react.PropTypes.bool,
25 | fast: _react.PropTypes.bool,
26 | color: _react.PropTypes.string,
27 | backgroundColor: _react.PropTypes.string
28 | },
29 |
30 | render: function render() {
31 | var className = (0, _classnames2.default)('re-loading-spinner', {
32 | 're-loading-spinner--mini': this.props.mini,
33 | 're-loading-spinner--small': this.props.small,
34 | 're-loading-spinner--slow': this.props.slow,
35 | 're-loading-spinner--fast': this.props.fast
36 | }, this.props.className);
37 | var style = {};
38 | if (this.props.color) style.borderLeftColor = this.props.color;
39 | if (this.props.backgroundColor) {
40 | style.borderBottomColor = this.props.backgroundColor;
41 | style.borderRightColor = this.props.backgroundColor;
42 | style.borderTopColor = this.props.backgroundColor;
43 | }
44 | return _react2.default.createElement(
45 | 'div',
46 | { className: className, style: style },
47 | 'Loading'
48 | );
49 | }
50 |
51 | });
52 |
53 | exports.default = LoadingSpinner;
54 |
--------------------------------------------------------------------------------
/src/Button.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import classnames from 'classnames';
3 | import LoadingSpinner from './LoadingSpinner';
4 | import omit from 'lodash/omit';
5 |
6 |
7 | const Button = React.createClass({
8 |
9 | propTypes: {
10 | btnStyle: PropTypes.string,
11 | block: PropTypes.bool,
12 | processing: PropTypes.bool,
13 | loadingSpinnerColor: PropTypes.string,
14 | loadingSpinnerBackgroundColor: PropTypes.string
15 | },
16 |
17 | render: function() {
18 | let btnStyle = this.props.btnStyle;
19 | let className = classnames(
20 | 're-btn',
21 | {
22 | 're-btn--default': !btnStyle || btnStyle === 'default',
23 | 're-btn--primary': btnStyle === 'primary',
24 | 're-btn--danger': btnStyle === 'danger',
25 | 're-btn--link': btnStyle === 'link',
26 | 're-btn--block': this.props.block,
27 | 're-btn--processing': this.props.processing
28 | },
29 | this.props.className
30 | );
31 | let props = omit(this.props, ['btnStyle', 'block', 'processing']);
32 | props.type = props.type ? props.type : 'button';
33 |
34 | let spinner;
35 | if (this.props.processing) {
36 | spinner = (
37 |
43 | );
44 | }
45 |
46 | return (
47 |
48 | {spinner}
49 |
50 | {this.props.children}
51 |
52 |
53 | );
54 | }
55 |
56 | });
57 |
58 |
59 | export default Button;
60 |
--------------------------------------------------------------------------------
/styles/Button.scss:
--------------------------------------------------------------------------------
1 | @import 'mixins';
2 |
3 | .re-btn {
4 | $re-btn-focus-outline: none !default;
5 | $re-btn-padding: 6px 12px !default;
6 | $re-btn-border-radius: 4px !default;
7 | $re-btn-default-bg: #f7f7f7 !default;
8 | $re-btn-default-color: #444 !default;
9 | $re-btn-primary-bg: #337ab7 !default;
10 | $re-btn-primary-color: #fff !default;
11 | $re-btn-danger-bg: #d9534f !default;
12 | $re-btn-danger-color: #fff !default;
13 | $re-btn-link-bg: transparent !default;
14 | $re-btn-link-color: #000 !default;
15 |
16 | background-image: none;
17 | border: 1px solid transparent;
18 | border-radius: $re-btn-border-radius;
19 | cursor: pointer;
20 | display: inline-block;
21 | font-size: 14px;
22 | font-weight: 400;
23 | line-height: 1.42857143;
24 | margin-bottom: 0;
25 | padding: $re-btn-padding;
26 | position: relative;
27 | text-align: center;
28 | touch-action: manipulation;
29 | user-select: none;
30 | vertical-align: middle;
31 | white-space: nowrap;
32 |
33 | &:focus {
34 | outline: $re-btn-focus-outline;
35 | }
36 |
37 | &__spinner {
38 | left: 50%;
39 | margin-left: -10px;
40 | margin-top: -10px;
41 | position: absolute !important;
42 | top: 50%;
43 | }
44 |
45 | &--default {
46 | @include button-background($re-btn-default-bg, $re-btn-default-color);
47 | }
48 |
49 | &--primary {
50 | @include button-background($re-btn-primary-bg, $re-btn-primary-color);
51 | }
52 |
53 | &--danger {
54 | @include button-background($re-btn-danger-bg, $re-btn-danger-color);
55 | }
56 |
57 | &--link {
58 | @include button-background($re-btn-link-bg, $re-btn-link-color);
59 | }
60 |
61 | &--block {
62 | display: block;
63 | width: 100%;
64 | }
65 |
66 | &--processing &__content {
67 | visibility: hidden;
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/styles/Tabs.scss:
--------------------------------------------------------------------------------
1 | .re-tabs {
2 | $re-tab-border-color: #eee !default;
3 | $re-tab-border-radius: 4px !default;
4 | $re-tab-background: #fff !default;
5 | $re-tab-background-selected: #e7e7e7 !default;
6 | $re-tab-background-hover: #f7f7f7 !default;
7 | $re-tab-padding: .5em 1em !default;
8 | $re-tab-margin: 0 2px !default;
9 | $re-tab-font-family: Arial, serif !default;
10 |
11 | list-style: none;
12 | margin: 0;
13 | padding: 0;
14 | font-family: $re-tab-font-family;
15 |
16 | &--block &__tab {
17 | display: block;
18 | border-radius: 0;
19 | margin: 0;
20 |
21 | &:first-child {
22 | border-top-right-radius: $re-tab-border-radius;
23 | border-top-left-radius: $re-tab-border-radius;
24 | }
25 |
26 | &:last-child {
27 | border-bottom-right-radius: $re-tab-border-radius;
28 | border-bottom-left-radius: $re-tab-border-radius;
29 | border-bottom: 1px solid $re-tab-border-color;
30 | }
31 | }
32 |
33 | &__tab {
34 | display: inline-block;
35 | border-left: 1px solid $re-tab-border-color;
36 | border-right: 1px solid $re-tab-border-color;
37 | border-top: 1px solid $re-tab-border-color;
38 | border-top-right-radius: $re-tab-border-radius;
39 | border-top-left-radius: $re-tab-border-radius;
40 | cursor: pointer;
41 | margin: $re-tab-margin;
42 | padding: $re-tab-padding;
43 | user-select: none;
44 | background: $re-tab-background;
45 |
46 | &:first-child {
47 | margin-left: 0;
48 | }
49 |
50 | &:last-child {
51 | margin-right: 0;
52 | }
53 |
54 | &:hover {
55 | background: $re-tab-background-hover;
56 | }
57 | }
58 |
59 | &__tab--selected {
60 | background: $re-tab-background-selected;
61 |
62 | &:hover {
63 | background: $re-tab-background-selected;
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-elements",
3 | "version": "1.3.1",
4 | "description": "A library of reusable React components.",
5 | "main": "index.js",
6 | "homepage": "https://github.com/willdady/react-elements",
7 | "repository": {
8 | "type": "git",
9 | "url": "git@github.com:willdady/react-elements.git"
10 | },
11 | "scripts": {
12 | "storybook": "start-storybook -p 9001",
13 | "deploy-storybook": "storybook-to-ghpages"
14 | },
15 | "keywords": [
16 | "react",
17 | "react-component",
18 | "color picker",
19 | "video",
20 | "input",
21 | "loading spinner",
22 | "pagination",
23 | "panel",
24 | "progress bar",
25 | "switch",
26 | "tabs"
27 | ],
28 | "author": "Will Dady",
29 | "license": "MIT",
30 | "dependencies": {
31 | "classnames": "^2.2.3",
32 | "lodash": "^4.11.1",
33 | "react": "^15.0.1",
34 | "react-dom": "^15.0.1",
35 | "tinycolor2": "^1.0.0"
36 | },
37 | "peerDependencies": {
38 | "react": ">=0.14.0"
39 | },
40 | "devDependencies": {
41 | "@kadira/storybook": "^1.27.0",
42 | "@kadira/storybook-deployer": "^1.0.0",
43 | "autoprefixer": "^6.0.3",
44 | "autoprefixer-loader": "^3.2.0",
45 | "babel-cli": "^6.7.5",
46 | "babel-core": "^6.7.6",
47 | "babel-eslint": "^6.0.4",
48 | "babel-loader": "^6.2.4",
49 | "babel-preset-es2015": "^6.6.0",
50 | "babel-preset-react": "^6.5.0",
51 | "css-loader": "^0.23.1",
52 | "eslint": "^2.10.2",
53 | "eslint-plugin-react": "^5.1.1",
54 | "file-loader": "^0.8.5",
55 | "grunt": "^0.4.5",
56 | "grunt-babel": "^6.0.0",
57 | "grunt-contrib-clean": "^1.0.0",
58 | "grunt-contrib-copy": "^1.0.0",
59 | "grunt-contrib-sass": "^0.9.2",
60 | "grunt-postcss": "^0.7.0",
61 | "load-grunt-tasks": "^3.3.0",
62 | "node-sass": "^3.3.3",
63 | "sass-loader": "^3.2.0",
64 | "style-loader": "^0.13.1"
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/styles/Panel.scss:
--------------------------------------------------------------------------------
1 | .re-panel {
2 | $re-panel-background: #fff !default;
3 | $re-panel-border: 1px solid #eee !default;
4 | $re-panel-padding: 15px !default;
5 | $re-panel-border-radius: 4px !default;
6 | $re-panel-header-color: inherit !default;
7 | $re-panel-header-background: #e7e7e7 !default;
8 | $re-panel-footer-background: #e7e7e7 !default;
9 | $re-panel-footer-color: inherit !default;
10 | $re-panel-font-family: Arial, serif !default;
11 | $re-panel-margin-bottom: 20px !default;
12 |
13 | background: $re-panel-background;
14 | border: $re-panel-border;
15 | border-radius: $re-panel-border-radius;
16 | margin-bottom: $re-panel-margin-bottom;
17 | font-family: $re-panel-font-family;
18 |
19 | &__header {
20 | color: $re-panel-header-color;
21 | padding: $re-panel-padding;
22 | border-top-left-radius: $re-panel-border-radius;
23 | border-top-right-radius: $re-panel-border-radius;
24 | background: $re-panel-header-background;
25 |
26 | &--text-left {
27 | text-align: left;
28 | }
29 |
30 | &--text-center {
31 | text-align: center;
32 | }
33 |
34 | &--text-right {
35 | text-align: right;
36 | }
37 | }
38 |
39 | &__header-heading {
40 | margin: 0;
41 | padding: 0;
42 | }
43 |
44 | &__content {
45 | padding: $re-panel-padding;
46 |
47 | p {
48 | margin-top: 0;
49 | }
50 |
51 | p:last-child {
52 | margin-bottom: 0;
53 | }
54 | }
55 |
56 | &__footer {
57 | color: $re-panel-footer-color;
58 | padding: $re-panel-padding;
59 | border-bottom-left-radius: $re-panel-border-radius;
60 | border-bottom-right-radius: $re-panel-border-radius;
61 | background: $re-panel-footer-background;
62 |
63 | &--text-left {
64 | text-align: left;
65 | }
66 |
67 | &--text-center {
68 | text-align: center;
69 | }
70 |
71 | &--text-right {
72 | text-align: right;
73 | }
74 | }
75 |
76 | }
77 |
--------------------------------------------------------------------------------
/src/Tabs.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import classnames from 'classnames';
3 |
4 |
5 | const Tab = React.createClass({
6 |
7 | propTypes: {
8 | tabIndex: PropTypes.number,
9 | selected: PropTypes.bool,
10 | onClick: PropTypes.func.isRequired
11 | },
12 |
13 | onClick: function () {
14 | this.props.onClick(this.props.tabIndex);
15 | },
16 |
17 | render: function() {
18 | let className = classnames(
19 | 're-tabs__tab',
20 | {
21 | 're-tabs__tab--selected': this.props.selected
22 | }
23 | );
24 | return (
25 |
26 | {this.props.children}
27 |
28 | );
29 | }
30 |
31 | });
32 |
33 |
34 | const Tabs = React.createClass({
35 |
36 | propTypes: {
37 | initialIndex: PropTypes.number,
38 | block: PropTypes.bool,
39 | onChange: PropTypes.func.isRequired,
40 | children: PropTypes.arrayOf(PropTypes.element).isRequired
41 | },
42 |
43 | getInitialState: function () {
44 | return {
45 | selectedIndex: this.props.initialIndex || 0
46 | };
47 | },
48 |
49 | onTabClick: function (selectedIndex) {
50 | this.setState({ selectedIndex }, () => this.props.onChange(selectedIndex));
51 | },
52 |
53 | render: function () {
54 | let tabs = this.props.children.map((child, i) => {
55 | return (
56 |
62 | {child}
63 |
64 | );
65 | });
66 |
67 | let className = classnames(
68 | 're-tabs',
69 | {
70 | 're-tabs--block': this.props.block
71 | },
72 | this.props.className
73 | );
74 | return (
75 |
78 | );
79 | }
80 |
81 |
82 | });
83 |
84 |
85 | export default Tabs;
86 |
--------------------------------------------------------------------------------
/dist/Switch.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _react = require('react');
8 |
9 | var _react2 = _interopRequireDefault(_react);
10 |
11 | var _classnames = require('classnames');
12 |
13 | var _classnames2 = _interopRequireDefault(_classnames);
14 |
15 | var _values = require('lodash/values');
16 |
17 | var _values2 = _interopRequireDefault(_values);
18 |
19 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
20 |
21 | var SIZES = {
22 | EXTRA_SMALL: 'xs',
23 | SMALL: 'sm',
24 | LARGE: 'lg'
25 | };
26 |
27 | var Switch = _react2.default.createClass({
28 | displayName: 'Switch',
29 |
30 |
31 | propTypes: {
32 | name: _react.PropTypes.string,
33 | value: _react.PropTypes.bool.isRequired,
34 | size: _react.PropTypes.oneOf((0, _values2.default)(SIZES)),
35 | onClick: _react.PropTypes.func
36 | },
37 |
38 | onClick: function onClick() {
39 | if (!this.props.onClick) return;
40 | this.props.onClick(this.props.value, this.props.name);
41 | },
42 |
43 | render: function render() {
44 | var className = (0, _classnames2.default)({
45 | 're-switch': true,
46 | 're-switch--on': this.props.value,
47 | 're-switch--xs': this.props.size === SIZES.EXTRA_SMALL,
48 | 're-switch--sm': this.props.size === SIZES.SMALL,
49 | 're-switch--lg': this.props.size === SIZES.LARGE
50 | });
51 | return _react2.default.createElement(
52 | 'span',
53 | { className: className },
54 | _react2.default.createElement('input', {
55 | type: 'checkbox',
56 | name: this.props.name,
57 | className: 're-switch__checkbox',
58 | checked: this.props.value,
59 | onClick: this.onClick,
60 | readOnly: true
61 | }),
62 | _react2.default.createElement('span', { className: 're-switch__switch' })
63 | );
64 | }
65 | });
66 |
67 | exports.default = Switch;
68 |
--------------------------------------------------------------------------------
/styles/LoadingSpinner.scss:
--------------------------------------------------------------------------------
1 | .re-loading-spinner {
2 | $re-loading-spinner-size: 80px !default;
3 | $re-loading-spinner-border-width: $re-loading-spinner-size * .2 !default;
4 | $re-loading-spinner-background-color: rgba(#ccc, .2) !default;
5 | $re-loading-spinner-foreground-color: #337ab7 !default;
6 |
7 | animation-name: re-spinner-animation;
8 | animation-duration: 1s;
9 | animation-timing-function: linear;
10 | animation-iteration-count: infinite;
11 |
12 | box-sizing: border-box;
13 |
14 | border-bottom-color: $re-loading-spinner-background-color;
15 | border-left-color: $re-loading-spinner-foreground-color;
16 | border-right-color: $re-loading-spinner-background-color;
17 | border-top-color: $re-loading-spinner-background-color;
18 | border-width: $re-loading-spinner-border-width;
19 | border-style: solid;
20 |
21 | font-size: 10px;
22 | text-indent: -9999em;
23 | transform: translateZ(0);
24 | overflow: hidden;
25 |
26 | &,
27 | &::after {
28 | border-radius: 50%;
29 | height: $re-loading-spinner-size;
30 | width: $re-loading-spinner-size;
31 | }
32 |
33 | &--small {
34 | $re-loading-spinner-size: 40px;
35 | $re-loading-spinner-border-width: $re-loading-spinner-size * .2;
36 | border-width: $re-loading-spinner-border-width;
37 |
38 | &,
39 | &::after {
40 | height: $re-loading-spinner-size;
41 | width: $re-loading-spinner-size;
42 | }
43 | }
44 |
45 | &--mini {
46 | $re-loading-spinner-size: 20px;
47 | $re-loading-spinner-border-width: $re-loading-spinner-size * .2;
48 | border-width: $re-loading-spinner-border-width;
49 |
50 | &,
51 | &::after {
52 | height: $re-loading-spinner-size;
53 | width: $re-loading-spinner-size;
54 | }
55 | }
56 |
57 | &--slow {
58 | animation-duration: 2s;
59 | }
60 |
61 | &--fast {
62 | animation-duration: .5s;
63 | }
64 | }
65 |
66 | @keyframes re-spinner-animation {
67 | 0% {
68 | transform: rotate(0deg);
69 | }
70 |
71 | 100% {
72 | transform: rotate(360deg);
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/dist/css/LoadingSpinner.css:
--------------------------------------------------------------------------------
1 | .re-loading-spinner {
2 | -webkit-animation-name: re-spinner-animation;
3 | animation-name: re-spinner-animation;
4 | -webkit-animation-duration: 1s;
5 | animation-duration: 1s;
6 | -webkit-animation-timing-function: linear;
7 | animation-timing-function: linear;
8 | -webkit-animation-iteration-count: infinite;
9 | animation-iteration-count: infinite;
10 | box-sizing: border-box;
11 | border-bottom-color: rgba(204, 204, 204, 0.2);
12 | border-left-color: #337ab7;
13 | border-right-color: rgba(204, 204, 204, 0.2);
14 | border-top-color: rgba(204, 204, 204, 0.2);
15 | border-width: 16px;
16 | border-style: solid;
17 | font-size: 10px;
18 | text-indent: -9999em;
19 | -webkit-transform: translateZ(0);
20 | transform: translateZ(0);
21 | overflow: hidden; }
22 | .re-loading-spinner, .re-loading-spinner::after {
23 | border-radius: 50%;
24 | height: 80px;
25 | width: 80px; }
26 | .re-loading-spinner--small {
27 | border-width: 8px; }
28 | .re-loading-spinner--small, .re-loading-spinner--small::after {
29 | height: 40px;
30 | width: 40px; }
31 | .re-loading-spinner--mini {
32 | border-width: 4px; }
33 | .re-loading-spinner--mini, .re-loading-spinner--mini::after {
34 | height: 20px;
35 | width: 20px; }
36 | .re-loading-spinner--slow {
37 | -webkit-animation-duration: 2s;
38 | animation-duration: 2s; }
39 | .re-loading-spinner--fast {
40 | -webkit-animation-duration: .5s;
41 | animation-duration: .5s; }
42 |
43 | @-webkit-keyframes re-spinner-animation {
44 | 0% {
45 | -webkit-transform: rotate(0deg);
46 | transform: rotate(0deg); }
47 | 100% {
48 | -webkit-transform: rotate(360deg);
49 | transform: rotate(360deg); } }
50 |
51 | @keyframes re-spinner-animation {
52 | 0% {
53 | -webkit-transform: rotate(0deg);
54 | transform: rotate(0deg); }
55 | 100% {
56 | -webkit-transform: rotate(360deg);
57 | transform: rotate(360deg); } }
58 |
59 | /*# sourceMappingURL=LoadingSpinner.css.map */
--------------------------------------------------------------------------------
/src/VimeoVideo.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import assign from 'lodash/assign';
3 |
4 |
5 | const DEFAULT_VIMEO_PARAMS = {
6 | autopause: 1,
7 | autoplay: 0,
8 | badge: 1,
9 | byline: 1,
10 | color: '00adef',
11 | loop: 0,
12 | player_id: null,
13 | portrait: 1,
14 | title: 1
15 | };
16 |
17 |
18 | const VimeoVideo = React.createClass({
19 |
20 | getDefaultProps: function() {
21 | return assign(
22 | {
23 | width: 500,
24 | height: 281,
25 | frameBorder: 0,
26 | protocol: null
27 | },
28 | DEFAULT_VIMEO_PARAMS
29 | );
30 | },
31 |
32 | propTypes: {
33 | src: PropTypes.string.isRequired,
34 | width: PropTypes.number,
35 | height: PropTypes.number,
36 | frameBorder: PropTypes.number,
37 | protocol: PropTypes.oneOf(['http', 'https'])
38 | },
39 |
40 | getCleanedSrc: function() {
41 | var matches, vidID, src, protocol;
42 |
43 | src = this.props.src.trim();
44 | protocol = this.props.protocol ? this.props.protocol + ':' : '';
45 |
46 | // Extract video id from src.
47 | var pageURLRegexp = /.*vimeo\.com\/(\w+)$/g;
48 | matches = pageURLRegexp.exec(src);
49 | if (matches) {
50 | vidID = matches[1];
51 | } else {
52 | var embedURLRegexp = /.*video\/(\w+)$/g;
53 | matches = embedURLRegexp.exec(src);
54 | if (matches) {
55 | vidID = matches[1];
56 | }
57 | }
58 | if (!vidID) throw 'Unable to extract Video ID from URL.';
59 | // Build URL parameters
60 | var params = '';
61 | for (var k in DEFAULT_VIMEO_PARAMS) {
62 | if (this.props[k] !== DEFAULT_VIMEO_PARAMS[k]) {
63 | params += '&' + k + '=' + String(this.props[k]);
64 | }
65 | }
66 | params = params.replace('&', '?');
67 |
68 | return protocol + '//player.vimeo.com/video/' + vidID + params;
69 | },
70 |
71 | render: function() {
72 | return (
73 |
80 | );
81 | }
82 | });
83 |
84 |
85 | export default VimeoVideo;
86 |
--------------------------------------------------------------------------------
/dist/css/Switch.css:
--------------------------------------------------------------------------------
1 | .re-switch {
2 | display: inline-block;
3 | width: 51px;
4 | height: 34px;
5 | border-radius: 25.5px;
6 | background: #ccc;
7 | position: relative;
8 | border-color: #ccc;
9 | border-width: 1px;
10 | border-style: solid;
11 | transition: background-color 80ms ease-in-out, border-color 80ms ease-in-out;
12 | box-sizing: border-box; }
13 | .re-switch--xs {
14 | width: 30px;
15 | height: 20px;
16 | border-radius: 15px; }
17 | .re-switch--xs .re-switch__switch {
18 | width: 18px;
19 | height: 18px; }
20 | .re-switch--xs .re-switch__checkbox {
21 | width: 30px;
22 | height: 20px; }
23 | .re-switch--xs.re-switch--on .re-switch__switch {
24 | left: 10px; }
25 | .re-switch--sm {
26 | width: 45px;
27 | height: 30px;
28 | border-radius: 22.5px; }
29 | .re-switch--sm .re-switch__switch {
30 | width: 28px;
31 | height: 28px; }
32 | .re-switch--sm .re-switch__checkbox {
33 | width: 45px;
34 | height: 30px; }
35 | .re-switch--sm.re-switch--on .re-switch__switch {
36 | left: 15px; }
37 | .re-switch--lg {
38 | width: 69px;
39 | height: 46px;
40 | border-radius: 34.5px; }
41 | .re-switch--lg .re-switch__switch {
42 | width: 44px;
43 | height: 44px; }
44 | .re-switch--lg .re-switch__checkbox {
45 | width: 69px;
46 | height: 46px; }
47 | .re-switch--lg.re-switch--on .re-switch__switch {
48 | left: 23px; }
49 | .re-switch__switch {
50 | display: block;
51 | width: 32px;
52 | height: 32px;
53 | border-radius: 50%;
54 | background: #fff;
55 | z-index: 1;
56 | position: relative;
57 | left: 0;
58 | transition: left 80ms ease-in-out;
59 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
60 | box-sizing: border-box; }
61 | .re-switch__checkbox {
62 | margin: 0;
63 | padding: 0;
64 | position: absolute;
65 | z-index: 2;
66 | width: 51px;
67 | height: 34px;
68 | top: -1px;
69 | left: -1px;
70 | box-sizing: border-box;
71 | opacity: 0;
72 | cursor: pointer; }
73 | .re-switch--on {
74 | border-color: #9dda83;
75 | background-color: #9dda83; }
76 | .re-switch--on .re-switch__switch {
77 | left: 17px; }
78 |
79 | /*# sourceMappingURL=Switch.css.map */
--------------------------------------------------------------------------------
/styles/Switch.scss:
--------------------------------------------------------------------------------
1 | @import 'mixins';
2 |
3 | .re-switch {
4 | $re-switch-border-width: 1px !default;
5 | $re-switch-off-color: #ccc !default;
6 | $re-switch-on-color: #9dda83 !default;
7 | // Default
8 | $re-switch-height: 34px !default;
9 | $re-switch-width: $re-switch-height * 1.5 !default;
10 | // Extra small
11 | $re-switch-height-xs: 20px !default;
12 | $re-switch-width-xs: $re-switch-height-xs * 1.5 !default;
13 | // Small
14 | $re-switch-height-sm: 30px !default;
15 | $re-switch-width-sm: $re-switch-height-sm * 1.5 !default;
16 | // Large
17 | $re-switch-height-lg: 46px !default;
18 | $re-switch-width-lg: $re-switch-height-lg * 1.5 !default;
19 |
20 | @include re-switch('xs', $re-switch-width-xs, $re-switch-height-xs, $re-switch-border-width);
21 | @include re-switch('sm', $re-switch-width-sm, $re-switch-height-sm, $re-switch-border-width);
22 | @include re-switch('lg', $re-switch-width-lg, $re-switch-height-lg, $re-switch-border-width);
23 |
24 | display: inline-block;
25 | width: $re-switch-width;
26 | height: $re-switch-height;
27 | border-radius: $re-switch-width * .5;
28 | background: $re-switch-off-color;
29 | position: relative;
30 | border-color: $re-switch-off-color;
31 | border-width: $re-switch-border-width;
32 | border-style: solid;
33 | transition: background-color 80ms ease-in-out, border-color 80ms ease-in-out;
34 | box-sizing: border-box;
35 |
36 | &__switch {
37 | display: block;
38 | width: $re-switch-height - ($re-switch-border-width * 2);
39 | height: $re-switch-height - ($re-switch-border-width * 2);
40 | border-radius: 50%;
41 | background: #fff;
42 | z-index: 1;
43 | position: relative;
44 | left: 0;
45 | transition: left 80ms ease-in-out;
46 | box-shadow: 0 1px 1px rgba(0, 0, 0, .2);
47 | box-sizing: border-box;
48 | }
49 |
50 | &__checkbox {
51 | margin: 0;
52 | padding: 0;
53 | position: absolute;
54 | z-index: 2;
55 | width: $re-switch-width;
56 | height: $re-switch-height;
57 | top: -$re-switch-border-width;
58 | left: -$re-switch-border-width;
59 | box-sizing: border-box;
60 | opacity: 0;
61 | cursor: pointer;
62 | }
63 |
64 | &--on {
65 | border-color: $re-switch-on-color;
66 | background-color: $re-switch-on-color;
67 |
68 | .re-switch__switch {
69 | left: $re-switch-width - $re-switch-height;
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/styles/ColorPicker.scss:
--------------------------------------------------------------------------------
1 | .re-color-picker {
2 | // scss-lint:disable SpaceAfterComma
3 | $re-color-picker-sb-width: 128px !default;
4 | $re-color-picker-sb-height: $re-color-picker-sb-width !default;
5 | $re-color-picker-sb-cursor-width: 14px !default;
6 | $re-color-picker-sb-cursor-height: $re-color-picker-sb-cursor-width !default;
7 | $re-color-picker-h-height: 128px !default;
8 | $re-color-picker-h-width: 26px !default;
9 | $re-color-picker-h-cursor-height: 3px !default;
10 | $re-color-picker-h-cursor-border: 1px solid #fff !default;
11 |
12 | &__sb-picker {
13 | display: inline-block;
14 | position: relative;
15 | width: $re-color-picker-sb-width;
16 | height: $re-color-picker-sb-height;
17 | overflow: hidden;
18 | margin-right: 3px;
19 | }
20 |
21 | &__sb-picker--dragging {
22 | cursor: pointer;
23 | }
24 |
25 | &__sb-picker-layer {
26 | position: absolute;
27 | top: 0;
28 | left: 0;
29 | width: $re-color-picker-sb-width;
30 | height: $re-color-picker-sb-height;
31 | }
32 |
33 |
34 | &__sb-picker-cursor {
35 | position: absolute;
36 | width: $re-color-picker-sb-cursor-width;
37 | height: $re-color-picker-sb-cursor-height;
38 | background: transparent;
39 | top: 0;
40 | left: 0;
41 | pointer-events: none;
42 | border-radius: 50%;
43 | border: 1px solid #fff;
44 | box-shadow: 0 0 0 1px #000;
45 | }
46 |
47 | &__h-picker {
48 | position: relative;
49 | display: inline-block;
50 | width: $re-color-picker-h-width;
51 | height: $re-color-picker-h-height;
52 | background: linear-gradient(
53 | hsl(0, 100%, 50%) 0%,
54 | hsl(35, 100%, 50%) 10%,
55 | hsl(71, 100%, 50%) 20%,
56 | hsl(107, 100%, 50%) 30%,
57 | hsl(143, 100%, 50%) 40%,
58 | hsl(179, 100%, 50%) 50%,
59 | hsl(215, 100%, 50%) 60%,
60 | hsl(252, 100%, 50%) 70%,
61 | hsl(288, 100%, 50%) 80%,
62 | hsl(324, 100%, 50%) 90%,
63 | hsl(359, 100%, 50%) 100%
64 | );
65 | }
66 |
67 | &__h-picker--dragging {
68 | cursor: pointer;
69 | }
70 |
71 | &__h-picker-cursor {
72 | width: $re-color-picker-h-width;
73 | height: $re-color-picker-h-cursor-height;
74 | border: $re-color-picker-h-cursor-border;
75 | background: transparent;
76 | position: absolute;
77 | top: 0;
78 | left: 0;
79 | pointer-events: none;
80 | }
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/dist/Tabs.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _react = require('react');
8 |
9 | var _react2 = _interopRequireDefault(_react);
10 |
11 | var _classnames = require('classnames');
12 |
13 | var _classnames2 = _interopRequireDefault(_classnames);
14 |
15 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
16 |
17 | var Tab = _react2.default.createClass({
18 | displayName: 'Tab',
19 |
20 |
21 | propTypes: {
22 | tabIndex: _react.PropTypes.number,
23 | selected: _react.PropTypes.bool,
24 | onClick: _react.PropTypes.func.isRequired
25 | },
26 |
27 | onClick: function onClick() {
28 | this.props.onClick(this.props.tabIndex);
29 | },
30 |
31 | render: function render() {
32 | var className = (0, _classnames2.default)('re-tabs__tab', {
33 | 're-tabs__tab--selected': this.props.selected
34 | });
35 | return _react2.default.createElement(
36 | 'li',
37 | { className: className, onClick: this.onClick },
38 | this.props.children
39 | );
40 | }
41 |
42 | });
43 |
44 | var Tabs = _react2.default.createClass({
45 | displayName: 'Tabs',
46 |
47 |
48 | propTypes: {
49 | initialIndex: _react.PropTypes.number,
50 | block: _react.PropTypes.bool,
51 | onChange: _react.PropTypes.func.isRequired,
52 | children: _react.PropTypes.arrayOf(_react.PropTypes.element).isRequired
53 | },
54 |
55 | getInitialState: function getInitialState() {
56 | return {
57 | selectedIndex: this.props.initialIndex || 0
58 | };
59 | },
60 |
61 | onTabClick: function onTabClick(selectedIndex) {
62 | var _this = this;
63 |
64 | this.setState({ selectedIndex: selectedIndex }, function () {
65 | return _this.props.onChange(selectedIndex);
66 | });
67 | },
68 |
69 | render: function render() {
70 | var _this2 = this;
71 |
72 | var tabs = this.props.children.map(function (child, i) {
73 | return _react2.default.createElement(
74 | Tab,
75 | {
76 | key: i,
77 | tabIndex: i,
78 | selected: i === _this2.state.selectedIndex,
79 | onClick: _this2.onTabClick
80 | },
81 | child
82 | );
83 | });
84 |
85 | var className = (0, _classnames2.default)('re-tabs', {
86 | 're-tabs--block': this.props.block
87 | }, this.props.className);
88 | return _react2.default.createElement(
89 | 'ul',
90 | { className: className },
91 | tabs
92 | );
93 | }
94 |
95 | });
96 |
97 | exports.default = Tabs;
98 |
--------------------------------------------------------------------------------
/dist/VimeoVideo.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _react = require('react');
8 |
9 | var _react2 = _interopRequireDefault(_react);
10 |
11 | var _assign = require('lodash/assign');
12 |
13 | var _assign2 = _interopRequireDefault(_assign);
14 |
15 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
16 |
17 | var DEFAULT_VIMEO_PARAMS = {
18 | autopause: 1,
19 | autoplay: 0,
20 | badge: 1,
21 | byline: 1,
22 | color: '00adef',
23 | loop: 0,
24 | player_id: null,
25 | portrait: 1,
26 | title: 1
27 | };
28 |
29 | var VimeoVideo = _react2.default.createClass({
30 | displayName: 'VimeoVideo',
31 |
32 |
33 | getDefaultProps: function getDefaultProps() {
34 | return (0, _assign2.default)({
35 | width: 500,
36 | height: 281,
37 | frameBorder: 0,
38 | protocol: null
39 | }, DEFAULT_VIMEO_PARAMS);
40 | },
41 |
42 | propTypes: {
43 | src: _react.PropTypes.string.isRequired,
44 | width: _react.PropTypes.number,
45 | height: _react.PropTypes.number,
46 | frameBorder: _react.PropTypes.number,
47 | protocol: _react.PropTypes.oneOf(['http', 'https'])
48 | },
49 |
50 | getCleanedSrc: function getCleanedSrc() {
51 | var matches, vidID, src, protocol;
52 |
53 | src = this.props.src.trim();
54 | protocol = this.props.protocol ? this.props.protocol + ':' : '';
55 |
56 | // Extract video id from src.
57 | var pageURLRegexp = /.*vimeo\.com\/(\w+)$/g;
58 | matches = pageURLRegexp.exec(src);
59 | if (matches) {
60 | vidID = matches[1];
61 | } else {
62 | var embedURLRegexp = /.*video\/(\w+)$/g;
63 | matches = embedURLRegexp.exec(src);
64 | if (matches) {
65 | vidID = matches[1];
66 | }
67 | }
68 | if (!vidID) throw 'Unable to extract Video ID from URL.';
69 | // Build URL parameters
70 | var params = '';
71 | for (var k in DEFAULT_VIMEO_PARAMS) {
72 | if (this.props[k] !== DEFAULT_VIMEO_PARAMS[k]) {
73 | params += '&' + k + '=' + String(this.props[k]);
74 | }
75 | }
76 | params = params.replace('&', '?');
77 |
78 | return protocol + '//player.vimeo.com/video/' + vidID + params;
79 | },
80 |
81 | render: function render() {
82 | return _react2.default.createElement('iframe', {
83 | width: this.props.width,
84 | height: this.props.height,
85 | src: this.getCleanedSrc(),
86 | frameBorder: this.props.frameBorder,
87 | allowFullScreen: true
88 | });
89 | }
90 | });
91 |
92 | exports.default = VimeoVideo;
93 |
--------------------------------------------------------------------------------
/dist/css/Button.css:
--------------------------------------------------------------------------------
1 | .re-btn {
2 | background-image: none;
3 | border: 1px solid transparent;
4 | border-radius: 4px;
5 | cursor: pointer;
6 | display: inline-block;
7 | font-size: 14px;
8 | font-weight: 400;
9 | line-height: 1.42857143;
10 | margin-bottom: 0;
11 | padding: 6px 12px;
12 | position: relative;
13 | text-align: center;
14 | -ms-touch-action: manipulation;
15 | touch-action: manipulation;
16 | -webkit-user-select: none;
17 | -moz-user-select: none;
18 | -ms-user-select: none;
19 | user-select: none;
20 | vertical-align: middle;
21 | white-space: nowrap; }
22 | .re-btn:focus {
23 | outline: none; }
24 | .re-btn__spinner {
25 | left: 50%;
26 | margin-left: -10px;
27 | margin-top: -10px;
28 | position: absolute !important;
29 | top: 50%; }
30 | .re-btn--default {
31 | background: #f7f7f7;
32 | border-color: #eaeaea;
33 | color: #444; }
34 | .re-btn--default:hover {
35 | background: white;
36 | border-color: #f7f7f7; }
37 | .re-btn--default:disabled {
38 | color: rgba(68, 68, 68, 0.55); }
39 | .re-btn--default:disabled:hover {
40 | background: #f7f7f7;
41 | border-color: #eaeaea; }
42 | .re-btn--primary {
43 | background: #337ab7;
44 | border-color: #2d6da3;
45 | color: #fff; }
46 | .re-btn--primary:hover {
47 | background: #3b87c8;
48 | border-color: #337ab7; }
49 | .re-btn--primary:disabled {
50 | color: rgba(255, 255, 255, 0.55); }
51 | .re-btn--primary:disabled:hover {
52 | background: #337ab7;
53 | border-color: #2d6da3; }
54 | .re-btn--danger {
55 | background: #d9534f;
56 | border-color: #d43f3a;
57 | color: #fff; }
58 | .re-btn--danger:hover {
59 | background: #de6764;
60 | border-color: #d9534f; }
61 | .re-btn--danger:disabled {
62 | color: rgba(255, 255, 255, 0.55); }
63 | .re-btn--danger:disabled:hover {
64 | background: #d9534f;
65 | border-color: #d43f3a; }
66 | .re-btn--link {
67 | background: transparent;
68 | border-color: transparent;
69 | color: #000; }
70 | .re-btn--link:hover {
71 | background: rgba(13, 13, 13, 0);
72 | border-color: transparent; }
73 | .re-btn--link:disabled {
74 | color: rgba(0, 0, 0, 0.55); }
75 | .re-btn--link:disabled:hover {
76 | background: transparent;
77 | border-color: transparent; }
78 | .re-btn--block {
79 | display: block;
80 | width: 100%; }
81 | .re-btn--processing .re-btn__content {
82 | visibility: hidden; }
83 |
84 | /*# sourceMappingURL=Button.css.map */
--------------------------------------------------------------------------------
/dist/Button.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
8 |
9 | var _react = require('react');
10 |
11 | var _react2 = _interopRequireDefault(_react);
12 |
13 | var _classnames = require('classnames');
14 |
15 | var _classnames2 = _interopRequireDefault(_classnames);
16 |
17 | var _LoadingSpinner = require('./LoadingSpinner');
18 |
19 | var _LoadingSpinner2 = _interopRequireDefault(_LoadingSpinner);
20 |
21 | var _omit = require('lodash/omit');
22 |
23 | var _omit2 = _interopRequireDefault(_omit);
24 |
25 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
26 |
27 | var Button = _react2.default.createClass({
28 | displayName: 'Button',
29 |
30 |
31 | propTypes: {
32 | btnStyle: _react.PropTypes.string,
33 | block: _react.PropTypes.bool,
34 | processing: _react.PropTypes.bool,
35 | loadingSpinnerColor: _react.PropTypes.string,
36 | loadingSpinnerBackgroundColor: _react.PropTypes.string
37 | },
38 |
39 | render: function render() {
40 | var btnStyle = this.props.btnStyle;
41 | var className = (0, _classnames2.default)('re-btn', {
42 | 're-btn--default': !btnStyle || btnStyle === 'default',
43 | 're-btn--primary': btnStyle === 'primary',
44 | 're-btn--danger': btnStyle === 'danger',
45 | 're-btn--link': btnStyle === 'link',
46 | 're-btn--block': this.props.block,
47 | 're-btn--processing': this.props.processing
48 | }, this.props.className);
49 | var props = (0, _omit2.default)(this.props, ['btnStyle', 'block', 'processing']);
50 | props.type = props.type ? props.type : 'button';
51 |
52 | var spinner = void 0;
53 | if (this.props.processing) {
54 | spinner = _react2.default.createElement(_LoadingSpinner2.default, {
55 | className: 're-btn__spinner',
56 | color: this.props.loadingSpinnerColor || '#fff',
57 | backgroundColor: this.props.loadingSpinnerBackgroundColor,
58 | mini: true
59 | });
60 | }
61 |
62 | return _react2.default.createElement(
63 | 'button',
64 | _extends({}, props, { className: className }),
65 | spinner,
66 | _react2.default.createElement(
67 | 'span',
68 | { className: 're-btn__content' },
69 | this.props.children
70 | )
71 | );
72 | }
73 |
74 | });
75 |
76 | exports.default = Button;
77 |
--------------------------------------------------------------------------------
/src/YoutubeVideo.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import assign from 'lodash/assign';
3 |
4 |
5 | /* The following params are the defaults as documented at
6 | https://developers.google.com/youtube/player_parameters*/
7 | const DEFAULT_YOUTUBE_PARAMS = {
8 | autohide: 2,
9 | autoplay: 0,
10 | cc_load_policy: 0,
11 | color: null,
12 | controls: 1,
13 | disablekb: 0,
14 | enablejsapi: 0,
15 | end: null,
16 | fs: 1,
17 | hl: null,
18 | iv_load_policy: 1,
19 | list: null,
20 | listType: null,
21 | loop: 0,
22 | modestbranding: null,
23 | origin: null,
24 | playerapiid: null,
25 | playlist: null,
26 | playsinline: 0,
27 | rel: 1,
28 | showinfo: 1,
29 | start: null,
30 | theme: null
31 | };
32 |
33 |
34 | const YouTubeVideo = React.createClass({
35 |
36 | getDefaultProps: function() {
37 | return assign(
38 | {},
39 | DEFAULT_YOUTUBE_PARAMS,
40 | {
41 | width: 560,
42 | height: 315,
43 | frameBorder: 0,
44 | protocol: null
45 | }
46 | );
47 | },
48 |
49 | propTypes: {
50 | src: PropTypes.string.isRequired,
51 | width: PropTypes.number,
52 | height: PropTypes.number,
53 | frameBorder: PropTypes.number,
54 | protocol: PropTypes.oneOf(['http', 'https'])
55 | },
56 |
57 | getCleanedSrc: function() {
58 | // var matches, vidID, src, protocol;
59 | let vidID;
60 | let src = this.props.src.trim();
61 | let protocol = this.props.protocol ? this.props.protocol + ':' : '';
62 |
63 | // Extract video id from src.
64 | let pageURLRegexp = /.*watch\?v=(\w+)$/g;
65 | let matches = pageURLRegexp.exec(src);
66 | if (matches) {
67 | vidID = matches[1];
68 | } else {
69 | let embedURLRegexp = /.*embed\/(\w+)$/g;
70 | matches = embedURLRegexp.exec(src);
71 | if (matches) vidID = matches[1];
72 | }
73 | if (!vidID) throw 'Unable to extract Video ID from URL.';
74 | // Build URL parameters
75 | let params = '';
76 | for (let k in DEFAULT_YOUTUBE_PARAMS) {
77 | if (this.props[k] !== DEFAULT_YOUTUBE_PARAMS[k]) {
78 | params += '&' + k + '=' + String(this.props[k]);
79 | }
80 | }
81 | params = params.replace('&', '?');
82 |
83 | return `${protocol}//www.youtube.com/embed/${vidID}${params}`;
84 | },
85 |
86 | render: function() {
87 | return (
88 |
95 | );
96 | }
97 | });
98 |
99 |
100 | export default YouTubeVideo;
101 |
--------------------------------------------------------------------------------
/src/Panel.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import isString from 'lodash/isString';
3 | import classnames from 'classnames';
4 |
5 |
6 | const PanelHeader = React.createClass({
7 |
8 | propTypes: {
9 | textAlign: PropTypes.oneOf(['left', 'center', 'right'])
10 | },
11 |
12 | render: function() {
13 | let children = this.props.children;
14 | if (isString(children)) {
15 | children = (
16 | { children }
17 | );
18 | }
19 | let className = classnames(
20 | 're-panel__header',
21 | {
22 | 're-panel__header--text-left': this.props.textAlign === 'left',
23 | 're-panel__header--text-center': this.props.textAlign === 'center',
24 | 're-panel__header--text-right': this.props.textAlign === 'right'
25 | }
26 | );
27 | return (
28 |
31 | );
32 | }
33 |
34 | });
35 |
36 |
37 | const PanelFooter = React.createClass({
38 |
39 | propTypes: {
40 | textAlign: PropTypes.oneOf(['left', 'center', 'right'])
41 | },
42 |
43 | render: function() {
44 | let className = classnames(
45 | 're-panel__footer',
46 | {
47 | 're-panel__footer--text-left': this.props.textAlign === 'left',
48 | 're-panel__footer--text-center': this.props.textAlign === 'center',
49 | 're-panel__footer--text-right': this.props.textAlign === 'right'
50 | }
51 | );
52 | return (
53 |
54 | {this.props.children}
55 |
56 | );
57 | }
58 |
59 | });
60 |
61 |
62 | const Panel = React.createClass({
63 |
64 | propTypes: {
65 | header: PropTypes.node,
66 | footer: PropTypes.node,
67 | headerTextAlign: PropTypes.oneOf(['left', 'center', 'right']),
68 | footerTextAlign: PropTypes.oneOf(['left', 'center', 'right'])
69 | },
70 |
71 | render: function() {
72 | let className = classnames('re-panel', this.props.className);
73 |
74 | if (this.props.header) {
75 | var header = (
76 |
77 | {this.props.header}
78 |
79 | );
80 | }
81 |
82 | if (this.props.footer) {
83 | var footer = (
84 |
85 | {this.props.footer}
86 |
87 | );
88 | }
89 |
90 | return (
91 |
92 | {header}
93 |
94 | {this.props.children}
95 |
96 | {footer}
97 |
98 | );
99 | }
100 |
101 | });
102 |
103 |
104 | export default Panel;
105 |
--------------------------------------------------------------------------------
/dist/YoutubeVideo.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _react = require('react');
8 |
9 | var _react2 = _interopRequireDefault(_react);
10 |
11 | var _assign = require('lodash/assign');
12 |
13 | var _assign2 = _interopRequireDefault(_assign);
14 |
15 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
16 |
17 | /* The following params are the defaults as documented at
18 | https://developers.google.com/youtube/player_parameters*/
19 | var DEFAULT_YOUTUBE_PARAMS = {
20 | autohide: 2,
21 | autoplay: 0,
22 | cc_load_policy: 0,
23 | color: null,
24 | controls: 1,
25 | disablekb: 0,
26 | enablejsapi: 0,
27 | end: null,
28 | fs: 1,
29 | hl: null,
30 | iv_load_policy: 1,
31 | list: null,
32 | listType: null,
33 | loop: 0,
34 | modestbranding: null,
35 | origin: null,
36 | playerapiid: null,
37 | playlist: null,
38 | playsinline: 0,
39 | rel: 1,
40 | showinfo: 1,
41 | start: null,
42 | theme: null
43 | };
44 |
45 | var YouTubeVideo = _react2.default.createClass({
46 | displayName: 'YouTubeVideo',
47 |
48 |
49 | getDefaultProps: function getDefaultProps() {
50 | return (0, _assign2.default)({}, DEFAULT_YOUTUBE_PARAMS, {
51 | width: 560,
52 | height: 315,
53 | frameBorder: 0,
54 | protocol: null
55 | });
56 | },
57 |
58 | propTypes: {
59 | src: _react.PropTypes.string.isRequired,
60 | width: _react.PropTypes.number,
61 | height: _react.PropTypes.number,
62 | frameBorder: _react.PropTypes.number,
63 | protocol: _react.PropTypes.oneOf(['http', 'https'])
64 | },
65 |
66 | getCleanedSrc: function getCleanedSrc() {
67 | // var matches, vidID, src, protocol;
68 | var vidID = void 0;
69 | var src = this.props.src.trim();
70 | var protocol = this.props.protocol ? this.props.protocol + ':' : '';
71 |
72 | // Extract video id from src.
73 | var pageURLRegexp = /.*watch\?v=(\w+)$/g;
74 | var matches = pageURLRegexp.exec(src);
75 | if (matches) {
76 | vidID = matches[1];
77 | } else {
78 | var embedURLRegexp = /.*embed\/(\w+)$/g;
79 | matches = embedURLRegexp.exec(src);
80 | if (matches) vidID = matches[1];
81 | }
82 | if (!vidID) throw 'Unable to extract Video ID from URL.';
83 | // Build URL parameters
84 | var params = '';
85 | for (var k in DEFAULT_YOUTUBE_PARAMS) {
86 | if (this.props[k] !== DEFAULT_YOUTUBE_PARAMS[k]) {
87 | params += '&' + k + '=' + String(this.props[k]);
88 | }
89 | }
90 | params = params.replace('&', '?');
91 |
92 | return protocol + '//www.youtube.com/embed/' + vidID + params;
93 | },
94 |
95 | render: function render() {
96 | return _react2.default.createElement('iframe', {
97 | width: this.props.width,
98 | height: this.props.height,
99 | src: this.getCleanedSrc(),
100 | frameBorder: this.props.frameBorder,
101 | allowFullScreen: true
102 | });
103 | }
104 | });
105 |
106 | exports.default = YouTubeVideo;
107 |
--------------------------------------------------------------------------------
/dist/Panel.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _react = require('react');
8 |
9 | var _react2 = _interopRequireDefault(_react);
10 |
11 | var _isString = require('lodash/isString');
12 |
13 | var _isString2 = _interopRequireDefault(_isString);
14 |
15 | var _classnames = require('classnames');
16 |
17 | var _classnames2 = _interopRequireDefault(_classnames);
18 |
19 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
20 |
21 | var PanelHeader = _react2.default.createClass({
22 | displayName: 'PanelHeader',
23 |
24 |
25 | propTypes: {
26 | textAlign: _react.PropTypes.oneOf(['left', 'center', 'right'])
27 | },
28 |
29 | render: function render() {
30 | var children = this.props.children;
31 | if ((0, _isString2.default)(children)) {
32 | children = _react2.default.createElement(
33 | 'h3',
34 | { className: 're-panel__header-heading' },
35 | children
36 | );
37 | }
38 | var className = (0, _classnames2.default)('re-panel__header', {
39 | 're-panel__header--text-left': this.props.textAlign === 'left',
40 | 're-panel__header--text-center': this.props.textAlign === 'center',
41 | 're-panel__header--text-right': this.props.textAlign === 'right'
42 | });
43 | return _react2.default.createElement(
44 | 'header',
45 | { className: className },
46 | children
47 | );
48 | }
49 |
50 | });
51 |
52 | var PanelFooter = _react2.default.createClass({
53 | displayName: 'PanelFooter',
54 |
55 |
56 | propTypes: {
57 | textAlign: _react.PropTypes.oneOf(['left', 'center', 'right'])
58 | },
59 |
60 | render: function render() {
61 | var className = (0, _classnames2.default)('re-panel__footer', {
62 | 're-panel__footer--text-left': this.props.textAlign === 'left',
63 | 're-panel__footer--text-center': this.props.textAlign === 'center',
64 | 're-panel__footer--text-right': this.props.textAlign === 'right'
65 | });
66 | return _react2.default.createElement(
67 | 'footer',
68 | { className: className },
69 | this.props.children
70 | );
71 | }
72 |
73 | });
74 |
75 | var Panel = _react2.default.createClass({
76 | displayName: 'Panel',
77 |
78 |
79 | propTypes: {
80 | header: _react.PropTypes.node,
81 | footer: _react.PropTypes.node,
82 | headerTextAlign: _react.PropTypes.oneOf(['left', 'center', 'right']),
83 | footerTextAlign: _react.PropTypes.oneOf(['left', 'center', 'right'])
84 | },
85 |
86 | render: function render() {
87 | var className = (0, _classnames2.default)('re-panel', this.props.className);
88 |
89 | if (this.props.header) {
90 | var header = _react2.default.createElement(
91 | PanelHeader,
92 | { textAlign: this.props.headerTextAlign },
93 | this.props.header
94 | );
95 | }
96 |
97 | if (this.props.footer) {
98 | var footer = _react2.default.createElement(
99 | PanelFooter,
100 | { textAlign: this.props.footerTextAlign },
101 | this.props.footer
102 | );
103 | }
104 |
105 | return _react2.default.createElement(
106 | 'section',
107 | { className: className },
108 | header,
109 | _react2.default.createElement(
110 | 'div',
111 | { className: 're-panel__content' },
112 | this.props.children
113 | ),
114 | footer
115 | );
116 | }
117 |
118 | });
119 |
120 | exports.default = Panel;
121 |
--------------------------------------------------------------------------------
/src/Pagination.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import range from 'lodash/range';
3 | import classnames from 'classnames';
4 |
5 |
6 | const PaginationItem = React.createClass({
7 |
8 | propTypes: {
9 | onClick: PropTypes.func.isRequired,
10 | value: PropTypes.node,
11 | active: PropTypes.bool
12 | },
13 |
14 | onClick: function (e) {
15 | e.preventDefault();
16 | this.props.onClick(this.props.value);
17 | },
18 |
19 | render: function() {
20 | let className = classnames(
21 | 're-pagination__item',
22 | {
23 | 're-pagination__item--active': this.props.active
24 | },
25 | this.props.className
26 | );
27 | return (
28 |
29 |
34 | {Array.isArray(this.props.value) ? '\u2026' : this.props.value}
35 |
36 |
37 | );
38 | }
39 |
40 | });
41 |
42 |
43 | const Pagination = React.createClass({
44 |
45 | propTypes: {
46 | totalPages: PropTypes.number.isRequired,
47 | currentPage: PropTypes.number.isRequired,
48 | onClick: PropTypes.func.isRequired,
49 | onNext: PropTypes.func,
50 | onPrevious: PropTypes.func
51 | },
52 |
53 | onPrevious: function () {
54 | this.props.onPrevious(this.props.currentPage - 1);
55 | },
56 |
57 | onNext: function () {
58 | this.props.onNext(this.props.currentPage + 1);
59 | },
60 |
61 | render: function() {
62 | // If there's only one page we don't render anything.
63 | if (this.props.totalPages === 1) return null;
64 |
65 | let lastPageNumber = this.props.totalPages + 1;
66 | let pageNumbers = range(1, lastPageNumber);
67 | let maxSize = 12; // Must not be < 12.
68 | if (this.props.totalPages > maxSize) {
69 | // Last, Second-last, Third-last or Forth-last page selected
70 | if (this.props.currentPage >= lastPageNumber - 6) {
71 | pageNumbers = range(lastPageNumber - (maxSize - 2), lastPageNumber);
72 | pageNumbers.unshift(1, [2, lastPageNumber - (maxSize - 2) - 1]);
73 | // First, Second, Third or Forth page selected
74 | } else if (this.props.currentPage <= 6) {
75 | pageNumbers = range(1, maxSize - 1);
76 | pageNumbers.push([maxSize - 1, this.props.totalPages-1], this.props.totalPages);
77 | // Everything else
78 | } else {
79 | pageNumbers = [
80 | 1,
81 | [2, this.props.currentPage - 4],
82 | this.props.currentPage - 3,
83 | this.props.currentPage - 2,
84 | this.props.currentPage - 1,
85 | this.props.currentPage,
86 | this.props.currentPage + 1,
87 | this.props.currentPage + 2,
88 | this.props.currentPage + 3,
89 | [this.props.currentPage + 4, this.props.totalPages - 1],
90 | this.props.totalPages
91 | ];
92 | }
93 | }
94 |
95 | let items = pageNumbers.map((value) => {
96 | return (
97 |
103 | );
104 | });
105 |
106 | if (this.props.onPrevious && this.props.currentPage !== 1) {
107 | items.unshift(
108 |
113 | );
114 | }
115 | if (this.props.onNext && this.props.currentPage !== this.props.totalPages) {
116 | items.push(
117 |
122 | );
123 | }
124 |
125 | let className = classnames('re-pagination', this.props.className);
126 | return (
127 |
130 | );
131 | }
132 |
133 | });
134 |
135 |
136 | export default Pagination;
137 |
--------------------------------------------------------------------------------
/src/stories/Pagination.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { storiesOf, action } from '@kadira/storybook';
3 | import Pagination from '../Pagination';
4 |
5 |
6 | storiesOf('Pagination', module)
7 | .add('default', () => (
8 |
11 | ))
12 | .add('with next and previous', () => (
13 |
18 | ))
19 | .add('with next and previous, first selected', () => (
20 |
25 | ))
26 | .add('with next and previous, last selected', () => (
27 |
32 | ))
33 | .add('3 of 13', () => (
34 |
37 | ))
38 | .add('11 of 13', () => (
39 |
42 | ))
43 | .add('big range', () => (
44 |
47 | ))
48 | .add('big range, last selected', () => (
49 |
52 | ))
53 | .add('big range, second last selected', () => (
54 |
57 | ))
58 | .add('big range, third last selected', () => (
59 |
62 | ))
63 | .add('big range, forth last selected', () => (
64 |
67 | ))
68 | .add('big range, fifth last selected', () => (
69 |
72 | ))
73 | .add('big range, sixth last selected', () => (
74 |
77 | ))
78 | .add('big range, seventh last selected', () => (
79 |
82 | ))
83 | .add('big range, first selected', () => (
84 |
87 | ))
88 | .add('big range, second selected', () => (
89 |
92 | ))
93 | .add('big range, third selected', () => (
94 |
97 | ))
98 | .add('big range, forth selected', () => (
99 |
102 | ))
103 | .add('big range, fifth selected', () => (
104 |
107 | ))
108 | .add('big range, sixth selected', () => (
109 |
112 | ))
113 | .add('big range, seventh selected', () => (
114 |
117 | ))
118 | ;
119 |
--------------------------------------------------------------------------------
/dist/Pagination.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _react = require('react');
8 |
9 | var _react2 = _interopRequireDefault(_react);
10 |
11 | var _range = require('lodash/range');
12 |
13 | var _range2 = _interopRequireDefault(_range);
14 |
15 | var _classnames = require('classnames');
16 |
17 | var _classnames2 = _interopRequireDefault(_classnames);
18 |
19 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
20 |
21 | var PaginationItem = _react2.default.createClass({
22 | displayName: 'PaginationItem',
23 |
24 |
25 | propTypes: {
26 | onClick: _react.PropTypes.func.isRequired,
27 | value: _react.PropTypes.node,
28 | active: _react.PropTypes.bool
29 | },
30 |
31 | onClick: function onClick(e) {
32 | e.preventDefault();
33 | this.props.onClick(this.props.value);
34 | },
35 |
36 | render: function render() {
37 | var className = (0, _classnames2.default)('re-pagination__item', {
38 | 're-pagination__item--active': this.props.active
39 | }, this.props.className);
40 | return _react2.default.createElement(
41 | 'li',
42 | { className: className, key: this.props.value },
43 | _react2.default.createElement(
44 | 'a',
45 | {
46 | className: 're-pagination__item-link',
47 | href: '#',
48 | onClick: this.onClick
49 | },
50 | Array.isArray(this.props.value) ? '…' : this.props.value
51 | )
52 | );
53 | }
54 |
55 | });
56 |
57 | var Pagination = _react2.default.createClass({
58 | displayName: 'Pagination',
59 |
60 |
61 | propTypes: {
62 | totalPages: _react.PropTypes.number.isRequired,
63 | currentPage: _react.PropTypes.number.isRequired,
64 | onClick: _react.PropTypes.func.isRequired,
65 | onNext: _react.PropTypes.func,
66 | onPrevious: _react.PropTypes.func
67 | },
68 |
69 | onPrevious: function onPrevious() {
70 | this.props.onPrevious(this.props.currentPage - 1);
71 | },
72 |
73 | onNext: function onNext() {
74 | this.props.onNext(this.props.currentPage + 1);
75 | },
76 |
77 | render: function render() {
78 | var _this = this;
79 |
80 | // If there's only one page we don't render anything.
81 | if (this.props.totalPages === 1) return null;
82 |
83 | var lastPageNumber = this.props.totalPages + 1;
84 | var pageNumbers = (0, _range2.default)(1, lastPageNumber);
85 | var maxSize = 12; // Must not be < 12.
86 | if (this.props.totalPages > maxSize) {
87 | // Last, Second-last, Third-last or Forth-last page selected
88 | if (this.props.currentPage >= lastPageNumber - 6) {
89 | pageNumbers = (0, _range2.default)(lastPageNumber - (maxSize - 2), lastPageNumber);
90 | pageNumbers.unshift(1, [2, lastPageNumber - (maxSize - 2) - 1]);
91 | // First, Second, Third or Forth page selected
92 | } else if (this.props.currentPage <= 6) {
93 | pageNumbers = (0, _range2.default)(1, maxSize - 1);
94 | pageNumbers.push([maxSize - 1, this.props.totalPages - 1], this.props.totalPages);
95 | // Everything else
96 | } else {
97 | pageNumbers = [1, [2, this.props.currentPage - 4], this.props.currentPage - 3, this.props.currentPage - 2, this.props.currentPage - 1, this.props.currentPage, this.props.currentPage + 1, this.props.currentPage + 2, this.props.currentPage + 3, [this.props.currentPage + 4, this.props.totalPages - 1], this.props.totalPages];
98 | }
99 | }
100 |
101 | var items = pageNumbers.map(function (value) {
102 | return _react2.default.createElement(PaginationItem, {
103 | value: value,
104 | key: value,
105 | active: _this.props.currentPage === value,
106 | onClick: _this.props.onClick
107 | });
108 | });
109 |
110 | if (this.props.onPrevious && this.props.currentPage !== 1) {
111 | items.unshift(_react2.default.createElement(PaginationItem, {
112 | value: '«',
113 | key: '«',
114 | onClick: this.onPrevious
115 | }));
116 | }
117 | if (this.props.onNext && this.props.currentPage !== this.props.totalPages) {
118 | items.push(_react2.default.createElement(PaginationItem, {
119 | value: '»',
120 | key: '»',
121 | onClick: this.onNext
122 | }));
123 | }
124 |
125 | var className = (0, _classnames2.default)('re-pagination', this.props.className);
126 | return _react2.default.createElement(
127 | 'ul',
128 | { className: className },
129 | items
130 | );
131 | }
132 |
133 | });
134 |
135 | exports.default = Pagination;
136 |
--------------------------------------------------------------------------------
/src/ColorPicker.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import tinycolor from 'tinycolor2';
3 | import classNames from 'classnames';
4 |
5 |
6 | const SB_PICKER_WIDTH = 128;
7 | const SB_PICKER_HEIGHT = 128;
8 | const SB_PICKER_CURSOR_WIDTH = 14;
9 | const SB_PICKER_CURSOR_HEIGHT = 14;
10 | const H_PICKER_HEIGHT = 128;
11 | const H_PICKER_CURSOR_HEIGHT = 3;
12 |
13 |
14 | const TwoStopGradient = React.createClass({
15 |
16 | getDefaultProps: function() {
17 | return {
18 | horizontal: false
19 | };
20 | },
21 |
22 | propTypes: {
23 | color1: PropTypes.string.isRequired,
24 | color2: PropTypes.string.isRequired,
25 | horizontal: PropTypes.bool
26 | },
27 |
28 | render: function() {
29 | let angle = this.props.horizontal ? 'to right, ' : '';
30 | let styles = {
31 | background: `linear-gradient(${angle}${this.props.color1}, ${this.props.color2})`
32 | };
33 | return (
34 |
35 | );
36 | }
37 |
38 | });
39 |
40 |
41 | const SaturationBrightnessCursor = React.createClass({
42 |
43 | getDefaultProps: function() {
44 | return {
45 | position: {
46 | x: 0,
47 | y: 0
48 | }
49 | };
50 | },
51 |
52 | propTypes: {
53 | position: PropTypes.shape({
54 | x: PropTypes.number,
55 | y: PropTypes.number
56 | }).isRequired
57 | },
58 |
59 | render: function() {
60 | // We center the cursor around our x/y position
61 | let style = {
62 | left: this.props.position.x - Math.round(SB_PICKER_CURSOR_WIDTH * 0.5),
63 | top: this.props.position.y - Math.round(SB_PICKER_CURSOR_HEIGHT * 0.5)
64 | };
65 | return (
66 |
67 | );
68 | }
69 |
70 | });
71 |
72 |
73 | const SaturationBrightnessPicker = React.createClass({
74 |
75 | getInitialState: function() {
76 | return {
77 | cursorPosition: {
78 | x: Math.round(SB_PICKER_WIDTH * this.props.saturation),
79 | y: Math.round(SB_PICKER_HEIGHT * (1 - this.props.brightness))
80 | },
81 | mouseDown: false,
82 | hue: 0
83 | };
84 | },
85 |
86 | getDefaultProps: function() {
87 | return {
88 | saturation: 1,
89 | brightness: 0
90 | };
91 | },
92 |
93 | propTypes: {
94 | onChange: PropTypes.func.isRequired,
95 | hue: PropTypes.number.isRequired,
96 | saturation: PropTypes.number,
97 | brightness: PropTypes.number
98 | },
99 |
100 | onMouseDownHandler: function(e) {
101 | e.preventDefault();
102 | window.addEventListener('mouseup', this.onMouseUpHandler);
103 | window.addEventListener('mousemove', this.onMouseMoveHandler);
104 | this._update(e.nativeEvent.pageX, e.nativeEvent.pageY, true);
105 | },
106 |
107 | onMouseUpHandler: function(e) {
108 | window.removeEventListener('mouseup', this.onMouseUpHandler);
109 | window.removeEventListener('mousemove', this.onMouseMoveHandler);
110 | this._update(e.pageX, e.pageY, false);
111 | },
112 |
113 | onMouseMoveHandler: function(e) {
114 | this._update(e.pageX, e.pageY, true);
115 | },
116 |
117 | _update: function(pageX, pageY, mouseDown) {
118 | // Get the position of the root element relative to the document.
119 | // var rootPos = this.refs.root.getDOMNode().getBoundingClientRect();
120 | let rootPos = this._root.getBoundingClientRect();
121 | // Convert coordinates into local space.
122 | let x = pageX - rootPos.left;
123 | let y = pageY - (rootPos.top + window.scrollY);
124 | // Keep x and y within our bounds
125 | x = x < 0 ? 0 : x;
126 | x = x > SB_PICKER_WIDTH ? SB_PICKER_WIDTH : x;
127 | y = y < 0 ? 0 : y;
128 | y = y > SB_PICKER_HEIGHT ? SB_PICKER_HEIGHT : y;
129 | // Normalize to saturation and brightness ratios
130 | let saturation = x / SB_PICKER_WIDTH;
131 | let brightness = 1 - (y / SB_PICKER_HEIGHT);
132 | this.setState(
133 | {
134 | mouseDown: mouseDown,
135 | cursorPosition: {x: x, y: y}
136 | },
137 | () => this.props.onChange(saturation, brightness)
138 | );
139 | },
140 |
141 | render: function() {
142 | let className = classNames({
143 | 're-color-picker__sb-picker': true,
144 | 're-color-picker__sb-picker--dragging': this.state.mouseDown
145 | });
146 |
147 | let hue = 'hsl('+Math.round(360 * this.props.hue)+', 100%, 50%)';
148 |
149 | return(
150 | this._root = c}
152 | onMouseDown={this.onMouseDownHandler}
153 | >
154 |
160 |
165 |
166 |
167 | );
168 | }
169 |
170 | });
171 |
172 |
173 | const HuePickerCursor = React.createClass({
174 |
175 | propTypes: {
176 | position: PropTypes.number
177 | },
178 |
179 | getDefaultProps: function() {
180 | return { position: 0 };
181 | },
182 |
183 | render: function() {
184 | // We vertically center the cursor around our y position and make sure
185 | // we're always within HuePicker's bounds.
186 | let y = this.props.position - Math.floor(H_PICKER_CURSOR_HEIGHT * 0.5);
187 | y = (y + H_PICKER_CURSOR_HEIGHT) > H_PICKER_HEIGHT ?
188 | H_PICKER_HEIGHT - H_PICKER_CURSOR_HEIGHT :
189 | y;
190 | y = y < 0 ? 0 : y;
191 | let styles = {
192 | top: y
193 | };
194 | return (
195 |
196 | );
197 | }
198 |
199 | });
200 |
201 |
202 | const HuePicker = React.createClass({
203 |
204 | getInitialState: function() {
205 | return {
206 | mouseDown: false,
207 | cursorPosition: Math.round(H_PICKER_HEIGHT * this.props.hue)
208 | };
209 | },
210 |
211 | propTypes: {
212 | hue: PropTypes.number.isRequired,
213 | onChange: PropTypes.func.isRequired
214 | },
215 |
216 | onMouseDownHandler: function(e) {
217 | e.preventDefault();
218 | window.addEventListener('mouseup', this.onMouseUpHandler);
219 | window.addEventListener('mousemove', this.onMouseMoveHandler);
220 | this._update(e.nativeEvent.pageY, true);
221 | },
222 |
223 | onMouseUpHandler: function(e) {
224 | window.removeEventListener('mouseup', this.onMouseUpHandler);
225 | window.removeEventListener('mousemove', this.onMouseMoveHandler);
226 | this._update(e.pageY, false);
227 | },
228 |
229 | onMouseMoveHandler: function(e) {
230 | this._update(e.pageY, true);
231 | },
232 |
233 | _update: function(pageY, mouseDown) {
234 | // Get the position of the root element relative to the document.
235 | let rootPos = this._root.getBoundingClientRect();
236 | // Convert coordinate into local space.
237 | let y = pageY - (rootPos.top + window.scrollY);
238 | // Keep y within our bounds
239 | y = y < 0 ? 0 : y;
240 | y = y > H_PICKER_HEIGHT ? H_PICKER_HEIGHT : y;
241 | // Normalize to a ratio
242 | let hue = y / H_PICKER_HEIGHT;
243 | this.setState(
244 | {
245 | mouseDown: mouseDown,
246 | cursorPosition: y
247 | },
248 | () => this.props.onChange(hue)
249 | );
250 | },
251 |
252 | render: function() {
253 | let className = classNames({
254 | 're-color-picker__h-picker': true,
255 | 're-color-picker__h-picker--dragging': this.state.mouseDown
256 | });
257 |
258 | return (
259 | this._root = c}
263 | >
264 |
265 |
266 | );
267 | }
268 |
269 | });
270 |
271 |
272 | const ColorPicker = React.createClass({
273 |
274 | getInitialState: function() {
275 | let hsv = tinycolor(this.props.color).toHsv();
276 | return {
277 | hue: hsv.h / 360,
278 | saturation: hsv.s,
279 | brightness: hsv.v
280 | };
281 | },
282 |
283 | propTypes: {
284 | color: PropTypes.oneOfType([
285 | PropTypes.string,
286 | PropTypes.object
287 | ]),
288 | onChange: PropTypes.func.isRequired
289 | },
290 |
291 | saturationBrightnessChangeHandler: function(saturation, brightness) {
292 | this.setState(
293 | {
294 | saturation: saturation,
295 | brightness: brightness
296 | },
297 | () => this._update()
298 | );
299 | },
300 |
301 | hueChangeHandler: function(hue) {
302 | this.setState(
303 | {
304 | hue: hue
305 | },
306 | () => this._update()
307 | );
308 | },
309 |
310 | _update: function() {
311 | let color = tinycolor({
312 | h: Math.round(360 * this.state.hue),
313 | s: this.state.saturation,
314 | v: this.state.brightness
315 | });
316 | this.props.onChange(color);
317 | },
318 |
319 | render: function() {
320 | let className = classNames('re-color-picker', this.props.className);
321 | return(
322 |
323 |
329 |
330 |
331 | );
332 | }
333 |
334 | });
335 |
336 |
337 | export default ColorPicker;
338 |
--------------------------------------------------------------------------------
/dist/ColorPicker.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _react = require('react');
8 |
9 | var _react2 = _interopRequireDefault(_react);
10 |
11 | var _tinycolor = require('tinycolor2');
12 |
13 | var _tinycolor2 = _interopRequireDefault(_tinycolor);
14 |
15 | var _classnames = require('classnames');
16 |
17 | var _classnames2 = _interopRequireDefault(_classnames);
18 |
19 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
20 |
21 | var SB_PICKER_WIDTH = 128;
22 | var SB_PICKER_HEIGHT = 128;
23 | var SB_PICKER_CURSOR_WIDTH = 14;
24 | var SB_PICKER_CURSOR_HEIGHT = 14;
25 | var H_PICKER_HEIGHT = 128;
26 | var H_PICKER_CURSOR_HEIGHT = 3;
27 |
28 | var TwoStopGradient = _react2.default.createClass({
29 | displayName: 'TwoStopGradient',
30 |
31 |
32 | getDefaultProps: function getDefaultProps() {
33 | return {
34 | horizontal: false
35 | };
36 | },
37 |
38 | propTypes: {
39 | color1: _react.PropTypes.string.isRequired,
40 | color2: _react.PropTypes.string.isRequired,
41 | horizontal: _react.PropTypes.bool
42 | },
43 |
44 | render: function render() {
45 | var angle = this.props.horizontal ? 'to right, ' : '';
46 | var styles = {
47 | background: 'linear-gradient(' + angle + this.props.color1 + ', ' + this.props.color2 + ')'
48 | };
49 | return _react2.default.createElement('div', { className: this.props.className, style: styles });
50 | }
51 |
52 | });
53 |
54 | var SaturationBrightnessCursor = _react2.default.createClass({
55 | displayName: 'SaturationBrightnessCursor',
56 |
57 |
58 | getDefaultProps: function getDefaultProps() {
59 | return {
60 | position: {
61 | x: 0,
62 | y: 0
63 | }
64 | };
65 | },
66 |
67 | propTypes: {
68 | position: _react.PropTypes.shape({
69 | x: _react.PropTypes.number,
70 | y: _react.PropTypes.number
71 | }).isRequired
72 | },
73 |
74 | render: function render() {
75 | // We center the cursor around our x/y position
76 | var style = {
77 | left: this.props.position.x - Math.round(SB_PICKER_CURSOR_WIDTH * 0.5),
78 | top: this.props.position.y - Math.round(SB_PICKER_CURSOR_HEIGHT * 0.5)
79 | };
80 | return _react2.default.createElement('div', { className: 're-color-picker__sb-picker-cursor', style: style });
81 | }
82 |
83 | });
84 |
85 | var SaturationBrightnessPicker = _react2.default.createClass({
86 | displayName: 'SaturationBrightnessPicker',
87 |
88 |
89 | getInitialState: function getInitialState() {
90 | return {
91 | cursorPosition: {
92 | x: Math.round(SB_PICKER_WIDTH * this.props.saturation),
93 | y: Math.round(SB_PICKER_HEIGHT * (1 - this.props.brightness))
94 | },
95 | mouseDown: false,
96 | hue: 0
97 | };
98 | },
99 |
100 | getDefaultProps: function getDefaultProps() {
101 | return {
102 | saturation: 1,
103 | brightness: 0
104 | };
105 | },
106 |
107 | propTypes: {
108 | onChange: _react.PropTypes.func.isRequired,
109 | hue: _react.PropTypes.number.isRequired,
110 | saturation: _react.PropTypes.number,
111 | brightness: _react.PropTypes.number
112 | },
113 |
114 | onMouseDownHandler: function onMouseDownHandler(e) {
115 | e.preventDefault();
116 | window.addEventListener('mouseup', this.onMouseUpHandler);
117 | window.addEventListener('mousemove', this.onMouseMoveHandler);
118 | this._update(e.nativeEvent.pageX, e.nativeEvent.pageY, true);
119 | },
120 |
121 | onMouseUpHandler: function onMouseUpHandler(e) {
122 | window.removeEventListener('mouseup', this.onMouseUpHandler);
123 | window.removeEventListener('mousemove', this.onMouseMoveHandler);
124 | this._update(e.pageX, e.pageY, false);
125 | },
126 |
127 | onMouseMoveHandler: function onMouseMoveHandler(e) {
128 | this._update(e.pageX, e.pageY, true);
129 | },
130 |
131 | _update: function _update(pageX, pageY, mouseDown) {
132 | var _this = this;
133 |
134 | // Get the position of the root element relative to the document.
135 | // var rootPos = this.refs.root.getDOMNode().getBoundingClientRect();
136 | var rootPos = this._root.getBoundingClientRect();
137 | // Convert coordinates into local space.
138 | var x = pageX - rootPos.left;
139 | var y = pageY - (rootPos.top + window.scrollY);
140 | // Keep x and y within our bounds
141 | x = x < 0 ? 0 : x;
142 | x = x > SB_PICKER_WIDTH ? SB_PICKER_WIDTH : x;
143 | y = y < 0 ? 0 : y;
144 | y = y > SB_PICKER_HEIGHT ? SB_PICKER_HEIGHT : y;
145 | // Normalize to saturation and brightness ratios
146 | var saturation = x / SB_PICKER_WIDTH;
147 | var brightness = 1 - y / SB_PICKER_HEIGHT;
148 | this.setState({
149 | mouseDown: mouseDown,
150 | cursorPosition: { x: x, y: y }
151 | }, function () {
152 | return _this.props.onChange(saturation, brightness);
153 | });
154 | },
155 |
156 | render: function render() {
157 | var _this2 = this;
158 |
159 | var className = (0, _classnames2.default)({
160 | 're-color-picker__sb-picker': true,
161 | 're-color-picker__sb-picker--dragging': this.state.mouseDown
162 | });
163 |
164 | var hue = 'hsl(' + Math.round(360 * this.props.hue) + ', 100%, 50%)';
165 |
166 | return _react2.default.createElement(
167 | 'div',
168 | { className: className,
169 | ref: function ref(c) {
170 | return _this2._root = c;
171 | },
172 | onMouseDown: this.onMouseDownHandler
173 | },
174 | _react2.default.createElement(TwoStopGradient, {
175 | className: 're-color-picker__sb-picker-layer',
176 | color1: 'white',
177 | color2: hue,
178 | horizontal: true
179 | }),
180 | _react2.default.createElement(TwoStopGradient, {
181 | className: 're-color-picker__sb-picker-layer',
182 | color1: 'rgba(0, 0, 0, 0)',
183 | color2: 'black'
184 | }),
185 | _react2.default.createElement(SaturationBrightnessCursor, { position: this.state.cursorPosition })
186 | );
187 | }
188 |
189 | });
190 |
191 | var HuePickerCursor = _react2.default.createClass({
192 | displayName: 'HuePickerCursor',
193 |
194 |
195 | propTypes: {
196 | position: _react.PropTypes.number
197 | },
198 |
199 | getDefaultProps: function getDefaultProps() {
200 | return { position: 0 };
201 | },
202 |
203 | render: function render() {
204 | // We vertically center the cursor around our y position and make sure
205 | // we're always within HuePicker's bounds.
206 | var y = this.props.position - Math.floor(H_PICKER_CURSOR_HEIGHT * 0.5);
207 | y = y + H_PICKER_CURSOR_HEIGHT > H_PICKER_HEIGHT ? H_PICKER_HEIGHT - H_PICKER_CURSOR_HEIGHT : y;
208 | y = y < 0 ? 0 : y;
209 | var styles = {
210 | top: y
211 | };
212 | return _react2.default.createElement('div', { className: 're-color-picker__h-picker-cursor', style: styles });
213 | }
214 |
215 | });
216 |
217 | var HuePicker = _react2.default.createClass({
218 | displayName: 'HuePicker',
219 |
220 |
221 | getInitialState: function getInitialState() {
222 | return {
223 | mouseDown: false,
224 | cursorPosition: Math.round(H_PICKER_HEIGHT * this.props.hue)
225 | };
226 | },
227 |
228 | propTypes: {
229 | hue: _react.PropTypes.number.isRequired,
230 | onChange: _react.PropTypes.func.isRequired
231 | },
232 |
233 | onMouseDownHandler: function onMouseDownHandler(e) {
234 | e.preventDefault();
235 | window.addEventListener('mouseup', this.onMouseUpHandler);
236 | window.addEventListener('mousemove', this.onMouseMoveHandler);
237 | this._update(e.nativeEvent.pageY, true);
238 | },
239 |
240 | onMouseUpHandler: function onMouseUpHandler(e) {
241 | window.removeEventListener('mouseup', this.onMouseUpHandler);
242 | window.removeEventListener('mousemove', this.onMouseMoveHandler);
243 | this._update(e.pageY, false);
244 | },
245 |
246 | onMouseMoveHandler: function onMouseMoveHandler(e) {
247 | this._update(e.pageY, true);
248 | },
249 |
250 | _update: function _update(pageY, mouseDown) {
251 | var _this3 = this;
252 |
253 | // Get the position of the root element relative to the document.
254 | var rootPos = this._root.getBoundingClientRect();
255 | // Convert coordinate into local space.
256 | var y = pageY - (rootPos.top + window.scrollY);
257 | // Keep y within our bounds
258 | y = y < 0 ? 0 : y;
259 | y = y > H_PICKER_HEIGHT ? H_PICKER_HEIGHT : y;
260 | // Normalize to a ratio
261 | var hue = y / H_PICKER_HEIGHT;
262 | this.setState({
263 | mouseDown: mouseDown,
264 | cursorPosition: y
265 | }, function () {
266 | return _this3.props.onChange(hue);
267 | });
268 | },
269 |
270 | render: function render() {
271 | var _this4 = this;
272 |
273 | var className = (0, _classnames2.default)({
274 | 're-color-picker__h-picker': true,
275 | 're-color-picker__h-picker--dragging': this.state.mouseDown
276 | });
277 |
278 | return _react2.default.createElement(
279 | 'div',
280 | {
281 | className: className,
282 | onMouseDown: this.onMouseDownHandler,
283 | ref: function ref(c) {
284 | return _this4._root = c;
285 | }
286 | },
287 | _react2.default.createElement(HuePickerCursor, { position: this.state.cursorPosition })
288 | );
289 | }
290 |
291 | });
292 |
293 | var ColorPicker = _react2.default.createClass({
294 | displayName: 'ColorPicker',
295 |
296 |
297 | getInitialState: function getInitialState() {
298 | var hsv = (0, _tinycolor2.default)(this.props.color).toHsv();
299 | return {
300 | hue: hsv.h / 360,
301 | saturation: hsv.s,
302 | brightness: hsv.v
303 | };
304 | },
305 |
306 | propTypes: {
307 | color: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.object]),
308 | onChange: _react.PropTypes.func.isRequired
309 | },
310 |
311 | saturationBrightnessChangeHandler: function saturationBrightnessChangeHandler(saturation, brightness) {
312 | var _this5 = this;
313 |
314 | this.setState({
315 | saturation: saturation,
316 | brightness: brightness
317 | }, function () {
318 | return _this5._update();
319 | });
320 | },
321 |
322 | hueChangeHandler: function hueChangeHandler(hue) {
323 | var _this6 = this;
324 |
325 | this.setState({
326 | hue: hue
327 | }, function () {
328 | return _this6._update();
329 | });
330 | },
331 |
332 | _update: function _update() {
333 | var color = (0, _tinycolor2.default)({
334 | h: Math.round(360 * this.state.hue),
335 | s: this.state.saturation,
336 | v: this.state.brightness
337 | });
338 | this.props.onChange(color);
339 | },
340 |
341 | render: function render() {
342 | var className = (0, _classnames2.default)('re-color-picker', this.props.className);
343 | return _react2.default.createElement(
344 | 'div',
345 | { className: className },
346 | _react2.default.createElement(SaturationBrightnessPicker, {
347 | onChange: this.saturationBrightnessChangeHandler,
348 | hue: this.state.hue,
349 | saturation: this.state.saturation,
350 | brightness: this.state.brightness
351 | }),
352 | _react2.default.createElement(HuePicker, { onChange: this.hueChangeHandler, hue: this.state.hue })
353 | );
354 | }
355 |
356 | });
357 |
358 | exports.default = ColorPicker;
359 |
--------------------------------------------------------------------------------