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:
parent
fd86a9e3e8
commit
c4e5a4616d
@ -4,6 +4,7 @@ const serviceImplementation = {
|
|||||||
console.debug('show() NOT IMPLEMENTED');
|
console.debug('show() NOT IMPLEMENTED');
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
_customComponent: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
type ToastType = 'success' | 'error' | 'info' | 'warning' | 'loading';
|
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 {*} {
|
* @param {*} {
|
||||||
* hide: hideImplementation,
|
* hide: hideImplementation,
|
||||||
* show: showImplementation,
|
* show: showImplementation,
|
||||||
|
* component: componentImplementation
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
public setServiceImplementation({ hide: hideImplementation, show: showImplementation }): void {
|
public setServiceImplementation({
|
||||||
|
hide: hideImplementation,
|
||||||
|
show: showImplementation,
|
||||||
|
customComponent: customComponentImplementation,
|
||||||
|
}): void {
|
||||||
if (hideImplementation) {
|
if (hideImplementation) {
|
||||||
serviceImplementation._hide = hideImplementation;
|
serviceImplementation._hide = hideImplementation;
|
||||||
}
|
}
|
||||||
if (showImplementation) {
|
if (showImplementation) {
|
||||||
serviceImplementation._show = showImplementation;
|
serviceImplementation._show = showImplementation;
|
||||||
}
|
}
|
||||||
|
if (customComponentImplementation) {
|
||||||
|
serviceImplementation._customComponent = customComponentImplementation;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -34,12 +34,14 @@ is expected to support, [check out it's interface in `@ohif/core`][interface]
|
|||||||
| ---------- | --------------------------------------- |
|
| ---------- | --------------------------------------- |
|
||||||
| `hide()` | Hides the specified notification |
|
| `hide()` | Hides the specified notification |
|
||||||
| `show()` | Creates and displays a new notification |
|
| `show()` | Creates and displays a new notification |
|
||||||
|
| `customComponent()` | Overrides the default Notification component |
|
||||||
|
|
||||||
## Implementations
|
## Implementations
|
||||||
|
|
||||||
| Implementation | Consumer |
|
| Implementation | Consumer |
|
||||||
| ---------------------------------------- | ----------------------------------------- |
|
| ---------------------------------------- | ----------------------------------------- |
|
||||||
| [Snackbar Provider][snackbar-provider]\* | [SnackbarContainer][snackbar-container]\* |
|
| [Snackbar Provider][snackbar-provider]\* | [SnackbarContainer][snackbar-container]\* |
|
||||||
|
| customComponent | user extensions via `setServiceImplementation({customComponent: Snackbar})` |
|
||||||
|
|
||||||
`*` - Denotes maintained by OHIF
|
`*` - Denotes maintained by OHIF
|
||||||
|
|
||||||
|
|||||||
@ -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 PropTypes from 'prop-types';
|
||||||
import { Toaster, toast } from '../components';
|
import { Toaster, toast } from '../components';
|
||||||
|
|
||||||
@ -27,17 +35,26 @@ const NotificationProvider = ({
|
|||||||
title: '',
|
title: '',
|
||||||
message: '',
|
message: '',
|
||||||
duration: 5000,
|
duration: 5000,
|
||||||
position: 'bottom-right', // Aligning to Sonner's positioning system
|
position: 'bottom-right',
|
||||||
type: 'info', // info, success, error
|
type: 'info',
|
||||||
|
visible: true,
|
||||||
};
|
};
|
||||||
|
const [options, setOptions] = useState([]);
|
||||||
|
|
||||||
// Cache for recent notifications to prevent duplicates
|
// Cache for recent notifications to prevent duplicates
|
||||||
// Structure: { [title_message_type]: { timestamp, id } }
|
// Structure: { [title_message_type]: { timestamp, id } }
|
||||||
const recentNotificationsRef = useRef<Record<string, NotificationCacheEntry>>({});
|
const recentNotificationsRef = useRef<Record<string, NotificationCacheEntry>>({});
|
||||||
|
|
||||||
|
const CustomNotification = service?.getCustomComponent();
|
||||||
|
|
||||||
// Use the configurable deduplication interval from props
|
// Use the configurable deduplication interval from props
|
||||||
|
|
||||||
const show = useCallback(options => {
|
const show = useCallback(options => {
|
||||||
|
const newNotification = {
|
||||||
|
...DEFAULT_OPTIONS,
|
||||||
|
...options,
|
||||||
|
};
|
||||||
|
|
||||||
const {
|
const {
|
||||||
title,
|
title,
|
||||||
message,
|
message,
|
||||||
@ -48,10 +65,7 @@ const NotificationProvider = ({
|
|||||||
allowDuplicates = false,
|
allowDuplicates = false,
|
||||||
deduplicationInterval: optionsDeduplicationInterval,
|
deduplicationInterval: optionsDeduplicationInterval,
|
||||||
action,
|
action,
|
||||||
} = {
|
} = newNotification;
|
||||||
...DEFAULT_OPTIONS,
|
|
||||||
...options,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Use the provider's deduplicationInterval by default, but allow it to be overridden per notification
|
// Use the provider's deduplicationInterval by default, but allow it to be overridden per notification
|
||||||
const notificationDeduplicationInterval = optionsDeduplicationInterval || deduplicationInterval;
|
const notificationDeduplicationInterval = optionsDeduplicationInterval || deduplicationInterval;
|
||||||
@ -143,10 +157,13 @@ const NotificationProvider = ({
|
|||||||
// The entry will be checked against the deduplication interval
|
// The entry will be checked against the deduplication interval
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setOptions(prev => [...prev, { ...newNotification, id: id }]);
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const hide = useCallback(id => {
|
const hide = useCallback(id => {
|
||||||
|
setOptions(state => [...state.filter(item => item.id !== id)]);
|
||||||
toast.dismiss(id);
|
toast.dismiss(id);
|
||||||
|
|
||||||
// Remove from cache if present
|
// Remove from cache if present
|
||||||
@ -160,6 +177,7 @@ const NotificationProvider = ({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const hideAll = useCallback(() => {
|
const hideAll = useCallback(() => {
|
||||||
|
setOptions([]);
|
||||||
toast.dismiss();
|
toast.dismiss();
|
||||||
// Clear notification cache
|
// Clear notification cache
|
||||||
recentNotificationsRef.current = {};
|
recentNotificationsRef.current = {};
|
||||||
@ -198,7 +216,11 @@ const NotificationProvider = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<NotificationContext.Provider value={{ show, hide, hideAll, getNotificationCache }}>
|
<NotificationContext.Provider value={{ show, hide, hideAll, getNotificationCache }}>
|
||||||
<Toaster position="bottom-right" />
|
{CustomNotification ? (
|
||||||
|
<CustomNotification options={options} />
|
||||||
|
) : (
|
||||||
|
<Toaster position="bottom-right" />
|
||||||
|
)}
|
||||||
{children}
|
{children}
|
||||||
</NotificationContext.Provider>
|
</NotificationContext.Provider>
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user