import { html, unsafeCSS } from "lit"; import { property, query } from "lit/decorators.js"; import eleStyle from "./f-breadcrumb.scss?inline"; import globalStyle from "./f-breadcrumb-global.scss?inline"; import { FRoot } from "../../mixins/components/f-root/f-root"; import { FDiv } from "../f-div/f-div"; import { flowElement } from "../../utils"; import { FText } from "../f-text/f-text"; import { FPopover } from "../f-popover/f-popover"; import { injectCss } from "@cldcvr/flow-core-config"; injectCss("f-breadcrumb", globalStyle); const variants = ["text", "icon"] as const; const sizes = ["medium", "small"] as const; export type FBreadCrumbsProp = { tabIndex: number; title: string; icon?: string }; export type FBreadcrumbs = FBreadCrumbsProp[]; export type FBreadcrumbSize = (typeof sizes)[number]; export type FBreadcrumbVariant = (typeof variants)[number]; @flowElement("f-breadcrumb") export class FBreadcrumb extends FRoot { /** * css loaded from scss file */ static styles = [ unsafeCSS(eleStyle), unsafeCSS(globalStyle), ...FDiv.styles, ...FText.styles, ...FPopover.styles ]; /** * @attribute The medium size is the default. */ @property({ reflect: true, type: String }) size?: FBreadcrumbSize = "medium"; /** * @attribute variant defines the type of brundcrumbs. */ @property({ reflect: true, type: String }) variant?: FBreadcrumbVariant = "text"; /** * @attribute The medium size is the default. */ @property({ reflect: false, type: Array }) crumbs!: FBreadcrumbs; /** * @attribute The disabled attribute can be set to keep a user from clicking. */ @property({ reflect: true, type: Boolean }) disabled?: boolean = false; /** * popover element reference */ @query("f-popover") popOverElement!: FPopover; @query("#breadcrumb-popover") breadCrumbPopover!: FDiv; initialCrumbs!: FBreadCrumbsProp; middlePopoverCrumbs: FBreadcrumbs = []; endingCrumbs: FBreadcrumbs = []; /** * text size computed */ get textSize() { return this.size === "medium" ? "small" : "x-small"; } get iconSize() { return this.size === "medium" ? "small" : "x-small"; } get crumbSize() { return this.size === "medium" ? "32px" : "28px"; } /** * * @param index index number of crumbs * @param array crumbs array * @returns checks whether index is current crumb or not */ isCurrentCrumb(index: number, array: FBreadcrumbs) { return index === array.length - 1; } /** * create seperate crumbs when crumbs length is greater than 4 */ createSeperateCrumbs() { if (this.crumbs.length > 4) { this.initialCrumbs = this.crumbs[0]; this.middlePopoverCrumbs = this.crumbs.slice(1, this.crumbs.length - 2); this.endingCrumbs = this.crumbs.slice(this.crumbs.length - 2, this.crumbs.length); } } /** * * @param crumb * @param index * @param array * @returns returns html to be printed in loop */ crumbLoop(crumb: FBreadCrumbsProp, index: number, array: FBreadcrumbs) { return html` this.handleDispatchEvent(event, crumb)} > ${crumb?.title} ${!this.isCurrentCrumb(index, array) ? html`` : null}`; } /** * * @param action action whether to close or open popover */ toggleBreadcrumbPopover(action: "open" | "close") { this.popOverElement.open = action === "open" ? true : false; this.popOverElement.target = this.breadCrumbPopover; } /** * dispatch click event * @param e event * @param value crumb value */ handleDispatchEvent(e: MouseEvent, value: FBreadCrumbsProp) { e.stopPropagation(); const event = new CustomEvent("click", { detail: { value: value }, bubbles: true, composed: true }); if (this.variant === "text") { this.popOverElement.open = false; } this.dispatchEvent(event); } render() { this.createSeperateCrumbs(); const textBreadcrumb = html` ${this.crumbs?.length <= 4 ? this.crumbs?.map((crumb, index) => this.crumbLoop(crumb, index, this.crumbs)) : html` this.handleDispatchEvent(event, this.initialCrumbs)} > ${this.initialCrumbs?.title} this.toggleBreadcrumbPopover("open")} id="breadcrumb-popover" > ... this.toggleBreadcrumbPopover("close")} > ${this.middlePopoverCrumbs?.map( (crumb, index) => html` this.handleDispatchEvent(event, crumb)} >${crumb?.title}` )} ${this.endingCrumbs?.map((crumb, index) => this.crumbLoop(crumb, index, this.endingCrumbs) )} `} `; const iconBreadcrumb = html` ${this.crumbs.map((item, index) => index !== this.crumbs.length - 1 ? html` this.handleDispatchEvent(event, item)} > ` : html`
this.handleDispatchEvent(event, item)} >
${item.title}
` )}
`; return this.variant === "text" ? textBreadcrumb : iconBreadcrumb; } } /** * Required for typescript */ declare global { interface HTMLElementTagNameMap { "f-breadcrumb": FBreadcrumb; } }