@schukai/monster
Version:
Monster is a simple library for creating fast, robust and lightweight websites.
290 lines (257 loc) • 6.71 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.en.html
*
* For those who do not wish to adhere to the AGPLv3, a commercial license is available.
* Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms.
* For more information about purchasing a commercial license, please contact Volker Schukai.
*
* SPDX-License-Identifier: AGPL-3.0
*/
import {
assembleMethodSymbol,
registerCustomElement,
} from "../../dom/customelement.mjs";
import { DetailsStyleSheet } from "./stylesheet/details.mjs";
import { ATTRIBUTE_BUTTON_LABEL } from "../host/constants.mjs";
import { isString } from "../../types/is.mjs";
import { generateUniqueConfigKey } from "../host/util.mjs";
import { Collapse, nameSymbol } from "./collapse.mjs";
import { instanceSymbol } from "../../constants.mjs";
import { getLocaleOfDocument } from "../../dom/locale.mjs";
export { Details };
/**
* @private
* @type {symbol}
*/
const buttonElementSymbol = Symbol("buttonElement");
/**
* @private
* @type {symbol}
*/
const buttonEventHandlerSymbol = Symbol("buttonEventHandler");
/**
* A Details component
*
* @fragments /fragments/components/layout/details/
*
* @example /examples/components/layout/details-simple
* @example /examples/components/layout/details-with-label
* @example /examples/components/layout/details-as-accordion
* @example /examples/components/layout/details-with-your-own-design
*
* @since 3.74.0
* @copyright Volker Schukai
* @summary A simple but cool detail component. This is based on the collapse component and extends it with a button.
* @summary You can also easily build an accordion from the component.
*/
class Details extends Collapse {
/**
* This method is called by the `instanceof` operator.
* @return {symbol}
*/
static get [instanceSymbol]() {
return Symbol.for("@schukai/monster/components/layout/details@@instance");
}
/**
*
*/
constructor() {
super();
// the name is only used for the host config and the event name
this[nameSymbol] = "details";
}
/**
* To set the options via the HTML Tag, the attribute `data-monster-options` must be used.
* @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
*
* The individual configuration values can be found in the table.
*
* @property {Object} templates Template definitions
* @property {string} templates.main Main template
* @property {Object} button Button configuration
* @property {string} button.label Button label
*/
get defaults() {
return Object.assign({}, super.defaults, {
templates: {
main: getTemplate(),
},
labels: getTranslations(),
});
}
/**
* @return {void}
*/
[assembleMethodSymbol]() {
super[assembleMethodSymbol]();
initButtonLabel.call(this);
initControlReferences.call(this);
initEventHandler.call(this);
}
/**
* @return {void}
*/
connectedCallback() {
super.connectedCallback();
const containDocument = this.shadowRoot;
if (containDocument !== null) {
const previousElement = this.previousElementSibling;
if (previousElement && previousElement.tagName === "MONSTER-DETAILS") {
this[buttonElementSymbol].style.borderTop = "0";
}
}
}
/**
* @return {string}
*/
static getTag() {
return "monster-details";
}
/**
* @return {CSSStyleSheet[]}
*/
static getCSSStyleSheet() {
const css = super.getCSSStyleSheet();
css.push(DetailsStyleSheet);
return css;
}
}
/**
* @private
* @returns {object}
*/
function getTranslations() {
const locale = getLocaleOfDocument();
switch (locale.language) {
case "de":
return {
button: "Details",
};
case "fr":
return {
button: "Détails",
};
case "sp":
return {
button: "Detalles",
};
case "it":
return {
button: "Dettagli",
};
case "pl":
return {
button: "Szczegóły",
};
case "no":
return {
button: "Detaljer",
};
case "dk":
return {
button: "Detaljer",
};
case "sw":
return {
button: "Detaljer",
};
default:
case "en":
return {
button: "Details",
};
}
}
/**
* @private
* @return {Select}
* @throws {Error} no shadow-root is defined
*/
function initControlReferences() {
if (!this.shadowRoot) {
throw new Error("no shadow-root is defined");
}
this[buttonElementSymbol] = this.shadowRoot.querySelector(
"[data-monster-role=button]",
);
}
/**
* @private
*/
function initEventHandler() {
if (!this.shadowRoot) {
throw new Error("no shadow-root is defined");
}
this[buttonEventHandlerSymbol] = (event) => {
this.toggle();
};
this[buttonElementSymbol].addEventListener(
"click",
this[buttonEventHandlerSymbol],
);
return this;
}
/**
* @private
* @return {string}
*/
function initButtonLabel() {
let label;
const setLabel = false;
if (this.hasAttribute(ATTRIBUTE_BUTTON_LABEL)) {
label = this.getAttribute(ATTRIBUTE_BUTTON_LABEL);
} else {
label = this.getOption("labels.button", "Details");
}
if (!isString(label)) {
label = "";
}
if (label === "") {
label = this.innerText;
}
label = label.trim();
if (label === "") {
label = this.getOption("labels.button", "Details");
}
if (label.length > 100) {
label = `${label.substring(0, 99)}…`;
}
this.setAttribute(ATTRIBUTE_BUTTON_LABEL, label);
this.setOption("labels.button", label);
return label;
}
/**
* @private
* @return {string}
*/
function getConfigKey() {
return generateUniqueConfigKey("details", this.id, "state");
}
/**
* @private
* @return {string}
*/
function getTemplate() {
// language=HTML
return `
<div data-monster-role="control" part="control" class="overflow-hidden">
<div data-monster-role="summary" part="summary">
<button part="button" data-monster-attributes="class path:classes.button"
data-monster-role="button"
data-monster-replace="path:labels.button | default:click me">click me
</button>
</div>
<div data-monster-role="detail">
<div data-monster-attributes="class path:classes.container" part="container"
data-monster-role="container">
<slot part="slot"></slot>
</div>
<div part="deco-line" class="deco-line" data-monster-role="deco"></div>
</div>
</div>`;
}
registerCustomElement(Details);