From cc2faa159d11db462dafb4df6335d31f203fb884 Mon Sep 17 00:00:00 2001
From: Volker Schukai <volker.schukai@schukai.com>
Date: Thu, 3 Oct 2024 19:17:22 +0200
Subject: [PATCH] feat: new copy control #246

---
 development/issues/closed/246.html            |  25 +
 development/issues/closed/246.mjs             |  15 +
 development/scripts/createNewClass.mjs        |   7 +-
 nix/scripts/go-task.nix                       |   2 +-
 source/components/content/copy.mjs            | 475 ++++++++++++++++++
 source/components/content/style/copy.pcss     |  33 ++
 source/components/content/stylesheet/copy.mjs |  31 ++
 source/components/form/context-help.mjs       |  36 --
 8 files changed, 584 insertions(+), 40 deletions(-)
 create mode 100644 development/issues/closed/246.html
 create mode 100644 development/issues/closed/246.mjs
 create mode 100644 source/components/content/copy.mjs
 create mode 100644 source/components/content/style/copy.pcss
 create mode 100644 source/components/content/stylesheet/copy.mjs

diff --git a/development/issues/closed/246.html b/development/issues/closed/246.html
new file mode 100644
index 000000000..b07c18822
--- /dev/null
+++ b/development/issues/closed/246.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>new copy control #246</title>
+    <script src="./246.mjs" type="module"></script>
+</head>
+<body>
+<h1>new copy control #246</h1>
+<p>a new copy control for clipboard</p>
+<ul>
+    <li><a href="https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/246">Issue #246</a></li>
+    <li><a href="/">Back to overview</a></li>
+</ul>
+<main>
+
+    <monster-copy>
+        <div id="c1">DAS IST!!! EIN TEST</div>
+        <div id="c2">DAS IST <b>!EIN!</b> TEST</div>
+    </monster-copy>
+
+</main>
+</body>
+</html>
diff --git a/development/issues/closed/246.mjs b/development/issues/closed/246.mjs
new file mode 100644
index 000000000..6a37e74cf
--- /dev/null
+++ b/development/issues/closed/246.mjs
@@ -0,0 +1,15 @@
+/**
+* @file development/issues/open/246.mjs
+* @url https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/246
+* @description new copy control
+* @issue 246
+*/
+
+import "../../../source/components/style/property.pcss";
+import "../../../source/components/style/link.pcss";
+import "../../../source/components/style/color.pcss";
+import "../../../source/components/style/theme.pcss";
+import "../../../source/components/style/normalize.pcss";
+import "../../../source/components/style/typography.pcss";
+import "../../../source/components/content/copy.mjs";
+
diff --git a/development/scripts/createNewClass.mjs b/development/scripts/createNewClass.mjs
index 14a3a2b29..8d3e1a323 100644
--- a/development/scripts/createNewClass.mjs
+++ b/development/scripts/createNewClass.mjs
@@ -48,11 +48,12 @@ export const {{CLASSNAME_LOWER_FIRST}}ElementSymbol = Symbol("{{CLASSNAME_LOWER_
 /**
  * A {{CLASSNAME}}
  * 
- * @fragments /fragments/components/form/{{HTML_TAG_SUFFIX}}/
+ * @fragments /fragments/components/{{NAMESPACE_AS_PATH}}/{{HTML_TAG_SUFFIX}}/
  * 
- * @example /examples/components/form/{{HTML_TAG_SUFFIX}}-simple
+ * @example /examples/components/{{NAMESPACE_AS_PATH}}/{{HTML_TAG_SUFFIX}}-simple
  * 
  * @since {{VERSION}}
+ * @since {{VERSION}}
  * @copyright schukai GmbH
  * @summary A beautiful {{CLASSNAME}} that can make your life easier and also looks good.
  */
@@ -77,7 +78,7 @@ export const {{CLASSNAME_LOWER_FIRST}}ElementSymbol = Symbol("{{CLASSNAME_LOWER_
 	}
 
 	/**
-	 * To set the options via the html tag the attribute \`data-monster-options\` must be used.
+	 * 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.
diff --git a/nix/scripts/go-task.nix b/nix/scripts/go-task.nix
index e43299f10..7ed623583 100644
--- a/nix/scripts/go-task.nix
+++ b/nix/scripts/go-task.nix
@@ -159,7 +159,7 @@
             silent: true
             aliases:
               - r
-
+              - start
 
           tool-mockoon:
             desc: Run example
diff --git a/source/components/content/copy.mjs b/source/components/content/copy.mjs
new file mode 100644
index 000000000..d4916d647
--- /dev/null
+++ b/source/components/content/copy.mjs
@@ -0,0 +1,475 @@
+/**
+ * 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.
+ */
+
+import {instanceSymbol} from "../../constants.mjs";
+import {addAttributeToken, removeAttributeToken} from "../../dom/attributes.mjs";
+import {
+    ATTRIBUTE_ERRORMESSAGE,
+    ATTRIBUTE_ROLE,
+} from "../../dom/constants.mjs";
+import {CustomElement, getSlottedElements} from "../../dom/customelement.mjs";
+import {
+    assembleMethodSymbol,
+    registerCustomElement,
+} from "../../dom/customelement.mjs";
+import {getDocument} from "../../dom/util.mjs";
+import {STYLE_DISPLAY_MODE_BLOCK} from "../form/constants.mjs";
+import {CopyStyleSheet} from "./stylesheet/copy.mjs";
+import {fireCustomEvent} from "../../dom/events.mjs";
+import { positionPopper } from "../form/util/floating-ui.mjs";
+import { DeadMansSwitch } from "../../util/deadmansswitch.mjs";
+
+export {Copy};
+
+/**
+ * @private
+ * @type {symbol}
+ */
+const timerCallbackSymbol = Symbol("timerCallback");
+
+
+/**
+ * @private
+ * @type {symbol}
+ */
+export const controlElementSymbol = Symbol("copyElement");
+
+/**
+ * @private
+ * @type {symbol}
+ */
+export const popperElementSymbol = Symbol("popperElement");
+
+/**
+ * @private
+ * @type {symbol}
+ */
+export const copyButtonElementSymbol = Symbol("copyButtonElement");
+
+/**
+ * local symbol
+ * @private
+ * @type {symbol}
+ */
+const closeEventHandler = Symbol("closeEventHandler");
+
+/**
+ * local symbol
+ * @private
+ * @type {symbol}
+ */
+const resizeObserverSymbol = Symbol("resizeObserver");
+
+/**
+ * A Copy
+ *
+ * @fragments /fragments/components/content/copy/
+ *
+ * @example /examples/components/content/copy-simple
+ *
+ * @since 3.77.0
+ * @copyright schukai GmbH
+ * @summary A beautiful Copy that can make your life easier and also looks good.
+ */
+class Copy extends CustomElement {
+    /**
+     * This method is called by the `instanceof` operator.
+     * @returns {symbol}
+     */
+    static get [instanceSymbol]() {
+        return Symbol.for("@schukai/monster/components/content/copy@@instance");
+    }
+
+    /**
+     *
+     * @return {Components.Content.Copy
+     */
+    [assembleMethodSymbol]() {
+        super[assembleMethodSymbol]();
+        initControlReferences.call(this);
+        initEventHandler.call(this);
+        return this;
+    }
+
+
+    /**
+     * This method is called when the element is connected to the dom.
+     *
+     * @return {void}
+     */
+    connectedCallback() {
+        super.connectedCallback();
+
+        const document = getDocument();
+
+        for (const [, type] of Object.entries(["click", "touch"])) {
+            // close on outside ui-events
+            document.addEventListener(type, this[closeEventHandler]);
+        }
+
+        updatePopper.call(this);
+        attachResizeObserver.call(this);
+    }
+
+
+    /**
+     * This method is called when the element is disconnected from the dom.
+     *
+     * @return {void}
+     */
+    disconnectedCallback() {
+        super.disconnectedCallback();
+
+        // close on outside ui-events
+        for (const [, type] of Object.entries(["click", "touch"])) {
+            document.removeEventListener(type, this[closeEventHandler]);
+        }
+
+        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} actions Callbacks
+     * @property {string} actions.click="throw Error" Callback when clicked
+     * @property {Object} features Features
+     * @property {boolean} features.stripTags=true Strip tags from the copied text
+     * @property {boolean} features.preventOpenEventSent=false Prevent open event from being sent
+     * @property {Object} popper Popper configuration
+     * @property {string} popper.placement="top" Popper placement
+     * @property {string[]} popper.middleware=["autoPlacement", "shift", "offset:15", "arrow"] Popper middleware
+     * @property {boolean} disabled=false Disabled state
+     */
+    get defaults() {
+        return Object.assign({}, super.defaults, {
+            templates: {
+                main: getTemplate(),
+            },
+            disabled: false,
+            features: {
+                stripTags: true,
+                preventOpenEventSent: false,
+            },
+            popper: {
+                placement: "top",
+                middleware: ["autoPlacement", "shift", "offset:15", "arrow"],
+            },            
+        });
+    }
+
+    /**
+     * @return {string}
+     */
+    static getTag() {
+        return "monster-copy";
+    }
+
+    /**
+     * @return {CSSStyleSheet[]}
+     */
+    static getCSSStyleSheet() {
+        return [CopyStyleSheet];
+    }
+
+    /**
+     * With this method, you can show the popper.
+     *
+     * @return {Copy}
+     */
+    showDialog() {
+        show.call(this);
+        return this;
+    }
+
+    /**
+     * With this method, you can hide the popper.
+     *
+     * @return {Copy}
+     */
+    hideDialog() {
+        hide.call(this);
+        return this;
+    }
+
+    /**
+     * With this method, you can toggle the popper.
+     *
+     * @return {Copy}
+     */
+    toggleDialog() {
+        if (this[popperElementSymbol].style.display === STYLE_DISPLAY_MODE_BLOCK) {
+            this.hideDialog();
+        } else {
+            this.showDialog();
+        }
+        return this;
+    }    
+
+
+}
+
+/**
+ * @private
+ */
+function attachResizeObserver() {
+    // against flickering
+    this[resizeObserverSymbol] = new ResizeObserver((entries) => {
+        if (this[timerCallbackSymbol] instanceof DeadMansSwitch) {
+            try {
+                this[timerCallbackSymbol].touch();
+                return;
+            } catch (e) {
+                delete this[timerCallbackSymbol];
+            }
+        }
+
+        this[timerCallbackSymbol] = new DeadMansSwitch(200, () => {
+            updatePopper.call(this);
+        });
+    });
+
+    this[resizeObserverSymbol].observe(this.parentElement);
+}
+
+function disconnectResizeObserver() {
+    if (this[resizeObserverSymbol] instanceof ResizeObserver) {
+        this[resizeObserverSymbol].disconnect();
+    }
+}
+
+/**
+ * @private
+ */
+function hide() {
+    const self = this;
+
+    fireCustomEvent(self, "monster-popper-hide", {
+        self,
+    });
+
+    self[popperElementSymbol].style.display = "none";
+    removeAttributeToken(self[controlElementSymbol], "class", "open");
+
+    setTimeout(() => {
+        fireCustomEvent(self, "monster-popper-hidden", {
+            self,
+        });
+    }, 0);
+}
+
+/**
+ * @private
+ */
+function show() {
+    const self = this;
+
+    if (self.getOption("disabled", false) === true) {
+        return;
+    }
+
+    if (self[popperElementSymbol].style.display === STYLE_DISPLAY_MODE_BLOCK) {
+        return;
+    }
+
+    fireCustomEvent(self, "monster-popper-open", {
+        self,
+    });
+
+    self[popperElementSymbol].style.visibility = "hidden";
+    self[popperElementSymbol].style.display = STYLE_DISPLAY_MODE_BLOCK;
+
+    addAttributeToken(self[controlElementSymbol], "class", "open");
+    updatePopper.call(self);
+
+    setTimeout(() => {
+        fireCustomEvent(self, "monster-popper-opened", {
+            self,
+        });
+    }, 0);
+}
+
+/**
+ * @private
+ */
+function updatePopper() {
+    if (this[popperElementSymbol].style.display !== STYLE_DISPLAY_MODE_BLOCK) {
+        return;
+    }
+
+    if (this.getOption("disabled", false) === true) {
+        return;
+    }
+
+    positionPopper.call(
+        this,
+        this[controlElementSymbol],
+        this[popperElementSymbol],
+        this.getOption("popper", {}),
+    );
+}
+
+/**
+ * @private
+ * @return {initEventHandler}
+ * @fires monster-copy-clicked
+ * @fires monster-copy-success
+ * @fires monster-copy-error
+ */
+function initEventHandler() {
+    const self = this;
+
+    this[closeEventHandler] = (event) => {
+        const path = event.composedPath();
+
+        for (const [, element] of Object.entries(path)) {
+            if (element === this) {
+                return;
+            }
+        }
+        hide.call(this);
+    };    
+
+    const type = "click";
+
+    this[controlElementSymbol].addEventListener("mouseenter", (event) => {
+        if (this.getOption("features.preventOpenEventSent") === true) {
+            event.preventDefault();
+        }
+        this.showDialog();
+    });
+
+    this[controlElementSymbol].addEventListener("focus", (event) => {
+        if (this.getOption("features.preventOpenEventSent") === true) {
+            event.preventDefault();
+        }
+        this.showDialog();
+    });
+    this[controlElementSymbol].addEventListener("blur", (event) => {
+        if (this.getOption("features.preventOpenEventSent") === true) {
+            event.preventDefault();
+        }
+        this.hideDialog();
+    });
+    
+    this[copyButtonElementSymbol].addEventListener(type, function (event) {
+
+        fireCustomEvent(self, "monster-copy-clicked", {
+            element: self,
+        });
+
+        const nodes = getSlottedElements.call(self, ":scope");
+
+        let text = "";
+        for (const node of nodes) {
+            if (self.getOption("features.stripTags")) {
+                text += node.textContent;
+            } else {
+                text += node.outerHTML;
+            }
+        }
+
+        navigator.clipboard.writeText(text).then(function () {
+
+            self[copyButtonElementSymbol].querySelector("use").setAttribute("href", "#copy-success");
+            setTimeout(() => {
+                self[copyButtonElementSymbol].querySelector("use").setAttribute("href", "#copy");
+            }, 2000);
+
+            fireCustomEvent(self, "monster-copy-success", {
+                element: self,
+            });
+
+        }).catch(function (e) {
+
+            self[copyButtonElementSymbol].querySelector("use").setAttribute("href", "#copy-error");
+            setTimeout(() => {
+                self[copyButtonElementSymbol].querySelector("use").setAttribute("href", "#copy");
+            }, 2000);
+            
+            fireCustomEvent(self, "monster-copy-error", {
+                element: self,
+            });
+            
+            addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, "" + e);
+        })
+    });
+
+    return this;
+}
+
+
+/**
+ * @private
+ * @return {void}
+ */
+function initControlReferences() {
+    this[controlElementSymbol] = this.shadowRoot.querySelector(
+        `[${ATTRIBUTE_ROLE}="control"]`,
+    );
+
+    this[copyButtonElementSymbol] = this.shadowRoot.querySelector(
+        `[${ATTRIBUTE_ROLE}="button"]`,
+    );
+    
+    this[popperElementSymbol] = this.shadowRoot.querySelector(
+        `[${ATTRIBUTE_ROLE}="popper"]`,
+    );
+
+}
+
+/**
+ * @private
+ * @return {string}
+ */
+function getTemplate() {
+    // language=HTML
+    return `
+        <div data-monster-role="control" part="control">
+            <slot></slot>
+            <div data-monster-role="popper" part="popper" tabindex="-1" class="monster-color-primary-1">
+                <div data-monster-role="arrow"></div>
+                <button data-monster-role="button" part="button">
+                    <svg data-monster-role="icon-map"><defs>
+                            <g id="copy">
+                                <path d="M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1h1a1 1 0 0 1 1 1V14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1h1z"/>
+                                <path d="M9.5 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5zm-3-1A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0z"/>
+                            </g>
+
+                            <g id="copy-success">
+                                <path fill-rule="evenodd"
+                                      d="M10.854 7.146a.5.5 0 0 1 0 .708l-3 3a.5.5 0 0 1-.708 0l-1.5-1.5a.5.5 0 1 1 .708-.708L7.5 9.793l2.646-2.647a.5.5 0 0 1 .708 0"/>
+                                <path d="M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1h1a1 1 0 0 1 1 1V14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1h1z"/>
+                                <path d="M9.5 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5zm-3-1A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0z"/>
+                            </g>
+
+                            <g id="copy-error">
+                                <path fill-rule="evenodd"
+                                      d="M6.146 7.146a.5.5 0 0 1 .708 0L8 8.293l1.146-1.147a.5.5 0 1 1 .708.708L8.707 9l1.147 1.146a.5.5 0 0 1-.708.708L8 9.707l-1.146 1.147a.5.5 0 0 1-.708-.708L7.293 9 6.146 7.854a.5.5 0 0 1 0-.708"/>
+                                <path d="M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1h1a1 1 0 0 1 1 1V14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1h1z"/>
+                                <path d="M9.5 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5zm-3-1A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0z"/>
+                            </g>
+
+                        </defs>
+                    </svg><svg data-monster-role="icon" xmlns="http://www.w3.org/2000/svg" width="25" height="25" viewBox="0 0 16 16"><use href="#copy"></use></svg>
+                </button>
+            </div>
+        </div>`;
+}
+
+
+registerCustomElement(Copy);
diff --git a/source/components/content/style/copy.pcss b/source/components/content/style/copy.pcss
new file mode 100644
index 000000000..5a6942164
--- /dev/null
+++ b/source/components/content/style/copy.pcss
@@ -0,0 +1,33 @@
+@import "../../style/control.pcss";
+@import "../../style/property.pcss";
+@import "../../style/floating-ui.pcss";
+
+:host {
+    display: inline-block;
+}
+
+[data-monster-role=control] {
+    display: inherit;
+}
+
+[data-monster-role="icon"] {
+    fill: var(--monster-color-primary-1);
+}
+
+[data-monster-role="icon-map"] {
+    display: none;
+}
+
+button {
+    border: none;
+    background: inherit;
+}
+
+button:hover {
+    cursor: copy;
+    
+}
+
+button:active {
+    transform: scale(0.95);
+}
diff --git a/source/components/content/stylesheet/copy.mjs b/source/components/content/stylesheet/copy.mjs
new file mode 100644
index 000000000..07856fcda
--- /dev/null
+++ b/source/components/content/stylesheet/copy.mjs
@@ -0,0 +1,31 @@
+/**
+ * Copyright © schukai GmbH and all contributing authors, 2024. 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.
+ */
+
+import {addAttributeToken} from "../../../dom/attributes.mjs";
+import {ATTRIBUTE_ERRORMESSAGE} from "../../../dom/constants.mjs";
+
+export {CopyStyleSheet}
+
+/**
+ * @private
+ * @type {CSSStyleSheet}
+ */
+const CopyStyleSheet = new CSSStyleSheet();
+
+try {
+  CopyStyleSheet.insertRule(`
+@layer copy { 
+[data-monster-role=control]{box-sizing:border-box;outline:none;width:100%}[data-monster-role=control].flex{align-items:center;display:flex;flex-direction:row}:host{box-sizing:border-box;display:block}:after,:before,:root{--monster-font-family:-apple-system,BlinkMacSystemFont,\"Quicksand\",\"Segoe UI\",\"Roboto\",\"Oxygen\",\"Ubuntu\",\"Cantarell\",\"Fira Sans\",\"Droid Sans\",\"Helvetica Neue\",Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\",\"Segoe UI Symbol\";--monster-color-primary-1:var(--monster-color-gray-6);--monster-color-primary-2:var(--monster-color-gray-6);--monster-color-primary-3:var(--monster-color-cinnamon-1);--monster-color-primary-4:var(--monster-color-cinnamon-1);--monster-bg-color-primary-1:var(--monster-color-gray-1);--monster-bg-color-primary-2:var(--monster-color-gray-2);--monster-bg-color-primary-3:var(--monster-color-gray-6);--monster-bg-color-primary-4:var(--monster-color-gray-4)}@media (prefers-color-scheme:dark){:after,:before,:root{--monster-color-primary-1:var(--monster-color-gray-1);--monster-color-primary-2:var(--monster-color-gray-1);--monster-color-primary-3:var(--monster-color-gray-6);--monster-color-primary-4:var(--monster-color-gray-6);--monster-bg-color-primary-1:var(--monster-color-gray-6);--monster-bg-color-primary-2:var(--monster-color-gray-3);--monster-bg-color-primary-3:var(--monster-color-gray-2);--monster-bg-color-primary-4:var(--monster-color-gray-1)}}:after,:before,:root{--monster-color-secondary-1:var(--monster-color-red-4);--monster-color-secondary-2:var(--monster-color-red-4);--monster-color-secondary-3:var(--monster-color-red-1);--monster-color-secondary-4:var(--monster-color-red-1);--monster-bg-color-secondary-1:var(--monster-color-gray-1);--monster-bg-color-secondary-2:var(--monster-color-red-2);--monster-bg-color-secondary-3:var(--monster-color-red-3);--monster-bg-color-secondary-4:var(--monster-color-red-6)}@media (prefers-color-scheme:dark){:after,:before,:root{--monster-color-secondary-1:var(--monster-color-red-1);--monster-color-secondary-2:var(--monster-color-red-1);--monster-color-secondary-3:var(--monster-color-red-6);--monster-color-secondary-4:var(--monster-color-red-4);--monster-bg-color-secondary-1:var(--monster-color-gray-6);--monster-bg-color-secondary-2:var(--monster-color-red-3);--monster-bg-color-secondary-3:var(--monster-color-red-2);--monster-bg-color-secondary-4:var(--monster-color-red-1)}}:after,:before,:root{--monster-color-tertiary-1:var(--monster-color-magenta-4);--monster-color-tertiary-2:var(--monster-color-magenta-4);--monster-color-tertiary-3:var(--monster-color-magenta-6);--monster-color-tertiary-4:var(--monster-color-magenta-1);--monster-bg-color-tertiary-1:var(--monster-color-gray-1);--monster-bg-color-tertiary-2:var(--monster-color-magenta-1);--monster-bg-color-tertiary-3:var(--monster-color-magenta-2);--monster-bg-color-tertiary-4:var(--monster-color-magenta-6)}@media (prefers-color-scheme:dark){:after,:before,:root{--monster-color-tertiary-1:var(--monster-color-magenta-1);--monster-color-tertiary-2:var(--monster-color-magenta-6);--monster-color-tertiary-3:var(--monster-color-magenta-4);--monster-color-tertiary-4:var(--monster-color-magenta-4);--monster-bg-color-tertiary-1:var(--monster-color-gray-6);--monster-bg-color-tertiary-2:var(--monster-color-magenta-2);--monster-bg-color-tertiary-3:var(--monster-color-magenta-1);--monster-bg-color-tertiary-4:var(--monster-color-magenta-1)}}:after,:before,:root{--monster-color-destructive-1:var(--monster-color-red-1);--monster-color-destructive-2:var(--monster-color-red-4);--monster-color-destructive-3:var(--monster-color-red-6);--monster-color-destructive-4:var(--monster-color-red-1);--monster-bg-color-destructive-1:var(--monster-color-red-4);--monster-bg-color-destructive-2:var(--monster-color-gray-1);--monster-bg-color-destructive-3:var(--monster-color-red-2);--monster-bg-color-destructive-4:var(--monster-color-red-5)}@media (prefers-color-scheme:dark){:after,:before,:root{--monster-color-destructive-1:var(--monster-color-red-1);--monster-color-destructive-2:var(--monster-color-red-3);--monster-color-destructive-3:var(--monster-color-red-4);--monster-color-destructive-4:var(--monster-color-red-1);--monster-bg-color-destructive-1:var(--monster-color-red-5);--monster-bg-color-destructive-2:var(--monster-color-gray-6);--monster-bg-color-destructive-3:var(--monster-color-red-1);--monster-bg-color-destructive-4:var(--monster-color-red-4)}}:after,:before,:root{--monster-color-success-1:var(--monster-color-green-1);--monster-color-success-2:var(--monster-color-green-4);--monster-color-success-3:var(--monster-color-green-6);--monster-color-success-4:var(--monster-color-green-1);--monster-bg-color-success-1:var(--monster-color-green-3);--monster-bg-color-success-2:var(--monster-color-gray-1);--monster-bg-color-success-3:var(--monster-color-green-2);--monster-bg-color-success-4:var(--monster-color-green-5)}@media (prefers-color-scheme:dark){:after,:before,:root{--monster-color-success-1:var(--monster-color-green-1);--monster-color-success-2:var(--monster-color-green-2);--monster-color-success-3:var(--monster-color-green-4);--monster-color-success-4:var(--monster-color-green-1);--monster-bg-color-success-1:var(--monster-color-green-5);--monster-bg-color-success-2:var(--monster-color-gray-6);--monster-bg-color-success-3:var(--monster-color-green-1);--monster-bg-color-success-4:var(--monster-color-green-3)}}:after,:before,:root{--monster-color-warning-1:var(--monster-color-orange-1);--monster-color-warning-2:var(--monster-color-orange-4);--monster-color-warning-3:var(--monster-color-orange-6);--monster-color-warning-4:var(--monster-color-orange-1);--monster-bg-color-warning-1:var(--monster-color-orange-3);--monster-bg-color-warning-2:var(--monster-color-gray-1);--monster-bg-color-warning-3:var(--monster-color-orange-2);--monster-bg-color-warning-4:var(--monster-color-orange-5)}@media (prefers-color-scheme:dark){:after,:before,:root{--monster-color-warning-1:var(--monster-color-orange-1);--monster-color-warning-2:var(--monster-color-orange-3);--monster-color-warning-3:var(--monster-color-orange-4);--monster-color-warning-4:var(--monster-color-orange-1);--monster-bg-color-warning-1:var(--monster-color-orange-5);--monster-bg-color-warning-2:var(--monster-color-gray-6);--monster-bg-color-warning-3:var(--monster-color-orange-1);--monster-bg-color-warning-4:var(--monster-color-orange-3)}}:after,:before,:root{--monster-color-error-1:var(--monster-color-red-1);--monster-color-error-2:var(--monster-color-red-4);--monster-color-error-3:var(--monster-color-red-6);--monster-color-error-4:var(--monster-color-red-1);--monster-bg-color-error-1:var(--monster-color-red-4);--monster-bg-color-error-2:var(--monster-color-gray-1);--monster-bg-color-error-3:var(--monster-color-red-2);--monster-bg-color-error-4:var(--monster-color-red-5)}@media (prefers-color-scheme:dark){:after,:before,:root{--monster-color-error-1:var(--monster-color-red-1);--monster-color-error-2:var(--monster-color-red-3);--monster-color-error-3:var(--monster-color-red-4);--monster-color-error-4:var(--monster-color-red-1);--monster-bg-color-error-1:var(--monster-color-red-5);--monster-bg-color-error-2:var(--monster-color-gray-6);--monster-bg-color-error-3:var(--monster-color-red-1);--monster-bg-color-error-4:var(--monster-color-red-4)}}:after,:before,:root{--monster-color-selection-1:var(--monster-color-gray-6);--monster-color-selection-2:var(--monster-color-gray-6);--monster-color-selection-3:var(--monster-color-gray-6);--monster-color-selection-4:var(--monster-color-gray-1);--monster-bg-color-selection-1:var(--monster-color-yellow-2);--monster-bg-color-selection-2:var(--monster-color-yellow-1);--monster-bg-color-selection-3:var(--monster-color-yellow-2);--monster-bg-color-selection-4:var(--monster-color-yellow-6)}@media (prefers-color-scheme:dark){:after,:before,:root{--monster-color-selection-1:var(--monster-color-gray-6);--monster-color-selection-2:var(--monster-color-gray-6);--monster-color-selection-3:var(--monster-color-gray-6);--monster-color-selection-4:var(--monster-color-gray-1);--monster-bg-color-selection-1:var(--monster-color-yellow-2);--monster-bg-color-selection-2:var(--monster-color-yellow-1);--monster-bg-color-selection-3:var(--monster-color-yellow-2);--monster-bg-color-selection-4:var(--monster-color-yellow-6)}}:after,:before,:root{--monster-color-primary-disabled-1:var(--monster-color-gray-4);--monster-color-primary-disabled-2:var(--monster-color-gray-4);--monster-color-primary-disabled-3:var(--monster-color-gray-4);--monster-color-primary-disabled-4:var(--monster-color-gray-4);--monster-bg-color-primary-disabled-1:var(--monster-color-gray-1);--monster-bg-color-primary-disabled-2:var(--monster-color-gray-2);--monster-bg-color-primary-disabled-3:var(--monster-color-gray-3);--monster-bg-color-primary-disabled-4:var(--monster-color-gray-6)}@media (prefers-color-scheme:dark){:after,:before,:root{--monster-color-primary-disabled-1:var(--monster-color-gray-4);--monster-color-primary-disabled-2:var(--monster-color-gray-4);--monster-color-primary-disabled-3:var(--monster-color-gray-3);--monster-color-primary-disabled-4:var(--monster-color-gray-3);--monster-bg-color-primary-disabled-1:var(--monster-color-gray-6);--monster-bg-color-primary-disabled-2:var(--monster-color-gray-3);--monster-bg-color-primary-disabled-3:var(--monster-color-gray-2);--monster-bg-color-primary-disabled-4:var(--monster-color-gray-1)}}:after,:before,:root{--monster-color-gradient-1:#833ab4;--monster-color-gradient-2:#fd1d1d;--monster-color-gradient-3:#fcb045;--monster-box-shadow-1:none;--monster-box-shadow-2:-1px 1px 10px 1px hsla(0,0%,76%,.61);--monster-text-shadow:none;--monster-theme-control-bg-color:var(--monster-color-seashell-1);--monster-theme-control-color:var(--monster-color-seashell-6);--monster-theme-control-hover-color:var(--monster-color-seashell-6);--monster-theme-control-hover-bg-color:var(--monster-color-seashell-2);--monster-theme-control-border-width:2px;--monster-theme-control-border-style:solid;--monster-theme-control-border-radius:0;--monster-theme-control-border-color:var(--monster-color-primary-1)}@media (prefers-color-scheme:dark){:after,:before,:root{--monster-theme-control-bg-color:var(--monster-color-gray-5);--monster-theme-control-color:var(--monster-color-gray-1);--monster-theme-control-border-color:var(--monster-color-gray-3);--monster-theme-control-hover-color:var(--monster-color-gray-1);--monster-theme-control-hover-bg-color:var(--monster-color-gray-6)}}:after,:before,:root{--monster-theme-on-color:var(--monster-color-green-1);--monster-theme-on-bg-color:var(--monster-color-green-5);--monster-theme-off-color:var(--monster-color-gray-1);--monster-theme-off-bg-color:var(--monster-color-gray-4)}@media (prefers-color-scheme:dark){:after,:before,:root{--monster-theme-on-color:var(--monster-color-gray-6);--monster-theme-on-bg-color:var(--monster-color-gray-1);--monster-theme-off-color:var(--monster-color-gray-1);--monster-theme-off-bg-color:var(--monster-color-gray-5)}}:after,:before,:root{--monster-border-style:solid;--monster-border-width:3px;--monster-border-radius:0;--monster-popper-witharrrow-distance:-4px;--monster-z-index-default:0;--monster-z-index-outline:10;--monster-z-index-dropdown:200;--monster-z-index-dropdown-overlay:210;--monster-z-index-sticky:300;--monster-z-index-sticky-overlay:310;--monster-z-index-fixed:400;--monster-z-index-fixed-overlay:410;--monster-z-index-modal-backdrop:500;--monster-z-index-modal-backdrop-overlay:510;--monster-z-index-offcanvas:600;--monster-z-index-offcanvas-overlay:610;--monster-z-index-modal:700;--monster-z-index-modal-overlay:710;--monster-z-index-popover:800;--monster-z-index-popover-overlay:810;--monster-z-index-tooltip:800;--monster-z-index-tooltip-overlay:910;--monster-space-0:0;--monster-space-1:2px;--monster-space-2:4px;--monster-space-3:6px;--monster-space-4:10px;--monster-space-5:16px;--monster-space-6:26px;--monster-space-7:42px;--monster-breakpoint-0:480px;--monster-breakpoint-4:480px;--monster-breakpoint-7:768px;--monster-breakpoint-9:992px;--monster-breakpoint-12:1200px;--monster-dragger-width:2px;--monster-dragger-handle-width:4px;--monster-dragger-handle-height:50px}div[data-monster-role=popper]{align-content:center;background:var(--monster-bg-color-primary-1);border-color:var(--monster-bg-color-primary-4);border-radius:var(--monster-border-radius);border-style:var(--monster-border-style);border-width:var(--monster-border-width);box-shadow:var(--monster-box-shadow-1);box-sizing:border-box;color:var(--monster-color-primary-1);display:none;justify-content:space-between;left:0;padding:1.1em;position:absolute;top:0;width:-moz-max-content;width:max-content;z-index:var(--monster-z-index-modal)}div[data-monster-role=popper] div[data-monster-role=arrow]{background:var(--monster-bg-color-primary-1);height:calc(max(var(--monster-popper-witharrrow-distance), -1 * var(--monster-popper-witharrrow-distance))*2);pointer-events:none;position:absolute;width:calc(max(var(--monster-popper-witharrrow-distance), -1 * var(--monster-popper-witharrrow-distance))*2);z-index:-1}:host{display:inline-block}[data-monster-role=control]{display:inherit}[data-monster-role=icon]{fill:var(--monster-color-primary-1)}[data-monster-role=icon-map]{display:none}button{background:inherit;border:none}button:hover{cursor:copy}button:active{transform:scale(.95)} 
+}`, 0);
+} catch (e) {
+  addAttributeToken(document.getRootNode().querySelector('html'), ATTRIBUTE_ERRORMESSAGE, e + "");
+}
diff --git a/source/components/form/context-help.mjs b/source/components/form/context-help.mjs
index ad6c244c3..7d0ea2842 100644
--- a/source/components/form/context-help.mjs
+++ b/source/components/form/context-help.mjs
@@ -20,42 +20,6 @@ import { Popper } from "./popper.mjs";
 
 export { ContextHelp };
 
-/**
- * The ContextHelp ist an element that can be used to display a tooltip or a popover.
- *
- * <img src="./images/context-help.png">
- *
- * Dependencies: the system uses functions of the [monsterjs](https://monsterjs.org/) library
- * as well as [pooperjs](https://popper.js.org/docs/v2/).
- *
- * You can create this control either by specifying the HTML tag <monster-context-help-button />` directly in the HTML or using
- * Javascript via the `document.createElement('monster-context-help');` method.
- *
- * ```html
- * <monster-context-help></monster-context-help>
- * ```
- *
- * Or you can create this CustomControl directly in Javascript:
- *
- * ```js
- * import {Popper} from '@schukai/monster/components/form/context-help.mjs';
- * document.createElement('monster-context-help');
- * ```
- *
- * @startuml context-help.png
- * skinparam monochrome true
- * skinparam shadowing false
- * HTMLElement <|-- CustomElement
- * CustomElement <|-- CustomControl
- * CustomControl <|-- Popper
- * Popper <|-- ContextHelp
- * @enduml
- *
- * @copyright schukai GmbH
- * @memberOf Monster.Components.Form
- * @summary A control that can be used to display a tooltip or a popover.
- */
-
 /**
  * A context help control.
  *
-- 
GitLab