From 8cb4f02d635937e3b211345d5cf093a1fd2fc4d1 Mon Sep 17 00:00:00 2001 From: Kevin Ross Date: Mon, 30 Nov 2015 17:59:43 -0600 Subject: [PATCH] ripples es6 refactoring with a util class --- Gruntfile.js | 3 + js/src/old/es6Template.js | 69 +++------------------ js/src/ripples.js | 124 +++++++++++++++++++------------------- js/src/util.js | 82 +++++++++++++++++++++++++ 4 files changed, 153 insertions(+), 125 deletions(-) create mode 100644 js/src/util.js diff --git a/Gruntfile.js b/Gruntfile.js index adee7e46..bd3fedd2 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -86,6 +86,7 @@ module.exports = function (grunt) { modules: 'ignore' }, files: { + 'js/dist/util.js' : 'js/src/util.js', 'js/dist/ripples.js' : 'js/src/ripples.js' //, //'js/dist/alert.js' : 'js/src/alert.js', //'js/dist/button.js' : 'js/src/button.js', @@ -112,6 +113,7 @@ module.exports = function (grunt) { modules: 'umd' }, files: { + 'dist/js/umd/util.js' : 'js/src/util.js', 'dist/js/umd/ripples.js' : 'js/src/ripples.js' //, //'dist/js/umd/alert.js' : 'js/src/alert.js', //'dist/js/umd/button.js' : 'js/src/button.js', @@ -173,6 +175,7 @@ module.exports = function (grunt) { }, bootstrap: { src: [ + 'js/src/util.js', 'js/src/ripples.js', //'js/src/alert.js', //'js/src/button.js', diff --git a/js/src/old/es6Template.js b/js/src/old/es6Template.js index f59ec092..cd569e81 100644 --- a/js/src/old/es6Template.js +++ b/js/src/old/es6Template.js @@ -1,5 +1,6 @@ -const Foo = (($) => { +import Util from './util' +const Foo = (($) => { /** * ------------------------------------------------------------------------ @@ -8,21 +9,8 @@ const Foo = (($) => { */ const NAME = 'foo' const DATA_KEY = `bmd.${NAME}` - const EVENT_KEY = `.${DATA_KEY}` - const DATA_API_KEY = '.data-api' 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 @@ -31,34 +19,15 @@ const Foo = (($) => { class Foo { constructor(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) + this.element = element } dispose() { - $.removeData(this._element, DATA_KEY) - this._element = null + $.removeData(this.element, DATA_KEY) + this.element = null } - + // ------------------------------------------------------------------------ // private _bar(element) { @@ -66,7 +35,7 @@ const Foo = (($) => { return x } - + // ------------------------------------------------------------------------ // static static _jQueryInterface(config) { 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 diff --git a/js/src/ripples.js b/js/src/ripples.js index 1c3ff9a2..f9d5b1cf 100644 --- a/js/src/ripples.js +++ b/js/src/ripples.js @@ -1,3 +1,5 @@ +// FIXME: look at bootstrap/Util.js for transition support functions + const Ripples = (($) => { /** @@ -8,7 +10,17 @@ const Ripples = (($) => { const NAME = 'ripples' const DATA_KEY = `bmd.${NAME}` const JQUERY_NO_CONFLICT = $.fn[NAME] - const DEFAULT_OPTIONS = {} + + const Default = { + containerSelector: '.ripple-container', + rippleSelector: 'div.ripple', + containerTemplate: `
`, + rippleTemplate: `
`, + 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 { - constructor(element, options) { - this._element = $(element) - this._options = $.extend({}, DEFAULT_OPTIONS, options) - this._element.on("mousedown touchstart", this._onStartRipple) + constructor(element, config) { + this.element = $(element) + this.config = $.extend({}, Default, config) + + // attach initial listener + this.element.on(this.config.triggerStart, this._onStartRipple) } dispose() { - $.removeData(this._element, DATA_KEY) - this._element = null - this._containerElement = null - this._rippleElement = null - this._options = null + $.removeData(this.element, DATA_KEY) + this.element = null + this.containerElement = null + this.rippleElement = null + this.config = null } // ------------------------------------------------------------------------ @@ -54,7 +68,7 @@ const Ripples = (($) => { } // set the location and color each time (even if element is cached) - this._rippleElement.addClass("ripple").css({ + this.rippleElement.css({ "left": relX, "top": relY, "background-color": this._getRipplesColor() @@ -69,38 +83,39 @@ const Ripples = (($) => { // Call the rippleEnd function when the transition "on" ends setTimeout(() => { this.rippleEnd() - }, 500) + }, this.config.duration) // Detect when the user leaves the element (attach only when necessary for performance) - this._element.on("mouseup mouseleave touchend", () => { - this._rippleElement.data("mousedown", "off") + this.element.on(this.config.triggerEnd, () => { + this.rippleElement.data("mousedown", "off") - if (this._rippleElement.data("animating") === "off") { + if (this.rippleElement.data("animating") === "off") { this.rippleOut() } }) } _findOrCreateContainer() { - if (!this._containerElement || !this._containerElement.length > 0) { - this._element.append("
") - this._containerElement = this._element.find(".ripple-container") - this._rippleElement = this._containerElement.find("div.ripple") + if (!this.containerElement || !this.containerElement.length > 0) { + this.element.append(this.config.containerTemplate) + this.containerElement = this.element.find(this.config.containerSelector) } - 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) _forceStyleApplication() { - return window.getComputedStyle(this._rippleElement[0]).opacity + return window.getComputedStyle(this.rippleElement[0]).opacity } /** * Get the relX */ _getRelX(event) { - let wrapperOffset = this._containerElement.offset() + let wrapperOffset = this.containerElement.offset() let result = null if (!this.isTouch()) { @@ -125,7 +140,7 @@ const Ripples = (($) => { * Get the relY */ _getRelY(event) { - let containerOffset = this._containerElement.offset() + let containerOffset = this.containerElement.offset() let result = null if (!this.isTouch()) { @@ -154,43 +169,25 @@ const Ripples = (($) => { * Get the ripple color */ _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 } - /** - * 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 */ 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 */ rippleEnd() { - this._rippleElement.data("animating", "off") + this.rippleElement.data("animating", "off") - if (this._rippleElement.data("mousedown") === "off") { - this.rippleOut(this._rippleElement) + if (this.rippleElement.data("mousedown") === "off") { + this.rippleOut(this.rippleElement) } } @@ -198,18 +195,19 @@ const Ripples = (($) => { * Turn off the ripple effect */ rippleOut() { - this._rippleElement.off() + this.rippleElement.off() - if (this._hasTransitionSupport()) { - this._rippleElement.addClass("ripple-out") + if ($.transitionEndSupported()) { + this.rippleElement.addClass("ripple-out") } else { - this._rippleElement.animate({ "opacity": 0 }, 100, () => { - this._rippleElement.trigger("transitionend") + this.rippleElement.animate({ "opacity": 0 }, 100, () => { + this.rippleElement.triggerStart("transitionend") }) } - this._rippleElement.on("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd", () => { - this._rippleElement.remove() + this.rippleElement.on($.transitionEndSelector(), () => { + this.rippleElement.remove() + this.rippleElement = null }) } @@ -219,8 +217,8 @@ const Ripples = (($) => { rippleOn() { let size = this._getNewSize() - if (this._hasTransitionSupport()) { - this._rippleElement + if ($.transitionEndSupported()) { + this.rippleElement .css({ "-ms-transform": `scale(${size})`, "-moz-transform": `scale(${size})`, @@ -231,14 +229,14 @@ const Ripples = (($) => { .data("animating", "on") .data("mousedown", "on") } else { - this._rippleElement.animate({ - "width": 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-top": Math.max(this._element.outerWidth(), this._element.outerHeight()) * (-1), + this.rippleElement.animate({ + "width": 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-top": Math.max(this.element.outerWidth(), this.element.outerHeight()) * (-1), "opacity": 0.2 - }, 500, () => { - this._rippleElement.trigger("transitionend") + }, this.config.duration, () => { + this.rippleElement.triggerStart("transitionend") }) } } @@ -247,7 +245,7 @@ const Ripples = (($) => { * Get the new size based on the element height/width and the ripple width */ _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 } // ------------------------------------------------------------------------ diff --git a/js/src/util.js b/js/src/util.js new file mode 100644 index 00000000..cfe8f6e4 --- /dev/null +++ b/js/src/util.js @@ -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