import { CSSResultArray , DefaultElementSize , html , PropertyValues , SizedMixin , TemplateResult , } from '@spectrum-web-components/base' ; import { property } from '@spectrum-web-components/base/src/decorators.js' ; import { ButtonBase } from '@spectrum-web-components/button' ; import buttonStyles from './action-button.css.js' ; import cornerTriangleStyles from '@spectrum-web-components/icon/src/spectrum-icon-corner-triangle.css.js' ; import '@spectrum-web-components/icons-ui/icons/sp-icon-corner-triangle300.js' ; const holdAffordanceClass = { s : 'spectrum-UIIcon-CornerTriangle75' , m : 'spectrum-UIIcon-CornerTriangle100' , l : 'spectrum-UIIcon-CornerTriangle200' , xl : 'spectrum-UIIcon-CornerTriangle300' , } ; const LONGPRESS_DURATION = 300 ; let LONGPRESS_TIMEOUT ; export type LongpressEvent = { source : 'pointer' | 'keyboard' ; } ; element slot slot fires fires export class ActionButton extends SizedMixin ( ButtonBase ) { public static get styles ( ) : CSSResultArray { return [ buttonStyles , cornerTriangleStyles ] ; } @ property ( { type : Boolean , reflect : true } ) public emphasized = false ; @ property ( { type : Boolean , reflect : true , attribute : 'hold-affordance' } ) public holdAffordance = false ; @ property ( { type : Boolean , reflect : true } ) public quiet = false ; @ property ( { reflect : true } ) public role = 'button' ; /**
     * Whether an Action Button with `role='button'`
     * should also be `aria-pressed='true'`
     */ @ property ( { type : Boolean , reflect : true } ) public selected = false ; /**
     * Whether to automatically manage the `selected`
     * attribute on interaction and whether `aria-pressed="false"`
     * should be used when `selected === false`
     */ @ property ( { type : Boolean , reflect : true } ) public toggles = false ; @ property ( { type : String } ) public get value ( ) : string { return this . _value || this . itemText ; } public set value ( value ) { if ( value === this . _value ) { return ; } this . _value = value || '' ; if ( this . _value ) { this . setAttribute ( 'value' , this . _value ) ; } else { this . removeAttribute ( 'value' ) ; } } private _value = '' ; private public get itemText ( ) : string { return ( this . textContent || '' ) . trim ( ) ; } constructor ( ) { super ( ) ; this . addEventListener ( 'click' , this . onClick ) ; this . addEventListener ( 'pointerdown' , this . onPointerdown ) ; } private onClick = ( ) : void => { if ( ! this . toggles ) { return ; } this . selected = ! this . selected ; const applyDefault = this . dispatchEvent ( new Event ( 'change' , { cancelable : true , } ) ) ; if ( ! applyDefault ) { this . selected = ! this . selected ; } } ; private onPointerdown ( ) { this . addEventListener ( 'pointerup' , this . onPointerup ) ; this . addEventListener ( 'pointercancel' , this . onPointerup ) ; LONGPRESS_TIMEOUT = setTimeout ( ( ) => { this . dispatchEvent ( new CustomEvent < LongpressEvent > ( 'longpress' , { bubbles : true , composed : true , detail : { source : 'pointer' , } , } ) ) ; } , LONGPRESS_DURATION ) ; } private onPointerup ( ) { clearTimeout ( LONGPRESS_TIMEOUT ) ; this . removeEventListener ( 'pointerup' , this . onPointerup ) ; this . removeEventListener ( 'pointercancel' , this . onPointerup ) ; } private protected handleKeydown ( event ) { if ( ! this . holdAffordance ) { return super . handleKeydown ( event ) ; } const { code , altKey } = event ; if ( code === 'Space' || ( altKey && code === 'ArrowDown' ) ) { event . preventDefault ( ) ; if ( code === 'ArrowDown' ) { event . stopPropagation ( ) ; event . stopImmediatePropagation ( ) ; } this . addEventListener ( 'keyup' , this . handleKeyup ) ; this . active = true ; } } protected handleKeyup ( event ) { if ( ! this . holdAffordance ) { return super . handleKeyup ( event ) ; } const { code , altKey } = event ; if ( code === 'Space' || ( altKey && code === 'ArrowDown' ) ) { event . stopPropagation ( ) ; this . dispatchEvent ( new CustomEvent < LongpressEvent > ( 'longpress' , { bubbles : true , composed : true , detail : { source : 'keyboard' , } , } ) ) ; this . active = false ; } } protected get buttonContent ( ) : TemplateResult [ ] { const buttonContent = super . buttonContent ; if ( this . holdAffordance ) { buttonContent . unshift ( html `
                <sp-icon-corner-triangle300
                    id="hold-affordance"
                    class="${ holdAffordanceClass [ this . size as DefaultElementSize ] }"
                ></sp-icon-corner-triangle300>
            ` ) ; } return buttonContent ; } protected updated ( changes ) { super . updated ( changes ) ; const isButton = this . role === 'button' ; const canBePressed = isButton && ( this . selected || this . toggles ) ; if ( changes . has ( 'selected' ) || changes . has ( 'role' ) ) { if ( canBePressed ) { this . setAttribute ( 'aria-pressed' , this . selected ? 'true' : 'false' ) ; } else { this . removeAttribute ( 'aria-pressed' ) ; } } } } declare global { interface GlobalEventHandlersEventMap { longpress : CustomEvent < LongpressEvent > ; } }