Files
overleaf-cep/services/web/frontend/js/features/ide-react/components/global-toasts.tsx
T
Rebeka Dekany d751b88e6b Bootstrap files and folders cleanup (#27692)
* Remove icons folder

* Create folders for badge, button, and dropdown components

* Remove Bootstrap 5 from test

* Rename `getBootstrap5Breakpoint` to `getBootstrapBreakpoint`

* Cleanup and update BS 5 comments

* Move components to the shared folder

* Rename `tooltips-bs5` to `tooltip`

* Remove `-bs5` suffix

* Fix path

* Delete BS3 version file

* Rename `_form_marketing-bootstrap-5` to `_form_marketing`

* Delete BS3 version file

* Rename `_contact_general_modal-marketing-bootstrap-5` to `_contact_general_modal-marketing`

* Delete BS3 version file

* Rename `_contact_modal-marketing-bootstrap-5` to `_contact_modal-marketing`

* Delete BS3 version file

* Rename `thin-footer-bootstrap-5` to `thin-footer`

* Delete BS3 version file

* Rename `language-picker-bootstrap-5` to `language-picker`

* Rename `fat-footer-react-bootstrap-5` to `fat-footer-react`

* Delete BS3 version file

* Rename `navbar-marketing-bootstrap-5` to `navbar-marketing`

* Rename `navbar-marketing-react-bootstrap-5` to `navbar-marketing-react`

* Delete BS3 version file

* Rename `layout-website-redesign-cms-bootstrap-5` to `layout-website-redesign-cms`

* Source format

* Fix path

GitOrigin-RevId: cf0f5db7c84cf545c69213dcc271d9ff17fe5db7
2025-08-11 08:06:16 +00:00

100 lines
2.9 KiB
TypeScript

import { OLToast, OLToastProps } from '@/shared/components/ol/ol-toast'
import useEventListener from '@/shared/hooks/use-event-listener'
import { Fragment, ReactElement, useCallback, useState } from 'react'
import { debugConsole } from '@/utils/debugging'
import importOverleafModules from '../../../../macros/import-overleaf-module.macro'
import { OLToastContainer } from '@/shared/components/ol/ol-toast-container'
const moduleGeneratorsImport = importOverleafModules('toastGenerators') as {
import: { default: GlobalToastGeneratorEntry[] }
}[]
const moduleGenerators = moduleGeneratorsImport.map(
({ import: { default: listEntry } }) => listEntry
)
export type GlobalToastGeneratorEntry = {
key: string
generator: GlobalToastGenerator
}
type GlobalToastGenerator = (
args: Record<string, any>
) => Omit<OLToastProps, 'onDismiss'>
const GENERATOR_LIST: GlobalToastGeneratorEntry[] = moduleGenerators.flat()
const GENERATOR_MAP: Map<string, GlobalToastGenerator> = new Map(
GENERATOR_LIST.map(({ key, generator }) => [key, generator])
)
let toastCounter = 1
export const GlobalToasts = () => {
const [toasts, setToasts] = useState<
{ component: ReactElement; id: string }[]
>([])
const removeToast = useCallback((id: string) => {
setToasts(current => current.filter(toast => toast.id !== id))
}, [])
const createToast = useCallback(
(id: string, key: string, data: any): ReactElement | null => {
const generator = GENERATOR_MAP.get(key)
if (!generator) {
debugConsole.error('No toast generator found for key:', key)
return null
}
const props = generator(data)
if (!props.autoHide && !props.isDismissible) {
// We don't want any toasts that are not dismissible and don't auto-hide
props.isDismissible = true
}
if (props.autoHide && !props.isDismissible && props.delay !== undefined) {
// If the toast is auto-hiding but not dismissible, we need to make sure the delay is not too long
props.delay = Math.min(props.delay, 60_000)
}
return <OLToast {...props} onDismiss={() => removeToast(id)} />
},
[removeToast]
)
const addToast = useCallback(
(key: string, data?: any) => {
const id = `toast-${toastCounter++}`
const component = createToast(id, key, data)
if (!component) {
return
}
setToasts(current => [...current, { id, component }])
},
[createToast]
)
const showToastListener = useCallback(
(event: CustomEvent) => {
if (!event.detail?.key) {
debugConsole.error('No key provided for toast')
return
}
const { key, ...rest } = event.detail
addToast(key, rest)
},
[addToast]
)
useEventListener('ide:show-toast', showToastListener)
return (
<OLToastContainer className="global-toasts">
{toasts.map(({ component, id }) => (
<Fragment key={id}>{component}</Fragment>
))}
</OLToastContainer>
)
}