From a9a4ea4ed337fab8fb95bd1a13e3ae442bddf018 Mon Sep 17 00:00:00 2001 From: Sam Thorogood Date: Mon, 27 Jul 2020 10:35:19 +1000 Subject: [PATCH] support oncancel and onclose --- dist/dialog-polyfill.esm.js | 28 +++++++++++++++++++++++----- dist/dialog-polyfill.js | 32 +++++++++++++++++++++++++------- index.js | 20 ++++++++++++++++++-- suite.js | 15 ++++++++++++++- 4 files changed, 80 insertions(+), 15 deletions(-) diff --git a/dist/dialog-polyfill.esm.js b/dist/dialog-polyfill.esm.js index f52b58e..ec5c985 100644 --- a/dist/dialog-polyfill.esm.js +++ b/dist/dialog-polyfill.esm.js @@ -10,6 +10,22 @@ if (!supportCustomEvent || typeof supportCustomEvent === 'object') { supportCustomEvent.prototype = window.Event.prototype; } +/** + * Dispatches the passed event to both an "on" handler as well as via the + * normal dispatch operation. Does not bubble. + * + * @param {!EventTarget} target + * @param {!Event} event + * @return {boolean} + */ +function safeDispatchEvent(target, event) { + var check = 'on' + event.type.toLowerCase(); + if (typeof target[check] === 'function') { + target[check](event); + } + return target.dispatchEvent(event); +} + /** * @param {Element} el to check for stacking context * @return {boolean} whether this el or its parents creates a stacking context @@ -193,7 +209,9 @@ function dialogPolyfillInfo(dialog) { this.backdrop_ = document.createElement('div'); this.backdrop_.className = 'backdrop'; - this.backdrop_.addEventListener('click', this.backdropClick_.bind(this)); + this.backdrop_.addEventListener('mouseup' , this.backdropMouseEvent_.bind(this)); + this.backdrop_.addEventListener('mousedown', this.backdropMouseEvent_.bind(this)); + this.backdrop_.addEventListener('click' , this.backdropMouseEvent_.bind(this)); } dialogPolyfillInfo.prototype = /** @type {HTMLDialogElement.prototype} */ ({ @@ -246,12 +264,12 @@ dialogPolyfillInfo.prototype = /** @type {HTMLDialogElement.prototype} */ ({ }, /** - * Handles clicks on the fake .backdrop element, redirecting them as if + * Handles mouse events ('mouseup', 'mousedown', 'click') on the fake .backdrop element, redirecting them as if * they were on the dialog itself. * * @param {!Event} e to redirect */ - backdropClick_: function(e) { + backdropMouseEvent_: function(e) { if (!this.dialog_.hasAttribute('tabindex')) { // Clicking on the backdrop should move the implicit cursor, even if dialog cannot be // focused. Create a fake thing to focus on. If the backdrop was _before_ the dialog, this @@ -374,7 +392,7 @@ dialogPolyfillInfo.prototype = /** @type {HTMLDialogElement.prototype} */ ({ bubbles: false, cancelable: false }); - this.dialog_.dispatchEvent(closeEvent); + safeDispatchEvent(this.dialog_, closeEvent); } }); @@ -602,7 +620,7 @@ dialogPolyfill.DialogManager.prototype.handleKey_ = function(event) { cancelable: true }); var dpi = this.pendingDialogStack[0]; - if (dpi && dpi.dialog.dispatchEvent(cancelEvent)) { + if (dpi && safeDispatchEvent(dpi.dialog, cancelEvent)) { dpi.dialog.close(); } } else if (event.keyCode === 9) { diff --git a/dist/dialog-polyfill.js b/dist/dialog-polyfill.js index 6acda90..8b5d7f2 100644 --- a/dist/dialog-polyfill.js +++ b/dist/dialog-polyfill.js @@ -2,7 +2,7 @@ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global = global || self, global.dialogPolyfill = factory()); -}(this, function () { 'use strict'; +}(this, (function () { 'use strict'; // nb. This is for IE10 and lower _only_. var supportCustomEvent = window.CustomEvent; @@ -16,6 +16,22 @@ supportCustomEvent.prototype = window.Event.prototype; } + /** + * Dispatches the passed event to both an "on" handler as well as via the + * normal dispatch operation. Does not bubble. + * + * @param {!EventTarget} target + * @param {!Event} event + * @return {boolean} + */ + function safeDispatchEvent(target, event) { + var check = 'on' + event.type.toLowerCase(); + if (typeof target[check] === 'function') { + target[check](event); + } + return target.dispatchEvent(event); + } + /** * @param {Element} el to check for stacking context * @return {boolean} whether this el or its parents creates a stacking context @@ -199,7 +215,9 @@ this.backdrop_ = document.createElement('div'); this.backdrop_.className = 'backdrop'; - this.backdrop_.addEventListener('click', this.backdropClick_.bind(this)); + this.backdrop_.addEventListener('mouseup' , this.backdropMouseEvent_.bind(this)); + this.backdrop_.addEventListener('mousedown', this.backdropMouseEvent_.bind(this)); + this.backdrop_.addEventListener('click' , this.backdropMouseEvent_.bind(this)); } dialogPolyfillInfo.prototype = /** @type {HTMLDialogElement.prototype} */ ({ @@ -252,12 +270,12 @@ }, /** - * Handles clicks on the fake .backdrop element, redirecting them as if + * Handles mouse events ('mouseup', 'mousedown', 'click') on the fake .backdrop element, redirecting them as if * they were on the dialog itself. * * @param {!Event} e to redirect */ - backdropClick_: function(e) { + backdropMouseEvent_: function(e) { if (!this.dialog_.hasAttribute('tabindex')) { // Clicking on the backdrop should move the implicit cursor, even if dialog cannot be // focused. Create a fake thing to focus on. If the backdrop was _before_ the dialog, this @@ -380,7 +398,7 @@ bubbles: false, cancelable: false }); - this.dialog_.dispatchEvent(closeEvent); + safeDispatchEvent(this.dialog_, closeEvent); } }); @@ -608,7 +626,7 @@ cancelable: true }); var dpi = this.pendingDialogStack[0]; - if (dpi && dpi.dialog.dispatchEvent(cancelEvent)) { + if (dpi && safeDispatchEvent(dpi.dialog, cancelEvent)) { dpi.dialog.close(); } } else if (event.keyCode === 9) { @@ -776,4 +794,4 @@ return dialogPolyfill; -})); +}))); diff --git a/index.js b/index.js index c303563..b8da809 100644 --- a/index.js +++ b/index.js @@ -11,6 +11,22 @@ if (!supportCustomEvent || typeof supportCustomEvent === 'object') { supportCustomEvent.prototype = window.Event.prototype; } +/** + * Dispatches the passed event to both an "on" handler as well as via the + * normal dispatch operation. Does not bubble. + * + * @param {!EventTarget} target + * @param {!Event} event + * @return {boolean} + */ +function safeDispatchEvent(target, event) { + var check = 'on' + event.type.toLowerCase(); + if (typeof target[check] === 'function') { + target[check](event); + } + return target.dispatchEvent(event); +} + /** * @param {Element} el to check for stacking context * @return {boolean} whether this el or its parents creates a stacking context @@ -377,7 +393,7 @@ dialogPolyfillInfo.prototype = /** @type {HTMLDialogElement.prototype} */ ({ bubbles: false, cancelable: false }); - this.dialog_.dispatchEvent(closeEvent); + safeDispatchEvent(this.dialog_, closeEvent); } }); @@ -607,7 +623,7 @@ dialogPolyfill.DialogManager.prototype.handleKey_ = function(event) { cancelable: true }); var dpi = this.pendingDialogStack[0]; - if (dpi && dpi.dialog.dispatchEvent(cancelEvent)) { + if (dpi && safeDispatchEvent(dpi.dialog, cancelEvent)) { dpi.dialog.close(); } } else if (event.keyCode === 9) { diff --git a/suite.js b/suite.js index 129bda8..4d8fab1 100644 --- a/suite.js +++ b/suite.js @@ -532,7 +532,7 @@ void function() { dialog.showModal(); dialog.dispatchEvent(createKeyboardEvent(27)); assert.equal(cancelFired, 1, 'expected cancel to be fired'); - assert.isFalse(dialog.open), 'esc should close modal again'; + assert.isFalse(dialog.open, 'esc should close modal again'); // Sanity-check that non-modals aren't effected. dialog.show(); @@ -540,6 +540,19 @@ void function() { assert.isTrue(dialog.open, 'esc should only close modal dialog'); assert.equal(cancelFired, 1); }); + test('cancel event via oncancel', function() { + dialog.showModal(); + + var cancelFired = 0; + dialog.oncancel = function(event) { + ++cancelFired; + event.preventDefault(); + }; + + dialog.dispatchEvent(createKeyboardEvent(27)); + assert.equal(cancelFired, 1, 'expected cancel to be fired'); + assert.isTrue(dialog.open, 'behavior should be prevented'); + }); test('overlay click is prevented', function() { dialog.showModal();