Source: xui/Dialog.js

/**
 * The base class of dialog boxes.
 * <p>Part of the XUI module which, for now, has an undocumented API.
 */
export class Dialog {
    constructor(options) {
        this._testMode = false;
        
        let opts = Object.assign({
            type: "modal", movable: false, resizable: false,
            classes: "xui-control xui-dialog xui-dlg",
            title: null, closeable: false,
            template: null,
            buttons: null
        }, options);

        this._type = opts.type;
        this._classes = opts.classes;
        
        this._form = document.createElement("div");
        this._form.classList.add("xui-dlg-container");
        if (opts.resizable) {
            this._form.classList.add("xui-dlg-resizable");
        }
        this._form.appendChild(Dialog.TEMPLATE.content.cloneNode(true));
        this._titlePane = this._form.firstElementChild;
        this._contentPane = this._titlePane.nextElementSibling;
        this._buttonPane = this._form.lastElementChild;
        this._titleText = this._titlePane.firstElementChild;
        this._closeButton = this._titlePane.lastElementChild;
        
        if (opts.title === null && !opts.closeable) {
            this._titleText.style.display = "none";
            this._closeButton.style.display = "none";
        } else {
            if (opts.title !== null) {
                this._titleText.textContent = opts.title;
            }

            if (opts.closeable) {
                this._closeButton.textContent = StockIcon["cancel"];
                this._closeButton.setAttribute("title", "Close");
                this._closeButton.addEventListener("click", (e) => {
                    Util.consumeEvent(e);
                    
                    this.close(null);
                });
            }
        }
        
        if (opts.template !== null) {
           this._contentPane.appendChild(opts.template.content.cloneNode(true));
        }

        this._buttons = [];
        if (opts.buttons !== null) {
            let buttonSpecs = opts.buttons;
            if (!Array.isArray(buttonSpecs)) {
                buttonSpecs = [ buttonSpecs ];
            }

            let containsSepar = false;
            let defaultAction = null;
            for (let buttonSpec of buttonSpecs) {
                if (Object.keys(buttonSpec).length === 0) {
                    // Separator ---
                    
                    let separ = document.createElement("span");
                    separ.setAttribute("class", "xui-dlg-button-separ");
                    this._buttonPane.appendChild(separ);

                    containsSepar = true;
                } else {
                    // Button ---
                    
                    let action = (typeof buttonSpec["action"] === "string")?
                        buttonSpec["action"] : null;
                    let label = (typeof buttonSpec["label"] === "string")?
                        buttonSpec["label"] : "???";
                    let isDefault = (buttonSpec["default"] === true);
                    
                    let button = document.createElement("button");
                    button.setAttribute("type", "button");
                    button.classList.add("xui-control", "xui-dlg-button");
                    if (isDefault) {
                        button.classList.add("xui-dlg-default-button");
                        if (action !== null) {
                            defaultAction = this[action].bind(this);
                        }
                    }
                    button.textContent = label;
                    this._buttonPane.appendChild(button);

                    this._buttons.push(button);

                    if (action !== null) {
                        const buttonAction = this[action].bind(this);
                        button.addEventListener("click", (e) => {
                            Util.consumeEvent(e);
                            buttonAction();
                        });
                    }
                }
            }

            if (defaultAction !== null) {
                const onPressEnter = (e) => {
                    if (e.key === "Enter") {
                        Util.consumeEvent(e);
                        defaultAction();
                    }
                };
                
                const inputs = this._contentPane.querySelectorAll(
                    "input[type=text],input:not([type]),select,[tabindex]");
                for (let input of inputs) {
                    input.addEventListener("keydown", onPressEnter);
                }
            }
            
            if (!containsSepar) {
                this._buttonPane.classList.add("xui-dlg-button-default-layout");
            }
        }

        if (opts.movable && opts.title !== null) {
            this._titleText.addEventListener("mousedown",
                                             this.onDragStart.bind(this));
            
            // When the mousemove handler is set on this._titleText, dragging
            // stops when the mouse is moved quickly (because the mouse leaves
            // this._titleText. That's why the mousemove handler must be set
            // on the document.
            this._onDrag = this.onDrag.bind(this);
            
            this._titleText.addEventListener("mouseup",
                                             this.onDragEnd.bind(this));

            this._dragOffsetX = this._dragOffsetY = -1;
            this._dragging = false;
        }
    }

    get testMode() {
        return this._testMode;
    }
    
    set testMode(testing) {
        this._testMode = testing;
    }
    
    get form() {
        return this._form;
    }
    
    get titlePane() {
        return this._titlePane;
    }
    
    get contentPane() {
        return this._contentPane;
    }
    
    get buttonPane() {
        return this._buttonPane;
    }
    
    get titleText() {
        return this._titleText;
    }
    
    get closeButton() {
        return this._closeButton;
    }
    
    get buttons() {
        return this._buttons;
    }
    
    open(position="center", reference=null) {
        this._dialog = Dialogs.open({
            type: this._type, classes: this._classes, 
            form: this._form, 
            position: position, reference: reference
        });
        
        this._dialog.addEventListener("dialogclosed", (e) => {
            this.dialogClosed(e.xuiDialogResult);
        });

        return this._dialog;
    }

    close(result=null) {
        Dialogs.close(this._dialog, result);
        this._dialog = null;
    }

    dialogClosed(result) {
        // Default implementation does nothing at all.
    }

    onDragStart(event) {
        if (event.buttons === 1) { // Primary button pressed.
            Util.consumeEvent(event);
            
            this._dragging = true;

            // Coordinates of the top/left of the dialog relative to this
            // initial mouse down.
            let dialogRect = this._dialog.getBoundingClientRect();
            this._dragOffsetX = event.clientX - dialogRect.left;
            this._dragOffsetY = event.clientY - dialogRect.top;
            
            this._titleText.style.cursor = "move";
            
            document.addEventListener("mousemove", this._onDrag);
        }
    }
    
    onDrag(event) {
        if (this._dragging) {
            Util.consumeEvent(event);
            
            this._dialog.style.left =
                String(event.pageX - this._dragOffsetX) + "px";
            this._dialog.style.top =
                String(event.pageY - this._dragOffsetY) + "px";
        }
    }
    
    onDragEnd(event) {
        if (this._dragging) {
            Util.consumeEvent(event);
            
            document.removeEventListener("mousemove", this._onDrag);

            this._titleText.style.cursor = null;
            
            this._dragOffsetX = this._dragOffsetY = -1;
            this._dragging = false;
        }
    }
}

Dialog.TEMPLATE = document.createElement("template");
Dialog.TEMPLATE.innerHTML = `
<div class="xui-dlg-title-pane">
  <span draggable="false" class="xui-dlg-title"></span>
  <span class="xui-small-icon xui-dlg-close-button"></span>
</div>
<div class="xui-dlg-content-pane"></div>
<div class="xui-dlg-button-pane"></div>
`;