license declare global { interface Event { path : Element [ ] ; } } import { fade } from '../../utils/fade.js' ; import { DEBUG_LEVEL , log } from '../../utils/logger.js' ; import { html , styles } from './card.template.js' ; see Card export interface CardData { /**
     * The name for the card.
     */ name ? : string ; /**
     * A short description used for disambiguating the item from other similar
     * items. _Not used for rendering._
     */ disambiguatingDescription ? : string ; /**
     * A description used for the item.
     */ description ? : string ; /**
     * The image for the item. For rendering images that are 16:9 are preferred.
     */ image ? : string ; /**
     * The URL for more information on the item.
     */ url ? : string ; /**
     * An additional entry for more information on the item. For example, if the
     * `url` property points to a review of a website, `mainEntity`
     * would point to the website itself.
     */ mainEntity ? : { url ? : string ; } ; } /**
 * A data card for information output.
 *
 * ```javascript
 * // Text card.
 * const card = new Card();
 * card.src = 'Card Message';
 *
 * // Schema card.
 * card.src = {
 *   name: 'My card',
 *   description: 'More information about the contents',
 *   image: '/path/to/some/image.jpg',
 *   url: '/path/to/more/details/'
 * };
 *
 * // Or iframe some content in. By default the card supports same-origin
 * // content.
 * card.src = new URL('http://example.com');
 * ```
 */ export class Card extends HTMLElement { /**
     * The Cards's default tag name for registering with `customElements.define`.
     */ static defaultTagName = 'data-card' ; /**
     * The duration of the card's fade animation when dismissed.
     */ fadeDuration = 200 ; see https sandboxAttribute : string = 'allow-same-origin' ; private srcInternal : string | URL | CardData = '' ; private widthInternal : number | undefined ; private heightInternal : number | undefined ; private root = this . attachShadow ( { mode : 'open' } ) ; private onClickBound = this . onClick . bind ( this ) ; constructor ( ) { super ( ) ; } /**
     * Gets & sets the src for the card. If the src is a URL the content is
     * `iframe`'d in using a sandbox that disallows
     */ get src ( ) { return this . srcInternal ; } set src ( src ) { this . srcInternal = src ; this . render ( ) ; } /**
     * Gets & sets the width of the card.
     */ get width ( ) { return this . widthInternal ; } set width ( width ) { this . widthInternal = width ; this . setDimensions ( ) ; } /**
     * Gets & sets the height of the card.
     */ get height ( ) { return this . heightInternal ; } set height ( height ) { this . heightInternal = height ; this . setDimensions ( ) ; } ignore connectedCallback ( ) { this . root . innerHTML = `<style>${ styles }</style> ${ html }` ; this . render ( ) ; this . setDimensions ( ) ; this . addEventListener ( 'click' , this . onClickBound ) ; } ignore disconnectedCallback ( ) { this . removeEventListener ( 'click' , this . onClickBound ) ; } /**
     * Closes the card with an optional fade.
     */ async close ( fadeDuration = this . fadeDuration ) { if ( fadeDuration === 0 ) { this . remove ( ) ; return ; } await fade ( this , { duration : fadeDuration } ) ; this . remove ( ) ; } private async onClick ( evt ) { const clicked = evt . path ? evt . path [ 0 ] : evt . composedPath ( ) [ 0 ] as HTMLElement ; if ( clicked . id !== 'close' ) { return ; } await this . close ( ) ; } private render ( ) { const container = this . root . querySelector ( '#container' ) as HTMLElement ; if ( ! container ) { return ; } container . classList . remove ( 'padded' ) ; if ( this . srcIsString ( this . src ) ) { container . textContent = this . src ; container . classList . add ( 'padded' ) ; } else if ( this . srcIsCardData ( this . src ) ) { this . renderCardData ( this . src ) ; } else if ( this . srcIsUrl ( this . src ) ) { const iframe = document . createElement ( 'iframe' ) ; iframe . src = this . src . toString ( ) ; iframe . setAttribute ( 'sandbox' , this . sandboxAttribute ) ; iframe . style . border = 'none' ; iframe . id = 'external-content' ; iframe . width = ( this . width || 0 ) . toString ( ) ; iframe . height = ( this . height || 0 ) . toString ( ) ; container . appendChild ( iframe ) ; } else { log ( `Unexpected card content: ${ this . src }` , DEBUG_LEVEL . WARNING ) ; container . textContent = 'Unexpected content' ; container . classList . add ( 'padded' ) ; } } private renderCardData ( data ) { const container = this . root . querySelector ( '#container' ) as HTMLElement ; if ( ! container ) { return ; } container . innerHTML = '' ; if ( data . name ) { const title = document . createElement ( 'h1' ) ; title . setAttribute ( 'id' , 'title' ) ; title . textContent = data . name ; container . appendChild ( title ) ; } if ( data . description ) { const description = document . createElement ( 'div' ) ; description . setAttribute ( 'id' , 'description' ) ; description . textContent = data . description ; container . appendChild ( description ) ; } if ( data . image ) { const img = document . createElement ( 'div' ) ; img . setAttribute ( 'id' , 'image' ) ; img . style . backgroundImage = `url(${ data . image })` ; if ( data . description || data . name ) { img . classList . add ( 'top-margin' ) ; } container . appendChild ( img ) ; } } private srcIsString ( msg ) { return typeof msg === 'string' ; } private srcIsCardData ( msg ) { return typeof msg === 'object' && typeof ( msg as URL ) . href === 'undefined' ; } private srcIsUrl ( msg ) { return typeof msg === 'object' && typeof ( msg as URL ) . href !== 'undefined' ; } private setDimensions ( ) { const container = this . root . querySelector ( '#container' ) as HTMLElement ; if ( ! container ) { return ; } if ( this . widthInternal ) { container . style . width = `${ this . widthInternal }px` ; } if ( this . heightInternal ) { container . style . height = `${ this . heightInternal }px` ; } } }