@schukai/monster
Version:
Monster is a simple library for creating fast, robust and lightweight websites.
258 lines (225 loc) • 6.77 kB
JavaScript
/**
* 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";
import { Formatter } from "../../text/formatter.mjs";
export { DatasourceStatus };
/**
* @private
* @type {symbol}
*/
const errorElementSymbol = Symbol.for("errorElement");
/**
* @private
* @type {symbol}
*/
const datasourceLinkedElementSymbol = Symbol("datasourceLinkedElement");
/**
* A simple dataset status component
*
* @fragments /fragments/components/datatable/datasource-status
*
* @example /examples/components/datatable/datasource-status-simple Simple dataset status
*
* @issue https://localhost.alvine.dev:8440/development/issues/closed/274.html
*
* @copyright schukai GmbH
* @summary The Status component is used to show the current status of a datasource.
*/
class DatasourceStatus extends CustomElement {
/**
*/
constructor() {
super();
}
/**
* This method is called by the `instanceof` operator.
* @return {symbol}
*/
static get [instanceSymbol]() {
return Symbol.for("@schukai/monster/components/datatable/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
* @property {Object} callbacks Callbacks
* @property {Function} callbacks.onError Callback function for error handling <code>function(message: string, event: Event): string</code>
* @property {Object} timeouts Timeouts
* @property {number} timeouts.message Timeout for the message
* @property {Object} state State
*/
get defaults() {
return Object.assign({}, super.defaults, {
templates: {
main: getTemplate(),
},
datasource: {
selector: null,
},
callbacks: {
onError: 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);
}
/**
*
* @param message
* @param timeout
* @returns {DatasourceStatus}
*/
setErrorMessage(message, timeout) {
this[errorElementSymbol].setErrorMessage(message, timeout);
return 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");
}
let fadeOutTimer = null;
this[datasourceLinkedElementSymbol] = element;
element.addEventListener("monster-datasource-fetched", function () {
fadeOutTimer = setTimeout(() => {
self.setOption("state.spinner", "hide");
}, 800);
});
element.addEventListener("monster-datasource-fetch", function () {
if (fadeOutTimer) {
clearTimeout(fadeOutTimer);
fadeOutTimer = null;
}
self.setOption("state.spinner", "show");
});
element.addEventListener("monster-datasource-error", function (event) {
if (fadeOutTimer) {
clearTimeout(fadeOutTimer);
fadeOutTimer = null;
}
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 {
const callback = self.getOption("callbacks.onError", null);
if (callback) {
callback.call(self, msg, event);
} else {
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-spinner"
data-monster-attributes="data-monster-state-loader path:state.spinner"></div>
</div>
`;
}
registerCustomElement(DatasourceStatus);