pkmntrade.club/static/js/base.js

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);
}
}
};
}
})();