feat(dashboard): add immersive day range views
This commit is contained in:
+535
-3
@@ -39,6 +39,10 @@
|
||||
return `/assets/icons/mood-${sentiment}.svg`;
|
||||
}
|
||||
|
||||
function dashboardDayPath(date) {
|
||||
return `/?view=day&date=${encodeURIComponent(date)}`;
|
||||
}
|
||||
|
||||
function sportIconPath(icon) {
|
||||
return `/assets/icons/sport-${icon}.svg`;
|
||||
}
|
||||
@@ -705,7 +709,7 @@
|
||||
}
|
||||
|
||||
return `
|
||||
<a class="calendar-link" data-date="${item.date}" href="/track?date=${encodeURIComponent(item.date)}" aria-label="${title}">
|
||||
<a class="calendar-link" data-date="${item.date}" href="${dashboardDayPath(item.date)}" aria-label="${title}">
|
||||
<rect class="calendar-cell calendar-cell--active ${activeDate === item.date ? "calendar-cell--selected" : ""}" x="${x}" y="${y}" width="${cellSize}" height="${cellSize}" rx="4" fill="${fill}"></rect>
|
||||
<title>${title}</title>
|
||||
</a>
|
||||
@@ -730,7 +734,7 @@
|
||||
<span data-calendar-score>${formatNumber(Number(latestVisibleEntry.score))}</span>
|
||||
<small>Punkte</small>
|
||||
</div>
|
||||
<a class="ghost-link calendar-detail__link" data-calendar-link href="/track?date=${encodeURIComponent(latestVisibleEntry.date)}">Tag öffnen</a>
|
||||
<a class="ghost-link calendar-detail__link" data-calendar-link href="${dashboardDayPath(latestVisibleEntry.date)}">Tag öffnen</a>
|
||||
`;
|
||||
|
||||
container.innerHTML = `
|
||||
@@ -773,7 +777,7 @@
|
||||
detailDate.textContent = formatDateLabel(entry.date);
|
||||
detailLabel.textContent = entry.label;
|
||||
detailScore.textContent = formatNumber(Number(entry.score));
|
||||
detailLink.href = `/track?date=${encodeURIComponent(entry.date)}`;
|
||||
detailLink.href = dashboardDayPath(entry.date);
|
||||
|
||||
container.querySelectorAll(".calendar-cell--selected").forEach(cell => {
|
||||
cell.classList.remove("calendar-cell--selected");
|
||||
@@ -971,6 +975,532 @@
|
||||
syncPresets();
|
||||
}
|
||||
|
||||
function initDashboardExperience() {
|
||||
const summaryOverlay = document.querySelector("[data-summary-overlay]");
|
||||
const openSummary = document.querySelector("[data-summary-overlay-open]");
|
||||
const closeSummary = [...document.querySelectorAll("[data-summary-overlay-close]")];
|
||||
const momentOverlay = document.querySelector("[data-moment-overlay]");
|
||||
const settingsMenuOverlay = document.querySelector("[data-settings-menu-overlay]");
|
||||
const openSettingsMenu = document.querySelector("[data-settings-menu-open]");
|
||||
const closeSettingsMenu = [...document.querySelectorAll("[data-settings-menu-close]")];
|
||||
const openMoment = document.querySelector("[data-moment-overlay-open]");
|
||||
const closeMoment = [...document.querySelectorAll("[data-moment-overlay-close]")];
|
||||
const chooseStep = document.querySelector('[data-moment-step="choose"]');
|
||||
const formStep = document.querySelector('[data-moment-step="form"]');
|
||||
const momentSubmit = document.querySelector("[data-moment-submit]");
|
||||
const typeInput = document.querySelector("[data-moment-type-input]");
|
||||
const formNameInput = document.querySelector("[data-moment-form-name]");
|
||||
const eventIdInput = document.querySelector("[data-moment-event-id]");
|
||||
const typeLabel = document.querySelector("[data-moment-type-label]");
|
||||
const valueField = document.querySelector("[data-moment-value-field]");
|
||||
const valueLabel = document.querySelector("[data-moment-value-label]");
|
||||
const valueInput = document.querySelector("[data-moment-value-input]");
|
||||
const walkField = document.querySelector("[data-moment-walk-field]");
|
||||
const walkModeInput = document.querySelector("[data-walk-mode-input]");
|
||||
const sportField = document.querySelector("[data-moment-sport-field]");
|
||||
const alcoholField = document.querySelector("[data-moment-alcohol-field]");
|
||||
const momentComment = document.querySelector("[data-moment-comment]");
|
||||
const backButton = document.querySelector("[data-moment-back]");
|
||||
const deleteForm = document.querySelector("[data-moment-delete-form]");
|
||||
const deleteIdInput = document.querySelector("[data-moment-delete-id]");
|
||||
const typeSelect = document.querySelector("[data-event-type-select]");
|
||||
const unitInput = document.querySelector("[data-event-unit]");
|
||||
const swipeContainer = document.querySelector("[data-day-swipe]");
|
||||
const periodRail = document.querySelector(".range-period-rail");
|
||||
|
||||
const walkMode = document.body.dataset.walkMode || "time";
|
||||
const walkUnit = walkMode === "steps" ? "steps" : "min";
|
||||
|
||||
const hoistOverlay = overlay => {
|
||||
if (!(overlay instanceof HTMLElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (overlay.parentElement !== document.body) {
|
||||
document.body.appendChild(overlay);
|
||||
}
|
||||
};
|
||||
|
||||
hoistOverlay(summaryOverlay);
|
||||
hoistOverlay(momentOverlay);
|
||||
hoistOverlay(settingsMenuOverlay);
|
||||
|
||||
if (periodRail instanceof HTMLElement) {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const view = params.get("view") || "day";
|
||||
const storageKey = `mood:${view}:period-scroll`;
|
||||
const storedScroll = window.sessionStorage.getItem(storageKey);
|
||||
|
||||
if (storedScroll !== null) {
|
||||
periodRail.scrollLeft = Number(storedScroll) || 0;
|
||||
}
|
||||
|
||||
periodRail.addEventListener("click", event => {
|
||||
const link = event.target.closest("a[href]");
|
||||
if (!link) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.sessionStorage.setItem(storageKey, String(periodRail.scrollLeft));
|
||||
});
|
||||
}
|
||||
|
||||
const stepperConfigs = {
|
||||
event: { label: "Ereignis", valueLabel: "Wert", unit: "", placeholder: "optional", showValue: false, showSport: false, showAlcohol: false, commentPlaceholder: "Was hast du erlebt?" },
|
||||
walk: { label: "Spaziergang", valueLabel: "Spaziergang", unit: walkUnit, placeholder: walkMode === "steps" ? "Schritte" : "Minuten", showValue: true, showSport: false, showAlcohol: false, showWalk: true, commentPlaceholder: "Was war dabei besonders?" },
|
||||
sport: { label: "Sport", valueLabel: "Dauer", unit: "min", placeholder: "Minuten", showValue: true, showSport: true, showAlcohol: false, commentPlaceholder: "Was hast du gemacht?" },
|
||||
sleep: { label: "Schlaf", valueLabel: "Dauer", unit: "h", placeholder: "Stunden", showValue: true, showSport: false, showAlcohol: false, commentPlaceholder: "Wie war der Schlaf?" },
|
||||
alcohol: { label: "Alkohol", valueLabel: "", unit: "", placeholder: "", showValue: false, showSport: false, showAlcohol: true, commentPlaceholder: "Optionaler Kommentar" },
|
||||
};
|
||||
|
||||
const toneClass = (value, metric = "mood") => {
|
||||
const current = Math.max(-2, Math.min(2, Number(value || 0)));
|
||||
if (metric === "stress") {
|
||||
if (current <= -2) return "tone-pos2";
|
||||
if (current === -1) return "tone-pos1";
|
||||
if (current === 1) return "tone-neg1";
|
||||
if (current >= 2) return "tone-neg2";
|
||||
return "tone-zero";
|
||||
}
|
||||
if (current <= -2) return "tone-neg2";
|
||||
if (current === -1) return "tone-neg1";
|
||||
if (current === 1) return "tone-pos1";
|
||||
if (current >= 2) return "tone-pos2";
|
||||
return "tone-zero";
|
||||
};
|
||||
|
||||
const setHidden = (element, hidden) => {
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hidden) {
|
||||
element.setAttribute("hidden", "hidden");
|
||||
} else {
|
||||
element.removeAttribute("hidden");
|
||||
}
|
||||
};
|
||||
|
||||
const setOverlay = (overlay, open) => {
|
||||
if (!overlay) {
|
||||
return;
|
||||
}
|
||||
|
||||
setHidden(overlay, !open);
|
||||
document.body.classList.toggle("is-dashboard-overlay-open", open);
|
||||
|
||||
if (open) {
|
||||
const focusTarget = overlay.querySelector("input, textarea, select, button");
|
||||
if (focusTarget instanceof HTMLElement) {
|
||||
window.setTimeout(() => focusTarget.focus(), 10);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
document.querySelectorAll("[data-stepper]").forEach(stepper => {
|
||||
const input = stepper.querySelector("[data-stepper-input]");
|
||||
const value = stepper.querySelector("[data-stepper-value]");
|
||||
const minus = stepper.querySelector("[data-stepper-minus]");
|
||||
const plus = stepper.querySelector("[data-stepper-plus]");
|
||||
|
||||
if (!input || !value || !minus || !plus) {
|
||||
return;
|
||||
}
|
||||
|
||||
const render = () => {
|
||||
const current = Math.max(-2, Math.min(2, Number(input.value || 0)));
|
||||
const metric = stepper.dataset.stepperMetric || "mood";
|
||||
input.value = String(current);
|
||||
value.textContent = `${current > 0 ? "+" : ""}${current}`;
|
||||
minus.disabled = current <= -2;
|
||||
plus.disabled = current >= 2;
|
||||
const ring = stepper.querySelector(".overlay-signal-card__ring");
|
||||
if (ring) {
|
||||
ring.classList.remove("tone-neg2", "tone-neg1", "tone-zero", "tone-pos1", "tone-pos2");
|
||||
ring.classList.add(toneClass(current, metric));
|
||||
}
|
||||
};
|
||||
|
||||
minus.addEventListener("click", () => {
|
||||
input.value = String(Number(input.value || 0) - 1);
|
||||
render();
|
||||
});
|
||||
|
||||
plus.addEventListener("click", () => {
|
||||
input.value = String(Number(input.value || 0) + 1);
|
||||
render();
|
||||
});
|
||||
|
||||
render();
|
||||
});
|
||||
|
||||
if (openSummary) {
|
||||
openSummary.addEventListener("click", event => {
|
||||
event.preventDefault();
|
||||
setOverlay(summaryOverlay, true);
|
||||
});
|
||||
}
|
||||
|
||||
closeSummary.forEach(button => {
|
||||
button.addEventListener("click", event => {
|
||||
event.preventDefault();
|
||||
setOverlay(summaryOverlay, false);
|
||||
});
|
||||
});
|
||||
|
||||
if (openSettingsMenu) {
|
||||
openSettingsMenu.addEventListener("click", event => {
|
||||
event.preventDefault();
|
||||
setOverlay(settingsMenuOverlay, true);
|
||||
});
|
||||
}
|
||||
|
||||
closeSettingsMenu.forEach(button => {
|
||||
button.addEventListener("click", event => {
|
||||
event.preventDefault();
|
||||
setOverlay(settingsMenuOverlay, false);
|
||||
});
|
||||
});
|
||||
|
||||
const setMomentEditMode = payload => {
|
||||
if (!formNameInput || !eventIdInput) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (payload) {
|
||||
formNameInput.value = "update_event";
|
||||
eventIdInput.value = payload.id || "";
|
||||
if (deleteForm && deleteIdInput) {
|
||||
deleteIdInput.value = payload.id || "";
|
||||
setHidden(deleteForm, false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
formNameInput.value = "add_event";
|
||||
eventIdInput.value = "";
|
||||
if (deleteForm && deleteIdInput) {
|
||||
deleteIdInput.value = "";
|
||||
setHidden(deleteForm, true);
|
||||
}
|
||||
};
|
||||
|
||||
const showMomentChoose = () => {
|
||||
setMomentEditMode(null);
|
||||
setHidden(chooseStep, false);
|
||||
setHidden(formStep, true);
|
||||
document.querySelectorAll("[data-sport-choice]").forEach(item => item.classList.remove("is-selected"));
|
||||
if (momentSubmit) {
|
||||
momentSubmit.disabled = true;
|
||||
}
|
||||
};
|
||||
|
||||
const showMomentForm = type => {
|
||||
const config = stepperConfigs[type] || stepperConfigs.event;
|
||||
if (typeInput) typeInput.value = type;
|
||||
if (typeLabel) typeLabel.textContent = config.label;
|
||||
if (valueLabel) valueLabel.textContent = config.valueLabel || "Wert";
|
||||
if (valueInput) {
|
||||
valueInput.placeholder = config.placeholder;
|
||||
valueInput.required = !!config.showValue;
|
||||
valueInput.value = config.showValue ? valueInput.value : "";
|
||||
valueInput.step = type === "sleep" ? "0.25" : "1";
|
||||
}
|
||||
if (unitInput) {
|
||||
unitInput.value = config.unit;
|
||||
}
|
||||
if (walkModeInput) {
|
||||
walkModeInput.value = walkMode;
|
||||
}
|
||||
if (momentComment) {
|
||||
momentComment.placeholder = config.commentPlaceholder;
|
||||
momentComment.required = type !== "alcohol";
|
||||
}
|
||||
|
||||
if (sportField) setHidden(sportField, !config.showSport);
|
||||
if (alcoholField) setHidden(alcoholField, !config.showAlcohol);
|
||||
if (valueField) setHidden(valueField, !config.showValue);
|
||||
if (walkField) setHidden(walkField, !config.showWalk);
|
||||
if (momentSubmit) {
|
||||
momentSubmit.disabled = false;
|
||||
}
|
||||
|
||||
if (config.showWalk) {
|
||||
document.querySelectorAll('input[name="event_walk_mode"]').forEach(radio => {
|
||||
radio.checked = radio.value === walkMode;
|
||||
});
|
||||
}
|
||||
|
||||
setHidden(chooseStep, true);
|
||||
setHidden(formStep, false);
|
||||
};
|
||||
|
||||
const populateMomentForm = payload => {
|
||||
showMomentForm(payload.type || "event");
|
||||
setMomentEditMode(payload);
|
||||
|
||||
const form = document.querySelector("#moment-form");
|
||||
if (!form) {
|
||||
return;
|
||||
}
|
||||
|
||||
const setValue = (selector, value) => {
|
||||
const field = form.querySelector(selector);
|
||||
if (field) {
|
||||
field.value = value;
|
||||
}
|
||||
};
|
||||
|
||||
setValue('input[name="event_time"]', payload.time || "");
|
||||
setValue('[name="event_comment"]', payload.comment || "");
|
||||
setValue('[name="event_value"]', payload.value || "");
|
||||
setValue('[name="event_sport_type_id"]', payload.sport_type_id || "");
|
||||
setValue('[name="event_unit"]', payload.unit || "");
|
||||
setValue('[name="event_walk_mode"]', payload.unit === "steps" ? "steps" : "time");
|
||||
setValue('[name="event_mood"]', payload.mood ?? 0);
|
||||
setValue('[name="event_energy"]', payload.energy ?? 0);
|
||||
setValue('[name="event_stress"]', payload.stress ?? 0);
|
||||
|
||||
const consumedYes = form.querySelector('input[name="event_consumed"][value="1"]');
|
||||
const consumedNo = form.querySelector('input[name="event_consumed"][value="0"]');
|
||||
if (consumedYes && consumedNo) {
|
||||
if (payload.consumed) {
|
||||
consumedYes.checked = true;
|
||||
} else {
|
||||
consumedNo.checked = true;
|
||||
}
|
||||
}
|
||||
|
||||
form.querySelectorAll('input[name="event_walk_mode"]').forEach(radio => {
|
||||
radio.checked = radio.value === (payload.unit === "steps" ? "steps" : "time");
|
||||
});
|
||||
|
||||
form.querySelectorAll("[data-stepper]").forEach(stepper => {
|
||||
const input = stepper.querySelector("[data-stepper-input]");
|
||||
const value = stepper.querySelector("[data-stepper-value]");
|
||||
const minus = stepper.querySelector("[data-stepper-minus]");
|
||||
const plus = stepper.querySelector("[data-stepper-plus]");
|
||||
if (!input || !value || !minus || !plus) return;
|
||||
const current = Math.max(-2, Math.min(2, Number(input.value || 0)));
|
||||
const metric = stepper.dataset.stepperMetric || "mood";
|
||||
input.value = String(current);
|
||||
value.textContent = `${current > 0 ? "+" : ""}${current}`;
|
||||
minus.disabled = current <= -2;
|
||||
plus.disabled = current >= 2;
|
||||
const ring = stepper.querySelector(".overlay-signal-card__ring");
|
||||
if (ring) {
|
||||
ring.classList.remove("tone-neg2", "tone-neg1", "tone-zero", "tone-pos1", "tone-pos2");
|
||||
ring.classList.add(toneClass(current, metric));
|
||||
}
|
||||
});
|
||||
|
||||
form.querySelectorAll("[data-sport-choice]").forEach(button => {
|
||||
button.classList.toggle("is-selected", button.dataset.sportChoice === (payload.sport_type_id || ""));
|
||||
});
|
||||
};
|
||||
|
||||
if (openMoment) {
|
||||
openMoment.addEventListener("click", event => {
|
||||
event.preventDefault();
|
||||
showMomentChoose();
|
||||
setOverlay(momentOverlay, true);
|
||||
});
|
||||
}
|
||||
|
||||
closeMoment.forEach(button => {
|
||||
button.addEventListener("click", event => {
|
||||
event.preventDefault();
|
||||
setOverlay(momentOverlay, false);
|
||||
showMomentChoose();
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll("[data-moment-type-choice]").forEach(button => {
|
||||
button.addEventListener("click", event => {
|
||||
event.preventDefault();
|
||||
showMomentForm(button.dataset.momentTypeChoice || "event");
|
||||
});
|
||||
});
|
||||
|
||||
if (backButton) {
|
||||
backButton.addEventListener("click", event => {
|
||||
event.preventDefault();
|
||||
showMomentChoose();
|
||||
});
|
||||
}
|
||||
|
||||
document.querySelectorAll("[data-sport-choice]").forEach(button => {
|
||||
button.addEventListener("click", event => {
|
||||
event.preventDefault();
|
||||
const hidden = document.querySelector('input[name="event_sport_type_id"]');
|
||||
if (!hidden) {
|
||||
return;
|
||||
}
|
||||
|
||||
hidden.value = button.dataset.sportChoice || "";
|
||||
document.querySelectorAll("[data-sport-choice]").forEach(item => item.classList.remove("is-selected"));
|
||||
button.classList.add("is-selected");
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('input[name="event_walk_mode"]').forEach(radio => {
|
||||
radio.addEventListener("change", () => {
|
||||
if (!valueInput || !valueLabel || !unitInput || !walkModeInput) {
|
||||
return;
|
||||
}
|
||||
|
||||
const mode = radio.checked ? radio.value : walkModeInput.value;
|
||||
walkModeInput.value = mode;
|
||||
unitInput.value = mode === "steps" ? "steps" : "min";
|
||||
valueLabel.textContent = mode === "steps" ? "Schritte" : "Dauer";
|
||||
valueInput.placeholder = mode === "steps" ? "Schritte" : "Minuten";
|
||||
valueInput.step = "1";
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll("[data-confirm-delete]").forEach(button => {
|
||||
button.addEventListener("click", event => {
|
||||
if (!window.confirm("Diesen Moment wirklich löschen?")) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll("[data-event-editable]").forEach(card => {
|
||||
card.addEventListener("click", event => {
|
||||
if (event.target.closest("form") || event.target.closest("button")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const payload = decodePayload(card.dataset.eventPayload || "");
|
||||
if (!payload) {
|
||||
return;
|
||||
}
|
||||
|
||||
populateMomentForm(payload);
|
||||
setOverlay(momentOverlay, true);
|
||||
});
|
||||
});
|
||||
|
||||
if (typeSelect && unitInput) {
|
||||
const syncUnit = () => {
|
||||
const option = typeSelect.options[typeSelect.selectedIndex];
|
||||
unitInput.value = option?.dataset.defaultUnit || "";
|
||||
};
|
||||
|
||||
syncUnit();
|
||||
typeSelect.addEventListener("change", syncUnit);
|
||||
}
|
||||
|
||||
if (swipeContainer) {
|
||||
let pointerStartX = 0;
|
||||
let pointerStartY = 0;
|
||||
let dragging = false;
|
||||
|
||||
const handleSwipe = (deltaX, deltaY) => {
|
||||
if (Math.abs(deltaX) < 70 || Math.abs(deltaY) > 50) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (deltaX < 0 && swipeContainer.dataset.nextDate) {
|
||||
window.location.href = dashboardDayPath(swipeContainer.dataset.nextDate);
|
||||
} else if (deltaX > 0 && swipeContainer.dataset.prevDate) {
|
||||
window.location.href = dashboardDayPath(swipeContainer.dataset.prevDate);
|
||||
}
|
||||
};
|
||||
|
||||
swipeContainer.addEventListener("pointerdown", event => {
|
||||
dragging = true;
|
||||
pointerStartX = event.clientX;
|
||||
pointerStartY = event.clientY;
|
||||
});
|
||||
|
||||
swipeContainer.addEventListener("pointerup", event => {
|
||||
if (!dragging) {
|
||||
return;
|
||||
}
|
||||
|
||||
dragging = false;
|
||||
handleSwipe(event.clientX - pointerStartX, event.clientY - pointerStartY);
|
||||
});
|
||||
|
||||
swipeContainer.addEventListener("pointercancel", () => {
|
||||
dragging = false;
|
||||
});
|
||||
|
||||
window.addEventListener("keydown", event => {
|
||||
if (document.body.classList.contains("is-dashboard-overlay-open")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.key === "ArrowLeft" && swipeContainer.dataset.prevDate) {
|
||||
window.location.href = dashboardDayPath(swipeContainer.dataset.prevDate);
|
||||
}
|
||||
|
||||
if (event.key === "ArrowRight" && swipeContainer.dataset.nextDate) {
|
||||
window.location.href = dashboardDayPath(swipeContainer.dataset.nextDate);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function initOptionsPanels() {
|
||||
const overlay = document.querySelector("[data-options-overlay]");
|
||||
if (!overlay) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (overlay.parentElement !== document.body) {
|
||||
document.body.appendChild(overlay);
|
||||
}
|
||||
|
||||
const panels = [...overlay.querySelectorAll("[data-options-panel]")];
|
||||
const closeButtons = [...overlay.querySelectorAll("[data-options-close]")];
|
||||
const backButtons = [...overlay.querySelectorAll("[data-options-back]")];
|
||||
const initialPanel = overlay.dataset.openPanel || null;
|
||||
|
||||
const setOpen = (panelName) => {
|
||||
overlay.hidden = panelName === null;
|
||||
document.body.classList.toggle("is-dashboard-overlay-open", panelName !== null);
|
||||
|
||||
panels.forEach(panel => {
|
||||
panel.hidden = panel.dataset.optionsPanel !== panelName;
|
||||
});
|
||||
|
||||
if (panelName === "stats") {
|
||||
window.setTimeout(() => {
|
||||
initDashboardCharts();
|
||||
}, 40);
|
||||
}
|
||||
};
|
||||
|
||||
document.querySelectorAll("[data-options-open]").forEach(button => {
|
||||
button.addEventListener("click", event => {
|
||||
event.preventDefault();
|
||||
setOpen(button.dataset.optionsOpen || null);
|
||||
});
|
||||
});
|
||||
|
||||
closeButtons.forEach(button => {
|
||||
button.addEventListener("click", event => {
|
||||
event.preventDefault();
|
||||
setOpen(null);
|
||||
});
|
||||
});
|
||||
|
||||
backButtons.forEach(button => {
|
||||
button.addEventListener("click", event => {
|
||||
event.preventDefault();
|
||||
setOpen(null);
|
||||
});
|
||||
});
|
||||
|
||||
if (initialPanel) {
|
||||
setOpen(initialPanel);
|
||||
}
|
||||
}
|
||||
|
||||
function csrfToken() {
|
||||
return document.querySelector('meta[name="csrf-token"]')?.getAttribute("content") || "";
|
||||
}
|
||||
@@ -1300,6 +1830,8 @@
|
||||
initTrackPreview();
|
||||
initArchiveMobileDetail();
|
||||
initDashboardCharts();
|
||||
initDashboardExperience();
|
||||
initOptionsPanels();
|
||||
initSportTypeManager();
|
||||
initPwaShell();
|
||||
initPullToRefresh();
|
||||
|
||||
Reference in New Issue
Block a user