import { html , PropertyValues , TemplateResult , } from '@spectrum-web-components/base' ; import { query } from '@spectrum-web-components/base/src/decorators.js' ; import { Iconset } from './iconset.js' ; export abstract class IconsetSVG extends Iconset { private iconMap : Map < string , SVGSymbolElement > = new Map ( ) ; @ query ( 'slot' ) private slotContainer ? : HTMLSlotElement ; /**
     * First updated handler just ensures we've processed any slotted symbols
     */ public updated ( changedProperties ) { if ( ! this . slotContainer ) { return ; } const currentSVGNodes = this . getSVGNodes ( this . slotContainer ) ; this . updateSVG ( currentSVGNodes ) ; super . updated ( changedProperties ) ; } param el param icon public async applyIconToElement ( el , icon , _size , label ) { await this . updateComplete ; const iconSymbol = this . iconMap . get ( icon ) ; if ( ! iconSymbol ) { throw new Error ( `Unable to find icon ${ icon }` ) ; } const clonedNode = this . prepareSvgClone ( iconSymbol ) ; clonedNode . setAttribute ( 'role' , 'img' ) ; if ( label ) { clonedNode . setAttribute ( 'aria-label' , label ) ; } else { clonedNode . setAttribute ( 'aria-hidden' , 'true' ) ; } if ( el . shadowRoot ) { el . shadowRoot . appendChild ( clonedNode ) ; } else { el . appendChild ( clonedNode ) ; } } /**
     * Returns a list of all icons in this iconset.
     */ public getIconList ( ) { return [ ... this . iconMap . keys ( ) ] ; } protected prepareSvgClone ( sourceSvg ) { const content = sourceSvg . cloneNode ( true ) as SVGSymbolElement ; const svg = document . createElementNS ( 'http://www.w3.org/2000/svg' , 'svg' ) ; const viewBox = content . getAttribute ( 'viewBox' ) || '' ; const cssText = 'pointer-events: none; display: block; width: 100%; height: 100%;' ; svg . style . cssText = cssText ; svg . setAttribute ( 'viewBox' , viewBox ) ; svg . setAttribute ( 'preserveAspectRatio' , 'xMidYMid meet' ) ; svg . setAttribute ( 'focusable' , 'false' ) ; while ( content . childNodes . length > 0 ) { svg . appendChild ( content . childNodes [ 0 ] ) ; } return svg ; } protected getSVGIconName ( icon ) { return icon ; } protected getSanitizedIconName ( icon ) { return icon ; } protected renderDefaultContent ( ) { return html `` ; } protected render ( ) { return html `
            <slot @slotchange=${ this . onSlotChange }>
                ${ this . renderDefaultContent ( ) }
            </slot>
        ` ; } protected updateSVG ( nodes ) { const symbols = nodes . reduce ( ( prev , svgNode ) => { const containedSymbols = svgNode . querySelectorAll ( 'symbol' ) ; prev . push ( ... containedSymbols ) ; return prev ; } , [ ] as SVGSymbolElement [ ] ) ; symbols . forEach ( ( symbol ) => { this . iconMap . set ( this . getSanitizedIconName ( symbol . id ) , symbol ) ; } ) ; } protected getSVGNodes ( slotTarget ) { const nodes = slotTarget . assignedNodes ( { flatten : true } ) ; const svgNodes = nodes . filter ( ( node ) => { return node . nodeName === 'svg' ; } ) as SVGElement [ ] ; return svgNodes ; } private onSlotChange ( event ) { const slotTarget = event . target as HTMLSlotElement ; const svgNodes = this . getSVGNodes ( slotTarget ) ; this . updateSVG ( svgNodes ) ; } }