Source: xui/Prompt.js

/**
 * A prompt dialog box.
 * <p>Part of the XUI module which, for now, has an undocumented API.
 */
export class Prompt extends Dialog {
    static showPrompt(title, prompt,
                      columns=40, initialValue=null,
                      valueList=null, inputValueChecker=null, 
                      reference=null) {
        return Prompt.prompt({ title: title,
                               prompt: prompt,
                               columns: columns,
                               initialValue: initialValue,
                               valueList: valueList,
                               inputValueChecker: inputValueChecker },
                             reference);
    }
    
    static prompt(options, reference) {
        return new Promise((resolve, reject) => { 
            let dialog = new Prompt(options, resolve);
            dialog.open("center", reference);
        });
    }
    
    constructor(options, resolve) {
        const opts = Object.assign({
            title: "???",
            prompt: "???",
            hint: null,
            columns: 40,
            initialValue: null,
            valueList: null,
            inputValueChecker: null
        }, options);
        
        super({ movable: true, title: opts.title, closeable: true,
                template: Prompt.TEMPLATE,
                buttons: [ { label: "Cancel", action: "cancelAction" },
                           { label: "OK", action: "okAction",
                             default: true } ]});
        
        this._resolve = resolve;
        
        let pane = this.contentPane.firstElementChild;
        let icon = pane.firstElementChild;
        icon.textContent = StockIcon["question"];
        
        let form = pane.lastElementChild;
        let question = form.firstElementChild;
        question.innerHTML = Prompt.textToHTML(opts.prompt);
            
        this._input = question.nextElementSibling;
        if (opts.columns < 10) {
            opts.columns = 10;
        }
        this._input.size = opts.columns;

        let hint = form.lastElementChild;
        if (opts.hint) {
            hint.innerHTML = Prompt.textToHTML(opts.hint);
        } else {
            hint.style.display = "none";
        }
        
        if (opts.initialValue !== null) {
            this._input.value = opts.initialValue;
            this.selectInputValue();
        }

        this._input.autocomplete = "off";
        if (opts.valueList !== null && Array.isArray(opts.valueList) &&
            opts.valueList.length > 0) {
            let allValues = [];
            for (let value of opts.valueList) {
                value = value.toString().trim();
                if (value.length > 0) {
                    allValues.push(value);
                }
            }

            if (allValues.length > 0) {
                // valueList is assumed to have been sorted by client code.
                // (This may be easily done using valueList.sort().)
                
                let datalistId = Util.uid();
                Util.appendDatalist(datalistId, allValues, form);
                this._input.setAttribute("list", datalistId);
            } 
        }
        
        this._checkInputValue = opts.inputValueChecker;
        
        this._input.addEventListener("keydown", (e) => {
            if (e.key === "Enter") {
                Util.consumeEvent(e);
                
                this.okAction();
            }
        });
    }

    static textToHTML(text) {
        if (text.startsWith("<html>")) {
            text = text.substring(6);
        } else {
            text = Util.escapeHTML(text);
            if (text.indexOf('\n') >= 0) {
                text = text.replaceAll('\n', "<br>");
            }
        }
        return text;
    }
    
    dialogClosed(result) {
        // Close button clicked ==> null result, which is just fine.
        this._resolve(result);
    }
    
    cancelAction() {
        this.close(null);
    }
    
    okAction() {
        let inputValue = this.getInputValue();
        if (this._checkInputValue !== null) {
            // checkInputValue may or may not accept null.
            if (!this._checkInputValue(inputValue)) {
                this.selectInputValue();
                return;
            }
        }
        
        this.close(inputValue);
    }

    // -----------------------------------
    // Validation helpers
    // -----------------------------------
    
    getInputValue() {
        let result = this._input.value;
        if (result !== null && (result = result.trim()).length === 0) {
            result = null;
        }
        return result;
    }

    selectInputValue() {
        this._input.focus();
        this._input.select();
    }
}

Prompt.TEMPLATE = document.createElement("template");
Prompt.TEMPLATE.innerHTML = `
<div class="xui-prompt-pane">
  <div class="xui-prompt-icon"></div>
  <div class="xui-prompt-form">
    <div class="xui-prompt-question"></div>
    <input class="xui-prompt-input" type="text" spellcheck="false" />
    <div class="xui-prompt-hint"></div>
  </div>
</div>
`;