Created
January 22, 2016 16:01
-
-
Save foleyatwork/83a54322e3ff863c5519 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* globals Ok, _, ReactDOM, ENV, Mousetrap */ | |
const OkModalManager = require("~/okmodal/util/OkModalManager"); | |
const App = require("~/okmodal/components/App"); | |
const C = require("~/okmodal/util/Constants"); | |
/** | |
* Log an error string to the console with a styling OkModal prefix. | |
* @method logSimpleError | |
* @param {String} message | |
*/ | |
function logSimpleError(message) { | |
if (!message || ENV === "prod") { | |
return; | |
} | |
console.error("%cOkModal:", "font-weight: bold; font-style: italic;", message); | |
} | |
/** | |
* Creates a new instance of a modal and renders it inside the target wrapper. | |
* Most of the time you won't need to get all fancy, just create a new OkModal | |
* (as shown in the example below) and don't do anything else, you don't even | |
* really need to store the instance. | |
* | |
* To close the OkModal manually, run OkModalManager.closeAll() | |
* | |
* @class OkModal | |
* @param {Object} config | |
* @example | |
* // Default values are filled in below. | |
* new OkModal({ | |
* body: "", | |
* ctaLink: "#", // Required, if onAction isn't defined. | |
* ctaText: "Learn more", | |
* headline: "", // Required | |
* image: "", // Required | |
* onAction: function(){}, // Required, if ctaLink isn't defined. | |
* onClose: function(){}, | |
* subheadline: "", | |
* windowshade: true, | |
* }); | |
*/ | |
class OkModal { | |
constructor(config) { | |
if (!config.headline || (!config.ctaLink && !config.onAction)) { | |
logSimpleError("Not enough information to initiate modal."); | |
return; | |
} | |
this._active = true; | |
this.config = _.extend(C.DEFAULT_CONFIG, config); | |
// Set the default target node. | |
// This has to be done here instead of stored in constants becase the | |
// node isn't on the page when Constants.coffee is pulled in. | |
if (!this.config.target) { | |
this.config.target = document.getElementById(C.CSS_PREFIX); | |
} | |
if (!this.config.target) { | |
logSimpleError("Couldn't find the target element. Pass an HTMLElement in the \"target\" prop."); | |
return; | |
} | |
this._modalID = `modal-${Date.now()}`; | |
OkModalManager.add(this._modalID, this); | |
this.open(); | |
} | |
destructor() { | |
// Make sure the destructor runs once and only once. This is here so devs | |
// can indiscriminately call OkModalManager.closeAll(). | |
if (this._active === false) { | |
return; | |
} | |
this._active = false; | |
this.fadeOut(() => { | |
this.config.onClose(); | |
// Remove the node from OkModalManager. | |
OkModalManager.remove(this._modalID); | |
// Remove event listeners. | |
this._modalNode.removeEventListener("click", (e) => { | |
this._handleMouseClick(e); | |
}); | |
Mousetrap.unbind("escape"); | |
// Remove markup. | |
this.config.target.removeChild(this._modalNode); | |
// Null out variables to hint to the browser they're ready for GC. | |
this.config = null; | |
this._modalID = null; | |
this._modalNode = null; | |
this._active = null; | |
}); | |
} | |
/** | |
* It's usually better to run OkModalManager.closeAll() but if you do need to | |
* close a specific modal, use this method. | |
* | |
* @method close | |
*/ | |
close() { | |
this.destructor(); | |
} | |
/** | |
* Fade the modal in then run a callback function. | |
* | |
* @method fadeIn | |
* @param {Function} callback | |
*/ | |
fadeIn(callback) { | |
this._modalNode.className += " is-visible"; | |
setTimeout(callback, C.FADE_OUT_TIME); | |
} | |
/** | |
* Fade the modal out then run a callback function. | |
* | |
* @method fadeOut | |
* @param {Function} callback | |
*/ | |
fadeOut(callback) { | |
this._modalNode.className = this._modalNode.className.replace(/\ ?is\-visible/, ""); | |
setTimeout(callback, C.FADE_OUT_TIME); | |
} | |
/** | |
* Opens the modal. | |
* | |
* @method open | |
*/ | |
open() { | |
this._modalNode = document.getElementById(this._modalID); | |
if (!this._modalNode) { | |
this._modalNode = document.createElement("div"); | |
this._modalNode.id = this._modalID; | |
this._modalNode.className = C.CSS_PREFIX + "-inner"; | |
this.config.target.appendChild(this._modalNode); | |
} | |
ReactDOM.render(<App {...this.config} />, this._modalNode); | |
setTimeout(() => { | |
this._modalNode.className += " is-visible"; | |
}, 50); | |
this._modalNode.addEventListener("click", (e) => { | |
this._handleMouseClick(e); | |
}); | |
Mousetrap.bind("escape", this.close.bind(this)); | |
} | |
/** | |
* Callback for delegated click events on the modal screen. | |
* | |
* @method _handleMouseClick | |
* @param {Object} e | |
*/ | |
_handleMouseClick(e) { | |
if ( | |
e.target.className.indexOf("FullscreenOverlay") > -1 || | |
e.target.parentNode.className.indexOf(`${C.CSS_PREFIX}Content-dismiss`) > -1 | |
) { | |
this.close(); | |
} | |
} | |
} | |
// For legacy JS, Jan. 4 2016. Once we can use require() everywhere this is no | |
// longer necessary. | |
Ok.OkModal = OkModal; | |
/** @exports OkModal */ | |
module.exports = OkModal; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment