import { CSSResultArray , html , PropertyValues , SizedMixin , SpectrumElement , TemplateResult , } from '@spectrum-web-components/base' ; import { property , query , } from '@spectrum-web-components/base/src/decorators.js' ; import type { Focusable } from '@spectrum-web-components/shared' ; import '@spectrum-web-components/icons-ui/icons/sp-icon-asterisk100.js' ; import asteriskIconStyles from '@spectrum-web-components/icon/src/spectrum-icon-asterisk.css.js' ; import styles from './field-label.css.js' ; type AcceptsFocusVisisble = HTMLElement & { forceFocusVisible ? ( ) : void ; } ; element slot export class FieldLabel extends SizedMixin ( SpectrumElement ) { public static get styles ( ) : CSSResultArray { return [ styles , asteriskIconStyles ] ; } private static instanceCount = 0 ; @ property ( { type : Boolean , reflect : true } ) public disabled = false ; @ property ( { type : String } ) public id = '' ; @ property ( { type : String } ) public for = '' ; @ property ( { type : Boolean , reflect : true } ) public required = false ; @ query ( 'slot' ) public slotEl ! : HTMLSlotElement ; @ property ( { type : String , reflect : true , attribute : 'side-aligned' } ) public sideAligned ? : 'start' | 'end' ; private target ? : HTMLElement ; private handleClick ( event ) { if ( ! this . target || this . disabled || event . defaultPrevented ) return ; this . target . focus ( ) ; const parent = this . getRootNode ( ) as ShadowRoot ; const target = this . target as AcceptsFocusVisisble ; const targetParent = target . getRootNode ( ) as ShadowRoot ; const targetHost = targetParent . host as AcceptsFocusVisisble ; if ( targetParent === parent && target . forceFocusVisible ) { target . forceFocusVisible ( ) ; } else if ( targetHost && targetHost . forceFocusVisible ) { targetHost . forceFocusVisible ( ) ; } } private async manageFor ( ) { if ( ! this . for ) { return ; } const parent = this . getRootNode ( ) as HTMLElement ; const target = parent . querySelector ( `#${ this . for }` ) as Focusable ; if ( ! target ) { return ; } if ( target . localName . search ( '-' ) > 0 ) { await customElements . whenDefined ( target . localName ) ; } if ( typeof target . updateComplete !== 'undefined' ) { await target . updateComplete ; } this . target = target . focusElement || target ; if ( this . target ) { const targetParent = this . target . getRootNode ( ) as HTMLElement ; if ( targetParent === parent ) { this . target . setAttribute ( 'aria-labelledby' , this . id ) ; } else { this . target . setAttribute ( 'aria-label' , this . labelText ) ; } } return Promise . resolve ( ) ; } private get labelText ( ) : string { const assignedNodes = this . slotEl . assignedNodes ( { flatten : true } ) ; if ( ! assignedNodes . length ) { return '' ; } const labelText = assignedNodes . map ( ( node ) => ( node . textContent || '' ) . trim ( ) ) ; return labelText . join ( ' ' ) ; } protected render ( ) { return html `
            <label>
                <slot @slotchange=${ this . manageFor }></slot>
                ${ this . required ? html `
                          <sp-icon-asterisk100
                              class="required-icon spectrum-UIIcon-Asterisk100"
                          ></sp-icon-asterisk100>
                      ` : html `` }
            </label>
        ` ; } protected firstUpdated ( changes ) { super . firstUpdated ( changes ) ; if ( ! this . hasAttribute ( 'id' ) ) { this . setAttribute ( 'id' , `${ this . tagName . toLowerCase ( ) }-${ FieldLabel . instanceCount ++ }` ) ; } this . addEventListener ( 'click' , this . handleClick ) ; } protected updated ( changes ) { super . updated ( changes ) ; if ( changes . has ( 'for' ) || changes . has ( 'id' ) ) { this . manageFor ( ) ; } } }