create dynamic prompts for input

This commit is contained in:
joshuaboud 2021-07-14 17:10:56 -03:00
parent 35bb8241b7
commit 723941747c
No known key found for this signature in database
GPG Key ID: 17EFB59E2A8BF50E
4 changed files with 195 additions and 26 deletions

View File

@ -2,7 +2,10 @@ export class ModalPrompt {
constructor() {
this.ok = document.createElement("button");
this.ok.innerText = "OK";
this.ok.classList.add("pf-c-button", "pf-m-secondary");
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");
@ -55,11 +58,21 @@ export class ModalPrompt {
this.header.innerText = header;
}
/**
*
* @param {string} message
*/
set_body(message) {
this.body.innerHTML = "";
this.body.innerText = message;
}
/**
*
* @param {string} header
* @param {string} message
* @returns {Promise}
*/
alert(header, message = "") {
this.set_header(header);
this.set_body(message);
@ -74,12 +87,18 @@ export class ModalPrompt {
});
}
/**
*
* @param {string} header
* @param {string} message
* @returns {Promise<boolean>}
*/
confirm(header, message = "") {
this.set_header(header);
this.set_body(message);
this.footer.innerHTML = "";
this.footer.appendChild(this.no);
this.footer.appendChild(this.yes);
this.footer.append(this.no, this.yes);
// this.footer.appendChild(this.yes);
this.show();
return new Promise((resolve, reject) => {
let resolve_true = () => {
@ -90,9 +109,86 @@ export class ModalPrompt {
resolve(false);
this.hide();
}
this.confirm.onclick = this.yes.onclick = resolve_true;
this.yes.onclick = resolve_true;
this.no.onclick = resolve_false;
});
}
/**
*
* @param {string} header
* @param {Object.<string, {label: string, type: string, default: (string|undefined)}>} request
* @returns {Promise<Object|string>}
*/
prompt(header, requests) {
this.set_header(header);
this.body.innerHTML = "";
this.footer.innerHTML = "";
this.footer.append(this.cancel, this.ok);
let inputs = [];
if (typeof requests === "object") {
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.for = key;
label.innerText = request.label;
label.style.marginRight = "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";
if (request.hasOwnProperty("default")) {
req.value = request.default;
}
row.append(label, req);
req_holder.appendChild(row);
inputs.push(req);
switch (request.type) {
case "text":
req.style.flexGrow = "3";
break;
default:
break;
}
}
}
this.show();
inputs[0].focus();
return new Promise((resolve, reject) => {
this.ok.onclick = () => {
let 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;
}
}
resolve(response);
this.hide();
}
this.cancel.onclick = () => {
resolve(null);
this.hide();
}
});
}
}

View File

@ -71,9 +71,18 @@ export class NavContextMenu {
async rename() {
this.hide();
var new_name = window.prompt("New Name: ", this.target.filename());
if (new_name === null)
let response = await this.nav_window_ref.modal_prompt.prompt("Renaming " + this.target.filename(),
{
new_name: {
label: "New Name: ",
type: "text",
default: this.target.filename()
}
}
);
if (response === null)
return;
var new_name = response.new_name;
if (new_name.includes("/")) {
this.nav_window_ref.modal_prompt.alert("File name can't contain `/`.");
return;

View File

@ -392,9 +392,21 @@ export class NavWindow {
}
async mkdir() {
var new_dir_name = window.prompt("Directory Name: ");
if (new_dir_name === null)
let response = await this.modal_prompt.prompt("Creating Directory",
{
new_name: {
label: "Name: ",
type: "text"
}
}
);
if (response === null)
return;
var new_dir_name = response.new_name;
if (new_dir_name === "") {
this.modal_prompt.alert("Directory name can't be empty.");
return;
}
if (new_dir_name.includes("/")) {
this.modal_prompt.alert("Directory name can't contain `/`.");
return;
@ -420,9 +432,21 @@ export class NavWindow {
}
async touch() {
var new_file_name = window.prompt("File Name: ");
if (new_file_name === null)
let response = await this.modal_prompt.prompt("Creating File",
{
new_name: {
label: "Name: ",
type: "text"
}
}
);
if (response === null)
return;
var new_file_name = response.new_name;
if (new_file_name === "") {
this.modal_prompt.alert("File name can't be empty.");
return;
}
if (new_file_name.includes("/")) {
this.modal_prompt.alert("File name can't contain `/`.");
return;
@ -448,12 +472,31 @@ export class NavWindow {
}
async ln(default_target = "") {
var link_target = window.prompt("Link Target: ", default_target);
if (link_target === null)
let response = await this.modal_prompt.prompt("Creating Symbolic Link",
{
target: {
label: "Target: ",
type: "text",
default: default_target
},
name: {
label: "Name: ",
type: "text"
}
}
);
if (response === null)
return;
var link_name = window.prompt("Link Name: ");
if (link_name === null)
var link_target = response.target;
if (link_target === "") {
this.modal_prompt.alert("Link target can't be empty.");
return;
}
var link_name = response.name;
if (link_name === "") {
this.modal_prompt.alert("Link name can't be empty.");
return;
}
if (link_name.includes("/")) {
this.modal_prompt.alert("Link name can't contain `/`.");
return;
@ -499,7 +542,6 @@ export class NavWindow {
}
async paste_clipboard() {
this.start_load();
var cmd = ["/usr/share/cockpit/navigator/scripts/paste.py"];
var dest = this.pwd().path_str();
if (this.copy_or_move === "move") {
@ -519,8 +561,30 @@ export class NavWindow {
proc.stream(async (data) => {
var payload = JSON.parse(data);
if (payload["wants-response"]) {
var user_response = await this.modal_prompt.confirm(payload["message"]);
proc.input(JSON.stringify(user_response) + "\n", true);
if (payload.hasOwnProperty("conflicts")) {
let requests = {};
for (let conflict of payload["conflicts"]) {
requests[conflict[0]] = {
label: conflict[1],
type: "checkbox",
default: false
}
}
let responses = await this.modal_prompt.prompt("Overwrite?", requests);
if (responses === null) {
proc.input(JSON.stringify("abort") + "\n");
return;
}
let keepers = [];
for (let response of Object.keys(responses)) {
if (responses[response])
keepers.push(response)
}
proc.input(JSON.stringify(keepers) + "\n", true);
} else {
var user_response = await this.modal_prompt.confirm(payload["message"]);
proc.input(JSON.stringify(user_response) + "\n", true);
}
} else {
await this.modal_prompt.alert(payload["message"]);
}

View File

@ -28,15 +28,19 @@ from optparse import OptionParser
import json
import subprocess
def prompt_user(message, wants_response):
def prompt_user(message, wants_response, conflicts = None):
payload = {
"wants-response": wants_response,
"message": message
}
if conflicts != None:
payload["conflicts"] = conflicts
print(json.dumps(payload) + "\n")
if wants_response:
response = input()
return json.loads(response)
response = json.loads(input())
if isinstance(response, str) and response == "abort":
sys.exit(0)
return response
return
def split_paths_at_cwd(paths, cwd):
@ -70,12 +74,8 @@ def filter_existing(args, cwd):
dest = args[-1]
(conflicts, non_conflicts) = recursive_get_conflicts(sources, cwd, dest)
if len(conflicts):
check_continue = prompt_user("Conflicts were found while pasting. `Cancel` to abort operation, `OK` to overwrite selectively.", True)
if not check_continue:
sys.exit(0)
for conflict in conflicts:
if prompt_user("Overwrite " + conflict[1] + "?", True):
non_conflicts.append(conflict[0])
conflicts = prompt_user("Overwrite?", True, conflicts)
non_conflicts.extend(conflicts)
if not len(non_conflicts):
sys.exit(0) # exit if nothing to copy
filtered_args = [*split_paths_at_cwd(non_conflicts, cwd), dest]