├── .babelrc ├── .eslintrc.json ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── dist ├── grudus-timepicker.es5.js ├── grudus-timepicker.es5.js.map ├── grudus-timepicker.js ├── grudus-timepicker.js.map ├── grudus-timepicker.umd.js ├── grudus-timepicker.umd.js.map └── index.css ├── package.json ├── rollup ├── es5.js ├── es6.js └── umd.js └── src ├── js ├── clock.js ├── clockHeader.js ├── colorStylists.js ├── face │ ├── clockFace.js │ ├── clockFaceCreator.js │ ├── hoursFace.js │ └── minutesFace.js ├── index.js ├── meta │ ├── clockHtml.js │ ├── config.js │ └── utils.js ├── timeExtractor.js ├── timeFormatter.js └── timepickerCreator.js └── styles └── index.css /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "es2015", 5 | { 6 | "modules": false 7 | } 8 | ] 9 | ], 10 | "ignore": "node_modules/**", 11 | "plugins": [ 12 | "external-helpers" 13 | ] 14 | } -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true, 5 | "es6": true 6 | }, 7 | "extends": "eslint:recommended", 8 | "parserOptions": { 9 | "sourceType": "module" 10 | }, 11 | "rules": { 12 | "indent": [ 13 | "error", 14 | 4 15 | ], 16 | "quotes": [ 17 | "error", 18 | "double" 19 | ], 20 | "semi": [ 21 | "error", 22 | "always" 23 | ], 24 | "dot-location": [ 25 | "error", 26 | "property" 27 | ], 28 | "eqeqeq": [ 29 | "error", 30 | "always" 31 | ], 32 | "camelcase": "error", 33 | "max-len": [ 34 | "error", 35 | 120 36 | ], 37 | "max-lines": [ 38 | "error", 39 | 150 40 | ], 41 | "max-depth": [ 42 | "error", 43 | 2 44 | ], 45 | "max-params": [ 46 | "error", 47 | 4 48 | ], 49 | "no-var": "error", 50 | "one-var-declaration-per-line": "error", 51 | "prefer-const": "warn" 52 | } 53 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | node_modules/ -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | node_modules/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2017 grudus 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Grudus Timepicker 2 | 3 | ![npm](https://img.shields.io/npm/v/grudus-timepicker.svg?style=flat-square) 4 | ![downloads](https://img.shields.io/npm/dt/grudus-timepicker.svg?style=flat-square) 5 | ![dependencies](https://img.shields.io/david/grudus/timepicker.svg?style=flat-square) 6 | ![dev dependencies](https://img.shields.io/david/dev/grudus/timepicker.svg?style=flat-square) 7 | ![license](https://img.shields.io/github/license/grudus/timepicker.svg?style=flat-square) 8 | 9 | Material design timepicker written in Javascript (without any external dependencies - **no jQuery, no Bootstrap, only one file!**) 10 | See https://grudus.github.io/Timepicker/ for more usage! 11 | 12 | ## How it looks? 13 | 14 | By default picker uses blue-white theme: 15 | 16 | ![Normal](https://user-images.githubusercontent.com/18220458/29241865-a3f13518-7f82-11e7-99b2-c0fafe807b40.png) 17 | 18 |
19 | But you can change its colors by overriding some of default configuration: 20 | 21 | ````javascript 22 | defaultConfig = { 23 | headerBackground: "#1976D2", 24 | headerColor: "#c7d6e1", 25 | headerSelected: "#ffffff", 26 | wrapperBackground: "#f0fff0", 27 | footerBackground: "#f0fff0", 28 | submitColor: "#1976D2", 29 | cancelColor: "#1976D2", 30 | clockBackground: "#CFD8DC", 31 | clockItemColor: "#212121", 32 | clockItemInnerColor: "#212121", 33 | handColor: "#1976D2" 34 | }; 35 | ```` 36 | 37 |
38 | How custom theme can look: 39 |
40 | 41 | ![brave](https://user-images.githubusercontent.com/18220458/29241863-a3ee3f3e-7f82-11e7-8b10-14a874813de2.png) 42 | 43 |
44 | But you can also create more user-friendly view - create your own dark theme: 45 |
46 | 47 | ![dark](https://user-images.githubusercontent.com/18220458/29241864-a3f0d6d6-7f82-11e7-9349-27fed0fd0480.png) 48 | 49 |
50 | 51 | ## How to get it? 52 | 53 | You can include all in single `html` file! Just add 54 | 55 | ````html 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | ```` 64 | 65 | Or, you can download it from npm 66 | 67 | ````bash 68 | npm install --save grudus-timepicker 69 | ```` 70 | 71 | Then, include `index.css` into your project (e.g. add 72 | `` into your html file) 73 | 74 | And lastly, somewhere in your code put 75 | 76 | ````javascript 77 | import Timepicker from "path/to/grudus-timepicker/dist/grudus-timepicker.js"; 78 | 79 | // ... 80 | 81 | Timepicker.showPicker({ 82 | time: new Date(), 83 | onSubmit: (time) => { 84 | //some action ... 85 | }, 86 | headerColor: "#ff0000" 87 | // more color configuration ... 88 | }) 89 | ```` 90 | 91 | You can set initial time by passing `time` field in argument. `time` may be a `Date` object, an object `{hours: 12, minutes: 44}` or a string in format `HH:mm`. If you want to learn more, visit [customization section](https://grudus.github.io/Timepicker/#customization) 92 | -------------------------------------------------------------------------------- /dist/grudus-timepicker.es5.js: -------------------------------------------------------------------------------- 1 | /*! grudus-timepicker | (c) 2017-2017 2 | grudus | Apache-2.0 license (see LICENSE) */ 3 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.Timepicker=t()}(this,function(){"use strict";function e(e){if(e){if(e instanceof Date)return i(e);if(r.test(e.hours)&&u.test(e.minutes))return{hours:parseInt(e.hours),minutes:parseInt(e.minutes)};if(a.test(e))return t(e);throw new TypeError("INVALID FORMAT: {"+JSON.stringify(e)+"}.\n Time must be a Date or 'hh:MM' string or object with 'hours' and 'minutes' fields")}return i(new Date)}function t(e){var t=a.exec(e);return{hours:parseInt(t[1]),minutes:parseInt(t[2])}}function i(e){return{hours:e.getHours(),minutes:e.getMinutes()}}function n(e){return new Promise(function(t){setTimeout(t,e)})}function o(e){document.getElementById(f.headerId).style.background=e.headerBackground,document.getElementById(f.headerId).style.color=e.headerColor,document.getElementById(f.wrapperId).style.background=e.wrapperBackground,document.getElementById(f.clockId).style.background=e.clockBackground,document.getElementById(f.handId).style.background=e.handColor,document.getElementById(f.dotId).style.background=e.handColor,document.getElementById(f.buttonsId).style.background=e.footerBackground,document.getElementById(f.submitId).style.color=e.submitColor,document.getElementById(f.cancelId).style.color=e.cancelColor,s(m.clockItem,e.clockItemColor),s(m.inner,e.clockItemInnerColor),s(m.outer,e.handColor,"borderColor")}function s(e,t){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"color",n=Array.from(document.getElementsByClassName(e)),o=!0,s=!1,c=void 0;try{for(var l,r=n[Symbol.iterator]();!(o=(l=r.next()).done);o=!0)l.value.style[i]=t}catch(e){s=!0,c=e}finally{try{!o&&r.return&&r.return()}finally{if(s)throw c}}}function c(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};l();var i=Object.assign({},k.clockConfig,t),n=e(i.time),s=new b(i,n);o(i),s.onStart()}function l(){if(document.getElementById(k.clockId))throw Error("There is already one running grudus-timepicker instance!");var e=document.createElement("div");e.id=k.clockId,e.innerHTML=h,document.body.appendChild(e)}var r=/^([0-1]?[0-9]|2[0-3])$/,u=/^([0-5]?[0-9])$/,a=/^([0-1]?[0-9]|2[0-3]):([0-5][0-9])$/,d=function(t){var i=e(t);return(i.hours<10?"0"+i.hours:i.hours)+":"+(i.minutes<10?"0"+i.minutes:i.minutes)},h="
\n
\n
\n 21\n :\n 37\n
\n
\n\n\n
\n
\n
\n
\n
\n
\n\n\n \n\n
",m={clock:"g-clock",clockItem:"g-clock-item",inner:"g-clock-inner",outer:"g-clock-outer",item:"g-clock-item",hand:"g-hand-of-a-clock",fadeOut:"g-fade-out",selected:"g-selected",active:"g-active",submit:"g-submit",cancel:"g-cancel",hour:"g-hour",minute:"g-minute"},f={headerId:"g-head",hoursId:"g-hours",minutesId:"g-minutes",clockId:"g-clock",innerId:"g-clock-inner",wrapperId:"g-clock-wrapper",dotId:"g-middle-dot",handId:"g-hand-of-a-clock",buttonsId:"g-buttons",submitId:"g-time-submit",cancelId:"g-time-cancel"},k={clockId:"grudus-clock",clockConfig:{onSubmit:function(){},onCancel:function(){},headerBackground:"#1976D2",headerColor:"#c7d6e1",headerSelected:"#ffffff",wrapperBackground:"#f0fff0",footerBackground:"#f0fff0",submitColor:"#1976D2",cancelColor:"#1976D2",clockBackground:"#CFD8DC",clockItemColor:"#212121",clockItemInnerColor:"#212121",handColor:"#1976D2"},FaceType:{HOURS:"hours",MINUTES:"minutes"}},g=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},p=function(){function e(e,t){for(var i=0;i-1}}]),e}(),I=function(){function e(t,i,n){g(this,e),this.displayed=["12","1","2","3","4","5","6","7","8","9","10","11"],this.displayedInner=["00","13","14","15","16","17","18","19","20","21","22","23"],this.type=k.FaceType.MINUTES,this.selected=void 0,this.options=t.options,this.items=t,this.hours=i,this.updateHours=n}return p(e,[{key:"onEnter",value:function(){this.items.innerClockElem.style.display="block";var e=this.hours<13&&0!==this.hours,t=this.hours%12;this.selected=e?this.items.clockItems[t]:this.items.innerClockItems[t],this.colorSelected(),this.updateHours(this.hours,30*t,e?this.items.radius:this.items.radius-50)}},{key:"onLeave",value:function(){this.items.innerClockElem.style.display="none",this.selected&&(this.removeSelected(),this.selected=void 0)}},{key:"selectTime",value:function(e,t){this.selected&&this.removeSelected();var i=Math.round(e/30)%12;this.selected=(t===this.items.innerClockElem?this.items.innerClockItems:this.items.clockItems)[i],this.colorSelected(),this.hours=parseInt(this.selected.innerText);var n=30*Math.round(e/30);this.updateHours(this.hours,n,t===this.items.innerClockElem?this.items.radius-50:this.items.radius)}},{key:"colorSelected",value:function(){this.selected.style.background=this.options.handColor,this.selected.style.color="#ffffff"}},{key:"removeSelected",value:function(){this.selected.style.background="transparent",this.selected.style.color=this.isInner()?this.options.clockItemInnerColor:this.options.clockItemColor}},{key:"isInner",value:function(){return Array.from(this.items.innerClockItems).indexOf(this.selected)>-1}}]),e}();Promise.delay=function(e,t){return t||(t=e,e=function(){}),n(t).then(e)},Promise.prototype.delay=function(e,t){return this.then(function(){return Promise.delay(e,t)})};var C={toRadians:function(e){return e*(Math.PI/180)},toDegrees:function(e){return e*(180/Math.PI)},findMousePosition:function(e,t){var i=t.getBoundingClientRect();return{x:e.clientX-i.left,y:e.clientY-i.top}}},E=function(){function e(t,i){g(this,e),this.clockElem=t,this.innerClockElem=i,this.size={},this.middle={}}return p(e,[{key:"create",value:function(t,i,n,o){e.doCreate(t,this.clockElem,function(e){return e.classList.add(m.item)}),e.doCreate(i,this.innerClockElem,function(e,t){e.classList.add(m.item,m.inner),e.innerText=o.displayedInner[t]});for(var s=0;s<60;s++){var c=document.createElement("span");c.classList.add(m.outer),n.push(c),this.clockElem.appendChild(c)}}},{key:"calculateSize",value:function(t,i,n){this.size.width=this.clockElem.offsetWidth,this.size.height=this.clockElem.offsetHeight,this.middle.x=this.size.width/2,this.middle.y=this.size.height/2,this.itemsRadius=this.size.width/2-20;var o=this.innerClockElem.offsetWidth/2,s=this.innerClockElem.offsetHeight/2;e.doCalculateSize(this.middle.x,this.middle.y,this.itemsRadius,t),e.doCalculateSize(o,s,this.itemsRadius-40,i),e.doCalculateSize(this.middle.x,this.middle.y,this.itemsRadius,n)}}],[{key:"doCreate",value:function(e,t,i){for(var n=0;n<12;n++){var o=document.createElement("span");i(o,n),e.push(o),t.appendChild(o)}}},{key:"doCalculateSize",value:function(e,t,i,n){for(var o=360/n.length,s=0;s1&&void 0!==arguments[1]?arguments[1]:this.itemsRadius;this.handOfAClock.style.transform="rotate("+(e-90)+"deg)",this.handOfAClock.style.width=t+"px"}},{key:"toggleToHours",value:function(){this.minutesFace.onLeave(),this.toggleTime(this.hoursFace)}},{key:"toggleToMinutes",value:function(){this.hoursFace.onLeave(),this.toggleTime(this.minutesFace)}},{key:"toggleTime",value:function(e){var t=this;this.currentFace!==e&&(this.onEachClockElement(function(e){return e.classList.add(m.fadeOut)}),this.handOfAClock.classList.add(m.fadeOut),Promise.delay(function(){t.onEachClockElement(function(e){return e.classList.remove(m.fadeOut)}),t.handOfAClock.classList.remove(m.fadeOut),t.changeDisplayed(e.displayed),t.currentFace=e,t.onEachClockElement(function(e){return t.removeSelected(e)}),e.onEnter()},300))}},{key:"removeSelected",value:function(e){e.classList.remove(m.selected),e.style.background="transparent",e.style.color=this.options.clockItemColor}}]),e}(),b=function(){function e(t,i){g(this,e),this.options=t,this.initView(),this.time=i,this.initElements()}return p(e,[{key:"initView",value:function(){var t=this;this.submitButton=document.getElementById(f.submitId),this.submitButton.onclick=function(){var i=t.time;i.formatted=function(){return d(i)},t.options.onSubmit(i),e.dispose()},this.cancelButton=document.getElementById(f.cancelId),this.cancelButton.onclick=function(){t.options.onCancel(),e.dispose()}}},{key:"initElements",value:function(){var e=this;this.header=new y({options:this.options,time:this.time,onHourClicked:function(){return e.toggleToHours()},onMinutesClicked:function(){return e.toggleToMinutes()}}),this.clockFace=new T(this.options,this.time,function(t,i){return e.onTimeUpdate(t,i)})}},{key:"onStart",value:function(){this.clockFace.onStart()}},{key:"toggleToHours",value:function(){this.clockFace.toggleToHours()}},{key:"toggleToMinutes",value:function(){this.clockFace.toggleToMinutes()}},{key:"onTimeUpdate",value:function(e,t){this.time=e,this.header.time=e,this.header.updateDisplayedTime(),t===k.FaceType.MINUTES&&this.header.toggleActiveToMinutes()}}],[{key:"dispose",value:function(){document.body.removeChild(document.getElementById(k.clockId))}}]),e}();return{showPicker:function(e){return c(e)},format:function(e){return d(e)}}}); 4 | //# sourceMappingURL=grudus-timepicker.es5.js.map 5 | -------------------------------------------------------------------------------- /dist/grudus-timepicker.es5.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"grudus-timepicker.es5.js","sources":["../src/js/timeExtractor.js","../src/js/meta/utils.js","../src/js/colorStylists.js","../src/js/timepickerCreator.js","../src/js/timeFormatter.js","../src/js/meta/clockHtml.js","../src/js/meta/config.js","../src/js/clockHeader.js","../src/js/face/minutesFace.js","../src/js/face/hoursFace.js","../src/js/face/clockFaceCreator.js","../src/js/face/clockFace.js","../src/js/clock.js","../src/js/index.js"],"sourcesContent":["const hoursRegex = /^([0-1]?[0-9]|2[0-3])$/;\r\nconst minutesRegex = /^([0-5]?[0-9])$/;\r\nconst regex = /^([0-1]?[0-9]|2[0-3]):([0-5][0-9])$/;\r\n\r\nexport default function extractTime(date) {\r\n if (!date)\r\n return fromDate(new Date());\r\n else if (date instanceof Date)\r\n return fromDate(date);\r\n else if (hoursRegex.test(date.hours) && minutesRegex.test(date.minutes))\r\n return {hours: parseInt(date.hours), minutes: parseInt(date.minutes)};\r\n else if (regex.test(date))\r\n return fromRegex(date);\r\n else\r\n throw new TypeError(`INVALID FORMAT: {${JSON.stringify(date)}}.\r\n Time must be a Date or 'hh:MM' string or object with 'hours' and 'minutes' fields`);\r\n}\r\n\r\nfunction fromRegex(date) {\r\n const parsed = regex.exec(date);\r\n return {hours: parseInt(parsed[1]), minutes: parseInt(parsed[2])};\r\n}\r\n\r\nfunction fromDate(date) {\r\n return {hours: date.getHours(), minutes: date.getMinutes()};\r\n}","function toRadians(angle) {\r\n return angle * (Math.PI / 180);\r\n}\r\n\r\nfunction toDegrees(angle) {\r\n return angle * (180 / Math.PI);\r\n}\r\n\r\nfunction findMousePosition(event, object) {\r\n const rect = object.getBoundingClientRect();\r\n return {\r\n x: event.clientX - rect.left,\r\n y: event.clientY - rect.top\r\n };\r\n}\r\n\r\nfunction delay(t) {\r\n return new Promise(function (resolve) {\r\n setTimeout(resolve, t);\r\n });\r\n}\r\n\r\nPromise.delay = function (fn, t) {\r\n if (!t) {\r\n t = fn;\r\n fn = function () {\r\n };\r\n }\r\n return delay(t).then(fn);\r\n};\r\n\r\nPromise.prototype.delay = function (fn, t) {\r\n return this.then(function () {\r\n return Promise.delay(fn, t);\r\n });\r\n};\r\n\r\nexport default {toRadians, toDegrees, findMousePosition};","import {css, DOM} from \"./meta/config\";\r\n\r\nexport default function styleColors(options) {\r\n document.getElementById(DOM.headerId).style.background = options.headerBackground;\r\n document.getElementById(DOM.headerId).style.color = options.headerColor;\r\n document.getElementById(DOM.wrapperId).style.background = options.wrapperBackground;\r\n document.getElementById(DOM.clockId).style.background = options.clockBackground;\r\n document.getElementById(DOM.handId).style.background = options.handColor;\r\n document.getElementById(DOM.dotId).style.background = options.handColor;\r\n document.getElementById(DOM.buttonsId).style.background = options.footerBackground;\r\n document.getElementById(DOM.submitId).style.color = options.submitColor;\r\n document.getElementById(DOM.cancelId).style.color = options.cancelColor;\r\n\r\n changeColor(css.clockItem, options.clockItemColor);\r\n changeColor(css.inner, options.clockItemInnerColor);\r\n changeColor(css.outer, options.handColor, \"borderColor\");\r\n}\r\n\r\nfunction changeColor(className, color, property = \"color\") {\r\n const items = Array.from(document.getElementsByClassName(className));\r\n for (const item of items)\r\n item.style[property] = color;\r\n}\r\n","import clockHtml from \"./meta/clockHtml\";\r\nimport Config from \"./meta/config\";\r\nimport Clock from \"./clock\";\r\nimport styleColors from \"./colorStylists\";\r\nimport getTime from \"./timeExtractor\";\r\n\r\n\r\nexport default function showPicker(config = {}) {\r\n createDom();\r\n\r\n const options = Object.assign({}, Config.clockConfig, config);\r\n const time = getTime(options.time);\r\n\r\n const clock = new Clock(options, time);\r\n styleColors(options);\r\n clock.onStart();\r\n}\r\n\r\n\r\nfunction createDom() {\r\n if (document.getElementById(Config.clockId))\r\n throw Error(\"There is already one running grudus-timepicker instance!\");\r\n\r\n const clockDiv = document.createElement(\"div\");\r\n clockDiv.id = Config.clockId;\r\n clockDiv.innerHTML = clockHtml;\r\n document.body.appendChild(clockDiv);\r\n}\r\n","import extractTime from \"./timeExtractor\";\r\n\r\nexport default function (time) {\r\n const extractedTime = extractTime(time);\r\n return (extractedTime.hours < 10 ? \"0\" + extractedTime.hours : extractedTime.hours)\r\n + \":\" + (extractedTime.minutes < 10 ? \"0\" + extractedTime.minutes : extractedTime.minutes);\r\n}","export default \"
\\n\" +\r\n\"
\\n\" +\r\n\"
\\n\" +\r\n\" 21\\n\" +\r\n\" :\\n\" +\r\n\" 37\\n\" +\r\n\"
\\n\" +\r\n\"
\\n\" +\r\n\"\\n\" +\r\n\"\\n\" +\r\n\"
\\n\" +\r\n\"
\" +\r\n\" \\n\" +\r\n\"
\\n\" +\r\n\"
\\n\" +\r\n\"
\\n\" +\r\n\"
\\n\" +\r\n\"\\n\" +\r\n\"\\n\" +\r\n\"
\\n\" +\r\n\" \\n\" +\r\n\" \\n\" +\r\n\"
\\n\" +\r\n\"\\n\" +\r\n\"
\";","const clockId = \"grudus-clock\";\r\n\r\nconst defaultConfig = {\r\n onSubmit: () => {\r\n },\r\n onCancel: () => {\r\n },\r\n headerBackground: \"#1976D2\",\r\n headerColor: \"#c7d6e1\",\r\n headerSelected: \"#ffffff\",\r\n wrapperBackground: \"#f0fff0\",\r\n footerBackground: \"#f0fff0\",\r\n submitColor: \"#1976D2\",\r\n cancelColor: \"#1976D2\",\r\n clockBackground: \"#CFD8DC\",\r\n clockItemColor: \"#212121\",\r\n clockItemInnerColor: \"#212121\",\r\n handColor: \"#1976D2\"\r\n};\r\n\r\nconst FaceType = {HOURS: \"hours\", MINUTES: \"minutes\"};\r\n\r\nconst css = {\r\n clock: \"g-clock\",\r\n clockItem: \"g-clock-item\",\r\n inner: \"g-clock-inner\",\r\n outer: \"g-clock-outer\",\r\n item: \"g-clock-item\",\r\n hand: \"g-hand-of-a-clock\",\r\n fadeOut: \"g-fade-out\",\r\n selected: \"g-selected\",\r\n active: \"g-active\",\r\n submit: \"g-submit\",\r\n cancel: \"g-cancel\",\r\n hour: \"g-hour\",\r\n minute: \"g-minute\"\r\n};\r\n\r\nconst DOM = {\r\n headerId: \"g-head\",\r\n hoursId: \"g-hours\",\r\n minutesId: \"g-minutes\",\r\n clockId: \"g-clock\",\r\n innerId: \"g-clock-inner\",\r\n wrapperId: \"g-clock-wrapper\",\r\n dotId: \"g-middle-dot\",\r\n handId: \"g-hand-of-a-clock\",\r\n buttonsId: \"g-buttons\",\r\n submitId: \"g-time-submit\",\r\n cancelId: \"g-time-cancel\"\r\n};\r\n\r\nexport default {clockId, clockConfig: defaultConfig, FaceType};\r\nexport {css, DOM};","import {DOM} from \"./meta/config\";\r\n\r\nexport default class ClockHeader {\r\n\r\n constructor(config) {\r\n this.options = config.options;\r\n this.time = config.time;\r\n this.onHourClicked = config.onHourClicked;\r\n this.onMinutesClicked = config.onMinutesClicked;\r\n\r\n this.initView();\r\n }\r\n\r\n initView() {\r\n this.headerHours = document.getElementById(DOM.hoursId);\r\n this.headerHours.onclick = () => {\r\n this.toggleActiveToHours();\r\n this.onHourClicked();\r\n };\r\n\r\n this.headerMinutes = document.getElementById(DOM.minutesId);\r\n this.headerMinutes.onclick = () => {\r\n this.toggleActiveToMinutes();\r\n this.onMinutesClicked();\r\n };\r\n\r\n this.updateDisplayedTime();\r\n this.toggleActiveToHours();\r\n }\r\n\r\n toggleActiveToMinutes() {\r\n this.toggleActive(this.headerHours, this.headerMinutes);\r\n }\r\n\r\n toggleActiveToHours() {\r\n this.toggleActive(this.headerMinutes, this.headerHours);\r\n }\r\n\r\n toggleActive(objectToRemoveClass, objectToAddClass) {\r\n objectToRemoveClass.style.color = this.options.headerColor;\r\n objectToAddClass.style.color = this.options.headerSelected;\r\n }\r\n\r\n updateDisplayedTime() {\r\n ClockHeader.doUpdateDisplayedTime(this.headerHours, this.time.hours);\r\n ClockHeader.doUpdateDisplayedTime(this.headerMinutes, this.time.minutes);\r\n }\r\n\r\n static doUpdateDisplayedTime(node, value) {\r\n if (value < 10)\r\n node.innerText = \"0\" + value;\r\n else node.innerText = value;\r\n }\r\n}","import Config, {css} from \"../meta/config\";\r\n\r\nexport default class MinutesFace {\r\n\r\n constructor(items, initialMinutes, updateMinutes) {\r\n this.displayed = [\"00\", \"05\", \"10\", \"15\", \"20\", \"25\", \"30\", \"35\", \"40\", \"45\", \"50\", \"55\"];\r\n this.options = items.options;\r\n this.type = Config.FaceType.MINUTES;\r\n this.selected = undefined;\r\n this.items = items;\r\n this.minutes = initialMinutes;\r\n this.updateMinutes = updateMinutes;\r\n }\r\n\r\n onEnter() {\r\n this.selected = this.findSelected(this.minutes);\r\n this.colorSelected();\r\n this.updateMinutes(this.minutes, this.minutes * 6);\r\n }\r\n\r\n onLeave() {\r\n if (this.selected) {\r\n this.removeSelected();\r\n this.selected = undefined;\r\n }\r\n }\r\n\r\n selectTime(angle) {\r\n if (this.selected)\r\n this.removeSelected();\r\n\r\n const minute = Math.round(angle / 6) % 60;\r\n this.selected = this.findSelected(minute);\r\n this.colorSelected();\r\n this.minutes = minute;\r\n this.updateMinutes(this.minutes, angle);\r\n }\r\n\r\n findSelected(minute) {\r\n return (minute % 5 === 0) ? this.items.clockItems[minute / 5] : this.items.outerClockItems[minute];\r\n }\r\n\r\n colorSelected() {\r\n if (this.isOuter()) {\r\n this.selected.classList.add(css.selected);\r\n return;\r\n }\r\n this.selected.style.background = this.options.handColor;\r\n this.selected.style.color = \"whitesmoke\";\r\n }\r\n\r\n removeSelected() {\r\n if (this.isOuter()) {\r\n this.selected.classList.remove(css.selected);\r\n return;\r\n }\r\n this.selected.style.background = \"transparent\";\r\n this.selected.style.color = this.options.clockItemColor;\r\n }\r\n\r\n isOuter() {\r\n return this.items.outerClockItems.indexOf(this.selected) > -1;\r\n }\r\n}","import Config from \"../meta/config\";\r\n\r\nexport default class HoursFace {\r\n\r\n constructor(items, initialHours, updateHours) {\r\n this.displayed = [\"12\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \"10\", \"11\"];\r\n this.displayedInner = [\"00\", \"13\", \"14\", \"15\", \"16\", \"17\", \"18\", \"19\", \"20\", \"21\", \"22\", \"23\"];\r\n this.type = Config.FaceType.MINUTES;\r\n this.selected = undefined;\r\n this.options = items.options;\r\n\r\n this.items = items;\r\n this.hours = initialHours;\r\n this.updateHours = updateHours;\r\n }\r\n\r\n onEnter() {\r\n this.items.innerClockElem.style.display = \"block\";\r\n const isInnerClock = this.hours < 13 && this.hours !== 0;\r\n const hoursIndex = this.hours % 12;\r\n this.selected = isInnerClock ? this.items.clockItems[hoursIndex] : this.items.innerClockItems[hoursIndex];\r\n this.colorSelected();\r\n\r\n this.updateHours(this.hours, hoursIndex * 30, isInnerClock ? this.items.radius : this.items.radius - 50);\r\n }\r\n\r\n onLeave() {\r\n this.items.innerClockElem.style.display = \"none\";\r\n if (this.selected) {\r\n this.removeSelected();\r\n this.selected = undefined;\r\n }\r\n }\r\n\r\n selectTime(angle, elem) {\r\n if (this.selected)\r\n this.removeSelected();\r\n\r\n const index = Math.round(angle / 30) % 12;\r\n this.selected = (elem === this.items.innerClockElem\r\n ? this.items.innerClockItems\r\n : this.items.clockItems)[index];\r\n\r\n this.colorSelected();\r\n this.hours = parseInt(this.selected.innerText);\r\n const selectedAngle = Math.round(angle / 30) * 30;\r\n\r\n this.updateHours(this.hours, selectedAngle,\r\n elem === this.items.innerClockElem ? this.items.radius - 50 : this.items.radius);\r\n }\r\n\r\n colorSelected() {\r\n this.selected.style.background = this.options.handColor;\r\n this.selected.style.color = \"#ffffff\";\r\n }\r\n\r\n removeSelected() {\r\n this.selected.style.background = \"transparent\";\r\n this.selected.style.color = this.isInner()\r\n ? this.options.clockItemInnerColor\r\n : this.options.clockItemColor;\r\n }\r\n\r\n isInner() {\r\n return Array.from(this.items.innerClockItems).indexOf(this.selected) > -1;\r\n }\r\n}","import Utils from \"../meta/utils\";\r\nimport {css} from \"../meta/config\";\r\n\r\nexport default class ClockFaceCreator {\r\n\r\n constructor(clockElem, innerClockElem) {\r\n this.clockElem = clockElem;\r\n this.innerClockElem = innerClockElem;\r\n this.size = {};\r\n this.middle = {};\r\n }\r\n\r\n create(clockItems, innerClockItems, outerClockItems, face) {\r\n ClockFaceCreator.doCreate(clockItems, this.clockElem, span => span.classList.add(css.item));\r\n ClockFaceCreator.doCreate(innerClockItems, this.innerClockElem, (span, i) => {\r\n span.classList.add(css.item, css.inner);\r\n span.innerText = face.displayedInner[i];\r\n });\r\n\r\n for (let i = 0; i < 60; i++) {\r\n const span = document.createElement(\"span\");\r\n span.classList.add(css.outer);\r\n outerClockItems.push(span);\r\n this.clockElem.appendChild(span);\r\n }\r\n }\r\n\r\n static doCreate(clockItems, clockElem, fun) {\r\n for (let i = 0; i < 12; i++) {\r\n const span = document.createElement(\"span\");\r\n fun(span, i);\r\n clockItems.push(span);\r\n clockElem.appendChild(span);\r\n }\r\n }\r\n\r\n calculateSize(clockItems, innerClockItems, outerClockItems) {\r\n this.size.width = this.clockElem.offsetWidth;\r\n this.size.height = this.clockElem.offsetHeight;\r\n this.middle.x = this.size.width / 2;\r\n this.middle.y = this.size.height / 2;\r\n this.itemsRadius = this.size.width / 2 - 20;\r\n\r\n const innerWidth = this.innerClockElem.offsetWidth;\r\n const innerHeight = this.innerClockElem.offsetHeight;\r\n const middleX = innerWidth / 2;\r\n const middleY = innerHeight / 2;\r\n\r\n ClockFaceCreator.doCalculateSize(this.middle.x, this.middle.y, this.itemsRadius, clockItems);\r\n ClockFaceCreator.doCalculateSize(middleX, middleY, this.itemsRadius - 40, innerClockItems);\r\n ClockFaceCreator.doCalculateSize(this.middle.x, this.middle.y, this.itemsRadius, outerClockItems);\r\n }\r\n\r\n static doCalculateSize(middleX, middleY, radius, items) {\r\n const angleQuantum = 360 / items.length;\r\n for (let i = 0; i < items.length; i++) {\r\n\r\n const angle = Utils.toRadians(i * angleQuantum);\r\n const item = items[i];\r\n const itemWidth = item.offsetWidth;\r\n const itemHeight = item.offsetHeight;\r\n\r\n item.style.left = ((middleX + Math.sin(angle) * radius) - itemWidth / 2) + \"px\";\r\n item.style.bottom = ((middleY + Math.cos(angle) * radius) - itemHeight / 2) + \"px\";\r\n }\r\n }\r\n}","import MinutesFace from \"./minutesFace\";\r\nimport HoursFace from \"./hoursFace\";\r\nimport Utils from \"../meta/utils\";\r\nimport Config, {css, DOM} from \"../meta/config\";\r\nimport ClockFaceCreator from \"./clockFaceCreator\";\r\n\r\nexport default class ClockFace {\r\n\r\n constructor(options, initialTime, onTimeUpdate) {\r\n this.options = options;\r\n this.time = initialTime;\r\n this.onTimeUpdate = onTimeUpdate;\r\n this.isMouseDown = false;\r\n this.clockItems = [];\r\n this.innerClockItems = [];\r\n this.outerClockItems = [];\r\n this.size = {};\r\n this.middle = {};\r\n\r\n this.initViews();\r\n this.initTimeFaces(initialTime);\r\n this.createFace();\r\n\r\n this.hoursFace.items.radius = this.itemsRadius;\r\n\r\n this.currentFace = this.hoursFace;\r\n this.changeDisplayed(this.currentFace.displayed);\r\n }\r\n\r\n initViews() {\r\n this.clockElem = document.getElementById(DOM.clockId);\r\n this.innerClockElem = document.getElementById(DOM.innerId);\r\n this.handOfAClock = document.getElementById(DOM.handId);\r\n\r\n this.clockElem.onmousedown = () => this.isMouseDown = true;\r\n this.clockElem.onmouseup = () => {this.isMouseDown = false;\r\n this.toggleToMinutes();\r\n };\r\n\r\n this.handOfAClock.onmouseup = (e) => e.stopPropagation();\r\n this.handOfAClock.onmousemove = (e) => e.stopPropagation();\r\n this.handOfAClock.onclick = (e) => e.stopPropagation();\r\n\r\n this.clockElem.onmousemove = (e) => this.selectTime(e, false, this.clockElem);\r\n this.clockElem.onclick = (e) => this.selectTime(e, true, this.clockElem);\r\n\r\n this.innerClockElem.onmousemove = (e) => this.selectTime(e, false, this.innerClockElem);\r\n this.innerClockElem.onclick = (e) => this.selectTime(e, true, this.innerClockElem);\r\n }\r\n\r\n initTimeFaces(initialTime) {\r\n this.minutesFace = new MinutesFace({\r\n options: this.options,\r\n clockItems: this.clockItems,\r\n outerClockItems: this.outerClockItems\r\n }, initialTime.minutes, (minutes, angle) => this.updateMinutes(minutes, angle));\r\n\r\n this.hoursFace = new HoursFace({\r\n options: this.options,\r\n innerClockItems: this.innerClockItems,\r\n clockItems: this.clockItems,\r\n innerClockElem: this.innerClockElem\r\n }, initialTime.hours, (hours, angle, radius) => this.updateHours(hours, angle, radius));\r\n }\r\n\r\n onStart() {\r\n this.currentFace.onEnter();\r\n }\r\n\r\n createFace() {\r\n const clockFaceCreator = new ClockFaceCreator(this.clockElem, this.innerClockElem);\r\n clockFaceCreator.create(this.clockItems, this.innerClockItems, this.outerClockItems, this.hoursFace);\r\n clockFaceCreator.calculateSize(this.clockItems, this.innerClockItems, this.outerClockItems);\r\n\r\n this.size = clockFaceCreator.size;\r\n this.middle = clockFaceCreator.middle;\r\n this.itemsRadius = clockFaceCreator.itemsRadius;\r\n }\r\n\r\n selectTime(event, isMouseDown, elem) {\r\n if (!(isMouseDown || this.isMouseDown))\r\n return;\r\n const mouse = Utils.findMousePosition(event, this.clockElem);\r\n const x = mouse.x - this.middle.x;\r\n const y = this.middle.y - mouse.y;\r\n let angle = 90 - Utils.toDegrees(Math.atan(y / x));\r\n if (x < 0) angle += 180;\r\n\r\n this.currentFace.selectTime(angle, elem);\r\n event.stopPropagation();\r\n }\r\n\r\n changeDisplayed(array) {\r\n for (let i = 0; i < this.clockItems.length; i++)\r\n this.clockItems[i].innerText = array[i];\r\n }\r\n\r\n onEachClockElement(fun) {\r\n [].forEach.call(this.clockItems, c => fun(c));\r\n }\r\n\r\n updateMinutes(minutes, angle) {\r\n this.time.minutes = minutes;\r\n this.calculateHandOfTheClock(angle, this.itemsRadius);\r\n this.onTimeUpdate(this.time, Config.FaceType.MINUTES);\r\n }\r\n\r\n updateHours(hours, angle, radius) {\r\n this.time.hours = hours;\r\n this.calculateHandOfTheClock(angle, radius);\r\n this.onTimeUpdate(this.time, Config.FaceType.HOURS);\r\n }\r\n\r\n calculateHandOfTheClock(angle, size = this.itemsRadius) {\r\n this.handOfAClock.style.transform = `rotate(${angle - 90}deg)`;\r\n this.handOfAClock.style.width = size + \"px\";\r\n }\r\n\r\n toggleToHours() {\r\n this.minutesFace.onLeave();\r\n this.toggleTime(this.hoursFace);\r\n }\r\n\r\n toggleToMinutes() {\r\n this.hoursFace.onLeave();\r\n this.toggleTime(this.minutesFace);\r\n }\r\n\r\n toggleTime(face) {\r\n if (this.currentFace !== face) {\r\n this.onEachClockElement(c => c.classList.add(css.fadeOut));\r\n this.handOfAClock.classList.add(css.fadeOut);\r\n Promise.delay(() => {\r\n this.onEachClockElement(c => c.classList.remove(css.fadeOut));\r\n this.handOfAClock.classList.remove(css.fadeOut);\r\n this.changeDisplayed(face.displayed);\r\n this.currentFace = face;\r\n this.onEachClockElement(c => this.removeSelected(c));\r\n face.onEnter();\r\n }, 300);\r\n }\r\n }\r\n\r\n removeSelected(c) {\r\n c.classList.remove(css.selected);\r\n c.style.background = \"transparent\";\r\n c.style.color = this.options.clockItemColor;\r\n }\r\n}","import ClockHeader from \"./clockHeader\";\r\nimport ClockFace from \"./face/clockFace\";\r\nimport Config, {DOM} from \"./meta/config\";\r\nimport formatTime from \"./timeFormatter\";\r\n\r\nexport default class Clock {\r\n\r\n constructor(options, time) {\r\n this.options = options;\r\n\r\n this.initView();\r\n this.time = time;\r\n this.initElements();\r\n }\r\n\r\n initView() {\r\n this.submitButton = document.getElementById(DOM.submitId);\r\n this.submitButton.onclick = () => {\r\n const time = this.time;\r\n time.formatted = () => formatTime(time);\r\n this.options.onSubmit(time);\r\n Clock.dispose();\r\n };\r\n\r\n this.cancelButton = document.getElementById(DOM.cancelId);\r\n this.cancelButton.onclick = () => {\r\n this.options.onCancel();\r\n Clock.dispose();\r\n };\r\n }\r\n\r\n initElements() {\r\n this.header = new ClockHeader({\r\n options: this.options,\r\n time: this.time,\r\n onHourClicked: () => this.toggleToHours(),\r\n onMinutesClicked: () => this.toggleToMinutes()\r\n });\r\n this.clockFace = new ClockFace(this.options, this.time, (time, type) => this.onTimeUpdate(time, type));\r\n }\r\n\r\n onStart() {\r\n this.clockFace.onStart();\r\n }\r\n\r\n toggleToHours() {\r\n this.clockFace.toggleToHours();\r\n }\r\n\r\n toggleToMinutes() {\r\n this.clockFace.toggleToMinutes();\r\n }\r\n\r\n onTimeUpdate(time, type) {\r\n this.time = time;\r\n this.header.time = time;\r\n this.header.updateDisplayedTime();\r\n if (type === Config.FaceType.MINUTES)\r\n this.header.toggleActiveToMinutes();\r\n\r\n }\r\n\r\n static dispose() {\r\n document.body.removeChild(document.getElementById(Config.clockId));\r\n }\r\n}","import formatTime from \"./timeFormatter\";\r\nimport showPicker from \"./timepickerCreator\";\r\n\r\nexport default {\r\n showPicker: (config) => showPicker(config),\r\n format: (time) => formatTime(time)\r\n};\r\n\r\n"],"names":["extractTime","date","Date","fromDate","hoursRegex","test","hours","minutesRegex","minutes","parseInt","regex","fromRegex","TypeError","JSON","stringify","parsed","exec","getHours","getMinutes","delay","t","Promise","resolve","styleColors","options","getElementById","DOM","headerId","style","background","headerBackground","color","headerColor","wrapperId","wrapperBackground","clockId","clockBackground","handId","handColor","dotId","buttonsId","footerBackground","submitId","submitColor","cancelId","cancelColor","css","clockItem","clockItemColor","inner","clockItemInnerColor","outer","changeColor","className","property","items","Array","from","document","getElementsByClassName","showPicker","config","Object","assign","Config","clockConfig","time","getTime","clock","Clock","onStart","createDom","Error","clockDiv","createElement","id","innerHTML","clockHtml","body","appendChild","extractedTime","FaceType","HOURS","MINUTES","ClockHeader","onHourClicked","onMinutesClicked","initView","headerHours","hoursId","onclick","toggleActiveToHours","headerMinutes","minutesId","toggleActiveToMinutes","updateDisplayedTime","toggleActive","this","objectToRemoveClass","objectToAddClass","headerSelected","doUpdateDisplayedTime","node","value","innerText","MinutesFace","initialMinutes","updateMinutes","displayed","type","selected","undefined","findSelected","colorSelected","removeSelected","angle","minute","Math","round","clockItems","outerClockItems","isOuter","classList","add","remove","indexOf","HoursFace","initialHours","updateHours","displayedInner","innerClockElem","display","isInnerClock","hoursIndex","innerClockItems","radius","elem","index","selectedAngle","isInner","fn","then","prototype","toRadians","PI","toDegrees","findMousePosition","event","object","rect","getBoundingClientRect","clientX","left","clientY","top","clockElem","size","middle","face","doCreate","span","item","i","push","width","offsetWidth","height","offsetHeight","x","y","itemsRadius","middleX","middleY","doCalculateSize","fun","angleQuantum","length","Utils","itemWidth","itemHeight","sin","bottom","cos","ClockFace","initialTime","onTimeUpdate","isMouseDown","initViews","initTimeFaces","createFace","hoursFace","currentFace","changeDisplayed","innerId","handOfAClock","onmousedown","_this","onmouseup","toggleToMinutes","e","stopPropagation","onmousemove","selectTime","minutesFace","_this2","onEnter","clockFaceCreator","ClockFaceCreator","create","calculateSize","mouse","atan","array","forEach","call","c","calculateHandOfTheClock","transform","onLeave","toggleTime","onEachClockElement","fadeOut","_this3","initElements","submitButton","formatted","formatTime","onSubmit","dispose","cancelButton","onCancel","header","toggleToHours","clockFace","removeChild"],"mappings":"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"} -------------------------------------------------------------------------------- /dist/grudus-timepicker.js: -------------------------------------------------------------------------------- 1 | /*! grudus-timepicker | (c) 2017-2017 2 | grudus | Apache-2.0 license (see LICENSE) */ 3 | var hoursRegex = /^([0-1]?[0-9]|2[0-3])$/; 4 | var minutesRegex = /^([0-5]?[0-9])$/; 5 | var regex = /^([0-1]?[0-9]|2[0-3]):([0-5][0-9])$/; 6 | 7 | function extractTime(date) { 8 | if (!date) return fromDate(new Date());else if (date instanceof Date) return fromDate(date);else if (hoursRegex.test(date.hours) && minutesRegex.test(date.minutes)) return { hours: parseInt(date.hours), minutes: parseInt(date.minutes) };else if (regex.test(date)) return fromRegex(date);else throw new TypeError("INVALID FORMAT: {" + JSON.stringify(date) + "}.\n Time must be a Date or 'hh:MM' string or object with 'hours' and 'minutes' fields"); 9 | } 10 | 11 | function fromRegex(date) { 12 | var parsed = regex.exec(date); 13 | return { hours: parseInt(parsed[1]), minutes: parseInt(parsed[2]) }; 14 | } 15 | 16 | function fromDate(date) { 17 | return { hours: date.getHours(), minutes: date.getMinutes() }; 18 | } 19 | 20 | var formatTime = function (time) { 21 | var extractedTime = extractTime(time); 22 | return (extractedTime.hours < 10 ? "0" + extractedTime.hours : extractedTime.hours) + ":" + (extractedTime.minutes < 10 ? "0" + extractedTime.minutes : extractedTime.minutes); 23 | }; 24 | 25 | var clockHtml = "
\n" + "
\n" + "
\n" + " 21\n" + " :\n" + " 37\n" + "
\n" + "
\n" + "\n" + "\n" + "
\n" + "
" + " \n" + "
\n" + "
\n" + "
\n" + "
\n" + "\n" + "\n" + "
\n" + " \n" + " \n" + "
\n" + "\n" + "
"; 26 | 27 | var clockId = "grudus-clock"; 28 | 29 | var defaultConfig = { 30 | onSubmit: function onSubmit() {}, 31 | onCancel: function onCancel() {}, 32 | headerBackground: "#1976D2", 33 | headerColor: "#c7d6e1", 34 | headerSelected: "#ffffff", 35 | wrapperBackground: "#f0fff0", 36 | footerBackground: "#f0fff0", 37 | submitColor: "#1976D2", 38 | cancelColor: "#1976D2", 39 | clockBackground: "#CFD8DC", 40 | clockItemColor: "#212121", 41 | clockItemInnerColor: "#212121", 42 | handColor: "#1976D2" 43 | }; 44 | 45 | var FaceType = { HOURS: "hours", MINUTES: "minutes" }; 46 | 47 | var css = { 48 | clock: "g-clock", 49 | clockItem: "g-clock-item", 50 | inner: "g-clock-inner", 51 | outer: "g-clock-outer", 52 | item: "g-clock-item", 53 | hand: "g-hand-of-a-clock", 54 | fadeOut: "g-fade-out", 55 | selected: "g-selected", 56 | active: "g-active", 57 | submit: "g-submit", 58 | cancel: "g-cancel", 59 | hour: "g-hour", 60 | minute: "g-minute" 61 | }; 62 | 63 | var DOM = { 64 | headerId: "g-head", 65 | hoursId: "g-hours", 66 | minutesId: "g-minutes", 67 | clockId: "g-clock", 68 | innerId: "g-clock-inner", 69 | wrapperId: "g-clock-wrapper", 70 | dotId: "g-middle-dot", 71 | handId: "g-hand-of-a-clock", 72 | buttonsId: "g-buttons", 73 | submitId: "g-time-submit", 74 | cancelId: "g-time-cancel" 75 | }; 76 | 77 | var Config = { clockId: clockId, clockConfig: defaultConfig, FaceType: FaceType }; 78 | 79 | var classCallCheck = function (instance, Constructor) { 80 | if (!(instance instanceof Constructor)) { 81 | throw new TypeError("Cannot call a class as a function"); 82 | } 83 | }; 84 | 85 | var createClass = function () { 86 | function defineProperties(target, props) { 87 | for (var i = 0; i < props.length; i++) { 88 | var descriptor = props[i]; 89 | descriptor.enumerable = descriptor.enumerable || false; 90 | descriptor.configurable = true; 91 | if ("value" in descriptor) descriptor.writable = true; 92 | Object.defineProperty(target, descriptor.key, descriptor); 93 | } 94 | } 95 | 96 | return function (Constructor, protoProps, staticProps) { 97 | if (protoProps) defineProperties(Constructor.prototype, protoProps); 98 | if (staticProps) defineProperties(Constructor, staticProps); 99 | return Constructor; 100 | }; 101 | }(); 102 | 103 | var ClockHeader = function () { 104 | function ClockHeader(config) { 105 | classCallCheck(this, ClockHeader); 106 | 107 | this.options = config.options; 108 | this.time = config.time; 109 | this.onHourClicked = config.onHourClicked; 110 | this.onMinutesClicked = config.onMinutesClicked; 111 | 112 | this.initView(); 113 | } 114 | 115 | createClass(ClockHeader, [{ 116 | key: "initView", 117 | value: function initView() { 118 | var _this = this; 119 | 120 | this.headerHours = document.getElementById(DOM.hoursId); 121 | this.headerHours.onclick = function () { 122 | _this.toggleActiveToHours(); 123 | _this.onHourClicked(); 124 | }; 125 | 126 | this.headerMinutes = document.getElementById(DOM.minutesId); 127 | this.headerMinutes.onclick = function () { 128 | _this.toggleActiveToMinutes(); 129 | _this.onMinutesClicked(); 130 | }; 131 | 132 | this.updateDisplayedTime(); 133 | this.toggleActiveToHours(); 134 | } 135 | }, { 136 | key: "toggleActiveToMinutes", 137 | value: function toggleActiveToMinutes() { 138 | this.toggleActive(this.headerHours, this.headerMinutes); 139 | } 140 | }, { 141 | key: "toggleActiveToHours", 142 | value: function toggleActiveToHours() { 143 | this.toggleActive(this.headerMinutes, this.headerHours); 144 | } 145 | }, { 146 | key: "toggleActive", 147 | value: function toggleActive(objectToRemoveClass, objectToAddClass) { 148 | objectToRemoveClass.style.color = this.options.headerColor; 149 | objectToAddClass.style.color = this.options.headerSelected; 150 | } 151 | }, { 152 | key: "updateDisplayedTime", 153 | value: function updateDisplayedTime() { 154 | ClockHeader.doUpdateDisplayedTime(this.headerHours, this.time.hours); 155 | ClockHeader.doUpdateDisplayedTime(this.headerMinutes, this.time.minutes); 156 | } 157 | }], [{ 158 | key: "doUpdateDisplayedTime", 159 | value: function doUpdateDisplayedTime(node, value) { 160 | if (value < 10) node.innerText = "0" + value;else node.innerText = value; 161 | } 162 | }]); 163 | return ClockHeader; 164 | }(); 165 | 166 | var MinutesFace = function () { 167 | function MinutesFace(items, initialMinutes, updateMinutes) { 168 | classCallCheck(this, MinutesFace); 169 | 170 | this.displayed = ["00", "05", "10", "15", "20", "25", "30", "35", "40", "45", "50", "55"]; 171 | this.options = items.options; 172 | this.type = Config.FaceType.MINUTES; 173 | this.selected = undefined; 174 | this.items = items; 175 | this.minutes = initialMinutes; 176 | this.updateMinutes = updateMinutes; 177 | } 178 | 179 | createClass(MinutesFace, [{ 180 | key: "onEnter", 181 | value: function onEnter() { 182 | this.selected = this.findSelected(this.minutes); 183 | this.colorSelected(); 184 | this.updateMinutes(this.minutes, this.minutes * 6); 185 | } 186 | }, { 187 | key: "onLeave", 188 | value: function onLeave() { 189 | if (this.selected) { 190 | this.removeSelected(); 191 | this.selected = undefined; 192 | } 193 | } 194 | }, { 195 | key: "selectTime", 196 | value: function selectTime(angle) { 197 | if (this.selected) this.removeSelected(); 198 | 199 | var minute = Math.round(angle / 6) % 60; 200 | this.selected = this.findSelected(minute); 201 | this.colorSelected(); 202 | this.minutes = minute; 203 | this.updateMinutes(this.minutes, angle); 204 | } 205 | }, { 206 | key: "findSelected", 207 | value: function findSelected(minute) { 208 | return minute % 5 === 0 ? this.items.clockItems[minute / 5] : this.items.outerClockItems[minute]; 209 | } 210 | }, { 211 | key: "colorSelected", 212 | value: function colorSelected() { 213 | if (this.isOuter()) { 214 | this.selected.classList.add(css.selected); 215 | return; 216 | } 217 | this.selected.style.background = this.options.handColor; 218 | this.selected.style.color = "whitesmoke"; 219 | } 220 | }, { 221 | key: "removeSelected", 222 | value: function removeSelected() { 223 | if (this.isOuter()) { 224 | this.selected.classList.remove(css.selected); 225 | return; 226 | } 227 | this.selected.style.background = "transparent"; 228 | this.selected.style.color = this.options.clockItemColor; 229 | } 230 | }, { 231 | key: "isOuter", 232 | value: function isOuter() { 233 | return this.items.outerClockItems.indexOf(this.selected) > -1; 234 | } 235 | }]); 236 | return MinutesFace; 237 | }(); 238 | 239 | var HoursFace = function () { 240 | function HoursFace(items, initialHours, updateHours) { 241 | classCallCheck(this, HoursFace); 242 | 243 | this.displayed = ["12", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"]; 244 | this.displayedInner = ["00", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23"]; 245 | this.type = Config.FaceType.MINUTES; 246 | this.selected = undefined; 247 | this.options = items.options; 248 | 249 | this.items = items; 250 | this.hours = initialHours; 251 | this.updateHours = updateHours; 252 | } 253 | 254 | createClass(HoursFace, [{ 255 | key: "onEnter", 256 | value: function onEnter() { 257 | this.items.innerClockElem.style.display = "block"; 258 | var isInnerClock = this.hours < 13 && this.hours !== 0; 259 | var hoursIndex = this.hours % 12; 260 | this.selected = isInnerClock ? this.items.clockItems[hoursIndex] : this.items.innerClockItems[hoursIndex]; 261 | this.colorSelected(); 262 | 263 | this.updateHours(this.hours, hoursIndex * 30, isInnerClock ? this.items.radius : this.items.radius - 50); 264 | } 265 | }, { 266 | key: "onLeave", 267 | value: function onLeave() { 268 | this.items.innerClockElem.style.display = "none"; 269 | if (this.selected) { 270 | this.removeSelected(); 271 | this.selected = undefined; 272 | } 273 | } 274 | }, { 275 | key: "selectTime", 276 | value: function selectTime(angle, elem) { 277 | if (this.selected) this.removeSelected(); 278 | 279 | var index = Math.round(angle / 30) % 12; 280 | this.selected = (elem === this.items.innerClockElem ? this.items.innerClockItems : this.items.clockItems)[index]; 281 | 282 | this.colorSelected(); 283 | this.hours = parseInt(this.selected.innerText); 284 | var selectedAngle = Math.round(angle / 30) * 30; 285 | 286 | this.updateHours(this.hours, selectedAngle, elem === this.items.innerClockElem ? this.items.radius - 50 : this.items.radius); 287 | } 288 | }, { 289 | key: "colorSelected", 290 | value: function colorSelected() { 291 | this.selected.style.background = this.options.handColor; 292 | this.selected.style.color = "#ffffff"; 293 | } 294 | }, { 295 | key: "removeSelected", 296 | value: function removeSelected() { 297 | this.selected.style.background = "transparent"; 298 | this.selected.style.color = this.isInner() ? this.options.clockItemInnerColor : this.options.clockItemColor; 299 | } 300 | }, { 301 | key: "isInner", 302 | value: function isInner() { 303 | return Array.from(this.items.innerClockItems).indexOf(this.selected) > -1; 304 | } 305 | }]); 306 | return HoursFace; 307 | }(); 308 | 309 | function toRadians(angle) { 310 | return angle * (Math.PI / 180); 311 | } 312 | 313 | function toDegrees(angle) { 314 | return angle * (180 / Math.PI); 315 | } 316 | 317 | function findMousePosition(event, object) { 318 | var rect = object.getBoundingClientRect(); 319 | return { 320 | x: event.clientX - rect.left, 321 | y: event.clientY - rect.top 322 | }; 323 | } 324 | 325 | function delay(t) { 326 | return new Promise(function (resolve) { 327 | setTimeout(resolve, t); 328 | }); 329 | } 330 | 331 | Promise.delay = function (fn, t) { 332 | if (!t) { 333 | t = fn; 334 | fn = function fn() {}; 335 | } 336 | return delay(t).then(fn); 337 | }; 338 | 339 | Promise.prototype.delay = function (fn, t) { 340 | return this.then(function () { 341 | return Promise.delay(fn, t); 342 | }); 343 | }; 344 | 345 | var Utils = { toRadians: toRadians, toDegrees: toDegrees, findMousePosition: findMousePosition }; 346 | 347 | var ClockFaceCreator = function () { 348 | function ClockFaceCreator(clockElem, innerClockElem) { 349 | classCallCheck(this, ClockFaceCreator); 350 | 351 | this.clockElem = clockElem; 352 | this.innerClockElem = innerClockElem; 353 | this.size = {}; 354 | this.middle = {}; 355 | } 356 | 357 | createClass(ClockFaceCreator, [{ 358 | key: "create", 359 | value: function create(clockItems, innerClockItems, outerClockItems, face) { 360 | ClockFaceCreator.doCreate(clockItems, this.clockElem, function (span) { 361 | return span.classList.add(css.item); 362 | }); 363 | ClockFaceCreator.doCreate(innerClockItems, this.innerClockElem, function (span, i) { 364 | span.classList.add(css.item, css.inner); 365 | span.innerText = face.displayedInner[i]; 366 | }); 367 | 368 | for (var i = 0; i < 60; i++) { 369 | var span = document.createElement("span"); 370 | span.classList.add(css.outer); 371 | outerClockItems.push(span); 372 | this.clockElem.appendChild(span); 373 | } 374 | } 375 | }, { 376 | key: "calculateSize", 377 | value: function calculateSize(clockItems, innerClockItems, outerClockItems) { 378 | this.size.width = this.clockElem.offsetWidth; 379 | this.size.height = this.clockElem.offsetHeight; 380 | this.middle.x = this.size.width / 2; 381 | this.middle.y = this.size.height / 2; 382 | this.itemsRadius = this.size.width / 2 - 20; 383 | 384 | var innerWidth = this.innerClockElem.offsetWidth; 385 | var innerHeight = this.innerClockElem.offsetHeight; 386 | var middleX = innerWidth / 2; 387 | var middleY = innerHeight / 2; 388 | 389 | ClockFaceCreator.doCalculateSize(this.middle.x, this.middle.y, this.itemsRadius, clockItems); 390 | ClockFaceCreator.doCalculateSize(middleX, middleY, this.itemsRadius - 40, innerClockItems); 391 | ClockFaceCreator.doCalculateSize(this.middle.x, this.middle.y, this.itemsRadius, outerClockItems); 392 | } 393 | }], [{ 394 | key: "doCreate", 395 | value: function doCreate(clockItems, clockElem, fun) { 396 | for (var i = 0; i < 12; i++) { 397 | var span = document.createElement("span"); 398 | fun(span, i); 399 | clockItems.push(span); 400 | clockElem.appendChild(span); 401 | } 402 | } 403 | }, { 404 | key: "doCalculateSize", 405 | value: function doCalculateSize(middleX, middleY, radius, items) { 406 | var angleQuantum = 360 / items.length; 407 | for (var i = 0; i < items.length; i++) { 408 | 409 | var angle = Utils.toRadians(i * angleQuantum); 410 | var item = items[i]; 411 | var itemWidth = item.offsetWidth; 412 | var itemHeight = item.offsetHeight; 413 | 414 | item.style.left = middleX + Math.sin(angle) * radius - itemWidth / 2 + "px"; 415 | item.style.bottom = middleY + Math.cos(angle) * radius - itemHeight / 2 + "px"; 416 | } 417 | } 418 | }]); 419 | return ClockFaceCreator; 420 | }(); 421 | 422 | var ClockFace = function () { 423 | function ClockFace(options, initialTime, onTimeUpdate) { 424 | classCallCheck(this, ClockFace); 425 | 426 | this.options = options; 427 | this.time = initialTime; 428 | this.onTimeUpdate = onTimeUpdate; 429 | this.isMouseDown = false; 430 | this.clockItems = []; 431 | this.innerClockItems = []; 432 | this.outerClockItems = []; 433 | this.size = {}; 434 | this.middle = {}; 435 | 436 | this.initViews(); 437 | this.initTimeFaces(initialTime); 438 | this.createFace(); 439 | 440 | this.hoursFace.items.radius = this.itemsRadius; 441 | 442 | this.currentFace = this.hoursFace; 443 | this.changeDisplayed(this.currentFace.displayed); 444 | } 445 | 446 | createClass(ClockFace, [{ 447 | key: "initViews", 448 | value: function initViews() { 449 | var _this = this; 450 | 451 | this.clockElem = document.getElementById(DOM.clockId); 452 | this.innerClockElem = document.getElementById(DOM.innerId); 453 | this.handOfAClock = document.getElementById(DOM.handId); 454 | 455 | this.clockElem.onmousedown = function () { 456 | return _this.isMouseDown = true; 457 | }; 458 | this.clockElem.onmouseup = function () { 459 | _this.isMouseDown = false; 460 | _this.toggleToMinutes(); 461 | }; 462 | 463 | this.handOfAClock.onmouseup = function (e) { 464 | return e.stopPropagation(); 465 | }; 466 | this.handOfAClock.onmousemove = function (e) { 467 | return e.stopPropagation(); 468 | }; 469 | this.handOfAClock.onclick = function (e) { 470 | return e.stopPropagation(); 471 | }; 472 | 473 | this.clockElem.onmousemove = function (e) { 474 | return _this.selectTime(e, false, _this.clockElem); 475 | }; 476 | this.clockElem.onclick = function (e) { 477 | return _this.selectTime(e, true, _this.clockElem); 478 | }; 479 | 480 | this.innerClockElem.onmousemove = function (e) { 481 | return _this.selectTime(e, false, _this.innerClockElem); 482 | }; 483 | this.innerClockElem.onclick = function (e) { 484 | return _this.selectTime(e, true, _this.innerClockElem); 485 | }; 486 | } 487 | }, { 488 | key: "initTimeFaces", 489 | value: function initTimeFaces(initialTime) { 490 | var _this2 = this; 491 | 492 | this.minutesFace = new MinutesFace({ 493 | options: this.options, 494 | clockItems: this.clockItems, 495 | outerClockItems: this.outerClockItems 496 | }, initialTime.minutes, function (minutes, angle) { 497 | return _this2.updateMinutes(minutes, angle); 498 | }); 499 | 500 | this.hoursFace = new HoursFace({ 501 | options: this.options, 502 | innerClockItems: this.innerClockItems, 503 | clockItems: this.clockItems, 504 | innerClockElem: this.innerClockElem 505 | }, initialTime.hours, function (hours, angle, radius) { 506 | return _this2.updateHours(hours, angle, radius); 507 | }); 508 | } 509 | }, { 510 | key: "onStart", 511 | value: function onStart() { 512 | this.currentFace.onEnter(); 513 | } 514 | }, { 515 | key: "createFace", 516 | value: function createFace() { 517 | var clockFaceCreator = new ClockFaceCreator(this.clockElem, this.innerClockElem); 518 | clockFaceCreator.create(this.clockItems, this.innerClockItems, this.outerClockItems, this.hoursFace); 519 | clockFaceCreator.calculateSize(this.clockItems, this.innerClockItems, this.outerClockItems); 520 | 521 | this.size = clockFaceCreator.size; 522 | this.middle = clockFaceCreator.middle; 523 | this.itemsRadius = clockFaceCreator.itemsRadius; 524 | } 525 | }, { 526 | key: "selectTime", 527 | value: function selectTime(event, isMouseDown, elem) { 528 | if (!(isMouseDown || this.isMouseDown)) return; 529 | var mouse = Utils.findMousePosition(event, this.clockElem); 530 | var x = mouse.x - this.middle.x; 531 | var y = this.middle.y - mouse.y; 532 | var angle = 90 - Utils.toDegrees(Math.atan(y / x)); 533 | if (x < 0) angle += 180; 534 | 535 | this.currentFace.selectTime(angle, elem); 536 | event.stopPropagation(); 537 | } 538 | }, { 539 | key: "changeDisplayed", 540 | value: function changeDisplayed(array) { 541 | for (var i = 0; i < this.clockItems.length; i++) { 542 | this.clockItems[i].innerText = array[i]; 543 | } 544 | } 545 | }, { 546 | key: "onEachClockElement", 547 | value: function onEachClockElement(fun) { 548 | [].forEach.call(this.clockItems, function (c) { 549 | return fun(c); 550 | }); 551 | } 552 | }, { 553 | key: "updateMinutes", 554 | value: function updateMinutes(minutes, angle) { 555 | this.time.minutes = minutes; 556 | this.calculateHandOfTheClock(angle, this.itemsRadius); 557 | this.onTimeUpdate(this.time, Config.FaceType.MINUTES); 558 | } 559 | }, { 560 | key: "updateHours", 561 | value: function updateHours(hours, angle, radius) { 562 | this.time.hours = hours; 563 | this.calculateHandOfTheClock(angle, radius); 564 | this.onTimeUpdate(this.time, Config.FaceType.HOURS); 565 | } 566 | }, { 567 | key: "calculateHandOfTheClock", 568 | value: function calculateHandOfTheClock(angle) { 569 | var size = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.itemsRadius; 570 | 571 | this.handOfAClock.style.transform = "rotate(" + (angle - 90) + "deg)"; 572 | this.handOfAClock.style.width = size + "px"; 573 | } 574 | }, { 575 | key: "toggleToHours", 576 | value: function toggleToHours() { 577 | this.minutesFace.onLeave(); 578 | this.toggleTime(this.hoursFace); 579 | } 580 | }, { 581 | key: "toggleToMinutes", 582 | value: function toggleToMinutes() { 583 | this.hoursFace.onLeave(); 584 | this.toggleTime(this.minutesFace); 585 | } 586 | }, { 587 | key: "toggleTime", 588 | value: function toggleTime(face) { 589 | var _this3 = this; 590 | 591 | if (this.currentFace !== face) { 592 | this.onEachClockElement(function (c) { 593 | return c.classList.add(css.fadeOut); 594 | }); 595 | this.handOfAClock.classList.add(css.fadeOut); 596 | Promise.delay(function () { 597 | _this3.onEachClockElement(function (c) { 598 | return c.classList.remove(css.fadeOut); 599 | }); 600 | _this3.handOfAClock.classList.remove(css.fadeOut); 601 | _this3.changeDisplayed(face.displayed); 602 | _this3.currentFace = face; 603 | _this3.onEachClockElement(function (c) { 604 | return _this3.removeSelected(c); 605 | }); 606 | face.onEnter(); 607 | }, 300); 608 | } 609 | } 610 | }, { 611 | key: "removeSelected", 612 | value: function removeSelected(c) { 613 | c.classList.remove(css.selected); 614 | c.style.background = "transparent"; 615 | c.style.color = this.options.clockItemColor; 616 | } 617 | }]); 618 | return ClockFace; 619 | }(); 620 | 621 | var Clock = function () { 622 | function Clock(options, time) { 623 | classCallCheck(this, Clock); 624 | 625 | this.options = options; 626 | 627 | this.initView(); 628 | this.time = time; 629 | this.initElements(); 630 | } 631 | 632 | createClass(Clock, [{ 633 | key: "initView", 634 | value: function initView() { 635 | var _this = this; 636 | 637 | this.submitButton = document.getElementById(DOM.submitId); 638 | this.submitButton.onclick = function () { 639 | var time = _this.time; 640 | time.formatted = function () { 641 | return formatTime(time); 642 | }; 643 | _this.options.onSubmit(time); 644 | Clock.dispose(); 645 | }; 646 | 647 | this.cancelButton = document.getElementById(DOM.cancelId); 648 | this.cancelButton.onclick = function () { 649 | _this.options.onCancel(); 650 | Clock.dispose(); 651 | }; 652 | } 653 | }, { 654 | key: "initElements", 655 | value: function initElements() { 656 | var _this2 = this; 657 | 658 | this.header = new ClockHeader({ 659 | options: this.options, 660 | time: this.time, 661 | onHourClicked: function onHourClicked() { 662 | return _this2.toggleToHours(); 663 | }, 664 | onMinutesClicked: function onMinutesClicked() { 665 | return _this2.toggleToMinutes(); 666 | } 667 | }); 668 | this.clockFace = new ClockFace(this.options, this.time, function (time, type) { 669 | return _this2.onTimeUpdate(time, type); 670 | }); 671 | } 672 | }, { 673 | key: "onStart", 674 | value: function onStart() { 675 | this.clockFace.onStart(); 676 | } 677 | }, { 678 | key: "toggleToHours", 679 | value: function toggleToHours() { 680 | this.clockFace.toggleToHours(); 681 | } 682 | }, { 683 | key: "toggleToMinutes", 684 | value: function toggleToMinutes() { 685 | this.clockFace.toggleToMinutes(); 686 | } 687 | }, { 688 | key: "onTimeUpdate", 689 | value: function onTimeUpdate(time, type) { 690 | this.time = time; 691 | this.header.time = time; 692 | this.header.updateDisplayedTime(); 693 | if (type === Config.FaceType.MINUTES) this.header.toggleActiveToMinutes(); 694 | } 695 | }], [{ 696 | key: "dispose", 697 | value: function dispose() { 698 | document.body.removeChild(document.getElementById(Config.clockId)); 699 | } 700 | }]); 701 | return Clock; 702 | }(); 703 | 704 | function styleColors(options) { 705 | document.getElementById(DOM.headerId).style.background = options.headerBackground; 706 | document.getElementById(DOM.headerId).style.color = options.headerColor; 707 | document.getElementById(DOM.wrapperId).style.background = options.wrapperBackground; 708 | document.getElementById(DOM.clockId).style.background = options.clockBackground; 709 | document.getElementById(DOM.handId).style.background = options.handColor; 710 | document.getElementById(DOM.dotId).style.background = options.handColor; 711 | document.getElementById(DOM.buttonsId).style.background = options.footerBackground; 712 | document.getElementById(DOM.submitId).style.color = options.submitColor; 713 | document.getElementById(DOM.cancelId).style.color = options.cancelColor; 714 | 715 | changeColor(css.clockItem, options.clockItemColor); 716 | changeColor(css.inner, options.clockItemInnerColor); 717 | changeColor(css.outer, options.handColor, "borderColor"); 718 | } 719 | 720 | function changeColor(className, color) { 721 | var property = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "color"; 722 | 723 | var items = Array.from(document.getElementsByClassName(className)); 724 | var _iteratorNormalCompletion = true; 725 | var _didIteratorError = false; 726 | var _iteratorError = undefined; 727 | 728 | try { 729 | for (var _iterator = items[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { 730 | var item = _step.value; 731 | 732 | item.style[property] = color; 733 | } 734 | } catch (err) { 735 | _didIteratorError = true; 736 | _iteratorError = err; 737 | } finally { 738 | try { 739 | if (!_iteratorNormalCompletion && _iterator.return) { 740 | _iterator.return(); 741 | } 742 | } finally { 743 | if (_didIteratorError) { 744 | throw _iteratorError; 745 | } 746 | } 747 | } 748 | } 749 | 750 | function showPicker() { 751 | var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 752 | 753 | createDom(); 754 | 755 | var options = Object.assign({}, Config.clockConfig, config); 756 | var time = extractTime(options.time); 757 | 758 | var clock = new Clock(options, time); 759 | styleColors(options); 760 | clock.onStart(); 761 | } 762 | 763 | function createDom() { 764 | if (document.getElementById(Config.clockId)) throw Error("There is already one running grudus-timepicker instance!"); 765 | 766 | var clockDiv = document.createElement("div"); 767 | clockDiv.id = Config.clockId; 768 | clockDiv.innerHTML = clockHtml; 769 | document.body.appendChild(clockDiv); 770 | } 771 | 772 | var index = { 773 | showPicker: function showPicker$$1(config) { 774 | return showPicker(config); 775 | }, 776 | format: function format(time) { 777 | return formatTime(time); 778 | } 779 | }; 780 | 781 | export default index; 782 | //# sourceMappingURL=grudus-timepicker.js.map 783 | -------------------------------------------------------------------------------- /dist/grudus-timepicker.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"grudus-timepicker.js","sources":["../src/js/timeExtractor.js","../src/js/timeFormatter.js","../src/js/meta/clockHtml.js","../src/js/meta/config.js","../src/js/clockHeader.js","../src/js/face/minutesFace.js","../src/js/face/hoursFace.js","../src/js/meta/utils.js","../src/js/face/clockFaceCreator.js","../src/js/face/clockFace.js","../src/js/clock.js","../src/js/colorStylists.js","../src/js/timepickerCreator.js","../src/js/index.js"],"sourcesContent":["const hoursRegex = /^([0-1]?[0-9]|2[0-3])$/;\r\nconst minutesRegex = /^([0-5]?[0-9])$/;\r\nconst regex = /^([0-1]?[0-9]|2[0-3]):([0-5][0-9])$/;\r\n\r\nexport default function extractTime(date) {\r\n if (!date)\r\n return fromDate(new Date());\r\n else if (date instanceof Date)\r\n return fromDate(date);\r\n else if (hoursRegex.test(date.hours) && minutesRegex.test(date.minutes))\r\n return {hours: parseInt(date.hours), minutes: parseInt(date.minutes)};\r\n else if (regex.test(date))\r\n return fromRegex(date);\r\n else\r\n throw new TypeError(`INVALID FORMAT: {${JSON.stringify(date)}}.\r\n Time must be a Date or 'hh:MM' string or object with 'hours' and 'minutes' fields`);\r\n}\r\n\r\nfunction fromRegex(date) {\r\n const parsed = regex.exec(date);\r\n return {hours: parseInt(parsed[1]), minutes: parseInt(parsed[2])};\r\n}\r\n\r\nfunction fromDate(date) {\r\n return {hours: date.getHours(), minutes: date.getMinutes()};\r\n}","import extractTime from \"./timeExtractor\";\r\n\r\nexport default function (time) {\r\n const extractedTime = extractTime(time);\r\n return (extractedTime.hours < 10 ? \"0\" + extractedTime.hours : extractedTime.hours)\r\n + \":\" + (extractedTime.minutes < 10 ? \"0\" + extractedTime.minutes : extractedTime.minutes);\r\n}","export default \"
\\n\" +\r\n\"
\\n\" +\r\n\"
\\n\" +\r\n\" 21\\n\" +\r\n\" :\\n\" +\r\n\" 37\\n\" +\r\n\"
\\n\" +\r\n\"
\\n\" +\r\n\"\\n\" +\r\n\"\\n\" +\r\n\"
\\n\" +\r\n\"
\" +\r\n\" \\n\" +\r\n\"
\\n\" +\r\n\"
\\n\" +\r\n\"
\\n\" +\r\n\"
\\n\" +\r\n\"\\n\" +\r\n\"\\n\" +\r\n\"
\\n\" +\r\n\" \\n\" +\r\n\" \\n\" +\r\n\"
\\n\" +\r\n\"\\n\" +\r\n\"
\";","const clockId = \"grudus-clock\";\r\n\r\nconst defaultConfig = {\r\n onSubmit: () => {\r\n },\r\n onCancel: () => {\r\n },\r\n headerBackground: \"#1976D2\",\r\n headerColor: \"#c7d6e1\",\r\n headerSelected: \"#ffffff\",\r\n wrapperBackground: \"#f0fff0\",\r\n footerBackground: \"#f0fff0\",\r\n submitColor: \"#1976D2\",\r\n cancelColor: \"#1976D2\",\r\n clockBackground: \"#CFD8DC\",\r\n clockItemColor: \"#212121\",\r\n clockItemInnerColor: \"#212121\",\r\n handColor: \"#1976D2\"\r\n};\r\n\r\nconst FaceType = {HOURS: \"hours\", MINUTES: \"minutes\"};\r\n\r\nconst css = {\r\n clock: \"g-clock\",\r\n clockItem: \"g-clock-item\",\r\n inner: \"g-clock-inner\",\r\n outer: \"g-clock-outer\",\r\n item: \"g-clock-item\",\r\n hand: \"g-hand-of-a-clock\",\r\n fadeOut: \"g-fade-out\",\r\n selected: \"g-selected\",\r\n active: \"g-active\",\r\n submit: \"g-submit\",\r\n cancel: \"g-cancel\",\r\n hour: \"g-hour\",\r\n minute: \"g-minute\"\r\n};\r\n\r\nconst DOM = {\r\n headerId: \"g-head\",\r\n hoursId: \"g-hours\",\r\n minutesId: \"g-minutes\",\r\n clockId: \"g-clock\",\r\n innerId: \"g-clock-inner\",\r\n wrapperId: \"g-clock-wrapper\",\r\n dotId: \"g-middle-dot\",\r\n handId: \"g-hand-of-a-clock\",\r\n buttonsId: \"g-buttons\",\r\n submitId: \"g-time-submit\",\r\n cancelId: \"g-time-cancel\"\r\n};\r\n\r\nexport default {clockId, clockConfig: defaultConfig, FaceType};\r\nexport {css, DOM};","import {DOM} from \"./meta/config\";\r\n\r\nexport default class ClockHeader {\r\n\r\n constructor(config) {\r\n this.options = config.options;\r\n this.time = config.time;\r\n this.onHourClicked = config.onHourClicked;\r\n this.onMinutesClicked = config.onMinutesClicked;\r\n\r\n this.initView();\r\n }\r\n\r\n initView() {\r\n this.headerHours = document.getElementById(DOM.hoursId);\r\n this.headerHours.onclick = () => {\r\n this.toggleActiveToHours();\r\n this.onHourClicked();\r\n };\r\n\r\n this.headerMinutes = document.getElementById(DOM.minutesId);\r\n this.headerMinutes.onclick = () => {\r\n this.toggleActiveToMinutes();\r\n this.onMinutesClicked();\r\n };\r\n\r\n this.updateDisplayedTime();\r\n this.toggleActiveToHours();\r\n }\r\n\r\n toggleActiveToMinutes() {\r\n this.toggleActive(this.headerHours, this.headerMinutes);\r\n }\r\n\r\n toggleActiveToHours() {\r\n this.toggleActive(this.headerMinutes, this.headerHours);\r\n }\r\n\r\n toggleActive(objectToRemoveClass, objectToAddClass) {\r\n objectToRemoveClass.style.color = this.options.headerColor;\r\n objectToAddClass.style.color = this.options.headerSelected;\r\n }\r\n\r\n updateDisplayedTime() {\r\n ClockHeader.doUpdateDisplayedTime(this.headerHours, this.time.hours);\r\n ClockHeader.doUpdateDisplayedTime(this.headerMinutes, this.time.minutes);\r\n }\r\n\r\n static doUpdateDisplayedTime(node, value) {\r\n if (value < 10)\r\n node.innerText = \"0\" + value;\r\n else node.innerText = value;\r\n }\r\n}","import Config, {css} from \"../meta/config\";\r\n\r\nexport default class MinutesFace {\r\n\r\n constructor(items, initialMinutes, updateMinutes) {\r\n this.displayed = [\"00\", \"05\", \"10\", \"15\", \"20\", \"25\", \"30\", \"35\", \"40\", \"45\", \"50\", \"55\"];\r\n this.options = items.options;\r\n this.type = Config.FaceType.MINUTES;\r\n this.selected = undefined;\r\n this.items = items;\r\n this.minutes = initialMinutes;\r\n this.updateMinutes = updateMinutes;\r\n }\r\n\r\n onEnter() {\r\n this.selected = this.findSelected(this.minutes);\r\n this.colorSelected();\r\n this.updateMinutes(this.minutes, this.minutes * 6);\r\n }\r\n\r\n onLeave() {\r\n if (this.selected) {\r\n this.removeSelected();\r\n this.selected = undefined;\r\n }\r\n }\r\n\r\n selectTime(angle) {\r\n if (this.selected)\r\n this.removeSelected();\r\n\r\n const minute = Math.round(angle / 6) % 60;\r\n this.selected = this.findSelected(minute);\r\n this.colorSelected();\r\n this.minutes = minute;\r\n this.updateMinutes(this.minutes, angle);\r\n }\r\n\r\n findSelected(minute) {\r\n return (minute % 5 === 0) ? this.items.clockItems[minute / 5] : this.items.outerClockItems[minute];\r\n }\r\n\r\n colorSelected() {\r\n if (this.isOuter()) {\r\n this.selected.classList.add(css.selected);\r\n return;\r\n }\r\n this.selected.style.background = this.options.handColor;\r\n this.selected.style.color = \"whitesmoke\";\r\n }\r\n\r\n removeSelected() {\r\n if (this.isOuter()) {\r\n this.selected.classList.remove(css.selected);\r\n return;\r\n }\r\n this.selected.style.background = \"transparent\";\r\n this.selected.style.color = this.options.clockItemColor;\r\n }\r\n\r\n isOuter() {\r\n return this.items.outerClockItems.indexOf(this.selected) > -1;\r\n }\r\n}","import Config from \"../meta/config\";\r\n\r\nexport default class HoursFace {\r\n\r\n constructor(items, initialHours, updateHours) {\r\n this.displayed = [\"12\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \"10\", \"11\"];\r\n this.displayedInner = [\"00\", \"13\", \"14\", \"15\", \"16\", \"17\", \"18\", \"19\", \"20\", \"21\", \"22\", \"23\"];\r\n this.type = Config.FaceType.MINUTES;\r\n this.selected = undefined;\r\n this.options = items.options;\r\n\r\n this.items = items;\r\n this.hours = initialHours;\r\n this.updateHours = updateHours;\r\n }\r\n\r\n onEnter() {\r\n this.items.innerClockElem.style.display = \"block\";\r\n const isInnerClock = this.hours < 13 && this.hours !== 0;\r\n const hoursIndex = this.hours % 12;\r\n this.selected = isInnerClock ? this.items.clockItems[hoursIndex] : this.items.innerClockItems[hoursIndex];\r\n this.colorSelected();\r\n\r\n this.updateHours(this.hours, hoursIndex * 30, isInnerClock ? this.items.radius : this.items.radius - 50);\r\n }\r\n\r\n onLeave() {\r\n this.items.innerClockElem.style.display = \"none\";\r\n if (this.selected) {\r\n this.removeSelected();\r\n this.selected = undefined;\r\n }\r\n }\r\n\r\n selectTime(angle, elem) {\r\n if (this.selected)\r\n this.removeSelected();\r\n\r\n const index = Math.round(angle / 30) % 12;\r\n this.selected = (elem === this.items.innerClockElem\r\n ? this.items.innerClockItems\r\n : this.items.clockItems)[index];\r\n\r\n this.colorSelected();\r\n this.hours = parseInt(this.selected.innerText);\r\n const selectedAngle = Math.round(angle / 30) * 30;\r\n\r\n this.updateHours(this.hours, selectedAngle,\r\n elem === this.items.innerClockElem ? this.items.radius - 50 : this.items.radius);\r\n }\r\n\r\n colorSelected() {\r\n this.selected.style.background = this.options.handColor;\r\n this.selected.style.color = \"#ffffff\";\r\n }\r\n\r\n removeSelected() {\r\n this.selected.style.background = \"transparent\";\r\n this.selected.style.color = this.isInner()\r\n ? this.options.clockItemInnerColor\r\n : this.options.clockItemColor;\r\n }\r\n\r\n isInner() {\r\n return Array.from(this.items.innerClockItems).indexOf(this.selected) > -1;\r\n }\r\n}","function toRadians(angle) {\r\n return angle * (Math.PI / 180);\r\n}\r\n\r\nfunction toDegrees(angle) {\r\n return angle * (180 / Math.PI);\r\n}\r\n\r\nfunction findMousePosition(event, object) {\r\n const rect = object.getBoundingClientRect();\r\n return {\r\n x: event.clientX - rect.left,\r\n y: event.clientY - rect.top\r\n };\r\n}\r\n\r\nfunction delay(t) {\r\n return new Promise(function (resolve) {\r\n setTimeout(resolve, t);\r\n });\r\n}\r\n\r\nPromise.delay = function (fn, t) {\r\n if (!t) {\r\n t = fn;\r\n fn = function () {\r\n };\r\n }\r\n return delay(t).then(fn);\r\n};\r\n\r\nPromise.prototype.delay = function (fn, t) {\r\n return this.then(function () {\r\n return Promise.delay(fn, t);\r\n });\r\n};\r\n\r\nexport default {toRadians, toDegrees, findMousePosition};","import Utils from \"../meta/utils\";\r\nimport {css} from \"../meta/config\";\r\n\r\nexport default class ClockFaceCreator {\r\n\r\n constructor(clockElem, innerClockElem) {\r\n this.clockElem = clockElem;\r\n this.innerClockElem = innerClockElem;\r\n this.size = {};\r\n this.middle = {};\r\n }\r\n\r\n create(clockItems, innerClockItems, outerClockItems, face) {\r\n ClockFaceCreator.doCreate(clockItems, this.clockElem, span => span.classList.add(css.item));\r\n ClockFaceCreator.doCreate(innerClockItems, this.innerClockElem, (span, i) => {\r\n span.classList.add(css.item, css.inner);\r\n span.innerText = face.displayedInner[i];\r\n });\r\n\r\n for (let i = 0; i < 60; i++) {\r\n const span = document.createElement(\"span\");\r\n span.classList.add(css.outer);\r\n outerClockItems.push(span);\r\n this.clockElem.appendChild(span);\r\n }\r\n }\r\n\r\n static doCreate(clockItems, clockElem, fun) {\r\n for (let i = 0; i < 12; i++) {\r\n const span = document.createElement(\"span\");\r\n fun(span, i);\r\n clockItems.push(span);\r\n clockElem.appendChild(span);\r\n }\r\n }\r\n\r\n calculateSize(clockItems, innerClockItems, outerClockItems) {\r\n this.size.width = this.clockElem.offsetWidth;\r\n this.size.height = this.clockElem.offsetHeight;\r\n this.middle.x = this.size.width / 2;\r\n this.middle.y = this.size.height / 2;\r\n this.itemsRadius = this.size.width / 2 - 20;\r\n\r\n const innerWidth = this.innerClockElem.offsetWidth;\r\n const innerHeight = this.innerClockElem.offsetHeight;\r\n const middleX = innerWidth / 2;\r\n const middleY = innerHeight / 2;\r\n\r\n ClockFaceCreator.doCalculateSize(this.middle.x, this.middle.y, this.itemsRadius, clockItems);\r\n ClockFaceCreator.doCalculateSize(middleX, middleY, this.itemsRadius - 40, innerClockItems);\r\n ClockFaceCreator.doCalculateSize(this.middle.x, this.middle.y, this.itemsRadius, outerClockItems);\r\n }\r\n\r\n static doCalculateSize(middleX, middleY, radius, items) {\r\n const angleQuantum = 360 / items.length;\r\n for (let i = 0; i < items.length; i++) {\r\n\r\n const angle = Utils.toRadians(i * angleQuantum);\r\n const item = items[i];\r\n const itemWidth = item.offsetWidth;\r\n const itemHeight = item.offsetHeight;\r\n\r\n item.style.left = ((middleX + Math.sin(angle) * radius) - itemWidth / 2) + \"px\";\r\n item.style.bottom = ((middleY + Math.cos(angle) * radius) - itemHeight / 2) + \"px\";\r\n }\r\n }\r\n}","import MinutesFace from \"./minutesFace\";\r\nimport HoursFace from \"./hoursFace\";\r\nimport Utils from \"../meta/utils\";\r\nimport Config, {css, DOM} from \"../meta/config\";\r\nimport ClockFaceCreator from \"./clockFaceCreator\";\r\n\r\nexport default class ClockFace {\r\n\r\n constructor(options, initialTime, onTimeUpdate) {\r\n this.options = options;\r\n this.time = initialTime;\r\n this.onTimeUpdate = onTimeUpdate;\r\n this.isMouseDown = false;\r\n this.clockItems = [];\r\n this.innerClockItems = [];\r\n this.outerClockItems = [];\r\n this.size = {};\r\n this.middle = {};\r\n\r\n this.initViews();\r\n this.initTimeFaces(initialTime);\r\n this.createFace();\r\n\r\n this.hoursFace.items.radius = this.itemsRadius;\r\n\r\n this.currentFace = this.hoursFace;\r\n this.changeDisplayed(this.currentFace.displayed);\r\n }\r\n\r\n initViews() {\r\n this.clockElem = document.getElementById(DOM.clockId);\r\n this.innerClockElem = document.getElementById(DOM.innerId);\r\n this.handOfAClock = document.getElementById(DOM.handId);\r\n\r\n this.clockElem.onmousedown = () => this.isMouseDown = true;\r\n this.clockElem.onmouseup = () => {this.isMouseDown = false;\r\n this.toggleToMinutes();\r\n };\r\n\r\n this.handOfAClock.onmouseup = (e) => e.stopPropagation();\r\n this.handOfAClock.onmousemove = (e) => e.stopPropagation();\r\n this.handOfAClock.onclick = (e) => e.stopPropagation();\r\n\r\n this.clockElem.onmousemove = (e) => this.selectTime(e, false, this.clockElem);\r\n this.clockElem.onclick = (e) => this.selectTime(e, true, this.clockElem);\r\n\r\n this.innerClockElem.onmousemove = (e) => this.selectTime(e, false, this.innerClockElem);\r\n this.innerClockElem.onclick = (e) => this.selectTime(e, true, this.innerClockElem);\r\n }\r\n\r\n initTimeFaces(initialTime) {\r\n this.minutesFace = new MinutesFace({\r\n options: this.options,\r\n clockItems: this.clockItems,\r\n outerClockItems: this.outerClockItems\r\n }, initialTime.minutes, (minutes, angle) => this.updateMinutes(minutes, angle));\r\n\r\n this.hoursFace = new HoursFace({\r\n options: this.options,\r\n innerClockItems: this.innerClockItems,\r\n clockItems: this.clockItems,\r\n innerClockElem: this.innerClockElem\r\n }, initialTime.hours, (hours, angle, radius) => this.updateHours(hours, angle, radius));\r\n }\r\n\r\n onStart() {\r\n this.currentFace.onEnter();\r\n }\r\n\r\n createFace() {\r\n const clockFaceCreator = new ClockFaceCreator(this.clockElem, this.innerClockElem);\r\n clockFaceCreator.create(this.clockItems, this.innerClockItems, this.outerClockItems, this.hoursFace);\r\n clockFaceCreator.calculateSize(this.clockItems, this.innerClockItems, this.outerClockItems);\r\n\r\n this.size = clockFaceCreator.size;\r\n this.middle = clockFaceCreator.middle;\r\n this.itemsRadius = clockFaceCreator.itemsRadius;\r\n }\r\n\r\n selectTime(event, isMouseDown, elem) {\r\n if (!(isMouseDown || this.isMouseDown))\r\n return;\r\n const mouse = Utils.findMousePosition(event, this.clockElem);\r\n const x = mouse.x - this.middle.x;\r\n const y = this.middle.y - mouse.y;\r\n let angle = 90 - Utils.toDegrees(Math.atan(y / x));\r\n if (x < 0) angle += 180;\r\n\r\n this.currentFace.selectTime(angle, elem);\r\n event.stopPropagation();\r\n }\r\n\r\n changeDisplayed(array) {\r\n for (let i = 0; i < this.clockItems.length; i++)\r\n this.clockItems[i].innerText = array[i];\r\n }\r\n\r\n onEachClockElement(fun) {\r\n [].forEach.call(this.clockItems, c => fun(c));\r\n }\r\n\r\n updateMinutes(minutes, angle) {\r\n this.time.minutes = minutes;\r\n this.calculateHandOfTheClock(angle, this.itemsRadius);\r\n this.onTimeUpdate(this.time, Config.FaceType.MINUTES);\r\n }\r\n\r\n updateHours(hours, angle, radius) {\r\n this.time.hours = hours;\r\n this.calculateHandOfTheClock(angle, radius);\r\n this.onTimeUpdate(this.time, Config.FaceType.HOURS);\r\n }\r\n\r\n calculateHandOfTheClock(angle, size = this.itemsRadius) {\r\n this.handOfAClock.style.transform = `rotate(${angle - 90}deg)`;\r\n this.handOfAClock.style.width = size + \"px\";\r\n }\r\n\r\n toggleToHours() {\r\n this.minutesFace.onLeave();\r\n this.toggleTime(this.hoursFace);\r\n }\r\n\r\n toggleToMinutes() {\r\n this.hoursFace.onLeave();\r\n this.toggleTime(this.minutesFace);\r\n }\r\n\r\n toggleTime(face) {\r\n if (this.currentFace !== face) {\r\n this.onEachClockElement(c => c.classList.add(css.fadeOut));\r\n this.handOfAClock.classList.add(css.fadeOut);\r\n Promise.delay(() => {\r\n this.onEachClockElement(c => c.classList.remove(css.fadeOut));\r\n this.handOfAClock.classList.remove(css.fadeOut);\r\n this.changeDisplayed(face.displayed);\r\n this.currentFace = face;\r\n this.onEachClockElement(c => this.removeSelected(c));\r\n face.onEnter();\r\n }, 300);\r\n }\r\n }\r\n\r\n removeSelected(c) {\r\n c.classList.remove(css.selected);\r\n c.style.background = \"transparent\";\r\n c.style.color = this.options.clockItemColor;\r\n }\r\n}","import ClockHeader from \"./clockHeader\";\r\nimport ClockFace from \"./face/clockFace\";\r\nimport Config, {DOM} from \"./meta/config\";\r\nimport formatTime from \"./timeFormatter\";\r\n\r\nexport default class Clock {\r\n\r\n constructor(options, time) {\r\n this.options = options;\r\n\r\n this.initView();\r\n this.time = time;\r\n this.initElements();\r\n }\r\n\r\n initView() {\r\n this.submitButton = document.getElementById(DOM.submitId);\r\n this.submitButton.onclick = () => {\r\n const time = this.time;\r\n time.formatted = () => formatTime(time);\r\n this.options.onSubmit(time);\r\n Clock.dispose();\r\n };\r\n\r\n this.cancelButton = document.getElementById(DOM.cancelId);\r\n this.cancelButton.onclick = () => {\r\n this.options.onCancel();\r\n Clock.dispose();\r\n };\r\n }\r\n\r\n initElements() {\r\n this.header = new ClockHeader({\r\n options: this.options,\r\n time: this.time,\r\n onHourClicked: () => this.toggleToHours(),\r\n onMinutesClicked: () => this.toggleToMinutes()\r\n });\r\n this.clockFace = new ClockFace(this.options, this.time, (time, type) => this.onTimeUpdate(time, type));\r\n }\r\n\r\n onStart() {\r\n this.clockFace.onStart();\r\n }\r\n\r\n toggleToHours() {\r\n this.clockFace.toggleToHours();\r\n }\r\n\r\n toggleToMinutes() {\r\n this.clockFace.toggleToMinutes();\r\n }\r\n\r\n onTimeUpdate(time, type) {\r\n this.time = time;\r\n this.header.time = time;\r\n this.header.updateDisplayedTime();\r\n if (type === Config.FaceType.MINUTES)\r\n this.header.toggleActiveToMinutes();\r\n\r\n }\r\n\r\n static dispose() {\r\n document.body.removeChild(document.getElementById(Config.clockId));\r\n }\r\n}","import {css, DOM} from \"./meta/config\";\r\n\r\nexport default function styleColors(options) {\r\n document.getElementById(DOM.headerId).style.background = options.headerBackground;\r\n document.getElementById(DOM.headerId).style.color = options.headerColor;\r\n document.getElementById(DOM.wrapperId).style.background = options.wrapperBackground;\r\n document.getElementById(DOM.clockId).style.background = options.clockBackground;\r\n document.getElementById(DOM.handId).style.background = options.handColor;\r\n document.getElementById(DOM.dotId).style.background = options.handColor;\r\n document.getElementById(DOM.buttonsId).style.background = options.footerBackground;\r\n document.getElementById(DOM.submitId).style.color = options.submitColor;\r\n document.getElementById(DOM.cancelId).style.color = options.cancelColor;\r\n\r\n changeColor(css.clockItem, options.clockItemColor);\r\n changeColor(css.inner, options.clockItemInnerColor);\r\n changeColor(css.outer, options.handColor, \"borderColor\");\r\n}\r\n\r\nfunction changeColor(className, color, property = \"color\") {\r\n const items = Array.from(document.getElementsByClassName(className));\r\n for (const item of items)\r\n item.style[property] = color;\r\n}\r\n","import clockHtml from \"./meta/clockHtml\";\r\nimport Config from \"./meta/config\";\r\nimport Clock from \"./clock\";\r\nimport styleColors from \"./colorStylists\";\r\nimport getTime from \"./timeExtractor\";\r\n\r\n\r\nexport default function showPicker(config = {}) {\r\n createDom();\r\n\r\n const options = Object.assign({}, Config.clockConfig, config);\r\n const time = getTime(options.time);\r\n\r\n const clock = new Clock(options, time);\r\n styleColors(options);\r\n clock.onStart();\r\n}\r\n\r\n\r\nfunction createDom() {\r\n if (document.getElementById(Config.clockId))\r\n throw Error(\"There is already one running grudus-timepicker instance!\");\r\n\r\n const clockDiv = document.createElement(\"div\");\r\n clockDiv.id = Config.clockId;\r\n clockDiv.innerHTML = clockHtml;\r\n document.body.appendChild(clockDiv);\r\n}\r\n","import formatTime from \"./timeFormatter\";\r\nimport showPicker from \"./timepickerCreator\";\r\n\r\nexport default {\r\n showPicker: (config) => showPicker(config),\r\n format: (time) => formatTime(time)\r\n};\r\n\r\n"],"names":["hoursRegex","minutesRegex","regex","extractTime","date","fromDate","Date","test","hours","minutes","parseInt","fromRegex","TypeError","JSON","stringify","parsed","exec","getHours","getMinutes","time","extractedTime","clockId","defaultConfig","FaceType","HOURS","MINUTES","css","DOM","clockConfig","ClockHeader","config","options","onHourClicked","onMinutesClicked","initView","headerHours","document","getElementById","hoursId","onclick","toggleActiveToHours","headerMinutes","minutesId","toggleActiveToMinutes","updateDisplayedTime","toggleActive","objectToRemoveClass","objectToAddClass","style","color","headerColor","headerSelected","doUpdateDisplayedTime","node","value","innerText","MinutesFace","items","initialMinutes","updateMinutes","displayed","type","Config","selected","undefined","findSelected","colorSelected","removeSelected","angle","minute","Math","round","clockItems","outerClockItems","isOuter","classList","add","background","handColor","remove","clockItemColor","indexOf","HoursFace","initialHours","updateHours","displayedInner","innerClockElem","display","isInnerClock","hoursIndex","innerClockItems","radius","elem","index","selectedAngle","isInner","clockItemInnerColor","Array","from","toRadians","PI","toDegrees","findMousePosition","event","object","rect","getBoundingClientRect","clientX","left","clientY","top","delay","t","Promise","resolve","fn","then","prototype","ClockFaceCreator","clockElem","size","middle","face","doCreate","span","item","i","inner","createElement","outer","push","appendChild","width","offsetWidth","height","offsetHeight","x","y","itemsRadius","innerWidth","innerHeight","middleX","middleY","doCalculateSize","fun","angleQuantum","length","Utils","itemWidth","itemHeight","sin","bottom","cos","ClockFace","initialTime","onTimeUpdate","isMouseDown","initViews","initTimeFaces","createFace","hoursFace","currentFace","changeDisplayed","innerId","handOfAClock","handId","onmousedown","onmouseup","toggleToMinutes","e","stopPropagation","onmousemove","selectTime","minutesFace","onEnter","clockFaceCreator","create","calculateSize","mouse","atan","array","forEach","call","c","calculateHandOfTheClock","transform","onLeave","toggleTime","onEachClockElement","fadeOut","Clock","initElements","submitButton","submitId","formatted","formatTime","onSubmit","dispose","cancelButton","cancelId","onCancel","header","toggleToHours","clockFace","onStart","body","removeChild","styleColors","headerId","headerBackground","wrapperId","wrapperBackground","clockBackground","dotId","buttonsId","footerBackground","submitColor","cancelColor","clockItem","changeColor","className","property","getElementsByClassName","showPicker","Object","assign","getTime","clock","createDom","Error","clockDiv","id","innerHTML","clockHtml"],"mappings":"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"} -------------------------------------------------------------------------------- /dist/grudus-timepicker.umd.js: -------------------------------------------------------------------------------- 1 | /*! grudus-timepicker | (c) 2017-2017 2 | grudus | Apache-2.0 license (see LICENSE) */ 3 | (function (global, factory) { 4 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 5 | typeof define === 'function' && define.amd ? define(factory) : 6 | (global.Timepicker = factory()); 7 | }(this, (function () { 'use strict'; 8 | 9 | var hoursRegex = /^([0-1]?[0-9]|2[0-3])$/; 10 | var minutesRegex = /^([0-5]?[0-9])$/; 11 | var regex = /^([0-1]?[0-9]|2[0-3]):([0-5][0-9])$/; 12 | 13 | function extractTime(date) { 14 | if (!date) return fromDate(new Date());else if (date instanceof Date) return fromDate(date);else if (hoursRegex.test(date.hours) && minutesRegex.test(date.minutes)) return { hours: parseInt(date.hours), minutes: parseInt(date.minutes) };else if (regex.test(date)) return fromRegex(date);else throw new TypeError("INVALID FORMAT: {" + JSON.stringify(date) + "}.\n Time must be a Date or 'hh:MM' string or object with 'hours' and 'minutes' fields"); 15 | } 16 | 17 | function fromRegex(date) { 18 | var parsed = regex.exec(date); 19 | return { hours: parseInt(parsed[1]), minutes: parseInt(parsed[2]) }; 20 | } 21 | 22 | function fromDate(date) { 23 | return { hours: date.getHours(), minutes: date.getMinutes() }; 24 | } 25 | 26 | var formatTime = function (time) { 27 | var extractedTime = extractTime(time); 28 | return (extractedTime.hours < 10 ? "0" + extractedTime.hours : extractedTime.hours) + ":" + (extractedTime.minutes < 10 ? "0" + extractedTime.minutes : extractedTime.minutes); 29 | }; 30 | 31 | var clockHtml = "
\n" + "
\n" + "
\n" + " 21\n" + " :\n" + " 37\n" + "
\n" + "
\n" + "\n" + "\n" + "
\n" + "
" + " \n" + "
\n" + "
\n" + "
\n" + "
\n" + "\n" + "\n" + "
\n" + " \n" + " \n" + "
\n" + "\n" + "
"; 32 | 33 | var clockId = "grudus-clock"; 34 | 35 | var defaultConfig = { 36 | onSubmit: function onSubmit() {}, 37 | onCancel: function onCancel() {}, 38 | headerBackground: "#1976D2", 39 | headerColor: "#c7d6e1", 40 | headerSelected: "#ffffff", 41 | wrapperBackground: "#f0fff0", 42 | footerBackground: "#f0fff0", 43 | submitColor: "#1976D2", 44 | cancelColor: "#1976D2", 45 | clockBackground: "#CFD8DC", 46 | clockItemColor: "#212121", 47 | clockItemInnerColor: "#212121", 48 | handColor: "#1976D2" 49 | }; 50 | 51 | var FaceType = { HOURS: "hours", MINUTES: "minutes" }; 52 | 53 | var css = { 54 | clock: "g-clock", 55 | clockItem: "g-clock-item", 56 | inner: "g-clock-inner", 57 | outer: "g-clock-outer", 58 | item: "g-clock-item", 59 | hand: "g-hand-of-a-clock", 60 | fadeOut: "g-fade-out", 61 | selected: "g-selected", 62 | active: "g-active", 63 | submit: "g-submit", 64 | cancel: "g-cancel", 65 | hour: "g-hour", 66 | minute: "g-minute" 67 | }; 68 | 69 | var DOM = { 70 | headerId: "g-head", 71 | hoursId: "g-hours", 72 | minutesId: "g-minutes", 73 | clockId: "g-clock", 74 | innerId: "g-clock-inner", 75 | wrapperId: "g-clock-wrapper", 76 | dotId: "g-middle-dot", 77 | handId: "g-hand-of-a-clock", 78 | buttonsId: "g-buttons", 79 | submitId: "g-time-submit", 80 | cancelId: "g-time-cancel" 81 | }; 82 | 83 | var Config = { clockId: clockId, clockConfig: defaultConfig, FaceType: FaceType }; 84 | 85 | var classCallCheck = function (instance, Constructor) { 86 | if (!(instance instanceof Constructor)) { 87 | throw new TypeError("Cannot call a class as a function"); 88 | } 89 | }; 90 | 91 | var createClass = function () { 92 | function defineProperties(target, props) { 93 | for (var i = 0; i < props.length; i++) { 94 | var descriptor = props[i]; 95 | descriptor.enumerable = descriptor.enumerable || false; 96 | descriptor.configurable = true; 97 | if ("value" in descriptor) descriptor.writable = true; 98 | Object.defineProperty(target, descriptor.key, descriptor); 99 | } 100 | } 101 | 102 | return function (Constructor, protoProps, staticProps) { 103 | if (protoProps) defineProperties(Constructor.prototype, protoProps); 104 | if (staticProps) defineProperties(Constructor, staticProps); 105 | return Constructor; 106 | }; 107 | }(); 108 | 109 | var ClockHeader = function () { 110 | function ClockHeader(config) { 111 | classCallCheck(this, ClockHeader); 112 | 113 | this.options = config.options; 114 | this.time = config.time; 115 | this.onHourClicked = config.onHourClicked; 116 | this.onMinutesClicked = config.onMinutesClicked; 117 | 118 | this.initView(); 119 | } 120 | 121 | createClass(ClockHeader, [{ 122 | key: "initView", 123 | value: function initView() { 124 | var _this = this; 125 | 126 | this.headerHours = document.getElementById(DOM.hoursId); 127 | this.headerHours.onclick = function () { 128 | _this.toggleActiveToHours(); 129 | _this.onHourClicked(); 130 | }; 131 | 132 | this.headerMinutes = document.getElementById(DOM.minutesId); 133 | this.headerMinutes.onclick = function () { 134 | _this.toggleActiveToMinutes(); 135 | _this.onMinutesClicked(); 136 | }; 137 | 138 | this.updateDisplayedTime(); 139 | this.toggleActiveToHours(); 140 | } 141 | }, { 142 | key: "toggleActiveToMinutes", 143 | value: function toggleActiveToMinutes() { 144 | this.toggleActive(this.headerHours, this.headerMinutes); 145 | } 146 | }, { 147 | key: "toggleActiveToHours", 148 | value: function toggleActiveToHours() { 149 | this.toggleActive(this.headerMinutes, this.headerHours); 150 | } 151 | }, { 152 | key: "toggleActive", 153 | value: function toggleActive(objectToRemoveClass, objectToAddClass) { 154 | objectToRemoveClass.style.color = this.options.headerColor; 155 | objectToAddClass.style.color = this.options.headerSelected; 156 | } 157 | }, { 158 | key: "updateDisplayedTime", 159 | value: function updateDisplayedTime() { 160 | ClockHeader.doUpdateDisplayedTime(this.headerHours, this.time.hours); 161 | ClockHeader.doUpdateDisplayedTime(this.headerMinutes, this.time.minutes); 162 | } 163 | }], [{ 164 | key: "doUpdateDisplayedTime", 165 | value: function doUpdateDisplayedTime(node, value) { 166 | if (value < 10) node.innerText = "0" + value;else node.innerText = value; 167 | } 168 | }]); 169 | return ClockHeader; 170 | }(); 171 | 172 | var MinutesFace = function () { 173 | function MinutesFace(items, initialMinutes, updateMinutes) { 174 | classCallCheck(this, MinutesFace); 175 | 176 | this.displayed = ["00", "05", "10", "15", "20", "25", "30", "35", "40", "45", "50", "55"]; 177 | this.options = items.options; 178 | this.type = Config.FaceType.MINUTES; 179 | this.selected = undefined; 180 | this.items = items; 181 | this.minutes = initialMinutes; 182 | this.updateMinutes = updateMinutes; 183 | } 184 | 185 | createClass(MinutesFace, [{ 186 | key: "onEnter", 187 | value: function onEnter() { 188 | this.selected = this.findSelected(this.minutes); 189 | this.colorSelected(); 190 | this.updateMinutes(this.minutes, this.minutes * 6); 191 | } 192 | }, { 193 | key: "onLeave", 194 | value: function onLeave() { 195 | if (this.selected) { 196 | this.removeSelected(); 197 | this.selected = undefined; 198 | } 199 | } 200 | }, { 201 | key: "selectTime", 202 | value: function selectTime(angle) { 203 | if (this.selected) this.removeSelected(); 204 | 205 | var minute = Math.round(angle / 6) % 60; 206 | this.selected = this.findSelected(minute); 207 | this.colorSelected(); 208 | this.minutes = minute; 209 | this.updateMinutes(this.minutes, angle); 210 | } 211 | }, { 212 | key: "findSelected", 213 | value: function findSelected(minute) { 214 | return minute % 5 === 0 ? this.items.clockItems[minute / 5] : this.items.outerClockItems[minute]; 215 | } 216 | }, { 217 | key: "colorSelected", 218 | value: function colorSelected() { 219 | if (this.isOuter()) { 220 | this.selected.classList.add(css.selected); 221 | return; 222 | } 223 | this.selected.style.background = this.options.handColor; 224 | this.selected.style.color = "whitesmoke"; 225 | } 226 | }, { 227 | key: "removeSelected", 228 | value: function removeSelected() { 229 | if (this.isOuter()) { 230 | this.selected.classList.remove(css.selected); 231 | return; 232 | } 233 | this.selected.style.background = "transparent"; 234 | this.selected.style.color = this.options.clockItemColor; 235 | } 236 | }, { 237 | key: "isOuter", 238 | value: function isOuter() { 239 | return this.items.outerClockItems.indexOf(this.selected) > -1; 240 | } 241 | }]); 242 | return MinutesFace; 243 | }(); 244 | 245 | var HoursFace = function () { 246 | function HoursFace(items, initialHours, updateHours) { 247 | classCallCheck(this, HoursFace); 248 | 249 | this.displayed = ["12", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"]; 250 | this.displayedInner = ["00", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23"]; 251 | this.type = Config.FaceType.MINUTES; 252 | this.selected = undefined; 253 | this.options = items.options; 254 | 255 | this.items = items; 256 | this.hours = initialHours; 257 | this.updateHours = updateHours; 258 | } 259 | 260 | createClass(HoursFace, [{ 261 | key: "onEnter", 262 | value: function onEnter() { 263 | this.items.innerClockElem.style.display = "block"; 264 | var isInnerClock = this.hours < 13 && this.hours !== 0; 265 | var hoursIndex = this.hours % 12; 266 | this.selected = isInnerClock ? this.items.clockItems[hoursIndex] : this.items.innerClockItems[hoursIndex]; 267 | this.colorSelected(); 268 | 269 | this.updateHours(this.hours, hoursIndex * 30, isInnerClock ? this.items.radius : this.items.radius - 50); 270 | } 271 | }, { 272 | key: "onLeave", 273 | value: function onLeave() { 274 | this.items.innerClockElem.style.display = "none"; 275 | if (this.selected) { 276 | this.removeSelected(); 277 | this.selected = undefined; 278 | } 279 | } 280 | }, { 281 | key: "selectTime", 282 | value: function selectTime(angle, elem) { 283 | if (this.selected) this.removeSelected(); 284 | 285 | var index = Math.round(angle / 30) % 12; 286 | this.selected = (elem === this.items.innerClockElem ? this.items.innerClockItems : this.items.clockItems)[index]; 287 | 288 | this.colorSelected(); 289 | this.hours = parseInt(this.selected.innerText); 290 | var selectedAngle = Math.round(angle / 30) * 30; 291 | 292 | this.updateHours(this.hours, selectedAngle, elem === this.items.innerClockElem ? this.items.radius - 50 : this.items.radius); 293 | } 294 | }, { 295 | key: "colorSelected", 296 | value: function colorSelected() { 297 | this.selected.style.background = this.options.handColor; 298 | this.selected.style.color = "#ffffff"; 299 | } 300 | }, { 301 | key: "removeSelected", 302 | value: function removeSelected() { 303 | this.selected.style.background = "transparent"; 304 | this.selected.style.color = this.isInner() ? this.options.clockItemInnerColor : this.options.clockItemColor; 305 | } 306 | }, { 307 | key: "isInner", 308 | value: function isInner() { 309 | return Array.from(this.items.innerClockItems).indexOf(this.selected) > -1; 310 | } 311 | }]); 312 | return HoursFace; 313 | }(); 314 | 315 | function toRadians(angle) { 316 | return angle * (Math.PI / 180); 317 | } 318 | 319 | function toDegrees(angle) { 320 | return angle * (180 / Math.PI); 321 | } 322 | 323 | function findMousePosition(event, object) { 324 | var rect = object.getBoundingClientRect(); 325 | return { 326 | x: event.clientX - rect.left, 327 | y: event.clientY - rect.top 328 | }; 329 | } 330 | 331 | function delay(t) { 332 | return new Promise(function (resolve) { 333 | setTimeout(resolve, t); 334 | }); 335 | } 336 | 337 | Promise.delay = function (fn, t) { 338 | if (!t) { 339 | t = fn; 340 | fn = function fn() {}; 341 | } 342 | return delay(t).then(fn); 343 | }; 344 | 345 | Promise.prototype.delay = function (fn, t) { 346 | return this.then(function () { 347 | return Promise.delay(fn, t); 348 | }); 349 | }; 350 | 351 | var Utils = { toRadians: toRadians, toDegrees: toDegrees, findMousePosition: findMousePosition }; 352 | 353 | var ClockFaceCreator = function () { 354 | function ClockFaceCreator(clockElem, innerClockElem) { 355 | classCallCheck(this, ClockFaceCreator); 356 | 357 | this.clockElem = clockElem; 358 | this.innerClockElem = innerClockElem; 359 | this.size = {}; 360 | this.middle = {}; 361 | } 362 | 363 | createClass(ClockFaceCreator, [{ 364 | key: "create", 365 | value: function create(clockItems, innerClockItems, outerClockItems, face) { 366 | ClockFaceCreator.doCreate(clockItems, this.clockElem, function (span) { 367 | return span.classList.add(css.item); 368 | }); 369 | ClockFaceCreator.doCreate(innerClockItems, this.innerClockElem, function (span, i) { 370 | span.classList.add(css.item, css.inner); 371 | span.innerText = face.displayedInner[i]; 372 | }); 373 | 374 | for (var i = 0; i < 60; i++) { 375 | var span = document.createElement("span"); 376 | span.classList.add(css.outer); 377 | outerClockItems.push(span); 378 | this.clockElem.appendChild(span); 379 | } 380 | } 381 | }, { 382 | key: "calculateSize", 383 | value: function calculateSize(clockItems, innerClockItems, outerClockItems) { 384 | this.size.width = this.clockElem.offsetWidth; 385 | this.size.height = this.clockElem.offsetHeight; 386 | this.middle.x = this.size.width / 2; 387 | this.middle.y = this.size.height / 2; 388 | this.itemsRadius = this.size.width / 2 - 20; 389 | 390 | var innerWidth = this.innerClockElem.offsetWidth; 391 | var innerHeight = this.innerClockElem.offsetHeight; 392 | var middleX = innerWidth / 2; 393 | var middleY = innerHeight / 2; 394 | 395 | ClockFaceCreator.doCalculateSize(this.middle.x, this.middle.y, this.itemsRadius, clockItems); 396 | ClockFaceCreator.doCalculateSize(middleX, middleY, this.itemsRadius - 40, innerClockItems); 397 | ClockFaceCreator.doCalculateSize(this.middle.x, this.middle.y, this.itemsRadius, outerClockItems); 398 | } 399 | }], [{ 400 | key: "doCreate", 401 | value: function doCreate(clockItems, clockElem, fun) { 402 | for (var i = 0; i < 12; i++) { 403 | var span = document.createElement("span"); 404 | fun(span, i); 405 | clockItems.push(span); 406 | clockElem.appendChild(span); 407 | } 408 | } 409 | }, { 410 | key: "doCalculateSize", 411 | value: function doCalculateSize(middleX, middleY, radius, items) { 412 | var angleQuantum = 360 / items.length; 413 | for (var i = 0; i < items.length; i++) { 414 | 415 | var angle = Utils.toRadians(i * angleQuantum); 416 | var item = items[i]; 417 | var itemWidth = item.offsetWidth; 418 | var itemHeight = item.offsetHeight; 419 | 420 | item.style.left = middleX + Math.sin(angle) * radius - itemWidth / 2 + "px"; 421 | item.style.bottom = middleY + Math.cos(angle) * radius - itemHeight / 2 + "px"; 422 | } 423 | } 424 | }]); 425 | return ClockFaceCreator; 426 | }(); 427 | 428 | var ClockFace = function () { 429 | function ClockFace(options, initialTime, onTimeUpdate) { 430 | classCallCheck(this, ClockFace); 431 | 432 | this.options = options; 433 | this.time = initialTime; 434 | this.onTimeUpdate = onTimeUpdate; 435 | this.isMouseDown = false; 436 | this.clockItems = []; 437 | this.innerClockItems = []; 438 | this.outerClockItems = []; 439 | this.size = {}; 440 | this.middle = {}; 441 | 442 | this.initViews(); 443 | this.initTimeFaces(initialTime); 444 | this.createFace(); 445 | 446 | this.hoursFace.items.radius = this.itemsRadius; 447 | 448 | this.currentFace = this.hoursFace; 449 | this.changeDisplayed(this.currentFace.displayed); 450 | } 451 | 452 | createClass(ClockFace, [{ 453 | key: "initViews", 454 | value: function initViews() { 455 | var _this = this; 456 | 457 | this.clockElem = document.getElementById(DOM.clockId); 458 | this.innerClockElem = document.getElementById(DOM.innerId); 459 | this.handOfAClock = document.getElementById(DOM.handId); 460 | 461 | this.clockElem.onmousedown = function () { 462 | return _this.isMouseDown = true; 463 | }; 464 | this.clockElem.onmouseup = function () { 465 | _this.isMouseDown = false; 466 | _this.toggleToMinutes(); 467 | }; 468 | 469 | this.handOfAClock.onmouseup = function (e) { 470 | return e.stopPropagation(); 471 | }; 472 | this.handOfAClock.onmousemove = function (e) { 473 | return e.stopPropagation(); 474 | }; 475 | this.handOfAClock.onclick = function (e) { 476 | return e.stopPropagation(); 477 | }; 478 | 479 | this.clockElem.onmousemove = function (e) { 480 | return _this.selectTime(e, false, _this.clockElem); 481 | }; 482 | this.clockElem.onclick = function (e) { 483 | return _this.selectTime(e, true, _this.clockElem); 484 | }; 485 | 486 | this.innerClockElem.onmousemove = function (e) { 487 | return _this.selectTime(e, false, _this.innerClockElem); 488 | }; 489 | this.innerClockElem.onclick = function (e) { 490 | return _this.selectTime(e, true, _this.innerClockElem); 491 | }; 492 | } 493 | }, { 494 | key: "initTimeFaces", 495 | value: function initTimeFaces(initialTime) { 496 | var _this2 = this; 497 | 498 | this.minutesFace = new MinutesFace({ 499 | options: this.options, 500 | clockItems: this.clockItems, 501 | outerClockItems: this.outerClockItems 502 | }, initialTime.minutes, function (minutes, angle) { 503 | return _this2.updateMinutes(minutes, angle); 504 | }); 505 | 506 | this.hoursFace = new HoursFace({ 507 | options: this.options, 508 | innerClockItems: this.innerClockItems, 509 | clockItems: this.clockItems, 510 | innerClockElem: this.innerClockElem 511 | }, initialTime.hours, function (hours, angle, radius) { 512 | return _this2.updateHours(hours, angle, radius); 513 | }); 514 | } 515 | }, { 516 | key: "onStart", 517 | value: function onStart() { 518 | this.currentFace.onEnter(); 519 | } 520 | }, { 521 | key: "createFace", 522 | value: function createFace() { 523 | var clockFaceCreator = new ClockFaceCreator(this.clockElem, this.innerClockElem); 524 | clockFaceCreator.create(this.clockItems, this.innerClockItems, this.outerClockItems, this.hoursFace); 525 | clockFaceCreator.calculateSize(this.clockItems, this.innerClockItems, this.outerClockItems); 526 | 527 | this.size = clockFaceCreator.size; 528 | this.middle = clockFaceCreator.middle; 529 | this.itemsRadius = clockFaceCreator.itemsRadius; 530 | } 531 | }, { 532 | key: "selectTime", 533 | value: function selectTime(event, isMouseDown, elem) { 534 | if (!(isMouseDown || this.isMouseDown)) return; 535 | var mouse = Utils.findMousePosition(event, this.clockElem); 536 | var x = mouse.x - this.middle.x; 537 | var y = this.middle.y - mouse.y; 538 | var angle = 90 - Utils.toDegrees(Math.atan(y / x)); 539 | if (x < 0) angle += 180; 540 | 541 | this.currentFace.selectTime(angle, elem); 542 | event.stopPropagation(); 543 | } 544 | }, { 545 | key: "changeDisplayed", 546 | value: function changeDisplayed(array) { 547 | for (var i = 0; i < this.clockItems.length; i++) { 548 | this.clockItems[i].innerText = array[i]; 549 | } 550 | } 551 | }, { 552 | key: "onEachClockElement", 553 | value: function onEachClockElement(fun) { 554 | [].forEach.call(this.clockItems, function (c) { 555 | return fun(c); 556 | }); 557 | } 558 | }, { 559 | key: "updateMinutes", 560 | value: function updateMinutes(minutes, angle) { 561 | this.time.minutes = minutes; 562 | this.calculateHandOfTheClock(angle, this.itemsRadius); 563 | this.onTimeUpdate(this.time, Config.FaceType.MINUTES); 564 | } 565 | }, { 566 | key: "updateHours", 567 | value: function updateHours(hours, angle, radius) { 568 | this.time.hours = hours; 569 | this.calculateHandOfTheClock(angle, radius); 570 | this.onTimeUpdate(this.time, Config.FaceType.HOURS); 571 | } 572 | }, { 573 | key: "calculateHandOfTheClock", 574 | value: function calculateHandOfTheClock(angle) { 575 | var size = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.itemsRadius; 576 | 577 | this.handOfAClock.style.transform = "rotate(" + (angle - 90) + "deg)"; 578 | this.handOfAClock.style.width = size + "px"; 579 | } 580 | }, { 581 | key: "toggleToHours", 582 | value: function toggleToHours() { 583 | this.minutesFace.onLeave(); 584 | this.toggleTime(this.hoursFace); 585 | } 586 | }, { 587 | key: "toggleToMinutes", 588 | value: function toggleToMinutes() { 589 | this.hoursFace.onLeave(); 590 | this.toggleTime(this.minutesFace); 591 | } 592 | }, { 593 | key: "toggleTime", 594 | value: function toggleTime(face) { 595 | var _this3 = this; 596 | 597 | if (this.currentFace !== face) { 598 | this.onEachClockElement(function (c) { 599 | return c.classList.add(css.fadeOut); 600 | }); 601 | this.handOfAClock.classList.add(css.fadeOut); 602 | Promise.delay(function () { 603 | _this3.onEachClockElement(function (c) { 604 | return c.classList.remove(css.fadeOut); 605 | }); 606 | _this3.handOfAClock.classList.remove(css.fadeOut); 607 | _this3.changeDisplayed(face.displayed); 608 | _this3.currentFace = face; 609 | _this3.onEachClockElement(function (c) { 610 | return _this3.removeSelected(c); 611 | }); 612 | face.onEnter(); 613 | }, 300); 614 | } 615 | } 616 | }, { 617 | key: "removeSelected", 618 | value: function removeSelected(c) { 619 | c.classList.remove(css.selected); 620 | c.style.background = "transparent"; 621 | c.style.color = this.options.clockItemColor; 622 | } 623 | }]); 624 | return ClockFace; 625 | }(); 626 | 627 | var Clock = function () { 628 | function Clock(options, time) { 629 | classCallCheck(this, Clock); 630 | 631 | this.options = options; 632 | 633 | this.initView(); 634 | this.time = time; 635 | this.initElements(); 636 | } 637 | 638 | createClass(Clock, [{ 639 | key: "initView", 640 | value: function initView() { 641 | var _this = this; 642 | 643 | this.submitButton = document.getElementById(DOM.submitId); 644 | this.submitButton.onclick = function () { 645 | var time = _this.time; 646 | time.formatted = function () { 647 | return formatTime(time); 648 | }; 649 | _this.options.onSubmit(time); 650 | Clock.dispose(); 651 | }; 652 | 653 | this.cancelButton = document.getElementById(DOM.cancelId); 654 | this.cancelButton.onclick = function () { 655 | _this.options.onCancel(); 656 | Clock.dispose(); 657 | }; 658 | } 659 | }, { 660 | key: "initElements", 661 | value: function initElements() { 662 | var _this2 = this; 663 | 664 | this.header = new ClockHeader({ 665 | options: this.options, 666 | time: this.time, 667 | onHourClicked: function onHourClicked() { 668 | return _this2.toggleToHours(); 669 | }, 670 | onMinutesClicked: function onMinutesClicked() { 671 | return _this2.toggleToMinutes(); 672 | } 673 | }); 674 | this.clockFace = new ClockFace(this.options, this.time, function (time, type) { 675 | return _this2.onTimeUpdate(time, type); 676 | }); 677 | } 678 | }, { 679 | key: "onStart", 680 | value: function onStart() { 681 | this.clockFace.onStart(); 682 | } 683 | }, { 684 | key: "toggleToHours", 685 | value: function toggleToHours() { 686 | this.clockFace.toggleToHours(); 687 | } 688 | }, { 689 | key: "toggleToMinutes", 690 | value: function toggleToMinutes() { 691 | this.clockFace.toggleToMinutes(); 692 | } 693 | }, { 694 | key: "onTimeUpdate", 695 | value: function onTimeUpdate(time, type) { 696 | this.time = time; 697 | this.header.time = time; 698 | this.header.updateDisplayedTime(); 699 | if (type === Config.FaceType.MINUTES) this.header.toggleActiveToMinutes(); 700 | } 701 | }], [{ 702 | key: "dispose", 703 | value: function dispose() { 704 | document.body.removeChild(document.getElementById(Config.clockId)); 705 | } 706 | }]); 707 | return Clock; 708 | }(); 709 | 710 | function styleColors(options) { 711 | document.getElementById(DOM.headerId).style.background = options.headerBackground; 712 | document.getElementById(DOM.headerId).style.color = options.headerColor; 713 | document.getElementById(DOM.wrapperId).style.background = options.wrapperBackground; 714 | document.getElementById(DOM.clockId).style.background = options.clockBackground; 715 | document.getElementById(DOM.handId).style.background = options.handColor; 716 | document.getElementById(DOM.dotId).style.background = options.handColor; 717 | document.getElementById(DOM.buttonsId).style.background = options.footerBackground; 718 | document.getElementById(DOM.submitId).style.color = options.submitColor; 719 | document.getElementById(DOM.cancelId).style.color = options.cancelColor; 720 | 721 | changeColor(css.clockItem, options.clockItemColor); 722 | changeColor(css.inner, options.clockItemInnerColor); 723 | changeColor(css.outer, options.handColor, "borderColor"); 724 | } 725 | 726 | function changeColor(className, color) { 727 | var property = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "color"; 728 | 729 | var items = Array.from(document.getElementsByClassName(className)); 730 | var _iteratorNormalCompletion = true; 731 | var _didIteratorError = false; 732 | var _iteratorError = undefined; 733 | 734 | try { 735 | for (var _iterator = items[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { 736 | var item = _step.value; 737 | 738 | item.style[property] = color; 739 | } 740 | } catch (err) { 741 | _didIteratorError = true; 742 | _iteratorError = err; 743 | } finally { 744 | try { 745 | if (!_iteratorNormalCompletion && _iterator.return) { 746 | _iterator.return(); 747 | } 748 | } finally { 749 | if (_didIteratorError) { 750 | throw _iteratorError; 751 | } 752 | } 753 | } 754 | } 755 | 756 | function showPicker() { 757 | var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 758 | 759 | createDom(); 760 | 761 | var options = Object.assign({}, Config.clockConfig, config); 762 | var time = extractTime(options.time); 763 | 764 | var clock = new Clock(options, time); 765 | styleColors(options); 766 | clock.onStart(); 767 | } 768 | 769 | function createDom() { 770 | if (document.getElementById(Config.clockId)) throw Error("There is already one running grudus-timepicker instance!"); 771 | 772 | var clockDiv = document.createElement("div"); 773 | clockDiv.id = Config.clockId; 774 | clockDiv.innerHTML = clockHtml; 775 | document.body.appendChild(clockDiv); 776 | } 777 | 778 | var index = { 779 | showPicker: function showPicker$$1(config) { 780 | return showPicker(config); 781 | }, 782 | format: function format(time) { 783 | return formatTime(time); 784 | } 785 | }; 786 | 787 | return index; 788 | 789 | }))); 790 | //# sourceMappingURL=grudus-timepicker.umd.js.map 791 | -------------------------------------------------------------------------------- /dist/index.css: -------------------------------------------------------------------------------- 1 | @-webkit-keyframes fade-in { 2 | from { 3 | opacity: 0; 4 | } 5 | to { 6 | opacity: 1; 7 | } 8 | } 9 | 10 | @keyframes fade-in { 11 | from { 12 | opacity: 0; 13 | } 14 | to { 15 | opacity: 1; 16 | } 17 | } 18 | 19 | @-webkit-keyframes fade-out { 20 | from { 21 | opacity: 1; 22 | } 23 | to { 24 | opacity: 0; 25 | } 26 | } 27 | 28 | @keyframes fade-out { 29 | from { 30 | opacity: 1; 31 | } 32 | to { 33 | opacity: 0; 34 | } 35 | } 36 | 37 | .g-time-wrapper { 38 | -webkit-box-sizing: content-box; 39 | box-sizing: content-box; 40 | min-width: 300px; 41 | min-height: 450px; 42 | position: fixed; 43 | top: 50%; 44 | left: 50%; 45 | -webkit-transform: translate(-50%, -50%); 46 | transform: translate(-50%, -50%); 47 | font-family: 'Roboto', sans-serif; 48 | 49 | display: -webkit-box; 50 | 51 | display: -ms-flexbox; 52 | 53 | display: flex; 54 | -webkit-box-orient: vertical; 55 | -webkit-box-direction: normal; 56 | -ms-flex-direction: column; 57 | flex-direction: column; 58 | -webkit-box-pack: start; 59 | -ms-flex-pack: start; 60 | justify-content: flex-start; 61 | } 62 | 63 | .g-flex { 64 | -webkit-box-flex: 1; 65 | -ms-flex-positive: 1; 66 | flex-grow: 1; 67 | } 68 | 69 | .g-head { 70 | -webkit-box-flex: 1; 71 | -ms-flex-positive: 1; 72 | flex-grow: 1; 73 | display: -webkit-box; 74 | display: -ms-flexbox; 75 | display: flex; 76 | -webkit-box-pack: center; 77 | -ms-flex-pack: center; 78 | justify-content: center; 79 | -webkit-box-align: center; 80 | -ms-flex-align: center; 81 | align-items: center; 82 | } 83 | 84 | .g-head-content { 85 | font-size: 3em; 86 | display: -webkit-box; 87 | display: -ms-flexbox; 88 | display: flex; 89 | } 90 | 91 | .g-current { 92 | margin: 0 2px; 93 | } 94 | 95 | .g-clock-wrapper { 96 | -webkit-box-flex: 4; 97 | -ms-flex-positive: 4; 98 | flex-grow: 4; 99 | display: -webkit-box; 100 | display: -ms-flexbox; 101 | display: flex; 102 | -webkit-box-align: center; 103 | -ms-flex-align: center; 104 | align-items: center; 105 | -webkit-box-pack: center; 106 | -ms-flex-pack: center; 107 | justify-content: center; 108 | } 109 | 110 | .g-clock { 111 | position: relative; 112 | border-radius: 100%; 113 | height: 240px; 114 | width: 240px; 115 | cursor: default; 116 | } 117 | 118 | .g-clock.g-clock-inner { 119 | top: 50%; 120 | left: 50%; 121 | -webkit-transform: translate(-50%, -50%); 122 | transform: translate(-50%, -50%); 123 | height: 160px; 124 | width: 160px; 125 | } 126 | 127 | .g-clock-item { 128 | position: absolute; 129 | border-radius: 100%; 130 | width: 20px; 131 | height: 20px; 132 | text-align: center; 133 | padding: 10px; 134 | -webkit-user-select: none; 135 | -moz-user-select: none; 136 | -ms-user-select: none; 137 | user-select: none; 138 | cursor: default; 139 | font-size: 1.1em; 140 | z-index: 3; 141 | } 142 | 143 | .g-clock-item.g-clock-inner { 144 | font-size: 0.9em; 145 | z-index: 3; 146 | } 147 | 148 | 149 | .g-clock-outer { 150 | position: absolute; 151 | width: 4px; 152 | height: 4px; 153 | border: 18px solid #1976D2; 154 | background: whitesmoke; 155 | color: whitesmoke; 156 | border-radius: 100%; 157 | visibility: hidden; 158 | z-index: 2; 159 | } 160 | 161 | .g-clock-outer.g-selected { 162 | visibility: visible; 163 | } 164 | 165 | .g-middle-dot { 166 | z-index: 2; 167 | position: absolute; 168 | width: 1px; 169 | height: 1px; 170 | padding: 7px; 171 | border-radius: 100%; 172 | top: 50%; 173 | left: 50%; 174 | -webkit-transform: translate(-50%, -50%); 175 | transform: translate(-50%, -50%); 176 | } 177 | 178 | .g-hand-of-a-clock { 179 | position: absolute; 180 | z-index: 2; 181 | top: 50%; 182 | left: 50%; 183 | -webkit-transform: translateY(-50%); 184 | transform: translateY(-50%); 185 | -webkit-transform-origin: left center; 186 | transform-origin: left center; 187 | height: 3px; 188 | width: 20px; 189 | } 190 | 191 | .g-buttons { 192 | display: -webkit-box; 193 | display: -ms-flexbox; 194 | display: flex; 195 | -webkit-box-pack: end; 196 | -ms-flex-pack: end; 197 | justify-content: flex-end; 198 | -webkit-box-align: center; 199 | -ms-flex-align: center; 200 | align-items: center; 201 | -webkit-box-flex: 0.5; 202 | -ms-flex-positive: 0.5; 203 | flex-grow: 0.5; 204 | 205 | } 206 | 207 | .g-button { 208 | font-weight: 600; 209 | border: none; 210 | background: transparent; 211 | margin-right: 16px; 212 | padding: 8px; 213 | } 214 | 215 | .g-button:hover { 216 | cursor: pointer; 217 | background: #CFD8DC; 218 | } 219 | 220 | .g-fade-in { 221 | -webkit-animation: fade-in 0.4s; 222 | animation: fade-in 0.4s; 223 | } 224 | 225 | .g-fade-out { 226 | -webkit-animation: fade-out 0.4s; 227 | animation: fade-out 0.4s; 228 | } 229 | 230 | .g-pointer:hover { 231 | cursor: pointer; 232 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grudus-timepicker", 3 | "version": "1.0.2", 4 | "description": "Material design time picker in pure Javascript without any dependencies", 5 | "main": "dist/grudus-timepicker.es5.js", 6 | "jsnext:main": "dist/grudus-timepicker.js", 7 | "scripts": { 8 | "prebuild": "eslint src/**/*.js && npm run css", 9 | "build": "rollup -c rollup/es5.js && rollup -c rollup/umd.js && rollup -c rollup/es6.js", 10 | "patch": "npm version patch && npm publish && git push --tags && git push", 11 | "dev": "rollup -w -c rollup/es5.js", 12 | "fix": "eslint src/**/*.js --fix", 13 | "css": "postcss src/**/*.css --use autoprefixer --no-map -d dist/" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/grudus/Timepicker.git" 18 | }, 19 | "devDependencies": { 20 | "autoprefixer": "^7.1.2", 21 | "babel-plugin-external-helpers": "^6.22.0", 22 | "babel-preset-es2015": "^6.24.1", 23 | "eslint": "^4.4.0", 24 | "postcss-cli": "^4.1.0", 25 | "rollup": "^0.45.2", 26 | "rollup-plugin-babel": "^2.7.1", 27 | "rollup-plugin-commonjs": "^8.1.0", 28 | "rollup-plugin-node-resolve": "^3.0.0", 29 | "rollup-plugin-uglify": "^2.0.1", 30 | "rollup-watch": "^4.3.1", 31 | "uglify-js": "^3.0.27" 32 | }, 33 | "keywords": [ 34 | "vanillajs", 35 | "javascript", 36 | "js", 37 | "materialdesign", 38 | "material", 39 | "design", 40 | "timepicker" 41 | ], 42 | "author": "grudus", 43 | "license": "Apache-2.0", 44 | "bugs": { 45 | "url": "https://github.com/grudus/Timepicker/issues" 46 | }, 47 | "homepage": "https://github.com/grudus/Timepicker#readme" 48 | } 49 | -------------------------------------------------------------------------------- /rollup/es5.js: -------------------------------------------------------------------------------- 1 | import nodeResolve from "rollup-plugin-node-resolve"; 2 | import convertCJS from "rollup-plugin-commonjs"; 3 | import babel from "rollup-plugin-babel"; 4 | import uglify from "rollup-plugin-uglify"; 5 | 6 | const packageInfo = require("../package.json"); 7 | const banner = `/*! ${packageInfo.name} | (c) 2017-${new Date().getFullYear()} 8 | ${packageInfo.author} | ${packageInfo.license} license (see LICENSE) */`; 9 | 10 | 11 | export default { 12 | entry: "src/js/index.js", 13 | format: "umd", 14 | moduleName: "Timepicker", 15 | sourceMap: true, 16 | plugins: [ 17 | nodeResolve({ 18 | jsnext: true, 19 | main: true, 20 | browser: true, 21 | }), 22 | convertCJS(), 23 | babel(), 24 | uglify({ 25 | output: { 26 | preamble: banner 27 | } 28 | }) 29 | ], 30 | dest: `dist/${packageInfo.name}.es5.js` 31 | }; -------------------------------------------------------------------------------- /rollup/es6.js: -------------------------------------------------------------------------------- 1 | import nodeResolve from "rollup-plugin-node-resolve"; 2 | import convertCJS from "rollup-plugin-commonjs"; 3 | import babel from "rollup-plugin-babel"; 4 | 5 | const packageInfo = require("../package.json"); 6 | const banner = `/*! ${packageInfo.name} | (c) 2017-${new Date().getFullYear()} 7 | ${packageInfo.author} | ${packageInfo.license} license (see LICENSE) */`; 8 | 9 | export default { 10 | entry: "src/js/index.js", 11 | format: "es", 12 | sourceMap: true, 13 | plugins: [ 14 | nodeResolve({ 15 | jsnext: true, 16 | main: false 17 | }), 18 | convertCJS(), 19 | babel() 20 | ], 21 | banner, 22 | dest: `dist/${packageInfo.name}.js` 23 | }; -------------------------------------------------------------------------------- /rollup/umd.js: -------------------------------------------------------------------------------- 1 | import nodeResolve from "rollup-plugin-node-resolve"; 2 | import convertCJS from "rollup-plugin-commonjs"; 3 | import babel from "rollup-plugin-babel"; 4 | 5 | 6 | const packageInfo = require("../package.json"); 7 | const banner = `/*! ${packageInfo.name} | (c) 2017-${new Date().getFullYear()} 8 | ${packageInfo.author} | ${packageInfo.license} license (see LICENSE) */`; 9 | 10 | export default { 11 | entry: "src/js/index.js", 12 | format: "umd", 13 | moduleName: "Timepicker", 14 | sourceMap: true, 15 | plugins: [ 16 | nodeResolve({ 17 | jsnext: true, 18 | main: false 19 | }), 20 | convertCJS(), 21 | babel(), 22 | ], 23 | banner, 24 | dest: `dist/${packageInfo.name}.umd.js` 25 | }; -------------------------------------------------------------------------------- /src/js/clock.js: -------------------------------------------------------------------------------- 1 | import ClockHeader from "./clockHeader"; 2 | import ClockFace from "./face/clockFace"; 3 | import Config, {DOM} from "./meta/config"; 4 | import formatTime from "./timeFormatter"; 5 | 6 | export default class Clock { 7 | 8 | constructor(options, time) { 9 | this.options = options; 10 | 11 | this.initView(); 12 | this.time = time; 13 | this.initElements(); 14 | } 15 | 16 | initView() { 17 | this.submitButton = document.getElementById(DOM.submitId); 18 | this.submitButton.onclick = () => { 19 | const time = this.time; 20 | time.formatted = () => formatTime(time); 21 | this.options.onSubmit(time); 22 | Clock.dispose(); 23 | }; 24 | 25 | this.cancelButton = document.getElementById(DOM.cancelId); 26 | this.cancelButton.onclick = () => { 27 | this.options.onCancel(); 28 | Clock.dispose(); 29 | }; 30 | } 31 | 32 | initElements() { 33 | this.header = new ClockHeader({ 34 | options: this.options, 35 | time: this.time, 36 | onHourClicked: () => this.toggleToHours(), 37 | onMinutesClicked: () => this.toggleToMinutes() 38 | }); 39 | this.clockFace = new ClockFace(this.options, this.time, (time, type) => this.onTimeUpdate(time, type)); 40 | } 41 | 42 | onStart() { 43 | this.clockFace.onStart(); 44 | } 45 | 46 | toggleToHours() { 47 | this.clockFace.toggleToHours(); 48 | } 49 | 50 | toggleToMinutes() { 51 | this.clockFace.toggleToMinutes(); 52 | } 53 | 54 | onTimeUpdate(time, type) { 55 | this.time = time; 56 | this.header.time = time; 57 | this.header.updateDisplayedTime(); 58 | if (type === Config.FaceType.MINUTES) 59 | this.header.toggleActiveToMinutes(); 60 | 61 | } 62 | 63 | static dispose() { 64 | document.body.removeChild(document.getElementById(Config.clockId)); 65 | } 66 | } -------------------------------------------------------------------------------- /src/js/clockHeader.js: -------------------------------------------------------------------------------- 1 | import {DOM} from "./meta/config"; 2 | 3 | export default class ClockHeader { 4 | 5 | constructor(config) { 6 | this.options = config.options; 7 | this.time = config.time; 8 | this.onHourClicked = config.onHourClicked; 9 | this.onMinutesClicked = config.onMinutesClicked; 10 | 11 | this.initView(); 12 | } 13 | 14 | initView() { 15 | this.headerHours = document.getElementById(DOM.hoursId); 16 | this.headerHours.onclick = () => { 17 | this.toggleActiveToHours(); 18 | this.onHourClicked(); 19 | }; 20 | 21 | this.headerMinutes = document.getElementById(DOM.minutesId); 22 | this.headerMinutes.onclick = () => { 23 | this.toggleActiveToMinutes(); 24 | this.onMinutesClicked(); 25 | }; 26 | 27 | this.updateDisplayedTime(); 28 | this.toggleActiveToHours(); 29 | } 30 | 31 | toggleActiveToMinutes() { 32 | this.toggleActive(this.headerHours, this.headerMinutes); 33 | } 34 | 35 | toggleActiveToHours() { 36 | this.toggleActive(this.headerMinutes, this.headerHours); 37 | } 38 | 39 | toggleActive(objectToRemoveClass, objectToAddClass) { 40 | objectToRemoveClass.style.color = this.options.headerColor; 41 | objectToAddClass.style.color = this.options.headerSelected; 42 | } 43 | 44 | updateDisplayedTime() { 45 | ClockHeader.doUpdateDisplayedTime(this.headerHours, this.time.hours); 46 | ClockHeader.doUpdateDisplayedTime(this.headerMinutes, this.time.minutes); 47 | } 48 | 49 | static doUpdateDisplayedTime(node, value) { 50 | if (value < 10) 51 | node.innerText = "0" + value; 52 | else node.innerText = value; 53 | } 54 | } -------------------------------------------------------------------------------- /src/js/colorStylists.js: -------------------------------------------------------------------------------- 1 | import {css, DOM} from "./meta/config"; 2 | 3 | export default function styleColors(options) { 4 | document.getElementById(DOM.headerId).style.background = options.headerBackground; 5 | document.getElementById(DOM.headerId).style.color = options.headerColor; 6 | document.getElementById(DOM.wrapperId).style.background = options.wrapperBackground; 7 | document.getElementById(DOM.clockId).style.background = options.clockBackground; 8 | document.getElementById(DOM.handId).style.background = options.handColor; 9 | document.getElementById(DOM.dotId).style.background = options.handColor; 10 | document.getElementById(DOM.buttonsId).style.background = options.footerBackground; 11 | document.getElementById(DOM.submitId).style.color = options.submitColor; 12 | document.getElementById(DOM.cancelId).style.color = options.cancelColor; 13 | 14 | changeColor(css.clockItem, options.clockItemColor); 15 | changeColor(css.inner, options.clockItemInnerColor); 16 | changeColor(css.outer, options.handColor, "borderColor"); 17 | } 18 | 19 | function changeColor(className, color, property = "color") { 20 | const items = Array.from(document.getElementsByClassName(className)); 21 | for (const item of items) 22 | item.style[property] = color; 23 | } 24 | -------------------------------------------------------------------------------- /src/js/face/clockFace.js: -------------------------------------------------------------------------------- 1 | import MinutesFace from "./minutesFace"; 2 | import HoursFace from "./hoursFace"; 3 | import Utils from "../meta/utils"; 4 | import Config, {css, DOM} from "../meta/config"; 5 | import ClockFaceCreator from "./clockFaceCreator"; 6 | 7 | export default class ClockFace { 8 | 9 | constructor(options, initialTime, onTimeUpdate) { 10 | this.options = options; 11 | this.time = initialTime; 12 | this.onTimeUpdate = onTimeUpdate; 13 | this.isMouseDown = false; 14 | this.clockItems = []; 15 | this.innerClockItems = []; 16 | this.outerClockItems = []; 17 | this.size = {}; 18 | this.middle = {}; 19 | 20 | this.initViews(); 21 | this.initTimeFaces(initialTime); 22 | this.createFace(); 23 | 24 | this.hoursFace.items.radius = this.itemsRadius; 25 | 26 | this.currentFace = this.hoursFace; 27 | this.changeDisplayed(this.currentFace.displayed); 28 | } 29 | 30 | initViews() { 31 | this.clockElem = document.getElementById(DOM.clockId); 32 | this.innerClockElem = document.getElementById(DOM.innerId); 33 | this.handOfAClock = document.getElementById(DOM.handId); 34 | 35 | this.clockElem.onmousedown = () => this.isMouseDown = true; 36 | this.clockElem.onmouseup = () => {this.isMouseDown = false; 37 | this.toggleToMinutes(); 38 | }; 39 | 40 | this.handOfAClock.onmouseup = (e) => e.stopPropagation(); 41 | this.handOfAClock.onmousemove = (e) => e.stopPropagation(); 42 | this.handOfAClock.onclick = (e) => e.stopPropagation(); 43 | 44 | this.clockElem.onmousemove = (e) => this.selectTime(e, false, this.clockElem); 45 | this.clockElem.onclick = (e) => this.selectTime(e, true, this.clockElem); 46 | 47 | this.innerClockElem.onmousemove = (e) => this.selectTime(e, false, this.innerClockElem); 48 | this.innerClockElem.onclick = (e) => this.selectTime(e, true, this.innerClockElem); 49 | } 50 | 51 | initTimeFaces(initialTime) { 52 | this.minutesFace = new MinutesFace({ 53 | options: this.options, 54 | clockItems: this.clockItems, 55 | outerClockItems: this.outerClockItems 56 | }, initialTime.minutes, (minutes, angle) => this.updateMinutes(minutes, angle)); 57 | 58 | this.hoursFace = new HoursFace({ 59 | options: this.options, 60 | innerClockItems: this.innerClockItems, 61 | clockItems: this.clockItems, 62 | innerClockElem: this.innerClockElem 63 | }, initialTime.hours, (hours, angle, radius) => this.updateHours(hours, angle, radius)); 64 | } 65 | 66 | onStart() { 67 | this.currentFace.onEnter(); 68 | } 69 | 70 | createFace() { 71 | const clockFaceCreator = new ClockFaceCreator(this.clockElem, this.innerClockElem); 72 | clockFaceCreator.create(this.clockItems, this.innerClockItems, this.outerClockItems, this.hoursFace); 73 | clockFaceCreator.calculateSize(this.clockItems, this.innerClockItems, this.outerClockItems); 74 | 75 | this.size = clockFaceCreator.size; 76 | this.middle = clockFaceCreator.middle; 77 | this.itemsRadius = clockFaceCreator.itemsRadius; 78 | } 79 | 80 | selectTime(event, isMouseDown, elem) { 81 | if (!(isMouseDown || this.isMouseDown)) 82 | return; 83 | const mouse = Utils.findMousePosition(event, this.clockElem); 84 | const x = mouse.x - this.middle.x; 85 | const y = this.middle.y - mouse.y; 86 | let angle = 90 - Utils.toDegrees(Math.atan(y / x)); 87 | if (x < 0) angle += 180; 88 | 89 | this.currentFace.selectTime(angle, elem); 90 | event.stopPropagation(); 91 | } 92 | 93 | changeDisplayed(array) { 94 | for (let i = 0; i < this.clockItems.length; i++) 95 | this.clockItems[i].innerText = array[i]; 96 | } 97 | 98 | onEachClockElement(fun) { 99 | [].forEach.call(this.clockItems, c => fun(c)); 100 | } 101 | 102 | updateMinutes(minutes, angle) { 103 | this.time.minutes = minutes; 104 | this.calculateHandOfTheClock(angle, this.itemsRadius); 105 | this.onTimeUpdate(this.time, Config.FaceType.MINUTES); 106 | } 107 | 108 | updateHours(hours, angle, radius) { 109 | this.time.hours = hours; 110 | this.calculateHandOfTheClock(angle, radius); 111 | this.onTimeUpdate(this.time, Config.FaceType.HOURS); 112 | } 113 | 114 | calculateHandOfTheClock(angle, size = this.itemsRadius) { 115 | this.handOfAClock.style.transform = `rotate(${angle - 90}deg)`; 116 | this.handOfAClock.style.width = size + "px"; 117 | } 118 | 119 | toggleToHours() { 120 | this.minutesFace.onLeave(); 121 | this.toggleTime(this.hoursFace); 122 | } 123 | 124 | toggleToMinutes() { 125 | this.hoursFace.onLeave(); 126 | this.toggleTime(this.minutesFace); 127 | } 128 | 129 | toggleTime(face) { 130 | if (this.currentFace !== face) { 131 | this.onEachClockElement(c => c.classList.add(css.fadeOut)); 132 | this.handOfAClock.classList.add(css.fadeOut); 133 | Promise.delay(() => { 134 | this.onEachClockElement(c => c.classList.remove(css.fadeOut)); 135 | this.handOfAClock.classList.remove(css.fadeOut); 136 | this.changeDisplayed(face.displayed); 137 | this.currentFace = face; 138 | this.onEachClockElement(c => this.removeSelected(c)); 139 | face.onEnter(); 140 | }, 300); 141 | } 142 | } 143 | 144 | removeSelected(c) { 145 | c.classList.remove(css.selected); 146 | c.style.background = "transparent"; 147 | c.style.color = this.options.clockItemColor; 148 | } 149 | } -------------------------------------------------------------------------------- /src/js/face/clockFaceCreator.js: -------------------------------------------------------------------------------- 1 | import Utils from "../meta/utils"; 2 | import {css} from "../meta/config"; 3 | 4 | export default class ClockFaceCreator { 5 | 6 | constructor(clockElem, innerClockElem) { 7 | this.clockElem = clockElem; 8 | this.innerClockElem = innerClockElem; 9 | this.size = {}; 10 | this.middle = {}; 11 | } 12 | 13 | create(clockItems, innerClockItems, outerClockItems, face) { 14 | ClockFaceCreator.doCreate(clockItems, this.clockElem, span => span.classList.add(css.item)); 15 | ClockFaceCreator.doCreate(innerClockItems, this.innerClockElem, (span, i) => { 16 | span.classList.add(css.item, css.inner); 17 | span.innerText = face.displayedInner[i]; 18 | }); 19 | 20 | for (let i = 0; i < 60; i++) { 21 | const span = document.createElement("span"); 22 | span.classList.add(css.outer); 23 | outerClockItems.push(span); 24 | this.clockElem.appendChild(span); 25 | } 26 | } 27 | 28 | static doCreate(clockItems, clockElem, fun) { 29 | for (let i = 0; i < 12; i++) { 30 | const span = document.createElement("span"); 31 | fun(span, i); 32 | clockItems.push(span); 33 | clockElem.appendChild(span); 34 | } 35 | } 36 | 37 | calculateSize(clockItems, innerClockItems, outerClockItems) { 38 | this.size.width = this.clockElem.offsetWidth; 39 | this.size.height = this.clockElem.offsetHeight; 40 | this.middle.x = this.size.width / 2; 41 | this.middle.y = this.size.height / 2; 42 | this.itemsRadius = this.size.width / 2 - 20; 43 | 44 | const innerWidth = this.innerClockElem.offsetWidth; 45 | const innerHeight = this.innerClockElem.offsetHeight; 46 | const middleX = innerWidth / 2; 47 | const middleY = innerHeight / 2; 48 | 49 | ClockFaceCreator.doCalculateSize(this.middle.x, this.middle.y, this.itemsRadius, clockItems); 50 | ClockFaceCreator.doCalculateSize(middleX, middleY, this.itemsRadius - 40, innerClockItems); 51 | ClockFaceCreator.doCalculateSize(this.middle.x, this.middle.y, this.itemsRadius, outerClockItems); 52 | } 53 | 54 | static doCalculateSize(middleX, middleY, radius, items) { 55 | const angleQuantum = 360 / items.length; 56 | for (let i = 0; i < items.length; i++) { 57 | 58 | const angle = Utils.toRadians(i * angleQuantum); 59 | const item = items[i]; 60 | const itemWidth = item.offsetWidth; 61 | const itemHeight = item.offsetHeight; 62 | 63 | item.style.left = ((middleX + Math.sin(angle) * radius) - itemWidth / 2) + "px"; 64 | item.style.bottom = ((middleY + Math.cos(angle) * radius) - itemHeight / 2) + "px"; 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /src/js/face/hoursFace.js: -------------------------------------------------------------------------------- 1 | import Config from "../meta/config"; 2 | 3 | export default class HoursFace { 4 | 5 | constructor(items, initialHours, updateHours) { 6 | this.displayed = ["12", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"]; 7 | this.displayedInner = ["00", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23"]; 8 | this.type = Config.FaceType.MINUTES; 9 | this.selected = undefined; 10 | this.options = items.options; 11 | 12 | this.items = items; 13 | this.hours = initialHours; 14 | this.updateHours = updateHours; 15 | } 16 | 17 | onEnter() { 18 | this.items.innerClockElem.style.display = "block"; 19 | const isInnerClock = this.hours < 13 && this.hours !== 0; 20 | const hoursIndex = this.hours % 12; 21 | this.selected = isInnerClock ? this.items.clockItems[hoursIndex] : this.items.innerClockItems[hoursIndex]; 22 | this.colorSelected(); 23 | 24 | this.updateHours(this.hours, hoursIndex * 30, isInnerClock ? this.items.radius : this.items.radius - 50); 25 | } 26 | 27 | onLeave() { 28 | this.items.innerClockElem.style.display = "none"; 29 | if (this.selected) { 30 | this.removeSelected(); 31 | this.selected = undefined; 32 | } 33 | } 34 | 35 | selectTime(angle, elem) { 36 | if (this.selected) 37 | this.removeSelected(); 38 | 39 | const index = Math.round(angle / 30) % 12; 40 | this.selected = (elem === this.items.innerClockElem 41 | ? this.items.innerClockItems 42 | : this.items.clockItems)[index]; 43 | 44 | this.colorSelected(); 45 | this.hours = parseInt(this.selected.innerText); 46 | const selectedAngle = Math.round(angle / 30) * 30; 47 | 48 | this.updateHours(this.hours, selectedAngle, 49 | elem === this.items.innerClockElem ? this.items.radius - 50 : this.items.radius); 50 | } 51 | 52 | colorSelected() { 53 | this.selected.style.background = this.options.handColor; 54 | this.selected.style.color = "#ffffff"; 55 | } 56 | 57 | removeSelected() { 58 | this.selected.style.background = "transparent"; 59 | this.selected.style.color = this.isInner() 60 | ? this.options.clockItemInnerColor 61 | : this.options.clockItemColor; 62 | } 63 | 64 | isInner() { 65 | return Array.from(this.items.innerClockItems).indexOf(this.selected) > -1; 66 | } 67 | } -------------------------------------------------------------------------------- /src/js/face/minutesFace.js: -------------------------------------------------------------------------------- 1 | import Config, {css} from "../meta/config"; 2 | 3 | export default class MinutesFace { 4 | 5 | constructor(items, initialMinutes, updateMinutes) { 6 | this.displayed = ["00", "05", "10", "15", "20", "25", "30", "35", "40", "45", "50", "55"]; 7 | this.options = items.options; 8 | this.type = Config.FaceType.MINUTES; 9 | this.selected = undefined; 10 | this.items = items; 11 | this.minutes = initialMinutes; 12 | this.updateMinutes = updateMinutes; 13 | } 14 | 15 | onEnter() { 16 | this.selected = this.findSelected(this.minutes); 17 | this.colorSelected(); 18 | this.updateMinutes(this.minutes, this.minutes * 6); 19 | } 20 | 21 | onLeave() { 22 | if (this.selected) { 23 | this.removeSelected(); 24 | this.selected = undefined; 25 | } 26 | } 27 | 28 | selectTime(angle) { 29 | if (this.selected) 30 | this.removeSelected(); 31 | 32 | const minute = Math.round(angle / 6) % 60; 33 | this.selected = this.findSelected(minute); 34 | this.colorSelected(); 35 | this.minutes = minute; 36 | this.updateMinutes(this.minutes, angle); 37 | } 38 | 39 | findSelected(minute) { 40 | return (minute % 5 === 0) ? this.items.clockItems[minute / 5] : this.items.outerClockItems[minute]; 41 | } 42 | 43 | colorSelected() { 44 | if (this.isOuter()) { 45 | this.selected.classList.add(css.selected); 46 | return; 47 | } 48 | this.selected.style.background = this.options.handColor; 49 | this.selected.style.color = "whitesmoke"; 50 | } 51 | 52 | removeSelected() { 53 | if (this.isOuter()) { 54 | this.selected.classList.remove(css.selected); 55 | return; 56 | } 57 | this.selected.style.background = "transparent"; 58 | this.selected.style.color = this.options.clockItemColor; 59 | } 60 | 61 | isOuter() { 62 | return this.items.outerClockItems.indexOf(this.selected) > -1; 63 | } 64 | } -------------------------------------------------------------------------------- /src/js/index.js: -------------------------------------------------------------------------------- 1 | import formatTime from "./timeFormatter"; 2 | import showPicker from "./timepickerCreator"; 3 | 4 | export default { 5 | showPicker: (config) => showPicker(config), 6 | format: (time) => formatTime(time) 7 | }; 8 | 9 | -------------------------------------------------------------------------------- /src/js/meta/clockHtml.js: -------------------------------------------------------------------------------- 1 | export default "
\n" + 2 | "
\n" + 3 | "
\n" + 4 | " 21\n" + 5 | " :\n" + 6 | " 37\n" + 7 | "
\n" + 8 | "
\n" + 9 | "\n" + 10 | "\n" + 11 | "
\n" + 12 | "
" + 13 | " \n" + 14 | "
\n" + 15 | "
\n" + 16 | "
\n" + 17 | "
\n" + 18 | "\n" + 19 | "\n" + 20 | " \n" + 24 | "\n" + 25 | "
"; -------------------------------------------------------------------------------- /src/js/meta/config.js: -------------------------------------------------------------------------------- 1 | const clockId = "grudus-clock"; 2 | 3 | const defaultConfig = { 4 | onSubmit: () => { 5 | }, 6 | onCancel: () => { 7 | }, 8 | headerBackground: "#1976D2", 9 | headerColor: "#c7d6e1", 10 | headerSelected: "#ffffff", 11 | wrapperBackground: "#f0fff0", 12 | footerBackground: "#f0fff0", 13 | submitColor: "#1976D2", 14 | cancelColor: "#1976D2", 15 | clockBackground: "#CFD8DC", 16 | clockItemColor: "#212121", 17 | clockItemInnerColor: "#212121", 18 | handColor: "#1976D2" 19 | }; 20 | 21 | const FaceType = {HOURS: "hours", MINUTES: "minutes"}; 22 | 23 | const css = { 24 | clock: "g-clock", 25 | clockItem: "g-clock-item", 26 | inner: "g-clock-inner", 27 | outer: "g-clock-outer", 28 | item: "g-clock-item", 29 | hand: "g-hand-of-a-clock", 30 | fadeOut: "g-fade-out", 31 | selected: "g-selected", 32 | active: "g-active", 33 | submit: "g-submit", 34 | cancel: "g-cancel", 35 | hour: "g-hour", 36 | minute: "g-minute" 37 | }; 38 | 39 | const DOM = { 40 | headerId: "g-head", 41 | hoursId: "g-hours", 42 | minutesId: "g-minutes", 43 | clockId: "g-clock", 44 | innerId: "g-clock-inner", 45 | wrapperId: "g-clock-wrapper", 46 | dotId: "g-middle-dot", 47 | handId: "g-hand-of-a-clock", 48 | buttonsId: "g-buttons", 49 | submitId: "g-time-submit", 50 | cancelId: "g-time-cancel" 51 | }; 52 | 53 | export default {clockId, clockConfig: defaultConfig, FaceType}; 54 | export {css, DOM}; -------------------------------------------------------------------------------- /src/js/meta/utils.js: -------------------------------------------------------------------------------- 1 | function toRadians(angle) { 2 | return angle * (Math.PI / 180); 3 | } 4 | 5 | function toDegrees(angle) { 6 | return angle * (180 / Math.PI); 7 | } 8 | 9 | function findMousePosition(event, object) { 10 | const rect = object.getBoundingClientRect(); 11 | return { 12 | x: event.clientX - rect.left, 13 | y: event.clientY - rect.top 14 | }; 15 | } 16 | 17 | function delay(t) { 18 | return new Promise(function (resolve) { 19 | setTimeout(resolve, t); 20 | }); 21 | } 22 | 23 | Promise.delay = function (fn, t) { 24 | if (!t) { 25 | t = fn; 26 | fn = function () { 27 | }; 28 | } 29 | return delay(t).then(fn); 30 | }; 31 | 32 | Promise.prototype.delay = function (fn, t) { 33 | return this.then(function () { 34 | return Promise.delay(fn, t); 35 | }); 36 | }; 37 | 38 | export default {toRadians, toDegrees, findMousePosition}; -------------------------------------------------------------------------------- /src/js/timeExtractor.js: -------------------------------------------------------------------------------- 1 | const hoursRegex = /^([0-1]?[0-9]|2[0-3])$/; 2 | const minutesRegex = /^([0-5]?[0-9])$/; 3 | const regex = /^([0-1]?[0-9]|2[0-3]):([0-5][0-9])$/; 4 | 5 | export default function extractTime(date) { 6 | if (!date) 7 | return fromDate(new Date()); 8 | else if (date instanceof Date) 9 | return fromDate(date); 10 | else if (hoursRegex.test(date.hours) && minutesRegex.test(date.minutes)) 11 | return {hours: parseInt(date.hours), minutes: parseInt(date.minutes)}; 12 | else if (regex.test(date)) 13 | return fromRegex(date); 14 | else 15 | throw new TypeError(`INVALID FORMAT: {${JSON.stringify(date)}}. 16 | Time must be a Date or 'hh:MM' string or object with 'hours' and 'minutes' fields`); 17 | } 18 | 19 | function fromRegex(date) { 20 | const parsed = regex.exec(date); 21 | return {hours: parseInt(parsed[1]), minutes: parseInt(parsed[2])}; 22 | } 23 | 24 | function fromDate(date) { 25 | return {hours: date.getHours(), minutes: date.getMinutes()}; 26 | } -------------------------------------------------------------------------------- /src/js/timeFormatter.js: -------------------------------------------------------------------------------- 1 | import extractTime from "./timeExtractor"; 2 | 3 | export default function (time) { 4 | const extractedTime = extractTime(time); 5 | return (extractedTime.hours < 10 ? "0" + extractedTime.hours : extractedTime.hours) 6 | + ":" + (extractedTime.minutes < 10 ? "0" + extractedTime.minutes : extractedTime.minutes); 7 | } -------------------------------------------------------------------------------- /src/js/timepickerCreator.js: -------------------------------------------------------------------------------- 1 | import clockHtml from "./meta/clockHtml"; 2 | import Config from "./meta/config"; 3 | import Clock from "./clock"; 4 | import styleColors from "./colorStylists"; 5 | import getTime from "./timeExtractor"; 6 | 7 | 8 | export default function showPicker(config = {}) { 9 | createDom(); 10 | 11 | const options = Object.assign({}, Config.clockConfig, config); 12 | const time = getTime(options.time); 13 | 14 | const clock = new Clock(options, time); 15 | styleColors(options); 16 | clock.onStart(); 17 | } 18 | 19 | 20 | function createDom() { 21 | if (document.getElementById(Config.clockId)) 22 | throw Error("There is already one running grudus-timepicker instance!"); 23 | 24 | const clockDiv = document.createElement("div"); 25 | clockDiv.id = Config.clockId; 26 | clockDiv.innerHTML = clockHtml; 27 | document.body.appendChild(clockDiv); 28 | } 29 | -------------------------------------------------------------------------------- /src/styles/index.css: -------------------------------------------------------------------------------- 1 | @keyframes fade-in { 2 | from { 3 | opacity: 0; 4 | } 5 | to { 6 | opacity: 1; 7 | } 8 | } 9 | 10 | @keyframes fade-out { 11 | from { 12 | opacity: 1; 13 | } 14 | to { 15 | opacity: 0; 16 | } 17 | } 18 | 19 | .g-time-wrapper { 20 | box-sizing: content-box; 21 | min-width: 300px; 22 | min-height: 450px; 23 | position: fixed; 24 | top: 50%; 25 | left: 50%; 26 | transform: translate(-50%, -50%); 27 | font-family: 'Roboto', sans-serif; 28 | 29 | display: flex; 30 | flex-direction: column; 31 | justify-content: flex-start; 32 | } 33 | 34 | .g-flex { 35 | flex-grow: 1; 36 | } 37 | 38 | .g-head { 39 | flex-grow: 1; 40 | display: flex; 41 | justify-content: center; 42 | align-items: center; 43 | } 44 | 45 | .g-head-content { 46 | font-size: 3em; 47 | display: flex; 48 | } 49 | 50 | .g-current { 51 | margin: 0 2px; 52 | } 53 | 54 | .g-clock-wrapper { 55 | flex-grow: 4; 56 | display: flex; 57 | align-items: center; 58 | justify-content: center; 59 | } 60 | 61 | .g-clock { 62 | position: relative; 63 | border-radius: 100%; 64 | height: 240px; 65 | width: 240px; 66 | cursor: default; 67 | } 68 | 69 | .g-clock.g-clock-inner { 70 | top: 50%; 71 | left: 50%; 72 | transform: translate(-50%, -50%); 73 | height: 160px; 74 | width: 160px; 75 | } 76 | 77 | .g-clock-item { 78 | position: absolute; 79 | border-radius: 100%; 80 | width: 20px; 81 | height: 20px; 82 | text-align: center; 83 | padding: 10px; 84 | user-select: none; 85 | cursor: default; 86 | font-size: 1.1em; 87 | z-index: 3; 88 | } 89 | 90 | .g-clock-item.g-clock-inner { 91 | font-size: 0.9em; 92 | z-index: 3; 93 | } 94 | 95 | 96 | .g-clock-outer { 97 | position: absolute; 98 | width: 4px; 99 | height: 4px; 100 | border: 18px solid #1976D2; 101 | background: whitesmoke; 102 | color: whitesmoke; 103 | border-radius: 100%; 104 | visibility: hidden; 105 | z-index: 2; 106 | } 107 | 108 | .g-clock-outer.g-selected { 109 | visibility: visible; 110 | } 111 | 112 | .g-middle-dot { 113 | z-index: 2; 114 | position: absolute; 115 | width: 1px; 116 | height: 1px; 117 | padding: 7px; 118 | border-radius: 100%; 119 | top: 50%; 120 | left: 50%; 121 | transform: translate(-50%, -50%); 122 | } 123 | 124 | .g-hand-of-a-clock { 125 | position: absolute; 126 | z-index: 2; 127 | top: 50%; 128 | left: 50%; 129 | transform: translateY(-50%); 130 | transform-origin: left center; 131 | height: 3px; 132 | width: 20px; 133 | } 134 | 135 | .g-buttons { 136 | display: flex; 137 | justify-content: flex-end; 138 | align-items: center; 139 | flex-grow: 0.5; 140 | 141 | } 142 | 143 | .g-button { 144 | font-weight: 600; 145 | border: none; 146 | background: transparent; 147 | margin-right: 16px; 148 | padding: 8px; 149 | } 150 | 151 | .g-button:hover { 152 | cursor: pointer; 153 | background: #CFD8DC; 154 | } 155 | 156 | .g-fade-in { 157 | animation: fade-in 0.4s; 158 | } 159 | 160 | .g-fade-out { 161 | animation: fade-out 0.4s; 162 | } 163 | 164 | .g-pointer:hover { 165 | cursor: pointer; 166 | } --------------------------------------------------------------------------------