@sandlada/mdc
Version:
@sandlada/mdc is an open source component library that follows the Material Design 3 design specifications.
20 lines • 5.41 kB
JavaScript
import{property as e}from"../../../node_modules/@lit/reactive-element/decorators/property.js";import{query as t}from"../../../node_modules/@lit/reactive-element/decorators/query.js";import{queryAssignedElements as n}from"../../../node_modules/@lit/reactive-element/decorators/query-assigned-elements.js";import{queryAssignedNodes as r}from"../../../node_modules/@lit/reactive-element/decorators/query-assigned-nodes.js";import"../../../node_modules/lit/decorators.js";import{html as i}from"../../../node_modules/lit-html/lit-html.js";import{LitElement as a}from"../../../node_modules/lit-element/lit-element.js";import"../../../node_modules/lit/index.js";import{classMap as o}from"../../../node_modules/lit-html/directives/class-map.js";import"../../../node_modules/lit/directives/class-map.js";import{mixinElementInternals as s}from"../../../utils/behaviors/element-internals.js";import{composeMixin as c}from"../../../utils/compose-mixin/compose-mixin.js";import l from"../../../_virtual/_@oxc-project_runtime@0.144.0/helpers/esm/decorate.js";import{MeasuredDimensionController as u}from"../../../utils/controller/measured-dimension-controller.js";import{OpacityTransitionController as d}from"../../../utils/controller/opacity-transition-controller.js";import{mixinFocusRingOptions as f}from"../../focus-ring/focus-ring-options.mixin.js";import{mixinRippleOptions as p}from"../../ripple/ripple-options.mixin.js";
/**
* @license
* Copyright 2026 Kai-Orion & Sandlada
* SPDX-License-Identifier: MIT
*
* @fileoverview
* Internal base class for `mdc-tab`.
*
* A tab is a single-select cell inside an `mdc-tabs` bar. Selection is owned
* by the parent bar (roving `tabindex`, `role="tab"`, `aria-selected`), while
* the tab is responsible for rendering its icon / label and the animated
* active indicator.
*
* Label and icon width/opacity changes are animated via
* {@link MeasuredDimensionController} and {@link OpacityTransitionController},
* so a tab visibly resizes / fades when its slotted content changes.
*/
const m=Symbol(`animateIndicator`);var h=class extends c(s,p,f)(a){get selected(){return this.active}set selected(e){this.active=e}get rippleControl(){return this}get focusRingControl(){return this}constructor(){super(),this.isTab=!0,this.active=!1,this.variant=`primary`,this.index=0,this.hasIcon=!1,this.iconOnly=!1,this.labelWidthController=new u(this,{target:()=>this.labelElement}),this.labelOpacityController=new d(this,{target:()=>this.labelElement}),this.iconOpacityController=new d(this,{target:()=>this.iconElement}),this.handleKeydown=e=>{e.defaultPrevented||(e.key===`Enter`||e.key===` `)&&(e.preventDefault(),this.click())},this.handleContentClick=e=>{e.stopPropagation(),this.click()},this.handleSlotChange=()=>{this.iconOnly=!1;for(let e of this.assignedDefaultNodes){let t=e.nodeType===Node.TEXT_NODE&&!!e.wholeText.match(/\S/);if(e.nodeType===Node.ELEMENT_NODE||t)return}this.iconOnly=!0},this.handleIconSlotChange=()=>{this.hasIcon=this.assignedIcons.length>0},this.role=`tab`,this.setAttribute(`aria-selected`,String(this.active)),this.addEventListener(`keydown`,this.handleKeydown)}getContentClasses(){return{"has-icon":this.hasIcon,"has-label":!this.iconOnly}}render(){return i`<div class="tab" role="presentation" ="${this.handleContentClick}">${this.renderFocusRing()} ${this.renderRipple()}<div class="content ${o(this.getContentClasses())}" role="presentation"><span class="icon" aria-hidden="true"><slot name="icon" ="${this.handleIconSlotChange}"></slot></span><span class="label"><slot ="${this.handleSlotChange}"></slot></span></div><div class="indicator" aria-hidden="true"></div></div>`}updated(e){super.updated(e),e.has(`active`)&&this.setAttribute(`aria-selected`,String(this.active))}[m](e){if(!this.indicatorElement)return;this.indicatorElement.getAnimations().forEach(e=>e.cancel());let t=this.getKeyframes(e);t!==null&&this.indicatorElement.animate(t,{duration:250,easing:`cubic-bezier(0.38, 1.21, 0.22, 1)`})}getKeyframes(e){let t=g();if(!this.active)return t?[{opacity:1},{transform:`none`}]:null;let n={},r=e?.indicatorElement?.getBoundingClientRect()??{},i=r.left,a=r.width,o=this.indicatorElement.getBoundingClientRect(),s=o.left,c=a/o.width;return!t&&i!==void 0&&s!==void 0&&!isNaN(c)?n.transform=`translateX(${(i-s).toFixed(4)}px) scaleX(${c.toFixed(4)})`:n.opacity=0,[n,{transform:`none`}]}};l([e({type:Boolean,reflect:!0,attribute:`mdc-tab`})],h.prototype,`isTab`,void 0),l([e({type:Boolean,reflect:!0})],h.prototype,`active`,void 0),l([e({type:Boolean})],h.prototype,`selected`,null),l([e({type:String,reflect:!0})],h.prototype,`variant`,void 0),l([e({type:Number})],h.prototype,`index`,void 0),l([e({type:Boolean,reflect:!0,attribute:`has-icon`})],h.prototype,`hasIcon`,void 0),l([e({type:Boolean,reflect:!0,attribute:`icon-only`})],h.prototype,`iconOnly`,void 0),l([t(`.tab`)],h.prototype,`buttonElement`,void 0),l([t(`.content`)],h.prototype,`contentElement`,void 0),l([t(`.indicator`)],h.prototype,`indicatorElement`,void 0),l([t(`.label`)],h.prototype,`labelElement`,void 0),l([t(`.icon`)],h.prototype,`iconElement`,void 0),l([r({flatten:!0})],h.prototype,`assignedDefaultNodes`,void 0),l([n({slot:`icon`,flatten:!0})],h.prototype,`assignedIcons`,void 0);function g(){return window.matchMedia(`(prefers-reduced-motion: reduce)`).matches}export{m as ANIMATE_INDICATOR,h as BaseTab};
//# sourceMappingURL=base-tab.js.map