|
| 1 | +/** |
| 2 | + * Datagrid plugin that asks for confirmation before deleting. |
| 3 | + * If there is a modal in the DOM, use it, otherwise use a native confirm window. |
| 4 | + */ |
1 | 5 | import { Datagrid } from "../../datagrid"; |
2 | 6 | import { DatagridPlugin } from "../../types"; |
3 | 7 |
|
| 8 | +interface NajaInteractDetail { |
| 9 | + method: string; |
| 10 | + url: string; |
| 11 | + payload: any; |
| 12 | + options: Record<string, any>; |
| 13 | +} |
| 14 | + |
4 | 15 | export const ConfirmAttribute = "data-datagrid-confirm"; |
5 | 16 |
|
6 | 17 | export class ConfirmPlugin implements DatagridPlugin { |
| 18 | + private datagrid!: Datagrid; |
| 19 | + |
| 20 | + private modalId = 'datagridConfirmModal'; |
| 21 | + private messageBoxId = 'datagridConfirmMessage'; |
| 22 | + private confirmButtonId = 'datagridConfirmOk'; |
| 23 | + |
| 24 | + /** |
| 25 | + * Initializes the plugin and registers event handlers. |
| 26 | + * @param datagrid The datagrid instance that the plugin is connected to. |
| 27 | + * @returns true if initialization was successful. |
| 28 | + */ |
| 29 | + |
7 | 30 | onDatagridInit(datagrid: Datagrid): boolean { |
8 | | - datagrid.el |
9 | | - .querySelectorAll<HTMLElement>(`[${ConfirmAttribute}]:not(.ajax)`) |
10 | | - .forEach(confirmEl => |
11 | | - confirmEl.addEventListener("click", e => this.confirmEventHandler.call(datagrid, e.target as HTMLElement, e)) |
12 | | - ); |
| 31 | + this.datagrid = datagrid; |
| 32 | + |
| 33 | + const confirmElements = datagrid.el.querySelectorAll<HTMLElement>(`[${ConfirmAttribute}]:not(.ajax)`); |
| 34 | + confirmElements.forEach(el => el.addEventListener("click", e => this.handleClick(el, e))); |
13 | 35 |
|
14 | 36 | datagrid.ajax.addEventListener("interact", e => { |
15 | | - if (datagrid.el.contains(e.detail.element)) { |
16 | | - this.confirmEventHandler.call(datagrid, e.detail.element, e); |
| 37 | + const target = e.detail.element; |
| 38 | + if (datagrid.el.contains(target)) { |
| 39 | + this.handleClick(target, e); |
17 | 40 | } |
18 | 41 | }); |
19 | 42 |
|
20 | 43 | return true; |
21 | 44 | } |
22 | 45 |
|
23 | | - confirmEventHandler(this: Datagrid, el: HTMLElement, e: Event) { |
24 | | - const message = el.closest('a')?.getAttribute(ConfirmAttribute)!; |
| 46 | + private handleClick(el: HTMLElement, e: Event): void { |
| 47 | + const message = this.getConfirmationMessage(el); |
25 | 48 | if (!message) return; |
26 | 49 |
|
27 | | - if (!window.confirm.bind(window)(message)) { |
28 | | - e.stopPropagation(); |
29 | | - e.preventDefault(); |
| 50 | + e.preventDefault(); |
| 51 | + e.stopPropagation(); |
| 52 | + |
| 53 | + const modal = this.getElement(this.modalId); |
| 54 | + if (modal) { |
| 55 | + this.showModalConfirm(modal, message, el, e); |
| 56 | + } else { |
| 57 | + if (window.confirm(message)) { |
| 58 | + this.executeConfirmedAction(el, e); |
| 59 | + } |
| 60 | + } |
| 61 | + } |
| 62 | + |
| 63 | + private getConfirmationMessage(el: HTMLElement): string | null { |
| 64 | + return el.getAttribute(ConfirmAttribute) ?? el.closest('a')?.getAttribute(ConfirmAttribute) ?? null; |
| 65 | + } |
| 66 | + |
| 67 | + private showModalConfirm(modal: HTMLElement, message: string, el: HTMLElement, e: Event): void { |
| 68 | + if (typeof bootstrap === 'undefined') { |
| 69 | + if (window.confirm(message)) { |
| 70 | + this.executeConfirmedAction(el, e); |
| 71 | + } |
| 72 | + return; |
| 73 | + } |
| 74 | + |
| 75 | + const messageBox = this.getElement(this.messageBoxId); |
| 76 | + const confirmButton = this.getElement(this.confirmButtonId); |
| 77 | + |
| 78 | + if (!messageBox || !confirmButton) { |
| 79 | + if (window.confirm(message)) { |
| 80 | + this.executeConfirmedAction(el, e); |
| 81 | + } |
| 82 | + return; |
| 83 | + } |
| 84 | + |
| 85 | + messageBox.textContent = message; |
| 86 | + |
| 87 | + const newButton = confirmButton.cloneNode(true) as HTMLElement; |
| 88 | + confirmButton.parentNode!.replaceChild(newButton, confirmButton); |
| 89 | + |
| 90 | + newButton.addEventListener("click", () => { |
| 91 | + bootstrap.Modal.getInstance(modal)?.hide(); |
| 92 | + this.executeConfirmedAction(el, e); |
| 93 | + }, { once: true }); |
| 94 | + |
| 95 | + const modalInstance = bootstrap.Modal.getInstance(modal) || new bootstrap.Modal(modal); |
| 96 | + modalInstance.show(); |
| 97 | + } |
| 98 | + |
| 99 | + private executeConfirmedAction(el: HTMLElement, e?: Event): void { |
| 100 | + //const detail: NajaInteractDetail | null = (e instanceof CustomEvent ? (e.detail as NajaInteractDetail) : null); |
| 101 | + const detail: NajaInteractDetail | null = ( |
| 102 | + e instanceof CustomEvent && |
| 103 | + e.detail && |
| 104 | + typeof e.detail === 'object' |
| 105 | + ) ? e.detail as NajaInteractDetail : null; |
| 106 | + const isAjax = el.classList.contains('ajax'); |
| 107 | + |
| 108 | + if (el instanceof HTMLAnchorElement && el.href && isAjax) { |
| 109 | + if (typeof naja === 'undefined') { |
| 110 | + return; |
| 111 | + } |
| 112 | + |
| 113 | + if (detail && typeof detail.method === 'string' && typeof detail.url === 'string') { |
| 114 | + const options = { ...detail.options, history: false }; |
| 115 | + naja.makeRequest(detail.method, detail.url, detail.payload ?? null, options); |
| 116 | + } else { |
| 117 | + const method = el.getAttribute('data-naja-method') ?? 'GET'; |
| 118 | + naja.makeRequest(method, el.href, null, { history: false }); |
| 119 | + } |
| 120 | + return; |
| 121 | + } |
| 122 | + |
| 123 | + this.triggerNativeInteraction(el); |
| 124 | + } |
| 125 | + |
| 126 | + private getElement(id: string): HTMLElement | null { |
| 127 | + return document.getElementById(id); |
| 128 | + } |
| 129 | + |
| 130 | + private triggerNativeInteraction(el: HTMLElement): void { |
| 131 | + const confirmValue = el.getAttribute(ConfirmAttribute); |
| 132 | + |
| 133 | + if (confirmValue !== null) { |
| 134 | + el.removeAttribute(ConfirmAttribute); |
| 135 | + } |
| 136 | + |
| 137 | + try { |
| 138 | + el.click(); |
| 139 | + } finally { |
| 140 | + if (confirmValue !== null) { |
| 141 | + el.setAttribute(ConfirmAttribute, confirmValue); |
| 142 | + } |
30 | 143 | } |
31 | 144 | } |
32 | 145 | } |
0 commit comments