Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
adc8d9ea0f | ||
|
|
f65639c2b0 | ||
|
|
cdd75bda2a | ||
|
|
d92fe44d3c | ||
|
|
d90c2ac024 | ||
|
|
7ce5e1d2d3 | ||
|
|
a3bf47f9bf | ||
|
|
81f79a26ad | ||
|
|
60b4b5d7cf | ||
|
|
69c54d4f49 | ||
|
|
0f8868a232 | ||
|
|
1bbfee4087 |
No files matched your search
@@ -2,7 +2,7 @@
|
||||
name: "\U0001F41B Bug report"
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: 'Community: Report :bug:'
|
||||
labels: 'Community: Report :bug:, Awaiting Reproduction, Triage :white_flag:'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name: "\U0001F680 Feature request"
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: 'Community: Request :hand:'
|
||||
labels: 'Community: Request :hand:, Triage :white_flag:'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name: "\U0001F917 Support Question"
|
||||
about: "I have a question \U0001F4AC"
|
||||
title: ''
|
||||
labels: 'Community: Question :question:'
|
||||
labels: 'Community: Question :question:, Triage :white_flag:'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ exemptLabels:
|
||||
- 'Task: Refactor 🛠'
|
||||
- 'Task: Tests :microscope:'
|
||||
- 'PR: Awaiting Review 👀'
|
||||
- 'Triage 🏳️'
|
||||
- 'Triage :white_flag:'
|
||||
- 'Announcement 🎉'
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: 'Stale :baguette_bread:'
|
||||
|
||||
@@ -67,10 +67,27 @@ While we don't yet have a long-term or quarterly road map, we do regularly add i
|
||||
|
||||
## Contributions (Pull Requests)
|
||||
|
||||
..
|
||||
Incoming Pull Requests (PRs) are triaged using the following labels. Code review is performed on all PRs where the bug fix or added functionality is deemed appropriate:
|
||||
|
||||
- PR: Bug Fix - Describes a PR filed to address a Bug.
|
||||
- PR: Draft - Describes a PR filed to gather early feedback from the core team, but which is not intended for merging in the short term.
|
||||
- Awaiting Response - Describes a PR where the core team is waiting for additional information from the author. This may occur before or after code review.
|
||||
- Awaiting Review - Describes a PR where the core team has not yet performed a code review.
|
||||
- Awaiting Revisions - Following code review, this label is applied until the author has made sufficient changes.
|
||||
|
||||
We rely on GitHub Checks and integrations with third party services to evaluate changes in code quality and test coverage. Tests must pass before a PR can be merged to master, and code quality and test coverage must not changed by a significant margin. For some repositories, visual screenshot-based tests are also included, and video recordings of end-to-end tests are stored for later review.
|
||||
|
||||
[You can read more about our continous integration efforts here](/continous-integration.md)
|
||||
|
||||
## Releases
|
||||
|
||||
Releases are made automatically based on the type of commits which have been merged (major.minor.patch). Releases are automatically pushed to NPM. Release notes are automatically generated. Users can subscribe to GitHub and NPM releases.
|
||||
|
||||
Important announcements are made on GitHub, tagged as Announcement, and pinned so that they remain at the top of the Issue page.
|
||||
|
||||
The Core team occasionally performs full manual testing to begin the process of releasing a Stable version. Once testing is complete, the known issues are addressed and a Stable version
|
||||
|
||||
|
||||
<!--
|
||||
LINKS
|
||||
-->
|
||||
|
||||
@@ -3,6 +3,17 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.6.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@1.5.1...@ohif/extension-cornerstone@1.6.0) (2019-11-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* New dialog service ([#1202](https://github.com/OHIF/Viewers/issues/1202)) ([f65639c](https://github.com/OHIF/Viewers/commit/f65639c2b0dab01decd20cab2cef4263cb4fab37))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.5.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@1.5.0...@ohif/extension-cornerstone@1.5.1) (2019-11-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-cornerstone",
|
||||
"version": "1.5.1",
|
||||
"version": "1.6.0",
|
||||
"description": "OHIF extension for Cornerstone",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -23,6 +23,17 @@ const TOOLBAR_BUTTON_TYPES = {
|
||||
BUILT_IN: 'builtIn',
|
||||
};
|
||||
|
||||
const TOOLBAR_BUTTON_BEHAVIORS = {
|
||||
CINE: 'CINE',
|
||||
DOWNLOAD_SCREEN_SHOT: 'DOWNLOAD_SCREEN_SHOT',
|
||||
};
|
||||
|
||||
/* TODO: Export enums through a extension manager. */
|
||||
const enums = {
|
||||
TOOLBAR_BUTTON_TYPES,
|
||||
TOOLBAR_BUTTON_BEHAVIORS,
|
||||
};
|
||||
|
||||
const definitions = [
|
||||
{
|
||||
id: 'StackScroll',
|
||||
@@ -102,7 +113,7 @@ const definitions = [
|
||||
//
|
||||
type: TOOLBAR_BUTTON_TYPES.BUILT_IN,
|
||||
options: {
|
||||
behavior: 'CINE',
|
||||
behavior: TOOLBAR_BUTTON_BEHAVIORS.CINE,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -220,7 +231,8 @@ const definitions = [
|
||||
//
|
||||
type: TOOLBAR_BUTTON_TYPES.BUILT_IN,
|
||||
options: {
|
||||
behavior: 'DOWNLOAD_SCREEN_SHOT',
|
||||
behavior: TOOLBAR_BUTTON_BEHAVIORS.DOWNLOAD_SCREEN_SHOT,
|
||||
togglable: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.53.11](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@0.53.10...@ohif/extension-vtk@0.53.11) (2019-11-19)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.53.10](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@0.53.9...@ohif/extension-vtk@0.53.10) (2019-11-19)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-vtk",
|
||||
"version": "0.53.10",
|
||||
"version": "0.53.11",
|
||||
"description": "OHIF extension for VTK.js",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -52,8 +52,8 @@
|
||||
"react-vtkjs-viewport": "^0.3.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ohif/core": "^1.11.0",
|
||||
"@ohif/ui": "^0.63.0",
|
||||
"@ohif/core": "^1.12.0",
|
||||
"@ohif/ui": "^0.64.0",
|
||||
"cornerstone-tools": "^4.0.9",
|
||||
"cornerstone-wado-image-loader": "^3.0.0",
|
||||
"dcmjs": "^0.6.1",
|
||||
|
||||
@@ -3,6 +3,17 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.12.0](https://github.com/OHIF/Viewers/compare/@ohif/core@1.11.0...@ohif/core@1.12.0) (2019-11-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* New dialog service ([#1202](https://github.com/OHIF/Viewers/issues/1202)) ([f65639c](https://github.com/OHIF/Viewers/commit/f65639c2b0dab01decd20cab2cef4263cb4fab37))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [1.11.0](https://github.com/OHIF/Viewers/compare/@ohif/core@1.10.0...@ohif/core@1.11.0) (2019-11-19)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/core",
|
||||
"version": "1.11.0",
|
||||
"version": "1.12.0",
|
||||
"description": "Generic business logic for web-based medical imaging applications",
|
||||
"author": "OHIF Core Team",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -19,7 +19,11 @@ import ui from './ui';
|
||||
import user from './user.js';
|
||||
import utils from './utils/';
|
||||
|
||||
import { createUINotificationService, createUIModalService } from './services';
|
||||
import {
|
||||
createUINotificationService,
|
||||
createUIModalService,
|
||||
createUIDialogService,
|
||||
} from './services';
|
||||
|
||||
const OHIF = {
|
||||
MODULE_TYPES,
|
||||
@@ -48,6 +52,7 @@ const OHIF = {
|
||||
//
|
||||
createUINotificationService,
|
||||
createUIModalService,
|
||||
createUIDialogService,
|
||||
};
|
||||
|
||||
export {
|
||||
@@ -76,6 +81,7 @@ export {
|
||||
//
|
||||
createUINotificationService,
|
||||
createUIModalService,
|
||||
createUIDialogService,
|
||||
};
|
||||
|
||||
export { OHIF };
|
||||
|
||||
@@ -12,6 +12,7 @@ describe('Top level exports', () => {
|
||||
//
|
||||
'createUINotificationService',
|
||||
'createUIModalService',
|
||||
'createUIDialogService',
|
||||
//
|
||||
'utils',
|
||||
'studies',
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* A UI Element
|
||||
*
|
||||
* @typedef {ReactElement|HTMLElement} DialogContent
|
||||
*/
|
||||
|
||||
/**
|
||||
* A UI Position
|
||||
*
|
||||
* @typedef {Object} ElementPosition
|
||||
* @property {number} top -
|
||||
* @property {number} left -
|
||||
* @property {number} right -
|
||||
* @property {number} bottom -
|
||||
*/
|
||||
|
||||
/**
|
||||
* UI Dialog
|
||||
*
|
||||
* @typedef {Object} DialogProps
|
||||
* @property {string} id -
|
||||
* @property {DialogContent} content -
|
||||
* @property {boolean} isDraggable -
|
||||
* @property {ElementPosition} defaultPosition -
|
||||
* @property {ElementPosition} position -
|
||||
* @property {Function} onSubmit -
|
||||
* @property {Function} onClose -
|
||||
* @property {Function} onStart -
|
||||
* @property {Function} onStop -
|
||||
* @property {Function} onDrag -
|
||||
*/
|
||||
|
||||
const uiDialogServicePublicAPI = {
|
||||
name: 'UIDialogService',
|
||||
dismiss,
|
||||
dismissAll,
|
||||
create,
|
||||
setServiceImplementation,
|
||||
};
|
||||
|
||||
const uiDialogServiceImplementation = {
|
||||
_dismiss: () => console.warn('dismiss() NOT IMPLEMENTED'),
|
||||
_dismissAll: () => console.warn('dismissAll() NOT IMPLEMENTED'),
|
||||
_create: () => console.warn('create() NOT IMPLEMENTED'),
|
||||
};
|
||||
|
||||
function createUIDialogService() {
|
||||
return uiDialogServicePublicAPI;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a new UI dialog;
|
||||
*
|
||||
* @param {DialogProps} props { id, content, onSubmit, onClose, onStart, onDrag, onStop, isDraggable, defaultPosition, position }
|
||||
*/
|
||||
function create({
|
||||
id,
|
||||
content,
|
||||
onSubmit,
|
||||
onClose,
|
||||
onStart,
|
||||
onDrag,
|
||||
onStop,
|
||||
isDraggable,
|
||||
defaultPosition,
|
||||
position,
|
||||
}) {
|
||||
return uiDialogServiceImplementation._create({
|
||||
id,
|
||||
content,
|
||||
onSubmit,
|
||||
onClose,
|
||||
onStart,
|
||||
onDrag,
|
||||
onStop,
|
||||
isDraggable,
|
||||
defaultPosition,
|
||||
position,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys all dialogs, if any
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
function dismissAll() {
|
||||
return uiDialogServiceImplementation._dismissAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the dialog, if currently created
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
function dismiss({ id }) {
|
||||
return uiDialogServiceImplementation._dismiss({ id });
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param {*} {
|
||||
* dismiss: dismissImplementation,
|
||||
* dismissAll: dismissAllImplementation,
|
||||
* create: createImplementation,
|
||||
* }
|
||||
*/
|
||||
function setServiceImplementation({
|
||||
dismiss: dismissImplementation,
|
||||
dismissAll: dismissAllImplementation,
|
||||
create: createImplementation,
|
||||
}) {
|
||||
if (dismissImplementation) {
|
||||
uiDialogServiceImplementation._dismiss = dismissImplementation;
|
||||
}
|
||||
if (dismissAllImplementation) {
|
||||
uiDialogServiceImplementation._dismissAll = dismissAllImplementation;
|
||||
}
|
||||
if (createImplementation) {
|
||||
uiDialogServiceImplementation._create = createImplementation;
|
||||
}
|
||||
}
|
||||
|
||||
export default createUIDialogService;
|
||||
@@ -8,14 +8,11 @@
|
||||
* UI Modal
|
||||
*
|
||||
* @typedef {Object} ModalProps
|
||||
* @property {string} [header=null] -
|
||||
* @property {string} [footer=null] -
|
||||
* @property {string} [backdrop=false] -
|
||||
* @property {string} [keyboard=false] -
|
||||
* @property {number} [show=true] -
|
||||
* @property {string} [closeButton=true] -
|
||||
* @property {boolean} [shouldCloseOnEsc=false] -
|
||||
* @property {boolean} [isOpen=true] -
|
||||
* @property {boolean} [closeButton=true] -
|
||||
* @property {string} [title=null] - 'Modal Title'
|
||||
* @property {boolean} [customClassName=null] - '.ModalClass'
|
||||
* @property {string} [customClassName=null] - '.ModalClass'
|
||||
*/
|
||||
|
||||
const uiModalServicePublicAPI = {
|
||||
@@ -38,16 +35,13 @@ function createUIModalService() {
|
||||
* Show a new UI modal;
|
||||
*
|
||||
* @param {Modal} component React component
|
||||
* @param {ModalProps} props { header, footer, backdrop, keyboard, show, closeButton, title, customClassName }
|
||||
* @param {ModalProps} props { shouldCloseOnEsc, isOpen, closeButton, title, customClassName }
|
||||
*/
|
||||
function show(
|
||||
component,
|
||||
props = {
|
||||
header: null,
|
||||
footer: null,
|
||||
backdrop: false,
|
||||
keyboard: false,
|
||||
show: true,
|
||||
shouldCloseOnEsc: false,
|
||||
isOpen: true,
|
||||
closeButton: true,
|
||||
title: null,
|
||||
customClassName: null,
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import ServicesManager from './ServicesManager.js';
|
||||
import createUINotificationService from './UINotificationService';
|
||||
import createUIModalService from './UIModalService';
|
||||
import createUIDialogService from './UIDialogService';
|
||||
|
||||
export { createUINotificationService, createUIModalService, ServicesManager };
|
||||
export {
|
||||
createUINotificationService,
|
||||
createUIModalService,
|
||||
createUIDialogService,
|
||||
ServicesManager,
|
||||
};
|
||||
@@ -3,6 +3,17 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [0.64.0](https://github.com/OHIF/Viewers/compare/@ohif/ui@0.63.0...@ohif/ui@0.64.0) (2019-11-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* New dialog service ([#1202](https://github.com/OHIF/Viewers/issues/1202)) ([f65639c](https://github.com/OHIF/Viewers/commit/f65639c2b0dab01decd20cab2cef4263cb4fab37))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.63.0](https://github.com/OHIF/Viewers/compare/@ohif/ui@0.62.4...@ohif/ui@0.63.0) (2019-11-19)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/ui",
|
||||
"version": "0.63.0",
|
||||
"version": "0.64.0",
|
||||
"description": "A set of React components for Medical Imaging Viewers",
|
||||
"author": "OHIF Contributors",
|
||||
"license": "MIT",
|
||||
@@ -52,7 +52,9 @@
|
||||
"react-dnd": "9.4.0",
|
||||
"react-dnd-html5-backend": "^9.4.0",
|
||||
"react-dnd-touch-backend": "^9.4.0",
|
||||
"react-draggable": "^4.1.0",
|
||||
"react-i18next": "^10.11.0",
|
||||
"react-modal": "^3.11.1",
|
||||
"react-with-direction": "1.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import './CineDialog.styl';
|
||||
|
||||
import React, { PureComponent } from 'react';
|
||||
import { withTranslation } from '../../utils/LanguageProvider';
|
||||
import { withTranslation } from '../../contextProviders';
|
||||
import { Icon } from './../../elements/Icon';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ const AboutContent = () => {
|
||||
);
|
||||
|
||||
return (
|
||||
<div data-cy="about-modal">
|
||||
<div className="AboutContent" data-cy="about-modal">
|
||||
<div className="btn-group">
|
||||
<a
|
||||
className="btn btn-default"
|
||||
|
||||
@@ -189,7 +189,7 @@ const ViewportDownloadForm = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="ViewportDownloadForm">
|
||||
<div className="title">
|
||||
{t(
|
||||
'Please specify the dimensions, filename, and desired type for the output image.'
|
||||
@@ -284,10 +284,6 @@ const ViewportDownloadForm = ({
|
||||
className="viewport-preview"
|
||||
src={viewportPreview.src}
|
||||
alt="Viewport Preview"
|
||||
style={{
|
||||
height: viewportPreview.height,
|
||||
width: viewportPreview.width,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -303,7 +299,7 @@ const ViewportDownloadForm = ({
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -3,15 +3,13 @@
|
||||
@import '../../../design/styles/common/button.styl'
|
||||
|
||||
.ViewportDownloadForm
|
||||
color: var(--text-secondary-color);
|
||||
filter: drop-shadow(0 0 3px var(--ui-gray-darkest));
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
width: inherit;
|
||||
padding: 15px;
|
||||
background: transparent;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
z-index: 1080 !important;
|
||||
|
||||
input, select
|
||||
max-height: 30px;
|
||||
|
||||
.title
|
||||
margin: 0;
|
||||
font-weight: bold;
|
||||
@@ -67,6 +65,7 @@
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
align-self: center;
|
||||
margin-bottom: 20px;
|
||||
@media screen and (max-width: 1023px)
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
@@ -90,7 +89,6 @@
|
||||
|
||||
.actions
|
||||
display: flex;
|
||||
height: 60px;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
|
||||
import i18n from '@ohif/i18n';
|
||||
|
||||
import './LanguageSwitcher.styl';
|
||||
import { withTranslation } from '../../utils/LanguageProvider';
|
||||
import { withTranslation } from '../../contextProviders';
|
||||
|
||||
const LanguageSwitcher = () => {
|
||||
const getCurrentLanguage = (language = i18n.language) =>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import './MeasurementTable.styl';
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { withTranslation } from '../../utils/LanguageProvider';
|
||||
import { withTranslation } from '../../contextProviders';
|
||||
|
||||
import { Icon } from './../../elements/Icon';
|
||||
import { MeasurementTableItem } from './MeasurementTableItem.js';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withTranslation } from '../../utils/LanguageProvider';
|
||||
import { withTranslation } from '../../contextProviders';
|
||||
|
||||
import { Icon } from './../../elements/Icon';
|
||||
import { OverlayTrigger } from './../overlayTrigger';
|
||||
|
||||
@@ -1,64 +1,69 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ReactBootstrapModal from 'react-bootstrap-modal';
|
||||
import Modal from 'react-modal';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import './OHIFModal.styl';
|
||||
|
||||
const customStyle = {
|
||||
overlay: {
|
||||
zIndex: 1071,
|
||||
backgroundColor: 'rgb(0, 0, 0, 0.5)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
};
|
||||
|
||||
Modal.setAppElement(document.getElementById('root'));
|
||||
|
||||
const OHIFModal = ({
|
||||
className,
|
||||
closeButton,
|
||||
backdrop,
|
||||
keyboard,
|
||||
show,
|
||||
shouldCloseOnEsc,
|
||||
isOpen,
|
||||
title,
|
||||
onHide,
|
||||
footer: Footer,
|
||||
header: Header,
|
||||
onClose,
|
||||
children,
|
||||
}) => (
|
||||
<ReactBootstrapModal
|
||||
className={classNames('modal fade themed in', className)}
|
||||
backdrop={backdrop}
|
||||
keyboard={keyboard}
|
||||
show={show}
|
||||
large={true}
|
||||
title={title}
|
||||
onHide={onHide}
|
||||
>
|
||||
{(Header || title) && (
|
||||
<ReactBootstrapModal.Header closeButton={closeButton}>
|
||||
{title && (
|
||||
<ReactBootstrapModal.Title>{title}</ReactBootstrapModal.Title>
|
||||
)}
|
||||
{Header && <Header hide={onHide} />}
|
||||
</ReactBootstrapModal.Header>
|
||||
)}
|
||||
<ReactBootstrapModal.Body>{children}</ReactBootstrapModal.Body>
|
||||
{Footer && (
|
||||
<ReactBootstrapModal.Footer>
|
||||
<Footer hide={onHide} />
|
||||
</ReactBootstrapModal.Footer>
|
||||
)}
|
||||
</ReactBootstrapModal>
|
||||
);
|
||||
}) => {
|
||||
const renderHeader = () => {
|
||||
return (
|
||||
title && (
|
||||
<div className="OHIFModal__header">
|
||||
<h4>{title}</h4>
|
||||
{closeButton && (
|
||||
<button data-cy="close-button" onClick={onClose}>
|
||||
×
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
className={classNames('OHIFModal', className)}
|
||||
shouldCloseOnEsc={shouldCloseOnEsc}
|
||||
isOpen={isOpen}
|
||||
title={title}
|
||||
style={customStyle}
|
||||
>
|
||||
<>
|
||||
{renderHeader()}
|
||||
<div className="OHIFModal__content">{children}</div>
|
||||
</>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
OHIFModal.propTypes = {
|
||||
className: PropTypes.string,
|
||||
closeButton: PropTypes.bool,
|
||||
backdrop: PropTypes.bool,
|
||||
keyboard: PropTypes.bool,
|
||||
show: PropTypes.bool,
|
||||
shouldCloseOnEsc: PropTypes.bool,
|
||||
isOpen: PropTypes.bool,
|
||||
title: PropTypes.string,
|
||||
onHide: PropTypes.func,
|
||||
footer: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.node),
|
||||
PropTypes.node,
|
||||
PropTypes.func,
|
||||
]),
|
||||
header: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.node),
|
||||
PropTypes.node,
|
||||
PropTypes.func,
|
||||
]),
|
||||
onClose: PropTypes.func,
|
||||
children: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.node),
|
||||
PropTypes.node,
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
.OHIFModal
|
||||
background-color: var(--ui-gray-darker)
|
||||
border-color: var(--ui-border-color)
|
||||
color: var(--text-secondary-color)
|
||||
border-radius: 6px
|
||||
border: 0
|
||||
color: var(--text-primary-color)
|
||||
position: relative
|
||||
-webkit-box-shadow: 0 3px 9px rgba(0,0,0,.5)
|
||||
box-shadow: 0 3px 9px rgba(0,0,0,.5);
|
||||
background-clip: padding-box
|
||||
outline: 0
|
||||
|
||||
@media (min-width: 320px)
|
||||
width: 78%
|
||||
min-width: 300px
|
||||
|
||||
@media (min-width: 768px)
|
||||
width: 600px
|
||||
|
||||
@media (min-width: 992px)
|
||||
width: 900px
|
||||
|
||||
&__content
|
||||
padding: 20px
|
||||
max-height: 90vh;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
&::-webkit-scrollbar
|
||||
display: none;
|
||||
|
||||
&__header
|
||||
display: flex
|
||||
justify-content: space-between
|
||||
align-items: center
|
||||
border-bottom-width: 3px
|
||||
border-bottom-style: solid
|
||||
border-bottom-color: #000000
|
||||
padding: 20px
|
||||
position: relative
|
||||
|
||||
h4
|
||||
color: var(--text-secondary-color)
|
||||
font-size: 20px
|
||||
font-weight: 500
|
||||
line-height: 24px
|
||||
padding-right: 24px
|
||||
margin: 0
|
||||
|
||||
button
|
||||
cursor: pointer
|
||||
margin: -10px 0 0 0
|
||||
padding: 0
|
||||
background-color: transparent
|
||||
border: none
|
||||
color: var(--text-secondary-color)
|
||||
font-size: 25px
|
||||
font-weight: lighter
|
||||
|
||||
&:active,
|
||||
&:focus,
|
||||
&:focus:active
|
||||
background-image: none
|
||||
outline: 0
|
||||
box-shadow: none
|
||||
@@ -3,7 +3,7 @@ import React, { cloneElement } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Overlay as BaseOverlay } from 'react-overlays';
|
||||
import elementType from 'prop-types-extra/lib/elementType';
|
||||
import { withTranslation } from '../../utils/LanguageProvider';
|
||||
import { withTranslation } from '../../contextProviders';
|
||||
|
||||
import Fade from './Fade';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import SnackbarItem from './SnackbarItem';
|
||||
import './Snackbar.css';
|
||||
import { useSnackbarContext } from '../../utils/SnackbarProvider';
|
||||
import { useSnackbarContext } from '../../contextProviders';
|
||||
|
||||
const SnackbarContainer = () => {
|
||||
const { snackbarItems, hide } = useSnackbarContext();
|
||||
|
||||
@@ -6,7 +6,7 @@ import TableSearchFilter from './TableSearchFilter.js';
|
||||
import useMedia from '../../hooks/useMedia.js';
|
||||
import PropTypes from 'prop-types';
|
||||
import { StudyListLoadingText } from './StudyListLoadingText.js';
|
||||
import { withTranslation } from '../../utils/LanguageProvider';
|
||||
import { withTranslation } from '../../contextProviders';
|
||||
|
||||
/**
|
||||
*
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Icon } from './../../elements/Icon';
|
||||
// TODO: useTranslation
|
||||
import { withTranslation } from '../../utils/LanguageProvider';
|
||||
import { withTranslation } from '../../contextProviders';
|
||||
|
||||
function StudyListLoadingText({ t: translate }) {
|
||||
return (
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import './PaginationArea.styl';
|
||||
import { withTranslation } from '../../utils/LanguageProvider';
|
||||
import { withTranslation } from '../../contextProviders';
|
||||
|
||||
class TablePagination extends PureComponent {
|
||||
static defaultProps = {
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
.HotKeysPreferences
|
||||
display: flex;
|
||||
margin-right: -15px;
|
||||
margin-left: -15px;
|
||||
|
||||
.column
|
||||
width: 50%;
|
||||
|
||||
@@ -90,8 +90,8 @@ export class UserPreferences extends Component {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<div className="dialog-separator-after">
|
||||
<div className="UserPreferences">
|
||||
<div className="UserPreferences__selector">
|
||||
<ul className="nav nav-tabs">
|
||||
<li
|
||||
onClick={() => {
|
||||
|
||||
@@ -3,23 +3,27 @@
|
||||
@import './../../design/styles/common/state.styl'
|
||||
@import './../../design/styles/common/global.styl'
|
||||
|
||||
.modal-body
|
||||
overflow: hidden
|
||||
.UserPreferences
|
||||
display: flex
|
||||
flex-direction: column
|
||||
|
||||
.errorMessage
|
||||
color: var(--state-error-text)
|
||||
font-size: 10px
|
||||
text-transform: uppercase;
|
||||
&__selector
|
||||
border-bottom: 3px solid black
|
||||
|
||||
.form-content
|
||||
border-bottom: 3px solid var(--primary-background-color)
|
||||
margin-bottom: 20px
|
||||
margin-left: -22px
|
||||
margin-right: -22px
|
||||
max-height: 70vh
|
||||
overflow-y: auto
|
||||
padding: 22px
|
||||
min-height: 500px
|
||||
.errorMessage
|
||||
color: var(--state-error-text)
|
||||
font-size: 10px
|
||||
text-transform: uppercase;
|
||||
|
||||
.popover
|
||||
width: 300px
|
||||
.form-content
|
||||
border-bottom: 3px solid var(--primary-background-color)
|
||||
margin-bottom: 20px
|
||||
margin-left: -20px
|
||||
margin-right: -20px
|
||||
max-height: 70vh
|
||||
overflow-y: auto
|
||||
padding: 20px
|
||||
min-height: 500px
|
||||
|
||||
.popover
|
||||
width: 300px
|
||||
@@ -2,7 +2,7 @@ import './UserPreferencesForm.styl';
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withTranslation } from '../../utils/LanguageProvider';
|
||||
import { withTranslation } from '../../contextProviders';
|
||||
|
||||
import cloneDeep from 'lodash.clonedeep';
|
||||
import isEqual from 'lodash.isequal';
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
.footer
|
||||
display: flex
|
||||
flex-direction: row
|
||||
padding-bottom: 20px
|
||||
justify-content: space-between
|
||||
|
||||
div
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
import React, {
|
||||
useState,
|
||||
createContext,
|
||||
useContext,
|
||||
useCallback,
|
||||
useEffect,
|
||||
} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Draggable from 'react-draggable';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { utils } from '@ohif/core';
|
||||
|
||||
import './DialogProvider.styl';
|
||||
|
||||
const DialogContext = createContext(null);
|
||||
|
||||
export const useDialog = () => useContext(DialogContext);
|
||||
|
||||
const DialogProvider = ({ children, service }) => {
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const [dialogs, setDialogs] = useState([]);
|
||||
const [lastDialogPosition, setLastDialogPosition] = useState(null);
|
||||
|
||||
/**
|
||||
* Sets the implementation of a dialog service that can be used by extensions.
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (service) {
|
||||
service.setServiceImplementation({ create, dismiss, dismissAll });
|
||||
}
|
||||
}, [create, dismiss, service]);
|
||||
|
||||
/**
|
||||
* Creates a dialog and return its id.
|
||||
*
|
||||
* @returns id
|
||||
*/
|
||||
const create = useCallback(
|
||||
({
|
||||
id,
|
||||
content,
|
||||
onSubmit,
|
||||
onClose,
|
||||
onDrag,
|
||||
onStop,
|
||||
isDraggable,
|
||||
defaultPosition,
|
||||
position,
|
||||
}) => {
|
||||
let dialogId = id;
|
||||
if (!dialogId) {
|
||||
dialogId = utils.guid();
|
||||
}
|
||||
|
||||
const newDialog = {
|
||||
id: dialogId,
|
||||
content,
|
||||
onSubmit,
|
||||
onClose,
|
||||
onDrag,
|
||||
onStop,
|
||||
isDraggable,
|
||||
defaultPosition,
|
||||
position,
|
||||
};
|
||||
|
||||
setDialogs(dialogs => [...dialogs, newDialog]);
|
||||
|
||||
return dialogId;
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
/**
|
||||
* Dismisses the dialog with a given id.
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
const dismiss = useCallback(({ id }) => {
|
||||
setDialogs(dialogs => dialogs.filter(dialog => dialog.id !== id));
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Dismisses all dialogs.
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
const dismissAll = () => {
|
||||
setDialogs([]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Moves the dialog to the foreground if clicked.
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
const _reorder = id => {
|
||||
setDialogs(dialogs => [
|
||||
...dialogs.filter(dialog => dialog.id !== id),
|
||||
dialogs.find(dialog => dialog.id === id),
|
||||
]);
|
||||
};
|
||||
|
||||
const _updateLastDialogPosition = dialogId => {
|
||||
const draggableItemBounds = document
|
||||
.querySelector(`#draggableItem-${dialogId}`)
|
||||
.getBoundingClientRect();
|
||||
setLastDialogPosition({
|
||||
x: draggableItemBounds.x,
|
||||
y: draggableItemBounds.y,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<DialogContext.Provider value={{ create, dismiss, dismissAll, dialogs }}>
|
||||
<div className="DraggableArea">
|
||||
{dialogs.map(dialog => {
|
||||
const {
|
||||
id,
|
||||
content: Dialog,
|
||||
position /* Position of the dialog. {{x: 0, y: 0}} */,
|
||||
defaultPosition,
|
||||
isDraggable = true,
|
||||
onStart = () => {},
|
||||
onStop = () => {},
|
||||
onDrag = () => {},
|
||||
} = dialog;
|
||||
|
||||
return (
|
||||
<Draggable
|
||||
key={id}
|
||||
disabled={!isDraggable}
|
||||
position={position}
|
||||
defaultPosition={lastDialogPosition || defaultPosition}
|
||||
bounds="parent"
|
||||
onStart={event => {
|
||||
const e = event || window.event;
|
||||
const target = e.target || e.srcElement;
|
||||
const BLACKLIST = ['SVG', 'BUTTON', 'PATH', 'INPUT'];
|
||||
if (BLACKLIST.includes(target.tagName.toUpperCase())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
onStart(event);
|
||||
}}
|
||||
onStop={event => {
|
||||
onStop(event);
|
||||
setIsDragging(false);
|
||||
return;
|
||||
}}
|
||||
onDrag={event => {
|
||||
setIsDragging(true);
|
||||
_reorder(id);
|
||||
_updateLastDialogPosition(id);
|
||||
onDrag(event);
|
||||
}}
|
||||
>
|
||||
<div
|
||||
id={`draggableItem-${id}`}
|
||||
className={classNames(
|
||||
'DraggableItem',
|
||||
isDragging && 'dragging'
|
||||
)}
|
||||
style={{ zIndex: '999', position: 'absolute' }}
|
||||
onClick={() => _reorder(id)}
|
||||
>
|
||||
<Dialog {...dialog} />
|
||||
</div>
|
||||
</Draggable>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{children}
|
||||
</DialogContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
DialogProvider.defaultProps = {
|
||||
service: null,
|
||||
};
|
||||
|
||||
DialogProvider.propTypes = {
|
||||
children: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.node),
|
||||
PropTypes.node,
|
||||
PropTypes.func,
|
||||
]).isRequired,
|
||||
service: PropTypes.shape({
|
||||
setServiceImplementation: PropTypes.func,
|
||||
}),
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* High Order Component to use the dialog methods through a Class Component
|
||||
*
|
||||
*/
|
||||
export const withDialog = Component => {
|
||||
return function WrappedComponent(props) {
|
||||
const { create, dismiss, dismissAll } = useDialog();
|
||||
return <Component {...props} dialog={{ create, dismiss, dismissAll }} />;
|
||||
};
|
||||
};
|
||||
|
||||
export default DialogProvider;
|
||||
@@ -0,0 +1,12 @@
|
||||
.DraggableItem
|
||||
div
|
||||
cursor: grab !important
|
||||
|
||||
.DraggableItem.dragging
|
||||
div
|
||||
cursor: grabbing !important
|
||||
|
||||
.DraggableArea
|
||||
width: 100vw
|
||||
height: 100vh
|
||||
position: absolute
|
||||
File renamed without changes.
+6
-12
@@ -16,14 +16,11 @@ export const useModal = () => useContext(ModalContext);
|
||||
const ModalProvider = ({ children, modal: Modal, service }) => {
|
||||
const DEFAULT_OPTIONS = {
|
||||
component: null /* The component instance inside the modal. */,
|
||||
header: null /* The content inside the modal header. */,
|
||||
footer: null /* The content inside the modal footer. */,
|
||||
backdrop: false /* Should the modal render a backdrop overlay. */,
|
||||
keyboard: false /* Modal is dismissible via the esc key. */,
|
||||
show: true /* Make the Modal visible or hidden. */,
|
||||
shouldCloseOnEsc: false /* Modal is dismissible via the esc key. */,
|
||||
isOpen: true /* Make the Modal visible or hidden. */,
|
||||
closeButton: true /* Should the modal body render the close button. */,
|
||||
title: null /* Should the modal render the title independently of the body content. */,
|
||||
customClassName: null /* The custom class to style the modal. */,
|
||||
customClassName: '' /* The custom class to style the modal. */,
|
||||
};
|
||||
|
||||
const [options, setOptions] = useState(DEFAULT_OPTIONS);
|
||||
@@ -69,14 +66,11 @@ const ModalProvider = ({ children, modal: Modal, service }) => {
|
||||
options.customClassName,
|
||||
options.component.className
|
||||
)}
|
||||
backdrop={options.backdrop}
|
||||
keyboard={options.keyboard}
|
||||
show={options.show}
|
||||
shouldCloseOnEsc={options.keyboard}
|
||||
isOpen={options.isOpen}
|
||||
title={options.title}
|
||||
closeButton={options.closeButton}
|
||||
footer={options.footer}
|
||||
header={options.header}
|
||||
onHide={hide}
|
||||
onClose={hide}
|
||||
>
|
||||
<Component {...options} show={show} hide={hide} />
|
||||
</Modal>
|
||||
File renamed without changes.
@@ -0,0 +1,20 @@
|
||||
export {
|
||||
default as ModalProvider,
|
||||
useModal,
|
||||
withModal,
|
||||
ModalConsumer,
|
||||
} from './ModalProvider.js';
|
||||
export {
|
||||
default as SnackbarProvider,
|
||||
useSnackbarContext,
|
||||
withSnackbar,
|
||||
} from './SnackbarProvider.js';
|
||||
export {
|
||||
default as LanguageProvider,
|
||||
withTranslation,
|
||||
} from './LanguageProvider.js';
|
||||
export {
|
||||
default as DialogProvider,
|
||||
withDialog,
|
||||
useDialog,
|
||||
} from './DialogProvider.js';
|
||||
@@ -49,15 +49,18 @@ import { ScrollableArea } from './ScrollableArea/ScrollableArea.js';
|
||||
import Toolbar from './viewer/Toolbar.js';
|
||||
import ToolbarButton from './viewer/ToolbarButton.js';
|
||||
import ViewerbaseDragDropContext from './utils/viewerbaseDragDropContext.js';
|
||||
import SnackbarProvider, {
|
||||
import {
|
||||
SnackbarProvider,
|
||||
useSnackbarContext,
|
||||
withSnackbar,
|
||||
} from './utils/SnackbarProvider';
|
||||
import ModalProvider, {
|
||||
DialogProvider,
|
||||
useDialog,
|
||||
withDialog,
|
||||
ModalProvider,
|
||||
ModalConsumer,
|
||||
useModal,
|
||||
withModal,
|
||||
ModalConsumer,
|
||||
} from './utils/ModalProvider';
|
||||
} from './contextProviders';
|
||||
|
||||
export {
|
||||
// Elements
|
||||
@@ -111,6 +114,9 @@ export {
|
||||
ModalConsumer,
|
||||
withModal,
|
||||
OHIFModal,
|
||||
DialogProvider,
|
||||
withDialog,
|
||||
useDialog,
|
||||
// Hooks
|
||||
useDebounce,
|
||||
useMedia,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Icon } from './../elements/Icon';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import classnames from 'classnames';
|
||||
import { withTranslation } from '../utils/LanguageProvider';
|
||||
import { withTranslation } from '../contextProviders';
|
||||
|
||||
export function ToolbarButton(props) {
|
||||
const { isActive, icon, labelWhenActive, onClick, t } = props;
|
||||
|
||||
@@ -3,6 +3,17 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [2.10.0](https://github.com/OHIF/Viewers/compare/@ohif/viewer@2.9.0...@ohif/viewer@2.10.0) (2019-11-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* New dialog service ([#1202](https://github.com/OHIF/Viewers/issues/1202)) ([f65639c](https://github.com/OHIF/Viewers/commit/f65639c2b0dab01decd20cab2cef4263cb4fab37))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.9.0](https://github.com/OHIF/Viewers/compare/@ohif/viewer@2.8.5...@ohif/viewer@2.9.0) (2019-11-19)
|
||||
|
||||
|
||||
|
||||
@@ -273,7 +273,7 @@ describe('OHIF Study Viewer Page', function() {
|
||||
cy.get('[data-cy="about-item-menu"]')
|
||||
.first()
|
||||
.click();
|
||||
cy.get('.modal-content')
|
||||
cy.get('[data-cy="about-modal"]')
|
||||
.as('aboutOverlay')
|
||||
.should('be.visible');
|
||||
|
||||
@@ -296,7 +296,7 @@ describe('OHIF Study Viewer Page', function() {
|
||||
cy.percyCanvasSnapshot('About modal - Should display modal');
|
||||
|
||||
//close modal
|
||||
cy.get('.close').click();
|
||||
cy.get('[data-cy="close-button"]').click();
|
||||
cy.get('@aboutOverlay').should('not.be.enabled');
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/viewer",
|
||||
"version": "2.9.0",
|
||||
"version": "2.10.0",
|
||||
"description": "OHIF Viewer",
|
||||
"author": "OHIF Contributors",
|
||||
"license": "MIT",
|
||||
@@ -45,14 +45,14 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"@ohif/core": "^1.11.0",
|
||||
"@ohif/core": "^1.12.0",
|
||||
"@ohif/extension-cornerstone": "^2.0.0",
|
||||
"@ohif/extension-dicom-html": "^1.0.1",
|
||||
"@ohif/extension-dicom-microscopy": "^0.50.6",
|
||||
"@ohif/extension-dicom-pdf": "^1.0.0",
|
||||
"@ohif/extension-vtk": "^0.53.10",
|
||||
"@ohif/extension-vtk": "^0.53.11",
|
||||
"@ohif/i18n": "^0.52.1",
|
||||
"@ohif/ui": "^0.63.0",
|
||||
"@ohif/ui": "^0.64.0",
|
||||
"@tanem/react-nprogress": "^1.1.25",
|
||||
"classnames": "^2.2.6",
|
||||
"core-js": "^3.2.1",
|
||||
|
||||
+50
-30
@@ -1,7 +1,18 @@
|
||||
import React, { Component } from 'react';
|
||||
import { OidcProvider } from 'redux-oidc';
|
||||
import { I18nextProvider } from 'react-i18next';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Provider } from 'react-redux';
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
import OHIFCornerstoneExtension from '@ohif/extension-cornerstone';
|
||||
import { hot } from 'react-hot-loader/root';
|
||||
|
||||
// TODO: This should not be here
|
||||
import './config';
|
||||
import {
|
||||
SnackbarProvider,
|
||||
ModalProvider,
|
||||
DialogProvider,
|
||||
OHIFModal,
|
||||
} from '@ohif/ui';
|
||||
|
||||
import {
|
||||
CommandsManager,
|
||||
@@ -10,44 +21,48 @@ import {
|
||||
HotkeysManager,
|
||||
createUINotificationService,
|
||||
createUIModalService,
|
||||
createUIDialogService,
|
||||
utils,
|
||||
} from '@ohif/core';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import i18n from '@ohif/i18n';
|
||||
|
||||
// TODO: This should not be here
|
||||
import './config';
|
||||
|
||||
/** Utils */
|
||||
import {
|
||||
getUserManagerForOpenIdConnectClient,
|
||||
initWebWorkers,
|
||||
} from './utils/index.js';
|
||||
|
||||
import { I18nextProvider } from 'react-i18next';
|
||||
|
||||
// ~~ EXTENSIONS
|
||||
/** Extensions */
|
||||
import { GenericViewerCommands, MeasurementsPanel } from './appExtensions';
|
||||
import OHIFCornerstoneExtension from '@ohif/extension-cornerstone';
|
||||
import OHIFStandaloneViewer from './OHIFStandaloneViewer';
|
||||
import { OidcProvider } from 'redux-oidc';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Provider } from 'react-redux';
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
import { getActiveContexts } from './store/layout/selectors.js';
|
||||
import i18n from '@ohif/i18n';
|
||||
import store from './store';
|
||||
import { SnackbarProvider, ModalProvider, OHIFModal } from '@ohif/ui';
|
||||
|
||||
// Contexts
|
||||
/** Viewer */
|
||||
import OHIFStandaloneViewer from './OHIFStandaloneViewer';
|
||||
|
||||
/** Store */
|
||||
import { getActiveContexts } from './store/layout/selectors.js';
|
||||
import store from './store';
|
||||
|
||||
/** Contexts */
|
||||
import WhiteLabellingContext from './context/WhiteLabellingContext';
|
||||
import UserManagerContext from './context/UserManagerContext';
|
||||
import AppContext from './context/AppContext';
|
||||
|
||||
// ~~~~ APP SETUP
|
||||
/** ~~~~~~~~~~~~~ Application Setup */
|
||||
const commandsManagerConfig = {
|
||||
getAppState: () => store.getState(),
|
||||
getActiveContexts: () => getActiveContexts(store.getState()),
|
||||
};
|
||||
|
||||
// Services
|
||||
/** Services */
|
||||
const UINotificationService = createUINotificationService();
|
||||
const UIModalService = createUIModalService();
|
||||
const UIDialogService = createUIDialogService();
|
||||
|
||||
/** Managers */
|
||||
const commandsManager = new CommandsManager(commandsManagerConfig);
|
||||
const hotkeysManager = new HotkeysManager(commandsManager);
|
||||
const servicesManager = new ServicesManager();
|
||||
@@ -55,7 +70,7 @@ const extensionManager = new ExtensionManager({
|
||||
commandsManager,
|
||||
servicesManager,
|
||||
});
|
||||
// ~~~~ END APP SETUP
|
||||
/** ~~~~~~~~~~~~~ End Application Setup */
|
||||
|
||||
// TODO[react] Use a provider when the whole tree is React
|
||||
window.store = store;
|
||||
@@ -72,6 +87,7 @@ class App extends Component {
|
||||
id: PropTypes.string.isRequired,
|
||||
})
|
||||
),
|
||||
hotkeys: PropTypes.array,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
@@ -91,7 +107,7 @@ class App extends Component {
|
||||
const { servers, extensions, hotkeys, oidc } = props;
|
||||
|
||||
this.initUserManager(oidc);
|
||||
_initServices([UINotificationService, UIModalService]);
|
||||
_initServices([UINotificationService, UIModalService, UIDialogService]);
|
||||
_initExtensions(extensions, hotkeys);
|
||||
_initServers(servers);
|
||||
initWebWorkers();
|
||||
@@ -114,12 +130,14 @@ class App extends Component {
|
||||
<Router basename={routerBasename}>
|
||||
<WhiteLabellingContext.Provider value={whiteLabelling}>
|
||||
<SnackbarProvider service={UINotificationService}>
|
||||
<ModalProvider
|
||||
modal={OHIFModal}
|
||||
service={UIModalService}
|
||||
>
|
||||
<OHIFStandaloneViewer userManager={userManager} />
|
||||
</ModalProvider>
|
||||
<DialogProvider service={UIDialogService}>
|
||||
<ModalProvider
|
||||
modal={OHIFModal}
|
||||
service={UIModalService}
|
||||
>
|
||||
<OHIFStandaloneViewer userManager={userManager} />
|
||||
</ModalProvider>
|
||||
</DialogProvider>
|
||||
</SnackbarProvider>
|
||||
</WhiteLabellingContext.Provider>
|
||||
</Router>
|
||||
@@ -138,9 +156,11 @@ class App extends Component {
|
||||
<Router basename={routerBasename}>
|
||||
<WhiteLabellingContext.Provider value={whiteLabelling}>
|
||||
<SnackbarProvider service={UINotificationService}>
|
||||
<ModalProvider modal={OHIFModal} service={UIModalService}>
|
||||
<OHIFStandaloneViewer />
|
||||
</ModalProvider>
|
||||
<DialogProvider service={UIDialogService}>
|
||||
<ModalProvider modal={OHIFModal} service={UIModalService}>
|
||||
<OHIFStandaloneViewer />
|
||||
</ModalProvider>
|
||||
</DialogProvider>
|
||||
</SnackbarProvider>
|
||||
</WhiteLabellingContext.Provider>
|
||||
</Router>
|
||||
|
||||
@@ -49,7 +49,6 @@ class Header extends Component {
|
||||
onClick: () =>
|
||||
show(AboutContent, {
|
||||
title: t('OHIF Viewer - About'),
|
||||
customClassName: 'AboutContent',
|
||||
}),
|
||||
},
|
||||
{
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
RoundedButtonGroup,
|
||||
ToolbarButton,
|
||||
withModal,
|
||||
withDialog,
|
||||
} from '@ohif/ui';
|
||||
|
||||
import './ToolbarRow.css';
|
||||
@@ -45,7 +46,6 @@ class ToolbarRow extends Component {
|
||||
this.state = {
|
||||
toolbarButtons: toolbarButtonDefinitions,
|
||||
activeButtons: [],
|
||||
isCineDialogOpen: false,
|
||||
};
|
||||
|
||||
this._handleBuiltIn = _handleBuiltIn.bind(this);
|
||||
@@ -106,13 +106,6 @@ class ToolbarRow extends Component {
|
||||
this.state.activeButtons
|
||||
);
|
||||
|
||||
const cineDialogContainerStyle = {
|
||||
display: this.state.isCineDialogOpen ? 'block' : 'none',
|
||||
position: 'absolute',
|
||||
top: '82px',
|
||||
zIndex: 999,
|
||||
};
|
||||
|
||||
const onPress = (side, value) => {
|
||||
this.props.handleSidePanelChange(side, value);
|
||||
};
|
||||
@@ -145,9 +138,6 @@ class ToolbarRow extends Component {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="CineDialogContainer" style={cineDialogContainerStyle}>
|
||||
<ConnectedCineDialog />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -160,7 +150,8 @@ function _getCustomButtonComponent(button, activeButtons) {
|
||||
// Check if its a valid customComponent. Later on an CustomToolbarComponent interface could be implemented.
|
||||
if (isValidComponent) {
|
||||
const parentContext = this;
|
||||
const isActive = activeButtons.includes(button.id);
|
||||
const activeButtonsIds = activeButtons.map(button => button.id);
|
||||
const isActive = activeButtonsIds.includes(button.id);
|
||||
|
||||
return (
|
||||
<CustomComponent
|
||||
@@ -168,7 +159,7 @@ function _getCustomButtonComponent(button, activeButtons) {
|
||||
toolbarClickCallback={_handleToolbarButtonClick.bind(this)}
|
||||
button={button}
|
||||
key={button.id}
|
||||
activeButtons={activeButtons}
|
||||
activeButtons={activeButtonsIds}
|
||||
isActive={isActive}
|
||||
/>
|
||||
);
|
||||
@@ -181,7 +172,7 @@ function _getExpandableButtonComponent(button, activeButtons) {
|
||||
const childButtons = button.buttons.map(childButton => {
|
||||
childButton.onClick = _handleToolbarButtonClick.bind(this, childButton);
|
||||
|
||||
if (activeButtons.indexOf(childButton.id) > -1) {
|
||||
if (activeButtons.map(button => button.id).indexOf(childButton.id) > -1) {
|
||||
activeCommand = childButton.id;
|
||||
}
|
||||
|
||||
@@ -206,7 +197,7 @@ function _getDefaultButtonComponent(button, activeButtons) {
|
||||
label={button.label}
|
||||
icon={button.icon}
|
||||
onClick={_handleToolbarButtonClick.bind(this, button)}
|
||||
isActive={activeButtons.includes(button.id)}
|
||||
isActive={activeButtons.map(button => button.id).includes(button.id)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -241,6 +232,8 @@ function _getButtonComponents(toolbarButtons, activeButtons) {
|
||||
* @param {*} props
|
||||
*/
|
||||
function _handleToolbarButtonClick(button, evt, props) {
|
||||
const { activeButtons } = this.state;
|
||||
|
||||
if (button.commandName) {
|
||||
const options = Object.assign({ evt }, button.commandOptions);
|
||||
commandsManager.runCommand(button.commandName, options);
|
||||
@@ -250,11 +243,12 @@ function _handleToolbarButtonClick(button, evt, props) {
|
||||
// TODO: We can update this to be a `getter` on the extension to query
|
||||
// For the active tools after we apply our updates?
|
||||
if (button.type === 'setToolActive') {
|
||||
this.setState({
|
||||
activeButtons: [button.id],
|
||||
});
|
||||
const toggables = activeButtons.filter(
|
||||
({ options }) => options && !options.togglable
|
||||
);
|
||||
this.setState({ activeButtons: [...toggables, button] });
|
||||
} else if (button.type === 'builtIn') {
|
||||
this._handleBuiltIn(button.options);
|
||||
this._handleBuiltIn(button);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,21 +273,47 @@ function _getVisibleToolbarButtons() {
|
||||
return toolbarButtonDefinitions;
|
||||
}
|
||||
|
||||
function _handleBuiltIn({ behavior } = {}) {
|
||||
if (behavior === 'CINE') {
|
||||
this.setState({
|
||||
isCineDialogOpen: !this.state.isCineDialogOpen,
|
||||
});
|
||||
function _handleBuiltIn(button) {
|
||||
/* TODO: Keep cine button active until its unselected. */
|
||||
const { dialog, modal, t } = this.props;
|
||||
const { dialogId } = this.state;
|
||||
const { id, options } = button;
|
||||
|
||||
if (options.behavior === 'CINE') {
|
||||
if (dialogId) {
|
||||
dialog.dismiss({ id: dialogId });
|
||||
this.setState(state => ({
|
||||
dialogId: null,
|
||||
activeButtons: [
|
||||
...state.activeButtons.filter(button => button.id !== id),
|
||||
],
|
||||
}));
|
||||
} else {
|
||||
const spacing = 20;
|
||||
const { x, y } = document
|
||||
.querySelector(`.ViewerMain`)
|
||||
.getBoundingClientRect();
|
||||
const newDialogId = dialog.create({
|
||||
content: ConnectedCineDialog,
|
||||
defaultPosition: {
|
||||
x: x + spacing || 0,
|
||||
y: y + spacing || 0,
|
||||
},
|
||||
});
|
||||
this.setState(state => ({
|
||||
dialogId: newDialogId,
|
||||
activeButtons: [...state.activeButtons, button],
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
if (behavior === 'DOWNLOAD_SCREEN_SHOT') {
|
||||
this.props.modal.show(ConnectedViewportDownloadForm, {
|
||||
title: this.props.t('Download High Quality Image'),
|
||||
customClassName: 'ViewportDownloadForm',
|
||||
if (options.behavior === 'DOWNLOAD_SCREEN_SHOT') {
|
||||
modal.show(ConnectedViewportDownloadForm, {
|
||||
title: t('Download High Quality Image'),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default withTranslation(['Common', 'ViewportDownloadForm'])(
|
||||
withModal(ToolbarRow)
|
||||
withModal(withDialog(ToolbarRow))
|
||||
);
|
||||
@@ -1100,13 +1100,34 @@
|
||||
pirates "^4.0.0"
|
||||
source-map-support "^0.5.9"
|
||||
|
||||
"@babel/runtime@7.1.2", "@babel/runtime@7.5.5", "@babel/runtime@7.6.0", "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.3":
|
||||
"@babel/runtime@7.1.2":
|
||||
version "7.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.1.2.tgz#81c89935f4647706fc54541145e6b4ecfef4b8e3"
|
||||
integrity sha512-Y3SCjmhSupzFB6wcv1KmmFucH6gDVnI30WjOcicV10ju0cZjak3Jcs67YLIXBrmZYw1xCrVeJPbycFwrqNyxpg==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.12.0"
|
||||
|
||||
"@babel/runtime@7.6.0":
|
||||
version "7.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.6.0.tgz#4fc1d642a9fd0299754e8b5de62c631cf5568205"
|
||||
integrity sha512-89eSBLJsxNxOERC0Op4vd+0Bqm6wRMqMbFtV3i0/fbaWw/mJ8Q3eBvgX0G4SyrOOLCtbu98HspF8o09MRT+KzQ==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.2"
|
||||
|
||||
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5":
|
||||
version "7.5.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132"
|
||||
integrity sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.2"
|
||||
|
||||
"@babel/runtime@^7.6.0", "@babel/runtime@^7.6.3":
|
||||
version "7.7.2"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.7.2.tgz#111a78002a5c25fc8e3361bedc9529c696b85a6a"
|
||||
integrity sha512-JONRbXbTXc9WQE2mAZd1p0Z3DZ/6vaQIkgYMSTP3KjRCyd7rCZCcfhCyX+YjwcKxcZ82UrxbRD358bpExNgrjw==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.2"
|
||||
|
||||
"@babel/template@^7.0.0", "@babel/template@^7.1.0", "@babel/template@^7.4.0", "@babel/template@^7.4.4", "@babel/template@^7.6.0":
|
||||
version "7.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.6.0.tgz#7f0159c7f5012230dad64cca42ec9bdb5c9536e6"
|
||||
@@ -8074,6 +8095,11 @@ executable@4.1.1:
|
||||
dependencies:
|
||||
pify "^2.2.0"
|
||||
|
||||
exenv@^1.2.0:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d"
|
||||
integrity sha1-KueOhdmJQVhnCwPUe+wfA72Ru50=
|
||||
|
||||
exif-parser@^0.1.12, exif-parser@^0.1.9:
|
||||
version "0.1.12"
|
||||
resolved "https://registry.yarnpkg.com/exif-parser/-/exif-parser-0.1.12.tgz#58a9d2d72c02c1f6f02a0ef4a9166272b7760922"
|
||||
@@ -16019,6 +16045,14 @@ react-dom@^16.8.6:
|
||||
prop-types "^15.6.2"
|
||||
scheduler "^0.17.0"
|
||||
|
||||
react-draggable@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/react-draggable/-/react-draggable-4.1.0.tgz#e1c5b774001e32f0bff397254e1e9d5448ac92a4"
|
||||
integrity sha512-Or/qe70cfymshqoC8Lsp0ukTzijJObehb7Vfl7tb5JRxoV+b6PDkOGoqYaWBzZ59k9dH/bwraLGsnlW78/3vrA==
|
||||
dependencies:
|
||||
classnames "^2.2.5"
|
||||
prop-types "^15.6.0"
|
||||
|
||||
react-dropzone@^10.1.7:
|
||||
version "10.1.10"
|
||||
resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-10.1.10.tgz#f340290dfc26ac09ad68abc020ab6232c23d6cf3"
|
||||
@@ -16082,7 +16116,7 @@ react-is@^16.3.2, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.11.0.tgz#b85dfecd48ad1ce469ff558a882ca8e8313928fa"
|
||||
integrity sha512-gbBVYR2p8mnriqAwWx9LbuUrShnAuSCNnuPGyc7GJrMVQtPDAh8iLpv7FRuMPFb56KkaVZIYSz1PrjI9q0QPCw==
|
||||
|
||||
react-lifecycles-compat@^3.0.4:
|
||||
react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
|
||||
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
|
||||
@@ -16101,6 +16135,16 @@ react-live@2.0.1:
|
||||
react-simple-code-editor "^0.9.0"
|
||||
unescape "^0.2.0"
|
||||
|
||||
react-modal@^3.11.1:
|
||||
version "3.11.1"
|
||||
resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-3.11.1.tgz#2a0d6877c9e98f123939ea92d2bb4ad7fa5a17f9"
|
||||
integrity sha512-8uN744Yq0X2lbfSLxsEEc2UV3RjSRb4yDVxRQ1aGzPo86QjNOwhQSukDb8U8kR+636TRTvfMren10fgOjAy9eA==
|
||||
dependencies:
|
||||
exenv "^1.2.0"
|
||||
prop-types "^15.5.10"
|
||||
react-lifecycles-compat "^3.0.0"
|
||||
warning "^4.0.3"
|
||||
|
||||
react-moment-proptypes@^1.6.0:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/react-moment-proptypes/-/react-moment-proptypes-1.7.0.tgz#89881479840a76c13574a86e3bb214c4ba564e7a"
|
||||
@@ -16613,6 +16657,11 @@ regenerator-runtime@^0.11.0, regenerator-runtime@^0.11.1:
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
|
||||
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
|
||||
|
||||
regenerator-runtime@^0.12.0:
|
||||
version "0.12.1"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de"
|
||||
integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==
|
||||
|
||||
regenerator-runtime@^0.13.1, regenerator-runtime@^0.13.2:
|
||||
version "0.13.3"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5"
|
||||
@@ -20041,6 +20090,13 @@ warning@^3.0.0:
|
||||
dependencies:
|
||||
loose-envify "^1.0.0"
|
||||
|
||||
warning@^4.0.3:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
|
||||
integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==
|
||||
dependencies:
|
||||
loose-envify "^1.0.0"
|
||||
|
||||
watchpack@^1.6.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00"
|
||||
|
||||
Reference in new issue
Block a user