fix/chore: Various style improvements (#2015)
* Update all the icons * Turn off global tool sync; watch as the world burns * Shift a bunch of things around so we can start tracking/setting per element * ToolBarService to initiate one of three different calls; callback passed to all button types * SplitButton and Toolbar Button to use new `onInteraction` prop and new toolbar state * Changes to toolbar button interface and config * Fix broken layout selector * Update SR viewport to activate tools in viewport component * Duplicate activation logic in measurement tracking extension * Add alternative/dashed variants for SR Viewport * pass through "setToolActive" commands for other viewport types * fix small overlay bugs (no wwwc or scale info) * Show SpacingBetweenSlices instead of PixelSpacing in patient information dialog * Fix prop-types * Update tracked viewport to have alternative tracked styling * alt styling for SR viewports * Update to support isLocked + isRehydratable * fix broken logic * fix broken logic * switch icon style * fix icon styles * hover and click to start flow * expedited workflow when data is not dirty (just after SR hydration) * fix: setting elliptical roi tool * fix arrow annotate dialog * fix: do not show learn more button for now * remove dead code * simpler cache invalidation * simpler cache invalidation part 2 * Fix for unable to spand study cards on separate pages * fix: viewport grid area top padding should align with sidepanel gap * fix: don't wrap split button list item text * fix: cine player moz/chrome styles * fix: split button arrows * fix: tighten toolbar and splitbutton styles * chore: clean up worklist sort logic
This commit is contained in:
parent
ba38cebcd7
commit
2fc7169fa4
@ -163,7 +163,7 @@ function ViewerLayout({
|
||||
</Header>
|
||||
<div
|
||||
className="flex flex-row flex-no-wrap items-stretch w-full overflow-hidden"
|
||||
style={{ height: 'calc(100vh - 57px' }}
|
||||
style={{ height: 'calc(100vh - 52px' }}
|
||||
>
|
||||
{/* LEFT SIDEPANELS */}
|
||||
{leftPanelComponents.length && (
|
||||
@ -177,7 +177,7 @@ function ViewerLayout({
|
||||
)}
|
||||
{/* TOOLBAR + GRID */}
|
||||
<div className="flex flex-col flex-1 h-full">
|
||||
<div className="flex items-center justify-center flex-1 h-full pt-1 pb-2 overflow-hidden bg-black">
|
||||
<div className="flex items-center justify-center flex-1 h-full -mt-1 overflow-hidden bg-black">
|
||||
<ErrorBoundary context="Grid">
|
||||
<ViewportGridComp
|
||||
servicesManager={servicesManager}
|
||||
|
||||
@ -120,9 +120,9 @@ export default [
|
||||
),
|
||||
secondary: {
|
||||
icon: 'chevron-down',
|
||||
label: '',
|
||||
label: 'W/L Manual',
|
||||
isActive: true,
|
||||
tooltip: 'More Measure Tools',
|
||||
tooltip: 'W/L Presets',
|
||||
},
|
||||
isAction: true, // ?
|
||||
renderer: WindowLevelMenuItem,
|
||||
@ -177,7 +177,7 @@ export default [
|
||||
icon: 'chevron-down',
|
||||
label: '',
|
||||
isActive: true,
|
||||
tooltip: 'More Measure Tools',
|
||||
tooltip: 'More Tools',
|
||||
},
|
||||
items: [
|
||||
_createActionButton(
|
||||
|
||||
@ -13,7 +13,7 @@ const CinePlayer = ({
|
||||
frameRate: defaultFrameRate,
|
||||
onFrameRateChange,
|
||||
onPlayPauseChange,
|
||||
onClose
|
||||
onClose,
|
||||
}) => {
|
||||
const [frameRate, setFrameRate] = useState(defaultFrameRate);
|
||||
const debouncedSetFrameRate = debounce(onFrameRateChange, 300);
|
||||
@ -28,21 +28,21 @@ const CinePlayer = ({
|
||||
|
||||
const action = {
|
||||
false: { icon: 'old-play' },
|
||||
true: { icon: 'old-stop' }
|
||||
true: { icon: 'old-stop' },
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="CinePlayer flex flex-row h-10 items-center justify-center border border-primary-light rounded-full">
|
||||
<div className="flex flex-row items-center justify-center h-10 border rounded-full CinePlayer border-primary-light">
|
||||
<IconButton
|
||||
variant="text"
|
||||
color="inherit"
|
||||
size="initial"
|
||||
className="mr-3 ml-4 text-primary-active"
|
||||
className="ml-4 mr-3 text-primary-active"
|
||||
onClick={onPlayPauseChangeHandler}
|
||||
>
|
||||
<Icon width="15px" height="15px" name={action[isPlaying].icon} />
|
||||
</IconButton>
|
||||
<div className="flex flex-col h-full justify-center pt-2 mr-3 pl-1 pr-1">
|
||||
<div className="flex flex-col justify-center h-full pt-2 pl-1 pr-1 mr-3">
|
||||
<input
|
||||
type="range"
|
||||
name="frameRate"
|
||||
@ -52,12 +52,14 @@ const CinePlayer = ({
|
||||
value={frameRate}
|
||||
onChange={onFrameRateChangeHandler}
|
||||
/>
|
||||
<p className="mt-1 text-sm text-primary-light">{`${frameRate.toFixed(1)} fps`}</p>
|
||||
<p className="-mt-2 text-sm text-primary-light">{`${frameRate.toFixed(
|
||||
1
|
||||
)} fps`}</p>
|
||||
</div>
|
||||
<IconButton
|
||||
color="inherit"
|
||||
size="initial"
|
||||
className="mr-3 text-primary-active border border-primary-active rounded-full"
|
||||
className="mr-3 border rounded-full text-primary-active border-primary-active"
|
||||
onClick={onClose}
|
||||
>
|
||||
<Icon name="close" width="15px" height="15px" />
|
||||
@ -66,7 +68,7 @@ const CinePlayer = ({
|
||||
);
|
||||
};
|
||||
|
||||
const noop = () => { };
|
||||
const noop = () => {};
|
||||
|
||||
CinePlayer.defaultProps = {
|
||||
isPlaying: false,
|
||||
@ -76,7 +78,7 @@ CinePlayer.defaultProps = {
|
||||
frameRate: 24,
|
||||
onPlayPauseChange: noop,
|
||||
onFrameRateChange: noop,
|
||||
onClose: noop
|
||||
onClose: noop,
|
||||
};
|
||||
|
||||
CinePlayer.propTypes = {
|
||||
|
||||
@ -3,17 +3,19 @@
|
||||
* written in plain CSS with color variables from tailwind
|
||||
* to avoid complex compatibility configuration.
|
||||
*/
|
||||
.CinePlayer input[type=range] {
|
||||
.CinePlayer input[type='range'] {
|
||||
-webkit-appearance: none;
|
||||
background: transparent;
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.CinePlayer input[type=range]:focus {
|
||||
.CinePlayer input[type='range']:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.CinePlayer input[type=range]::-webkit-slider-runnable-track {
|
||||
.CinePlayer input[type='range']::-webkit-slider-runnable-track {
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
cursor: pointer;
|
||||
@ -24,7 +26,7 @@
|
||||
border: 0px solid #000000;
|
||||
}
|
||||
|
||||
.CinePlayer input[type=range]::-webkit-slider-thumb {
|
||||
.CinePlayer input[type='range']::-webkit-slider-thumb {
|
||||
box-shadow: 0px 0px 0px #000000;
|
||||
border: 4px solid #000000;
|
||||
height: 18px;
|
||||
@ -36,11 +38,11 @@
|
||||
margin-top: -9px;
|
||||
}
|
||||
|
||||
.CinePlayer input[type=range]:focus::-webkit-slider-runnable-track {
|
||||
.CinePlayer input[type='range']:focus::-webkit-slider-runnable-track {
|
||||
@apply bg-primary-light;
|
||||
}
|
||||
|
||||
.CinePlayer input[type=range]::-moz-range-track {
|
||||
.CinePlayer input[type='range']::-moz-range-track {
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
cursor: pointer;
|
||||
@ -51,17 +53,17 @@
|
||||
border: 0px solid #000000;
|
||||
}
|
||||
|
||||
.CinePlayer input[type=range]::-moz-range-thumb {
|
||||
.CinePlayer input[type='range']::-moz-range-thumb {
|
||||
box-shadow: 0px 0px 0px #000000;
|
||||
border: 4px solid #000000;
|
||||
height: 18px;
|
||||
width: 17px;
|
||||
border: 2px solid #000000;
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
border-radius: 50px;
|
||||
@apply bg-primary-light;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.CinePlayer input[type=range]::-ms-track {
|
||||
.CinePlayer input[type='range']::-ms-track {
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
cursor: pointer;
|
||||
@ -71,21 +73,21 @@
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.CinePlayer input[type=range]::-ms-fill-lower {
|
||||
.CinePlayer input[type='range']::-ms-fill-lower {
|
||||
@apply bg-primary-light;
|
||||
border: 0px solid #000000;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0px 0px 0px #000000;
|
||||
}
|
||||
|
||||
.CinePlayer input[type=range]::-ms-fill-upper {
|
||||
.CinePlayer input[type='range']::-ms-fill-upper {
|
||||
@apply bg-primary-light;
|
||||
border: 0px solid #000000;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0px 0px 0px #000000;
|
||||
}
|
||||
|
||||
.CinePlayer input[type=range]::-ms-thumb {
|
||||
.CinePlayer input[type='range']::-ms-thumb {
|
||||
margin-top: 1px;
|
||||
box-shadow: 0px 0px 0px #000000;
|
||||
border: 4px solid #000000;
|
||||
@ -96,10 +98,10 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.CinePlayer input[type=range]:focus::-ms-fill-lower {
|
||||
.CinePlayer input[type='range']:focus::-ms-fill-lower {
|
||||
@apply bg-primary-light;
|
||||
}
|
||||
|
||||
.CinePlayer input[type=range]:focus::-ms-fill-upper {
|
||||
.CinePlayer input[type='range']:focus::-ms-fill-upper {
|
||||
@apply bg-primary-light;
|
||||
}
|
||||
|
||||
@ -62,12 +62,14 @@ const sizeClasses = {
|
||||
medium: 'py-3 px-3 text-lg',
|
||||
large: 'py-4 px-4 text-xl',
|
||||
initial: '',
|
||||
toolbar: 'text-lg',
|
||||
};
|
||||
|
||||
const iconSizeClasses = {
|
||||
small: 'w-4 h-4',
|
||||
medium: 'w-5 h-5',
|
||||
large: 'w-6 h-6',
|
||||
toolbar: 'w-5 h-5',
|
||||
};
|
||||
|
||||
const fullWidthClasses = {
|
||||
@ -106,6 +108,9 @@ const IconButton = ({
|
||||
disabledClasses[disabled],
|
||||
className
|
||||
)}
|
||||
style={{
|
||||
padding: size === 'toolbar' ? '10px' : null,
|
||||
}}
|
||||
ref={buttonElement}
|
||||
onClick={handleOnClick}
|
||||
type={type}
|
||||
|
||||
@ -9,11 +9,12 @@ const NavBar = ({ className, children, isSticky }) => {
|
||||
return (
|
||||
<div
|
||||
className={classnames(
|
||||
'flex flex-row items-center bg-secondary-dark px-3 py-1 min-h-16 border-b-4 border-black z-20',
|
||||
'flex flex-row items-center bg-secondary-dark px-3 border-b-4 border-black z-20',
|
||||
isSticky && stickyClasses,
|
||||
!isSticky && notStickyClasses,
|
||||
className
|
||||
)}
|
||||
style={{ paddingTop: '4px', paddingBottom: '4px', minHeight: '52px' }}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
@ -7,7 +7,7 @@ import { Icon, Tooltip, ListMenu } from '@ohif/ui';
|
||||
|
||||
const baseClasses = {
|
||||
Button:
|
||||
'h-12 flex items-center rounded-md border-transparent border-2 cursor-pointer',
|
||||
'flex items-center rounded-md border-transparent border-2 cursor-pointer',
|
||||
Primary:
|
||||
'h-full flex flex-1 items-center rounded-md rounded-tr-none rounded-br-none',
|
||||
Secondary:
|
||||
@ -15,7 +15,7 @@ const baseClasses = {
|
||||
PrimaryIcon: 'w-5 h-5',
|
||||
SecondaryIcon: 'w-4 h-full stroke-1',
|
||||
Separator: 'border-l pt-2 pb-2',
|
||||
Content: 'absolute z-10 top-0 mt-16',
|
||||
Content: 'absolute z-10 top-0 mt-12',
|
||||
};
|
||||
|
||||
const classes = {
|
||||
@ -154,6 +154,7 @@ const SplitButton = ({
|
||||
...state,
|
||||
primary: { isActive: isPrimaryActive },
|
||||
})}
|
||||
style={{ height: '40px' }}
|
||||
onMouseEnter={onMouseEnterHandler}
|
||||
onMouseLeave={onMouseLeaveHandler}
|
||||
>
|
||||
@ -169,7 +170,10 @@ const SplitButton = ({
|
||||
isDisabled={!state.primary.tooltip}
|
||||
content={state.primary.tooltip}
|
||||
>
|
||||
<div className="flex items-center justify-center w-full h-full p-3">
|
||||
<div
|
||||
className="flex items-center justify-center w-full h-full"
|
||||
style={{ padding: '10px' }}
|
||||
>
|
||||
<Icon
|
||||
name={state.primary.icon}
|
||||
className={classes.PrimaryIcon({
|
||||
@ -225,10 +229,12 @@ const DefaultListItemRenderer = ({ icon, label, isActive }) => (
|
||||
isActive && 'bg-primary-dark'
|
||||
)}
|
||||
>
|
||||
<span className="mr-4 text-base text-common-bright">
|
||||
<span className="mr-4 text-base whitespace-pre text-common-bright">
|
||||
<Icon name={icon} className="w-5 h-5 text-common-bright" />
|
||||
</span>
|
||||
<span className="mr-5 text-base text-common-bright">{label}</span>
|
||||
<span className="mr-5 text-base whitespace-pre text-common-bright">
|
||||
{label}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
|
||||
@ -24,7 +24,7 @@ import { SplitButton, WindowLevelMenuItem } from '@ohif/ui';
|
||||
primary: {
|
||||
tooltip: 'W/L',
|
||||
icon: 'tool-window-level',
|
||||
onClick: (args) => console.debug('Primary click!', args)
|
||||
onClick: args => console.debug('Primary click!', args),
|
||||
},
|
||||
secondary: {
|
||||
icon: 'chevron-down',
|
||||
@ -33,23 +33,43 @@ import { SplitButton, WindowLevelMenuItem } from '@ohif/ui';
|
||||
tooltip: 'More Measure Tools',
|
||||
},
|
||||
items: [
|
||||
{ id: '1', icon: 'tool-layout', label: 'Layout', onClick: (args) => console.debug('Item click!', args) },
|
||||
{ id: '2', icon: 'tool-window-level', label: 'W/L', onClick: (args) => console.debug('Item click!', args) },
|
||||
{ id: '3', icon: 'tool-length', label: 'Length', onClick: (args) => console.debug('Item click!', args) }
|
||||
{
|
||||
id: '1',
|
||||
icon: 'tool-layout',
|
||||
label: 'Layout',
|
||||
onClick: args => console.debug('Item click!', args),
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
icon: 'tool-window-level',
|
||||
label: 'W/L',
|
||||
onClick: args => console.debug('Item click!', args),
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
icon: 'tool-length',
|
||||
label: 'Length',
|
||||
onClick: args => console.debug('Item click!', args),
|
||||
},
|
||||
],
|
||||
onClick: (args) => console.debug('Any click!', args)
|
||||
onClick: args => console.debug('Any click!', args),
|
||||
};
|
||||
return (
|
||||
<div className="mb-2">
|
||||
<div className="flex flex-row min-h-16 w-full justify-center items-center bg-secondary-dark">
|
||||
<div className="flex flex-row w-full justify-center items-center bg-secondary-dark">
|
||||
<SplitButton {...mockedProps} isRadio />
|
||||
<SplitButton {...mockedProps} isAction renderer={WindowLevelMenuItem} items={[
|
||||
{ id: '1', value: 1, title: 'Soft tissue', subtitle: '400 / 40' },
|
||||
{ id: '2', value: 2, title: 'Lung', subtitle: '1500 / -600' },
|
||||
{ id: '3', value: 3, title: 'Liver', subtitle: '150 / 90' },
|
||||
{ id: '4', value: 4, title: 'Bone', subtitle: '80 / 40' },
|
||||
{ id: '5', value: 5, title: 'Brain', subtitle: '2500 / 480' },
|
||||
]} />
|
||||
<SplitButton
|
||||
{...mockedProps}
|
||||
isAction
|
||||
renderer={WindowLevelMenuItem}
|
||||
items={[
|
||||
{ id: '1', value: 1, title: 'Soft tissue', subtitle: '400 / 40' },
|
||||
{ id: '2', value: 2, title: 'Lung', subtitle: '1500 / -600' },
|
||||
{ id: '3', value: 3, title: 'Liver', subtitle: '150 / 90' },
|
||||
{ id: '4', value: 4, title: 'Bone', subtitle: '80 / 40' },
|
||||
{ id: '5', value: 5, title: 'Brain', subtitle: '2500 / 480' },
|
||||
]}
|
||||
/>
|
||||
<SplitButton {...mockedProps} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -61,7 +61,7 @@ const StudyListFilter = ({
|
||||
</div>
|
||||
<div
|
||||
className="sticky z-10 border-b-4 border-black"
|
||||
style={{ top: '57px' }}
|
||||
style={{ top: '52px' }}
|
||||
>
|
||||
<div className="pt-3 pb-3 bg-primary-dark ">
|
||||
<InputGroup
|
||||
|
||||
@ -41,6 +41,7 @@ const ToolbarButton = ({
|
||||
>
|
||||
<IconButton
|
||||
variant={isActive ? 'contained' : 'text'}
|
||||
size="toolbar"
|
||||
className={classnames('mx-1', classes.type[type])}
|
||||
onClick={() => {
|
||||
onInteraction({
|
||||
|
||||
@ -11,7 +11,7 @@ const Viewer = () => {
|
||||
<Header />
|
||||
<div
|
||||
className="flex flex-row flex-no-wrap items-stretch flex-1 w-full overflow-hidden"
|
||||
style={{ height: 'calc(100vh - 57px' }}
|
||||
style={{ height: 'calc(100vh - 52px' }}
|
||||
>
|
||||
<SidePanel
|
||||
side="left"
|
||||
|
||||
@ -57,7 +57,7 @@ import { tabs } from './studyBrowserMockData';
|
||||
<Header />
|
||||
<div
|
||||
className="flex flex-row flex-no-wrap items-stretch overflow-hidden w-full"
|
||||
style={{ height: 'calc(100vh - 57px' }}
|
||||
style={{ height: 'calc(100vh - 52px' }}
|
||||
>
|
||||
{/* LEFT SIDEPANELS */}
|
||||
<SidePanel
|
||||
|
||||
@ -62,21 +62,21 @@ function WorkList({
|
||||
* The default sort value keep the filters synchronized with runtime conditional sorting
|
||||
* Only applied if no other sorting is specified and there are less than 101 studies
|
||||
*/
|
||||
let defaultSortValues = {};
|
||||
|
||||
const sortedStudies = studies
|
||||
// TOOD: Move sort to DataSourceWrapper?
|
||||
// TODO: MOTIVATION, this is triggered on every render, even if list/sort does not change
|
||||
.sort((s1, s2) => {
|
||||
const noSortApplied = sortBy === '' || !sortBy;
|
||||
const sortModifier = sortDirection === 'descending' ? 1 : -1;
|
||||
const canSort = studiesTotal < STUDIES_LIMIT;
|
||||
const shouldUseDefaultSort = sortBy === '' || !sortBy;
|
||||
const sortModifier = sortDirection === 'descending' ? 1 : -1;
|
||||
const defaultSortValues =
|
||||
shouldUseDefaultSort && canSort
|
||||
? { sortBy: 'studyDate', sortDirection: 'ascending' }
|
||||
: {};
|
||||
const sortedStudies = studies;
|
||||
|
||||
if (noSortApplied && studiesTotal < STUDIES_LIMIT) {
|
||||
if (canSort) {
|
||||
studies.sort((s1, s2) => {
|
||||
if (shouldUseDefaultSort) {
|
||||
const ascendingSortModifier = -1;
|
||||
defaultSortValues = { sortBy: 'studyDate', sortDirection: 'ascending' };
|
||||
return _sortStringDates(s1, s2, ascendingSortModifier);
|
||||
} else if (noSortApplied) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const s1Prop = s1[sortBy];
|
||||
@ -96,6 +96,7 @@ function WorkList({
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
// ~ Rows & Studies
|
||||
const [expandedRows, setExpandedRows] = useState([]);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user