2023-06-27 05:45:24 +03:00
import { htmlEscape } from 'escape-goat' ;
2024-07-07 18:32:30 +03:00
import { svg } from '../svg.ts' ;
import { animateOnce , showElem } from '../utils/dom.ts' ;
2023-08-23 10:25:13 +03:00
import Toastify from 'toastify-js' ; // don't use "async import", because when network error occurs, the "async import" also fails and nothing is shown
2024-07-26 02:31:24 +03:00
import type { Intent } from '../types.ts' ;
import type { SvgName } from '../svg.ts' ;
import type { Options } from 'toastify-js' ;
2023-06-27 05:45:24 +03:00
2024-07-26 02:31:24 +03:00
type ToastLevels = {
[ intent in Intent ] : {
icon : SvgName ,
background : string ,
duration : number ,
}
}
const levels : ToastLevels = {
2023-06-27 05:45:24 +03:00
info : {
icon : 'octicon-check' ,
background : 'var(--color-green)' ,
duration : 2500 ,
} ,
warning : {
icon : 'gitea-exclamation' ,
background : 'var(--color-orange)' ,
duration : - 1 , // requires dismissal to hide
} ,
error : {
icon : 'gitea-exclamation' ,
background : 'var(--color-red)' ,
duration : - 1 , // requires dismissal to hide
} ,
} ;
2024-07-26 02:31:24 +03:00
type ToastOpts = {
useHtmlBody? : boolean ,
preventDuplicates? : boolean ,
} & Options ;
2023-06-27 05:45:24 +03:00
// See https://github.com/apvarun/toastify-js#api for options
2024-07-26 02:31:24 +03:00
function showToast ( message : string , level : Intent , { gravity , position , duration , useHtmlBody , preventDuplicates = true , . . . other } : ToastOpts = { } ) {
2024-06-27 16:58:38 +03:00
const body = useHtmlBody ? String ( message ) : htmlEscape ( message ) ;
const key = ` ${ level } - ${ body } ` ;
// prevent showing duplicate toasts with same level and message, and give a visual feedback for end users
if ( preventDuplicates ) {
const toastEl = document . querySelector ( ` .toastify[data-toast-unique-key=" ${ CSS . escape ( key ) } "] ` ) ;
if ( toastEl ) {
const toastDupNumEl = toastEl . querySelector ( '.toast-duplicate-number' ) ;
showElem ( toastDupNumEl ) ;
toastDupNumEl . textContent = String ( Number ( toastDupNumEl . textContent ) + 1 ) ;
animateOnce ( toastDupNumEl , 'pulse-1p5-200' ) ;
return ;
}
}
2023-06-27 05:45:24 +03:00
const { icon , background , duration : levelDuration } = levels [ level ? ? 'info' ] ;
const toast = Toastify ( {
text : `
< div class = 'toast-icon' > $ { svg ( icon ) } < / div >
2024-06-27 16:58:38 +03:00
< div class = 'toast-body' > < span class = "toast-duplicate-number tw-hidden" > 1 < / span > $ { body } < / div >
< button class = 'btn toast-close' > $ { svg ( 'octicon-x' ) } < / button >
2023-06-27 05:45:24 +03:00
` ,
escapeMarkup : false ,
gravity : gravity ? ? 'top' ,
position : position ? ? 'center' ,
duration : duration ? ? levelDuration ,
style : { background } ,
. . . other ,
} ) ;
toast . showToast ( ) ;
2023-08-23 10:25:13 +03:00
toast . toastElement . querySelector ( '.toast-close' ) . addEventListener ( 'click' , ( ) = > toast . hideToast ( ) ) ;
2024-06-27 16:58:38 +03:00
toast . toastElement . setAttribute ( 'data-toast-unique-key' , key ) ;
2024-03-31 18:39:50 +03:00
return toast ;
2023-06-27 05:45:24 +03:00
}
2024-07-26 02:31:24 +03:00
export function showInfoToast ( message : string , opts? : ToastOpts ) {
2023-08-23 10:25:13 +03:00
return showToast ( message , 'info' , opts ) ;
2023-06-27 05:45:24 +03:00
}
2024-07-26 02:31:24 +03:00
export function showWarningToast ( message : string , opts? : ToastOpts ) {
2023-08-23 10:25:13 +03:00
return showToast ( message , 'warning' , opts ) ;
2023-06-27 05:45:24 +03:00
}
2024-07-26 02:31:24 +03:00
export function showErrorToast ( message : string , opts? : ToastOpts ) {
2023-08-23 10:25:13 +03:00
return showToast ( message , 'error' , opts ) ;
2023-06-27 05:45:24 +03:00
}