UNPKG

@schukai/monster

Version:

Monster is a simple library for creating fast, robust and lightweight websites.

306 lines (281 loc) 8.03 kB
/** * 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);