Open Merge and Discard confirmations beside their buttons

This commit is contained in:
Ian Gulliver
2026-10-15 14:00:18 -07:00
parent 0f41dab390
commit 17898b0191
8 changed files with 43 additions and 20 deletions
+16 -6
View File
@@ -57,9 +57,15 @@ export function errorText(err: unknown): string {
return err instanceof Error ? err.message : String(err);
}
function modal(text: string, buttons: { label: string; value: string; class?: string }[]): Promise<string> {
function modal(text: string, buttons: { label: string; value: string; class?: string }[], anchor: Element | null = null): Promise<string> {
const actions = buttons.map((b) => h("button", { type: "submit", value: b.value, class: b.class ?? "" }, b.label));
const dialog = h("dialog", { class: "modal" }, h("form", { method: "dialog" }, h("p", { class: "modal-text" }, text), h("div", { class: "modal-actions" }, ...actions)));
if (anchor) {
const rect = anchor.getBoundingClientRect();
dialog.classList.add("anchored");
dialog.style.top = `${rect.bottom + 4}px`;
dialog.style.right = `${document.documentElement.clientWidth - rect.right}px`;
}
document.body.append(dialog);
return new Promise((resolve) => {
dialog.onclose = () => {
@@ -97,10 +103,14 @@ export async function showAlert(text: string): Promise<void> {
await modal(text, [{ label: "OK", value: "ok", class: "primary" }]);
}
export async function showConfirm(text: string, action: string, danger = false): Promise<boolean> {
const value = await modal(text, [
{ label: "Cancel", value: "cancel" },
{ label: action, value: "ok", class: danger ? "danger-primary" : "primary" },
]);
export async function showConfirm(text: string, action: string, danger = false, anchor: Element | null = null): Promise<boolean> {
const value = await modal(
text,
[
{ label: "Cancel", value: "cancel" },
{ label: action, value: "ok", class: danger ? "danger-primary" : "primary" },
],
anchor,
);
return value === "ok";
}
+5 -4
View File
@@ -139,6 +139,7 @@ function taskHead(rerender: () => void): Head {
const merge = h(
"button",
{
class: "merge-button",
disabled: busy || held || unmerged?.taskId !== task.id || !unmerged.has,
title: held
? "Waiting for the merge in progress in this project to finish"
@@ -159,7 +160,7 @@ function taskHead(rerender: () => void): Head {
};
const discard = h("button", { class: "danger", disabled: busy, title: "Delete the worktree and branch without merging" }, "Discard");
discard.onclick = () =>
confirmThen(`Discard "${task.title}"?`, "Discard", true, async () => {
confirmThen(`Discard "${task.title}"?`, "Discard", true, discard, async () => {
await post("/api/tasks/{taskId}/discard", { path: { taskId: task.id } });
await setFocus({ task_id: null });
});
@@ -1081,7 +1082,7 @@ export async function confirmMerge(taskId: string): Promise<void> {
void showAlert(errorText(err));
return;
}
await confirmThen(mergeQuestion(task.merge_title, target, coAuthors), "Merge", false, async () => {
await confirmThen(mergeQuestion(task.merge_title, target, coAuthors), "Merge", false, document.querySelector(".merge-button"), async () => {
await post("/api/tasks/{taskId}/merge", { path: { taskId } });
});
}
@@ -1099,8 +1100,8 @@ function mergeQuestion(title: string, target: string, coAuthors: Participant[]):
return lines.join("\n");
}
async function confirmThen(question: string, action: string, danger: boolean, act: () => Promise<void>) {
if (!(await showConfirm(question, action, danger))) {
async function confirmThen(question: string, action: string, danger: boolean, anchor: Element | null, act: () => Promise<void>) {
if (!(await showConfirm(question, action, danger, anchor))) {
return;
}
try {