OHIF-323: Cine Dialog (#1961)
* ohif-323: add cineplayer flow * ohif-323: improve toolbar flow of cine * ohif-323: improve cine styles * ohif-323: use toggle instead of set * ohif-323: add compatibility comment * ohif-323: move cine logic to command * ohif-323: cr update * ohif-323: remove typo and update svg title * ohif-323: remove log * ohif-323: update props and remove dead code Co-authored-by: Danny Brown <danny.ri.brown@gmail.com>
This commit is contained in:
parent
3e944780dc
commit
760e2a62ae
@ -6,12 +6,6 @@ import debounce from 'lodash.debounce';
|
|||||||
|
|
||||||
import { setEnabledElement } from './state';
|
import { setEnabledElement } from './state';
|
||||||
|
|
||||||
|
|
||||||
// const cine = viewportSpecificData.cine;
|
|
||||||
|
|
||||||
// isPlaying = cine.isPlaying === true;
|
|
||||||
// frameRate = cine.cineFrameRate || frameRate;
|
|
||||||
|
|
||||||
const { StackManager } = OHIF.utils;
|
const { StackManager } = OHIF.utils;
|
||||||
|
|
||||||
class OHIFCornerstoneViewport extends Component {
|
class OHIFCornerstoneViewport extends Component {
|
||||||
|
|||||||
@ -91,10 +91,10 @@ function ToolbarSecondary({ servicesManager }) {
|
|||||||
isActive: false,
|
isActive: false,
|
||||||
};
|
};
|
||||||
const [toolbars, setToolbars] = useState({ primary: [], secondary: [] });
|
const [toolbars, setToolbars] = useState({ primary: [], secondary: [] });
|
||||||
const [activeTool, setActiveTool] = useState(defaultTool);
|
const [nestedActiveTool, setNestedActiveTool] = useState(defaultTool);
|
||||||
|
|
||||||
const setActiveToolHandler = (tool, isNested) => {
|
const setActiveToolHandler = (tool, isNested) => {
|
||||||
setActiveTool(isNested ? tool : defaultTool);
|
setNestedActiveTool(isNested ? tool : defaultTool);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onPrimaryClickHandler = (evt, btn) => {
|
const onPrimaryClickHandler = (evt, btn) => {
|
||||||
@ -257,6 +257,7 @@ function ViewerLayout({
|
|||||||
<ViewportGridComp
|
<ViewportGridComp
|
||||||
servicesManager={servicesManager}
|
servicesManager={servicesManager}
|
||||||
viewportComponents={viewportComponents}
|
viewportComponents={viewportComponents}
|
||||||
|
commandsManager={commandsManager}
|
||||||
/>
|
/>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,10 +1,17 @@
|
|||||||
const commandsModule = ({ servicesManager }) => {
|
const commandsModule = ({ servicesManager, commandsManager }) => {
|
||||||
const { MeasurementService } = servicesManager.services;
|
const { MeasurementService, ViewportGridService, ToolBarService, CineService } = servicesManager.services;
|
||||||
|
|
||||||
const actions = {
|
const actions = {
|
||||||
clearMeasurements: () => {
|
clearMeasurements: () => {
|
||||||
MeasurementService.clear();
|
MeasurementService.clear();
|
||||||
},
|
},
|
||||||
|
toggleCine: () => {
|
||||||
|
const { viewports } = ViewportGridService.getState();
|
||||||
|
const { isCineEnabled } = CineService.getState();
|
||||||
|
CineService.setIsCineEnabled(!isCineEnabled);
|
||||||
|
ToolBarService.setButton('Cine', { props: { isActive: !isCineEnabled } });
|
||||||
|
viewports.forEach((_, index) => CineService.setCine({ id: index, isPlaying: false }));
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const definitions = {
|
const definitions = {
|
||||||
@ -13,6 +20,11 @@ const commandsModule = ({ servicesManager }) => {
|
|||||||
storeContexts: [],
|
storeContexts: [],
|
||||||
options: {},
|
options: {},
|
||||||
},
|
},
|
||||||
|
toggleCine: {
|
||||||
|
commandFn: actions.toggleCine,
|
||||||
|
storeContexts: [],
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -85,8 +85,8 @@ export default function getToolbarModule({ commandsManager, servicesManager }) {
|
|||||||
thisButton.props.isActive = !thisButton.props.isActive;
|
thisButton.props.isActive = !thisButton.props.isActive;
|
||||||
|
|
||||||
// Run button logic/command
|
// Run button logic/command
|
||||||
// MAKE SURE THIS SUPPORTS TOGGLE!
|
commandsManager.runCommand(props.commandName, props.commandOptions);
|
||||||
// commandsManager.runCommand(props.commandName, props.commandOptions);
|
|
||||||
// What if just toggled "content"?
|
// What if just toggled "content"?
|
||||||
// commandName OR content?
|
// commandName OR content?
|
||||||
|
|
||||||
|
|||||||
@ -18,7 +18,7 @@ export default {
|
|||||||
getPanelModule,
|
getPanelModule,
|
||||||
getSopClassHandlerModule,
|
getSopClassHandlerModule,
|
||||||
getToolbarModule,
|
getToolbarModule,
|
||||||
getCommandsModule({ servicesManager }) {
|
getCommandsModule({ servicesManager, commandsManager }) {
|
||||||
return commandsModule({ servicesManager });
|
return commandsModule({ servicesManager, commandsManager });
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -12,10 +12,10 @@ const OHIFCornerstoneViewport = props => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
function getViewportModule({ servicesManager }) {
|
function getViewportModule({ servicesManager, commandsManager }) {
|
||||||
const ExtendedOHIFCornerstoneSRViewport = props => {
|
const ExtendedOHIFCornerstoneSRViewport = props => {
|
||||||
return (
|
return (
|
||||||
<OHIFCornerstoneViewport servicesManager={servicesManager} {...props} />
|
<OHIFCornerstoneViewport servicesManager={servicesManager} commandsManager={commandsManager} {...props} />
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import OHIF, { utils } from '@ohif/core';
|
|||||||
import {
|
import {
|
||||||
Notification,
|
Notification,
|
||||||
ViewportActionBar,
|
ViewportActionBar,
|
||||||
|
useCine,
|
||||||
useViewportGrid,
|
useViewportGrid,
|
||||||
useViewportDialog,
|
useViewportDialog,
|
||||||
} from '@ohif/ui';
|
} from '@ohif/ui';
|
||||||
@ -35,10 +36,6 @@ const BaseAnnotationTool = cornerstoneTools.importInternal(
|
|||||||
'base/BaseAnnotationTool'
|
'base/BaseAnnotationTool'
|
||||||
);
|
);
|
||||||
|
|
||||||
// const cine = viewportSpecificData.cine;
|
|
||||||
// isPlaying = cine.isPlaying === true;
|
|
||||||
// frameRate = cine.cineFrameRate || frameRate;
|
|
||||||
|
|
||||||
const { StackManager } = OHIF.utils;
|
const { StackManager } = OHIF.utils;
|
||||||
|
|
||||||
function TrackedCornerstoneViewport({
|
function TrackedCornerstoneViewport({
|
||||||
@ -47,6 +44,7 @@ function TrackedCornerstoneViewport({
|
|||||||
displaySet,
|
displaySet,
|
||||||
viewportIndex,
|
viewportIndex,
|
||||||
servicesManager,
|
servicesManager,
|
||||||
|
commandsManager
|
||||||
}) {
|
}) {
|
||||||
const {
|
const {
|
||||||
ToolBarService,
|
ToolBarService,
|
||||||
@ -54,12 +52,11 @@ function TrackedCornerstoneViewport({
|
|||||||
MeasurementService,
|
MeasurementService,
|
||||||
} = servicesManager.services;
|
} = servicesManager.services;
|
||||||
const [trackedMeasurements] = useTrackedMeasurements();
|
const [trackedMeasurements] = useTrackedMeasurements();
|
||||||
const [{ activeViewportIndex, viewports }] = useViewportGrid();
|
const [{ activeViewportIndex, viewports }, viewportGridService] = useViewportGrid();
|
||||||
// viewportIndex, onSubmit
|
const [{ isCineEnabled, cines }, cineService] = useCine();
|
||||||
const [viewportDialogState, viewportDialogApi] = useViewportDialog();
|
const [viewportDialogState, viewportDialogApi] = useViewportDialog();
|
||||||
const [viewportData, setViewportData] = useState(null);
|
const [viewportData, setViewportData] = useState(null);
|
||||||
const [element, setElement] = useState(null);
|
const [element, setElement] = useState(null);
|
||||||
|
|
||||||
const [isTracked, setIsTracked] = useState(false);
|
const [isTracked, setIsTracked] = useState(false);
|
||||||
const [trackedMeasurementId, setTrackedMeasurementId] = useState(null);
|
const [trackedMeasurementId, setTrackedMeasurementId] = useState(null);
|
||||||
|
|
||||||
@ -71,6 +68,10 @@ function TrackedCornerstoneViewport({
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
cineService.setCine({ id: viewportIndex });
|
||||||
|
}, [viewportIndex]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unsubcribeFromJumpToMeasurementEvents = _subscribeToJumpToMeasurementEvents(
|
const unsubcribeFromJumpToMeasurementEvents = _subscribeToJumpToMeasurementEvents(
|
||||||
MeasurementService,
|
MeasurementService,
|
||||||
@ -298,7 +299,9 @@ function TrackedCornerstoneViewport({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO -> disabled double click for now: onDoubleClick={_onDoubleClick}
|
const cine = cines[viewportIndex];
|
||||||
|
const isPlaying = cine && cine.isPlaying || false;
|
||||||
|
const frameRate = cine && cine.frameRate || 24;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -333,6 +336,14 @@ function TrackedCornerstoneViewport({
|
|||||||
scanner: ManufacturerModelName || '',
|
scanner: ManufacturerModelName || '',
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
showNavArrows={!isCineEnabled}
|
||||||
|
showCine={isCineEnabled}
|
||||||
|
cineProps={{
|
||||||
|
isPlaying,
|
||||||
|
onClose: () => commandsManager.runCommand('toggleCine'),
|
||||||
|
onPlayPauseChange: isPlaying => cineService.setCine({ id: activeViewportIndex, isPlaying }),
|
||||||
|
onFrameRateChange: frameRate => cineService.setCine({ id: activeViewportIndex, frameRate }),
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
{/* TODO: Viewport interface to accept stack or layers of content like this? */}
|
{/* TODO: Viewport interface to accept stack or layers of content like this? */}
|
||||||
<div className="relative flex flex-row w-full h-full overflow-hidden">
|
<div className="relative flex flex-row w-full h-full overflow-hidden">
|
||||||
@ -347,8 +358,8 @@ function TrackedCornerstoneViewport({
|
|||||||
// TODO: ViewportGrid Context?
|
// TODO: ViewportGrid Context?
|
||||||
isActive={true} // todo
|
isActive={true} // todo
|
||||||
isStackPrefetchEnabled={true} // todo
|
isStackPrefetchEnabled={true} // todo
|
||||||
isPlaying={false}
|
isPlaying={isPlaying}
|
||||||
frameRate={24}
|
frameRate={frameRate}
|
||||||
isOverlayVisible={true}
|
isOverlayVisible={true}
|
||||||
loadingIndicatorComponent={ViewportLoadingIndicator}
|
loadingIndicatorComponent={ViewportLoadingIndicator}
|
||||||
viewportOverlayComponent={props => {
|
viewportOverlayComponent={props => {
|
||||||
@ -408,6 +419,7 @@ function _getCornerstoneStack(displaySet, dataSource) {
|
|||||||
return stack;
|
return stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO -> disabled double click for now: onDoubleClick={_onDoubleClick}
|
||||||
function _onDoubleClick() {
|
function _onDoubleClick() {
|
||||||
const cancelActiveManipulatorsForElement = cornerstoneTools.getModule(
|
const cancelActiveManipulatorsForElement = cornerstoneTools.getModule(
|
||||||
'manipulatorState'
|
'manipulatorState'
|
||||||
|
|||||||
@ -189,14 +189,17 @@ export default [
|
|||||||
type: 'primary',
|
type: 'primary',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// TODO: Toggle
|
|
||||||
{
|
{
|
||||||
id: 'Cine',
|
id: 'Cine',
|
||||||
type: 'ohif.action',
|
type: 'ohif.toggle',
|
||||||
|
config: {
|
||||||
|
groupName: 'primaryTool',
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
|
isActive: false,
|
||||||
icon: 'old-youtube',
|
icon: 'old-youtube',
|
||||||
label: 'Cine',
|
label: 'Cine',
|
||||||
commandName: '',
|
commandName: 'toggleCine',
|
||||||
type: 'primary',
|
type: 'primary',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -22,6 +22,11 @@ export default [
|
|||||||
keys: ['v'],
|
keys: ['v'],
|
||||||
isEditable: true,
|
isEditable: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
commandName: 'toggleCine',
|
||||||
|
label: 'Cine',
|
||||||
|
keys: ['c'],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
commandName: 'invertViewport',
|
commandName: 'invertViewport',
|
||||||
label: 'Invert',
|
label: 'Invert',
|
||||||
|
|||||||
@ -24,6 +24,7 @@ import utils from './utils/';
|
|||||||
import defaults from './defaults';
|
import defaults from './defaults';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
CineService,
|
||||||
UIDialogService,
|
UIDialogService,
|
||||||
UIModalService,
|
UIModalService,
|
||||||
UINotificationService,
|
UINotificationService,
|
||||||
@ -31,7 +32,7 @@ import {
|
|||||||
//
|
//
|
||||||
DicomMetadataStore,
|
DicomMetadataStore,
|
||||||
DisplaySetService,
|
DisplaySetService,
|
||||||
ToolBarSerivce, // TODO: Typo
|
ToolBarService,
|
||||||
MeasurementService,
|
MeasurementService,
|
||||||
ViewportGridService,
|
ViewportGridService,
|
||||||
HangingProtocolService,
|
HangingProtocolService,
|
||||||
@ -73,13 +74,14 @@ const OHIF = {
|
|||||||
measurements,
|
measurements,
|
||||||
hangingProtocols,
|
hangingProtocols,
|
||||||
//
|
//
|
||||||
|
CineService,
|
||||||
UIDialogService,
|
UIDialogService,
|
||||||
UIModalService,
|
UIModalService,
|
||||||
UINotificationService,
|
UINotificationService,
|
||||||
UIViewportDialogService,
|
UIViewportDialogService,
|
||||||
DisplaySetService,
|
DisplaySetService,
|
||||||
MeasurementService,
|
MeasurementService,
|
||||||
ToolBarSerivce, // TODO: TYPO
|
ToolBarService, // TODO: TYPO
|
||||||
ViewportGridService,
|
ViewportGridService,
|
||||||
HangingProtocolService,
|
HangingProtocolService,
|
||||||
IWebApiDataSource,
|
IWebApiDataSource,
|
||||||
@ -117,13 +119,14 @@ export {
|
|||||||
measurements,
|
measurements,
|
||||||
hangingProtocols,
|
hangingProtocols,
|
||||||
//
|
//
|
||||||
|
CineService,
|
||||||
UIDialogService,
|
UIDialogService,
|
||||||
UIModalService,
|
UIModalService,
|
||||||
UINotificationService,
|
UINotificationService,
|
||||||
UIViewportDialogService,
|
UIViewportDialogService,
|
||||||
DisplaySetService,
|
DisplaySetService,
|
||||||
MeasurementService,
|
MeasurementService,
|
||||||
ToolBarSerivce,
|
ToolBarService,
|
||||||
ViewportGridService,
|
ViewportGridService,
|
||||||
HangingProtocolService,
|
HangingProtocolService,
|
||||||
IWebApiDataSource,
|
IWebApiDataSource,
|
||||||
|
|||||||
52
platform/core/src/services/CineService/CineService.js
Normal file
52
platform/core/src/services/CineService/CineService.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
const name = 'CineService';
|
||||||
|
|
||||||
|
const publicAPI = {
|
||||||
|
name,
|
||||||
|
getState: _getState,
|
||||||
|
setCine: _setCine,
|
||||||
|
setIsCineEnabled: _setIsCineEnabled,
|
||||||
|
setServiceImplementation,
|
||||||
|
};
|
||||||
|
|
||||||
|
const serviceImplementation = {
|
||||||
|
_getState: () => console.warn('getState() NOT IMPLEMENTED'),
|
||||||
|
_setCine: () =>
|
||||||
|
console.warn('setCine() NOT IMPLEMENTED'),
|
||||||
|
_setIsCineEnabled: () =>
|
||||||
|
console.warn('setIsCineEnabled() NOT IMPLEMENTED'),
|
||||||
|
};
|
||||||
|
|
||||||
|
function _getState() {
|
||||||
|
return serviceImplementation._getState();
|
||||||
|
}
|
||||||
|
|
||||||
|
function _setCine({ id, frameRate, isPlaying }) {
|
||||||
|
return serviceImplementation._setCine({ id, frameRate, isPlaying });
|
||||||
|
}
|
||||||
|
|
||||||
|
function _setIsCineEnabled(isCineEnabled) {
|
||||||
|
return serviceImplementation._setIsCineEnabled(isCineEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setServiceImplementation({
|
||||||
|
getState: getStateImplementation,
|
||||||
|
setCine: setCineImplementation,
|
||||||
|
setIsCineEnabled: setIsCineEnabledImplementation,
|
||||||
|
}) {
|
||||||
|
if (getStateImplementation) {
|
||||||
|
serviceImplementation._getState = getStateImplementation;
|
||||||
|
}
|
||||||
|
if (setCineImplementation) {
|
||||||
|
serviceImplementation._setCine = setCineImplementation;
|
||||||
|
}
|
||||||
|
if (setIsCineEnabledImplementation) {
|
||||||
|
serviceImplementation._setIsCineEnabled = setIsCineEnabledImplementation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name,
|
||||||
|
create: ({ configuration = {} }) => {
|
||||||
|
return publicAPI;
|
||||||
|
},
|
||||||
|
};
|
||||||
2
platform/core/src/services/CineService/index.js
Normal file
2
platform/core/src/services/CineService/index.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import CineService from './CineService';
|
||||||
|
export default CineService;
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
import merge from 'lodash.merge';
|
||||||
import pubSubServiceInterface from '../_shared/pubSubServiceInterface';
|
import pubSubServiceInterface from '../_shared/pubSubServiceInterface';
|
||||||
|
|
||||||
const EVENTS = {
|
const EVENTS = {
|
||||||
@ -36,9 +37,23 @@ export default class ToolBarService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setButton(id, button) {
|
||||||
|
if (this.buttons[id]) {
|
||||||
|
this.buttons[id] = merge(this.buttons[id], button);
|
||||||
|
this._broadcastChange(this.EVENTS.TOOL_BAR_MODIFIED, {
|
||||||
|
buttons: this.buttons,
|
||||||
|
button: this.buttons[id],
|
||||||
|
buttonSections: this.buttonSections
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setButtons(buttons) {
|
setButtons(buttons) {
|
||||||
this.buttons = buttons;
|
this.buttons = buttons;
|
||||||
this._broadcastChange(this.EVENTS.TOOL_BAR_MODIFIED, {});
|
this._broadcastChange(this.EVENTS.TOOL_BAR_MODIFIED, {
|
||||||
|
buttons: this.buttons,
|
||||||
|
buttonSections: this.buttonSections
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_buttonTypes() {
|
_buttonTypes() {
|
||||||
@ -61,7 +76,6 @@ export default class ToolBarService {
|
|||||||
return buttonTypes;
|
return buttonTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
createButtonSection(key, buttons) {
|
createButtonSection(key, buttons) {
|
||||||
// Maybe do this mapping at time of return, instead of time of create
|
// Maybe do this mapping at time of return, instead of time of create
|
||||||
// Props check important for validation here...
|
// Props check important for validation here...
|
||||||
|
|||||||
@ -50,6 +50,7 @@ function _set(state) {
|
|||||||
function _reset() {
|
function _reset() {
|
||||||
return serviceImplementation._reset({});
|
return serviceImplementation._reset({});
|
||||||
}
|
}
|
||||||
|
|
||||||
function _setCachedLayout({ numCols, numRows, viewports }) {
|
function _setCachedLayout({ numCols, numRows, viewports }) {
|
||||||
return serviceImplementation._setLayout({ numCols, numRows, viewports });
|
return serviceImplementation._setLayout({ numCols, numRows, viewports });
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,8 +6,9 @@ import UINotificationService from './UINotificationService';
|
|||||||
import UIViewportDialogService from './UIViewportDialogService';
|
import UIViewportDialogService from './UIViewportDialogService';
|
||||||
import DicomMetadataStore from './DicomMetadataStore';
|
import DicomMetadataStore from './DicomMetadataStore';
|
||||||
import DisplaySetService from './DisplaySetService';
|
import DisplaySetService from './DisplaySetService';
|
||||||
import ToolBarSerivce from './ToolBarService';
|
import ToolBarService from './ToolBarService';
|
||||||
import ViewportGridService from './ViewportGridService';
|
import ViewportGridService from './ViewportGridService';
|
||||||
|
import CineService from './CineService';
|
||||||
import HangingProtocolService from './HangingProtocolService';
|
import HangingProtocolService from './HangingProtocolService';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
@ -19,7 +20,8 @@ export {
|
|||||||
UIViewportDialogService,
|
UIViewportDialogService,
|
||||||
DicomMetadataStore,
|
DicomMetadataStore,
|
||||||
DisplaySetService,
|
DisplaySetService,
|
||||||
ToolBarSerivce,
|
ToolBarService,
|
||||||
ViewportGridService,
|
ViewportGridService,
|
||||||
HangingProtocolService,
|
HangingProtocolService,
|
||||||
|
CineService
|
||||||
};
|
};
|
||||||
|
|||||||
@ -4,6 +4,8 @@ export { utils };
|
|||||||
|
|
||||||
/** CONTEXT/HOOKS */
|
/** CONTEXT/HOOKS */
|
||||||
export {
|
export {
|
||||||
|
useCine,
|
||||||
|
CineProvider,
|
||||||
DialogProvider,
|
DialogProvider,
|
||||||
useDialog,
|
useDialog,
|
||||||
withDialog,
|
withDialog,
|
||||||
@ -38,6 +40,7 @@ export {
|
|||||||
Button,
|
Button,
|
||||||
ButtonGroup,
|
ButtonGroup,
|
||||||
ContextMenu,
|
ContextMenu,
|
||||||
|
CinePlayer,
|
||||||
DateRange,
|
DateRange,
|
||||||
Dialog,
|
Dialog,
|
||||||
Dropdown,
|
Dropdown,
|
||||||
|
|||||||
9
platform/ui/src/assets/icons/old-play.svg
Normal file
9
platform/ui/src/assets/icons/old-play.svg
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 448 512"
|
||||||
|
aria-labelledby="title"
|
||||||
|
fill="currentColor"
|
||||||
|
>
|
||||||
|
<title id="title">Play</title>
|
||||||
|
<path d="M424.4 214.7L72.4 6.6C43.8-10.3 0 6.1 0 47.9V464c0 37.5 40.7 60.1 72.4 41.3l352-208c31.4-18.5 31.5-64.1 0-82.6z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 283 B |
9
platform/ui/src/assets/icons/old-stop.svg
Normal file
9
platform/ui/src/assets/icons/old-stop.svg
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 448 512"
|
||||||
|
aria-labelledby="title"
|
||||||
|
fill="currentColor"
|
||||||
|
>
|
||||||
|
<title id="title">Stop</title>
|
||||||
|
<path d="M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 277 B |
97
platform/ui/src/components/CinePlayer/CinePlayer.jsx
Normal file
97
platform/ui/src/components/CinePlayer/CinePlayer.jsx
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import debounce from 'lodash.debounce';
|
||||||
|
import { IconButton, Icon } from '@ohif/ui';
|
||||||
|
|
||||||
|
import './CinePlayerCustomInputRange.css';
|
||||||
|
|
||||||
|
const CinePlayer = ({
|
||||||
|
isPlaying,
|
||||||
|
minFrameRate,
|
||||||
|
maxFrameRate,
|
||||||
|
stepFrameRate,
|
||||||
|
frameRate: defaultFrameRate,
|
||||||
|
onFrameRateChange,
|
||||||
|
onPlayPauseChange,
|
||||||
|
onClose
|
||||||
|
}) => {
|
||||||
|
const [frameRate, setFrameRate] = useState(defaultFrameRate);
|
||||||
|
const debouncedSetFrameRate = debounce(onFrameRateChange, 300);
|
||||||
|
|
||||||
|
const onFrameRateChangeHandler = ({ target }) => {
|
||||||
|
const frameRate = parseFloat(target.value);
|
||||||
|
debouncedSetFrameRate(frameRate);
|
||||||
|
setFrameRate(frameRate);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onPlayPauseChangeHandler = () => onPlayPauseChange(!isPlaying);
|
||||||
|
|
||||||
|
const action = {
|
||||||
|
false: { icon: 'old-play' },
|
||||||
|
true: { icon: 'old-stop' }
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="CinePlayer flex flex-row h-10 items-center justify-center border border-primary-light rounded-full">
|
||||||
|
<IconButton
|
||||||
|
variant="text"
|
||||||
|
color="inherit"
|
||||||
|
size="initial"
|
||||||
|
className="mr-3 ml-4 text-primary-active"
|
||||||
|
onClick={onPlayPauseChangeHandler}
|
||||||
|
>
|
||||||
|
<Icon width="15px" height="15px" name={action[isPlaying].icon} />
|
||||||
|
</IconButton>
|
||||||
|
<div className="flex flex-col h-full justify-center pt-2 mr-3 pl-1 pr-1">
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
name="frameRate"
|
||||||
|
min={minFrameRate}
|
||||||
|
max={maxFrameRate}
|
||||||
|
step={stepFrameRate}
|
||||||
|
value={frameRate}
|
||||||
|
onChange={onFrameRateChangeHandler}
|
||||||
|
/>
|
||||||
|
<p className="mt-1 text-sm text-primary-light">{`${frameRate.toFixed(1)} fps`}</p>
|
||||||
|
</div>
|
||||||
|
<IconButton
|
||||||
|
color="inherit"
|
||||||
|
size="initial"
|
||||||
|
className="mr-3 text-primary-active border border-primary-active rounded-full"
|
||||||
|
onClick={onClose}
|
||||||
|
>
|
||||||
|
<Icon name="close" width="15px" height="15px" />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const noop = () => { };
|
||||||
|
|
||||||
|
CinePlayer.defaultProps = {
|
||||||
|
isPlaying: false,
|
||||||
|
minFrameRate: 1,
|
||||||
|
maxFrameRate: 90,
|
||||||
|
stepFrameRate: 1,
|
||||||
|
frameRate: 24,
|
||||||
|
onPlayPauseChange: noop,
|
||||||
|
onFrameRateChange: noop,
|
||||||
|
onClose: noop
|
||||||
|
};
|
||||||
|
|
||||||
|
CinePlayer.propTypes = {
|
||||||
|
/** Minimum value for range slider */
|
||||||
|
minFrameRate: PropTypes.number.isRequired,
|
||||||
|
/** Maximum value for range slider */
|
||||||
|
maxFrameRate: PropTypes.number.isRequired,
|
||||||
|
/** Increment range slider can "step" in either direction */
|
||||||
|
stepFrameRate: PropTypes.number.isRequired,
|
||||||
|
frameRate: PropTypes.number.isRequired,
|
||||||
|
/** 'true' if playing, 'false' if paused */
|
||||||
|
isPlaying: PropTypes.bool.isRequired,
|
||||||
|
onPlayPauseChange: PropTypes.func,
|
||||||
|
onFrameRateChange: PropTypes.func,
|
||||||
|
onClose: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CinePlayer;
|
||||||
42
platform/ui/src/components/CinePlayer/CinePlayer.mdx
Normal file
42
platform/ui/src/components/CinePlayer/CinePlayer.mdx
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
name: CinePlayer
|
||||||
|
menu: General
|
||||||
|
route: components/CinePlayer
|
||||||
|
---
|
||||||
|
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { Playground, Props } from 'docz';
|
||||||
|
import { CinePlayer } from '@ohif/ui';
|
||||||
|
|
||||||
|
# Input Text
|
||||||
|
|
||||||
|
## Import
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { CinePlayer } from '@ohif/ui';
|
||||||
|
```
|
||||||
|
|
||||||
|
## Basic usage
|
||||||
|
|
||||||
|
<Playground>
|
||||||
|
{() => {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col flex-1 p-4 items-center">
|
||||||
|
<div className="w-56">
|
||||||
|
<CinePlayer
|
||||||
|
cineMinFrameRate={1}
|
||||||
|
cineMaxFrameRate={90}
|
||||||
|
cineStepFrameRate={1}
|
||||||
|
cineFrameRate={24}
|
||||||
|
isPlaying={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
|
||||||
|
</Playground>
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
<Props of={CinePlayer} />
|
||||||
@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* This is a custom input style scoped specifically to CinePlayer
|
||||||
|
* written in plain CSS with color variables from tailwind
|
||||||
|
* to avoid complex compatibility configuration.
|
||||||
|
*/
|
||||||
|
.CinePlayer input[type=range] {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
background: transparent;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CinePlayer input[type=range]:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CinePlayer input[type=range]::-webkit-slider-runnable-track {
|
||||||
|
width: 100%;
|
||||||
|
height: 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
animate: 0.2s;
|
||||||
|
box-shadow: 0px 0px 0px #000000;
|
||||||
|
@apply bg-primary-light;
|
||||||
|
border-radius: 5px;
|
||||||
|
border: 0px solid #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CinePlayer input[type=range]::-webkit-slider-thumb {
|
||||||
|
box-shadow: 0px 0px 0px #000000;
|
||||||
|
border: 4px solid #000000;
|
||||||
|
height: 18px;
|
||||||
|
width: 17px;
|
||||||
|
border-radius: 50px;
|
||||||
|
@apply bg-primary-light;
|
||||||
|
cursor: pointer;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
margin-top: -9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CinePlayer input[type=range]:focus::-webkit-slider-runnable-track {
|
||||||
|
@apply bg-primary-light;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CinePlayer input[type=range]::-moz-range-track {
|
||||||
|
width: 100%;
|
||||||
|
height: 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
animate: 0.2s;
|
||||||
|
box-shadow: 0px 0px 0px #000000;
|
||||||
|
@apply bg-primary-light;
|
||||||
|
border-radius: 5px;
|
||||||
|
border: 0px solid #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CinePlayer input[type=range]::-moz-range-thumb {
|
||||||
|
box-shadow: 0px 0px 0px #000000;
|
||||||
|
border: 4px solid #000000;
|
||||||
|
height: 18px;
|
||||||
|
width: 17px;
|
||||||
|
border-radius: 50px;
|
||||||
|
@apply bg-primary-light;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CinePlayer input[type=range]::-ms-track {
|
||||||
|
width: 100%;
|
||||||
|
height: 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
animate: 0.2s;
|
||||||
|
background: transparent;
|
||||||
|
border-color: transparent;
|
||||||
|
color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CinePlayer input[type=range]::-ms-fill-lower {
|
||||||
|
@apply bg-primary-light;
|
||||||
|
border: 0px solid #000000;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0px 0px 0px #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CinePlayer input[type=range]::-ms-fill-upper {
|
||||||
|
@apply bg-primary-light;
|
||||||
|
border: 0px solid #000000;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0px 0px 0px #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CinePlayer input[type=range]::-ms-thumb {
|
||||||
|
margin-top: 1px;
|
||||||
|
box-shadow: 0px 0px 0px #000000;
|
||||||
|
border: 4px solid #000000;
|
||||||
|
height: 18px;
|
||||||
|
width: 17px;
|
||||||
|
border-radius: 50px;
|
||||||
|
@apply bg-primary-light;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CinePlayer input[type=range]:focus::-ms-fill-lower {
|
||||||
|
@apply bg-primary-light;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CinePlayer input[type=range]:focus::-ms-fill-upper {
|
||||||
|
@apply bg-primary-light;
|
||||||
|
}
|
||||||
2
platform/ui/src/components/CinePlayer/index.js
Normal file
2
platform/ui/src/components/CinePlayer/index.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import CinePlayer from './CinePlayer';
|
||||||
|
export default CinePlayer;
|
||||||
@ -60,6 +60,8 @@ import oldAngleLeft from './../../assets/icons/old-angle-left.svg';
|
|||||||
import oldReset from './../../assets/icons/old-reset.svg';
|
import oldReset from './../../assets/icons/old-reset.svg';
|
||||||
import oldCircleO from './../../assets/icons/old-circle-o.svg';
|
import oldCircleO from './../../assets/icons/old-circle-o.svg';
|
||||||
import oldTrash from './../../assets/icons/old-trash.svg';
|
import oldTrash from './../../assets/icons/old-trash.svg';
|
||||||
|
import oldPlay from './../../assets/icons/old-play.svg';
|
||||||
|
import oldStop from './../../assets/icons/old-stop.svg';
|
||||||
|
|
||||||
const ICONS = {
|
const ICONS = {
|
||||||
'arrow-down': arrowDown,
|
'arrow-down': arrowDown,
|
||||||
@ -121,6 +123,8 @@ const ICONS = {
|
|||||||
'old-reset': oldReset,
|
'old-reset': oldReset,
|
||||||
'old-circle-o': oldCircleO,
|
'old-circle-o': oldCircleO,
|
||||||
'old-trash': oldTrash,
|
'old-trash': oldTrash,
|
||||||
|
'old-play': oldPlay,
|
||||||
|
'old-stop': oldStop,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import React, { useState, useRef, useEffect } from 'react';
|
import React, { useState, useRef, useEffect } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { Icon, ButtonGroup, Button, Tooltip } from '../';
|
import { Icon, ButtonGroup, Button, Tooltip, CinePlayer } from '../';
|
||||||
import useOnClickOutside from '../../utils/useOnClickOutside';
|
import useOnClickOutside from '../../utils/useOnClickOutside';
|
||||||
|
|
||||||
const classes = {
|
const classes = {
|
||||||
@ -14,6 +14,8 @@ const classes = {
|
|||||||
const ViewportActionBar = ({
|
const ViewportActionBar = ({
|
||||||
studyData,
|
studyData,
|
||||||
showNavArrows,
|
showNavArrows,
|
||||||
|
showCine,
|
||||||
|
cineProps,
|
||||||
showPatientInfo: patientInfoVisibility,
|
showPatientInfo: patientInfoVisibility,
|
||||||
onSeriesChange,
|
onSeriesChange,
|
||||||
onDoubleClick,
|
onDoubleClick,
|
||||||
@ -137,10 +139,10 @@ const ViewportActionBar = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="flex items-center p-2 border-b select-none border-primary-light min-h-12"
|
className="flex flex-wrap items-center p-2 border-b select-none border-primary-light -mt-2"
|
||||||
onDoubleClick={onDoubleClick}
|
onDoubleClick={onDoubleClick}
|
||||||
>
|
>
|
||||||
<div className="flex flex-grow">
|
<div className="flex flex-grow min-w-48 flex-1 mt-2">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
{renderIconStatus()}
|
{renderIconStatus()}
|
||||||
<span className="ml-2 text-white text-large">{label}</span>
|
<span className="ml-2 text-white text-large">{label}</span>
|
||||||
@ -166,8 +168,8 @@ const ViewportActionBar = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{showNavArrows && (
|
{showNavArrows && !showCine && (
|
||||||
<div className="ml-2">
|
<div className="mt-2">
|
||||||
<ButtonGroup>
|
<ButtonGroup>
|
||||||
<Button
|
<Button
|
||||||
size="initial"
|
size="initial"
|
||||||
@ -186,7 +188,12 @@ const ViewportActionBar = ({
|
|||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="flex ml-4 mr-2" onClick={onPatientInfoClick}>
|
{showCine && !showNavArrows && (
|
||||||
|
<div className="mt-2 min-w-48 max-w-48 mr-auto">
|
||||||
|
<CinePlayer {...cineProps} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="flex h-8 ml-4 mr-2 mt-2" onClick={onPatientInfoClick}>
|
||||||
<PatientInfo
|
<PatientInfo
|
||||||
showPatientInfoRef={showPatientInfoRef}
|
showPatientInfoRef={showPatientInfoRef}
|
||||||
isOpen={showPatientInfo}
|
isOpen={showPatientInfo}
|
||||||
@ -206,6 +213,8 @@ const ViewportActionBar = ({
|
|||||||
ViewportActionBar.propTypes = {
|
ViewportActionBar.propTypes = {
|
||||||
onSeriesChange: PropTypes.func.isRequired,
|
onSeriesChange: PropTypes.func.isRequired,
|
||||||
showNavArrows: PropTypes.bool,
|
showNavArrows: PropTypes.bool,
|
||||||
|
showCine: PropTypes.bool,
|
||||||
|
cineProps: PropTypes.object,
|
||||||
showPatientInfo: PropTypes.bool,
|
showPatientInfo: PropTypes.bool,
|
||||||
studyData: PropTypes.shape({
|
studyData: PropTypes.shape({
|
||||||
label: PropTypes.string.isRequired,
|
label: PropTypes.string.isRequired,
|
||||||
@ -228,6 +237,8 @@ ViewportActionBar.propTypes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
ViewportActionBar.defaultProps = {
|
ViewportActionBar.defaultProps = {
|
||||||
|
cineProps: {},
|
||||||
|
showCine: false,
|
||||||
showNavArrows: true,
|
showNavArrows: true,
|
||||||
showPatientInfo: false,
|
showPatientInfo: false,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import AboutModal from './AboutModal';
|
|||||||
import Button from './Button';
|
import Button from './Button';
|
||||||
import ButtonGroup from './ButtonGroup';
|
import ButtonGroup from './ButtonGroup';
|
||||||
import ContextMenu from './ContextMenu';
|
import ContextMenu from './ContextMenu';
|
||||||
|
import CinePlayer from './CinePlayer';
|
||||||
import DateRange from './DateRange';
|
import DateRange from './DateRange';
|
||||||
import Dialog from './Dialog';
|
import Dialog from './Dialog';
|
||||||
import Dropdown from './Dropdown';
|
import Dropdown from './Dropdown';
|
||||||
@ -68,6 +69,7 @@ export {
|
|||||||
Button,
|
Button,
|
||||||
ButtonGroup,
|
ButtonGroup,
|
||||||
ContextMenu,
|
ContextMenu,
|
||||||
|
CinePlayer,
|
||||||
DateRange,
|
DateRange,
|
||||||
Dialog,
|
Dialog,
|
||||||
Dropdown,
|
Dropdown,
|
||||||
|
|||||||
105
platform/ui/src/contextProviders/CineProvider.jsx
Normal file
105
platform/ui/src/contextProviders/CineProvider.jsx
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import React, {
|
||||||
|
createContext,
|
||||||
|
useCallback,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
useReducer,
|
||||||
|
} from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
const DEFAULT_STATE = {
|
||||||
|
isCineEnabled: false,
|
||||||
|
cines: {
|
||||||
|
/*
|
||||||
|
* 1: { isPlaying: false, frameRate: 24 };
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const DEFAULT_CINE = { isPlaying: false, frameRate: 24 };
|
||||||
|
|
||||||
|
export const CineContext = createContext(DEFAULT_STATE);
|
||||||
|
|
||||||
|
export default function CineProvider({ children, service }) {
|
||||||
|
const reducer = (state, action) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case 'SET_CINE': {
|
||||||
|
const { id, frameRate, isPlaying = undefined } = action.payload;
|
||||||
|
const cines = state.cines;
|
||||||
|
|
||||||
|
if (!cines[id]) cines[id] = { id, ...DEFAULT_CINE };
|
||||||
|
cines[id].frameRate = frameRate || cines[id].frameRate;
|
||||||
|
cines[id].isPlaying = isPlaying !== undefined ? isPlaying : cines[id].isPlaying;
|
||||||
|
|
||||||
|
return { ...state, ...{ cines } };
|
||||||
|
}
|
||||||
|
case 'SET_IS_CINE_ENABLED': {
|
||||||
|
return { ...state, ...{ isCineEnabled: action.payload } };
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return action.payload;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const [state, dispatch] = useReducer(
|
||||||
|
reducer,
|
||||||
|
DEFAULT_STATE
|
||||||
|
);
|
||||||
|
|
||||||
|
const getState = useCallback(() => state, [state]);
|
||||||
|
|
||||||
|
const setIsCineEnabled = useCallback(
|
||||||
|
isCineEnabled => dispatch({ type: 'SET_IS_CINE_ENABLED', payload: isCineEnabled }),
|
||||||
|
[dispatch]
|
||||||
|
);
|
||||||
|
|
||||||
|
const setCine = useCallback(
|
||||||
|
({ id, frameRate, isPlaying }) =>
|
||||||
|
dispatch({
|
||||||
|
type: 'SET_CINE',
|
||||||
|
payload: {
|
||||||
|
id,
|
||||||
|
frameRate,
|
||||||
|
isPlaying,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
[dispatch]
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the implementation of a modal service that can be used by extensions.
|
||||||
|
*
|
||||||
|
* @returns void
|
||||||
|
*/
|
||||||
|
useEffect(() => {
|
||||||
|
if (service) {
|
||||||
|
service.setServiceImplementation({ getState, setIsCineEnabled, setCine });
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
getState,
|
||||||
|
service,
|
||||||
|
setCine,
|
||||||
|
setIsCineEnabled,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const api = {
|
||||||
|
getState,
|
||||||
|
setCine,
|
||||||
|
setIsCineEnabled,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CineContext.Provider value={[state, api]}>
|
||||||
|
{children}
|
||||||
|
</CineContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
CineProvider.propTypes = {
|
||||||
|
children: PropTypes.any,
|
||||||
|
service: PropTypes.shape({
|
||||||
|
setServiceImplementation: PropTypes.func,
|
||||||
|
}).isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useCine = () => useContext(CineContext);
|
||||||
@ -11,9 +11,11 @@ const DEFAULT_STATE = {
|
|||||||
numRows: 1,
|
numRows: 1,
|
||||||
numCols: 1,
|
numCols: 1,
|
||||||
viewports: [
|
viewports: [
|
||||||
// {
|
/*
|
||||||
// displaySetInstanceUID: string,
|
* {
|
||||||
// }
|
* displaySetInstanceUID: string,
|
||||||
|
* }
|
||||||
|
*/
|
||||||
],
|
],
|
||||||
activeViewportIndex: 0,
|
activeViewportIndex: 0,
|
||||||
};
|
};
|
||||||
@ -59,7 +61,9 @@ export function ViewportGridProvider({ children, service }) {
|
|||||||
numCols: 1,
|
numCols: 1,
|
||||||
numRows: 1,
|
numRows: 1,
|
||||||
activeViewportIndex: 0,
|
activeViewportIndex: 0,
|
||||||
viewports: [{ displaySetInstanceUID: null }],
|
viewports: [{
|
||||||
|
displaySetInstanceUID: null,
|
||||||
|
}],
|
||||||
cachedLayout: null,
|
cachedLayout: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -84,13 +88,13 @@ export function ViewportGridProvider({ children, service }) {
|
|||||||
DEFAULT_STATE
|
DEFAULT_STATE
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log('viewportGridState',viewportGridState)
|
|
||||||
|
|
||||||
const getState = useCallback(() => viewportGridState, [viewportGridState]);
|
const getState = useCallback(() => viewportGridState, [viewportGridState]);
|
||||||
|
|
||||||
const setActiveViewportIndex = useCallback(
|
const setActiveViewportIndex = useCallback(
|
||||||
index => dispatch({ type: 'SET_ACTIVE_VIEWPORT_INDEX', payload: index }),
|
index => dispatch({ type: 'SET_ACTIVE_VIEWPORT_INDEX', payload: index }),
|
||||||
[dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
|
|
||||||
const setDisplaysetForViewport = useCallback(
|
const setDisplaysetForViewport = useCallback(
|
||||||
({ viewportIndex, displaySetInstanceUID }) =>
|
({ viewportIndex, displaySetInstanceUID }) =>
|
||||||
dispatch({
|
dispatch({
|
||||||
@ -123,6 +127,7 @@ export function ViewportGridProvider({ children, service }) {
|
|||||||
}),
|
}),
|
||||||
[dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
|
|
||||||
const setCachedLayout = useCallback(
|
const setCachedLayout = useCallback(
|
||||||
payload =>
|
payload =>
|
||||||
dispatch({
|
dispatch({
|
||||||
|
|||||||
@ -19,6 +19,12 @@ export {
|
|||||||
useImageViewer,
|
useImageViewer,
|
||||||
} from './ImageViewerProvider';
|
} from './ImageViewerProvider';
|
||||||
|
|
||||||
|
export {
|
||||||
|
CineContext,
|
||||||
|
default as CineProvider,
|
||||||
|
useCine,
|
||||||
|
} from './CineProvider';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
default as SnackbarProvider,
|
default as SnackbarProvider,
|
||||||
useSnackbar,
|
useSnackbar,
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import {
|
|||||||
ViewportDialogProvider,
|
ViewportDialogProvider,
|
||||||
ViewportGridProvider,
|
ViewportGridProvider,
|
||||||
HangingProtocolProvider,
|
HangingProtocolProvider,
|
||||||
|
CineProvider,
|
||||||
} from '@ohif/ui';
|
} from '@ohif/ui';
|
||||||
// Viewer Project
|
// Viewer Project
|
||||||
// TODO: Should this influence study list?
|
// TODO: Should this influence study list?
|
||||||
@ -60,6 +61,7 @@ function App({ config, defaultExtensions }) {
|
|||||||
UIViewportDialogService,
|
UIViewportDialogService,
|
||||||
ViewportGridService, // TODO: Should this be a "UI" Service?
|
ViewportGridService, // TODO: Should this be a "UI" Service?
|
||||||
HangingProtocolService,
|
HangingProtocolService,
|
||||||
|
CineService
|
||||||
} = servicesManager.services;
|
} = servicesManager.services;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -70,6 +72,7 @@ function App({ config, defaultExtensions }) {
|
|||||||
<ViewportGridProvider service={ViewportGridService}>
|
<ViewportGridProvider service={ViewportGridService}>
|
||||||
<HangingProtocolProvider service={HangingProtocolService}>
|
<HangingProtocolProvider service={HangingProtocolService}>
|
||||||
<ViewportDialogProvider service={UIViewportDialogService}>
|
<ViewportDialogProvider service={UIViewportDialogService}>
|
||||||
|
<CineProvider service={CineService}>
|
||||||
<SnackbarProvider service={UINotificationService}>
|
<SnackbarProvider service={UINotificationService}>
|
||||||
<DialogProvider service={UIDialogService}>
|
<DialogProvider service={UIDialogService}>
|
||||||
<ModalProvider modal={Modal} service={UIModalService}>
|
<ModalProvider modal={Modal} service={UIModalService}>
|
||||||
@ -77,6 +80,7 @@ function App({ config, defaultExtensions }) {
|
|||||||
</ModalProvider>
|
</ModalProvider>
|
||||||
</DialogProvider>
|
</DialogProvider>
|
||||||
</SnackbarProvider>
|
</SnackbarProvider>
|
||||||
|
</CineProvider>
|
||||||
</ViewportDialogProvider>
|
</ViewportDialogProvider>
|
||||||
</HangingProtocolProvider>
|
</HangingProtocolProvider>
|
||||||
</ViewportGridProvider>
|
</ViewportGridProvider>
|
||||||
|
|||||||
@ -9,9 +9,10 @@ import {
|
|||||||
UIViewportDialogService,
|
UIViewportDialogService,
|
||||||
MeasurementService,
|
MeasurementService,
|
||||||
DisplaySetService,
|
DisplaySetService,
|
||||||
ToolBarSerivce,
|
ToolBarService,
|
||||||
ViewportGridService,
|
ViewportGridService,
|
||||||
HangingProtocolService
|
HangingProtocolService,
|
||||||
|
CineService
|
||||||
// utils,
|
// utils,
|
||||||
// redux as reduxOHIF,
|
// redux as reduxOHIF,
|
||||||
} from '@ohif/core';
|
} from '@ohif/core';
|
||||||
@ -54,9 +55,10 @@ function appInit(appConfigOrFunc, defaultExtensions) {
|
|||||||
UIViewportDialogService,
|
UIViewportDialogService,
|
||||||
MeasurementService,
|
MeasurementService,
|
||||||
DisplaySetService,
|
DisplaySetService,
|
||||||
ToolBarSerivce,
|
ToolBarService,
|
||||||
ViewportGridService,
|
ViewportGridService,
|
||||||
HangingProtocolService
|
HangingProtocolService,
|
||||||
|
CineService
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -1,12 +1,10 @@
|
|||||||
/**
|
/**
|
||||||
* CSS Grid Reference: http://grid.malven.co/
|
* CSS Grid Reference: http://grid.malven.co/
|
||||||
*/
|
*/
|
||||||
import React, { useEffect, useCallback, useState } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { ViewportGrid, ViewportPane, useViewportGrid } from '@ohif/ui';
|
import { ViewportGrid, ViewportPane, useViewportGrid } from '@ohif/ui';
|
||||||
import EmptyViewport from './EmptyViewport';
|
import EmptyViewport from './EmptyViewport';
|
||||||
import { classes } from '@ohif/core';
|
|
||||||
const { ImageSet } = classes;
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
function ViewerViewportGrid(props) {
|
function ViewerViewportGrid(props) {
|
||||||
@ -194,7 +192,7 @@ function ViewerViewportGrid(props) {
|
|||||||
isActive={isActive}
|
isActive={isActive}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={classNames('h-full w-full flex flex-col align-center', {
|
className={classNames('h-full w-full flex flex-col', {
|
||||||
'pointer-events-none': !isActive,
|
'pointer-events-none': !isActive,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user