Something went wrong on our end
Select Git revision
Monster.DOM.CustomElement.html
-
Volker Schukai authoredVolker Schukai authored
columnbar.mjs 7.50 KiB
/**
* Copyright 2023 schukai GmbH
* SPDX-License-Identifier: AGPL-3.0
*/
import { instanceSymbol } from "../../constants.mjs";
import {
assembleMethodSymbol,
CustomElement,
registerCustomElement,
} from "../../dom/customelement.mjs";
import { findTargetElementFromEvent } from "../../dom/events.mjs";
import { clone } from "../../util/clone.mjs";
import { ColumnBarStyleSheet } from "./stylesheet/column-bar.mjs";
import { createPopper } from "@popperjs/core";
export { ColumnBar };
/**
* @private
* @type {symbol}
*/
const settingsButtonElementSymbol = Symbol("settingButtonElement");
/**
* @private
* @type {symbol}
*/
const settingsButtonEventHandlerSymbol = Symbol("settingsButtonEventHandler");
/**
* @private
* @type {symbol}
*/
const settingsLayerElementSymbol = Symbol("settingsLayerElement");
/**
* @private
* @type {symbol}
*/
const dotsContainerElementSymbol = Symbol("dotsContainerElement");
/**
* @private
* @type {symbol}
*/
const popperInstanceSymbol = Symbol("popperInstance");
/**
* The ColumnBar component is used to show and configure the columns of a datatable.
*
* <img src="./images/column-bar.png">
*
* You can create this control either by specifying the HTML tag <monster-column-bar />` directly in the HTML or using
* Javascript via the `document.createElement('monster-column-bar');` method.
*
* ```html
* <monster-column-bar></monster-column-bar>
* ```
*
* Or you can create this CustomControl directly in Javascript:
*
* ```js
* import '@schukai/monster/components/datatable/column-bar.mjs';
* document.createElement('monster-column-bar');
* ```
*
* The Body should have a class "hidden" to ensure that the styles are applied correctly.
*
* ```css
* body.hidden {
* visibility: hidden;
* }
* ```
*
* @startuml column-bar.png
* skinparam monochrome true
* skinparam shadowing false
* HTMLElement <|-- CustomElement
* CustomElement <|-- ColumnBar
* @enduml
*
* @copyright schukai GmbH
* @memberOf Monster.Components.Datatable
* @summary A data set
*/
class ColumnBar extends CustomElement {
/**
* This method is called by the `instanceof` operator.
* @returns {symbol}
*/
static get [instanceSymbol]() {
return Symbol.for("@schukai/monster/components/column-bar");
}
/**
* 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 {boolean} autoLoad If true, the datasource is called immediately after the control is created.
*/
get defaults() {
const obj = Object.assign({}, super.defaults, {
templates: {
main: getTemplate(),
},
locale: {
settings: "Settings",
},
columns: [],
});
return obj;
}
/**
*
* @return {string}
*/
static getTag() {
return "monster-column-bar";
}
/**
*
* @return {Monster.Components.Form.Form}
*/
[assembleMethodSymbol]() {
super[assembleMethodSymbol]();
initControlReferences.call(this);
initEventHandler.call(this);
}
/**
* @return {Array<ColumnBarStyleSheet>}
*/
static getCSSStyleSheet() {
return [ColumnBarStyleSheet];
}
}
/**
* @private
* @return {Monster.Components.Datatable.Form}
*/
function initControlReferences() {
if (!this.shadowRoot) {
throw new Error("no shadow-root is defined");
}
this[settingsButtonElementSymbol] = this.shadowRoot.querySelector(
"[data-monster-role=settings-button]",
);
this[settingsLayerElementSymbol] = this.shadowRoot.querySelector(
"[data-monster-role=settings-layer]",
);
this[dotsContainerElementSymbol] = this.shadowRoot.querySelector(
"[data-monster-role=dots]",
);
return this;
}
/**
* @private
*/
function initEventHandler() {
const self = this;
self[popperInstanceSymbol] = createPopper(
self[settingsButtonElementSymbol],
self[settingsLayerElementSymbol],
{
placement: "auto",
modifiers: [
{
name: "offset",
options: {
offset: [10, 10],
},
},
],
},
);
self[dotsContainerElementSymbol].addEventListener("click", function (event) {
const element = findTargetElementFromEvent(
event,
"data-monster-role",
"column",
);
if (element) {
const index = element.getAttribute("data-monster-index");
event.preventDefault();
const columns = clone(self.getOption("columns"));
const column = columns.find((col) => {
return parseInt(col.index) === parseInt(index);
});
column.visible = !column.visible;
self.setOption("columns", columns);
}
});
self[settingsButtonEventHandlerSymbol] = (event) => {
const clickTarget = event.composedPath()?.[0];
if (
self[settingsLayerElementSymbol] === clickTarget ||
self[settingsLayerElementSymbol].contains(clickTarget)
) {
return;
}
self[settingsLayerElementSymbol].classList.remove("visible");
document.body.removeEventListener(
"click",
self[settingsButtonEventHandlerSymbol],
);
};
self[settingsButtonElementSymbol].addEventListener("click", function (event) {
const element = findTargetElementFromEvent(
event,
"data-monster-role",
"settings-button",
);
if (element) {
self[settingsLayerElementSymbol].classList.toggle("visible");
event.preventDefault();
if (self[settingsLayerElementSymbol].classList.contains("visible")) {
self[popperInstanceSymbol].update();
setTimeout(() => {
document.body.addEventListener(
"click",
self[settingsButtonEventHandlerSymbol],
);
}, 0);
}
}
});
self[settingsLayerElementSymbol].addEventListener("change", function (event) {
const control = event.target;
const index = control.getAttribute("data-monster-index");
const columns = clone(self.getOption("columns"));
const column = columns.find((col) => {
return parseInt(col.index) === parseInt(index);
});
column.visible = control.checked;
self.setOption("columns", columns);
});
}
/**
* @private
* @return {string}
*/
function getTemplate() {
// language=HTML
return `
<template id="column">
<div data-monster-role="column">
<label><input type="checkbox" data-monster-attributes="
data-monster-index path:column.index,
checked path:column.visible | ?:checked:"><span
data-monster-replace="path:column.name"
></span></label>
</div>
</template>
<template id="dots">
<li data-monster-insert="">
<a href="#" data-monster-role="column"
data-monster-attributes="
class path:dots.visible | ?:is-hidden:is-visible,
title path:dots.name,
data-monster-index path:dots.index">
</a>
</li>
</template>
<div data-monster-role="control" part="control" data-monster-select-this="true" data-monster-attributes="class path:columns | has-entries | ?::hidden">
<ul data-monster-insert="dots path:columns"
data-monster-role="dots"></ul>
<a href="#" data-monster-role="settings-button" data-monster-replace="path:locale.settings">Settings</a>
<div data-monster-role="settings-layer">
<div data-monster-insert="column path:columns" data-monster-role="settings-popup-list">
</div>
</div>
</div>
`;
}
registerCustomElement(ColumnBar);