Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 88 additions & 21 deletions assets/plugins/features/confirm.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,95 @@
/**
* Datagrid plugin that asks for confirmation before deleting.
* If there is a modal in the DOM, use it, otherwise use a native confirm window.
*/
import { Datagrid } from "../../datagrid";
import { DatagridPlugin } from "../../types";

export const ConfirmAttribute = "data-datagrid-confirm";

export class ConfirmPlugin implements DatagridPlugin {
onDatagridInit(datagrid: Datagrid): boolean {
datagrid.el
.querySelectorAll<HTMLElement>(`[${ConfirmAttribute}]:not(.ajax)`)
.forEach(confirmEl =>
confirmEl.addEventListener("click", e => this.confirmEventHandler.bind(datagrid)(e.target as HTMLElement, e))
);

datagrid.ajax.addEventListener("interact", e => this.confirmEventHandler.bind(datagrid)(e.detail.element, e));

return true;
}

confirmEventHandler(this: Datagrid, el: HTMLElement, e: Event) {
const message = el.closest('a')?.getAttribute(ConfirmAttribute)!;
if (!message) return;

if (!window.confirm.bind(window)(message)) {
e.stopPropagation();
e.preventDefault();
}
}
/**
* Initializes the plugin and registers event handlers.
* @param datagrid The datagrid instance that the plugin is connected to.
* @returns true if initialization was successful.
*/
private datagrid!: Datagrid;

private modalId = 'datagridConfirmModal';
private messageBoxId = 'datagridConfirmMessage';
private confirmButtonId = 'datagridConfirmOk';

onDatagridInit(datagrid: Datagrid): boolean {
this.datagrid = datagrid;

const confirmElements = datagrid.el.querySelectorAll<HTMLElement>(`[${ConfirmAttribute}]:not(.ajax)`);
confirmElements.forEach(el => el.addEventListener("click", e => this.handleClick(el, e)));

datagrid.ajax.addEventListener("interact", e => {
const target = e.detail.element;
if (datagrid.el.contains(target)) {
this.handleClick(target, e);
}
});

return true;
}

private handleClick(el: HTMLElement, e: Event): void {
const message = this.getConfirmationMessage(el);
if (!message) return;

e.preventDefault();
e.stopPropagation();

const modal = this.getElement(this.modalId);
if (modal) {
this.showModalConfirm(modal, message, el);
} else {
if (!window.confirm(message)) {
e.preventDefault();
e.stopPropagation();
} else {
this.executeConfirmedAction(el);
}
}
}

private getConfirmationMessage(el: HTMLElement): string | null {
return el.closest('a')?.getAttribute(ConfirmAttribute) ?? null;
}

private showModalConfirm(modal: HTMLElement, message: string, el: HTMLElement): void {
const messageBox = this.getElement(this.messageBoxId);
const confirmButton = this.getElement(this.confirmButtonId);

if (!messageBox || !confirmButton) return;

messageBox.textContent = message;

const newButton = confirmButton.cloneNode(true) as HTMLElement;
confirmButton.parentNode!.replaceChild(newButton, confirmButton);

newButton.addEventListener("click", () => {
bootstrap.Modal.getInstance(modal)?.hide();
this.executeConfirmedAction(el);
}, { once: true });

new bootstrap.Modal(modal).show();
}

private executeConfirmedAction(el: HTMLElement): void {
if (el instanceof HTMLAnchorElement && el.href) {
const isAjax = el.classList.contains('ajax');
isAjax
? naja.makeRequest('GET', el.href, null, { history: false })
: window.location.href = el.href;
} else {
el.click();
}
}

private getElement(id: string): HTMLElement | null {
return document.getElementById(id);
}
}