From 1e45953b05da8177ceab6b13151fe8dbeb39704e Mon Sep 17 00:00:00 2001 From: Volker Schukai <volker.schukai@schukai.com> Date: Sat, 13 Apr 2024 18:27:44 +0200 Subject: [PATCH] feat: new toc component #188 --- .../scripts/createNewComponentClass.mjs | 3 +- .../navigation/table-of-content.mjs | 4 + .../components/navigation/design.html | 22 +++ .../components/navigation/overview.html | 44 +++++ .../components/navigation/show-it.html | 2 + .../navigation/style/table-of-content.pcss | 0 .../stylesheet/table-of-content.mjs | 31 +++ .../navigation/table-of-content.mjs | 177 ++++++++++++++++++ 8 files changed, 282 insertions(+), 1 deletion(-) create mode 100644 showroom/source/examples/components/navigation/table-of-content.mjs create mode 100644 showroom/source/fragments/components/navigation/design.html create mode 100644 showroom/source/fragments/components/navigation/overview.html create mode 100644 showroom/source/fragments/components/navigation/show-it.html create mode 100644 source/components/navigation/style/table-of-content.pcss create mode 100644 source/components/navigation/stylesheet/table-of-content.mjs create mode 100644 source/components/navigation/table-of-content.mjs diff --git a/development/scripts/createNewComponentClass.mjs b/development/scripts/createNewComponentClass.mjs index 5beafd8c7..b606e9953 100644 --- a/development/scripts/createNewComponentClass.mjs +++ b/development/scripts/createNewComponentClass.mjs @@ -28,6 +28,7 @@ import { ATTRIBUTE_ROLE, } from "{{BACK_TO_ROOT_PATH}}/dom/constants.mjs"; import { CustomControl } from "{{BACK_TO_ROOT_PATH}}/dom/customcontrol.mjs"; +import { CustomElement } from "{{BACK_TO_ROOT_PATH}}/dom/customelement.mjs"; import { assembleMethodSymbol, registerCustomElement, @@ -62,7 +63,7 @@ export const {{CLASSNAME_LOWER_FIRST}}ElementSymbol = Symbol("{{CLASSNAME_LOWER_ * @returns {symbol} */ static get [instanceSymbol]() { - return Symbol.for("@schukai/monster/{{NAMESPACE_AS_PATH}}/{{CLASSNAME_LOWER}}@@instance"); + return Symbol.for("@schukai/monster/{{NAMESPACE_AS_PATH}}/{{HTML_TAG_SUFFIX}}@@instance"); } /** diff --git a/showroom/source/examples/components/navigation/table-of-content.mjs b/showroom/source/examples/components/navigation/table-of-content.mjs new file mode 100644 index 000000000..ef65203c4 --- /dev/null +++ b/showroom/source/examples/components/navigation/table-of-content.mjs @@ -0,0 +1,4 @@ +import "@schukai/monster/source/components/navigation/table-of-content.mjs"; +const element = document.createElement('monster-table-of-content'); +element.setOption('disable', 'false') +document.body.appendChild(element); diff --git a/showroom/source/fragments/components/navigation/design.html b/showroom/source/fragments/components/navigation/design.html new file mode 100644 index 000000000..6fd96361e --- /dev/null +++ b/showroom/source/fragments/components/navigation/design.html @@ -0,0 +1,22 @@ + + <h2>Design</h2> + <p>The control element can be adapted to your own requirements. To do this, the control element + can be designed with CSS like almost any other HTML element.</p> + + <p>However, there are a few things to bear in mind. As the innards of the control are located + in a ShadowRoot, they cannot be accessed directly with CSS selectors. Only the elements specified + for this purpose can be accessed. These elements have the attribute <i>part</i>.</p> + + <p>In CSS, these parts can then be used for styling via a CSS pseudo-element Parts. + Here you can see an example of how you can use this.</p> + + <pre><code class="language-css"> + ::part(container) { + border: 1px solid red; + } + </code></pre> + + <!-- p>The following diagram shows the parts and the slots.</p> + + <img src="assets/Components.Navigation.{{CLASSNAME_LOWER}}.svg" alt="Parts and slots of the TableOfContent"--> + \ No newline at end of file diff --git a/showroom/source/fragments/components/navigation/overview.html b/showroom/source/fragments/components/navigation/overview.html new file mode 100644 index 000000000..b774ec0a4 --- /dev/null +++ b/showroom/source/fragments/components/navigation/overview.html @@ -0,0 +1,44 @@ + +<h2>Introduction</h2> +<p> + This is the Monster TableOfContent component. It is a versatile and customizable control element + to an interactive and engaging user experience that integrates seamlessly into various web applications. + Whether you are developing a simple website or a complex enterprise application, the Monster + Button is designed to increase user interaction and satisfaction. +</p> + +<h2>Key Features</h2> +<ul> + <li><strong>Dynamic interaction</strong>: Users can interact with content dynamically, + making the Web experience more intuitive and user-centric. + </li> + <li><strong>Customizable appearance</strong>: Customize the appearance of the button + to match the design of your brand or application to improve visual consistency. + </li> + <li><strong>Accessibility</strong>: Designed with accessibility in mind to ensure all + users have a seamless experience regardless of their browsing context. + </li> + <li><strong>Programmatic Control</strong>: Provides methods such as click, focus, + and blur to programmatically control the behavior of the button, giving developers flexibility. + </li> +</ul> + +<h2>Improving the user experience</h2> +<p> + The Monster TableOfContent goes beyond the traditional functions of a TableOfContent to provide an enhanced + and interactive user experience. +</p> + +<p> + These improvements are supported by user studies that show a positive impact on user + commitment and satisfaction. +</p> + +<h2>Efficiency in the development process</h2> +<p> + Integrating the Monster TableOfContent into your development process is easy. Its compatibility with + standard web technologies and ease of customization allow for seamless integration with + your existing tools and libraries. Whether you are working on a small project or a large + application, Monster TableOfContent's modular design guarantees easy integration that streamlines + your development process and increases your productivity. +</p> diff --git a/showroom/source/fragments/components/navigation/show-it.html b/showroom/source/fragments/components/navigation/show-it.html new file mode 100644 index 000000000..fac442c40 --- /dev/null +++ b/showroom/source/fragments/components/navigation/show-it.html @@ -0,0 +1,2 @@ + +<monster-table-of-content></monster-table-of-content> diff --git a/source/components/navigation/style/table-of-content.pcss b/source/components/navigation/style/table-of-content.pcss new file mode 100644 index 000000000..e69de29bb diff --git a/source/components/navigation/stylesheet/table-of-content.mjs b/source/components/navigation/stylesheet/table-of-content.mjs new file mode 100644 index 000000000..798ef9312 --- /dev/null +++ b/source/components/navigation/stylesheet/table-of-content.mjs @@ -0,0 +1,31 @@ +/** + * Copyright © schukai GmbH and all contributing authors, 2024. All rights reserved. + * Node module: @schukai/monster + * + * This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3). + * The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.en.html + * + * For those who do not wish to adhere to the AGPLv3, a commercial license is available. + * Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms. + * For more information about purchasing a commercial license, please contact schukai GmbH. + */ + +import {addAttributeToken} from "../../../dom/attributes.mjs"; +import {ATTRIBUTE_ERRORMESSAGE} from "../../../dom/constants.mjs"; + +export {TableOfContentStyleSheet} + +/** + * @private + * @type {CSSStyleSheet} + */ +const TableOfContentStyleSheet = new CSSStyleSheet(); + +try { + TableOfContentStyleSheet.insertRule(` +@layer tableofcontent { + +}`, 0); +} catch (e) { + addAttributeToken(document.getRootNode().querySelector('html'), ATTRIBUTE_ERRORMESSAGE, e + ""); +} diff --git a/source/components/navigation/table-of-content.mjs b/source/components/navigation/table-of-content.mjs new file mode 100644 index 000000000..da8af84a6 --- /dev/null +++ b/source/components/navigation/table-of-content.mjs @@ -0,0 +1,177 @@ +/** + * Copyright © schukai GmbH and all contributing authors, {{copyRightYear}}. All rights reserved. + * Node module: @schukai/monster + * + * This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3). + * The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.en.html + * + * For those who do not wish to adhere to the AGPLv3, a commercial license is available. + * Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms. + * For more information about purchasing a commercial license, please contact schukai GmbH. + */ + +import {instanceSymbol} from "../../constants.mjs"; +import {addAttributeToken} from "../../dom/attributes.mjs"; +import { + ATTRIBUTE_ERRORMESSAGE, + ATTRIBUTE_ROLE, +} from "../../dom/constants.mjs"; +import {CustomElement} from "../../dom/customelement.mjs"; +import { + assembleMethodSymbol, + registerCustomElement, +} from "../../dom/customelement.mjs"; +import {findTargetElementFromEvent} from "../../dom/events.mjs"; +import {isFunction} from "../../types/is.mjs"; +import {TableOfContentStyleSheet} from "./stylesheet/table-of-content.mjs"; +import {fireCustomEvent} from "../../dom/events.mjs"; + +export {TableOfContent}; + +/** + * @private + * @type {symbol} + */ +export const tableOfContentElementSymbol = Symbol("tableOfContentElement"); + +/** + * A TableOfContent + * + * @fragments /fragments/components/form/table-of-content/ + * + * @example /examples/components/form/table-of-content-simple + * + * @since 3.66.0 + * @copyright schukai GmbH + * @summary A beautiful TableOfContent that can make your life easier and also looks good. + */ +class TableOfContent extends CustomElement { + /** + * This method is called by the `instanceof` operator. + * @returns {symbol} + */ + static get [instanceSymbol]() { + return Symbol.for("@schukai/monster/components/navigation/table-of-content@@instance"); + } + + /** + * + * @return {Components.Navigation.TableOfContent + */ + [assembleMethodSymbol]() { + super[assembleMethodSymbol](); + initControlReferences.call(this); + initEventHandler.call(this); + return this; + } + + /** + * To set the options via the html tag the attribute `data-monster-options` must be used. + * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control} + * + * The individual configuration values can be found in the table. + * + * @property {Object} templates Template definitions + * @property {string} templates.main Main template + * @property {Object} labels Label definitions + * @property {Object} actions Callbacks + * @property {string} actions.click="throw Error" Callback when clicked + * @property {Object} features Features + * @property {Object} classes CSS classes + * @property {boolean} disabled=false Disabled state + */ + get defaults() { + return Object.assign({}, super.defaults, { + templates: { + main: getTemplate(), + }, + labels: {}, + classes: {}, + disabled: false, + features: {}, + actions: { + click: () => { + throw new Error("the click action is not defined"); + }, + } + }); + } + + /** + * @return {string} + */ + static getTag() { + return "monster-table-of-content"; + } + + /** + * @return {CSSStyleSheet[]} + */ + static getCSSStyleSheet() { + return [TableOfContentStyleSheet]; + } + + +} + +/** + * @private + * @return {initEventHandler} + * @fires monster-table-of-content-clicked + */ +function initEventHandler() { + const self = this; + const element = this[tableOfContentElementSymbol]; + + const type = "click"; + + element.addEventListener(type, function (event) { + const callback = self.getOption("actions.click"); + + fireCustomEvent(self, "monster-table-of-content-clicked", { + element: self, + }); + + if (!isFunction(callback)) { + return; + } + + const element = findTargetElementFromEvent( + event, + ATTRIBUTE_ROLE, + "control", + ); + + if (!(element instanceof Node && self.hasNode(element))) { + return; + } + + callback.call(self, event); + }); + + return this; +} + +/** + * @private + * @return {void} + */ +function initControlReferences() { + this[tableOfContentElementSymbol] = this.shadowRoot.querySelector( + `[${ATTRIBUTE_ROLE}="control"]`, + ); +} + +/** + * @private + * @return {string} + */ +function getTemplate() { + // language=HTML + return ` + <div data-monster-role="control" part="control"> + </div>`; +} + + +registerCustomElement(TableOfContent); -- GitLab