ripples es6 refactoring with a util class

This commit is contained in:
Kevin Ross 2015-11-30 17:59:43 -06:00
parent 3abd40f72a
commit 8cb4f02d63
4 changed files with 153 additions and 125 deletions

View File

@ -86,6 +86,7 @@ module.exports = function (grunt) {
modules: 'ignore' modules: 'ignore'
}, },
files: { files: {
'js/dist/util.js' : 'js/src/util.js',
'js/dist/ripples.js' : 'js/src/ripples.js' //, 'js/dist/ripples.js' : 'js/src/ripples.js' //,
//'js/dist/alert.js' : 'js/src/alert.js', //'js/dist/alert.js' : 'js/src/alert.js',
//'js/dist/button.js' : 'js/src/button.js', //'js/dist/button.js' : 'js/src/button.js',
@ -112,6 +113,7 @@ module.exports = function (grunt) {
modules: 'umd' modules: 'umd'
}, },
files: { files: {
'dist/js/umd/util.js' : 'js/src/util.js',
'dist/js/umd/ripples.js' : 'js/src/ripples.js' //, 'dist/js/umd/ripples.js' : 'js/src/ripples.js' //,
//'dist/js/umd/alert.js' : 'js/src/alert.js', //'dist/js/umd/alert.js' : 'js/src/alert.js',
//'dist/js/umd/button.js' : 'js/src/button.js', //'dist/js/umd/button.js' : 'js/src/button.js',
@ -173,6 +175,7 @@ module.exports = function (grunt) {
}, },
bootstrap: { bootstrap: {
src: [ src: [
'js/src/util.js',
'js/src/ripples.js', 'js/src/ripples.js',
//'js/src/alert.js', //'js/src/alert.js',
//'js/src/button.js', //'js/src/button.js',

View File

@ -1,5 +1,6 @@
const Foo = (($) => { import Util from './util'
const Foo = (($) => {
/** /**
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
@ -8,21 +9,8 @@ const Foo = (($) => {
*/ */
const NAME = 'foo' const NAME = 'foo'
const DATA_KEY = `bmd.${NAME}` const DATA_KEY = `bmd.${NAME}`
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
const JQUERY_NO_CONFLICT = $.fn[NAME] const JQUERY_NO_CONFLICT = $.fn[NAME]
const Selector = {
DATA_DISMISS: '[data-dismiss="foo"]'
}
const Event = {
CLOSE: `close${EVENT_KEY}`,
CLOSED: `closed${EVENT_KEY}`,
CLICK_DATA_API: `click${EVENT_KEY}${DATA_API_KEY}`
}
/** /**
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
* Class Definition * Class Definition
@ -31,34 +19,15 @@ const Foo = (($) => {
class Foo { class Foo {
constructor(element) { constructor(element) {
this._element = element this.element = element
}
// getters
static get NAME() {
return NAME
}
// public
close(element) {
element = element || this._element
let rootElement = this._getRootElement(element)
let customEvent = this._triggerCloseEvent(rootElement)
if (customEvent.isDefaultPrevented()) {
return
}
this._removeElement(rootElement)
} }
dispose() { dispose() {
$.removeData(this._element, DATA_KEY) $.removeData(this.element, DATA_KEY)
this._element = null this.element = null
} }
// ------------------------------------------------------------------------
// private // private
_bar(element) { _bar(element) {
@ -66,7 +35,7 @@ const Foo = (($) => {
return x return x
} }
// ------------------------------------------------------------------------
// static // static
static _jQueryInterface(config) { static _jQueryInterface(config) {
return this.each(function () { return this.each(function () {
@ -83,32 +52,8 @@ const Foo = (($) => {
} }
}) })
} }
static _handleClose(fooInstance) {
return function (event) {
if (event) {
event.preventDefault()
}
fooInstance.close(this)
}
}
} }
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(document).on(
Event.CLICK_DATA_API,
Selector.DATA_DISMISS,
Foo._handleClose(new Foo())
)
/** /**
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
* jQuery * jQuery

View File

@ -1,3 +1,5 @@
// FIXME: look at bootstrap/Util.js for transition support functions
const Ripples = (($) => { const Ripples = (($) => {
/** /**
@ -8,7 +10,17 @@ const Ripples = (($) => {
const NAME = 'ripples' const NAME = 'ripples'
const DATA_KEY = `bmd.${NAME}` const DATA_KEY = `bmd.${NAME}`
const JQUERY_NO_CONFLICT = $.fn[NAME] const JQUERY_NO_CONFLICT = $.fn[NAME]
const DEFAULT_OPTIONS = {}
const Default = {
containerSelector: '.ripple-container',
rippleSelector: 'div.ripple',
containerTemplate: `<div class='ripple-container'></div>`,
rippleTemplate: `<div class='ripple'></div>`,
touchUserAgentRegex: /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i,
triggerStart: 'mousedown touchstart',
triggerEnd: 'mouseup mouseleave touchend',
duration: 500
}
/** /**
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
@ -17,18 +29,20 @@ const Ripples = (($) => {
*/ */
class Ripples { class Ripples {
constructor(element, options) { constructor(element, config) {
this._element = $(element) this.element = $(element)
this._options = $.extend({}, DEFAULT_OPTIONS, options) this.config = $.extend({}, Default, config)
this._element.on("mousedown touchstart", this._onStartRipple)
// attach initial listener
this.element.on(this.config.triggerStart, this._onStartRipple)
} }
dispose() { dispose() {
$.removeData(this._element, DATA_KEY) $.removeData(this.element, DATA_KEY)
this._element = null this.element = null
this._containerElement = null this.containerElement = null
this._rippleElement = null this.rippleElement = null
this._options = null this.config = null
} }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@ -54,7 +68,7 @@ const Ripples = (($) => {
} }
// set the location and color each time (even if element is cached) // set the location and color each time (even if element is cached)
this._rippleElement.addClass("ripple").css({ this.rippleElement.css({
"left": relX, "left": relX,
"top": relY, "top": relY,
"background-color": this._getRipplesColor() "background-color": this._getRipplesColor()
@ -69,38 +83,39 @@ const Ripples = (($) => {
// Call the rippleEnd function when the transition "on" ends // Call the rippleEnd function when the transition "on" ends
setTimeout(() => { setTimeout(() => {
this.rippleEnd() this.rippleEnd()
}, 500) }, this.config.duration)
// Detect when the user leaves the element (attach only when necessary for performance) // Detect when the user leaves the element (attach only when necessary for performance)
this._element.on("mouseup mouseleave touchend", () => { this.element.on(this.config.triggerEnd, () => {
this._rippleElement.data("mousedown", "off") this.rippleElement.data("mousedown", "off")
if (this._rippleElement.data("animating") === "off") { if (this.rippleElement.data("animating") === "off") {
this.rippleOut() this.rippleOut()
} }
}) })
} }
_findOrCreateContainer() { _findOrCreateContainer() {
if (!this._containerElement || !this._containerElement.length > 0) { if (!this.containerElement || !this.containerElement.length > 0) {
this._element.append("<div class='ripple-container'><div class='ripple'></div></div>") this.element.append(this.config.containerTemplate)
this._containerElement = this._element.find(".ripple-container") this.containerElement = this.element.find(this.config.containerSelector)
this._rippleElement = this._containerElement.find("div.ripple")
} }
return this._containerElement // always add the rippleElement, it is always removed
this.containerElement.append(this.config.rippleTemplate)
this.rippleElement = this.containerElement.find(this.config.rippleSelector)
} }
// Make sure the ripple has the styles applied (ugly hack but it works) // Make sure the ripple has the styles applied (ugly hack but it works)
_forceStyleApplication() { _forceStyleApplication() {
return window.getComputedStyle(this._rippleElement[0]).opacity return window.getComputedStyle(this.rippleElement[0]).opacity
} }
/** /**
* Get the relX * Get the relX
*/ */
_getRelX(event) { _getRelX(event) {
let wrapperOffset = this._containerElement.offset() let wrapperOffset = this.containerElement.offset()
let result = null let result = null
if (!this.isTouch()) { if (!this.isTouch()) {
@ -125,7 +140,7 @@ const Ripples = (($) => {
* Get the relY * Get the relY
*/ */
_getRelY(event) { _getRelY(event) {
let containerOffset = this._containerElement.offset() let containerOffset = this.containerElement.offset()
let result = null let result = null
if (!this.isTouch()) { if (!this.isTouch()) {
@ -154,43 +169,25 @@ const Ripples = (($) => {
* Get the ripple color * Get the ripple color
*/ */
_getRipplesColor() { _getRipplesColor() {
let color = this._element.data("ripple-color") ? this._element.data("ripple-color") : window.getComputedStyle(this._element[0]).color let color = this.element.data("ripple-color") ? this.element.data("ripple-color") : window.getComputedStyle(this.element[0]).color
return color return color
} }
/**
* Verify if the client browser has transistion support
*/
_hasTransitionSupport() {
let thisBody = document.body || document.documentElement
let thisStyle = thisBody.style
let support = (
thisStyle.transition !== undefined ||
thisStyle.WebkitTransition !== undefined ||
thisStyle.MozTransition !== undefined ||
thisStyle.MsTransition !== undefined ||
thisStyle.OTransition !== undefined
)
return support
}
/** /**
* Verify if the client is using a mobile device * Verify if the client is using a mobile device
*/ */
isTouch() { isTouch() {
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) return this.config.touchUserAgentRegex.test(navigator.userAgent)
} }
/** /**
* End the animation of the ripple * End the animation of the ripple
*/ */
rippleEnd() { rippleEnd() {
this._rippleElement.data("animating", "off") this.rippleElement.data("animating", "off")
if (this._rippleElement.data("mousedown") === "off") { if (this.rippleElement.data("mousedown") === "off") {
this.rippleOut(this._rippleElement) this.rippleOut(this.rippleElement)
} }
} }
@ -198,18 +195,19 @@ const Ripples = (($) => {
* Turn off the ripple effect * Turn off the ripple effect
*/ */
rippleOut() { rippleOut() {
this._rippleElement.off() this.rippleElement.off()
if (this._hasTransitionSupport()) { if ($.transitionEndSupported()) {
this._rippleElement.addClass("ripple-out") this.rippleElement.addClass("ripple-out")
} else { } else {
this._rippleElement.animate({ "opacity": 0 }, 100, () => { this.rippleElement.animate({ "opacity": 0 }, 100, () => {
this._rippleElement.trigger("transitionend") this.rippleElement.triggerStart("transitionend")
}) })
} }
this._rippleElement.on("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd", () => { this.rippleElement.on($.transitionEndSelector(), () => {
this._rippleElement.remove() this.rippleElement.remove()
this.rippleElement = null
}) })
} }
@ -219,8 +217,8 @@ const Ripples = (($) => {
rippleOn() { rippleOn() {
let size = this._getNewSize() let size = this._getNewSize()
if (this._hasTransitionSupport()) { if ($.transitionEndSupported()) {
this._rippleElement this.rippleElement
.css({ .css({
"-ms-transform": `scale(${size})`, "-ms-transform": `scale(${size})`,
"-moz-transform": `scale(${size})`, "-moz-transform": `scale(${size})`,
@ -231,14 +229,14 @@ const Ripples = (($) => {
.data("animating", "on") .data("animating", "on")
.data("mousedown", "on") .data("mousedown", "on")
} else { } else {
this._rippleElement.animate({ this.rippleElement.animate({
"width": Math.max(this._element.outerWidth(), this._element.outerHeight()) * 2, "width": Math.max(this.element.outerWidth(), this.element.outerHeight()) * 2,
"height": Math.max(this._element.outerWidth(), this._element.outerHeight()) * 2, "height": Math.max(this.element.outerWidth(), this.element.outerHeight()) * 2,
"margin-left": Math.max(this._element.outerWidth(), this._element.outerHeight()) * (-1), "margin-left": Math.max(this.element.outerWidth(), this.element.outerHeight()) * (-1),
"margin-top": Math.max(this._element.outerWidth(), this._element.outerHeight()) * (-1), "margin-top": Math.max(this.element.outerWidth(), this.element.outerHeight()) * (-1),
"opacity": 0.2 "opacity": 0.2
}, 500, () => { }, this.config.duration, () => {
this._rippleElement.trigger("transitionend") this.rippleElement.triggerStart("transitionend")
}) })
} }
} }
@ -247,7 +245,7 @@ const Ripples = (($) => {
* Get the new size based on the element height/width and the ripple width * Get the new size based on the element height/width and the ripple width
*/ */
_getNewSize() { _getNewSize() {
return (Math.max(this._element.outerWidth(), this._element.outerHeight()) / this._rippleElement.outerWidth()) * 2.5 return (Math.max(this.element.outerWidth(), this.element.outerHeight()) / this.rippleElement.outerWidth()) * 2.5
} }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------

82
js/src/util.js Normal file
View File

@ -0,0 +1,82 @@
const Util = (($) => {
/**
* ------------------------------------------------------------------------
* Private TransitionEnd Helpers
* ------------------------------------------------------------------------
*/
let transitionEnd = false
let transitionEndSelector = ""
const TransitionEndEvent = {
WebkitTransition: 'webkitTransitionEnd',
MozTransition: 'transitionend',
OTransition: 'oTransitionEnd otransitionend',
transition: 'transitionend'
}
function transitionEndTest() {
if (window.QUnit) {
return false
}
let el = document.createElement('mdb')
for (let name in TransitionEndEvent) {
if (el.style[name] !== undefined) {
return TransitionEndEvent[name] //{ end: TransitionEndEvent[name] }
}
}
return false
}
function setTransitionEndSupport() {
transitionEnd = transitionEndTest()
$.fn.transitionEndSupported = () => {
return transitionEnd
}
// generate a selector
for (let name in TransitionEndEvent) {
transitionEndSelector += ` ${TransitionEndEvent[name]}`
}
$.fn.transitionEndSelector = () => {
return transitionEndSelector
} // FIXME: make this a Util.* method instead?
}
/**
* --------------------------------------------------------------------------
* Public Util Api
* --------------------------------------------------------------------------
*/
let Util = {
isChar(evt) {
if (typeof evt.which == "undefined") {
return true
} else if (typeof evt.which == "number" && evt.which > 0) {
return !evt.ctrlKey && !evt.metaKey && !evt.altKey && evt.which != 8 && evt.which != 9
}
else {
return false
}
},
/* /!**
* Verify if the client browser has transistion support
*!/
hasTransitionSupport() {
return transition
}*/
}
setTransitionEndSupport()
return Util
})(jQuery)
export default Util