feat(notification): add custom notification component support (#5605)

* feat: add custom notification component support

* update the documentation of UINotificationService to support custom component integration

---------

Co-authored-by: trenser-belbin <belbin.kunjumon@prenuvo.com>
This commit is contained in:
Belbin-GK 2025-12-05 18:36:32 +05:30 committed by GitHub
parent fd86a9e3e8
commit c4e5a4616d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 51 additions and 9 deletions

View File

@ -4,6 +4,7 @@ const serviceImplementation = {
console.debug('show() NOT IMPLEMENTED');
return null;
},
_customComponent: null,
};
type ToastType = 'success' | 'error' | 'info' | 'warning' | 'loading';
@ -17,21 +18,38 @@ class UINotificationService {
},
};
/**
* This provides flexibility in customizing the Notification default component
*
* @returns {React.Component}
*/
getCustomComponent() {
return serviceImplementation._customComponent;
}
/**
*
*
* @param {*} {
* hide: hideImplementation,
* show: showImplementation,
* component: componentImplementation
* }
*/
public setServiceImplementation({ hide: hideImplementation, show: showImplementation }): void {
public setServiceImplementation({
hide: hideImplementation,
show: showImplementation,
customComponent: customComponentImplementation,
}): void {
if (hideImplementation) {
serviceImplementation._hide = hideImplementation;
}
if (showImplementation) {
serviceImplementation._show = showImplementation;
}
if (customComponentImplementation) {
serviceImplementation._customComponent = customComponentImplementation;
}
}
/**

View File

@ -34,12 +34,14 @@ is expected to support, [check out it's interface in `@ohif/core`][interface]
| ---------- | --------------------------------------- |
| `hide()` | Hides the specified notification |
| `show()` | Creates and displays a new notification |
| `customComponent()` | Overrides the default Notification component |
## Implementations
| Implementation | Consumer |
| ---------------------------------------- | ----------------------------------------- |
| [Snackbar Provider][snackbar-provider]\* | [SnackbarContainer][snackbar-container]\* |
| customComponent | user extensions via `setServiceImplementation({customComponent: Snackbar})` |
`*` - Denotes maintained by OHIF

View File

@ -1,4 +1,12 @@
import React, { createContext, useContext, useCallback, useEffect, ReactNode, useRef } from 'react';
import React, {
createContext,
useContext,
useCallback,
useEffect,
ReactNode,
useState,
useRef,
} from 'react';
import PropTypes from 'prop-types';
import { Toaster, toast } from '../components';
@ -27,17 +35,26 @@ const NotificationProvider = ({
title: '',
message: '',
duration: 5000,
position: 'bottom-right', // Aligning to Sonner's positioning system
type: 'info', // info, success, error
position: 'bottom-right',
type: 'info',
visible: true,
};
const [options, setOptions] = useState([]);
// Cache for recent notifications to prevent duplicates
// Structure: { [title_message_type]: { timestamp, id } }
const recentNotificationsRef = useRef<Record<string, NotificationCacheEntry>>({});
const CustomNotification = service?.getCustomComponent();
// Use the configurable deduplication interval from props
const show = useCallback(options => {
const newNotification = {
...DEFAULT_OPTIONS,
...options,
};
const {
title,
message,
@ -48,10 +65,7 @@ const NotificationProvider = ({
allowDuplicates = false,
deduplicationInterval: optionsDeduplicationInterval,
action,
} = {
...DEFAULT_OPTIONS,
...options,
};
} = newNotification;
// Use the provider's deduplicationInterval by default, but allow it to be overridden per notification
const notificationDeduplicationInterval = optionsDeduplicationInterval || deduplicationInterval;
@ -143,10 +157,13 @@ const NotificationProvider = ({
// The entry will be checked against the deduplication interval
}
setOptions(prev => [...prev, { ...newNotification, id: id }]);
return id;
}, []);
const hide = useCallback(id => {
setOptions(state => [...state.filter(item => item.id !== id)]);
toast.dismiss(id);
// Remove from cache if present
@ -160,6 +177,7 @@ const NotificationProvider = ({
}, []);
const hideAll = useCallback(() => {
setOptions([]);
toast.dismiss();
// Clear notification cache
recentNotificationsRef.current = {};
@ -198,7 +216,11 @@ const NotificationProvider = ({
return (
<NotificationContext.Provider value={{ show, hide, hideAll, getNotificationCache }}>
<Toaster position="bottom-right" />
{CustomNotification ? (
<CustomNotification options={options} />
) : (
<Toaster position="bottom-right" />
)}
{children}
</NotificationContext.Provider>
);