(() => {
const { wireOf, wireFrom } =
typeof module !== 'undefined' && module.exports
? require('./wire.js')
: window.TabloWire;
function deletedKey(action) {
let url;
try {
url = new URL(action, 'http://localhost');
} catch {
return null;
}
const segments = url.pathname.split('/').filter((part) => part !== '');
if (segments.length < 2 || segments[segments.length - 1] !== 'delete') {
return null;
}
try {
return decodeURIComponent(segments[segments.length - 2]);
} catch {
return null;
}
}
function removedKeys(form, action) {
const ids = form.querySelector('input[name="ids"]');
if (ids) return isBulkDelete(action) ? wireOf(ids.value) : [];
const key = deletedKey(action);
return key === null ? [] : [key];
}
function isBulkDelete(action) {
try {
return new URL(action, 'http://localhost').pathname.endsWith('/bulk-delete');
} catch {
return false;
}
}
function pruneWire(wire, removed) {
const gone = new Set(removed);
return wireFrom(wireOf(wire).filter((key) => !gone.has(key)));
}
function swapTargets(doc, name) {
const named = name
? `[data-boundary="table"][data-table="${name.replace(/["\\]/g, '\\$&')}"]`
: '[data-boundary="table"]';
const streamed = Array.from(doc.querySelectorAll('template[data-topcoat-swap]'))
.map((template) => template.content)
.find((content) => content.querySelector(named));
const root = streamed || doc;
return {
table: root.querySelector(`${named}:not([aria-busy])`),
toasts: Array.from(
doc.querySelectorAll('[data-sonner-toaster] > [data-sonner-toast]'),
),
};
}
function triggerFor(action) {
if (!action) return null;
return (
Array.from(document.querySelectorAll('[data-row-delete-action]')).find(
(el) => el.getAttribute('data-row-delete-action') === action,
) || null
);
}
function tableRootFor(form, action) {
const own = form.closest('[data-table-root]');
if (own) return own;
const trigger = triggerFor(action);
return trigger ? trigger.closest('[data-table-root]') : null;
}
function rowOf(form, region) {
const trigger = triggerFor(form.getAttribute('action'));
if (!trigger || !region || !region.contains(trigger)) return null;
return trigger.closest('tr');
}
let revisionToken = 0;
function bumpRevision(input) {
revisionToken += 1;
input.value = `r${revisionToken}`;
input.dispatchEvent(new Event('change', { bubbles: true }));
}
function submitTarget(form, submitter) {
const own = submitter && submitter.getAttribute && submitter.getAttribute('formaction');
return own || form.getAttribute('action');
}
function install() {
document.addEventListener('submit', (event) => {
const form = event.target.closest('form[data-mutation-submit]');
if (!form) return;
const action = submitTarget(form, event.submitter);
if (!action) return;
event.preventDefault();
send(form, action, event.submitter || form.querySelector('button[type="submit"]'));
});
}
async function send(form, action, submitter) {
const dialog = form.closest('dialog') || form.querySelector('dialog');
const root = tableRootFor(form, action);
const region = root ? root.closest('[data-boundary="table"]') : null;
const row = rowOf(form, region);
const index = row ? Array.from(row.parentElement.children).indexOf(row) : -1;
if (submitter) submitter.disabled = true;
if (dialog) dialog.dataset.dialogBusy = 'true';
let response;
try {
response = await fetch(action, {
method: 'POST',
body: new FormData(form),
credentials: 'same-origin',
redirect: 'follow',
});
} catch {
window.location.reload();
return;
}
if (!response.redirected) {
if (submitter) submitter.disabled = false;
clearDialogBusy(dialog);
showResponse(await response.text());
return;
}
if (submitter) submitter.disabled = false;
if (!response.ok) {
window.location.reload();
return;
}
const doc = new DOMParser().parseFromString(await response.text(), 'text/html');
const { table, toasts } = swapTargets(doc, region && region.getAttribute('data-table'));
const revision = root ? root.querySelector('[data-table-revision]') : null;
clearDialogBusy(dialog);
if (!region || (!revision && !table)) {
window.location.assign(response.url);
return;
}
const transport = region.querySelector('form[data-bulk-form] input[name="ids"]');
const removed = removedKeys(form, action);
const wire =
transport && removed.length > 0
? pruneWire(transport.value, removed)
: transport
? transport.value
: '';
insertToasts(toasts);
dismiss(dialog);
if (revision) {
writeSelection(transport, wire);
bumpRevision(revision);
const url = new URL(window.location.href);
url.searchParams.delete('delete');
url.searchParams.delete('open');
window.history.replaceState(window.history.state, '', url);
afterRegionChange(region, () => focusAfter(region, index));
} else {
region.replaceChildren(
...Array.from(table.childNodes).map((node) => document.importNode(node, true)),
);
writeSelection(
region.querySelector('form[data-bulk-form] input[name="ids"]'),
wire,
);
window.history.replaceState(window.history.state, '', response.url);
focusAfter(region, index);
}
}
function writeSelection(transport, wire) {
if (!transport || transport.value === wire) return;
transport.value = wire;
transport.dispatchEvent(new Event('change', { bubbles: true }));
}
function insertToasts(toasts) {
const toaster = document.querySelector('[data-sonner-toaster]');
if (!toaster || toasts.length === 0) return;
toaster.prepend(...toasts.map((toast) => document.importNode(toast, true)));
}
function dismiss(dialog) {
if (dialog && dialog.open) dialog.close();
}
function clearDialogBusy(dialog) {
if (dialog) delete dialog.dataset.dialogBusy;
}
function showResponse(html) {
document.open();
document.write(html);
document.close();
}
function focusAfter(region, index) {
const tbody = region.querySelector('tbody');
const rows = tbody ? Array.from(tbody.children) : [];
const target = index >= 0 ? rows[Math.min(index, rows.length - 1)] : null;
const control =
(target &&
(target.querySelector('a[data-row-delete-action]') ||
target.querySelector(
'a[href], button:not([disabled]), input:not([type="hidden"]):not([disabled])',
))) ||
region.querySelector('[data-bulk-confirm-trigger]') ||
region.querySelector('[data-bulk-action]');
if (control) control.focus();
}
function afterRegionChange(region, run) {
let done = false;
const finish = (focus) => {
if (done) return;
done = true;
observer.disconnect();
window.clearTimeout(timer);
if (focus) run();
};
const observer = new MutationObserver(() => finish(true));
observer.observe(region, { childList: true, subtree: true });
const timer = window.setTimeout(() => finish(false), 3000);
}
if (typeof document !== 'undefined') install();
if (typeof module !== 'undefined' && module.exports) {
module.exports = {
deletedKey,
pruneWire,
removedKeys,
submitTarget,
swapTargets,
tableRootFor,
};
}
})();