@sandlada/mdc
Version:
@sandlada/mdc is an open source component library that follows the Material Design 3 design specifications.
26 lines • 9.43 kB
JavaScript
import{customElement as e}from"../../node_modules/@lit/reactive-element/decorators/custom-element.js";import{property as t}from"../../node_modules/@lit/reactive-element/decorators/property.js";import"../../node_modules/lit/decorators.js";import{html as n,nothing as r}from"../../node_modules/lit-html/lit-html.js";import"../../node_modules/lit/index.js";import{classMap as i}from"../../node_modules/lit-html/directives/class-map.js";import"../../node_modules/lit/directives/class-map.js";import a from"../../_virtual/_@oxc-project_runtime@0.144.0/helpers/esm/decorate.js";import{styleMap as o}from"../../node_modules/lit-html/directives/style-map.js";import"../../node_modules/lit/directives/style-map.js";import{when as s}from"../../node_modules/lit-html/directives/when.js";import{BaseSlider as c}from"../slider/internal/base-slider.js";import{ExpressiveSliderStyles as l}from"./expressive-slider.style.js";import{ExpressiveSliderDirection as u,ExpressiveSliderType as d}from"./expressive-slider.interface.js";
/**
* @license
* Copyright 2026 Kai-Orion & Sandlada
* SPDX-License-Identifier: MIT
*
* @fileoverview
* `mdc-expressive-slider` — Material Design 3 Expressive slider.
*
* Layout pulled from the Figma prototype (node-id `58008:10353`):
* - Standard: `[active-track] [handle] [inactive-track]`, with the visible
* track inset by `handleWidth/2` from each slider edge so the handle
* never sits flush against the slider edge.
* - Centered: two inactive halves split at 50%, with the same inset
* mirrored around the center (`--_center-inset`) so the active overlay
* has a center gap and rounded ends on BOTH sides.
* - Range: two inactive halves with two handles symmetric about the
* center and a center dot at the 50% mark.
*
* Five size presets (`extra-small` / `small` / `medium` / `large` /
* `extra-large`) and two orientations (`horizontal` / `vertical`).
*
* @link https://m3.material.io/components/sliders/specs
*/
let f=class extends c{constructor(...e){super(...e),this.size=`medium`,this.direction=u.Horizontal,this.type=d.Standard,this.reversed=!1}static{this.styles=l}get range(){return this.type===d.Range}set range(e){e&&this.type!==d.Range&&(this.type=d.Range)}getRenderClasses(){return{ranged:this.type===d.Range,centered:this.type===d.Centered,reversed:this.reversed,[this.size]:!0,[this.direction]:!0}}render(){let e=this.computeFractions(),{visualNormalized:t,visualStartFraction:a,visualEndFraction:c,minVisualFraction:l,maxVisualFraction:u}=e,f=this.step===0?1:this.step,p=Math.max(this.max-this.min,f),m={"--_start-fraction":String(l),"--_end-fraction":String(u),"--_tick-count":String(p/f)},h=-this.max,g=this.max,_=e=>`inset-inline-start: calc(${e} * (100% - var(--_handle-width)));`;this.type===d.Range&&(m[`--_clip-to-start`]=`calc(var(--_edge-inset) + ${(a+c)/2} * (100% - 2 * var(--_edge-inset)))`,m[`--_clip-to-end`]=`calc(100% - ${m[`--_clip-to-start`]})`);let v=this.valueLabelStart||String(this.renderValueStart),y=(this.type===d.Range?this.valueLabelEnd:this.valueLabel)||String(this.renderValueEnd);return n`<div class="container ${i(this.getRenderClasses())}" style="${o(m)}">${this.renderTrackStart()} ${s(this.type===d.Range,()=>this.renderTrackMiddle(l,u))} ${this.type===d.Standard?this.renderHandle({start:!1,hover:this.handleEndHover,label:y,style:_(t)}):r} ${this.renderTrackEnd()} ${this.type===d.Centered?this.renderHandle({start:!1,hover:this.handleEndHover,label:y,style:_(t)}):r} ${s(this.type===d.Range,()=>this.renderHandle({start:!1,hover:this.handleEndHover,label:y,style:_(c)}))} ${s(this.type===d.Range,()=>this.renderHandle({start:!0,hover:this.handleStartHover,label:v,style:_(a)}))} ${s(this.type===d.Range,()=>this.renderCenterDot())} ${s(this.type===d.Centered,()=>this.renderCenteredOverlay(e))} ${this.ticks?n`<div class="tickmarks"></div>`:r} ${s(this.type===d.Range,()=>this.renderInput({start:!0,value:this.renderValueStart,ariaLabel:this.renderAriaLabelStart,ariaValueText:this.renderAriaValueTextStart,ariaMin:this.min,ariaMax:this.max,inputMin:this.min,inputMax:this.max}))} ${this.renderInput({start:!1,value:this.renderValueEnd,ariaLabel:this.renderAriaLabelEnd,ariaValueText:this.renderAriaValueTextEnd,ariaMin:this.type===d.Centered?h:this.min,ariaMax:this.type===d.Centered?g:this.max,inputMin:this.type===d.Centered?h:this.min,inputMax:this.type===d.Centered?g:this.max})}</div>`}computeFractions(){let e=this.step===0?1:this.step,t=Math.max(this.max-this.min,e),n,r,i=0,a;if(this.type===d.Range)n=((this.renderValueStart??this.min)-this.min)/t,r=((this.renderValueEnd??this.min)-this.min)/t,i=r;else if(this.type===d.Centered){let e=this.max;i=((this.value??0)+e)/(2*e),a=i,n=i<.5?(.5-i)*2:0,r=i>.5?(i-.5)*2:0}else n=0,i=((this.renderValueEnd??this.min)-this.min)/t,r=i;let o=this.direction===u.Vertical?!this.reversed:this.reversed,s=o?1-i:i,c,l,f,p;return this.type===d.Range?(c=o?1-n:n,l=o?1-r:r,f=Math.min(c,l),p=Math.max(c,l)):this.type===d.Centered?(c=s,l=s,f=Math.min(.5,s),p=Math.max(.5,s)):(c=0,l=s,f=o?s:0,p=o?1:s),{startFraction:n,endFraction:r,normalized:i,centeredFraction:a,isVisualReversed:o,visualNormalized:s,visualStartFraction:c,visualEndFraction:l,minVisualFraction:f,maxVisualFraction:p}}renderTrackStart(){return this.renderTrackSegment(`start`)}renderTrackEnd(){return this.renderTrackSegment(`end`)}renderTrackMiddle(e,t){let r=`inset-inline-start: calc(${e} * (100% - var(--_handle-width)) + var(--_handle-width) + var(--_thumb-track-gap)); inset-inline-end: calc(100% - ${t} * (100% - var(--_handle-width)) + var(--_thumb-track-gap));`;return n`<div class="track track-middle" data-position="middle" data-active="true" style="${r}"></div>`}renderTrackSegment(e){let t=this.type===d.Centered,r=this.type===d.Range,i=this.type===d.Standard,{isVisualReversed:a,visualNormalized:o,minVisualFraction:s,maxVisualFraction:c}=this.computeFractions(),l=`inset-inline-start`,u=`inset-inline-end`;if(i){let t=a?e===`end`:e===`start`,r=e===`start`?`${l}: var(--_edge-inset); ${u}: calc(100% - ${o} * (100% - var(--_handle-width)) + var(--_thumb-track-gap));`:`${l}: calc(${o} * (100% - var(--_handle-width)) + var(--_handle-width) + var(--_thumb-track-gap)); ${u}: var(--_edge-inset);`;return n`<div class="track" data-position="${e}" data-active="${t}" style="${r}"><div class="stop-indicator"></div></div>`}if(r){let t=e===`start`?`${l}: var(--_edge-inset); ${u}: calc(100% - ${s} * (100% - var(--_handle-width)) + var(--_thumb-track-gap));`:`${l}: calc(${c} * (100% - var(--_handle-width)) + var(--_handle-width) + var(--_thumb-track-gap)); ${u}: var(--_edge-inset);`;return n`<div class="track" data-position="${e}" data-active="false" style="${t}"><div class="stop-indicator"></div></div>`}return t?n`<div class="track" data-position="${e}" data-active="false" style="${e===`start`?`${l}: var(--_edge-inset); ${u}: calc(50% + var(--_edge-inset));`:`${l}: calc(50% + var(--_edge-inset)); ${u}: var(--_edge-inset);`}"><div class="stop-indicator"></div></div>`:n`<div class="track" data-position="${e}" data-active="false" style="${l}${u}"><div class="stop-indicator"></div></div>`}renderCenteredOverlay(e){let{visualNormalized:t}=e,r=`inset-inline-start`,i=`inset-inline-end`;if(t===.5)return n`<div class="center-stop" aria-hidden="true"></div>`;let a=t>.5,o=a?`${r}: calc(50% + var(--_edge-inset)); ${i}: calc(100% - ${t} * (100% - var(--_handle-width)) + var(--_thumb-track-gap));`:`${r}: calc(${t} * (100% - var(--_handle-width)) + var(--_handle-width) + var(--_thumb-track-gap)); ${i}: calc(50% + var(--_edge-inset));`;return n`<div class="active-overlay" data-position="${a?`end`:`start`}" style="${o}"></div>`}renderCenterDot(){return n`<div class="center-dot" aria-hidden="true"></div>`}renderInput({start:e,value:t,ariaLabel:a,ariaValueText:o,ariaMin:s,ariaMax:c,inputMin:l,inputMax:u}){let d=e?`start`:`end`,f=this.computeFractions().isVisualReversed?`rtl`:r;return n`<input type="range" class="${i({start:e,end:!e})}" dir="${f}" ="${this.handleFocus}" ="${this.handleDown}" ="${this.handleUp}" ="${this.handleEnter}" ="${this.handleMove}" ="${this.handleLeave}" ="${this.handleKeydown}" ="${this.handleKeyup}" ="${this.handleInput}" ="${this.handleChange}" id="${d}" .disabled="${this.disabled}" .min="${String(l)}" aria-valuemin="${s}" .max="${String(u)}" aria-valuemax="${c}" .step="${String(this.step)}" .value="${String(t)}" .tabIndex="${+!!e}" aria-label="${a||r}" aria-valuetext="${o}">`}renderHandle({start:e,hover:t,label:r,style:a}){let o=this.type===d.Range,c=e?`start`:`end`;return n`<div class="handle ${i({[c]:!0,hover:t,"range-handle-start":o&&e,"range-handle-end":o&&!e})}" style="${a||``}"><mdc-focus-ring part="focus-ring" for="${c}"></mdc-focus-ring><mdc-ripple for="${c}" class="${c}" ?disabled="${this.disabled}"></mdc-ripple><div class="handleNub"></div>${s(this.labeled,()=>this.renderLabel(r))}</div>`}renderLabel(e){return n`<div class="label" aria-hidden="true"><span class="labelContent" part="label">${e}</span></div>`}};a([t({type:String,reflect:!0})],f.prototype,`size`,void 0),a([t({type:String,reflect:!0})],f.prototype,`direction`,void 0),a([t({type:String,reflect:!0})],f.prototype,`type`,void 0),a([t({type:Boolean,reflect:!0})],f.prototype,`reversed`,void 0),f=a([e(`mdc-expressive-slider`)],f);export{f as MDCExpressiveSlider};
//# sourceMappingURL=expressive-slider.js.map