Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8055e458f | ||
|
|
5c04e34c8f | ||
|
|
c59c5b3f14 | ||
|
|
c4adcfd39e | ||
|
|
0fbaf95971 | ||
|
|
a2a7e05e7d | ||
|
|
9da8647c84 | ||
|
|
e62f5f8dd2 |
No files matched your search
@@ -22,6 +22,7 @@
|
|||||||
[![NPM downloads][npm-downloads-image]][npm-url]
|
[![NPM downloads][npm-downloads-image]][npm-url]
|
||||||
[![Pulls][docker-pulls-img]][docker-image-url]
|
[![Pulls][docker-pulls-img]][docker-image-url]
|
||||||
[![MIT License][license-image]][license-url]
|
[![MIT License][license-image]][license-url]
|
||||||
|
[](https://app.fossa.io/projects/git%2Bgithub.com%2FOHIF%2FViewers?ref=badge_shield)
|
||||||
|
|
||||||
[![Netlify Status][netlify-image]][netlify-url]
|
[![Netlify Status][netlify-image]][netlify-url]
|
||||||
[![CircleCI][circleci-image]][circleci-url]
|
[![CircleCI][circleci-image]][circleci-url]
|
||||||
@@ -324,3 +325,6 @@ MIT © [OHIF](https://github.com/OHIF)
|
|||||||
[extension-vtk]: extensions/vtk/README.md
|
[extension-vtk]: extensions/vtk/README.md
|
||||||
[vtk-npm]: https://www.npmjs.com/package/@ohif/extension-vtk
|
[vtk-npm]: https://www.npmjs.com/package/@ohif/extension-vtk
|
||||||
<!-- prettier-ignore-end -->
|
<!-- prettier-ignore-end -->
|
||||||
|
|
||||||
|
|
||||||
|
[](https://app.fossa.io/projects/git%2Bgithub.com%2FOHIF%2FViewers?ref=badge_large)
|
||||||
@@ -11,7 +11,7 @@ export default {
|
|||||||
* LIFECYCLE HOOKS
|
* LIFECYCLE HOOKS
|
||||||
*/
|
*/
|
||||||
|
|
||||||
preRegistration(extensionConfiguration) {},
|
preRegistration({ serviceManager, configuration: extensionConfiguration }) {},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MODULE GETTERS
|
* MODULE GETTERS
|
||||||
|
|||||||
@@ -3,6 +3,17 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
# [1.5.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@1.4.1...@ohif/extension-cornerstone@1.5.0) (2019-11-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* expose UiNotifications service ([#1172](https://github.com/OHIF/Viewers/issues/1172)) ([5c04e34](https://github.com/OHIF/Viewers/commit/5c04e34c8fb2394ab7acd9eb4f2ab12afeb2f255))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [1.4.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@1.4.0...@ohif/extension-cornerstone@1.4.1) (2019-11-08)
|
## [1.4.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@1.4.0...@ohif/extension-cornerstone@1.4.1) (2019-11-08)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/extension-cornerstone",
|
"name": "@ohif/extension-cornerstone",
|
||||||
"version": "1.4.1",
|
"version": "1.5.0",
|
||||||
"description": "OHIF extension for Cornerstone",
|
"description": "OHIF extension for Cornerstone",
|
||||||
"author": "OHIF",
|
"author": "OHIF",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ export default {
|
|||||||
* @param {object} [configuration={}]
|
* @param {object} [configuration={}]
|
||||||
* @param {object|array} [configuration.csToolsConfig] - Passed directly to `initCornerstoneTools`
|
* @param {object|array} [configuration.csToolsConfig] - Passed directly to `initCornerstoneTools`
|
||||||
*/
|
*/
|
||||||
preRegistration(configuration = {}) {
|
preRegistration({ serviceManager, configuration = {} }) {
|
||||||
init(configuration);
|
init({ serviceManager, configuration });
|
||||||
},
|
},
|
||||||
getViewportModule() {
|
getViewportModule() {
|
||||||
return OHIFCornerstoneViewport;
|
return OHIFCornerstoneViewport;
|
||||||
|
|||||||
@@ -4,16 +4,18 @@ import csTools from 'cornerstone-tools';
|
|||||||
import initCornerstoneTools from './initCornerstoneTools.js';
|
import initCornerstoneTools from './initCornerstoneTools.js';
|
||||||
import queryString from 'query-string';
|
import queryString from 'query-string';
|
||||||
|
|
||||||
function fallbackMetaDataProvider (type, imageId) {
|
function fallbackMetaDataProvider(type, imageId) {
|
||||||
if (!imageId.includes('wado?requestType=WADO')) {
|
if (!imageId.includes('wado?requestType=WADO')) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If you call for an WADO-URI imageId and get no
|
// If you call for an WADO-URI imageId and get no
|
||||||
// metadata, try reformatting to WADO-RS imageId
|
// metadata, try reformatting to WADO-RS imageId
|
||||||
const qs = queryString.parse(imageId);
|
const qs = queryString.parse(imageId);
|
||||||
const wadoRoot = window.store.getState().servers.servers[0].wadoRoot
|
const wadoRoot = window.store.getState().servers.servers[0].wadoRoot;
|
||||||
const wadoRsImageId = `wadors:${wadoRoot}/studies/${qs.studyUID}/series/${qs.seriesUID}/instances/${qs.objectUID}/frames/${qs.frame || 1}`;
|
const wadoRsImageId = `wadors:${wadoRoot}/studies/${qs.studyUID}/series/${
|
||||||
|
qs.seriesUID
|
||||||
|
}/instances/${qs.objectUID}/frames/${qs.frame || 1}`;
|
||||||
|
|
||||||
return cornerstone.metaData.get(type, wadoRsImageId);
|
return cornerstone.metaData.get(type, wadoRsImageId);
|
||||||
}
|
}
|
||||||
@@ -21,13 +23,12 @@ function fallbackMetaDataProvider (type, imageId) {
|
|||||||
// Add this fallback provider with a low priority so it is handled last
|
// Add this fallback provider with a low priority so it is handled last
|
||||||
cornerstone.metaData.addProvider(fallbackMetaDataProvider, -1);
|
cornerstone.metaData.addProvider(fallbackMetaDataProvider, -1);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {object} configuration
|
* @param {object} configuration
|
||||||
* @param {Object|Array} configuration.csToolsConfig
|
* @param {Object|Array} configuration.csToolsConfig
|
||||||
*/
|
*/
|
||||||
export default function init(configuration = {}) {
|
export default function init({ serviceManager, configuration = {} }) {
|
||||||
const { csToolsConfig } = configuration;
|
const { csToolsConfig } = configuration;
|
||||||
const { StackManager } = OHIF.utils;
|
const { StackManager } = OHIF.utils;
|
||||||
const metadataProvider = new OHIF.cornerstone.MetadataProvider();
|
const metadataProvider = new OHIF.cornerstone.MetadataProvider();
|
||||||
@@ -97,6 +98,7 @@ export default function init(configuration = {}) {
|
|||||||
];
|
];
|
||||||
|
|
||||||
tools.forEach(tool => csTools.addTool(tool));
|
tools.forEach(tool => csTools.addTool(tool));
|
||||||
|
|
||||||
csTools.setToolActive('Pan', { mouseButtonMask: 4 });
|
csTools.setToolActive('Pan', { mouseButtonMask: 4 });
|
||||||
csTools.setToolActive('Zoom', { mouseButtonMask: 2 });
|
csTools.setToolActive('Zoom', { mouseButtonMask: 2 });
|
||||||
csTools.setToolActive('Wwwc', { mouseButtonMask: 1 });
|
csTools.setToolActive('Wwwc', { mouseButtonMask: 1 });
|
||||||
|
|||||||
@@ -3,6 +3,22 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [0.53.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@0.53.4...@ohif/extension-vtk@0.53.5) (2019-11-13)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.53.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@0.53.3...@ohif/extension-vtk@0.53.4) (2019-11-12)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [0.53.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@0.53.2...@ohif/extension-vtk@0.53.3) (2019-11-11)
|
## [0.53.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@0.53.2...@ohif/extension-vtk@0.53.3) (2019-11-11)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/extension-vtk",
|
"name": "@ohif/extension-vtk",
|
||||||
"version": "0.53.3",
|
"version": "0.53.5",
|
||||||
"description": "OHIF extension for VTK.js",
|
"description": "OHIF extension for VTK.js",
|
||||||
"author": "OHIF",
|
"author": "OHIF",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -52,8 +52,8 @@
|
|||||||
"react-vtkjs-viewport": "^0.3.9"
|
"react-vtkjs-viewport": "^0.3.9"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@ohif/core": "^1.7.1",
|
"@ohif/core": "^1.9.0",
|
||||||
"@ohif/ui": "^0.60.1",
|
"@ohif/ui": "^0.62.0",
|
||||||
"cornerstone-tools": "^4.0.9",
|
"cornerstone-tools": "^4.0.9",
|
||||||
"cornerstone-wado-image-loader": "^3.0.0",
|
"cornerstone-wado-image-loader": "^3.0.0",
|
||||||
"dcmjs": "^0.6.1",
|
"dcmjs": "^0.6.1",
|
||||||
|
|||||||
@@ -3,6 +3,28 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
# [1.9.0](https://github.com/OHIF/Viewers/compare/@ohif/core@1.8.0...@ohif/core@1.9.0) (2019-11-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* expose UiNotifications service ([#1172](https://github.com/OHIF/Viewers/issues/1172)) ([5c04e34](https://github.com/OHIF/Viewers/commit/5c04e34c8fb2394ab7acd9eb4f2ab12afeb2f255))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [1.8.0](https://github.com/OHIF/Viewers/compare/@ohif/core@1.7.1...@ohif/core@1.8.0) (2019-11-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* 🎸 Update hotkeys and user preferences modal ([#1135](https://github.com/OHIF/Viewers/issues/1135)) ([e62f5f8](https://github.com/OHIF/Viewers/commit/e62f5f8dd28ab363f23671cd21cee115abb870ff)), closes [#923](https://github.com/OHIF/Viewers/issues/923)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [1.7.1](https://github.com/OHIF/Viewers/compare/@ohif/core@1.7.0...@ohif/core@1.7.1) (2019-11-08)
|
## [1.7.1](https://github.com/OHIF/Viewers/compare/@ohif/core@1.7.0...@ohif/core@1.7.1) (2019-11-08)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/core",
|
"name": "@ohif/core",
|
||||||
"version": "1.7.1",
|
"version": "1.9.0",
|
||||||
"description": "Generic business logic for web-based medical imaging applications",
|
"description": "Generic business logic for web-based medical imaging applications",
|
||||||
"author": "OHIF Core Team",
|
"author": "OHIF Core Team",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import cloneDeep from 'lodash.clonedeep';
|
||||||
import hotkeys from './hotkeys';
|
import hotkeys from './hotkeys';
|
||||||
import log from './../log.js';
|
import log from './../log.js';
|
||||||
|
|
||||||
@@ -51,10 +52,11 @@ export class HotkeysManager {
|
|||||||
* @param {Boolean} [isDefaultDefinitions]
|
* @param {Boolean} [isDefaultDefinitions]
|
||||||
*/
|
*/
|
||||||
setHotkeys(hotkeyDefinitions, isDefaultDefinitions = false) {
|
setHotkeys(hotkeyDefinitions, isDefaultDefinitions = false) {
|
||||||
hotkeyDefinitions.forEach(definition => this.registerHotkeys(definition));
|
const definitions = cloneDeep(hotkeyDefinitions);
|
||||||
|
definitions.forEach(definition => this.registerHotkeys(definition));
|
||||||
|
|
||||||
if (isDefaultDefinitions) {
|
if (isDefaultDefinitions) {
|
||||||
this.hotkeyDefaults = hotkeyDefinitions;
|
this.hotkeyDefaults = definitions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,12 +2,13 @@ import MODULE_TYPES from './MODULE_TYPES.js';
|
|||||||
import log from './../log.js';
|
import log from './../log.js';
|
||||||
|
|
||||||
export default class ExtensionManager {
|
export default class ExtensionManager {
|
||||||
constructor({ commandsManager }) {
|
constructor({ commandsManager, servicesManager }) {
|
||||||
this.modules = {};
|
this.modules = {};
|
||||||
this.registeredExtensionIds = [];
|
this.registeredExtensionIds = [];
|
||||||
this.moduleTypeNames = Object.values(MODULE_TYPES);
|
this.moduleTypeNames = Object.values(MODULE_TYPES);
|
||||||
//
|
//
|
||||||
this._commandsManager = commandsManager;
|
this._commandsManager = commandsManager;
|
||||||
|
this._servicesManager = servicesManager;
|
||||||
|
|
||||||
this.moduleTypeNames.forEach(moduleType => {
|
this.moduleTypeNames.forEach(moduleType => {
|
||||||
this.modules[moduleType] = [];
|
this.modules[moduleType] = [];
|
||||||
@@ -66,7 +67,10 @@ export default class ExtensionManager {
|
|||||||
|
|
||||||
// preRegistrationHook
|
// preRegistrationHook
|
||||||
if (extension.preRegistration) {
|
if (extension.preRegistration) {
|
||||||
extension.preRegistration(configuration);
|
extension.preRegistration({
|
||||||
|
serviceManager: this._servicesManager,
|
||||||
|
configuration,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register Modules
|
// Register Modules
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import './lib';
|
import './lib';
|
||||||
|
|
||||||
import { ExtensionManager, MODULE_TYPES } from './extensions';
|
import { ExtensionManager, MODULE_TYPES } from './extensions';
|
||||||
|
import { ServicesManager } from './services';
|
||||||
import classes, { CommandsManager, HotkeysManager } from './classes/';
|
import classes, { CommandsManager, HotkeysManager } from './classes/';
|
||||||
|
|
||||||
import DICOMWeb from './DICOMWeb';
|
import DICOMWeb from './DICOMWeb';
|
||||||
@@ -18,12 +19,15 @@ import ui from './ui';
|
|||||||
import user from './user.js';
|
import user from './user.js';
|
||||||
import utils from './utils/';
|
import utils from './utils/';
|
||||||
|
|
||||||
|
import { createUiNotificationService } from './services';
|
||||||
|
|
||||||
const OHIF = {
|
const OHIF = {
|
||||||
MODULE_TYPES,
|
MODULE_TYPES,
|
||||||
//
|
//
|
||||||
CommandsManager,
|
CommandsManager,
|
||||||
ExtensionManager,
|
ExtensionManager,
|
||||||
HotkeysManager,
|
HotkeysManager,
|
||||||
|
ServicesManager,
|
||||||
//
|
//
|
||||||
utils,
|
utils,
|
||||||
studies,
|
studies,
|
||||||
@@ -41,6 +45,8 @@ const OHIF = {
|
|||||||
viewer: {},
|
viewer: {},
|
||||||
measurements,
|
measurements,
|
||||||
hangingProtocols,
|
hangingProtocols,
|
||||||
|
//
|
||||||
|
createUiNotificationService,
|
||||||
};
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
@@ -49,6 +55,7 @@ export {
|
|||||||
CommandsManager,
|
CommandsManager,
|
||||||
ExtensionManager,
|
ExtensionManager,
|
||||||
HotkeysManager,
|
HotkeysManager,
|
||||||
|
ServicesManager,
|
||||||
//
|
//
|
||||||
utils,
|
utils,
|
||||||
studies,
|
studies,
|
||||||
@@ -65,6 +72,8 @@ export {
|
|||||||
DICOMWeb,
|
DICOMWeb,
|
||||||
measurements,
|
measurements,
|
||||||
hangingProtocols,
|
hangingProtocols,
|
||||||
|
//
|
||||||
|
createUiNotificationService,
|
||||||
};
|
};
|
||||||
|
|
||||||
export { OHIF };
|
export { OHIF };
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ describe('Top level exports', () => {
|
|||||||
'CommandsManager',
|
'CommandsManager',
|
||||||
'ExtensionManager',
|
'ExtensionManager',
|
||||||
'HotkeysManager',
|
'HotkeysManager',
|
||||||
|
'ServicesManager',
|
||||||
|
//
|
||||||
|
'createUiNotificationService',
|
||||||
//
|
//
|
||||||
'utils',
|
'utils',
|
||||||
'studies',
|
'studies',
|
||||||
|
|||||||
@@ -1,18 +1,15 @@
|
|||||||
import cloneDeep from 'lodash.clonedeep';
|
import cloneDeep from 'lodash.clonedeep';
|
||||||
|
|
||||||
const defaultState = {
|
const defaultState = {
|
||||||
// Top level key
|
// First tab
|
||||||
viewer: {
|
hotkeyDefinitions: [
|
||||||
// First tab
|
// commandName, label, keys
|
||||||
hotKeysData: {
|
// [{ zoom: { label: 'Zoom', keys: ['z'] }}]
|
||||||
// hotkeyName, label, keys, column
|
],
|
||||||
// zoom: { label: 'Zoom', command: 'Z', column: 0 },
|
// Second tab
|
||||||
},
|
windowLevelData: {
|
||||||
// Second tab
|
// order, description, window (int), level (int)
|
||||||
windowLevelData: {
|
// 0: { description: 'Soft tissue', window: '', level: '' },
|
||||||
// order, description, window (int), level (int)
|
|
||||||
// 0: { description: 'Soft tissue', window: '', level: '' },
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
export default class ServicesManager {
|
||||||
|
constructor() {
|
||||||
|
this.services = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
register(service) {
|
||||||
|
this.services[service.name] = service;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
/**
|
||||||
|
* A UI Notification
|
||||||
|
*
|
||||||
|
* @typedef {Object} Notification
|
||||||
|
* @property {string} title -
|
||||||
|
* @property {string} message -
|
||||||
|
* @property {number} [duration=5000] - in ms
|
||||||
|
* @property {string} [position="bottomRight"] -"topLeft" | "topCenter | "topRight" | "bottomLeft" | "bottomCenter" | "bottomRight"
|
||||||
|
* @property {string} [type="info"] - "info" | "error" | "warning" | "success"
|
||||||
|
* @property {boolean} [autoClose=true]
|
||||||
|
*/
|
||||||
|
|
||||||
|
const uiNotificationServicePublicApi = {
|
||||||
|
name: 'UINotificationService',
|
||||||
|
hide,
|
||||||
|
show,
|
||||||
|
setServiceImplementation,
|
||||||
|
};
|
||||||
|
|
||||||
|
const uiNotificationServiceImplementation = {
|
||||||
|
_hide: () => console.warn('hide() NOT IMPLEMENTED'),
|
||||||
|
_show: () => console.warn('show() NOT IMPLEMENTED'),
|
||||||
|
};
|
||||||
|
|
||||||
|
function createUiNotificationService() {
|
||||||
|
return uiNotificationServicePublicApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and show a new UI notification; returns the
|
||||||
|
* ID of the created notification.
|
||||||
|
*
|
||||||
|
* @param {Notification} notification { title, message, duration, position, type, autoClose}
|
||||||
|
* @returns {number} id
|
||||||
|
*/
|
||||||
|
function show({
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
duration = 5000,
|
||||||
|
position = 'bottomRight',
|
||||||
|
type = 'info',
|
||||||
|
autoClose = true,
|
||||||
|
}) {
|
||||||
|
return uiNotificationServiceImplementation._show({
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
duration,
|
||||||
|
position,
|
||||||
|
type,
|
||||||
|
autoClose,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hides/dismisses the notification, if currently shown
|
||||||
|
*
|
||||||
|
* @param {number} id - id of the notification to hide/dismiss
|
||||||
|
* @returns undefined
|
||||||
|
*/
|
||||||
|
function hide(id) {
|
||||||
|
return uiNotificationServiceImplementation._hide({ id });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param {*} {
|
||||||
|
* hide: hideImplementation,
|
||||||
|
* show: showImplementation,
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
function setServiceImplementation({
|
||||||
|
hide: hideImplementation,
|
||||||
|
show: showImplementation,
|
||||||
|
}) {
|
||||||
|
if (hideImplementation) {
|
||||||
|
uiNotificationServiceImplementation._hide = hideImplementation;
|
||||||
|
}
|
||||||
|
if (showImplementation) {
|
||||||
|
uiNotificationServiceImplementation._show = showImplementation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default createUiNotificationService;
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import ServicesManager from './ServicesManager.js';
|
||||||
|
import createUiNotificationService from './UINotificationService';
|
||||||
|
|
||||||
|
export { createUiNotificationService, ServicesManager };
|
||||||
@@ -3,6 +3,28 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
# [0.62.0](https://github.com/OHIF/Viewers/compare/@ohif/ui@0.61.0...@ohif/ui@0.62.0) (2019-11-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* expose UiNotifications service ([#1172](https://github.com/OHIF/Viewers/issues/1172)) ([5c04e34](https://github.com/OHIF/Viewers/commit/5c04e34c8fb2394ab7acd9eb4f2ab12afeb2f255))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [0.61.0](https://github.com/OHIF/Viewers/compare/@ohif/ui@0.60.1...@ohif/ui@0.61.0) (2019-11-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* 🎸 Update hotkeys and user preferences modal ([#1135](https://github.com/OHIF/Viewers/issues/1135)) ([e62f5f8](https://github.com/OHIF/Viewers/commit/e62f5f8dd28ab363f23671cd21cee115abb870ff)), closes [#923](https://github.com/OHIF/Viewers/issues/923)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [0.60.1](https://github.com/OHIF/Viewers/compare/@ohif/ui@0.60.0...@ohif/ui@0.60.1) (2019-11-08)
|
## [0.60.1](https://github.com/OHIF/Viewers/compare/@ohif/ui@0.60.0...@ohif/ui@0.60.1) (2019-11-08)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/ui",
|
"name": "@ohif/ui",
|
||||||
"version": "0.60.1",
|
"version": "0.62.0",
|
||||||
"description": "A set of React components for Medical Imaging Viewers",
|
"description": "A set of React components for Medical Imaging Viewers",
|
||||||
"author": "OHIF Contributors",
|
"author": "OHIF Contributors",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import { TableList, TableListItem } from './tableList';
|
|||||||
import {
|
import {
|
||||||
AboutContent,
|
AboutContent,
|
||||||
UserPreferences,
|
UserPreferences,
|
||||||
UserPreferencesModal,
|
UserPreferencesForm,
|
||||||
} from './userPreferencesModal';
|
} from './userPreferencesForm';
|
||||||
|
|
||||||
import { Checkbox } from './checkbox';
|
import { Checkbox } from './checkbox';
|
||||||
import { CineDialog } from './cineDialog';
|
import { CineDialog } from './cineDialog';
|
||||||
@@ -52,6 +52,6 @@ export {
|
|||||||
Tooltip,
|
Tooltip,
|
||||||
AboutContent,
|
AboutContent,
|
||||||
UserPreferences,
|
UserPreferences,
|
||||||
UserPreferencesModal,
|
UserPreferencesForm,
|
||||||
OHIFModal,
|
OHIFModal,
|
||||||
};
|
};
|
||||||
File renamed without changes.
+23
-45
@@ -10,31 +10,14 @@ import PropTypes from 'prop-types';
|
|||||||
|
|
||||||
export class HotKeysPreferences extends Component {
|
export class HotKeysPreferences extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
hotKeysData: PropTypes.objectOf(
|
hotkeyDefinitions: PropTypes.array.isRequired,
|
||||||
PropTypes.shape({
|
|
||||||
keys: PropTypes.arrayOf(PropTypes.string).isRequired,
|
|
||||||
label: PropTypes.string.isRequired,
|
|
||||||
})
|
|
||||||
).isRequired,
|
|
||||||
onChange: PropTypes.func,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
const hotkeyCommands = Object.keys(this.props.hotKeysData);
|
|
||||||
const localHotKeys = hotkeyCommands.map(commandName => {
|
|
||||||
const definition = this.props.hotKeysData[commandName];
|
|
||||||
|
|
||||||
return {
|
|
||||||
commandName,
|
|
||||||
keys: definition.keys,
|
|
||||||
label: definition.label,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
hotKeys: localHotKeys,
|
hotKeys: this.props.hotkeyDefinitions,
|
||||||
errorMessages: {},
|
errorMessages: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -53,24 +36,28 @@ export class HotKeysPreferences extends Component {
|
|||||||
const { ctrlKey, altKey, shiftKey } = keyDownEvent;
|
const { ctrlKey, altKey, shiftKey } = keyDownEvent;
|
||||||
|
|
||||||
if (ctrlKey && !altKey) {
|
if (ctrlKey && !altKey) {
|
||||||
keysPressedArray.push('CTRL');
|
keysPressedArray.push('ctrl');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shiftKey && !altKey) {
|
if (shiftKey && !altKey) {
|
||||||
keysPressedArray.push('SHIFT');
|
keysPressedArray.push('shift');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (altKey && !ctrlKey) {
|
if (altKey && !ctrlKey) {
|
||||||
keysPressedArray.push('ALT');
|
keysPressedArray.push('alt');
|
||||||
}
|
}
|
||||||
|
|
||||||
return keysPressedArray;
|
return keysPressedArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
getConflictingCommand(currentToolKey, hotKeyCommand) {
|
getConflictingCommand(currentCommandName, currentHotKeys) {
|
||||||
return Object.keys(this.state.hotKeys).find(tool => {
|
return this.state.hotKeys.find((tool, index) => {
|
||||||
const value = this.state.hotKeys[tool].command;
|
const toolHotKeys = tool.keys[0];
|
||||||
return value && value === hotKeyCommand && tool !== currentToolKey;
|
return (
|
||||||
|
toolHotKeys &&
|
||||||
|
toolHotKeys === currentHotKeys &&
|
||||||
|
tool.commandName !== currentCommandName
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +76,7 @@ export class HotKeysPreferences extends Component {
|
|||||||
specialKeyName ||
|
specialKeyName ||
|
||||||
keyDownEvent.key ||
|
keyDownEvent.key ||
|
||||||
String.fromCharCode(keyDownEvent.keyCode);
|
String.fromCharCode(keyDownEvent.keyCode);
|
||||||
pressedKeys.push(keyName.toUpperCase());
|
pressedKeys.push(keyName);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updateHotKeysState(commandName, pressedKeys.join('+'));
|
this.updateHotKeysState(commandName, pressedKeys.join('+'));
|
||||||
@@ -136,17 +123,17 @@ export class HotKeysPreferences extends Component {
|
|||||||
const hotKey = this.state.hotKeys[hotKeyIndex];
|
const hotKey = this.state.hotKeys[hotKeyIndex];
|
||||||
const keys = hotKey.keys[0];
|
const keys = hotKey.keys[0];
|
||||||
const pressedKeys = keys.split('+');
|
const pressedKeys = keys.split('+');
|
||||||
const lastPressedKey = pressedKeys[pressedKeys.length - 1].toUpperCase();
|
const lastPressedKey = pressedKeys[pressedKeys.length - 1];
|
||||||
|
|
||||||
// clear the prior errors
|
// clear the prior errors
|
||||||
this.setState({ errorMessages: {} }, () => {
|
this.setState({ errorMessages: {} }, () => {
|
||||||
// Check if it has a valid modifier
|
// Check if it has a valid modifier
|
||||||
const isModifier = ['CTRL', 'ALT', 'SHIFT'].includes(lastPressedKey);
|
const isModifier = ['ctrl', 'alt', 'shift'].includes(lastPressedKey);
|
||||||
if (isModifier) {
|
if (isModifier) {
|
||||||
this.updateHotKeysState(commandName, '');
|
this.updateHotKeysState(commandName, '');
|
||||||
this.updateErrorsState(
|
this.updateErrorsState(
|
||||||
commandName,
|
commandName,
|
||||||
"It's not possible to define only modifier keys (CTRL, ALT and SHIFT) as a shortcut"
|
"It's not possible to define only modifier keys (ctrl, alt and shift) as a shortcut"
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -154,21 +141,13 @@ export class HotKeysPreferences extends Component {
|
|||||||
/*
|
/*
|
||||||
* Check if it has some conflict
|
* Check if it has some conflict
|
||||||
*/
|
*/
|
||||||
const conflictedCommandKey = this.getConflictingCommand(
|
const conflictedCommand = this.getConflictingCommand(commandName, keys);
|
||||||
commandName,
|
if (conflictedCommand) {
|
||||||
keys
|
this.updateHotKeysState(commandName, '');
|
||||||
);
|
|
||||||
if (conflictedCommandKey) {
|
|
||||||
const conflictedCommand = this.state.hotKeys[conflictedCommandKey];
|
|
||||||
|
|
||||||
this.updateErrorsState(
|
this.updateErrorsState(
|
||||||
commandName,
|
commandName,
|
||||||
`"${conflictedCommand.label}" is already using the "${
|
`"${conflictedCommand.label}" is already using the "${keys}" shortcut.`
|
||||||
conflictedCommand.command
|
|
||||||
}" shortcut.`
|
|
||||||
);
|
);
|
||||||
this.updateErrorsState(conflictedCommandKey, '');
|
|
||||||
this.updateHotKeysState(commandName, '');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,8 +156,7 @@ export class HotKeysPreferences extends Component {
|
|||||||
*/
|
*/
|
||||||
const modifierCommand = pressedKeys
|
const modifierCommand = pressedKeys
|
||||||
.slice(0, pressedKeys.length - 1)
|
.slice(0, pressedKeys.length - 1)
|
||||||
.join('+')
|
.join('+');
|
||||||
.toUpperCase();
|
|
||||||
|
|
||||||
const disallowedCombination = disallowedCombinations[modifierCommand];
|
const disallowedCombination = disallowedCombinations[modifierCommand];
|
||||||
const hasDisallowedCombinations = disallowedCombination
|
const hasDisallowedCombinations = disallowedCombination
|
||||||
@@ -189,7 +167,7 @@ export class HotKeysPreferences extends Component {
|
|||||||
this.updateHotKeysState(commandName, '');
|
this.updateHotKeysState(commandName, '');
|
||||||
this.updateErrorsState(
|
this.updateErrorsState(
|
||||||
commandName,
|
commandName,
|
||||||
"It's not possible to define only modifier keys (CTRL, ALT and SHIFT) as a shortcut"
|
`"${pressedKeys.join('+')}" shortcut combination is not allowed`
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
File renamed without changes.
+17
-13
@@ -8,14 +8,14 @@ import './UserPreferences.styl';
|
|||||||
|
|
||||||
export class UserPreferences extends Component {
|
export class UserPreferences extends Component {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
hotKeysData: {},
|
hotkeyDefinitions: [],
|
||||||
windowLevelData: {},
|
windowLevelData: {},
|
||||||
generalData: {},
|
generalData: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Make this more generic. Tabs should not be restricted to these entries
|
// TODO: Make this more generic. Tabs should not be restricted to these entries
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
hotKeysData: PropTypes.object.isRequired,
|
hotkeyDefinitions: PropTypes.array.isRequired,
|
||||||
windowLevelData: PropTypes.object.isRequired,
|
windowLevelData: PropTypes.object.isRequired,
|
||||||
generalData: PropTypes.object.isRequired,
|
generalData: PropTypes.object.isRequired,
|
||||||
};
|
};
|
||||||
@@ -32,7 +32,9 @@ export class UserPreferences extends Component {
|
|||||||
return (
|
return (
|
||||||
<form className="form-themed themed">
|
<form className="form-themed themed">
|
||||||
<div className="form-content">
|
<div className="form-content">
|
||||||
<HotKeysPreferences hotKeysData={this.props.hotKeysData} />
|
<HotKeysPreferences
|
||||||
|
hotkeyDefinitions={this.props.hotkeyDefinitions}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
@@ -66,8 +68,8 @@ export class UserPreferences extends Component {
|
|||||||
switch (tabIndex) {
|
switch (tabIndex) {
|
||||||
case 0:
|
case 0:
|
||||||
return this.renderHotkeysTab();
|
return this.renderHotkeysTab();
|
||||||
case 1:
|
/* case 1:
|
||||||
return this.renderWindowLevelTab();
|
return this.renderWindowLevelTab(); */
|
||||||
case 2:
|
case 2:
|
||||||
return this.renderGeneralTab();
|
return this.renderGeneralTab();
|
||||||
|
|
||||||
@@ -93,14 +95,16 @@ export class UserPreferences extends Component {
|
|||||||
>
|
>
|
||||||
<button>Hotkeys</button>
|
<button>Hotkeys</button>
|
||||||
</li>
|
</li>
|
||||||
<li
|
{false && (
|
||||||
onClick={() => {
|
<li
|
||||||
this.tabClick(1);
|
onClick={() => {
|
||||||
}}
|
this.tabClick(1);
|
||||||
className={this.getTabClass(1)}
|
}}
|
||||||
>
|
className={this.getTabClass(1)}
|
||||||
<button>Window Level</button>
|
>
|
||||||
</li>
|
<button>Window Level</button>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
<li
|
<li
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
this.tabClick(2);
|
this.tabClick(2);
|
||||||
File renamed without changes.
@@ -0,0 +1,86 @@
|
|||||||
|
import './UserPreferencesForm.styl';
|
||||||
|
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { withTranslation } from '../../utils/LanguageProvider';
|
||||||
|
|
||||||
|
import cloneDeep from 'lodash.clonedeep';
|
||||||
|
import isEqual from 'lodash.isequal';
|
||||||
|
import { UserPreferences } from './UserPreferences';
|
||||||
|
|
||||||
|
class UserPreferencesForm extends Component {
|
||||||
|
// TODO: Make this component more generic to allow things other than W/L and hotkeys...
|
||||||
|
static propTypes = {
|
||||||
|
onClose: PropTypes.func,
|
||||||
|
onSave: PropTypes.func,
|
||||||
|
onResetToDefaults: PropTypes.func,
|
||||||
|
windowLevelData: PropTypes.object,
|
||||||
|
hotkeyDefinitions: PropTypes.array,
|
||||||
|
t: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
windowLevelData: cloneDeep(props.windowLevelData),
|
||||||
|
hotkeyDefinitions: cloneDeep(props.hotkeyDefinitions),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
save = () => {
|
||||||
|
this.props.onSave({
|
||||||
|
windowLevelData: this.state.windowLevelData,
|
||||||
|
hotkeyDefinitions: this.state.hotkeyDefinitions,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
componentDidUpdate(prev, next) {
|
||||||
|
const newStateData = {};
|
||||||
|
|
||||||
|
if (!isEqual(prev.windowLevelData, next.windowLevelData)) {
|
||||||
|
newStateData.windowLevelData = prev.windowLevelData;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isEqual(prev.hotkeyDefinitions, next.hotkeyDefinitions)) {
|
||||||
|
newStateData.hotkeyDefinitions = prev.hotkeyDefinitions;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newStateData.hotkeyDefinitions || newStateData.windowLevelData) {
|
||||||
|
this.setState(newStateData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="UserPreferencesForm">
|
||||||
|
<UserPreferences
|
||||||
|
windowLevelData={this.state.windowLevelData}
|
||||||
|
hotkeyDefinitions={this.state.hotkeyDefinitions}
|
||||||
|
/>
|
||||||
|
<div className="footer">
|
||||||
|
<button
|
||||||
|
className="btn btn-danger pull-left"
|
||||||
|
onClick={this.props.onResetToDefaults}
|
||||||
|
>
|
||||||
|
{this.props.t('Reset to Defaults')}
|
||||||
|
</button>
|
||||||
|
<div>
|
||||||
|
<div onClick={this.props.onClose} className="btn btn-default">
|
||||||
|
{this.props.t('Cancel')}
|
||||||
|
</div>
|
||||||
|
<button className="btn btn-primary" onClick={this.save}>
|
||||||
|
{this.props.t('Save')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const connectedComponent = withTranslation('UserPreferencesForm')(
|
||||||
|
UserPreferencesForm
|
||||||
|
);
|
||||||
|
export { connectedComponent as UserPreferencesForm };
|
||||||
|
export default connectedComponent;
|
||||||
+10
-3
@@ -12,6 +12,13 @@
|
|||||||
text-shadow: 0 1px 0 #fff;
|
text-shadow: 0 1px 0 #fff;
|
||||||
opacity: .2;
|
opacity: .2;
|
||||||
|
|
||||||
.ModalHeader
|
.UserPreferencesForm
|
||||||
ol, ul
|
.footer
|
||||||
margin-top: 0;
|
display: flex
|
||||||
|
flex-direction: row
|
||||||
|
padding-bottom: 20px
|
||||||
|
justify-content: space-between
|
||||||
|
|
||||||
|
div
|
||||||
|
button:last-child
|
||||||
|
margin-left: 10px
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+8
-8
@@ -1,18 +1,18 @@
|
|||||||
---
|
---
|
||||||
name: User Preferences Modal
|
name: User Preferences Form
|
||||||
menu: Components
|
menu: Components
|
||||||
route: /components/user-preferences-modal
|
route: /components/user-preferences-form
|
||||||
---
|
---
|
||||||
|
|
||||||
import { Playground, Props } from 'docz'
|
import { Playground, Props } from 'docz'
|
||||||
import { State } from 'react-powerplug'
|
import { State } from 'react-powerplug'
|
||||||
import { UserPreferencesModal } from './../index.js'
|
import { UserPreferencesForm } from './../index.js'
|
||||||
import NameSpace from '../../../__docs__/NameSpace'
|
import NameSpace from '../../../__docs__/NameSpace'
|
||||||
//
|
//
|
||||||
import windowLevelDefaults from './windowLevelDefaults.js'
|
import windowLevelDefaults from './windowLevelDefaults.js'
|
||||||
import hotkeyDefaults from './hotkeyDefaults.js'
|
import hotkeyDefaults from './hotkeyDefaults.js'
|
||||||
|
|
||||||
# User Preferences Modal
|
# User Preferences Form
|
||||||
|
|
||||||
## Basic usage
|
## Basic usage
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ import hotkeyDefaults from './hotkeyDefaults.js'
|
|||||||
<State initial={{
|
<State initial={{
|
||||||
isOpen: false,
|
isOpen: false,
|
||||||
windowLevelData: windowLevelDefaults,
|
windowLevelData: windowLevelDefaults,
|
||||||
hotKeysData: hotkeyDefaults,
|
hotkeyDefinitions: hotkeyDefaults,
|
||||||
}}>
|
}}>
|
||||||
|
|
||||||
{({ state, setState }) => (
|
{({ state, setState }) => (
|
||||||
@@ -32,7 +32,7 @@ import hotkeyDefaults from './hotkeyDefaults.js'
|
|||||||
>
|
>
|
||||||
Open user preferences
|
Open user preferences
|
||||||
</button>
|
</button>
|
||||||
<UserPreferencesModal
|
<UserPreferencesForm
|
||||||
{...state}
|
{...state}
|
||||||
onCancel={() => setState({ isOpen: false })}
|
onCancel={() => setState({ isOpen: false })}
|
||||||
onSave={() => alert('on save')}
|
onSave={() => alert('on save')}
|
||||||
@@ -47,8 +47,8 @@ import hotkeyDefaults from './hotkeyDefaults.js'
|
|||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
<Props of={UserPreferencesModal} />
|
<Props of={UserPreferencesForm} />
|
||||||
|
|
||||||
## Translation Namespace
|
## Translation Namespace
|
||||||
|
|
||||||
<NameSpace name="UserPreferencesModal" />
|
<NameSpace name="UserPreferencesForm" />
|
||||||
File renamed without changes.
+28
-28
@@ -4,38 +4,38 @@ const range = (start, end) => {
|
|||||||
|
|
||||||
export const disallowedCombinations = {
|
export const disallowedCombinations = {
|
||||||
'': [],
|
'': [],
|
||||||
ALT: ['SPACE'],
|
alt: ['space'],
|
||||||
SHIFT: [],
|
shift: [],
|
||||||
CTRL: [
|
ctrl: [
|
||||||
'F4',
|
'f4',
|
||||||
'F5',
|
'f5',
|
||||||
'F11',
|
'f11',
|
||||||
'W',
|
'w',
|
||||||
'R',
|
'r',
|
||||||
'T',
|
't',
|
||||||
'O',
|
'o',
|
||||||
'P',
|
'p',
|
||||||
'A',
|
'a',
|
||||||
'D',
|
'd',
|
||||||
'F',
|
'f',
|
||||||
'G',
|
'g',
|
||||||
'H',
|
'h',
|
||||||
'J',
|
'j',
|
||||||
'L',
|
'l',
|
||||||
'Z',
|
'z',
|
||||||
'X',
|
'x',
|
||||||
'C',
|
'c',
|
||||||
'V',
|
'v',
|
||||||
'B',
|
'b',
|
||||||
'N',
|
'n',
|
||||||
'PAGEDOWN',
|
'pagedown',
|
||||||
'PAGEUP',
|
'pageup',
|
||||||
],
|
],
|
||||||
'CTRL+SHIFT': ['Q', 'W', 'R', 'T', 'P', 'A', 'H', 'V', 'B', 'N'],
|
'ctrl+shift': ['q', 'w', 'r', 't', 'p', 'a', 'h', 'v', 'b', 'n'],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const allowedKeys = [
|
export const allowedKeys = [
|
||||||
...[8, 13, 27, 32, 46], // BACKSPACE, ENTER, ESCAPE, SPACE, DELETE
|
...[8, 13, 27, 32, 46], // backspace, enter, escape, space, delete
|
||||||
...[12, 106, 107, 109, 110, 111], // Numpad keys
|
...[12, 106, 107, 109, 110, 111], // Numpad keys
|
||||||
...range(218, 220), // [\]
|
...range(218, 220), // [\]
|
||||||
...range(185, 190), // ;=,-./
|
...range(185, 190), // ;=,-./
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
export { UserPreferences } from './UserPreferences.js';
|
export { UserPreferences } from './UserPreferences.js';
|
||||||
export { AboutContent } from '../content/aboutContent/AboutContent.js';
|
export { AboutContent } from '../content/aboutContent/AboutContent.js';
|
||||||
export { UserPreferencesModal } from './UserPreferencesModal.js';
|
export { UserPreferencesForm } from './UserPreferencesForm.js';
|
||||||
export { GeneralPreferences } from './GeneralPreferences.js';
|
export { GeneralPreferences } from './GeneralPreferences.js';
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
import './UserPreferencesModal.styl';
|
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import Modal from 'react-bootstrap-modal';
|
|
||||||
import { withTranslation } from '../../utils/LanguageProvider';
|
|
||||||
|
|
||||||
import 'react-bootstrap-modal/lib/css/rbm-patch.css';
|
|
||||||
import cloneDeep from 'lodash.clonedeep';
|
|
||||||
import isEqual from 'lodash.isequal';
|
|
||||||
import { UserPreferences } from './UserPreferences';
|
|
||||||
|
|
||||||
// TODO: Is this the only component importing these?
|
|
||||||
import './../../design/styles/common/modal.styl';
|
|
||||||
|
|
||||||
class UserPreferencesModal extends Component {
|
|
||||||
// TODO: Make this component more generic to allow things other than W/L and hotkeys...
|
|
||||||
static propTypes = {
|
|
||||||
isOpen: PropTypes.bool.isRequired,
|
|
||||||
onCancel: PropTypes.func,
|
|
||||||
onSave: PropTypes.func,
|
|
||||||
onResetToDefaults: PropTypes.func,
|
|
||||||
windowLevelData: PropTypes.object,
|
|
||||||
hotKeysData: PropTypes.object,
|
|
||||||
t: PropTypes.func,
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
windowLevelData: cloneDeep(props.windowLevelData),
|
|
||||||
hotKeysData: cloneDeep(props.hotKeysData),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static defaultProps = {
|
|
||||||
isOpen: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
save = () => {
|
|
||||||
this.props.onSave({
|
|
||||||
windowLevelData: this.state.windowLevelData,
|
|
||||||
hotKeysData: this.state.hotKeysData,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidUpdate(prev, next) {
|
|
||||||
const newStateData = {};
|
|
||||||
|
|
||||||
if (!isEqual(prev.windowLevelData, next.windowLevelData)) {
|
|
||||||
newStateData.windowLevelData = prev.windowLevelData;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isEqual(prev.hotKeysData, next.hotKeysData)) {
|
|
||||||
newStateData.hotKeysData = prev.hotKeysData;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newStateData.hotKeysData || newStateData.windowLevelData) {
|
|
||||||
this.setState(newStateData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
show={this.props.isOpen}
|
|
||||||
onHide={this.props.onCancel}
|
|
||||||
aria-labelledby="ModalHeader"
|
|
||||||
className="ModalHeader modal fade themed in"
|
|
||||||
backdrop={false}
|
|
||||||
large={true}
|
|
||||||
keyboard={false}
|
|
||||||
>
|
|
||||||
<Modal.Header closeButton>
|
|
||||||
<Modal.Title>{this.props.t('User Preferences')}</Modal.Title>
|
|
||||||
</Modal.Header>
|
|
||||||
<Modal.Body>
|
|
||||||
<UserPreferences
|
|
||||||
windowLevelData={this.state.windowLevelData}
|
|
||||||
hotKeysData={this.state.hotKeysData}
|
|
||||||
/>
|
|
||||||
</Modal.Body>
|
|
||||||
<Modal.Footer>
|
|
||||||
<button
|
|
||||||
className="btn btn-danger pull-left"
|
|
||||||
onClick={this.props.onResetToDefaults}
|
|
||||||
>
|
|
||||||
{this.props.t('Reset to Defaults')}
|
|
||||||
</button>
|
|
||||||
<Modal.Dismiss className="btn btn-default">
|
|
||||||
{this.props.t('Cancel')}
|
|
||||||
</Modal.Dismiss>
|
|
||||||
<button className="btn btn-primary" onClick={this.save}>
|
|
||||||
{this.props.t('Save')}
|
|
||||||
</button>
|
|
||||||
</Modal.Footer>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const connectedComponent = withTranslation('UserPreferencesModal')(
|
|
||||||
UserPreferencesModal
|
|
||||||
);
|
|
||||||
export { connectedComponent as UserPreferencesModal };
|
|
||||||
export default connectedComponent;
|
|
||||||
@@ -24,7 +24,7 @@ import {
|
|||||||
Tooltip,
|
Tooltip,
|
||||||
AboutContent,
|
AboutContent,
|
||||||
UserPreferences,
|
UserPreferences,
|
||||||
UserPreferencesModal,
|
UserPreferencesForm,
|
||||||
OHIFModal,
|
OHIFModal,
|
||||||
} from './components';
|
} from './components';
|
||||||
import { useDebounce, useMedia } from './hooks';
|
import { useDebounce, useMedia } from './hooks';
|
||||||
@@ -101,7 +101,7 @@ export {
|
|||||||
Tooltip,
|
Tooltip,
|
||||||
AboutContent,
|
AboutContent,
|
||||||
UserPreferences,
|
UserPreferences,
|
||||||
UserPreferencesModal,
|
UserPreferencesForm,
|
||||||
ViewerbaseDragDropContext,
|
ViewerbaseDragDropContext,
|
||||||
SnackbarProvider,
|
SnackbarProvider,
|
||||||
useSnackbarContext,
|
useSnackbarContext,
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
import React, { useState, createContext, useContext } from 'react';
|
import React, {
|
||||||
|
useState,
|
||||||
|
createContext,
|
||||||
|
useContext,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
} from 'react';
|
||||||
import SnackbarContainer from '../components/snackbar/SnackbarContainer';
|
import SnackbarContainer from '../components/snackbar/SnackbarContainer';
|
||||||
import SnackbarTypes from '../components/snackbar/SnackbarTypes';
|
import SnackbarTypes from '../components/snackbar/SnackbarTypes';
|
||||||
|
|
||||||
@@ -6,7 +12,7 @@ const SnackbarContext = createContext(null);
|
|||||||
|
|
||||||
export const useSnackbarContext = () => useContext(SnackbarContext);
|
export const useSnackbarContext = () => useContext(SnackbarContext);
|
||||||
|
|
||||||
const SnackbarProvider = ({ children }) => {
|
const SnackbarProvider = ({ children, service }) => {
|
||||||
const DEFAULT_OPTIONS = {
|
const DEFAULT_OPTIONS = {
|
||||||
title: '',
|
title: '',
|
||||||
message: '',
|
message: '',
|
||||||
@@ -19,7 +25,11 @@ const SnackbarProvider = ({ children }) => {
|
|||||||
const [count, setCount] = useState(1);
|
const [count, setCount] = useState(1);
|
||||||
const [snackbarItems, setSnackbarItems] = useState([]);
|
const [snackbarItems, setSnackbarItems] = useState([]);
|
||||||
|
|
||||||
const show = options => {
|
useEffect(() => {
|
||||||
|
service.setServiceImplementation({ hide, show });
|
||||||
|
}, [service, hide, show]);
|
||||||
|
|
||||||
|
const show = useCallback(options => {
|
||||||
if (!options || (!options.title && !options.message)) {
|
if (!options || (!options.title && !options.message)) {
|
||||||
console.warn(
|
console.warn(
|
||||||
'Snackbar cannot be rendered without required parameters: title | message'
|
'Snackbar cannot be rendered without required parameters: title | message'
|
||||||
@@ -37,9 +47,9 @@ const SnackbarProvider = ({ children }) => {
|
|||||||
|
|
||||||
setSnackbarItems(state => [...state, newItem]);
|
setSnackbarItems(state => [...state, newItem]);
|
||||||
setCount(count + 1);
|
setCount(count + 1);
|
||||||
};
|
});
|
||||||
|
|
||||||
const hide = id => {
|
const hide = useCallback(id => {
|
||||||
const hideItem = items => {
|
const hideItem = items => {
|
||||||
const newItems = items.map(item => {
|
const newItems = items.map(item => {
|
||||||
if (item.id === id) {
|
if (item.id === id) {
|
||||||
@@ -57,7 +67,7 @@ const SnackbarProvider = ({ children }) => {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setSnackbarItems(state => [...state.filter(item => item.id !== id)]);
|
setSnackbarItems(state => [...state.filter(item => item.id !== id)]);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
};
|
});
|
||||||
|
|
||||||
const hideAll = () => {
|
const hideAll = () => {
|
||||||
// reset count
|
// reset count
|
||||||
|
|||||||
@@ -3,6 +3,40 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
# [2.8.0](https://github.com/OHIF/Viewers/compare/@ohif/viewer@2.7.1...@ohif/viewer@2.8.0) (2019-11-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* expose UiNotifications service ([#1172](https://github.com/OHIF/Viewers/issues/1172)) ([5c04e34](https://github.com/OHIF/Viewers/commit/5c04e34c8fb2394ab7acd9eb4f2ab12afeb2f255))
|
||||||
|
* filter field for google api windows ([#1170](https://github.com/OHIF/Viewers/issues/1170)) ([c59c5b3](https://github.com/OHIF/Viewers/commit/c59c5b3f14d44f1c06aa396125a1f4caaa431c25))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.7.1](https://github.com/OHIF/Viewers/compare/@ohif/viewer@2.7.0...@ohif/viewer@2.7.1) (2019-11-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 🐛 Fix for JS breaking on header ([#1164](https://github.com/OHIF/Viewers/issues/1164)) ([0fbaf95](https://github.com/OHIF/Viewers/commit/0fbaf95971dc0b3a671e1f586a876d9019e860ed))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [2.7.0](https://github.com/OHIF/Viewers/compare/@ohif/viewer@2.6.4...@ohif/viewer@2.7.0) (2019-11-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* 🎸 Update hotkeys and user preferences modal ([#1135](https://github.com/OHIF/Viewers/issues/1135)) ([e62f5f8](https://github.com/OHIF/Viewers/commit/e62f5f8dd28ab363f23671cd21cee115abb870ff)), closes [#923](https://github.com/OHIF/Viewers/issues/923)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [2.6.4](https://github.com/OHIF/Viewers/compare/@ohif/viewer@2.6.3...@ohif/viewer@2.6.4) (2019-11-11)
|
## [2.6.4](https://github.com/OHIF/Viewers/compare/@ohif/viewer@2.6.3...@ohif/viewer@2.6.4) (2019-11-11)
|
||||||
|
|
||||||
**Note:** Version bump only for package @ohif/viewer
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|||||||
@@ -267,8 +267,12 @@ describe('OHIF Study Viewer Page', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('opens About modal and verify the displayed information', function() {
|
it('opens About modal and verify the displayed information', function() {
|
||||||
cy.get('[data-cy="options-menu"]').click();
|
cy.get('[data-cy="options-menu"]')
|
||||||
cy.get('[data-cy="about-item-menu"]').click();
|
.first()
|
||||||
|
.click();
|
||||||
|
cy.get('[data-cy="about-item-menu"]')
|
||||||
|
.first()
|
||||||
|
.click();
|
||||||
cy.get('.modal-content')
|
cy.get('.modal-content')
|
||||||
.as('aboutOverlay')
|
.as('aboutOverlay')
|
||||||
.should('be.visible');
|
.should('be.visible');
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/viewer",
|
"name": "@ohif/viewer",
|
||||||
"version": "2.6.4",
|
"version": "2.8.0",
|
||||||
"description": "OHIF Viewer",
|
"description": "OHIF Viewer",
|
||||||
"author": "OHIF Contributors",
|
"author": "OHIF Contributors",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -45,14 +45,14 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.5.5",
|
"@babel/runtime": "^7.5.5",
|
||||||
"@ohif/core": "^1.7.1",
|
"@ohif/core": "^1.9.0",
|
||||||
"@ohif/extension-cornerstone": "^2.0.0",
|
"@ohif/extension-cornerstone": "^2.0.0",
|
||||||
"@ohif/extension-dicom-html": "^1.0.1",
|
"@ohif/extension-dicom-html": "^1.0.1",
|
||||||
"@ohif/extension-dicom-microscopy": "^0.50.6",
|
"@ohif/extension-dicom-microscopy": "^0.50.6",
|
||||||
"@ohif/extension-dicom-pdf": "^1.0.0",
|
"@ohif/extension-dicom-pdf": "^1.0.0",
|
||||||
"@ohif/extension-vtk": "^0.53.3",
|
"@ohif/extension-vtk": "^0.53.5",
|
||||||
"@ohif/i18n": "^0.52.0",
|
"@ohif/i18n": "^0.52.0",
|
||||||
"@ohif/ui": "^0.60.1",
|
"@ohif/ui": "^0.62.0",
|
||||||
"@tanem/react-nprogress": "^1.1.25",
|
"@tanem/react-nprogress": "^1.1.25",
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
"core-js": "^3.2.1",
|
"core-js": "^3.2.1",
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ import './config';
|
|||||||
import {
|
import {
|
||||||
CommandsManager,
|
CommandsManager,
|
||||||
ExtensionManager,
|
ExtensionManager,
|
||||||
|
ServicesManager,
|
||||||
HotkeysManager,
|
HotkeysManager,
|
||||||
|
createUiNotificationService,
|
||||||
utils,
|
utils,
|
||||||
} from '@ohif/core';
|
} from '@ohif/core';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
@@ -41,9 +43,16 @@ const commandsManagerConfig = {
|
|||||||
getActiveContexts: () => getActiveContexts(store.getState()),
|
getActiveContexts: () => getActiveContexts(store.getState()),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Services
|
||||||
|
const UINotificationService = createUiNotificationService();
|
||||||
|
|
||||||
const commandsManager = new CommandsManager(commandsManagerConfig);
|
const commandsManager = new CommandsManager(commandsManagerConfig);
|
||||||
const hotkeysManager = new HotkeysManager(commandsManager);
|
const hotkeysManager = new HotkeysManager(commandsManager);
|
||||||
const extensionManager = new ExtensionManager({ commandsManager });
|
const servicesManager = new ServicesManager();
|
||||||
|
const extensionManager = new ExtensionManager({
|
||||||
|
commandsManager,
|
||||||
|
servicesManager,
|
||||||
|
});
|
||||||
// ~~~~ END APP SETUP
|
// ~~~~ END APP SETUP
|
||||||
|
|
||||||
// TODO[react] Use a provider when the whole tree is React
|
// TODO[react] Use a provider when the whole tree is React
|
||||||
@@ -76,9 +85,11 @@ class App extends Component {
|
|||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this._appConfig = props;
|
this._appConfig = props;
|
||||||
|
|
||||||
const { servers, extensions, hotkeys, oidc } = props;
|
const { servers, extensions, hotkeys, oidc } = props;
|
||||||
|
|
||||||
this.initUserManager(oidc);
|
this.initUserManager(oidc);
|
||||||
|
_initServices([UINotificationService]);
|
||||||
_initExtensions(extensions, hotkeys);
|
_initExtensions(extensions, hotkeys);
|
||||||
_initServers(servers);
|
_initServers(servers);
|
||||||
initWebWorkers();
|
initWebWorkers();
|
||||||
@@ -100,7 +111,7 @@ class App extends Component {
|
|||||||
<UserManagerContext.Provider value={userManager}>
|
<UserManagerContext.Provider value={userManager}>
|
||||||
<Router basename={routerBasename}>
|
<Router basename={routerBasename}>
|
||||||
<WhiteLabellingContext.Provider value={whiteLabelling}>
|
<WhiteLabellingContext.Provider value={whiteLabelling}>
|
||||||
<SnackbarProvider>
|
<SnackbarProvider service={UINotificationService}>
|
||||||
<ModalProvider modal={OHIFModal}>
|
<ModalProvider modal={OHIFModal}>
|
||||||
<OHIFStandaloneViewer userManager={userManager} />
|
<OHIFStandaloneViewer userManager={userManager} />
|
||||||
</ModalProvider>
|
</ModalProvider>
|
||||||
@@ -121,7 +132,7 @@ class App extends Component {
|
|||||||
<I18nextProvider i18n={i18n}>
|
<I18nextProvider i18n={i18n}>
|
||||||
<Router basename={routerBasename}>
|
<Router basename={routerBasename}>
|
||||||
<WhiteLabellingContext.Provider value={whiteLabelling}>
|
<WhiteLabellingContext.Provider value={whiteLabelling}>
|
||||||
<SnackbarProvider>
|
<SnackbarProvider service={UINotificationService}>
|
||||||
<ModalProvider modal={OHIFModal}>
|
<ModalProvider modal={OHIFModal}>
|
||||||
<OHIFStandaloneViewer />
|
<OHIFStandaloneViewer />
|
||||||
</ModalProvider>
|
</ModalProvider>
|
||||||
@@ -168,6 +179,10 @@ class App extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _initServices(services) {
|
||||||
|
services.forEach(service => servicesManager.register(service));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param
|
* @param
|
||||||
*/
|
*/
|
||||||
@@ -214,4 +229,4 @@ function _makeAbsoluteIfNecessary(url, base_url) {
|
|||||||
const ExportedApp = process.env.NODE_ENV === 'development' ? hot(App) : App;
|
const ExportedApp = process.env.NODE_ENV === 'development' ? hot(App) : App;
|
||||||
|
|
||||||
export default ExportedApp;
|
export default ExportedApp;
|
||||||
export { commandsManager, extensionManager, hotkeysManager };
|
export { commandsManager, extensionManager, hotkeysManager, servicesManager };
|
||||||
@@ -7,8 +7,8 @@ export default {
|
|||||||
*/
|
*/
|
||||||
id: 'measurements-table',
|
id: 'measurements-table',
|
||||||
|
|
||||||
preRegistration(configuration = {}) {
|
preRegistration({ serviceManager, configuration = {} }) {
|
||||||
init(configuration);
|
init({ serviceManager, configuration });
|
||||||
},
|
},
|
||||||
getPanelModule() {
|
getPanelModule() {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ const MEASUREMENT_ACTION_MAP = {
|
|||||||
* @export
|
* @export
|
||||||
* @param {*} configuration
|
* @param {*} configuration
|
||||||
*/
|
*/
|
||||||
export default function init(configuration) {
|
export default function init({ serviceManager, configuration = {} }) {
|
||||||
// If these tools were already added by a different extension, we want to replace
|
// If these tools were already added by a different extension, we want to replace
|
||||||
// them with the same tools that have an alternative configuration. By passing in
|
// them with the same tools that have an alternative configuration. By passing in
|
||||||
// our custom `getMeasurementLocationCallback`, we can...
|
// our custom `getMeasurementLocationCallback`, we can...
|
||||||
|
|||||||
@@ -4,6 +4,10 @@
|
|||||||
height: var(--top-bar-height);
|
height: var(--top-bar-height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dd-item {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
/* Home Page */
|
/* Home Page */
|
||||||
.entry-header.header-big {
|
.entry-header.header-big {
|
||||||
background: rgba(21, 25, 30, 0.7);
|
background: rgba(21, 25, 30, 0.7);
|
||||||
|
|||||||
@@ -3,13 +3,11 @@ import { Link, withRouter } from 'react-router-dom';
|
|||||||
import { withTranslation } from 'react-i18next';
|
import { withTranslation } from 'react-i18next';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { Dropdown } from '@ohif/ui';
|
import ConnectedUserPreferencesForm from '../../connectedComponents/ConnectedUserPreferencesForm';
|
||||||
import { AboutContent } from '@ohif/ui';
|
import { Dropdown, AboutContent, withModal } from '@ohif/ui';
|
||||||
import { withModal } from '@ohif/ui';
|
|
||||||
|
|
||||||
import OHIFLogo from '../OHIFLogo/OHIFLogo.js';
|
import OHIFLogo from '../OHIFLogo/OHIFLogo.js';
|
||||||
import { hotkeysManager } from './../../App.js';
|
|
||||||
import './Header.css';
|
import './Header.css';
|
||||||
|
|
||||||
// Context
|
// Context
|
||||||
import AppContext from './../../context/AppContext';
|
import AppContext from './../../context/AppContext';
|
||||||
|
|
||||||
@@ -30,23 +28,9 @@ class Header extends Component {
|
|||||||
children: OHIFLogo(),
|
children: OHIFLogo(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// onSave: data => {
|
|
||||||
// const contextName = store.getState().commandContext.context;
|
|
||||||
// const preferences = cloneDeep(store.getState().preferences);
|
|
||||||
// preferences[contextName] = data;
|
|
||||||
// dispatch(setUserPreferences(preferences));
|
|
||||||
// dispatch(setUserPreferencesModalOpen(false));
|
|
||||||
// OHIF.hotkeysUtil.setHotkeys(data.hotKeysData);
|
|
||||||
// },
|
|
||||||
// onResetToDefaults: () => {
|
|
||||||
// dispatch(setUserPreferences());
|
|
||||||
// dispatch(setUserPreferencesModalOpen(false));
|
|
||||||
// OHIF.hotkeysUtil.setHotkeys();
|
|
||||||
// },
|
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = { isUserPreferencesOpen: false, isOpen: false };
|
this.state = { isOpen: false };
|
||||||
|
|
||||||
this.loadOptions();
|
this.loadOptions();
|
||||||
}
|
}
|
||||||
@@ -68,6 +52,16 @@ class Header extends Component {
|
|||||||
customClassName: 'AboutContent',
|
customClassName: 'AboutContent',
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: 'Preferences ',
|
||||||
|
icon: {
|
||||||
|
name: 'user',
|
||||||
|
},
|
||||||
|
onClick: () =>
|
||||||
|
show(ConnectedUserPreferencesForm, {
|
||||||
|
title: t('User Preferences'),
|
||||||
|
}),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
if (user && userManager) {
|
if (user && userManager) {
|
||||||
@@ -77,15 +71,6 @@ class Header extends Component {
|
|||||||
onClick: () => userManager.signoutRedirect(),
|
onClick: () => userManager.signoutRedirect(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.hotKeysData = hotkeysManager.hotkeyDefinitions;
|
|
||||||
}
|
|
||||||
|
|
||||||
onUserPreferencesSave({ windowLevelData, hotKeysData }) {
|
|
||||||
// console.log(windowLevelData);
|
|
||||||
// console.log(hotKeysData);
|
|
||||||
// TODO: Update hotkeysManager
|
|
||||||
// TODO: reset `this.hotKeysData`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ANTD -- Hamburger, Drawer, Menu
|
// ANTD -- Hamburger, Drawer, Menu
|
||||||
|
|||||||
@@ -1,10 +1,19 @@
|
|||||||
import Header from '../components/Header/Header.js';
|
import Header from '../components/Header/Header.js';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
import { hotkeysManager } from '../App.js';
|
||||||
|
|
||||||
const mapStateToProps = state => {
|
const mapStateToProps = state => {
|
||||||
|
const { hotkeyDefinitions = [] } = state.preferences || {};
|
||||||
|
|
||||||
|
const _hotkeyDefinitions =
|
||||||
|
hotkeyDefinitions.length > 0
|
||||||
|
? hotkeyDefinitions
|
||||||
|
: hotkeysManager.hotkeyDefaults;
|
||||||
|
|
||||||
|
hotkeysManager.setHotkeys(_hotkeyDefinitions);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
user: state.oidc && state.oidc.user,
|
user: state.oidc && state.oidc.user,
|
||||||
isOpen: state.ui.userPreferencesModalOpen,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { UserPreferencesForm } from '@ohif/ui';
|
||||||
|
import OHIF from '@ohif/core';
|
||||||
|
import { hotkeysManager } from '../App.js';
|
||||||
|
|
||||||
|
const { setUserPreferences } = OHIF.redux.actions;
|
||||||
|
|
||||||
|
const mapStateToProps = (state, ownProps) => {
|
||||||
|
const hotkeyDefinitions =
|
||||||
|
state.preferences.hotkeyDefinitions.length > 0
|
||||||
|
? state.preferences.hotkeyDefinitions
|
||||||
|
: hotkeysManager.hotkeyDefaults;
|
||||||
|
hotkeysManager.setHotkeys(hotkeyDefinitions);
|
||||||
|
return {
|
||||||
|
onClose: ownProps.hide,
|
||||||
|
windowLevelData: state.preferences ? state.preferences.windowLevelData : {},
|
||||||
|
hotkeyDefinitions,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch, ownProps) => {
|
||||||
|
return {
|
||||||
|
onSave: ({ windowLevelData, hotkeyDefinitions }) => {
|
||||||
|
hotkeysManager.setHotkeys(hotkeyDefinitions);
|
||||||
|
ownProps.hide();
|
||||||
|
dispatch(setUserPreferences({ windowLevelData, hotkeyDefinitions }));
|
||||||
|
},
|
||||||
|
onResetToDefaults: () => {
|
||||||
|
hotkeysManager.restoreDefaultBindings();
|
||||||
|
ownProps.hide();
|
||||||
|
dispatch(setUserPreferences());
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const ConnectedUserPreferencesForm = connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(UserPreferencesForm);
|
||||||
|
|
||||||
|
export default ConnectedUserPreferencesForm;
|
||||||
@@ -9,6 +9,7 @@ export default class DatasetPicker extends Component {
|
|||||||
error: null,
|
error: null,
|
||||||
loading: true,
|
loading: true,
|
||||||
datasets: [],
|
datasets: [],
|
||||||
|
filterStr: ""
|
||||||
};
|
};
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
@@ -41,15 +42,23 @@ export default class DatasetPicker extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { datasets, loading, error } = this.state;
|
const { datasets, loading, error, filterStr } = this.state;
|
||||||
const { onSelect } = this.props;
|
const { onSelect } = this.props;
|
||||||
return (
|
return (
|
||||||
|
<div>
|
||||||
|
<input class="form-control gcp-input"
|
||||||
|
type="text"
|
||||||
|
value={ filterStr }
|
||||||
|
onChange={ e => this.setState({ filterStr: e.target.value }) }
|
||||||
|
/>
|
||||||
<DatasetsList
|
<DatasetsList
|
||||||
datasets={datasets}
|
datasets={datasets}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
error={error}
|
error={error}
|
||||||
|
filter={filterStr}
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -46,21 +46,23 @@ class DatasetsList extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (this.props.error) {
|
const { loading, datasets, filter, error } = this.props;
|
||||||
return <p>{this.props.error}</p>;
|
|
||||||
|
if (error) {
|
||||||
|
return <p>{error}</p>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadingIcon = (
|
const loadingIcon = (
|
||||||
<Icon name="circle-notch" className="loading-icon-spin loading-icon" />
|
<Icon name="circle-notch" className="loading-icon-spin loading-icon" />
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this.props.loading) {
|
if (loading) {
|
||||||
return loadingIcon;
|
return loadingIcon;
|
||||||
}
|
}
|
||||||
|
|
||||||
const body = (
|
const body = (
|
||||||
<tbody id="DatasetList">
|
<tbody id="DatasetList">
|
||||||
{this.props.datasets.map(this.renderTableRow)}
|
{datasets.filter(dataset => (dataset.name.split('/')[5].toLowerCase().includes(filter.toLowerCase()) || filter=="")).map(this.renderTableRow)}
|
||||||
</tbody>
|
</tbody>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -71,7 +73,7 @@ class DatasetsList extends Component {
|
|||||||
<th>{this.props.t('Dataset')}</th>
|
<th>{this.props.t('Dataset')}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
{this.props.datasets && body}
|
{datasets && body}
|
||||||
</table>
|
</table>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,20 +46,24 @@ class DicomStoreList extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (this.props.error) {
|
const { loading, stores, filter, error } = this.props;
|
||||||
return <p>{this.props.error}</p>;
|
|
||||||
|
if (error) {
|
||||||
|
return <p>{error}</p>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadingIcon = (
|
const loadingIcon = (
|
||||||
<Icon name="circle-notch" className="loading-icon-spin loading-icon" />
|
<Icon name="circle-notch" className="loading-icon-spin loading-icon" />
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this.props.loading) {
|
if (loading) {
|
||||||
return loadingIcon;
|
return loadingIcon;
|
||||||
}
|
}
|
||||||
|
|
||||||
const body = (
|
const body = (
|
||||||
<tbody id="StoreList">{this.props.stores.map(this.renderTableRow)}</tbody>
|
<tbody id="StoreList">{
|
||||||
|
stores.filter(store => (store.name.split('/')[7].toLowerCase().includes(filter.toLowerCase()) || filter=="")).map(this.renderTableRow)}
|
||||||
|
</tbody>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -69,7 +73,7 @@ class DicomStoreList extends Component {
|
|||||||
<th>{this.props.t('DICOM Store')}</th>
|
<th>{this.props.t('DICOM Store')}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
{this.props.stores && body}
|
{stores && body}
|
||||||
</table>
|
</table>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export default class DicomStorePicker extends Component {
|
|||||||
loading: true,
|
loading: true,
|
||||||
stores: [],
|
stores: [],
|
||||||
locations: [],
|
locations: [],
|
||||||
|
filterStr: ""
|
||||||
};
|
};
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
@@ -38,16 +39,24 @@ export default class DicomStorePicker extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { stores, loading, error } = this.state;
|
const { stores, loading, error, filterStr } = this.state;
|
||||||
const { onSelect } = this.props;
|
const { onSelect } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DicomStoreList
|
<div>
|
||||||
stores={stores}
|
<input class="form-control gcp-input"
|
||||||
loading={loading}
|
type="text"
|
||||||
error={error}
|
value={ filterStr }
|
||||||
onSelect={onSelect}
|
onChange={ e => this.setState({ filterStr: e.target.value }) }
|
||||||
/>
|
/>
|
||||||
|
<DicomStoreList
|
||||||
|
stores={stores}
|
||||||
|
loading={loading}
|
||||||
|
error={error}
|
||||||
|
filter={filterStr}
|
||||||
|
onSelect={onSelect}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,6 +9,7 @@ export default class LocationPicker extends Component {
|
|||||||
error: null,
|
error: null,
|
||||||
loading: true,
|
loading: true,
|
||||||
locations: [],
|
locations: [],
|
||||||
|
filterStr: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
@@ -37,15 +38,23 @@ export default class LocationPicker extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { locations, loading, error } = this.state;
|
const { locations, loading, error, filterStr } = this.state;
|
||||||
const { onSelect } = this.props;
|
const { onSelect } = this.props;
|
||||||
return (
|
return (
|
||||||
<LocationsList
|
<div>
|
||||||
locations={locations}
|
<input class="form-control gcp-input"
|
||||||
loading={loading}
|
type="text"
|
||||||
error={error}
|
value={ filterStr }
|
||||||
onSelect={onSelect}
|
onChange={ e => this.setState({ filterStr: e.target.value }) }
|
||||||
/>
|
/>
|
||||||
|
<LocationsList
|
||||||
|
locations={locations}
|
||||||
|
loading={loading}
|
||||||
|
error={error}
|
||||||
|
filter={filterStr}
|
||||||
|
onSelect={onSelect}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -46,21 +46,23 @@ class LocationsList extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (this.props.error) {
|
const { loading, locations, filter, error } = this.props;
|
||||||
return <p>{this.props.error}</p>;
|
|
||||||
|
if (error) {
|
||||||
|
return <p>{error}</p>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadingIcon = (
|
const loadingIcon = (
|
||||||
<Icon name="circle-notch" className="loading-icon-spin loading-icon" />
|
<Icon name="circle-notch" className="loading-icon-spin loading-icon" />
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this.props.loading) {
|
if (loading) {
|
||||||
return loadingIcon;
|
return loadingIcon;
|
||||||
}
|
}
|
||||||
|
|
||||||
const body = (
|
const body = (
|
||||||
<tbody id="LocationList">
|
<tbody id="LocationList">
|
||||||
{this.props.locations.map(this.renderTableRow)}
|
{locations.filter(location => (location.name.split('/')[3].toLowerCase().includes(filter.toLowerCase()) || filter=="")).map(this.renderTableRow)}
|
||||||
</tbody>
|
</tbody>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -71,7 +73,7 @@ class LocationsList extends Component {
|
|||||||
<th>{this.props.t('Location')}</th>
|
<th>{this.props.t('Location')}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
{this.props.locations && body}
|
{locations && body}
|
||||||
</table>
|
</table>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,20 +30,29 @@ export default class ProjectPicker extends Component {
|
|||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
projects: response.data.projects || [],
|
projects: response.data.projects || [],
|
||||||
|
filterStr: "",
|
||||||
loading: false,
|
loading: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { projects, loading, error } = this.state;
|
const { projects, loading, filterStr, error } = this.state;
|
||||||
const { onSelect } = this.props;
|
const { onSelect } = this.props;
|
||||||
return (
|
return (
|
||||||
<ProjectsList
|
<div>
|
||||||
projects={projects}
|
<input class="form-control gcp-input"
|
||||||
loading={loading}
|
type="text"
|
||||||
error={error}
|
value={ filterStr }
|
||||||
onSelect={onSelect}
|
onChange={ e => this.setState({ filterStr: e.target.value }) }
|
||||||
/>
|
/>
|
||||||
|
<ProjectsList
|
||||||
|
projects={projects}
|
||||||
|
loading={loading}
|
||||||
|
filter={filterStr}
|
||||||
|
error={error}
|
||||||
|
onSelect={onSelect}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -50,21 +50,22 @@ class ProjectsList extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (this.props.error) {
|
const { loading, projects, filter, error } = this.props;
|
||||||
return <p>{this.props.error}</p>;
|
|
||||||
|
if (error) {
|
||||||
|
return <p>{error}</p>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadingIcon = (
|
const loadingIcon = (
|
||||||
<Icon name="circle-notch" className="loading-icon-spin loading-icon" />
|
<Icon name="circle-notch" className="loading-icon-spin loading-icon" />
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this.props.loading) {
|
if (loading) {
|
||||||
return loadingIcon;
|
return loadingIcon;
|
||||||
}
|
}
|
||||||
|
|
||||||
const body = (
|
const body = (
|
||||||
<tbody id="ProjectList">
|
<tbody id="ProjectList">
|
||||||
{this.props.projects.map(this.renderTableRow)}
|
{projects.filter(project => (project.name.toLowerCase().includes(filter.toLowerCase()) || filter=="")).map(this.renderTableRow)}
|
||||||
</tbody>
|
</tbody>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -76,7 +77,7 @@ class ProjectsList extends Component {
|
|||||||
<th>{this.props.t('ID')}</th>
|
<th>{this.props.t('ID')}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
{this.props.projects && body}
|
{projects && body}
|
||||||
</table>
|
</table>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,3 +141,15 @@
|
|||||||
margin: 20px auto;
|
margin: 20px auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gcp-input{
|
||||||
|
height: 40px;
|
||||||
|
margin: 0 5px 20px 5px;
|
||||||
|
padding: 0 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
border: none;
|
||||||
|
background-color: var(--input-background-color);
|
||||||
|
color: var(--input-placeholder-color);
|
||||||
|
font-size: 10pt;
|
||||||
|
font-weight: normal;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user