diff --git a/.gitignore b/.gitignore index 0866f6f..4e2dd72 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,7 @@ logs # Build dist .publish -lib +# lib # Dependencies node_modules diff --git a/less/base/octicons.less b/less/base/octicons.less index 2625103..52bb274 100644 --- a/less/base/octicons.less +++ b/less/base/octicons.less @@ -1,5 +1,5 @@ -@octicons-font-path: "https://cdnjs.cloudflare.com/ajax/libs/octicons/3.1.0"; -@octicons-version: "396334ee3da78f4302d25c758ae3e3ce5dc3c97d"; +@octicons-font-path: "."; +@octicons-version: "c5a1d52cb40008f6d4ed65bf3f12d508b2fe8c88"; @font-face { font-family: 'octicons'; @@ -39,6 +39,7 @@ .octicon-microscope:before, .octicon-beaker:before { content: '\f0dd'} /*  */ .octicon-bell:before { content: '\f0de'} /*  */ +.octicon-bold:before { content: '\f0e2'} /*  */ .octicon-book:before { content: '\f007'} /*  */ .octicon-bookmark:before { content: '\f07b'} /*  */ .octicon-briefcase:before { content: '\f0d3'} /*  */ @@ -59,7 +60,6 @@ .octicon-cloud-download:before { content: '\f00b'} /*  */ .octicon-cloud-upload:before { content: '\f00c'} /*  */ .octicon-code:before { content: '\f05f'} /*  */ -.octicon-color-mode:before { content: '\f065'} /*  */ .octicon-comment-add:before, .octicon-comment:before { content: '\f02b'} /*  */ .octicon-comment-discussion:before { content: '\f04f'} /*  */ @@ -119,6 +119,7 @@ .octicon-issue-closed:before { content: '\f028'} /*  */ .octicon-issue-opened:before { content: '\f026'} /*  */ .octicon-issue-reopened:before { content: '\f027'} /*  */ +.octicon-italic:before { content: '\f0e4'} /*  */ .octicon-jersey:before { content: '\f019'} /*  */ .octicon-key:before { content: '\f049'} /*  */ .octicon-keyboard:before { content: '\f00d'} /*  */ @@ -133,6 +134,7 @@ .octicon-mirror-private:before, .octicon-git-fork-private:before, .octicon-lock:before { content: '\f06a'} /*  */ +.octicon-logo-gist:before { content: '\f0ad'} /*  */ .octicon-logo-github:before { content: '\f092'} /*  */ .octicon-mail:before { content: '\f03b'} /*  */ .octicon-mail-read:before { content: '\f03c'} /*  */ @@ -179,8 +181,6 @@ .octicon-rocket:before { content: '\f033'} /*  */ .octicon-rss:before { content: '\f034'} /*  */ .octicon-ruby:before { content: '\f047'} /*  */ -.octicon-screen-full:before { content: '\f066'} /*  */ -.octicon-screen-normal:before { content: '\f067'} /*  */ .octicon-search-save:before, .octicon-search:before { content: '\f02e'} /*  */ .octicon-server:before { content: '\f097'} /*  */ @@ -190,6 +190,7 @@ .octicon-sign-in:before { content: '\f036'} /*  */ .octicon-log-out:before, .octicon-sign-out:before { content: '\f032'} /*  */ +.octicon-smiley:before { content: '\f0e7'} /*  */ .octicon-squirrel:before { content: '\f0b2'} /*  */ .octicon-star-add:before, .octicon-star-delete:before, @@ -200,8 +201,10 @@ .octicon-tag-remove:before, .octicon-tag-add:before, .octicon-tag:before { content: '\f015'} /*  */ +.octicon-tasklist:before { content: '\f0e5'} /*  */ .octicon-telescope:before { content: '\f088'} /*  */ .octicon-terminal:before { content: '\f0c8'} /*  */ +.octicon-text-size:before { content: '\f0e3'} /*  */ .octicon-three-bars:before { content: '\f05e'} /*  */ .octicon-thumbsdown:before { content: '\f0db'} /*  */ .octicon-thumbsup:before { content: '\f0da'} /*  */ @@ -213,6 +216,8 @@ .octicon-triangle-up:before { content: '\f0aa'} /*  */ .octicon-unfold:before { content: '\f039'} /*  */ .octicon-unmute:before { content: '\f0ba'} /*  */ +.octicon-unverified:before { content: '\f0e8'} /*  */ +.octicon-verified:before { content: '\f0e6'} /*  */ .octicon-versions:before { content: '\f064'} /*  */ .octicon-watch:before { content: '\f0e0'} /*  */ .octicon-remove-close:before, diff --git a/lib/Elemental.js b/lib/Elemental.js new file mode 100644 index 0000000..001c7ef --- /dev/null +++ b/lib/Elemental.js @@ -0,0 +1,40 @@ +'use strict'; + +exports.Alert = require('./components/Alert'); +exports.BlankState = require('./components/BlankState'); +exports.Button = require('./components/Button'); +exports.ButtonGroup = require('./components/ButtonGroup'); +exports.Checkbox = require('./components/Checkbox'); +exports.Card = require('./components/Card'); +exports.Col = require('./components/Col'); +exports.Container = require('./components/Container'); +exports.Dropdown = require('./components/Dropdown'); +exports.EmailInputGroup = require('./components/EmailInputGroup'); +exports.FileDragAndDrop = require('./components/FileDragAndDrop'); +exports.FileUpload = require('./components/FileUpload'); +exports.Form = require('./components/Form'); +exports.FormField = require('./components/FormField'); +exports.FormIcon = require('./components/FormIcon'); +exports.FormIconField = require('./components/FormIconField'); +exports.FormInput = require('./components/FormInput'); +exports.FormLabel = require('./components/FormLabel'); +exports.FormNote = require('./components/FormNote'); +exports.FormRow = require('./components/FormRow'); +exports.FormSelect = require('./components/FormSelect'); +exports.Glyph = require('./components/Glyph'); +exports.InputGroup = require('./components/InputGroup'); +exports.InputGroupSection = require('./components/InputGroupSection'); +exports.Modal = require('./components/Modal'); +exports.ModalBody = require('./components/ModalBody'); +exports.ModalFooter = require('./components/ModalFooter'); +exports.ModalHeader = require('./components/ModalHeader'); +exports.Pagination = require('./components/Pagination'); +exports.PasswordInputGroup = require('./components/PasswordInputGroup'); +exports.Pill = require('./components/Pill'); +exports.Radio = require('./components/Radio'); +exports.ResponsiveText = require('./components/ResponsiveText'); +exports.Row = require('./components/Row'); +exports.RadioGroup = require('./components/RadioGroup'); +exports.SegmentedControl = require('./components/SegmentedControl'); +exports.Spinner = require('./components/Spinner'); +exports.Table = require('./components/Table'); \ No newline at end of file diff --git a/lib/Octicons.js b/lib/Octicons.js new file mode 100644 index 0000000..0589bd4 --- /dev/null +++ b/lib/Octicons.js @@ -0,0 +1,19 @@ +'use strict'; + +var list = [{ label: 'Alert', value: 'alert', className: 'octicon octicon-alert' }, { label: 'Arrow Down', value: 'arrow-down', className: 'octicon octicon-arrow-down' }, { label: 'Arrow Left', value: 'arrow-left', className: 'octicon octicon-arrow-left' }, { label: 'Arrow Right', value: 'arrow-right', className: 'octicon octicon-arrow-right' }, { label: 'Arrow Small-down', value: 'arrow-small-down', className: 'octicon octicon-arrow-small-down' }, { label: 'Arrow Small-left', value: 'arrow-small-left', className: 'octicon octicon-arrow-small-left' }, { label: 'Arrow Small-right', value: 'arrow-small-right', className: 'octicon octicon-arrow-small-right' }, { label: 'Arrow Small-up', value: 'arrow-small-up', className: 'octicon octicon-arrow-small-up' }, { label: 'Arrow Up', value: 'arrow-up', className: 'octicon octicon-arrow-up' }, { label: 'Microscope', value: 'microscope', className: 'octicon octicon-microscope' }, { label: 'Beaker', value: 'beaker', className: 'octicon octicon-beaker' }, { label: 'Bell', value: 'bell', className: 'octicon octicon-bell' }, { label: 'Book', value: 'book', className: 'octicon octicon-book' }, { label: 'Bookmark', value: 'bookmark', className: 'octicon octicon-bookmark' }, { label: 'Briefcase', value: 'briefcase', className: 'octicon octicon-briefcase' }, { label: 'Broadcast', value: 'broadcast', className: 'octicon octicon-broadcast' }, { label: 'Browser', value: 'browser', className: 'octicon octicon-browser' }, { label: 'Bug', value: 'bug', className: 'octicon octicon-bug' }, { label: 'Calendar', value: 'calendar', className: 'octicon octicon-calendar' }, { label: 'Check', value: 'check', className: 'octicon octicon-check' }, { label: 'Checklist', value: 'checklist', className: 'octicon octicon-checklist' }, { label: 'Chevron Down', value: 'chevron-down', className: 'octicon octicon-chevron-down' }, { label: 'Chevron Left', value: 'chevron-left', className: 'octicon octicon-chevron-left' }, { label: 'Chevron Right', value: 'chevron-right', className: 'octicon octicon-chevron-right' }, { label: 'Chevron Up', value: 'chevron-up', className: 'octicon octicon-chevron-up' }, { label: 'Circle Slash', value: 'circle-slash', className: 'octicon octicon-circle-slash' }, { label: 'Circuit Board', value: 'circuit-board', className: 'octicon octicon-circuit-board' }, { label: 'Clippy', value: 'clippy', className: 'octicon octicon-clippy' }, { label: 'Clock', value: 'clock', className: 'octicon octicon-clock' }, { label: 'Cloud Download', value: 'cloud-download', className: 'octicon octicon-cloud-download' }, { label: 'Cloud Upload', value: 'cloud-upload', className: 'octicon octicon-cloud-upload' }, { label: 'Code', value: 'code', className: 'octicon octicon-code' }, { label: 'Color Mode', value: 'color-mode', className: 'octicon octicon-color-mode' }, { label: 'Comment Add', value: 'comment-add', className: 'octicon octicon-comment-add' }, { label: 'Comment', value: 'comment', className: 'octicon octicon-comment' }, { label: 'Comment Discussion', value: 'comment-discussion', className: 'octicon octicon-comment-discussion' }, { label: 'Credit Card', value: 'credit-card', className: 'octicon octicon-credit-card' }, { label: 'Dash', value: 'dash', className: 'octicon octicon-dash' }, { label: 'Dashboard', value: 'dashboard', className: 'octicon octicon-dashboard' }, { label: 'Database', value: 'database', className: 'octicon octicon-database' }, { label: 'Clone', value: 'clone', className: 'octicon octicon-clone' }, { label: 'Desktop Download', value: 'desktop-download', className: 'octicon octicon-desktop-download' }, { label: 'Device Camera', value: 'device-camera', className: 'octicon octicon-device-camera' }, { label: 'Device Camera-video', value: 'device-camera-video', className: 'octicon octicon-device-camera-video' }, { label: 'Device Desktop', value: 'device-desktop', className: 'octicon octicon-device-desktop' }, { label: 'Device Mobile', value: 'device-mobile', className: 'octicon octicon-device-mobile' }, { label: 'Diff', value: 'diff', className: 'octicon octicon-diff' }, { label: 'Diff Added', value: 'diff-added', className: 'octicon octicon-diff-added' }, { label: 'Diff Ignored', value: 'diff-ignored', className: 'octicon octicon-diff-ignored' }, { label: 'Diff Modified', value: 'diff-modified', className: 'octicon octicon-diff-modified' }, { label: 'Diff Removed', value: 'diff-removed', className: 'octicon octicon-diff-removed' }, { label: 'Diff Renamed', value: 'diff-renamed', className: 'octicon octicon-diff-renamed' }, { label: 'Ellipsis', value: 'ellipsis', className: 'octicon octicon-ellipsis' }, { label: 'Eye Unwatch', value: 'eye-unwatch', className: 'octicon octicon-eye-unwatch' }, { label: 'Eye Watch', value: 'eye-watch', className: 'octicon octicon-eye-watch' }, { label: 'Eye', value: 'eye', className: 'octicon octicon-eye' }, { label: 'File Binary', value: 'file-binary', className: 'octicon octicon-file-binary' }, { label: 'File Code', value: 'file-code', className: 'octicon octicon-file-code' }, { label: 'File Directory', value: 'file-directory', className: 'octicon octicon-file-directory' }, { label: 'File Media', value: 'file-media', className: 'octicon octicon-file-media' }, { label: 'File Pdf', value: 'file-pdf', className: 'octicon octicon-file-pdf' }, { label: 'File Submodule', value: 'file-submodule', className: 'octicon octicon-file-submodule' }, { label: 'File Symlink-directory', value: 'file-symlink-directory', className: 'octicon octicon-file-symlink-directory' }, { label: 'File Symlink-file', value: 'file-symlink-file', className: 'octicon octicon-file-symlink-file' }, { label: 'File Text', value: 'file-text', className: 'octicon octicon-file-text' }, { label: 'File Zip', value: 'file-zip', className: 'octicon octicon-file-zip' }, { label: 'Flame', value: 'flame', className: 'octicon octicon-flame' }, { label: 'Fold', value: 'fold', className: 'octicon octicon-fold' }, { label: 'Gear', value: 'gear', className: 'octicon octicon-gear' }, { label: 'Gift', value: 'gift', className: 'octicon octicon-gift' }, { label: 'Gist', value: 'gist', className: 'octicon octicon-gist' }, { label: 'Gist Secret', value: 'gist-secret', className: 'octicon octicon-gist-secret' }, { label: 'Git Branch-create', value: 'git-branch-create', className: 'octicon octicon-git-branch-create' }, { label: 'Git Branch-delete', value: 'git-branch-delete', className: 'octicon octicon-git-branch-delete' }, { label: 'Git Branch', value: 'git-branch', className: 'octicon octicon-git-branch' }, { label: 'Git Commit', value: 'git-commit', className: 'octicon octicon-git-commit' }, { label: 'Git Compare', value: 'git-compare', className: 'octicon octicon-git-compare' }, { label: 'Git Merge', value: 'git-merge', className: 'octicon octicon-git-merge' }, { label: 'Git Pull-request-abandoned', value: 'git-pull-request-abandoned', className: 'octicon octicon-git-pull-request-abandoned' }, { label: 'Git Pull-request', value: 'git-pull-request', className: 'octicon octicon-git-pull-request' }, { label: 'Globe', value: 'globe', className: 'octicon octicon-globe' }, { label: 'Graph', value: 'graph', className: 'octicon octicon-graph' }, { label: 'Heart', value: 'heart', className: 'octicon octicon-heart' }, { label: 'History', value: 'history', className: 'octicon octicon-history' }, { label: 'Home', value: 'home', className: 'octicon octicon-home' }, { label: 'Horizontal Rule', value: 'horizontal-rule', className: 'octicon octicon-horizontal-rule' }, { label: 'Hubot', value: 'hubot', className: 'octicon octicon-hubot' }, { label: 'Inbox', value: 'inbox', className: 'octicon octicon-inbox' }, { label: 'Info', value: 'info', className: 'octicon octicon-info' }, { label: 'Issue Closed', value: 'issue-closed', className: 'octicon octicon-issue-closed' }, { label: 'Issue Opened', value: 'issue-opened', className: 'octicon octicon-issue-opened' }, { label: 'Issue Reopened', value: 'issue-reopened', className: 'octicon octicon-issue-reopened' }, { label: 'Jersey', value: 'jersey', className: 'octicon octicon-jersey' }, { label: 'Key', value: 'key', className: 'octicon octicon-key' }, { label: 'Keyboard', value: 'keyboard', className: 'octicon octicon-keyboard' }, { label: 'Law', value: 'law', className: 'octicon octicon-law' }, { label: 'Light Bulb', value: 'light-bulb', className: 'octicon octicon-light-bulb' }, { label: 'Link', value: 'link', className: 'octicon octicon-link' }, { label: 'Link External', value: 'link-external', className: 'octicon octicon-link-external' }, { label: 'List Ordered', value: 'list-ordered', className: 'octicon octicon-list-ordered' }, { label: 'List Unordered', value: 'list-unordered', className: 'octicon octicon-list-unordered' }, { label: 'Location', value: 'location', className: 'octicon octicon-location' }, { label: 'Gist Private', value: 'gist-private', className: 'octicon octicon-gist-private' }, { label: 'Mirror Private', value: 'mirror-private', className: 'octicon octicon-mirror-private' }, { label: 'Git Fork-private', value: 'git-fork-private', className: 'octicon octicon-git-fork-private' }, { label: 'Lock', value: 'lock', className: 'octicon octicon-lock' }, { label: 'Logo Github', value: 'logo-github', className: 'octicon octicon-logo-github' }, { label: 'Mail', value: 'mail', className: 'octicon octicon-mail' }, { label: 'Mail Read', value: 'mail-read', className: 'octicon octicon-mail-read' }, { label: 'Mail Reply', value: 'mail-reply', className: 'octicon octicon-mail-reply' }, { label: 'Mark Github', value: 'mark-github', className: 'octicon octicon-mark-github' }, { label: 'Markdown', value: 'markdown', className: 'octicon octicon-markdown' }, { label: 'Megaphone', value: 'megaphone', className: 'octicon octicon-megaphone' }, { label: 'Mention', value: 'mention', className: 'octicon octicon-mention' }, { label: 'Milestone', value: 'milestone', className: 'octicon octicon-milestone' }, { label: 'Mirror Public', value: 'mirror-public', className: 'octicon octicon-mirror-public' }, { label: 'Mirror', value: 'mirror', className: 'octicon octicon-mirror' }, { label: 'Mortar Board', value: 'mortar-board', className: 'octicon octicon-mortar-board' }, { label: 'Mute', value: 'mute', className: 'octicon octicon-mute' }, { label: 'No Newline', value: 'no-newline', className: 'octicon octicon-no-newline' }, { label: 'Octoface', value: 'octoface', className: 'octicon octicon-octoface' }, { label: 'Organization', value: 'organization', className: 'octicon octicon-organization' }, { label: 'Package', value: 'package', className: 'octicon octicon-package' }, { label: 'Paintcan', value: 'paintcan', className: 'octicon octicon-paintcan' }, { label: 'Pencil', value: 'pencil', className: 'octicon octicon-pencil' }, { label: 'Person Add', value: 'person-add', className: 'octicon octicon-person-add' }, { label: 'Person Follow', value: 'person-follow', className: 'octicon octicon-person-follow' }, { label: 'Person', value: 'person', className: 'octicon octicon-person' }, { label: 'Pin', value: 'pin', className: 'octicon octicon-pin' }, { label: 'Plug', value: 'plug', className: 'octicon octicon-plug' }, { label: 'Repo Create', value: 'repo-create', className: 'octicon octicon-repo-create' }, { label: 'Gist New', value: 'gist-new', className: 'octicon octicon-gist-new' }, { label: 'File Directory-create', value: 'file-directory-create', className: 'octicon octicon-file-directory-create' }, { label: 'File Add', value: 'file-add', className: 'octicon octicon-file-add' }, { label: 'Plus', value: 'plus', className: 'octicon octicon-plus' }, { label: 'Primitive Dot', value: 'primitive-dot', className: 'octicon octicon-primitive-dot' }, { label: 'Primitive Square', value: 'primitive-square', className: 'octicon octicon-primitive-square' }, { label: 'Pulse', value: 'pulse', className: 'octicon octicon-pulse' }, { label: 'Question', value: 'question', className: 'octicon octicon-question' }, { label: 'Quote', value: 'quote', className: 'octicon octicon-quote' }, { label: 'Radio Tower', value: 'radio-tower', className: 'octicon octicon-radio-tower' }, { label: 'Repo Delete', value: 'repo-delete', className: 'octicon octicon-repo-delete' }, { label: 'Repo', value: 'repo', className: 'octicon octicon-repo' }, { label: 'Repo Clone', value: 'repo-clone', className: 'octicon octicon-repo-clone' }, { label: 'Repo Force-push', value: 'repo-force-push', className: 'octicon octicon-repo-force-push' }, { label: 'Gist Fork', value: 'gist-fork', className: 'octicon octicon-gist-fork' }, { label: 'Repo Forked', value: 'repo-forked', className: 'octicon octicon-repo-forked' }, { label: 'Repo Pull', value: 'repo-pull', className: 'octicon octicon-repo-pull' }, { label: 'Repo Push', value: 'repo-push', className: 'octicon octicon-repo-push' }, { label: 'Rocket', value: 'rocket', className: 'octicon octicon-rocket' }, { label: 'Rss', value: 'rss', className: 'octicon octicon-rss' }, { label: 'Ruby', value: 'ruby', className: 'octicon octicon-ruby' }, { label: 'Screen Full', value: 'screen-full', className: 'octicon octicon-screen-full' }, { label: 'Screen Normal', value: 'screen-normal', className: 'octicon octicon-screen-normal' }, { label: 'Search Save', value: 'search-save', className: 'octicon octicon-search-save' }, { label: 'Search', value: 'search', className: 'octicon octicon-search' }, { label: 'Server', value: 'server', className: 'octicon octicon-server' }, { label: 'Settings', value: 'settings', className: 'octicon octicon-settings' }, { label: 'Shield', value: 'shield', className: 'octicon octicon-shield' }, { label: 'Log In', value: 'log-in', className: 'octicon octicon-log-in' }, { label: 'Sign In', value: 'sign-in', className: 'octicon octicon-sign-in' }, { label: 'Log Out', value: 'log-out', className: 'octicon octicon-log-out' }, { label: 'Sign Out', value: 'sign-out', className: 'octicon octicon-sign-out' }, { label: 'Squirrel', value: 'squirrel', className: 'octicon octicon-squirrel' }, { label: 'Star Add', value: 'star-add', className: 'octicon octicon-star-add' }, { label: 'Star Delete', value: 'star-delete', className: 'octicon octicon-star-delete' }, { label: 'Star', value: 'star', className: 'octicon octicon-star' }, { label: 'Stop', value: 'stop', className: 'octicon octicon-stop' }, { label: 'Repo Sync', value: 'repo-sync', className: 'octicon octicon-repo-sync' }, { label: 'Sync', value: 'sync', className: 'octicon octicon-sync' }, { label: 'Tag Remove', value: 'tag-remove', className: 'octicon octicon-tag-remove' }, { label: 'Tag Add', value: 'tag-add', className: 'octicon octicon-tag-add' }, { label: 'Tag', value: 'tag', className: 'octicon octicon-tag' }, { label: 'Telescope', value: 'telescope', className: 'octicon octicon-telescope' }, { label: 'Terminal', value: 'terminal', className: 'octicon octicon-terminal' }, { label: 'Three Bars', value: 'three-bars', className: 'octicon octicon-three-bars' }, { label: 'Thumbsdown', value: 'thumbsdown', className: 'octicon octicon-thumbsdown' }, { label: 'Thumbsup', value: 'thumbsup', className: 'octicon octicon-thumbsup' }, { label: 'Tools', value: 'tools', className: 'octicon octicon-tools' }, { label: 'Trashcan', value: 'trashcan', className: 'octicon octicon-trashcan' }, { label: 'Triangle Down', value: 'triangle-down', className: 'octicon octicon-triangle-down' }, { label: 'Triangle Left', value: 'triangle-left', className: 'octicon octicon-triangle-left' }, { label: 'Triangle Right', value: 'triangle-right', className: 'octicon octicon-triangle-right' }, { label: 'Triangle Up', value: 'triangle-up', className: 'octicon octicon-triangle-up' }, { label: 'Unfold', value: 'unfold', className: 'octicon octicon-unfold' }, { label: 'Unmute', value: 'unmute', className: 'octicon octicon-unmute' }, { label: 'Versions', value: 'versions', className: 'octicon octicon-versions' }, { label: 'Watch', value: 'watch', className: 'octicon octicon-watch' }, { label: 'Remove Close', value: 'remove-close', className: 'octicon octicon-remove-close' }, { label: 'X', value: 'x', className: 'octicon octicon-x' }, { label: 'Zap', value: 'zap', className: 'octicon octicon-zap' }]; + +var map = {}; +list.forEach(function (icon) { + map[icon.value] = icon; +}); +function pluck(arr, key) { + return arr.map(function (obj) { + return obj[key]; + }); +} + +module.exports = { + list: list, + keys: pluck(list, 'value'), + map: map +}; \ No newline at end of file diff --git a/lib/components/Alert.js b/lib/components/Alert.js new file mode 100644 index 0000000..2700f8a --- /dev/null +++ b/lib/components/Alert.js @@ -0,0 +1,25 @@ +'use strict'; + +var React = require('react'); +var classNames = require('classnames'); + +var ALERT_TYPES = ['danger', 'error', // alias for danger +'info', 'primary', 'success', 'warning']; + +module.exports = React.createClass({ + displayName: 'ElementalAlert', + propTypes: { + children: React.PropTypes.node.isRequired, + className: React.PropTypes.string, + type: React.PropTypes.oneOf(ALERT_TYPES).isRequired + }, + render: function render() { + var componentClass = classNames('Alert', 'Alert--' + this.props.type, this.props.className); + + return React.createElement( + 'div', + { className: componentClass }, + this.props.children + ); + } +}); \ No newline at end of file diff --git a/lib/components/BlankState.js b/lib/components/BlankState.js new file mode 100644 index 0000000..7a7372a --- /dev/null +++ b/lib/components/BlankState.js @@ -0,0 +1,25 @@ +'use strict'; + +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; }; + +var React = require('react'); + +module.exports = React.createClass({ + displayName: 'BlankState', + propTypes: { + children: React.PropTypes.node.isRequired + }, + render: function render() { + return React.createElement('div', _extends({ className: 'BlankState' }, this.props)); + } +}); + +module.exports.Heading = React.createClass({ + displayName: 'BlankStateHeading', + propTypes: { + children: React.PropTypes.node.isRequired + }, + render: function render() { + return React.createElement('h2', _extends({ className: 'BlankState__heading' }, this.props)); + } +}); \ No newline at end of file diff --git a/lib/components/Button.js b/lib/components/Button.js new file mode 100644 index 0000000..adb2a07 --- /dev/null +++ b/lib/components/Button.js @@ -0,0 +1,53 @@ +'use strict'; + +var React = require('react'); +var classNames = require('classnames'); +var blacklist = require('blacklist'); + +var BUTTON_SIZES = ['lg', 'sm', 'xs']; + +var BUTTON_TYPES = ['default', 'default-primary', 'default-success', 'default-warning', 'default-danger', 'hollow-primary', 'hollow-success', 'hollow-warning', 'hollow-danger', 'primary', 'success', 'warning', 'danger', 'link', 'link-text', 'link-primary', 'link-success', 'link-warning', 'link-danger', 'link-cancel', 'link-delete']; + +module.exports = React.createClass({ + displayName: 'Button', + propTypes: { + block: React.PropTypes.bool, + className: React.PropTypes.string, + component: React.PropTypes.element, + href: React.PropTypes.string, + isActive: React.PropTypes.bool, + size: React.PropTypes.oneOf(BUTTON_SIZES), + submit: React.PropTypes.bool, + type: React.PropTypes.oneOf(BUTTON_TYPES) + }, + getDefaultProps: function getDefaultProps() { + return { + type: 'default' + }; + }, + render: function render() { + // classes + var componentClass = classNames('Button', 'Button--' + this.props.type, this.props.size ? 'Button--' + this.props.size : null, { + 'Button--block': this.props.block, + 'is-active': this.props.isActive + }, this.props.className); + + // props + var props = blacklist(this.props, 'type', 'size', 'component', 'className'); + props.className = componentClass; + + if (this.props.component) { + return React.cloneElement(this.props.component, props); + } + + var tag = 'button'; + props.type = this.props.submit ? 'submit' : 'button'; + + if (props.href) { + tag = 'a'; + delete props.type; + } + + return React.createElement(tag, props, this.props.children); + } +}); \ No newline at end of file diff --git a/lib/components/ButtonGroup.js b/lib/components/ButtonGroup.js new file mode 100644 index 0000000..b4a9f3d --- /dev/null +++ b/lib/components/ButtonGroup.js @@ -0,0 +1,18 @@ +'use strict'; + +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; }; + +var classnames = require('classnames'); +var React = require('react'); + +module.exports = React.createClass({ + displayName: 'ButtonGroup', + propTypes: { + children: React.PropTypes.node.isRequired, + className: React.PropTypes.string + }, + render: function render() { + var className = classnames('ButtonGroup', this.props.className); + return React.createElement('div', _extends({}, this.props, { className: className })); + } +}); \ No newline at end of file diff --git a/lib/components/Card.js b/lib/components/Card.js new file mode 100644 index 0000000..b6ecc4a --- /dev/null +++ b/lib/components/Card.js @@ -0,0 +1,19 @@ +'use strict'; + +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; }; + +var React = require('react'); +var classNames = require('classnames'); + +module.exports = React.createClass({ + displayName: 'Card', + propTypes: { + children: React.PropTypes.node.isRequired, + className: React.PropTypes.string + }, + render: function render() { + var className = classNames('Card', this.props.className); + + return React.createElement('div', _extends({}, this.props, { className: className })); + } +}); \ No newline at end of file diff --git a/lib/components/Checkbox.js b/lib/components/Checkbox.js new file mode 100644 index 0000000..509cadc --- /dev/null +++ b/lib/components/Checkbox.js @@ -0,0 +1,53 @@ +'use strict'; + +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; }; + +var blacklist = require('blacklist'); +var classNames = require('classnames'); +var React = require('react'); + +var Checkbox = React.createClass({ + displayName: 'Checkbox', + + propTypes: { + autofocus: React.PropTypes.bool, + className: React.PropTypes.string, + disabled: React.PropTypes.bool, + indeterminate: React.PropTypes.bool, + inline: React.PropTypes.bool, + label: React.PropTypes.string, + style: React.PropTypes.object, + title: React.PropTypes.string + }, + componentDidMount: function componentDidMount() { + if (this.props.autofocus) { + this.refs.target.focus(); + } + this.setIndeterminate(this.props.indeterminate); + }, + componentWillReceiveProps: function componentWillReceiveProps(nextProps) { + this.setIndeterminate(nextProps.indeterminate); + }, + setIndeterminate: function setIndeterminate(value) { + this.refs.target.indeterminate = value; + }, + render: function render() { + var componentClass = classNames('Checkbox', { + 'Checkbox--disabled': this.props.disabled, + 'Checkbox--inline': this.props.inline + }, this.props.className); + var props = blacklist(this.props, 'className', 'label', 'style', 'title'); + return React.createElement( + 'label', + { className: componentClass, style: this.props.style, title: this.props.title }, + React.createElement('input', _extends({ ref: 'target', type: 'checkbox', className: 'Checkbox__input' }, props)), + this.props.label && React.createElement( + 'span', + { className: 'Checkbox__label' }, + this.props.label + ) + ); + } +}); + +module.exports = Checkbox; \ No newline at end of file diff --git a/lib/components/Col.js b/lib/components/Col.js new file mode 100644 index 0000000..111431b --- /dev/null +++ b/lib/components/Col.js @@ -0,0 +1,106 @@ +'use strict'; + +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; }; + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +var _react = require('react'); + +var _react2 = _interopRequireDefault(_react); + +var _blacklist = require('blacklist'); + +var _blacklist2 = _interopRequireDefault(_blacklist); + +var _constants = require('../constants'); + +var _constants2 = _interopRequireDefault(_constants); + +module.exports = _react2['default'].createClass({ + displayName: 'Col', + propTypes: { + /* eslint-disable react/jsx-sort-prop-types */ + basis: _react2['default'].PropTypes.oneOfType([_react2['default'].PropTypes.number, // allow pixels + _react2['default'].PropTypes.string]), + // allow percentage + children: _react2['default'].PropTypes.node, + gutter: _react2['default'].PropTypes.number, + style: _react2['default'].PropTypes.object, + lg: _react2['default'].PropTypes.string, // width as a percentage or fraction + md: _react2['default'].PropTypes.string, // width as a percentage or fraction + sm: _react2['default'].PropTypes.string, // width as a percentage or fraction + xs: _react2['default'].PropTypes.string }, + // width as a percentage or fraction + /* eslint-enable */ + getDefaultProps: function getDefaultProps() { + return { + gutter: _constants2['default'].width.gutter + }; + }, + getInitialState: function getInitialState() { + return { + windowWidth: typeof window !== 'undefined' ? window.innerWidth : 0 + }; + }, + componentDidMount: function componentDidMount() { + if (typeof window !== 'undefined') window.addEventListener('resize', this.handleResize); + }, + componentWillUnmount: function componentWillUnmount() { + if (typeof window !== 'undefined') window.removeEventListener('resize', this.handleResize); + }, + handleResize: function handleResize() { + this.setState({ + windowWidth: typeof window !== 'undefined' ? window.innerWidth : 0 + }); + }, + render: function render() { + var _props = this.props; + var basis = _props.basis; + var gutter = _props.gutter; + var xs = _props.xs; + var sm = _props.sm; + var md = _props.md; + var lg = _props.lg; + var windowWidth = this.state.windowWidth; + + var columnStyle = { + minHeight: 1, + paddingLeft: gutter / 2, + paddingRight: gutter / 2 + }; + + // if no width control is provided fill available space + if (!basis && !xs && !sm && !md && !lg) { + columnStyle.flex = '1 1 auto'; + columnStyle.msFlex = '1 1 auto'; + columnStyle.WebkitFlex = '1 1 auto'; + } + + // set widths / flex-basis + if (basis) { + columnStyle.flex = '1 0 ' + basis; + columnStyle.msFlex = '1 0 ' + basis; + columnStyle.WebkitFlex = '1 0 ' + basis; + } else if (windowWidth < _constants2['default'].breakpoint.xs) { + columnStyle.width = xs; + } else if (windowWidth < _constants2['default'].breakpoint.sm) { + columnStyle.width = sm || xs; + } else if (windowWidth < _constants2['default'].breakpoint.md) { + columnStyle.width = md || sm || xs; + } else { + columnStyle.width = lg || md || sm || xs; + } + + if (!columnStyle.width) { + columnStyle.width = '100%'; + } + + if (columnStyle.width in _constants2['default'].fractions) { + columnStyle.width = _constants2['default'].fractions[columnStyle.width]; + } + + var props = (0, _blacklist2['default'])(this.props, 'basis', 'gutter', 'style', 'xs', 'sm', 'md', 'lg'); + + return _react2['default'].createElement('div', _extends({ style: _extends(columnStyle, this.props.style) }, props)); + } +}); \ No newline at end of file diff --git a/lib/components/Container.js b/lib/components/Container.js new file mode 100644 index 0000000..6bd43ad --- /dev/null +++ b/lib/components/Container.js @@ -0,0 +1,57 @@ +'use strict'; + +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; }; + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +var _react = require('react'); + +var _react2 = _interopRequireDefault(_react); + +var _blacklist = require('blacklist'); + +var _blacklist2 = _interopRequireDefault(_blacklist); + +var _constants = require('../constants'); + +var _constants2 = _interopRequireDefault(_constants); + +module.exports = _react2['default'].createClass({ + displayName: 'Container', + propTypes: { + children: _react2['default'].PropTypes.node.isRequired, + clearfix: _react2['default'].PropTypes.bool, + gutter: _react2['default'].PropTypes.number, + maxWidth: _react2['default'].PropTypes.number, + style: _react2['default'].PropTypes.object + }, + getDefaultProps: function getDefaultProps() { + return { + gutter: _constants2['default'].width.gutter, + maxWidth: _constants2['default'].width.container + }; + }, + render: function render() { + var _props = this.props; + var gutter = _props.gutter; + var maxWidth = _props.maxWidth; + + var containerStyle = { + marginLeft: 'auto', + marginRight: 'auto', + paddingLeft: gutter, + paddingRight: gutter, + maxWidth: maxWidth + }; + var clearfixStyle = { clear: 'both', display: 'table' }; + var props = (0, _blacklist2['default'])(this.props, 'gutter', 'maxWidth', 'style'); + + return this.props.clearfix ? _react2['default'].createElement( + 'div', + _extends({ style: _extends(containerStyle, this.props.style) }, props), + _react2['default'].createElement('span', { style: clearfixStyle }), + this.props.children, + _react2['default'].createElement('span', { style: clearfixStyle }) + ) : _react2['default'].createElement('div', _extends({ style: _extends(containerStyle, this.props.style) }, props)); + } +}); \ No newline at end of file diff --git a/lib/components/Dropdown.js b/lib/components/Dropdown.js new file mode 100644 index 0000000..2feead0 --- /dev/null +++ b/lib/components/Dropdown.js @@ -0,0 +1,145 @@ +'use strict'; + +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; }; + +var React = require('react'); +var Transition = require('react-addons-css-transition-group'); +var blacklist = require('blacklist'); +var classNames = require('classnames'); +var Button = require('./Button'); + +var ESC_KEYCODE = 27; +var NO_OP = function NO_OP() { + return undefined; +}; + +module.exports = React.createClass({ + displayName: 'Dropdown', + propTypes: { + alignRight: React.PropTypes.bool, + buttonHasDisclosureArrow: React.PropTypes.bool, + buttonLabel: React.PropTypes.string, + buttonType: React.PropTypes.string, + children: React.PropTypes.element, + className: React.PropTypes.string, + isOpen: React.PropTypes.bool, + items: React.PropTypes.array.isRequired, + onSelect: React.PropTypes.func + }, + getDefaultProps: function getDefaultProps() { + return { + buttonHasDisclosureArrow: true, + onSelect: NO_OP + }; + }, + getInitialState: function getInitialState() { + return { + isOpen: this.props.isOpen || false + }; + }, + componentWillUpdate: function componentWillUpdate(nextProps, nextState) { + if (typeof window === 'undefined') return; + if (nextState.isOpen) { + window.addEventListener('keydown', this.handleKeyDown); + } else { + window.removeEventListener('keydown', this.handleKeyDown); + } + }, + openDropdown: function openDropdown() { + this.setState({ isOpen: true }); + }, + closeDropdown: function closeDropdown() { + this.setState({ isOpen: false }); + }, + handleKeyDown: function handleKeyDown(e) { + if (e.keyCode === ESC_KEYCODE) { + this.closeDropdown(); + } + }, + renderChildren: function renderChildren() { + var _this = this; + + return React.Children.map(this.props.children, function (child) { + return React.cloneElement(child, { + onClick: _this.state.isOpen ? _this.closeDropdown : _this.openDropdown, + className: classNames(child.props.className, 'Dropdown-toggle') + }); + }); + }, + renderButton: function renderButton() { + var disclosureArrow = this.props.buttonHasDisclosureArrow ? React.createElement('span', { className: 'disclosure-arrow' }) : null; + + return React.createElement( + Button, + { type: this.props.buttonType, onClick: this.state.isOpen ? this.closeDropdown : this.openDropdown, className: 'Dropdown-toggle' }, + this.props.buttonLabel, + disclosureArrow + ); + }, + onClick: function onClick(selectedItem) { + this.setState({ + isOpen: !this.state.isOpen + }); + this.props.onSelect(selectedItem); + }, + renderDropdownMenu: function renderDropdownMenu() { + var self = this; + if (!this.state.isOpen) return null; + + var dropdownMenuItems = this.props.items.map(function (item, i) { + var menuItem; + if (item.type === 'header') { + menuItem = React.createElement( + 'li', + { key: 'item-' + i, className: 'Dropdown-menu__header' }, + item.label + ); + } else if (item.type === 'divider') { + menuItem = React.createElement('li', { key: 'item-' + i, className: 'Dropdown-menu__divider' }); + } else { + menuItem = React.createElement( + 'li', + { key: 'item-' + i, className: 'Dropdown-menu__item' }, + React.createElement( + 'span', + { className: 'Dropdown-menu__action', onClick: self.onClick.bind(self, item.value) }, + item.label + ) + ); + } + return menuItem; + }); + + return React.createElement( + 'ul', + { key: 'Dropdown-menu', className: 'Dropdown-menu', role: 'menu' }, + dropdownMenuItems + ); + }, + renderDropdownMenuBackground: function renderDropdownMenuBackground() { + if (!this.state.isOpen) return null; + return React.createElement('div', { className: 'Dropdown-menu-backdrop', onClick: this.closeDropdown }); + }, + render: function render() { + // classes + var dropdownClass = classNames('Dropdown', { + 'is-open': this.state.isOpen, + 'align-right': this.props.alignRight + }, this.props.className); + + // props + var props = blacklist(this.props, 'alignRight', 'buttonHasDisclosureArrow', 'buttonLabel', 'buttonType', 'className', 'isOpen', 'items'); + + return React.createElement( + 'span', + _extends({ className: dropdownClass }, props), + React.Children.count(this.props.children) ? this.renderChildren() : this.renderButton(), + React.createElement( + Transition, + { transitionName: 'Dropdown-menu', transitionEnterTimeout: 100, transitionLeaveTimeout: 100 }, + this.renderDropdownMenu() + ), + this.renderDropdownMenuBackground() + ); + } +}); \ No newline at end of file diff --git a/lib/components/EmailInputGroup.js b/lib/components/EmailInputGroup.js new file mode 100644 index 0000000..7e6e1d4 --- /dev/null +++ b/lib/components/EmailInputGroup.js @@ -0,0 +1,102 @@ +'use strict'; + +var React = require('react'); +var classNames = require('classnames'); + +var REGEXP_EMAIL = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + +function validateEmail(value) { + return REGEXP_EMAIL.test(value); +} + +module.exports = React.createClass({ + displayName: 'EmailInputGroup', + propTypes: { + alwaysValidate: React.PropTypes.bool, + className: React.PropTypes.string, + invalidMessage: React.PropTypes.string, + label: React.PropTypes.string, + onChange: React.PropTypes.func, + required: React.PropTypes.bool, + requiredMessage: React.PropTypes.string, + value: React.PropTypes.string + }, + getDefaultProps: function getDefaultProps() { + return { + requiredMessage: 'Email address is required', + invalidMessage: 'Please enter a valid email address' + }; + }, + getInitialState: function getInitialState() { + return { + isValid: true, + validationIsActive: this.props.alwaysValidate + }; + }, + componentDidMount: function componentDidMount() { + if (this.state.validationIsActive) { + this.validateInput(this.props.value); + } + }, + componentWillReceiveProps: function componentWillReceiveProps(newProps) { + if (this.state.validationIsActive) { + if (newProps.value !== this.props.value && newProps.value !== this._lastChangeValue && !newProps.alwaysValidate) { + // reset validation state if the value was changed outside the component + return this.setState({ + isValid: true, + validationIsActive: false + }); + } + this.validateInput(newProps.value); + } + }, + handleChange: function handleChange(e) { + this._lastChangeValue = e.target.value; + if (this.props.onChange) this.props.onChange(e); + }, + handleBlur: function handleBlur() { + if (!this.props.alwaysValidate) { + this.setState({ validationIsActive: false }); + } + this.validateInput(this.props.value); + }, + validateInput: function validateInput(value) { + var newState = { + isValid: true + }; + if (value.length && !validateEmail(value) || !value.length && this.props.required) { + newState.isValid = false; + } + if (!newState.isValid) { + newState.validationIsActive = true; + } + this.setState(newState); + }, + render: function render() { + var validationMessage; + if (!this.state.isValid) { + validationMessage = React.createElement( + 'div', + { className: 'form-validation is-invalid' }, + this.props.value.length ? this.props.invalidMessage : this.props.requiredMessage + ); + } + var formGroupClass = classNames('FormField', { + 'is-invalid': !this.state.isValid + }, this.props.className); + + var componentLabel = this.props.label ? React.createElement( + 'label', + { className: 'FormLabel', htmlFor: 'inputEmail' }, + this.props.label + ) : null; + + return React.createElement( + 'div', + { className: formGroupClass }, + componentLabel, + React.createElement('input', { onChange: this.handleChange, onBlur: this.handleBlur, value: this.props.value, type: 'email', className: 'FormInput', placeholder: 'Enter email', id: 'inputEmail' }), + validationMessage + ); + } +}); \ No newline at end of file diff --git a/lib/components/FileDragAndDrop.js b/lib/components/FileDragAndDrop.js new file mode 100644 index 0000000..349c4c5 --- /dev/null +++ b/lib/components/FileDragAndDrop.js @@ -0,0 +1,82 @@ +'use strict'; + +var React = require('react'); +var classNames = require('classnames'); + +/* + Based on: https://github.com/paramaggarwal/react-dropzone +*/ + +var Dropzone = React.createClass({ + displayName: 'Dropzone', + + propTypes: { + className: React.PropTypes.string, + label: React.PropTypes.string, + labelActive: React.PropTypes.string, + onDrop: React.PropTypes.func.isRequired + }, + getDefaultProps: function getDefaultProps() { + return { + label: 'Drag Files Here', + labelActive: 'Drop to Upload' + }; + }, + getInitialState: function getInitialState() { + return { + isDragActive: false + }; + }, + onDragLeave: function onDragLeave() { + this.setState({ + isDragActive: false + }); + }, + onDragOver: function onDragOver(e) { + e.preventDefault(); + e.dataTransfer.dropEffect = 'copy'; + this.setState({ + isDragActive: true + }); + }, + onDrop: function onDrop(e) { + e.preventDefault(); + + this.setState({ + isDragActive: false + }); + + var files; + if (e.dataTransfer) { + files = e.dataTransfer.files; + } else if (e.target) { + files = e.target.files; + } + + if (this.props.onDrop) { + files = Array.prototype.slice.call(files); + this.props.onDrop(files); + } + }, + onClick: function onClick() { + this.refs.fileInput.click(); + }, + render: function render() { + var className = classNames('FileDragAndDrop', { + 'active': this.state.isDragActive + }, this.props.className); + return React.createElement( + 'button', + { className: className, type: 'button', onClick: this.onClick, onDragLeave: this.onDragLeave, onDragOver: this.onDragOver, onDrop: this.onDrop }, + React.createElement('input', { style: { display: 'none' }, type: 'file', multiple: true, ref: 'fileInput', onChange: this.onDrop }), + React.createElement( + 'div', + { className: 'FileDragAndDrop__label' }, + this.state.isDragActive ? this.props.labelActive : this.props.label + ), + this.props.children + ); + } +}); + +module.exports = Dropzone; \ No newline at end of file diff --git a/lib/components/FileUpload.js b/lib/components/FileUpload.js new file mode 100644 index 0000000..d38dca9 --- /dev/null +++ b/lib/components/FileUpload.js @@ -0,0 +1,132 @@ +'use strict'; + +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; }; + +var React = require('react'); +var blacklist = require('blacklist'); + +var Button = require('./Button'); +var Spinner = require('./Spinner'); + +module.exports = React.createClass({ + displayName: 'FileUpload', + propTypes: { + buttonLabelChange: React.PropTypes.string, + buttonLabelInitial: React.PropTypes.string, + disabled: React.PropTypes.bool, + file: React.PropTypes.object, // https://developer.mozilla.org/en/docs/Using_files_from_web_applications + onChange: React.PropTypes.func + }, + getDefaultProps: function getDefaultProps() { + return { + buttonLabelInitial: 'Upload File', + buttonLabelChange: 'Change File' + }; + }, + getInitialState: function getInitialState() { + return { + dataURI: null, + file: {}, + loading: false + }; + }, + componentDidMount: function componentDidMount() { + this.refs.fileInput.addEventListener('click', function () { + this.value = ''; + }, false); + }, + triggerFileBrowser: function triggerFileBrowser() { + this.refs.fileInput.click(); + }, + handleChange: function handleChange(e) { + var _this = this; + + var reader = new FileReader(); + var file = e.target.files[0]; + + reader.readAsDataURL(file); + + reader.onloadstart = function () { + _this.setState({ + loading: true + }); + }; + reader.onloadend = function (upload) { + _this.setState({ + loading: false, + file: file, + dataURI: upload.target.result + }); + if (typeof _this.props.onChange === 'function') { + _this.props.onChange(e, { + file: file, + dataURI: upload.target.result + }); + } + }; + }, + cancelUpload: function cancelUpload(e) { + this.setState({ + dataURI: null, + file: {} + }); + this.props.onChange(e, null); + }, + render: function render() { + var _state = this.state; + var dataURI = _state.dataURI; + var file = _state.file; + + // props + var props = blacklist(this.props, 'buttonClassChange', 'buttonClassInitial', 'buttonLabelChange', 'buttonLabelInitial', 'disabled', 'file', 'onChange'); + // elements + var component = dataURI ? React.createElement( + 'div', + { className: 'FileUpload' }, + React.createElement( + 'div', + { className: 'FileUpload__image' }, + React.createElement('img', { className: 'FileUpload__image-src', src: dataURI }) + ), + React.createElement( + 'div', + { className: 'FileUpload__content' }, + React.createElement( + 'div', + { className: 'FileUpload__message' }, + file.name, + ' (', + Math.round(file.size / 1024), + 'Kb)' + ), + React.createElement( + 'div', + { className: 'FileUpload__buttons' }, + React.createElement( + Button, + { onClick: this.triggerFileBrowser, disabled: this.state.loading }, + this.state.loading && React.createElement(Spinner, null), + this.props.buttonLabelChange + ), + React.createElement( + Button, + { onClick: this.cancelUpload, type: 'link-cancel', disabled: this.state.loading }, + 'Cancel' + ) + ) + ) + ) : React.createElement( + Button, + { onClick: this.triggerFileBrowser, disabled: this.props.disabled || this.state.loading }, + this.state.loading ? React.createElement(Spinner, null) : null, + this.props.buttonLabelInitial + ); + + return React.createElement( + 'div', + null, + component, + React.createElement('input', _extends({ style: { display: 'none' }, type: 'file', ref: 'fileInput', onChange: this.handleChange }, props)) + ); + } +}); \ No newline at end of file diff --git a/lib/components/Form.js b/lib/components/Form.js new file mode 100644 index 0000000..2e72041 --- /dev/null +++ b/lib/components/Form.js @@ -0,0 +1,27 @@ +'use strict'; + +var blacklist = require('blacklist'); +var classnames = require('classnames'); +var React = require('react'); + +module.exports = React.createClass({ + displayName: 'Form', + propTypes: { + children: React.PropTypes.node.isRequired, + className: React.PropTypes.string, + component: React.PropTypes.oneOfType([React.PropTypes.element, React.PropTypes.string]), + type: React.PropTypes.oneOf(['basic', 'horizontal', 'inline']) + }, + getDefaultProps: function getDefaultProps() { + return { + component: 'form', + type: 'basic' + }; + }, + render: function render() { + var props = blacklist(this.props, 'children', 'type'); + props.className = classnames('Form', 'Form--' + this.props.type, this.props.className); + + return React.createElement(this.props.component, props, this.props.children); + } +}); \ No newline at end of file diff --git a/lib/components/FormField.js b/lib/components/FormField.js new file mode 100644 index 0000000..7338fc7 --- /dev/null +++ b/lib/components/FormField.js @@ -0,0 +1,42 @@ +'use strict'; + +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; }; + +var React = require('react'); +var blacklist = require('blacklist'); +var classNames = require('classnames'); + +module.exports = React.createClass({ + displayName: 'FormField', + propTypes: { + className: React.PropTypes.string, + htmlFor: React.PropTypes.string, + id: React.PropTypes.string, + label: React.PropTypes.string, + offsetAbsentLabel: React.PropTypes.bool, + width: React.PropTypes.oneOf(['one-half', 'two-quarters', 'three-sixths', 'one-quarter', 'three-quarters', 'one-third', 'two-sixths', 'two-thirds', 'four-sixths', 'one-fifth', 'two-fifths', 'three-fifths', 'four-fifths', 'one-sixth', 'five-sixths']) + }, + render: function render() { + // classes + var componentClass = classNames('FormField', { + 'offset-absent-label': this.props.offsetAbsentLabel + }, this.props.width, this.props.className); + + // props + var props = blacklist(this.props, 'className', 'label', 'offsetAbsentLabel', 'width'); + + // elements + var componentLabel = this.props.label ? React.createElement( + 'label', + { className: 'FormLabel', htmlFor: this.props.id || this.props.htmlFor }, + this.props.label + ) : null; + + return React.createElement( + 'div', + _extends({ className: componentClass }, props), + componentLabel, + this.props.children + ); + } +}); \ No newline at end of file diff --git a/lib/components/FormIcon.js b/lib/components/FormIcon.js new file mode 100644 index 0000000..0c13cb1 --- /dev/null +++ b/lib/components/FormIcon.js @@ -0,0 +1,25 @@ +'use strict'; + +var React = require('react'); +var classNames = require('classnames'); +var Spinner = require('./Spinner'); + +var icons = require('../Octicons').map; + +module.exports = React.createClass({ + displayName: 'FormIcon', + propTypes: { + className: React.PropTypes.string, + color: React.PropTypes.oneOf(['danger', 'default', 'muted', 'primary', 'success', 'warning']), + fill: React.PropTypes.oneOf(['danger', 'default', 'muted', 'primary', 'success', 'warning']), + icon: React.PropTypes.string, + isLoading: React.PropTypes.bool, + type: React.PropTypes.string + }, + render: function render() { + // classes + var className = classNames('IconField__icon', icons[this.props.icon].className, this.props.fill ? 'IconField__icon-fill--' + this.props.fill : null, this.props.type ? 'IconField__icon-color--' + this.props.type : null, this.props.className); + var component = this.props.isLoading ? React.createElement(Spinner, { size: 'sm' }) : React.createElement('div', { className: className }); + return component; + } +}); \ No newline at end of file diff --git a/lib/components/FormIconField.js b/lib/components/FormIconField.js new file mode 100644 index 0000000..a6f09a6 --- /dev/null +++ b/lib/components/FormIconField.js @@ -0,0 +1,54 @@ +'use strict'; + +var React = require('react'); +var blacklist = require('blacklist'); +var classNames = require('classnames'); + +var FormField = require('./FormField'); +var Spinner = require('./Spinner'); + +var ICON_MAP = require('../Octicons').map; +var ICON_KEYS = require('../Octicons').keys; +var COLOR_VARIANTS = ['danger', 'default', 'primary', 'success', 'warning']; + +module.exports = React.createClass({ + displayName: 'FormIconField', + propTypes: { + className: React.PropTypes.string, + iconColor: React.PropTypes.oneOf(COLOR_VARIANTS), + iconFill: React.PropTypes.oneOf(COLOR_VARIANTS), + iconIsLoading: React.PropTypes.bool, + iconKey: React.PropTypes.oneOf(ICON_KEYS).isRequired, + iconPosition: React.PropTypes.oneOf(['left', 'right']) + }, + getDefaultProps: function getDefaultProps() { + return { + iconPosition: 'left' + }; + }, + render: function render() { + // props + var props = blacklist(this.props, 'children', 'iconPosition', 'iconKey', 'iconFill', 'iconColor', 'iconIsLoading'); + + // classes + var fieldClass = classNames('IconField', { + 'has-fill-icon': this.props.iconFill, + 'is-loading-icon': this.props.iconIsLoading + }, this.props.iconFill ? 'field-context-' + this.props.iconFill : null, this.props.iconColor ? 'field-context-' + this.props.iconColor : null, this.props.iconPosition); + + var iconClass = classNames('IconField__icon', this.props.iconFill ? 'IconField__icon-fill--' + this.props.iconFill : null, this.props.iconColor ? 'IconField__icon-color--' + this.props.iconColor : null, ICON_MAP[this.props.iconKey].className); + + var icon = this.props.iconIsLoading ? React.createElement(Spinner, { size: 'sm' }) : React.createElement('span', { className: iconClass }); + + return React.createElement( + FormField, + props, + React.createElement( + 'div', + { className: fieldClass }, + this.props.children, + icon + ) + ); + } +}); \ No newline at end of file diff --git a/lib/components/FormInput.js b/lib/components/FormInput.js new file mode 100644 index 0000000..3ea5269 --- /dev/null +++ b/lib/components/FormInput.js @@ -0,0 +1,65 @@ +'use strict'; + +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; }; + +var React = require('react'); +var blacklist = require('blacklist'); +var classNames = require('classnames'); + +module.exports = React.createClass({ + displayName: 'FormInput', + propTypes: { + autofocus: React.PropTypes.bool, + className: React.PropTypes.string, + disabled: React.PropTypes.bool, + href: React.PropTypes.string, + id: React.PropTypes.string, + multiline: React.PropTypes.bool, + name: React.PropTypes.string, + noedit: React.PropTypes.bool, + onChange: React.PropTypes.func, + size: React.PropTypes.oneOf(['lg', 'sm', 'xs']), + type: React.PropTypes.string, + value: React.PropTypes.oneOfType([React.PropTypes.number, React.PropTypes.string]) + }, + + getDefaultProps: function getDefaultProps() { + return { + type: 'text' + }; + }, + + componentDidMount: function componentDidMount() { + if (this.props.autofocus) { + this.focus(); + } + }, + + focus: function focus() { + this.refs.input.focus(); + }, + + render: function render() { + // classes + var className = classNames({ + 'FormInput-noedit': this.props.noedit, + 'FormInput-noedit--multiline': this.props.noedit && this.props.multiline, + 'FormInput': !this.props.noedit + }, this.props.size ? 'FormInput--' + this.props.size : null, this.props.className); + var props = _extends({}, this.props, { className: className, ref: 'input' }); + var Element = 'input'; + if (this.props.noedit && this.props.href) { + Element = 'a'; + props.type = null; + props.children = props.children || props.value; + } else if (this.props.noedit) { + Element = 'div'; + props.type = null; + props.children = props.children || props.value; + } else if (this.props.multiline) { + Element = 'textarea'; + } + + return React.createElement(Element, props); + } +}); \ No newline at end of file diff --git a/lib/components/FormLabel.js b/lib/components/FormLabel.js new file mode 100644 index 0000000..9d4128e --- /dev/null +++ b/lib/components/FormLabel.js @@ -0,0 +1,36 @@ +'use strict'; + +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; }; + +var React = require('react'); +var blacklist = require('blacklist'); +var classNames = require('classnames'); + +module.exports = React.createClass({ + displayName: 'FormLabel', + propTypes: { + className: React.PropTypes.string, + htmlFor: React.PropTypes.string, + id: React.PropTypes.string, + style: React.PropTypes.object, + verticalAlign: React.PropTypes.oneOf(['baseline', 'bottom', 'inherit', 'initial', 'middle', 'sub', 'super', 'text-bottom', 'text-top', 'top']) + }, + render: function render() { + // classes + var className = classNames('FormLabel', this.props.className); + // props + var props = blacklist(this.props, 'htmlFor', 'id', 'className', 'style'); + // style + var style; + if (this.props.verticalAlign) { + style = { + verticalAlign: this.props.verticalAlign + }; + } + return React.createElement( + 'label', + _extends({ className: className, htmlFor: this.props.htmlFor || this.props.id, style: style || this.props.style }, props), + this.props.children + ); + } +}); \ No newline at end of file diff --git a/lib/components/FormNote.js b/lib/components/FormNote.js new file mode 100644 index 0000000..a98d053 --- /dev/null +++ b/lib/components/FormNote.js @@ -0,0 +1,37 @@ +'use strict'; + +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; }; + +var React = require('react'); +var blacklist = require('blacklist'); +var classNames = require('classnames'); + +var NOTE_TYPES = ['default', 'primary', 'success', 'warning', 'danger']; + +module.exports = React.createClass({ + displayName: 'FormNote', + propTypes: { + className: React.PropTypes.string, + note: React.PropTypes.string, + type: React.PropTypes.oneOf(NOTE_TYPES) + }, + getDefaultProps: function getDefaultProps() { + return { + type: 'default' + }; + }, + render: function render() { + // classes + var componentClass = classNames('FormNote', this.props.type ? 'FormNote--' + this.props.type : null, this.props.className); + + // props + var props = blacklist(this.props, 'className', 'note', 'type'); + + // allow users to pass through the note as an attribute or as children + return React.createElement( + 'div', + _extends({ className: componentClass, dangerouslySetInnerHTML: this.props.note ? { __html: this.props.note } : null }, props), + this.props.children + ); + } +}); \ No newline at end of file diff --git a/lib/components/FormRow.js b/lib/components/FormRow.js new file mode 100644 index 0000000..25bf916 --- /dev/null +++ b/lib/components/FormRow.js @@ -0,0 +1,20 @@ +'use strict'; + +var React = require('react'); +var classNames = require('classnames'); + +module.exports = React.createClass({ + displayName: 'FormRow', + propTypes: { + className: React.PropTypes.string + }, + render: function render() { + var className = classNames('FormRow', this.props.className); + + return React.createElement( + 'div', + { className: className }, + this.props.children + ); + } +}); \ No newline at end of file diff --git a/lib/components/FormSelect.js b/lib/components/FormSelect.js new file mode 100644 index 0000000..744680c --- /dev/null +++ b/lib/components/FormSelect.js @@ -0,0 +1,161 @@ +'use strict'; + +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; }; + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +var _blacklist = require('blacklist'); + +var _blacklist2 = _interopRequireDefault(_blacklist); + +var _classnames = require('classnames'); + +var _classnames2 = _interopRequireDefault(_classnames); + +var _react = require('react'); + +var _react2 = _interopRequireDefault(_react); + +var _icons = require('../icons'); + +var _icons2 = _interopRequireDefault(_icons); + +module.exports = _react2['default'].createClass({ + displayName: 'FormSelect', + propTypes: { + alwaysValidate: _react2['default'].PropTypes.bool, + className: _react2['default'].PropTypes.string, + disabled: _react2['default'].PropTypes.bool, + firstOption: _react2['default'].PropTypes.string, + htmlFor: _react2['default'].PropTypes.string, + id: _react2['default'].PropTypes.string, + label: _react2['default'].PropTypes.string, + onChange: _react2['default'].PropTypes.func.isRequired, + options: _react2['default'].PropTypes.arrayOf(_react2['default'].PropTypes.shape({ + label: _react2['default'].PropTypes.string, + value: _react2['default'].PropTypes.string + })).isRequired, + prependEmptyOption: _react2['default'].PropTypes.bool, + required: _react2['default'].PropTypes.bool, + requiredMessage: _react2['default'].PropTypes.string, + value: _react2['default'].PropTypes.string + }, + getDefaultProps: function getDefaultProps() { + return { + requiredMessage: 'This field is required' + }; + }, + getInitialState: function getInitialState() { + return { + isValid: true, + validationIsActive: this.props.alwaysValidate + }; + }, + componentDidMount: function componentDidMount() { + if (this.state.validationIsActive) { + this.validateInput(this.props.value); + } + }, + componentWillReceiveProps: function componentWillReceiveProps(newProps) { + if (this.state.validationIsActive) { + if (newProps.value !== this.props.value && newProps.value !== this._lastChangeValue && !newProps.alwaysValidate) { + // reset validation state if the value was changed outside the component + return this.setState({ + isValid: true, + validationIsActive: false + }); + } + this.validateInput(newProps.value); + } + }, + handleChange: function handleChange(e) { + this._lastChangeValue = e.target.value; + if (this.props.onChange) this.props.onChange(e.target.value); + }, + handleBlur: function handleBlur() { + if (!this.props.alwaysValidate) { + this.setState({ + validationIsActive: false + }); + } + this.validateInput(this.props.value); + }, + validateInput: function validateInput(value) { + var newState = { + isValid: true + }; + if (this.props.required && (!value || value && !value.length)) { + newState.isValid = false; + } + if (!newState.isValid) { + newState.validationIsActive = true; + } + this.setState(newState); + }, + renderIcon: function renderIcon(icon) { + var iconClassname = (0, _classnames2['default'])('FormSelect__arrows', { + 'FormSelect__arrows--disabled': this.props.disabled + }); + return _react2['default'].createElement('span', { dangerouslySetInnerHTML: { __html: icon }, className: iconClassname }); + }, + render: function render() { + // props + var props = (0, _blacklist2['default'])(this.props, 'prependEmptyOption', 'firstOption', 'alwaysValidate', 'htmlFor', 'id', 'label', 'onChange', 'options', 'required', 'requiredMessage', 'className'); + + // classes + var componentClass = (0, _classnames2['default'])('FormField', { + 'is-invalid': !this.state.isValid + }, this.props.className); + + // validation message + var validationMessage = undefined; + if (!this.state.isValid) { + validationMessage = _react2['default'].createElement( + 'div', + { className: 'form-validation is-invalid' }, + this.props.requiredMessage + ); + } + + // dynamic elements + var forAndID = this.props.htmlFor || this.props.id; + var componentLabel = this.props.label ? _react2['default'].createElement( + 'label', + { className: 'FormLabel', htmlFor: forAndID }, + this.props.label + ) : null; + + // options + var options = this.props.options.map(function (opt, i) { + return _react2['default'].createElement( + 'option', + { key: 'option-' + i, value: opt.value }, + opt.label + ); + }); + if (this.props.prependEmptyOption || this.props.firstOption) { + options.unshift(_react2['default'].createElement( + 'option', + { key: 'option-blank', value: '' }, + this.props.firstOption ? this.props.firstOption : 'Select...' + )); + } + + return _react2['default'].createElement( + 'div', + { className: componentClass }, + componentLabel, + _react2['default'].createElement( + 'div', + { className: 'u-pos-relative' }, + _react2['default'].createElement( + 'select', + _extends({ className: 'FormInput FormSelect', id: forAndID, onChange: this.handleChange, onBlur: this.handleBlur }, props), + options + ), + this.renderIcon(_icons2['default'].selectArrows) + ), + validationMessage + ); + } +}); \ No newline at end of file diff --git a/lib/components/Glyph.js b/lib/components/Glyph.js new file mode 100644 index 0000000..882356f --- /dev/null +++ b/lib/components/Glyph.js @@ -0,0 +1,24 @@ +'use strict'; + +var React = require('react'); +var classNames = require('classnames'); + +var icons = require('../Octicons').map; +var validNames = require('../Octicons').keys; + +var Glyph = React.createClass({ + displayName: 'Glyph', + propTypes: { + className: React.PropTypes.string, + icon: React.PropTypes.oneOf(validNames), + type: React.PropTypes.oneOf(['danger', 'default', 'muted', 'primary', 'success', 'warning']) + }, + render: function render() { + // classes + var className = classNames('Glyph__icon', icons[this.props.icon].className, this.props.type ? 'IconField__icon-color--' + this.props.type : null, this.props.className); + return React.createElement('i', { className: className }); + } +}); + +module.exports = Glyph; +module.exports.names = validNames; \ No newline at end of file diff --git a/lib/components/InputGroup.js b/lib/components/InputGroup.js new file mode 100644 index 0000000..43c5495 --- /dev/null +++ b/lib/components/InputGroup.js @@ -0,0 +1,25 @@ +'use strict'; + +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; }; + +var React = require('react'); +var classNames = require('classnames'); + +module.exports = React.createClass({ + displayName: 'InputGroup', + propTypes: { + className: React.PropTypes.string, + contiguous: React.PropTypes.bool + }, + render: function render() { + // classes + var className = classNames('InputGroup', { + 'InputGroup--contiguous': this.props.contiguous + }, this.props.className); + + return React.createElement('div', _extends({}, this.props, { className: className })); + } +}); + +// expose the child to the top level export +module.exports.Section = require('./InputGroupSection'); \ No newline at end of file diff --git a/lib/components/InputGroupSection.js b/lib/components/InputGroupSection.js new file mode 100644 index 0000000..b033bb2 --- /dev/null +++ b/lib/components/InputGroupSection.js @@ -0,0 +1,22 @@ +'use strict'; + +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; }; + +var React = require('react'); +var classNames = require('classnames'); + +module.exports = React.createClass({ + displayName: 'InputGroupSection', + propTypes: { + className: React.PropTypes.string, + grow: React.PropTypes.bool + }, + render: function render() { + // classes + var className = classNames('InputGroup_section', { + 'InputGroup_section--grow': this.props.grow + }, this.props.className); + + return React.createElement('div', _extends({}, this.props, { className: className })); + } +}); \ No newline at end of file diff --git a/lib/components/Modal.js b/lib/components/Modal.js new file mode 100644 index 0000000..075745f --- /dev/null +++ b/lib/components/Modal.js @@ -0,0 +1,189 @@ +'use strict'; + +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; }; + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +var _react = require('react'); + +var _react2 = _interopRequireDefault(_react); + +var _reactDom = require('react-dom'); + +var _reactDom2 = _interopRequireDefault(_reactDom); + +var _reactAddonsCssTransitionGroup = require('react-addons-css-transition-group'); + +var _reactAddonsCssTransitionGroup2 = _interopRequireDefault(_reactAddonsCssTransitionGroup); + +var _blacklist = require('blacklist'); + +var _blacklist2 = _interopRequireDefault(_blacklist); + +var _classnames = require('classnames'); + +var _classnames2 = _interopRequireDefault(_classnames); + +var _constants = require('../constants'); + +var TransitionPortal = _react2['default'].createClass({ + displayName: 'TransitionPortal', + componentDidMount: function componentDidMount() { + if (!_constants.canUseDOM) return; + var p = document.createElement('div'); + document.body.appendChild(p); + this.portalElement = p; + this.componentDidUpdate(); + }, + componentDidUpdate: function componentDidUpdate() { + if (!_constants.canUseDOM) return; + _reactDom2['default'].render(_react2['default'].createElement( + _reactAddonsCssTransitionGroup2['default'], + this.props, + this.props.children + ), this.portalElement); + }, + componentWillUnmount: function componentWillUnmount() { + if (!_constants.canUseDOM) return; + document.body.removeChild(this.portalElement); + }, + portalElement: null, + render: function render() { + return null; + } +}); + +module.exports = _react2['default'].createClass({ + displayName: 'Modal', + propTypes: { + autofocusFirstElement: _react2['default'].PropTypes.bool, + backdropClosesModal: _react2['default'].PropTypes.bool, + className: _react2['default'].PropTypes.string, + isOpen: _react2['default'].PropTypes.bool, + onCancel: _react2['default'].PropTypes.func, + width: _react2['default'].PropTypes.oneOfType([_react2['default'].PropTypes.oneOf(['small', 'medium', 'large']), _react2['default'].PropTypes.number]) + }, + getDefaultProps: function getDefaultProps() { + return { + width: 'medium' + }; + }, + componentWillReceiveProps: function componentWillReceiveProps(nextProps) { + if (!_constants.canUseDOM) return; + if (!this.props.isOpen && nextProps.isOpen) { + // setTimeout(() => this.handleAccessibility()); + document.body.style.overflow = 'hidden'; + } else if (this.props.isOpen && !nextProps.isOpen) { + // setTimeout(() => this.removeAccessibilityHandlers()); + document.body.style.overflow = null; + } + }, + /* + handleAccessibility () { + // Remember the element that was focused before we opened the modal + // so we can return focus to it once we close the modal. + this.focusedElementBeforeModalOpened = document.activeElement; + // We're using a transition to reveal the modal, + // so wait until the element is visible, before + // finding the first keyboard focusable element + // and passing focus to it, otherwise the browser + // might scroll the document to reveal the element + // receiving focus + if (this.props.autofocusFirstElement) { + ally.when.visibleArea({ + context: this.modalElement, + callback: function(context) { + // the modal is visible on screen, so find the first + // keyboard focusable element (giving any element with + // autofocus attribute precendence). If the modal does + // not contain any keyboard focusabe elements, focus will + // be given to the modal itself. + var element = ally.query.firstTabbable({ + context: context, + defaultToContext: true, + }); + element.focus(); + }, + }); + } + // Make sure that no element outside of the modal + // can be interacted with while the modal is visible. + this.disabledHandle = ally.maintain.disabled({ + filter: this.modalElement, + }); + // Make sure that no element outside of the modal + // is exposed via the Accessibility Tree, to prevent + // screen readers from navigating to content it shouldn't + // be seeing while the modal is open. + this.hiddenHandle = ally.maintain.hidden({ + filter: this.modalElement, + }); + // React to escape keys as mandated by ARIA Practices + this.keyHandle = ally.when.key({ + escape: this.handleClose, + }); + }, + removeAccessibilityHandlers () { + // undo listening to keyboard + this.keyHandle && this.keyHandle.disengage(); + // undo hiding elements outside of the modal + this.hiddenHandle && this.hiddenHandle.disengage(); + // undo disabling elements outside of the modal + this.disabledHandle && this.disabledHandle.disengage(); + // return focus to where it was before we opened the modal + this.focusedElementBeforeModalOpened && this.focusedElementBeforeModalOpened.focus(); + }, + handleModalClick (event) { + if (event.target.dataset.modal) this.handleClose(); + }, + */ + handleClose: function handleClose() { + this.props.onCancel(); + }, + renderDialog: function renderDialog() { + var _this = this; + + if (!this.props.isOpen) return; + var dialogClassname = (0, _classnames2['default'])('Modal-dialog', this.props.width && isNaN(this.props.width) ? 'Modal-dialog--' + this.props.width : null); + return _react2['default'].createElement( + 'div', + { className: dialogClassname, style: this.props.width && !isNaN(this.props.width) ? { width: this.props.width + 20 } : null }, + _react2['default'].createElement( + 'div', + { ref: function (ref) { + _this.modalElement = ref; + }, className: 'Modal-content' }, + this.props.children + ) + ); + }, + renderBackdrop: function renderBackdrop() { + if (!this.props.isOpen) return; + return _react2['default'].createElement('div', { className: 'Modal-backdrop', onClick: this.props.backdropClosesModal ? this.handleClose : null }); + }, + render: function render() { + var className = (0, _classnames2['default'])('Modal', { + 'is-open': this.props.isOpen + }, this.props.className); + var props = (0, _blacklist2['default'])(this.props, 'backdropClosesModal', 'className', 'isOpen', 'onCancel'); + return _react2['default'].createElement( + 'div', + null, + _react2['default'].createElement( + TransitionPortal, + _extends({}, props, { 'data-modal': 'true', className: className, /*onClick={this.handleModalClick}*/transitionName: 'Modal-dialog', transitionEnterTimeout: 260, transitionLeaveTimeout: 140, component: 'div' }), + this.renderDialog() + ), + _react2['default'].createElement( + TransitionPortal, + { transitionName: 'Modal-background', transitionEnterTimeout: 140, transitionLeaveTimeout: 240, component: 'div' }, + this.renderBackdrop() + ) + ); + } +}); + +// expose the children to the top level export +module.exports.Body = require('./ModalBody'); +module.exports.Footer = require('./ModalFooter'); +module.exports.Header = require('./ModalHeader'); \ No newline at end of file diff --git a/lib/components/ModalBody.js b/lib/components/ModalBody.js new file mode 100644 index 0000000..1b343ed --- /dev/null +++ b/lib/components/ModalBody.js @@ -0,0 +1,18 @@ +'use strict'; + +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; }; + +var classnames = require('classnames'); +var React = require('react'); + +module.exports = React.createClass({ + displayName: 'ModalBody', + propTypes: { + children: React.PropTypes.node.isRequired, + className: React.PropTypes.string + }, + render: function render() { + var className = classnames('Modal__body', this.props.className); + return React.createElement('div', _extends({}, this.props, { className: className })); + } +}); \ No newline at end of file diff --git a/lib/components/ModalFooter.js b/lib/components/ModalFooter.js new file mode 100644 index 0000000..f3871dc --- /dev/null +++ b/lib/components/ModalFooter.js @@ -0,0 +1,18 @@ +'use strict'; + +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; }; + +var classnames = require('classnames'); +var React = require('react'); + +module.exports = React.createClass({ + displayName: 'ModalFooter', + propTypes: { + children: React.PropTypes.node.isRequired, + className: React.PropTypes.string + }, + render: function render() { + var className = classnames('Modal__footer', this.props.className); + return React.createElement('div', _extends({}, this.props, { className: className })); + } +}); \ No newline at end of file diff --git a/lib/components/ModalHeader.js b/lib/components/ModalHeader.js new file mode 100644 index 0000000..dbe05fa --- /dev/null +++ b/lib/components/ModalHeader.js @@ -0,0 +1,39 @@ +'use strict'; + +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; }; + +var classnames = require('classnames'); +var React = require('react'); + +module.exports = React.createClass({ + displayName: 'ModalHeader', + propTypes: { + children: React.PropTypes.node, + className: React.PropTypes.string, + onClose: React.PropTypes.func, + showCloseButton: React.PropTypes.bool, + text: React.PropTypes.string + }, + handleClose: function handleClose() { + document.body.style.overflow = null; + this.props.onClose(); + }, + render: function render() { + + // elements + var className = classnames('Modal__header', this.props.className); + var close = this.props.showCloseButton ? React.createElement('button', { type: 'button', onClick: this.handleClose, className: 'Modal__header__close' }) : null; + var text = this.props.text ? React.createElement( + 'h4', + { className: 'Modal__header__text' }, + this.props.text + ) : null; + return React.createElement( + 'div', + _extends({}, this.props, { className: className }), + close, + text, + this.props.children + ); + } +}); \ No newline at end of file diff --git a/lib/components/Pagination.js b/lib/components/Pagination.js new file mode 100644 index 0000000..1c019eb --- /dev/null +++ b/lib/components/Pagination.js @@ -0,0 +1,150 @@ +'use strict'; + +var React = require('react'); +var classNames = require('classnames'); + +var Page = React.createClass({ + displayName: 'Page', + propTypes: { + children: React.PropTypes.node, + label: React.PropTypes.string, + onSelect: React.PropTypes.func, + page: React.PropTypes.number, + selected: React.PropTypes.bool + }, + onSelect: function onSelect() { + this.props.onSelect(this.props.page); + }, + render: function render() { + var _props = this.props; + var children = _props.children; + var selected = _props.selected; + var label = _props.label; + + var className = classNames('Pagination__list__item', { + 'is-selected': selected + }); + return React.createElement( + 'button', + { className: className, onClick: this.onSelect }, + children + ); + } +}); + +module.exports = React.createClass({ + displayName: 'Pagination', + propTypes: { + className: React.PropTypes.string, + currentPage: React.PropTypes.number.isRequired, + limit: React.PropTypes.number, + onPageSelect: React.PropTypes.func, + pageSize: React.PropTypes.number.isRequired, + plural: React.PropTypes.string, + singular: React.PropTypes.string, + style: React.PropTypes.object, + total: React.PropTypes.number.isRequired + }, + renderCount: function renderCount() { + var count = ''; + var _props2 = this.props; + var currentPage = _props2.currentPage; + var pageSize = _props2.pageSize; + var plural = _props2.plural; + var singular = _props2.singular; + var total = _props2.total; + + if (!total) { + count = 'No ' + (plural || 'records'); + } else if (total > pageSize) { + var start = pageSize * (currentPage - 1) + 1; + var end = Math.min(start + pageSize - 1, total); + count = 'Showing ' + start + ' to ' + end + ' of ' + total; + } else { + count = 'Showing ' + total; + if (total > 1 && plural) { + count += ' ' + plural; + } else if (total === 1 && singular) { + count += ' ' + singular; + } + } + return React.createElement( + 'div', + { className: 'Pagination__count' }, + count + ); + }, + onPageSelect: function onPageSelect(page) { + if (this.props.onPageSelect) { + this.props.onPageSelect(page); + } + }, + renderPages: function renderPages() { + if (this.props.total <= this.props.pageSize) return null; + + var pages = []; + var _props3 = this.props; + var currentPage = _props3.currentPage; + var pageSize = _props3.pageSize; + var total = _props3.total; + var limit = _props3.limit; + + var totalPages = Math.ceil(total / pageSize); + var minPage = 1; + var maxPage = totalPages; + + if (limit && limit < totalPages) { + var rightLimit = Math.floor(limit / 2); + var leftLimit = rightLimit + limit % 2 - 1; + minPage = currentPage - leftLimit; + maxPage = currentPage + rightLimit; + + if (minPage < 1) { + maxPage = limit; + minPage = 1; + } + if (maxPage > totalPages) { + minPage = totalPages - limit + 1; + maxPage = totalPages; + } + } + if (minPage > 1) { + pages.push(React.createElement( + Page, + { key: 'page_start', onSelect: this.onPageSelect, page: 1 }, + '...' + )); + } + for (var page = minPage; page <= maxPage; page++) { + var selected = page === currentPage; + /* eslint-disable no-loop-func */ + pages.push(React.createElement( + Page, + { key: 'page_' + page, selected: selected, onSelect: this.onPageSelect, page: page }, + page + )); + /* eslint-enable */ + } + if (maxPage < totalPages) { + pages.push(React.createElement( + Page, + { key: 'page_end', onSelect: this.onPageSelect, page: totalPages }, + '...' + )); + } + return React.createElement( + 'div', + { className: 'Pagination__list' }, + pages + ); + }, + render: function render() { + var className = classNames('Pagination', this.props.className); + return React.createElement( + 'div', + { className: className, style: this.props.style }, + this.renderCount(), + this.renderPages() + ); + } +}); \ No newline at end of file diff --git a/lib/components/PasswordInputGroup.js b/lib/components/PasswordInputGroup.js new file mode 100644 index 0000000..ad45cc8 --- /dev/null +++ b/lib/components/PasswordInputGroup.js @@ -0,0 +1,102 @@ +'use strict'; + +var React = require('react'); +var classNames = require('classnames'); + +function validatePassword(value) { + return value.length >= 8; +} + +module.exports = React.createClass({ + displayName: 'PasswordInputGroup', + propTypes: { + alwaysValidate: React.PropTypes.bool, + className: React.PropTypes.string, + invalidMessage: React.PropTypes.string, + label: React.PropTypes.string, + onChange: React.PropTypes.func, + required: React.PropTypes.bool, + requiredMessage: React.PropTypes.string, + validatePassword: React.PropTypes.func, + value: React.PropTypes.string + }, + getDefaultProps: function getDefaultProps() { + return { + validatePassword: validatePassword, + requiredMessage: 'Password is required', + invalidMessage: 'Password must be at least 8 characters in length' + }; + }, + getInitialState: function getInitialState() { + return { + isValid: true, + validationIsActive: this.props.alwaysValidate + }; + }, + componentDidMount: function componentDidMount() { + if (this.state.validationIsActive) { + this.validateInput(this.props.value); + } + }, + componentWillReceiveProps: function componentWillReceiveProps(newProps) { + if (this.state.validationIsActive) { + if (newProps.value !== this.props.value && newProps.value !== this._lastChangeValue && !newProps.alwaysValidate) { + // reset validation state if the value was changed outside the component + return this.setState({ + isValid: true, + validationIsActive: false + }); + } + this.validateInput(newProps.value); + } + }, + handleChange: function handleChange(e) { + this._lastChangeValue = e.target.value; + if (this.props.onChange) this.props.onChange(e); + }, + handleBlur: function handleBlur() { + if (!this.props.alwaysValidate) { + this.setState({ validationIsActive: false }); + } + this.validateInput(this.props.value); + }, + validateInput: function validateInput(value) { + var newState = { + isValid: true + }; + if (value.length && !this.props.validatePassword(value) || !value.length && this.props.required) { + newState.isValid = false; + } + if (!newState.isValid) { + newState.validationIsActive = true; + } + this.setState(newState); + }, + render: function render() { + var validationMessage; + if (!this.state.isValid) { + validationMessage = React.createElement( + 'div', + { className: 'form-validation is-invalid' }, + this.props.value.length ? this.props.invalidMessage : this.props.requiredMessage + ); + } + var formGroupClass = classNames('FormField', { + 'is-invalid': !this.state.isValid + }, this.props.className); + + var componentLabel = this.props.label ? React.createElement( + 'label', + { className: 'FormLabel', htmlFor: 'inputPassword' }, + this.props.label + ) : null; + + return React.createElement( + 'div', + { className: formGroupClass }, + componentLabel, + React.createElement('input', { onChange: this.handleChange, onBlur: this.handleBlur, value: this.props.value, type: 'password', className: 'FormInput', placeholder: 'Enter password', id: 'inputPassword' }), + validationMessage + ); + } +}); \ No newline at end of file diff --git a/lib/components/Pill.js b/lib/components/Pill.js new file mode 100644 index 0000000..6eab7fd --- /dev/null +++ b/lib/components/Pill.js @@ -0,0 +1,48 @@ +'use strict'; + +var React = require('react'); +var blacklist = require('blacklist'); +var classNames = require('classnames'); + +var ALERT_TYPES = ['danger', 'default', 'info', 'primary', 'success', 'warning', 'danger-inverted', 'default-inverted', 'info-inverted', 'primary-inverted', 'success-inverted', 'warning-inverted']; + +module.exports = React.createClass({ + displayName: 'Pill', + propTypes: { + className: React.PropTypes.string, + label: React.PropTypes.string.isRequired, + onClear: React.PropTypes.func, + onClick: React.PropTypes.func, + type: React.PropTypes.oneOf(ALERT_TYPES) + }, + getDefaultProps: function getDefaultProps() { + return { + type: 'default' + }; + }, + renderClearButton: function renderClearButton() { + if (!this.props.onClear) return null; + return React.createElement( + 'button', + { type: 'button', onClick: this.props.onClear, className: 'Pill__clear' }, + '×' + ); + }, + render: function render() { + var componentClass = classNames('Pill', 'Pill--' + this.props.type, this.props.className); + + var props = blacklist(this.props, 'className', 'label', 'onClear', 'onClick', 'type'); + props.className = componentClass; + + return React.createElement( + 'div', + props, + React.createElement( + 'button', + { type: 'button', onClick: this.props.onClick, className: 'Pill__label' }, + this.props.label + ), + this.renderClearButton() + ); + } +}); \ No newline at end of file diff --git a/lib/components/Radio.js b/lib/components/Radio.js new file mode 100644 index 0000000..c875a17 --- /dev/null +++ b/lib/components/Radio.js @@ -0,0 +1,38 @@ +'use strict'; + +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; }; + +var blacklist = require('blacklist'); +var classNames = require('classnames'); +var React = require('react'); + +var Radio = React.createClass({ + displayName: 'Radio', + + propTypes: { + className: React.PropTypes.string, + disabled: React.PropTypes.bool, + inline: React.PropTypes.bool, + label: React.PropTypes.string + }, + render: function render() { + var componentClass = classNames('Radio', { + 'Radio--disabled': this.props.disabled, + 'Radio--inline': this.props.inline + }, this.props.className); + var props = blacklist(this.props, 'className', 'label'); + + return React.createElement( + 'label', + { className: componentClass }, + React.createElement('input', _extends({ type: 'radio', className: 'Radio__input' }, props)), + this.props.label && React.createElement( + 'span', + { className: 'Radio__label' }, + this.props.label + ) + ); + } +}); + +module.exports = Radio; \ No newline at end of file diff --git a/lib/components/RadioGroup.js b/lib/components/RadioGroup.js new file mode 100644 index 0000000..123f45c --- /dev/null +++ b/lib/components/RadioGroup.js @@ -0,0 +1,129 @@ +'use strict'; + +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; }; + +var React = require('react'); +var blacklist = require('blacklist'); +var classNames = require('classnames'); + +module.exports = React.createClass({ + displayName: 'RadioGroup', + propTypes: { + alwaysValidate: React.PropTypes.bool, + className: React.PropTypes.string, + inline: React.PropTypes.bool, + label: React.PropTypes.string, + onChange: React.PropTypes.func.isRequired, + options: React.PropTypes.array.isRequired, + required: React.PropTypes.bool, + requiredMessage: React.PropTypes.string, + value: React.PropTypes.string + }, + getDefaultProps: function getDefaultProps() { + return { + requiredMessage: 'This field is required' + }; + }, + getInitialState: function getInitialState() { + return { + isValid: true, + validationIsActive: this.props.alwaysValidate + }; + }, + componentDidMount: function componentDidMount() { + if (this.state.validationIsActive) { + this.validateInput(this.props.value); + } + }, + componentWillReceiveProps: function componentWillReceiveProps(newProps) { + if (this.state.validationIsActive) { + if (newProps.value !== this.props.value && newProps.value !== this._lastChangeValue && !newProps.alwaysValidate) { + // reset validation state if the value was changed outside the component + return this.setState({ + isValid: true, + validationIsActive: false + }); + } + this.validateInput(newProps.value); + } + }, + handleChange: function handleChange(e) { + this._lastChangeValue = e.target.value; + if (this.props.onChange) this.props.onChange(e.target.value); + }, + handleBlur: function handleBlur() { + if (!this.props.alwaysValidate) { + this.setState({ validationIsActive: false }); + } + this.validateInput(this.props.value); + }, + validateInput: function validateInput(value) { + var newState = { + isValid: true + }; + if (this.props.required && (!value || value && !value.length)) { + newState.isValid = false; + } + if (!newState.isValid) { + newState.validationIsActive = true; + } + this.setState(newState); + }, + render: function render() { + var self = this; + + // props + var props = blacklist(this.props, 'alwaysValidate', 'label', 'onChange', 'options', 'required', 'requiredMessage', 'value'); + + // classes + var componentClass = classNames('FormField', { + 'is-invalid': !this.state.isValid + }, this.props.className); + + // validation message + var validationMessage; + if (!this.state.isValid) { + validationMessage = React.createElement( + 'div', + { className: 'form-validation is-invalid' }, + this.props.requiredMessage + ); + } + + // dynamic elements + var componentLabel = this.props.label ? React.createElement( + 'label', + { className: 'FormLabel' }, + this.props.label + ) : null; + + // options + var radios = this.props.options.map(function (radio, i) { + return React.createElement( + 'label', + { key: 'radio-' + i, className: 'Radio' }, + React.createElement('input', { value: radio.value, type: 'radio', onChange: self.handleChange, onBlur: self.handleBlur, name: self.props.name, className: 'Radio__input' }), + React.createElement( + 'span', + { className: 'Radio__label' }, + radio.label + ) + ); + }); + if (this.props.inline) { + radios = React.createElement( + 'div', + { className: 'inline-controls' }, + radios + ); + } + + return React.createElement( + 'div', + _extends({ className: componentClass }, props), + componentLabel, + radios, + validationMessage + ); + } +}); \ No newline at end of file diff --git a/lib/components/ResponsiveText.js b/lib/components/ResponsiveText.js new file mode 100644 index 0000000..2cd1bfd --- /dev/null +++ b/lib/components/ResponsiveText.js @@ -0,0 +1,87 @@ +'use strict'; + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +var _react = require('react'); + +var _react2 = _interopRequireDefault(_react); + +var _blacklist = require('blacklist'); + +var _blacklist2 = _interopRequireDefault(_blacklist); + +var _constants = require('../constants'); + +var _constants2 = _interopRequireDefault(_constants); + +module.exports = _react2['default'].createClass({ + displayName: 'ResponsiveText', + propTypes: { + hiddenLG: _react2['default'].PropTypes.string, + hiddenMD: _react2['default'].PropTypes.string, + hiddenSM: _react2['default'].PropTypes.string, + hiddenXS: _react2['default'].PropTypes.string, + visibleLG: _react2['default'].PropTypes.string, + visibleMD: _react2['default'].PropTypes.string, + visibleSM: _react2['default'].PropTypes.string, + visibleXS: _react2['default'].PropTypes.string + }, + getInitialState: function getInitialState() { + return { + windowWidth: typeof window !== 'undefined' ? window.innerWidth : 0 + }; + }, + componentDidMount: function componentDidMount() { + if (typeof window !== 'undefined') window.addEventListener('resize', this.handleResize); + }, + componentWillUnmount: function componentWillUnmount() { + if (typeof window !== 'undefined') window.removeEventListener('resize', this.handleResize); + }, + handleResize: function handleResize() { + this.setState({ + windowWidth: typeof window !== 'undefined' ? window.innerWidth : 0 + }); + }, + render: function render() { + var _props = this.props; + var hiddenXS = _props.hiddenXS; + var hiddenSM = _props.hiddenSM; + var hiddenMD = _props.hiddenMD; + var hiddenLG = _props.hiddenLG; + var visibleXS = _props.visibleXS; + var visibleSM = _props.visibleSM; + var visibleMD = _props.visibleMD; + var visibleLG = _props.visibleLG; + var windowWidth = this.state.windowWidth; + + var text = undefined; + + // set widths / flex-basis + if (windowWidth < _constants2['default'].breakpoint.xs) { + text = visibleXS || hiddenSM || hiddenMD || hiddenLG; + } else if (windowWidth < _constants2['default'].breakpoint.sm) { + text = hiddenXS || visibleSM || hiddenMD || hiddenLG; + } else if (windowWidth < _constants2['default'].breakpoint.md) { + text = hiddenXS || hiddenSM || visibleMD || hiddenLG; + } else { + text = hiddenXS || hiddenSM || hiddenMD || visibleLG; + } + + var props = (0, _blacklist2['default'])(this.props, { + 'hiddenXS': true, + 'hiddenSM': true, + 'hiddenMD': true, + 'hiddenLG': true, + 'visibleXS': true, + 'visibleSM': true, + 'visibleMD': true, + 'visibleLG': true + }); + + return _react2['default'].createElement( + 'span', + props, + text + ); + } +}); \ No newline at end of file diff --git a/lib/components/Row.js b/lib/components/Row.js new file mode 100644 index 0000000..5336385 --- /dev/null +++ b/lib/components/Row.js @@ -0,0 +1,52 @@ +'use strict'; + +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; }; + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +var _react = require('react'); + +var _react2 = _interopRequireDefault(_react); + +var _blacklist = require('blacklist'); + +var _blacklist2 = _interopRequireDefault(_blacklist); + +var _classnames = require('classnames'); + +var _classnames2 = _interopRequireDefault(_classnames); + +var _constants = require('../constants'); + +var _constants2 = _interopRequireDefault(_constants); + +module.exports = _react2['default'].createClass({ + displayName: 'Row', + propTypes: { + children: _react2['default'].PropTypes.node.isRequired, + className: _react2['default'].PropTypes.string, + gutter: _react2['default'].PropTypes.number, + style: _react2['default'].PropTypes.object + }, + getDefaultProps: function getDefaultProps() { + return { + gutter: _constants2['default'].width.gutter + }; + }, + render: function render() { + var gutter = this.props.gutter; + + var rowStyle = { + display: 'flex', + flexWrap: 'wrap', + msFlexWrap: 'wrap', + WebkitFlexWrap: 'wrap', + marginLeft: gutter / -2, + marginRight: gutter / -2 + }; + var className = (0, _classnames2['default'])('Row', this.props.className); + var props = (0, _blacklist2['default'])(this.props, 'className', 'gutter', 'style'); + + return _react2['default'].createElement('div', _extends({}, props, { style: _extends(rowStyle, this.props.style), className: className })); + } +}); \ No newline at end of file diff --git a/lib/components/SegmentedControl.js b/lib/components/SegmentedControl.js new file mode 100644 index 0000000..3fb68ea --- /dev/null +++ b/lib/components/SegmentedControl.js @@ -0,0 +1,65 @@ +'use strict'; + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +var _classnames = require('classnames'); + +var _classnames2 = _interopRequireDefault(_classnames); + +var _react = require('react'); + +var _react2 = _interopRequireDefault(_react); + +module.exports = _react2['default'].createClass({ + displayName: 'SegmentedControl', + + propTypes: { + className: _react2['default'].PropTypes.string, + equalWidthSegments: _react2['default'].PropTypes.bool, + onChange: _react2['default'].PropTypes.func.isRequired, + options: _react2['default'].PropTypes.array.isRequired, + type: _react2['default'].PropTypes.oneOf(['default', 'muted', 'danger', 'info', 'primary', 'success', 'warning']), + value: _react2['default'].PropTypes.string + }, + + getDefaultProps: function getDefaultProps() { + return { + type: 'default' + }; + }, + + onChange: function onChange(value) { + this.props.onChange(value); + }, + + render: function render() { + var _this = this; + + var componentClassName = (0, _classnames2['default'])('SegmentedControl', 'SegmentedControl--' + this.props.type, { + 'SegmentedControl--equal-widths': this.props.equalWidthSegments + }, this.props.className); + + var options = this.props.options.map(function (op) { + + var buttonClassName = (0, _classnames2['default'])('SegmentedControl__button', { + 'is-selected': op.value === _this.props.value + }); + + return _react2['default'].createElement( + 'span', + { key: 'option-' + op.value, className: 'SegmentedControl__item' }, + _react2['default'].createElement( + 'button', + { type: 'button', onClick: _this.onChange.bind(_this, op.value), className: buttonClassName }, + op.label + ) + ); + }); + + return _react2['default'].createElement( + 'div', + { className: componentClassName }, + options + ); + } +}); \ No newline at end of file diff --git a/lib/components/Spinner.js b/lib/components/Spinner.js new file mode 100644 index 0000000..8f3a175 --- /dev/null +++ b/lib/components/Spinner.js @@ -0,0 +1,30 @@ +'use strict'; + +var React = require('react'); +var classNames = require('classnames'); + +module.exports = React.createClass({ + displayName: 'Spinner', + propTypes: { + className: React.PropTypes.string, + size: React.PropTypes.oneOf(['sm', 'md', 'lg']), + type: React.PropTypes.oneOf(['default', 'primary', 'inverted']) + }, + getDefaultProps: function getDefaultProps() { + return { + type: 'default', + size: 'sm' + }; + }, + render: function render() { + var componentClass = classNames('Spinner', 'Spinner--' + this.props.type, 'Spinner--' + this.props.size, this.props.className); + + return React.createElement( + 'div', + { className: componentClass }, + React.createElement('span', { className: 'Spinner_dot Spinner_dot--first' }), + React.createElement('span', { className: 'Spinner_dot Spinner_dot--second' }), + React.createElement('span', { className: 'Spinner_dot Spinner_dot--third' }) + ); + } +}); \ No newline at end of file diff --git a/lib/components/Table.js b/lib/components/Table.js new file mode 100644 index 0000000..3f95d8a --- /dev/null +++ b/lib/components/Table.js @@ -0,0 +1,30 @@ +'use strict'; + +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; }; + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +var _classnames = require('classnames'); + +var _classnames2 = _interopRequireDefault(_classnames); + +var _react = require('react'); + +var _react2 = _interopRequireDefault(_react); + +module.exports = _react2['default'].createClass({ + displayName: 'Table', + + propTypes: { + children: _react2['default'].PropTypes.any, + className: _react2['default'].PropTypes.string + }, + + render: function render() { + // classes + var className = (0, _classnames2['default'])('Table', this.props.className); + + // render table element + return _react2['default'].createElement('table', _extends({}, this.props, { className: className })); + } +}); \ No newline at end of file diff --git a/lib/constants.js b/lib/constants.js new file mode 100644 index 0000000..797a804 --- /dev/null +++ b/lib/constants.js @@ -0,0 +1,69 @@ +'use strict'; + +var canUseDOM = !!(typeof window !== 'undefined' && window.document && window.document.createElement); + +exports.canUseDOM = canUseDOM; + +// breakpoints +exports.breakpoint = { + xs: 480, + sm: 768, + md: 992, + lg: 1200 +}; + +// border radii +exports.borderRadius = { + xs: 2, + sm: 4, + md: 8, + lg: 16, + xl: 32 +}; + +// color +exports.color = { + appDanger: '#d64242', + appInfo: '#56cdfc', + appPrimary: '#1385e5', + appSuccess: '#34c240', + appWarning: '#fa9f47', + brandPrimary: '#31adb8' +}; + +// spacing +exports.spacing = { + xs: 5, + sm: 10, + md: 20, + lg: 40, + xl: 80 +}; + +// widths +exports.width = { + container: 1170, + gutter: 20 +}; + +// fractions (for col widths) + +function perc(n) { + return n * 100 + '%'; +} + +function denominators(n) { + for (var d = 2; d <= 20; d++) { + if (n < d) { + exports.fractions[n + '/' + d] = perc(n / d); + } + } +} + +exports.fractions = { + '1': '100%' +}; + +for (var numerator = 1; numerator <= 19; numerator++) { + denominators(numerator); +} \ No newline at end of file diff --git a/lib/icons/index.js b/lib/icons/index.js new file mode 100644 index 0000000..7c1b1a3 --- /dev/null +++ b/lib/icons/index.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = { + selectArrows: require('./selectArrows') +}; \ No newline at end of file diff --git a/lib/icons/selectArrows.js b/lib/icons/selectArrows.js new file mode 100644 index 0000000..6894f49 --- /dev/null +++ b/lib/icons/selectArrows.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = '' + '' + '' + ''; \ No newline at end of file