modal-component/ModalDialog.js

/**
 *  Modal
 *  Provides a way to open modal dialogs
 * 
 *  Copyright 2020, 2021 Rolf Bagge, Janus B. Kristensen, CAVI,
 *  Center for Advanced Visualization and Interacion, Aarhus University
 *    
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0

 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
**/

/* global mdc */

/**
 * @typedef {Object} ModalDialog~ModalDialogOptions
 * @property {string} [title=""] - The dialog title
 * @property {boolean} [closeOnOutsideClick=true] - Automatically close the dialog when clicking outside the dialog
 * @property {boolean} [closeOnEscape=true] - Automatically close the dialog when ESCAPE is pressed
 * @property {boolean} [autoStackButtons=true] - Automatically stack buttons if they do not fit horizontally
 */

/**
 * Modal component using MDCDialog as a framework
 */
class ModalDialog {

    /**
     * Creates a new ModalDialog
     * @param {Node} content - The content to place inside the Dialog
     * @param {ModalDialog~ModalDialogOptions} [options]
     */
    constructor(content, options = {}) {
        const self = this;

        const defaultOptions = {
            title: "",
            classes: [],
            closeOnOutsideClick: true,
            closeOnEscape: true,
            autoStackButtons: true,
            maximize: false,
            flexContent: false,
            actions: {}
        };

        this.uuid = UUIDGenerator.generateUUID("dialog-");

        this.options = Object.assign({}, defaultOptions, options);

        this.html = WebstrateComponents.Tools.loadTemplate("#webstrate-components-modal-dialog-tpl");

        this.title = this.html.querySelector(".mdc-dialog__title");
        this.content = this.html.querySelector(".mdc-dialog__content");
        this.actions = this.html.querySelector(".mdc-dialog__actions");

        this.title.id = this.uuid + "-title";
        this.content.id = this.uuid + "-content";

        this.html.querySelector(".mdc-dialog__surface").setAttribute("aria-labelledby", this.title.id);
        this.html.querySelector(".mdc-dialog__surface").setAttribute("aria-describedby", this.content.id);

        // Action buttons
        if (this.options.actions === null || Object.keys(this.options.actions).length===0){
                this.actions.remove();
        } else {
                for (const [action, actionOptions] of Object.entries(this.options.actions)) {
                        let actionButton = document.createElement("button");
                        actionButton.classList.add("mdc-button");
                        actionButton.classList.add("mdc-dialog__button");
                        if (actionOptions.primary){
                                actionButton.classList.add("mdc-button--raised");
                        }
                        actionButton.setAttribute("data-mdc-dialog-action", action);
                        
                        if (actionOptions.mdcIcon){
                                let icon = document.createElement("i");
                                icon.classList.add("material-icons");
                                icon.classList.add("mdc-button__icon");
                                icon.innerText = actionOptions.mdcIcon;
                                actionButton.appendChild(icon);
                        }
                        
                        let ripple = document.createElement("div");
                        ripple.classList.add("mdc-button__ripple");
                        actionButton.appendChild(ripple);
                        
                        let label = document.createElement("span");
                        label.classList.add("mdc-button__label");
                        if (actionOptions.label){
                                label.innerText = actionOptions.label;
                        } else {
                                label.innerText = action;
                        }
                        actionButton.append(label);
                        
                        this.actions.appendChild(actionButton);
                }
        }

        if (this.options.flexContent){
            this.content.classList.add("flexcontent");
        }
        this.content.appendChild(content);
        if(this.options.title != null && this.options.title.trim() !== "") {
            this.title.textContent = this.options.title.trim();
        } else {
            this.title.remove();
        }

        if(this.options.classes != null) {
            let classes = this.options.classes;
            if(!Array.isArray(classes)) {
                classes = [classes];
            }

            classes.forEach((c)=>{
                this.html.classList.add(c);
            });
        }

        if (this.options.maximize){
            this.html.classList.add("wc-modal-maximized");
        }
        
        this.dialog = new mdc.dialog.MDCDialog(this.html);

        //Dont add anything to body!
        this.dialog.foundation.adapter.addBodyClass = ()=>{};

        this.dialog.autoStackButtons = this.options.autoStackButtons;

        if(!this.options.closeOnEscape) {
            this.dialog.escapeKeyAction = "";
        }

        if(!this.options.closeOnOutsideClick) {
            this.dialog.scrimClickAction = "";
        }

        this.dialog.listen("MDCDialog:opening", (evt)=>{
            EventSystem.triggerEvent("ModalDialog.Opening", {
                dialog: self
            });
        });

        this.dialog.listen("MDCDialog:opened", (evt)=>{
            EventSystem.triggerEvent("ModalDialog.Opened", {
                dialog: self
            });
        });

        this.dialog.listen("MDCDialog:closing", (evt)=>{
            EventSystem.triggerEvent("ModalDialog.Closing", {
                dialog: self,
                action: evt.detail.action
            });
        });

        this.dialog.listen("MDCDialog:closed", (evt)=>{
            EventSystem.triggerEvent("ModalDialog.Closed", {
                dialog: self,
                action: evt.detail.action
            });
        });
        
        mdc.autoInit(this.html);
    }

    open() {
        this.dialog.open();
    }

    close(action = null) {
        this.dialog.close(action);
    }
}

window.WebstrateComponents.ModalDialog = ModalDialog;