Skip to content

Commit 2049776

Browse files
authored
fix: improve RoiList callback arguments (#174)
* fix: improve RoiList callback arguments - Do not expose implementation details. - Improve argument separation logic: first for ROI related data, second for global data - Export types related to those callbacks - unrelated fix: remove references to wrong "select_rotate" mode BREAKING-CHANGE: getStyle, getReadOnly, renderLabel, getOverlayOpacity callback props on `RoiList` have changed their contract. Closes: #147
1 parent b4a7c3a commit 2049776

File tree

17 files changed

+120
-87
lines changed

17 files changed

+120
-87
lines changed

src/components/RoiList.tsx

Lines changed: 9 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,19 @@
1-
import type { CSSProperties, JSX, ReactNode, SVGAttributes } from 'react';
21
import { useMemo } from 'react';
32

43
import { useRois } from '../hooks/useRois.js';
54
import { useRoiState } from '../index.js';
6-
import type { Roi } from '../types/Roi.js';
5+
import type {
6+
GetOverlayOpacity,
7+
GetReadOnlyCallback,
8+
GetStyleCallback,
9+
RenderLabelCallback,
10+
} from '../types/RoiList.ts';
711
import { assert } from '../utilities/assert.js';
812

913
import { RoiBox } from './box/RoiBox.js';
1014
import { defaultGridLineOpacity, defaultHandlerColor } from './constants.js';
1115
import { LabelContainer } from './label/LabelContainer.js';
1216

13-
export interface RoiAdditionalCallbackState {
14-
isSelected: boolean;
15-
isReadOnly: boolean;
16-
zoomScale: number;
17-
}
18-
19-
export interface CustomRoiStyle {
20-
/**
21-
* The attributes to forward to the SVG rect element which draws the ROI
22-
*/
23-
rectAttributes?: SVGAttributes<SVGRectElement>;
24-
resizeHandlerColor?: CSSProperties['color'];
25-
gridLineOpacity?: CSSProperties['opacity'];
26-
/**
27-
* This property allows to render custom markup inside the ROI SVG
28-
* This can be used for example to render an svg pattern that can then be used as a fill in `rectAttributes`
29-
* It is not meant to be used to render markup that will be displayed on screen, see `rectAttributes` to customize the ROI appearance
30-
*/
31-
renderCustomPattern?: () => JSX.Element;
32-
}
33-
34-
export type GetStyleCallback<TData = unknown> = (
35-
roi: Roi<TData>,
36-
roiAdditionalState: RoiAdditionalCallbackState,
37-
) => CustomRoiStyle;
38-
39-
export type GetReadOnlyCallback<TData = unknown> = (roi: Roi<TData>) => boolean;
40-
export type GetOverlayOpacity<TData = unknown> = (
41-
roi: Roi<TData>,
42-
roiAdditionalState: RoiAdditionalCallbackState,
43-
) => number;
44-
export type RenderLabelCallback<TData = unknown> = (
45-
roi: Roi<TData>,
46-
roiAdditionalState: RoiAdditionalCallbackState,
47-
) => ReactNode;
48-
4917
export interface RoiListProps<TData = unknown> {
5018
getStyle?: GetStyleCallback<TData>;
5119
getReadOnly?: GetReadOnlyCallback<TData>;
@@ -132,12 +100,12 @@ export function RoiList<TData = unknown>(props: RoiListProps<TData>) {
132100
);
133101
}
134102

135-
const defaultGetStyle: GetStyleCallback = (roi, state) => {
103+
const defaultGetStyle: GetStyleCallback = ({ isReadOnly, isSelected }) => {
136104
return {
137105
rectAttributes: {
138-
fill: state.isReadOnly
106+
fill: isReadOnly
139107
? 'rgba(0,0,0,0.6)'
140-
: state.isSelected
108+
: isSelected
141109
? 'rgba(0,0,0,0.2)'
142110
: 'rgba(0,0,0,0.4)',
143111
},

src/components/box/BoxSvg.tsx

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,20 @@ export function BoxSvg({
5555
const isSelected = roi.id === roiState.selectedRoi;
5656
const styles = {
5757
...baseRoiStyle,
58-
...getStyle(roi, {
59-
isReadOnly,
60-
isSelected,
61-
zoomScale: panZoom.panZoom.scale * panZoom.initialPanZoom.scale,
62-
}),
58+
...getStyle(
59+
{
60+
isReadOnly,
61+
isSelected,
62+
id: roi.id,
63+
box: roi.box,
64+
label: roi.label,
65+
data: roi.data,
66+
action: roi.action.type,
67+
},
68+
{
69+
zoomScale: panZoom.panZoom.scale * panZoom.initialPanZoom.scale,
70+
},
71+
),
6372
};
6473

6574
const handlerSizes = getHandlerSizes(roi, panZoom);

src/components/box/RoiBox.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type {
88
GetReadOnlyCallback,
99
GetStyleCallback,
1010
RenderLabelCallback,
11+
RoiCallbackPayload,
1112
} from '../../index.js';
1213
import type { Roi } from '../../types/Roi.js';
1314
import { applyTransformToBox } from '../../utilities/box.js';
@@ -49,13 +50,21 @@ function RoiBoxInternal(props: RoiBoxProps): JSX.Element {
4950
const roiDispatch = useRoiDispatch();
5051
const isReadOnly = getReadOnly(roi) || false;
5152

52-
const roiAdditionalState = {
53+
const roiPayload: RoiCallbackPayload = {
54+
id,
5355
isReadOnly,
5456
isSelected,
57+
box: roi.box,
58+
label: roi.label,
59+
action: roi.action.type,
60+
data: roi.data,
61+
};
62+
63+
const globalStatePayload = {
5564
zoomScale: panzoom.panZoom.scale * panzoom.initialPanZoom.scale,
5665
};
5766

58-
const shadowOpacity = getOverlayOpacity(roi, roiAdditionalState);
67+
const shadowOpacity = getOverlayOpacity(roiPayload, globalStatePayload);
5968

6069
useEffect(() => {
6170
if (isReadOnly) {
@@ -64,7 +73,7 @@ function RoiBoxInternal(props: RoiBoxProps): JSX.Element {
6473
}, [id, isReadOnly, roiDispatch]);
6574

6675
const box = applyTransformToBox(totalPanzoom, roi.box);
67-
const label = renderLabel(roi, roiAdditionalState);
76+
const label = renderLabel(roiPayload, globalStatePayload);
6877
return (
6978
<>
7079
<div

src/components/box/RoiBoxRotateHandler.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1+
import type { CustomRoiStyle } from '../../types/RoiList.ts';
12
import type { Box } from '../../utilities/box.js';
2-
import type { CustomRoiStyle } from '../RoiList.js';
33

44
import { baseHandlerSizes } from './sizes.js';
55

src/components/box/styles.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { CustomRoiStyle } from '../RoiList.js';
1+
import type { CustomRoiStyle } from '../../types/RoiList.js';
22
import { defaultHandlerColor } from '../constants.js';
33

44
export const baseRoiStyle: CustomRoiStyle = {

src/context/RoiProvider.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export interface RoiProviderInitialConfig<TData> {
7979
rois?: Array<CommittedRoiProperties<TData>>;
8080
selectedRoiId?: string;
8181
/**
82-
* When in select_rotate mode, this defines how much the mouse movement transnlates to a rotation angle.
82+
* When in rotate_selected mode, this defines how much the mouse movement transnlates to a rotation angle.
8383
* It is expressed in number of pixels for a full 360 degree rotation.
8484
* @default 1200
8585
*/

src/context/roiReducer.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export interface ReactRoiState<TData = unknown> {
6767
commitRoiBoundaryStrategy: BoundaryStrategy;
6868

6969
/**
70-
* When in select_rotate mode, this defines how much the mouse movement transnlates to a rotation angle.
70+
* When in rotate_selected mode, this defines how much the mouse movement transnlates to a rotation angle.
7171
* It is expressed in number of pixels for a full 360 degree rotation.
7272
* @default 1200
7373
*/

src/context/updaters/selectBoxAndStartAction.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type {
44
MoveAction,
55
RotateAction,
66
RotateFreeAction,
7-
} from '../../types/Roi.ts';
7+
} from '../../types/Roi.js';
88
import { assert } from '../../utilities/assert.js';
99
import { changeBoxRotationCenter } from '../../utilities/box.js';
1010
import type { ReactRoiState } from '../roiReducer.js';

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export * from './types/state.js';
99
export * from './types/utils.js';
1010
export * from './types/box.js';
1111
export * from './types/CommittedRoi.js';
12+
export * from './types/RoiList.js';
1213

1314
export * from './hooks/useActions.js';
1415
export * from './hooks/useCommittedRois.js';

src/types/RoiList.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import type { CSSProperties, JSX, ReactNode, SVGAttributes } from 'react';
2+
3+
import type { Roi, RoiAction } from './Roi.ts';
4+
5+
export interface GlobalStateCallbackPayload {
6+
/**
7+
* The current zoom scale.
8+
*/
9+
zoomScale: number;
10+
}
11+
12+
export type RoiCallbackPayload<TData = unknown> = Pick<
13+
Roi<TData>,
14+
'id' | 'box' | 'label' | 'data'
15+
> & {
16+
action: RoiAction['type'];
17+
isSelected: boolean;
18+
isReadOnly: boolean;
19+
};
20+
21+
export interface CustomRoiStyle {
22+
/**
23+
* The attributes to forward to the SVG rect element which draws the ROI
24+
*/
25+
rectAttributes?: SVGAttributes<SVGRectElement>;
26+
resizeHandlerColor?: CSSProperties['color'];
27+
gridLineOpacity?: CSSProperties['opacity'];
28+
/**
29+
* This property allows to render custom markup inside the ROI SVG
30+
* This can be used for example to render an svg pattern that can then be used as a fill in `rectAttributes`
31+
* It is not meant to be used to render markup that will be displayed on screen, see `rectAttributes` to customize the ROI appearance
32+
*/
33+
renderCustomPattern?: () => JSX.Element;
34+
}
35+
36+
export type GetStyleCallback<TData = unknown> = (
37+
roi: RoiCallbackPayload<TData>,
38+
globalState: GlobalStateCallbackPayload,
39+
) => CustomRoiStyle;
40+
41+
export type GetReadOnlyCallback<TData = unknown> = (roi: Roi<TData>) => boolean;
42+
export type GetOverlayOpacity<TData = unknown> = (
43+
roi: RoiCallbackPayload<TData>,
44+
globalState: GlobalStateCallbackPayload,
45+
) => number;
46+
47+
export type RenderLabelCallback<TData = unknown> = (
48+
roi: RoiCallbackPayload<TData>,
49+
globalState: GlobalStateCallbackPayload,
50+
) => ReactNode;

0 commit comments

Comments
 (0)