From f183bb1e2fe0415427e23c86cdf0ca5539298a9b Mon Sep 17 00:00:00 2001
From: Martin Massenberg <martin.massenberg@schukai.com>
Date: Wed, 6 Mar 2024 16:12:02 +0000
Subject: [PATCH] Neues Switch Control #159

---
 CHANGELOG.md                                  |   8 +
 example/components/form/toggle-switch.mjs     |   7 +
 playground/bind-with-datasource/index.html    | 231 +++++-----
 playground/bind-with-datasource/main.mjs      |  20 +-
 playground/toggle-switch/index.html           |  41 ++
 playground/toggle-switch/main.mjs             |  36 ++
 playground/toggle-switch/main.pcss            |   9 +
 .../components/form/style/toggle-switch.pcss  |  74 +++
 .../form/stylesheet/toggle-switch.mjs         |  27 ++
 source/components/form/toggle-switch.mjs      | 427 ++++++++++++++++++
 test/cases/components/form/toggle-switch.mjs  | 310 +++++++++++++
 11 files changed, 1085 insertions(+), 105 deletions(-)
 create mode 100644 example/components/form/toggle-switch.mjs
 create mode 100644 playground/toggle-switch/index.html
 create mode 100644 playground/toggle-switch/main.mjs
 create mode 100644 playground/toggle-switch/main.pcss
 create mode 100644 source/components/form/style/toggle-switch.pcss
 create mode 100644 source/components/form/stylesheet/toggle-switch.mjs
 create mode 100644 source/components/form/toggle-switch.mjs
 create mode 100644 test/cases/components/form/toggle-switch.mjs

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 37bea2016..bf6d063e8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,11 @@
+## 2024-03-06
+## Add Features
+- Neues CustomControl Switch [#159](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/159)
+
+## 2024-03-05
+## Bugfixes
+- Updater Methode getControlEventHandler the Function `retrieveAndSetValue` is called with settimeout [#158](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/158)
+- Updater Methode addObjectWithUpdaterToElement the Function `updaterTransformerMethodsSymbol` is called with `call` so that the `this` pointer is available 
 
 ## [3.57.0] - 2024-03-02
 
diff --git a/example/components/form/toggle-switch.mjs b/example/components/form/toggle-switch.mjs
new file mode 100644
index 000000000..7b23a82e8
--- /dev/null
+++ b/example/components/form/toggle-switch.mjs
@@ -0,0 +1,7 @@
+import {Switch} from '@schukai/component-form/source/toggle-switch.js';
+
+// create element
+const toggleSwitch = document.createElement('monster-toggle-switch');
+
+// insert element into the DOM
+document.body.appendChild(toggleSwitch);
\ No newline at end of file
diff --git a/playground/bind-with-datasource/index.html b/playground/bind-with-datasource/index.html
index 1c289f47e..04d7362c4 100644
--- a/playground/bind-with-datasource/index.html
+++ b/playground/bind-with-datasource/index.html
@@ -10,120 +10,118 @@
 </head>
 
 <body>
-<main>
-    <div>
-        <!--
+    <main>
+        <div>
+            <!--
             Datasource mit fest definieren Daten
         -->
 
-        <monster-datasource-rest id="data1"
-                                 data-monster-option-write-url="/demo/bind-with-datasource/data.json"
-                                 data-monster-option-read-url="/demo/bind-with-datasource/data.json"></monster-datasource-rest>
+            <monster-datasource-rest id="data1" data-monster-option-write-url="/demo/bind-with-datasource/data.json" data-monster-option-read-url="/demo/bind-with-datasource/data.json"></monster-datasource-rest>
 
-        <h1>Bestellungen</h1>
+            <h1>Bestellungen</h1>
 
 
-        <!--
+            <!--
             Datatable stellt diese Daten dar
             mit "data-monster-datasource-selector" wird die Datenquelle "#data1" definiert
         -->
-        <monster-datatable id="test-datatable" data-monster-datasource-selector="#data1">
-
-            <monster-datasource-save-button slot="bar" data-monster-option-datasource-selector="#data1">
-            </monster-datasource-save-button>
-            <monster-datasource-status slot="bar" data-monster-option-datasource-selector="#data1">
-            </monster-datasource-status>
-
-            <template id="test-datatable-row">
-
-                <div data-monster-head="erpName" data-monster-replace="path:test-datatable-row.erpName "></div>
-                <div data-monster-head="OID" data-monster-mode="fixed" data-monster-sortable="oid"
-                     data-monster-replace="path:test-datatable-row.oid | tostring"></div>
-                <div data-monster-head="orderLastStatusChange"
-                     data-monster-replace="path:test-datatable-row.orderLastStatusChange"></div>
-                <div data-monster-head="customerUID" data-monster-replace="path:test-datatable-row.customerUID"></div>
-                <div data-monster-head="resubmissionDate"
-                     data-monster-replace="path:test-datatable-row.resubmissionDate"></div>
-                <div data-monster-head="billingAddressAID"
-                     data-monster-replace="path:test-datatable-row.billingAddressAID"></div>
-                <div data-monster-head="deliveryAddressAID"
-                     data-monster-replace="path:test-datatable-row.deliveryAddressAID"></div>
-
-                <div data-monster-head=" " data-alvine-role="actionHolder" class="actionButtons">
-
-                    <monster-datatable-change-button
-                            style="height:100%;display:flex;margin-right:0.1rem;"
-                            data-monster-option-dataset-selector="#dataset1"
-                            data-monster-option-overlay-selector="#overlay1">
-
-                    </monster-datatable-change-button>
-                    <monster-datatable-change-button
-                            style="height:100%;;display:flex;"
-                            data-monster-option-labels-button="o2"
-                            data-monster-option-dataset-selector="#dataset1"
-                            data-monster-option-overlay-selector="#overlay2">
-
-                    </monster-datatable-change-button>
-
-
-                    <!--monster-button
-                            data-alvine-role="changeButton"
-                            data-monster-option-actions-click=""
-                            data-monster-attributes="data-alvine-oid path:test-datatable-row.index">
-                        <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
-                             class="bi bi-grid" viewBox="0 0 16 16">
-                            <path d="M1 2.5A1.5 1.5 0 0 1 2.5 1h3A1.5 1.5 0 0 1 7 2.5v3A1.5 1.5 0 0 1 5.5 7h-3A1.5 1.5 0 0 1 1 5.5zM2.5 2a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5zm6.5.5A1.5 1.5 0 0 1 10.5 1h3A1.5 1.5 0 0 1 15 2.5v3A1.5 1.5 0 0 1 13.5 7h-3A1.5 1.5 0 0 1 9 5.5zm1.5-.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5zM1 10.5A1.5 1.5 0 0 1 2.5 9h3A1.5 1.5 0 0 1 7 10.5v3A1.5 1.5 0 0 1 5.5 15h-3A1.5 1.5 0 0 1 1 13.5zm1.5-.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5zm6.5.5A1.5 1.5 0 0 1 10.5 9h3a1.5 1.5 0 0 1 1.5 1.5v3a1.5 1.5 0 0 1-1.5 1.5h-3A1.5 1.5 0 0 1 9 13.5zm1.5-.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5z"/>
-                        </svg>
-                    </monster-button -->
-                </div>
-            </template>
-        </monster-datatable>
+            <monster-datatable id="test-datatable" data-monster-datasource-selector="#data1">
 
-        <monster-overlay id="overlay1" data-monster-option-features-openbutton="false">
+                <monster-datasource-save-button slot="bar" data-monster-option-datasource-selector="#data1">
+                </monster-datasource-save-button>
+                <monster-datasource-status slot="bar" data-monster-option-datasource-selector="#data1">
+                </monster-datasource-status>
 
+                <template id="test-datatable-row">
 
-            <monster-dataset id="dataset1" data-monster-datasource-selector="#data1">
+                    <div data-monster-head="erpName" data-monster-replace="path:test-datatable-row.erpName "></div>
+                    <div data-monster-head="OID" data-monster-mode="fixed" data-monster-sortable="oid" data-monster-replace="path:test-datatable-row.oid | tostring"></div>
+                    <div data-monster-head="erpLastUpdate" data-monster-replace="path:test-datatable-row.erpLastUpdate"></div>
+                    <div data-monster-head="type" data-monster-replace="path:test-datatable-row.type"></div>
+                    <div data-monster-head="customerUID" data-monster-replace="path:test-datatable-row.customerUID"></div>
+                    <div data-monster-head="resubmissionDate" data-monster-replace="path:test-datatable-row.resubmissionDate"></div>
+                    <div data-monster-head="billingAddressAID" data-monster-replace="path:test-datatable-row.billingAddressAID"></div>
+                    <div data-monster-head="deliveryAddressAID" data-monster-replace="path:test-datatable-row.deliveryAddressAID"></div>
 
-                <div class="form-container">
-                    <h2>OVERLAY 1</h2>
-                    <div class="inner-form">
-                        <label>test
-                            <input data-monster-bind="path:data.oid" data-monster-bind-type="integer"
-                                   data-monster-attributes="value path:data.oid "></label>
-                        <label>test
-                            <input data-monster-bind="path:data.erpName"
-                                   data-monster-attributes="value path:data.erpName "></label>
-                        <label>test
-                            <input data-monster-bind="path:data.erpName"
-                                   data-monster-attributes="value path:data.erpNumber "></label>
+                    <div data-monster-head=" " data-alvine-role="actionHolder" class="actionButtons">
 
-                        <div data-monster-replace="path:data.oid ">xx</div>
-                        <div data-monster-replace="path:data.orderState ">xx</div>
-                        <div data-monster-replace="path:data.orderLastStatusChange ">xx</div>
+                        <monster-datatable-change-button style="height:100%;display:flex;margin-right:0.1rem;" data-monster-option-dataset-selector="#dataset1" data-monster-option-overlay-selector="#overlay1">
 
-                        <select data-monster-bind="path:data.type" data-monster-attributes="value path:data.type">
-                            <option value="">bitte wählen</option>
-                            <option value="order">order</option>
-                            <option value="22">22</option>
-                            <option value="33">33</option>
-                        </select>
+                        </monster-datatable-change-button>
+                        <monster-datatable-change-button style="height:100%;;display:flex;" data-monster-option-labels-button="o2" data-monster-option-dataset-selector="#dataset1" data-monster-option-overlay-selector="#overlay2">
 
-                        <!-- Ckeckbox wird ausgewählt wenn archived den Wert TRUE hat -->
-                        <label>Checkbox
-                            <input type="checkbox" value="true" data-monster-bind="path:data.archived" data-monster-bind-type="boolean"
-                            data-monster-defaultvalue="false"   data-monster-attributes="checked path:data.archived | ?:true: "></label>
+                        </monster-datatable-change-button>
 
-                        <div class="buttons">
-                            <monster-button
-                                    data-monster-role="saveButton"
-                                    id="saveButton"
-                                    style="display:block;width:200px;">save
-                            </monster-button>
-                            <monster-button
-                                    data-monster-role="cancelButton"
-                                    id="cancelButton"
-                                    style="display:block;width:200px;">cancel
-                            </monster-button>
+
+                        <!--monster-button
+                            data-alvine-role="changeButton"
+                            data-monster-option-actions-click=""
+                            data-monster-attributes="data-alvine-oid path:test-datatable-row.index">
+                        <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
+                             class="bi bi-grid" viewBox="0 0 16 16">
+                            <path d="M1 2.5A1.5 1.5 0 0 1 2.5 1h3A1.5 1.5 0 0 1 7 2.5v3A1.5 1.5 0 0 1 5.5 7h-3A1.5 1.5 0 0 1 1 5.5zM2.5 2a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5zm6.5.5A1.5 1.5 0 0 1 10.5 1h3A1.5 1.5 0 0 1 15 2.5v3A1.5 1.5 0 0 1 13.5 7h-3A1.5 1.5 0 0 1 9 5.5zm1.5-.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5zM1 10.5A1.5 1.5 0 0 1 2.5 9h3A1.5 1.5 0 0 1 7 10.5v3A1.5 1.5 0 0 1 5.5 15h-3A1.5 1.5 0 0 1 1 13.5zm1.5-.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5zm6.5.5A1.5 1.5 0 0 1 10.5 9h3a1.5 1.5 0 0 1 1.5 1.5v3a1.5 1.5 0 0 1-1.5 1.5h-3A1.5 1.5 0 0 1 9 13.5zm1.5-.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5z"/>
+                        </svg>
+                    </monster-button -->
+                    </div>
+                </template>
+            </monster-datatable>
+
+            <monster-overlay id="overlay1" data-monster-option-features-openbutton="false">
+
+
+                <monster-dataset id="dataset1" data-monster-datasource-selector="#data1">
+
+                    <div class="form-container">
+                        <h2>OVERLAY 1</h2>
+                        <div class="inner-form">
+                            <label>test
+                                <input data-monster-bind="path:data.oid" data-monster-bind-type="integer" data-monster-attributes="value path:data.oid "></label>
+                            <label>test
+                                <input data-monster-bind="path:data.erpName" data-monster-attributes="value path:data.erpName "></label>
+                            <label>test
+                                <input data-monster-bind="path:data.erpName" data-monster-attributes="value path:data.erpNumber "></label>
+
+                            <div data-monster-replace="path:data.oid ">xx</div>
+                            <div data-monster-replace="path:data.orderState ">xx</div>
+                            <div data-monster-replace="path:data.orderLastStatusChange ">xx</div>
+
+                            <label>select
+                                <select data-monster-bind="path:data.type" data-monster-attributes="value path:data.type">
+                                    <option value="">bitte wählen</option>
+                                    <option value="order">order</option>
+                                    <option value="22">22</option>
+                                    <option value="33">33</option>
+                                </select>
+                            </label>
+
+                            <label>monster select
+                                <monster-select data-monster-bind="path:data.type" data-monster-attributes="value path:data.type">
+                                    <div data-monster-value="order">Bestellung</div>
+                                    <div data-monster-value="test">label-2</div>
+                                    <div data-monster-value="wewe">label-3</div>
+                                </monster-select>
+                            </label>
+
+                            <label><!-- data-monster-option-values-on="true" data-monster-option-values-off="false" -->
+                                toggle Switch
+                                <monster-toggle-switch data-monster-bind="path:data.archived" data-monster-option-values-on="true" data-monster-option-values-off="false"  data-monster-bind-type="boolean" data-monster-attributes="value path:data.archived | ?:true:false "></monster-toggle-switch>
+                            </label>
+
+                            <!-- Ckeckbox wird ausgewählt wenn archived den Wert TRUE hat -->
+                            <label>Checkbox
+                                <input type="checkbox" value="true" data-monster-bind="path:data.archived" data-monster-bind-type="boolean" data-monster-defaultvalue="false" data-monster-attributes="checked path:data.archived | ?:true: "></label>
+
+                            <label>Datum erpLastUpdate
+                                <input type="datetime-local" data-monster-bind="path:data.erpLastUpdate" data-monster-attributes="value path:data.erpLastUpdate" />
+                            </label>
+
+                            <div class="buttons">
+                                <monster-button data-monster-role="saveButton" id="saveButton" style="display:block;width:200px;">save
+                                </monster-button>
+                                <monster-button data-monster-role="cancelButton" id="cancelButton" style="display:block;width:200px;">cancel
+                                </monster-button>
+
+                            </div>
 
                         </div>
 
@@ -170,18 +168,45 @@
                             </monster-button>
 
                         </div>
+                    </div>
 
+                </monster-dataset>
+            </monster-overlay>
+
+
+            <monster-overlay id="overlay2" data-monster-option-features-openbutton="false">
+
+
+                <monster-dataset id="dataset2" data-monster-datasource-selector="#data1">
+
+                    <div class="form-container">
+                        <h2>OVERLAY 2</h2>
+                        <div class="inner-form">
+                            <label>test
+                                <input data-monster-bind="path:data.oid" data-monster-bind-type="integer" data-monster-attributes="value path:data.oid "></label>
+                            <label>test
+                                <input data-monster-bind="path:data.erpName" data-monster-attributes="value path:data.erpName "></label>
+
+
+                            <div class="buttons">
+                                <monster-button data-monster-role="saveButton" id="saveButton" style="display:block;width:200px;">save
+                                </monster-button>
+                                <monster-button data-monster-role="cancelButton" id="cancelButton" style="display:block;width:200px;">cancel
+                                </monster-button>
+
+                            </div>
+
+                        </div>
                     </div>
-                </div>
 
-               
 
-            </monster-dataset>
-        </monster-overlay>
-    </div>
+
+                </monster-dataset>
+            </monster-overlay>
+        </div>
 
 
-</main>
+    </main>
 </body>
 
 </html>
\ No newline at end of file
diff --git a/playground/bind-with-datasource/main.mjs b/playground/bind-with-datasource/main.mjs
index 340f42598..c836edc54 100644
--- a/playground/bind-with-datasource/main.mjs
+++ b/playground/bind-with-datasource/main.mjs
@@ -1,5 +1,5 @@
 
-import "../../source/components/form/button.mjs";
+
 import "../../source/components/host/overlay.mjs";
 import "../../source/components/datatable/datatable.mjs";
 import "../../source/components/datatable/dataset.mjs";
@@ -7,10 +7,14 @@ import "../../source/components/datatable/datasource/dom.mjs";
 import "../../source/components/datatable/datasource/rest.mjs";
 import "../../source/components/datatable/save-button.mjs";
 
+import "../../source/components/form/button.mjs";
+import "../../source/components/form/select.mjs";
+import "../../source/components/form/toggle-switch.mjs";
+
+import "../../source/components/style/common.pcss";
 import "../../source/components/style/color.pcss";
 import "../../source/components/style/theme.pcss";
 import "../../source/components/style/table.pcss";
-import "../../source/components/style/property.pcss";
 import "../../source/components/style/badge.pcss";
 import "../../source/components/style/button.pcss";
 import "../../source/components/style/link.pcss";
@@ -18,6 +22,7 @@ import "../../source/components/style/data-grid.pcss";
 import "../../source/components/style/property.pcss";
 import "../../source/components/style/typography.pcss";
 import "../../source/components/style/display.pcss";
+
 import "../../source/components/datatable/datasource/rest.mjs";
 import "../../source/components/datatable/filter.mjs";
 import "../../source/components/datatable/filter-button.mjs";
@@ -53,6 +58,17 @@ let callbacks = {
     }};
 data.setOption('write.mapping.callbacks', callbacks);
 
+data.setOption('read.mapping.transformer', 'call:agenorFormater');
+let readCallbacks = {
+    agenorFormater: function (data) {
+        for (const [key, value] of Object.entries(data.dataset)) {
+            data.dataset[key].erpLastUpdate = '2020-01-16T10:27:18';
+        }
+        return data;
+    }
+};
+data.setOption('read.mapping.callbacks', readCallbacks);
+
 
 let dataset1 = document.getElementById('dataset1');
 //let dataset2 = document.getElementById('dataset2');
diff --git a/playground/toggle-switch/index.html b/playground/toggle-switch/index.html
new file mode 100644
index 000000000..c25526dbf
--- /dev/null
+++ b/playground/toggle-switch/index.html
@@ -0,0 +1,41 @@
+<!doctype html>
+<html lang="en">
+
+<head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0">
+
+    <title>Switch</title>
+    <script src="main.mjs" type="module"></script>
+
+    <script id="translations" type="application/json" data-monster-role="translations">
+        {
+            "toggle-switch-on": "an",
+            "toggle-switch-off": "aus"
+        }
+    </script>
+
+</head>
+
+<body style="display:flex;justify-content: center">
+
+    <main style="width: 600px">
+
+        <h1 class="deco">Switch</h1>
+
+        <p class="deco"></p>
+
+        <div class="container">
+            <form class="monster-form">
+                <label>monster-switch
+                    <monster-toggle-switch id="monsterToggleSwitch" name="agb" ></monster-toggle-switch>
+                </label>
+                <button>Submit</button>
+            </form>
+        </div>
+
+    </main>
+
+</body>
+
+</html>
\ No newline at end of file
diff --git a/playground/toggle-switch/main.mjs b/playground/toggle-switch/main.mjs
new file mode 100644
index 000000000..117b82573
--- /dev/null
+++ b/playground/toggle-switch/main.mjs
@@ -0,0 +1,36 @@
+import "./main.pcss";
+
+import "../../source/components/style/common.pcss";
+import "../../source/components/form/toggle-switch.mjs";
+import "../../source/components/style/form.pcss";
+import "../../source/components/style/color.pcss";
+import "../../source/components/style/property.pcss";
+
+import { Embed } from "../../source/i18n/providers/embed.mjs";
+
+Embed.assignTranslationsToElement().then(() => {
+    let toggleSwitch = document.getElementById('monsterToggleSwitch');
+    toggleSwitch.updateI18n();
+
+});
+
+
+
+// debugger
+ //let toggleSwitch = document.createElement('monster-toggle-switch');
+
+// /**
+//  * define the values for on and off
+//  */
+// toggleSwitch.setOption('values.on', 'true');
+// toggleSwitch.setOption('values.off', 'false');
+
+// /**
+//  * This value is not "true" and not "false"
+//  */
+ //toggleSwitch.value = "on";
+
+ //document.body.appendChild(toggleSwitch);
+
+// let a = toggleSwitch.hasAttribute('disabled');
+
diff --git a/playground/toggle-switch/main.pcss b/playground/toggle-switch/main.pcss
new file mode 100644
index 000000000..4a5405cb7
--- /dev/null
+++ b/playground/toggle-switch/main.pcss
@@ -0,0 +1,9 @@
+@import"../../source/components/style/mixin/form.pcss";
+
+
+.container{
+    font-size: 12px;
+    display: flex;
+    flex-direction: column;
+}
+
diff --git a/source/components/form/style/toggle-switch.pcss b/source/components/form/style/toggle-switch.pcss
new file mode 100644
index 000000000..6c5814e31
--- /dev/null
+++ b/source/components/form/style/toggle-switch.pcss
@@ -0,0 +1,74 @@
+@import "../../style/color.pcss";
+@import "../../style/theme.pcss";
+@import "../../style/border.pcss";
+
+[data-monster-role=control] {
+
+  font-family: inherit;
+  font-size: 100%;
+  padding: 0.4rem 0.6rem;
+  margin: 0;
+  outline: none;
+  box-sizing: border-box;
+}
+
+[data-monster-role=control]:focus {
+  outline: 1px dashed var(--monster-color-selection-3);
+  outline-offset: 2px;
+}
+
+.switch {
+  position: relative;
+  display: inline-block;
+  width: 60px;
+  height: 34px;
+  
+  border-color: var(--monster-bg-color-primary-3);
+  border-width: thin;
+  border-style: var(--monster-border-style);
+  transition: background-color 0.2s;
+  display: grid;
+    grid-template-columns: 1fr 1fr;
+}
+
+.switch-radio input[type="radio"] {
+  display: none;
+}
+
+.label{
+    height: 34px;
+    display: block;
+    text-align: center;
+    line-height: 34px;
+    user-select: none;
+}
+
+.switch-slider {
+  position: absolute;
+  top: 4px;
+  bottom: 4px;
+  left: 4px;
+  right: 28px;
+  background-color: var(--monster-bg-color-primary-4);
+  transition: 0.2s;
+}
+
+.switch[data-monster-state="on"] .label.off  {
+  visibility: hidden;
+}
+.switch[data-monster-state="on"] .label.on  {
+  visibility: visible;
+}
+.switch[data-monster-state="off"] .label.off  {
+  visibility: visible;
+}
+.switch[data-monster-state="off"] .label.on  {
+  visibility: hidden;
+}
+
+
+.switch[data-monster-state="on"] .switch-slider  {
+  transform: translateX(24px);
+
+}
+
diff --git a/source/components/form/stylesheet/toggle-switch.mjs b/source/components/form/stylesheet/toggle-switch.mjs
new file mode 100644
index 000000000..b2b7318bd
--- /dev/null
+++ b/source/components/form/stylesheet/toggle-switch.mjs
@@ -0,0 +1,27 @@
+
+/**
+ * Copyright schukai GmbH and contributors 2024. All Rights Reserved.
+ * Node module: @schukai/monster
+ * This file is licensed under the AGPLv3 License.
+ * License text available at https://www.gnu.org/licenses/agpl-3.0.en.html
+ */
+
+import {addAttributeToken} from "../../../dom/attributes.mjs";
+import {ATTRIBUTE_ERRORMESSAGE} from "../../../dom/constants.mjs";
+
+export {ToggleSwitchStyleSheet}
+
+/**
+ * @private
+ * @type {CSSStyleSheet}
+ */
+const ToggleSwitchStyleSheet = new CSSStyleSheet();
+
+try {
+  ToggleSwitchStyleSheet.insertRule(`
+@layer toggleswitch { 
+:after,:before,:root{--monster-color-gray-1:#f6f6f6;--monster-color-gray-2:#e2e2e2;--monster-color-gray-3:#8b8b8b;--monster-color-gray-4:#6f6f6f;--monster-color-gray-5:#3e3e3e;--monster-color-gray-6:#222;--monster-color-rose-1:#fff7f9;--monster-color-rose-2:#ffdce5;--monster-color-rose-3:#ff3b8d;--monster-color-rose-4:#db0072;--monster-color-rose-5:#800040;--monster-color-rose-6:#4c0023;--monster-color-raspberry-1:#fff8f8;--monster-color-raspberry-2:#ffdddf;--monster-color-raspberry-3:#ff426c;--monster-color-raspberry-4:#de0051;--monster-color-raspberry-5:#82002c;--monster-color-raspberry-6:#510018;--monster-color-red-1:#fff8f6;--monster-color-red-2:#ffddd8;--monster-color-red-3:#ff4647;--monster-color-red-4:#e0002b;--monster-color-red-5:#830014;--monster-color-red-6:#530003;--monster-color-orange-1:#fff8f5;--monster-color-orange-2:#ffded1;--monster-color-orange-3:#fd4d00;--monster-color-orange-4:#cd3c00;--monster-color-orange-5:#752100;--monster-color-orange-6:#401600;--monster-color-cinnamon-1:#fff8f3;--monster-color-cinnamon-2:#ffdfc6;--monster-color-cinnamon-3:#d57300;--monster-color-cinnamon-4:#ac5c00;--monster-color-cinnamon-5:#633300;--monster-color-cinnamon-6:#371d00;--monster-color-amber-1:#fff8ef;--monster-color-amber-2:#ffe0b2;--monster-color-amber-3:#b98300;--monster-color-amber-4:#926700;--monster-color-amber-5:#523800;--monster-color-amber-6:#302100;--monster-color-yellow-1:#fff9e5;--monster-color-yellow-2:#ffe53e;--monster-color-yellow-3:#9c8b00;--monster-color-yellow-4:#7d6f00;--monster-color-yellow-5:#463d00;--monster-color-yellow-6:#292300;--monster-color-lime-1:#f7ffac;--monster-color-lime-2:#d5f200;--monster-color-lime-3:#819300;--monster-color-lime-4:#677600;--monster-color-lime-5:#394100;--monster-color-lime-6:#222600;--monster-color-chartreuse-1:#e5ffc3;--monster-color-chartreuse-2:#98fb00;--monster-color-chartreuse-3:#5c9b00;--monster-color-chartreuse-4:#497c00;--monster-color-chartreuse-5:#264500;--monster-color-chartreuse-6:#182600;--monster-color-green-1:#e0ffd9;--monster-color-green-2:#72ff6c;--monster-color-green-3:#00a21f;--monster-color-green-4:#008217;--monster-color-green-5:#004908;--monster-color-green-6:#062800;--monster-color-emerald-1:#dcffe6;--monster-color-emerald-2:#5dffa2;--monster-color-emerald-3:#00a05a;--monster-color-emerald-4:#008147;--monster-color-emerald-5:#004825;--monster-color-emerald-6:#002812;--monster-color-aquamarine-1:#daffef;--monster-color-aquamarine-2:#42ffc6;--monster-color-aquamarine-3:#009f78;--monster-color-aquamarine-4:#007f5f;--monster-color-aquamarine-5:#004734;--monster-color-aquamarine-6:#00281b;--monster-color-teal-1:#d7fff7;--monster-color-teal-2:#00ffe4;--monster-color-teal-3:#009e8c;--monster-color-teal-4:#007c6e;--monster-color-teal-5:#00443c;--monster-color-teal-6:#002722;--monster-color-cyan-1:#c4fffe;--monster-color-cyan-2:#00fafb;--monster-color-cyan-3:#00999a;--monster-color-cyan-4:#007a7b;--monster-color-cyan-5:#004344;--monster-color-cyan-6:#002525;--monster-color-powder-1:#dafaff;--monster-color-powder-2:#8df0ff;--monster-color-powder-3:#0098a9;--monster-color-powder-4:#007987;--monster-color-powder-5:#004048;--monster-color-powder-6:#002227;--monster-color-sky-1:#e3f7ff;--monster-color-sky-2:#aee9ff;--monster-color-sky-3:#0094b4;--monster-color-sky-4:#007590;--monster-color-sky-5:#00404f;--monster-color-sky-6:#001f28;--monster-color-cerulean-1:#e8f6ff;--monster-color-cerulean-2:#b9e3ff;--monster-color-cerulean-3:#0092c5;--monster-color-cerulean-4:#00749d;--monster-color-cerulean-5:#003c54;--monster-color-cerulean-6:#001d2a;--monster-color-azure-1:#e8f2ff;--monster-color-azure-2:#c6e0ff;--monster-color-azure-3:#008fdb;--monster-color-azure-4:#0071af;--monster-color-azure-5:#003b5e;--monster-color-azure-6:#001c30;--monster-color-blue-1:#f0f4ff;--monster-color-blue-2:#d4e0ff;--monster-color-blue-3:#0089fc;--monster-color-blue-4:#006dca;--monster-color-blue-5:#00386d;--monster-color-blue-6:#001a39;--monster-color-indigo-1:#f3f3ff;--monster-color-indigo-2:#deddff;--monster-color-indigo-3:#657eff;--monster-color-indigo-4:#0061fc;--monster-color-indigo-5:#00328a;--monster-color-indigo-6:#001649;--monster-color-violet-1:#f7f1ff;--monster-color-violet-2:#e8daff;--monster-color-violet-3:#9b70ff;--monster-color-violet-4:#794aff;--monster-color-violet-5:#2d0fbf;--monster-color-violet-6:#0b0074;--monster-color-purple-1:#fdf4ff;--monster-color-purple-2:#f7d9ff;--monster-color-purple-3:#d150ff;--monster-color-purple-4:#b01fe3;--monster-color-purple-5:#660087;--monster-color-purple-6:#3a004f;--monster-color-magenta-1:#fff3fc;--monster-color-magenta-2:#ffd7f6;--monster-color-magenta-3:#f911e0;--monster-color-magenta-4:#ca00b6;--monster-color-magenta-5:#740068;--monster-color-magenta-6:#44003c;--monster-color-pink-1:#fff7fb;--monster-color-pink-2:#ffdcec;--monster-color-pink-3:#ff2fb2;--monster-color-pink-4:#d2008f;--monster-color-pink-5:#790051;--monster-color-pink-6:#4b0030}.monster-theme-primary-1{background-color:var(--monster-bg-color-primary-1);color:var(--monster-color-primary-1)}.monster-theme-primary-disabled-1{background-color:var(--monster-bg-color-primary-disabled-1);color:var(--monster-color-primary-disabled-1)}.monster-theme-secondary-1{background-color:var(--monster-bg-color-secondary-1);color:var(--monster-color-secondary-1)}.monster-theme-tertiary-1{background-color:var(--monster-bg-color-tertiary-1);color:var(--monster-color-tertiary-1)}.monster-theme-destructive-1{background-color:var(--monster-bg-color-destructive-1);color:var(--monster-color-destructive-1)}.monster-theme-success-1{background-color:var(--monster-bg-color-success-1);color:var(--monster-color-success-1)}.monster-theme-warning-1{background-color:var(--monster-bg-color-warning-1);color:var(--monster-color-warning-1)}.monster-theme-error-1{background-color:var(--monster-bg-color-error-1);color:var(--monster-color-error-1)}.monster-theme-selection-1{background-color:var(--monster-bg-color-selection-1);color:var(--monster-color-selection-1)}.monster-border-color-1{border-color:var(--monster-color-border-1)}.monster-color-neutral-1{color:var(--monster-color-primary-1)}.monster-bg-color-primary-1{background-color:var(--monster-bg-color-primary-1)}.monster-bg-color-secondary-1{background-color:var(--monster-bg-color-secondary-1)}.monster-bg-color-tertiary-1{background-color:var(--monster-bg-color-tertiary-1)}.monster-color-primary-1{background-color:var(--monster-bg-color-primary-1);color:var(--monster-color-primary-1)}.monster-color-secondary-1{background-color:var(--monster-bg-color-secondary-1);color:var(--monster-color-secondary-1)}.monster-color-tertiary-1{background-color:var(--monster-bg-color-tertiary-1);color:var(--monster-color-tertiary-1)}.monster-color-destructive-1{background-color:var(--monster-bg-color-destructive-1);color:var(--monster-color-destructive-1)}.monster-color-success-1{background-color:var(--monster-bg-color-success-1);color:var(--monster-color-success-1)}.monster-color-warning-1{background-color:var(--monster-bg-color-warning-1);color:var(--monster-color-warning-1)}.monster-color-error-1{background-color:var(--monster-bg-color-error-1);color:var(--monster-color-error-1)}.monster-color-selection-1{background-color:var(--monster-bg-color-selection-1);color:var(--monster-color-selection-1)}.monster-theme-primary-2{background-color:var(--monster-bg-color-primary-2);color:var(--monster-color-primary-2)}.monster-theme-primary-disabled-2{background-color:var(--monster-bg-color-primary-disabled-2);color:var(--monster-color-primary-disabled-2)}.monster-theme-secondary-2{background-color:var(--monster-bg-color-secondary-2);color:var(--monster-color-secondary-2)}.monster-theme-tertiary-2{background-color:var(--monster-bg-color-tertiary-2);color:var(--monster-color-tertiary-2)}.monster-theme-destructive-2{background-color:var(--monster-bg-color-destructive-2);color:var(--monster-color-destructive-2)}.monster-theme-success-2{background-color:var(--monster-bg-color-success-2);color:var(--monster-color-success-2)}.monster-theme-warning-2{background-color:var(--monster-bg-color-warning-2);color:var(--monster-color-warning-2)}.monster-theme-error-2{background-color:var(--monster-bg-color-error-2);color:var(--monster-color-error-2)}.monster-theme-selection-2{background-color:var(--monster-bg-color-selection-2);color:var(--monster-color-selection-2)}.monster-border-color-2{border-color:var(--monster-color-border-2)}.monster-color-neutral-2{color:var(--monster-color-primary-2)}.monster-bg-color-primary-2{background-color:var(--monster-bg-color-primary-2)}.monster-bg-color-secondary-2{background-color:var(--monster-bg-color-secondary-2)}.monster-bg-color-tertiary-2{background-color:var(--monster-bg-color-tertiary-2)}.monster-color-primary-2{background-color:var(--monster-bg-color-primary-2);color:var(--monster-color-primary-2)}.monster-color-secondary-2{background-color:var(--monster-bg-color-secondary-2);color:var(--monster-color-secondary-2)}.monster-color-tertiary-2{background-color:var(--monster-bg-color-tertiary-2);color:var(--monster-color-tertiary-2)}.monster-color-destructive-2{background-color:var(--monster-bg-color-destructive-2);color:var(--monster-color-destructive-2)}.monster-color-success-2{background-color:var(--monster-bg-color-success-2);color:var(--monster-color-success-2)}.monster-color-warning-2{background-color:var(--monster-bg-color-warning-2);color:var(--monster-color-warning-2)}.monster-color-error-2{background-color:var(--monster-bg-color-error-2);color:var(--monster-color-error-2)}.monster-color-selection-2{background-color:var(--monster-bg-color-selection-2);color:var(--monster-color-selection-2)}.monster-theme-primary-3{background-color:var(--monster-bg-color-primary-3);color:var(--monster-color-primary-3)}.monster-theme-primary-disabled-3{background-color:var(--monster-bg-color-primary-disabled-3);color:var(--monster-color-primary-disabled-3)}.monster-theme-secondary-3{background-color:var(--monster-bg-color-secondary-3);color:var(--monster-color-secondary-3)}.monster-theme-tertiary-3{background-color:var(--monster-bg-color-tertiary-3);color:var(--monster-color-tertiary-3)}.monster-theme-destructive-3{background-color:var(--monster-bg-color-destructive-3);color:var(--monster-color-destructive-3)}.monster-theme-success-3{background-color:var(--monster-bg-color-success-3);color:var(--monster-color-success-3)}.monster-theme-warning-3{background-color:var(--monster-bg-color-warning-3);color:var(--monster-color-warning-3)}.monster-theme-error-3{background-color:var(--monster-bg-color-error-3);color:var(--monster-color-error-3)}.monster-theme-selection-3{background-color:var(--monster-bg-color-selection-3);color:var(--monster-color-selection-3)}.monster-border-color-3{border-color:var(--monster-color-border-3)}.monster-color-neutral-3{color:var(--monster-color-primary-3)}.monster-bg-color-primary-3{background-color:var(--monster-bg-color-primary-3)}.monster-bg-color-secondary-3{background-color:var(--monster-bg-color-secondary-3)}.monster-bg-color-tertiary-3{background-color:var(--monster-bg-color-tertiary-3)}.monster-color-primary-3{background-color:var(--monster-bg-color-primary-3);color:var(--monster-color-primary-3)}.monster-color-secondary-3{background-color:var(--monster-bg-color-secondary-3);color:var(--monster-color-secondary-3)}.monster-color-tertiary-3{background-color:var(--monster-bg-color-tertiary-3);color:var(--monster-color-tertiary-3)}.monster-color-destructive-3{background-color:var(--monster-bg-color-destructive-3);color:var(--monster-color-destructive-3)}.monster-color-success-3{background-color:var(--monster-bg-color-success-3);color:var(--monster-color-success-3)}.monster-color-warning-3{background-color:var(--monster-bg-color-warning-3);color:var(--monster-color-warning-3)}.monster-color-error-3{background-color:var(--monster-bg-color-error-3);color:var(--monster-color-error-3)}.monster-color-selection-3{background-color:var(--monster-bg-color-selection-3);color:var(--monster-color-selection-3)}.monster-theme-primary-4{background-color:var(--monster-bg-color-primary-4);color:var(--monster-color-primary-4)}.monster-theme-primary-disabled-4{background-color:var(--monster-bg-color-primary-disabled-4);color:var(--monster-color-primary-disabled-4)}.monster-theme-secondary-4{background-color:var(--monster-bg-color-secondary-4);color:var(--monster-color-secondary-4)}.monster-theme-tertiary-4{background-color:var(--monster-bg-color-tertiary-4);color:var(--monster-color-tertiary-4)}.monster-theme-destructive-4{background-color:var(--monster-bg-color-destructive-4);color:var(--monster-color-destructive-4)}.monster-theme-success-4{background-color:var(--monster-bg-color-success-4);color:var(--monster-color-success-4)}.monster-theme-warning-4{background-color:var(--monster-bg-color-warning-4);color:var(--monster-color-warning-4)}.monster-theme-error-4{background-color:var(--monster-bg-color-error-4);color:var(--monster-color-error-4)}.monster-theme-selection-4{background-color:var(--monster-bg-color-selection-4);color:var(--monster-color-selection-4)}.monster-border-color-4{border-color:var(--monster-color-border-4)}.monster-color-neutral-4{color:var(--monster-color-primary-4)}.monster-bg-color-primary-4{background-color:var(--monster-bg-color-primary-4)}.monster-bg-color-secondary-4{background-color:var(--monster-bg-color-secondary-4)}.monster-bg-color-tertiary-4{background-color:var(--monster-bg-color-tertiary-4)}.monster-color-primary-4{background-color:var(--monster-bg-color-primary-4);color:var(--monster-color-primary-4)}.monster-color-secondary-4{background-color:var(--monster-bg-color-secondary-4);color:var(--monster-color-secondary-4)}.monster-color-tertiary-4{background-color:var(--monster-bg-color-tertiary-4);color:var(--monster-color-tertiary-4)}.monster-color-destructive-4{background-color:var(--monster-bg-color-destructive-4);color:var(--monster-color-destructive-4)}.monster-color-success-4{background-color:var(--monster-bg-color-success-4);color:var(--monster-color-success-4)}.monster-color-warning-4{background-color:var(--monster-bg-color-warning-4);color:var(--monster-color-warning-4)}.monster-color-error-4{background-color:var(--monster-bg-color-error-4);color:var(--monster-color-error-4)}.monster-color-selection-4{background-color:var(--monster-bg-color-selection-4);color:var(--monster-color-selection-4)}.monster-border-primary-1,.monster-border-primary-2,.monster-border-primary-3,.monster-border-primary-4{border-radius:var(--monster-border-radius);border-style:var(--monster-border-style);border-width:var(--monster-border-width);border-shadow:var(--monster-box-shadow-1)}.monster-border-0{border-radius:0;border-style:none;border-width:0;border-shadow:none}.monster-border-primary-1{border-color:var(--monster-bg-color-primary-1)}.monster-border-primary-2{border-color:var(--monster-bg-color-primary-2)}.monster-border-primary-3{border-color:var(--monster-bg-color-primary-3)}.monster-border-primary-4{border-color:var(--monster-bg-color-primary-4)}.monster-border-secondary-1,.monster-border-secondary-2,.monster-border-secondary-3,.monster-border-secondary-4{border-radius:var(--monster-border-radius);border-style:var(--monster-border-style);border-width:var(--monster-border-width);border-shadow:var(--monster-box-shadow-1)}.monster-border-secondary-1{border-color:var(--monster-bg-color-secondary-1)}.monster-border-secondary-2{border-color:var(--monster-bg-color-secondary-2)}.monster-border-secondary-3{border-color:var(--monster-bg-color-secondary-3)}.monster-border-secondary-4{border-color:var(--monster-bg-color-secondary-4)}.monster-border-tertiary-1,.monster-border-tertiary-2,.monster-border-tertiary-3,.monster-border-tertiary-4{border-radius:var(--monster-border-radius);border-style:var(--monster-border-style);border-width:var(--monster-border-width);border-shadow:var(--monster-box-shadow-1)}.monster-border-tertiary-1{border-color:var(--monster-bg-color-tertiary-1)}.monster-border-tertiary-2{border-color:var(--monster-bg-color-tertiary-2)}.monster-border-tertiary-3{border-color:var(--monster-bg-color-tertiary-3)}.monster-border-tertiary-4{border-color:var(--monster-bg-color-tertiary-4)}[data-monster-role=control]{box-sizing:border-box;font-family:inherit;font-size:100%;margin:0;outline:none;padding:.4rem .6rem}[data-monster-role=control]:focus{outline:1px dashed var(--monster-color-selection-3);outline-offset:2px}.switch{border-color:var(--monster-bg-color-primary-3);border-style:var(--monster-border-style);border-width:thin;display:inline-block;display:grid;grid-template-columns:1fr 1fr;height:34px;position:relative;transition:background-color .2s;width:60px}.switch-radio input[type=radio]{display:none}.label{display:block;height:34px;line-height:34px;text-align:center;-webkit-user-select:none;-moz-user-select:none;user-select:none}.switch-slider{background-color:var(--monster-bg-color-primary-4);bottom:4px;left:4px;position:absolute;right:28px;top:4px;transition:.2s}.switch[data-monster-state=on] .label.off{visibility:hidden}.switch[data-monster-state=off] .label.off,.switch[data-monster-state=on] .label.on{visibility:visible}.switch[data-monster-state=off] .label.on{visibility:hidden}.switch[data-monster-state=on] .switch-slider{transform:translateX(24px)} 
+}`, 0);
+} catch (e) {
+  addAttributeToken(document.getRootNode().querySelector('html'), ATTRIBUTE_ERRORMESSAGE, e + "");
+}
diff --git a/source/components/form/toggle-switch.mjs b/source/components/form/toggle-switch.mjs
new file mode 100644
index 000000000..aa2a4a90c
--- /dev/null
+++ b/source/components/form/toggle-switch.mjs
@@ -0,0 +1,427 @@
+/**
+ * Copyright schukai GmbH and contributors 2024. All Rights Reserved.
+ * Node module: @schukai/monster
+ * This file is licensed under the AGPLv3 License.
+ * License text available at https://www.gnu.org/licenses/agpl-3.0.en.html
+ */
+import { instanceSymbol } from "../../constants.mjs";
+import { internalSymbol } from "../../constants.mjs";
+import { CustomControl } from "../../dom/customcontrol.mjs";
+import { Observer } from "../../types/observer.mjs";
+import { ProxyObserver } from "../../types/proxyobserver.mjs";
+
+import { addAttributeToken } from "../../dom/attributes.mjs";
+import {
+    assembleMethodSymbol,
+    registerCustomElement,
+    updaterTransformerMethodsSymbol
+} from "../../dom/customelement.mjs";
+import {
+    isObject
+} from "../../types/is.mjs";
+import { ToggleSwitchStyleSheet } from "./stylesheet/toggle-switch.mjs";
+import {
+    ATTRIBUTE_ERRORMESSAGE,
+    ATTRIBUTE_ROLE,
+} from "../../dom/constants.mjs";
+export { ToggleSwitch };
+
+/**
+ * @private
+ * @type {symbol}
+ */
+const switchElementSymbol = Symbol("switchElement");
+
+/**
+ * @private
+ * @type {symbol}
+ */
+const switchElementSymbolOn = Symbol("switchElementOn");
+
+/**
+ * @private
+ * @type {symbol}
+ */
+const switchElementSymbolOff = Symbol("switchElementOff");
+
+/**
+ * @type {string}
+ */
+export const STATE_ON = 'on';
+
+/**
+ * @type {string}
+ */
+export const STATE_OFF = 'off';
+
+/**
+ * This CustomControl creates a ToggleSwitch element
+ *
+ * <img src="./images/switch.png">
+ * 
+ *
+ * @startuml toggleswitch.png
+ * skinparam monochrome true
+ * skinparam shadowing false
+ * HTMLElement <|-- CustomElement
+ * CustomElement <|-- CustomControl
+ * CustomControl <|-- ToggleSwitch
+ * @enduml
+ * 
+ * @since 3.57.0  
+ * @copyright schukai GmbH
+ * @memberOf Monster.Components.Form
+ * @summary A simple Switch
+ */
+class ToggleSwitch extends CustomControl {
+
+    /**
+     * This method is called by the `instanceof` operator.
+     * @returns {symbol}
+     * @since 2.1.0
+     */
+    static get [instanceSymbol]() {
+        return Symbol.for("@schukai/monster/components/form/toggle-switch@@instance");
+    }
+
+    static getTag() {
+        return "monster-toggle-switch";
+    }
+
+    /**
+     * 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 {string} value=current value of the element
+     * @property {Boolean} disabled=disabled=false Disabled state
+     * @property {Object} classes
+     * @property {string} classes.on=specifies the class for the on state.
+     * @property {string} classes.off=specifies the class for the off state.
+     * @property {Object} values
+     * @property {string} values.off=specifies the value of the element if it is not selected
+     * @property {Object} labels
+     * @property {string} labels.on=specifies the label for the on state.
+     * @property {string} labels.off=specifies the label for the off state.
+     * @property {Object} templates
+     * @property {string} templates.main=specifies the main template used by the control.
+     * 
+     * @since 3.57.0
+     */
+    get defaults() {
+        return Object.assign({}, super.defaults, {
+            value: null,
+            disabled: false,
+            classes: {
+                "on": "monster-theme-primary-3",
+                "off": "monster-theme-primary-2"
+            },
+            values: {
+                on: "on",
+                off: "off"
+            },
+            labels: {
+                "toggle-switch-on": "ON",
+                "toggle-switch-off": "OFF",
+            },
+            templates: {
+                main: getTemplate()
+            }
+        })
+    }
+
+    /**
+     * 
+     * @return {Monster.Components.Form.Button}
+     */
+    [assembleMethodSymbol]() {
+        const self = this;
+        super[assembleMethodSymbol]();
+        initControlReferences.call(this);
+        initEventHandler.call(this);
+
+        /**
+         * init value to off
+         * if the value was not defined before inserting it into the HTML
+         */
+        if (self.getOption("value") === null) {
+            self.setOption('value', self.getOption("values.off"));
+        }
+
+        /**
+         * value from attribute
+         */
+        if (self.hasAttribute("value")) {
+            self.setOption('value', self.getAttribute("value"));
+        }
+
+        /**
+         * validate value
+         */
+        validateAndSetValue.call(self);
+
+        if(this.state === STATE_ON) {
+            toggleClassOn.call(self);
+        }else{
+            toggleClassOff.call(self);
+        }
+
+        /**
+         * is called when options changed
+         */
+        self[internalSymbol].attachObserver(
+            new Observer(function () {
+                if (isObject(this) && this instanceof ProxyObserver) {
+                    validateAndSetValue.call(self);
+                    toggleClass.call(self);
+                }
+            }),
+        );
+
+        return this;
+    }
+
+    /**
+     * updater transformer methods for pipe
+     * 
+     * @return {function}
+     */
+    [updaterTransformerMethodsSymbol]() {
+        const self = this;
+        return {
+            "state-callback": (Wert) => {
+                return self.state;
+            }
+        }
+    }
+
+    /**
+     * @return [SwitchStyleSheet]
+     */
+    static getCSSStyleSheet() {
+        return [ToggleSwitchStyleSheet];
+    }
+
+    /**
+     * toggle switch
+     * 
+     * ```
+	 * e = document.querySelector('monster-toggle-switch');
+	 * e.click()
+	 * ```
+     */
+    click() {
+        toggleValues.call(this);
+    }
+
+    /**
+     * toggle switch on/off
+     * 
+     * ```
+	 * e = document.querySelector('monster-toggle-switch');
+	 * e.toggle()
+	 * ```
+     * 
+     * @return {ToggleSwitch}
+     */
+    toggle() {
+        this.click();
+        return this;
+    }
+
+    /**
+     * toggle switch on
+     * 
+     * ```
+	 * e = document.querySelector('monster-toggle-switch');
+	 * e.toggleOn()
+	 * ```
+     * 
+     * @return {ToggleSwitch}
+     */
+    toggleOn() {
+        this.setOption('value', this.getOption('values.on'));
+        return this;
+    };
+
+    /**
+     * toggle switch off
+     * 
+     * ```
+	 * e = document.querySelector('monster-toggle-switch');
+	 * e.toggleOff()
+	 * ```
+     * 
+     * @return {ToggleSwitch}
+     */
+    toggleOff() {
+        this.setOption('value', this.getOption('values.off'));
+        return this;
+    };
+
+    /**
+     * returns the status of the element
+     * 
+     * ```
+	 * e = document.querySelector('monster-toggle-switch');
+	 * console.log(e.state)
+	 * // ↦ off
+	 * ```
+     * 
+     * @return {string}
+     */
+    get state() {
+        return this.getOption('value') === this.getOption('values.on') ? STATE_ON : STATE_OFF;
+    }
+
+    /**
+	 * The current value of the Switch
+	 *
+	 * ```
+	 * e = document.querySelector('monster-toggle-switch');
+	 * console.log(e.value)
+	 * // ↦ on
+	 * ```
+	 *
+	 * @return {string}
+	 */
+    get value() {
+        return this.state === STATE_ON ? this.getOption('values.on') : this.getOption('values.off');
+    }
+
+    /**
+	 * Set value
+	 *
+	 * ```
+	 * e = document.querySelector('monster-toggle-switch');
+	 * e.value="on"
+	 * ```
+	 *
+	 * @property {string} value
+	 */
+    set value(value) {
+        this.setOption('value', value);
+    }
+
+}
+
+/**
+ * @private
+ */
+function initControlReferences() {
+    this[switchElementSymbol] = this.shadowRoot.querySelector(
+        `[${ATTRIBUTE_ROLE}=switch]`,
+    );
+}
+
+/**
+* @private
+*/
+function toggleClassOn() {
+    this[switchElementSymbol].classList.remove(this.getOption('classes.off')); // change color
+    this[switchElementSymbol].classList.add(this.getOption('classes.on'));// change color
+}
+
+/**
+* @private
+*/
+function toggleClassOff() {
+    this[switchElementSymbol].classList.remove(this.getOption('classes.on'));// change color
+    this[switchElementSymbol].classList.add(this.getOption('classes.off'));// change color
+}
+
+/**
+* @private
+*/
+function toggleClass() {
+    const self = this;
+    if (self.getOption('value') === self.getOption('values.on')) {
+        toggleClassOn.call(self);
+    } else {
+        toggleClassOff.call(self);
+    }
+}
+
+/**
+ * @private
+ */
+function toggleValues() {
+    const self = this;
+
+    if (self.getOption('disabled') === true) {
+        return;
+    }
+
+    if (self.getOption('value') === self.getOption('values.on')) {
+        self.setOption('value', this.getOption('values.off'));
+        self?.setFormValue(self.getOption('value')); // set form value
+    } else {
+        self.setOption('value', this.getOption('values.on'));
+        self?.setFormValue(self.getOption('values.off')); // set form value
+    }
+
+    self.setOption('state', self.state);
+}
+
+/**
+* @private
+*/
+function validateAndSetValue() {
+    let self = this;
+    let value = self.getOption('value');
+
+    let validatedValues = [];
+    validatedValues.push(this.getOption('values.on'));
+    validatedValues.push(this.getOption('values.off'));
+
+    if (validatedValues.includes(value) === false) {
+        addAttributeToken(
+            this,
+            ATTRIBUTE_ERRORMESSAGE,
+            'The value "' + value + '" must be "' + self.getOption("values.on") + '" or "' + self.getOption("values.off"),
+        );
+        self.setOption('disabled', true);
+        self.formDisabledCallback(true);
+    } else {
+        self.setOption('disabled', false);
+        self.formDisabledCallback(false);
+    }
+}
+
+/**
+ * @private
+ * @return {initEventHandler}
+ */
+function initEventHandler() {
+    const self = this;
+    self.addEventListener("keyup", function (event) {
+        if (event.code === 'Space') {
+            self[switchElementSymbol].click();
+        }
+    });
+    self.addEventListener("click", function (event) {
+        toggleValues.call(self);
+    });
+    return this;
+}
+
+/**
+ * @private
+ * @return {string}
+ */
+function getTemplate() {
+    // language=HTML
+    return `
+        <div data-monster-role="control" part="control" tabindex="0">
+     
+            <div class="switch" data-monster-role="switch" data-monster-attributes="data-monster-state path:value | call:state-callback " >
+                <div class="label on" data-monster-replace="path:labels.toggle-switch-on"></div>
+                <div class="label off" data-monster-replace="path:labels.toggle-switch-off"></div>
+                <div class="switch-slider"></div>
+            </div>
+
+        </div>`;
+}
+
+registerCustomElement(ToggleSwitch);
\ No newline at end of file
diff --git a/test/cases/components/form/toggle-switch.mjs b/test/cases/components/form/toggle-switch.mjs
new file mode 100644
index 000000000..bcd509230
--- /dev/null
+++ b/test/cases/components/form/toggle-switch.mjs
@@ -0,0 +1,310 @@
+import { getGlobal } from "../../../../source/types/global.mjs";
+import chai from "chai"
+import { chaiDom } from "../../../util/chai-dom.mjs";
+import { initJSDOM } from "../../../util/jsdom.mjs";
+
+let expect = chai.expect;
+chai.use(chaiDom);
+
+const global = getGlobal();
+
+let html1 = `
+    <div id="test1">
+    </div>
+`;
+
+let html2 = `
+    <div id="test2">
+         <monster-toggle-switch></monster-toggle-switch>
+    </div>
+`;
+
+let ToggleSwitch;
+
+describe('ToggleSwitch', function () {
+
+    before(function (done) {
+        initJSDOM().then(() => {
+
+            import("element-internals-polyfill").catch(e => done(e));
+
+            import("../../../../source/components/form/toggle-switch.mjs").then((m) => {
+                ToggleSwitch = m['ToggleSwitch'];
+                done()
+            }).catch(e => done(e))
+
+
+        });
+    })
+
+    describe('new ToggleSwitch', function () {
+
+        beforeEach(() => {
+            let mocks = document.getElementById('mocks');
+            mocks.innerHTML = html1;
+        })
+
+        afterEach(() => {
+            let mocks = document.getElementById('mocks');
+            mocks.innerHTML = "";
+        })
+
+        describe('create from template', function () {
+            beforeEach(() => {
+                let mocks = document.getElementById('mocks');
+                mocks.innerHTML = html2;
+            });
+
+            afterEach(() => {
+                let mocks = document.getElementById('mocks');
+                mocks.innerHTML = "";
+
+            })
+
+            describe('create from template', function () {
+                it('should contains monster-toggle-switch', function () {
+                    expect(document.getElementById('test2')).contain.html('<monster-toggle-switch');
+                });
+            });
+
+        });
+
+        describe('document.createElement', function () {
+            it('should instance of monster-toggle-switch', function () {
+                expect(document.createElement('monster-toggle-switch')).is.instanceof(ToggleSwitch);
+            });
+        });
+
+    });
+
+    describe('toggle', function () {
+        beforeEach(() => {
+            let mocks = document.getElementById('mocks');
+            mocks.innerHTML = html1;
+        });
+
+        afterEach(() => {
+            let mocks = document.getElementById('mocks');
+            mocks.innerHTML = "";
+        })
+
+        it('toggle to on', function () {
+
+            const toggleSwitch = document.createElement('monster-toggle-switch');
+
+            expect(toggleSwitch.value).is.equal('off');
+            expect(toggleSwitch.state).is.equal('off');
+
+            toggleSwitch.toggle();
+
+            expect(toggleSwitch.value).is.equal('on');
+            expect(toggleSwitch.state).is.equal('on');
+
+            toggleSwitch.toggle();
+
+            expect(toggleSwitch.value).is.equal('off');
+            expect(toggleSwitch.state).is.equal('off');
+        });
+
+        it('toggle on to off', function () {
+
+            const toggleSwitch = document.createElement('monster-toggle-switch');
+
+            toggleSwitch.toggleOn();
+
+            expect(toggleSwitch.value).is.equal('on');
+            expect(toggleSwitch.state).is.equal('on');
+
+            toggleSwitch.toggleOff();
+
+            expect(toggleSwitch.value).is.equal('off');
+            expect(toggleSwitch.state).is.equal('off');
+
+        });
+
+
+    });
+
+    describe('describe css', function () {
+
+        beforeEach(() => {
+            let mocks = document.getElementById('mocks');
+            mocks.innerHTML = html1;
+        });
+
+        afterEach(() => {
+            let mocks = document.getElementById('mocks');
+            mocks.innerHTML = "";
+        })
+
+        it('css toggle', function (done) {
+
+            /**
+             * new Control
+             */
+            const toggleSwitch = document.createElement('monster-toggle-switch');
+
+            /**
+             * set init value to on
+             */
+            toggleSwitch.value = "on";
+
+            /**
+             * insert DOM
+             */
+            document.getElementById('mocks').appendChild(toggleSwitch);
+
+            /**
+             * expect that classes.on is set to Element Switch
+             */
+            let hasClassA = toggleSwitch.shadowRoot.querySelectorAll('[data-monster-role="switch"]')[0].classList.contains(toggleSwitch.getOption('classes.on'));
+            expect(hasClassA).is.true;
+
+            /**
+            * switch off
+            */
+            toggleSwitch.value = "off";
+
+            /**
+             * Updater prozess runs in setTimeout
+             * self[internalSymbol].attachObserver();
+             */
+            setTimeout(() => {
+                
+                /**
+                * expect that classes.on is removed from Element Switch
+                */
+                let hasClassB  = toggleSwitch.shadowRoot.querySelectorAll('[data-monster-role="switch"]')[0].classList.contains(toggleSwitch.getOption('classes.on'));
+                expect(hasClassB).is.false;
+
+                /**
+                * expect that classes.off is set to Element Switch
+                */
+                let hasClassC  = toggleSwitch.shadowRoot.querySelectorAll('[data-monster-role="switch"]')[0].classList.contains(toggleSwitch.getOption('classes.off'));
+                expect(hasClassC).is.true;    
+
+
+                done();
+            }, 0);
+
+        })
+
+    });
+
+    describe('describe value', function () {
+
+        beforeEach(() => {
+            let mocks = document.getElementById('mocks');
+            mocks.innerHTML = html1;
+        });
+
+        afterEach(() => {
+            let mocks = document.getElementById('mocks');
+            mocks.innerHTML = "";
+
+        })
+
+        it('the default value is off', function () {
+
+            /**
+             * new Control
+             */
+            let toggleSwitch = document.createElement('monster-toggle-switch');
+
+            /**
+             * the switch is off and provides the value for off
+             */
+            expect(toggleSwitch.value).is.equal('off');
+
+            /**
+             * the switch is off
+             */
+            expect(toggleSwitch.state).is.equal('off');
+
+        });
+
+        it('incorrect values are not accepted', function () {
+
+            let toggleSwitch = document.createElement('monster-toggle-switch');
+
+            /**
+             * define the values for on and off
+             */
+            toggleSwitch.setOption('values.on', 'true');
+            toggleSwitch.setOption('values.off', 'false');
+
+            /**
+             * This value is not "true" and not "false"
+             */
+            toggleSwitch.value = "test";
+
+            /**
+             * the switch is off and provides the value for off
+             */
+            expect(toggleSwitch.value).is.equal('false');
+
+            /**
+             * the switch is off
+             */
+            expect(toggleSwitch.state).is.equal('off');
+
+            /**
+            * disabled attribute is only set when the element has been mounted in the DOM
+            */
+            expect(toggleSwitch.hasAttribute('disabled')).is.false;
+
+            /**
+             * insert DOM
+             */
+            document.getElementById('mocks').appendChild(toggleSwitch);
+
+            /**
+            * now the element is disabled
+            */
+            expect(toggleSwitch.hasAttribute('disabled')).is.true;
+
+
+        });
+
+        it('correct values are accepted', function () {
+
+            const toggleSwitch = document.createElement('monster-toggle-switch');
+
+            /**
+             * define the values for on and off
+             */
+            toggleSwitch.setOption('values.on', 'true');
+            toggleSwitch.setOption('values.off', 'false');
+
+            /**
+             * This value is correct
+             */
+            toggleSwitch.value = "true";
+
+            /**
+             * the switch is on and provides the value for on
+             */
+            expect(toggleSwitch.value).is.equal('true');
+
+            /**
+             * the switch is on
+             */
+            expect(toggleSwitch.state).is.equal('on');
+
+            /**
+            * insert DOM
+            */
+            document.getElementById('mocks').appendChild(toggleSwitch);
+
+            /**
+             * disabled attribute is not set
+             */
+            expect(toggleSwitch.hasAttribute('disabled')).is.false;
+
+        });
+
+    });
+
+
+
+});
\ No newline at end of file
-- 
GitLab