diff --git a/flake.lock b/flake.lock index 41aa0a67825381e55af57b9e1a6582897543c40c..0bc61a9356ced33ec95f18e07ab00e65a8873eb9 100644 --- a/flake.lock +++ b/flake.lock @@ -195,11 +195,11 @@ }, "nixpkgs_5": { "locked": { - "lastModified": 1728067476, - "narHash": "sha256-/uJcVXuBt+VFCPQIX+4YnYrHaubJSx4HoNsJVNRgANM=", + "lastModified": 1728193676, + "narHash": "sha256-PbDWAIjKJdlVg+qQRhzdSor04bAPApDqIv2DofTyynk=", "owner": "nixos", "repo": "nixpkgs", - "rev": "6e6b3dd395c3b1eb9be9f2d096383a8d05add030", + "rev": "ecbc1ca8ffd6aea8372ad16be9ebbb39889e55b6", "type": "github" }, "original": { diff --git a/nix/config/release.nix b/nix/config/release.nix index 0e2c557c31443c5934622b8c61bf894984bd7cbd..8688f99ad197ff8f0a4eccc15c287eaa212cd488 100644 --- a/nix/config/release.nix +++ b/nix/config/release.nix @@ -3,4 +3,4 @@ commit = "b24bda8a67da6d14ac986b9d72c5129e8c6c6687"; name = "Monster"; mnemonic = "monster"; -} \ No newline at end of file +} diff --git a/source/components/content/copy.mjs b/source/components/content/copy.mjs index 0a78f8c35bba99257bc22c2176be4c439c17e50a..104ab94a91f4e55e11a7a11ac479871248af5e97 100644 --- a/source/components/content/copy.mjs +++ b/source/components/content/copy.mjs @@ -72,7 +72,7 @@ const closeEventHandler = Symbol("closeEventHandler"); const resizeObserverSymbol = Symbol("resizeObserver"); /** - * A Copy + * A Copy Component * * @fragments /fragments/components/content/copy/ * diff --git a/source/components/tree-menu/dragable-tree-menu.mjs b/source/components/tree-menu/dragable-tree-menu.mjs index 80e502cbf326cab9344e21d5b00ace9e104b4596..a97a34597cf6a798aac8252f1de5bce212891dce 100644 --- a/source/components/tree-menu/dragable-tree-menu.mjs +++ b/source/components/tree-menu/dragable-tree-menu.mjs @@ -11,683 +11,683 @@ * * SPDX-License-Identifier: AGPL-3.0 */ - -import { buildTree } from "../../data/buildtree.mjs"; -import { Datasource } from "../../data/datasource.mjs"; -import { addAttributeToken } from "../../dom/attributes.mjs"; -import { - ATTRIBUTE_DISABLED, - ATTRIBUTE_ERRORMESSAGE, - ATTRIBUTE_ROLE, - ATTRIBUTE_UPDATER_INSERT_REFERENCE, -} from "../../dom/constants.mjs"; -import { - assembleMethodSymbol, - CustomElement, - initMethodSymbol, - registerCustomElement, -} from "../../dom/customelement.mjs"; -import { findTargetElementFromEvent } from "../../dom/events.mjs"; -import { findElementWithSelectorUpwards } from "../../dom/util.mjs"; -import { Formatter } from "../../text/formatter.mjs"; -import { isObject, isString } from "../../types/is.mjs"; -import { Node } from "../../types/node.mjs"; -import { NodeRecursiveIterator } from "../../types/noderecursiveiterator.mjs"; -import { Observer } from "../../types/observer.mjs"; -import { ProxyObserver } from "../../types/proxyobserver.mjs"; -import { validateInstance } from "../../types/validate.mjs"; -import { - datasourceLinkedElementSymbol, - handleDataSourceChanges, -} from "../datatable/util.mjs"; -import { ATTRIBUTE_INTEND } from "./../constants.mjs"; -import { CommonStyleSheet } from "../stylesheet/common.mjs"; -import { TreeMenuStyleSheet } from "./stylesheet/tree-menu.mjs"; - -export { TreeMenu }; - -/** - * @private - * @type {symbol} - */ -const internalNodesSymbol = Symbol("internalNodes"); - -/** - * @private - * @type {symbol} - */ -const controlElementSymbol = Symbol("controlElement"); - -/** - * @private - * @type {symbol} - */ -const openEntryEventHandlerSymbol = Symbol("openEntryEventHandler"); - -/** - * @private - * @type {symbol} - */ -const dragstartEventHandlerSymbol = Symbol("dragstartEventHandler"); -/** - * @private - * @type {symbol} - */ -const dragenterEventHandlerSymbol = Symbol("dragenterEventHandler"); - -/** - * @private - * @type {symbol} - */ -const dragleaveEventHandlerSymbol = Symbol("dragleaveEventHandler"); - -/** - * @private - * @type {symbol} - */ -const dragEventHandlerSymbol = Symbol("dragEventHandler"); - -/** - * @private - * @type {symbol} - */ -const dragoverEventHandlerSymbol = Symbol("dragoverEventHandler"); - -/** - * @private - * @type {symbol} - */ -const dropEventHandlerSymbol = Symbol("dropEventHandlerSymbol"); - -/** - * TreeMenu - * - * <img src="./images/tree-menu.png"> - * - * You can create this control either by specifying the HTML tag `<monster-tree-menu />` directly in the HTML - * - * ```html - * <monster-tree-menu></monster-tree-menu> - * ``` - * - * or using Javascript via the `document.createElement('monster-tree-menu');` method. - * - * ```javascript - * import {TreeMenu} from 'https://cdn.jsdelivr.net/npm/@schukai/component-treemenu@0.1.0/dist/modules/treemenu.js'; - * document.createElement('monster-treemenu'); - * ``` - * - * @startuml tree-menu.png - * skinparam monochrome true - * skinparam shadowing false - * HTMLElement <|-- CustomElement - * CustomElement <|-- CustomControl - * CustomControl <|-- TreeMenu - * @enduml - * @since 1.0.0 - * @copyright schukai GmbH - * @memberOf Monster.Components.TreeMenu - * @summary A TreeMenu control - * @fires Monster.Components.TreeMenu.event:monster-fetched - */ -class TreeMenu extends CustomElement { - /** - * This method is called internal and should not be called directly. - * - * The defaults can be set either directly in the object or via an attribute in the HTML tag. - * The value of the attribute `data-monster-options` in the HTML tag must be a JSON string. - * - * ``` - * <monster-treemenu data-monster-options="{}"></monster-treemenu> - * ``` - * - * Since 1.18.0 the JSON can be specified as a DataURI. - * - * ``` - * new Monster.Types.DataUrl(btoa(JSON.stringify({ - * shadowMode: 'open', - * })),'application/json',true).toString() - * ``` - * @property {Object} toggleEventType=click,touch List of event types to be observed for opening the dropdown - * @property {Object} templates Template definitions - * @property {string} templates.main Main template - * @property {Datasource} datasource data source - * @property {Object} mapping - * @property {String} mapping.selector=* Path to select the appropriate entries - * @property {String} mapping.labelTemplate="" template with the label placeholders in the form ${name}, where name is the key - * @property {String} mapping.keyTemplate="" template with the key placeholders in the form ${name}, where name is the key - * @property {String} mapping.rootReferences=['0', undefined, null] - * @property {String} mapping.idTemplate=id - * @property {String} mapping.parentTemplate=parent - * @property {String} mapping.selection - */ - get defaults() { - return Object.assign( - {}, - super.defaults, - { - toggleEventType: ["click", "touch"], - mapping: { - rootReferences: ["0", undefined, null], - idTemplate: "id", - parentTemplate: "parent", - selector: "*", - labelTemplate: "", - valueTemplate: "", - filter: undefined, - }, - templates: { - main: getTemplate(), - }, - - datasource: { - selector: null, - }, - - data: [], - }, - initOptionsFromArguments.call(this), - ); - } - - /** - * This method determines which attributes are to be monitored by `attributeChangedCallback()`. - * - * @return {string[]} - * @since 1.15.0 - */ - static get observedAttributes() { - const list = super.observedAttributes; - //list.push(ATTRIBUTE_FORM_URL); - return list; - } - - /** - * - */ - [initMethodSymbol]() { - super[initMethodSymbol](); - } - - /** - * - * @return {Monster.Components.TreeMenu.Form} - */ - [assembleMethodSymbol]() { - super[assembleMethodSymbol](); - - initControlReferences.call(this); - initEventHandler.call(this); - // importEntriesFromDatasource.call(this); - initObserver.call(this); - - return this; - } - - /** - * This method is called internal and should not be called directly. - * - * @return {CSSStyleSheet[]} - */ - static getCSSStyleSheet() { - return [CommonStyleSheet, TreeMenuStyleSheet]; - } - - /** - * This method is called internal and should not be called directly. - * - * @return {string} - */ - static getTag() { - return "monster-tree-menu"; - } -} - -/** - * @private - */ -function initEventHandler() { - switchToConfig.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)), - ); - } - - this[openEntryEventHandlerSymbol] = (event) => { - const container = findTargetElementFromEvent( - event, - ATTRIBUTE_ROLE, - "entry", - ); - if (!(container instanceof HTMLElement)) { - return; - } - - //let container = findClosestByAttribute(element, ATTRIBUTE_ROLE, 'option'); - const index = container - .getAttribute(ATTRIBUTE_UPDATER_INSERT_REFERENCE) - .split("-") - .pop(); - - const currentState = this.getOption("data." + index + ".state"); - - const newState = currentState === "close" ? "open" : "close"; - this.setOption("data." + index + ".state", newState); - - const newVisibility = newState === "open" ? "visible" : "hidden"; - - if (container.hasAttribute(ATTRIBUTE_INTEND)) { - const intend = container.getAttribute(ATTRIBUTE_INTEND); - - let ref = container.nextElementSibling; - const childIntend = parseInt(intend) + 1; - - const cmp = (a, b) => { - if (newState === "open") { - return a === b; - } - - return a >= b; - }; - - while ( - ref && - ref.hasAttribute(ATTRIBUTE_INTEND) && - cmp(parseInt(ref.getAttribute(ATTRIBUTE_INTEND)), childIntend) - ) { - const refIndex = ref - .getAttribute(ATTRIBUTE_UPDATER_INSERT_REFERENCE) - .split("-") - .pop(); - this.setOption("data." + refIndex + ".visibility", newVisibility); - - if (newState === "close") { - this.setOption("data." + refIndex + ".state", "close"); - } - - ref = ref.nextElementSibling; - } - } - }; - - const types = this.getOption("toggleEventType", ["click"]); - for (const [, type] of Object.entries(types)) { - this.shadowRoot.addEventListener(type, this[openEntryEventHandlerSymbol]); - } - - // for (const [, type] of Object.entries(types)) { - // - // self[controlElementSymbol].addEventListener(type, function (event) { - // - // const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, 'entry'); - // if (!(element instanceof HTMLElement)) { - // return; - // } - // - // toggle.call(self); - // - // - // }) - // - // } - - return this; -} - -/** - * @private - * @this Form - */ -function initObserver() {} - -/** - * Import Menu Entries from dataset - * - * @since 1.0.0 - * @param {array|object|Map|Set} data - * @return {TreeMenu} - * @throws {Error} map is not iterable - * @private - */ -function importEntries(data) { - this[internalNodesSymbol] = new Map(); - - const mappingOptions = this.getOption("mapping", {}); - - const filter = mappingOptions?.["filter"]; - const rootReferences = mappingOptions?.["rootReferences"]; - - const id = this.getOption("mapping.idTemplate", "id"); - const parentID = this.getOption("mapping.parentTemplate", "parent"); - - const selector = mappingOptions?.["selector"]; - - const nodes = buildTree(data, selector, id, parentID, { - filter, - rootReferences, - }); - - const options = []; - for (const node of nodes) { - const iterator = new NodeRecursiveIterator(node); - for (const n of iterator) { - const formattedValues = formatKeyLabel.call(this, n); - - const label = formattedValues.label; - const value = formattedValues.value; - const intend = n.level; - - const visibility = intend > 0 ? "hidden" : "visible"; - const state = "close"; - - this[internalNodesSymbol].set(value, n); - - options.push({ - value, - label, - intend, - state, - visibility, - ["has-children"]: n.hasChildNodes(), - }); - } - } - - this.setOption("entries", options); - return this; -} - -/** - * @private - */ -function importEntriesFromDatasource() { - const self = this; - self.setAttribute(ATTRIBUTE_DISABLED, ATTRIBUTE_DISABLED); - - const datasource = self.getOption("datasource"); - if (!(datasource instanceof Datasource)) { - addAttributeToken( - self, - ATTRIBUTE_ERRORMESSAGE, - "datasource is not defined", - ); - return; - } - - datasource.attachObserver( - new Observer(function () { - if (isObject(this) && this instanceof ProxyObserver) { - importEntries.call(self, datasource.get()); - } - }), - ); - - datasource - .read() - .then(() => { - new Processing(() => { - self.removeAttribute(ATTRIBUTE_DISABLED); - }).run(); - }) - .catch((e) => { - addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, e.toString()); - }); - - return self; -} - -/** - * - * @param {Node} node - * @return {array<label, value>} - * @memberOf Monster.Components.TreeMenu - * @private - */ -function formatKeyLabel(node) { - validateInstance(node, Node); - - const label = new Formatter(node.value).format( - this.getOption("mapping.labelTemplate", ""), - ); - const value = new Formatter(node.value).format( - this.getOption("mapping.valueTemplate", ""), - ); - - return { - value, - label, - }; -} - -/** - * @private - * @return {Monster.Components.TreeMenu.Form} - */ -function initControlReferences() { - if (!this.shadowRoot) { - throw new Error("no shadow-root is defined"); - } - - this[controlElementSymbol] = this.shadowRoot.querySelector( - "[data-monster-role=control]", - ); - - return this; -} - -/** - * - * ``` - * <monster-tree-menu data-monster-url="https://example.com/"></monster-tree-menu> - * ``` - - * @private - * @return {object} - */ -function initOptionsFromArguments() { - const options = {}; - - // let url = self.getAttribute(ATTRIBUTE_FORM_URL); - // - // if (isString(url)) { - // options['url'] = new URL(url, document.location).toString() - // } - - return options; -} - -function switchToConfig() { - if (!this.shadowRoot) { - throw new Error("no shadow-root is defined"); - } - - this[dragoverEventHandlerSymbol] = (event) => { - const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, "entry"); - event.preventDefault(); - if (!(element instanceof HTMLElement)) { - return; - } - - const dropzone = document.createElement("div"); - dropzone.classList.add("dropzone"); - - element.prepend(dropzone); - - //console.log("over", element.outerHTML, event); - - event.dataTransfer.dropEffect = "move"; - }; - - this[dragenterEventHandlerSymbol] = (event) => { - const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, "entry"); - //console.log("enter", element.outerHTML, event); - - event.dataTransfer.dropEffect = "move"; - event.preventDefault(); - }; - - this[dragleaveEventHandlerSymbol] = (event) => { - const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, "entry"); - - event.preventDefault(); - if (!(element instanceof HTMLElement)) { - return; - } - - //console.log("leave", element.outerHTML, event); - - event.dataTransfer.dropEffect = "move"; - event.preventDefault(); - }; - - this[dragEventHandlerSymbol] = (event) => { - event.preventDefault(); - }; - - this[dropEventHandlerSymbol] = (event) => { - const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, "entry"); - //console.log("drop", element.outerHTML, event); - event.preventDefault(); - }; - - this[dragstartEventHandlerSymbol] = (event) => { - const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, "entry"); - if (!(element instanceof HTMLElement)) { - return; - } - - //let container = findClosestByAttribute(element, ATTRIBUTE_ROLE, 'option'); - const index = element - .getAttribute(ATTRIBUTE_UPDATER_INSERT_REFERENCE) - .split("-") - .pop(); - - const currentState = this.getOption("entries." + index + ".state"); - event.dataTransfer.setData("text/plain", "22"); - event.dataTransfer.setData("text/html", "22"); - event.dataTransfer.effectAllowed = "move"; - }; - - this[controlElementSymbol].addEventListener( - "dragstart", - this[dragstartEventHandlerSymbol], - ); - this[controlElementSymbol].addEventListener( - "dragenter", - this[dragenterEventHandlerSymbol], - ); - this[controlElementSymbol].addEventListener( - "dragleave", - this[dragleaveEventHandlerSymbol], - ); - this[controlElementSymbol].addEventListener( - "dragover", - this[dragoverEventHandlerSymbol], - ); - this[controlElementSymbol].addEventListener( - "drop", - this[dropEventHandlerSymbol], - ); -} - +// +// import { buildTree } from "../../data/buildtree.mjs"; +// import { Datasource } from "../../data/datasource.mjs"; +// import { addAttributeToken } from "../../dom/attributes.mjs"; +// import { +// ATTRIBUTE_DISABLED, +// ATTRIBUTE_ERRORMESSAGE, +// ATTRIBUTE_ROLE, +// ATTRIBUTE_UPDATER_INSERT_REFERENCE, +// } from "../../dom/constants.mjs"; +// import { +// assembleMethodSymbol, +// CustomElement, +// initMethodSymbol, +// registerCustomElement, +// } from "../../dom/customelement.mjs"; +// import { findTargetElementFromEvent } from "../../dom/events.mjs"; +// import { findElementWithSelectorUpwards } from "../../dom/util.mjs"; +// import { Formatter } from "../../text/formatter.mjs"; +// import { isObject, isString } from "../../types/is.mjs"; +// import { Node } from "../../types/node.mjs"; +// import { NodeRecursiveIterator } from "../../types/noderecursiveiterator.mjs"; +// import { Observer } from "../../types/observer.mjs"; +// import { ProxyObserver } from "../../types/proxyobserver.mjs"; +// import { validateInstance } from "../../types/validate.mjs"; +// import { +// datasourceLinkedElementSymbol, +// handleDataSourceChanges, +// } from "../datatable/util.mjs"; +// import { ATTRIBUTE_INTEND } from "./../constants.mjs"; +// import { CommonStyleSheet } from "../stylesheet/common.mjs"; +// import { TreeMenuStyleSheet } from "./stylesheet/tree-menu.mjs"; +// +// export { TreeMenu }; +// // /** // * @private -// * @throws {Error} missing default slot -// * @throws {Error} no shadow-root is defined -// * @throws {Error} missing url -// * @throws {Error} we won't be able to read the data -// * @throws {Error} request failed -// * @throws {Error} not found -// * @throws {Error} undefined status or type +// * @type {symbol} +// */ +// const internalNodesSymbol = Symbol("internalNodes"); +// +// /** +// * @private +// * @type {symbol} +// */ +// const controlElementSymbol = Symbol("controlElement"); +// +// /** +// * @private +// * @type {symbol} +// */ +// const openEntryEventHandlerSymbol = Symbol("openEntryEventHandler"); +// +// /** +// * @private +// * @type {symbol} +// */ +// const dragstartEventHandlerSymbol = Symbol("dragstartEventHandler"); +// /** +// * @private +// * @type {symbol} +// */ +// const dragenterEventHandlerSymbol = Symbol("dragenterEventHandler"); +// +// /** +// * @private +// * @type {symbol} +// */ +// const dragleaveEventHandlerSymbol = Symbol("dragleaveEventHandler"); +// +// /** +// * @private +// * @type {symbol} +// */ +// const dragEventHandlerSymbol = Symbol("dragEventHandler"); +// +// /** +// * @private +// * @type {symbol} +// */ +// const dragoverEventHandlerSymbol = Symbol("dragoverEventHandler"); +// +// /** +// * @private +// * @type {symbol} +// */ +// const dropEventHandlerSymbol = Symbol("dropEventHandlerSymbol"); +// +// /** +// * TreeMenu +// * +// * <img src="./images/tree-menu.png"> +// * +// * You can create this control either by specifying the HTML tag `<monster-tree-menu />` directly in the HTML +// * +// * ```html +// * <monster-tree-menu></monster-tree-menu> +// * ``` +// * +// * or using Javascript via the `document.createElement('monster-tree-menu');` method. +// * +// * ```javascript +// * import {TreeMenu} from 'https://cdn.jsdelivr.net/npm/@schukai/component-treemenu@0.1.0/dist/modules/treemenu.js'; +// * document.createElement('monster-treemenu'); +// * ``` +// * +// * @startuml tree-menu.png +// * skinparam monochrome true +// * skinparam shadowing false +// * HTMLElement <|-- CustomElement +// * CustomElement <|-- CustomControl +// * CustomControl <|-- TreeMenu +// * @enduml +// * @since 1.0.0 +// * @copyright schukai GmbH +// * @memberOf Monster.Components.TreeMenu +// * @summary A TreeMenu control // * @fires Monster.Components.TreeMenu.event:monster-fetched // */ -// function initIntersectionObserver() { -// const self = this; +// class TreeMenu extends CustomElement { +// /** +// * This method is called internal and should not be called directly. +// * +// * The defaults can be set either directly in the object or via an attribute in the HTML tag. +// * The value of the attribute `data-monster-options` in the HTML tag must be a JSON string. +// * +// * ``` +// * <monster-treemenu data-monster-options="{}"></monster-treemenu> +// * ``` +// * +// * Since 1.18.0 the JSON can be specified as a DataURI. +// * +// * ``` +// * new Monster.Types.DataUrl(btoa(JSON.stringify({ +// * shadowMode: 'open', +// * })),'application/json',true).toString() +// * ``` +// * @property {Object} toggleEventType=click,touch List of event types to be observed for opening the dropdown +// * @property {Object} templates Template definitions +// * @property {string} templates.main Main template +// * @property {Datasource} datasource data source +// * @property {Object} mapping +// * @property {String} mapping.selector=* Path to select the appropriate entries +// * @property {String} mapping.labelTemplate="" template with the label placeholders in the form ${name}, where name is the key +// * @property {String} mapping.keyTemplate="" template with the key placeholders in the form ${name}, where name is the key +// * @property {String} mapping.rootReferences=['0', undefined, null] +// * @property {String} mapping.idTemplate=id +// * @property {String} mapping.parentTemplate=parent +// * @property {String} mapping.selection +// */ +// get defaults() { +// return Object.assign( +// {}, +// super.defaults, +// { +// toggleEventType: ["click", "touch"], +// mapping: { +// rootReferences: ["0", undefined, null], +// idTemplate: "id", +// parentTemplate: "parent", +// selector: "*", +// labelTemplate: "", +// valueTemplate: "", +// filter: undefined, +// }, +// templates: { +// main: getTemplate(), +// }, +// +// datasource: { +// selector: null, +// }, // -// if (self[intersectionObserverWasInitialized] === true) { -// return -// } +// data: [], +// }, +// initOptionsFromArguments.call(this), +// ); +// } // -// self[intersectionObserverWasInitialized] = true; +// /** +// * This method determines which attributes are to be monitored by `attributeChangedCallback()`. +// * +// * @return {string[]} +// * @since 1.15.0 +// */ +// static get observedAttributes() { +// const list = super.observedAttributes; +// //list.push(ATTRIBUTE_FORM_URL); +// return list; +// } +// +// /** +// * +// */ +// [initMethodSymbol]() { +// super[initMethodSymbol](); +// } +// +// /** +// * +// * @return {Monster.Components.TreeMenu.Form} +// */ +// [assembleMethodSymbol]() { +// super[assembleMethodSymbol](); +// +// initControlReferences.call(this); +// initEventHandler.call(this); +// // importEntriesFromDatasource.call(this); +// initObserver.call(this); +// +// return this; +// } +// +// /** +// * This method is called internal and should not be called directly. +// * +// * @return {CSSStyleSheet[]} +// */ +// static getCSSStyleSheet() { +// return [CommonStyleSheet, TreeMenuStyleSheet]; +// } +// +// /** +// * This method is called internal and should not be called directly. +// * +// * @return {string} +// */ +// static getTag() { +// return "monster-tree-menu"; +// } +// } +// +// /** +// * @private +// */ +// function initEventHandler() { +// switchToConfig.call(this); // -// let options = { -// threshold: [0.5] -// } +// const selector = this.getOption("datasource.selector"); // -// const callback = (entries, observer) => { +// if (isString(selector)) { +// const element = findElementWithSelectorUpwards(this, selector); +// if (element === null) { +// throw new Error("the selector must match exactly one element"); +// } // -// for (const [, entry] of entries.entries()) { -// if (entry.isIntersecting === true) { -// if (!self.hasAttribute(ATTRIBUTE_FORM_RELOAD) || self.getAttribute(ATTRIBUTE_FORM_RELOAD).toLowerCase() === 'onshow') { -// observer.disconnect(); -// } +// if (!(element instanceof Datasource)) { +// throw new TypeError("the element must be a datasource"); +// } // -// try { -// loadContent.call(self); -// } catch (e) { -// self.setAttribute(ATTRIBUTE_ERRORMESSAGE, e.toString()); -// } +// this[datasourceLinkedElementSymbol] = element; +// element.datasource.attachObserver( +// new Observer(handleDataSourceChanges.bind(this)), +// ); +// } // +// this[openEntryEventHandlerSymbol] = (event) => { +// const container = findTargetElementFromEvent( +// event, +// ATTRIBUTE_ROLE, +// "entry", +// ); +// if (!(container instanceof HTMLElement)) { +// return; +// } // -// } -// } -// } +// //let container = findClosestByAttribute(element, ATTRIBUTE_ROLE, 'option'); +// const index = container +// .getAttribute(ATTRIBUTE_UPDATER_INSERT_REFERENCE) +// .split("-") +// .pop(); // -// const observer = new IntersectionObserver(callback, options); -// observer.observe(self); +// const currentState = this.getOption("data." + index + ".state"); // +// const newState = currentState === "close" ? "open" : "close"; +// this.setOption("data." + index + ".state", newState); // +// const newVisibility = newState === "open" ? "visible" : "hidden"; +// +// if (container.hasAttribute(ATTRIBUTE_INTEND)) { +// const intend = container.getAttribute(ATTRIBUTE_INTEND); +// +// let ref = container.nextElementSibling; +// const childIntend = parseInt(intend) + 1; +// +// const cmp = (a, b) => { +// if (newState === "open") { +// return a === b; +// } +// +// return a >= b; +// }; +// +// while ( +// ref && +// ref.hasAttribute(ATTRIBUTE_INTEND) && +// cmp(parseInt(ref.getAttribute(ATTRIBUTE_INTEND)), childIntend) +// ) { +// const refIndex = ref +// .getAttribute(ATTRIBUTE_UPDATER_INSERT_REFERENCE) +// .split("-") +// .pop(); +// this.setOption("data." + refIndex + ".visibility", newVisibility); +// +// if (newState === "close") { +// this.setOption("data." + refIndex + ".state", "close"); +// } +// +// ref = ref.nextElementSibling; +// } +// } +// }; +// +// const types = this.getOption("toggleEventType", ["click"]); +// for (const [, type] of Object.entries(types)) { +// this.shadowRoot.addEventListener(type, this[openEntryEventHandlerSymbol]); +// } +// +// // for (const [, type] of Object.entries(types)) { +// // +// // self[controlElementSymbol].addEventListener(type, function (event) { +// // +// // const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, 'entry'); +// // if (!(element instanceof HTMLElement)) { +// // return; +// // } +// // +// // toggle.call(self); +// // +// // +// // }) +// // +// // } +// +// return this; // } - -/** - * @private - * @return {string} - */ -function getTemplate() { - // language=HTML - return ` - <template id="entries"> - <div data-monster-role="entry" - draggable="true" - data-monster-attributes=" - data-monster-intend path:entries.intend, - data-monster-state path:entries.state, - data-monster-visibility path:entries.visibility, - data-monster-filtered path:entries.filtered, - data-monster-has-children path:entries.has-children"> - - <button data-monster-role="button" - data-monster-attributes=" - type path:type, - role path:role, - value path:entries.value, - name path:name, - part path:type | prefix:option- | suffix: form" tabindex="-1"> - <span data-monster-role="folder-handler"></span> - <span data-monster-replace="path:entries | index:label" part="entry-label"></span> - </button> - </template> - - <div data-monster-role="control" part="control"> - <div part="entries" data-monster-role="entries" - data-monster-insert="entries path:entries" - tabindex="-1"></div> - </div> - `; -} - -registerCustomElement(TreeMenu); +// +// /** +// * @private +// * @this Form +// */ +// function initObserver() {} +// +// /** +// * Import Menu Entries from dataset +// * +// * @since 1.0.0 +// * @param {array|object|Map|Set} data +// * @return {TreeMenu} +// * @throws {Error} map is not iterable +// * @private +// */ +// function importEntries(data) { +// this[internalNodesSymbol] = new Map(); +// +// const mappingOptions = this.getOption("mapping", {}); +// +// const filter = mappingOptions?.["filter"]; +// const rootReferences = mappingOptions?.["rootReferences"]; +// +// const id = this.getOption("mapping.idTemplate", "id"); +// const parentID = this.getOption("mapping.parentTemplate", "parent"); +// +// const selector = mappingOptions?.["selector"]; +// +// const nodes = buildTree(data, selector, id, parentID, { +// filter, +// rootReferences, +// }); +// +// const options = []; +// for (const node of nodes) { +// const iterator = new NodeRecursiveIterator(node); +// for (const n of iterator) { +// const formattedValues = formatKeyLabel.call(this, n); +// +// const label = formattedValues.label; +// const value = formattedValues.value; +// const intend = n.level; +// +// const visibility = intend > 0 ? "hidden" : "visible"; +// const state = "close"; +// +// this[internalNodesSymbol].set(value, n); +// +// options.push({ +// value, +// label, +// intend, +// state, +// visibility, +// ["has-children"]: n.hasChildNodes(), +// }); +// } +// } +// +// this.setOption("entries", options); +// return this; +// } +// +// /** +// * @private +// */ +// function importEntriesFromDatasource() { +// const self = this; +// self.setAttribute(ATTRIBUTE_DISABLED, ATTRIBUTE_DISABLED); +// +// const datasource = self.getOption("datasource"); +// if (!(datasource instanceof Datasource)) { +// addAttributeToken( +// self, +// ATTRIBUTE_ERRORMESSAGE, +// "datasource is not defined", +// ); +// return; +// } +// +// datasource.attachObserver( +// new Observer(function () { +// if (isObject(this) && this instanceof ProxyObserver) { +// importEntries.call(self, datasource.get()); +// } +// }), +// ); +// +// datasource +// .read() +// .then(() => { +// new Processing(() => { +// self.removeAttribute(ATTRIBUTE_DISABLED); +// }).run(); +// }) +// .catch((e) => { +// addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, e.toString()); +// }); +// +// return self; +// } +// +// /** +// * +// * @param {Node} node +// * @return {array<label, value>} +// * @memberOf Monster.Components.TreeMenu +// * @private +// */ +// function formatKeyLabel(node) { +// validateInstance(node, Node); +// +// const label = new Formatter(node.value).format( +// this.getOption("mapping.labelTemplate", ""), +// ); +// const value = new Formatter(node.value).format( +// this.getOption("mapping.valueTemplate", ""), +// ); +// +// return { +// value, +// label, +// }; +// } +// +// /** +// * @private +// * @return {Monster.Components.TreeMenu.Form} +// */ +// function initControlReferences() { +// if (!this.shadowRoot) { +// throw new Error("no shadow-root is defined"); +// } +// +// this[controlElementSymbol] = this.shadowRoot.querySelector( +// "[data-monster-role=control]", +// ); +// +// return this; +// } +// +// /** +// * +// * ``` +// * <monster-tree-menu data-monster-url="https://example.com/"></monster-tree-menu> +// * ``` +// +// * @private +// * @return {object} +// */ +// function initOptionsFromArguments() { +// const options = {}; +// +// // let url = self.getAttribute(ATTRIBUTE_FORM_URL); +// // +// // if (isString(url)) { +// // options['url'] = new URL(url, document.location).toString() +// // } +// +// return options; +// } +// +// function switchToConfig() { +// if (!this.shadowRoot) { +// throw new Error("no shadow-root is defined"); +// } +// +// this[dragoverEventHandlerSymbol] = (event) => { +// const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, "entry"); +// event.preventDefault(); +// if (!(element instanceof HTMLElement)) { +// return; +// } +// +// const dropzone = document.createElement("div"); +// dropzone.classList.add("dropzone"); +// +// element.prepend(dropzone); +// +// //console.log("over", element.outerHTML, event); +// +// event.dataTransfer.dropEffect = "move"; +// }; +// +// this[dragenterEventHandlerSymbol] = (event) => { +// const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, "entry"); +// //console.log("enter", element.outerHTML, event); +// +// event.dataTransfer.dropEffect = "move"; +// event.preventDefault(); +// }; +// +// this[dragleaveEventHandlerSymbol] = (event) => { +// const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, "entry"); +// +// event.preventDefault(); +// if (!(element instanceof HTMLElement)) { +// return; +// } +// +// //console.log("leave", element.outerHTML, event); +// +// event.dataTransfer.dropEffect = "move"; +// event.preventDefault(); +// }; +// +// this[dragEventHandlerSymbol] = (event) => { +// event.preventDefault(); +// }; +// +// this[dropEventHandlerSymbol] = (event) => { +// const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, "entry"); +// //console.log("drop", element.outerHTML, event); +// event.preventDefault(); +// }; +// +// this[dragstartEventHandlerSymbol] = (event) => { +// const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, "entry"); +// if (!(element instanceof HTMLElement)) { +// return; +// } +// +// //let container = findClosestByAttribute(element, ATTRIBUTE_ROLE, 'option'); +// const index = element +// .getAttribute(ATTRIBUTE_UPDATER_INSERT_REFERENCE) +// .split("-") +// .pop(); +// +// const currentState = this.getOption("entries." + index + ".state"); +// event.dataTransfer.setData("text/plain", "22"); +// event.dataTransfer.setData("text/html", "22"); +// event.dataTransfer.effectAllowed = "move"; +// }; +// +// this[controlElementSymbol].addEventListener( +// "dragstart", +// this[dragstartEventHandlerSymbol], +// ); +// this[controlElementSymbol].addEventListener( +// "dragenter", +// this[dragenterEventHandlerSymbol], +// ); +// this[controlElementSymbol].addEventListener( +// "dragleave", +// this[dragleaveEventHandlerSymbol], +// ); +// this[controlElementSymbol].addEventListener( +// "dragover", +// this[dragoverEventHandlerSymbol], +// ); +// this[controlElementSymbol].addEventListener( +// "drop", +// this[dropEventHandlerSymbol], +// ); +// } +// +// // /** +// // * @private +// // * @throws {Error} missing default slot +// // * @throws {Error} no shadow-root is defined +// // * @throws {Error} missing url +// // * @throws {Error} we won't be able to read the data +// // * @throws {Error} request failed +// // * @throws {Error} not found +// // * @throws {Error} undefined status or type +// // * @fires Monster.Components.TreeMenu.event:monster-fetched +// // */ +// // function initIntersectionObserver() { +// // const self = this; +// // +// // if (self[intersectionObserverWasInitialized] === true) { +// // return +// // } +// // +// // self[intersectionObserverWasInitialized] = true; +// // +// // let options = { +// // threshold: [0.5] +// // } +// // +// // const callback = (entries, observer) => { +// // +// // for (const [, entry] of entries.entries()) { +// // if (entry.isIntersecting === true) { +// // if (!self.hasAttribute(ATTRIBUTE_FORM_RELOAD) || self.getAttribute(ATTRIBUTE_FORM_RELOAD).toLowerCase() === 'onshow') { +// // observer.disconnect(); +// // } +// // +// // try { +// // loadContent.call(self); +// // } catch (e) { +// // self.setAttribute(ATTRIBUTE_ERRORMESSAGE, e.toString()); +// // } +// // +// // +// // } +// // } +// // } +// // +// // const observer = new IntersectionObserver(callback, options); +// // observer.observe(self); +// // +// // +// // } +// +// /** +// * @private +// * @return {string} +// */ +// function getTemplate() { +// // language=HTML +// return ` +// <template id="entries"> +// <div data-monster-role="entry" +// draggable="true" +// data-monster-attributes=" +// data-monster-intend path:entries.intend, +// data-monster-state path:entries.state, +// data-monster-visibility path:entries.visibility, +// data-monster-filtered path:entries.filtered, +// data-monster-has-children path:entries.has-children"> +// +// <button data-monster-role="button" +// data-monster-attributes=" +// type path:type, +// role path:role, +// value path:entries.value, +// name path:name, +// part path:type | prefix:option- | suffix: form" tabindex="-1"> +// <span data-monster-role="folder-handler"></span> +// <span data-monster-replace="path:entries | index:label" part="entry-label"></span> +// </button> +// </template> +// +// <div data-monster-role="control" part="control"> +// <div part="entries" data-monster-role="entries" +// data-monster-insert="entries path:entries" +// tabindex="-1"></div> +// </div> +// `; +// } +// +// registerCustomElement(TreeMenu); diff --git a/source/components/tree-menu/tree-menu.mjs b/source/components/tree-menu/tree-menu.mjs index 57e7c086a9879355c089ba4fffc971c4342a863b..dabf74c3a6b0214c9751a9ad4882df85488f3c29 100644 --- a/source/components/tree-menu/tree-menu.mjs +++ b/source/components/tree-menu/tree-menu.mjs @@ -73,32 +73,9 @@ const openEntryEventHandlerSymbol = Symbol("openEntryEventHandler"); /** * TreeMenu * - * <img src="./images/tree-menu.png"> - * - * You can create this control either by specifying the HTML tag `<monster-tree-menu />` directly in the HTML - * - * ```html - * <monster-tree-menu></monster-tree-menu> - * ``` - * - * or using Javascript via the `document.createElement('monster-tree-menu');` method. - * - * ```javascript - * import {TreeMenu} from 'https://cdn.jsdelivr.net/npm/@schukai/component-treemenu@0.1.0/dist/modules/treemenu.js'; - * document.createElement('monster-treemenu'); - * ``` - * - * @startuml tree-menu.png - * skinparam monochrome true - * skinparam shadowing false - * HTMLElement <|-- CustomElement - * CustomElement <|-- CustomControl - * CustomControl <|-- TreeMenu - * @enduml * @since 1.0.0 - * @copyright schukai GmbH * @summary A TreeMenu control - * @fires Monster.Components.TreeMenu.event:monster-fetched + * @fires monster-fetched */ class TreeMenu extends CustomElement { /** diff --git a/source/dom/customelement.mjs b/source/dom/customelement.mjs index 91e1db64086cfd6a5485fe0096c7fc8d3ada1c51..40b4b34f3de8320f6164d2319171bae977112e2d 100644 --- a/source/dom/customelement.mjs +++ b/source/dom/customelement.mjs @@ -1211,7 +1211,7 @@ function initShadowRoot() { const mapping = this.getOption("templateMapping", {}); if (isObject(mapping)) { const formatter = new Formatter(mapping); - if (this.getOption("templateFormatter.marker.open")!==null) { + if (this.getOption("templateFormatter.marker.open") !== null) { formatter.setMarker( this.getOption("templateFormatter.marker.open"), this.getOption("templateFormatter.marker.close"), diff --git a/source/monster.mjs b/source/monster.mjs index 13697bb9ac653ec18ff825f99b495ef4ae5363b1..4e721f4f7bbdb98c9cfd53a09af3cebd4e075152 100644 --- a/source/monster.mjs +++ b/source/monster.mjs @@ -56,7 +56,6 @@ export * from "./components/form/constants.mjs"; export * from "./components/notify/message.mjs"; export * from "./components/notify/notify.mjs"; export * from "./components/notify/constants.mjs"; -export * from "./components/tree-menu/dragable-tree-menu.mjs"; export * from "./components/tree-menu/tree-menu.mjs"; export * from "./components/host/collapse.mjs"; export * from "./components/host/config-manager.mjs"; diff --git a/source/types/base.mjs b/source/types/base.mjs index c05a8ea50be147cda4fff1564e13a0c57beff3a6..c0fabff3e6901865a36014af49eecf5befac231a 100644 --- a/source/types/base.mjs +++ b/source/types/base.mjs @@ -37,14 +37,12 @@ export { Base }; * * The class was formerly called Object. * - * @license AGPLv3 * @since 1.5.0 * @copyright schukai GmbH - * @memberOf Monster.Types + * @summary The base class for the most classes in the monster library */ class Base extends Object { /** - * * @returns {string} */ toString() { diff --git a/source/types/nodelist.mjs b/source/types/nodelist.mjs index 68944380079b6c787383681c86b1ba8b700ed81d..02224e28258f081efbfb5d14a077d7a16d11238a 100644 --- a/source/types/nodelist.mjs +++ b/source/types/nodelist.mjs @@ -24,7 +24,6 @@ export { NodeList }; * @license AGPLv3 * @since 1.26.0 * @copyright schukai GmbH - * @memberOf Monster.Types * @summary A NodeList class */ class NodeList extends Set { diff --git a/source/types/proxyobserver.mjs b/source/types/proxyobserver.mjs index 436084f1759a250a0666f661486abda7471a76b3..8af7d89b2dc76317ffb492fb2b5d95c7bc0e60e5 100644 --- a/source/types/proxyobserver.mjs +++ b/source/types/proxyobserver.mjs @@ -30,11 +30,9 @@ export { ProxyObserver }; * * This also applies to nested objects. * - * @externalExample ../../example/types/proxyobserver.mjs * @license AGPLv3 * @since 1.0.0 * @copyright schukai GmbH - * @memberOf Monster.Types */ class ProxyObserver extends Base { /** diff --git a/source/types/queue.mjs b/source/types/queue.mjs index e37115ba177f895c59dc358cb42cc1e23d6f429f..153c0d3f6b43e62117ff36c505fafbdc5e77e10e 100644 --- a/source/types/queue.mjs +++ b/source/types/queue.mjs @@ -26,11 +26,9 @@ export { Queue }; * * You can create the instance via `new Queue()`. * - * @externalExample ../../example/types/queue.mjs * @license AGPLv3 * @since 1.4.0 * @copyright schukai GmbH - * @memberOf Monster.Types * @summary A Queue (Fifo) */ class Queue extends Base { diff --git a/source/types/randomid.mjs b/source/types/randomid.mjs index e7e5b008ee87829d0378fdf0658be9a9d5467508..35b8711150a5c8f88cb34278ba9fbf89e1d6eb5f 100644 --- a/source/types/randomid.mjs +++ b/source/types/randomid.mjs @@ -30,7 +30,6 @@ let internalCounter = 0; * @license AGPLv3 * @since 1.6.0 * @copyright schukai GmbH - * @memberOf Monster.Types * @summary class to generate random numbers */ class RandomID extends ID { diff --git a/source/types/regex.mjs b/source/types/regex.mjs index bd46de08b01b05cd3db2c57b6a892e81a25e3d74..ab6a5f284398376ceaf728227dd9c07e3abc8fe0 100644 --- a/source/types/regex.mjs +++ b/source/types/regex.mjs @@ -24,7 +24,6 @@ export { escapeString }; * @license AGPLv3 * @since 1.26.0 * @copyright schukai GmbH - * @memberOf Monster.Types * @throws {TypeError} value is not a string */ function escapeString(value) { diff --git a/source/types/stack.mjs b/source/types/stack.mjs index a1a7fc87968aa3344e841d445212b6551c88313a..bd8e08d0a8129dc93a00cc31b14ad81c1966cbc8 100644 --- a/source/types/stack.mjs +++ b/source/types/stack.mjs @@ -17,12 +17,11 @@ import { instanceSymbol } from "../constants.mjs"; export { Stack }; /** - * You can call the method via the monster namespace `new Monster.Types.Queue()`. + * You can call the method via the monster namespace `new Queue()`. * * @license AGPLv3 * @since 1.4.0 * @copyright schukai GmbH - * @memberOf Monster.Types */ class Stack extends Base { /** diff --git a/source/types/tokenlist.mjs b/source/types/tokenlist.mjs index af93a3fb13c6b06d0defff1353d5caea213fdb2d..c8f2f24d456bd2014aae54277fe379bb7bf65ee7 100644 --- a/source/types/tokenlist.mjs +++ b/source/types/tokenlist.mjs @@ -25,11 +25,9 @@ export { TokenList }; * * This class implements the [iteration protocol](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols). * - * @externalExample ../../example/types/tokenlist-1.mjs * @license AGPLv3 * @since 1.2.0 * @copyright schukai GmbH - * @memberOf Monster.Types */ class TokenList extends Base { /** diff --git a/source/types/typeof.mjs b/source/types/typeof.mjs index dccf95e118bb91c10f29b31f09836bb321f92a37..e71780252c5bf7e9645af5717da2ee49fd231ba0 100644 --- a/source/types/typeof.mjs +++ b/source/types/typeof.mjs @@ -17,13 +17,11 @@ export { typeOf }; /** * The built-in typeof method is known to have some historical weaknesses. This function tries to provide a better and more accurate result. * - * @externalExample ../../example/types/typeof.mjs * @param {*} value * @return {string} * @license AGPLv3 * @since 1.7.0 * @copyright schukai GmbH - * @memberOf Monster.Types * @throws {TypeError} value is not a primitive */ function typeOf(value) { diff --git a/source/types/uniquequeue.mjs b/source/types/uniquequeue.mjs index 15a98523318bf1c853dac91d92985f074575e371..16eda2533930d4f69a763d5819e6347d91a4a197 100644 --- a/source/types/uniquequeue.mjs +++ b/source/types/uniquequeue.mjs @@ -24,7 +24,6 @@ export { UniqueQueue }; * @license AGPLv3 * @since 1.4.0 * @copyright schukai GmbH - * @memberOf Monster.Types * @summary A queue for unique values */ class UniqueQueue extends Queue { diff --git a/source/types/uuid.mjs b/source/types/uuid.mjs index be65e4aa8201e14eb593c6dff24c90b0091dbf58..9e140fae2e59fe0121743cbf7b46a5ba39462dfe 100644 --- a/source/types/uuid.mjs +++ b/source/types/uuid.mjs @@ -14,7 +14,7 @@ import { internalSymbol } from "../constants.mjs"; import { random } from "../math/random.mjs"; -import { isObject } from "../types/is.mjs"; +import { isObject } from "./is.mjs"; import { Base } from "./base.mjs"; import { getGlobalObject } from "./global.mjs"; @@ -26,7 +26,6 @@ export { UUID }; * @license AGPLv3 * @since 1.25.0 * @copyright schukai GmbH - * @memberOf Monster.Types * @throws {Error} unsupported */ class UUID extends Base { diff --git a/source/types/version.mjs b/source/types/version.mjs index 73ee751c46f0974f82e3827df5bcc16eada7c5f8..13d0bb1d269b882eb5415ffcb7ca4d2eb4b19234 100644 --- a/source/types/version.mjs +++ b/source/types/version.mjs @@ -25,8 +25,7 @@ export { Version, getMonsterVersion }; * @since 1.0.0 * @author schukai GmbH * @copyright schukai GmbH - * @memberOf Monster.Types - * @summary The version object contains a sematic version number + * @summary The version object contains a semantic version number */ class Version extends Base { /** @@ -145,13 +144,11 @@ let monsterVersion; /** * Version of monster * - * @externalExample ../../example/types/version-2.mjs - * @returns {Monster.Types.Version} + * @returns {Version} * @license AGPLv3 * @since 1.0.0 * @copyright schukai GmbH * @author schukai GmbH - * @memberOf Monster */ function getMonsterVersion() { if (monsterVersion instanceof Version) { diff --git a/test/web/test.html b/test/web/test.html index 86e1f9393d61661b6dc4c74360172b4845f6554b..805db41ee3701969e7e45db50d108466552f2cb2 100644 --- a/test/web/test.html +++ b/test/web/test.html @@ -10,7 +10,7 @@ <body> <div id="headline" style="display: flex;align-items: center;justify-content: center;flex-direction: column;"> <h1 style='margin-bottom: 0.1em;'>Monster 3.79.0</h1> - <div id="lastupdate" style='font-size:0.7em'>last update So 6. Okt 14:31:55 CEST 2024</div> + <div id="lastupdate" style='font-size:0.7em'>last update So 6. Okt 14:35:17 CEST 2024</div> </div> <div id="mocha-errors" style="color: red;font-weight: bold;display: flex;align-items: center;justify-content: center;flex-direction: column;margin:20px;"></div>