feat(ui): move to React 18 and base for using shadcn/ui (#4174)

Co-authored-by: IbrahimCSAE <ibrahim.mdev@gmail.com>
This commit is contained in:
AlirezaandIbrahimCSAE authored and GitHub committed 2024-05-27 14:30:25 -04:00
1 parent 72b63dcd6a
commit 70f2c797f4
153 files changed
+2973 -2043

No files matched your search

+1
View File
@@ -28,6 +28,7 @@ export default defineConfig({
defaultCommandTimeout: 10000,
requestTimeout: 10000,
responseTimeout: 10000,
pageLoadTimeout: 10000,
specPattern: 'cypress/integration/**/*.spec.[jt]s',
projectId: '4oe38f',
video: true,
@@ -78,8 +78,8 @@ describe('OHIF MPR', () => {
const fOR = Object.keys(fORMap)[0];
const fORAnnotation = fORMap[fOR];
// it should have crosshairs as the only key
expect(Object.keys(fORAnnotation)).to.have.length(1);
// it should have crosshairs as the only key (references lines make this 2)
expect(Object.keys(fORAnnotation)).to.have.length(2);
const crosshairs = fORAnnotation.Crosshairs;
+2 -2
View File
@@ -61,8 +61,8 @@ export function initStudyListAliasesOnDesktop() {
// We can't use data attributes (e.g. data--cy) for these since
// they are using third party libraries (i.e. react-dates, react-select)
cy.get('#date-range-studyDate-start-date').as('studyListStartDate');
cy.get('#date-range-studyDate-end-date').as('studyListEndDate');
cy.get('[data-cy="input-date-range-start"').as('studyListStartDate');
cy.get('[data-cy="input-date-range-end"').as('studyListEndDate');
cy.get('#input-modalities').as('modalities');
}
+6 -4
View File
@@ -71,7 +71,9 @@
"@ohif/mode-microscopy": "3.9.0-beta.21",
"@ohif/mode-test": "3.9.0-beta.21",
"@ohif/ui": "3.9.0-beta.21",
"@types/react": "^17.0.38",
"@ohif/ui-next": "3.9.0-beta.14",
"@svgr/webpack": "^8.1.0",
"@types/react": "^18.3.3",
"classnames": "^2.3.2",
"core-js": "^3.16.1",
"cornerstone-math": "^0.1.9",
@@ -87,11 +89,11 @@
"oidc-client": "1.11.5",
"prop-types": "^15.7.2",
"query-string": "^6.12.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-dropzone": "^10.1.7",
"react-i18next": "^12.2.2",
"react-resize-detector": "^9.1.0",
"react-resize-detector": "^10.0.1",
"react-router": "^6.8.1",
"react-router-dom": "^6.8.1"
},
+28 -22
View File
@@ -1,4 +1,5 @@
// External
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import i18n from '@ohif/i18n';
@@ -23,6 +24,7 @@ import {
UserAuthenticationProvider,
ToolboxProvider,
} from '@ohif/ui';
import { ThemeWrapper as ThemeWrapperNext, NotificationProvider } from '@ohif/ui-next';
// Viewer Project
// TODO: Should this influence study list?
import { AppConfigProvider } from '@state';
@@ -36,7 +38,27 @@ let commandsManager: CommandsManager,
serviceProvidersManager: ServiceProvidersManager,
hotkeysManager: HotkeysManager;
function App({ config, defaultExtensions, defaultModes }) {
function App({
config = {
/**
* Relative route from domain root that OHIF instance is installed at.
* For example:
*
* Hosted at: https://ohif.org/where-i-host-the/viewer/
* Value: `/where-i-host-the/viewer/`
* */
routerBaseName: '/',
/**
*
*/
showLoadingIndicator: true,
showStudyList: true,
oidc: [],
extensions: [],
},
defaultExtensions = [],
defaultModes = [],
}) {
const [init, setInit] = useState(null);
useEffect(() => {
const run = async () => {
@@ -83,11 +105,13 @@ function App({ config, defaultExtensions, defaultModes }) {
[AppConfigProvider, { value: appConfigState }],
[UserAuthenticationProvider, { service: userAuthenticationService }],
[I18nextProvider, { i18n }],
[ThemeWrapperNext],
[ThemeWrapper],
[ToolboxProvider],
[ViewportGridProvider, { service: viewportGridService }],
[ViewportDialogProvider, { service: uiViewportDialogService }],
[CineProvider, { service: cineService }],
// [NotificationProvider, { service: uiNotificationService }],
[SnackbarProvider, { service: uiNotificationService }],
[DialogProvider, { service: uiDialogService }],
[ModalProvider, { service: uiModalService, modal: Modal }],
@@ -153,27 +177,9 @@ App.propTypes = {
/* Extensions that are "bundled" or "baked-in" to the application.
* These would be provided at build time as part of they entry point. */
defaultExtensions: PropTypes.array,
};
App.defaultProps = {
config: {
/**
* Relative route from domain root that OHIF instance is installed at.
* For example:
*
* Hosted at: https://ohif.org/where-i-host-the/viewer/
* Value: `/where-i-host-the/viewer/`
* */
routerBaseName: '/',
/**
*
*/
showLoadingIndicator: true,
showStudyList: true,
oidc: [],
extensions: [],
},
defaultExtensions: [],
/* Modes that are "bundled" or "baked-in" to the application.
* These would be provided at build time as part of they entry point. */
defaultModes: PropTypes.array,
};
export default App;
+11 -61
View File
@@ -1,5 +1,5 @@
import React, { useEffect, useCallback, useRef, useState } from 'react';
import ReactResizeDetector from 'react-resize-detector';
import { useResizeDetector } from 'react-resize-detector';
import PropTypes from 'prop-types';
import { Types, MeasurementService } from '@ohif/core';
import { ViewportGrid, ViewportPane, useViewportGrid } from '@ohif/ui';
@@ -8,16 +8,22 @@ import classNames from 'classnames';
import { useAppConfig } from '@state';
function ViewerViewportGrid(props: withAppTypes) {
const { servicesManager, viewportComponents, dataSource } = props;
const { servicesManager, viewportComponents = [], dataSource } = props;
const [viewportGrid, viewportGridService] = useViewportGrid();
const [appConfig] = useAppConfig();
const { layout, activeViewportId, viewports, isHangingProtocolLayout } = viewportGrid;
const { numCols, numRows } = layout;
const elementRef = useRef(null);
const { ref: resizeRef } = useResizeDetector({
refreshMode: 'debounce',
refreshRate: 7,
refreshOptions: { leading: true },
onResize: () => {
viewportGridService.setViewportGridSizeChanged();
},
});
const layoutHash = useRef(null);
// TODO -> Need some way of selecting which displaySets hit the viewports.
const { displaySetService, measurementService, hangingProtocolService, uiNotificationService } =
servicesManager.services;
@@ -198,50 +204,6 @@ function ViewerViewportGrid(props: withAppTypes) {
};
}, [viewports]);
/**
const onDoubleClick = viewportId => {
// TODO -> Disabled for now.
// onNewImage on a cornerstone viewport is firing setDisplaySetsForViewport.
// Which it really really shouldn't. We need a larger fix for jump to
// measurements and all cornerstone "imageIndex" state to fix this.
if (cachedLayout) {
viewportGridService.set({
numCols: cachedLayout.numCols,
numRows: cachedLayout.numRows,
activeViewportId: cachedLayout.activeViewportId,
viewports: cachedLayout.viewports,
cachedLayout: null,
});
return;
}
const cachedViewports = viewports.map(viewport => {
return {
displaySetInstanceUID: viewport.displaySetInstanceUID,
};
});
viewportGridService.set({
numCols: 1,
numRows: 1,
activeViewportId: 0,
viewports: [
{
displaySetInstanceUID: viewports[viewportId].displaySetInstanceUID,
imageIndex: undefined,
},
],
cachedLayout: {
numCols,
numRows,
viewports: cachedViewports,
activeViewportId: viewportId,
},
});
};
*/
const onDropHandler = (viewportId, { displaySetInstanceUID }) => {
const updatedViewports = _getUpdatedViewports(viewportId, displaySetInstanceUID);
viewportGridService.setDisplaySetsForViewports(updatedViewports);
@@ -363,21 +325,13 @@ function ViewerViewportGrid(props: withAppTypes) {
return (
<div
ref={elementRef}
ref={resizeRef}
className="h-full w-full"
>
<ViewportGrid
numRows={numRows}
numCols={numCols}
>
<ReactResizeDetector
refreshMode="debounce"
refreshRate={7} // ms seems to be fine for 10 viewports
onResize={() => {
viewportGridService.setViewportGridSizeChanged();
}}
targetRef={elementRef.current}
/>
{getViewportPanes()}
</ViewportGrid>
</div>
@@ -389,10 +343,6 @@ ViewerViewportGrid.propTypes = {
servicesManager: PropTypes.instanceOf(Object).isRequired,
};
ViewerViewportGrid.defaultProps = {
viewportComponents: [],
};
function _getViewportComponent(displaySets, viewportComponents, uiNotificationService) {
if (!displaySets || !displaySets.length) {
return EmptyViewport;
+5 -4
View File
@@ -2,6 +2,7 @@
* Entry point for development and production PWA builds.
*/
import 'regenerator-runtime/runtime';
import { createRoot } from 'react-dom/client';
import App from './App';
import React from 'react';
import ReactDOM from 'react-dom';
@@ -35,10 +36,10 @@ loadDynamicConfig(window.config).then(config_json => {
defaultModes,
};
/** Create App */
const app = React.createElement(App, appProps, null);
/** Render */
ReactDOM.render(app, document.getElementById('root'));
const container = document.getElementById('root');
const root = createRoot(container);
root.render(React.createElement(App, appProps));
});
export { history };
+5 -3
View File
@@ -306,9 +306,11 @@ export default function ModeRoute({
}
// The unsubscriptions must occur before the extension onModeExit
// in order to prevent exceptions during cleanup caused by spurious events
unsubscriptions.forEach(unsub => {
unsub();
});
if (unsubscriptions) {
unsubscriptions.forEach(unsub => {
unsub();
});
}
// The extension manager must be called after the mode, this is
// expected to cleanup the state to a standard setup.
extensionManager.onModeExit();
+3 -744
View File
@@ -1,5 +1,4 @@
// Todo: we probably should add external installed extensions/modes here as well.
/** @type {import('tailwindcss').Config} */
module.exports = {
// Note: in Tailwind 3.0, JIT will purge unused styles by default
// but in development, it is often useful to disable this to see
@@ -7,6 +6,7 @@ module.exports = {
// ...(process.env.NODE_ENV === 'development' && {
// safelist: [{ pattern: /.*/ }],
// }),
presets: [require('../ui/tailwind.config.js'), require('../ui-next/tailwind.config.js')],
content: [
'./src/**/*.{jsx,js,ts,tsx, css}',
'../../extensions/**/*.{jsx,js,ts,tsx, css}',
@@ -14,256 +14,10 @@ module.exports = {
'../../modes/**/*.{jsx,js,ts,tsx, css}',
'./node_modules/@ohif/ui/src/**/*.{js,jsx,ts,tsx, css}',
'../../node_modules/@ohif/ui/src/**/*.{js,jsx,ts,tsx,css}',
'../../node_modules/@ohif/ui/src/**/*.{js,jsx,ts,tsx,css}',
'../../node_modules/@ohif/ui-next/src/**/*.{js,jsx,ts,tsx,css}',
'../../node_modules/@ohif/extension-*/src/**/*.{js,jsx,css, ts,tsx}',
],
theme: {
screens: {
sm: '640px',
md: '768px',
lg: '1024px',
xl: '1280px',
},
colors: {
overlay: 'rgba(0, 0, 0, 0.8)',
transparent: 'transparent',
black: '#000',
white: '#fff',
initial: 'initial',
inherit: 'inherit',
indigo: {
dark: '#0b1a42',
},
aqua: {
pale: '#7bb2ce',
},
primary: {
light: '#5acce6',
main: '#0944b3',
dark: '#090c29',
active: '#348cfd',
},
inputfield: {
main: '#3a3f99',
disabled: '#2b166b',
focus: '#5acce6',
placeholder: '#39383f',
},
secondary: {
light: '#3a3f99',
main: '#2b166b',
dark: '#041c4a',
active: '#1f1f27',
},
common: {
bright: '#e1e1e1',
light: '#a19fad',
main: '#fff',
dark: '#726f7e',
active: '#2c3074',
},
customgreen: {
100: '#05D97C',
200: '#0FD97C',
},
customblue: {
10: '#0A163F',
20: '#0B1F54',
30: '#09286e',
40: '#0E307F',
50: '#0F3A94',
80: '#1454D4',
100: '#c4fdff',
200: '#38daff',
300: '#1D204D',
400: '#90A0C1',
},
customgray: {
100: '#262943',
},
gray: {
100: '#f7fafc',
200: '#edf2f7',
300: '#e2e8f0',
400: '#cbd5e0',
500: '#a0aec0',
600: '#718096',
700: '#4a5568',
800: '#2d3748',
900: '#1a202c',
},
red: {
100: '#fff5f5',
200: '#fed7d7',
300: '#feb2b2',
400: '#fc8181',
500: '#f56565',
600: '#e53e3e',
700: '#c53030',
800: '#9b2c2c',
900: '#742a2a',
},
orange: {
100: '#fffaf0',
200: '#feebc8',
300: '#fbd38d',
400: '#f6ad55',
500: '#ed8936',
600: '#dd6b20',
700: '#c05621',
800: '#9c4221',
900: '#7b341e',
},
yellow: {
100: '#fffff0',
200: '#fefcbf',
300: '#faf089',
400: '#f6e05e',
500: '#ecc94b',
600: '#d69e2e',
700: '#b7791f',
800: '#975a16',
900: '#744210',
},
green: {
100: '#f0fff4',
200: '#c6f6d5',
300: '#9ae6b4',
400: '#68d391',
500: '#48bb78',
600: '#38a169',
700: '#2f855a',
800: '#276749',
900: '#22543d',
},
teal: {
100: '#e6fffa',
200: '#b2f5ea',
300: '#81e6d9',
400: '#4fd1c5',
500: '#38b2ac',
600: '#319795',
700: '#2c7a7b',
800: '#285e61',
900: '#234e52',
},
blue: {
100: '#ebf8ff',
200: '#bee3f8',
300: '#90cdf4',
400: '#63b3ed',
500: '#4299e1',
600: '#3182ce',
700: '#2b6cb0',
800: '#2c5282',
900: '#2a4365',
},
indigo: {
100: '#ebf4ff',
200: '#c3dafe',
300: '#a3bffa',
400: '#7f9cf5',
500: '#667eea',
600: '#5a67d8',
700: '#4c51bf',
800: '#434190',
900: '#3c366b',
},
purple: {
100: '#faf5ff',
200: '#e9d8fd',
300: '#d6bcfa',
400: '#b794f4',
500: '#9f7aea',
600: '#805ad5',
700: '#6b46c1',
800: '#553c9a',
900: '#44337a',
},
pink: {
100: '#fff5f7',
200: '#fed7e2',
300: '#fbb6ce',
400: '#f687b3',
500: '#ed64a6',
600: '#d53f8c',
700: '#b83280',
800: '#97266d',
900: '#702459',
},
},
backgroundColor: theme => theme('colors'),
backgroundPosition: {
bottom: 'bottom',
center: 'center',
left: 'left',
'left-bottom': 'left bottom',
'left-top': 'left top',
right: 'right',
'right-bottom': 'right bottom',
'right-top': 'right top',
top: 'top',
},
backgroundSize: {
auto: 'auto',
cover: 'cover',
contain: 'contain',
},
borderColor: theme => ({
...theme('colors'),
DEFAULT: theme('colors.gray.300', 'currentColor'),
}),
boxShadow: {
xs: '0 0 0 1px rgba(0, 0, 0, 0.05)',
sm: '0 1px 2px 0 rgba(0, 0, 0, 0.05)',
DEFAULT: '0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)',
md: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)',
lg: '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)',
xl: '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)',
'2xl': '0 25px 50px -12px rgba(0, 0, 0, 0.25)',
inner: 'inset 0 2px 4px 0 rgba(0, 0, 0, 0.06)',
outline: '0 0 0 3px rgba(66, 153, 225, 0.5)',
none: 'none',
},
container: {},
cursor: {
auto: 'auto',
default: 'default',
pointer: 'pointer',
wait: 'wait',
text: 'text',
move: 'move',
'not-allowed': 'not-allowed',
},
fill: {
current: 'currentColor',
},
flex: {
1: '1 1 0%',
0.3: '0.3 0.3 0%',
0.5: '0.5 0.5 0%',
auto: '1 1 auto',
initial: '0 1 auto',
none: 'none',
static: '0 0 auto',
},
flexGrow: {
0: '0',
DEFAULT: '1',
},
flexShrink: {
0: '0',
DEFAULT: '1',
},
fontFamily: {
sans: [
'Inter',
@@ -296,500 +50,5 @@ module.exports = {
'5xl': '3rem',
'6xl': '4rem',
},
fontWeight: {
hairline: '100',
thin: '200',
light: '300',
normal: '300',
medium: '500',
semibold: '600',
bold: '700',
extrabold: '800',
black: '900',
},
height: theme => ({
auto: 'auto',
...theme('spacing'),
full: '100%',
screen: '100vh',
inherit: 'inherit',
}),
inset: theme => ({
...theme('spacing'),
0: '0',
auto: 'auto',
full: '100%',
viewport: '0.5rem',
'1/2': '50%',
'viewport-scrollbar': '1.3rem',
}),
minHeight: theme => ({
...theme('spacing'),
0: '0',
full: '100%',
screen: '100vh',
}),
letterSpacing: {
tighter: '-0.05em',
tight: '-0.025em',
normal: '0',
wide: '0.025em',
wider: '0.05em',
widest: '0.1em',
},
lineHeight: {
none: '1',
tight: '1.25',
snug: '1.375',
normal: '1.5',
relaxed: '1.625',
loose: '2',
3: '.75rem',
4: '1rem',
5: '1.25rem',
6: '1.5rem',
7: '1.75rem',
8: '2rem',
9: '2.25rem',
10: '2.5rem',
},
listStyleType: {
none: 'none',
disc: 'disc',
decimal: 'decimal',
},
margin: (theme, { negative }) => ({
auto: 'auto',
...theme('spacing'),
...negative(theme('spacing')),
}),
maxHeight: theme => ({
full: '100%',
screen: '100vh',
...theme('spacing'),
}),
maxWidth: (theme, { breakpoints }) => ({
none: 'none',
xs: '20rem',
sm: '24rem',
md: '28rem',
lg: '32rem',
xl: '36rem',
'2xl': '42rem',
'3xl': '48rem',
'4xl': '56rem',
'5xl': '64rem',
'6xl': '72rem',
full: '100%',
...breakpoints(theme('screens')),
...theme('spacing'),
}),
minWidth: theme => ({
...theme('spacing'),
0: '0',
xs: '2rem',
sm: '4rem',
md: '6rem',
lg: '8rem',
xl: '10rem',
full: '100%',
}),
objectPosition: {
bottom: 'bottom',
center: 'center',
left: 'left',
'left-bottom': 'left bottom',
'left-top': 'left top',
right: 'right',
'right-bottom': 'right bottom',
'right-top': 'right top',
top: 'top',
},
opacity: {
0: '0',
5: '.5',
10: '.10',
15: '.15',
20: '.20',
25: '.25',
30: '.30',
35: '.35',
40: '.40',
45: '.45',
50: '.50',
55: '.55',
60: '.60',
65: '.65',
70: '.70',
75: '.75',
80: '.80',
85: '.85',
90: '.90',
95: '.95',
100: '1',
},
order: {
first: '-9999',
last: '9999',
none: '0',
1: '1',
2: '2',
3: '3',
4: '4',
5: '5',
6: '6',
7: '7',
8: '8',
9: '9',
10: '10',
11: '11',
12: '12',
},
padding: theme => theme('spacing'),
placeholderColor: theme => theme('colors'),
stroke: theme => ({
...theme('colors'),
current: 'currentColor',
}),
strokeWidth: {
0: '0',
1: '1',
2: '2',
},
textColor: theme => theme('colors'),
width: theme => ({
auto: 'auto',
...theme('spacing'),
'1/2': '50%',
'1/3': '33.333333%',
'2/3': '66.666667%',
'1/4': '25%',
'2/4': '50%',
'3/4': '75%',
'1/5': '20%',
'2/5': '40%',
'3/5': '60%',
'4/5': '80%',
'1/6': '16.666667%',
'2/6': '33.333333%',
'3/6': '50%',
'4/6': '66.666667%',
'5/6': '83.333333%',
'1/12': '8.333333%',
'2/12': '16.666667%',
'3/12': '25%',
'4/12': '33.333333%',
'5/12': '41.666667%',
'6/12': '50%',
'7/12': '58.333333%',
'8/12': '66.666667%',
'9/12': '75%',
'10/12': '83.333333%',
'11/12': '91.666667%',
'1/24': '4.166666667%',
'2/24': '8.333333333%',
'3/24': '12.5%',
'4/24': '16.66666667%',
'5/24': '20.83333333%',
'6/24': '25%',
'7/24': '29.16666667%',
'8/24': '33.33333333%',
'9/24': '37.5%',
'10/24': '41.66666667%',
'11/24': '45.83333333%',
'12/24': '50%',
'13/24': '54.16666667%',
'14/24': '58.33333333%',
'15/24': '62.5%',
'16/24': '66.66666667%',
'17/24': '70.83333333%',
'18/24': '75%',
'19/24': '79.16666667%',
'20/24': '83.33333333%',
'21/24': '87.5%',
'22/24': '91.66666667%',
'23/24': '95.83333333%',
full: '100%',
screen: '100vw',
'max-content': 'max-content',
}),
zIndex: {
auto: 'auto',
0: '0',
10: '10',
20: '20',
30: '30',
40: '40',
50: '50',
},
gap: theme => theme('spacing'),
gridTemplateColumns: {
none: 'none',
1: 'repeat(1, minmax(0, 1fr))',
2: 'repeat(2, minmax(0, 1fr))',
3: 'repeat(3, minmax(0, 1fr))',
4: 'repeat(4, minmax(0, 1fr))',
5: 'repeat(5, minmax(0, 1fr))',
6: 'repeat(6, minmax(0, 1fr))',
7: 'repeat(7, minmax(0, 1fr))',
8: 'repeat(8, minmax(0, 1fr))',
9: 'repeat(9, minmax(0, 1fr))',
10: 'repeat(10, minmax(0, 1fr))',
11: 'repeat(11, minmax(0, 1fr))',
12: 'repeat(12, minmax(0, 1fr))',
},
gridColumn: {
auto: 'auto',
'span-1': 'span 1 / span 1',
'span-2': 'span 2 / span 2',
'span-3': 'span 3 / span 3',
'span-4': 'span 4 / span 4',
'span-5': 'span 5 / span 5',
'span-6': 'span 6 / span 6',
'span-7': 'span 7 / span 7',
'span-8': 'span 8 / span 8',
'span-9': 'span 9 / span 9',
'span-10': 'span 10 / span 10',
'span-11': 'span 11 / span 11',
'span-12': 'span 12 / span 12',
},
gridColumnStart: {
auto: 'auto',
1: '1',
2: '2',
3: '3',
4: '4',
5: '5',
6: '6',
7: '7',
8: '8',
9: '9',
10: '10',
11: '11',
12: '12',
13: '13',
},
gridColumnEnd: {
auto: 'auto',
1: '1',
2: '2',
3: '3',
4: '4',
5: '5',
6: '6',
7: '7',
8: '8',
9: '9',
10: '10',
11: '11',
12: '12',
13: '13',
},
gridTemplateRows: {
none: 'none',
1: 'repeat(1, minmax(0, 1fr))',
2: 'repeat(2, minmax(0, 1fr))',
3: 'repeat(3, minmax(0, 1fr))',
4: 'repeat(4, minmax(0, 1fr))',
5: 'repeat(5, minmax(0, 1fr))',
6: 'repeat(6, minmax(0, 1fr))',
},
gridRow: {
auto: 'auto',
'span-1': 'span 1 / span 1',
'span-2': 'span 2 / span 2',
'span-3': 'span 3 / span 3',
'span-4': 'span 4 / span 4',
'span-5': 'span 5 / span 5',
'span-6': 'span 6 / span 6',
},
gridRowStart: {
auto: 'auto',
1: '1',
2: '2',
3: '3',
4: '4',
5: '5',
6: '6',
7: '7',
},
gridRowEnd: {
auto: 'auto',
1: '1',
2: '2',
3: '3',
4: '4',
5: '5',
6: '6',
7: '7',
},
transformOrigin: {
center: 'center',
top: 'top',
'top-right': 'top right',
right: 'right',
'bottom-right': 'bottom right',
bottom: 'bottom',
'bottom-left': 'bottom left',
left: 'left',
'top-left': 'top left',
},
scale: {
0: '0',
50: '.5',
75: '.75',
90: '.9',
95: '.95',
100: '1',
105: '1.05',
110: '1.1',
125: '1.25',
150: '1.5',
},
rotate: {
'-180': '-180deg',
'-90': '-90deg',
'-45': '-45deg',
0: '0',
45: '45deg',
90: '90deg',
180: '180deg',
},
translate: (theme, { negative }) => ({
...theme('spacing'),
...negative(theme('spacing')),
'-full': '-100%',
'-1/2': '-50%',
'1/2': '50%',
full: '100%',
}),
skew: {
'-12': '-12deg',
'-6': '-6deg',
'-3': '-3deg',
0: '0',
3: '3deg',
6: '6deg',
12: '12deg',
},
transitionProperty: {
none: 'none',
all: 'all',
height: 'height',
DEFAULT:
'background-color, border-color, color, fill, stroke, opacity, box-shadow, transform',
colors: 'background-color, border-color, color, fill, stroke',
opacity: 'opacity',
shadow: 'box-shadow',
transform: 'transform',
},
transitionTimingFunction: {
linear: 'linear',
in: 'cubic-bezier(0.4, 0, 1, 1)',
out: 'cubic-bezier(0, 0, 0.2, 1)',
'in-out': 'cubic-bezier(0.4, 0, 0.2, 1)',
},
transitionDuration: {
75: '75ms',
100: '100ms',
150: '150ms',
200: '200ms',
300: '300ms',
500: '500ms',
700: '700ms',
1000: '1000ms',
},
},
variants: {
accessibility: ['responsive', 'focus'],
alignContent: ['responsive'],
alignItems: ['responsive'],
alignSelf: ['responsive'],
appearance: ['responsive'],
backgroundAttachment: ['responsive'],
backgroundColor: ['responsive', 'hover', 'focus', 'active', 'group-focus', 'group-hover'],
backgroundPosition: ['responsive'],
backgroundRepeat: ['responsive'],
backgroundSize: ['responsive'],
borderCollapse: ['responsive'],
borderColor: ['responsive', 'hover', 'focus', 'active', 'group-focus', 'group-hover'],
borderRadius: ['responsive', 'focus', 'first', 'last'],
borderStyle: ['responsive', 'focus'],
borderWidth: ['responsive', 'focus', 'first', 'last'],
boxShadow: ['responsive', 'hover', 'focus'],
boxSizing: ['responsive'],
cursor: ['responsive'],
display: ['responsive'],
fill: ['responsive'],
flex: ['responsive'],
flexDirection: ['responsive'],
flexGrow: ['responsive'],
flexShrink: ['responsive'],
flexWrap: ['responsive'],
float: ['responsive'],
clear: ['responsive'],
fontFamily: ['responsive'],
fontSize: ['responsive'],
fontSmoothing: ['responsive'],
fontStyle: ['responsive'],
fontWeight: ['responsive', 'hover', 'focus'],
height: ['responsive'],
inset: ['responsive'],
justifyContent: ['responsive'],
letterSpacing: ['responsive'],
lineHeight: ['responsive'],
listStylePosition: ['responsive'],
listStyleType: ['responsive'],
margin: ['responsive'],
maxHeight: ['responsive'],
maxWidth: ['responsive'],
minHeight: ['responsive'],
minWidth: ['responsive'],
objectFit: ['responsive'],
objectPosition: ['responsive'],
opacity: ['responsive', 'hover', 'focus', 'active'],
order: ['responsive'],
outline: ['responsive', 'focus'],
overflow: ['responsive'],
padding: ['responsive', 'first'],
placeholderColor: ['responsive', 'focus'],
pointerEvents: ['responsive'],
position: ['responsive'],
resize: ['responsive'],
stroke: ['responsive'],
strokeWidth: ['responsive'],
tableLayout: ['responsive'],
textAlign: ['responsive'],
textColor: ['responsive', 'hover', 'focus', 'active', 'group-hover'],
textDecoration: ['responsive', 'hover', 'focus'],
textTransform: ['responsive'],
userSelect: ['responsive'],
verticalAlign: ['responsive'],
visibility: ['responsive'],
whitespace: ['responsive'],
width: ['responsive'],
wordBreak: ['responsive'],
zIndex: ['responsive'],
gap: ['responsive'],
gridAutoFlow: ['responsive'],
gridTemplateColumns: ['responsive'],
gridColumn: ['responsive'],
gridColumnStart: ['responsive'],
gridColumnEnd: ['responsive'],
gridTemplateRows: ['responsive'],
gridRow: ['responsive'],
gridRowStart: ['responsive'],
gridRowEnd: ['responsive'],
transform: ['responsive'],
transformOrigin: ['responsive'],
scale: ['responsive', 'hover', 'focus'],
rotate: ['responsive', 'hover', 'focus'],
translate: ['responsive', 'hover', 'focus'],
skew: ['responsive', 'hover', 'focus'],
transitionProperty: ['responsive'],
transitionTimingFunction: ['responsive'],
transitionDuration: ['responsive'],
},
corePlugins: {},
plugins: [],
};
@@ -43,7 +43,40 @@ const config = {
},
],
module: {
rules: [
{
test: /\.svg?$/,
oneOf: [
{
use: [
{
loader: '@svgr/webpack',
options: {
svgoConfig: {
plugins: [
{
name: 'preset-default',
params: {
overrides: {
removeViewBox: false
},
},
},
]
},
prettier: false,
svgo: true,
titleProp: true,
},
},
],
issuer: {
and: [/\.(ts|tsx|js|jsx|md|mdx)$/],
},
},
],
},
{
test: /(\.jsx|\.js|\.tsx|\.ts)$/,
loader: 'babel-loader',
@@ -1,21 +1,5 @@
// https://babeljs.io/docs/en/options#babelrcroots
const { extendDefaultPlugins } = require('svgo');
module.exports = {
plugins: [
[
'inline-react-svg',
{
svgo: {
plugins: extendDefaultPlugins([
{
name: 'removeViewBox',
active: false,
},
]),
},
},
],
['@babel/plugin-proposal-class-properties', { loose: true }],
'@babel/plugin-transform-typescript',
['@babel/plugin-proposal-private-property-in-object', { loose: true }],
@@ -20,8 +20,8 @@
"@ohif/extension-cornerstone": "^3.0.0",
"@ohif/i18n": "^1.0.0",
"prop-types": "^15.6.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-i18next": "^12.2.2",
"react-router": "^6.8.1",
"react-router-dom": "^6.8.1",
@@ -47,7 +47,7 @@
"@babel/plugin-proposal-private-property-in-object": "7.21.11",
"babel-eslint": "9.x",
"babel-loader": "^8.2.4",
"babel-plugin-inline-react-svg": "^2.0.2",
"@svgr/webpack": "^8.1.0",
"babel-plugin-module-resolver": "^5.0.0",
"clean-webpack-plugin": "^4.0.0",
"copy-webpack-plugin": "^10.2.0",
@@ -49,6 +49,38 @@ const config = {
],
module: {
rules: [
{
test: /\.svg?$/,
oneOf: [
{
use: [
{
loader: '@svgr/webpack',
options: {
svgoConfig: {
plugins: [
{
name: 'preset-default',
params: {
overrides: {
removeViewBox: false
},
},
},
]
},
prettier: false,
svgo: true,
titleProp: true,
},
},
],
issuer: {
and: [/\.(ts|tsx|js|jsx|md|mdx)$/],
},
},
],
},
{
test: /(\.jsx|\.js|\.tsx|\.ts)$/,
loader: 'babel-loader',
+1 -1
View File
@@ -1,5 +1,5 @@
module.exports = {
plugins: ['inline-react-svg', '@babel/plugin-proposal-class-properties'],
plugins: ['@babel/plugin-proposal-class-properties'],
env: {
test: {
presets: [
@@ -37,7 +37,7 @@
"@babel/preset-typescript": "^7.13.0",
"babel-eslint": "^8.0.3",
"babel-loader": "^8.0.0-beta.4",
"babel-plugin-inline-react-svg": "^2.0.1",
"@svgr/webpack": "^8.1.0",
"clean-webpack-plugin": "^4.0.0",
"copy-webpack-plugin": "^10.2.0",
"cross-env": "^7.0.3",
+2 -2
View File
@@ -47,8 +47,8 @@
"url-loader": "^4.1.1"
},
"peerDependencies": {
"react": "^17.0.2",
"react-dom": "^17.0.2"
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"browserslist": {
"production": [
+4 -4
View File
@@ -33,8 +33,8 @@
"peerDependencies": {
"i18next": "^17.0.3",
"i18next-browser-languagedetector": "^3.0.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-i18next": "^12.2.2"
},
"dependencies": {
@@ -46,8 +46,8 @@
"devDependencies": {
"i18next": "^17.0.3",
"i18next-browser-languagedetector": "^3.0.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-i18next": "^12.2.2",
"webpack-merge": "^5.7.3"
}
+17
View File
@@ -0,0 +1,17 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "tailwind.config.js",
"css": "src/tailwind.css",
"baseColor": "slate",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "src/components",
"utils": "src/lib/utils"
}
}
+37
View File
@@ -0,0 +1,37 @@
{
"name": "@ohif/ui-next",
"version": "3.9.0-beta.14",
"description": "Next version of OHIF Viewers UI, more customizable using shadcn/ui",
"main": "index.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"exports": {
"./tailwind.config": "./tailwind.config.ts",
"./lib/*": "./src/lib/*.ts",
"./components/*": "./src/components/*.tsx",
".": "./src/index.ts"
},
"dependencies": {
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-slot": "^1.0.2",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"cmdk": "^1.0.0",
"date-fns": "^3.6.0",
"framer-motion": "6.2.4",
"lucide-react": "^0.379.0",
"next-themes": "^0.3.0",
"react": "^18.3.1",
"react-day-picker": "^8.10.1",
"sonner": "^1.4.41",
"tailwind-merge": "^2.3.0",
"tailwindcss": "3.2.4",
"tailwindcss-animate": "^1.0.7"
},
"keywords": [],
"author": "OHIF",
"license": "MIT"
}
@@ -0,0 +1,54 @@
import * as React from 'react';
import { Slot } from '@radix-ui/react-slot';
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '../../lib/utils';
const buttonVariants = cva(
'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground shadow hover:bg-primary/90',
destructive: 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',
outline:
'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
secondary: 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline',
},
size: {
default: 'h-9 px-4 py-2',
sm: 'h-8 rounded-md px-3 text-xs',
lg: 'h-10 rounded-md px-8',
icon: 'h-9 w-9',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
}
);
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean;
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : 'button';
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
);
}
);
Button.displayName = 'Button';
export { Button, buttonVariants };
@@ -0,0 +1 @@
export { Button , buttonVariants} from "./Button"
@@ -0,0 +1,61 @@
import * as React from 'react';
import { ChevronLeft, ChevronRight } from 'lucide-react';
import { DayPicker } from 'react-day-picker';
import { cn } from '../../lib/utils';
import { buttonVariants } from '../Button';
export type CalendarProps = React.ComponentProps<typeof DayPicker>;
function Calendar({ className, classNames, showOutsideDays = true, ...props }: CalendarProps) {
return (
<DayPicker
showOutsideDays={showOutsideDays}
className={cn('p-3', className)}
captionLayout="dropdown"
fromYear={1945}
toYear={2024}
labels={{
labelMonthDropdown: () => undefined,
labelYearDropdown: () => undefined,
}}
classNames={{
months: 'flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0',
month: 'space-y-4',
caption: 'flex justify-between items-center px-2',
caption_dropdowns: 'flex space-x-2',
caption_label: 'hidden',
nav: 'space-x-1 flex items-center',
table: 'w-full border-collapse space-y-1',
head_row: 'flex',
head_cell: 'text-muted-foreground rounded-md w-9 font-normal text-[0.8rem]',
row: 'flex w-full mt-2',
cell: 'h-9 w-9 text-center text-sm p-0 relative [&:has([aria-selected].day-range-end)]:rounded-r-md [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected])]:bg-accent first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20',
day: cn(
buttonVariants({ variant: 'ghost' }),
'h-9 w-9 p-0 font-normal aria-selected:opacity-100'
),
day_range_end: 'day-range-end',
day_selected:
'bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground',
day_today: 'bg-accent text-accent-foreground',
day_outside:
'day-outside text-muted-foreground opacity-50 aria-selected:bg-accent/50 aria-selected:text-muted-foreground aria-selected:opacity-30',
day_disabled: 'text-muted-foreground opacity-50',
day_range_middle: 'aria-selected:bg-accent aria-selected:text-accent-foreground',
day_hidden: 'invisible',
...classNames,
}}
components={{
IconLeft: ({ ...props }) => <ChevronLeft className="h-4 w-4" />,
IconRight: ({ ...props }) => <ChevronRight className="h-4 w-4" />,
}}
{...props}
/>
);
}
Calendar.displayName = 'Calendar';
export { Calendar };
@@ -0,0 +1,3 @@
import { Calendar } from './Calendar';
export default Calendar;
@@ -0,0 +1,66 @@
import * as React from 'react';
import { Check, ChevronsUpDown } from 'lucide-react';
import { cn } from '../../lib/utils';
import { Button } from '../Button/Button';
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from '../Command/Command';
import { Popover, PopoverContent, PopoverTrigger } from '../Popover/Popover';
export function Combobox({ data = [], placeholder = 'Select item...' }) {
const [open, setOpen] = React.useState(false);
const [value, setValue] = React.useState('');
return (
<Popover
open={open}
onOpenChange={setOpen}
>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
aria-expanded={open}
className="w-[200px] justify-between"
>
{value ? data.find(item => item.value === value)?.label : placeholder}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-[200px] p-0">
<Command>
<CommandInput placeholder={`Search ${placeholder.toLowerCase()}...`} />
<CommandEmpty>No {placeholder.toLowerCase()} found.</CommandEmpty>
<CommandList>
<CommandGroup>
{data.map(item => (
<CommandItem
key={item.value}
value={item.value}
onSelect={currentValue => {
setValue(currentValue === value ? '' : currentValue);
setOpen(false);
}}
>
<Check
className={cn(
'mr-2 h-4 w-4',
value === item.value ? 'opacity-100' : 'opacity-0'
)}
/>
{item.label}
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
);
}
@@ -0,0 +1,3 @@
import { Combobox } from './Combobox';
export default Combobox;
@@ -0,0 +1,150 @@
import * as React from 'react';
import { type DialogProps } from '@radix-ui/react-dialog';
import { MagnifyingGlassIcon } from '@radix-ui/react-icons';
import { Command as CommandPrimitive } from 'cmdk';
import { cn } from '../../lib/utils';
import { Dialog, DialogContent } from '../Dialog/Dialog';
const Command = React.forwardRef<
React.ElementRef<typeof CommandPrimitive>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive>
>(({ className, ...props }, ref) => (
<CommandPrimitive
ref={ref}
className={cn(
'bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md',
className
)}
{...props}
/>
));
Command.displayName = CommandPrimitive.displayName;
interface CommandDialogProps extends DialogProps {}
const CommandDialog = ({ children, ...props }: CommandDialogProps) => {
return (
<Dialog {...props}>
<DialogContent className="overflow-hidden p-0">
<Command className="[&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
{children}
</Command>
</DialogContent>
</Dialog>
);
};
const CommandInput = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Input>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
>(({ className, ...props }, ref) => (
<div
className="flex items-center border-b px-3"
cmdk-input-wrapper=""
>
<MagnifyingGlassIcon className="mr-2 h-4 w-4 shrink-0 opacity-50" />
<CommandPrimitive.Input
ref={ref}
className={cn(
'placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none disabled:cursor-not-allowed disabled:opacity-50',
className
)}
{...props}
/>
</div>
));
CommandInput.displayName = CommandPrimitive.Input.displayName;
const CommandList = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.List>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>
>(({ className, ...props }, ref) => (
<CommandPrimitive.List
ref={ref}
className={cn('max-h-[300px] overflow-y-auto overflow-x-hidden', className)}
{...props}
/>
));
CommandList.displayName = CommandPrimitive.List.displayName;
const CommandEmpty = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Empty>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>
>((props, ref) => (
<CommandPrimitive.Empty
ref={ref}
className="py-6 text-center text-sm"
{...props}
/>
));
CommandEmpty.displayName = CommandPrimitive.Empty.displayName;
const CommandGroup = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Group>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>
>(({ className, ...props }, ref) => (
<CommandPrimitive.Group
ref={ref}
className={cn(
'text-foreground [&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium',
className
)}
{...props}
/>
));
CommandGroup.displayName = CommandPrimitive.Group.displayName;
const CommandSeparator = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>
>(({ className, ...props }, ref) => (
<CommandPrimitive.Separator
ref={ref}
className={cn('bg-border -mx-1 h-px', className)}
{...props}
/>
));
CommandSeparator.displayName = CommandPrimitive.Separator.displayName;
const CommandItem = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>
>(({ className, ...props }, ref) => (
<CommandPrimitive.Item
ref={ref}
className={cn(
'aria-selected:bg-accent aria-selected:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50',
className
)}
{...props}
/>
));
CommandItem.displayName = CommandPrimitive.Item.displayName;
const CommandShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
return (
<span
className={cn('text-muted-foreground ml-auto text-xs tracking-widest', className)}
{...props}
/>
);
};
CommandShortcut.displayName = 'CommandShortcut';
export {
Command,
CommandDialog,
CommandInput,
CommandList,
CommandEmpty,
CommandGroup,
CommandItem,
CommandShortcut,
CommandSeparator,
};
@@ -0,0 +1,3 @@
import * as CommandComponents from './Command';
export default CommandComponents;
@@ -0,0 +1,153 @@
import * as React from 'react';
import { format, parse, isValid } from 'date-fns';
import { Calendar as CalendarIcon } from 'lucide-react';
import { cn } from '../../lib/utils';
import Calendar from '../Calendar';
import Popover from '../Popover';
export type DatePickerWithRangeProps = {
id: string;
/** YYYYMMDD (19921022) */
startDate: string;
/** YYYYMMDD (19921022) */
endDate: string;
/** Callback that received { startDate: string(YYYYMMDD), endDate: string(YYYYMMDD)} */
onChange: (value: { startDate: string; endDate: string }) => void;
};
export function DatePickerWithRange({
className,
id,
startDate,
endDate,
onChange,
...props
}: React.HTMLAttributes<HTMLDivElement> & DatePickerWithRangeProps) {
const [start, setStart] = React.useState<string>(
startDate ? format(parse(startDate, 'yyyyMMdd', new Date()), 'yyyy-MM-dd') : ''
);
const [end, setEnd] = React.useState<string>(
endDate ? format(parse(endDate, 'yyyyMMdd', new Date()), 'yyyy-MM-dd') : ''
);
const [openEnd, setOpenEnd] = React.useState(false);
const handleStartSelect = (selectedDate: Date | undefined) => {
if (selectedDate) {
const formattedDate = format(selectedDate, 'yyyy-MM-dd');
setStart(formattedDate);
setOpenEnd(true);
onChange({
startDate: format(selectedDate, 'yyyyMMdd'),
endDate: end.replace(/-/g, ''),
});
}
};
const handleEndSelect = (selectedDate: Date | undefined) => {
if (selectedDate) {
const formattedDate = format(selectedDate, 'yyyy-MM-dd');
setEnd(formattedDate);
setOpenEnd(false);
onChange({
startDate: start.replace(/-/g, ''),
endDate: format(selectedDate, 'yyyyMMdd'),
});
}
};
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>, type: 'start' | 'end') => {
const value = e.target.value;
const date = parse(value, 'yyyy-MM-dd', new Date());
if (type === 'start') {
setStart(value);
if (isValid(date)) {
handleStartSelect(date);
}
} else {
setEnd(value);
if (isValid(date)) {
handleEndSelect(date);
}
}
};
React.useEffect(() => {
setStart(startDate ? format(parse(startDate, 'yyyyMMdd', new Date()), 'yyyy-MM-dd') : '');
setEnd(endDate ? format(parse(endDate, 'yyyyMMdd', new Date()), 'yyyy-MM-dd') : '');
}, [startDate, endDate]);
return (
<div className={cn('flex gap-2', className)}>
<Popover.Popover>
<Popover.PopoverTrigger asChild>
<div className="relative w-full">
<CalendarIcon className="absolute right-2 top-1/2 h-4 w-4 -translate-y-1/2 transform text-white" />
<input
id={`${id}-start`}
type="text"
placeholder="Start date"
autoComplete="off"
value={start}
onChange={e => handleInputChange(e, 'start')}
className={cn(
'border-inputfield-main focus:border-inputfield-focus h-full w-full justify-start rounded border bg-black py-[6.5px] pl-[6.5px] pr-[6.5px] text-left text-sm font-normal hover:bg-black hover:text-white',
!start && 'text-muted-foreground'
)}
data-cy="input-date-range-start"
/>
</div>
</Popover.PopoverTrigger>
<Popover.PopoverContent
className="w-auto p-0"
align="start"
>
<Calendar
initialFocus
mode="single"
defaultMonth={start ? parse(start, 'yyyy-MM-dd', new Date()) : new Date()}
selected={start ? parse(start, 'yyyy-MM-dd', new Date()) : undefined}
onSelect={handleStartSelect}
numberOfMonths={1}
/>
</Popover.PopoverContent>
</Popover.Popover>
<Popover.Popover
open={openEnd}
onOpenChange={setOpenEnd}
>
<Popover.PopoverTrigger asChild>
<div className="relative w-full">
<CalendarIcon className="absolute right-2 top-1/2 h-4 w-4 -translate-y-1/2 transform text-white" />
<input
id={`${id}-end`}
type="text"
placeholder="End date"
autoComplete="off"
value={end}
onChange={e => handleInputChange(e, 'end')}
className={cn(
'border-inputfield-main focus:border-inputfield-focus h-full w-full justify-start rounded border bg-black py-[6.5px] pl-[6.5px] pr-[6.5px] text-left text-sm font-normal hover:bg-black hover:text-white',
!end && 'text-muted-foreground'
)}
data-cy="input-date-range-end"
/>
</div>
</Popover.PopoverTrigger>
<Popover.PopoverContent
className="w-auto p-0"
align="start"
>
<Calendar
initialFocus
mode="single"
defaultMonth={start ? parse(start, 'yyyy-MM-dd', new Date()) : new Date()}
selected={end ? parse(end, 'yyyy-MM-dd', new Date()) : undefined}
onSelect={handleEndSelect}
numberOfMonths={1}
/>
</Popover.PopoverContent>
</Popover.Popover>
</div>
);
}
@@ -0,0 +1,3 @@
import { DatePickerWithRange } from "./DateRange";
export default DatePickerWithRange;
@@ -0,0 +1,105 @@
import * as React from 'react';
import * as DialogPrimitive from '@radix-ui/react-dialog';
import { Cross2Icon } from '@radix-ui/react-icons';
import { cn } from '../../lib/utils';
const Dialog = DialogPrimitive.Root;
const DialogTrigger = DialogPrimitive.Trigger;
const DialogPortal = DialogPrimitive.Portal;
const DialogClose = DialogPrimitive.Close;
const DialogOverlay = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Overlay
ref={ref}
className={cn(
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80',
className
)}
{...props}
/>
));
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
const DialogContent = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<DialogPortal>
<DialogOverlay />
<DialogPrimitive.Content
ref={ref}
className={cn(
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border p-6 shadow-lg duration-200 sm:rounded-lg',
className
)}
{...props}
>
{children}
<DialogPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:pointer-events-none">
<Cross2Icon className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</DialogPortal>
));
DialogContent.displayName = DialogPrimitive.Content.displayName;
const DialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn('flex flex-col space-y-1.5 text-center sm:text-left', className)}
{...props}
/>
);
DialogHeader.displayName = 'DialogHeader';
const DialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', className)}
{...props}
/>
);
DialogFooter.displayName = 'DialogFooter';
const DialogTitle = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Title
ref={ref}
className={cn('text-lg font-semibold leading-none tracking-tight', className)}
{...props}
/>
));
DialogTitle.displayName = DialogPrimitive.Title.displayName;
const DialogDescription = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Description
ref={ref}
className={cn('text-muted-foreground text-sm', className)}
{...props}
/>
));
DialogDescription.displayName = DialogPrimitive.Description.displayName;
export {
Dialog,
DialogPortal,
DialogOverlay,
DialogTrigger,
DialogClose,
DialogContent,
DialogHeader,
DialogFooter,
DialogTitle,
DialogDescription,
};
@@ -0,0 +1,3 @@
import * as DialogComponents from './Dialog';
export default DialogComponents;
@@ -0,0 +1,31 @@
import * as React from 'react';
import * as PopoverPrimitive from '@radix-ui/react-popover';
import { cn } from '../../lib/utils';
const Popover = PopoverPrimitive.Root;
const PopoverTrigger = PopoverPrimitive.Trigger;
const PopoverAnchor = PopoverPrimitive.Anchor;
const PopoverContent = React.forwardRef<
React.ElementRef<typeof PopoverPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
>(({ className, align = 'center', sideOffset = 4, ...props }, ref) => (
<PopoverPrimitive.Portal>
<PopoverPrimitive.Content
ref={ref}
align={align}
sideOffset={sideOffset}
className={cn(
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 rounded-md border p-4 shadow-md outline-none',
className
)}
{...props}
/>
</PopoverPrimitive.Portal>
));
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor };
@@ -0,0 +1,3 @@
import * as Popover from './Popover';
export default Popover;
@@ -0,0 +1,4 @@
import React from 'react';
import '../../tailwind.css';
export const ThemeWrapper = ({ children }) => <React.Fragment>{children}</React.Fragment>;
@@ -0,0 +1 @@
export { ThemeWrapper } from './ThemeWrapper';
+10
View File
@@ -0,0 +1,10 @@
import { Button, buttonVariants } from './Button';
import { ThemeWrapper } from './ThemeWrapper';
import Command from './Command';
import Dialog from './Dialog';
import Combobox from './Combobox';
import Popover from './Popover';
import Calendar from './Calendar';
import DatePickerWithRange from './DateRange';
export { Button, buttonVariants, ThemeWrapper, Command, Dialog, Combobox, Popover, Calendar, DatePickerWithRange};
@@ -0,0 +1,85 @@
import React, { useState, createContext, useContext, useCallback, useEffect } from 'react';
import PropTypes from 'prop-types';
import { Toaster, toast } from 'sonner';
const NotificationContext = createContext(null);
export const useNotification = () => useContext(NotificationContext);
const NotificationProvider = ({ children, service }) => {
const DEFAULT_OPTIONS = {
title: '',
message: '',
duration: 5000,
position: 'bottom-right', // Aligning to Sonner's positioning system
type: 'info', // info, success, error
};
const [notificationItems, setNotificationItems] = useState([]);
const show = useCallback(options => {
const { title, message, duration, position, type } = { ...DEFAULT_OPTIONS, ...options };
const id = toast[type](message, {
duration,
position,
render: () => (
<div>
<strong>{title}</strong>
<p>{message}</p>
</div>
),
});
setNotificationItems(state => [...state, { id, ...options }]);
}, []);
const hide = useCallback(id => {
toast.dismiss(id);
setNotificationItems(state => state.filter(item => item.id !== id));
}, []);
const hideAll = useCallback(() => {
toast.dismiss();
setNotificationItems([]);
}, []);
useEffect(() => {
window.notification = { show, hide, hideAll };
}, [show, hide, hideAll]);
/**
* Sets the implementation of a notification service that can be used by extensions.
*
* @returns void
*/
useEffect(() => {
if (service) {
service.setServiceImplementation({ hide, show });
}
}, [service, hide, show]);
return (
<NotificationContext.Provider value={{ show, hide, hideAll, notificationItems }}>
<Toaster position="bottom-right" />
{children}
</NotificationContext.Provider>
);
};
NotificationProvider.propTypes = {
children: PropTypes.node.isRequired,
};
export const withNotification = Component => {
return function WrappedComponent(props) {
const notificationContext = useNotification();
return (
<Component
{...props}
notificationContext={notificationContext}
/>
);
};
};
export default NotificationProvider;
@@ -0,0 +1,3 @@
import NotificationProvider, { useNotification } from './NotificationProvider';
export { useNotification, NotificationProvider };
+28
View File
@@ -0,0 +1,28 @@
import {
Button,
buttonVariants,
ThemeWrapper,
Dialog,
Command,
Popover,
Combobox,
Calendar,
DatePickerWithRange,
} from './components';
import { useNotification, NotificationProvider } from './contextProviders';
export {
// components
Button,
Dialog,
Command,
Popover,
Combobox,
buttonVariants,
ThemeWrapper,
Calendar,
DatePickerWithRange,
// contextProviders
NotificationProvider,
useNotification,
};
+6
View File
@@ -0,0 +1,6 @@
import { type ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
+67
View File
@@ -0,0 +1,67 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 222.2 84% 4.9%;
--radius: 0.5rem;
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;
--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;
--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 11.2%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: 212.7 26.8% 83.9%;
}
}
+70
View File
@@ -0,0 +1,70 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
presets: [require('../ui/tailwind.config.js')],
content: [
'./pages/**/*.{ts,tsx}',
'./components/**/*.{ts,tsx}',
'./app/**/*.{ts,tsx}',
'./src/**/*.{ts,tsx}',
],
prefix: '',
theme: {
extend: {
colors: {
border: 'hsl(var(--border))',
input: 'hsl(var(--input))',
ring: 'hsl(var(--ring))',
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))',
},
secondary: {
DEFAULT: 'hsl(var(--secondary))',
foreground: 'hsl(var(--secondary-foreground))',
},
destructive: {
DEFAULT: 'hsl(var(--destructive))',
foreground: 'hsl(var(--destructive-foreground))',
},
muted: {
DEFAULT: 'hsl(var(--muted))',
foreground: 'hsl(var(--muted-foreground))',
},
accent: {
DEFAULT: 'hsl(var(--accent))',
foreground: 'hsl(var(--accent-foreground))',
},
popover: {
DEFAULT: 'hsl(var(--popover))',
foreground: 'hsl(var(--popover-foreground))',
},
card: {
DEFAULT: 'hsl(var(--card))',
foreground: 'hsl(var(--card-foreground))',
},
},
borderRadius: {
lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)',
sm: 'calc(var(--radius) - 4px)',
},
keyframes: {
'accordion-down': {
from: { height: '0' },
to: { height: 'var(--radix-accordion-content-height)' },
},
'accordion-up': {
from: { height: 'var(--radix-accordion-content-height)' },
to: { height: '0' },
},
},
animation: {
'accordion-down': 'accordion-down 0.2s ease-out',
'accordion-up': 'accordion-up 0.2s ease-out',
},
},
},
plugins: [require('tailwindcss-animate')],
};
+11
View File
@@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@ui/*": ["./src/*"]
}
},
"include": ["src"],
"exclude": ["node_modules"]
}
+4 -4
View File
@@ -29,8 +29,8 @@
"build-storybook": "storybook build"
},
"peerDependencies": {
"react": "17.0.2",
"react-dom": "17.0.2"
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"dependencies": {
"@testing-library/react-hooks": "^3.2.1",
@@ -46,11 +46,11 @@
"lodash.debounce": "4.0.8",
"moment": "2.29.4",
"mousetrap": "^1.6.5",
"react": "^17.0.2",
"react": "^18.3.1",
"react-dates": "^21.8.0",
"react-dnd": "14.0.2",
"react-dnd-html5-backend": "14.0.0",
"react-dom": "^17.0.2",
"react-dom": "^18.3.1",
"react-draggable": "4.4.3",
"react-error-boundary": "^3.1.3",
"react-modal": "3.11.2",
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import { Button, ButtonEnums } from '../../components';
function ActionButtons({ actions, disabled, t }) {
function ActionButtons({ actions, disabled = false, t }) {
return (
<React.Fragment>
{actions.map((action, index) => (
@@ -33,8 +33,4 @@ ActionButtons.propTypes = {
disabled: PropTypes.bool,
};
ActionButtons.defaultProps = {
disabled: false,
};
export default ActionButtons;
+2 -10
View File
@@ -58,7 +58,7 @@ const defaults = {
};
const Button = ({
children,
children = '',
size = defaults.size,
disabled = defaults.disabled,
type = defaults.type,
@@ -66,7 +66,7 @@ const Button = ({
endIcon: endIconProp,
name,
className,
onClick,
onClick = () => {},
dataCY,
startIconTooltip = null,
endIconTooltip = null,
@@ -121,14 +121,6 @@ const Button = ({
);
};
Button.defaultProps = {
disabled: false,
children: '',
onClick: () => {},
type: defaults.type,
size: defaults.size,
};
Button.propTypes = {
/** What is inside the button, can be text or react component */
children: PropTypes.node,
@@ -31,15 +31,15 @@ const fpsButtonClassNames =
const CinePlayer: React.FC<CinePlayerProps> = ({
className,
isPlaying,
minFrameRate,
maxFrameRate,
stepFrameRate,
frameRate: defaultFrameRate,
isPlaying = false,
minFrameRate = 1,
maxFrameRate = 90,
stepFrameRate = 1,
frameRate: defaultFrameRate = 24,
onFrameRateChange = () => {},
onPlayPauseChange = () => {},
onClose = () => {},
dynamicInfo = {},
onFrameRateChange,
onPlayPauseChange,
onClose,
updateDynamicInfo,
}) => {
const isDynamic = !!dynamicInfo?.numTimePoints;
@@ -163,21 +163,6 @@ const CinePlayer: React.FC<CinePlayerProps> = ({
);
};
const noop = () => {};
CinePlayer.defaultProps = {
isPlaying: false,
minFrameRate: 1,
maxFrameRate: 90,
stepFrameRate: 1,
frameRate: 24,
onPlayPauseChange: noop,
onFrameRateChange: noop,
onClose: noop,
isDynamic: false,
dynamicInfo: {},
};
CinePlayer.propTypes = {
/** Minimum value for range slider */
minFrameRate: PropTypes.number,
@@ -29,7 +29,7 @@ const renderYearsOptions = () => {
};
const DateRange = props => {
const { id, onChange, startDate, endDate } = props;
const { id = '', onChange, startDate = null, endDate = null } = props;
const [focusedInput, setFocusedInput] = useState(null);
const renderYearsOptionsCallback = useCallback(renderYearsOptions, []);
const { t } = useTranslation('DatePicker');
@@ -165,12 +165,6 @@ const DateRange = props => {
);
};
DateRange.defaultProps = {
id: '',
startDate: null,
endDate: null,
};
DateRange.propTypes = {
id: PropTypes.string,
/** YYYYMMDD (19921022) */
+5 -11
View File
@@ -15,10 +15,10 @@ const Dialog = ({
actions,
onShow,
onSubmit,
header: HeaderComponent,
body: BodyComponent,
footer: FooterComponent,
value: defaultValue,
header: HeaderComponent = Header,
body: BodyComponent = Body,
footer: FooterComponent = Footer,
value: defaultValue = {},
}) => {
const [value, setValue] = useState(defaultValue);
@@ -78,13 +78,7 @@ Dialog.propTypes = {
type: PropTypes.oneOf(['primary', 'secondary', 'cancel']).isRequired,
})
).isRequired,
};
Dialog.defaultProps = {
header: Header,
footer: Footer,
body: Body,
value: {},
onShow: PropTypes.func,
};
export default Dialog;
+1 -6
View File
@@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
import Button, { ButtonEnums } from '../Button';
const Footer = ({ actions, className, onSubmit, value }) => {
const Footer = ({ actions = [], className, onSubmit = () => {}, value }) => {
const flex = 'flex items-center justify-end';
const padding = 'pt-[20px]';
@@ -47,9 +47,4 @@ Footer.propTypes = {
).isRequired,
};
Footer.defaultProps = {
onSubmit: noop,
actions: [],
};
export default Footer;
+1 -5
View File
@@ -20,7 +20,7 @@ CloseButton.propTypes = {
onClick: PropTypes.func,
};
const Header = ({ title, noCloseButton, onClose }) => {
const Header = ({ title, noCloseButton = false, onClose }) => {
const theme = 'bg-primary-dark';
const flex = 'flex items-center justify-between';
const padding = 'pb-[20px]';
@@ -46,8 +46,4 @@ Header.propTypes = {
onClose: PropTypes.func,
};
Header.defaultProps = {
noCloseButton: false,
};
export default Header;
@@ -11,15 +11,15 @@ const borderStyle = 'border-b last:border-b-0 border-secondary-main';
const Dropdown = ({
id,
children,
showDropdownIcon,
showDropdownIcon = true,
list,
itemsClassName,
titleClassName,
showBorders,
showBorders = true,
alignment,
// By default the max characters per line is the longest title
// if you wish to override this, you can pass in a number
maxCharactersPerLine,
maxCharactersPerLine = 20,
}) => {
const [open, setOpen] = useState(false);
const elementRef = useRef(null);
@@ -199,11 +199,6 @@ const Dropdown = ({
</div>
);
};
Dropdown.defaultProps = {
showDropdownIcon: true,
maxCharactersPerLine: 20,
showBorders: true,
};
Dropdown.propTypes = {
id: PropTypes.string,
@@ -7,7 +7,7 @@ import Icon from '../Icon';
import Typography from '../Typography';
// TODO: Add loading spinner to OHIF + use it here.
const EmptyStudies = ({ className }) => {
const EmptyStudies = ({ className = '' }) => {
const { t } = useTranslation('StudyList');
return (
<div className={classnames('inline-flex flex-col items-center', className)}>
@@ -25,10 +25,6 @@ const EmptyStudies = ({ className }) => {
);
};
EmptyStudies.defaultProps = {
className: '',
};
EmptyStudies.propTypes = {
className: PropTypes.string,
};
@@ -10,7 +10,7 @@ import IconButton from '../IconButton';
const isProduction = process.env.NODE_ENV === 'production';
const DefaultFallback = ({ error, context, resetErrorBoundary, fallbackRoute }) => {
const DefaultFallback = ({ error, context, resetErrorBoundary = () => {}, fallbackRoute }) => {
const { t } = useTranslation('ErrorBoundary');
const [showDetails, setShowDetails] = useState(false);
const title = `${t('Something went wrong')}${!isProduction && ` ${t('in')} ${context}`}.`;
@@ -57,25 +57,19 @@ const DefaultFallback = ({ error, context, resetErrorBoundary, fallbackRoute })
);
};
const noop = () => {};
DefaultFallback.propTypes = {
error: PropTypes.object.isRequired,
resetErrorBoundary: PropTypes.func,
componentStack: PropTypes.string,
};
DefaultFallback.defaultProps = {
resetErrorBoundary: noop,
};
const ErrorBoundary = ({
context,
onReset,
onError,
fallbackComponent: FallbackComponent,
context = 'OHIF',
onReset = () => {},
onError = () => {},
fallbackComponent: FallbackComponent = DefaultFallback,
children,
fallbackRoute,
fallbackRoute = null,
isPage,
}) => {
const [isOpen, setIsOpen] = useState(true);
@@ -132,12 +126,4 @@ ErrorBoundary.propTypes = {
fallbackRoute: PropTypes.string,
};
ErrorBoundary.defaultProps = {
context: 'OHIF',
onReset: noop,
onError: noop,
fallbackComponent: DefaultFallback,
fallbackRoute: null,
};
export default ErrorBoundary;
@@ -8,14 +8,14 @@ import Icon from '../Icon';
import './ExpandableToolbarButton.css';
const ExpandableToolbarButton = ({
type,
id,
isActive,
onClick,
icon,
type = 'primary',
id = '',
isActive = false,
onClick = () => {},
icon = 'clipboard',
className,
content: Content,
contentProps,
content: Content = null,
contentProps = {},
}) => {
const classes = {
type: {
@@ -71,15 +71,6 @@ const ExpandableToolbarButton = ({
const noop = () => {};
ExpandableToolbarButton.defaultProps = {
id: '',
icon: 'clipboard',
isActive: false,
type: 'primary',
content: null,
onClick: noop,
};
ExpandableToolbarButton.propTypes = {
/* Influences background/hover styling */
type: PropTypes.oneOf(['primary', 'secondary']),
+2 -7
View File
@@ -14,9 +14,9 @@ import { PatientInfoVisibility } from '../../types/PatientInfoVisibility';
function Header({
children,
menuOptions,
isReturnEnabled,
isReturnEnabled = true,
onClickReturnButton,
isSticky,
isSticky = false,
WhiteLabeling,
showPatientInfo = PatientInfoVisibility.VISIBLE_COLLAPSED,
servicesManager,
@@ -113,9 +113,4 @@ Header.propTypes = {
servicesManager: PropTypes.object,
};
Header.defaultProps = {
isReturnEnabled: true,
isSticky: false,
};
export default Header;
@@ -15,7 +15,7 @@ import { getKeys, formatKeysForInput } from './utils';
* @param {string} props.className input classes
* @param {Array[]} props.modifierKeys
*/
const HotkeyField = ({ disabled, keys, onChange, className, modifierKeys, hotkeys }) => {
const HotkeyField = ({ disabled = false, keys, onChange, className, modifierKeys, hotkeys }) => {
const inputValue = formatKeysForInput(keys);
const onInputKeyDown = event => {
@@ -58,8 +58,4 @@ HotkeyField.propTypes = {
}).isRequired,
};
HotkeyField.defaultProps = {
disabled: false,
};
export default HotkeyField;
@@ -11,10 +11,10 @@ import { MODIFIER_KEYS } from './hotkeysConfig';
import { validate, splitHotkeyDefinitionsAndCreateTuples } from './utils';
const HotkeysPreferences = ({
disabled,
disabled = false,
hotkeyDefinitions,
errors: controlledErrors,
onChange,
onChange = () => {},
hotkeysModule,
}) => {
const { t } = useTranslation('UserPreferencesModal');
@@ -127,8 +127,6 @@ const HotkeysPreferences = ({
);
};
const noop = () => {};
HotkeysPreferences.propTypes = {
onChange: PropTypes.func,
disabled: PropTypes.bool,
@@ -142,9 +140,4 @@ HotkeysPreferences.propTypes = {
}).isRequired,
};
HotkeysPreferences.defaultProps = {
onChange: noop,
disabled: false,
};
export default HotkeysPreferences;
+173 -194
View File
@@ -1,179 +1,179 @@
import React from 'react';
// Icons
import arrowDown from './../../assets/icons/arrow-down.svg';
import arrowLeft from './../../assets/icons/arrow-left.svg';
import arrowRight from './../../assets/icons/arrow-right.svg';
import arrowLeftSmall from './../../assets/icons/arrow-left-small.svg';
import arrowRightSmall from './../../assets/icons/arrow-right-small.svg';
import calendar from './../../assets/icons/calendar.svg';
import cancel from './../../assets/icons/cancel.svg';
import clipboard from './../../assets/icons/clipboard.svg';
import close from './../../assets/icons/closeIcon.svg';
import dottedCircle from './../../assets/icons/dotted-circle.svg';
import circledCheckmark from './../../assets/icons/circled-checkmark.svg';
import chevronDown from './../../assets/icons/chevron-down.svg';
import chevronLeft from './../../assets/icons/chevron-left.svg';
import chevronMenu from './../../assets/icons/chevron-menu.svg';
import chevronNext from './../../assets/icons/chevron-next.svg';
import chevronPrev from './../../assets/icons/chevron-prev.svg';
import chevronRight from './../../assets/icons/chevron-right.svg';
import contentNext from './../../assets/icons/content-next.svg';
import contentPrev from './../../assets/icons/content-prev.svg';
import eyeVisible from './../../assets/icons/eye-visible.svg';
import eyeHidden from './../../assets/icons/eye-hidden.svg';
import exclamation from './../../assets/icons/exclamation.svg';
import externalLink from './../../assets/icons/external-link.svg';
import groupLayers from './../../assets/icons/group-layers.svg';
import info from './../../assets/icons/info.svg';
import infoAction from './../../assets/icons/info-action.svg';
import infoLink from './../../assets/icons/info-link.svg';
import launchArrow from './../../assets/icons/launch-arrow.svg';
import launchInfo from './../../assets/icons/launch-info.svg';
import link from './../../assets/icons/tool-stack-image-sync.svg';
import listBullets from './../../assets/icons/list-bullets.svg';
import lock from './../../assets/icons/lock.svg';
import logoOhifSmall from './../../assets/icons/logo-ohif-small.svg';
import logoDarkBackGround from './../../assets/icons/ohif-logo-color-darkbg.svg';
import magnifier from './../../assets/icons/magnifier.svg';
import notificationwarningDiamond from './../../assets/icons/notificationwarning-diamond.svg';
import pencil from './../../assets/icons/pencil.svg';
import powerOff from './../../assets/icons/power-off.svg';
import profile from './../../assets/icons/profile.svg';
import pushLeft from './../../assets/icons/push-left.svg';
import pushRight from './../../assets/icons/push-right.svg';
import settings from './../../assets/icons/settings.svg';
import sidePanelCloseLeft from './../../assets/icons/side-panel-close-left.svg';
import sidePanelCloseRight from './../../assets/icons/side-panel-close-right.svg';
import sorting from './../../assets/icons/sorting.svg';
import sortingActiveDown from './../../assets/icons/sorting-active-down.svg';
import sortingActiveUp from './../../assets/icons/sorting-active-up.svg';
import statusAlertWarning from './../../assets/icons/status-alert-warning.svg';
import statusAlert from './../../assets/icons/status-alert.svg';
import statusLocked from './../../assets/icons/status-locked.svg';
import statusTracked from './../../assets/icons/status-tracked.svg';
import statusUntracked from './../../assets/icons/status-untracked.svg';
import tracked from './../../assets/icons/tracked.svg';
import unlink from './../../assets/icons/unlink.svg';
import checkboxChecked from './../../assets/icons/checkbox-checked.svg';
import checkboxUnchecked from './../../assets/icons/checkbox-unchecked.svg';
import iconAlertOutline from './../../assets/icons/icons-alert-outline.svg';
import iconAlertSmall from './../../assets/icons/icon-alert-small.svg';
import iconClose from './../../assets/icons/icon-close.svg';
import iconClearField from './../../assets/icons/icon-clear-field.svg';
import iconNextInactive from './../../assets/icons/icon-next-inactive.svg';
import iconNext from './../../assets/icons/icon-next.svg';
import iconPlay from './../../assets/icons/icon-play.svg';
import iconPause from './../../assets/icons/icon-pause.svg';
import iconPrevInactive from './../../assets/icons/icon-prev-inactive.svg';
import iconPrev from './../../assets/icons/icon-prev.svg';
import iconSearch from './../../assets/icons/icon-search.svg';
import iconStatusAlert from './../../assets/icons/icon-status-alert.svg';
import iconTransferring from './../../assets/icons/icon-transferring.svg';
import iconUpload from './../../assets/icons/icon-upload.svg';
import navigationPanelRightHide from './../../assets/icons/navigation-panel-right-hide.svg';
import navigationPanelRightReveal from './../../assets/icons/navigation-panel-right-reveal.svg';
import tabLinear from './../../assets/icons/tab-linear.svg';
import tabPatientInfo from './../../assets/icons/tab-patient-info.svg';
import tabROIThreshold from './../../assets/icons/tab-roi-threshold.svg';
import tabSegmentation from './../../assets/icons/tab-segmentation.svg';
import tabStudies from './../../assets/icons/tab-studies.svg';
import uiArrowDown from './../../assets/icons/ui-arrow-down.svg';
import uiArrowUp from './../../assets/icons/ui-arrow-up.svg';
import uiArrowLeft from './../../assets/icons/ui-arrow-left.svg';
import uiArrowRight from './../../assets/icons/ui-arrow-right.svg';
import loadingOHIFMark from './../../assets/icons/loading-ohif-mark.svg';
import notificationsInfo from './../../assets/icons/notifications-info.svg';
import notificationsWarning from './../../assets/icons/notifications-warning.svg';
import notificationsError from './../../assets/icons/notifications-error.svg';
import notificationsSuccess from './../../assets/icons/notifications-success.svg';
import nextArrow from './../../assets/icons/next-arrow.svg';
import prevArrow from './../../assets/icons/prev-arrow.svg';
import viewportStatusTracked from './../../assets/icons/viewport-status-tracked.svg';
import { ReactComponent as arrowDown } from './../../assets/icons/arrow-down.svg';
import { ReactComponent as arrowLeft } from './../../assets/icons/arrow-left.svg';
import { ReactComponent as arrowRight } from './../../assets/icons/arrow-right.svg';
import { ReactComponent as arrowLeftSmall } from './../../assets/icons/arrow-left-small.svg';
import { ReactComponent as arrowRightSmall } from './../../assets/icons/arrow-right-small.svg';
import { ReactComponent as calendar } from './../../assets/icons/calendar.svg';
import { ReactComponent as cancel } from './../../assets/icons/cancel.svg';
import { ReactComponent as clipboard } from './../../assets/icons/clipboard.svg';
import { ReactComponent as close } from './../../assets/icons/closeIcon.svg';
import { ReactComponent as dottedCircle } from './../../assets/icons/dotted-circle.svg';
import { ReactComponent as circledCheckmark } from './../../assets/icons/circled-checkmark.svg';
import { ReactComponent as chevronDown } from './../../assets/icons/chevron-down.svg';
import { ReactComponent as chevronLeft } from './../../assets/icons/chevron-left.svg';
import { ReactComponent as chevronMenu } from './../../assets/icons/chevron-menu.svg';
import { ReactComponent as chevronNext } from './../../assets/icons/chevron-next.svg';
import { ReactComponent as chevronPrev } from './../../assets/icons/chevron-prev.svg';
import { ReactComponent as chevronRight } from './../../assets/icons/chevron-right.svg';
import { ReactComponent as contentNext } from './../../assets/icons/content-next.svg';
import { ReactComponent as contentPrev } from './../../assets/icons/content-prev.svg';
import { ReactComponent as eyeVisible } from './../../assets/icons/eye-visible.svg';
import { ReactComponent as eyeHidden } from './../../assets/icons/eye-hidden.svg';
import { ReactComponent as exclamation } from './../../assets/icons/exclamation.svg';
import { ReactComponent as externalLink } from './../../assets/icons/external-link.svg';
import { ReactComponent as groupLayers } from './../../assets/icons/group-layers.svg';
import { ReactComponent as info } from './../../assets/icons/info.svg';
import { ReactComponent as infoAction } from './../../assets/icons/info-action.svg';
import { ReactComponent as infoLink } from './../../assets/icons/info-link.svg';
import { ReactComponent as launchArrow } from './../../assets/icons/launch-arrow.svg';
import { ReactComponent as launchInfo } from './../../assets/icons/launch-info.svg';
import { ReactComponent as link } from './../../assets/icons/tool-stack-image-sync.svg';
import { ReactComponent as listBullets } from './../../assets/icons/list-bullets.svg';
import { ReactComponent as lock } from './../../assets/icons/lock.svg';
import { ReactComponent as logoOhifSmall } from './../../assets/icons/logo-ohif-small.svg';
import { ReactComponent as logoDarkBackGround } from './../../assets/icons/ohif-logo-color-darkbg.svg';
import { ReactComponent as magnifier } from './../../assets/icons/magnifier.svg';
import { ReactComponent as notificationwarningDiamond } from './../../assets/icons/notificationwarning-diamond.svg';
import { ReactComponent as pencil } from './../../assets/icons/pencil.svg';
import { ReactComponent as powerOff } from './../../assets/icons/power-off.svg';
import { ReactComponent as profile } from './../../assets/icons/profile.svg';
import { ReactComponent as pushLeft } from './../../assets/icons/push-left.svg';
import { ReactComponent as pushRight } from './../../assets/icons/push-right.svg';
import { ReactComponent as settings } from './../../assets/icons/settings.svg';
import { ReactComponent as sidePanelCloseLeft } from './../../assets/icons/side-panel-close-left.svg';
import { ReactComponent as sidePanelCloseRight } from './../../assets/icons/side-panel-close-right.svg';
import { ReactComponent as sorting } from './../../assets/icons/sorting.svg';
import { ReactComponent as sortingActiveDown } from './../../assets/icons/sorting-active-down.svg';
import { ReactComponent as sortingActiveUp } from './../../assets/icons/sorting-active-up.svg';
import { ReactComponent as statusAlertWarning } from './../../assets/icons/status-alert-warning.svg';
import { ReactComponent as statusAlert } from './../../assets/icons/status-alert.svg';
import { ReactComponent as statusLocked } from './../../assets/icons/status-locked.svg';
import { ReactComponent as statusTracked } from './../../assets/icons/status-tracked.svg';
import { ReactComponent as statusUntracked } from './../../assets/icons/status-untracked.svg';
import { ReactComponent as tracked } from './../../assets/icons/tracked.svg';
import { ReactComponent as unlink } from './../../assets/icons/unlink.svg';
import { ReactComponent as checkboxChecked } from './../../assets/icons/checkbox-checked.svg';
import { ReactComponent as checkboxUnchecked } from './../../assets/icons/checkbox-unchecked.svg';
import { ReactComponent as iconAlertOutline } from './../../assets/icons/icons-alert-outline.svg';
import { ReactComponent as iconAlertSmall } from './../../assets/icons/icon-alert-small.svg';
import { ReactComponent as iconClose } from './../../assets/icons/icon-close.svg';
import { ReactComponent as iconClearField } from './../../assets/icons/icon-clear-field.svg';
import { ReactComponent as iconNextInactive } from './../../assets/icons/icon-next-inactive.svg';
import { ReactComponent as iconNext } from './../../assets/icons/icon-next.svg';
import { ReactComponent as iconPlay } from './../../assets/icons/icon-play.svg';
import { ReactComponent as iconPause } from './../../assets/icons/icon-pause.svg';
import { ReactComponent as iconPrevInactive } from './../../assets/icons/icon-prev-inactive.svg';
import { ReactComponent as iconPrev } from './../../assets/icons/icon-prev.svg';
import { ReactComponent as iconSearch } from './../../assets/icons/icon-search.svg';
import { ReactComponent as iconStatusAlert } from './../../assets/icons/icon-status-alert.svg';
import { ReactComponent as iconTransferring } from './../../assets/icons/icon-transferring.svg';
import { ReactComponent as iconUpload } from './../../assets/icons/icon-upload.svg';
import { ReactComponent as navigationPanelRightHide } from './../../assets/icons/navigation-panel-right-hide.svg';
import { ReactComponent as navigationPanelRightReveal } from './../../assets/icons/navigation-panel-right-reveal.svg';
import { ReactComponent as tabLinear } from './../../assets/icons/tab-linear.svg';
import { ReactComponent as tabPatientInfo } from './../../assets/icons/tab-patient-info.svg';
import { ReactComponent as tabROIThreshold } from './../../assets/icons/tab-roi-threshold.svg';
import { ReactComponent as tabSegmentation } from './../../assets/icons/tab-segmentation.svg';
import { ReactComponent as tabStudies } from './../../assets/icons/tab-studies.svg';
import { ReactComponent as uiArrowDown } from './../../assets/icons/ui-arrow-down.svg';
import { ReactComponent as uiArrowUp } from './../../assets/icons/ui-arrow-up.svg';
import { ReactComponent as uiArrowLeft } from './../../assets/icons/ui-arrow-left.svg';
import { ReactComponent as uiArrowRight } from './../../assets/icons/ui-arrow-right.svg';
import { ReactComponent as loadingOHIFMark } from './../../assets/icons/loading-ohif-mark.svg';
import { ReactComponent as notificationsInfo } from './../../assets/icons/notifications-info.svg';
import { ReactComponent as notificationsWarning } from './../../assets/icons/notifications-warning.svg';
import { ReactComponent as notificationsError } from './../../assets/icons/notifications-error.svg';
import { ReactComponent as notificationsSuccess } from './../../assets/icons/notifications-success.svg';
import { ReactComponent as nextArrow } from './../../assets/icons/next-arrow.svg';
import { ReactComponent as prevArrow } from './../../assets/icons/prev-arrow.svg';
import { ReactComponent as viewportStatusTracked } from './../../assets/icons/viewport-status-tracked.svg';
import { ReactComponent as toggleDicomOverlay } from './../../assets/icons/tool-toggle-dicom-overlay.svg';
import { ReactComponent as toolZoom } from './../../assets/icons/tool-zoom.svg';
import { ReactComponent as toolCapture } from './../../assets/icons/tool-capture.svg';
import { ReactComponent as toolLayout } from './../../assets/icons/tool-layout-default.svg';
import { ReactComponent as toolMore } from './../../assets/icons/tool-more-menu.svg';
import { ReactComponent as toolMove } from './../../assets/icons/tool-move.svg';
import { ReactComponent as toolWindow } from './../../assets/icons/tool-window-level.svg';
import { ReactComponent as toolAnnotate } from './../../assets/icons/tool-annotate.svg';
import { ReactComponent as toolBidirectional } from './../../assets/icons/tool-bidirectional.svg';
import { ReactComponent as toolElipse } from './../../assets/icons/tool-measure-elipse.svg';
import { ReactComponent as toolCircle } from './../../assets/icons/tool-circle.svg';
import { ReactComponent as toolLength } from './../../assets/icons/tool-length.svg';
import { ReactComponent as toolStackScroll } from './../../assets/icons/tool-stack-scroll.svg';
import { ReactComponent as toolMagnify } from './../../assets/icons/tool-quick-magnify.svg';
import { ReactComponent as toolFlipHorizontal } from './../../assets/icons/tool-flip-horizontal.svg';
import { ReactComponent as toolInvert } from './../../assets/icons/tool-invert.svg';
import { ReactComponent as toolRotateRight } from './../../assets/icons/tool-rotate-right.svg';
import { ReactComponent as toolCine } from './../../assets/icons/tool-cine.svg';
import { ReactComponent as toolCrosshair } from './../../assets/icons/tool-crosshair.svg';
import { ReactComponent as toolProbe } from './../../assets/icons/focus-frame-target.svg';
import { ReactComponent as toolAngle } from './../../assets/icons/tool-angle.svg';
import { ReactComponent as toolReset } from './../../assets/icons/tool-reset.svg';
import { ReactComponent as toolRectangle } from './../../assets/icons/tool-rectangle.svg';
import { ReactComponent as toolFusionColor } from './../../assets/icons/tool-fusion-color.svg';
import { ReactComponent as toolCreateThreshold } from './../../assets/icons/tool-create-threshold.svg';
import { ReactComponent as toolCalibration } from './../../assets/icons/tool-calibrate.svg';
import { ReactComponent as toolFreehand } from './../../assets/icons/tool-freehand.svg';
import { ReactComponent as toolFreehandPolygon } from './../../assets/icons/tool-freehand-polygon.svg';
import { ReactComponent as toolPolygon } from './../../assets/icons/tool-polygon.svg';
import { ReactComponent as editPatient } from './../../assets/icons/edit-patient.svg';
import { ReactComponent as panelGroupMore } from './../../assets/icons/panel-group-more.svg';
import { ReactComponent as panelGroupOpenClose } from './../../assets/icons/panel-group-open-close.svg';
import { ReactComponent as rowAdd } from './../../assets/icons/row-add.svg';
import { ReactComponent as rowEdit } from './../../assets/icons/row-edit.svg';
import { ReactComponent as rowHidden } from './../../assets/icons/row-hidden.svg';
import { ReactComponent as rowShown } from './../../assets/icons/row-shown.svg';
import { ReactComponent as rowLock } from './../../assets/icons/row-lock.svg';
import { ReactComponent as rowUnlock } from './../../assets/icons/row-unlock.svg';
import { ReactComponent as iconMPR } from './../../assets/icons/icon-mpr-alt.svg';
import { ReactComponent as checkboxDefault } from './../../assets/icons/checkbox-default.svg';
import { ReactComponent as checkboxActive } from './../../assets/icons/checkbox-active.svg';
import { ReactComponent as referenceLines } from './../../assets/icons/tool-reference-lines.svg';
import { ReactComponent as chevronDownNew } from './../../assets/icons/icon-disclosure-close.svg';
import { ReactComponent as chevronLeftNew } from './../../assets/icons/icon-disclosure-open.svg';
import { ReactComponent as settingsBars } from './../../assets/icons/icon-display-settings.svg';
import { ReactComponent as iconAdd } from './../../assets/icons/icon-add.svg';
import { ReactComponent as iconRename } from './../../assets/icons/icon-rename.svg';
import { ReactComponent as iconDelete } from './../../assets/icons/icon-delete.svg';
import { ReactComponent as iconMoreMenu } from './../../assets/icons/icon-more-menu.svg';
import { ReactComponent as iconToolBrush } from './../../assets/icons/tool-seg-brush.svg';
import { ReactComponent as iconToolEraser } from './../../assets/icons/tool-seg-eraser.svg';
import { ReactComponent as iconToolScissor } from './../../assets/icons/icon-tool-scissor.svg';
import { ReactComponent as iconToolShape } from './../../assets/icons/tool-seg-shape.svg';
import { ReactComponent as iconToolThreshold } from './../../assets/icons/tool-seg-threshold.svg';
import { ReactComponent as viewportWindowLevel } from './../../assets/icons/viewport-window-level.svg';
import { ReactComponent as dicomTagBrowser } from './../../assets/icons/tool-dicom-tag-browser.svg';
import { ReactComponent as iconToolFreehandRoi } from './../../assets/icons/tool-freehand-roi.svg';
import { ReactComponent as iconToolLivewire } from './../../assets/icons/tool-magnetic-roi.svg';
import { ReactComponent as iconToolSplineRoi } from './../../assets/icons/tool-spline-roi.svg';
import { ReactComponent as iconToolUltrasoundBidirectional } from './../../assets/icons/tool-ultrasound-bidirectional.svg';
import { ReactComponent as iconToolLoupe } from './../../assets/icons/tool-magnify.svg';
import { ReactComponent as oldTrash } from './../../assets/icons/old-trash.svg';
import { ReactComponent as oldPlay } from './../../assets/icons/old-play.svg';
import { ReactComponent as oldStop } from './../../assets/icons/old-stop.svg';
import { ReactComponent as iconColorLUT } from './../../assets/icons/icon-color-lut.svg';
import { ReactComponent as iconChevronPatient } from './../../assets/icons/icon-chevron-patient.svg';
import { ReactComponent as iconPatient } from './../../assets/icons/icon-patient.svg';
import { ReactComponent as iconSettings } from './../../assets/icons/icon-settings.svg';
import { ReactComponent as iconToolbarBack } from './../../assets/icons/icon-toolbar-back.svg';
import { ReactComponent as iconMultiplePatients } from './../../assets/icons/icon-multiple-patients.svg';
import { ReactComponent as layoutAdvanced3DFourUp } from './../../assets/icons/layout-advanced-3d-four-up.svg';
import { ReactComponent as layoutAdvanced3DMain } from './../../assets/icons/layout-advanced-3d-main.svg';
import { ReactComponent as layoutAdvanced3DOnly } from './../../assets/icons/layout-advanced-3d-only.svg';
import { ReactComponent as layoutAdvanced3DPrimary } from './../../assets/icons/layout-advanced-3d-primary.svg';
import { ReactComponent as layoutAdvancedAxialPrimary } from './../../assets/icons/layout-advanced-axial-primary.svg';
import { ReactComponent as layoutAdvancedMPR } from './../../assets/icons/layout-advanced-mpr.svg';
import { ReactComponent as layoutCommon1x1 } from './../../assets/icons/layout-common-1x1.svg';
import { ReactComponent as layoutCommon1x2 } from './../../assets/icons/layout-common-1x2.svg';
import { ReactComponent as layoutCommon2x2 } from './../../assets/icons/layout-common-2x2.svg';
import { ReactComponent as layoutCommon2x3 } from './../../assets/icons/layout-common-2x3.svg';
import { ReactComponent as iconToolRotate } from './../../assets/icons/tool-3d-rotate.svg';
import { ReactComponent as tab4D } from './../../assets/icons/tab-4d.svg';
import { ReactComponent as investigationalUse } from './../../assets/icons/illustration-investigational-use.svg';
import { ReactComponent as actionNewDialog } from './../../assets/icons/action-new-dialog.svg';
/** Tools */
import toggleDicomOverlay from './../../assets/icons/tool-toggle-dicom-overlay.svg';
import toolZoom from './../../assets/icons/tool-zoom.svg';
import toolCapture from './../../assets/icons/tool-capture.svg';
import toolLayout from './../../assets/icons/tool-layout-default.svg';
import toolMore from './../../assets/icons/tool-more-menu.svg';
import toolMove from './../../assets/icons/tool-move.svg';
import toolWindow from './../../assets/icons/tool-window-level.svg';
import toolAnnotate from './../../assets/icons/tool-annotate.svg';
import toolBidirectional from './../../assets/icons/tool-bidirectional.svg';
import toolElipse from './../../assets/icons/tool-measure-elipse.svg';
import toolCircle from './../../assets/icons/tool-circle.svg';
import toolLength from './../../assets/icons/tool-length.svg';
import toolStackScroll from './../../assets/icons/tool-stack-scroll.svg';
import toolMagnify from './../../assets/icons/tool-quick-magnify.svg';
import toolFlipHorizontal from './../../assets/icons/tool-flip-horizontal.svg';
import toolInvert from './../../assets/icons/tool-invert.svg';
import toolRotateRight from './../../assets/icons/tool-rotate-right.svg';
import toolCine from './../../assets/icons/tool-cine.svg';
import toolCrosshair from './../../assets/icons/tool-crosshair.svg';
import toolProbe from './../../assets/icons/focus-frame-target.svg';
import toolAngle from './../../assets/icons/tool-angle.svg';
import toolReset from './../../assets/icons/tool-reset.svg';
import toolRectangle from './../../assets/icons/tool-rectangle.svg';
import toolFusionColor from './../../assets/icons/tool-fusion-color.svg';
import toolCreateThreshold from './../../assets/icons/tool-create-threshold.svg';
import toolCalibration from './../../assets/icons/tool-calibrate.svg';
import toolFreehand from './../../assets/icons/tool-freehand.svg';
import toolFreehandPolygon from './../../assets/icons/tool-freehand-polygon.svg';
import toolPolygon from './../../assets/icons/tool-polygon.svg';
import toolBrush from './../../assets/icons/tool-brush.svg';
import toolEraser from './../../assets/icons/tool-eraser.svg';
import toolScissorRect from './../../assets/icons/tool-scissor-rect.svg';
import toolScissorCircle from './../../assets/icons/tool-scissor-circle.svg';
import toolPaintFill from './../../assets/icons/tool-paint-fill.svg';
import editPatient from './../../assets/icons/edit-patient.svg';
import panelGroupMore from './../../assets/icons/panel-group-more.svg';
import panelGroupOpenClose from './../../assets/icons/panel-group-open-close.svg';
import rowAdd from './../../assets/icons/row-add.svg';
import rowEdit from './../../assets/icons/row-edit.svg';
import rowHidden from './../../assets/icons/row-hidden.svg';
import rowShown from './../../assets/icons/row-shown.svg';
import rowLock from './../../assets/icons/row-lock.svg';
import rowUnlock from './../../assets/icons/row-unlock.svg';
import iconMPR from './../../assets/icons/icon-mpr-alt.svg';
import checkboxDefault from './../../assets/icons/checkbox-default.svg';
import checkboxActive from './../../assets/icons/checkbox-active.svg';
import referenceLines from './../../assets/icons/tool-reference-lines.svg';
import chevronDownNew from './../../assets/icons/icon-disclosure-close.svg';
import chevronLeftNew from './../../assets/icons/icon-disclosure-open.svg';
import settingsBars from './../../assets/icons/icon-display-settings.svg';
import iconAdd from './../../assets/icons/icon-add.svg';
import iconRename from './../../assets/icons/icon-rename.svg';
import iconDelete from './../../assets/icons/icon-delete.svg';
import iconMoreMenu from './../../assets/icons/icon-more-menu.svg';
import iconToolBrush from './../../assets/icons/tool-seg-brush.svg';
import iconToolEraser from './../../assets/icons/tool-seg-eraser.svg';
import iconToolScissor from './../../assets/icons/icon-tool-scissor.svg';
import iconToolShape from './../../assets/icons/tool-seg-shape.svg';
import iconToolThreshold from './../../assets/icons/tool-seg-threshold.svg';
import viewportWindowLevel from './../../assets/icons/viewport-window-level.svg';
import dicomTagBrowser from './../../assets/icons/tool-dicom-tag-browser.svg';
import iconToolFreehandRoi from './../../assets/icons/tool-freehand-roi.svg';
import iconToolLivewire from './../../assets/icons/tool-magnetic-roi.svg';
import iconToolSplineRoi from './../../assets/icons/tool-spline-roi.svg';
import iconToolUltrasoundBidirectional from './../../assets/icons/tool-ultrasound-bidirectional.svg';
import iconToolLoupe from './../../assets/icons/tool-magnify.svg';
/** Old OHIF */
import oldTrash from './../../assets/icons/old-trash.svg';
import oldPlay from './../../assets/icons/old-play.svg';
import oldStop from './../../assets/icons/old-stop.svg';
/** ColorLut */
import iconColorLUT from './../../assets/icons/icon-color-lut.svg';
/** New Patient Info Toolbar */
import iconChevronPatient from './../../assets/icons/icon-chevron-patient.svg';
import iconPatient from './../../assets/icons/icon-patient.svg';
import iconSettings from './../../assets/icons/icon-settings.svg';
import iconToolbarBack from './../../assets/icons/icon-toolbar-back.svg';
import iconMultiplePatients from './../../assets/icons/icon-multiple-patients.svg';
/** Volume Rendering */
import CTAAA from './../../assets/icons/CT-AAA.png';
import CTAAA2 from './../../assets/icons/CT-AAA2.png';
import CTAir from './../../assets/icons/CT-Air.png';
@@ -201,27 +201,6 @@ import MRDefault from './../../assets/icons/MR-Default.png';
import MRMIP from './../../assets/icons/MR-MIP.png';
import MRT2Brain from './../../assets/icons/MR-T2-Brain.png';
import VolumeRendering from './../../assets/icons/VolumeRendering.png';
import actionNewDialog from './../../assets/icons/action-new-dialog.svg';
/** LAYOUT */
import layoutAdvanced3DFourUp from './../../assets/icons/layout-advanced-3d-four-up.svg';
import layoutAdvanced3DMain from './../../assets/icons/layout-advanced-3d-main.svg';
import layoutAdvanced3DOnly from './../../assets/icons/layout-advanced-3d-only.svg';
import layoutAdvanced3DPrimary from './../../assets/icons/layout-advanced-3d-primary.svg';
import layoutAdvancedAxialPrimary from './../../assets/icons/layout-advanced-axial-primary.svg';
import layoutAdvancedMPR from './../../assets/icons/layout-advanced-mpr.svg';
import layoutCommon1x1 from './../../assets/icons/layout-common-1x1.svg';
import layoutCommon1x2 from './../../assets/icons/layout-common-1x2.svg';
import layoutCommon2x2 from './../../assets/icons/layout-common-2x2.svg';
import layoutCommon2x3 from './../../assets/icons/layout-common-2x3.svg';
import iconToolRotate from './../../assets/icons/tool-3d-rotate.svg';
//
import tab4D from './../../assets/icons/tab-4d.svg';
/** New investigational use */
import investigationalUse from './../../assets/icons/illustration-investigational-use.svg';
const ICONS = {
'arrow-down': arrowDown,
@@ -74,14 +74,14 @@ const fullWidthClasses = {
const IconButton = ({
children,
variant,
color,
size,
rounded,
disabled,
type,
fullWidth,
onClick,
variant = 'contained',
color = 'default',
size = 'medium',
rounded = 'medium',
disabled = false,
type = 'button',
fullWidth = false,
onClick = () => {},
className,
id,
...rest
@@ -122,17 +122,6 @@ const IconButton = ({
);
};
IconButton.defaultProps = {
onClick: () => {},
color: 'default',
disabled: false,
fullWidth: false,
rounded: 'medium',
size: 'medium',
type: 'button',
variant: 'contained',
};
IconButton.propTypes = {
children: PropTypes.node.isRequired,
size: PropTypes.oneOf(['small', 'medium', 'large', 'initial', 'toolbar', 'toolbox']),
@@ -20,10 +20,12 @@ class ImageScrollbar extends PureComponent {
width: `${this.props.height}`,
};
const { onContextMenu = e => e.preventDefault() } = this.props;
return (
<div
className="scroll"
onContextMenu={this.props.onContextMenu}
onContextMenu={onContextMenu}
>
<div className="scroll-holder">
<input
@@ -70,8 +72,4 @@ class ImageScrollbar extends PureComponent {
};
}
ImageScrollbar.defaultProps = {
onContextMenu: e => e.preventDefault(),
};
export default ImageScrollbar;
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import DateRange from '../DateRange';
import { DatePickerWithRange } from '@ohif/ui-next';
import InputLabelWrapper from '../InputLabelWrapper';
const InputDateRange = ({
@@ -9,8 +9,8 @@ const InputDateRange = ({
label,
isSortable,
sortDirection,
onLabelClick,
value,
onLabelClick = () => {},
value = {},
onChange,
}) => {
const { startDate, endDate } = value;
@@ -26,9 +26,11 @@ const InputDateRange = ({
isSortable={isSortable}
sortDirection={sortDirection}
onLabelClick={onClickHandler}
className="xl:min-w-[284px]"
>
<div className="relative">
<DateRange
<div className="relative xl:max-w-[246px]">
<DatePickerWithRange
className="mt-2"
id={id}
startDate={startDate}
endDate={endDate}
@@ -39,13 +41,6 @@ const InputDateRange = ({
);
};
const noop = () => {};
InputDateRange.defaultProps = {
value: {},
onLabelClick: noop,
};
InputDateRange.propTypes = {
id: PropTypes.string,
label: PropTypes.string.isRequired,
@@ -25,20 +25,20 @@ type InputDoubleRangeProps = {
const InputDoubleRange: React.FC<InputDoubleRangeProps> = ({
values,
onChange,
minValue,
maxValue,
step,
unit,
containerClassName,
inputClassName,
labelClassName,
labelVariant,
showLabel,
labelPosition,
trackColor,
allowNumberEdit,
minValue = 0,
maxValue = 100,
step = 1,
unit = '',
containerClassName = '',
inputClassName = '',
labelClassName = '',
labelVariant = 'body1',
showLabel = false,
labelPosition = 'left',
trackColor = 'primary',
allowNumberEdit = false,
allowOutOfRange = false,
showAdjustmentArrows,
showAdjustmentArrows = false,
}) => {
// Set initial thumb positions as percentages
const initialPercentageStart = Math.round(((values[0] - minValue) / (maxValue - minValue)) * 100);
@@ -235,20 +235,4 @@ const InputDoubleRange: React.FC<InputDoubleRangeProps> = ({
);
};
InputDoubleRange.defaultProps = {
minValue: 0,
maxValue: 100,
step: 1,
unit: '',
containerClassName: '',
inputClassName: '',
labelClassName: '',
labelVariant: 'body1',
showLabel: false,
labelPosition: 'left',
trackColor: 'primary',
allowNumberEdit: false,
showAdjustmentArrows: false,
};
export default InputDoubleRange;
@@ -17,7 +17,7 @@ const InputLabelWrapper = ({
isSortable,
sortDirection,
onLabelClick,
className,
className = '',
children,
}) => {
const onClickHandler = e => {
@@ -53,10 +53,6 @@ const InputLabelWrapper = ({
);
};
InputLabelWrapper.defaultProps = {
className: '',
};
InputLabelWrapper.propTypes = {
label: PropTypes.string.isRequired,
isSortable: PropTypes.bool.isRequired,
@@ -10,9 +10,9 @@ const InputMultiSelect = ({
isSortable,
sortDirection,
onLabelClick,
value,
placeholder,
options,
value = [],
placeholder = '',
options = [],
onChange,
}) => {
return (
@@ -50,12 +50,6 @@ const InputMultiSelect = ({
);
};
InputMultiSelect.defaultProps = {
value: [],
placeholder: '',
options: [],
};
InputMultiSelect.propTypes = {
id: PropTypes.string,
label: PropTypes.string.isRequired,
@@ -4,7 +4,15 @@ import PropTypes from 'prop-types';
import Input from '../Input';
import InputLabelWrapper from '../InputLabelWrapper';
const InputText = ({ id, label, isSortable, sortDirection, onLabelClick, value, onChange }) => {
const InputText = ({
id,
label,
isSortable = false,
sortDirection = 'none',
onLabelClick = () => {},
value = '',
onChange,
}) => {
return (
<InputLabelWrapper
label={label}
@@ -26,13 +34,6 @@ const InputText = ({ id, label, isSortable, sortDirection, onLabelClick, value,
);
};
InputText.defaultProps = {
value: '',
isSortable: false,
onLabelClick: () => {},
sortDirection: 'none',
};
InputText.propTypes = {
id: PropTypes.string,
label: PropTypes.string.isRequired,
@@ -9,7 +9,11 @@ export enum showDialogOption {
ShowOnceAndConfigure = 'configure',
}
const InvestigationalUseDialog = ({ dialogConfiguration }) => {
const InvestigationalUseDialog = ({
dialogConfiguration = {
option: showDialogOption.AlwaysShowDialog,
},
}) => {
const { option, days } = dialogConfiguration;
const [isHidden, setIsHidden] = useState(true);
@@ -97,13 +101,7 @@ InvestigationalUseDialog.propTypes = {
dialogConfiguration: PropTypes.shape({
option: PropTypes.oneOf(Object.values(showDialogOption)).isRequired,
days: PropTypes.number,
}).isRequired,
};
InvestigationalUseDialog.defaultProps = {
dialogConfiguration: {
option: showDialogOption.AlwaysShowDialog,
},
}),
};
export default InvestigationalUseDialog;
@@ -4,7 +4,7 @@ import classNames from 'classnames';
import Icon from '../Icon/Icon';
function LayoutPreset({
onSelection,
onSelection = () => {},
title,
icon,
commandOptions,
@@ -27,10 +27,6 @@ function LayoutPreset({
);
}
LayoutPreset.defaultProps = {
onSelection: () => {},
};
LayoutPreset.propTypes = {
onSelection: PropTypes.func.isRequired,
title: PropTypes.string,
@@ -1,7 +1,7 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
function LayoutSelector({ onSelection, rows, columns }) {
function LayoutSelector({ onSelection = () => {}, rows = 3, columns = 4 }) {
const [hoveredIndex, setHoveredIndex] = useState();
const hoverX = hoveredIndex % columns;
const hoverY = Math.floor(hoveredIndex / columns);
@@ -48,12 +48,6 @@ function LayoutSelector({ onSelection, rows, columns }) {
);
}
LayoutSelector.defaultProps = {
onSelection: () => {},
columns: 4,
rows: 3,
};
LayoutSelector.propTypes = {
onSelection: PropTypes.func.isRequired,
columns: PropTypes.number.isRequired,
@@ -137,7 +137,7 @@ const fullWidthClasses = {
};
const LegacyButton = ({
children,
children = '',
variant = defaults.variant,
color = defaults.color,
border = defaults.border,
@@ -151,7 +151,7 @@ const LegacyButton = ({
endIcon: endIconProp,
name,
className,
onClick,
onClick = () => {},
/** TODO: All possible props should be explicitly defined -- avoid spreading props */
...rest
}) => {
@@ -206,13 +206,6 @@ const LegacyButton = ({
);
};
LegacyButton.defaultProps = {
color: defaults.color,
disabled: false,
children: '',
onClick: () => {},
};
LegacyButton.propTypes = {
/** What is inside the button, can be text or react component */
children: PropTypes.node,
@@ -8,14 +8,14 @@ import Icon from '../Icon';
import './LegacyCinePlayerCustomInputRange.css';
const LegacyCinePlayer = ({
isPlaying,
minFrameRate,
maxFrameRate,
stepFrameRate,
frameRate: defaultFrameRate,
onFrameRateChange,
onPlayPauseChange,
onClose,
isPlaying = false,
minFrameRate = 1,
maxFrameRate = 90,
stepFrameRate = 1,
frameRate: defaultFrameRate = 24,
onFrameRateChange = () => {},
onPlayPauseChange = () => {},
onClose = () => {},
}) => {
const [frameRate, setFrameRate] = useState(defaultFrameRate);
const debouncedSetFrameRate = debounce(onFrameRateChange, 300);
@@ -74,17 +74,6 @@ const LegacyCinePlayer = ({
const noop = () => {};
LegacyCinePlayer.defaultProps = {
isPlaying: false,
minFrameRate: 1,
maxFrameRate: 90,
stepFrameRate: 1,
frameRate: 24,
onPlayPauseChange: noop,
onFrameRateChange: noop,
onClose: noop,
};
LegacyCinePlayer.propTypes = {
/** Minimum value for range slider */
minFrameRate: PropTypes.number.isRequired,
@@ -283,10 +283,6 @@ const LegacySidePanel = ({
);
};
LegacySidePanel.defaultProps = {
defaultComponentOpen: null,
};
LegacySidePanel.propTypes = {
servicesManager: PropTypes.object.isRequired,
side: PropTypes.oneOf(['left', 'right']).isRequired,
@@ -72,10 +72,10 @@ const LegacySplitButton = ({
primary,
secondary,
items,
renderer,
renderer = null,
onInteraction,
Component,
isActive,
Component = null,
isActive = false,
}) => {
const { t } = useTranslation('Buttons');
const [state, setState] = useState({ isHovering: false, isExpanded: false });
@@ -149,7 +149,6 @@ const LegacySplitButton = ({
};
LegacySplitButton.propTypes = {
isToggle: PropTypes.bool,
groupId: PropTypes.string.isRequired,
primary: PropTypes.object.isRequired,
secondary: PropTypes.object.isRequired,
@@ -160,11 +159,4 @@ LegacySplitButton.propTypes = {
Component: PropTypes.elementType,
};
LegacySplitButton.defaultProps = {
isToggle: false,
renderer: null,
isActive: false,
Component: null,
};
export default LegacySplitButton;
@@ -11,11 +11,11 @@ import LegacyCinePlayer from '../LegacyCinePlayer';
const LegacyViewportActionBar = ({
studyData,
showNavArrows,
showStatus,
showCine,
cineProps,
showPatientInfo: patientInfoVisibility,
showNavArrows = true,
showStatus = true,
showCine = false,
cineProps = {},
showPatientInfo: patientInfoVisibility = false,
onArrowsClick,
onDoubleClick,
getStatusComponent,
@@ -166,14 +166,8 @@ LegacyViewportActionBar.propTypes = {
}),
}).isRequired,
getStatusComponent: PropTypes.func.isRequired,
};
LegacyViewportActionBar.defaultProps = {
cineProps: {},
showCine: false,
showStatus: true,
showNavArrows: true,
showPatientInfo: false,
onDoubleClick: PropTypes.func.isRequired,
showStatus: PropTypes.bool,
};
export default LegacyViewportActionBar;
@@ -10,11 +10,11 @@ const LineChart = ({
height: heightProp,
axis,
series,
showAxisLabels,
showAxisGrid,
showLegend,
legendWidth,
transparentChartBackground,
showAxisLabels = true,
showAxisGrid = true,
showLegend = false,
legendWidth = 120,
transparentChartBackground = false,
containerClassName,
chartContainerClassName,
}: {
@@ -105,14 +105,6 @@ const LineChart = ({
);
};
LineChart.defaultProps = {
showAxisLabels: true,
showAxisGrid: true,
showLegend: false,
legendWidth: 120,
transparentChartBackground: false,
};
LineChart.propTypes = {
title: PropTypes.string,
width: PropTypes.number,
@@ -5,7 +5,7 @@ import PropTypes from 'prop-types';
const flex = 'flex flex-row justify-between items-center';
const theme = 'bg-indigo-dark text-white';
const ListMenu = ({ items = [], renderer, onClick }) => {
const ListMenu = ({ items = [], renderer, onClick = () => {} }) => {
const [selectedIndex, setSelectedIndex] = useState(null);
const ListItem = ({ item, index, isSelected }) => {
@@ -48,16 +48,10 @@ const ListMenu = ({ items = [], renderer, onClick }) => {
);
};
const noop = () => {};
ListMenu.propTypes = {
items: PropTypes.array.isRequired,
renderer: PropTypes.func.isRequired,
onClick: PropTypes.func,
};
ListMenu.defaultProps = {
onClick: noop,
};
export default ListMenu;
@@ -9,7 +9,7 @@ const MeasurementItem = ({
index,
label,
displayText,
isActive,
isActive = false,
onClick,
onEdit,
onDelete,
@@ -110,8 +110,4 @@ MeasurementItem.propTypes = {
onDelete: PropTypes.func,
};
MeasurementItem.defaultProps = {
isActive: false,
};
export default MeasurementItem;
@@ -5,10 +5,10 @@ import { useTranslation } from 'react-i18next';
import MeasurementItem from './MeasurementItem';
const MeasurementTable = ({
data,
data = [],
title,
onClick,
onEdit,
onClick = () => {},
onEdit = () => {},
onDelete,
servicesManager,
}: withAppTypes) => {
@@ -71,12 +71,6 @@ const MeasurementTable = ({
);
};
MeasurementTable.defaultProps = {
data: [],
onClick: () => {},
onEdit: () => {},
};
MeasurementTable.propTypes = {
title: PropTypes.string.isRequired,
data: PropTypes.arrayOf(
+2 -10
View File
@@ -15,12 +15,12 @@ if (typeof document !== 'undefined') {
const Modal = ({
closeButton,
shouldCloseOnEsc,
shouldCloseOnEsc = true,
isOpen,
title,
onClose,
children,
shouldCloseOnOverlayClick,
shouldCloseOnOverlayClick = true,
movable = false,
containerDimensions = null,
contentDimensions = null,
@@ -99,14 +99,6 @@ const Modal = ({
);
};
Modal.defaultProps = {
shouldCloseOnEsc: true,
shouldCloseOnOverlayClick: true,
movable: false,
containerDimensions: null,
contentDimensions: null,
};
Modal.propTypes = {
closeButton: PropTypes.bool,
shouldCloseOnEsc: PropTypes.bool,
@@ -5,7 +5,15 @@ import PropTypes from 'prop-types';
import Button, { ButtonEnums } from '../Button';
import Icon from '../Icon';
const Notification = ({ id, type, message, actions, onSubmit, onOutsideClick, onKeyPress }) => {
const Notification = ({
id,
type = 'info',
message,
actions,
onSubmit,
onOutsideClick = () => {},
onKeyPress,
}) => {
const notificationRef = useRef(null);
useEffect(() => {
@@ -99,11 +107,6 @@ const Notification = ({ id, type, message, actions, onSubmit, onOutsideClick, on
);
};
Notification.defaultProps = {
type: 'info',
onOutsideClick: () => {},
};
Notification.propTypes = {
type: PropTypes.oneOf(['error', 'warning', 'info', 'success']),
message: PropTypes.string.isRequired,
@@ -49,8 +49,6 @@ const PanelSection = ({ title, children, actionIcons = [], childrenClassName })
);
};
PanelSection.defaultProps = {};
PanelSection.propTypes = {
title: PropTypes.string,
children: PropTypes.node,
@@ -7,7 +7,7 @@ function SegmentationDropDownRow({
segmentations = [],
activeSegmentation,
onActiveSegmentationChange,
disableEditing,
disableEditing = false,
onToggleSegmentationVisibility,
onSegmentationEdit,
onSegmentationDownload,
@@ -180,9 +180,4 @@ SegmentationDropDownRow.propTypes = {
onSegmentationAdd: PropTypes.func,
};
SegmentationDropDownRow.defaultProps = {
segmentations: [],
disableEditing: false,
};
export default SegmentationDropDownRow;
@@ -8,7 +8,7 @@ const SegmentItem = ({
segmentIndex,
segmentationId,
label,
isActive,
isActive = false,
isVisible,
color,
showDelete,
@@ -236,8 +236,4 @@ SegmentItem.propTypes = {
displayText: PropTypes.string,
};
SegmentItem.defaultProps = {
isActive: false,
};
export default SegmentItem;
@@ -9,39 +9,34 @@ import SegmentationGroupSegment from './SegmentationGroupSegment';
import { useTranslation } from 'react-i18next';
const SegmentationGroupTable = ({
segmentations,
// segmentation initial config
segmentations = [],
segmentationConfig,
// UI show/hide
disableEditing,
showAddSegmentation,
showAddSegment,
showDeleteSegment,
// segmentation/segment handlers
onSegmentationAdd,
onSegmentationEdit,
onSegmentationClick,
onSegmentationDelete,
onSegmentationDownload,
onSegmentationDownloadRTSS,
storeSegmentation,
// segment handlers
onSegmentClick,
onSegmentAdd,
onSegmentDelete,
onSegmentEdit,
onToggleSegmentationVisibility,
onToggleSegmentVisibility,
onToggleSegmentLock,
onSegmentColorClick,
// segmentation config handlers
setFillAlpha,
setFillAlphaInactive,
setOutlineWidthActive,
setOutlineOpacityActive,
setRenderFill,
setRenderInactiveSegmentations,
setRenderOutline,
disableEditing = false,
showAddSegmentation = true,
showAddSegment = true,
showDeleteSegment = true,
onSegmentationAdd = () => {},
onSegmentationEdit = () => {},
onSegmentationClick = () => {},
onSegmentationDelete = () => {},
onSegmentationDownload = () => {},
onSegmentationDownloadRTSS = () => {},
storeSegmentation = () => {},
onSegmentClick = () => {},
onSegmentAdd = () => {},
onSegmentDelete = () => {},
onSegmentEdit = () => {},
onToggleSegmentationVisibility = () => {},
onToggleSegmentVisibility = () => {},
onToggleSegmentLock = () => {},
onSegmentColorClick = () => {},
setFillAlpha = () => {},
setFillAlphaInactive = () => {},
setOutlineWidthActive = () => {},
setOutlineOpacityActive = () => {},
setRenderFill = () => {},
setRenderInactiveSegmentations = () => {},
setRenderOutline = () => {},
addSegmentationClassName,
}) => {
const [isConfigOpen, setIsConfigOpen] = useState(false);
@@ -216,33 +211,4 @@ SegmentationGroupTable.propTypes = {
setRenderOutline: PropTypes.func.isRequired,
};
SegmentationGroupTable.defaultProps = {
segmentations: [],
disableEditing: false,
showAddSegmentation: true,
showAddSegment: true,
showDeleteSegment: true,
onSegmentationAdd: () => {},
onSegmentationEdit: () => {},
onSegmentationClick: () => {},
onSegmentationDelete: () => {},
onSegmentationDownload: () => {},
onSemgnetationDownloadRTSS: () => {},
storeSegmentation: () => {},
onSegmentClick: () => {},
onSegmentAdd: () => {},
onSegmentDelete: () => {},
onSegmentEdit: () => {},
onToggleSegmentationVisibility: () => {},
onToggleSegmentVisibility: () => {},
onToggleSegmentLock: () => {},
onSegmentColorClick: () => {},
setFillAlpha: () => {},
setFillAlphaInactive: () => {},
setOutlineWidthActive: () => {},
setOutlineOpacityActive: () => {},
setRenderFill: () => {},
setRenderInactiveSegmentations: () => {},
setRenderOutline: () => {},
};
export default SegmentationGroupTable;
@@ -7,39 +7,34 @@ import { useTranslation } from 'react-i18next';
import SegmentationItem from './SegmentationItem';
const SegmentationGroupTableExpanded = ({
segmentations,
// segmentation initial config
segmentations = [],
segmentationConfig,
// UI show/hide
disableEditing,
showAddSegmentation,
showAddSegment,
showDeleteSegment,
// segmentation/segment handlers
onSegmentationAdd,
onSegmentationEdit,
onSegmentationClick,
onSegmentationDelete,
onSegmentationDownload,
onSegmentationDownloadRTSS,
storeSegmentation,
// segment handlers
onSegmentClick,
onSegmentAdd,
onSegmentDelete,
onSegmentEdit,
onToggleSegmentationVisibility,
onToggleSegmentVisibility,
onToggleSegmentLock,
onSegmentColorClick,
// segmentation config handlers
setFillAlpha,
setFillAlphaInactive,
setOutlineWidthActive,
setOutlineOpacityActive,
setRenderFill,
setRenderInactiveSegmentations,
setRenderOutline,
disableEditing = false,
showAddSegmentation = true,
showAddSegment = true,
showDeleteSegment = true,
onSegmentationAdd = () => {},
onSegmentationEdit = () => {},
onSegmentationClick = () => {},
onSegmentationDelete = () => {},
onSegmentationDownload = () => {},
onSegmentationDownloadRTSS = () => {},
storeSegmentation = () => {},
onSegmentClick = () => {},
onSegmentAdd = () => {},
onSegmentDelete = () => {},
onSegmentEdit = () => {},
onToggleSegmentationVisibility = () => {},
onToggleSegmentVisibility = () => {},
onToggleSegmentLock = () => {},
onSegmentColorClick = () => {},
setFillAlpha = () => {},
setFillAlphaInactive = () => {},
setOutlineWidthActive = () => {},
setOutlineOpacityActive = () => {},
setRenderFill = () => {},
setRenderInactiveSegmentations = () => {},
setRenderOutline = () => {},
}) => {
const [isConfigOpen, setIsConfigOpen] = useState(false);
const [activeSegmentationId, setActiveSegmentationId] = useState(null);
@@ -179,33 +174,4 @@ SegmentationGroupTableExpanded.propTypes = {
setRenderOutline: PropTypes.func.isRequired,
};
SegmentationGroupTableExpanded.defaultProps = {
segmentations: [],
disableEditing: false,
showAddSegmentation: true,
showAddSegment: true,
showDeleteSegment: true,
onSegmentationAdd: () => {},
onSegmentationEdit: () => {},
onSegmentationClick: () => {},
onSegmentationDelete: () => {},
onSegmentationDownload: () => {},
onSemgnetationDownloadRTSS: () => {},
storeSegmentation: () => {},
onSegmentClick: () => {},
onSegmentAdd: () => {},
onSegmentDelete: () => {},
onSegmentEdit: () => {},
onToggleSegmentationVisibility: () => {},
onToggleSegmentVisibility: () => {},
onToggleSegmentLock: () => {},
onSegmentColorClick: () => {},
setFillAlpha: () => {},
setFillAlphaInactive: () => {},
setOutlineWidthActive: () => {},
setOutlineOpacityActive: () => {},
setRenderFill: () => {},
setRenderInactiveSegmentations: () => {},
setRenderOutline: () => {},
};
export default SegmentationGroupTableExpanded;
@@ -8,8 +8,8 @@ import SegmentationGroupSegment from './SegmentationGroupSegment';
import { Tooltip } from '../../components';
function SegmentationItem({
segmentation,
disableEditing,
segmentation = {},
disableEditing = false,
onSegmentationEdit,
onSegmentationDownload,
onSegmentationDownloadRTSS,
@@ -203,9 +203,4 @@ SegmentationItem.propTypes = {
activeSegmentationId: PropTypes.string,
};
SegmentationItem.defaultProps = {
segmentation: null,
disableEditing: false,
};
export default SegmentationItem;
@@ -8,8 +8,8 @@ const SegmentationItem = ({
id,
index,
label,
displayText,
isActive,
displayText = [],
isActive = false,
isVisible,
onClick,
onEdit,
@@ -138,9 +138,4 @@ SegmentationItem.propTypes = {
toggleVisibility: PropTypes.func,
};
SegmentationItem.defaultProps = {
isActive: false,
displayText: [],
};
export default SegmentationItem;
@@ -5,14 +5,14 @@ import SegmentationItem from './SegmentationItem';
import Icon from '../Icon';
const SegmentationTable = ({
title,
segmentations,
activeSegmentationId,
onClick,
onEdit,
onDelete,
onToggleVisibility,
onToggleVisibilityAll,
title = '',
segmentations = [],
activeSegmentationId = '',
onClick = () => {},
onEdit = () => {},
onDelete = () => {},
onToggleVisibility = () => {},
onToggleVisibilityAll = () => {},
}) => {
const [hiddenSegmentationIds, setHiddenSegmentationIds] = useState([]);
const amount = segmentations.length;
@@ -112,14 +112,4 @@ SegmentationTable.propTypes = {
onToggleVisibilityAll: PropTypes.func.isRequired,
};
SegmentationTable.defaultProps = {
title: '',
segmentations: [],
activeSegmentationId: '',
onClick: () => {},
onEdit: () => {},
onToggleVisibility: () => {},
onToggleVisibilityAll: () => {},
};
export default SegmentationTable;
+14 -30
View File
@@ -42,20 +42,20 @@ const Option = props => {
const Select = ({
id,
className,
closeMenuOnSelect,
hideSelectedOptions,
isClearable,
isDisabled,
isMulti,
isSearchable,
className = '',
closeMenuOnSelect = true,
hideSelectedOptions = false,
isClearable = true,
isDisabled = false,
isMulti = false,
isSearchable = true,
onChange,
options,
placeholder,
noIcons,
menuPlacement,
components,
value,
noIcons = false,
menuPlacement = 'auto',
components = {},
value = [],
}) => {
const _noIconComponents = {
DropdownIndicator: () => null,
@@ -96,11 +96,9 @@ const Select = ({
options={options}
blurInputOnSelect={true}
menuPortalTarget={document.body}
styles={
{
menuPortal: base => ({ ...base, zIndex: 9999 }),
}
}
styles={{
menuPortal: base => ({ ...base, zIndex: 9999 }),
}}
value={value && Array.isArray(value) ? selectedOptions : value}
onChange={(selectedOptions, { action }) => {
const newSelection = !selectedOptions.length
@@ -112,20 +110,6 @@ const Select = ({
);
};
Select.defaultProps = {
className: '',
closeMenuOnSelect: true,
hideSelectedOptions: false,
isClearable: true,
components: {},
isDisabled: false,
isMulti: false,
isSearchable: true,
noIcons: false,
menuPlacement: 'auto',
value: [],
};
Select.propTypes = {
className: PropTypes.string,
closeMenuOnSelect: PropTypes.bool,
@@ -161,7 +161,7 @@ const createBaseStyle = (expandedWidth: number) => {
const SidePanel = ({
side,
className,
activeTabIndex: activeTabIndexProp,
activeTabIndex: activeTabIndexProp = null,
tabs,
onOpen,
expandedWidth = 248,
@@ -400,11 +400,6 @@ const SidePanel = ({
);
};
SidePanel.defaultProps = {
defaultComponentOpen: null,
activeTabIndex: null, // the default is to close the side panel
};
SidePanel.propTypes = {
side: PropTypes.oneOf(['left', 'right']).isRequired,
className: PropTypes.string,
@@ -91,9 +91,9 @@ const SplitButton = ({
primary,
secondary,
items,
renderer,
renderer = null,
onInteraction,
Component,
Component = Icon,
}) => {
const { t } = useTranslation('Buttons');
const [state, setState] = useState({ isHovering: false, isExpanded: false });
@@ -172,7 +172,6 @@ const SplitButton = ({
};
SplitButton.propTypes = {
isToggle: PropTypes.bool,
groupId: PropTypes.string.isRequired,
primary: PropTypes.object.isRequired,
secondary: PropTypes.object.isRequired,
@@ -184,11 +183,4 @@ SplitButton.propTypes = {
interactionType: PropTypes.oneOf(['action', 'tool', 'toggle']),
};
SplitButton.defaultProps = {
isToggle: false,
renderer: null,
isActive: false,
Component: null,
};
export default SplitButton;
@@ -19,15 +19,17 @@ const getTrackedSeries = displaySets => {
return trackedSeries;
};
const noop = () => {};
const StudyBrowser = ({
tabs,
activeTabName,
expandedStudyInstanceUIDs,
onClickTab,
onClickStudy,
onClickThumbnail,
onDoubleClickThumbnail,
onClickUntrack,
onClickTab = noop,
onClickStudy = noop,
onClickThumbnail = noop,
onDoubleClickThumbnail = noop,
onClickUntrack = noop,
activeDisplaySetInstanceUIDs,
servicesManager,
}: withAppTypes) => {
@@ -168,14 +170,4 @@ StudyBrowser.propTypes = {
),
};
const noop = () => {};
StudyBrowser.defaultProps = {
onClickTab: noop,
onClickStudy: noop,
onClickThumbnail: noop,
onDoubleClickThumbnail: noop,
onClickUntrack: noop,
};
export default StudyBrowser;
+1 -1
View File
@@ -1,6 +1,6 @@
import React from 'react';
// Svgs
import logoOhif from './../../assets/svgs/ohif-logo.svg';
import { ReactComponent as logoOhif } from './../../assets/svgs/ohif-logo.svg';
const SVGS = {
'logo-ohif': logoOhif,
+1 -7
View File
@@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
const Table = ({ children, className, fullWidth, style }) => {
const Table = ({ children, className = '', fullWidth = true, style = {} }) => {
const classes = {
base: 'text-lg text-white',
fullWidth: {
@@ -21,12 +21,6 @@ const Table = ({ children, className, fullWidth, style }) => {
);
};
Table.defaultProps = {
className: '',
fullWidth: true,
style: {},
};
Table.propTypes = {
fullWidth: PropTypes.bool,
children: PropTypes.node.isRequired,
@@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
const TableBody = ({ children, className, style }) => {
const TableBody = ({ children, className = '', style = {} }) => {
return (
<div
className={classnames('ohif-scrollbar mt-2 max-h-48 overflow-y-scroll', className)}
@@ -17,11 +17,6 @@ const TableBody = ({ children, className, style }) => {
);
};
TableBody.defaultProps = {
className: '',
style: {},
};
TableBody.propTypes = {
children: function (props, propName, componentName) {
const elements = React.Children.toArray(props.children);
@@ -4,14 +4,14 @@ import classnames from 'classnames';
const TableCell = ({
children,
className,
colSpan,
className = '',
colSpan = 1,
// ignored because we don't want to expose this prop
// eslint-disable-next-line react/prop-types
cellsNum,
isTableHead,
align,
style,
isTableHead = false,
align = 'left',
style = {},
}) => {
const classes = {
align: {
@@ -42,14 +42,6 @@ const TableCell = ({
);
};
TableCell.defaultProps = {
align: 'left',
className: '',
colSpan: 1,
isTableHead: false,
style: {},
};
TableCell.propTypes = {
align: PropTypes.oneOf(['left', 'center', 'right', 'justify']),
children: PropTypes.node.isRequired,
@@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
const TableHead = ({ children, className, style }) => {
const TableHead = ({ children, className = '', style = {} }) => {
return (
<div
className={classnames(
@@ -20,11 +20,6 @@ const TableHead = ({ children, className, style }) => {
);
};
TableHead.defaultProps = {
className: '',
style: {},
};
TableHead.propTypes = {
children: function (props, propName, componentName) {
const elements = React.Children.toArray(props.children);
@@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
const TableRow = ({ children, className, isTableHead, style }) => {
const TableRow = ({ children, className = '', isTableHead = false, style = {} }) => {
const childrens = React.Children.map(children, child => {
const isValidReactElement = React.isValidElement(child);
@@ -21,12 +21,6 @@ const TableRow = ({ children, className, isTableHead, style }) => {
);
};
TableRow.defaultProps = {
isTableHead: false,
className: '',
style: {},
};
TableRow.propTypes = {
isTableHead: PropTypes.bool,
children: function (props, propName, componentName) {
@@ -19,7 +19,7 @@ const Thumbnail = ({
numInstances,
countIcon,
messages,
dragData,
dragData = {},
isActive,
onClick,
onDoubleClick,
@@ -130,8 +130,4 @@ Thumbnail.propTypes = {
onDoubleClick: PropTypes.func.isRequired,
};
Thumbnail.defaultProps = {
dragData: {},
};
export default Thumbnail;
@@ -12,7 +12,7 @@ const ToolbarButton = ({
label,
commands,
onInteraction,
dropdownContent,
dropdownContent = null,
//
className,
disabled,
@@ -65,10 +65,6 @@ const ToolbarButton = ({
);
};
ToolbarButton.defaultProps = {
dropdownContent: null,
};
ToolbarButton.propTypes = {
/* Influences background/hover styling */
id: PropTypes.string.isRequired,
Loaded 100 of 153 files, more files were not shown because too many files have changed in this diff. Show more