├── .babelrc ├── .eslintrc.json ├── .gitignore ├── .npmignore ├── .travis.yml ├── README.md ├── package.json ├── src ├── example │ ├── index.html │ └── index.js ├── test │ ├── timepicker-test.js │ ├── utils-test.js │ └── utils.js └── timepicker │ ├── gradients.js │ ├── index.js │ ├── style.js │ └── utils.js ├── webpack-server.js └── webpack.config.development.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react", "es2015", "stage-1"] 3 | } -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "ecmaFeatures": { 3 | "arrowFunctions": true, 4 | "blockBindings": true, 5 | "classes": true, 6 | "defaultParams": true, 7 | "destructuring": true, 8 | "forOf": true, 9 | "generators": false, 10 | "modules": true, 11 | "objectLiteralComputedProperties": true, 12 | "objectLiteralDuplicateProperties": false, 13 | "objectLiteralShorthandMethods": true, 14 | "objectLiteralShorthandProperties": true, 15 | "spread": true, 16 | "superInFunctions": true, 17 | "templateStrings": true, 18 | "jsx": true, 19 | "experimentalObjectRestSpread": true 20 | }, 21 | "plugins": [ 22 | "react" 23 | ], 24 | "env": { 25 | "es6": true, 26 | "browser": true, 27 | "node": true 28 | }, 29 | "parser": "babel-eslint", 30 | "rules": { 31 | 32 | /** 33 | * Strict mode 34 | */ 35 | // babel inserts "use strict"; for us 36 | // http://eslint.org/docs/rules/strict 37 | "strict": [2, "never"], 38 | 39 | /** 40 | * ES6 41 | */ 42 | "no-var": 2, // http://eslint.org/docs/rules/no-var 43 | 44 | /** 45 | * Variables 46 | */ 47 | "no-shadow": 2, // http://eslint.org/docs/rules/no-shadow 48 | "no-shadow-restricted-names": 2, // http://eslint.org/docs/rules/no-shadow-restricted-names 49 | "no-unused-vars": [2, { // http://eslint.org/docs/rules/no-unused-vars 50 | "vars": "local", 51 | "args": "after-used" 52 | }], 53 | "no-use-before-define": 2, // http://eslint.org/docs/rules/no-use-before-define 54 | 55 | 56 | /** 57 | * Possible errors 58 | */ 59 | "comma-dangle": [2, "never"], // http://eslint.org/docs/rules/comma-dangle 60 | "no-cond-assign": [2, "always"], // http://eslint.org/docs/rules/no-cond-assign 61 | "no-console": 2, // http://eslint.org/docs/rules/no-console 62 | "no-debugger": 2, // http://eslint.org/docs/rules/no-debugger 63 | "no-alert": 2, // http://eslint.org/docs/rules/no-alert 64 | "no-constant-condition": 2, // http://eslint.org/docs/rules/no-constant-condition 65 | "no-dupe-keys": 2, // http://eslint.org/docs/rules/no-dupe-keys 66 | "no-duplicate-case": 2, // http://eslint.org/docs/rules/no-duplicate-case 67 | "no-empty": 2, // http://eslint.org/docs/rules/no-empty 68 | "no-ex-assign": 2, // http://eslint.org/docs/rules/no-ex-assign 69 | "no-extra-boolean-cast": 0, // http://eslint.org/docs/rules/no-extra-boolean-cast 70 | "no-extra-semi": 2, // http://eslint.org/docs/rules/no-extra-semi 71 | "no-func-assign": 2, // http://eslint.org/docs/rules/no-func-assign 72 | "no-inner-declarations": 2, // http://eslint.org/docs/rules/no-inner-declarations 73 | "no-invalid-regexp": 2, // http://eslint.org/docs/rules/no-invalid-regexp 74 | "no-irregular-whitespace": 2, // http://eslint.org/docs/rules/no-irregular-whitespace 75 | "no-obj-calls": 2, // http://eslint.org/docs/rules/no-obj-calls 76 | "quote-props": [2, "as-needed"],// http://eslint.org/docs/rules/quote-props 77 | "no-sparse-arrays": 2, // http://eslint.org/docs/rules/no-sparse-arrays 78 | "no-unreachable": 2, // http://eslint.org/docs/rules/no-unreachable 79 | "use-isnan": 2, // http://eslint.org/docs/rules/use-isnan 80 | "block-scoped-var": 2, // http://eslint.org/docs/rules/block-scoped-var 81 | "valid-typeof": 2, // http://eslint.org/docs/rules/valid-typeof.html 82 | "no-unexpected-multiline": 2, //http://eslint.org/docs/rules/no-unexpected-multiline 83 | // "no-empty-function": 2, //http://eslint.org/docs/rules/no-empty-function.html 84 | 85 | 86 | 87 | /** 88 | * Best practices 89 | */ 90 | "consistent-return": 2, // http://eslint.org/docs/rules/consistent-return 91 | "curly": [2, "multi-line"], // http://eslint.org/docs/rules/curly 92 | "dot-notation": [2, { // http://eslint.org/docs/rules/dot-notation 93 | "allowKeywords": true 94 | }], 95 | "eqeqeq": 2, // http://eslint.org/docs/rules/eqeqeq 96 | "guard-for-in": 2, // http://eslint.org/docs/rules/guard-for-in 97 | "no-caller": 2, // http://eslint.org/docs/rules/no-caller 98 | "no-else-return": 2, // http://eslint.org/docs/rules/no-else-return 99 | "no-eq-null": 2, // http://eslint.org/docs/rules/no-eq-null 100 | "no-eval": 2, // http://eslint.org/docs/rules/no-eval 101 | "no-extend-native": 2, // http://eslint.org/docs/rules/no-extend-native 102 | "no-extra-bind": 2, // http://eslint.org/docs/rules/no-extra-bind 103 | "no-fallthrough": 2, // http://eslint.org/docs/rules/no-fallthrough 104 | "no-floating-decimal": 2, // http://eslint.org/docs/rules/no-floating-decimal 105 | "no-implied-eval": 2, // http://eslint.org/docs/rules/no-implied-eval 106 | "no-lone-blocks": 2, // http://eslint.org/docs/rules/no-lone-blocks 107 | "no-loop-func": 2, // http://eslint.org/docs/rules/no-loop-func 108 | "no-multi-str": 2, // http://eslint.org/docs/rules/no-multi-str 109 | "no-native-reassign": 2, // http://eslint.org/docs/rules/no-native-reassign 110 | "no-new": 2, // http://eslint.org/docs/rules/no-new 111 | "no-new-func": 2, // http://eslint.org/docs/rules/no-new-func 112 | "no-new-wrappers": 2, // http://eslint.org/docs/rules/no-new-wrappers 113 | "no-octal": 2, // http://eslint.org/docs/rules/no-octal 114 | "no-octal-escape": 2, // http://eslint.org/docs/rules/no-octal-escape 115 | "no-proto": 2, // http://eslint.org/docs/rules/no-proto 116 | "no-redeclare": 2, // http://eslint.org/docs/rules/no-redeclare 117 | "no-return-assign": 2, // http://eslint.org/docs/rules/no-return-assign 118 | "no-script-url": 2, // http://eslint.org/docs/rules/no-script-url 119 | "no-self-compare": 2, // http://eslint.org/docs/rules/no-self-compare 120 | "no-sequences": 2, // http://eslint.org/docs/rules/no-sequences 121 | "no-throw-literal": 2, // http://eslint.org/docs/rules/no-throw-literal 122 | "no-with": 2, // http://eslint.org/docs/rules/no-with 123 | "radix": 2, // http://eslint.org/docs/rules/radix 124 | "vars-on-top": 2, // http://eslint.org/docs/rules/vars-on-top 125 | "wrap-iife": [2, "any"], // http://eslint.org/docs/rules/wrap-iife 126 | "yoda": 2, // http://eslint.org/docs/rules/yoda 127 | "dot-location": [2, "property"], // http://eslint.org/docs/rules/dot-location.html 128 | "no-case-declarations": 2, // http://eslint.org/docs/rules/no-case-declarations.html 129 | /* "no-implicit-globals": 2, */ // http://eslint.org/docs/rules/no-implicit-globals 130 | // "no-magic-numbers": [2, { // http://eslint.org/docs/rules/no-magic-numbers.html 131 | // "ignoreArrayIndexes": true 132 | // }], 133 | "no-labels": 2, //http://eslint.org/docs/rules/no-labels.html 134 | 135 | 136 | /** 137 | * Style 138 | */ 139 | "indent": [2, 2], // http://eslint.org/docs/rules/ 140 | "brace-style": [2, // http://eslint.org/docs/rules/brace-style 141 | "1tbs", { 142 | "allowSingleLine": true 143 | }], 144 | "quotes": [ 145 | 2, "single", "avoid-escape" // http://eslint.org/docs/rules/quotes 146 | ], 147 | "camelcase": [2, { // http://eslint.org/docs/rules/camelcase 148 | "properties": "never" 149 | }], 150 | "comma-spacing": [2, { // http://eslint.org/docs/rules/comma-spacing 151 | "before": false, 152 | "after": true 153 | }], 154 | "comma-style": [2, "last"], // http://eslint.org/docs/rules/comma-style 155 | "eol-last": 2, // http://eslint.org/docs/rules/eol-last 156 | "func-names": 2, // http://eslint.org/docs/rules/func-names 157 | "key-spacing": [2, { // http://eslint.org/docs/rules/key-spacing 158 | "align": "colon", 159 | "beforeColon": true, 160 | "afterColon": true 161 | }], 162 | "new-cap": [2, { // http://eslint.org/docs/rules/new-cap 163 | "newIsCap": true 164 | }], 165 | "no-multiple-empty-lines": [2, { // http://eslint.org/docs/rules/no-multiple-empty-lines 166 | "max": 2 167 | }], 168 | "no-new-object": 2, // http://eslint.org/docs/rules/no-new-object 169 | "no-spaced-func": 2, // http://eslint.org/docs/rules/no-spaced-func 170 | "no-trailing-spaces": 2, // http://eslint.org/docs/rules/no-trailing-spaces 171 | "no-extra-parens": [2, "functions"],// http://eslint.org/docs/rules/no-extra-parens 172 | "no-underscore-dangle": 0, // http://eslint.org/docs/rules/no-underscore-dangle 173 | "one-var": [2, "never"], // http://eslint.org/docs/rules/one-var 174 | "padded-blocks": [2, "never"], // http://eslint.org/docs/rules/padded-blocks 175 | "semi": [2, "always"], // http://eslint.org/docs/rules/semi 176 | "semi-spacing": [2, { // http://eslint.org/docs/rules/semi-spacing 177 | "before": false, 178 | "after": true 179 | }], 180 | 181 | "space-before-blocks": 2, // http://eslint.org/docs/rules/space-before-blocks 182 | "space-before-function-paren": [2, "never"], // http://eslint.org/docs/rules/space-before-function-paren 183 | "space-infix-ops": 2, // http://eslint.org/docs/rules/space-infix-ops 184 | "spaced-comment": [2, "always"], // http://eslint.org/docs/rules/spaced-comment 185 | "max-params": [2, 3], // http://eslint.org/docs/rules/max-params.html 186 | 187 | /** 188 | * JSX style 189 | */ 190 | "react/display-name": 2, 191 | "react/jsx-boolean-value": 2, 192 | "react/jsx-no-undef": 2, 193 | "react/jsx-sort-prop-types": 0, 194 | "react/jsx-uses-react": 2, 195 | "react/jsx-uses-vars": 2, 196 | "react/no-did-mount-set-state": 2, 197 | "react/no-did-update-set-state": 2, 198 | "react/no-multi-comp": 2, 199 | "react/no-unknown-property": 2, 200 | "react/prop-types": 2, 201 | "react/react-in-jsx-scope": 2, 202 | "react/self-closing-comp": 2, 203 | "react/wrap-multilines": 2, 204 | "react/sort-comp": [2, { 205 | "order": [ 206 | "static-methods", 207 | "lifecycle", 208 | "everything-else", 209 | "render" 210 | ], 211 | "groups": { 212 | "lifecycle": [ 213 | "displayName", 214 | "propTypes", 215 | "contextTypes", 216 | "childContextTypes", 217 | "mixins", 218 | "statics", 219 | "defaultProps", 220 | "constructor", 221 | "getDefaultProps", 222 | "getInitialState", 223 | "state", 224 | "getChildContext", 225 | "componentWillMount", 226 | "componentDidMount", 227 | "componentWillReceiveProps", 228 | "shouldComponentUpdate", 229 | "componentWillUpdate", 230 | "componentDidUpdate", 231 | "componentWillUnmount" 232 | ] 233 | } 234 | }], 235 | "react/jsx-equals-spacing": [2, "never"], 236 | "react/jsx-handler-names": 2, 237 | "react/jsx-closing-bracket-location": [2, "line-aligned"], 238 | "react/jsx-indent-props": [2, 2], 239 | "react/jsx-indent": [2, 2], 240 | "react/jsx-max-props-per-line": [2, {"maximum": 2}], 241 | "react/jsx-no-duplicate-props": [2, {"ignoreCase": true}], 242 | /* "react/jsx-space-before-closing": [2, "always"],*/ 243 | "react/prefer-es6-class": 2, 244 | "react/no-string-refs": 2, 245 | /*"react/prefer-stateless-function" : 2,*/ 246 | "react/jsx-pascal-case": 2 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IntelliJ project files 2 | .idea 3 | *.iml 4 | out 5 | /node_modules/ 6 | /build 7 | npm-debug.log -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .idea 2 | /src 3 | .babelrc 4 | webpack-server.js 5 | webpack.config.development.js 6 | .eslintrc.json 7 | npm-debug.log -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - stable 5 | 6 | install: 7 | - npm install 8 | 9 | script: 10 | - npm test -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-gradient-timepicker [![Build Status](https://travis-ci.org/rahuldhawani/react-gradient-timepicker.svg?branch=master)](https://travis-ci.org/rahuldhawani/react-gradient-timepicker) 2 | A beautiful gradients timepicker component built in and for awesome React. 3 | 4 | ### Demo 5 | https://rahuldhawani.github.io/react-gradient-timepicker/ 6 | 7 | ### Installation 8 | ```bash 9 | npm install react-gradient-timepicker --save 10 | ``` 11 | ### How to use? 12 | ```jsx 13 | import TimePicker from 'react-gradient-timepicker'; // or 14 | var TimePicker = require('react-gradient-timepicker'); 15 | ``` 16 | ```jsx 17 | { 23 | alert('val:' + val.format12); 24 | }} 25 | /> 26 | ``` 27 | ### PropTypes 28 | | prop | type | description | optional | 29 | |-------------|----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------| 30 | | #time | string | 24 hours format time to initial time | Yes. Takes current time if not provided | 31 | | theme | string | Gradient name from www.uigradients.com. header, buttons, hand colors will take theme color. If you want to change buttons and hand color, provide color1 prop. Note: Header color cant be changed if theme is provided | Yes. If not provided it takes color1. if color1 is not provided it takes default color ie #F26B83. | 32 | | color1 | string | color1 is primary color ie. header, buttons, hand will take this color. If you want to header color, provide headerColor prop | Yes. Takes default color for header, buttons, hand ie. #F26B83 | 33 | | headerColor | string | Header color for timepicker. Does not work if theme prop is provided. | Yes. If not provided it takes color1. if color1 is not provided it takes default color ie #F26B83. | 34 | | placeholder | string | Placeholder string for timepicker input | No | 35 | | className | string | Class name for timepicker input | Yes | 36 | | onSet | function | The funciton to call when Set button is clicked | No | 37 | 38 | 39 | ##### onSet PropType 40 | onSet function is called everytime **SET** button is clicked with parameter 41 | ```js 42 | { 43 | format12: "01:00 AM" // 12 hour format, 44 | format24: "13:00" // 24 hour format 45 | } 46 | ``` 47 | ##### theme PropType 48 | react-gradient-timepicker uses gradients color www.uigradients.com . theme proptype takes name of the gradient scheme from www.uigradients.com. 49 | 50 | ### Screenshots 51 | ![Ash theme](http://i.imgur.com/wGHZYQF.png) 52 | ![Bourbon theme ](http://i.imgur.com/aXhofjL.png) 53 | ![default ](http://i.imgur.com/B2cZala.png) 54 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-gradient-timepicker", 3 | "version": "0.0.3", 4 | "author": "Rahul Dhawani", 5 | "main": "build/index.js", 6 | "description": "React Timepicker", 7 | "scripts": { 8 | "dev": "node webpacker-server.js", 9 | "test": "mocha --require ./src/test/utils.js --compilers js:babel-core/register --reporter spec ./src/test", 10 | "build": "rm -rf ./build && babel src/timepicker -d build/" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/rahuldhawani/react-gradient-timepicker.git" 15 | }, 16 | "homepage": "https://github.com/rahuldhawani/react-gradient-timepicker", 17 | "bugs": "https://github.com/rahuldhawani/react-gradient-timepicker/issues", 18 | "keywords": [ 19 | "react", 20 | "time", 21 | "timepicker", 22 | "time-picker", 23 | "gradient" 24 | ], 25 | "license": "MIT", 26 | "dependencies": { 27 | "lodash.find": "^4.6.0", 28 | "react": "^15.3.2", 29 | "react-dom": "^15.3.2" 30 | }, 31 | "devDependencies": { 32 | "babel-core": "^6.2.1", 33 | "babel-eslint": "^6.1.2", 34 | "babel-loader": "^6.2.0", 35 | "babel-preset-es2015": "^6.1.18", 36 | "babel-preset-stage-1": "^6.16.0", 37 | "babel-preset-react": "^6.1.18", 38 | "chai": "^3.5.0", 39 | "enzyme": "^2.5.1", 40 | "eslint": "^3.6.1", 41 | "eslint-plugin-react": "^6.2.0", 42 | "jsdom": "^9.8.0", 43 | "mocha": "^3.1.2", 44 | "mocha-jsdom": "^1.1.0", 45 | "react-addons-test-utils": "^15.3.2", 46 | "sinon": "^1.17.6", 47 | "webpack": "^1.13.2", 48 | "webpack-dev-server": "^1.16.2" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Hi

8 |
9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/example/index.js: -------------------------------------------------------------------------------- 1 | /* eslint no-alert:0 */ 2 | import React, { Component } from 'react'; 3 | import ReactDOM from 'react-dom'; 4 | import TimePicker from '../timepicker'; 5 | class Example extends Component { 6 | static propTypes = {}; 7 | 8 | render() { 9 | return ( 10 |
11 | { 16 | alert('val:' + val.format24); 17 | } } 18 | /> 19 | 20 | { 27 | alert('val:' + val.format12); 28 | } } 29 | /> 30 |
31 | ); 32 | } 33 | } 34 | 35 | ReactDOM.render( 36 | 37 | , document.getElementById('example')); 38 | -------------------------------------------------------------------------------- /src/test/timepicker-test.js: -------------------------------------------------------------------------------- 1 | /* eslint func-names: 0 */ 2 | import { expect } from 'chai'; 3 | import Timepicker from '../timepicker'; 4 | import { mount } from 'enzyme'; 5 | import React from 'react'; 6 | import sinon from 'sinon'; 7 | 8 | 9 | describe('Timepicker', function() { 10 | before(function() { 11 | this.handleSetSpy = sinon.spy(); 12 | this.timepicker = mount( 13 | 21 | ); 22 | }); 23 | 24 | describe('#setHour', function() { 25 | it('returns hour, index, degree in object for any degree', function() { 26 | expect(this.timepicker.instance().setHour(0)).to.deep.equal({ 27 | degree : 0, 28 | hour : '12', 29 | selectedIndexDegree : 0 30 | }); 31 | 32 | expect(this.timepicker.instance().setHour(20)).to.deep.equal({ 33 | degree : 30, 34 | hour : '01', 35 | selectedIndexDegree : 1 36 | }); 37 | 38 | expect(this.timepicker.instance().setHour(13)).to.deep.equal({ 39 | degree : 0, 40 | hour : '12', 41 | selectedIndexDegree : 0 42 | }); 43 | }); 44 | }); 45 | 46 | describe('#setMinute', function() { 47 | it('returns minute, index, degree in object for any degree', function() { 48 | expect(this.timepicker.instance().setMinute(0)).to.deep.equal({ 49 | degree : 0, 50 | minute : '00', 51 | selectedIndexDegree : 0 52 | }); 53 | 54 | expect(this.timepicker.instance().setMinute(20)).to.deep.equal({ 55 | degree : 18, 56 | minute : '03', 57 | selectedIndexDegree : 0.6 58 | }); 59 | 60 | expect(this.timepicker.instance().setMinute(344)).to.deep.equal({ 61 | degree : 342, 62 | minute : '57', 63 | selectedIndexDegree : 11.4 64 | }); 65 | }); 66 | }); 67 | 68 | describe('#addStyles', function() { 69 | before(function() { 70 | this.timepicker1 = mount( 71 |
72 | 79 | 86 | 92 |
93 | ); 94 | }); 95 | 96 | it('adds common styles only once', function() { 97 | expect(document.querySelectorAll('#react-timepicker-common-style').length).to.equal(1); 98 | }); 99 | 100 | it('adds particular theme styles only once', function() { 101 | console.log(this.timepicker1); 102 | expect(document.querySelectorAll('#' + this.timepicker.instance().getThemeSelector('Pinky')).length).to.equal(1); 103 | expect(document.querySelectorAll('#' + this.timepicker.instance().getThemeSelector('Instagram')).length).to.equal(1); 104 | }); 105 | 106 | it('adds style with id \'react-theme-style\' if no theme name is given', function() { 107 | expect(document.querySelectorAll('#react-time-picker-theme').length).to.equal(1); 108 | }); 109 | }); 110 | 111 | describe('handleSet', function() { 112 | before(function() { 113 | this.timepicker.instance().handleSet(); 114 | }); 115 | 116 | it('closes modal; sets state toShow to false', function() { 117 | expect(this.timepicker.state().toShow).to.equal(false); 118 | }); 119 | 120 | it('calls onSet from props', function() { 121 | sinon.assert.callCount(this.handleSetSpy, 1); 122 | }); 123 | 124 | after(function() { 125 | this.handleSetSpy.reset(); 126 | }); 127 | }); 128 | 129 | describe('#getTime', function() { 130 | it('returns object with 12hour and 24hour format for any hour, minute, and meridiem given', function() { 131 | expect(this.timepicker.instance().getTime(12, 12, true)).to.deep.equal({ 132 | format12 : '12:12 AM', 133 | format24 : '00:12' 134 | }); 135 | 136 | expect(this.timepicker.instance().getTime(1, 12, true)).to.deep.equal({ 137 | format12 : '01:12 AM', 138 | format24 : '01:12' 139 | }); 140 | 141 | expect(this.timepicker.instance().getTime(1, 12, false)).to.deep.equal({ 142 | format12 : '01:12 PM', 143 | format24 : '13:12' 144 | }); 145 | }); 146 | }); 147 | }); 148 | -------------------------------------------------------------------------------- /src/test/utils-test.js: -------------------------------------------------------------------------------- 1 | /* eslint func-names: 0 */ 2 | import { expect } from 'chai'; 3 | 4 | import { appendZero, getFormat12, format12to24, format24to12 } from '../timepicker/utils'; 5 | 6 | describe('#appendZero', function() { 7 | it('should append 0 for digits in range from 0 to 9', function() { 8 | expect(appendZero(0)).to.equal('00'); 9 | expect(appendZero(9)).to.equal('09'); 10 | expect(appendZero(11)).to.equal('11'); 11 | }); 12 | }); 13 | 14 | describe('#getFormat12', function() { // this doesn't convent 24hour format to 12hour format 15 | it('should return string in 12hour format', function() { 16 | expect(getFormat12(1, 33, true)).to.equal('01:33 AM'); 17 | expect(getFormat12(1, 33, false)).to.equal('01:33 PM'); 18 | }); 19 | }); 20 | 21 | describe('#format12to24', function() { 22 | it('should convert given 12hour format to 24hour format', function() { 23 | expect(format12to24(1, 33, true)).to.equal('01:33'); 24 | expect(format12to24(1, 33, false)).to.equal('13:33'); 25 | expect(format12to24(12, 0, true)).to.equal('00:00'); 26 | }); 27 | }); 28 | 29 | describe('#format24to12', function() { 30 | it('should convert given 24hour format to 12hour format', function() { 31 | expect(format24to12(1, 33)).to.equal('01:33 AM'); 32 | expect(format24to12(2, 33)).to.equal('02:33 AM'); 33 | expect(format24to12(12, 0)).to.equal('12:00 PM'); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /src/test/utils.js: -------------------------------------------------------------------------------- 1 | var jsdom = require('jsdom') 2 | 3 | // setup the simplest document possible 4 | var doc = jsdom.jsdom('') 5 | 6 | // get the window object out of the document 7 | var win = doc.defaultView 8 | 9 | // set globals for mocha that make access to document and window feel 10 | // natural in the test environment 11 | global.document = doc 12 | global.window = win 13 | 14 | // take all properties of the window object and also attach it to the 15 | // mocha global object 16 | propagateToGlobal(win) 17 | 18 | // from mocha-jsdom https://github.com/rstacruz/mocha-jsdom/blob/master/index.js#L80 19 | function propagateToGlobal (window) { 20 | for (let key in window) { 21 | if (!window.hasOwnProperty(key)) continue 22 | if (key in global) continue 23 | 24 | global[key] = window[key] 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/timepicker/gradients.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | const gradients = [ 3 | { 4 | "name": "Noon to Dusk", 5 | "colors": ["#ff6e7f", "#bfe9ff"] 6 | }, 7 | { 8 | "name": "YouTube", 9 | "colors": ["#e52d27", "#b31217"] 10 | }, 11 | { 12 | "name": "Cool Brown", 13 | "colors": ["#603813", "#b29f94"] 14 | }, 15 | { 16 | "name": "Harmonic Energy", 17 | "colors": ["#16A085", "#F4D03F"] 18 | }, 19 | { 20 | "name": "Playing with Reds", 21 | "colors": ["#D31027", "#EA384D"] 22 | }, 23 | { 24 | "name": "Sunny Days", 25 | "colors": ["#EDE574", "#E1F5C4"] 26 | }, 27 | { 28 | "name": "Green Beach", 29 | "colors": ["#02AAB0", "#00CDAC"] 30 | }, 31 | { 32 | "name": "Intuitive Purple", 33 | "colors": ["#DA22FF", "#9733EE"] 34 | }, 35 | { 36 | "name": "Emerald Water", 37 | "colors": ["#348F50", "#56B4D3"] 38 | }, 39 | { 40 | "name": "Lemon Twist", 41 | "colors": ["#3CA55C", "#B5AC49"] 42 | }, 43 | { 44 | "name": "Horizon", 45 | "colors": ["#003973", "#E5E5BE"] 46 | }, 47 | { 48 | "name": "Rose Water", 49 | "colors": ["#E55D87", "#5FC3E4"] 50 | }, 51 | { 52 | "name": "Frozen", 53 | "colors": ["#403B4A", "#E7E9BB"] 54 | }, 55 | { 56 | "name": "Mango Pulp", 57 | "colors": ["#F09819", "#EDDE5D"] 58 | }, 59 | { 60 | "name": "Bloody Mary", 61 | "colors": ["#FF512F", "#DD2476"] 62 | }, 63 | { 64 | "name": "Aubergine", 65 | "colors": ["#AA076B", "#61045F"] 66 | }, 67 | { 68 | "name": "Aqua Marine", 69 | "colors": ["#1A2980", "#26D0CE"] 70 | }, 71 | { 72 | "name": "Sunrise", 73 | "colors": ["#FF512F", "#F09819"] 74 | }, 75 | { 76 | "name": "Purple Paradise", 77 | "colors": ["#1D2B64", "#F8CDDA"] 78 | }, 79 | { 80 | "name": "Sea Weed", 81 | "colors": ["#4CB8C4", "#3CD3AD"] 82 | }, 83 | { 84 | "name": "Pinky", 85 | "colors": ["#DD5E89", "#F7BB97"] 86 | }, 87 | { 88 | "name": "Cherry", 89 | "colors": ["#EB3349", "#F45C43"] 90 | }, 91 | { 92 | "name": "Mojito", 93 | "colors": ["#1D976C", "#93F9B9"] 94 | }, 95 | { 96 | "name": "Juicy Orange", 97 | "colors": ["#FF8008", "#FFC837"] 98 | }, 99 | { 100 | "name": "Mirage", 101 | "colors": ["#16222A", "#3A6073"] 102 | }, 103 | { 104 | "name": "Steel Gray", 105 | "colors": ["#1F1C2C", "#928DAB"] 106 | }, 107 | { 108 | "name": "Kashmir", 109 | "colors": ["#614385", "#516395"] 110 | }, 111 | { 112 | "name": "Electric Violet", 113 | "colors": ["#4776E6", "#8E54E9"] 114 | }, 115 | { 116 | "name": "Venice Blue", 117 | "colors": ["#085078", "#85D8CE"] 118 | }, 119 | { 120 | "name": "Bora Bora", 121 | "colors": ["#2BC0E4", "#EAECC6"] 122 | }, 123 | { 124 | "name": "Moss", 125 | "colors": ["#134E5E", "#71B280"] 126 | }, 127 | { 128 | "name": "Shroom Haze", 129 | "colors": ["#5C258D", "#4389A2"] 130 | }, 131 | { 132 | "name": "Mystic", 133 | "colors": ["#757F9A", "#D7DDE8"] 134 | }, 135 | { 136 | "name": "Midnight City", 137 | "colors": ["#232526", "#414345"] 138 | }, 139 | { 140 | "name": "Sea Blizz", 141 | "colors": ["#1CD8D2", "#93EDC7"] 142 | }, 143 | { 144 | "name": "Opa", 145 | "colors": ["#3D7EAA", "#FFE47A"] 146 | }, 147 | { 148 | "name": "Titanium", 149 | "colors": ["#283048", "#859398"] 150 | }, 151 | { 152 | "name": "Mantle", 153 | "colors": ["#24C6DC", "#514A9D"] 154 | }, 155 | { 156 | "name": "Dracula", 157 | "colors": ["#DC2424", "#4A569D"] 158 | }, 159 | { 160 | "name": "Peach", 161 | "colors": ["#ED4264", "#FFEDBC"] 162 | }, 163 | { 164 | "name": "Moonrise", 165 | "colors": ["#DAE2F8", "#D6A4A4"] 166 | }, 167 | { 168 | "name": "Clouds", 169 | "colors": ["#ECE9E6", "#FFFFFF"] 170 | }, 171 | { 172 | "name": "Stellar", 173 | "colors": ["#7474BF", "#348AC7"] 174 | }, 175 | { 176 | "name": "Bourbon", 177 | "colors": ["#EC6F66", "#F3A183"] 178 | }, 179 | { 180 | "name": "Calm Darya", 181 | "colors": ["#5f2c82", "#49a09d"] 182 | }, 183 | { 184 | "name": "Influenza", 185 | "colors": ["#C04848", "#480048"] 186 | }, 187 | { 188 | "name": "Shrimpy", 189 | "colors": ["#e43a15", "#e65245"] 190 | }, 191 | { 192 | "name": "Army", 193 | "colors": ["#414d0b", "#727a17"] 194 | }, 195 | { 196 | "name": "Miaka", 197 | "colors": ["#FC354C", "#0ABFBC"] 198 | }, 199 | { 200 | "name": "Pinot Noir", 201 | "colors": ["#4b6cb7", "#182848"] 202 | }, 203 | { 204 | "name": "Day Tripper", 205 | "colors": ["#f857a6", "#ff5858"] 206 | }, 207 | { 208 | "name": "Namn", 209 | "colors": ["#a73737", "#7a2828"] 210 | }, 211 | { 212 | "name": "Blurry Beach", 213 | "colors": ["#d53369", "#cbad6d"] 214 | }, 215 | { 216 | "name": "Vasily", 217 | "colors": ["#e9d362", "#333333"] 218 | }, 219 | { 220 | "name": "A Lost Memory", 221 | "colors": ["#DE6262", "#FFB88C"] 222 | }, 223 | { 224 | "name": "Petrichor", 225 | "colors": ["#666600", "#999966"] 226 | }, 227 | { 228 | "name": "Jonquil", 229 | "colors": ["#FFEEEE", "#DDEFBB"] 230 | }, 231 | { 232 | "name": "Sirius Tamed", 233 | "colors": ["#EFEFBB", "#D4D3DD"] 234 | }, 235 | { 236 | "name": "Kyoto", 237 | "colors": ["#c21500", "#ffc500"] 238 | }, 239 | { 240 | "name": "Misty Meadow", 241 | "colors": ["#215f00", "#e4e4d9"] 242 | }, 243 | { 244 | "name": "Aqualicious", 245 | "colors": ["#50C9C3", "#96DEDA"] 246 | }, 247 | { 248 | "name": "Moor", 249 | "colors": ["#616161", "#9bc5c3"] 250 | }, 251 | { 252 | "name": "Almost", 253 | "colors": ["#ddd6f3", "#faaca8"] 254 | }, 255 | { 256 | "name": "Forever Lost", 257 | "colors": ["#5D4157", "#A8CABA"] 258 | }, 259 | { 260 | "name": "Winter", 261 | "colors": ["#E6DADA", "#274046"] 262 | }, 263 | { 264 | "name": "Autumn", 265 | "colors": ["#DAD299", "#B0DAB9"] 266 | }, 267 | { 268 | "name": "Candy", 269 | "colors": ["#D3959B", "#BFE6BA"] 270 | }, 271 | { 272 | "name": "Reef", 273 | "colors": ["#00d2ff", "#3a7bd5"] 274 | }, 275 | { 276 | "name": "The Strain", 277 | "colors": ["#870000", "#190A05"] 278 | }, 279 | { 280 | "name": "Dirty Fog", 281 | "colors": ["#B993D6", "#8CA6DB"] 282 | }, 283 | { 284 | "name": "Earthly", 285 | "colors": ["#649173", "#DBD5A4"] 286 | }, 287 | { 288 | "name": "Virgin", 289 | "colors": ["#C9FFBF", "#FFAFBD"] 290 | }, 291 | { 292 | "name": "Ash", 293 | "colors": ["#606c88", "#3f4c6b"] 294 | }, 295 | { 296 | "name": "Shadow Night", 297 | "colors": ["#000000", "#53346D"] 298 | }, 299 | { 300 | "name": "Cherryblossoms", 301 | "colors": ["#FBD3E9", "#BB377D"] 302 | }, 303 | { 304 | "name": "Parklife", 305 | "colors": ["#ADD100", "#7B920A"] 306 | }, 307 | { 308 | "name": "Dance To Forget", 309 | "colors": ["#FF4E50", "#F9D423"] 310 | }, 311 | { 312 | "name": "Starfall", 313 | "colors": ["#F0C27B", "#4B1248"] 314 | }, 315 | { 316 | "name": "Red Mist", 317 | "colors": ["#000000", "#e74c3c"] 318 | }, 319 | { 320 | "name": "Teal Love", 321 | "colors": ["#AAFFA9", "#11FFBD"] 322 | }, 323 | { 324 | "name": "Neon Life", 325 | "colors": ["#B3FFAB", "#12FFF7"] 326 | }, 327 | { 328 | "name": "Man of Steel", 329 | "colors": ["#780206", "#061161"] 330 | }, 331 | { 332 | "name": "Amethyst", 333 | "colors": ["#9D50BB", "#6E48AA"] 334 | }, 335 | { 336 | "name": "Cheer Up Emo Kid", 337 | "colors": ["#556270", "#FF6B6B"] 338 | }, 339 | { 340 | "name": "Shore", 341 | "colors": ["#70e1f5", "#ffd194"] 342 | }, 343 | { 344 | "name": "Facebook Messenger", 345 | "colors": ["#00c6ff", "#0072ff"] 346 | }, 347 | { 348 | "name": "SoundCloud", 349 | "colors": ["#fe8c00", "#f83600"] 350 | }, 351 | { 352 | "name": "Behongo", 353 | "colors": ["#52c234", "#061700"] 354 | }, 355 | { 356 | "name": "ServQuick", 357 | "colors": ["#485563", "#29323c"] 358 | }, 359 | { 360 | "name": "Friday", 361 | "colors": ["#83a4d4", "#b6fbff"] 362 | }, 363 | { 364 | "name": "Martini", 365 | "colors": ["#FDFC47", "#24FE41"] 366 | }, 367 | { 368 | "name": "Metallic Toad", 369 | "colors": ["#abbaab", "#ffffff"] 370 | }, 371 | { 372 | "name": "Between The Clouds", 373 | "colors": ["#73C8A9", "#373B44"] 374 | }, 375 | { 376 | "name": "Crazy Orange I", 377 | "colors": ["#D38312", "#A83279"] 378 | }, 379 | { 380 | "name": "Hersheys", 381 | "colors": ["#1e130c", "#9a8478"] 382 | }, 383 | { 384 | "name": "Talking To Mice Elf", 385 | "colors": ["#948E99", "#2E1437"] 386 | }, 387 | { 388 | "name": "Purple Bliss", 389 | "colors": ["#360033", "#0b8793"] 390 | }, 391 | { 392 | "name": "Predawn", 393 | "colors": ["#FFA17F", "#00223E"] 394 | }, 395 | { 396 | "name": "Endless River", 397 | "colors": ["#43cea2", "#185a9d"] 398 | }, 399 | { 400 | "name": "Pastel Orange at the Sun", 401 | "colors": ["#ffb347", "#ffcc33"] 402 | }, 403 | { 404 | "name": "Twitch", 405 | "colors": ["#6441A5", "#2a0845"] 406 | }, 407 | { 408 | "name": "Instagram", 409 | "colors": ["#517fa4", "#243949"] 410 | }, 411 | { 412 | "name": "Flickr", 413 | "colors": ["#ff0084", "#33001b"] 414 | }, 415 | { 416 | "name": "Vine", 417 | "colors": ["#00bf8f", "#001510"] 418 | }, 419 | { 420 | "name": "Turquoise flow", 421 | "colors": ["#136a8a", "#267871"] 422 | }, 423 | { 424 | "name": "Portrait", 425 | "colors": ["#8e9eab", "#eef2f3"] 426 | }, 427 | { 428 | "name": "Virgin America", 429 | "colors": ["#7b4397", "#dc2430"] 430 | }, 431 | { 432 | "name": "Koko Caramel", 433 | "colors": ["#D1913C", "#FFD194"] 434 | }, 435 | { 436 | "name": "Fresh Turboscent", 437 | "colors": ["#F1F2B5", "#135058"] 438 | }, 439 | { 440 | "name": "Green to dark", 441 | "colors": ["#6A9113", "#141517"] 442 | }, 443 | { 444 | "name": "Ukraine", 445 | "colors": ["#004FF9", "#FFF94C"] 446 | }, 447 | { 448 | "name": "Curiosity blue", 449 | "colors": ["#525252", "#3d72b4"] 450 | }, 451 | { 452 | "name": "Dark Knight", 453 | "colors": ["#BA8B02", "#181818"] 454 | }, 455 | { 456 | "name": "Piglet", 457 | "colors": ["#ee9ca7", "#ffdde1"] 458 | }, 459 | { 460 | "name": "Lizard", 461 | "colors": ["#304352", "#d7d2cc"] 462 | }, 463 | { 464 | "name": "Sage Persuasion", 465 | "colors": ["#CCCCB2", "#757519"] 466 | }, 467 | { 468 | "name": "Between Night and Day", 469 | "colors": ["#2c3e50", "#3498db"] 470 | }, 471 | { 472 | "name": "Timber", 473 | "colors": ["#fc00ff", "#00dbde"] 474 | }, 475 | { 476 | "name": "Passion", 477 | "colors": ["#e53935", "#e35d5b"] 478 | }, 479 | { 480 | "name": "Clear Sky", 481 | "colors": ["#005C97", "#363795"] 482 | }, 483 | { 484 | "name": "Master Card", 485 | "colors": ["#f46b45", "#eea849"] 486 | }, 487 | { 488 | "name": "Back To Earth", 489 | "colors": ["#00C9FF", "#92FE9D"] 490 | }, 491 | { 492 | "name": "Deep Purple", 493 | "colors": ["#673AB7", "#512DA8"] 494 | }, 495 | { 496 | "name": "Little Leaf", 497 | "colors": ["#76b852", "#8DC26F"] 498 | }, 499 | { 500 | "name": "Netflix", 501 | "colors": ["#8E0E00", "#1F1C18"] 502 | }, 503 | { 504 | "name": "Light Orange", 505 | "colors": ["#FFB75E", "#ED8F03"] 506 | }, 507 | { 508 | "name": "Green and Blue", 509 | "colors": ["#c2e59c", "#64b3f4"] 510 | }, 511 | { 512 | "name": "Poncho", 513 | "colors": ["#403A3E", "#BE5869"] 514 | }, 515 | { 516 | "name": "Back to the Future", 517 | "colors": ["#C02425", "#F0CB35"] 518 | }, 519 | { 520 | "name": "Blush", 521 | "colors": ["#B24592", "#F15F79"] 522 | }, 523 | { 524 | "name": "Inbox", 525 | "colors": ["#457fca", "#5691c8"] 526 | }, 527 | { 528 | "name": "Purplin", 529 | "colors": ["#6a3093", "#a044ff"] 530 | }, 531 | { 532 | "name": "Pale Wood", 533 | "colors": ["#eacda3", "#d6ae7b"] 534 | }, 535 | { 536 | "name": "Haikus", 537 | "colors": ["#fd746c", "#ff9068"] 538 | }, 539 | { 540 | "name": "Pizelex", 541 | "colors": ["#114357", "#F29492"] 542 | }, 543 | { 544 | "name": "Joomla", 545 | "colors": ["#1e3c72", "#2a5298"] 546 | }, 547 | { 548 | "name": "Christmas", 549 | "colors": ["#2F7336", "#AA3A38"] 550 | }, 551 | { 552 | "name": "Minnesota Vikings", 553 | "colors": ["#5614B0", "#DBD65C"] 554 | }, 555 | { 556 | "name": "Miami Dolphins", 557 | "colors": ["#4DA0B0", "#D39D38"] 558 | }, 559 | { 560 | "name": "Forest", 561 | "colors": ["#5A3F37", "#2C7744"] 562 | }, 563 | { 564 | "name": "Nighthawk", 565 | "colors": ["#2980b9", "#2c3e50"] 566 | }, 567 | { 568 | "name": "Superman", 569 | "colors": ["#0099F7", "#F11712"] 570 | }, 571 | { 572 | "name": "Suzy", 573 | "colors": ["#834d9b", "#d04ed6"] 574 | }, 575 | { 576 | "name": "Dark Skies", 577 | "colors": ["#4B79A1", "#283E51"] 578 | }, 579 | { 580 | "name": "Deep Space", 581 | "colors": ["#000000", "#434343"] 582 | }, 583 | { 584 | "name": "Decent", 585 | "colors": ["#4CA1AF", "#C4E0E5"] 586 | }, 587 | { 588 | "name": "Colors Of Sky", 589 | "colors": ["#E0EAFC", "#CFDEF3"] 590 | }, 591 | { 592 | "name": "Purple White", 593 | "colors": ["#BA5370", "#F4E2D8"] 594 | }, 595 | { 596 | "name": "Ali", 597 | "colors": ["#ff4b1f", "#1fddff"] 598 | }, 599 | { 600 | "name": "Alihossein", 601 | "colors": ["#f7ff00", "#db36a4"] 602 | }, 603 | { 604 | "name": "Shahabi", 605 | "colors": ["#a80077", "#66ff00"] 606 | }, 607 | { 608 | "name": "Red Ocean", 609 | "colors": ["#1D4350", "#A43931"] 610 | }, 611 | { 612 | "name": "Tranquil", 613 | "colors": ["#EECDA3", "#EF629F"] 614 | }, 615 | { 616 | "name": "Transfile", 617 | "colors": ["#16BFFD", "#CB3066"] 618 | }, 619 | 620 | { 621 | "name": "Sylvia", 622 | "colors": ["#ff4b1f", "#ff9068"] 623 | }, 624 | { 625 | "name": "Sweet Morning", 626 | "colors": ["#FF5F6D", "#FFC371"] 627 | }, 628 | { 629 | "name": "Politics", 630 | "colors": ["#2196f3", "#f44336"] 631 | }, 632 | { 633 | "name": "Bright Vault", 634 | "colors": ["#00d2ff", "#928DAB"] 635 | }, 636 | { 637 | "name": "Solid Vault", 638 | "colors": ["#3a7bd5", "#3a6073"] 639 | }, 640 | { 641 | "name": "Sunset", 642 | "colors": ["#0B486B", "#F56217"] 643 | }, 644 | { 645 | "name": "Grapefruit Sunset", 646 | "colors": ["#e96443", "#904e95"] 647 | }, 648 | { 649 | "name": "Deep Sea Space", 650 | "colors": ["#2C3E50", "#4CA1AF"] 651 | }, 652 | { 653 | "name": "Dusk", 654 | "colors": ["#2C3E50", "#FD746C"] 655 | }, 656 | { 657 | "name": "Minimal Red", 658 | "colors": ["#F00000", "#DC281E"] 659 | }, 660 | { 661 | "name": "Royal", 662 | "colors": ["#141E30", "#243B55"] 663 | }, 664 | { 665 | "name": "Mauve", 666 | "colors": ["#42275a", "#734b6d"] 667 | }, 668 | { 669 | "name": "Frost", 670 | "colors": ["#000428", "#004e92"] 671 | }, 672 | { 673 | "name": "Lush", 674 | "colors": ["#56ab2f", "#a8e063"] 675 | }, 676 | { 677 | "name": "Firewatch", 678 | "colors": ["#cb2d3e", "#ef473a"] 679 | }, 680 | { 681 | "name": "Sherbert", 682 | "colors": ["#f79d00", "#64f38c"] 683 | }, 684 | { 685 | "name": "Blood Red", 686 | "colors": ["#f85032", "#e73827"] 687 | }, 688 | { 689 | "name": "Sun on the Horizon", 690 | "colors": ["#fceabb", "#f8b500"] 691 | }, 692 | { 693 | "name": "IIIT Delhi", 694 | "colors": ["#808080", "#3fada8"] 695 | }, 696 | { 697 | "name": "Dusk", 698 | "colors": ["#ffd89b", "#19547b"] 699 | }, 700 | { 701 | "name": "50 Shades of Grey", 702 | "colors": ["#bdc3c7", "#2c3e50"] 703 | }, 704 | { 705 | "name": "Dania", 706 | "colors": ["#BE93C5", "#7BC6CC"] 707 | }, 708 | { 709 | "name": "Limeade", 710 | "colors": ["#A1FFCE", "#FAFFD1"] 711 | }, 712 | { 713 | "name": "Disco", 714 | "colors": ["#4ECDC4", "#556270"] 715 | }, 716 | { 717 | "name": "Love Couple", 718 | "colors": ["#3a6186", "#89253e"] 719 | }, 720 | { 721 | "name": "Azure Pop", 722 | "colors": ["#ef32d9", "#89fffd"] 723 | }, 724 | { 725 | "name": "Nepal", 726 | "colors": ["#de6161", "#2657eb"] 727 | }, 728 | { 729 | "name": "Cosmic Fusion", 730 | "colors": ["#ff00cc", "#333399"] 731 | }, 732 | { 733 | "name": "Brady Brady Fun Fun", 734 | "colors": ["#00c3ff", "#ffff1c"] 735 | } 736 | ]; 737 | 738 | export default gradients; -------------------------------------------------------------------------------- /src/timepicker/index.js: -------------------------------------------------------------------------------- 1 | import React, {PropTypes, Component} from 'react'; 2 | import {getColorStyles, style as commonStyles} from './style'; 3 | import { appendZero, getFormat12, format12to24, format24to12, isMousePressed } from './utils'; 4 | 5 | export default class TimePicker extends Component { 6 | static propTypes = { 7 | time : PropTypes.string, 8 | theme : PropTypes.string, 9 | color1 : PropTypes.string, 10 | headerColor : PropTypes.string, 11 | placeholder : PropTypes.string.isRequired, 12 | className : PropTypes.string, 13 | onSet : PropTypes.func.isRequired, 14 | style : PropTypes.object 15 | }; 16 | 17 | /* static defaultProps = { 18 | keyName : 'react-theme-style' 19 | };*/ 20 | 21 | constructor(props) { 22 | super(props); 23 | let format12 = ''; 24 | if (this.props.time) { 25 | let time = this.props.time.split(':'); 26 | format12 = format24to12(Number(time[0]), Number(time[1])); 27 | } 28 | this.state = { 29 | toShow : false, 30 | time : { 31 | format12 : format12, 32 | format24 : '' 33 | } 34 | }; 35 | } 36 | 37 | componentWillMount() { 38 | this.addStyles(); 39 | } 40 | 41 | 42 | componentWillReceiveProps(nextProps) { 43 | let format12 = ''; 44 | let format24 = ''; 45 | 46 | if (nextProps.time) { 47 | let temp = nextProps.time.split(':'); 48 | format12 = format24to12(Number(temp[0]), Number(temp[1])); 49 | format24 = nextProps.time; 50 | } 51 | 52 | this.setState({ 53 | time : { 54 | format12, 55 | format24 56 | } 57 | }); 58 | } 59 | 60 | addStyles() { 61 | const commonSelector = 'react-timepicker-common-style'; 62 | const themeSelector = this.getThemeSelector(this.props.theme, this.props.color1); 63 | const {theme, color1, headerColor} = this.props; 64 | let head = document.head || document.getElementsByTagName('head')[0]; 65 | 66 | if (!document.getElementById(commonSelector)) { 67 | let style = document.createElement('style'); 68 | style.type = 'text/css'; 69 | style.id = commonSelector; 70 | 71 | if (style.styleSheet) { 72 | style.styleSheet.cssText = commonStyles; 73 | } else { 74 | style.appendChild(document.createTextNode(commonStyles)); 75 | } 76 | 77 | head.appendChild(style); 78 | } 79 | 80 | if (!document.getElementById(themeSelector)) { 81 | let themeStyles = getColorStyles({ 82 | themeSelector, 83 | theme, 84 | color1, 85 | headerColor 86 | }); 87 | 88 | let style = document.createElement('style'); 89 | style.type = 'text/css'; 90 | style.id = themeSelector; 91 | 92 | if (style.styleSheet) { 93 | style.styleSheet.cssText = themeStyles; 94 | } else { 95 | style.appendChild(document.createTextNode(themeStyles)); 96 | } 97 | head.appendChild(style); 98 | } 99 | } 100 | 101 | // utils 102 | removeEventListener() { 103 | window.removeEventListener('keydown', this.handleKeyPress); 104 | } 105 | 106 | addEventListener() { 107 | window.addEventListener('keydown', this.handleKeyPress); 108 | } 109 | 110 | // handlers 111 | 112 | handleKeyPress = (e) => { 113 | switch (e.which) { 114 | case 13 : { 115 | if (e.target !== this.closeButton) { 116 | this.handleSet(); 117 | } 118 | } 119 | break; 120 | 121 | case 27 : { 122 | this.toggleToShow(false); 123 | } 124 | break; 125 | 126 | case 38 : { // up 127 | this.state.toShowHourContainer ? 128 | this.setHour(((this.state.degree + 30) % 360) || 360) 129 | : 130 | this.setMinute(((this.state.degree + 6) % 360) || 360); 131 | } 132 | break; 133 | 134 | case 40 : { // down 135 | this.state.toShowHourContainer ? 136 | this.setHour(((this.state.degree - 30) % 360) || 360) 137 | : 138 | this.setMinute(((this.state.degree - 6) % 360) || 360); 139 | } 140 | break; 141 | } 142 | }; 143 | 144 | handleMove = (event) => { 145 | event.preventDefault(); 146 | if (isMousePressed(event)) return; 147 | this.changeClock(event.nativeEvent.clientX, event.nativeEvent.clientY); 148 | }; 149 | 150 | 151 | handleTouchMove = (event) => { 152 | event.preventDefault(); 153 | this.changeClock(event.changedTouches[0].clientX, event.changedTouches[0].clientY); 154 | }; 155 | 156 | handleTouchUp = (event) => { 157 | if (event.target === this.mask) return; 158 | this.changeClock(event.changedTouches[0].clientX, event.changedTouches[0].clientY); 159 | }; 160 | 161 | handleMoveUp = (event) => { 162 | if (event.target === this.mask) return; 163 | this.changeClock(event.nativeEvent.clientX, event.nativeEvent.clientY); 164 | }; 165 | 166 | changeClock = (clientX, clientY) => { 167 | const x = clientX - this.containerPos.x; 168 | const y = clientY - this.containerPos.y; 169 | this.state.toShowHourContainer ? this.setHour(this.getDegree(x, y)) : this.setMinute(this.getDegree(x, y)); 170 | }; 171 | 172 | toggleHourOrMinuteContainer(toShowHourContainer) { 173 | let { degree, selectedIndexDegree } = toShowHourContainer ? this.getSelectedIndexDegreeAndDegreeForHour(Number(this.state.hour)) : this.getSelectedIndexDegreeAndDegreeForMinute(Number(this.state.minute)); 174 | this.setState({ 175 | toShowHourContainer, 176 | degree, 177 | selectedIndexDegree 178 | }); 179 | } 180 | 181 | toggleAmPm(isAmSelected) { 182 | this.setState({ 183 | isAmSelected 184 | }); 185 | } 186 | 187 | handleFocus = (e)=> { 188 | e.preventDefault(); 189 | let format24 = this.props.time; 190 | let {hour, minute, degree, selectedIndexDegree, isAmSelected} = this.getInitialConfig(format24); 191 | // hour = appendZero(Math.round(degree / 30) || '12'); //not sure why i added this 192 | this.toggleToShow(); 193 | this.setState({ 194 | degree, 195 | hour, 196 | minute, 197 | toShowHourContainer : true, 198 | selectedIndexDegree, 199 | isAmSelected 200 | }, () => { 201 | this.init(); 202 | }); 203 | }; 204 | 205 | toggleToShow(toShow = true) { 206 | this.setState({ 207 | toShow 208 | }); 209 | 210 | toShow ? this.addEventListener() : this.removeEventListener(); 211 | } 212 | 213 | handleSet = () => { 214 | let allFormat = this.getTime(Number(this.state.hour), Number(this.state.minute), this.state.isAmSelected); 215 | this.setState({ 216 | time : allFormat 217 | }); 218 | 219 | this.toggleToShow(false); 220 | this.inputField.blur(); 221 | this.props.onSet(allFormat); 222 | }; 223 | 224 | // functionality 225 | init() { 226 | this.center = { 227 | x : 130, 228 | y : 130 229 | }; 230 | 231 | const maskPosition = this.mask.getBoundingClientRect(); 232 | this.containerPos = { 233 | y : maskPosition.top, 234 | x : maskPosition.left 235 | }; 236 | this.basePoint = { 237 | x : 130, 238 | y : 0 239 | }; 240 | } 241 | 242 | getThemeSelector(theme, color1) { 243 | let _color1 = color1; 244 | if (color1 && color1.indexOf('#') === 0) { 245 | _color1 = color1.substr(1); 246 | } 247 | 248 | if (theme) { 249 | if (color1) { 250 | return theme.split(' ').join('-') + _color1; 251 | } 252 | return theme.split(' ').join('-'); 253 | } 254 | 255 | if (_color1) { 256 | return _color1; 257 | } 258 | 259 | return 'react-time-picker-theme'; 260 | } 261 | 262 | getHour(val) { 263 | return appendZero((val % 12) || '12'); 264 | } 265 | 266 | getMinute(val) { 267 | return appendZero((val % 60) || '0'); 268 | } 269 | 270 | getInitialConfig(time) { 271 | let date = new Date(); 272 | time = time ? time : date.getHours() + ':' + date.getMinutes(); 273 | const temp = time.split(':'); 274 | const hour24 = Number(temp[0]); 275 | const minute24 = Number(temp[1]); 276 | const {degree, selectedIndexDegree} = this.getSelectedIndexDegreeAndDegreeForHour(hour24); 277 | 278 | return { 279 | hour : this.getHour(hour24), 280 | minute : this.getMinute(minute24), 281 | degree, 282 | selectedIndexDegree, 283 | isAmSelected : Number(temp[0]) <= 12 284 | }; 285 | } 286 | 287 | getSelectedIndexDegreeAndDegreeForHour(val) { 288 | const degree = (val * 30) % 360; 289 | return { 290 | selectedIndexDegree : this.getSelectedIndexDegree(degree), 291 | degree 292 | }; 293 | } 294 | 295 | getSelectedIndexDegreeAndDegreeForMinute(val) { 296 | /* const degree = val * 6 || 360; // why? 297 | const i = (degree / 6) % 5; 298 | return { 299 | selectedIndexDegree : i ? -1 : this.getSelectedIndexDegree(degree), 300 | degree 301 | }; 302 | */ 303 | const degree = (val * 6) % 360; // why? 304 | // const i = (degree / 6) % 5; 305 | return { 306 | selectedIndexDegree : this.getSelectedIndexDegree(degree), 307 | degree 308 | }; 309 | } 310 | 311 | getSelectedIndexDegree(degree) { 312 | return (degree / 30) % 12; 313 | } 314 | 315 | 316 | getDegree(offsetX, offsetY) { 317 | const x = offsetX - this.center.x; 318 | const y = offsetY - this.center.y; 319 | const cx = this.basePoint.x - this.center.x; 320 | const cy = this.basePoint.y - this.center.y; 321 | const atan = Math.atan2(cx, cy) - Math.atan2(x, y); 322 | return (atan * 57.29577951308232) % 360; 323 | } 324 | 325 | getClock = (className) => { 326 | const hours = [12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; 327 | const minutes = [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55]; 328 | const hoursOrMinutes = this.state.toShowHourContainer ? hours : minutes; 329 | return hoursOrMinutes.map((i, j) => { 330 | return ( 331 | 335 | {i} 336 | 337 | ); 338 | }); 339 | }; 340 | 341 | getTime(hour, minute, isAmSelected) { 342 | const format12 = getFormat12(hour, minute, isAmSelected); 343 | const format24 = format12to24(hour, minute, isAmSelected); 344 | return { 345 | format12, 346 | format24 347 | }; 348 | } 349 | 350 | setMinute(degree) { 351 | /* let toRound = degree % 6; 352 | toRound < 3 ? degree -= toRound : degree += (6 - toRound); */ 353 | const base = Math.round(degree / 6); 354 | degree = base * 6; 355 | const minute = this.getMinute(base); 356 | const selectedIndexDegree = this.getSelectedIndexDegree(degree); 357 | const toReturn = { 358 | degree, 359 | minute, 360 | selectedIndexDegree 361 | }; 362 | this.setState(toReturn); 363 | return toReturn; 364 | } 365 | 366 | setHour(degree) { 367 | /* let toRound = degree % 30; 368 | toRound < 15 ? degree -= toRound : degree += (30 - toRound);*/ 369 | const base = Math.round(degree / 30); 370 | degree = base * 30; 371 | const hour = this.getHour(base); 372 | const selectedIndexDegree = this.getSelectedIndexDegree(degree); 373 | const toReturn = { 374 | degree, 375 | hour, 376 | selectedIndexDegree 377 | }; 378 | this.setState(toReturn); 379 | return toReturn; 380 | } 381 | 382 | 383 | getBody() { 384 | if (!this.state.toShow) return false; 385 | const themeSelector = this.getThemeSelector(this.props.theme, this.props.color1); 386 | const primaryColorColorClassName = `react-timepicker-primary-color-color-${themeSelector}`; 387 | const primaryColorBackgroundClassName = `react-timepicker-primary-color-background-${themeSelector}`; 388 | const backgroundColorClassName = `react-timepicker-background-color-${themeSelector}`; 389 | return ( 390 |
391 |
395 | 396 |
397 |
398 |
399 | 403 | {this.state.hour} 404 | 405 | : 406 | 410 | {this.state.minute} 411 | 412 |
413 |
414 | 423 | 424 | 433 | 434 |
435 |
436 | 437 |
438 |
{this.mask = ref;}} 441 | onTouchMove={this.handleTouchMove} 442 | onTouchEnd={this.handleTouchUp} 443 | onMouseMove={this.handleMove} 444 | onMouseUp={this.handleMoveUp} 445 | > 446 |
453 |   454 |
455 | {this.getClock(primaryColorBackgroundClassName)} 456 |
457 |
458 |
459 | 467 | 474 |
475 |
476 |
477 | ); 478 | } 479 | 480 | render() { 481 | return ( 482 |
483 | {this.inputField = ref;}} 491 | style={this.props.style} 492 | /> 493 | {this.getBody()} 494 |
495 | ); 496 | } 497 | } 498 | -------------------------------------------------------------------------------- /src/timepicker/style.js: -------------------------------------------------------------------------------- 1 | import gradients from './gradients.js'; 2 | import _find from 'lodash.find'; 3 | 4 | const DEFAULT_PRIMARY_COLOR = '#F26B83'; 5 | 6 | export function getColorStyles({themeSelector, theme, color1, headerColor = color1}) { 7 | let backgroundColor = ''; 8 | let primaryColor = ''; 9 | if (theme) { 10 | let selectedGradient = _find(gradients, function getTheme(o) { 11 | return o.name === theme; 12 | }); 13 | 14 | if (selectedGradient) { 15 | const colors = selectedGradient.colors; 16 | primaryColor = color1 ? color1 : colors[0]; 17 | backgroundColor = ` 18 | background: ${colors[0]}; 19 | background: -webkit-linear-gradient(to left,${colors[0]} , ${colors[1]}); 20 | background: linear-gradient(to left, ${colors[0]} ,${colors[1]});`; 21 | } else { 22 | backgroundColor = `background-color: ${DEFAULT_PRIMARY_COLOR};`; 23 | primaryColor = DEFAULT_PRIMARY_COLOR; 24 | } 25 | } else { 26 | primaryColor = color1 || DEFAULT_PRIMARY_COLOR; 27 | backgroundColor = `background-color: ${headerColor || primaryColor};`; 28 | } 29 | 30 | return ` 31 | .react-timepicker-background-color-${themeSelector} { 32 | ${backgroundColor}; 33 | } 34 | .react-timepicker-primary-color-background-${themeSelector} { 35 | background: ${primaryColor} !important; 36 | } 37 | .react-timepicker-primary-color-color-${themeSelector} { 38 | color: ${primaryColor} !important; 39 | } 40 | `; 41 | } 42 | const transformLeftRightMiddle = 'translateX(-50%) translateY(-50%);'; 43 | export const style = ` 44 | .timepicker-backdrop { 45 | position: fixed; 46 | width: 100%; 47 | height: 100%; 48 | max-width: 100%; 49 | max-height: 100%; 50 | top: 0; 51 | left: 0; 52 | padding: 0; 53 | margin: 0; 54 | z-index: 100000; 55 | background: rgba(0, 0, 0, 0.49); 56 | } 57 | 58 | .timepicker-modal { 59 | font-family: "Helvetica"; 60 | width: 50%; 61 | max-width: 400px; 62 | min-width: 300px; 63 | background: #fff; 64 | position: fixed; 65 | top: 50%; 66 | z-index: 100001; 67 | left: 50%; 68 | -webkit-transform: translateX(-50%) translateY(-50%); 69 | transform: translateX(-50%) translateY(-50%); 70 | box-shadow: rgba(0, 0, 0, 0.25) 0px 14px 45px, rgba(0, 0, 0, 0.22) 0px 10px 18px; 71 | border-radius: 6px; 72 | overflow: hidden; 73 | } 74 | 75 | .timepicker-modal .timepicker-header { 76 | height: 120px; 77 | display: -webkit-box; 78 | display: -ms-flexbox; 79 | display: flex; 80 | -webkit-box-pack: center; 81 | -ms-flex-pack: center; 82 | justify-content: center; 83 | -webkit-box-align: center; 84 | -ms-flex-align: center; 85 | align-items: center; 86 | color: #fff; 87 | -webkit-user-select: none; 88 | -moz-user-select: none; 89 | -ms-user-select: none; 90 | user-select: none; 91 | } 92 | 93 | .timepicker-modal .am-pm-input { 94 | width: 0.1px; 95 | height: 0.1px; 96 | opacity: 0; 97 | overflow: hidden; 98 | position: absolute; 99 | z-index: -1; 100 | } 101 | 102 | .text-shadow { 103 | text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.13); 104 | } 105 | 106 | .timepicker-modal .am-pm-label { 107 | color: rgba(255, 255, 255, 0.7); 108 | font-size: 14px; 109 | margin-left: 12px; 110 | cursor: pointer; 111 | text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.13); 112 | } 113 | 114 | .timepicker-modal .timepicker-time-container { 115 | font-size: 42px; 116 | color: inherit; 117 | letter-spacing: 2px; 118 | cursor: pointer; 119 | font-weight: bold; 120 | } 121 | 122 | .timepicker-modal .timepicker-time-container .is-not-selected { 123 | color: rgba(255, 255, 255, 0.7); 124 | } 125 | 126 | .timepicker-modal .am-pm-input:checked + .am-pm-label { 127 | color: inherit; 128 | display: block; 129 | } 130 | 131 | .timepicker-modal .timepicker-main { 132 | display: -webkit-box; 133 | display: -ms-flexbox; 134 | display: flex; 135 | -webkit-box-pack: center; 136 | -ms-flex-pack: center; 137 | justify-content: center; 138 | -webkit-box-align: center; 139 | -ms-flex-align: center; 140 | align-items: center; 141 | padding: 20px; 142 | box-sizing: border-box; 143 | } 144 | 145 | .timepicker-modal .hours-container { 146 | width: 260px; 147 | height: 260px; 148 | position: relative; 149 | display: -webkit-box; 150 | display: -ms-flexbox; 151 | display: flex; 152 | -webkit-box-pack: center; 153 | -ms-flex-pack: center; 154 | justify-content: center; 155 | -webkit-box-align: center; 156 | -ms-flex-align: center; 157 | align-items: center; 158 | background: #efefef; 159 | border-radius: 50%; 160 | -webkit-user-select: none; 161 | -moz-user-select: none; 162 | -ms-user-select: none; 163 | user-select: none; 164 | } 165 | 166 | .timepicker-modal .hand { 167 | -webkit-user-select: none; 168 | -moz-user-select: none; 169 | -ms-user-select: none; 170 | user-select: none; 171 | width: 45%; 172 | position: absolute; 173 | left: 50%; 174 | bottom: 50%; 175 | -webkit-transform-origin: center left 0px; 176 | transform-origin: center left 0px; 177 | pointer-events: none; 178 | -webkit-transform: rotateZ(0deg); 179 | transform: rotateZ(0deg); 180 | height: 2px; 181 | } 182 | 183 | .timepicker-modal .hand:after { 184 | position: absolute; 185 | content: ""; 186 | top: -5px; 187 | border-radius: 50%; 188 | right: 0; 189 | width: 12px; 190 | height: 12px; 191 | background: inherit; 192 | } 193 | 194 | .timepicker-modal .circle { 195 | -webkit-user-select: none; 196 | -moz-user-select: none; 197 | -ms-user-select: none; 198 | user-select: none; 199 | font-size: 16px; 200 | display: -webkit-box; 201 | display: -ms-flexbox; 202 | display: flex; 203 | -webkit-box-pack: center; 204 | -ms-flex-pack: center; 205 | justify-content: center; 206 | -webkit-box-align: center; 207 | -ms-flex-align: center; 208 | align-items: center; 209 | cursor: pointer; 210 | width: 25px; 211 | height: 25px; 212 | position: absolute; 213 | border-radius: 50%; 214 | top: 50%; 215 | left: 50%; 216 | } 217 | 218 | .timepicker-modal .circle.selected { 219 | color: #fff; 220 | } 221 | 222 | .timepicker-modal .circle:nth-of-type(1) { 223 | -webkit-transform: translate3d(0px, -110px, 0) ${transformLeftRightMiddle}; 224 | transform: translate3d(0px, -110px, 0) ${transformLeftRightMiddle}; 225 | } 226 | 227 | .timepicker-modal .circle:nth-of-type(2) { 228 | transform: translate3d(55px, -95px, 0) ${transformLeftRightMiddle}; 229 | -webkit-transform: translate3d(55px, -95px, 0) ${transformLeftRightMiddle}; 230 | } 231 | 232 | .timepicker-modal .circle:nth-of-type(3) { 233 | -webkit-transform: translate3d(95px, -55px, 0) ${transformLeftRightMiddle}; 234 | transform: translate3d(95px, -55px, 0) ${transformLeftRightMiddle}; 235 | } 236 | 237 | .timepicker-modal .circle:nth-of-type(4) { 238 | transform: translate3d(110px, 0px, 0) ${transformLeftRightMiddle}; 239 | -webkit-transform: translate3d(110px, 0px, 0) ${transformLeftRightMiddle}; 240 | } 241 | 242 | .timepicker-modal .circle:nth-of-type(5) { 243 | transform: translate3d(95px, 55px, 0) ${transformLeftRightMiddle}; 244 | -webkit-transform: translate3d(95px, 55px, 0) ${transformLeftRightMiddle}; 245 | } 246 | 247 | .timepicker-modal .circle:nth-of-type(6) { 248 | transform: translate3d(55px, 95px, 0) ${transformLeftRightMiddle}; 249 | -webkit-transform: translate3d(55px, 95px, 0) ${transformLeftRightMiddle}; 250 | } 251 | 252 | .timepicker-modal .circle:nth-of-type(7) { 253 | transform: translate3d(0px, 110px, 0) ${transformLeftRightMiddle}; 254 | -webkit-transform: translate3d(0px, 110px, 0) ${transformLeftRightMiddle}; 255 | } 256 | 257 | .timepicker-modal .circle:nth-of-type(8) { 258 | transform: translate3d(-55px, 95px, 0) ${transformLeftRightMiddle}; 259 | -webkit-transform: translate3d(-55px, 95px, 0) ${transformLeftRightMiddle}; 260 | } 261 | 262 | .timepicker-modal .circle:nth-of-type(9) { 263 | transform: translate3d(-95px, 55px, 0) ${transformLeftRightMiddle}; 264 | -webkit-transform: translate3d(-95px, 55px, 0) ${transformLeftRightMiddle}; 265 | } 266 | 267 | .timepicker-modal .circle:nth-of-type(10) { 268 | transform: translate3d(-110px, 0px, 0) ${transformLeftRightMiddle}; 269 | -webkit-transform: translate3d(-110px, 0px, 0) ${transformLeftRightMiddle}; 270 | } 271 | 272 | .timepicker-modal .circle:nth-of-type(11) { 273 | transform: translate3d(-95px, -55px, 0) ${transformLeftRightMiddle}; 274 | -webkit-transform: translate3d(-95px, -55px, 0) ${transformLeftRightMiddle}; 275 | } 276 | 277 | .timepicker-modal .circle:nth-of-type(12) { 278 | transform: translate3d(-55px, -95px, 0) ${transformLeftRightMiddle}; 279 | -webkit-transform: translate3d(-55px, -95px, 0) ${transformLeftRightMiddle}; 280 | } 281 | 282 | .timepicker-modal .mask { 283 | width: 100%; 284 | height: 100%; 285 | } 286 | 287 | .timepicker-modal footer { 288 | text-align: right; 289 | padding: 20px; 290 | } 291 | 292 | .timepicker-modal .timepicker-button { 293 | border: 0; 294 | background: white; 295 | text-transform: uppercase; 296 | cursor: pointer; 297 | font-size: 14px; 298 | margin-left: 12px; 299 | font-weight: bold; 300 | color: #ffffff; 301 | padding: 10px 21px; 302 | border-radius: 21px; 303 | } 304 | 305 | .timepicker-modal .timepicker-button.close { 306 | background: white; 307 | }`; 308 | export default style; 309 | -------------------------------------------------------------------------------- /src/timepicker/utils.js: -------------------------------------------------------------------------------- 1 | export function appendZero(val) { 2 | return val < 10 ? '0' + val : String(val); 3 | } 4 | 5 | export function isMousePressed(event) { 6 | if (typeof event.buttons === 'undefined') { 7 | return event.nativeEvent.which !== 1; 8 | } 9 | 10 | return event.buttons !== 1; 11 | } 12 | 13 | export function format12to24(hour, minute, isAmSelected) { 14 | if (isAmSelected && hour === 12) { 15 | hour -= 12; 16 | return appendZero(hour) + ':' + appendZero(minute); 17 | } 18 | 19 | if (!isAmSelected && hour !== 12) { 20 | hour += 12; 21 | return appendZero(hour) + ':' + appendZero(minute); 22 | } 23 | 24 | return appendZero(hour) + ':' + appendZero(minute); 25 | } 26 | 27 | export function format24to12(hour, minute) { 28 | if (hour === 0) { 29 | hour += 12; 30 | return appendZero(hour) + ':' + appendZero(minute) + ' AM'; 31 | } 32 | 33 | if (hour === 12) { 34 | return appendZero(hour) + ':' + appendZero(minute) + ' PM'; 35 | } 36 | 37 | if (hour > 0 && hour < 12) { 38 | return appendZero(hour) + ':' + appendZero(minute) + ' AM'; 39 | } 40 | 41 | hour -= 12; 42 | return appendZero(hour) + ':' + appendZero(minute) + ' PM'; 43 | } 44 | 45 | export function getFormat12(hour, minute, isAmSelected) { 46 | return appendZero(hour) + ':' + appendZero(minute) + ' ' + (isAmSelected ? 'AM' : 'PM'); 47 | } 48 | -------------------------------------------------------------------------------- /webpack-server.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var WebpackDevServer = require('webpack-dev-server'); 3 | var config = require('./webpack.config.development'); 4 | 5 | new WebpackDevServer(webpack(config), { 6 | publicPath: config.output.publicPath, 7 | historyApiFallback: true 8 | }).listen(8787, '0.0.0.0', function (err, result) { 9 | if (err) { 10 | return console.log(err); 11 | } 12 | 13 | console.log('Listening at http://localhost:8787/'); 14 | }); 15 | -------------------------------------------------------------------------------- /webpack.config.development.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | devtool: 'inline-source-map', 3 | debug: true, 4 | entry: [ 5 | './src/example/index.js', 6 | 'webpack-dev-server/client?http://localhost:8787' 7 | ], 8 | output: { 9 | path: __dirname + '/src/', 10 | filename: 'bundle.js', 11 | publicPath: 'http://localhost:8787/' 12 | }, 13 | module: { 14 | loaders: [{ 15 | exclude: /node_modules/, 16 | loader: 'babel' 17 | }] 18 | }, 19 | resolve: { 20 | extensions: ['', '.js', '.jsx'] 21 | }, 22 | devServer: { 23 | contentBase: './src', 24 | historyApiFallback: { 25 | index: '/example/index.html' 26 | } 27 | } 28 | }; 29 | --------------------------------------------------------------------------------