├── .gitignore ├── README ├── deploy.sh ├── favicon.ico ├── index.html ├── package.json └── src ├── App.js ├── Box.js ├── Joystick.js ├── Pannable.js ├── Pannable.service.js ├── index.css └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # production 7 | build 8 | 9 | # misc 10 | .DS_Store 11 | npm-debug.log 12 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Joystick 2 | 3 | Proof of concept for in browser animation rigging. 4 | 5 | npm start 6 | npm run build 7 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -o errexit #abort if any command fails 3 | 4 | main() { 5 | deploy_directory=${GIT_DEPLOY_DIR:-build} 6 | deploy_branch=${GIT_DEPLOY_BRANCH:-gh-pages} 7 | 8 | #if no user identity is already set in the current git environment, use this: 9 | default_username=${GIT_DEPLOY_USERNAME:-winkerVSbecks} 10 | default_email=${GIT_DEPLOY_EMAIL:-varunvachhar@gmail.com} 11 | 12 | #repository to deploy to. must be readable and writable. 13 | repo=${GIT_DEPLOY_REPO:-git@github.com:winkerVSbecks/joystick.git} 14 | 15 | #append commit hash to the end of message by default 16 | append_hash=true 17 | 18 | # Parse arg flags 19 | while : ; do 20 | if [[ $1 = "-v" || $1 = "--verbose" ]]; then 21 | verbose=true 22 | shift 23 | elif [[ $1 = "-e" || $1 = "--allow-empty" ]]; then 24 | allow_empty=true 25 | shift 26 | elif [[ ( $1 = "-m" || $1 = "--message" ) && -n $2 ]]; then 27 | commit_message=$2 28 | shift 2 29 | elif [[ $1 = "-n" || $1 = "--no-hash" ]]; then 30 | append_hash=false 31 | shift 32 | else 33 | break 34 | fi 35 | done 36 | 37 | enable_expanded_output 38 | 39 | if ! git diff --exit-code --quiet --cached; then 40 | echo Aborting due to uncommitted changes in the index >&2 41 | return 1 42 | fi 43 | 44 | commit_title=`git log -n 1 --format="%s" HEAD` 45 | commit_hash=` git log -n 1 --format="%H" HEAD` 46 | 47 | #default commit message uses last title if a custom one is not supplied 48 | if [[ -z $commit_message ]]; then 49 | commit_message="publish: $commit_title" 50 | fi 51 | 52 | #append hash to commit message unless no hash flag was found 53 | if [ $append_hash = true ]; then 54 | commit_message="$commit_message"$'\n\n'"generated from commit $commit_hash" 55 | fi 56 | 57 | previous_branch=`git rev-parse --abbrev-ref HEAD` 58 | 59 | if [ ! -d "$deploy_directory" ]; then 60 | echo "Deploy directory '$deploy_directory' does not exist. Aborting." >&2 61 | return 1 62 | fi 63 | 64 | # must use short form of flag in ls for compatibility with OS X and BSD 65 | if [[ -z `ls -A "$deploy_directory" 2> /dev/null` && -z $allow_empty ]]; then 66 | echo "Deploy directory '$deploy_directory' is empty. Aborting. If you're sure you want to deploy an empty tree, use the --allow-empty / -e flag." >&2 67 | return 1 68 | fi 69 | 70 | if git ls-remote --exit-code $repo "refs/heads/$deploy_branch" ; then 71 | # deploy_branch exists in $repo; make sure we have the latest version 72 | 73 | disable_expanded_output 74 | git fetch --force $repo $deploy_branch:$deploy_branch 75 | enable_expanded_output 76 | fi 77 | 78 | # check if deploy_branch exists locally 79 | if git show-ref --verify --quiet "refs/heads/$deploy_branch" 80 | then incremental_deploy 81 | else initial_deploy 82 | fi 83 | 84 | restore_head 85 | } 86 | 87 | initial_deploy() { 88 | git --work-tree "$deploy_directory" checkout --orphan $deploy_branch 89 | git --work-tree "$deploy_directory" add --all 90 | commit+push 91 | } 92 | 93 | incremental_deploy() { 94 | #make deploy_branch the current branch 95 | git symbolic-ref HEAD refs/heads/$deploy_branch 96 | #put the previously committed contents of deploy_branch into the index 97 | git --work-tree "$deploy_directory" reset --mixed --quiet 98 | git --work-tree "$deploy_directory" add --all 99 | 100 | set +o errexit 101 | diff=$(git --work-tree "$deploy_directory" diff --exit-code --quiet HEAD --)$? 102 | set -o errexit 103 | case $diff in 104 | 0) echo No changes to files in $deploy_directory. Skipping commit.;; 105 | 1) commit+push;; 106 | *) 107 | echo git diff exited with code $diff. Aborting. Staying on branch $deploy_branch so you can debug. To switch back to master, use: git symbolic-ref HEAD refs/heads/master && git reset --mixed >&2 108 | return $diff 109 | ;; 110 | esac 111 | } 112 | 113 | commit+push() { 114 | set_user_id 115 | git --work-tree "$deploy_directory" commit -m "$commit_message" 116 | 117 | disable_expanded_output 118 | #--quiet is important here to avoid outputting the repo URL, which may contain a secret token 119 | git push --quiet $repo $deploy_branch 120 | enable_expanded_output 121 | } 122 | 123 | #echo expanded commands as they are executed (for debugging) 124 | enable_expanded_output() { 125 | if [ $verbose ]; then 126 | set -o xtrace 127 | set +o verbose 128 | fi 129 | } 130 | 131 | #this is used to avoid outputting the repo URL, which may contain a secret token 132 | disable_expanded_output() { 133 | if [ $verbose ]; then 134 | set +o xtrace 135 | set -o verbose 136 | fi 137 | } 138 | 139 | set_user_id() { 140 | if [[ -z `git config user.name` ]]; then 141 | git config user.name "$default_username" 142 | fi 143 | if [[ -z `git config user.email` ]]; then 144 | git config user.email "$default_email" 145 | fi 146 | } 147 | 148 | restore_head() { 149 | if [[ $previous_branch = "HEAD" ]]; then 150 | #we weren't on any branch before, so just set HEAD back to the commit it was on 151 | git update-ref --no-deref HEAD $commit_hash $deploy_branch 152 | else 153 | git symbolic-ref HEAD refs/heads/$previous_branch 154 | fi 155 | 156 | git reset --mixed 157 | } 158 | 159 | filter() { 160 | sed -e "s|$repo|\$repo|g" 161 | } 162 | 163 | sanitize() { 164 | "$@" 2> >(filter 1>&2) | filter 165 | } 166 | 167 | [[ $1 = --source-only ]] || main "$@" 168 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winkerVSbecks/joystick/3889d149477a50e9d54c4bffc6439e06d8f92668/favicon.ico -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Joystick 7 | 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "joystick", 3 | "version": "0.0.1", 4 | "private": true, 5 | "devDependencies": { 6 | "babel-eslint": "^6.1.2", 7 | "eslint": "^3.3.1", 8 | "eslint-plugin-flowtype": "^2.11.0", 9 | "eslint-plugin-import": "^1.13.0", 10 | "eslint-plugin-jsx-a11y": "^2.1.0", 11 | "eslint-plugin-react": "^6.1.2", 12 | "react-scripts": "0.2.1" 13 | }, 14 | "dependencies": { 15 | "lodash.debounce": "^4.0.8", 16 | "react": "^15.2.1", 17 | "react-dom": "^15.2.1", 18 | "react-motion": "^0.4.4", 19 | "recompose": "^0.20.2" 20 | }, 21 | "scripts": { 22 | "start": "react-scripts start", 23 | "build": "react-scripts build", 24 | "eject": "react-scripts eject", 25 | "deploy": "bash deploy.sh" 26 | }, 27 | "eslintConfig": { 28 | "extends": "./node_modules/react-scripts/config/eslint.js" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { compose, withState, lifecycle } from 'recompose'; 3 | import './index.css'; 4 | import Joystick from './Joystick'; 5 | import Box from './Box'; 6 | import debounce from 'lodash.debounce'; 7 | 8 | const size = 128; 9 | 10 | const styles = { 11 | borderRadius: 6, 12 | border: '1px solid #444', 13 | background: `url("data:image/svg+xml;utf8,")`, 14 | backgroundSize: '7.5px 6px', 15 | }; 16 | 17 | function App({ 18 | size: { w, h }, 19 | joyStick1, setJoyStick1, 20 | joyStick2, setJoyStick2, 21 | }) { 22 | return ( 23 |
24 | 25 |
26 |

Joystick

27 |

28 | 30 | github 31 | 32 |

33 |
34 | 35 |
36 | 41 |
42 | 43 |
44 |
45 |
{ JSON.stringify(joyStick2, round, 2) }
46 |
47 |
48 |
{ JSON.stringify(joyStick1, round, 2) }
49 |
50 |
51 | 52 |
53 | 55 | 57 |
58 | 59 |
60 | ); 61 | } 62 | 63 | const enhance = compose( 64 | withState('size', 'setSize', { w: 0, h: 0 }), 65 | withState('joyStick1', 'setJoyStick1', { x: 0, y: 0 }), 66 | withState('joyStick2', 'setJoyStick2', { x: 0, y: 0 }), 67 | lifecycle({ 68 | componentWillMount(e) { 69 | window.addEventListener('resize', () => { 70 | this.props.setSize({ 71 | w: window.innerWidth, 72 | h: window.innerHeight, 73 | }); 74 | }) 75 | } 76 | }) 77 | ); 78 | 79 | export default enhance(App); 80 | 81 | function round(key, val) { 82 | return val.toFixed ? Number(val.toFixed(3)) : val; 83 | } 84 | -------------------------------------------------------------------------------- /src/Box.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function Box({ 4 | x = 0, y = 0, 5 | width, height, 6 | className, 7 | children, 8 | style, 9 | }) { 10 | const baseStyles = { 11 | width, 12 | height, 13 | transformOrigin: '50% 50%', 14 | willChange: 'transform, width, height', 15 | transform: `translate3d(${ x }px, ${ y }px, 0)`, 16 | }; 17 | 18 | return ( 19 |
20 | { children } 21 |
22 | ); 23 | } 24 | 25 | Box.propTypes = { 26 | x: React.PropTypes.number, 27 | y: React.PropTypes.number, 28 | width: React.PropTypes.number.isRequired, 29 | height: React.PropTypes.number.isRequired, 30 | style: React.PropTypes.object, 31 | className: React.PropTypes.string, 32 | children: React.PropTypes.node, 33 | }; 34 | 35 | export default Box; 36 | -------------------------------------------------------------------------------- /src/Joystick.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Box from './Box'; 3 | import Pannable from './Pannable'; 4 | 5 | const styles = { 6 | container: { 7 | borderColor: '#444', 8 | borderRadius: 8, 9 | borderWidth: 1, 10 | borderStyle: 'solid', 11 | cursor: 'pointer', 12 | }, 13 | pad: { 14 | borderColor: '#444', 15 | borderRadius: '50%', 16 | borderWidth: 1, 17 | opacity: 0.9, 18 | borderStyle: 'solid', 19 | background: `url("data:image/svg+xml;utf8,")`, 20 | backgroundSize: '7.5px 6px', 21 | backgroundColor: '#fff', 22 | } 23 | }; 24 | 25 | function Joystick({ w, h, onChange }) { 26 | return ( 27 |
28 | 32 | 33 | 34 | 37 | 38 | 39 | 40 |
41 | ); 42 | } 43 | 44 | Joystick.propTypes = { 45 | w: React.PropTypes.number.isRequired, 46 | h: React.PropTypes.number.isRequired, 47 | onChange: React.PropTypes.func.isRequired, 48 | }; 49 | 50 | export default Joystick; 51 | -------------------------------------------------------------------------------- /src/Pannable.js: -------------------------------------------------------------------------------- 1 | import { compose, withState, withHandlers } from 'recompose'; 2 | import React from 'react'; 3 | import * as pannableService from './Pannable.service'; 4 | 5 | function Pannable({ onPanStart, onPan, onPanEnd, pan, children }) { 6 | return ( 7 |
15 | { 16 | React.cloneElement(children, { ...pan }) 17 | } 18 |
19 | ); 20 | } 21 | 22 | Pannable.propTypes = { 23 | size: React.PropTypes.shape({ 24 | w: React.PropTypes.number, 25 | h: React.PropTypes.number, 26 | }).isRequired, 27 | onPan: React.PropTypes.func.isRequired, 28 | onPanStart: React.PropTypes.func.isRequired, 29 | onPanEnd: React.PropTypes.func.isRequired, 30 | children: React.PropTypes.node.isRequired, 31 | onChange: React.PropTypes.func.isRequired, 32 | pan: React.PropTypes.shape({ 33 | x: React.PropTypes.number, 34 | y: React.PropTypes.number, 35 | xNorm: React.PropTypes.number, 36 | yNorm: React.PropTypes.number, 37 | }).isRequired, 38 | }; 39 | 40 | const enhance = compose( 41 | withState('offset', 'setOffset', { top: 0, left: 0 }), 42 | withState('pan', 'setPan', { x: 0, y: 0 }), 43 | withHandlers({ 44 | onPanStart: pannableService.onPanStart, 45 | onPan: pannableService.onPan, 46 | onPanEnd: pannableService.onPanEnd, 47 | }) 48 | ); 49 | 50 | export default enhance(Pannable); 51 | -------------------------------------------------------------------------------- /src/Pannable.service.js: -------------------------------------------------------------------------------- 1 | export function onPanStart(props) { 2 | return e => { 3 | if (e.type === 'dragstart') { 4 | e.dataTransfer.setDragImage(getImage(), 0, 0); 5 | } 6 | 7 | const boundingRect = e.currentTarget.parentNode.getBoundingClientRect(); 8 | props.setOffset(boundingRect); 9 | }; 10 | } 11 | 12 | export function onPan(props) { 13 | return e => { 14 | const pan = getPan(e, props.offset, props.size); 15 | props.setPan(pan.raw); 16 | props.onChange(pan.normalized); 17 | }; 18 | } 19 | 20 | export function onPanEnd(props) { 21 | return e => { 22 | props.setPan( 23 | { x: 0, y: 0 }, 24 | () => props.onChange({ x: 0, y: 0 }) 25 | ); 26 | }; 27 | } 28 | 29 | function getPan(e, offset, size) { 30 | if (e.type === 'drag') { 31 | return withOffset(offset, size, { x: e.clientX, y: e.clientY }); 32 | } 33 | 34 | const touch = e.targetTouches[0] ? e.targetTouches[0] : e.changedTouches[0]; 35 | return withOffset(offset, size, { x: touch.clientX, y: touch.clientY }); 36 | } 37 | 38 | function withOffset({ top, left, width, height }, { w, h }, { x, y }) { 39 | const panX = constrainedTo(width, w / 4, x - (left + width / 2)); 40 | const panY = constrainedTo(height, h / 4, y - (top + height / 2)); 41 | return { 42 | raw: { 43 | x: panX, 44 | y: panY, 45 | }, 46 | normalized: { 47 | x: panX / (width * 0.5 - w / 4), 48 | y: panY / (height * 0.5 - h / 4), 49 | }, 50 | }; 51 | } 52 | 53 | function constrainedTo(delta, size, value) { 54 | return Math.min( Math.max(-delta / 2 + size, value), delta / 2 - size); 55 | } 56 | 57 | function getImage() { 58 | let img = new Image(); 59 | img.src = 'data:image/gif;base64' + 60 | ',R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='; 61 | return img; 62 | } 63 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | 7 | .vh-100 { height: 100vh } 8 | .bg-white { background-color: #fff } 9 | .cursor { cursor: move } 10 | .monospace { font-family: monospace } 11 | 12 | .h1{ font-size: 2rem } 13 | .h2{ font-size: 1.5rem } 14 | .h3{ font-size: 1.25rem } 15 | .h4{ font-size: 1rem } 16 | .h5{ font-size: .875rem } 17 | .h6{ font-size: .75rem } 18 | 19 | .font-family-inherit{ font-family:inherit } 20 | .font-size-inherit{ font-size:inherit } 21 | .text-decoration-none{ text-decoration:none } 22 | 23 | .bold{ font-weight: bold; font-weight: bold } 24 | .regular{ font-weight:normal } 25 | .italic{ font-style:italic } 26 | .caps{ text-transform:uppercase; letter-spacing: .2em; } 27 | 28 | .left-align{ text-align:left } 29 | .center{ text-align:center } 30 | .right-align{ text-align:right } 31 | .justify{ text-align:justify } 32 | 33 | .nowrap{ white-space:nowrap } 34 | .break-word{ word-wrap:break-word } 35 | 36 | .line-height-1{ line-height: 1 } 37 | .line-height-2{ line-height: 1.125 } 38 | .line-height-3{ line-height: 1.25 } 39 | .line-height-4{ line-height: 1.5 } 40 | 41 | .list-style-none{ list-style:none } 42 | .underline{ text-decoration:underline } 43 | 44 | .truncate{ 45 | max-width:100%; 46 | overflow:hidden; 47 | text-overflow:ellipsis; 48 | white-space:nowrap; 49 | } 50 | 51 | .list-reset{ 52 | list-style:none; 53 | padding-left:0; 54 | } 55 | 56 | .inline{ display:inline } 57 | .block{ display:block } 58 | .inline-block{ display:inline-block } 59 | .table{ display:table } 60 | .table-cell{ display:table-cell } 61 | 62 | .overflow-hidden{ overflow:hidden } 63 | .overflow-scroll{ overflow:scroll } 64 | .overflow-auto{ overflow:auto } 65 | 66 | .clearfix:before, 67 | .clearfix:after{ 68 | content:" "; 69 | display:table 70 | } 71 | .clearfix:after{ clear:both } 72 | 73 | .left{ float:left } 74 | .right{ float:right } 75 | 76 | .fit{ max-width:100% } 77 | 78 | .max-width-1{ max-width: 24rem } 79 | .max-width-2{ max-width: 32rem } 80 | .max-width-3{ max-width: 48rem } 81 | .max-width-4{ max-width: 64rem } 82 | 83 | .border-box{ box-sizing:border-box } 84 | 85 | .align-baseline{ vertical-align:baseline } 86 | .align-top{ vertical-align:top } 87 | .align-middle{ vertical-align:middle } 88 | .align-bottom{ vertical-align:bottom } 89 | 90 | .m0{ margin:0 } 91 | .mt0{ margin-top:0 } 92 | .mr0{ margin-right:0 } 93 | .mb0{ margin-bottom:0 } 94 | .ml0{ margin-left:0 } 95 | .mx0{ margin-left:0; margin-right:0 } 96 | .my0{ margin-top:0; margin-bottom:0 } 97 | 98 | .m1{ margin: .5rem } 99 | .mt1{ margin-top: .5rem } 100 | .mr1{ margin-right: .5rem } 101 | .mb1{ margin-bottom: .5rem } 102 | .ml1{ margin-left: .5rem } 103 | .mx1{ margin-left: .5rem; margin-right: .5rem } 104 | .my1{ margin-top: .5rem; margin-bottom: .5rem } 105 | 106 | .m2{ margin: 1rem } 107 | .mt2{ margin-top: 1rem } 108 | .mr2{ margin-right: 1rem } 109 | .mb2{ margin-bottom: 1rem } 110 | .ml2{ margin-left: 1rem } 111 | .mx2{ margin-left: 1rem; margin-right: 1rem } 112 | .my2{ margin-top: 1rem; margin-bottom: 1rem } 113 | 114 | .m3{ margin: 2rem } 115 | .mt3{ margin-top: 2rem } 116 | .mr3{ margin-right: 2rem } 117 | .mb3{ margin-bottom: 2rem } 118 | .ml3{ margin-left: 2rem } 119 | .mx3{ margin-left: 2rem; margin-right: 2rem } 120 | .my3{ margin-top: 2rem; margin-bottom: 2rem } 121 | 122 | .m4{ margin: 4rem } 123 | .mt4{ margin-top: 4rem } 124 | .mr4{ margin-right: 4rem } 125 | .mb4{ margin-bottom: 4rem } 126 | .ml4{ margin-left: 4rem } 127 | .mx4{ margin-left: 4rem; margin-right: 4rem } 128 | .my4{ margin-top: 4rem; margin-bottom: 4rem } 129 | 130 | .mxn1{ margin-left: -.5rem; margin-right: -.5rem; } 131 | .mxn2{ margin-left: -1rem; margin-right: -1rem; } 132 | .mxn3{ margin-left: -2rem; margin-right: -2rem; } 133 | .mxn4{ margin-left: -4rem; margin-right: -4rem; } 134 | 135 | .ml-auto{ margin-left:auto } 136 | .mr-auto{ margin-right:auto } 137 | .mx-auto{ margin-left:auto; margin-right:auto; } 138 | 139 | .p0{ padding:0 } 140 | .pt0{ padding-top:0 } 141 | .pr0{ padding-right:0 } 142 | .pb0{ padding-bottom:0 } 143 | .pl0{ padding-left:0 } 144 | .px0{ padding-left:0; padding-right:0 } 145 | .py0{ padding-top:0; padding-bottom:0 } 146 | 147 | .p1{ padding: .5rem } 148 | .pt1{ padding-top: .5rem } 149 | .pr1{ padding-right: .5rem } 150 | .pb1{ padding-bottom: .5rem } 151 | .pl1{ padding-left: .5rem } 152 | .py1{ padding-top: .5rem; padding-bottom: .5rem } 153 | .px1{ padding-left: .5rem; padding-right: .5rem } 154 | 155 | .p2{ padding: 1rem } 156 | .pt2{ padding-top: 1rem } 157 | .pr2{ padding-right: 1rem } 158 | .pb2{ padding-bottom: 1rem } 159 | .pl2{ padding-left: 1rem } 160 | .py2{ padding-top: 1rem; padding-bottom: 1rem } 161 | .px2{ padding-left: 1rem; padding-right: 1rem } 162 | 163 | .p3{ padding: 2rem } 164 | .pt3{ padding-top: 2rem } 165 | .pr3{ padding-right: 2rem } 166 | .pb3{ padding-bottom: 2rem } 167 | .pl3{ padding-left: 2rem } 168 | .py3{ padding-top: 2rem; padding-bottom: 2rem } 169 | .px3{ padding-left: 2rem; padding-right: 2rem } 170 | 171 | .p4{ padding: 4rem } 172 | .pt4{ padding-top: 4rem } 173 | .pr4{ padding-right: 4rem } 174 | .pb4{ padding-bottom: 4rem } 175 | .pl4{ padding-left: 4rem } 176 | .py4{ padding-top: 4rem; padding-bottom: 4rem } 177 | .px4{ padding-left: 4rem; padding-right: 4rem } 178 | 179 | .col{ 180 | float:left; 181 | box-sizing:border-box; 182 | } 183 | 184 | .col-right{ 185 | float:right; 186 | box-sizing:border-box; 187 | } 188 | 189 | .col-1{ 190 | width:8.33333%; 191 | } 192 | 193 | .col-2{ 194 | width:16.66667%; 195 | } 196 | 197 | .col-3{ 198 | width:25%; 199 | } 200 | 201 | .col-4{ 202 | width:33.33333%; 203 | } 204 | 205 | .col-5{ 206 | width:41.66667%; 207 | } 208 | 209 | .col-6{ 210 | width:50%; 211 | } 212 | 213 | .col-7{ 214 | width:58.33333%; 215 | } 216 | 217 | .col-8{ 218 | width:66.66667%; 219 | } 220 | 221 | .col-9{ 222 | width:75%; 223 | } 224 | 225 | .col-10{ 226 | width:83.33333%; 227 | } 228 | 229 | .col-11{ 230 | width:91.66667%; 231 | } 232 | 233 | .col-12{ 234 | width:100%; 235 | } 236 | @media (min-width: 40em){ 237 | 238 | .sm-col{ 239 | float:left; 240 | box-sizing:border-box; 241 | } 242 | 243 | .sm-col-right{ 244 | float:right; 245 | box-sizing:border-box; 246 | } 247 | 248 | .sm-col-1{ 249 | width:8.33333%; 250 | } 251 | 252 | .sm-col-2{ 253 | width:16.66667%; 254 | } 255 | 256 | .sm-col-3{ 257 | width:25%; 258 | } 259 | 260 | .sm-col-4{ 261 | width:33.33333%; 262 | } 263 | 264 | .sm-col-5{ 265 | width:41.66667%; 266 | } 267 | 268 | .sm-col-6{ 269 | width:50%; 270 | } 271 | 272 | .sm-col-7{ 273 | width:58.33333%; 274 | } 275 | 276 | .sm-col-8{ 277 | width:66.66667%; 278 | } 279 | 280 | .sm-col-9{ 281 | width:75%; 282 | } 283 | 284 | .sm-col-10{ 285 | width:83.33333%; 286 | } 287 | 288 | .sm-col-11{ 289 | width:91.66667%; 290 | } 291 | 292 | .sm-col-12{ 293 | width:100%; 294 | } 295 | 296 | } 297 | @media (min-width: 52em){ 298 | 299 | .md-col{ 300 | float:left; 301 | box-sizing:border-box; 302 | } 303 | 304 | .md-col-right{ 305 | float:right; 306 | box-sizing:border-box; 307 | } 308 | 309 | .md-col-1{ 310 | width:8.33333%; 311 | } 312 | 313 | .md-col-2{ 314 | width:16.66667%; 315 | } 316 | 317 | .md-col-3{ 318 | width:25%; 319 | } 320 | 321 | .md-col-4{ 322 | width:33.33333%; 323 | } 324 | 325 | .md-col-5{ 326 | width:41.66667%; 327 | } 328 | 329 | .md-col-6{ 330 | width:50%; 331 | } 332 | 333 | .md-col-7{ 334 | width:58.33333%; 335 | } 336 | 337 | .md-col-8{ 338 | width:66.66667%; 339 | } 340 | 341 | .md-col-9{ 342 | width:75%; 343 | } 344 | 345 | .md-col-10{ 346 | width:83.33333%; 347 | } 348 | 349 | .md-col-11{ 350 | width:91.66667%; 351 | } 352 | 353 | .md-col-12{ 354 | width:100%; 355 | } 356 | 357 | } 358 | @media (min-width: 64em){ 359 | 360 | .lg-col{ 361 | float:left; 362 | box-sizing:border-box; 363 | } 364 | 365 | .lg-col-right{ 366 | float:right; 367 | box-sizing:border-box; 368 | } 369 | 370 | .lg-col-1{ 371 | width:8.33333%; 372 | } 373 | 374 | .lg-col-2{ 375 | width:16.66667%; 376 | } 377 | 378 | .lg-col-3{ 379 | width:25%; 380 | } 381 | 382 | .lg-col-4{ 383 | width:33.33333%; 384 | } 385 | 386 | .lg-col-5{ 387 | width:41.66667%; 388 | } 389 | 390 | .lg-col-6{ 391 | width:50%; 392 | } 393 | 394 | .lg-col-7{ 395 | width:58.33333%; 396 | } 397 | 398 | .lg-col-8{ 399 | width:66.66667%; 400 | } 401 | 402 | .lg-col-9{ 403 | width:75%; 404 | } 405 | 406 | .lg-col-10{ 407 | width:83.33333%; 408 | } 409 | 410 | .lg-col-11{ 411 | width:91.66667%; 412 | } 413 | 414 | .lg-col-12{ 415 | width:100%; 416 | } 417 | 418 | } 419 | .flex{ display:-webkit-box; display:-webkit-flex; display:-ms-flexbox; display:flex } 420 | 421 | @media (min-width: 40em){ 422 | .sm-flex{ display:-webkit-box; display:-webkit-flex; display:-ms-flexbox; display:flex } 423 | } 424 | 425 | @media (min-width: 52em){ 426 | .md-flex{ display:-webkit-box; display:-webkit-flex; display:-ms-flexbox; display:flex } 427 | } 428 | 429 | @media (min-width: 64em){ 430 | .lg-flex{ display:-webkit-box; display:-webkit-flex; display:-ms-flexbox; display:flex } 431 | } 432 | 433 | .flex-column{ -webkit-box-orient:vertical; -webkit-box-direction:normal; -webkit-flex-direction:column; -ms-flex-direction:column; flex-direction:column } 434 | .flex-wrap{ -webkit-flex-wrap:wrap; -ms-flex-wrap:wrap; flex-wrap:wrap } 435 | 436 | .items-start{ -webkit-box-align:start; -webkit-align-items:flex-start; -ms-flex-align:start; -ms-grid-row-align:flex-start; align-items:flex-start } 437 | .items-end{ -webkit-box-align:end; -webkit-align-items:flex-end; -ms-flex-align:end; -ms-grid-row-align:flex-end; align-items:flex-end } 438 | .items-center{ -webkit-box-align:center; -webkit-align-items:center; -ms-flex-align:center; -ms-grid-row-align:center; align-items:center } 439 | .items-baseline{ -webkit-box-align:baseline; -webkit-align-items:baseline; -ms-flex-align:baseline; -ms-grid-row-align:baseline; align-items:baseline } 440 | .items-stretch{ -webkit-box-align:stretch; -webkit-align-items:stretch; -ms-flex-align:stretch; -ms-grid-row-align:stretch; align-items:stretch } 441 | 442 | .self-start{ -webkit-align-self:flex-start; -ms-flex-item-align:start; align-self:flex-start } 443 | .self-end{ -webkit-align-self:flex-end; -ms-flex-item-align:end; align-self:flex-end } 444 | .self-center{ -webkit-align-self:center; -ms-flex-item-align:center; align-self:center } 445 | .self-baseline{ -webkit-align-self:baseline; -ms-flex-item-align:baseline; align-self:baseline } 446 | .self-stretch{ -webkit-align-self:stretch; -ms-flex-item-align:stretch; align-self:stretch } 447 | 448 | .justify-start{ -webkit-box-pack:start; -webkit-justify-content:flex-start; -ms-flex-pack:start; justify-content:flex-start } 449 | .justify-end{ -webkit-box-pack:end; -webkit-justify-content:flex-end; -ms-flex-pack:end; justify-content:flex-end } 450 | .justify-center{ -webkit-box-pack:center; -webkit-justify-content:center; -ms-flex-pack:center; justify-content:center } 451 | .justify-between{ -webkit-box-pack:justify; -webkit-justify-content:space-between; -ms-flex-pack:justify; justify-content:space-between } 452 | .justify-around{ -webkit-justify-content:space-around; -ms-flex-pack:distribute; justify-content:space-around } 453 | 454 | .content-start{ -webkit-align-content:flex-start; -ms-flex-line-pack:start; align-content:flex-start } 455 | .content-end{ -webkit-align-content:flex-end; -ms-flex-line-pack:end; align-content:flex-end } 456 | .content-center{ -webkit-align-content:center; -ms-flex-line-pack:center; align-content:center } 457 | .content-between{ -webkit-align-content:space-between; -ms-flex-line-pack:justify; align-content:space-between } 458 | .content-around{ -webkit-align-content:space-around; -ms-flex-line-pack:distribute; align-content:space-around } 459 | .content-stretch{ -webkit-align-content:stretch; -ms-flex-line-pack:stretch; align-content:stretch } 460 | .flex-auto{ 461 | -webkit-box-flex:1; 462 | -webkit-flex:1 1 auto; 463 | -ms-flex:1 1 auto; 464 | flex:1 1 auto; 465 | min-width:0; 466 | min-height:0; 467 | } 468 | .flex-none{ -webkit-box-flex:0; -webkit-flex:none; -ms-flex:none; flex:none } 469 | 470 | .order-0{ -webkit-box-ordinal-group:1; -webkit-order:0; -ms-flex-order:0; order:0 } 471 | .order-1{ -webkit-box-ordinal-group:2; -webkit-order:1; -ms-flex-order:1; order:1 } 472 | .order-2{ -webkit-box-ordinal-group:3; -webkit-order:2; -ms-flex-order:2; order:2 } 473 | .order-3{ -webkit-box-ordinal-group:4; -webkit-order:3; -ms-flex-order:3; order:3 } 474 | .order-last{ -webkit-box-ordinal-group:100000; -webkit-order:99999; -ms-flex-order:99999; order:99999 } 475 | 476 | .relative{ position:relative } 477 | .absolute{ position:absolute } 478 | .fixed{ position:fixed } 479 | 480 | .top-0{ top:0 } 481 | .right-0{ right:0 } 482 | .bottom-0{ bottom:0 } 483 | .left-0{ left:0 } 484 | 485 | .z1{ z-index: 1 } 486 | .z2{ z-index: 2 } 487 | .z3{ z-index: 3 } 488 | .z4{ z-index: 4 } 489 | 490 | .border{ 491 | border-style:solid; 492 | border-width: 1px; 493 | } 494 | 495 | .border-top{ 496 | border-top-style:solid; 497 | border-top-width: 1px; 498 | } 499 | 500 | .border-right{ 501 | border-right-style:solid; 502 | border-right-width: 1px; 503 | } 504 | 505 | .border-bottom{ 506 | border-bottom-style:solid; 507 | border-bottom-width: 1px; 508 | } 509 | 510 | .border-left{ 511 | border-left-style:solid; 512 | border-left-width: 1px; 513 | } 514 | 515 | .border-none{ border:0 } 516 | 517 | .rounded{ border-radius: 3px } 518 | .circle{ border-radius:50% } 519 | 520 | .rounded-top{ border-radius: 3px 3px 0 0 } 521 | .rounded-right{ border-radius: 0 3px 3px 0 } 522 | .rounded-bottom{ border-radius: 0 0 3px 3px } 523 | .rounded-left{ border-radius: 3px 0 0 3px } 524 | 525 | .not-rounded{ border-radius:0 } 526 | 527 | .hide{ 528 | position:absolute !important; 529 | height:1px; 530 | width:1px; 531 | overflow:hidden; 532 | clip:rect(1px, 1px, 1px, 1px); 533 | } 534 | 535 | @media (max-width: 40em){ 536 | .xs-hide{ display:none !important } 537 | } 538 | 539 | @media (min-width: 40em) and (max-width: 52em){ 540 | .sm-hide{ display:none !important } 541 | } 542 | 543 | @media (min-width: 52em) and (max-width: 64em){ 544 | .md-hide{ display:none !important } 545 | } 546 | 547 | @media (min-width: 64em){ 548 | .lg-hide{ display:none !important } 549 | } 550 | 551 | .display-none{ display:none !important } 552 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | import './index.css'; 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ); 10 | 11 | document.ontouchmove = function(event) { 12 | event.preventDefault(); 13 | }; 14 | --------------------------------------------------------------------------------