Skip to content
Snippets Groups Projects
Select Git revision
  • f68f71f94d9f1be5143fe12dd572080090e63d8c
  • master default protected
  • 1.31
  • 4.38.3
  • 4.38.2
  • 4.38.1
  • 4.38.0
  • 4.37.2
  • 4.37.1
  • 4.37.0
  • 4.36.0
  • 4.35.0
  • 4.34.1
  • 4.34.0
  • 4.33.1
  • 4.33.0
  • 4.32.2
  • 4.32.1
  • 4.32.0
  • 4.31.0
  • 4.30.1
  • 4.30.0
  • 4.29.1
23 results

ripple.mjs

Blame
  • log.mjs 9.92 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 { instanceSymbol } from "../../constants.mjs";
    import {
    	assembleMethodSymbol,
    	CustomElement,
    	getSlottedElements,
    	registerCustomElement,
    } from "../../dom/customelement.mjs";
    import { LogStyleSheet } from "./stylesheet/log.mjs";
    import { Entry } from "./log/entry.mjs";
    import { validateInstance, validateString } from "../../types/validate.mjs";
    import "./state.mjs";
    import { getWindow } from "../../dom/util.mjs";
    
    export { Log };
    
    /**
     * @private
     * @type {symbol}
     */
    const logElementSymbol = Symbol("logElement");
    
    /**
     * @private
     * @type {symbol}
     */
    const emptyStateElementSymbol = Symbol("emptyStateElement");
    
    /**
     * A log entry
     *
     * @fragments /fragments/components/state/log
     *
     * @example /examples/components/state/log-simple
     *
     * @issue https://localhost.alvine.dev:8444/development/issues/closed/270.html
     *
     * @since 3.74.0
     * @copyright schukai GmbH
     * @summary The log entry is a single entry in the log.
     **/
    class Log extends CustomElement {
    	/**
    	 * @return {void}
    	 */
    	[assembleMethodSymbol]() {
    		super[assembleMethodSymbol]();
    
    		initControlReferences.call(this);
    		initEventHandler.call(this);
    	}
    
    	/**
    	 * This method is called by the `instanceof` operator.
    	 * @return {symbol}
    	 */
    	static get [instanceSymbol]() {
    		return Symbol.for("@schukai/monster/components/state/log@@instance");
    	}
    
    	/**
    	 * 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} labels Labels
    	 * @property {string} labels.nothingToReport Label for empty state
    	 * @property {Object} classes Classes
    	 * @property {string} classes.direction Direction of the log: ascending or descending
    	 * @property {number} updateFrequency Update frequency in milliseconds for the timestamp
    	 */
    	get defaults() {
    		return Object.assign({}, super.defaults, {
    			templates: {
    				main: getTemplate(),
    			},
    
    			labels: {
    				nothingToReport: "There is nothing to report yet.",
    			},
    
    			features: {
    				direction: "ascending",
    			},
    
    			updateFrequency: 10000,
    
    			entries: [],
    
    			timestamp: 0,
    		});
    	}
    
    	/**
    	 * @return {void}
    	 */
    	connectedCallback() {
    		super.connectedCallback();
    
    		const slottedElements = getSlottedElements.call(this);
    		if (slottedElements.size > 0) {
    			this[emptyStateElementSymbol].style.display = "none";
    		}
    	}
    
    	/**
    	 * Clear the log
    	 *
    	 * @return {Log}
    	 */
    	clear() {
    		this[logElementSymbol].setOption("entries", []);
    		return this;
    	}
    
    	/**
    	 * Add an entry to the log
    	 * @param {Entry} entry
    	 * @return {Log}
    	 */
    	addEntry(entry) {
    		validateInstance(entry, Entry);
    
    		if (entry.date === undefined || entry.date === null) {
    			entry.date = new Date();
    		}
    
    		const entries = this.getOption("entries");
    		if (this.getOption("features.direction") === "ascending") {
    			entries.unshift(entry);
    		} else {
    			entries.push(entry);
    		}
    
    		/** this field is not used, but triggers a change event */
    		this.setOption("length", entries.length - 1);
    
    		return this;
    	}
    
    	/**
    	 * Add a log message
    	 *
    	 * @param {string} message
    	 * @param {Date} date
    	 * @return {Log}
    	 * @throws {TypeError} message is not a string
    	 */
    	addMessage(message, date) {
    		if (!date) {
    			date = new Date();
    		}
    
    		validateString(message);
    
    		this.addEntry(
    			new Entry({
    				message: message,
    				date: date,
    			}),
    		);
    
    		return this;
    	}
    
    	/**
    	 *
    	 * @return {string}
    	 */
    	static getTag() {
    		return "monster-log";
    	}
    
    	/**
    	 * @return {CSSStyleSheet[]}
    	 */
    	static getCSSStyleSheet() {
    		return [LogStyleSheet];
    	}
    }
    
    /**
     * @private
     * @return {Select}
     * @throws {Error} no shadow-root is defined
     */
    function initControlReferences() {
    	if (!this.shadowRoot) {
    		throw new Error("no shadow-root is defined");
    	}
    
    	this[logElementSymbol] = this.shadowRoot.querySelector(
    		"[data-monster-role=control]",
    	);
    	this[emptyStateElementSymbol] = this.shadowRoot.querySelector(
    		"[data-monster-role=empty-state]",
    	);
    }
    
    /**
     * @private
     */
    function initEventHandler() {
    	if (!this.shadowRoot) {
    		throw new Error("no shadow-root is defined");
    	}
    
    	this.shadowRoot.addEventListener("slotchange", (event) => {
    		const slottedElements = getSlottedElements.call(this);
    
    		if (slottedElements.size > 0) {
    			this[emptyStateElementSymbol].style.display = "none";
    		} else {
    			this[emptyStateElementSymbol].style.display = "block";
    		}
    	});
    
    	setInterval(() => {
    		getWindow().requestAnimationFrame(() => {
    			const timestamp = new Date().toTimeString();
    			this.setOption("timestamp", timestamp);
    		});
    	}, this.getOption("updateFrequency"));
    
    	return this;
    }
    
    /**
     * @private
     * @return {string}
     */
    function getTemplate() {
    	// language=HTML
    	return `
            <template id="entry">
                <li data-monster-role="entry">
            <span data-monster-replace="path:entry.user"
                  data-monster-attributes="class path:entry.user | ?:user:hidden"></span>
                    <span data-monster-replace="path:entry.title"
                          data-monster-attributes="class path:entry.title | ?:title:hidden"></span>
                    <span data-monster-replace="path:entry.message"
                          data-monster-attributes="class path:entry.message | ?:message:hidden"></span>
                    <span data-monster-replace="path:entry.date | time-ago"
                          data-monster-attributes="title path:entry.date | datetime"></span>
    
                </li>
            </template>
    
            <div part="control" data-monster-role="control">
                <div data-monster-role="empty-state" data-monster-attributes="class path:entries | has-entries | ?:hidden:">
                    <monster-state>
                        <div part="visual">
                            <svg width="4rem" height="4rem" viewBox="0 -12 512.00032 512"
                                 xmlns="http://www.w3.org/2000/svg">
                                <path d="m455.074219 172.613281 53.996093-53.996093c2.226563-2.222657 3.273438-5.367188 2.828126-8.480469-.441407-3.113281-2.328126-5.839844-5.085938-7.355469l-64.914062-35.644531c-4.839844-2.65625-10.917969-.886719-13.578126 3.953125-2.65625 4.84375-.890624 10.921875 3.953126 13.578125l53.234374 29.230469-46.339843 46.335937-166.667969-91.519531 46.335938-46.335938 46.839843 25.722656c4.839844 2.65625 10.921875.890626 13.578125-3.953124 2.660156-4.839844.890625-10.921876-3.953125-13.578126l-53.417969-29.335937c-3.898437-2.140625-8.742187-1.449219-11.882812 1.695313l-54 54-54-54c-3.144531-3.144532-7.988281-3.832032-11.882812-1.695313l-184.929688 101.546875c-2.757812 1.515625-4.644531 4.238281-5.085938 7.355469-.445312 3.113281.601563 6.257812 2.828126 8.480469l53.996093 53.996093-53.996093 53.992188c-2.226563 2.226562-3.273438 5.367187-2.828126 8.484375.441407 3.113281 2.328126 5.839844 5.085938 7.351562l55.882812 30.6875v102.570313c0 3.652343 1.988282 7.011719 5.1875 8.769531l184.929688 101.542969c1.5.824219 3.15625 1.234375 4.8125 1.234375s3.3125-.410156 4.8125-1.234375l184.929688-101.542969c3.199218-1.757812 5.1875-5.117188 5.1875-8.769531v-102.570313l55.882812-30.683594c2.757812-1.515624 4.644531-4.242187 5.085938-7.355468.445312-3.113282-.601563-6.257813-2.828126-8.480469zm-199.074219 90.132813-164.152344-90.136719 164.152344-90.140625 164.152344 90.140625zm-62.832031-240.367188 46.332031 46.335938-166.667969 91.519531-46.335937-46.335937zm-120.328125 162.609375 166.667968 91.519531-46.339843 46.339844-166.671875-91.519531zm358.089844 184.796875-164.929688 90.5625v-102.222656c0-5.523438-4.476562-10-10-10s-10 4.476562-10 10v102.222656l-164.929688-90.5625v-85.671875l109.046876 59.878907c1.511718.828124 3.167968 1.234374 4.808593 1.234374 2.589844 0 5.152344-1.007812 7.074219-2.929687l54-54 54 54c1.921875 1.925781 4.484375 2.929687 7.074219 2.929687 1.640625 0 3.296875-.40625 4.808593-1.234374l109.046876-59.878907zm-112.09375-46.9375-46.339844-46.34375 166.667968-91.515625 46.34375 46.335938zm0 0"/>
                                <path d="m404.800781 68.175781c2.628907 0 5.199219-1.070312 7.070313-2.933593 1.859375-1.859376 2.929687-4.4375 2.929687-7.066407 0-2.632812-1.070312-5.210937-2.929687-7.070312-1.859375-1.863281-4.441406-2.929688-7.070313-2.929688-2.640625 0-5.210937 1.066407-7.070312 2.929688-1.871094 1.859375-2.929688 4.4375-2.929688 7.070312 0 2.628907 1.058594 5.207031 2.929688 7.066407 1.859375 1.863281 4.441406 2.933593 7.070312 2.933593zm0 0"/>
                                <path d="m256 314.925781c-2.628906 0-5.210938 1.066407-7.070312 2.929688-1.859376 1.867187-2.929688 4.4375-2.929688 7.070312 0 2.636719 1.070312 5.207031 2.929688 7.078125 1.859374 1.859375 4.441406 2.921875 7.070312 2.921875s5.210938-1.0625 7.070312-2.921875c1.859376-1.871094 2.929688-4.441406 2.929688-7.078125 0-2.632812-1.070312-5.203125-2.929688-7.070312-1.859374-1.863281-4.441406-2.929688-7.070312-2.929688zm0 0"/>
                            </svg>
                        </div>
                        <div part="content" data-monster-replace="path:labels.nothingToReport">
                            There is nothing to report yet.
                        </div>
                    </monster-state>
                </div>
                <div data-monster-role="entries">
                    <ul data-monster-insert="entry path:entries">
                    </ul>
                </div>
            </div>
        `;
    }
    
    registerCustomElement(Log);