@schukai/monster
Version:
Monster is a simple library for creating fast, robust and lightweight websites.
338 lines (296 loc) • 8.15 kB
JavaScript
/**
* 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
*/
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";
import { getLocaleOfDocument } from "../../dom/locale.mjs";
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");
/**
* A column bar for a datatable
*
* @fragments /fragments/components/datatable/datatable/
*
* @example /examples/components/datatable/empty
*
* @copyright schukai GmbH
* @summary The ColumnBar component is used to show and configure the columns of a datatable.
**/
class ColumnBar extends CustomElement {
/**
* This method is called by the `instanceof` operator.
* @return {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: getTranslations(),
columns: [],
});
return obj;
}
/**
*
* @return {string}
*/
static getTag() {
return "monster-column-bar";
}
/**
*
* @return {void}
*/
[assembleMethodSymbol]() {
super[assembleMethodSymbol]();
initControlReferences.call(this);
initEventHandler.call(this);
}
/**
* @return {CSSStyleSheet[]}
*/
static getCSSStyleSheet() {
return [ColumnBarStyleSheet];
}
}
/**
* @private
* @returns {{settings: string}}
*/
function getTranslations() {
const locale = getLocaleOfDocument();
switch (locale.language) {
case "de":
return {
settings: "Einstellungen",
};
case "fr":
return {
settings: "Paramètres",
};
case "sp":
return {
settings: "Configuración",
};
case "it":
return {
settings: "Impostazioni",
};
case "pl":
return {
settings: "Ustawienia",
};
case "no":
return {
settings: "Innstillinger",
};
case "dk":
return {
settings: "Indstillinger",
};
case "sw":
return {
settings: "Inställningar",
};
default:
case "en":
return {
settings: "Settings",
};
}
}
/**
* @private
* @return {ColumnBar}
*/
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;
}
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();
queueMicrotask(() => {
document.body.addEventListener(
"click",
self[settingsButtonEventHandlerSymbol],
);
});
}
}
});
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);