From bd266eca8213587d4cbaa8a7e5edc42cb269859e Mon Sep 17 00:00:00 2001
From: Volker Schukai <volker.schukai@schukai.com>
Date: Fri, 27 Dec 2024 12:48:17 +0100
Subject: [PATCH] fix: datatable, datasource optimisation #272

---
 development/issues/open/226.html              |  92 +-
 development/issues/open/272.html              | 179 ++++
 development/issues/open/272.mjs               |  42 +
 development/mock/issue-272.js                 | 289 ++++++
 source/components/datatable/dataset.mjs       | 632 ++++++------
 source/components/datatable/datasource.mjs    |  38 +-
 .../components/datatable/datasource/dom.mjs   |  38 +-
 .../components/datatable/datasource/rest.mjs  | 930 +++++++++---------
 .../datatable/embedded-pagination.mjs         |  11 +
 source/components/datatable/pagination.mjs    | 866 ++++++++--------
 source/components/datatable/status.mjs        |   2 +-
 .../datatable/style/pagination.pcss           |   4 +-
 .../datatable/stylesheet/pagination.mjs       |  21 +-
 source/components/datatable/util.mjs          |   1 +
 source/components/host/config-manager.mjs     |   4 +-
 source/data/datasource/server.mjs             |   1 +
 source/data/datasource/server/restapi.mjs     |  39 +-
 source/dom/customelement.mjs                  |  34 +
 source/types/has.mjs                          |  29 +
 19 files changed, 1938 insertions(+), 1314 deletions(-)
 create mode 100644 development/issues/open/272.html
 create mode 100644 development/issues/open/272.mjs
 create mode 100644 development/mock/issue-272.js
 create mode 100644 source/types/has.mjs

diff --git a/development/issues/open/226.html b/development/issues/open/226.html
index 9b4547168..83878821a 100644
--- a/development/issues/open/226.html
+++ b/development/issues/open/226.html
@@ -5,93 +5,13 @@
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>Datatable Pagination um Max Count erweitern #226</title>
     <script src="./226.mjs" type="module"></script>
-    
-    <style>
-
-        :not(:defined) {
-            display: none;
-        }
-        
-        .slide {
-            display: flex;
-            justify-content: center;
-            align-items: center;
-            width: 100%;
-            height: 100%;
-        }
-        
-        
-        
-          main {
-              display: flex;
-              justify-content: center;
-              align-items: center;
-              width: 100%;
-              height: 100%;
-          }
-          
-            monster-slider::part(prev) {
-            }
-            
-            monster-slider::part(control) {
-                
-            }
-            
-            .container {
-                padding: 10px;
-                width: 1200px;
-                height: 600px;
-                background-color: #cccccc;
-                box-sizing: border-box;
-                overflow: hidden;
-            }
-        
-    </style>
 </head>
 <body>
-    <h1>Datatable Pagination um Max Count erweitern #226</h1>
-    <p>user aborted</p>
-    <ul>
-        <li><a href="https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/226">Issue #226</a></li>
-        <li><a href="/">Back to overview</a></li>
-    </ul>
-    <main>
-        
-        <div class="container">
-          <monster-slider>
-              <div slot="prev"><svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" fill="currentColor" class="bi bi-arrow-left-square-fill" viewBox="0 0 16 16">
-                      <path d="M16 14a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2zm-4.5-6.5H5.707l2.147-2.146a.5.5 0 1 0-.708-.708l-3 3a.5.5 0 0 0 0 .708l3 3a.5.5 0 0 0 .708-.708L5.707 8.5H11.5a.5.5 0 0 0 0-1"/>
-                  </svg></div>
-              <div class="slide" style="display:none;background-color: #ff6666;width:50px">
-                  <h1>SLIDE 1</h1>
-              </div>
-              <div class="slide" style="background-color: #ff6666;width:50px">
-                  <h1>SLIDE 1 a</h1>
-              </div>
-              <div class="slide" style="background-color: #66ff66;width:50px;"><svg xmlns="http://www.w3.org/2000/svg"
-                                                                         width="467" height="462">
-                      <rect x="80" y="60" width="250" height="250" rx="20"
-                            style="fill:#ff0000; stroke:#000000;stroke-width:2px;" />
-
-                      <rect x="140" y="120" width="250" height="250" rx="40"
-                            style="fill:#0000ff; stroke:#000000; stroke-width:2px;
-      fill-opacity:0.7;" />
-                  </svg></div>
-              <div class="slide" style="background-color: #6666ff;width:50px;">
-                  
-                  <h1>SLIDE 3</h1>
-                  
-              </div>
-              <div slot="next">
-
-                  <svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" fill="currentColor" class="bi bi-arrow-right-square-fill" viewBox="0 0 16 16">
-                      <path d="M0 14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2a2 2 0 0 0-2 2zm4.5-6.5h5.793L8.146 5.354a.5.5 0 1 1 .708-.708l3 3a.5.5 0 0 1 0 .708l-3 3a.5.5 0 0 1-.708-.708L10.293 8.5H4.5a.5.5 0 0 1 0-1"/>
-                  </svg>
-                  
-              </div>
-          </monster-slider>
-        </div>
-
-    </main>
+<h1>Datatable Pagination um Max Count erweitern #226</h1>
+<ul>
+    <li><a href="https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/226">Issue #226</a></li>
+    <li><a href="/">Back to overview</a></li>
+</ul>
+<main></main>
 </body>
 </html>
diff --git a/development/issues/open/272.html b/development/issues/open/272.html
new file mode 100644
index 000000000..2fe8e354d
--- /dev/null
+++ b/development/issues/open/272.html
@@ -0,0 +1,179 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>check and update data save button #272</title>
+    <script src="./272.mjs" type="module"></script>
+</head>
+<body>
+<h1>check and update pagination and dataset #272</h1>
+<p></p>
+<ul>
+    <li><a href="https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/272">Issue #272</a></li>
+    <li><a href="/">Back to overview</a></li>
+</ul>
+<main>
+
+    <monster-datasource-rest id="ds272-r"
+                             data-monster-option-features-autoInit="true"
+                             data-monster-option-write-url="/issue-272.json"
+                             data-monster-option-write-acceptedstatus="400::200"
+                             data-monster-option-read-url="/issue-272.json?limit=1&page=${page}"
+                             data-x-monster-option-read-mapping-transformer="path:dataset"
+    ></monster-datasource-rest>
+
+
+    <monster-datasource-dom id="ds272-d">
+        <script type="application/json">
+            [
+                {
+                    "id": 1,
+                    "username": "martin89",
+                    "email": "elena.richards@domain.com",
+                    "full_name": "Elena Richards",
+                    "age": 29,
+                    "country": "Greece",
+                    "registered_date": "2019-11-23",
+                    "status": "active"
+                },
+                {
+                    "id": 2,
+                    "username": "sophiabell",
+                    "email": "thomas.cook@gmail.com",
+                    "full_name": "Thomas Cook",
+                    "age": 22,
+                    "country": "Portugal",
+                    "registered_date": "2022-04-15",
+                    "status": "banned"
+                },
+                {
+                    "id": 3,
+                    "username": "brianm",
+                    "email": "chloe.adams@webmail.com",
+                    "full_name": "Chloe Adams",
+                    "age": 37,
+                    "country": "New Zealand",
+                    "registered_date": "2021-06-01",
+                    "status": "inactive"
+                },
+                {
+                    "id": 4,
+                    "username": "larad",
+                    "email": "alejandro.fuentes@yahoo.com",
+                    "full_name": "Alejandro Fuentes",
+                    "age": 45,
+                    "country": "Japan",
+                    "registered_date": "2024-01-20",
+                    "status": "banned"
+                }
+            ]
+
+
+        </script>
+
+    </monster-datasource-dom>
+
+    <monster-button-bar id="bb-r">
+        <monster-button>#1</monster-button>
+        <monster-button>#2</monster-button>
+        <monster-button>#3</monster-button>
+        <monster-button>#4</monster-button>
+    </monster-button-bar>
+
+    <monster-pagination
+            data-monster-option-datasource-selector="#ds272-r"
+            data-monster-option-objectsperpage="1"
+    ></monster-pagination>
+
+    <hr>
+    <monster-dataset
+                      id="ds-272"
+                     data-monster-option-datasource-selector="#ds272-r">
+
+        <div style="display: grid; grid-template-columns: 1fr 1fr;gap: 0.1rem;width: 600px">
+            <div>ID</div>
+            <div data-monster-replace="path:data.id"></div>
+            <div>Username</div>
+            <div data-monster-replace="path:data.username"></div>
+            <div>Full Name</div>
+            <div data-monster-replace="path:data.full_name"></div>
+            <div>Email</div>
+            <div data-monster-replace="path:data.email"></div>
+            <div>Age</div>
+            <div data-monster-replace="path:data.age"></div>
+            <div>Country</div>
+            <div data-monster-replace="path:data.country"></div>
+            <div>Registered Date</div>
+            <div data-monster-replace="path:data.registered_date"></div>
+            <div>Status</div>
+            <div data-monster-replace="path:data.status"></div>
+        </div>
+
+    </monster-dataset>
+
+
+    <monster-pagination
+            data-monster-option-datasource-selector="#ds272-r"
+            data-monster-option-objectsperpage="1"
+    ></monster-pagination>
+
+
+    <hr>
+    <hr>
+    <hr>
+    <hr>
+
+    <monster-pagination
+            data-monster-option-datasource-selector="#ds272-d"
+            data-monster-option-objectsperpage="1"
+    ></monster-pagination>
+
+
+    <monster-button-bar id="bb-d">
+        <monster-button>#1</monster-button>
+        <monster-button>#2</monster-button>
+        <monster-button>#3</monster-button>
+        <monster-button>#4</monster-button>
+    </monster-button-bar>
+
+    <hr>
+    <monster-dataset
+                     data-monster-option-mapping-data="" id="ds-272"
+                     data-monster-option-datasource-selector="#ds272-d">
+
+        <div style="display: grid; grid-template-columns: 1fr 1fr;gap: 0.1rem;width: 600px">
+            <div>ID</div>
+            <div data-monster-replace="path:data.id"></div>
+            <div>Username</div>
+            <div data-monster-replace="path:data.username"></div>
+            <div>Full Name</div>
+            <div data-monster-replace="path:data.full_name"></div>
+            <div>Email</div>
+            <div data-monster-replace="path:data.email"></div>
+            <div>Age</div>
+            <div data-monster-replace="path:data.age"></div>
+            <div>Country</div>
+            <div data-monster-replace="path:data.country"></div>
+            <div>Registered Date</div>
+            <div data-monster-replace="path:data.registered_date"></div>
+            <div>Status</div>
+            <div data-monster-replace="path:data.status"></div>
+        </div>
+
+    </monster-dataset>
+
+
+    <monster-pagination
+            data-monster-option-datasource-selector="#ds272-d"
+            data-monster-option-objectsperpage="1"
+    ></monster-pagination>
+
+    <hr>
+    <hr>
+    <hr>
+    <hr>
+
+</main>
+</body>
+</html>
diff --git a/development/issues/open/272.mjs b/development/issues/open/272.mjs
new file mode 100644
index 000000000..6a14260be
--- /dev/null
+++ b/development/issues/open/272.mjs
@@ -0,0 +1,42 @@
+/**
+* @file development/issues/open/272.mjs
+* @url https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/272
+* @description check and update data save button
+* @issue 272
+*/
+
+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/datatable/datasource/rest.mjs";
+import "../../../source/components/datatable/datasource/dom.mjs";
+import "../../../source/components/datatable/dataset.mjs";
+import "../../../source/components/datatable/pagination.mjs";
+import "../../../source/components/form/button.mjs";
+import "../../../source/components/form/button-bar.mjs";
+
+// DOM-Elemente für die Seiten-Datensätze
+const pageData1 = document.getElementById("ds272-d");
+const pageData2 = document.getElementById("ds272-r");
+
+// Allgemeine Funktion zur Einstellung der Seitennummer
+function setPage(dataElement, pageNum) {
+    dataElement.setParameters({page: pageNum});
+}
+
+// Initialisierung der Buttons und Event-Handler für die erste Datenquelle
+const buttonBar1 = document.getElementById("bb-d");
+['first', 'second', 'third', 'fourth'].forEach((label, index) => {
+    const button = buttonBar1.children[index];
+    button.setOption("actions.click", () => setPage(pageData1, index + 1));
+});
+
+// Initialisierung der Buttons und Event-Handler für die zweite Datenquelle
+const buttonBar2 = document.getElementById("bb-r");
+['first', 'second', 'third', 'fourth'].forEach((label, index) => {
+    const button = buttonBar2.children[index];
+    button.setOption("actions.click", () => setPage(pageData2, index + 1));
+});
diff --git a/development/mock/issue-272.js b/development/mock/issue-272.js
new file mode 100644
index 000000000..2c8acffdd
--- /dev/null
+++ b/development/mock/issue-272.js
@@ -0,0 +1,289 @@
+const json =
+    `[{
+      "id": 1,
+      "username": "wernerjennifer",
+      "email": "smithchristina@clark.biz",
+      "full_name": "Joshua Smith",
+      "age": 28,
+      "country": "Central African Republic",
+      "registered_date": "2020-01-17",
+      "status": "active"
+    },
+    {
+      "id": 2,
+      "username": "elizabethmurphy",
+      "email": "beverlywarren@gmail.com",
+      "full_name": "Sarah Snyder",
+      "age": 18,
+      "country": "Tajikistan",
+      "registered_date": "2023-05-10",
+      "status": "banned"
+    },
+    {
+      "id": 3,
+      "username": "ftaylor",
+      "email": "deckercassandra@conrad.com",
+      "full_name": "Rhonda Carroll",
+      "age": 34,
+      "country": "Australia",
+      "registered_date": "2022-07-31",
+      "status": "inactive"
+    },
+    {
+      "id": 4,
+      "username": "antoniocastillo",
+      "email": "tevans@hotmail.com",
+      "full_name": "Brad Brown",
+      "age": 39,
+      "country": "Nepal",
+      "registered_date": "2023-01-09",
+      "status": "banned"
+    },
+    {
+      "id": 5,
+      "username": "dylan46",
+      "email": "audreywalter@watts-conley.com",
+      "full_name": "Jacqueline Turner",
+      "age": 26,
+      "country": "United States Minor Outlying Islands",
+      "registered_date": "2024-10-14",
+      "status": "inactive"
+    },
+    {
+      "id": 6,
+      "username": "michael27",
+      "email": "thompsonsydney@gmail.com",
+      "full_name": "Stephanie Walls",
+      "age": 25,
+      "country": "Switzerland",
+      "registered_date": "2020-05-20",
+      "status": "inactive"
+    },
+    {
+      "id": 7,
+      "username": "vickie92",
+      "email": "mary35@jackson.com",
+      "full_name": "Colin Cohen",
+      "age": 23,
+      "country": "Saint Martin",
+      "registered_date": "2022-12-31",
+      "status": "active"
+    },
+    {
+      "id": 8,
+      "username": "fordomar",
+      "email": "hernandezbrian@gmail.com",
+      "full_name": "Christopher Garza",
+      "age": 65,
+      "country": "Antigua and Barbuda",
+      "registered_date": "2022-08-15",
+      "status": "banned"
+    },
+    {
+      "id": 9,
+      "username": "jerry82",
+      "email": "nwhite@yahoo.com",
+      "full_name": "Gabrielle Garza",
+      "age": 46,
+      "country": "Mauritania",
+      "registered_date": "2023-03-08",
+      "status": "inactive"
+    },
+    {
+      "id": 10,
+      "username": "alyssa54",
+      "email": "kevin11@hotmail.com",
+      "full_name": "Andrea Williams",
+      "age": 43,
+      "country": "Western Sahara",
+      "registered_date": "2023-04-19",
+      "status": "active"
+    },
+    {
+      "id": 11,
+      "username": "jacob30",
+      "email": "scottmary@yahoo.com",
+      "full_name": "Nicole Cunningham",
+      "age": 29,
+      "country": "French Polynesia",
+      "registered_date": "2022-02-21",
+      "status": "banned"
+    },
+    {
+      "id": 12,
+      "username": "ftucker",
+      "email": "simmonsrichard@jones.com",
+      "full_name": "Daniel Shelton",
+      "age": 42,
+      "country": "Cameroon",
+      "registered_date": "2024-04-20",
+      "status": "active"
+    },
+    {
+      "id": 13,
+      "username": "alvarezpaul",
+      "email": "ucannon@ortega.biz",
+      "full_name": "Matthew Poole",
+      "age": 44,
+      "country": "Chad",
+      "registered_date": "2021-09-22",
+      "status": "inactive"
+    },
+    {
+      "id": 14,
+      "username": "sarahunter",
+      "email": "allisoncharles@phillips-graves.com",
+      "full_name": "Nathan Fernandez",
+      "age": 40,
+      "country": "French Polynesia",
+      "registered_date": "2023-04-11",
+      "status": "inactive"
+    },
+    {
+      "id": 15,
+      "username": "sierra65",
+      "email": "hnicholson@gmail.com",
+      "full_name": "Michael Taylor",
+      "age": 27,
+      "country": "Indonesia",
+      "registered_date": "2021-08-15",
+      "status": "active"
+    },
+    {
+      "id": 16,
+      "username": "benjamin84",
+      "email": "zgallegos@hale-johnson.com",
+      "full_name": "Molly Santana",
+      "age": 32,
+      "country": "Holy See (Vatican City State)",
+      "registered_date": "2023-06-09",
+      "status": "inactive"
+    },
+    {
+      "id": 17,
+      "username": "peckpaul",
+      "email": "james03@hotmail.com",
+      "full_name": "Kelly Allen",
+      "age": 42,
+      "country": "Austria",
+      "registered_date": "2021-04-26",
+      "status": "active"
+    },
+    {
+      "id": 18,
+      "username": "jill42",
+      "email": "williamstephenson@hotmail.com",
+      "full_name": "Samuel Adkins",
+      "age": 22,
+      "country": "American Samoa",
+      "registered_date": "2024-10-04",
+      "status": "active"
+    },
+    {
+      "id": 19,
+      "username": "pottssavannah",
+      "email": "emily76@mckinney.com",
+      "full_name": "Jeremy Pearson",
+      "age": 42,
+      "country": "Lesotho",
+      "registered_date": "2020-06-15",
+      "status": "inactive"
+    },
+    {
+      "id": 20,
+      "username": "robertsdaniel",
+      "email": "robinsontodd@pineda.org",
+      "full_name": "Francis Castaneda",
+      "age": 58,
+      "country": "British Indian Ocean Territory (Chagos Archipelago)",
+      "registered_date": "2020-03-01",
+      "status": "inactive"
+    }
+  ]
+`;
+
+
+// check if JSON is valid
+const objects = JSON.parse(json)
+if (!Array.isArray(objects)) {
+    throw new Error('Invalid JSON')
+}
+
+const requestDelay = 10
+
+function wrapJsonWithPagination(data, page, limit) {
+    return JSON.stringify({
+        dataset: data,
+        sys: {
+            pagination: {
+                objectsPerPage: limit,
+                pages: Math.ceil(objects.length / limit),
+                currentPage: page
+            }
+        }
+    })
+}
+
+export default [
+    {
+        url: '/issue-272.json',
+        method: 'get',
+        rawResponse: async (req, res) => {
+            res.setHeader('Content-Type', 'application/json')
+            res.statusCode = 200
+
+            const url = new URL(req.url, `http://${req.headers.host}`)
+            const q = Object.fromEntries(url.searchParams)
+
+            let filtered = objects
+
+            if (q) {
+
+                let limit = 20
+
+                if (q.limit) {
+                    limit = parseInt(q.limit)
+                }
+
+                if (q.page) {
+                    const page = parseInt(q.page)
+                    const start = (page - 1) * limit
+                    const end = start + limit
+                    filtered = objects.slice(start, end)
+                } else {
+                    filtered = objects.slice(0, limit)
+                }
+
+                if (q.q) {
+                    const query = q.q.toLowerCase()
+                    filtered = objects.filter(item => item.name.toLowerCase().includes(query))
+
+                }
+
+                setTimeout(function () {
+                    res.end(wrapJsonWithPagination(filtered, q.page || 1, limit))
+                }, requestDelay);
+
+                return
+            }
+
+            setTimeout(function () {
+                res.end(wrapJsonWithPagination(filtered, 1, 20))
+            }, requestDelay);
+
+        },
+    },
+    {
+        url: '/issue-272.json',
+        method: 'post',
+        rawResponse: async (req, res) => {
+            res.setHeader('Content-Type', 'application/json')
+            res.statusCode = 200
+
+            const jsonRespond = JSON.stringify({message: "Data has been successfully saved"})
+
+            res.end(jsonRespond)
+        }
+    }
+
+];
\ No newline at end of file
diff --git a/source/components/datatable/dataset.mjs b/source/components/datatable/dataset.mjs
index 97b8dc2ec..015d41f7b 100644
--- a/source/components/datatable/dataset.mjs
+++ b/source/components/datatable/dataset.mjs
@@ -12,290 +12,312 @@
  * SPDX-License-Identifier: AGPL-3.0
  */
 
-import { instanceSymbol, internalSymbol } from "../../constants.mjs";
-import { Pathfinder } from "../../data/pathfinder.mjs";
-import { getLinkedObjects, hasObjectLink } from "../../dom/attributes.mjs";
-import { customElementUpdaterLinkSymbol } from "../../dom/constants.mjs";
+import {instanceSymbol} from "../../constants.mjs";
+import {Pathfinder} from "../../data/pathfinder.mjs";
 import {
-	assembleMethodSymbol,
-	CustomElement,
-	attributeObserverSymbol,
-	registerCustomElement,
+    assembleMethodSymbol,
+    CustomElement,
+    attributeObserverSymbol,
+    registerCustomElement,
 } from "../../dom/customelement.mjs";
-import { findElementWithSelectorUpwards } from "../../dom/util.mjs";
-import { isString } from "../../types/is.mjs";
-import { Observer } from "../../types/observer.mjs";
-import { clone } from "../../util/clone.mjs";
+import {findElementWithSelectorUpwards} from "../../dom/util.mjs";
+import {isString} from "../../types/is.mjs";
+import {Observer} from "../../types/observer.mjs";
 import {
-	ATTRIBUTE_DATASOURCE_SELECTOR,
-	ATTRIBUTE_DATATABLE_INDEX,
+    ATTRIBUTE_DATASOURCE_SELECTOR,
+    ATTRIBUTE_DATATABLE_INDEX,
 } from "./constants.mjs";
-import { Datasource } from "./datasource.mjs";
-import { DatasetStyleSheet } from "./stylesheet/dataset.mjs";
+import {Datasource} from "./datasource.mjs";
+import {DatasetStyleSheet} from "./stylesheet/dataset.mjs";
 import {
-	handleDataSourceChanges,
-	datasourceLinkedElementSymbol,
+    handleDataSourceChanges,
+    datasourceLinkedElementSymbol,
 } from "./util.mjs";
-import { FormStyleSheet } from "../stylesheet/form.mjs";
+import {FormStyleSheet} from "../stylesheet/form.mjs";
 
-export { DataSet };
+export {DataSet};
 
 /**
- * The data set component is used to show the data of a data source.
+ * A data set component
  *
- * <img src="./images/dataset.png">
+ * @fragments /fragments/components/datatable/dataset
  *
- * You can create this control either by specifying the HTML tag <monster-dataset />` directly in the HTML or using
- * Javascript via the `document.createElement('monster-dataset');` method.
+ * @example /examples/components/datatable/dataset-simple
+ * @example /examples/components/datatable/dataset-rest
  *
- * ```html
- * <monster-dataset></monster-dataset>
- * ```
- *
- * Or you can create this CustomControl directly in Javascript:
- *
- * ```js
- * import '@schukai/component-datatable/source/dataset.mjs';
- * document.createElement('monster-dataset');
- * ```
- *
- * The Body should have a class "hidden" to ensure that the styles are applied correctly.
- *
- * ```css
- * body.hidden {
- *    visibility: hidden;
- * }
- * ```
- *
- * @startuml dataset.png
- * skinparam monochrome true
- * skinparam shadowing false
- * HTMLElement <|-- CustomElement
- * CustomElement <|-- DataSet
- * @enduml
+ * @issue https://localhost.alvine.dev:8443/development/issues/closed/272.html
  *
  * @copyright schukai GmbH
- * @summary A data set
+ * @summary A dataset component that can be used to show the data of a data source
  */
 class DataSet extends CustomElement {
-	/**
-	 * This method is called by the `instanceof` operator.
-	 * @return {symbol}
-	 */
-	static get [instanceSymbol]() {
-		return Symbol.for("@schukai/monster/components/dataset@@instance");
-	}
-
-	/**
-	 * This method determines which attributes are to be monitored by `attributeChangedCallback()`.
-	 *
-	 * @return {string[]}
-	 * @since 1.15.0
-	 */
-	static get observedAttributes() {
-		const attributes = super.observedAttributes;
-		attributes.push(ATTRIBUTE_DATATABLE_INDEX);
-		return attributes;
-	}
-
-	/**
-	 * To set the options via the HTML tag, the attribute `data-monster-options` must be used.
-	 * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
-	 *
-	 * The individual configuration values can be found in the table.
-	 *
-	 * @property {Object} templates Template definitions
-	 * @property {string} templates.main Main template
-	 * @property {object} datasource The datasource
-	 * @property {string} datasource.selector The selector of the datasource
-	 * @property {object} mapping The mapping
-	 * @property {string} mapping.data The data
-	 * @property {number} mapping.index The index
-	 * @property {Array} data The data
-	 */
-	get defaults() {
-		const obj = Object.assign({}, super.defaults, {
-			templates: {
-				main: getTemplate(),
-			},
-
-			datasource: {
-				selector: null,
-			},
-
-			mapping: {
-				data: "dataset",
-				index: 0,
-			},
-
-			features: {
-				/**
-				 * @since 3.70.0
-				 * @type {boolean}
-				 */
-				refreshOnMutation: true,
-			},
-
-			/**
-			 * @since 3.70.0
-			 * @type {boolean}
-			 */
-			refreshOnMutation: {
-				selector: "input, select, textarea",
-			},
-
-			data: {},
-		});
-
-		updateOptionsFromArguments.call(this, obj);
-		return obj;
-	}
-
-	/**
-	 *
-	 * @return {string}
-	 */
-	static getTag() {
-		return "monster-dataset";
-	}
-
-	/**
-	 * This method is called when the component is created.
-	 * @since 3.70.0
-	 * @return {DataSet}
-	 */
-	refresh() {
-		// makes sure that handleDataSourceChanges is called
-		this.setOption("data", {});
-		return this;
-	}
-
-	/**
-	 *
-	 * @return {Promise<unknown>}
-	 */
-	write() {
-		return new Promise((resolve, reject) => {
-			if (!this[datasourceLinkedElementSymbol]) {
-				reject(new Error("No datasource"));
-				return;
-			}
-
-			const internalUpdateCloneData = this.getInternalUpdateCloneData();
-			if (!internalUpdateCloneData) {
-				reject(new Error("No update data"));
-				return;
-			}
-
-			const internalData = internalUpdateCloneData?.["data"];
-			if (
-				internalData === undefined ||
-				internalData === null ||
-				internalData === ""
-			) {
-				reject(new Error("No data"));
-				return;
-			}
-
-			queueMicrotask(() => {
-				const path = this.getOption("mapping.data");
-				const index = this.getOption("mapping.index");
-
-				let pathWithIndex;
-
-				if (isString(path) && path !== "") {
-					pathWithIndex = path + "." + index;
-				} else {
-					pathWithIndex = String(index);
-				}
-
-				const data = this[datasourceLinkedElementSymbol]?.data;
-				if (!data) {
-					reject(new Error("No data"));
-					return;
-				}
-
-				const unref = JSON.stringify(data);
-				const ref = JSON.parse(unref);
-
-				new Pathfinder(ref).setVia(pathWithIndex, internalData);
-
-				this[datasourceLinkedElementSymbol].data = ref;
-
-				resolve();
-			});
-		});
-	}
-
-	/**
-	 * This method is responsible for assembling the component.
-	 *
-	 * It calls the parent's assemble method first, then initializes control references and event handlers.
-	 * If the `datasource.selector` option is provided and is a string, it searches for the corresponding
-	 * element in the DOM using that selector.
-	 *
-	 * If the selector matches exactly one element, it checks if the element is an instance of the `Datasource` class.
-	 *
-	 * If it is, the component's `datasourceLinkedElementSymbol` property is set to the element, and the component
-	 * attaches an observer to the datasource's changes.
-	 *
-	 * The observer is a function that calls the `handleDataSourceChanges` method in the context of the component.
-	 * Additionally, the component attaches an observer to itself, which also calls the `handleDataSourceChanges`
-	 * method in the component's context.
-	 */
-	[assembleMethodSymbol]() {
-		super[assembleMethodSymbol]();
-
-		initEventHandler.call(this);
-
-		if (!this[datasourceLinkedElementSymbol]) {
-			const selector = this.getOption("datasource.selector");
-
-			if (isString(selector)) {
-				const element = findElementWithSelectorUpwards(this, selector);
-				if (element === null) {
-					throw new Error("the selector must match exactly one element");
-				}
-
-				if (!(element instanceof Datasource)) {
-					throw new TypeError("the element must be a datasource");
-				}
-
-				this[datasourceLinkedElementSymbol] = element;
-				element.datasource.attachObserver(
-					new Observer(handleDataSourceChanges.bind(this)),
-				);
-			} else {
-				throw new Error("the selector must be a string");
-			}
-		}
-
-		if (
-			this.getOption("features.refreshOnMutation") &&
-			this.getOption("refreshOnMutation.selector")
-		) {
-			initMutationObserver.call(this);
-		}
-	}
-
-	/**
-	 * @return [CSSStyleSheet]
-	 */
-	static getCSSStyleSheet() {
-		return [FormStyleSheet, DatasetStyleSheet];
-	}
+    /**
+     * This method is called by the `instanceof` operator.
+     * @return {symbol}
+     */
+    static get [instanceSymbol]() {
+        return Symbol.for("@schukai/monster/components/dataset@@instance");
+    }
+
+    /**
+     * This method determines which attributes are to be monitored by `attributeChangedCallback()`.
+     *
+     * @return {string[]}
+     * @since 1.15.0
+     */
+    static get observedAttributes() {
+        const attributes = super.observedAttributes;
+        attributes.push(ATTRIBUTE_DATATABLE_INDEX);
+        attributes.push("data-monster-option-mapping-index");
+        return attributes;
+    }
+
+    /**
+     * To set the options via the HTML tag, the attribute `data-monster-options` must be used.
+     * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
+     *
+     * The individual configuration values can be found in the table.
+     *
+     * @property {Object} templates Template definitions
+     * @property {string} templates.main Main template
+     * @property {object} datasource The datasource
+     * @property {string} datasource.selector The selector of the datasource
+     * @property {object} mapping The mapping
+     * @property {string} mapping.data The data
+     * @property {number} mapping.index The index
+     * @property {object} features The features
+     * @property {boolean} features.refreshOnMutation Refresh on mutation
+     * @property {object} refreshOnMutation The refresh on mutation
+     * @property {string} refreshOnMutation.selector The selector
+     */
+    get defaults() {
+        const obj = Object.assign({}, super.defaults, {
+            templates: {
+                main: getTemplate(),
+            },
+
+            datasource: {
+                selector: null,
+            },
+
+            mapping: {
+                data: "dataset",
+                index: 0,
+            },
+
+            features: {
+                /**
+                 * @since 3.70.0
+                 * @type {boolean}
+                 */
+                refreshOnMutation: true,
+            },
+
+            /**
+             * @since 3.70.0
+             * @type {boolean}
+             */
+            refreshOnMutation: {
+                selector: "input, select, textarea",
+            },
+
+            data: {},
+        });
+
+        updateOptionsFromArguments.call(this, obj);
+        return obj;
+    }
+
+    /**
+     *
+     * @return {string}
+     */
+    static getTag() {
+        return "monster-dataset";
+    }
+
+    /**
+     * This method is called when the component is created.
+     * @since 3.70.0
+     * @return {DataSet}
+     */
+    refresh() {
+        // makes sure that handleDataSourceChanges is called
+        this.setOption("data", {});
+        return this;
+    }
+
+    /**
+     *
+     * @return {Promise<unknown>}
+     */
+    write() {
+        return new Promise((resolve, reject) => {
+            if (!this[datasourceLinkedElementSymbol]) {
+                reject(new Error("No datasource"));
+                return;
+            }
+
+            const internalUpdateCloneData = this.getInternalUpdateCloneData();
+            if (!internalUpdateCloneData) {
+                reject(new Error("No update data"));
+                return;
+            }
+
+            const internalData = internalUpdateCloneData?.["data"];
+            if (
+                internalData === undefined ||
+                internalData === null ||
+                internalData === ""
+            ) {
+                reject(new Error("No data"));
+                return;
+            }
+
+            queueMicrotask(() => {
+                const path = this.getOption("mapping.data");
+                const index = this.getOption("mapping.index");
+
+                let pathWithIndex;
+
+                if (isString(path) && path !== "") {
+                    pathWithIndex = path + "." + index;
+                } else {
+                    pathWithIndex = String(index);
+                }
+
+                const data = this[datasourceLinkedElementSymbol]?.data;
+                if (!data) {
+                    reject(new Error("No data"));
+                    return;
+                }
+
+                const unref = JSON.stringify(data);
+                const ref = JSON.parse(unref);
+
+                new Pathfinder(ref).setVia(pathWithIndex, internalData);
+
+                this[datasourceLinkedElementSymbol].data = ref;
+
+                resolve();
+            });
+        });
+    }
+
+    /**
+     * This method is responsible for assembling the component.
+     *
+     * It calls the parent's assemble method first, then initializes control references and event handlers.
+     * If the `datasource.selector` option is provided and is a string, it searches for the corresponding
+     * element in the DOM using that selector.
+     *
+     * If the selector matches exactly one element, it checks if the element is an instance of the `Datasource` class.
+     *
+     * If it is, the component's `datasourceLinkedElementSymbol` property is set to the element, and the component
+     * attaches an observer to the datasource's changes.
+     *
+     * The observer is a function that calls the `handleDataSourceChanges` method in the context of the component.
+     * Additionally, the component attaches an observer to itself, which also calls the `handleDataSourceChanges`
+     * method in the component's context.
+     */
+    [assembleMethodSymbol]() {
+        super[assembleMethodSymbol]();
+
+        requestAnimationFrame(() => {
+
+            if (!this[datasourceLinkedElementSymbol]) {
+                const selector = this.getOption("datasource.selector");
+
+                if (isString(selector)) {
+                    const element = findElementWithSelectorUpwards(this, selector);
+                    if (element === null) {
+                        throw new Error("the selector must match exactly one element");
+                    }
+
+                    if (!(element instanceof Datasource)) {
+                        throw new TypeError("the element must be a datasource");
+                    }
+
+                    this[datasourceLinkedElementSymbol] = element;
+                    element.datasource.attachObserver(
+                        new Observer(handleDataSourceChanges.bind(this)),
+                    );
+
+                    handleDataSourceChanges.call(this);
+                } else {
+                    throw new Error("the selector must be a string");
+                }
+            }
+
+            if (
+                this.getOption("features.refreshOnMutation") &&
+                this.getOption("refreshOnMutation.selector")
+            ) {
+                initMutationObserver.call(this);
+            }
+
+            initEventHandler.call(this);
+
+        });
+
+    }
+
+    /**
+     * @return [CSSStyleSheet]
+     */
+    static getCSSStyleSheet() {
+        return [FormStyleSheet, DatasetStyleSheet];
+    }
 }
 
 /**
  * @private
  */
 function initEventHandler() {
-	this[attributeObserverSymbol][ATTRIBUTE_DATATABLE_INDEX] = () => {
-		const index = this.getAttribute(ATTRIBUTE_DATATABLE_INDEX);
-		if (index) {
-			this.setOption("mapping.index", parseInt(index, 10));
-		}
-	};
+
+    this[attributeObserverSymbol][ATTRIBUTE_DATATABLE_INDEX] = () => { // @deprecated use data-monster-option-mapping-index
+        const index = this.getAttribute(ATTRIBUTE_DATATABLE_INDEX);
+        if (index) {
+            this.setOption("mapping.index", parseInt(index, 10));
+            handleDataSourceChanges.call(this);
+        }
+    };
+
+    this[attributeObserverSymbol]["data-monster-option-mapping-index"] = () => {
+        const index = this.getAttribute("data-monster-option-mapping-index");
+        if (index !== null && index !== undefined && index !== "") {
+            this.setOption("mapping.index", parseInt(index, 10));
+            handleDataSourceChanges.call(this);
+        }
+    };
+
+    if (this[datasourceLinkedElementSymbol]) {
+
+        this[datasourceLinkedElementSymbol].datasource.attachObserver(
+            new Observer(() => {
+                const page = this[datasourceLinkedElementSymbol]?.currentPage();
+                if (page !== null && page !== undefined && page !== "") {
+                    const index = parseInt(page, 10) - 1;
+                    this.setOption("mapping.index", index);
+                    handleDataSourceChanges.call(this);
+                }
+            }),
+        );
+
+        this[datasourceLinkedElementSymbol].attachObserver(
+            new Observer(() => {
+                const page = this[datasourceLinkedElementSymbol]?.currentPage();
+                if (page !== null && page !== undefined && page !== "") {
+                    const index = parseInt(page, 10) - 1;
+                    this.setOption("mapping.index", index);
+                    handleDataSourceChanges.call(this);
+                }
+            }),
+        );
+
+        handleDataSourceChanges.call(this);
+    }
+
 }
 
 /**
@@ -303,56 +325,56 @@ function initEventHandler() {
  * @param {Object} options
  */
 function updateOptionsFromArguments(options) {
-	const index = this.getAttribute(ATTRIBUTE_DATATABLE_INDEX);
+    const index = this.getAttribute(ATTRIBUTE_DATATABLE_INDEX);  // @deprecated use data-monster-option-mapping-index
 
-	if (index !== null && index !== undefined) {
-		options.mapping.index = parseInt(index, 10);
-	}
+    if (index !== null && index !== undefined) {
+        options.mapping.index = parseInt(index, 10);
+    }
 
-	const selector = this.getAttribute(ATTRIBUTE_DATASOURCE_SELECTOR);
+    const selector = this.getAttribute(ATTRIBUTE_DATASOURCE_SELECTOR);
 
-	if (selector) {
-		options.datasource.selector = selector;
-	}
+    if (selector) {
+        options.datasource.selector = selector;
+    }
 }
 
 /**
  * @private
  */
 function initMutationObserver() {
-	const config = { attributes: false, childList: true, subtree: true };
-
-	const callback = (mutationList, observer) => {
-		if (mutationList.length === 0) {
-			return;
-		}
-
-		let doneFlag = false;
-		for (const mutation of mutationList) {
-			if (mutation.type === "childList") {
-				for (const node of mutation.addedNodes) {
-					if (
-						node instanceof HTMLElement &&
-						node.matches(this.getOption("refreshOnMutation.selector"))
-					) {
-						doneFlag = true;
-						break;
-					}
-				}
-
-				if (doneFlag) {
-					break;
-				}
-			}
-		}
-
-		if (doneFlag) {
-			this.refresh();
-		}
-	};
-
-	const observer = new MutationObserver(callback);
-	observer.observe(this, config);
+    const config = {attributes: false, childList: true, subtree: true};
+
+    const callback = (mutationList, observer) => {
+        if (mutationList.length === 0) {
+            return;
+        }
+
+        let doneFlag = false;
+        for (const mutation of mutationList) {
+            if (mutation.type === "childList") {
+                for (const node of mutation.addedNodes) {
+                    if (
+                        node instanceof HTMLElement &&
+                        node.matches(this.getOption("refreshOnMutation.selector"))
+                    ) {
+                        doneFlag = true;
+                        break;
+                    }
+                }
+
+                if (doneFlag) {
+                    break;
+                }
+            }
+        }
+
+        if (doneFlag) {
+            this.refresh();
+        }
+    };
+
+    const observer = new MutationObserver(callback);
+    observer.observe(this, config);
 }
 
 /**
@@ -360,8 +382,8 @@ function initMutationObserver() {
  * @return {string}
  */
 function getTemplate() {
-	// language=HTML
-	return `
+    // language=HTML
+    return `
         <div data-monster-role="control" part="control">
             <slot></slot>
         </div>
diff --git a/source/components/datatable/datasource.mjs b/source/components/datatable/datasource.mjs
index c69326a45..09c9d4c52 100644
--- a/source/components/datatable/datasource.mjs
+++ b/source/components/datatable/datasource.mjs
@@ -30,21 +30,16 @@ const dataSourceSymbol = Symbol.for(
 );
 
 /**
- * The Datasource component is a basic class for the datatable component.
+ * A datasource
  *
- * <img src="./images/datasource.png">
+ * @fragments /fragments/components/datatable/datasource
  *
- * Dependencies: the system uses functions of the [monsterjs](https://monsterjs.org/) library
+ * @example /examples/components/datatable/datasource
  *
- * @startuml datasource.png
- * skinparam monochrome true
- * skinparam shadowing false
- * HTMLElement <|-- CustomElement
- * CustomElement <|-- Datasource
- * @enduml
+ * @issue https://localhost.alvine.dev:8443/development/issues/closed/272.html
  *
  * @copyright schukai GmbH
- * @summary A abstract datasource
+ * @summary A generic datasource
  */
 class Datasource extends CustomElement {
 	/**
@@ -77,8 +72,7 @@ class Datasource extends CustomElement {
 	}
 
 	/**
-	 *
-	 * @return {Monster.Components.Form.Form}
+	 * @return {void}
 	 */
 	[assembleMethodSymbol]() {
 		super[assembleMethodSymbol]();
@@ -93,7 +87,7 @@ class Datasource extends CustomElement {
 	}
 
 	/**
-	 * set the data
+	 * set the data with proxy
 	 * @param {Object} data
 	 */
 	set data(data) {
@@ -101,18 +95,32 @@ class Datasource extends CustomElement {
 	}
 
 	/**
-	 * Get the datasource
-	 * @return {Monster.Data.Datasource}
+	 * Get the base datasource
+	 * @return {Datasource}
 	 */
 	get datasource() {
 		return this[dataSourceSymbol];
 	}
 
+	/**
+	 * Wrapper for the write method of the datasource
+	 */
 	write() {
 		this[dataSourceSymbol].write();
 	}
 
+	/**
+	 * Wrapper for the read method of the datasource
+	 */
 	read() {
 		this[dataSourceSymbol].read();
 	}
+
+	/**
+	 * @return {int}
+	 * @throws {Error} this method must be implemented by derived classes.
+	 */
+	currentPage() {
+		throw new Error("this method must be implemented by derived classes");
+	}
 }
diff --git a/source/components/datatable/datasource/dom.mjs b/source/components/datatable/datasource/dom.mjs
index 42269e03a..5004dc8a9 100644
--- a/source/components/datatable/datasource/dom.mjs
+++ b/source/components/datatable/datasource/dom.mjs
@@ -53,6 +53,8 @@ class Dom extends Datasource {
 	 *
 	 * @property {Object} templates Template definitions
 	 * @property {string} templates.main Main template
+	 * @property {Object} features Feature definitions
+	 * @property {boolean} features.autoInit Automatically initializes the component
 	 */
 	get defaults() {
 		return Object.assign({}, super.defaults, {
@@ -63,11 +65,19 @@ class Dom extends Datasource {
 			features: {
 				autoInit: true,
 			},
+
+			/** @private */
+			sys: {
+				pagination: {
+					pages: 1,
+					objectsPerPage: 10,
+					currentPage: 1,
+				}
+			}
 		});
 	}
 
 	/**
-	 *
 	 * @return {void}
 	 */
 	[assembleMethodSymbol]() {
@@ -76,6 +86,17 @@ class Dom extends Datasource {
 		updateDataSource.call(this);
 	}
 
+	/**
+	 * This method set the current page of the pagination
+	 *
+	 * @param {string} page
+	 * @return {Dom}
+	 */
+	setParameters({page}) {
+		this.setOption("sys.pagination.currentPage", page);
+		return this;
+	}
+
 	/**
 	 *
 	 * @return {CSSStyleSheet[]}
@@ -108,6 +129,13 @@ class Dom extends Datasource {
 			updateDataSource.call(this);
 		}
 	}
+
+	/**
+	 * @return {int}
+	 */
+	currentPage() {
+		return this.getOption("sys.pagination.currentPage");
+	}
 }
 
 /**
@@ -170,7 +198,15 @@ function updateDataSource() {
 		data = [];
 	}
 
+	// set pagination
+	this.setOption("sys.pagination.objectsPerPage", 1 );
+	this.setOption("sys.pagination.pages", data.length);
+	this.setOption("sys.pagination.currentPage", 1);
+
+	/** call setter */
 	this.data = data;
+
+
 }
 
 /**
diff --git a/source/components/datatable/datasource/rest.mjs b/source/components/datatable/datasource/rest.mjs
index cb0deebc0..51681f246 100644
--- a/source/components/datatable/datasource/rest.mjs
+++ b/source/components/datatable/datasource/rest.mjs
@@ -12,27 +12,27 @@
  * SPDX-License-Identifier: AGPL-3.0
  */
 
-import { diff } from "../../../data/diff.mjs";
-import { addAttributeToken } from "../../../dom/attributes.mjs";
-import { ATTRIBUTE_ERRORMESSAGE } from "../../../dom/constants.mjs";
-import { isArray } from "../../../types/is.mjs";
-import { Datasource, dataSourceSymbol } from "../datasource.mjs";
-import { DatasourceStyleSheet } from "../stylesheet/datasource.mjs";
-import { instanceSymbol } from "../../../constants.mjs";
+import {diff} from "../../../data/diff.mjs";
+import {addAttributeToken} from "../../../dom/attributes.mjs";
+import {ATTRIBUTE_ERRORMESSAGE} from "../../../dom/constants.mjs";
+import {isArray} from "../../../types/is.mjs";
+import {Datasource, dataSourceSymbol} from "../datasource.mjs";
+import {DatasourceStyleSheet} from "../stylesheet/datasource.mjs";
+import {instanceSymbol} from "../../../constants.mjs";
 import {
-	assembleMethodSymbol,
-	registerCustomElement,
+    assembleMethodSymbol,
+    registerCustomElement,
 } from "../../../dom/customelement.mjs";
-import { RestAPI } from "../../../data/datasource/server/restapi.mjs";
-import { Formatter } from "../../../text/formatter.mjs";
-import { clone } from "../../../util/clone.mjs";
-import { validateBoolean } from "../../../types/validate.mjs";
-import { findElementWithIdUpwards } from "../../../dom/util.mjs";
-import { Observer } from "../../../types/observer.mjs";
-import { Pathfinder } from "../../../data/pathfinder.mjs";
-import { fireCustomEvent } from "../../../dom/events.mjs";
+import {RestAPI} from "../../../data/datasource/server/restapi.mjs";
+import {Formatter} from "../../../text/formatter.mjs";
+import {clone} from "../../../util/clone.mjs";
+import {validateBoolean} from "../../../types/validate.mjs";
+import {findElementWithIdUpwards} from "../../../dom/util.mjs";
+import {Observer} from "../../../types/observer.mjs";
+import {Pathfinder} from "../../../data/pathfinder.mjs";
+import {fireCustomEvent} from "../../../dom/events.mjs";
 
-export { Rest };
+export {Rest};
 
 /**
  * @private
@@ -46,7 +46,7 @@ const intersectionObserverHandlerSymbol = Symbol("intersectionObserverHandler");
  * @type {symbol}
  */
 const rawDataSymbol = Symbol.for(
-	"@schukai/monster/data/datasource/server/restapi/rawdata",
+    "@schukai/monster/data/datasource/server/restapi/rawdata",
 );
 
 /**
@@ -54,7 +54,7 @@ const rawDataSymbol = Symbol.for(
  * @type {symbol}
  */
 const intersectionObserverObserverSymbol = Symbol(
-	"intersectionObserverObserver",
+    "intersectionObserverObserver",
 );
 
 /**
@@ -64,396 +64,427 @@ const intersectionObserverObserverSymbol = Symbol(
 const filterObserverSymbol = Symbol("filterObserver");
 
 /**
- * The Datasource component is a basic class for the datatable component.
+ * A rest api datasource
  *
- * <img src="./images/rest.png">
+ * @fragments /fragments/components/datatable/datasource/rest
  *
- * Dependencies: the system uses functions of the [monsterjs](https://monsterjs.org/) library
+ * @example /examples/components/datatable/datasource-rest-simple
+ * @example /examples/components/datatable/datasource-rest-auto-init
+ * @example /examples/components/datatable/datasource-rest-do-fetch
  *
- * @startuml rest.png
- * skinparam monochrome true
- * skinparam shadowing false
- * HTMLElement <|-- CustomElement
- * CustomElement <|-- Datasource
- * Datasource <|-- Rest
- * @enduml
+ * @issue https://localhost.alvine.dev:8443/development/issues/closed/272.html
  *
  * @copyright schukai GmbH
- * @summary A rest api datasource
+ * @summary A rest api datasource for the datatable or other components
  */
-
-
-
 class Rest extends Datasource {
-	/**
-	 * the constructor of the class
-	 */
-	constructor() {
-		super();
-		this[dataSourceSymbol] = new RestAPI();
-	}
-
-	/**
-	 * This method is called by the `instanceof` operator.
-	 * @return {symbol}
-	 */
-	static get [instanceSymbol]() {
-		return Symbol.for("@schukai/monster/components/datasource/rest@@instance");
-	}
-
-	/**
-	 * To set the options via the HTML tag, the attribute `data-monster-options` must be used.
-	 * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
-	 *
-	 * The individual configuration values can be found in the table.
-	 *
-	 * @property {Object} templates Template definitions
-	 * @property {string} templates.main Main template
-	 * @property {Object} features Feature definitions
-	 * @property {boolean} features.autoInit If true, the component is initialized automatically
-	 * @property {boolean} features.filter If true, the component is initialized automatically
-	 * @property {Object} autoInit Auto init definitions
-	 * @property {boolean} autoInit.intersectionObserver If true, the intersection observer is initialized automatically
-	 * @property {boolean} autoInit.oneTime If true, the intersection observer is initialized only once
-	 * @property {Object} filter Filter definitions
-	 * @property {string} filter.id The id of the filter control
-	 * @property {Object} datatable Datatable definitions
-	 * @property {string} datatable.id The id of the datatable control
-	 * @property {Object} response Response definitions
-	 * @property {Object} response.path Path definitions (changed in 3.56.0)
-	 * @property {string} response.path.message Path to the message (changed in 3.56.0)
-	 * @property {Object} read Read configuration
-	 * @property {string} read.url The url of the rest api
-	 * @property {string} read.method The method of the rest api
-	 * @property {Object} read.parameters The parameters of the rest api
-	 * @property {Object} read.parameters.filter The filter of the rest api
-	 * @property {Object} read.parameters.orderBy The order by of the rest api
-	 * @property {Object} read.parameters.page The page of the rest api
-	 * @property {Object} write Write configuration
-	 */
-	get defaults() {
-		const restOptions = new RestAPI().defaults;
-
-		restOptions.read.parameters = {
-			filter: undefined,
-			oderBy: undefined,
-			page: "1",
-		};
-
-		return Object.assign({}, super.defaults, restOptions, {
-			templates: {
-				main: getTemplate(),
-			},
-
-			features: {
-				autoInit: false,
-				filter: false,
-			},
-
-			autoInit: {
-				intersectionObserver: false,
-				oneTime: true,
-			},
-
-			filter: {
-				id: undefined,
-			},
-
-			datatable: {
-				id: undefined,
-			},
-
-			response: {
-				path: {
-					message: "sys.message",
-					code: "sys.code",
-				},
-			},
-		});
-	}
-
-	/**
-	 *
-	 * @param {string} page
-	 * @param {string} query
-	 * @param {string} orderBy
-	 * @return {Rest}
-	 */
-	setParameters({ page, query, orderBy }) {
-		const parameters = this.getOption("read.parameters");
-		if (query !== undefined) {
-			parameters.query = `${query}`;
-			parameters.page = "1";
-		}
-
-		// after a query the page is set to 1, so if the page is not set, it is set to 1
-		if (page !== undefined) parameters.page = `${page}`;
-		if (orderBy !== undefined) parameters.order = `${orderBy}`;
-		this.setOption("read.parameters", parameters);
-		return this;
-	}
-
-	/**
-	 * @return {void}
-	 */
-	[assembleMethodSymbol]() {
-		super[assembleMethodSymbol]();
-
-		initEventHandler.call(this);
-		initAutoInit.call(this);
-	}
-
-	/**
-	 * @deprecated 2023-06-25
-	 * @return {Promise<never>|*}
-	 */
-	reload() {
-		return this.fetch();
-	}
-
-	/**
-	 * Fetches the data from the rest api
-	 * @return {Promise<never>|*}
-	 */
-	fetch() {
-		const opt = clone(this.getOption("read"));
-		this[dataSourceSymbol].setOption("read", opt);
-
-		let url = this.getOption("read.url");
-		const formatter = new Formatter(this.getOption("read.parameters"));
-
-		if (!url) {
-			return Promise.reject(new Error("No url defined"));
-		}
-
-		url = formatter.format(url);
-
-		this[dataSourceSymbol].setOption("read.url", url);
-
-		return new Promise((resolve, reject) => {
-			fireCustomEvent(this, "monster-datasource-fetch", {
-				datasource: this,
-			});
-
-			queueMicrotask(() => {
-				this[dataSourceSymbol]
-					.read()
-					.then((response) => {
-						fireCustomEvent(this, "monster-datasource-fetched", {
-							datasource: this,
-						});
-
-						resolve(response);
-					})
-					.catch((error) => {
-						fireCustomEvent(this, "monster-datasource-error", {
-							error: error,
-						});
-
-						addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, error.toString());
-						reject(error);
-					});
-			});
-		});
-	}
-
-	/**
-	 *
-	 * @return {CSSStyleSheet[]}
-	 */
-	static getCSSStyleSheet() {
-		return [DatasourceStyleSheet];
-	}
-
-	/**
-	 * @private
-	 * @return {string}
-	 */
-	static getTag() {
-		return "monster-datasource-rest";
-	}
-
-	/**
-	 * This method activates the intersection observer manually.
-	 * For this purpose, the option `autoInit.intersectionObserver` must be set to `false`.
-	 *
-	 * @return {Monster.Components.Datatable.Datasource.Rest}
-	 */
-	initIntersectionObserver() {
-		initIntersectionObserver.call(this);
-		return this;
-	}
-
-	/**
-	 * @private
-	 */
-	connectedCallback() {
-		super.connectedCallback();
-
-		queueMicrotask(() => {
-			if (this.getOption("features.filter", false) === true) {
-				initFilter.call(this);
-			}
-		});
-	}
-
-	/**
-	 * @private
-	 */
-	disconnectedCallback() {
-		super.disconnectedCallback();
-		removeFilter.call(this);
-	}
-
-	/**
-	 * @return {Promise<never>|*}
-	 */
-	read() {
-		return this.fetch();
-	}
-
-	/**
-	 * Fetches the data from the rest api
-	 * @return {Promise<never>|*}
-	 */
-	write() {
-		const opt = clone(this.getOption("write"));
-		this[dataSourceSymbol].setOption("write", opt);
-
-		let url = this.getOption("write.url");
-		const formatter = new Formatter(this.getOption("write.parameters"));
-
-		if (!url) {
-			return Promise.reject(new Error("No url defined"));
-		}
-
-		url = formatter.format(url);
-
-		this[dataSourceSymbol].setOption("write.url", url);
-
-		return new Promise((resolve, reject) => {
-			fireCustomEvent(this, "monster-datasource-fetch", {
-				datasource: this,
-			});
-
-			queueMicrotask(() => {
-				this[dataSourceSymbol]
-					.write()
-					.then((response) => {
-						fireCustomEvent(this, "monster-datasource-fetched", {
-							datasource: this,
-						});
-
-						resolve(response);
-					})
-					.catch((error) => {
-						fireCustomEvent(this, "monster-datasource-error", {
-							error: error,
-						});
-
-						addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, error.toString());
-						reject(error);
-					});
-			});
-		});
-	}
+    /**
+     * the constructor of the class
+     */
+    constructor() {
+        super();
+        this[dataSourceSymbol] = new RestAPI();
+    }
+
+    /**
+     * This method is called by the `instanceof` operator.
+     * @return {symbol}
+     */
+    static get [instanceSymbol]() {
+        return Symbol.for("@schukai/monster/components/datasource/rest@@instance");
+    }
+
+    /**
+     * To set the options via the HTML tag, the attribute `data-monster-options` must be used.
+     * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
+     *
+     * The individual configuration values can be found in the table.
+     *
+     * @property {Object} templates Template definitions
+     * @property {string} templates.main Main template
+     * @property {Object} features Feature definitions
+     * @property {boolean} features.autoInit If true, the component is initialized automatically
+     * @property {boolean} features.filter If true, the component is initialized automatically
+     * @property {Object} autoInit Auto init definitions
+     * @property {boolean} autoInit.intersectionObserver If true, the intersection observer is initialized automatically
+     * @property {boolean} autoInit.oneTime If true, the intersection observer is initialized only once
+     * @property {Object} filter Filter definitions
+     * @property {string} filter.id The id of the filter control
+     * @property {Object} response Response definitions
+     * @property {Object} response.path Path definitions (changed in 3.56.0)
+     * @property {string} response.path.message Path to the message (changed in 3.56.0)
+     * @property {Object} read Read configuration
+     * @property {string} read.url The url of the rest api
+     * @property {string} read.method The method of the rest api
+     * @property {Object} read.parameters The parameters of the rest api
+     * @property {Object} read.parameters.filter The filter of the rest api
+     * @property {Object} read.parameters.orderBy The order by of the rest api
+     * @property {Object} read.parameters.page The page of the rest api
+     * @property {string} read.mapping.currentPage The current page
+     * @property {Object} write Write configuration
+     * @property {string} write.url The url of the rest api
+     * @property {string} write.method The method of the rest api
+     * @property {Object} write Write configuration
+     */
+    get defaults() {
+        const restOptions = new RestAPI().defaults;
+
+        restOptions.read.parameters = {
+            filter: null,
+            oderBy: null,
+            page: "1",
+        };
+
+        restOptions.read.mapping.currentPage = "sys.pagination.currentPage";
+
+        return Object.assign({}, super.defaults, restOptions, {
+            templates: {
+                main: getTemplate(),
+            },
+
+            features: {
+                autoInit: false,
+                filter: false,
+            },
+
+            autoInit: {
+                intersectionObserver: false,
+                oneTime: true,
+            },
+
+            filter: {
+                id: null,
+            },
+
+            /*datatable: {
+                id: undefined,  // not used?
+            },    */
+
+            response: {
+                path: {
+                    message: "sys.message",
+                    code: "sys.code",
+                },
+            },
+        });
+    }
+
+    /**
+     * With this method, you can set the parameters for the rest api. The parameters are
+     * used for building the url.
+     *
+     * @param {string} page
+     * @param {string} query
+     * @param {string} orderBy
+     * @return {Rest}
+     */
+    setParameters({page, query, orderBy}) {
+        const parameters = this.getOption("read.parameters");
+        if (query !== undefined) {
+            parameters.query = `${query}`;
+            parameters.page = "1";
+        }
+
+        // after a query the page is set to 1, so if the page is not set, it is set to 1
+        if (page !== undefined) parameters.page = `${page}`;
+        if (orderBy !== undefined) parameters.order = `${orderBy}`;
+        this.setOption("read.parameters", parameters);
+        return this;
+    }
+
+    /**
+     * @private
+     * @return {void}
+     */
+    [assembleMethodSymbol]() {
+        super[assembleMethodSymbol]();
+        initEventHandler.call(this);
+        initAutoInit.call(this);
+    }
+
+    /**
+     * This method reloads the data from the rest api, this method is deprecated.
+     * You should use the method `read` instead.
+     *
+     * @deprecated 2023-06-25
+     * @return {Promise<never>|*}
+     */
+    reload() {
+        return this.read();
+    }
+
+    /**
+     * Fetches the data from the rest api, this method is deprecated.
+     * You should use the method `read` instead.
+     *
+     * @deprecated 2024-12-24
+     * @return {Promise<never>|*}
+     */
+    fetch() {
+        return this.read();
+    }
+
+    /**
+     *
+     * @return {CSSStyleSheet[]}
+     */
+    static getCSSStyleSheet() {
+        return [DatasourceStyleSheet];
+    }
+
+    /**
+     * @private
+     * @return {string}
+     */
+    static getTag() {
+        return "monster-datasource-rest";
+    }
+
+    /**
+     * This method activates the intersection observer manually.
+     * For this purpose, the option `autoInit.intersectionObserver` must be set to `false`.
+     *
+     * @return {Rest}
+     */
+    initIntersectionObserver() {
+        initIntersectionObserver.call(this);
+        return this;
+    }
+
+    /**
+     * @private
+     */
+    connectedCallback() {
+        super.connectedCallback();
+
+        queueMicrotask(() => {
+            if (this.getOption("features.filter", false) === true) {
+                initFilter.call(this);
+            }
+        });
+    }
+
+    /**
+     * @private
+     */
+    disconnectedCallback() {
+        super.disconnectedCallback();
+        removeFilter.call(this);
+    }
+
+    /**
+     * This method reads the data from the rest api.
+     * The data is stored in the internal dataset object.
+     *
+     * @return {Promise}
+     * @fires monster-datasource-fetch
+     * @fires monster-datasource-fetched
+     * @fires monster-datasource-error
+     */
+    read() {
+        const opt = clone(this.getOption("read"));
+        this[dataSourceSymbol].setOption("read", opt);
+
+        let url = this.getOption("read.url");
+        const formatter = new Formatter(this.getOption("read.parameters"));
+
+        if (!url) {
+            return Promise.reject(new Error("No url defined"));
+        }
+
+        url = formatter.format(url);
+
+        this[dataSourceSymbol].setOption("read.url", url);
+
+        return new Promise((resolve, reject) => {
+            fireCustomEvent(this, "monster-datasource-fetch", {
+                datasource: this,
+            });
+
+            queueMicrotask(() => {
+                this[dataSourceSymbol]
+                    .read()
+                    .then((response) => {
+                        fireCustomEvent(this, "monster-datasource-fetched", {
+                            datasource: this,
+                        });
+
+                        resolve(response);
+                    })
+                    .catch((error) => {
+                        fireCustomEvent(this, "monster-datasource-error", {
+                            error: error,
+                        });
+
+                        addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, error.toString());
+                        reject(error);
+                    });
+            });
+        });
+    }
+
+    /**
+     * Fetches the data from the rest api.
+     * @return {Promise}
+     */
+    write() {
+        const opt = clone(this.getOption("write"));
+        this[dataSourceSymbol].setOption("write", opt);
+
+        let url = this.getOption("write.url");
+        const formatter = new Formatter(this.getOption("write.parameters"));
+
+        if (!url) {
+            return Promise.reject(new Error("No url defined"));
+        }
+
+        url = formatter.format(url);
+
+        this[dataSourceSymbol].setOption("write.url", url);
+
+        return new Promise((resolve, reject) => {
+            fireCustomEvent(this, "monster-datasource-fetch", {
+                datasource: this,
+            });
+
+            queueMicrotask(() => {
+                this[dataSourceSymbol]
+                    .write()
+                    .then((response) => {
+                        fireCustomEvent(this, "monster-datasource-fetched", {
+                            datasource: this,
+                        });
+
+                        resolve(response);
+                    })
+                    .catch((error) => {
+                        fireCustomEvent(this, "monster-datasource-error", {
+                            error: error,
+                        });
+
+                        addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, error.toString());
+                        reject(error);
+                    });
+            });
+        });
+    }
+
+    // /**
+    //  * @return {int}
+    //  */
+    // currentPage() {
+    //
+    //     const key = this.getOption("read.mapping.currentPage")
+    //     if (key === undefined) {
+    //         return 1;
+    //     }
+    //
+    //     const pf = new Pathfinder(this.data);
+    //     if (pf.exists(key)) {
+    //         return parseInt(pf.getVia(key), 10);
+    //     }
+    //
+    //     return 1;
+    //
+    // }
+
 }
 
 /**
  * @private
  */
 function removeFilter() {
-	const filterID = this.getOption("filter.id", undefined);
-	if (!filterID) return;
+    const filterID = this.getOption("filter.id", undefined);
+    if (!filterID) return;
 
-	const filterControl = findElementWithIdUpwards(this, filterID);
+    const filterControl = findElementWithIdUpwards(this, filterID);
 
-	if (filterControl && this[filterObserverSymbol]) {
-		filterControl?.detachObserver(this[filterObserverSymbol]);
-	}
+    if (filterControl && this[filterObserverSymbol]) {
+        filterControl?.detachObserver(this[filterObserverSymbol]);
+    }
 }
 
 /**
  * @private
  */
 function initFilter() {
-	const filterID = this.getOption("filter.id", undefined);
-
-	if (!filterID)
-		throw new Error("filter feature is enabled but no filter id is defined");
-
-	const filterControl = findElementWithIdUpwards(this, filterID);
-	if (!filterControl)
-		throw new Error(
-			"filter feature is enabled but no filter control with id " +
-				filterID +
-				" is found",
-		);
-
-	this[filterObserverSymbol] = new Observer(() => {
-		const query = filterControl.getOption("query");
-		if (query === undefined) {
-			return;
-		}
-		this.setParameters({ query: query });
-		this.fetch()
-			.then((response) => {
-				if (!(response instanceof Response)) {
-					throw new Error("Response is not an instance of Response");
-				}
-
-				if (response?.ok === true) {
-					this.dispatchEvent(new CustomEvent("reload", { bubbles: true }));
-					filterControl?.showSuccess();
-				}
-
-				if (response.bodyUsed === true) {
-					return handleIntersectionObserver.call(
-						this,
-						response[rawDataSymbol],
-						response,
-						filterControl,
-					);
-				}
-
-				response
-					.text()
-					.then((jsonAsText) => {
-						let json;
-						try {
-							json = JSON.parse(jsonAsText);
-						} catch (e) {
-							const message = e instanceof Error ? e.message : `${e}`;
-							filterControl?.showFailureMessage(message);
-							return Promise.reject(e);
-						}
-
-						return handleIntersectionObserver.call(
-							this,
-							json,
-							response,
-							filterControl,
-						);
-					})
-					.catch((e) => {
-						filterControl?.showFailureMessage(e.message);
-					});
-			})
-			.catch((e) => {
-				this.dispatchEvent(
-					new CustomEvent("error", { bubbles: true, detail: e }),
-				);
-
-				if (!(e instanceof Error)) {
-					e = new Error(e);
-				}
-
-				filterControl?.showFailureMessage(e.message);
-				return Promise.reject(e);
-			});
-	});
-
-	filterControl.attachObserver(this[filterObserverSymbol]);
+    const filterID = this.getOption("filter.id", undefined);
+
+    if (!filterID)
+        throw new Error("filter feature is enabled but no filter id is defined");
+
+    const filterControl = findElementWithIdUpwards(this, filterID);
+    if (!filterControl)
+        throw new Error(
+            "filter feature is enabled but no filter control with id " +
+            filterID +
+            " is found",
+        );
+
+    this[filterObserverSymbol] = new Observer(() => {
+        const query = filterControl.getOption("query");
+        if (query === undefined) {
+            return;
+        }
+        this.setParameters({query: query});
+        this.fetch()
+            .then((response) => {
+                if (!(response instanceof Response)) {
+                    throw new Error("Response is not an instance of Response");
+                }
+
+                if (response?.ok === true) {
+                    this.dispatchEvent(new CustomEvent("reload", {bubbles: true}));
+                    filterControl?.showSuccess();
+                }
+
+                if (response.bodyUsed === true) {
+                    return handleIntersectionObserver.call(
+                        this,
+                        response[rawDataSymbol],
+                        response,
+                        filterControl,
+                    );
+                }
+
+                response
+                    .text()
+                    .then((jsonAsText) => {
+                        let json;
+                        try {
+                            json = JSON.parse(jsonAsText);
+                        } catch (e) {
+                            const message = e instanceof Error ? e.message : `${e}`;
+                            filterControl?.showFailureMessage(message);
+                            return Promise.reject(e);
+                        }
+
+                        return handleIntersectionObserver.call(
+                            this,
+                            json,
+                            response,
+                            filterControl,
+                        );
+                    })
+                    .catch((e) => {
+                        filterControl?.showFailureMessage(e.message);
+                    });
+            })
+            .catch((e) => {
+                this.dispatchEvent(
+                    new CustomEvent("error", {bubbles: true, detail: e}),
+                );
+
+                if (!(e instanceof Error)) {
+                    e = new Error(e);
+                }
+
+                filterControl?.showFailureMessage(e.message);
+                return Promise.reject(e);
+            });
+    });
+
+    filterControl.attachObserver(this[filterObserverSymbol]);
 }
 
 /**
@@ -464,88 +495,89 @@ function initFilter() {
  * @returns {Promise<never>|Promise<Awaited<unknown>>}
  */
 function handleIntersectionObserver(json, response, filterControl) {
-	const path = new Pathfinder(json);
-
-	const codePath = this.getOption("response.path.code");
-
-	if (path.exists(codePath)) {
-		const code = `${path.getVia(codePath)}`;
-		if (code && code === "200") {
-			filterControl?.showSuccess();
-			return Promise.resolve(response);
-		}
-
-		const messagePath = this.getOption("response.path.message");
-		if (path.exists(messagePath)) {
-			const message = path.getVia(messagePath);
-			filterControl?.showFailureMessage(message);
-			return Promise.reject(new Error(message));
-		}
-
-		return Promise.reject(new Error("Response code is not 200"));
-	}
+    const path = new Pathfinder(json);
+
+    const codePath = this.getOption("response.path.code");
+
+    if (path.exists(codePath)) {
+        const code = `${path.getVia(codePath)}`;
+        if (code && code === "200") {
+            filterControl?.showSuccess();
+            return Promise.resolve(response);
+        }
+
+        const messagePath = this.getOption("response.path.message");
+        if (path.exists(messagePath)) {
+            const message = path.getVia(messagePath);
+            filterControl?.showFailureMessage(message);
+            return Promise.reject(new Error(message));
+        }
+
+        return Promise.reject(new Error("Response code is not 200"));
+    }
 }
 
 /**
  * @private
  */
 function initAutoInit() {
-	const autoInit = this.getOption("features.autoInit");
-	validateBoolean(autoInit);
+    const autoInit = this.getOption("features.autoInit");
+    validateBoolean(autoInit);
 
-	if (autoInit !== true) return;
+    if (autoInit !== true) return;
 
-	if (this.getOption("autoInit.intersectionObserver") === true) {
-		initIntersectionObserver.call(this);
-		return;
-	}
+    if (this.getOption("autoInit.intersectionObserver") === true) {
+        initIntersectionObserver.call(this);
+        return;
+    }
 
-	queueMicrotask(() => {
-		this.fetch().catch(() => {});
-	});
+    queueMicrotask(() => {
+        this.fetch().catch(() => {
+        });
+    });
 }
 
 /**
  * @private
  */
 function initEventHandler() {
-	this[intersectionObserverHandlerSymbol] = (entries) => {
-		entries.forEach((entry) => {
-			if (entry.isIntersecting) {
-				if (entry.intersectionRatio > 0) {
-					this.fetch();
-				}
-
-				// only load once
-				if (
-					this.getOption("autoInit.oneTime") === true &&
-					this[intersectionObserverObserverSymbol] !== undefined
-				) {
-					this[intersectionObserverObserverSymbol].unobserve(this);
-				}
-			}
-		});
-	};
+    this[intersectionObserverHandlerSymbol] = (entries) => {
+        entries.forEach((entry) => {
+            if (entry.isIntersecting) {
+                if (entry.intersectionRatio > 0) {
+                    this.fetch();
+                }
+
+                // only load once
+                if (
+                    this.getOption("autoInit.oneTime") === true &&
+                    this[intersectionObserverObserverSymbol] !== undefined
+                ) {
+                    this[intersectionObserverObserverSymbol].unobserve(this);
+                }
+            }
+        });
+    };
 }
 
 /**
  * @private
  */
 function initIntersectionObserver() {
-	this.classList.add("intersection-observer");
+    this.classList.add("intersection-observer");
 
-	const options = {
-		root: null,
-		rootMargin: "0px",
-		threshold: 0.1,
-	};
+    const options = {
+        root: null,
+        rootMargin: "0px",
+        threshold: 0.1,
+    };
 
-	this[intersectionObserverObserverSymbol] = new IntersectionObserver(
-		this[intersectionObserverHandlerSymbol],
-		options,
-	);
+    this[intersectionObserverObserverSymbol] = new IntersectionObserver(
+        this[intersectionObserverHandlerSymbol],
+        options,
+    );
 
-	this[intersectionObserverObserverSymbol].observe(this);
+    this[intersectionObserverObserverSymbol].observe(this);
 }
 
 /**
@@ -553,8 +585,8 @@ function initIntersectionObserver() {
  * @return {string}
  */
 function getTemplate() {
-	// language=HTML
-	return `
+    // language=HTML
+    return `
         <slot></slot>`;
 }
 
diff --git a/source/components/datatable/embedded-pagination.mjs b/source/components/datatable/embedded-pagination.mjs
index 02fc8b121..befdb068c 100644
--- a/source/components/datatable/embedded-pagination.mjs
+++ b/source/components/datatable/embedded-pagination.mjs
@@ -42,10 +42,21 @@ class EmbeddedPagination extends Pagination {
 		return Symbol.for("@schukai/monster/components/embedded-pagination");
 	}
 
+	/**
+	 * @private
+	 */
 	[assembleMethodSymbol]() {
 		super[assembleMethodSymbol]();
 	}
 
+	/**
+	 *
+	 * @property {Object} classes Class definitions
+	 * @property {string} classes.spinner Spinner class
+	 * @property {string} classes.spinnerContainer Spinner container class
+	 * @property {string} classes.error Error class
+	 * @property {string} classes.errorContainer Error container class
+	 */
 	get defaults() {
 		return Object.assign({}, super.defaults, {
 			classes: {
diff --git a/source/components/datatable/pagination.mjs b/source/components/datatable/pagination.mjs
index 41d64542a..b3eed0e66 100644
--- a/source/components/datatable/pagination.mjs
+++ b/source/components/datatable/pagination.mjs
@@ -13,32 +13,32 @@
  */
 
 import {
-	assembleMethodSymbol,
-	CustomElement,
-	registerCustomElement,
+    assembleMethodSymbol,
+    CustomElement,
+    registerCustomElement,
 } from "../../dom/customelement.mjs";
-import { findElementWithSelectorUpwards, getWindow } from "../../dom/util.mjs";
-import { DeadMansSwitch } from "../../util/deadmansswitch.mjs";
-import { ThemeStyleSheet } from "../stylesheet/theme.mjs";
-import { ATTRIBUTE_DATASOURCE_SELECTOR } from "./constants.mjs";
-import { Datasource } from "./datasource.mjs";
-import { Observer } from "../../types/observer.mjs";
-import { ATTRIBUTE_ROLE } from "../../dom/constants.mjs";
-import { findTargetElementFromEvent } from "../../dom/events.mjs";
-import { PaginationStyleSheet } from "./stylesheet/pagination.mjs";
-import { DisplayStyleSheet } from "../stylesheet/display.mjs";
-import { isString } from "../../types/is.mjs";
-import { Pathfinder } from "../../data/pathfinder.mjs";
-import { instanceSymbol } from "../../constants.mjs";
-import { Formatter } from "../../text/formatter.mjs";
+import {findElementWithSelectorUpwards, getWindow} from "../../dom/util.mjs";
+import {DeadMansSwitch} from "../../util/deadmansswitch.mjs";
+import {ThemeStyleSheet} from "../stylesheet/theme.mjs";
+import {ATTRIBUTE_DATASOURCE_SELECTOR} from "./constants.mjs";
+import {Datasource} from "./datasource.mjs";
+import {Observer} from "../../types/observer.mjs";
+import {ATTRIBUTE_ROLE} from "../../dom/constants.mjs";
+import {findTargetElementFromEvent} from "../../dom/events.mjs";
+import {PaginationStyleSheet} from "./stylesheet/pagination.mjs";
+import {DisplayStyleSheet} from "../stylesheet/display.mjs";
+import {isString} from "../../types/is.mjs";
+import {Pathfinder} from "../../data/pathfinder.mjs";
+import {instanceSymbol} from "../../constants.mjs";
+import {Formatter} from "../../text/formatter.mjs";
 import "../form/select.mjs";
-import { addAttributeToken } from "../../dom/attributes.mjs";
-import { ATTRIBUTE_ERRORMESSAGE } from "../../dom/constants.mjs";
+import {addAttributeToken} from "../../dom/attributes.mjs";
+import {ATTRIBUTE_ERRORMESSAGE} from "../../dom/constants.mjs";
 
 import "./datasource/dom.mjs";
 import "./datasource/rest.mjs";
 
-export { Pagination };
+export {Pagination};
 
 /**
  * @private
@@ -71,215 +71,227 @@ const sizeDataSymbol = Symbol("sizeData");
 const debounceSizeSymbol = Symbol("debounceSize");
 
 /**
- * The Pagination component
+ * A Pagination component
+ *
+ * @fragments /fragments/components/datatable/pagination
+ *
+ * @example /examples/components/datatable/pagination-simple
  *
  * @copyright schukai GmbH
  * @summary The Pagination component is used to show the current page and the total number of pages.
  */
 class Pagination extends CustomElement {
-	/**
-	 */
-	constructor() {
-		super();
-		this[datasourceLinkedElementSymbol] = null;
-	}
-
-	/**
-	 * This method is called by the `instanceof` operator.
-	 * @return {symbol}
-	 */
-	static get [instanceSymbol]() {
-		return Symbol.for("@schukai/monster/components/pagination");
-	}
-
-	/**
-	 * To set the options via the HTML tag, the attribute `data-monster-options` must be used.
-	 * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
-	 *
-	 * The individual configuration values can be found in the table.
-	 *
-	 * @property {Object} templates Template definitions
-	 * @property {string} templates.main Main template
-	 * @property {Object} datasource Datasource configuration
-	 * @property {string} datasource.selector Datasource selector
-	 * @property {Object} labels Label definitions
-	 * @property {string} labels.page Page label
-	 * @property {string} labels.description Description label
-	 * @property {string} labels.previous Previous label
-	 * @property {string} labels.next Next label
-	 * @property {string} labels.of Of label
-	 * @property {string} href Href
-	 * @property {number} currentPage Current page
-	 * @property {number} pages Pages
-	 * @property {number} objectsPerPage Objects per page
-	 * @property {Object} mapping Mapping
-	 * @property {string} mapping.pages Pages mapping
-	 * @property {string} mapping.objectsPerPage Objects per page mapping
-	 * @property {string} mapping.currentPage Current page mapping
-	 * @property {Object} pagination Pagination
-	 */
-	get defaults() {
-		return Object.assign(
-			{},
-			super.defaults,
-			{
-				templates: {
-					main: getTemplate(),
-				},
-
-				datasource: {
-					selector: null,
-				},
-
-				labels: {
-					page: "${page}",
-					description: "Page ${page}",
-					previous: "Previous",
-					next: "Next",
-					of: "of",
-				},
-
-				href: "page-${page}",
-
-				currentPage: undefined,
-				pages: undefined,
-				objectsPerPage: 20,
-
-				mapping: {
-					pages: "sys.pagination.pages",
-					objectsPerPage: "sys.pagination.objectsPerPage",
-					currentPage: "sys.pagination.currentPage",
-				},
-
-				pagination: {
-					items: [],
-				},
-			},
-			initOptionsFromArguments.call(this),
-		);
-	}
-
-	/**
-	 *
-	 * @return {string}
-	 */
-	static getTag() {
-		return "monster-pagination";
-	}
-
-	/**
-	 * @return {void}
-	 */
-	disconnectedCallback() {
-		super.disconnectedCallback();
-		if (this?.[resizeObserverSymbol] instanceof ResizeObserver) {
-			this[resizeObserverSymbol].disconnect();
-		}
-	}
-
-	/**
-	 * @return {void}
-	 */
-	connectedCallback() {
-		super.connectedCallback();
-
-		const parentNode = this.parentNode;
-		if (!parentNode) {
-			return;
-		}
-
-		const parentParentNode = parentNode?.parentNode || parentNode;
-
-		const parentWidth = parentParentNode.offsetWidth;
-		const ownWidth = this.offsetWidth;
-
-		this[sizeDataSymbol] = {
-			last: {
-				parentWidth: parentParentNode.offsetWidth || 0,
-			},
-			showNumbers: ownWidth < parentWidth,
-		};
-
-		handleDataSourceChanges.call(this);
-
-		setTimeout(() => {
-			this[resizeObserverSymbol] = new ResizeObserver((entries) => {
-				if (this[debounceSizeSymbol] instanceof DeadMansSwitch) {
-					try {
-						this[debounceSizeSymbol].touch();
-						return;
-					} catch (e) {
-						delete this[debounceSizeSymbol];
-					}
-				}
-
-				this[debounceSizeSymbol] = new DeadMansSwitch(250, () => {
-					queueMicrotask(() => {
-						const parentWidth = parentParentNode.offsetWidth;
-						const ownWidth = this.clientWidth;
-
-						if (this[sizeDataSymbol]?.last?.parentWidth === parentWidth) {
-							return;
-						}
-
-						this[sizeDataSymbol].last = {
-							parentWidth: parentWidth,
-						};
-
-						this[sizeDataSymbol].showNumbers = ownWidth < parentWidth;
-						handleDataSourceChanges.call(this);
-					});
-				});
-			});
-
-			this[resizeObserverSymbol].observe(this?.parentNode?.parentNode);
-		}, 500);
-	}
-
-	/**
-	 * @return {void}
-	 */
-	[assembleMethodSymbol]() {
-		super[assembleMethodSymbol]();
-
-		initControlReferences.call(this);
-		initEventHandler.call(this);
-
-		const selector = this.getOption("datasource.selector", "");
-
-		if (isString(selector)) {
-			const element = findElementWithSelectorUpwards(this, selector);
-			if (element === null) {
-				throw new Error("the selector must match exactly one element");
-			}
-
-			if (!(element instanceof Datasource)) {
-				throw new TypeError("the element must be a datasource");
-			}
-
-			this[datasourceLinkedElementSymbol] = element;
-			element.datasource.attachObserver(
-				new Observer(handleDataSourceChanges.bind(this)),
-			);
-
-			handleDataSourceChanges.call(this);
-		}
-	}
-
-	/**
-	 * @private
-	 * @return {CSSStyleSheet}
-	 */
-	static getControlCSSStyleSheet() {
-		return PaginationStyleSheet;
-	}
-
-	/**
-	 * @return {CSSStyleSheet[]}
-	 */
-	static getCSSStyleSheet() {
-		return [this.getControlCSSStyleSheet(), DisplayStyleSheet, ThemeStyleSheet];
-	}
+    /**
+     */
+    constructor() {
+        super();
+        this[datasourceLinkedElementSymbol] = null;
+    }
+
+    /**
+     * This method is called by the `instanceof` operator.
+     * @return {symbol}
+     */
+    static get [instanceSymbol]() {
+        return Symbol.for("@schukai/monster/components/pagination");
+    }
+
+    /**
+     * To set the options via the HTML tag, the attribute `data-monster-options` must be used.
+     * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
+     *
+     * The individual configuration values can be found in the table.
+     *
+     * @property {Object} templates Template definitions
+     * @property {string} templates.main Main template
+     * @property {Object} datasource Datasource configuration
+     * @property {string} datasource.selector Datasource selector
+     * @property {Object} labels Label definitions
+     * @property {string} labels.page Page label
+     * @property {string} labels.description Description label
+     * @property {string} labels.previous Previous label
+     * @property {string} labels.next Next label
+     * @property {string} labels.of Of label
+     * @property {string} href Href
+     * @property {number} currentPage Current page
+     * @property {number} pages Pages
+     * @property {number} objectsPerPage Objects per page
+     * @property {Object} mapping Mapping
+     * @property {string} mapping.pages Pages mapping
+     * @property {string} mapping.objectsPerPage Objects per page mapping
+     * @property {string} mapping.currentPage Current page mapping
+     */
+    get defaults() {
+        return Object.assign(
+            {},
+            super.defaults,
+            {
+                templates: {
+                    main: getTemplate(),
+                },
+
+                datasource: {
+                    selector: null,
+                },
+
+                labels: {
+                    page: "${page}",
+                    description: "Page ${page}",
+                    previous: "Previous",
+                    next: "Next",
+                    of: "of",
+                },
+
+                href: "page-${page}",
+
+                pages: null,
+                objectsPerPage: 20,
+                currentPage: null,
+
+                mapping: {
+                    pages: "sys.pagination.pages",
+                    objectsPerPage: "sys.pagination.objectsPerPage",
+                    currentPage: "sys.pagination.currentPage",
+                },
+
+                /* @private */
+                pagination: {
+                    items: [],
+                },
+            },
+            initOptionsFromArguments.call(this),
+        );
+    }
+
+    /**
+     *
+     * @return {string}
+     */
+    static getTag() {
+        return "monster-pagination";
+    }
+
+    /**
+     * @return {void}
+     */
+    disconnectedCallback() {
+        super.disconnectedCallback();
+        if (this?.[resizeObserverSymbol] instanceof ResizeObserver) {
+            this[resizeObserverSymbol].disconnect();
+        }
+    }
+
+    /**
+     * @return {void}
+     */
+    connectedCallback() {
+        super.connectedCallback();
+
+        const parentNode = this.parentNode;
+        if (!parentNode) {
+            return;
+        }
+
+        try {
+            handleDataSourceChanges.call(this);
+        } catch (e) {
+            addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e?.message || `${e}`);
+        }
+
+        requestAnimationFrame(() => {
+            const parentParentNode = parentNode?.parentNode || parentNode;
+
+            const parentWidth = parentParentNode.offsetWidth;
+            const ownWidth = this.offsetWidth;
+
+            this[sizeDataSymbol] = {
+                last: {
+                    parentWidth: 0,
+                },
+                showNumbers: ownWidth < parentWidth,
+            };
+
+            this[resizeObserverSymbol] = new ResizeObserver((entries) => {
+                if (this[debounceSizeSymbol] instanceof DeadMansSwitch) {
+                    try {
+                        this[debounceSizeSymbol].touch();
+                        return;
+                    } catch (e) {
+                        delete this[debounceSizeSymbol];
+                    }
+                }
+
+                this[debounceSizeSymbol] = new DeadMansSwitch(250, () => {
+                    queueMicrotask(() => {
+                        const parentWidth = parentParentNode.offsetWidth;
+                        const ownWidth = this.clientWidth;
+
+                        if (this[sizeDataSymbol]?.last?.parentWidth === parentWidth) {
+                            return;
+                        }
+
+                        this[sizeDataSymbol].last = {
+                            parentWidth: parentWidth,
+                        };
+
+                        this[sizeDataSymbol].showNumbers = ownWidth <= parentWidth;
+                        handleDataSourceChanges.call(this);
+                    });
+                });
+            });
+
+            this[resizeObserverSymbol].observe(this?.parentNode?.parentNode);
+        });
+    }
+
+    /**
+     * @return {void}
+     */
+    [assembleMethodSymbol]() {
+        super[assembleMethodSymbol]();
+
+        initControlReferences.call(this);
+        initEventHandler.call(this);
+
+        const selector = this.getOption("datasource.selector", "");
+
+        if (isString(selector)) {
+            const element = findElementWithSelectorUpwards(this, selector);
+            if (element === null) {
+                throw new Error("the selector must match exactly one element");
+            }
+
+            if (!(element instanceof Datasource)) {
+                throw new TypeError("the element must be a datasource");
+            }
+
+            this[datasourceLinkedElementSymbol] = element;
+            element.datasource.attachObserver(
+                new Observer(handleDataSourceChanges.bind(this)),
+            );
+
+            element.attachObserver(
+                new Observer(handleDataSourceChanges.bind(this)),
+            );
+
+            handleDataSourceChanges.call(this);
+        }
+    }
+
+    /**
+     * @private
+     * @return {CSSStyleSheet}
+     */
+    static getControlCSSStyleSheet() {
+        return PaginationStyleSheet;
+    }
+
+    /**
+     * @return {CSSStyleSheet[]}
+     */
+    static getCSSStyleSheet() {
+        return [this.getControlCSSStyleSheet(), DisplayStyleSheet, ThemeStyleSheet];
+    }
 }
 
 /**
@@ -288,87 +300,88 @@ class Pagination extends CustomElement {
  * @throws {Error} no shadow-root is defined
  */
 function initControlReferences() {
-	if (!this.shadowRoot) {
-		throw new Error("no shadow-root is defined");
-	}
+    if (!this.shadowRoot) {
+        throw new Error("no shadow-root is defined");
+    }
 
-	this[paginationElementSymbol] = this.shadowRoot.querySelector(
-		"[data-monster-role=pagination]",
-	);
+    this[paginationElementSymbol] = this.shadowRoot.querySelector(
+        "[data-monster-role=pagination]",
+    );
 }
 
 /**
  * @private
  */
 function initEventHandler() {
-	const self = this;
-
-	self[paginationElementSymbol].addEventListener("click", function (event) {
-		let element = null;
-		const datasource = self[datasourceLinkedElementSymbol];
-		if (!datasource) {
-			return;
-		}
-
-		element = findTargetElementFromEvent(
-			event,
-			ATTRIBUTE_ROLE,
-			"pagination-item",
-		);
-		if (!element) {
-			element = findTargetElementFromEvent(
-				event,
-				ATTRIBUTE_ROLE,
-				"pagination-next",
-			);
-			if (!element) {
-				element = findTargetElementFromEvent(
-					event,
-					ATTRIBUTE_ROLE,
-					"pagination-prev",
-				);
-				if (!element) {
-					return;
-				}
-			}
-		}
-
-		if (!(element instanceof HTMLElement)) {
-			return;
-		}
-
-		let page = null;
-
-		if (!element.hasAttribute("data-page-no")) {
-			return;
-		}
-
-		page = element.getAttribute("data-page-no");
-		event.preventDefault();
-
-		if (
-			!page ||
-			page === "" ||
-			page === null ||
-			page === undefined ||
-			page === "undefined" ||
-			page === "null"
-		) {
-			return;
-		}
-
-		if (typeof datasource.setParameters !== "function") {
-			return;
-		}
-
-		datasource.setParameters({ page });
-
-		if (typeof datasource.reload !== "function") {
-			return;
-		}
-
-		datasource.reload();
-	});
+    const self = this;
+
+    self[paginationElementSymbol].addEventListener("click", function (event) {
+        let element = null;
+        const datasource = self[datasourceLinkedElementSymbol];
+        if (!datasource) {
+            return;
+        }
+
+        element = findTargetElementFromEvent(
+            event,
+            ATTRIBUTE_ROLE,
+            "pagination-item",
+        );
+
+        if (!element) {
+            element = findTargetElementFromEvent(
+                event,
+                ATTRIBUTE_ROLE,
+                "pagination-next",
+            );
+            if (!element) {
+                element = findTargetElementFromEvent(
+                    event,
+                    ATTRIBUTE_ROLE,
+                    "pagination-prev",
+                );
+                if (!element) {
+                    return;
+                }
+            }
+        }
+
+        if (!(element instanceof HTMLElement)) {
+            return;
+        }
+
+        let page = null;
+
+        if (!element.hasAttribute("data-page-no")) {
+            return;
+        }
+
+        page = element.getAttribute("data-page-no");
+
+        if (
+            !page ||
+            page === "" ||
+            page === null ||
+            page === undefined ||
+            page === "undefined" ||
+            page === "null"
+        ) {
+            return;
+        }
+
+        if (typeof datasource.setParameters !== "function") {
+            return;
+        }
+
+        event.preventDefault();
+        datasource.setParameters({page});
+
+        if (typeof datasource.reload !== "function") {
+            return;
+        }
+
+        datasource.reload();
+    });
 }
 
 /**
@@ -384,53 +397,58 @@ function initEventHandler() {
  * @throws {Error} the datasource could not be initialized
  */
 function initOptionsFromArguments() {
-	const options = {};
-	const selector = this.getAttribute(ATTRIBUTE_DATASOURCE_SELECTOR);
-	if (selector) {
-		options.datasource = { selector: selector };
-	}
+    const options = {};
+    const selector = this.getAttribute(ATTRIBUTE_DATASOURCE_SELECTOR);
+    if (selector) {
+        options.datasource = {selector: selector};
+    }
 
-	return options;
+    return options;
 }
 
 /**
  * @private
  */
 function handleDataSourceChanges() {
-	let pagination;
-
-	if (!this[datasourceLinkedElementSymbol]) {
-		return;
-	}
-
-	const mapping = this.getOption("mapping");
-	for (const key in mapping) {
-		const path = mapping[key];
-
-		let value;
-		try {
-			value = new Pathfinder(this[datasourceLinkedElementSymbol].data).getVia(
-				path,
-			);
-			this.setOption(key, value);
-		} catch (e) {
-			addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
-		}
-	}
-
-	pagination = buildPagination.call(
-		this,
-		this.getOption("currentPage"),
-		this.getOption("pages"),
-	);
-
-	if (this?.[sizeDataSymbol]?.showNumbers !== true) {
-		pagination.items = [];
-	}
-
-	getWindow().requestAnimationFrame(() => {
-		this.setOption("pagination", pagination);
-	});
+    let pagination;
+
+    if (!this[datasourceLinkedElementSymbol]) {
+        return;
+    }
+
+    const mapping = this.getOption("mapping");
+    const pf = new Pathfinder(this[datasourceLinkedElementSymbol].data);
+
+    for (const key in mapping) {
+        const path = mapping[key];
+
+        if (pf.exists(path)) {
+            const value = pf.getVia(
+                path,
+            );
+            this.setOption(key, value);
+        }
+
+        const o = this[datasourceLinkedElementSymbol].getOption(path);
+        if (o !== undefined && o !== null) {
+            this.setOption(key, o);
+        }
+
+    }
+
+    pagination = buildPagination.call(
+        this,
+        this.getOption("currentPage"),
+        this.getOption("pages"),
+    );
+
+    if (this?.[sizeDataSymbol]?.showNumbers !== true) {
+        pagination.items = [];
+    }
+
+    getWindow().requestAnimationFrame(() => {
+        this.setOption("pagination", pagination);
+    });
 }
 
 /**
@@ -440,88 +458,92 @@ function handleDataSourceChanges() {
  * @return  {object}
  */
 function buildPagination(current, max) {
-	let prev = current === 1 ? null : current - 1;
-	let next = current === max ? null : current + 1;
-	const itemList = [1];
-
-	if (current > 4) itemList.push("…");
-
-	const r = 2;
-	const r1 = current - r;
-	const r2 = current + r;
-
-	for (let i = r1 > 2 ? r1 : 2; i <= Math.min(max, r2); i++) itemList.push(i);
-
-	if (r2 + 1 < max) itemList.push("…");
-	if (r2 < max) itemList.push(max);
-
-	let prevClass = "";
-
-	if (prev === null) {
-		prevClass = " disabled";
-	}
-
-	let nextClass = "";
-	if (next === null) {
-		nextClass = " disabled";
-	}
-
-	const items = itemList.map((item) => {
-		const p = `${item}`;
-		const c = `${current}`;
-
-		const obj = {
-			pageNo: item, // as integer
-			page: p, // as string
-			current: p === c,
-			class: (p === c ? "current" : "").trim(),
-		};
-
-		if (p === "…") {
-			obj.class += " disabled".trim();
-		}
-
-		const formatter = new Formatter(obj);
-
-		obj.description = formatter.format(this.getOption("labels.description"));
-		obj.label = formatter.format(this.getOption("labels.page"));
-		obj.href =
-			p === "…"
-				? "#"
-				: p === c
-					? "#"
-					: p === "1"
-						? "#"
-						: `#${formatter.format(this.getOption("href"))}`;
-		return obj;
-	});
-
-	const nextNo = next;
-	next = `${next}`;
-
-	const nextHref =
-		next === "null"
-			? "#"
-			: `#${new Formatter({ page: next }).format(this.getOption("href"))}`;
-	const prevNo = prev;
-	prev = `${prev}`;
-	const prevHref =
-		prev === "null"
-			? "#"
-			: `#${new Formatter({ page: prev }).format(this.getOption("href"))}`;
-
-	return {
-		current,
-		nextNo,
-		next,
-		nextClass,
-		nextHref,
-		prevNo,
-		prev,
-		prevClass,
-		prevHref,
-		items,
-	};
+
+    current = parseInt(current, 10);
+    max = parseInt(max, 10);
+
+    let prev = current === 1 ? null : current - 1;
+    let next = current === max ? null : current + 1;
+    const itemList = [1];
+
+    if (current > 4) itemList.push("…");
+
+    const r = 2;
+    const r1 = current - r;
+    const r2 = current + r;
+
+    for (let i = r1 > 2 ? r1 : 2; i <= Math.min(max, r2); i++) itemList.push(i);
+
+    if (r2 + 1 < max) itemList.push("…");
+    if (r2 < max) itemList.push(max);
+
+    let prevClass = "";
+
+    if (prev === null) {
+        prevClass = " disabled";
+    }
+
+    let nextClass = "";
+    if (next === null) {
+        nextClass = " disabled";
+    }
+
+    const items = itemList.map((item) => {
+        const p = `${item}`;
+        const c = `${current}`;
+
+        const obj = {
+            pageNo: item, // as integer
+            page: p, // as string
+            current: p === c,
+            class: (p === c ? "current" : "").trim(),
+        };
+
+        if (p === "…") {
+            obj.class += " disabled".trim();
+        }
+
+        const formatter = new Formatter(obj);
+
+        obj.description = formatter.format(this.getOption("labels.description"));
+        obj.label = formatter.format(this.getOption("labels.page"));
+        obj.href =
+            p === "…"
+                ? "#"
+                : p === c
+                    ? "#"
+                    : p === "1"
+                        ? "#"
+                        : `#${formatter.format(this.getOption("href"))}`;
+        return obj;
+    });
+
+    const nextNo = next;
+    next = `${next}`;
+
+    const nextHref =
+        next === "null"
+            ? "#"
+            : `#${new Formatter({page: next}).format(this.getOption("href"))}`;
+    const prevNo = prev;
+    prev = `${prev}`;
+    const prevHref =
+        prev === "null"
+            ? "#"
+            : `#${new Formatter({page: prev}).format(this.getOption("href"))}`;
+
+    return {
+        current,
+        nextNo,
+        next,
+        nextClass,
+        nextHref,
+        prevNo,
+        prev,
+        prevClass,
+        prevHref,
+        items,
+    };
 }
 
 /**
@@ -529,8 +551,8 @@ function buildPagination(current, max) {
  * @return {string}
  */
 function getTemplate() {
-	// language=HTML
-	return `
+    // language=HTML
+    return `
         <template id="items">
             <li><a data-monster-attributes="class path:items.class,
                                             href path:items.href,
diff --git a/source/components/datatable/status.mjs b/source/components/datatable/status.mjs
index 99240e660..046d0543b 100644
--- a/source/components/datatable/status.mjs
+++ b/source/components/datatable/status.mjs
@@ -89,7 +89,7 @@ class DatasourceStatus extends CustomElement {
 	 */
 	static get [instanceSymbol]() {
 		return Symbol.for(
-			"@schukai/monster/components/datatables/status@@instance",
+			"@schukai/monster/components/datatable/status@@instance",
 		);
 	}
 
diff --git a/source/components/datatable/style/pagination.pcss b/source/components/datatable/style/pagination.pcss
index 1ebe044b7..6ca49352c 100644
--- a/source/components/datatable/style/pagination.pcss
+++ b/source/components/datatable/style/pagination.pcss
@@ -33,9 +33,9 @@
 
     & ul li a {
         @mixin button;
-        background-color: var(--monster-theme-control-bg-color);
+        background-color: var(--monster-bg-color-primary-1);
         color: var(--monster-theme-control-color);
-        border-color: var(--monster-theme-control-bg-color);
+        border-color: var(--monster-bg-color-primary-1);
         width: max-content;
 
         &.current {
diff --git a/source/components/datatable/stylesheet/pagination.mjs b/source/components/datatable/stylesheet/pagination.mjs
index 43de9782f..fe03e3e99 100644
--- a/source/components/datatable/stylesheet/pagination.mjs
+++ b/source/components/datatable/stylesheet/pagination.mjs
@@ -10,10 +10,10 @@
  * 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";
+import {addAttributeToken} from "../../../dom/attributes.mjs";
+import {ATTRIBUTE_ERRORMESSAGE} from "../../../dom/constants.mjs";
 
-export { PaginationStyleSheet };
+export {PaginationStyleSheet}
 
 /**
  * @private
@@ -22,17 +22,10 @@ export { PaginationStyleSheet };
 const PaginationStyleSheet = new CSSStyleSheet();
 
 try {
-	PaginationStyleSheet.insertRule(
-		`
+  PaginationStyleSheet.insertRule(`
 @layer pagination { 
-:where(html){line-height:1.15;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;text-size-adjust:100%}:where(h1){font-size:2em;margin-block-end:.67em;margin-block-start:.67em}:where(dl,ol,ul) :where(dl,ol,ul){margin-block-end:0;margin-block-start:0}:where(hr){box-sizing:content-box;color:inherit;height:0}:where(abbr[title]){text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}:where(b,strong){font-weight:bolder}:where(code,kbd,pre,samp){font-family:monospace,monospace;font-size:1em}:where(small){font-size:80%}:where(table){border-color:currentColor;text-indent:0}:where(button,input,select){margin:0}:where(button){text-transform:none}:where(button,input:is([type=button i],[type=reset i],[type=submit i])){-webkit-appearance:button}:where(progress){vertical-align:baseline}:where(select){text-transform:none}:where(textarea){margin:0}:where(input[type=search i]){-webkit-appearance:textfield;outline-offset:-2px}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}::-webkit-input-placeholder{color:inherit;opacity:.54}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}:where(button,input:is([type=button i],[type=color i],[type=reset i],[type=submit i]))::-moz-focus-inner{border-style:none;padding:0}:where(button,input:is([type=button i],[type=color i],[type=reset i],[type=submit i]))::-moz-focusring{outline:1px dotted ButtonText}:where(:-moz-ui-invalid){box-shadow:none}:where(dialog){background-color:#fff;border:solid;color:#000;height:-moz-fit-content;height:fit-content;left:0;margin:auto;padding:1em;position:absolute;right:0;width:-moz-fit-content;width:fit-content}:where(dialog:not([open])){display:none}:where(summary){display:list-item}html{height:100%}body,html{min-height:calc(100vh - 40px)}body{box-sizing:border-box;margin:0;padding:0;word-break:break-word}.block{display:block}.inline{display:inline}.inline-block{display:inline-block}.grid{display:grid}.inline-grid{display:inline-grid}.flex{display:flex}.inline-flex{display:inline-flex}.hidden,.hide,.none{display:none}.visible{visibility:visible}.invisible{visibility:hidden}.monster-button-primary,button{align-items:center;background-color:var(--monster-bg-color-primary-1);background-position:50%;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);color:var(--monster-color-primary-1);cursor:pointer;display:flex;font-family:var(--monster-font-family);font-size:1rem;font-weight:400;gap:.4rem;justify-content:center;line-height:1.5;outline:none;overflow:hidden;padding:.375rem .75rem;position:relative;text-align:center;text-decoration:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;vertical-align:middle;width:-webkit-fill-available;width:-moz-available;width:stretch}.monster-button-primary{background-color:var(--monster-bg-color-primary-4);border-color:var(--monster-bg-color-primary-4);color:var(--monster-color-primary-4)}.monster-button-secondary{background-color:var(--monster-bg-color-primary-1);background-color:var(--monster-bg-color-secondary-4);border-color:var(--monster-bg-color-secondary-4);border-radius:var(--monster-border-radius);border-style:var(--monster-border-style);border-width:var(--monster-border-width);color:var(--monster-color-primary-1);color:var(--monster-color-secondary-4)}.monster-button-secondary,.monster-button-tertiary{align-items:center;background-position:50%;box-shadow:var(--monster-box-shadow-1);cursor:pointer;display:flex;font-family:var(--monster-font-family);font-size:1rem;font-weight:400;gap:.4rem;justify-content:center;line-height:1.5;outline:none;overflow:hidden;padding:.375rem .75rem;position:relative;text-align:center;text-decoration:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;vertical-align:middle;width:-webkit-fill-available;width:-moz-available;width:stretch}.monster-button-tertiary{background-color:var(--monster-bg-color-primary-1);background-color:var(--monster-bg-color-tertiary-4);border-color:var(--monster-bg-color-tertiary-4);border-radius:var(--monster-border-radius);border-style:var(--monster-border-style);border-width:var(--monster-border-width);color:var(--monster-color-primary-1);color:var(--monster-color-tertiary-4)}.monster-button-outline-primary{background-color:var(--monster-bg-color-primary-1);background-color:var(--monster-color-primary-4);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);color:var(--monster-color-primary-1);color:var(--monster-bg-color-primary-4)}.monster-button-outline-primary,.monster-button-outline-secondary{align-items:center;background-position:50%;box-shadow:var(--monster-box-shadow-1);cursor:pointer;display:flex;font-family:var(--monster-font-family);font-size:1rem;font-weight:400;gap:.4rem;justify-content:center;line-height:1.5;outline:none;overflow:hidden;padding:.375rem .75rem;position:relative;text-align:center;text-decoration:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;vertical-align:middle;width:-webkit-fill-available;width:-moz-available;width:stretch}.monster-button-outline-secondary{background-color:var(--monster-bg-color-primary-1);background-color:var(--monster-color-secondary-4);border-color:var(--monster-bg-color-secondary-4);border-radius:var(--monster-border-radius);border-style:var(--monster-border-style);border-width:var(--monster-border-width);color:var(--monster-color-primary-1);color:var(--monster-bg-color-secondary-4)}.monster-button-outline-tertiary{align-items:center;background-color:var(--monster-bg-color-primary-1);background-color:var(--monster-color-tertiary-4);background-position:50%;border-color:var(--monster-bg-color-tertiary-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);color:var(--monster-color-primary-1);color:var(--monster-bg-color-tertiary-4);cursor:pointer;display:flex;font-family:var(--monster-font-family);font-size:1rem;font-weight:400;gap:.4rem;justify-content:center;line-height:1.5;outline:none;overflow:hidden;padding:.375rem .75rem;position:relative;text-align:center;text-decoration:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;vertical-align:middle;width:-webkit-fill-available;width:-moz-available;width:stretch}button:active,button:hover{box-shadow:var(--monster-box-shadow-2);transition:background .8s,color .25s .0833333333s}button:active{z-index:var(--monster-z-index-outline)}.monster-button-bar,.monster-button-group{align-content:center;align-items:stretch;display:flex;flex-direction:row;flex-wrap:nowrap;justify-content:space-between}.monster-button-group{box-sizing:border-box;gap:0;margin:1rem 0}.monster-button-group>:not(:last-child){margin-right:calc(var(--monster-border-width)*-1)}.monster-button-group :hover{box-shadow:none}button:focus{outline:1px dashed var(--monster-color-selection-4);outline-offset:2px;z-index:var(--monster-z-index-outline)}@media (prefers-color-scheme:light){button:focus{outline:1px dashed var(--monster-color-selection-3);outline-offset:2px;z-index:var(--monster-z-index-outline)}}[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-font-family-monospace:\"Consolas\",\"Courier New\",\"Roboto Mono\",\"Source Code Pro\",\"Fira Mono\",monospace;--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}span.monster-fx-ripple{animation:monster-fx-ripple .6s linear;background-color:hsla(0,0%,100%,.7);border-radius:50%;position:absolute;transform:scale(0)}@keyframes monster-fx-ripple{to{opacity:0;transform:scale(4)}}[data-monster-role=pagination]{box-sizing:border-box;display:flex;font-size:1rem;font-weight:400;justify-content:center;line-height:1.6}[data-monster-role=pagination] ul{align-items:center;display:flex;flex-wrap:wrap;justify-content:center;list-style:none;margin:20px 0;padding-left:0}[data-monster-role=pagination] ul li{margin:0 1px}[data-monster-role=pagination] ul li a{align-items:center;background-color:var(--monster-bg-color-primary-1);background-color:var(--monster-theme-control-bg-color);background-position:50%;border-color:var(--monster-theme-control-bg-color);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);color:var(--monster-color-primary-1);color:var(--monster-theme-control-color);cursor:pointer;display:flex;font-family:var(--monster-font-family);font-size:1rem;font-weight:400;gap:.4rem;justify-content:center;line-height:1.5;outline:none;overflow:hidden;padding:.375rem .75rem;position:relative;text-align:center;text-decoration:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;vertical-align:middle;width:-webkit-fill-available;width:-moz-available;width:stretch;width:-moz-max-content;width:max-content}.current:is([data-monster-role=pagination] ul li a){background-color:var(--monster-bg-color-primary-4);border-color:var(--monster-bg-color-primary-4);color:var(--monster-color-primary-4);cursor:unset}.disabled:is([data-monster-role=pagination] ul li a){background-color:var(--monster-bg-color-primary-disabled-1);color:var(--monster-color-primary-disabled-1);cursor:not-allowed} 
-}`,
-		0,
-	);
+:where(html){line-height:1.15;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;text-size-adjust:100%}:where(h1){font-size:2em;margin-block-end:.67em;margin-block-start:.67em}:where(dl,ol,ul) :where(dl,ol,ul){margin-block-end:0;margin-block-start:0}:where(hr){box-sizing:content-box;color:inherit;height:0}:where(abbr[title]){text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}:where(b,strong){font-weight:bolder}:where(code,kbd,pre,samp){font-family:monospace,monospace;font-size:1em}:where(small){font-size:80%}:where(table){border-color:currentColor;text-indent:0}:where(button,input,select){margin:0}:where(button){text-transform:none}:where(button,input:is([type=button i],[type=reset i],[type=submit i])){-webkit-appearance:button}:where(progress){vertical-align:baseline}:where(select){text-transform:none}:where(textarea){margin:0}:where(input[type=search i]){-webkit-appearance:textfield;outline-offset:-2px}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}::-webkit-input-placeholder{color:inherit;opacity:.54}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}:where(button,input:is([type=button i],[type=color i],[type=reset i],[type=submit i]))::-moz-focus-inner{border-style:none;padding:0}:where(button,input:is([type=button i],[type=color i],[type=reset i],[type=submit i]))::-moz-focusring{outline:1px dotted ButtonText}:where(:-moz-ui-invalid){box-shadow:none}:where(dialog){background-color:#fff;border:solid;color:#000;height:-moz-fit-content;height:fit-content;left:0;margin:auto;padding:1em;position:absolute;right:0;width:-moz-fit-content;width:fit-content}:where(dialog:not([open])){display:none}:where(summary){display:list-item}html{height:100%}body,html{min-height:calc(100vh - 40px)}body{box-sizing:border-box;margin:0;padding:0;word-break:break-word}body:focus-visible{outline:none}:focus-visible{outline:none}.block{display:block}.inline{display:inline}.inline-block{display:inline-block}.grid{display:grid}.inline-grid{display:inline-grid}.flex{display:flex}.inline-flex{display:inline-flex}.hidden,.hide,.none{display:none}.visible{visibility:visible}.invisible{visibility:hidden}.monster-button-primary,button{align-items:center;background-color:var(--monster-bg-color-primary-1);background-position:50%;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);color:var(--monster-color-primary-1);cursor:pointer;display:flex;font-family:var(--monster-font-family);font-size:1rem;font-weight:400;gap:.4rem;justify-content:center;line-height:1.5;outline:none;overflow:hidden;padding:.375rem .75rem;position:relative;text-align:center;text-decoration:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;vertical-align:middle;width:-webkit-fill-available;width:-moz-available;width:stretch}.monster-button-primary{background-color:var(--monster-bg-color-primary-4);border-color:var(--monster-bg-color-primary-4);color:var(--monster-color-primary-4)}.monster-button-secondary{background-color:var(--monster-bg-color-primary-1);background-color:var(--monster-bg-color-secondary-4);border-color:var(--monster-bg-color-secondary-4);border-radius:var(--monster-border-radius);border-style:var(--monster-border-style);border-width:var(--monster-border-width);color:var(--monster-color-primary-1);color:var(--monster-color-secondary-4)}.monster-button-secondary,.monster-button-tertiary{align-items:center;background-position:50%;box-shadow:var(--monster-box-shadow-1);cursor:pointer;display:flex;font-family:var(--monster-font-family);font-size:1rem;font-weight:400;gap:.4rem;justify-content:center;line-height:1.5;outline:none;overflow:hidden;padding:.375rem .75rem;position:relative;text-align:center;text-decoration:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;vertical-align:middle;width:-webkit-fill-available;width:-moz-available;width:stretch}.monster-button-tertiary{background-color:var(--monster-bg-color-primary-1);background-color:var(--monster-bg-color-tertiary-4);border-color:var(--monster-bg-color-tertiary-4);border-radius:var(--monster-border-radius);border-style:var(--monster-border-style);border-width:var(--monster-border-width);color:var(--monster-color-primary-1);color:var(--monster-color-tertiary-4)}.monster-button-outline-primary{background-color:var(--monster-bg-color-primary-1);background-color:var(--monster-color-primary-4);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);color:var(--monster-color-primary-1);color:var(--monster-bg-color-primary-4)}.monster-button-outline-primary,.monster-button-outline-secondary{align-items:center;background-position:50%;box-shadow:var(--monster-box-shadow-1);cursor:pointer;display:flex;font-family:var(--monster-font-family);font-size:1rem;font-weight:400;gap:.4rem;justify-content:center;line-height:1.5;outline:none;overflow:hidden;padding:.375rem .75rem;position:relative;text-align:center;text-decoration:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;vertical-align:middle;width:-webkit-fill-available;width:-moz-available;width:stretch}.monster-button-outline-secondary{background-color:var(--monster-bg-color-primary-1);background-color:var(--monster-color-secondary-4);border-color:var(--monster-bg-color-secondary-4);border-radius:var(--monster-border-radius);border-style:var(--monster-border-style);border-width:var(--monster-border-width);color:var(--monster-color-primary-1);color:var(--monster-bg-color-secondary-4)}.monster-button-outline-tertiary{align-items:center;background-color:var(--monster-bg-color-primary-1);background-color:var(--monster-color-tertiary-4);background-position:50%;border-color:var(--monster-bg-color-tertiary-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);color:var(--monster-color-primary-1);color:var(--monster-bg-color-tertiary-4);cursor:pointer;display:flex;font-family:var(--monster-font-family);font-size:1rem;font-weight:400;gap:.4rem;justify-content:center;line-height:1.5;outline:none;overflow:hidden;padding:.375rem .75rem;position:relative;text-align:center;text-decoration:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;vertical-align:middle;width:-webkit-fill-available;width:-moz-available;width:stretch}button:active,button:hover{box-shadow:var(--monster-box-shadow-2);transition:background .8s,color .25s .0833333333s}button:active{z-index:var(--monster-z-index-outline)}.monster-button-bar,.monster-button-group{align-content:center;align-items:stretch;display:flex;flex-direction:row;flex-wrap:nowrap;justify-content:space-between}.monster-button-group{box-sizing:border-box;gap:0;margin:1rem 0}.monster-button-group>:not(:last-child){margin-right:calc(var(--monster-border-width)*-1)}.monster-button-group :hover{box-shadow:none}button:focus{outline:1px dashed var(--monster-color-selection-4);outline-offset:2px;z-index:var(--monster-z-index-outline)}@media (prefers-color-scheme:light){button:focus{outline:1px dashed var(--monster-color-selection-3);outline-offset:2px;z-index:var(--monster-z-index-outline)}}[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-font-family-monospace:\"Consolas\",\"Courier New\",\"Roboto Mono\",\"Source Code Pro\",\"Fira Mono\",monospace;--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-outline-width:1px;--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}span.monster-fx-ripple{animation:monster-fx-ripple .6s linear;background-color:hsla(0,0%,100%,.7);border-radius:50%;position:absolute;transform:scale(0)}@keyframes monster-fx-ripple{to{opacity:0;transform:scale(4)}}[data-monster-role=pagination]{box-sizing:border-box;display:flex;font-size:1rem;font-weight:400;justify-content:center;line-height:1.6}[data-monster-role=pagination] ul{align-items:center;display:flex;flex-wrap:wrap;justify-content:center;list-style:none;margin:20px 0;padding-left:0}[data-monster-role=pagination] ul li{margin:0 1px}[data-monster-role=pagination] ul li a{align-items:center;background-color:var(--monster-bg-color-primary-1);background-position:50%;border-color:var(--monster-bg-color-primary-1);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);color:var(--monster-color-primary-1);color:var(--monster-theme-control-color);cursor:pointer;display:flex;font-family:var(--monster-font-family);font-size:1rem;font-weight:400;gap:.4rem;justify-content:center;line-height:1.5;outline:none;overflow:hidden;padding:.375rem .75rem;position:relative;text-align:center;text-decoration:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;vertical-align:middle;width:-webkit-fill-available;width:-moz-available;width:stretch;width:-moz-max-content;width:max-content}.current:is([data-monster-role=pagination] ul li a){background-color:var(--monster-bg-color-primary-4);border-color:var(--monster-bg-color-primary-4);color:var(--monster-color-primary-4);cursor:unset}.disabled:is([data-monster-role=pagination] ul li a){background-color:var(--monster-bg-color-primary-disabled-1);color:var(--monster-color-primary-disabled-1);cursor:not-allowed} 
+}`, 0);
 } catch (e) {
-	addAttributeToken(
-		document.getRootNode().querySelector("html"),
-		ATTRIBUTE_ERRORMESSAGE,
-		e + "",
-	);
+  addAttributeToken(document.getRootNode().querySelector('html'), ATTRIBUTE_ERRORMESSAGE, e + "");
 }
diff --git a/source/components/datatable/util.mjs b/source/components/datatable/util.mjs
index 63478f05a..2743723de 100644
--- a/source/components/datatable/util.mjs
+++ b/source/components/datatable/util.mjs
@@ -29,6 +29,7 @@ const datasourceLinkedElementSymbol = Symbol("datasourceLinkedElement");
  * @private
  */
 function handleDataSourceChanges() {
+
 	if (!this[datasourceLinkedElementSymbol]) {
 		return;
 	}
diff --git a/source/components/host/config-manager.mjs b/source/components/host/config-manager.mjs
index d5194e2c2..bd6b3d349 100644
--- a/source/components/host/config-manager.mjs
+++ b/source/components/host/config-manager.mjs
@@ -238,12 +238,10 @@ function clearObjectStore() {
 	return new Promise((resolve, reject) => {
 		const req = store.clear();
 		req.onsuccess = function (evt) {
-			console.log("clearObjectStore completed");
 			resolve();
 		};
 		req.onerror = function (evt) {
-			console.error("clearObjectStore:", evt.target.errorCode);
-			reject(evt.target.errorCode);
+			reject(evt.target?.errorCode);
 		};
 	});
 }
diff --git a/source/data/datasource/server.mjs b/source/data/datasource/server.mjs
index a0d8f4642..f835139ec 100644
--- a/source/data/datasource/server.mjs
+++ b/source/data/datasource/server.mjs
@@ -125,6 +125,7 @@ function doDiff(obj) {
  */
 function doTransform(type, obj) {
 	const transformation = this.getOption(`${type}.mapping.transformer`);
+
 	if (transformation !== undefined && transformation !== null) {
 		const pipe = new Pipe(transformation);
 		const callbacks = this.getOption(`${type}.mapping.callbacks`);
diff --git a/source/data/datasource/server/restapi.mjs b/source/data/datasource/server/restapi.mjs
index 691c6e760..2847d1d49 100644
--- a/source/data/datasource/server/restapi.mjs
+++ b/source/data/datasource/server/restapi.mjs
@@ -18,6 +18,7 @@ import { diff } from "../../diff.mjs";
 import { Server } from "../server.mjs";
 import { WriteError } from "./restapi/writeerror.mjs";
 import { DataFetchError } from "./restapi/data-fetch-error.mjs";
+import {clone} from "../../../util/clone.mjs";
 
 export { RestAPI };
 
@@ -63,7 +64,7 @@ class RestAPI extends Server {
 
 	/**
 	 * @property {Object} write={} Options
-	 * @property {Object} write.init={} An options object containing any custom settings that you want to apply to the request. The parameters are identical to those of the {@link https://developer.mozilla.org/en-US/docs/Web/API/Request/Request|Request constructor}
+	 * @property {Object} write.init={} An option object, containing any custom settings that you want to apply to the request. The parameters are identical to those of the {@link https://developer.mozilla.org/en-US/docs/Web/API/Request/Request|Request constructor}
 	 * @property {string} write.init.method=POST
 	 * @property {Object} write.init.headers Object containing any custom headers that you want to apply to the request.
 	 * @property {string} write.responseCallback Callback function to be executed after the request has been completed.
@@ -71,7 +72,7 @@ class RestAPI extends Server {
 	 * @property {string} write.url URL
 	 * @property {Object} write.mapping the mapping is applied before writing.
 	 * @property {String} write.mapping.transformer Transformer to select the appropriate entries
-	 * @property {Monster.Data.Datasource~exampleCallback[]} write.mapping.callback with the help of the callback, the structures can be adjusted before writing.
+	 * @property {exampleCallback[]} write.mapping.callback with the help of the callback, the structures can be adjusted before writing.
 	 * @property {Object} write.report
 	 * @property {String} write.report.path Path to validations
 	 * @property {Object} write.partial
@@ -80,13 +81,13 @@ class RestAPI extends Server {
 	 * @property {Object} write.sheathing.object Object to be wrapped
 	 * @property {string} write.sheathing.path Path to the data
 	 * @property {Object} read={} Options
-	 * @property {Object} read.init={} An options object containing any custom settings that you want to apply to the request. The parameters are identical to those of the {@link https://developer.mozilla.org/en-US/docs/Web/API/Request/Request|Request constructor}
+	 * @property {Object} read.init={} An option object containing any custom settings that you want to apply to the request. The parameters are identical to those of the {@link https://developer.mozilla.org/en-US/docs/Web/API/Request/Request|Request constructor}
 	 * @property {string} read.init.method=GET
-	 * @property {string} read.acceptedStatus=[200]
+	 * @property {array} read.acceptedStatus=[200]
 	 * @property {string} read.url URL
 	 * @property {Object} read.mapping the mapping is applied after reading.
 	 * @property {String} read.mapping.transformer Transformer to select the appropriate entries
-	 * @property {Monster.Data.Datasource~exampleCallback[]} read.mapping.callback with the help of the callback, the structures can be adjusted after reading.
+	 * @property {exampleCallback[]} read.mapping.callback with the help of the callback, the structures can be adjusted after reading.
 	 */
 	get defaults() {
 		return Object.assign({}, super.defaults, {
@@ -94,19 +95,19 @@ class RestAPI extends Server {
 				init: {
 					method: "POST",
 				},
-				responseCallback: undefined,
+				responseCallback: null,
 				acceptedStatus: [200, 201],
 				url: null,
 				mapping: {
-					transformer: undefined,
+					transformer: null,
 					callbacks: [],
 				},
 				sheathing: {
-					object: undefined,
-					path: undefined,
+					object: null,
+					path: null,
 				},
 				report: {
-					path: undefined,
+					path: null,
 				},
 
 				partial: {
@@ -118,11 +119,11 @@ class RestAPI extends Server {
 					method: "GET",
 				},
 				path: null,
-				responseCallback: undefined,
+				responseCallback: null,
 				acceptedStatus: [200],
 				url: null,
 				mapping: {
-					transformer: undefined,
+					transformer: null,
 					callbacks: [],
 				},
 			},
@@ -175,10 +176,16 @@ class RestAPI extends Server {
 	 * @return {RestAPI}
 	 */
 	getClone() {
-		return new RestAPI(
-			this[internalSymbol].getRealSubject()["options"].read,
-			this[internalSymbol].getRealSubject()["options"].write,
-		);
+		const api =  new RestAPI();
+
+		const read = clone(this[internalSymbol].getRealSubject()["options"].read);
+		const write = clone(this[internalSymbol].getRealSubject()["options"].write);
+
+		api.setOption("read", read);
+		api.setOption("write", write);
+
+		return api;
+
 	}
 }
 
diff --git a/source/dom/customelement.mjs b/source/dom/customelement.mjs
index b8fa1cebc..3249e548c 100644
--- a/source/dom/customelement.mjs
+++ b/source/dom/customelement.mjs
@@ -219,6 +219,40 @@ class CustomElement extends HTMLElement {
 		return Symbol.for("@schukai/monster/dom/custom-element@@instance");
 	}
 
+// 	/**
+// 	 * This method is called by the `instanceof` operator.
+// 	 *
+// 	 * @param instance
+// 	 * @returns {arg is any[]}
+// 	 */
+// 	static [Symbol.hasInstance](instance) {
+// 		if (!isObject(instance)) {
+// 			return false;
+// 		}
+// debugger
+// 		const instanceSym = instance?.[instanceSymbol];
+// 		if (instanceSym === undefined) {
+// 			return false;
+// 		}
+//
+// 		// Direktes Matching des Symbols
+// 		if (instanceSym === this[instanceSymbol]) {
+// 			return true;
+// 		}
+//
+// 		// Prüfe den Prototypenbaum des Objekts für eine Übereinstimmung
+// 		let proto = Object.getPrototypeOf(instance);
+// 		while (proto) {
+// 			const protoSymbol = proto.constructor?.[instanceSymbol];
+// 			if (protoSymbol === this[instanceSymbol]) {
+// 				return true;
+// 			}
+// 			proto = Object.getPrototypeOf(proto); // Gehe weiter den Prototypenbaum entlang
+// 		}
+//
+// 		return false;
+// 	}
+
 	/**
 	 * This method determines which attributes are to be
 	 * monitored by `attributeChangedCallback()`. Unfortunately, this method is static.
diff --git a/source/types/has.mjs b/source/types/has.mjs
new file mode 100644
index 000000000..bb145ed06
--- /dev/null
+++ b/source/types/has.mjs
@@ -0,0 +1,29 @@
+/**
+ * Copyright © schukai GmbH and all contributing authors, {{copyRightYear}}. All rights reserved.
+ * Node module: @schukai/monster
+ *
+ * This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3).
+ * The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.en.html
+ *
+ * For those who do not wish to adhere to the AGPLv3, a commercial license is available.
+ * Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms.
+ * For more information about purchasing a commercial license, please contact schukai GmbH.
+ *
+ * SPDX-License-Identifier: AGPL-3.0
+ */
+
+export {
+    hasImplementation
+};
+
+
+/**
+ * With this function, you can check if a value is iterable.
+ *
+ * @param object
+ * @param methods
+ * @returns {boolean}
+ */
+function hasImplementation(object, methods) {
+    return methods.every(method => typeof object[method] === 'function');
+}
\ No newline at end of file
-- 
GitLab