Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/components/RoiList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export interface RoiListProps<TData = unknown> {
getReadOnly?: GetReadOnlyCallback<TData>;
renderLabel?: RenderLabelCallback<TData>;
getOverlayOpacity?: GetOverlayOpacity<TData>;
allowRotate?: boolean;
displayRotationHandle?: boolean;
showGrid?: boolean;
/**
* Spacing (in device pixels) between vertical grid lines along the horizontal axis.
Expand Down Expand Up @@ -83,7 +83,7 @@ export function RoiList<TData = unknown>(props: RoiListProps<TData>) {
getReadOnly = () => false,
getOverlayOpacity = () => 0,
renderLabel = defaultRenderLabel,
allowRotate = false,
displayRotationHandle = false,
showGrid = false,
gridHorizontalLineCount = 2,
gridVerticalLineCount = 2,
Expand Down Expand Up @@ -122,7 +122,7 @@ export function RoiList<TData = unknown>(props: RoiListProps<TData>) {
renderLabel={renderLabel as RenderLabelCallback}
getReadOnly={getReadOnly as GetReadOnlyCallback}
getOverlayOpacity={getOverlayOpacity as GetOverlayOpacity}
allowRotate={allowRotate}
displayRotationHandle={displayRotationHandle}
isSelected={roi.id === selectedRoi}
showGrid={showGrid}
gridOptions={gridOptions}
Expand Down
43 changes: 35 additions & 8 deletions src/components/box/BoxSvg.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import { useIsKeyDown } from '../../hooks/useIsKeyDown.js';
import { useLockContext } from '../../hooks/useLockContext.js';
import { usePanZoom } from '../../hooks/usePanZoom.js';
import { useRoiDispatch } from '../../hooks/useRoiDispatch.js';
import type { GetStyleCallback, ReactRoiAction, RoiMode } from '../../index.js';
import type {
GetStyleCallback,
ReactRoiAction,
RoiMode,
RoiState,
} from '../../index.js';
import { useRoiState } from '../../index.js';
import type { Roi } from '../../types/Roi.js';
import type { Box } from '../../utilities/box.js';
Expand All @@ -26,7 +31,7 @@ export interface BoxAnnotationProps {
className?: string;
isReadOnly: boolean;
getStyle: GetStyleCallback;
allowRotate: boolean;
displayRotationHandle: boolean;
showGrid: boolean;
gridOptions: GetGridLinesOptions;
}
Expand All @@ -38,7 +43,7 @@ export function BoxSvg({
isReadOnly,
getStyle,
box,
allowRotate,
displayRotationHandle,
showGrid,
gridOptions,
}: BoxAnnotationProps) {
Expand Down Expand Up @@ -70,7 +75,7 @@ export function BoxSvg({
width: box.width,
height: box.height,
cursor: getCursor(
roiState.mode,
roiState,
isReadOnly,
isAltKeyDown,
roiState.action,
Expand Down Expand Up @@ -103,7 +108,7 @@ export function BoxSvg({
});
} else {
roiDispatch({
type: 'SELECT_BOX_AND_START_MOVE',
type: 'SELECT_BOX_AND_START_ACTION',
payload: {
id: roi.id,
},
Expand Down Expand Up @@ -145,6 +150,7 @@ export function BoxSvg({
sizes={handlerSizes}
handlerColor={styles.resizeHandlerColor}
edge={edge}
disabled={isResizableMode(roiState.mode)}
gridLineOpacity={styles.gridLineOpacity}
/>
))}
Expand All @@ -153,6 +159,7 @@ export function BoxSvg({
getAllCorners(box, roi.box.angle).map((corner) => (
<RoiBoxCorner
key={`corner-${corner.xPosition}-${corner.yPosition}`}
disabled={!isResizableMode(roiState.mode)}
corner={corner}
roiId={roi.id}
sizes={handlerSizes}
Expand All @@ -161,7 +168,7 @@ export function BoxSvg({
))}

{isSelected &&
allowRotate &&
displayRotationHandle &&
(roi.action.type === 'rotating' || roi.action.type === 'idle') && (
<RoiBoxRotateHandler box={box} styles={styles} />
)}
Expand All @@ -170,17 +177,27 @@ export function BoxSvg({
}

function getCursor(
mode: RoiMode,
state: RoiState,
readOnly: boolean,
isAltKeyDown: boolean,
action: ReactRoiAction,
lockPan: boolean,
): CSSProperties['cursor'] {
const { mode, selectedRoi } = state;
if (mode === 'rotate_selected') {
if (selectedRoi) {
return 'ew-resize';
} else {
return 'default';
}
}
if (action !== 'idle') {
if (action === 'drawing') {
return 'crosshair';
} else if (action === 'moving') {
return 'move';
} else if (action === 'rotating') {
return 'ew-resize';
} else if (action === 'panning') {
return 'grab';
}
Expand All @@ -200,5 +217,15 @@ function getCursor(
}
}

return mode === 'draw' ? 'crosshair' : 'move';
// eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
switch (mode) {
case 'draw':
return 'crosshair';
default:
return 'move';
}
}

function isResizableMode(mode: RoiMode) {
return mode === 'select' || mode === 'hybrid';
}
6 changes: 3 additions & 3 deletions src/components/box/RoiBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ interface RoiBoxProps {
getStyle: GetStyleCallback;
getReadOnly: GetReadOnlyCallback;
renderLabel: RenderLabelCallback;
allowRotate: boolean;
displayRotationHandle: boolean;
getOverlayOpacity: GetOverlayOpacity;
showGrid: boolean;
gridOptions: GetGridLinesOptions;
Expand All @@ -37,7 +37,7 @@ function RoiBoxInternal(props: RoiBoxProps): JSX.Element {
isSelected,
renderLabel,
getOverlayOpacity,
allowRotate,
displayRotationHandle,
showGrid,
gridOptions,
} = props;
Expand Down Expand Up @@ -88,7 +88,7 @@ function RoiBoxInternal(props: RoiBoxProps): JSX.Element {
box={box}
isReadOnly={isReadOnly}
getStyle={getStyle}
allowRotate={allowRotate}
displayRotationHandle={displayRotationHandle}
showGrid={showGrid}
gridOptions={gridOptions}
/>
Expand Down
35 changes: 25 additions & 10 deletions src/components/box/RoiBoxCorner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,31 @@ import { getCursor } from './utils.js';

type PointerDownCallback = PointerEventHandler<SVGRectElement>;

interface RoiBoxCornerProps {
corner: CornerData;
roiId: string;
sizes: HandlerSizeOptions;
handlerColor?: CSSProperties['color'];
disabled: boolean;
}

export function RoiBoxCorner({
corner,
roiId,
sizes,
handlerColor,
}: {
corner: CornerData;
roiId: string;
sizes: HandlerSizeOptions;
handlerColor?: CSSProperties['color'];
}) {
disabled,
}: RoiBoxCornerProps) {
const roiDispatch = useRoiDispatch();
const roiState = useRoiState();
const onPointerDown: PointerDownCallback = useCallback(
(event) => {
if (event.altKey || event.button !== 0 || roiState.mode === 'draw') {
if (
event.altKey ||
event.button !== 0 ||
roiState.mode === 'draw' ||
disabled
) {
return;
}
event.stopPropagation();
Expand All @@ -39,7 +48,7 @@ export function RoiBoxCorner({
},
});
},
[roiDispatch, corner, roiId, roiState],
[roiDispatch, corner, roiId, roiState, disabled],
);
if (corner.xPosition === 'center' || corner.yPosition === 'center') {
return (
Expand All @@ -48,6 +57,7 @@ export function RoiBoxCorner({
onPointerDown={onPointerDown}
scaledSizes={sizes}
handlerColor={handlerColor}
disabled={disabled}
/>
);
}
Expand All @@ -56,6 +66,7 @@ export function RoiBoxCorner({
corner={corner}
onPointerDown={onPointerDown}
scaledSizes={sizes}
disabled={disabled}
handlerColor={handlerColor}
/>
);
Expand All @@ -65,11 +76,13 @@ function SideHandler({
corner,
onPointerDown,
scaledSizes,
disabled,
handlerColor = defaultHandlerColor,
}: {
corner: CornerData;
onPointerDown: PointerDownCallback;
scaledSizes: HandlerSizeOptions;
disabled: boolean;
handlerColor: CSSProperties['color'] | undefined;
}) {
const roiState = useRoiState();
Expand All @@ -87,7 +100,7 @@ function SideHandler({
/>
<rect
{...handlerRect}
cursor={getCursor(roiState, corner.cursor)}
cursor={getCursor(roiState, corner.cursor, disabled)}
fill="transparent"
style={{ pointerEvents: 'initial' }}
onPointerDown={onPointerDown}
Expand All @@ -100,12 +113,14 @@ function CornerHandler({
corner,
onPointerDown,
scaledSizes,
disabled,
handlerColor = defaultHandlerColor,
}: {
corner: CornerData;
onPointerDown: PointerDownCallback;
scaledSizes: HandlerSizeOptions;
handlerColor: CSSProperties['color'] | undefined;
disabled: boolean;
}) {
const roiState = useRoiState();

Expand All @@ -123,7 +138,7 @@ function CornerHandler({
/>
<rect
{...handlerRect}
cursor={getCursor(roiState, corner.cursor)}
cursor={getCursor(roiState, corner.cursor, disabled)}
fill="transparent"
style={{ pointerEvents: 'initial' }}
onPointerDown={onPointerDown}
Expand Down
13 changes: 10 additions & 3 deletions src/components/box/RoiBoxEdge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ interface RoiBoxEdgeProps {
edge: EdgeData;
roiId: string;
sizes: HandlerSizeOptions;
disabled: boolean;
handlerColor?: CSSProperties['color'];
gridLineOpacity?: CSSProperties['opacity'];
}
Expand All @@ -25,6 +26,7 @@ export function RoiBoxEdge(props: RoiBoxEdgeProps) {
sizes,
handlerColor,
roiId,
disabled,
gridLineOpacity = defaultGridLineOpacity,
} = props;
const roiDispatch = useRoiDispatch();
Expand All @@ -35,7 +37,12 @@ export function RoiBoxEdge(props: RoiBoxEdgeProps) {

const onPointerDown: PointerDownCallback = useCallback(
(event) => {
if (event.altKey || event.button !== 0 || roiState.mode === 'draw') {
if (
event.altKey ||
event.button !== 0 ||
roiState.mode === 'draw' ||
disabled
) {
return;
}
event.stopPropagation();
Expand All @@ -54,7 +61,7 @@ export function RoiBoxEdge(props: RoiBoxEdgeProps) {
},
});
},
[roiDispatch, edge, roiId, roiState],
[roiDispatch, edge, roiId, roiState, disabled],
);
return (
<g transform={transform}>
Expand All @@ -68,7 +75,7 @@ export function RoiBoxEdge(props: RoiBoxEdgeProps) {
<rect
{...handlerRect}
onPointerDown={onPointerDown}
cursor={getCursor(roiState, edge.cursor)}
cursor={getCursor(roiState, edge.cursor, disabled)}
fill="transparent"
style={{ pointerEvents: 'initial' }}
/>
Expand Down
4 changes: 4 additions & 0 deletions src/components/box/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import type { RoiState } from '../../types/state.js';
export function getCursor(
roiState: RoiState,
cursor: CSSProperties['cursor'],
disabled: boolean,
): CSSProperties['cursor'] {
if (disabled) {
return undefined;
}
if (roiState.mode === 'draw') {
return 'crosshair';
}
Expand Down
Loading
Loading