diff --git a/nix/config/release.nix b/nix/config/release.nix index d2ece1dc68c1d45bf474c1dc6f8b9772619b34f3..40ead55b6480a305660ebbbc3982e847bb3edc11 100644 --- a/nix/config/release.nix +++ b/nix/config/release.nix @@ -3,4 +3,4 @@ commit = "b88de4847a89d8e55505beabf01e817050c95ea0"; name = "Monster"; mnemonic = "monster"; -} \ No newline at end of file +} diff --git a/nix/scripts/build-stylesheets.nix b/nix/scripts/build-stylesheets.nix index 820476595fc2eac8dd95f8fb5221495247971293..dd5ad944e733eb7079128f9793a48ced6f55d64b 100644 --- a/nix/scripts/build-stylesheets.nix +++ b/nix/scripts/build-stylesheets.nix @@ -22,7 +22,7 @@ in echo_fail "Stylesheet build failed. Exiting." exit 1 fi - + echo_step "Formatting JS code" if ! ${pkgs'.biome}/bin/biome format --write ./source/ then diff --git a/source/components/form/message-state-button.mjs b/source/components/form/message-state-button.mjs index 16862ca19ef5ba6cd25ccc6ffca197584f7375e6..acf0e49b736c0a6854f1ad16fe47a6ca893c2f58 100644 --- a/source/components/form/message-state-button.mjs +++ b/source/components/form/message-state-button.mjs @@ -121,6 +121,9 @@ class MessageStateButton extends Popper { throw new Error("the click action is not defined"); }, }, + features: { + disableButton: false, + }, }); } @@ -208,7 +211,7 @@ class MessageStateButton extends Popper { /** * @param {number} timeout - * @return {Monster.Components.Form.MessageStateButton} + * @return {MessageStateButton} */ showMessage(timeout) { super.showDialog(); @@ -363,7 +366,7 @@ function getTemplate() { <div data-monster-role="control" part="control"> <monster-state-button exportparts="button:button-button,control:button-control" - data-monster-attributes="data-monster-option-classes-button path:classes.button" + data-monster-attributes="data-monster-option-classes-button path:classes.button, disabled path:features.disableButton" part="button" name="button" data-monster-role="button"> diff --git a/source/components/host/config-manager.mjs b/source/components/host/config-manager.mjs index e8cc9f63254d66826f222000eb670a4ae83502ff..622426808ebdf9f42b73c9a8193c8670ba30bccc 100644 --- a/source/components/host/config-manager.mjs +++ b/source/components/host/config-manager.mjs @@ -199,8 +199,6 @@ class ConfigManager extends CustomElement { } /** - * - * @return {Monster.Components.Host.Host} */ [assembleMethodSymbol]() { super[assembleMethodSymbol](); diff --git a/source/components/layout/iframe.mjs b/source/components/layout/iframe.mjs index 96fc67ad37b6524a950a23380c150527d768a413..871609341654c1476a416adc44c4416b577aec86 100644 --- a/source/components/layout/iframe.mjs +++ b/source/components/layout/iframe.mjs @@ -10,20 +10,20 @@ * For more information about purchasing a commercial license, please contact schukai GmbH. */ -import {instanceSymbol} from "../../constants.mjs"; -import {ATTRIBUTE_ROLE} from "../../dom/constants.mjs"; -import {CustomElement} from "../../dom/customelement.mjs"; +import { instanceSymbol } from "../../constants.mjs"; +import { ATTRIBUTE_ROLE } from "../../dom/constants.mjs"; +import { CustomElement } from "../../dom/customelement.mjs"; import { - assembleMethodSymbol, - registerCustomElement, + assembleMethodSymbol, + registerCustomElement, } from "../../dom/customelement.mjs"; -import {findTargetElementFromEvent} from "../../dom/events.mjs"; -import {isFunction} from "../../types/is.mjs"; -import {DeadMansSwitch} from "../../util/deadmansswitch.mjs"; -import {IframeStyleSheet} from "./stylesheet/iframe.mjs"; -import {fireCustomEvent} from "../../dom/events.mjs"; +import { findTargetElementFromEvent } from "../../dom/events.mjs"; +import { isFunction } from "../../types/is.mjs"; +import { DeadMansSwitch } from "../../util/deadmansswitch.mjs"; +import { IframeStyleSheet } from "./stylesheet/iframe.mjs"; +import { fireCustomEvent } from "../../dom/events.mjs"; -export {Iframe}; +export { Iframe }; /** * @private @@ -62,134 +62,134 @@ const timerCallbackSymbol = Symbol("timerCallback"); * @summary A cool and fancy Iframe that can make your life easier and also looks good. */ class Iframe extends CustomElement { - /** - * This method is called by the `instanceof` operator. - * @return {symbol} - */ - static get [instanceSymbol]() { - return Symbol.for("@schukai/monster/components/layout/iframe@@instance"); - } - - /** - * - * @return {Components.Layout.Iframe - * @fires monster-iframe-clicked - */ - [assembleMethodSymbol]() { - super[assembleMethodSymbol](); - initControlReferences.call(this); - initEventHandler.call(this); - calcHeight.call(this); - return this; - } - - /** - * This method is called by the dom and should not be called directly. - * - * @return {void} - */ - connectedCallback() { - super.connectedCallback(); - attachResizeObserver.call(this); - - // disable scrolling in parent node - if (this.parentNode && this.parentNode instanceof HTMLElement) { - this.parentNode.style.overflow = "hidden"; - } - } - - /** - * This method is called by the dom and should not be called directly. - * - * @return {void} - */ - disconnectedCallback() { - super.disconnectedCallback(); - disconnectResizeObserver.call(this); - } - - /** - * 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 Label definitions - * @property {Object} actions Callbacks - * @property {string} actions.click="throw Error" Callback when clicked - * @property {Object} features Features - * @property {boolean} features.allowFullScreen=true Allow fullscreen - * @property {boolean} features.allowPaymentRequest=true Allow payment request - * @property {boolean} features.replaceTargets=true Replace parent target in iframe - * @property {string} loading="eager" Loading state - * @property {string} referrerPolicy="no-referrer" Referrer policy - * @property {string} src Source - * @property {Object} classes CSS classes - * @property {boolean} disabled=false Disabled state - */ - get defaults() { - return Object.assign({}, super.defaults, { - templates: { - main: getTemplate(), - }, - src: null, - - /* "allow-forms allow-pointer-lock allow-popups allow-same-origin allow-scripts allow-top-navigation"*/ - sandbox: null, - - labels: {}, - classes: {}, - - name: "", - - referrerPolicy: "no-referrer", - - disabled: false, - features: { - allowFullScreen: true, - allowPaymentRequest: true, - replaceTargets: true, - }, - - loading: "eager", - - actions: { - click: () => { - throw new Error("the click action is not defined"); - }, - }, - }); - } - - /** - * @return {string} - */ - static getTag() { - return "monster-iframe"; - } - - /** - * @return {CSSStyleSheet[]} - */ - static getCSSStyleSheet() { - return [IframeStyleSheet]; - } + /** + * This method is called by the `instanceof` operator. + * @return {symbol} + */ + static get [instanceSymbol]() { + return Symbol.for("@schukai/monster/components/layout/iframe@@instance"); + } + + /** + * + * @return {Components.Layout.Iframe + * @fires monster-iframe-clicked + */ + [assembleMethodSymbol]() { + super[assembleMethodSymbol](); + initControlReferences.call(this); + initEventHandler.call(this); + calcHeight.call(this); + return this; + } + + /** + * This method is called by the dom and should not be called directly. + * + * @return {void} + */ + connectedCallback() { + super.connectedCallback(); + attachResizeObserver.call(this); + + // disable scrolling in parent node + if (this.parentNode && this.parentNode instanceof HTMLElement) { + this.parentNode.style.overflow = "hidden"; + } + } + + /** + * This method is called by the dom and should not be called directly. + * + * @return {void} + */ + disconnectedCallback() { + super.disconnectedCallback(); + disconnectResizeObserver.call(this); + } + + /** + * 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 Label definitions + * @property {Object} actions Callbacks + * @property {string} actions.click="throw Error" Callback when clicked + * @property {Object} features Features + * @property {boolean} features.allowFullScreen=true Allow fullscreen + * @property {boolean} features.allowPaymentRequest=true Allow payment request + * @property {boolean} features.replaceTargets=true Replace parent target in iframe + * @property {string} loading="eager" Loading state + * @property {string} referrerPolicy="no-referrer" Referrer policy + * @property {string} src Source + * @property {Object} classes CSS classes + * @property {boolean} disabled=false Disabled state + */ + get defaults() { + return Object.assign({}, super.defaults, { + templates: { + main: getTemplate(), + }, + src: null, + + /* "allow-forms allow-pointer-lock allow-popups allow-same-origin allow-scripts allow-top-navigation"*/ + sandbox: null, + + labels: {}, + classes: {}, + + name: "", + + referrerPolicy: "no-referrer", + + disabled: false, + features: { + allowFullScreen: true, + allowPaymentRequest: true, + replaceTargets: true, + }, + + loading: "eager", + + actions: { + click: () => { + throw new Error("the click action is not defined"); + }, + }, + }); + } + + /** + * @return {string} + */ + static getTag() { + return "monster-iframe"; + } + + /** + * @return {CSSStyleSheet[]} + */ + static getCSSStyleSheet() { + return [IframeStyleSheet]; + } } /** * @private */ function calcHeight() { - this.style.boxSizing = "border-box"; + this.style.boxSizing = "border-box"; - const height = calculateMaximumHeight.call(this, this.parentNode); - if (height < 0 || isNaN(height)) { - return; - } + const height = calculateMaximumHeight.call(this, this.parentNode); + if (height < 0 || isNaN(height)) { + return; + } - this[iframeElementSymbol].style.height = `${height}px`; + this[iframeElementSymbol].style.height = `${height}px`; } /** @@ -198,90 +198,90 @@ function calcHeight() { * @return {*} */ function calculateMaximumHeight(element) { - let totalBottomBorder = 0; - let totalBottomPadding = 0; - let totalBottomMargin = 0; - let totalOutlineHeight = 0; - let totalBoxShadowHeight = 0; - let currentElement = element; - - while (currentElement && currentElement !== document.body) { - const style = window.getComputedStyle(currentElement); - const boxSizing = style.boxSizing; - - const elementHeight = currentElement.getBoundingClientRect().height; - - const borderBottomWidth = parseFloat(style.borderBottomWidth); - const paddingBottom = parseFloat(style.paddingBottom); - const marginBottom = parseFloat(style.marginBottom); - - const outlineHeight = parseFloat(style.outlineWidth); - - totalBottomBorder += isNaN(borderBottomWidth) ? 0 : borderBottomWidth; - totalBottomPadding += - isNaN(paddingBottom) || boxSizing === "border-box" ? 0 : paddingBottom; - totalBottomMargin += isNaN(marginBottom) ? 0 : marginBottom; - totalOutlineHeight += isNaN(outlineHeight) ? 0 : outlineHeight; - - const boxShadow = style.boxShadow; - let boxShadowVerticalTotal = 0; - - if (boxShadow !== "none") { - const boxShadowValues = boxShadow.split(" "); - const verticalOffset = parseFloat(boxShadowValues[3]); - const blurRadius = parseFloat(boxShadowValues[4]); - const spreadRadius = parseFloat(boxShadowValues[5]); - - boxShadowVerticalTotal = verticalOffset + blurRadius + spreadRadius; - } - - totalBoxShadowHeight += isNaN(boxShadowVerticalTotal) - ? 0 - : boxShadowVerticalTotal; - - if (elementHeight > 200) { - return ( - elementHeight - - totalBottomBorder - - totalBottomPadding - - totalBottomMargin - - totalOutlineHeight - - totalBoxShadowHeight - ); - } - - currentElement = currentElement.parentNode || currentElement.host; - } + let totalBottomBorder = 0; + let totalBottomPadding = 0; + let totalBottomMargin = 0; + let totalOutlineHeight = 0; + let totalBoxShadowHeight = 0; + let currentElement = element; + + while (currentElement && currentElement !== document.body) { + const style = window.getComputedStyle(currentElement); + const boxSizing = style.boxSizing; + + const elementHeight = currentElement.getBoundingClientRect().height; + + const borderBottomWidth = parseFloat(style.borderBottomWidth); + const paddingBottom = parseFloat(style.paddingBottom); + const marginBottom = parseFloat(style.marginBottom); + + const outlineHeight = parseFloat(style.outlineWidth); + + totalBottomBorder += isNaN(borderBottomWidth) ? 0 : borderBottomWidth; + totalBottomPadding += + isNaN(paddingBottom) || boxSizing === "border-box" ? 0 : paddingBottom; + totalBottomMargin += isNaN(marginBottom) ? 0 : marginBottom; + totalOutlineHeight += isNaN(outlineHeight) ? 0 : outlineHeight; + + const boxShadow = style.boxShadow; + let boxShadowVerticalTotal = 0; + + if (boxShadow !== "none") { + const boxShadowValues = boxShadow.split(" "); + const verticalOffset = parseFloat(boxShadowValues[3]); + const blurRadius = parseFloat(boxShadowValues[4]); + const spreadRadius = parseFloat(boxShadowValues[5]); + + boxShadowVerticalTotal = verticalOffset + blurRadius + spreadRadius; + } + + totalBoxShadowHeight += isNaN(boxShadowVerticalTotal) + ? 0 + : boxShadowVerticalTotal; + + if (elementHeight > 200) { + return ( + elementHeight - + totalBottomBorder - + totalBottomPadding - + totalBottomMargin - + totalOutlineHeight - + totalBoxShadowHeight + ); + } + + currentElement = currentElement.parentNode || currentElement.host; + } } /** * @private */ function attachResizeObserver() { - // against flickering - this[resizeObserverSymbol] = new ResizeObserver(() => { - if (this[timerCallbackSymbol] instanceof DeadMansSwitch) { - try { - this[timerCallbackSymbol].touch(); - return; - } catch (e) { - delete this[timerCallbackSymbol]; - } - } - - this[timerCallbackSymbol] = new DeadMansSwitch(200, () => { - calcHeight.call(this); - }); - }); - - this[resizeObserverSymbol].observe(this.ownerDocument.body); - this[resizeObserverSymbol].observe(document.scrollingElement); + // against flickering + this[resizeObserverSymbol] = new ResizeObserver(() => { + if (this[timerCallbackSymbol] instanceof DeadMansSwitch) { + try { + this[timerCallbackSymbol].touch(); + return; + } catch (e) { + delete this[timerCallbackSymbol]; + } + } + + this[timerCallbackSymbol] = new DeadMansSwitch(200, () => { + calcHeight.call(this); + }); + }); + + this[resizeObserverSymbol].observe(this.ownerDocument.body); + this[resizeObserverSymbol].observe(document.scrollingElement); } function disconnectResizeObserver() { - if (this[resizeObserverSymbol] instanceof ResizeObserver) { - this[resizeObserverSymbol].disconnect(); - } + if (this[resizeObserverSymbol] instanceof ResizeObserver) { + this[resizeObserverSymbol].disconnect(); + } } /** @@ -289,46 +289,48 @@ function disconnectResizeObserver() { * @return {initEventHandler} */ function initEventHandler() { - const self = this; - const element = this[iframeElementSymbol]; - - const type = "click"; - - element.addEventListener(type, function (event) { - const callback = self.getOption("actions.click"); - - fireCustomEvent(self, "monster-iframe-clicked", { - element: self, - }); - - if (!isFunction(callback)) { - return; - } - - const element = findTargetElementFromEvent( - event, - ATTRIBUTE_ROLE, - "control", - ); - - if (!(element instanceof Node && self.hasNode(element))) { - return; - } - - callback.call(self, event); - }); - - this[iframeElementSymbol].addEventListener("load", () => { - calcHeight.call(this); - if (this.getOption("features.replaceTargets")) { - const links = this[iframeElementSymbol].contentDocument.querySelectorAll('a[target="_parent"], form[target="_parent"], a[target="_top"], form[target="_top"]'); - links.forEach(function(link) { - link.target = '_self'; - }); - } - }); - - return this; + const self = this; + const element = this[iframeElementSymbol]; + + const type = "click"; + + element.addEventListener(type, function (event) { + const callback = self.getOption("actions.click"); + + fireCustomEvent(self, "monster-iframe-clicked", { + element: self, + }); + + if (!isFunction(callback)) { + return; + } + + const element = findTargetElementFromEvent( + event, + ATTRIBUTE_ROLE, + "control", + ); + + if (!(element instanceof Node && self.hasNode(element))) { + return; + } + + callback.call(self, event); + }); + + this[iframeElementSymbol].addEventListener("load", () => { + calcHeight.call(this); + if (this.getOption("features.replaceTargets")) { + const links = this[iframeElementSymbol].contentDocument.querySelectorAll( + 'a[target="_parent"], form[target="_parent"], a[target="_top"], form[target="_top"]', + ); + links.forEach(function (link) { + link.target = "_self"; + }); + } + }); + + return this; } /** @@ -336,17 +338,17 @@ function initEventHandler() { * @return {void} */ function initControlReferences() { - if (!this.shadowRoot) { - throw new Error("no shadow-root is defined"); - } + if (!this.shadowRoot) { + throw new Error("no shadow-root is defined"); + } - this[PanelElementSymbol] = this.shadowRoot.querySelector( - "[data-monster-role=control]", - ); + this[PanelElementSymbol] = this.shadowRoot.querySelector( + "[data-monster-role=control]", + ); - this[iframeElementSymbol] = this.shadowRoot.querySelector( - `[${ATTRIBUTE_ROLE}="control"] iframe`, - ); + this[iframeElementSymbol] = this.shadowRoot.querySelector( + `[${ATTRIBUTE_ROLE}="control"] iframe`, + ); } /** @@ -354,8 +356,8 @@ function initControlReferences() { * @return {string} */ function getTemplate() { - // language=HTML - return ` + // language=HTML + return ` <div data-monster-role="control" part="control"> <iframe data-monster-role="iframe" data-monster-attributes="sandbox path:sandbox, diff --git a/source/monster.mjs b/source/monster.mjs index e635d6c06e5882395054dc7e74f8338e0f88f853..a8d1f399d10fc5e1f2a1cf276298cc27f4602c34 100644 --- a/source/monster.mjs +++ b/source/monster.mjs @@ -205,5 +205,6 @@ export { Monster }; * @license AGPLv3 or commercial license * @since 2.0.0 * @copyright schukai GmbH + * @memberOf Monster */ class Monster {}