/** * 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 { assembleMethodSymbol, CustomElement, registerCustomElement, } from "../../dom/customelement.mjs"; import { findElementWithSelectorUpwards } from "../../dom/util.mjs"; import { ThemeStyleSheet } from "../stylesheet/theme.mjs"; import { Datasource } from "./datasource.mjs"; import { SpinnerStyleSheet } from "../stylesheet/spinner.mjs"; import { isString } from "../../types/is.mjs"; import { instanceSymbol } from "../../constants.mjs"; import "../form/select.mjs"; import "./datasource/dom.mjs"; import "./datasource/rest.mjs"; import "../form/popper.mjs"; import "../form/context-error.mjs"; import { StatusStyleSheet } from "./stylesheet/status.mjs"; export { DatasourceStatus }; /** * @private * @type {symbol} */ const errorElementSymbol = Symbol.for("errorElement"); /** * @private * @type {symbol} */ const datasourceLinkedElementSymbol = Symbol("datasourceLinkedElement"); /** * The Status component is used to show the current status of a datasource. * * <img src="./images/datasource-status.png"> * * Dependencies: the system uses functions of the [monsterjs](https://monsterjs.org/) library * * You can create this control either by specifying the HTML tag <monster-datatable-status />` directly in the HTML or using * Javascript via the `document.createElement('monster-pagination');` method. * * ```html * <monster-pagination></monster-pagination> * ``` * * Or you can create this CustomControl directly in Javascript: * * ```js * import '@schukai/component-datatable/source/pagination.mjs'; * document.createElement('monster-pagination'); * ``` * * @startuml datasource-status.png * skinparam monochrome true * skinparam shadowing false * HTMLElement <|-- CustomElement * CustomElement <|-- Pagination * @enduml * * @copyright schukai GmbH * @memberOf Monster.Components.Datatable * @summary A datatable */ class DatasourceStatus extends CustomElement { /** */ constructor() { super(); } /** * This method is called by the `instanceof` operator. * @returns {symbol} */ static get [instanceSymbol]() { return Symbol.for( "@schukai/monster/components/datatables/status@@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} datasource Datasource configuration * @property {string} datasource.selector The selector of the datasource */ get defaults() { return Object.assign({}, super.defaults, { templates: { main: getTemplate(), }, datasource: { selector: null, }, timeouts: { message: 4000, }, state: { spinner: "hide", }, }); } /** * * @return {string} */ static getTag() { return "monster-datasource-status"; } /** * @private */ [assembleMethodSymbol]() { super[assembleMethodSymbol](); initControlReferences.call(this); initEventHandler.call(this); } /** * * @return [CSSStyleSheet] */ static getCSSStyleSheet() { return [StatusStyleSheet, SpinnerStyleSheet, ThemeStyleSheet]; } } /** * @private * @return {Select} * @throws {Error} no shadow-root is defined */ function initControlReferences() { if (!this.shadowRoot) { throw new Error("no shadow-root is defined"); } this[errorElementSymbol] = this.shadowRoot.querySelector( "monster-context-error", ); } /** * @private */ function initEventHandler() { const selector = this.getOption("datasource.selector", ""); const self = this; if (isString(selector)) { const element = findElementWithSelectorUpwards(this, selector); if (element === null) { throw new Error("the selector must match exactly one element"); } if (!(element instanceof Datasource)) { throw new TypeError("the element must be a datasource"); } this[datasourceLinkedElementSymbol] = element; element.addEventListener("monster-datasource-fetched", function () { self.setOption("state.spinner", "hide"); }); element.addEventListener("monster-datasource-fetch", function () { self.setOption("state.spinner", "show"); }); element.addEventListener("monster-datasource-error", function (event) { self.setOption("state.spinner", "hide"); const timeout = self.getOption("timeouts.message", 4000); let msg = "Cannot load data"; try { if (event.detail.error instanceof Error) { msg = event.detail.error.message; } else if (event.detail.error instanceof Object) { msg = JSON.stringify(event.detail.error); } else if (event.detail.error instanceof String) { msg = event.detail.error; } else if (event.detail.error instanceof Number) { msg = event.detail.error.toString(); } else { msg = event.detail.error; } } catch (e) { } finally { self[errorElementSymbol].setErrorMessage(msg, timeout); } }); } } /** * @private * @return {string} */ function getTemplate() { // language=HTML return ` <div data-monster-role="control" part="control" data-monster-attributes="disabled path:disabled | if:true"> <monster-context-error data-monster-option-classes-button="monster-theme-error-2 monster-theme-background-inherit"></monster-context-error> <div class="monster-theme-control-element" data-monster-attributes="data-monster-state-loader path:state.spinner"></div> </div> `; } registerCustomElement(DatasourceStatus);