diff --git a/h5p.classes.php b/h5p.classes.php index 5ede697..395e7a4 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -1664,6 +1664,8 @@ class H5PCore { ); public static $styles = array( 'styles/h5p.css', + 'styles/h5p-confirmation-dialog.css', + 'styles/h5p-core-button.css' ); public static $scripts = array( 'js/jquery.js', @@ -1672,6 +1674,7 @@ class H5PCore { 'js/h5p-x-api-event.js', 'js/h5p-x-api.js', 'js/h5p-content-type.js', + 'js/h5p-confirmation-dialog.js' ); public static $adminScripts = array( 'js/jquery.js', diff --git a/js/h5p-confirmation-dialog.js b/js/h5p-confirmation-dialog.js new file mode 100644 index 0000000..7675d06 --- /dev/null +++ b/js/h5p-confirmation-dialog.js @@ -0,0 +1,162 @@ +/*global H5P*/ +H5P.ConfirmationDialog = (function (EventDispatcher) { + "use strict"; + + /** + * Create a confirmation dialog + * + * @param [options] Options for confirmation dialog + * @param [options.headerText] Header text + * @param [options.dialogText] Dialog text + * @param [options.cancelText] Cancel dialog button text + * @param [options.confirmText] Confirm dialog button text + * @constructor + */ + function ConfirmationDialog(options) { + EventDispatcher.call(this); + var self = this; + + // Default options + options = options || {}; + options.headerText = options.headerText || + 'Confirm action'; + options.dialogText = options.dialogText || + 'Please confirm that you wish to proceed. This action will not be reversible.'; + options.cancelText = options.cancelText || 'Cancel'; + options.confirmText = options.confirmText || 'Confirm'; + + // Create background + var popupBackground = document.createElement('div'); + popupBackground.classList + .add('h5p-confirmation-dialog-background', 'hidden', 'hiding'); + + // Create outer popup + var popup = document.createElement('div'); + popup.classList.add('h5p-confirmation-dialog-popup', 'hidden'); + popupBackground.appendChild(popup); + + // Popup header + var header = document.createElement('div'); + header.classList.add('h5p-confirmation-dialog-header'); + popup.appendChild(header); + + // Header text + var headerText = document.createElement('div'); + headerText.classList.add('h5p-confirmation-dialog-header-text'); + headerText.innerHTML = options.headerText; + header.appendChild(headerText); + + // Popup body + var body = document.createElement('div'); + body.classList.add('h5p-confirmation-dialog-body'); + popup.appendChild(body); + + // Popup text + var text = document.createElement('div'); + text.classList.add('h5p-confirmation-dialog-text'); + text.innerHTML = options.dialogText; + body.appendChild(text); + + // Popup buttons + var buttons = document.createElement('div'); + buttons.classList.add('h5p-confirmation-dialog-buttons'); + body.appendChild(buttons); + + // Cancel button + var cancelButton = document.createElement('button'); + cancelButton.classList.add('h5p-core-cancel-button'); + cancelButton.tabindex = 0; + cancelButton.textContent = options.cancelText; + cancelButton.onclick = function () { + self.hide(); + self.trigger('canceled'); + }; + buttons.appendChild(cancelButton); + + // Confirm button + var confirmButton = document.createElement('button'); + confirmButton.classList.add('h5p-core-button', + 'h5p-confirmation-dialog-confirm-button'); + confirmButton.tabindex = 0; + confirmButton.textContent = options.confirmText; + confirmButton.onclick = function () { + self.hide(); + self.trigger('confirmed'); + }; + buttons.appendChild(confirmButton); + + // Exit button + var exitButton = document.createElement('button'); + exitButton.classList.add('h5p-confirmation-dialog-exit'); + exitButton.tabindex = 0; + exitButton.onclick = function () { + self.hide(); + self.trigger('canceled'); + }; + popup.appendChild(exitButton); + + // Wrapper element + var wrapperElement; + + /** + * Append confirmation dialog + * @param {HTMLElement} wrapper + * @returns {H5P.ConfirmationDialog} + */ + this.appendTo = function (wrapper) { + wrapper.appendChild(popupBackground); + wrapperElement = wrapper; + + return this; + }; + + /** + * Fit popup to container. Makes sure it doesn't overflow. + */ + var fitToContainer = function () { + var popupOffset = parseInt(popup.style.top, 10) + popup.offsetHeight; + + // Overflows wrapper + if (popupOffset > wrapperElement.offsetHeight) { + var overflowedContainerOffset = wrapperElement.offsetHeight - popup.offsetHeight; + popup.style.top = overflowedContainerOffset + 'px'; + } + }; + + /** + * Show confirmation dialog + * @params {number} offsetTop Offset top + * @returns {H5P.ConfirmationDialog} + */ + this.show = function (offsetTop) { + popup.style.top = offsetTop + 'px'; + popupBackground.classList.remove('hidden'); + fitToContainer(); + popupBackground.classList.remove('hiding'); + popup.classList.remove('hidden'); + cancelButton.focus(); + + return this; + }; + + /** + * Hide confirmation dialog + * @returns {H5P.ConfirmationDialog} + */ + this.hide = function () { + popupBackground.classList.add('hiding'); + popup.classList.add('hidden'); + setTimeout(function () { + popupBackground.classList.add('hidden'); + }, 100); + + return this; + }; + } + + ConfirmationDialog.prototype = Object.create(EventDispatcher.prototype); + ConfirmationDialog.prototype.constructor = ConfirmationDialog; + + return ConfirmationDialog; + +}(H5P.EventDispatcher)); diff --git a/js/h5p-event-dispatcher.js b/js/h5p-event-dispatcher.js index fb8dd66..592e8cf 100644 --- a/js/h5p-event-dispatcher.js +++ b/js/h5p-event-dispatcher.js @@ -99,7 +99,7 @@ H5P.EventDispatcher = (function () { * Event type * @param {H5P.EventCallback} listener * Event listener - * @param {Object} thisArg + * @param {Object} [thisArg] * Optionally specify the this value when calling listener. */ this.on = function (type, listener, thisArg) { diff --git a/styles/h5p-confirmation-dialog.css b/styles/h5p-confirmation-dialog.css new file mode 100644 index 0000000..b7b4475 --- /dev/null +++ b/styles/h5p-confirmation-dialog.css @@ -0,0 +1,103 @@ +.h5p-confirmation-dialog-background { + position: absolute; + height: 100%; + width: 100%; + left: 0; + top: 0; + + background: rgba(255, 255, 255, 0.85); + opacity: 1; + visibility: visible; + -webkit-transition: opacity 0.1s, linear 0s, visibility 0s linear 0s; + transition: opacity 0.1s linear 0s, visibility 0s linear 0s; + + z-index: 11; +} + +.h5p-confirmation-dialog-background.hidden { + display: none; +} + +.h5p-confirmation-dialog-background.hiding { + opacity: 0; + visibility: hidden; + -webkit-transition: opacity 0.1s, linear 0s, visibility 0s linear 0.1s; + transition: opacity 0.1s linear 0s, visibility 0s linear 0.1s; +} + +.h5p-confirmation-dialog-popup { + position: absolute; + display: flex; + flex-direction: column; + justify-content: center; + + box-sizing: border-box; + max-width: calc(100% - 2em); + min-width: 25em; + + top: 2em; + left: 50%; + -webkit-transform: translate(-50%, 0%); + -ms-transform: translate(-50%, 0%); + transform: translate(-50%, 0%); + + color: #555; + box-shadow: 0 0 6px 1px #ddd; + + -webkit-transition: transform 0.1s ease-in; + transition: transform 0.1s ease-in; +} + +.h5p-confirmation-dialog-popup.hidden { + -webkit-transform: translate(-50%, 50%); + -ms-transform: translate(-50%, 50%); + transform: translate(-50%, 50%); +} + +.h5p-confirmation-dialog-header { + padding: 1.5em; + background: #fff; + color: #1a73d9; +} + +.h5p-confirmation-dialog-header-text { + font-size: 1.25em; +} + +.h5p-confirmation-dialog-body { + padding: 1.25em 1.5em; + background: #fafafa; +} + +.h5p-confirmation-dialog-text { + margin-bottom: 1.5em; +} + +.h5p-confirmation-dialog-buttons { + float: right; +} + +.h5p-confirmation-dialog-exit { + position: absolute; + background: none; + border: none; + font-size: 2.5em; + top: -0.9em; + right: -1.15em; + color: #777; + cursor: pointer; +} + +.h5p-confirmation-dialog-exit:before { + font-family: "H5P"; + content: "\e890"; +} + +.h5p-core-button.h5p-confirmation-dialog-confirm-button { + padding-left: 0.75em; + margin-bottom: 0; +} + +.h5p-core-button.h5p-confirmation-dialog-confirm-button:before { + content: "\e601"; +} diff --git a/styles/h5p-core-button.css b/styles/h5p-core-button.css new file mode 100644 index 0000000..e073e74 --- /dev/null +++ b/styles/h5p-core-button.css @@ -0,0 +1,59 @@ +.h5p-core-button { + font-size: 1em; + line-height: 1.2; + padding: 0.5em 1.25em; + border-radius: 2em; + + background: #1a73d9; + color: #ffffff; + + cursor: pointer; + border: none; + box-shadow: none; + outline: none; + + display: inline-block; + text-align: center; + text-shadow: none; + vertical-align: baseline; +} +.h5p-core-button:hover, +.h5p-core-button:focus { + background: #1356a3; + color: #fff; + text-decoration: none; + -webkit-transition: initial; + transition: initial; +} +.h5p-core-button:active { + position: relative; + background: #104888; + + -webkit-box-shadow: inset 0 4px 0 #0e407a; + -moz-box-shadow: inset 0 4px 0 #0e407a; + box-shadow: inset 0 4px 0 #0e407a; +} + +.h5p-core-button:before { + font-family: 'H5P'; + padding-right: 0.15em; + font-size: 1.5em; + vertical-align: middle; + line-height: 0.7; +} + +.h5p-core-cancel-button { + cursor: pointer; + background: none; + border: none; + color: #a00; + margin-right: 1em; + font-size: 1em; +} + +.h5p-core-cancel-button:hover, +.h5p-core-cancel-button:focus { + background: none; + border: none; + color: #e40000; +}