/* ModalPrompt - A Custom Prompt Module for Cockpit Plugins. Copyright (C) 2021 Josh Boudreau This program is free software: you can redistribute it and/or modify it under the terms of the Lesser GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public License for more details. You should have received a copy of the Lesser GNU General Public License along with this program. If not, see . */ /** * @typedef {Object} Request * @property {string} label * @property {"text"|"checkbox"} type * @property {string|undefined} default */ let primary_btn = "pf-m-primary"; let secondary_btn = "pf-m-secondary"; let danger_btn = "pf-m-danger"; let all_btn = [primary_btn, secondary_btn, danger_btn]; export class ModalPrompt { constructor() { this.ok = document.createElement("button"); this.ok.innerText = "OK"; this.ok.classList.add("pf-c-button", "pf-m-primary"); this.cancel = document.createElement("button"); this.cancel.innerText = "Cancel"; this.cancel.classList.add("pf-c-button", "pf-m-secondary"); this.yes = document.createElement("button"); this.yes.innerText = "Yes"; this.yes.classList.add("pf-c-button", "pf-m-primary"); this.no = document.createElement("button"); this.no.innerText = "No"; this.no.classList.add("pf-c-button", "pf-m-secondary"); this.construct_element(); } construct_element() { let bg = this.modal = document.createElement("div"); bg.classList.add("modal"); bg.style.overflowY = "auto"; let fg = document.createElement("div"); fg.classList.add("modal-dialog"); bg.appendChild(fg); let popup = document.createElement("div"); popup.classList.add("modal-content"); fg.appendChild(popup); let header = document.createElement("div"); header.classList.add("modal-header"); popup.appendChild(header); let header_text = this.header = document.createElement("h4"); header_text.classList.add("modal-title"); header.appendChild(header_text); let body = this.body = document.createElement("div"); body.classList.add("modal-body"); popup.appendChild(body); let footer = this.footer = document.createElement("div"); footer.classList.add("modal-footer"); footer.style.display = "flex"; footer.style.flexFlow = "row no-wrap"; footer.style.justifyContent = "flex-end"; popup.appendChild(footer); document.body.appendChild(this.modal); } show() { this.modal.style.display = "block"; } hide() { this.modal.style.display = "none"; } /** * * @param {string} header */ set_header(header) { this.header.innerText = header; } /** * * @param {string} message */ set_body(message) { this.body.innerHTML = ""; this.body.innerHTML = message; } /** * * @param {string} header * @param {string} message * @returns {Promise} */ alert(header, message = "") { this.set_header(header); this.set_body(message); this.footer.innerHTML = ""; this.footer.appendChild(this.ok); this.show(); this.ok.focus(); return new Promise((resolve, reject) => { this.ok.onclick = () => { resolve(); this.hide(); } }); } /** * * @param {string} header * @param {string} message * @param {boolean} danger * @returns {Promise} */ confirm(header, message = "", danger = false) { this.set_header(header); this.set_body(message); this.footer.innerHTML = ""; this.footer.append(this.no, this.yes); this.yes.classList.remove(... all_btn); if (danger) this.yes.classList.add(danger_btn); else this.yes.classList.add(primary_btn); this.show(); if (danger) this.no.focus(); else this.yes.focus(); return new Promise((resolve, reject) => { let resolve_true = () => { resolve(true); this.hide(); } let resolve_false = () => { resolve(false); this.hide(); } this.yes.onclick = resolve_true; this.no.onclick = resolve_false; }); } /** * * @param {string} header * @param {Object.} requests * @returns {Promise} */ prompt(header, requests) { this.set_header(header); this.body.innerHTML = ""; this.footer.innerHTML = ""; this.footer.append(this.cancel, this.ok); let inputs = []; let simple_prompt = false; if (typeof requests === "string") { let label = requests; simple_prompt = true; requests = { "key": { "label": label, "type": "text" } } } let req_holder = document.createElement("div"); req_holder.style.display = "flex"; req_holder.style.flexFlow = "column nowrap"; req_holder.style.alignItems = "stretch"; this.body.appendChild(req_holder); for(let key of Object.keys(requests)) { let row = document.createElement("div"); row.style.display = "flex"; row.style.alignItems = "baseline"; row.style.padding = "2px"; let request = requests[key]; let label = document.createElement("label"); label.innerText = request.label; label.htmlFor = key; label.style.paddingRight = "1em"; label.style.flexBasis = "0"; label.style.flexGrow = "1"; let req = document.createElement("input"); req.id = key; req.type = request.type; req.style.flexBasis = "0"; row.append(label, req); req_holder.appendChild(row); inputs.push(req); switch (request.type) { case "text": req.style.flexGrow = "3"; if (request.hasOwnProperty("default")) req.value = request.default; break; case "radio": req.name = "radio-group"; req.internal_name = request.internal_name; case "checkbox": label.style.cursor = req.style.cursor = "pointer"; if (request.hasOwnProperty("default")) req.checked = request.default; break; default: break; } } this.show(); inputs[0].focus(); for (let i = 0; i < inputs.length - 1; i++) { inputs[i].onchange = () => { inputs[i+1].focus(); } } inputs[inputs.length - 1].onchange = () => { this.ok.focus(); } return new Promise((resolve, reject) => { this.ok.onclick = () => { let response if (simple_prompt) { response = inputs[0].value; } else { response = {}; for (let input of inputs) { switch (input.type) { case "checkbox": response[input.id] = input.checked; break; case "text": default: response[input.id] = input.value; break; case "radio": if (input.checked) response = input.internal_name; } } } resolve(response); this.hide(); } this.cancel.onclick = () => { resolve(null); this.hide(); } }); } }