184 lines
No EOL
7.3 KiB
JavaScript
184 lines
No EOL
7.3 KiB
JavaScript
/* global window, document, localStorage */
|
|
|
|
const $ = selector => Array.from(document.querySelectorAll(selector));
|
|
const $$ = selector => Array.from(document.querySelector(selector));
|
|
|
|
(() => {
|
|
"use strict";
|
|
|
|
/**
|
|
* Initialize the theme toggle button functionality.
|
|
* Toggles between 'dark' and 'light' themes and persists the state in localStorage.
|
|
*/
|
|
function initThemeToggle() {
|
|
const themeToggleButton = document.getElementById("theme-toggle-btn");
|
|
if (!themeToggleButton) return;
|
|
themeToggleButton.classList.toggle("btn-ghost", !("theme" in localStorage));
|
|
themeToggleButton.addEventListener("click", () => {
|
|
const documentRoot = document.documentElement;
|
|
const isSystemTheme = themeToggleButton.classList.contains("btn-ghost");
|
|
const isDarkTheme = documentRoot.classList.contains("dark");
|
|
const newTheme = isSystemTheme ? "dark" : (isDarkTheme ? "light" : "system");
|
|
|
|
if (newTheme === "system") {
|
|
documentRoot.classList.toggle("dark", window.matchMedia("(prefers-color-scheme: dark)").matches);
|
|
documentRoot.setAttribute("data-theme", window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light");
|
|
localStorage.removeItem("theme");
|
|
} else {
|
|
if (newTheme === "light") {
|
|
documentRoot.classList.remove("dark");
|
|
} else if (newTheme === "dark") {
|
|
documentRoot.classList.add("dark");
|
|
}
|
|
documentRoot.setAttribute("data-theme", newTheme);
|
|
localStorage.setItem("theme", newTheme);
|
|
}
|
|
themeToggleButton.classList.toggle("btn-ghost", newTheme === "system");
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Initialize event listeners for forms containing multiselect fields.
|
|
* When the form is submitted, process each 'card-multiselect' to create hidden inputs.
|
|
*/
|
|
function initCardMultiselectHandling() {
|
|
const forms = document.querySelectorAll("form");
|
|
forms.forEach(form => {
|
|
if (form.querySelector("select.card-multiselect")) {
|
|
form.addEventListener("submit", () => {
|
|
processMultiselectForm(form);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Process multiselect fields within a form before submission by:
|
|
* - Creating hidden inputs for each selected option with value in 'card_id:quantity' format.
|
|
* - Removing the original name attribute to avoid duplicate submissions.
|
|
*
|
|
* @param {HTMLFormElement} form - The form element to process.
|
|
*/
|
|
function processMultiselectForm(form) {
|
|
const multiselectFields = form.querySelectorAll("select.card-multiselect");
|
|
multiselectFields.forEach(selectField => {
|
|
const originalFieldName =
|
|
selectField.getAttribute("data-original-name") || selectField.getAttribute("name");
|
|
if (!originalFieldName) return;
|
|
selectField.setAttribute("data-original-name", originalFieldName);
|
|
|
|
// Remove any previously generated hidden inputs for this multiselect.
|
|
form
|
|
.querySelectorAll(`input[data-generated-for-card-multiselect="${originalFieldName}"]`)
|
|
.forEach(input => input.remove());
|
|
|
|
// For each selected option, create a hidden input.
|
|
selectField.querySelectorAll("option:checked").forEach(option => {
|
|
const cardId = option.value;
|
|
const quantity = option.getAttribute("data-quantity") || "1";
|
|
const hiddenInput = document.createElement("input");
|
|
hiddenInput.type = "hidden";
|
|
hiddenInput.name = originalFieldName;
|
|
hiddenInput.value = `${cardId}:${quantity}`;
|
|
hiddenInput.setAttribute("data-generated-for-card-multiselect", originalFieldName);
|
|
form.appendChild(hiddenInput);
|
|
});
|
|
|
|
// Prevent the browser from submitting the select field directly.
|
|
selectField.removeAttribute("name");
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Reset stale selections in all card multiselect fields.
|
|
* This is triggered on the window's 'pageshow' event to clear any lingering selections.
|
|
*/
|
|
function resetCardMultiselectState() {
|
|
const multiselectFields = document.querySelectorAll("select.card-multiselect");
|
|
multiselectFields.forEach(selectField => {
|
|
// Deselect all options.
|
|
selectField.querySelectorAll("option").forEach(option => {
|
|
option.selected = false;
|
|
});
|
|
|
|
// If the select field has an associated Choices.js instance, clear its selection.
|
|
if (selectField.choicesInstance) {
|
|
const activeSelections = selectField.choicesInstance.getValue(true);
|
|
if (activeSelections.length > 0) {
|
|
selectField.choicesInstance.removeActiveItemsByValue(activeSelections);
|
|
}
|
|
selectField.choicesInstance.setValue([]);
|
|
}
|
|
});
|
|
}
|
|
|
|
// On DOMContentLoaded, initialize theme toggling and form processing.
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
initThemeToggle();
|
|
initCardMultiselectHandling();
|
|
});
|
|
|
|
// On pageshow, only reset multiselect state if the page was loaded from bfcache.
|
|
window.addEventListener("pageshow", function(event) {
|
|
if (event.persisted) {
|
|
resetCardMultiselectState();
|
|
}
|
|
});
|
|
|
|
// Expose tradeOfferCard globally if not already defined.
|
|
if (!window.tradeOfferCard) {
|
|
window.tradeOfferCard = function() {
|
|
return {
|
|
flipped: false,
|
|
show_back: false,
|
|
collapsed: false,
|
|
/*
|
|
* flipWithCollapse() now applies the height transition directly on the visible card face.
|
|
* It measures the current face (front or back) and the target face's height,
|
|
* then animates the current face's height change before toggling the flip.
|
|
*
|
|
* Make sure your template markup includes:
|
|
* - x-ref="front" on the front card face
|
|
* - x-ref="back" on the back card face
|
|
*/
|
|
flipWithCollapse() {
|
|
// Determine the currently visible face and the target face.
|
|
const currentFace = this.flipped ? this.$refs.back : this.$refs.front;
|
|
const targetFace = this.flipped ? this.$refs.front : this.$refs.back;
|
|
const container = this.$refs.container;
|
|
|
|
// Temporarily force target face to display to measure its height.
|
|
const originalHeight = targetFace.style.height;
|
|
const originalDisplay = targetFace.style.display;
|
|
const originalPosition = targetFace.style.position;
|
|
const originalVisibility = targetFace.style.visibility;
|
|
targetFace.style.height = 'auto';
|
|
targetFace.style.display = 'block';
|
|
targetFace.style.position = 'absolute';
|
|
targetFace.style.visibility = 'hidden';
|
|
const targetHeight = targetFace.offsetHeight + 18;
|
|
targetFace.style.height = originalHeight;
|
|
targetFace.style.display = originalDisplay;
|
|
targetFace.style.position = originalPosition;
|
|
targetFace.style.visibility = originalVisibility;
|
|
|
|
container.setAttribute('x-collapse.duration.500ms.min.' + targetHeight + 'px', '');
|
|
this.collapsed = true;
|
|
setTimeout(() => {
|
|
this.show_back = this.flipped;
|
|
this.flipped = !this.flipped;
|
|
setTimeout(() => {
|
|
this.show_back = !this.show_back;
|
|
}, 250)
|
|
setTimeout(() => {
|
|
this.collapsed = false;
|
|
setTimeout(() => {
|
|
container.removeAttribute('x-collapse.duration.500ms.min.' + targetHeight + 'px');
|
|
}, 550);
|
|
}, 550);
|
|
}, 550);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
})(); |