# UI Services ## Overview A typical web application will have components and state for common UI like modals, notifications, dialogs, etc. A UI service makes it possible to leverage these components from an extension. We maintain the following UI Services: - [UI Notification Service](ui-notification-service.md) - [UI Modal Service](ui-modal-service.md) - [UI Dialog Service](ui-dialog-service.md) - [UI Viewport Dialog Service](ui-viewport-dialog-service.md) - [Cine Service](cine.md) - [Viewport Grid Service](ViewportGridService.md)
UI Services Diagram
Diagram showing relationship between React Context and UI Service
## Providers for UI services There are several context providers that wraps the application routes. This makes the context values exposed in the app, and service's `setImplementation` can get run to override the implementation of the service. ```js // platform/viewer/src/App.jsx function App({ config, defaultExtensions }) { /**...**/ /**...**/ return ( /**...**/ {appRoutes} /**...**/ ) } ``` ## Example For instance `UIModalService` has the following Public API: ```js // platform/core/src/services/UIModalService/index.js const publicAPI = { name, hide: _hide, show: _show, setServiceImplementation, } function setServiceImplementation({ hide: hideImplementation, show: showImplementation, }) { /** ... **/ serviceImplementation._hide = hideImplementation serviceImplementation._show = showImplementation /** ... **/ } export default { name: 'UIModalService', create: ({ configuration = {} }) => { return publicAPI }, } ``` `UIModalService` implementation can be set (override) in its context provider. For instance in `ModalProvider` we have: ```js // platform/ui/src/contextProviders/ModalProvider.jsx import { Modal } from '@ohif/ui' const ModalContext = createContext(null) const { Provider } = ModalContext export const useModal = () => useContext(ModalContext) const ModalProvider = ({ children, modal: Modal, service }) => { const DEFAULT_OPTIONS = { content: null, contentProps: null, shouldCloseOnEsc: true, isOpen: true, closeButton: true, title: null, customClassName: '', } const show = useCallback( (props) => setOptions({ ...options, ...props }), [options] ) const hide = useCallback(() => setOptions(DEFAULT_OPTIONS), [DEFAULT_OPTIONS]) useEffect(() => { if (service) { service.setServiceImplementation({ hide, show }) } }, [hide, service, show]) const { content: ModalContent, contentProps, isOpen, title, customClassName, shouldCloseOnEsc, closeButton, } = options return ( {ModalContent && ( )} {children} ) } export default ModalProvider export const ModalConsumer = ModalContext.Consumer ``` Therefore, anywhere in the app that we have access to react context we can use it by calling the `useModal` from `@ohif/ui`. As a matter of fact, we are utilizing the modal for the preference window which shows the hotkeys after clicking on the gear button on the right side of the header. A `simplified` code for our worklist is: ```js // platform/viewer/src/routes/WorkList/WorkList.jsx import { useModal, Header } from '@ohif/ui' function WorkList({ history, data: studies, dataTotal: studiesTotal, isLoadingData, dataSource, hotkeysManager, }) { const { show, hide } = useModal() /** ... **/ const menuOptions = [ { title: t('Header:About'), icon: 'info', onClick: () => show({ content: AboutModal, title: 'About OHIF Viewer' }), }, { title: t('Header:Preferences'), icon: 'settings', onClick: () => show({ title: t('UserPreferencesModal:User Preferences'), content: UserPreferences, contentProps: { hotkeyDefaults: hotkeysManager.getValidHotkeyDefinitions(hotkeyDefaults), hotkeyDefinitions, onCancel: hide, currentLanguage: currentLanguage(), availableLanguages, defaultLanguage, onSubmit: (state) => { i18n.changeLanguage(state.language.value) hotkeysManager.setHotkeys(state.hotkeyDefinitions) hide() }, onReset: () => hotkeysManager.restoreDefaultBindings(), }, }), }, ] /** ... **/ return (
/** ... **/
/** ... **/
) } ``` ## Tips & Tricks It's important to remember that all we're doing is making it possible to control bits of the application's UI from an extension. Here are a few non-obvious takeaways worth mentioning: - Your application code should continue to use React context (consumers/providers) as it normally would - You can substitute our "out of the box" UI implementations with your own - You can create and register your own UI services - You can choose not to register a service or provide a service implementation - In extensions, you can provide fallback/alternative behavior if an expected service is not registered - No `UIModalService`? Use the `UINotificationService` to notify users. - You can technically register a service in an extension and expose it to the core application > Note: These are recommended patterns, not hard and fast rules. Following them > will help reduce confusion and interoperability with the larger OHIF > community, but they're not silver bullets. Please speak up, create an issue, > if you would like to discuss new services or improvements to this pattern.