license import MDCSnackbarFoundation from '@material/snackbar/foundation' ; import { html , render , TemplateResult } from 'lit' ; import { AsyncDirective } from 'lit/async-directive.js' ; import { ChildPart , directive , DirectiveParameters , PartInfo , PartType } from 'lit/directive.js' ; const { ARIA_LIVE_DELAY_MS } = MDCSnackbarFoundation . numbers ; /**
 * A lit directive implementation of @material/mdc-snackbar/util.ts#announce,
 * which does some tricks to ensure that snackbar labels will be handled
 * correctly by screen readers.
 *
 * The existing MDC announce util function is difficult to use directly here,
 * because Lit can crash when DOM that it is managing changes outside of its
 * purvue. In this case, we would render our labelText as the text content of
 * the label div, but the MDC announce function then clears that text content,
 * and resets it after a timeout (see below for why). We do the same thing here,
 * but in a way that fits into Lit's lifecycle.
 *
 * TODO(aomarks) Investigate whether this can be simplified; but to do that we
 * first need testing infrastructure to verify that it remains compatible with
 * screen readers. For example, can we just create an entirely new label node
 * every time we open or labelText changes? If not, and the async text/::before
 * swap is strictly required, can we at elast make this directive more generic
 * (e.g. so that we don't hard-code the name of the label class).
 */ class AccessibleSnackbarLabel extends AsyncDirective { protected labelEl : HTMLElement | null = null ; protected timerId : number | null = null ; protected previousPart : ChildPart | null = null ; constructor ( partInfo ) { super ( partInfo ) ; if ( partInfo . type !== PartType . CHILD ) { throw new Error ( 'AccessibleSnackbarLabel only supports child parts.' ) ; } } override update ( part , [ labelText , isOpen ] ) { if ( ! isOpen ) { return ; } if ( this . labelEl === null ) { const wrapperEl = document . createElement ( 'div' ) ; const labelTemplate = html `<div class="mdc-snackbar__label" role="status" aria-live="polite"></div>` ; render ( labelTemplate , wrapperEl ) ; const labelEl = wrapperEl . firstElementChild ! as HTMLElement ; labelEl . textContent = labelText ; part . endNode ?. parentNode ! . insertBefore ( labelEl , part . endNode ) ; this . labelEl = labelEl ; return labelEl ; } const labelEl = this . labelEl ; labelEl . setAttribute ( 'aria-live' , 'off' ) ; labelEl . textContent = '' ; const spaceSpan = document . createElement ( 'span' ) ; spaceSpan . style . display = 'inline-block' ; spaceSpan . style . width = '0' ; spaceSpan . style . height = '1px' ; spaceSpan . textContent = '\u00A0' ; labelEl . appendChild ( spaceSpan ) ; labelEl . setAttribute ( 'data-mdc-snackbar-label-text' , labelText ) ; if ( this . timerId !== null ) { clearTimeout ( this . timerId ) ; } this . timerId = window . setTimeout ( ( ) => { this . timerId = null ; labelEl . setAttribute ( 'aria-live' , 'polite' ) ; labelEl . removeAttribute ( 'data-mdc-snackbar-label-text' ) ; labelEl . textContent = labelText ; this . setValue ( this . labelEl ) ; } , ARIA_LIVE_DELAY_MS ) ; return labelEl ; } render ( labelText , isOpen ) { if ( ! isOpen ) { return html `` ; } return html `
      <div class="mdc-snackbar__label" role="status" aria-live="polite">${ labelText }</div>` ; } } export const accessibleSnackbarLabel = directive ( AccessibleSnackbarLabel ) ;