@schukai/monster
Version:
Monster is a simple library for creating fast, robust and lightweight websites.
306 lines (281 loc) • 8.03 kB
JavaScript
/**
* Copyright © Volker Schukai and all contributing authors, {{copyRightYear}}. All rights reserved.
* Node module: @schukai/monster
*
* This source code is licensed under the GNU Affero General Public License Version 3 (AGPLv3).
* The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.de.html
*
* For those who do not wish to adhere to the AGPLv3, a commercial license is available.
* For more information about purchasing a commercial license, please contact Volker Schukai.
*/
import { instanceSymbol } from "../../constants.mjs";
import { ATTRIBUTE_ROLE } from "../../dom/constants.mjs";
import { CustomElement } from "../../dom/customelement.mjs";
import {
assembleMethodSymbol,
registerCustomElement,
} from "../../dom/customelement.mjs";
import { isFunction, isObject, isArray } from "../../types/is.mjs";
import { LocalePickerStyleSheet } from "./stylesheet/locale-picker.mjs";
import { detectUserLanguagePreference } from "../../i18n/util.mjs";
import "../form/select.mjs";
export { LocaleSelect as LocalSelector };
/**
* @private
* @type {symbol}
* Reference to the select element
*/
const selectElementSymbol = Symbol("selectElement");
/**
* @private
* @type {symbol}
* Stores detected languages
*/
const detectedLanguagesSymbol = Symbol("detectedLanguages");
/**
* Returns the localized label for the language selection.
* @return {string}
*/
function getLocalizedLabel() {
const lang = document.documentElement.lang || navigator.language || "en";
switch (lang.split("-")[0]) {
case "de":
return "Sprache wählen";
case "fr":
return "Sélectionnez une langue";
case "es":
return "Seleccione un idioma";
case "it":
return "Seleziona una lingua";
case "pt":
return "Selecione um idioma";
case "nl":
return "Selecteer een taal";
case "pl":
return "Wybierz język";
case "ru":
return "Выберите язык";
case "cs":
return "Vyberte jazyk";
case "sk":
return "Vyberte jazyk";
case "bg":
return "Изберете език";
case "hr":
return "Odaberite jezik";
case "fi":
return "Valitse kieli";
case "sv":
return "Välj ett språk";
case "el":
return "Επιλέξτε γλώσσα";
case "hu":
return "Válasszon egy nyelvet";
case "ro":
return "Selectați o limbă";
case "da":
return "Vælg et sprog";
case "no":
return "Velg et språk";
case "hi":
return "एक भाषा चुनें";
case "bn":
return "একটি ভাষা নির্বাচন করুন";
case "ta":
return "ஒரு மொழியைத் தேர்ந்தெடுக்கவும்";
case "te":
return "భాషను ఎంచుకోండి";
case "mr":
return "एक भाषा निवडा";
case "zh":
return "选择一种语言";
case "ja":
return "言語を選択してください";
default:
return "Select a language";
}
}
/**
* LocaleSelect component
*
* Displays a monster-select with all available languages except the current document language.
*
* @fragments /fragments/components/accessibility/locale-select/
*
* @example /examples/components/accessibility/locale-select-simple Simple language switcher
*
* @since 3.97.0
* @copyright Volker Schukai
* @summary A simple language switcher as a select.
*/
class LocaleSelect extends CustomElement {
/**
* Used by the instanceof operator.
* @returns {symbol}
*/
static get [instanceSymbol]() {
return Symbol.for(
"@schukai/monster/components/accessibility/local-selector@@instance",
);
}
/**
* Initializes the component.
* @return {LocaleSelect}
*/
[assembleMethodSymbol]() {
super[assembleMethodSymbol]();
initControlReferences.call(this);
initSelectOptions.call(this);
initEventHandler.call(this);
return this;
}
/**
* Default options
*
* @properties {Object} templates - Templates for the component.
* @properties {string} templates.main - Main template.
* @properties {Object} features - Feature toggles.
* @properties {boolean} features.resetCookie - Whether to show a reset cookie button.
* @properties {Object} cookie - Cookie settings.
* @properties {string} cookie.name - Name of the cookie to store the selected language.
* @properties {Object} labels - Localized labels.
* @properties {string} labels["select-an-option"] - Label for the select option.
* @properties {Object} callbacks - Callback functions.
* @properties {boolean} disabled - Whether the component is disabled.
*/
get defaults() {
return Object.assign({}, super.defaults, {
templates: {
main: getTemplate(),
},
features: {
resetCookie: true,
},
cookie: {
name: "language",
},
labels: {
"select-an-option": getLocalizedLabel(),
},
callbacks: {},
disabled: false,
});
}
/**
* connectedCallback
*/
connectedCallback() {
super.connectedCallback();
this[detectedLanguagesSymbol] = detectUserLanguagePreference();
initSelectOptions.call(this);
}
/**
* @return {string}
*/
static getTag() {
return "monster-locale-select";
}
/**
* @return {CSSStyleSheet[]}
*/
static getCSSStyleSheet() {
return [LocalePickerStyleSheet];
}
/**
* Export parts from monster-select to make them available for styling outside.
*/
static get exportparts() {
// The parts from monster-select, as defined in source/components/form/select.mjs:
return [
"control",
"container",
"popper",
"option",
"option-label",
"option-control",
"badge",
"badge-label",
"remove-badge",
"summary",
"status-or-remove-badges",
"remote-info",
"no-options",
"selection",
"inline-filter",
"popper-filter",
"content",
].join(",");
}
}
/**
* Initializes the reference to the select element.
* @private
*/
function initControlReferences() {
this[selectElementSymbol] = this.shadowRoot.querySelector(
`[${ATTRIBUTE_ROLE}="select"]`,
);
}
/**
* Initializes the options of the select element.
* @private
*/
function initSelectOptions() {
const detected =
this[detectedLanguagesSymbol] || detectUserLanguagePreference();
let options = [];
if (Array.isArray(detected.available)) {
const currentLang = detected.current;
options = detected.available.filter(
(lang) => lang.baseLang !== currentLang && lang.fullLang !== currentLang,
);
} else if (Array.isArray(detected.allOfferable)) {
options = detected.allOfferable;
}
options = detected.allOfferable;
if (Array.isArray(options)) {
options = options.filter(
(lang) => lang.baseLang !== "x-default" && lang.fullLang !== "x-default",
);
}
if (this[selectElementSymbol] && isArray(options)) {
this[selectElementSymbol].setOption("mapping.labelTemplate", "${label}");
this[selectElementSymbol].setOption("mapping.valueTemplate", "${href}");
this[selectElementSymbol].setOption(
"labels.select-an-option",
this.getOption("labels.select-an-option"),
);
this[selectElementSymbol].importOptions(options);
}
}
/**
* Initializes the event handler for the select element.
* @private
*/
function initEventHandler() {
if (!this[selectElementSymbol]) return;
this[selectElementSymbol].addEventListener("change", (event) => {
const selected = event.target?.value;
if (selected) {
if (
this.getOption("features.resetCookie") &&
this.getOption("cookie.name")
) {
document.cookie = `${this.getOption("cookie.name")}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`;
}
window.location.href = selected;
}
});
}
/**
* Returns the template for the component.
* @private
* @return {string}
*/
function getTemplate() {
// language=HTML
return `
<monster-select exportparts="control,container,popper,option,option-label,option-control,badge,badge-label,remove-badge,summary,status-or-remove-badges,remote-info,no-options,selection,inline-filter,popper-filter,content" data-monster-role="select"></monster-select>
`;
}
registerCustomElement(LocaleSelect);