wj-elements
Version:
WebJET Elements is a modern set of user interface tools harnessing the power of web components designed to simplify web application development.
237 lines (236 loc) • 10.4 kB
JavaScript
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
import WJElement from "./wje-element.js";
const styles = "/*\n[ WJ Tab Group ]\n*/\n\n:host {\n --wje-tab-top: 0;\n --wje-tab-start: 0;\n --wje-tab-end: 0;\n --wje-tab-bottom: 0;\n width: 100%;\n}\n.native-tab-group {\n display: flex;\n flex-direction: column;\n\n overflow: hidden;\n position: relative;\n}\n\n.native-tab-group > header {\n display: flex;\n flex-direction: column;\n\n & > nav {\n display: flex;\n align-items: center;\n }\n}\n\n.native-tab-group > section {\n width: 100%;\n\n & > article {\n scroll-snap-align: start;\n overflow-y: auto;\n overscroll-behavior-y: contain;\n }\n}\n\n/*TOP*/\n:host([variant='top']) {\n --wje-tab-top: auto !important;\n --wje-tab-writing-mode: horizontal-tb;\n .native-tab-group {\n flex-direction: column;\n }\n nav {\n border-bottom: 1px solid var(--wje-border-color);\n }\n}\n\n/*START*/\n:host([variant='start']) {\n --wje-tab-start: auto !important;\n --wje-tab-writing-mode: vertical-rl;\n .native-tab-group {\n flex-direction: row;\n }\n nav {\n flex-direction: column;\n border-right: 1px solid var(--wje-border-color);\n }\n}\n\n/*END*/\n:host([variant='end']) {\n --wje-tab-writing-mode: vertical-rl;\n .native-tab-group {\n flex-direction: row-reverse;\n }\n nav {\n flex-direction: column;\n border-left: 1px solid var(--wje-border-color);\n }\n}\n\n/*BOTTOM*/\n:host([variant='bottom']) {\n --wje-tab-bottom: auto !important;\n --wje-tab-writing-mode: horizontal-tb;\n .native-tab-group {\n flex-direction: column-reverse;\n }\n nav {\n border-top: 1px solid var(--wje-border-color);\n }\n}\n\n.dropdown-active {\n wje-button{\n &::part(native) {\n color: var(--wje-color-primary-8);\n }\n }\n}";
class TabGroup extends WJElement {
/**
* Creates an instance of TabGroup.
* @class
*/
constructor() {
super();
__publicField(this, "className", "TabGroup");
}
/**
* Sets the 'type' attribute of the element to the specified value.
* @param {string} value The value to set for the 'type' attribute.
*/
set type(value) {
this.setAttribute("type", value);
}
/**
* Retrieves the `type` attribute of the element.
* If the `type` attribute is not set, it defaults to `'panel'`.
* @returns {string} The value of the `type` attribute or the default value `'panel'`.
*/
get type() {
return this.getAttribute("type") || "panel";
}
/**
* Returns the CSS styles for the component.
* @static
* @returns {CSSStyleSheet}
*/
static get cssStyleSheet() {
return styles;
}
/**
* Sets up the attributes for the component.
*/
setupAttributes() {
this.isShadowRoot = "open";
}
/**
* Sets up the event listeners before the component is drawn.
* This method is called before the component is drawn.
* It is used to set up event listeners.
*/
beforeDraw() {
let activeTabName = location.hash.replace("#", "");
if (this.getPanelAllName().includes(activeTabName)) {
window.addEventListener("load", (e) => {
this.setActiveTab(activeTabName);
});
}
}
/**
* Creates and returns a document fragment containing a structured layout for a tab group.
* The tab group layout includes a `header` section with navigational elements,
* a `section` element for tab panels, and slots for customization such as additional navigation items,
* dropdowns, and more.
* The structure comprises:
* - A `div` container with relevant styling and part attributes.
* - A `header` for tabs, including a slot for navigation (`nav`) and additional tabs in a dropdown (`moreDropdown`).
* - A `section` for tab panels with a customizable `slot`.
* This function also initializes the `nav` and `moreDropdown` properties for external use.
* @returns {DocumentFragment} The completed document fragment containing the tab group layout.
*/
draw() {
let fragment = document.createDocumentFragment();
let native = document.createElement("div");
native.setAttribute("part", "native");
native.classList.add("native-tab-group");
let header = document.createElement("header");
header.setAttribute("part", "tabs");
header.classList.add("scroll-snap-x");
let nav = document.createElement("nav");
let section = document.createElement("section");
section.setAttribute("part", "panels");
let slot = document.createElement("slot");
let slotNav = document.createElement("slot");
slotNav.setAttribute("name", "nav");
let icon = document.createElement("wje-icon");
icon.setAttribute("name", "dots");
let button = document.createElement("wje-button");
button.setAttribute("slot", "trigger");
button.setAttribute("fill", "link");
let menu = document.createElement("wje-menu");
menu.setAttribute("variant", "context");
let slotMore = document.createElement("slot");
slotMore.setAttribute("name", "more");
let moreDropdown = document.createElement("wje-dropdown");
moreDropdown.setAttribute("placement", "bottom-end");
moreDropdown.setAttribute("collapsible", "");
moreDropdown.classList.add("more-tabs");
button.append(icon);
menu.append(slotMore);
moreDropdown.append(button);
moreDropdown.append(menu);
header.append(nav);
nav.append(slotNav);
nav.append(moreDropdown);
section.append(slot);
native.append(header);
native.append(section);
fragment.append(native);
this.nav = nav;
this.moreDropdown = moreDropdown;
return fragment;
}
/**
* Executes necessary initializations and attaches event listeners after a drawing operation.
* Handles active tab selection, 'wje-tab:change' event binding, and window resize event for overflow checking.
* @returns {void} Does not return a value.
*/
afterDraw() {
let activeTab = this.getActiveTab();
let activeTabName = activeTab ? activeTab[0][this.type] : this.getTabAll()[0][this.type];
this.setActiveTab(activeTabName);
this.addEventListener("wje-tab:change", (e) => {
if (e.detail.context.hasAttribute("disabled")) return;
this.setActiveTab(e.detail.context.panel);
});
this.checkOverflow = this.checkOverflow.bind(this);
window.addEventListener("resize", this.checkOverflow);
requestAnimationFrame(() => this.checkOverflow());
}
/**
* Removes the 'active' class from all panel and tab elements.
* @returns {void} This method does not return a value.
*/
removeActiveTab() {
this.getPanelAll().forEach((el) => {
el.classList.remove("active");
});
this.getTabAll().forEach((el) => {
el.classList.remove("active");
});
}
/**
* Sets the active tab and panel.
* @param {string} tab The name of the tab to set as active.
*/
setActiveTab(tab) {
var _a;
this.removeActiveTab();
const el = this.querySelector(`[${this.type}="${tab}"]`);
el == null ? void 0 : el.classList.add("active");
if (this.type === "panel")
(_a = this.querySelector(`[name="${tab}"]`)) == null ? void 0 : _a.classList.add("active");
if (el)
this.dropdownActive(el);
}
/**
* Returns the currently active tab.
* @returns {Element|null} The active tab, or null if no tab is active.
*/
getActiveTab() {
let activeTabs = Array.from(this.querySelectorAll("wje-tab.active"));
return activeTabs.length > 0 ? activeTabs : null;
}
/**
* Returns all tabs.
* @returns {Array<Element>} An array of all tabs.
*/
getTabAll() {
return this.context.querySelector('[name="nav"]').assignedElements();
}
/**
* Returns all panels.
* @returns {Array<Element>} An array of all panels.
*/
getPanelAll() {
return Array.from(this.querySelectorAll("wje-tab-panel"));
}
/**
* Returns the names of all tabs.
* @returns {Array<string>} An array of all tab names.
*/
getPanelAllName() {
return this.getPanelAll().map((el) => el.getAttribute("name"));
}
/**
* Toggles the visibility of the "more" dropdown based on the presence of tabs in the "more" slot.
* @returns {void} Does not return a value.
*/
toggleMoreVisibility() {
const hasTabsInMore = this.querySelector('wje-tab[slot="more"]');
this.moreDropdown.hidden = !hasTabsInMore;
}
/**
* Checks if the tabs within a navigation bar overflow the available space.
* Moves overflowing tabs into a dropdown menu and updates their state accordingly.
* @returns {void} This method does not return a value.
*/
checkOverflow() {
const nav = this.nav;
const moreBtn = this.moreDropdown;
const moreWidth = moreBtn.offsetWidth || 48;
const tabs = Array.from(this.querySelectorAll("wje-tab"));
tabs.forEach((tab) => tab.setAttribute("slot", "nav"));
requestAnimationFrame(() => {
const navRight = nav.getBoundingClientRect().right;
let overflowStarted = false;
for (const tab of tabs) {
const tabRect = tab.getBoundingClientRect();
const fits = tabRect.right + moreWidth <= navRight;
if (!fits || overflowStarted) {
tab.setAttribute("slot", "more");
this.dropdownActive(tab);
overflowStarted = true;
}
}
this.toggleMoreVisibility();
});
}
/**
* Toggles the "dropdown-active" class on the element based on its "active" status
* and the value of its "slot" attribute.
* @param {HTMLElement} el The HTML element to evaluate and apply the toggle logic.
* @returns {void} This method does not return any value.
*/
dropdownActive(el) {
if (el.classList.contains("active")) {
if (el.getAttribute("slot") === "more")
this.moreDropdown.classList.add("dropdown-active");
else
this.moreDropdown.classList.remove("dropdown-active");
}
}
}
TabGroup.define("wje-tab-group", TabGroup);
export {
TabGroup as default
};
//# sourceMappingURL=wje-tab-group.js.map