├── Quttons.css ├── Quttons.js ├── README.md └── package.json /Quttons.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin : 0; 3 | padding : 0; 4 | } 5 | .qutton{ 6 | box-sizing : content-box !important; 7 | background-repeat : no-repeat !important; 8 | background-position: center center !important; 9 | box-shadow: 0 3px 6px 0 rgba(0, 0, 0, 0.3); 10 | cursor : auto; 11 | } 12 | 13 | .qutton.close { 14 | position : absolute !important; 15 | cursor : pointer; 16 | } 17 | 18 | .quttonClonePlaceHolder { 19 | visibility: hidden; 20 | } 21 | -------------------------------------------------------------------------------- /Quttons.js: -------------------------------------------------------------------------------- 1 | (function(factory) { 2 | if (typeof module === 'object' && module.exports) { 3 | // CommonJS 4 | module.exports = factory(require('jquery')); 5 | } else if (typeof define === 'function' && define.amd) { 6 | // AMD 7 | define(['jquery'], factory); 8 | } else { 9 | // Browser globals 10 | window.Qutton = factory(window.jQuery); 11 | } 12 | })(function($) { 13 | 14 | /******************************************** 15 | * Quttons.js * 16 | * Quttons are buttons made of Quantum Paper * 17 | * Author : Nash Vail * 18 | *********************************************/ 19 | 20 | 'use strict'; 21 | 22 | // Qutton Object 23 | function Qutton(jQueryDOMElement) { 24 | 25 | // Cache the important elements as jQuery object 26 | this.$container = jQueryDOMElement; 27 | // Dialog is alias of the box that pops up on clicking the Qutton 28 | this.$dialog = this.$container.children(); 29 | // Cache the close button if it exists 30 | this.$closeButton = this.$container.find('.close'); 31 | 32 | // When button is expanded into a dialog isOpen holds true 33 | this.isOpen = false; 34 | 35 | // Configuration of the popped up dialog 36 | this.dialogConfig = { 37 | width : this.$dialog.outerWidth(), 38 | height : this.$dialog.outerHeight(), 39 | backgroundColor : toHex(this.$dialog.css('background-color')), 40 | borderRadius : this.$dialog.css('border-radius'), 41 | zIndex : this.$dialog.css('z-index') 42 | }; 43 | 44 | // Configuration of Qutton 45 | this.quttonConfig = { 46 | width : 60, 47 | height : 60, 48 | backgroundColor : '#EB1220', 49 | icon : '', // Url of the icon that the button is supposed to hold 50 | easing : 'easeInOutQuint' 51 | }; 52 | 53 | } 54 | 55 | // Initializes the click listeners on the qutton itself, document and close button 56 | Qutton.prototype.init = function(quttonConfig) { 57 | 58 | $.extend(this.quttonConfig, quttonConfig); 59 | 60 | this.$dialog.hide(); 61 | 62 | // Set up the icon and other properties of the div 63 | this.setIcon(); 64 | this.$container.css({ 65 | 'width' : this.quttonConfig.width + 'px', 66 | 'height' : this.quttonConfig.height + 'px', 67 | 'background-color' : this.quttonConfig.backgroundColor, 68 | 'border-radius' : this.quttonConfig.height + 'px' 69 | }); 70 | 71 | // Initialize the event handlers 72 | this.events.click.call(this); 73 | this.events.click_document.call(this); 74 | this.events.click_close_button.call(this); 75 | 76 | }; 77 | 78 | Qutton.prototype.closeDialog = function() { 79 | var dialog = this; 80 | if (dialog.isOpen){ 81 | dialog.setIcon(); 82 | dialog.animateOut(); 83 | } else if (dialog.isOpening) { 84 | setTimeout(function() { 85 | dialog.closeDialog(); 86 | }, 100); 87 | } 88 | }; 89 | 90 | Qutton.prototype.openDialog = function() { 91 | this.removeIcon(); 92 | this.animateIn(); 93 | }; 94 | 95 | Qutton.prototype.setIcon = function() { 96 | this.$container.css('background-image', 'url(' + this.quttonConfig.icon + ')'); 97 | this.$container.css('cursor', 'pointer'); 98 | }; 99 | 100 | Qutton.prototype.removeIcon = function() { 101 | this.$container.css('background-image', 'none'); 102 | this.$container.css('cursor', 'auto'); 103 | }; 104 | 105 | // Animates the button into dialog 106 | Qutton.prototype.animateIn = function() { 107 | var that = this; 108 | if (that.isOpening === true) { 109 | return; 110 | } 111 | 112 | that.isOpening = true; 113 | 114 | // Translate amount to make the dialog look like exploding from desired location 115 | var translate = { 116 | X : -1 * (this.dialogConfig.width/2 - this.quttonConfig.width/2), 117 | Y : -0.5 * (this.dialogConfig.height/2 - this.quttonConfig.width/2) 118 | }; 119 | 120 | var inSequence = [ 121 | { 122 | e : this.$container, 123 | p : { 124 | width : this.dialogConfig.width +'px', 125 | height : this.dialogConfig.height + 'px', 126 | borderRadius : this.dialogConfig.borderRadius, 127 | backgroundColor : this.dialogConfig.backgroundColor, 128 | translateX : translate.X + this.keepInBounds().X + 'px', 129 | translateY : translate.Y + this.keepInBounds().Y + 'px' 130 | }, 131 | o : { 132 | duration : 500, 133 | easing : this.quttonConfig.easing, 134 | begin : function() { 135 | // add a placeholder in place to maintain the flow of document 136 | if (!that.$container.next('.quttonClonePlaceHolder').length) 137 | that.$container.after(that.$container.clone().addClass('quttonClonePlaceHolder')); 138 | 139 | that.$container.css({ 140 | 'position' : 'absolute', 141 | 'z-index' : '10000' 142 | }); 143 | }, 144 | complete : function() { 145 | that.isOpen = true; 146 | that.isOpening = false; 147 | } 148 | } 149 | }, 150 | 151 | { 152 | e : this.$dialog, 153 | p : 'fadeIn', 154 | o : { 155 | duration : 300, 156 | complete : function() { 157 | that.isOpen = true; 158 | } 159 | } 160 | } 161 | ]; 162 | 163 | $.Velocity.RunSequence(inSequence); 164 | }; 165 | 166 | // Animtes dialog into button 167 | Qutton.prototype.animateOut = function() { 168 | var that = this; 169 | 170 | if (that.closing === true) { 171 | return; 172 | } 173 | 174 | that.closing = true; 175 | 176 | var outSequence = [ 177 | { 178 | e : this.$dialog, 179 | p : 'fadeOut', 180 | o : { 181 | duration : 150 182 | } 183 | }, 184 | 185 | { 186 | e : this.$container, 187 | p :{ 188 | width : this.quttonConfig.width + 'px', 189 | height : this.quttonConfig.height + 'px', 190 | backgroundColor : this.quttonConfig.backgroundColor, 191 | // For a perfect circle we will give border radius the same value as height and width 192 | borderRadius : this.quttonConfig.width, 193 | // Neutralize movement of button after it translated to maintain position 194 | translateX : '0px', 195 | translateY : '0px' 196 | }, 197 | o : { 198 | easing : this.quttonConfig.easing, 199 | duration : 200, 200 | complete : function() { 201 | // Remove the placeholder 202 | that.$container.next('.quttonClonePlaceHolder').remove(); 203 | that.$container.css({ 204 | 'position' : 'static', 205 | 'z-index' : that.dialogConfig.zIndex 206 | }); 207 | that.isOpen = false; 208 | that.closing = false; 209 | } 210 | } 211 | } 212 | ]; 213 | 214 | $.Velocity.RunSequence(outSequence); 215 | }; 216 | 217 | // Check if the explosion of Qutton is within the document bounds. 218 | // Returns an object containing values to translate in X or Y direction in order to 219 | // keep the dialog in bounds of the document on explosion. 220 | Qutton.prototype.keepInBounds= function() { 221 | var $window = $(window); 222 | var windowWidth = $window.width(); 223 | var windowHeight = $window.height(); 224 | 225 | var position = this.$container.position(); 226 | 227 | // Coordinates of top center of Qutton before it converts to a a dialog 228 | var buttonCenterTop = { 229 | top : position.top, 230 | left : position.left + (this.quttonConfig.width/2) 231 | }; 232 | 233 | // Coordinates of the dialog once it opens 234 | var dialogCoords = { 235 | top : buttonCenterTop.top - ( 0.5 * (this.dialogConfig.height/2 - this.quttonConfig.height/2)), 236 | left : buttonCenterTop.left - (this.dialogConfig.width/2), 237 | }; 238 | 239 | // How much the dialog extends beyond the document 240 | var extend = { 241 | left : dialogCoords.left, 242 | right : windowWidth - (dialogCoords.left + this.dialogConfig.width), 243 | top : dialogCoords.top, 244 | bottom : windowHeight - (dialogCoords.top + this.dialogConfig.height) 245 | }; 246 | 247 | // Amount to translate in X and Y if possible to bring dialog in bounds of document 248 | var translateInBounds = { 249 | X : this.calculateTranslateAmount(extend.left, extend.right), 250 | Y : this.calculateTranslateAmount(extend.top, extend.bottom) 251 | }; 252 | 253 | return translateInBounds; 254 | }; 255 | 256 | // Calculates and returns the amount to translate the dialog to keep in bounds of the window 257 | Qutton.prototype.calculateTranslateAmount = function(extendSideOne, extendSideTwo) { 258 | if ((extendSideOne < 0 && extendSideTwo < 0) || 259 | (extendSideOne > 0 && extendSideTwo > 0 )) { 260 | return 0; 261 | } 262 | 263 | // We want to translate in opposite direction of extension 264 | return (extendSideOne < 0 ? -extendSideOne : extendSideTwo); 265 | }; 266 | 267 | // Event listeners 268 | Qutton.prototype.events = { 269 | // Handles the click on Qutton 270 | click : function() { 271 | var that = this; 272 | this.$container.on('click', function(){ 273 | if (!that.isOpen){ 274 | that.openDialog(); 275 | } 276 | }); 277 | }, 278 | 279 | // Handle clicks on the document, aimed at closing the dialog 280 | click_document : function() { 281 | var that = this; 282 | $(document).on('click', function(event) { 283 | if (!$(event.target).closest(that.$container.selector).length){ 284 | if (that.isOpen){ 285 | that.closeDialog(); 286 | } 287 | } 288 | }); 289 | }, 290 | 291 | // Initializes clicks on close button if it exists 292 | click_close_button : function() { 293 | var that = this; 294 | if (this.$closeButton.length){ 295 | this.$closeButton.on('click', function(event){ 296 | if (that.isOpen){ 297 | that.closeDialog(); 298 | } 299 | }); 300 | } 301 | } 302 | }; 303 | 304 | // Converts and returns RGB color code to Hex Code(String) 305 | function toHex(rgb){ 306 | rgb = rgb.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i); 307 | return (rgb && rgb.length === 4) ? '#' + 308 | ('0' + parseInt(rgb[1],10).toString(16)).slice(-2) + 309 | ('0' + parseInt(rgb[2],10).toString(16)).slice(-2) + 310 | ('0' + parseInt(rgb[3],10).toString(16)).slice(-2) : ''; 311 | } 312 | 313 | // Factory method for producing new Material Dialog objects 314 | function factory(jQueryDOMElement) { 315 | if (jQueryDOMElement === null) throw new Error('Passed in element doesn\'t exist in DOM'); 316 | return new Qutton(jQueryDOMElement); 317 | } 318 | 319 | return { 320 | getInstance: factory 321 | }; 322 | }); 323 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Quttons are buttons made of Quantum Paper 2 | * Qunatum Paper is a digital paper that can change its size, shape and color to accommodate new content. 3 | Quantum paper is part of Google's new Material Design language. 4 | 5 | * With this plugin you can hide any div behind a Quantum [Paper] Button or Qutton 6 | 7 | 8 | #[Online Demo](http://nashvail.github.io/Quttons) 9 | 10 | ![Slow Mo Demo](http://i.imgur.com/I6xeQkn.gif) 11 | 12 | #Dependencies 13 | * jQuery 14 | * [Velocity.js](https://raw.githubusercontent.com/julianshapiro/velocity/master/velocity.js) with [UI Pack](https://raw.githubusercontent.com/julianshapiro/velocity/master/velocity.ui.js) 15 | 16 | #How To Use 17 | 18 | **Switch to gh-pages branch to look at code used in demo site.** 19 | 20 | ##Step 1, Include Dependencies : 21 | * Download and include all dependencies in your html file. 22 | * After all the dependencies have been included, include Quttons.js or Quttons.min.js and Quttons.css after downloading from this repo. 23 | * In the following order 24 | ```html 25 | 26 | 27 | 28 | 29 | ``` 30 | * And CSS 31 | ```html 32 | 33 | ``` 34 | 35 | ##Step 2, Design your dialog : 36 | * Design your dialog box. 37 | * Design a div as you normally would, with all the css styles you want. You can put whatever you want inside your div. 38 | * You can also add event listeners to buttons inside the dialog as you normally would. 39 | * You can add a close button, just remember to give it a class of `close`. 40 | * Here is sample code for the Upload File Qutton from the [Demo Site](http://nashvail.github.io/Quttons). 41 | ```html 42 |
43 |

Upload a new file

44 | 45 |
Choose a file to upload 46 |
47 |
48 | ``` 49 | 50 | ##Step 3, Wrap created dialog in a div 51 | * Wrap dialog created in previous step in a div with class of ***qutton*** and one ***custom id which will be used to reference this qutton in your js file***. 52 | * Example 53 | ```html 54 |
55 | ...(Dialog created in previous step) ... 56 |
57 | ``` 58 | 59 | ##Step 4, Initialize Qutton 60 | * In your .js file start by referencing the qutton in the following manner. 61 | ```javascript 62 | var quttonUpload = Qutton.getInstance($('#qutton_upload')); 63 | quttonUpload.init({ 64 | icon : './images/icon_upload.png', 65 | backgroundColor : "#917466" 66 | }); 67 | ``` 68 | * Inside `getInstance` you pass in a jQuery object referencing the **custom id** you set up in previous step 69 | * `init` function takes in an object specifying the configuration of the button, following are currently supported 70 | 71 | |Argument | Description | Default | 72 | |---|---|---|---|---| 73 | | icon | Icon to be displayed in Qutton | None | 74 | | backgroundColor | Background Color of Qutton | #FE0000 | 75 | | width | Width of the Qutton | 60 | 76 | | height | Height of the Qutton | 60 | 77 | | easing | Easing for the animation | easeInOutQuint | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "quttons", 3 | "version": "1.0.0", 4 | "description": "Buttons made of Quantum Paper", 5 | "main": "Quttons.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/nashvail/Quttons.git" 12 | }, 13 | "keywords": [ 14 | "quantum", 15 | "paper", 16 | "buttons" 17 | ], 18 | "author": "https://github.com/nashvail", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/nashvail/Quttons/issues" 22 | }, 23 | "homepage": "https://github.com/nashvail/Quttons#readme" 24 | } 25 | --------------------------------------------------------------------------------