242 lines
7.9 KiB
JavaScript
242 lines
7.9 KiB
JavaScript
(function () {
|
|
'use strict';
|
|
|
|
const cardSelector = '[data-shared-editable-action-card]';
|
|
const scrollRestoreStates = new WeakMap();
|
|
|
|
const getCard = (element) => element.closest(cardSelector);
|
|
|
|
const setToggleEnabled = (card, enabled) => {
|
|
const toggle = card?.querySelector('[data-shared-editable-card-toggle]');
|
|
if (!toggle) {
|
|
return;
|
|
}
|
|
toggle.disabled = !enabled;
|
|
toggle.setAttribute('aria-disabled', String(!enabled));
|
|
toggle.classList.toggle('sg-button--inactive', !enabled);
|
|
};
|
|
|
|
const setVisibility = (card, visible) => {
|
|
if (!card || !card.hasAttribute('data-shared-editable-card-collapsible')) {
|
|
return;
|
|
}
|
|
const readView = card.querySelector('[data-shared-editable-card-read]');
|
|
const toggle = card.querySelector('[data-shared-editable-card-toggle]');
|
|
card.dataset.sharedEditableCardVisible = String(visible);
|
|
card.classList.toggle('shared-editable-action-card--collapsed', !visible);
|
|
if (readView && card.querySelector('[data-shared-editable-card-form]')?.hidden !== false) {
|
|
readView.hidden = !visible;
|
|
}
|
|
if (toggle) {
|
|
toggle.textContent = visible ? 'Verbergen' : 'Anzeigen';
|
|
toggle.setAttribute('aria-expanded', String(visible));
|
|
}
|
|
};
|
|
|
|
const isVisible = (card) => card?.dataset.sharedEditableCardVisible === 'true';
|
|
|
|
const getScrollContainer = (element) => {
|
|
let candidate = element?.parentElement || null;
|
|
|
|
while (candidate && candidate !== document.body) {
|
|
const style = window.getComputedStyle(candidate);
|
|
const scrollable = /(auto|scroll|overlay)/.test(style.overflowY);
|
|
if (scrollable && candidate.scrollHeight > candidate.clientHeight) {
|
|
return candidate;
|
|
}
|
|
candidate = candidate.parentElement;
|
|
}
|
|
|
|
return document.scrollingElement || document.documentElement;
|
|
};
|
|
|
|
const captureScrollPosition = (root, card) => {
|
|
const container = getScrollContainer(card);
|
|
scrollRestoreStates.set(root, {
|
|
card,
|
|
container,
|
|
top: container.scrollTop,
|
|
left: container.scrollLeft,
|
|
timer: null,
|
|
});
|
|
};
|
|
|
|
const restoreScrollPosition = (root) => {
|
|
const state = scrollRestoreStates.get(root);
|
|
if (!state) {
|
|
return;
|
|
}
|
|
|
|
if (state.timer) {
|
|
window.clearTimeout(state.timer);
|
|
}
|
|
|
|
state.timer = window.setTimeout(() => {
|
|
requestAnimationFrame(() => {
|
|
requestAnimationFrame(() => {
|
|
if (state.container?.isConnected) {
|
|
state.container.scrollTo({ top: state.top, left: state.left, behavior: 'auto' });
|
|
}
|
|
scrollRestoreStates.delete(root);
|
|
});
|
|
});
|
|
}, 100);
|
|
};
|
|
|
|
const getDirectDeleteElement = (card, selector) => Array.from(card?.children || [])
|
|
.find((child) => child.matches(selector)) || null;
|
|
|
|
const setEditMode = (card, enabled) => {
|
|
if (!card) {
|
|
return;
|
|
}
|
|
|
|
const readView = card.querySelector('[data-shared-editable-card-read]');
|
|
const editView = card.querySelector('[data-shared-editable-card-form]');
|
|
const editButtons = card.querySelectorAll('[data-shared-editable-card-edit]');
|
|
|
|
if (card.hasAttribute('data-shared-editable-card-collapsible')) {
|
|
if (enabled) {
|
|
setVisibility(card, true);
|
|
} else if (!card.dataset.sharedEditableCardVisible) {
|
|
setVisibility(card, !card.hasAttribute('data-shared-editable-card-collapsed'));
|
|
}
|
|
setToggleEnabled(card, !enabled);
|
|
}
|
|
|
|
if (readView) {
|
|
readView.hidden = enabled;
|
|
}
|
|
if (editView) {
|
|
editView.hidden = !enabled;
|
|
}
|
|
editButtons.forEach((editButton) => {
|
|
editButton.disabled = enabled;
|
|
editButton.setAttribute('aria-disabled', String(enabled));
|
|
editButton.classList.toggle('sg-button--inactive', enabled);
|
|
});
|
|
|
|
if (card.hasAttribute('data-shared-editable-card-collapsible') && enabled && readView) {
|
|
readView.hidden = true;
|
|
}
|
|
};
|
|
|
|
const resetDeleteConfirmation = (card) => {
|
|
const stage = card?.closest('.sg-delete-confirmation-pattern__stage');
|
|
const confirmation = getDirectDeleteElement(card, '[data-shared-editable-card-delete-confirmation]');
|
|
const input = confirmation?.querySelector('[data-shared-editable-card-delete-input]');
|
|
const submit = confirmation?.querySelector('[data-shared-editable-card-delete-submit]');
|
|
|
|
if (input) {
|
|
input.value = '';
|
|
}
|
|
if (submit) {
|
|
submit.disabled = true;
|
|
submit.setAttribute('aria-disabled', 'true');
|
|
submit.classList.add('sg-button--process-inactive');
|
|
}
|
|
if (confirmation) {
|
|
confirmation.hidden = true;
|
|
}
|
|
if (stage) {
|
|
stage.dataset.dialogOpen = 'false';
|
|
}
|
|
};
|
|
|
|
const openDeleteConfirmation = (card) => {
|
|
const stage = card?.closest('.sg-delete-confirmation-pattern__stage');
|
|
const confirmation = getDirectDeleteElement(card, '[data-shared-editable-card-delete-confirmation]');
|
|
const input = confirmation?.querySelector('[data-shared-editable-card-delete-input]');
|
|
|
|
if (input) {
|
|
input.value = '';
|
|
input.focus();
|
|
}
|
|
if (confirmation) {
|
|
confirmation.hidden = false;
|
|
}
|
|
if (stage) {
|
|
stage.dataset.dialogOpen = 'true';
|
|
}
|
|
};
|
|
|
|
const updateDeleteButton = (input) => {
|
|
const card = getCard(input);
|
|
const confirmation = input?.closest('[data-shared-editable-card-delete-confirmation]');
|
|
const submit = confirmation?.querySelector('[data-shared-editable-card-delete-submit]');
|
|
const token = card?.dataset.sharedEditableDeleteToken || 'DELETE';
|
|
const valid = input.value === token;
|
|
|
|
if (submit) {
|
|
submit.disabled = !valid;
|
|
submit.setAttribute('aria-disabled', String(!valid));
|
|
submit.classList.toggle('sg-button--process-inactive', !valid);
|
|
}
|
|
};
|
|
|
|
const init = (root) => {
|
|
if (!root || root.dataset.sharedEditableActionCardsInitialized === 'true') {
|
|
return;
|
|
}
|
|
|
|
root.dataset.sharedEditableActionCardsInitialized = 'true';
|
|
root.querySelectorAll(cardSelector).forEach((card) => setEditMode(card, false));
|
|
root.addEventListener('click', (event) => {
|
|
const toggle = event.target.closest('[data-shared-editable-card-toggle]');
|
|
if (toggle && root.contains(toggle)) {
|
|
const card = getCard(toggle);
|
|
if (card?.querySelector('[data-shared-editable-card-form]')?.hidden === false) {
|
|
return;
|
|
}
|
|
setVisibility(card, !isVisible(card));
|
|
return;
|
|
}
|
|
|
|
const edit = event.target.closest('[data-shared-editable-card-edit]');
|
|
if (edit && root.contains(edit)) {
|
|
const card = getCard(edit);
|
|
captureScrollPosition(root, card);
|
|
setEditMode(card, true);
|
|
return;
|
|
}
|
|
|
|
const cancel = event.target.closest('[data-shared-editable-card-cancel]');
|
|
if (cancel && root.contains(cancel)) {
|
|
const card = getCard(cancel);
|
|
resetDeleteConfirmation(card);
|
|
setEditMode(card, false);
|
|
scrollRestoreStates.delete(root);
|
|
return;
|
|
}
|
|
|
|
const deleteButton = event.target.closest('[data-shared-editable-card-delete]');
|
|
if (deleteButton && root.contains(deleteButton)) {
|
|
openDeleteConfirmation(getCard(deleteButton));
|
|
return;
|
|
}
|
|
|
|
const deleteCancel = event.target.closest('[data-shared-editable-card-delete-cancel]');
|
|
if (deleteCancel && root.contains(deleteCancel)) {
|
|
resetDeleteConfirmation(getCard(deleteCancel));
|
|
}
|
|
});
|
|
|
|
const scrollObserver = new MutationObserver(() => {
|
|
const state = scrollRestoreStates.get(root);
|
|
if (state && !root.contains(state.card)) {
|
|
restoreScrollPosition(root);
|
|
}
|
|
});
|
|
scrollObserver.observe(root, { childList: true, subtree: true });
|
|
|
|
root.addEventListener('input', (event) => {
|
|
const input = event.target.closest('[data-shared-editable-card-delete-input]');
|
|
if (input && root.contains(input)) {
|
|
updateDeleteButton(input);
|
|
}
|
|
});
|
|
};
|
|
|
|
window.SharedEditableActionCard = { init, resetDeleteConfirmation, setEditMode, setToggleEnabled, setVisibility, isVisible };
|
|
}());
|