import { PropertyValues } from '@spectrum-web-components/base' ; import { property } from '@spectrum-web-components/base/src/decorators.js' ; import { Focusable } from '@spectrum-web-components/shared/src/focusable.js' ; import { ProvideLang } from '@spectrum-web-components/theme' ; import { NumberFormatOptions , NumberFormatter , } from '@internationalized/number' ; export type HandleMin = number | 'previous' ; export type HandleMax = number | 'next' ; export type HandleValues = { name : string ; value : number ; } [ ] ; export interface Controller { inputForHandle ( handle ) : HTMLInputElement | undefined ; requestUpdate ( ) : void ; setValueFromHandle ( handle ) : void ; handleHasChanged ( handle ) : void ; } export type SliderNormalization = { toNormalized : ( value : number , min : number , max : number ) => number ; fromNormalized : ( value : number , min : number , max : number ) => number ; } ; export const defaultNormalization = { toNormalized ( value , min , max ) { return ( value - min ) / ( max - min ) ; } , fromNormalized ( value , min , max ) { return value * ( max - min ) + min ; } , } ; const MinConverter = { fromAttribute : ( value ) : number | 'previous' => { if ( value === 'previous' ) return value ; return parseFloat ( value ) ; } , toAttribute : ( value ) : string => { return value . toString ( ) ; } , } ; const MaxConverter = { fromAttribute : ( value ) : number | 'next' => { if ( value === 'next' ) return value ; return parseFloat ( value ) ; } , toAttribute : ( value ) : string => { return value . toString ( ) ; } , } ; element fires fires export class SliderHandle extends Focusable { public handleController ? : Controller ; public get handleName ( ) : string { return this . name ; } public get focusElement ( ) : HTMLElement { return this . handleController ?. inputForHandle ( this ) ?? this ; } _forcedUnit = '' ; @ property ( { type : Number } ) value = 10 ; @ property ( { type : Boolean , reflect : true } ) public dragging = false ; @ property ( { type : Boolean } ) public highlight = false ; @ property ( { type : String } ) public name = '' ; @ property ( { reflect : true , converter : MinConverter } ) public min ? : number | 'previous' ; @ property ( { reflect : true , converter : MaxConverter } ) public max ? : number | 'next' ; @ property ( { attribute : false } ) private resolvedLanguage = document . documentElement . lang || navigator . language ; @ property ( { type : Number , reflect : true } ) public step ? : number ; @ property ( { type : Object , attribute : 'format-options' } ) public formatOptions ? : NumberFormatOptions ; @ property ( { type : String } ) public label = '' ; @ property ( { attribute : false } ) public getAriaHandleText : ( value : number , numberFormat : NumberFormatter ) => string = ( value , numberFormat ) => { return numberFormat . format ( value ) ; } ; protected update ( changes ) { if ( changes . has ( 'formatOptions' ) || changes . has ( 'resolvedLanguage' ) ) { delete this . _numberFormatCache ; } super . update ( changes ) ; } protected updated ( changedProperties ) { if ( changedProperties . has ( 'value' ) ) { const oldValue = changedProperties . get ( 'value' ) ; if ( oldValue != null ) { this . handleController ?. setValueFromHandle ( this ) ; } } this . handleController ?. handleHasChanged ( this ) ; super . updated ( changedProperties ) ; } protected firstUpdated ( changedProperties ) { super . firstUpdated ( changedProperties ) ; this . dispatchEvent ( new CustomEvent ( 'sp-slider-handle-ready' ) ) ; } @ property ( { attribute : false } ) public normalization : SliderNormalization = defaultNormalization ; public dispatchInputEvent ( ) { const inputEvent = new Event ( 'input' , { bubbles : true , composed : true , } ) ; this . dispatchEvent ( inputEvent ) ; } protected _numberFormatCache : { numberFormat : NumberFormatter ; language : string ; } | undefined ; protected getNumberFormat ( ) { if ( ! this . _numberFormatCache || this . resolvedLanguage !== this . _numberFormatCache . language ) { let numberFormatter ; try { numberFormatter = new NumberFormatter ( this . resolvedLanguage , this . formatOptions ) ; this . _forcedUnit = '' ; } catch ( error ) { const { style , unit , unitDisplay , ... formatOptionsNoUnit } = this . formatOptions || { } ; if ( style === 'unit' ) { this . _forcedUnit = unit as string ; } numberFormatter = new NumberFormatter ( this . resolvedLanguage , formatOptionsNoUnit ) ; } this . _numberFormatCache = { language : this . resolvedLanguage , numberFormat : numberFormatter , } ; } return this . _numberFormatCache ?. numberFormat ; } public get numberFormat ( ) : NumberFormatter | undefined { if ( ! this . formatOptions ) return ; return this . getNumberFormat ( ) ; } public connectedCallback ( ) { super . connectedCallback ( ) ; this . resolveLanguage ( ) ; } public disconnectedCallback ( ) { this . resolveLanguage ( ) ; super . disconnectedCallback ( ) ; } private resolveLanguage ( ) { const queryThemeEvent = new CustomEvent < ProvideLang > ( 'sp-language-context' , { bubbles : true , composed : true , detail : { callback : ( lang ) => { this . resolvedLanguage = lang ; } , } , cancelable : true , } ) ; this . dispatchEvent ( queryThemeEvent ) ; } }