import { CSSResultArray , html , PropertyValues , SizedMixin , SpectrumElement , TemplateResult , } from '@spectrum-web-components/base' ; import { property } from '@spectrum-web-components/base/src/decorators.js' ; import { ifDefined } from '@spectrum-web-components/base/src/directives.js' ; import { TopNavItem } from './TopNavItem.js' ; import tabStyles from '@spectrum-web-components/tabs/src/tabs.css.js' ; const noSelectionStyle = 'transform: translateX(0px) scaleX(0) scaleY(0)' ; element slot attr attr export class TopNav extends SizedMixin ( SpectrumElement ) { public static get styles ( ) : CSSResultArray { return [ tabStyles ] ; } @ property ( ) public selectionIndicatorStyle = noSelectionStyle ; @ property ( { attribute : false } ) public shouldAnimate = false ; private onClick = ( event ) : void => { const target = event . target as TopNavItem ; this . shouldAnimate = true ; this . selectTarget ( target ) ; } ; @ property ( { reflect : true } ) public set selected ( value ) { const oldValue = this . selected ; if ( value === oldValue ) { return ; } this . updateCheckedState ( value ) ; this . _selected = value ; this . requestUpdate ( 'selected' , oldValue ) ; } public get selected ( ) : string | undefined { return this . _selected ; } private _selected ! : string | undefined ; protected items : TopNavItem [ ] = [ ] ; private manageItems ( ) { this . items = [ ... this . querySelectorAll ( 'sp-top-nav-item' ) , ] as TopNavItem [ ] ; const selectedChild = this . items . find ( ( item ) => item . value === window . location . href ) ; if ( selectedChild ) { this . selectTarget ( selectedChild ) ; } } protected render ( ) { return html `
            <div @click=${ this . onClick } id="list">
                <slot @slotchange=${ this . onSlotChange }></slot>
                <div
                    id="selection-indicator"
                    class=${ ifDefined ( this . shouldAnimate ? undefined : 'first-position' ) }
                    style=${ this . selectionIndicatorStyle }
                ></div>
            </div>
        ` ; } protected firstUpdated ( changes ) { super . firstUpdated ( changes ) ; this . setAttribute ( 'direction' , 'horizontal' ) ; this . manageItems ( ) ; } protected updated ( changes ) { super . updated ( changes ) ; if ( changes . has ( 'dir' ) ) { this . updateSelectionIndicator ( ) ; } if ( ! this . shouldAnimate && typeof changes . get ( 'shouldAnimate' ) !== 'undefined' ) { this . shouldAnimate = true ; } } private selectTarget ( target ) { const { value } = target ; if ( value ) { this . selected = value ; } } protected onSlotChange ( ) { this . manageItems ( ) ; } protected updateCheckedState ( value ) { this . items . forEach ( ( item ) => { item . selected = false ; } ) ; requestAnimationFrame ( ( ) => { if ( value && value . length ) { const currentItem = this . items . find ( ( item ) => item . value === value || item . value === window . location . href ) ; if ( currentItem ) { currentItem . selected = true ; } else { this . selected = '' ; } } this . updateSelectionIndicator ( ) ; } ) ; } private updateSelectionIndicator = async ( ) : Promise < void > => { const selectedItem = this . items . find ( ( item ) => item . value === this . selected || item . value === window . location . href ) ; if ( ! selectedItem ) { this . selectionIndicatorStyle = noSelectionStyle ; return ; } await Promise . all ( [ selectedItem . updateComplete , document . fonts ? document . fonts . ready : Promise . resolve ( ) , ] ) ; const itemBoundingClientRect = selectedItem . getBoundingClientRect ( ) ; const parentBoundingClientRect = this . getBoundingClientRect ( ) ; const width = itemBoundingClientRect . width ; const offset = this . dir === 'ltr' ? itemBoundingClientRect . left - parentBoundingClientRect . left : itemBoundingClientRect . right - parentBoundingClientRect . right ; this . selectionIndicatorStyle = `transform: translateX(${ offset }px) scaleX(${ this . dir === 'ltr' ? width : - 1 * width });` ; } ; public connectedCallback ( ) { super . connectedCallback ( ) ; window . addEventListener ( 'resize' , this . updateSelectionIndicator ) ; if ( 'fonts' in document ) { document . fonts . addEventListener ( 'loadingdone' , this . updateSelectionIndicator ) ; } } public disconnectedCallback ( ) { window . removeEventListener ( 'resize' , this . updateSelectionIndicator ) ; if ( 'fonts' in document ) { ( document as unknown as { fonts : { removeEventListener : ( name : string , callback : ( ) => void ) => void ; } ; } ) . fonts . removeEventListener ( 'loadingdone' , this . updateSelectionIndicator ) ; } super . disconnectedCallback ( ) ; } }