class LocalizationForm extends HTMLElement {
constructor() {
super();
this.elements = {
modal: this.querySelector('[data-modal="localization"]'),
localizationButtons: this.querySelectorAll(
"button[data-target=\"[data-modal='localization']\"]"
),
languageInput: this.querySelector("input[name='locale_code']"),
languageSelector: this.querySelector("button#language-selector"),
languageSelectorText: null,
languageList: this.querySelector('ul.language-list[role="list"]'),
languageListItems: null,
currencyInput: this.querySelector("input[name='country_code']"),
currencySelector: this.querySelector("button#currency-selector"),
currencySelectorText: null,
currencyList: this.querySelector('ul.currency-list[role="list"]'),
currencyListItems: null,
};
this.languageOptions = [];
this.currencyOptions = [];
this.initLocalizationButtons();
this.initLanguageSelector();
this.initCurrencySelector();
}
initLocalizationButtons() {
this.elements.localizationButtons.forEach((button) => {
button.addEventListener("click", this.toggleModal.bind(this));
});
// Tap-outside-to-dismiss: click on the modal backdrop (the `fixed inset-0`
// wrapper) closes the modal. `e.target === this.elements.modal` ensures we
// only close when the click is on the backdrop itself, not on the white
// card or any of its descendants (events bubble up).
if (this.elements.modal) {
this.elements.modal.addEventListener("click", (e) => {
if (e.target === this.elements.modal) {
this.toggleModal();
}
});
}
// Escape-to-dismiss for keyboard / accessibility. Each
// instance adds its own document listener; each listener only acts on its
// own modal so multiple instances don't fight (only one modal can be
// open at any time because each form scopes its own modal subtree).
document.addEventListener("keydown", (e) => {
if (
e.key === "Escape" &&
this.elements.modal &&
!this.elements.modal.classList.contains("hidden")
) {
this.toggleModal();
}
});
}
initLanguageSelector() {
this.initLanguageButton();
this.initLanguageListItems();
}
initCurrencySelector() {
this.initCurrencyButton();
this.initCurrencyListItems();
}
initLanguageButton() {
if (!this.elements.languageSelector) {
return;
}
this.elements.languageSelectorText =
this.elements.languageSelector.querySelector(".language-button__text");
this.defaultLanguageOption = {
label: this.elements.languageSelectorText.innerText,
value: this.elements.languageSelector.getAttribute("data-value"),
};
this.elements.languageSelector.addEventListener(
"click",
this.toggleLanguageList.bind(this)
);
}
initCurrencyButton() {
if (!this.elements.currencySelector) {
return;
}
this.elements.currencySelectorText =
this.elements.currencySelector.querySelector(".currency-button__text");
this.defaultCurrencyOption = {
label: this.elements.currencySelectorText.innerText,
value: this.elements.currencySelector.getAttribute("data-value"),
};
this.elements.currencySelector.addEventListener(
"click",
this.toggleCurrencyList.bind(this)
);
}
initLanguageListItems() {
const languageItems = this.querySelectorAll("button.language-list__item");
this.elements.languageListItems = languageItems;
languageItems.forEach((item) => {
const textElement = item.querySelector(".language-list__item-text");
const label = textElement.innerText;
const value = item.getAttribute("data-value");
this.languageOptions.push({ label, value });
item.addEventListener("click", this.onSelectLanguage.bind(this));
});
}
initCurrencyListItems() {
const currencyItems = this.querySelectorAll("button.currency-list__item");
this.elements.currencyListItems = currencyItems;
currencyItems.forEach((item) => {
const textElement = item.querySelector(".currency-list__item-text");
const label = textElement.innerText;
const value = item.getAttribute("data-value");
this.currencyOptions.push({ label, value });
item.addEventListener("click", this.onSelectCurrency.bind(this));
});
}
toggleModal() {
this.elements.modal.classList.toggle("hidden");
this.resetLanguageSelector();
this.resetCurrencySelector();
}
toggleLanguageList() {
const isExpanded =
this.elements.languageSelector.getAttribute("aria-expanded") === "true";
this.elements.languageSelector.setAttribute(
"aria-expanded",
String(!isExpanded)
);
this.elements.languageList.hidden = isExpanded;
}
toggleCurrencyList() {
const isExpanded =
this.elements.currencySelector.getAttribute("aria-expanded") === "true";
this.elements.currencySelector.setAttribute(
"aria-expanded",
String(!isExpanded)
);
this.elements.currencyList.hidden = isExpanded;
}
onSelectLanguage(event) {
event.preventDefault();
const item = event.currentTarget;
const textElement = item.querySelector(".language-list__item-text");
const label = textElement.innerText;
const value = item.getAttribute("data-value");
item.setAttribute(
"data-value",
this.elements.languageSelector.getAttribute("data-value")
);
textElement.innerText = this.elements.languageSelectorText.innerText;
this.elements.languageSelector.setAttribute("data-value", value);
this.elements.languageSelectorText.innerText = label;
this.elements.languageInput.value = value;
this.toggleLanguageList();
}
onSelectCurrency(event) {
event.preventDefault();
const item = event.currentTarget;
const textElement = item.querySelector(".currency-list__item-text");
const label = textElement.innerText;
const value = item.getAttribute("data-value");
item.setAttribute(
"data-value",
this.elements.currencySelector.getAttribute("data-value")
);
textElement.innerText = this.elements.currencySelectorText.innerText;
this.elements.currencySelector.setAttribute("data-value", value);
this.elements.currencySelectorText.innerText = label;
this.elements.currencyInput.value = value;
this.toggleCurrencyList();
}
resetLanguageSelector() {
if (!this.elements.languageSelector) {
return;
}
const { value, label } = this.defaultLanguageOption;
this.elements.languageSelector.setAttribute("aria-expanded", "false");
this.elements.languageSelector.setAttribute("data-value", value);
this.elements.languageSelectorText.innerText = label;
this.elements.languageList.hidden = true;
this.elements.languageListItems.forEach((item, index) => {
const { label, value } = this.languageOptions[index];
item.setAttribute("data-value", value);
item.querySelector(".language-list__item-text").innerText = label;
});
}
resetCurrencySelector() {
if (!this.elements.currencySelector) {
return;
}
const { value, label } = this.defaultCurrencyOption;
this.elements.currencySelector.setAttribute("aria-expanded", "false");
this.elements.currencySelector.setAttribute("data-value", value);
this.elements.currencySelectorText.innerText = label;
this.elements.currencyList.hidden = true;
this.elements.currencyListItems.forEach((item, index) => {
const { label, value } = this.currencyOptions[index];
item.setAttribute("data-value", value);
item.querySelector(".currency-list__item-text").innerText = label;
});
}
}
if (!customElements.get("localization-form")) {
customElements.define("localization-form", LocalizationForm);
}