Source: xui/ItemChooser.js

/**
 * A dialog box letting the user select an item from a predefined list.
 * <p>Part of the XUI module which, for now, has an undocumented API.
 */
export class ItemChooser extends Dialog {
    static showChooser(title, items, initialText=null, options=null,
                       reference=null) {
        return new Promise((resolve, reject) => { 
            let dialog = new ItemChooser(title, items, initialText, options,
                                         resolve);
            dialog.open("center", reference);
        });
    }
    
    constructor(title, items, initialText, options, resolve) {
        super({ movable: true, resizable: true,
                title: title, closeable: true,
                template: ItemChooser.TEMPLATE });

        let pane = this.contentPane.firstElementChild;
        let fieldPane = pane.firstElementChild;
        
        let cancelButton = fieldPane.firstElementChild;
        cancelButton.setAttribute("title", "Cancel");
        cancelButton.textContent = StockIcon["cancel"];
        cancelButton.onclick = this.cancelAction.bind(this);
        
        let okButton = cancelButton.nextElementSibling;
        okButton.setAttribute("title", "OK");
        okButton.textContent = StockIcon["ok"];
        okButton.onclick = this.okAction.bind(this);
        
        this._field = fieldPane.lastElementChild;
        this._list = pane.lastElementChild;

        this._list.id = Util.uid();
        this._field.setAttribute("for", this._list.id);
        
        this._field.addEventListener("selectionaccepted",
                                     this.onSelectionAccepted.bind(this));
        
        // xui-autocomplete-field and xui-list cannot be configured until they
        // are "connected".
        
        this._items = items;
        this._initialText = initialText;
        this._options = options;
        this._resolve = resolve;
    }

    open(position="center", reference=null) {
        super.open(position, reference);

        // Now that xui-autocomplete-field and xui-list are "connected",
        // we can configure them.

        this._field.autofocus = true;
        
        if (this._options !== null) {
            for (let opt in this._options) {
                let val = this._options[opt];
                
                switch (opt.toLowerCase()) {
                case "completionkey":
                    this._field.completionKey = val;
                    break;
                case "acceptmode":
                    this._field.acceptMode =  val;
                    break;
                case "singleclicktoaccept":
                    this._field.singleClickToAccept = val;
                    break;
                case "lenienttextchecker":
                    this._field.lenientTextChecker = val;
                    break;
                case "labelmaker":
                    this._list.labelMaker = val;
                    break;
                case "htmlrenderer":
                    this._list.htmlRenderer = val;
                    break;
                case "disabledchecker":
                    this._list.disabledChecker = val;
                    break;
                case "columns":
                    {
                        let len = ItemChooser.countToLength(val);
                        if (len !== null) {
                            // Note that unlike the field width, the list
                            // width is "not flex".
                            this._field.style.width = len;
                        }
                    }
                    break;
                case "rows":
                    {
                        let len = ItemChooser.countToLength(val);
                        if (len !== null) {
                            this._list.style.height = len;
                        }
                    }
                    break;
                default:
                    console.warn(`ItemChooser: ignoring option "${opt}"`);
                    break;
                }
            }
        }

        this._list.setAll(this._items);
        if (this._initialText !== null) {
            this._field.text = this._initialText;
        }
    }

    static countToLength(val, mult=1) {
        let num = Number.parseInt(val);
        if (Number.isNaN(num) || num <= 0) {
            return null;
        }
        
        return String(mult * num) + "em";
    }
    
    onSelectionAccepted(event) {
        this.close([ event.xuiAcceptedText,
                     event.xuiSelectedIndex, event.xuiSelectedItem ]);
    }
    
    dialogClosed(result) {
        // Close button clicked ==> null result, which is just fine.
        this._resolve(result);
    }
    
    cancelAction() {
        this.close(null);
    }
    
    okAction() {
        this._field.acceptSelection();
    }
}

ItemChooser.TEMPLATE = document.createElement("template");
ItemChooser.TEMPLATE.innerHTML = `
<div class="xui-item-chooser-pane">
  <div class="xui-itmchsr-field-pane">
    <button type="button" 
            class="xui-control xui-small-icon xui-itmchsr-cancel"></button>
    <button type="button" 
            class="xui-control xui-small-icon xui-dlg-default-button
                   xui-itmchsr-ok"></button>
    <xui-autocomplete-field class="xui-itmchsr-field"></xui-autocomplete-field>
  </div>
  <xui-list class="xui-itmchsr-list"></xui-list>
</div>
`;