Files
cms-backend/Resources/Public/JavaScript/element/breadcrumb.js
T

14 lines
7.7 KiB
JavaScript

/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
import{LitElement as v,html as n,nothing as l,render as m}from"lit";import{property as b,customElement as C}from"lit/decorators.js";import{classMap as f}from"lit/directives/class-map.js";import"@typo3/backend/element/icon-element.js";import"@typo3/backend/dropdown.js";var p=function(d,e,t,i){var s=arguments.length,r=s<3?e:i===null?i=Object.getOwnPropertyDescriptor(e,t):i,o;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")r=Reflect.decorate(d,e,t,i);else for(var c=d.length-1;c>=0;c--)(o=d[c])&&(r=(s<3?o(r):s>3?o(e,t,r):o(e,t))||r);return s>3&&r&&Object.defineProperty(e,t,r),r},a;(function(d){d.FULL="full",d.PATH="path"})(a||(a={}));let u=class extends v{constructor(){super(...arguments),this.nodes=[],this.alignRight=!1,this.label="Breadcrumb",this.mode=a.FULL,this.collapsedNodes=[],this.adjustRafId=null,this.dropdownButtonWidth=null,this.ellipsisWidth=null,this.measurementContainer=null}connectedCallback(){super.connectedCallback(),this.identifier=Date.now().toString(36)+Math.random().toString(36).slice(2),this.resizeObserver=new ResizeObserver(()=>this.scheduleAdjust()),this.resizeObserver.observe(this),this.intersectionObserver=new IntersectionObserver(e=>{e.some(t=>t.isIntersecting)&&this.scheduleAdjust()}),this.intersectionObserver.observe(this)}disconnectedCallback(){this.resizeObserver.disconnect(),this.intersectionObserver.disconnect(),this.adjustRafId!==null&&(cancelAnimationFrame(this.adjustRafId),this.adjustRafId=null),super.disconnectedCallback()}createRenderRoot(){return this}willUpdate(e){e.has("nodes")&&(this.createMeasurementContainer(),this.measureEllipsis(),this.measureDropdownToggle(),this.measureNodeWidths())}render(){const e=this.nodes.filter(s=>!this.collapsedNodes.includes(s)),t=f({breadcrumb:!0,"breadcrumb-collapsible":!0,"breadcrumb-right":this.alignRight,"breadcrumb-condensed":this.mode===a.PATH}),i=n`${this.renderCollapsedNodesIndicator()} ${e.map(s=>{const r=f({"breadcrumb-item":!0,"breadcrumb-item-first":this.isFirstNode(s),"breadcrumb-item-last":this.isLastNode(s)});return n`<div class=${r} role=presentation>${this.renderBreadcrumbElement(s)}</div>`})}`;return this.mode===a.FULL?n`<nav class=${t} aria-label=${this.label}>${i}</nav>`:n`<span class=visually-hidden> ${this.label?`${this.label}: `:l}${this.nodes.map(s=>s.label).join(" / ")} </span><div class=${t} aria-hidden=true>${i}</div>`}scheduleAdjust(){this.adjustRafId===null&&(this.adjustRafId=requestAnimationFrame(()=>{this.adjustRafId=null,this.adjustBreadcrumb()}))}createMeasurementContainer(){this.measurementContainer===null&&(this.measurementContainer=document.createElement("div"),this.measurementContainer.classList.add("breadcrumb-measurement"),this.measurementContainer.ariaHidden="true",this.measurementContainer.style.position="absolute",this.measurementContainer.style.visibility="hidden",this.measurementContainer.style.pointerEvents="none",this.measurementContainer.style.width="auto",this.measurementContainer.style.whiteSpace="nowrap",this.renderRoot.appendChild(this.measurementContainer)),this.measurementContainer.classList.toggle("breadcrumb-condensed",this.mode===a.PATH)}measureDropdownToggle(){if(this.dropdownButtonWidth===null){const e=n`<div class="breadcrumb-item dropdown">${this.renderDropdownToggle()}</div>`;m(e,this.measurementContainer);const t=this.measurementContainer.querySelector("div");this.dropdownButtonWidth=t?.offsetWidth??0,m(l,this.measurementContainer)}}measureEllipsis(){if(this.ellipsisWidth===null){m(this.renderEllipsis(),this.measurementContainer);const e=this.measurementContainer.querySelector("div");this.ellipsisWidth=e?.offsetWidth??0,m(l,this.measurementContainer)}}measureNodeWidths(){const e=this.nodes,t=this.nodes.map(s=>{if(s.__width)return s;const r=n`<div class=${f({"breadcrumb-item":!0,"breadcrumb-item-first":this.isFirstNode(s),"breadcrumb-item-last":this.isLastNode(s)})} role=presentation>${this.renderBreadcrumbElement(s)}</div>`;m(r,this.measurementContainer);const c=this.measurementContainer.querySelector("div")?.offsetWidth??0;return m(l,this.measurementContainer),{...s,__width:c}});!this.areNodeCollectionsEqual(e,t)&&(this.nodes=t)}adjustBreadcrumb(){const e=this.renderRoot?.querySelector(".breadcrumb");if(!e)return;this.ellipsisWidth||(this.ellipsisWidth=null,this.measureEllipsis()),this.dropdownButtonWidth||(this.dropdownButtonWidth=null,this.measureDropdownToggle()),this.nodes.some(h=>!h.__width)&&this.measureNodeWidths();const t=this.mode===a.PATH?this.ellipsisWidth:this.dropdownButtonWidth;let s=this.nodes[this.nodes.length-1]?.__width||0,r=!1;const o=[];for(const h of this.nodes.slice(0,-1).reverse())!r&&e.clientWidth>s+t+h.__width?s+=h.__width:(r=!0,o.push(h));o.reverse(),(o.length!==this.collapsedNodes.length||o.some((h,g)=>h!==this.collapsedNodes[g]))&&(this.collapsedNodes=o,this.requestUpdate())}renderCollapsedNodesIndicator(){return this.collapsedNodes.length===0?l:this.mode===a.PATH?this.renderEllipsis():this.renderCollapsedNodesDropdown()}renderEllipsis(){return n`<div class="breadcrumb-item breadcrumb-ellipsis" aria-hidden=true title=${this.collapsedNodes.map(e=>e.label).join(" / ")}><span class=breadcrumb-element>...</span></div>`}renderCollapsedNodesDropdown(){return n`<div class="breadcrumb-item dropdown" role=presentation>${this.renderDropdownToggle()}<div class=dropdown-menu id=breadcrumb-menu-${this.identifier} popover=auto>${this.collapsedNodes.map(e=>this.renderDropdownItem(e))}</div></div>`}renderDropdownToggle(){return n`<button type=button class=breadcrumb-element popovertarget=breadcrumb-menu-${this.identifier}>...</button>`}renderDropdownItem(e){return e.url!==null?n`<button type=button class=dropdown-item title=${e.label} @click=${t=>this.triggerAction(t,e)}>${this.renderIcon(e)} <span class=dropdown-item-label>${e.label}</span></button>`:n`<div class=dropdown-item title=${e.label}>${this.renderIcon(e)} <span class=dropdown-item-label>${e.label}</span></div>`}renderBreadcrumbElement(e){const t=this.mode===a.FULL&&(e.forceShowIcon||this.isLastNode(e));return this.mode===a.FULL&&e.url!==null?n`<button type=button class=breadcrumb-element title=${e.label} aria-current=${this.mode===a.FULL&&this.isLastNode(e)?"page":l} @click=${s=>this.triggerAction(s,e)}>${t?this.renderIcon(e):l} <span class=breadcrumb-element-label>${e.label}</span></button>`:n`<div class=breadcrumb-element title=${e.label} aria-current=${this.mode===a.FULL&&this.isLastNode(e)?"page":l}>${t?this.renderIcon(e):l} <span class=breadcrumb-element-label>${e.label}</span></div>`}renderIcon(e){return e.icon?n`<typo3-backend-icon identifier=${e.icon} overlay=${e.iconOverlay} size=small></typo3-backend-icon>`:null}triggerAction(e,t){import("@typo3/backend/viewport.js").then(({default:i})=>{i.ContentContainer.setUrl(t.url)})}isFirstNode(e){return this.nodes[0]===e}isLastNode(e){return this.nodes[this.nodes.length-1]===e}areNodeCollectionsEqual(e,t){if(t.length!==e.length)return!1;for(let i=0;i<t.length;i++)if(!this.areNodesEqual(e[i],t[i]))return!1;return!0}areNodesEqual(e,t){return e.identifier===t.identifier&&e.label===t.label&&e.icon===t.icon&&e.iconOverlay===t.iconOverlay&&e.__width===t.__width}};p([b({type:Array})],u.prototype,"nodes",void 0),p([b({type:Boolean})],u.prototype,"alignRight",void 0),p([b({type:String})],u.prototype,"label",void 0),p([b({type:String})],u.prototype,"mode",void 0),u=p([C("typo3-breadcrumb")],u);export{u as Breadcrumb,a as BreadcrumbMode};