feat(typings): Enhance typing support with withAppTypes and custom services throughout OHIF (#4090)

This commit is contained in:
Ibrahim authored and GitHub committed 2024-05-08 12:12:49 -04:00
1 parent b2cd0172d6
commit 374065bc3b
153 files changed
+842 -657

No files matched your search

+1 -2
View File
@@ -6,7 +6,6 @@ import { I18nextProvider } from 'react-i18next';
import { BrowserRouter } from 'react-router-dom';
import Compose from './routes/Mode/Compose';
import {
ServicesManager,
ExtensionManager,
CommandsManager,
HotkeysManager,
@@ -33,7 +32,7 @@ import OpenIdConnectRoutes from './utils/OpenIdConnectRoutes';
let commandsManager: CommandsManager,
extensionManager: ExtensionManager,
servicesManager: ServicesManager,
servicesManager: AppTypes.ServicesManager,
serviceProvidersManager: ServiceProvidersManager,
hotkeysManager: HotkeysManager;
+5 -6
View File
@@ -1,13 +1,13 @@
import React, { useEffect, useCallback, useRef, useState } from 'react';
import ReactResizeDetector from 'react-resize-detector';
import PropTypes from 'prop-types';
import { ServicesManager, Types, MeasurementService } from '@ohif/core';
import { Types, MeasurementService } from '@ohif/core';
import { ViewportGrid, ViewportPane, useViewportGrid } from '@ohif/ui';
import EmptyViewport from './EmptyViewport';
import classNames from 'classnames';
import { useAppConfig } from '@state';
function ViewerViewportGrid(props) {
function ViewerViewportGrid(props: withAppTypes) {
const { servicesManager, viewportComponents, dataSource } = props;
const [viewportGrid, viewportGridService] = useViewportGrid();
const [appConfig] = useAppConfig();
@@ -18,9 +18,8 @@ function ViewerViewportGrid(props) {
const layoutHash = useRef(null);
// TODO -> Need some way of selecting which displaySets hit the viewports.
const { displaySetService, measurementService, hangingProtocolService, uiNotificationService } = (
servicesManager as ServicesManager
).services;
const { displaySetService, measurementService, hangingProtocolService, uiNotificationService } =
servicesManager.services;
const generateLayoutHash = () => `${numCols}-${numRows}`;
@@ -387,7 +386,7 @@ function ViewerViewportGrid(props) {
ViewerViewportGrid.propTypes = {
viewportComponents: PropTypes.array.isRequired,
servicesManager: PropTypes.instanceOf(ServicesManager),
servicesManager: PropTypes.instanceOf(Object).isRequired,
};
ViewerViewportGrid.defaultProps = {
+4 -5
View File
@@ -1,7 +1,7 @@
import React, { useEffect, useState, useRef } from 'react';
import { useParams, useLocation, useNavigate } from 'react-router';
import PropTypes from 'prop-types';
import { ServicesManager, utils } from '@ohif/core';
import { utils } from '@ohif/core';
import { DragAndDropProvider, ImageViewerProvider } from '@ohif/ui';
import { useSearchParams } from '@hooks';
import { useAppConfig } from '@state';
@@ -21,7 +21,7 @@ export default function ModeRoute({
servicesManager,
commandsManager,
hotkeysManager,
}) {
}: withAppTypes) {
const [appConfig] = useAppConfig();
// Parse route params/querystring
@@ -60,9 +60,8 @@ export default function ModeRoute({
locationRef.current = location;
}
const { displaySetService, panelService, hangingProtocolService, userAuthenticationService } = (
servicesManager as ServicesManager
).services;
const { displaySetService, panelService, hangingProtocolService, userAuthenticationService } =
servicesManager.services;
const { extensions, sopClassHandlers, hotkeys: hotkeyObj, hangingProtocol } = mode;
@@ -16,7 +16,7 @@ const { sortingCriteria, getSplitParam } = utils;
* @returns array of subscriptions to cancel
*/
export async function defaultRouteInit(
{ servicesManager, studyInstanceUIDs, dataSource, filters, appConfig },
{ servicesManager, studyInstanceUIDs, dataSource, filters, appConfig }: withAppTypes,
hangingProtocolId
) {
const { displaySetService, hangingProtocolService, uiNotificationService, customizationService } =
@@ -1,6 +1,6 @@
import React, { useState, useEffect, useMemo } from 'react';
import classnames from 'classnames';
import PropTypes from 'prop-types';
import PropTypes, { object } from 'prop-types';
import { Link, useNavigate } from 'react-router-dom';
import moment from 'moment';
import qs from 'query-string';
@@ -10,7 +10,7 @@ import { useTranslation } from 'react-i18next';
import filtersMeta from './filtersMeta.js';
import { useAppConfig } from '@state';
import { useDebounce, useSearchParams } from '@hooks';
import { utils, hotkeys, ServicesManager } from '@ohif/core';
import { utils, hotkeys } from '@ohif/core';
import {
Icon,
@@ -56,7 +56,7 @@ function WorkList({
dataPath,
onRefresh,
servicesManager,
}) {
}: withAppTypes) {
const { hotkeyDefinitions, hotkeyDefaults } = hotkeysManager;
const { show, hide } = useModal();
const { t } = useTranslation();
@@ -585,7 +585,7 @@ WorkList.propTypes = {
getConfig: PropTypes.func,
}).isRequired,
isLoadingData: PropTypes.bool.isRequired,
servicesManager: PropTypes.instanceOf(ServicesManager),
servicesManager: PropTypes.object.isRequired,
};
const defaultFilterValues = {
+1 -1
View File
@@ -28,7 +28,7 @@ export default function buildModeRoutes({
servicesManager,
commandsManager,
hotkeysManager,
}) {
}: withAppTypes) {
const routes = [];
const dataSourceNames = [];
+1 -1
View File
@@ -88,7 +88,7 @@ const createRoutes = ({
hotkeysManager,
routerBasename,
showStudyList,
}) => {
}: withAppTypes) => {
const routes =
buildModeRoutes({
modes,
+2 -2
View File
@@ -40,7 +40,7 @@ function modeFactory({ modeConfiguration }) {
* Runs when the Mode Route is mounted to the DOM. Usually used to initialize
* Services and other resources.
*/
onModeEnter: ({ servicesManager, extensionManager, commandsManager }) => {
onModeEnter: ({ servicesManager, extensionManager, commandsManager }: withAppTypes) => {
const { measurementService, toolbarService, toolGroupService } = servicesManager.services;
measurementService.clearMeasurements();
@@ -60,7 +60,7 @@ function modeFactory({ modeConfiguration }) {
'MoreTools',
]);
},
onModeExit: ({ servicesManager }) => {
onModeExit: ({ servicesManager }: withAppTypes) => {
const {
toolGroupService,
syncGroupService,
+2 -3
View File
@@ -2,7 +2,6 @@ import objectHash from 'object-hash';
import { hotkeys } from '../utils';
import isequal from 'lodash.isequal';
import Hotkey from './Hotkey';
import ServicesManager from '../services/ServicesManager';
/**
*
@@ -15,9 +14,9 @@ import ServicesManager from '../services/ServicesManager';
*/
export class HotkeysManager {
private _servicesManager: ServicesManager;
private _servicesManager: AppTypes.ServicesManager;
constructor(commandsManager, servicesManager) {
constructor(commandsManager, servicesManager: AppTypes.ServicesManager) {
this.hotkeyDefinitions = {};
this.hotkeyDefaults = [];
this.isEnabled = true;
@@ -1,7 +1,7 @@
import MODULE_TYPES from './MODULE_TYPES';
import log from '../log';
import { AppConfig } from '../types/AppConfig';
import { PubSubService, ServiceProvidersManager, ServicesManager } from '../services';
import { PubSubService, ServiceProvidersManager } from '../services';
import { HotkeysManager, CommandsManager } from '../classes';
import { DataSourceDefinition } from '../types';
@@ -9,7 +9,7 @@ import { DataSourceDefinition } from '../types';
* This is the arguments given to create the extension.
*/
export interface ExtensionConstructor {
servicesManager: ServicesManager;
servicesManager: AppTypes.ServicesManager;
serviceProvidersManager: ServiceProvidersManager;
commandsManager: CommandsManager;
hotkeysManager: HotkeysManager;
@@ -28,7 +28,7 @@ export type ExtensionConfiguration = Record<string, unknown>;
*/
export interface ExtensionParams extends ExtensionConstructor {
extensionManager: ExtensionManager;
servicesManager: ServicesManager;
servicesManager: AppTypes.ServicesManager;
serviceProvidersManager: ServiceProvidersManager;
configuration?: ExtensionConfiguration;
}
@@ -72,7 +72,7 @@ export default class ExtensionManager extends PubSubService {
public static readonly MODULE_TYPES = MODULE_TYPES;
private _commandsManager: CommandsManager;
private _servicesManager: ServicesManager;
private _servicesManager: AppTypes.ServicesManager;
private _hotkeysManager: HotkeysManager;
private _serviceProvidersManager: ServiceProvidersManager;
private modulesMap: Record<string, unknown>;
+1 -1
View File
@@ -1,6 +1,6 @@
import { useCallback, useEffect, useState } from 'react';
export function useToolbar({ servicesManager, buttonSection = 'primary' }) {
export function useToolbar({ servicesManager, buttonSection = 'primary' }: withAppTypes) {
const { toolbarService, viewportGridService } = servicesManager.services;
const { EVENTS } = toolbarService;
@@ -6,7 +6,6 @@ import ProtocolEngine from './ProtocolEngine';
import { StudyMetadata } from '../../types/StudyMetadata';
import IDisplaySet from '../DisplaySetService/IDisplaySet';
import { CommandsManager } from '../../classes';
import ServicesManager from '../ServicesManager';
import * as HangingProtocol from '../../types/HangingProtocol';
import { isDisplaySetFromUrl, sopInstanceLocation } from './custom-attribute/isDisplaySetFromUrl';
import numberOfDisplaySetsWithImages from './custom-attribute/numberOfDisplaySetsWithImages';
@@ -60,7 +59,7 @@ export default class HangingProtocolService extends PubSubService {
stageIndex = 0;
_commandsManager: CommandsManager;
_servicesManager: ServicesManager;
_servicesManager: AppTypes.ServicesManager;
protocolEngine: ProtocolEngine;
customViewportSettings = [];
displaySets: IDisplaySet[] = [];
@@ -136,7 +135,7 @@ export default class HangingProtocolService extends PubSubService {
HangingProtocol.ViewportMatchDetails
> = new Map();
constructor(commandsManager: CommandsManager, servicesManager) {
constructor(commandsManager: CommandsManager, servicesManager: AppTypes.ServicesManager) {
super(HangingProtocolService.EVENTS);
this._commandsManager = commandsManager;
this._servicesManager = servicesManager;
@@ -1,10 +1,9 @@
import log from './../log.js';
import Services from '../types/Services';
import CommandsManager from '../classes/CommandsManager';
import ExtensionManager from '../extensions/ExtensionManager';
export default class ServicesManager {
public services: Services = {};
public services: AppTypes.Services = {};
public registeredServiceNames: string[] = [];
private _commandsManager: CommandsManager;
private _extensionManager: ExtensionManager;
@@ -2,7 +2,6 @@ import { CommandsManager } from '../../classes';
import { ExtensionManager } from '../../extensions';
import { PubSubService } from '../_shared/pubSubServiceInterface';
import type { RunCommand } from '../../types/Command';
import ServicesManager from '../ServicesManager';
import { Button, ButtonProps, EvaluateFunction, EvaluatePublic, NestedButtonProps } from './types';
const EVENTS = {
@@ -52,14 +51,14 @@ export default class ToolbarService extends PubSubService {
_commandsManager: CommandsManager;
_extensionManager: ExtensionManager;
_servicesManager: ServicesManager;
_servicesManager: AppTypes.ServicesManager;
_evaluateFunction: Record<string, EvaluateFunction> = {};
_serviceSubscriptions = [];
constructor(
commandsManager: CommandsManager,
extensionManager: ExtensionManager,
servicesManager: ServicesManager
servicesManager: AppTypes.ServicesManager
) {
super(EVENTS);
this._commandsManager = commandsManager;
@@ -0,0 +1,80 @@
import { PubSubService } from '../_shared/pubSubServiceInterface';
class UIDialogService extends PubSubService {
public static readonly EVENTS = {};
public static REGISTRATION = {
name: 'uiDialogService',
altName: 'UIDialogService',
create: ({ configuration = {} }) => {
return new UIDialogService();
},
};
serviceImplementation = {
_dismiss: () => console.warn('dismiss() NOT IMPLEMENTED'),
_dismissAll: () => console.warn('dismissAll() NOT IMPLEMENTED'),
_create: () => console.warn('create() NOT IMPLEMENTED'),
};
constructor() {
super(UIDialogService.EVENTS);
this.serviceImplementation = {
...this.serviceImplementation,
};
}
public create({
id,
content,
contentProps,
onStart,
onDrag,
onStop,
centralize = false,
preservePosition = true,
isDraggable = true,
showOverlay = false,
defaultPosition,
}) {
return this.serviceImplementation._create({
id,
content,
contentProps,
onStart,
onDrag,
onStop,
centralize,
preservePosition,
isDraggable,
showOverlay,
defaultPosition,
});
}
public dismiss({ id }) {
return this.serviceImplementation._dismiss({ id });
}
public dismissAll() {
return this.serviceImplementation._dismissAll();
}
public setServiceImplementation({
dismiss: dismissImplementation,
dismissAll: dismissAllImplementation,
create: createImplementation,
}) {
if (dismissImplementation) {
this.serviceImplementation._dismiss = dismissImplementation;
}
if (dismissAllImplementation) {
this.serviceImplementation._dismissAll = dismissAllImplementation;
}
if (createImplementation) {
this.serviceImplementation._create = createImplementation;
}
}
}
export default UIDialogService;
@@ -1,131 +0,0 @@
/**
* A UI Element Position
*
* @typedef {Object} ElementPosition
* @property {number} top -
* @property {number} left -
* @property {number} right -
* @property {number} bottom -
*/
/**
* UI Dialog
*
* @typedef {Object} DialogProps
* @property {string} id The dialog id.
* @property {ReactElement|HTMLElement} content The dialog content.
* @property {Object} contentProps The dialog content props.
* @property {boolean} [isDraggable=true] Controls if dialog content is draggable or not.
* @property {boolean} [showOverlay=false] Controls dialog overlay.
* @property {boolean} [centralize=false] Center the dialog on the screen.
* @property {boolean} [preservePosition=true] Use last position instead of default.
* @property {ElementPosition} defaultPosition Specifies the `x` and `y` that the dragged item should start at.
* @property {Function} onStart Called when dragging starts. If `false` is returned any handler, the action will cancel.
* @property {Function} onStop Called when dragging stops.
* @property {Function} onDrag Called while dragging.
*/
const name = 'uiDialogService';
const publicAPI = {
name,
dismiss: _dismiss,
dismissAll: _dismissAll,
create: _create,
setServiceImplementation,
};
const serviceImplementation = {
_dismiss: () => console.warn('dismiss() NOT IMPLEMENTED'),
_dismissAll: () => console.warn('dismissAll() NOT IMPLEMENTED'),
_create: () => console.warn('create() NOT IMPLEMENTED'),
};
/**
* Show a new UI dialog;
*
* @param {DialogProps} props { id, content, contentProps, onStart, onDrag, onStop, centralize, isDraggable, showOverlay, preservePosition, defaultPosition }
*/
function _create({
id,
content,
contentProps,
onStart,
onDrag,
onStop,
centralize = false,
preservePosition = true,
isDraggable = true,
showOverlay = false,
onClickOutside,
defaultPosition,
}) {
return serviceImplementation._create({
id,
content,
contentProps,
onStart,
onDrag,
onStop,
centralize,
preservePosition,
isDraggable,
onClickOutside,
showOverlay,
defaultPosition,
});
}
/**
* Destroys all dialogs, if any
*
* @returns void
*/
function _dismissAll() {
return serviceImplementation._dismissAll();
}
/**
* Destroy the dialog, if currently created
*
* @returns void
*/
function _dismiss({ id }) {
return serviceImplementation._dismiss({ id });
}
/**
*
*
* @param {*} {
* dismiss: dismissImplementation,
* dismissAll: dismissAllImplementation,
* create: createImplementation,
* }
*/
function setServiceImplementation({
dismiss: dismissImplementation,
dismissAll: dismissAllImplementation,
create: createImplementation,
}) {
if (dismissImplementation) {
serviceImplementation._dismiss = dismissImplementation;
}
if (dismissAllImplementation) {
serviceImplementation._dismissAll = dismissAllImplementation;
}
if (createImplementation) {
serviceImplementation._create = createImplementation;
}
}
// TODO - export type here
export default {
REGISTRATION: {
name,
altName: 'UIDialogService',
create: ({ configuration = {} }) => {
return publicAPI;
},
},
};
@@ -0,0 +1,3 @@
import UIDialogService from './UIDialogService';
export default UIDialogService;
@@ -0,0 +1,52 @@
import { PubSubService } from '../_shared/pubSubServiceInterface';
class UIViewportDialogService extends PubSubService {
public static readonly EVENTS = {};
public static REGISTRATION = {
name: 'uiViewportDialogService',
altName: 'UIViewportDialogService',
create: ({ configuration = {} }) => {
return new UIViewportDialogService();
},
};
serviceImplementation = {
_hide: () => console.warn('hide() NOT IMPLEMENTED'),
_show: () => console.warn('show() NOT IMPLEMENTED'),
};
constructor() {
super(UIViewportDialogService.EVENTS);
this.serviceImplementation = {
...this.serviceImplementation,
};
}
public show({ viewportId, id, type, message, actions, onSubmit, onOutsideClick, onKeyPress }) {
return this.serviceImplementation._show({
viewportId,
id,
type,
message,
actions,
onSubmit,
onOutsideClick,
onKeyPress,
});
}
public hide() {
return this.serviceImplementation._hide();
}
public setServiceImplementation({ hide: hideImplementation, show: showImplementation }) {
if (hideImplementation) {
this.serviceImplementation._hide = hideImplementation;
}
if (showImplementation) {
this.serviceImplementation._show = showImplementation;
}
}
}
export default UIViewportDialogService;
@@ -1,75 +0,0 @@
/**
* Viewport Dialog
*
* @typedef {Object} ViewportDialogProps
* @property {ReactElement|HTMLElement} [content=null] Modal content.
* @property {Object} [contentProps=null] Modal content props.
* @property {boolean} [viewportId=false] Modal is dismissible via the esc key.
*/
const name = 'uiViewportDialogService';
const publicAPI = {
name,
hide: _hide,
show: _show,
setServiceImplementation,
};
const serviceImplementation = {
_hide: () => console.warn('hide() NOT IMPLEMENTED'),
_show: () => console.warn('show() NOT IMPLEMENTED'),
};
/**
* Show a new UI viewport dialog on the specified viewportId;
*
* @param {ViewportDialogProps} props { content, contentProps, viewportId }
*/
function _show({ viewportId, id, type, message, actions, onSubmit, onOutsideClick, onKeyPress }) {
return serviceImplementation._show({
viewportId,
id,
type,
message,
actions,
onSubmit,
onOutsideClick,
onKeyPress,
});
}
/**
* Hides/dismisses the viewport dialog, if currently shown
*/
function _hide() {
return serviceImplementation._hide();
}
/**
*
*
* @param {*} {
* hide: hideImplementation,
* show: showImplementation,
* viewportId,
* }
*/
function setServiceImplementation({ hide: hideImplementation, show: showImplementation }) {
if (hideImplementation) {
serviceImplementation._hide = hideImplementation;
}
if (showImplementation) {
serviceImplementation._show = showImplementation;
}
}
export default {
REGISTRATION: {
name,
altName: 'UIViewportDialogService',
create: ({ configuration = {} }) => {
return publicAPI;
},
},
};
@@ -0,0 +1,3 @@
import UIViewportDialogService from './UIViewportDialogService';
export default UIViewportDialogService;
@@ -1,94 +0,0 @@
const name = 'userAuthenticationService';
const publicAPI = {
name,
getState: _getState,
setUser: _setUser,
getUser: _getUser,
getAuthorizationHeader: _getAuthorizationHeader,
handleUnauthenticated: _handleUnauthenticated,
setServiceImplementation,
reset: _reset,
set: _set,
};
const serviceImplementation = {
_getState: () => console.warn('getState() NOT IMPLEMENTED'),
_setUser: () => console.warn('_setUser() NOT IMPLEMENTED'),
_getUser: () => console.warn('_setUser() NOT IMPLEMENTED'),
_getAuthorizationHeader: () => {}, // TODO: have enabled/disabled state?
//console.warn('_getAuthorizationHeader() NOT IMPLEMENTED'),
_handleUnauthenticated: () => console.warn('_handleUnauthenticated() NOT IMPLEMENTED'),
_reset: () => console.warn('reset() NOT IMPLEMENTED'),
_set: () => console.warn('set() NOT IMPLEMENTED'),
};
function _getState() {
return serviceImplementation._getState();
}
function _setUser(user) {
return serviceImplementation._setUser(user);
}
function _getUser() {
return serviceImplementation._getUser();
}
function _getAuthorizationHeader() {
return serviceImplementation._getAuthorizationHeader();
}
function _handleUnauthenticated() {
return serviceImplementation._handleUnauthenticated();
}
function _set(state) {
return serviceImplementation._set(state);
}
function _reset() {
return serviceImplementation._reset({});
}
function setServiceImplementation({
getState: getStateImplementation,
setUser: setUserImplementation,
getUser: getUserImplementation,
getAuthorizationHeader: getAuthorizationHeaderImplementation,
handleUnauthenticated: handleUnauthenticatedImplementation,
reset: resetImplementation,
set: setImplementation,
}) {
if (getStateImplementation) {
serviceImplementation._getState = getStateImplementation;
}
if (setUserImplementation) {
serviceImplementation._setUser = setUserImplementation;
}
if (getUserImplementation) {
serviceImplementation._getUser = getUserImplementation;
}
if (getAuthorizationHeaderImplementation) {
serviceImplementation._getAuthorizationHeader = getAuthorizationHeaderImplementation;
}
if (handleUnauthenticatedImplementation) {
serviceImplementation._handleUnauthenticated = handleUnauthenticatedImplementation;
}
if (resetImplementation) {
serviceImplementation._reset = resetImplementation;
}
if (setImplementation) {
serviceImplementation._set = setImplementation;
}
}
export default {
REGISTRATION: {
name,
altName: 'UserAuthenticationService',
create: ({ configuration = {} }) => {
return publicAPI;
},
},
};
@@ -0,0 +1,92 @@
import { PubSubService } from '../_shared/pubSubServiceInterface';
class UserAuthenticationService extends PubSubService {
public static readonly EVENTS = {};
public static REGISTRATION = {
name: 'userAuthenticationService',
altName: 'UserAuthenticationService',
create: ({ configuration = {} }) => {
return new UserAuthenticationService();
},
};
serviceImplementation = {
_getState: () => console.warn('getState() NOT IMPLEMENTED'),
_setUser: () => console.warn('_setUser() NOT IMPLEMENTED'),
_getUser: () => console.warn('_getUser() NOT IMPLEMENTED'),
_getAuthorizationHeader: () => {}, // TODO: Implement this method
_handleUnauthenticated: () => console.warn('_handleUnauthenticated() NOT IMPLEMENTED'),
_reset: () => console.warn('reset() NOT IMPLEMENTED'),
_set: () => console.warn('set() NOT IMPLEMENTED'),
};
constructor() {
super(UserAuthenticationService.EVENTS);
this.serviceImplementation = {
...this.serviceImplementation,
};
}
public getState() {
return this.serviceImplementation._getState();
}
public setUser(user) {
return this.serviceImplementation._setUser(user);
}
public getUser() {
return this.serviceImplementation._getUser();
}
public getAuthorizationHeader() {
return this.serviceImplementation._getAuthorizationHeader();
}
public handleUnauthenticated() {
return this.serviceImplementation._handleUnauthenticated();
}
public reset() {
return this.serviceImplementation._reset();
}
public set(state) {
return this.serviceImplementation._set(state);
}
public setServiceImplementation({
getState: getStateImplementation,
setUser: setUserImplementation,
getUser: getUserImplementation,
getAuthorizationHeader: getAuthorizationHeaderImplementation,
handleUnauthenticated: handleUnauthenticatedImplementation,
reset: resetImplementation,
set: setImplementation,
}) {
if (getStateImplementation) {
this.serviceImplementation._getState = getStateImplementation;
}
if (setUserImplementation) {
this.serviceImplementation._setUser = setUserImplementation;
}
if (getUserImplementation) {
this.serviceImplementation._getUser = getUserImplementation;
}
if (getAuthorizationHeaderImplementation) {
this.serviceImplementation._getAuthorizationHeader = getAuthorizationHeaderImplementation;
}
if (handleUnauthenticatedImplementation) {
this.serviceImplementation._handleUnauthenticated = handleUnauthenticatedImplementation;
}
if (resetImplementation) {
this.serviceImplementation._reset = resetImplementation;
}
if (setImplementation) {
this.serviceImplementation._set = setImplementation;
}
}
}
export default UserAuthenticationService;
@@ -1,6 +1,5 @@
import { CommandsManager } from '../../classes';
import { ExtensionManager } from '../../extensions';
import { ServicesManager } from '../../services';
import { PubSubService } from '../_shared/pubSubServiceInterface';
export const EVENTS = {
@@ -82,7 +81,7 @@ export type WorkflowStep = {
class WorkflowStepsService extends PubSubService {
private _extensionManager: ExtensionManager;
private _servicesManager: ServicesManager;
private _servicesManager: AppTypes.ServicesManager;
private _commandsManager: CommandsManager;
private _workflowSteps: WorkflowStep[];
private _activeWorkflowStep: WorkflowStep;
@@ -90,7 +89,7 @@ class WorkflowStepsService extends PubSubService {
constructor(
extensionManager: ExtensionManager,
commandsManager: CommandsManager,
servicesManager: ServicesManager
servicesManager: AppTypes.ServicesManager
) {
super(EVENTS);
this._workflowSteps = [];
+14
View File
@@ -4,4 +4,18 @@ export interface AppConfig {
extensions?: string[];
defaultDataSourceName?: string;
hotkeys?: Record<string, Hotkey> | Hotkey[];
useSharedArrayBuffer?: string;
preferSizeOverAccuracy?: boolean;
useNorm16Texture?: boolean;
useCPURendering?: boolean;
strictZSpacingForVolumeViewport?: boolean;
useCursors?: boolean;
maxCacheSize?: number;
showWarningMessageForCrossOrigin?: boolean;
showCPUFallbackMessage?: boolean;
maxNumRequests?: {
interaction?: number;
prefetch?: number;
thumbnail?: number;
};
}
+73
View File
@@ -0,0 +1,73 @@
/* eslint-disable @typescript-eslint/no-namespace */
import HangingProtocolServiceType from '../services/HangingProtocolService';
import CustomizationServiceType from '../services/CustomizationService';
import MeasurementServiceType from '../services/MeasurementService';
import ViewportGridServiceType from '../services/ViewportGridService';
import ToolbarServiceType from '../services/ToolBarService';
import DisplaySetServiceType from '../services/DisplaySetService';
import StateSyncServiceType from '../services/StateSyncService';
import UINotificationServiceType from '../services/UINotificationService';
import UIModalServiceType from '../services/UIModalService';
import WorkflowStepsServiceType from '../services/WorkflowStepsService';
import CineServiceType from '../services/CineService';
import UserAuthenticationServiceType from '../services/UserAuthenticationService';
import PanelServiceType from '../services/PanelService';
import UIDialogServiceType from '../services/UIDialogService';
import UIViewportDialogServiceType from '../services/UIViewportDialogService';
import ServicesManagerType from '../services/ServicesManager';
import CommandsManagerType from '../classes/CommandsManager';
import ExtensionManagerType from '../extensions/ExtensionManager';
declare global {
namespace AppTypes {
export type ServicesManager = ServicesManagerType;
export type CommandsManager = CommandsManagerType;
export type ExtensionManager = ExtensionManagerType;
export type HangingProtocolService = HangingProtocolServiceType;
export type CustomizationService = CustomizationServiceType;
export type MeasurementService = MeasurementServiceType;
export type DisplaySetService = DisplaySetServiceType;
export type ToolbarService = ToolbarServiceType;
export type ViewportGridService = ViewportGridServiceType;
export type UIModalService = UIModalServiceType;
export type UINotificationService = UINotificationServiceType;
export type StateSyncService = StateSyncServiceType;
export type WorkflowStepsService = WorkflowStepsServiceType;
export type CineService = CineServiceType;
export type UserAuthenticationService = UserAuthenticationServiceType;
export type UIDialogService = UIDialogServiceType;
export type UIViewportDialogService = UIViewportDialogServiceType;
export type PanelService = PanelServiceType;
export interface Managers {
servicesManager?: ServicesManager;
commandsManager?: CommandsManager;
extensionManager?: ExtensionManager;
}
export interface Services {
hangingProtocolService?: HangingProtocolServiceType;
customizationService?: CustomizationServiceType;
measurementService?: MeasurementServiceType;
displaySetService?: DisplaySetServiceType;
toolbarService?: ToolbarServiceType;
viewportGridService?: ViewportGridServiceType;
uiModalService?: UIModalServiceType;
uiNotificationService?: UINotificationServiceType;
stateSyncService?: StateSyncServiceType;
workflowStepsService?: WorkflowStepsServiceType;
cineService?: CineServiceType;
userAuthenticationService?: UserAuthenticationServiceType;
uiDialogService?: UIDialogServiceType;
uiViewportDialogService?: UIViewportDialogServiceType;
panelService?: PanelServiceType;
}
}
export type withAppTypes<T = object> = T &
AppTypes.Services &
AppTypes.Managers & {
[key: string]: any;
};
}
+1 -1
View File
@@ -322,7 +322,7 @@ export type Protocol = {
* to the GUI when this is used, and it can be expensive to apply.
* Alternatives include using the custom attributes where possible.
*/
export type ProtocolGenerator = ({ servicesManager: any, commandsManager: any }) => {
export type ProtocolGenerator = ({ servicesManager, commandsManager }: withAppTypes) => {
protocol: Protocol;
};
+14 -12
View File
@@ -9,12 +9,18 @@ import {
UINotificationService,
UIModalService,
WorkflowStepsService,
CineService,
UserAuthenticationService,
PanelService,
UIDialogService,
UIViewportDialogService,
} from '../services';
/**
* The interface for the services object
*/
export default interface Services {
interface Services {
hangingProtocolService?: HangingProtocolService;
customizationService?: CustomizationService;
measurementService?: MeasurementService;
@@ -25,15 +31,11 @@ export default interface Services {
uiNotificationService?: UINotificationService;
stateSyncService?: StateSyncService;
workflowStepsService: WorkflowStepsService;
cineService?: unknown;
userAuthenticationService?: unknown;
cornerstoneViewportService?: unknown;
uiDialogService?: unknown;
toolGroupService?: unknown;
uiViewportDialogService?: unknown;
syncGroupService?: unknown;
cornerstoneCacheService?: unknown;
segmentationService?: unknown;
panelService?: unknown;
colorbarService?: unknown;
cineService?: CineService;
userAuthenticationService?: UserAuthenticationService;
uiDialogService?: UIDialogService;
uiViewportDialogService?: UIViewportDialogService;
panelService?: PanelService;
}
export default Services;
+85
View File
@@ -0,0 +1,85 @@
---
sidebar_position: 10
sidebar_label: Global Types
---
# Extending App Types and Services in Your Application
This documentation provides an overview and examples on how to use and extend `withAppTypes`, integrate custom properties, and add services in the global namespace of the application. This helps in enhancing the application's modularity and extensibility.
## Overview of `withAppTypes`
The `withAppTypes` function is a TypeScript utility that extends the base properties of components or modules with the application's core service and manager types. It allows for a more flexible and type-safe way to pass around core functionality and custom properties.
### Using `withAppTypes`
`withAppTypes` can be enhanced using generics to include custom properties. This is particularly useful for passing additional data or configurations specific to your component or service.
### Extending with Custom Properties
You can extend `withAppTypes` to include custom properties by defining an interface for the props you need. For example:
```typescript
interface ColorbarProps {
viewportId: string;
displaySets: Array<any>;
colorbarProperties: ColorbarProperties;
}
export function Colorbar({
viewportId,
displaySets,
commandsManager, // injected type
servicesManager, // injected type
colorbarProperties,
}: withAppTypes<ColorbarProps>): ReactElement {
// Component logic here
}
```
In this example, `ColorbarProps` is a custom interface that extends the application types through `withAppTypes`.
## Typing the custom extensions's new services
Extensions can define additional services that integrate seamlessly into the application's global service architecture, and will be available on the ServicesManager for use across the application.
### Adding the extension's services Types
Declare your service in the global namespace and use it across your application as demonstrated below:
`extensions/my-extension/src/types/whatever.ts`
```typescript
declare global {
namespace AppTypes {
// only add if you need direct access to the service ex. AppTypes.MicroscopyService
export type MicroscopyService = MicroscopyServiceType;
// add to the global Services interface, and to withAppTypes
export interface Services {
microscopyService?: MicroscopyServiceType;
}
}
}
```
Doing the above adds the `microscopyService` to the global Services interface, which ServicesManager uses by default `public services: AppTypes.Services = {};` to type services, and is also used by withAppTypes to inject services into components.
You will also get access to the seperate services via `AppTypes.YourServiceName` in your application.
```typescript
export function CustomComponent({
servicesManager,
}: withAppTypes<CustomComponentProps>): ReactElement {
const { microscopyService } = servicesManager.services;
microscopyService.someMethod(); // auto completation available
}
```
```typescript
export function CustomComponent2(
microscopyService: AppTypes.MicroscopyService,
): ReactElement {
microscopyService.someMethod(); // auto completation available
}
```
@@ -36,11 +36,11 @@ for `OHIF-v3`.
// new service inside new extension
import MyNewService from './MyNewService';
export default function MyNewServiceWithServices(serviceManager) {
export default function MyNewServiceWithServices(servicesManager) {
return {
name: 'MyNewService',
create: ({ configuration = {} }) => {
return new MyNewService(serviceManager);
return new MyNewService(servicesManager);
},
};
}
@@ -146,12 +146,12 @@ and the logic for your service shall be
// Canonical name of upper camel case BackEndService for the class
import BackEndService from './BackEndService';
export default function WrappedBackEndService(serviceManager) {
export default function WrappedBackEndService(servicesManager) {
return {
// Note the canonical name of lower camel case backEndService
name: 'backEndService',
create: ({ configuration = {} }) => {
return new BackEndService(serviceManager);
return new BackEndService(servicesManager);
},
};
}
@@ -161,8 +161,8 @@ with implementation of
```ts
export default class BackEndService {
constructor(serviceManager) {
this.serviceManager = serviceManager;
constructor(servicesManager) {
this.servicesManager = servicesManager;
}
putAnnotations() {
@@ -28,7 +28,7 @@ We maintain the following non-ui Services:
![services-data](../../../assets/img/services-data.png)
> We have explained services and how to create a custom service in the
> [`ServiceManager`](../../managers/service.md) section of the docs
> [`ServicesManager`](../../managers/service.md) section of the docs
To recap: The simplest service return a new object that has a `name` property,
and `Create` method which instantiate the service class. The "Factory Function"
@@ -39,11 +39,11 @@ different for UI Services).
// extensions/customExtension/src/services/backEndService/index.js
import backEndService from './backEndService';
export default function WrappedBackEndService(serviceManager) {
export default function WrappedBackEndService(servicesManager) {
return {
name: 'myService',
create: ({ configuration = {} }) => {
return new backEndService(serviceManager);
return new backEndService(servicesManager);
},
};
}
+1 -1
View File
@@ -23,7 +23,7 @@ function Header({
Secondary,
appConfig,
...props
}): ReactNode {
}: withAppTypes): ReactNode {
const { t } = useTranslation('Header');
// TODO: this should be passed in as a prop instead and the react-router-dom
@@ -13,7 +13,7 @@ const formatWithEllipsis = (str, maxLength) => {
return str;
};
function usePatientInfo(servicesManager) {
function usePatientInfo(servicesManager: AppTypes.ServicesManager) {
const { displaySetService } = servicesManager.services;
const [patientInfo, setPatientInfo] = useState({
@@ -70,7 +70,7 @@ function usePatientInfo(servicesManager) {
return { patientInfo, isMixedPatients };
}
function HeaderPatientInfo({ servicesManager, appConfig }) {
function HeaderPatientInfo({ servicesManager, appConfig }: withAppTypes) {
const initialExpandedState =
appConfig.showPatientInfo === PatientInfoVisibility.VISIBLE ||
appConfig.showPatientInfo === PatientInfoVisibility.VISIBLE_READONLY;
@@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next';
import SwiperCore, { A11y, Controller, Navigation, Pagination, Scrollbar } from 'swiper';
import { Swiper, SwiperSlide } from 'swiper/react';
import { PanelService, ServicesManager, Types } from '@ohif/core';
import { Types } from '@ohif/core';
import LegacyButton from '../LegacyButton';
import Icon from '../Icon';
@@ -71,8 +71,8 @@ const LegacySidePanel = ({
className,
activeTabIndex: activeTabIndexProp,
tabs,
}) => {
const panelService: PanelService = servicesManager?.services?.panelService;
}: withAppTypes) => {
const panelService = servicesManager?.services?.panelService;
const { t } = useTranslation('LegacySidePanel');
@@ -288,7 +288,7 @@ LegacySidePanel.defaultProps = {
};
LegacySidePanel.propTypes = {
servicesManager: PropTypes.instanceOf(ServicesManager),
servicesManager: PropTypes.object.isRequired,
side: PropTypes.oneOf(['left', 'right']).isRequired,
className: PropTypes.string,
activeTabIndex: PropTypes.number,
@@ -1,12 +1,17 @@
import React from 'react';
import { ServicesManager } from '@ohif/core';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import MeasurementItem from './MeasurementItem';
const MeasurementTable = ({ data, title, onClick, onEdit, onDelete, servicesManager }) => {
servicesManager = servicesManager as ServicesManager;
const MeasurementTable = ({
data,
title,
onClick,
onEdit,
onDelete,
servicesManager,
}: withAppTypes) => {
const { customizationService, measurementService } = servicesManager.services;
const { t } = useTranslation('MeasurementTable');
const amount = data.length;
@@ -30,7 +30,7 @@ const StudyBrowser = ({
onClickUntrack,
activeDisplaySetInstanceUIDs,
servicesManager,
}) => {
}: withAppTypes) => {
const { t } = useTranslation('StudyBrowser');
const { customizationService } = servicesManager?.services || {};
@@ -13,7 +13,13 @@ import { useToolbox } from '../../contextProviders';
* role in enhancing the app with a toolbox by providing a way to integrate
* and display various tools and their corresponding options
*/
function Toolbox({ servicesManager, buttonSectionId, commandsManager, title, ...props }) {
function Toolbox({
servicesManager,
buttonSectionId,
commandsManager,
title,
...props
}: withAppTypes) {
const { state: toolboxState, api } = useToolbox(buttonSectionId);
const { onInteraction, toolbarButtons } = useToolbar({
servicesManager,
@@ -15,7 +15,7 @@ function usePrevious(value) {
/**
* Just refactoring from the toolbox component to make it more readable
*/
function ToolboxUI(props) {
function ToolboxUI(props: withAppTypes) {
const {
toolbarButtons,
handleToolSelect,