import { html , PropertyValues , TemplateResult , } from '@spectrum-web-components/base' ; import { property , query , } from '@spectrum-web-components/base/src/decorators.js' ; import { LikeAnchor } from '@spectrum-web-components/shared/src/like-anchor.js' ; import { Focusable } from '@spectrum-web-components/shared/src/focusable.js' ; import { ObserveSlotPresence , ObserveSlotText , } from '@spectrum-web-components/shared' ; slot slot export class ButtonBase extends LikeAnchor ( ObserveSlotText ( ObserveSlotPresence ( Focusable , '[slot="icon"]' ) ) ) { protected get hasIcon ( ) : boolean { return this . slotContentIsPresent ; } @ property ( { type : Boolean , reflect : true } ) public active = false ; @ property ( { type : String } ) public type : 'button' | 'submit' | 'reset' = 'button' ; protected get hasLabel ( ) : boolean { return this . slotHasContent ; } @ query ( '.anchor' ) private anchorElement ! : HTMLButtonElement ; public get focusElement ( ) : HTMLElement { return this ; } protected get buttonContent ( ) : TemplateResult [ ] { const content = [ html `
                <div id="label" ?hidden=${ ! this . hasLabel }>
                    <slot
                        id="slot"
                        @slotchange=${ this . manageTextObservedSlot }
                    ></slot>
                </div>
            ` , ] ; if ( this . hasIcon ) { content . unshift ( html `
                <slot name="icon" ?icon-only=${ ! this . hasLabel }></slot>
            ` ) ; } return content ; } constructor ( ) { super ( ) ; this . proxyFocus = this . proxyFocus . bind ( this ) ; this . addEventListener ( 'click' , this . handleClickCapture , { capture : true , } ) ; } public click ( ) { if ( this . disabled ) { return ; } if ( this . shouldProxyClick ( ) ) { return ; } super . click ( ) ; } private handleClickCapture ( event ) { if ( this . disabled ) { event . preventDefault ( ) ; event . stopImmediatePropagation ( ) ; event . stopPropagation ( ) ; return false ; } } private proxyFocus ( ) { this . focus ( ) ; } private shouldProxyClick ( ) { let handled = false ; if ( this . anchorElement ) { this . anchorElement . click ( ) ; handled = true ; } else if ( this . type !== 'button' ) { const proxy = document . createElement ( 'button' ) ; proxy . type = this . type ; this . insertAdjacentElement ( 'afterend' , proxy ) ; proxy . click ( ) ; proxy . remove ( ) ; handled = true ; } return handled ; } public renderAnchor ( ) { return html `
            ${ this . buttonContent }
            ${ super . renderAnchor ( { id : 'button' , ariaHidden : true , className : 'button anchor hidden' , } ) }
        ` ; } protected renderButton ( ) { return html `
            ${ this . buttonContent }
        ` ; } protected render ( ) { return this . href && this . href . length > 0 ? this . renderAnchor ( ) : this . renderButton ( ) ; } protected handleKeydown ( event ) { const { code } = event ; switch ( code ) { case 'Space' : event . preventDefault ( ) ; if ( typeof this . href === 'undefined' ) { this . addEventListener ( 'keyup' , this . handleKeyup ) ; this . active = true ; } break ; default : break ; } } private handleKeypress ( event ) { const { code } = event ; switch ( code ) { case 'Enter' : case 'NumpadEnter' : this . click ( ) ; break ; default : break ; } } protected handleKeyup ( event ) { const { code } = event ; switch ( code ) { case 'Space' : this . removeEventListener ( 'keyup' , this . handleKeyup ) ; this . active = false ; this . click ( ) ; break ; default : break ; } } private handleRemoveActive ( ) { this . active = false ; } private handlePointerdown ( ) { this . active = true ; } private manageAnchor ( ) { if ( this . href && this . href . length > 0 ) { if ( this . getAttribute ( 'role' ) === 'button' ) { this . setAttribute ( 'role' , 'link' ) ; } this . removeEventListener ( 'click' , this . shouldProxyClick ) ; } else { if ( ! this . hasAttribute ( 'role' ) || this . getAttribute ( 'role' ) === 'link' ) { this . setAttribute ( 'role' , 'button' ) ; } this . addEventListener ( 'click' , this . shouldProxyClick ) ; } } protected firstUpdated ( changed ) { super . firstUpdated ( changed ) ; if ( ! this . hasAttribute ( 'tabindex' ) ) { this . tabIndex = 0 ; } this . manageAnchor ( ) ; this . addEventListener ( 'keydown' , this . handleKeydown ) ; this . addEventListener ( 'keypress' , this . handleKeypress ) ; this . addEventListener ( 'pointerdown' , this . handlePointerdown ) ; } protected updated ( changed ) { super . updated ( changed ) ; if ( changed . has ( 'href' ) ) { this . manageAnchor ( ) ; } if ( changed . has ( 'label' ) ) { this . setAttribute ( 'aria-label' , this . label || '' ) ; } if ( changed . has ( 'active' ) ) { if ( this . active ) { this . addEventListener ( 'focusout' , this . handleRemoveActive ) ; this . addEventListener ( 'pointerup' , this . handleRemoveActive ) ; this . addEventListener ( 'pointerleave' , this . handleRemoveActive ) ; } else { this . removeEventListener ( 'focusout' , this . handleRemoveActive ) ; this . removeEventListener ( 'pointerup' , this . handleRemoveActive ) ; this . removeEventListener ( 'pointerleave' , this . handleRemoveActive ) ; } } if ( this . anchorElement ) { this . anchorElement . addEventListener ( 'focus' , this . proxyFocus ) ; this . anchorElement . tabIndex = - 1 ; } } }