import m from 'mithril' ; import classnames from 'classnames' ; import { Classes , IAttrs , safeCall } from '../../_shared' ; import { AbstractComponent } from '../abstract-component' ; import { IToastAttrs , Toast } from './Toast' ; import { Overlay } from '../overlay' ; export type IToastOptions = IToastAttrs & { key ? : string ; } ; export const ToasterPosition = { TOP : 'top' as 'top' , TOP_START : 'top-start' as 'top-start' , TOP_END : 'top-end' as 'top-end' , BOTTOM : 'bottom' as 'bottom' , BOTTOM_START : 'bottom-start' as 'bottom-start' , BOTTOM_END : 'bottom-end' as 'bottom-end' } ; export type ToasterPosition = typeof ToasterPosition [ keyof typeof ToasterPosition ] ; export interface IToaster { /** Show toast */ show ( attrs ) : string ; /** Update toast attrs by key */ update ( key , attrs ) : void ; /** Dismiss toast by key */ dismiss ( key ) : void ; /** Clear all toasts */ clear ( ) : void ; /** Get array of toasts */ getToasts ( ) : IToastOptions [ ] ; } export interface IToasterAttrs extends IAttrs { default clearOnEscapeKey ? : boolean ; /** Renders component relative to parent container */ inline ? : boolean ; default position ? : ToasterPosition ; /** Array of `Toast` items when used in declarative mode */ toasts ? : m . Vnode < IToastAttrs , any > [ ] ; } export class Toaster extends AbstractComponent < IToasterAttrs > { private toasts : IToastOptions [ ] = [ ] ; private toastId : number = 0 ; public getDefaultAttrs ( ) { return { clearOnEscapeKey : true , position : 'top' } as IToasterAttrs ; } public view ( ) { const { class : className , position , inline , toasts , clearOnEscapeKey , style } = this . attrs ; const classes = classnames ( Classes . TOASTER , `${ Classes . TOASTER }-${ position }` , inline && Classes . TOASTER_INLINE , className ) ; const renderedToasts = this . isControlled ( ) ? toasts || [ ] : this . toasts . map ( toastOptions => this . renderToast ( toastOptions ) ) ; return m ( Overlay , { closeOnEscapeKey : clearOnEscapeKey , closeOnOutsideClick : false , class : classes , content : renderedToasts , hasBackdrop : false , inline , isOpen : renderedToasts . length > 0 , transitionDuration : 0 , addToStack : false , onClose : ( ) => this . clear ( ) , style } ) ; } public onremove ( ) { this . clear ( ) ; } public show ( attrs ) { const toastOptions = { ... attrs , key : `cui-toast-${ this . toastId ++ }` } ; this . toasts . push ( toastOptions ) ; m . redraw ( ) ; return toastOptions . key ; } public update ( key , attrs ) { const index = this . toasts . findIndex ( x => x . key === key ) ; this . toasts [ index ] = { ... this . toasts [ index ] , ... attrs } ; m . redraw ( ) ; } public dismiss = ( key , timedOut = false ) => { const index = this . toasts . findIndex ( x => x . key === key ) ; const toast = this . toasts [ index ] ; if ( toast ) { safeCall ( toast . onDismiss , timedOut ) ; this . toasts . splice ( index , 1 ) ; } m . redraw ( ) ; } ; public clear ( ) { this . toasts . map ( ( x ) => safeCall ( x . onDismiss , false ) ) ; this . toasts . length = 0 ; m . redraw ( ) ; } public getToasts ( ) { return this . toasts ; } private renderToast ( attrs ) { return m ( Toast , { ... attrs , onDismiss : this . dismiss } ) ; } private isControlled ( ) { return this . attrs . toasts != null ; } }