Something went wrong on our end
Select Git revision
Monster_Logging_Handler.ConsoleHandler.html
-
Volker Schukai authoredVolker Schukai authored
message.mjs 7.46 KiB
/**
* Copyright © schukai GmbH 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 schukai GmbH.
*
* SPDX-License-Identifier: AGPL-3.0
*/
import {
ATTRIBUTE_ERRORMESSAGE,
ATTRIBUTE_PREFIX,
ATTRIBUTE_ROLE,
} from "../../dom/constants.mjs";
import {
assembleMethodSymbol,
CustomElement,
initMethodSymbol,
registerCustomElement,
} from "../../dom/customelement.mjs";
import { findTargetElementFromEvent } from "../../dom/events.mjs";
import { isString } from "../../types/is.mjs";
import { MessageStyleSheet } from "./stylesheet/message.mjs";
export { Message };
/**
* @private
* @type {symbol}
*/
const controlElementSymbol = Symbol("controlElement");
/**
* @private
* @type {symbol}
*/
const removeElementSymbol = Symbol("removeElement");
/**
* @private
* @type {symbol}
*/
const timerSymbol = Symbol("timer");
/**
* @private
* @type {symbol}
*/
const mouseenterEventHandlerSymbol = Symbol("mouseenterEventHandler");
/**
* @private
* @type {symbol}
*/
const mouseleaveEventHandlerSymbol = Symbol("mouseleaveEventHandler");
/**
* @private
* @type {symbol}
*/
const removeEventHandlerSymbol = Symbol("removeEventHandler");
/**
* A Message is a notification element that can be used to display messages to the user.
*
* @fragments /fragments/components/notify/message
*
* @example /examples/components/notify/message-simple
*
* @issue https://localhost.alvine.dev:8443/development/issues/closed/269.html
*
* @since 1.0.0
* @copyright schukai GmbH
* @summary The message is a notification element that can be used to display messages to the user. Typically, it is only used in conjunction with the notify container.
*/
class Message extends CustomElement {
/**
* The defaults can be set either directly in the object or via an attribute in the HTML tag.
* The value of the attribute `data-monster-options` in the HTML tag must be a JSON string.
*
* ```
* <monster-message data-monster-options="{}"></monster-message>
* ```
*
* Since 1.18.0 the JSON can be specified as a DataURI.
*
* ```
* new Monster.Types.DataUrl(btoa(JSON.stringify({
* timeout: 3000,
* features: {
* close: true,
* disappear: true
* }
* })),'application/json',true).toString()
* ```
*
* @property {Object} templates Template definitions
* @property {string} templates.main Main template
* @property {number} timeout The time in milliseconds after which the message disappears
* @property {Object} features The features of the message
* @property {boolean} features.close Whether the message can be closed
* @property {boolean} features.disappear Whether the message disappears after a certain time
* @property {string} content The content of the message
*/
get defaults() {
return Object.assign(
{},
super.defaults,
{
timeout: 6000,
features: {
close: true,
disappear: true,
},
content: "<slot></slot>",
templates: {
main: getTemplate(),
},
},
);
}
/**
*
* @return {Monster.Components.Notify.Message}
*/
[assembleMethodSymbol]() {
super[assembleMethodSymbol]();
initControlReferences.call(this);
initEventHandler.call(this);
return this;
}
/**
*
* @return {string}
*/
static getTag() {
return "monster-notify-message";
}
/**
*
* @return {CSSStyleSheet[]}
*/
static getCSSStyleSheet() {
return [MessageStyleSheet];
}
/**
*
*/
[initMethodSymbol]() {
super[initMethodSymbol]();
}
/**
* @return {void}
*/
connectedCallback() {
super.connectedCallback();
if (this.getOption("features.disappear") === true) {
startFadeout.call(this);
this.addEventListener("mouseenter", this[mouseenterEventHandlerSymbol]);
}
}
/**
* @return {void}
*/
disconnectedCallback() {
super.disconnectedCallback();
stopFadeout.call(this);
if (this.getOption("features.disappear") === true) {
this.removeEventListener(
"mouseenter",
this[mouseenterEventHandlerSymbol],
);
this.removeEventListener(
"mouseenter",
this[mouseleaveEventHandlerSymbol],
);
}
}
}
/**
* @private
*/
function startFadeout() {
if (!this?.[timerSymbol]) {
this[timerSymbol] = setTimeout(
() => {
removeSelf.call(this);
},
this.getOption("timeout", 1000),
);
}
}
function removeSelf() {
stopFadeout();
this.classList.add("fadeout");
setTimeout(() => {
this.remove();
}, 200);
}
/**
* @private
*/
function stopFadeout() {
if (this?.[timerSymbol]) {
clearTimeout(this[timerSymbol]);
this[timerSymbol] = undefined;
}
}
/**
* @private
* @return {Message}
* @throws {Error} no shadow-root is defined
*/
function initControlReferences() {
if (!this.shadowRoot) {
throw new Error("no shadow-root is defined");
}
this[controlElementSymbol] = this.shadowRoot.querySelector(
"[" + ATTRIBUTE_ROLE + "=control]",
);
this[removeElementSymbol] = this.shadowRoot.querySelector(
"[" + ATTRIBUTE_ROLE + "=close]",
);
}
/**
* @private
*/
function initEventHandler() {
/**
* @param {Event} event
*/
this[mouseenterEventHandlerSymbol] = (event) => {
const element = findTargetElementFromEvent(
event,
ATTRIBUTE_ROLE,
"control",
);
if (element instanceof HTMLElement) {
this.removeEventListener(
"mouseenter",
this[mouseenterEventHandlerSymbol],
);
this.addEventListener("mouseleave", this[mouseleaveEventHandlerSymbol]);
stopFadeout.call(this);
}
};
/**
* @param {Event} event
*/
this[mouseleaveEventHandlerSymbol] = (event) => {
const element = findTargetElementFromEvent(
event,
ATTRIBUTE_ROLE,
"control",
);
if (element instanceof HTMLElement) {
this.removeEventListener(
"mouseleave",
this[mouseleaveEventHandlerSymbol],
);
this.addEventListener("mouseenter", this[mouseenterEventHandlerSymbol]);
startFadeout.call(this);
}
};
/**
* @param {Event} event
*/
this[removeEventHandlerSymbol] = (event) => {
const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, "close");
if (element instanceof HTMLElement) {
removeSelf.call(this);
}
};
if (this.getOption("features.close") === true) {
this[removeElementSymbol].addEventListener(
"click",
this[removeEventHandlerSymbol],
);
this[removeElementSymbol].addEventListener(
"touch",
this[removeEventHandlerSymbol],
);
}
return this;
}
/**
* @private
* @return {string}
*/
function getTemplate() {
// language=HTML
return `
<div data-monster-role="control" part="control" class="center">
<div data-monster-role="message" part="message"
data-monster-attributes="data-monster-orientation path:orientation">
<div data-monster-replace="path:content" part="content"
data-monster-role="content">
</div>
<div part="remove"
data-monster-attributes="class path:features.close | ?::hidden "
data-monster-role="close" tabindex="-1"></div>
</div>
</div>
`;
}
registerCustomElement(Message);