cloudmr-ux 4.8.2 → 4.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -25,8 +25,8 @@ export interface CloudMrNiivuePanelProps {
25
25
  locationData: any[];
26
26
  decimalPrecision: number;
27
27
  drawToolkitProps: CloudMrDrawToolkitProps;
28
- drawShapeTool: "pen" | "rectangle" | "ellipse" | null;
29
- setDrawShapeTool: (t: "pen" | "rectangle" | "ellipse" | null) => void;
28
+ drawShapeTool: "pen" | "polyline" | "rectangle" | "ellipse" | null;
29
+ setDrawShapeTool: (t: "pen" | "polyline" | "rectangle" | "ellipse" | null) => void;
30
30
  resampleImage: () => void;
31
31
  layerList: React.ComponentProps<any>[];
32
32
  mins: number[];
@@ -109,24 +109,24 @@ export function CloudMrNiivuePanel(props) {
109
109
  props.resampleImage();
110
110
  }, [histogram]);
111
111
  function applyDrawShapeTool(tool) {
112
- var _a, _b, _c, _d, _e, _f;
112
+ var _a, _b, _c;
113
113
  if (props.shapeDraft) {
114
- // Apply (commit) the current draft rather than discarding it
115
114
  (_a = props.onApplyShapeDraftKeepTool) === null || _a === void 0 ? void 0 : _a.call(props);
116
115
  }
117
116
  if (props.penDraft) {
118
117
  (_b = props.onApplyPenDraftKeepTool) === null || _b === void 0 ? void 0 : _b.call(props);
119
118
  }
120
- if (tool !== "pen") {
121
- (_d = (_c = props.drawToolkitProps).onPenDrawModeChange) === null || _d === void 0 ? void 0 : _d.call(_c, "freehand");
122
- }
123
119
  props.setDrawShapeTool(tool);
124
120
  var nv = props.nv;
125
- var penMode = (_e = props.drawToolkitProps.penDrawMode) !== null && _e !== void 0 ? _e : "freehand";
126
121
  nv.opts.deferShapeCommit = tool === "rectangle" || tool === "ellipse";
127
122
  if (tool === "pen") {
128
- nv.opts.polylinePenMode = penMode === "polyline";
129
- nv.opts.isFilledPen = penMode === "freehand";
123
+ nv.opts.polylinePenMode = false;
124
+ nv.opts.isFilledPen = true;
125
+ nv.opts.deferFreehandCommit = false;
126
+ }
127
+ else if (tool === "polyline") {
128
+ nv.opts.polylinePenMode = true;
129
+ nv.opts.isFilledPen = false;
130
130
  nv.opts.deferFreehandCommit = false;
131
131
  }
132
132
  else {
@@ -143,13 +143,14 @@ export function CloudMrNiivuePanel(props) {
143
143
  nv.opts.penBounds = 0;
144
144
  nv.opts.penSize = 1;
145
145
  }
146
- else if (tool === "pen") {
147
- var penBrush = (_f = props.drawToolkitProps.brushSize) !== null && _f !== void 0 ? _f : 1;
146
+ else if (tool === "pen" || tool === "polyline") {
147
+ var penBrush = (_c = props.drawToolkitProps.brushSize) !== null && _c !== void 0 ? _c : 1;
148
148
  nv.opts.penBounds = (penBrush - 1) / 2;
149
149
  nv.opts.penSize = penBrush;
150
150
  }
151
151
  nv.drawScene();
152
- if ((tool === "rectangle" || tool === "ellipse") && !props.drawToolkitProps.drawingEnabled) {
152
+ if ((tool === "rectangle" || tool === "ellipse" || tool === "pen" || tool === "polyline") &&
153
+ !props.drawToolkitProps.drawingEnabled) {
153
154
  props.drawToolkitProps.setDrawingEnabled(true);
154
155
  }
155
156
  }
@@ -577,7 +577,8 @@ export default function CloudMrNiivueViewer(props) {
577
577
  function nvUpdateBrushSize(size) {
578
578
  setBrushSize(size);
579
579
  brushSizeRef.current = size;
580
- if (drawShapeToolRef.current === "pen" && drawPen !== 0 && drawPen !== 8) {
580
+ var tool = drawShapeToolRef.current;
581
+ if ((tool === "pen" || tool === "polyline") && drawPen !== 0 && drawPen !== 8) {
581
582
  applyNvBrushSize(size);
582
583
  }
583
584
  }
@@ -609,8 +610,14 @@ export default function CloudMrNiivueViewer(props) {
609
610
  }
610
611
  else if (drawShapeToolRef.current === "pen") {
611
612
  nv.opts.deferFreehandCommit = false;
612
- nv.opts.polylinePenMode = penDrawModeRef.current === "polyline";
613
- nv.opts.isFilledPen = penDrawModeRef.current === "freehand";
613
+ nv.opts.polylinePenMode = false;
614
+ nv.opts.isFilledPen = true;
615
+ applyNvBrushSize(brushSizeRef.current);
616
+ }
617
+ else if (drawShapeToolRef.current === "polyline") {
618
+ nv.opts.deferFreehandCommit = false;
619
+ nv.opts.polylinePenMode = true;
620
+ nv.opts.isFilledPen = false;
614
621
  applyNvBrushSize(brushSizeRef.current);
615
622
  }
616
623
  }
@@ -882,7 +889,8 @@ export default function CloudMrNiivueViewer(props) {
882
889
  setPenDraft(null);
883
890
  penDraftRef.current = null;
884
891
  nv._cloudMrPenDraftActive = false;
885
- if (drawShapeToolRef.current === "pen") {
892
+ var tool = drawShapeToolRef.current;
893
+ if (tool === "pen" || tool === "polyline") {
886
894
  nvSetDrawingEnabled(true);
887
895
  }
888
896
  }
@@ -915,9 +923,9 @@ export default function CloudMrNiivueViewer(props) {
915
923
  nv._cloudMrPenDraftActive = false;
916
924
  setDrawingChanged(true);
917
925
  if (keepTool) {
918
- var mode = penDrawModeRef.current;
919
- nv.opts.polylinePenMode = mode === "polyline";
920
- nv.opts.isFilledPen = mode === "freehand";
926
+ var tool = drawShapeToolRef.current;
927
+ nv.opts.polylinePenMode = tool === "polyline";
928
+ nv.opts.isFilledPen = tool === "pen";
921
929
  nv.opts.deferFreehandCommit = false;
922
930
  nvSetDrawingEnabled(true);
923
931
  }
@@ -974,11 +982,10 @@ export default function CloudMrNiivueViewer(props) {
974
982
  setPenDraft(draft);
975
983
  penDraftRef.current = draft;
976
984
  nv._cloudMrPenDraftActive = true;
977
- // Auto-select pen tool so the palette opens
978
- setDrawShapeTool("pen");
979
- var mode = draft.kind === "polyline" ? "polyline" : "freehand";
980
- setPenDrawMode(mode);
981
- penDrawModeRef.current = mode;
985
+ // Auto-select the correct pen tool so the right palette opens
986
+ var tool = draft.kind === "polyline" ? "polyline" : "pen";
987
+ setDrawShapeTool(tool);
988
+ penDrawModeRef.current = draft.kind === "polyline" ? "polyline" : "freehand";
982
989
  if (draft.kind === "polyline") {
983
990
  setPolylineVertexCount((_b = (_a = draft.vertices) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0);
984
991
  }
@@ -989,7 +996,7 @@ export default function CloudMrNiivueViewer(props) {
989
996
  nv.onPolylineChange = function (count) {
990
997
  var _a, _b;
991
998
  setPolylineVertexCount(count);
992
- if (penDrawModeRef.current === "polyline" && count >= 2) {
999
+ if (drawShapeToolRef.current === "polyline" && count >= 2) {
993
1000
  var prev = penDraftRef.current;
994
1001
  var preserveFill = (prev === null || prev === void 0 ? void 0 : prev.kind) === "polyline" &&
995
1002
  prev.filled &&
@@ -1,10 +1,8 @@
1
- export default function DrawColorPlatte({ expanded, updateDrawPen, setDrawingEnabled, showPenModes, penDrawMode, onPenDrawModeChange, polylineVertexCount, penDraftActive, penDraftKind, penDraftFilled, onApplyPenDraft, onDeletePenDraft, onFillPenDraft, brushSize, updateBrushSize, shapeDraftActive, onApplyShapeDraft, onDeleteShapeDraft, }: {
1
+ export default function DrawColorPlatte({ expanded, updateDrawPen, setDrawingEnabled, penToolKind, polylineVertexCount, penDraftActive, penDraftKind, penDraftFilled, onApplyPenDraft, onDeletePenDraft, onFillPenDraft, brushSize, updateBrushSize, shapeDraftActive, onApplyShapeDraft, onDeleteShapeDraft, }: {
2
2
  expanded: any;
3
3
  updateDrawPen: any;
4
4
  setDrawingEnabled: any;
5
- showPenModes?: boolean | undefined;
6
- penDrawMode?: string | undefined;
7
- onPenDrawModeChange: any;
5
+ penToolKind?: null | undefined;
8
6
  polylineVertexCount?: number | undefined;
9
7
  penDraftActive?: boolean | undefined;
10
8
  penDraftKind: any;
@@ -11,7 +11,8 @@ var __assign = (this && this.__assign) || function () {
11
11
  };
12
12
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
13
  /**
14
- * Pen palette adds freehand vs polyline mode; pen/shape drafts show Apply while adjusting.
14
+ * Color palette for pen/shape ROI tools. Freehand and polyline each use their own
15
+ * toolbar button and pass `penToolKind` to show the matching options.
15
16
  */
16
17
  import { Stack, IconButton, Button, Tooltip, Typography } from "@mui/material";
17
18
  import FiberManualRecordIcon from "@mui/icons-material/FiberManualRecord";
@@ -28,16 +29,14 @@ var FILLED_COLORS = [
28
29
  ];
29
30
  var ACTION_FONT_SIZE = "0.75rem";
30
31
  var ACTION_ICON_SIZE = "0.875rem";
31
- var modeBtnSx = function (active) { return ({
32
- color: active ? "#c9a0e8" : "#bbb",
33
- fontSize: ACTION_FONT_SIZE,
34
- textTransform: "none",
35
- minWidth: 0,
36
- py: 0.25,
37
- px: 0.75
38
- }); };
39
32
  export default function DrawColorPlatte(_a) {
40
- var expanded = _a.expanded, updateDrawPen = _a.updateDrawPen, setDrawingEnabled = _a.setDrawingEnabled, _b = _a.showPenModes, showPenModes = _b === void 0 ? false : _b, _c = _a.penDrawMode, penDrawMode = _c === void 0 ? "freehand" : _c, onPenDrawModeChange = _a.onPenDrawModeChange, _d = _a.polylineVertexCount, polylineVertexCount = _d === void 0 ? 0 : _d, _e = _a.penDraftActive, penDraftActive = _e === void 0 ? false : _e, penDraftKind = _a.penDraftKind, _f = _a.penDraftFilled, penDraftFilled = _f === void 0 ? false : _f, onApplyPenDraft = _a.onApplyPenDraft, onDeletePenDraft = _a.onDeletePenDraft, onFillPenDraft = _a.onFillPenDraft, _g = _a.brushSize, brushSize = _g === void 0 ? 1 : _g, updateBrushSize = _a.updateBrushSize, _h = _a.shapeDraftActive, shapeDraftActive = _h === void 0 ? false : _h, onApplyShapeDraft = _a.onApplyShapeDraft, onDeleteShapeDraft = _a.onDeleteShapeDraft;
33
+ var expanded = _a.expanded, updateDrawPen = _a.updateDrawPen, setDrawingEnabled = _a.setDrawingEnabled,
34
+ /** @type {"freehand" | "polyline" | null} */
35
+ _b = _a.penToolKind,
36
+ /** @type {"freehand" | "polyline" | null} */
37
+ penToolKind = _b === void 0 ? null : _b, _c = _a.polylineVertexCount, polylineVertexCount = _c === void 0 ? 0 : _c, _d = _a.penDraftActive, penDraftActive = _d === void 0 ? false : _d, penDraftKind = _a.penDraftKind, _e = _a.penDraftFilled, penDraftFilled = _e === void 0 ? false : _e, onApplyPenDraft = _a.onApplyPenDraft, onDeletePenDraft = _a.onDeletePenDraft, onFillPenDraft = _a.onFillPenDraft, _f = _a.brushSize, brushSize = _f === void 0 ? 1 : _f, updateBrushSize = _a.updateBrushSize, _g = _a.shapeDraftActive, shapeDraftActive = _g === void 0 ? false : _g, onApplyShapeDraft = _a.onApplyShapeDraft, onDeleteShapeDraft = _a.onDeleteShapeDraft;
38
+ var isFreehandTool = penToolKind === "freehand";
39
+ var isPolylineTool = penToolKind === "polyline";
41
40
  return (_jsxs(Stack, __assign({ style: {
42
41
  position: "absolute",
43
42
  top: "100%",
@@ -51,13 +50,13 @@ export default function DrawColorPlatte(_a) {
51
50
  borderTopLeftRadius: "6pt",
52
51
  borderTopRightRadius: "6pt",
53
52
  background: "#333"
54
- }, direction: "column", spacing: 0.5, sx: { py: expanded ? 0.5 : 0 } }, { children: [showPenModes && expanded && (_jsxs(Stack, __assign({ direction: "row", alignItems: "center", spacing: 0.5, sx: { px: 0.75, pt: 0.25 } }, { children: [_jsx(Button, __assign({ size: "small", onClick: function () { return onPenDrawModeChange === null || onPenDrawModeChange === void 0 ? void 0 : onPenDrawModeChange("freehand"); }, sx: modeBtnSx(penDrawMode === "freehand") }, { children: "Freehand" })), _jsx(Button, __assign({ size: "small", onClick: function () { return onPenDrawModeChange === null || onPenDrawModeChange === void 0 ? void 0 : onPenDrawModeChange("polyline"); }, sx: modeBtnSx(penDrawMode === "polyline") }, { children: "Polyline" }))] }))), showPenModes && expanded && updateBrushSize && (_jsx(BrushSizeSlider, { label: "Line thickness", brushSize: brushSize, updateBrushSize: updateBrushSize })), _jsx(Stack, __assign({ direction: "row" }, { children: FILLED_COLORS.map(function (color, index) { return (_jsx(IconButton, __assign({ onClick: function () {
53
+ }, direction: "column", spacing: 0.5, sx: { py: expanded ? 0.5 : 0 } }, { children: [(isFreehandTool || isPolylineTool) && expanded && updateBrushSize && (_jsx(BrushSizeSlider, { label: "Line thickness", brushSize: brushSize, updateBrushSize: updateBrushSize })), _jsx(Stack, __assign({ direction: "row" }, { children: FILLED_COLORS.map(function (color, index) { return (_jsx(IconButton, __assign({ onClick: function () {
55
54
  updateDrawPen({ target: { value: index + 1 } });
56
55
  setDrawingEnabled(true);
57
- } }, { children: _jsx(FiberManualRecordIcon, { sx: color.sx }) }), index)); }) })), showPenModes && penDrawMode === "polyline" && expanded && polylineVertexCount === 0 && (_jsx(Typography, __assign({ sx: { px: 1, pb: 0.5, fontSize: "0.68rem", color: "#aaa", userSelect: "none" } }, { children: "Click each vertex to draw connected line segments" }))), showPenModes &&
58
- penDraftActive &&
59
- (penDrawMode === "polyline" || penDraftKind === "freehand") &&
60
- expanded && (_jsxs(Stack, __assign({ direction: "row", alignItems: "center", justifyContent: "space-between", sx: { px: 1, py: 0.5, borderTop: "1px solid #555", width: "100%" } }, { children: [_jsx(Tooltip, __assign({ title: "Delete this ROI drawing" }, { children: _jsx(Button, __assign({ size: "small", "aria-label": "delete pen draft", onClick: function () { return onDeletePenDraft === null || onDeletePenDraft === void 0 ? void 0 : onDeletePenDraft(); }, startIcon: _jsx(DeleteOutlineIcon, { sx: { fontSize: ACTION_ICON_SIZE } }), sx: {
56
+ } }, { children: _jsx(FiberManualRecordIcon, { sx: color.sx }) }), index)); }) })), isPolylineTool && expanded && polylineVertexCount === 0 && (_jsx(Typography, __assign({ sx: { px: 1, pb: 0.5, fontSize: "0.68rem", color: "#aaa", userSelect: "none" } }, { children: "Click each vertex to draw connected line segments" }))), penDraftActive &&
57
+ expanded &&
58
+ ((isFreehandTool && penDraftKind === "freehand") ||
59
+ (isPolylineTool && penDraftKind === "polyline")) && (_jsxs(Stack, __assign({ direction: "row", alignItems: "center", justifyContent: "space-between", sx: { px: 1, py: 0.5, borderTop: "1px solid #555", width: "100%" } }, { children: [_jsx(Tooltip, __assign({ title: "Delete this ROI drawing" }, { children: _jsx(Button, __assign({ size: "small", "aria-label": "delete pen draft", onClick: function () { return onDeletePenDraft === null || onDeletePenDraft === void 0 ? void 0 : onDeletePenDraft(); }, startIcon: _jsx(DeleteOutlineIcon, { sx: { fontSize: ACTION_ICON_SIZE } }), sx: {
61
60
  color: "#f44336",
62
61
  fontSize: ACTION_FONT_SIZE,
63
62
  textTransform: "none",
@@ -65,7 +64,7 @@ export default function DrawColorPlatte(_a) {
65
64
  py: 0.25,
66
65
  px: 0.75,
67
66
  "& .MuiButton-startIcon": { mr: 0.5, ml: 0 }
68
- } }, { children: "Delete" })) })), _jsxs(Stack, __assign({ direction: "row", spacing: 1, alignItems: "center" }, { children: [penDrawMode === "polyline" && polylineVertexCount >= 3 && (_jsx(Tooltip, __assign({ title: penDraftFilled
67
+ } }, { children: "Delete" })) })), isPolylineTool && (_jsxs(Stack, __assign({ direction: "row", spacing: 1, alignItems: "center" }, { children: [polylineVertexCount >= 3 && (_jsx(Tooltip, __assign({ title: penDraftFilled
69
68
  ? "Remove fill (keeps outline editable)"
70
69
  : "Fill interior (keeps outline editable until Apply)" }, { children: _jsx(Button, __assign({ size: "small", "aria-label": penDraftFilled ? "undo fill polyline" : "fill polyline", onClick: function () { return onFillPenDraft === null || onFillPenDraft === void 0 ? void 0 : onFillPenDraft(); }, sx: {
71
70
  color: penDraftFilled ? "#ffb74d" : "#c9a0e8",
@@ -74,7 +73,7 @@ export default function DrawColorPlatte(_a) {
74
73
  minWidth: 0,
75
74
  py: 0.25,
76
75
  px: 0.75
77
- } }, { children: penDraftFilled ? "Undo Fill" : "Fill" })) }))), penDrawMode === "polyline" && (_jsx(Tooltip, __assign({ title: "Apply polyline (Enter or right-click)" }, { children: _jsx(Button, __assign({ size: "small", "aria-label": "apply pen draft", onClick: function () { return onApplyPenDraft === null || onApplyPenDraft === void 0 ? void 0 : onApplyPenDraft(); }, startIcon: _jsx(CheckIcon, { sx: { fontSize: ACTION_ICON_SIZE } }), sx: {
76
+ } }, { children: penDraftFilled ? "Undo Fill" : "Fill" })) }))), _jsx(Tooltip, __assign({ title: "Apply polyline (Enter or right-click)" }, { children: _jsx(Button, __assign({ size: "small", "aria-label": "apply pen draft", onClick: function () { return onApplyPenDraft === null || onApplyPenDraft === void 0 ? void 0 : onApplyPenDraft(); }, startIcon: _jsx(CheckIcon, { sx: { fontSize: ACTION_ICON_SIZE } }), sx: {
78
77
  color: "#c9a0e8",
79
78
  fontSize: ACTION_FONT_SIZE,
80
79
  textTransform: "none",
@@ -82,7 +81,7 @@ export default function DrawColorPlatte(_a) {
82
81
  py: 0.25,
83
82
  px: 0.75,
84
83
  "& .MuiButton-startIcon": { mr: 0.5, ml: 0 }
85
- } }, { children: "Apply" })) })))] }))] }))), shapeDraftActive && expanded && (_jsx(Stack, __assign({ direction: "row", alignItems: "center", justifyContent: "flex-start", sx: { px: 1, py: 0.5, borderTop: "1px solid #555", width: "100%" } }, { children: _jsx(Tooltip, __assign({ title: "Delete this ROI drawing" }, { children: _jsx(Button, __assign({ size: "small", "aria-label": "delete shape draft", onClick: function () { return onDeleteShapeDraft === null || onDeleteShapeDraft === void 0 ? void 0 : onDeleteShapeDraft(); }, startIcon: _jsx(DeleteOutlineIcon, { sx: { fontSize: ACTION_ICON_SIZE } }), sx: {
84
+ } }, { children: "Apply" })) }))] })))] }))), shapeDraftActive && expanded && (_jsx(Stack, __assign({ direction: "row", alignItems: "center", justifyContent: "flex-start", sx: { px: 1, py: 0.5, borderTop: "1px solid #555", width: "100%" } }, { children: _jsx(Tooltip, __assign({ title: "Delete this ROI drawing" }, { children: _jsx(Button, __assign({ size: "small", "aria-label": "delete shape draft", onClick: function () { return onDeleteShapeDraft === null || onDeleteShapeDraft === void 0 ? void 0 : onDeleteShapeDraft(); }, startIcon: _jsx(DeleteOutlineIcon, { sx: { fontSize: ACTION_ICON_SIZE } }), sx: {
86
85
  color: "#f44336",
87
86
  fontSize: ACTION_FONT_SIZE,
88
87
  textTransform: "none",
@@ -20,6 +20,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
20
20
  import React, { useState, useEffect } from "react";
21
21
  import { Box, Card, CardContent, IconButton, Slider, Stack, Tooltip, Typography, } from "@mui/material";
22
22
  import DrawIcon from "@mui/icons-material/Draw";
23
+ import TimelineIcon from "@mui/icons-material/Timeline";
23
24
  import CropSquareOutlinedIcon from "@mui/icons-material/CropSquareOutlined";
24
25
  import CircleOutlinedIcon from "@mui/icons-material/CircleOutlined";
25
26
  import AutoFixNormalOutlinedIcon from "@mui/icons-material/AutoFixNormalOutlined";
@@ -42,6 +43,14 @@ function clickTargetIsNiivueCanvas(target) {
42
43
  return false;
43
44
  return !!target.closest("#niiCanvas");
44
45
  }
46
+ /** Click-away scoped to one tool button + its dropdown palette. */
47
+ function ToolClickAway(_a) {
48
+ var active = _a.active, onClickAway = _a.onClickAway, sx = _a.sx, children = _a.children;
49
+ var box = _jsx(Box, __assign({ sx: sx }, { children: children }));
50
+ if (!active)
51
+ return box;
52
+ return _jsx(ClickAwayListener, __assign({ onClickAway: onClickAway }, { children: box }));
53
+ }
45
54
  /** Same icon tone as typical MUI on-paper controls (matches slice panel body). */
46
55
  var ICON_COLOR = "#212121";
47
56
  function EraserIcon(props) {
@@ -73,9 +82,11 @@ export function MroDrawToolkit(props) {
73
82
  var _d = useState(undefined), setMaskColor = _d[1];
74
83
  var filled = props.drawPen > 7;
75
84
  useEffect(function () {
76
- // Close palette when tool is deactivated programmatically (e.g. after Apply)
85
+ var eraserSelected = props.drawPen === 0 || props.drawPen === 8;
86
+ // Close palette when tool is deactivated programmatically (e.g. after Apply).
87
+ // Keep eraser palette open when switching from shape/pen edit → eraser in one click.
77
88
  if (drawShapeTool === null && !props.shapeDraftActive && !props.penDraftActive) {
78
- setExpandedOption("n");
89
+ setExpandedOption(eraserSelected ? "e" : "n");
79
90
  return;
80
91
  }
81
92
  if (!props.shapeDraftActive && !props.penDraftActive)
@@ -86,8 +97,14 @@ export function MroDrawToolkit(props) {
86
97
  setExpandedOption("l");
87
98
  else if (drawShapeTool === "pen")
88
99
  setExpandedOption("d");
89
- }, [props.shapeDraftActive, props.penDraftActive, drawShapeTool]);
100
+ else if (drawShapeTool === "polyline")
101
+ setExpandedOption("p");
102
+ }, [props.shapeDraftActive, props.penDraftActive, drawShapeTool, props.drawPen]);
90
103
  var shapeSelectedSx = function (shape) {
104
+ if (shape === "pen")
105
+ return (drawShapeTool === "pen") ? theme.selectedToolSx : {};
106
+ if (shape === "polyline")
107
+ return (drawShapeTool === "polyline") ? theme.selectedToolSx : {};
91
108
  return drawShapeTool === shape ? theme.selectedToolSx : {};
92
109
  };
93
110
  var eraserActive = expandedOption === "e";
@@ -112,6 +129,19 @@ export function MroDrawToolkit(props) {
112
129
  props.setDrawingEnabled(true);
113
130
  }
114
131
  }
132
+ function clickPolyline() {
133
+ leaveEraserIfActive();
134
+ onDrawShapeToolChange === null || onDrawShapeToolChange === void 0 ? void 0 : onDrawShapeToolChange("polyline");
135
+ if (expandedOption === "p") {
136
+ setExpandedOption("n");
137
+ props.setDrawingEnabled(false);
138
+ }
139
+ else {
140
+ setExpandedOption("p");
141
+ setExpandOpacityOptions(false);
142
+ props.setDrawingEnabled(true);
143
+ }
144
+ }
115
145
  function clickRectangle() {
116
146
  leaveEraserIfActive();
117
147
  onDrawShapeToolChange === null || onDrawShapeToolChange === void 0 ? void 0 : onDrawShapeToolChange("rectangle");
@@ -165,43 +195,52 @@ export function MroDrawToolkit(props) {
165
195
  "&:hover": { backgroundColor: "rgba(0,0,0,0.04)" },
166
196
  "&.Mui-disabled": { color: "rgba(0,0,0,0.26)" }
167
197
  };
168
- return (_jsx(ClickAwayListener, __assign({ onClickAway: function (event) {
169
- var _a;
170
- if (clickTargetIsNiivueCanvas(event.target))
171
- return;
172
- if (props.shapeDraftActive || props.penDraftActive)
173
- return;
174
- setExpandedOption("n");
175
- setExpandOpacityOptions(false);
176
- (_a = props.onDeactivateDrawTools) === null || _a === void 0 ? void 0 : _a.call(props);
177
- } }, { children: _jsxs("div", __assign({ style: __assign({ width: "100%", position: "relative", zIndex: 1080 }, props.style) }, { children: [_jsx("div", __assign({ className: "title", style: { width: "100%" } }, { children: "ROI Tools" })), _jsx(Card, __assign({ variant: "outlined", sx: {
178
- mb: 2,
179
- borderTopLeftRadius: 0,
180
- borderTopRightRadius: 0,
181
- overflow: "visible"
182
- } }, { children: _jsx(CardContent, __assign({ sx: {
183
- overflow: "visible",
184
- "&:last-child": { paddingBottom: 2 }
185
- } }, { children: _jsx("div", __assign({ style: { display: "flex", flexDirection: "column", overflow: "visible" } }, { children: _jsxs("div", __assign({ style: {
186
- display: "flex",
187
- flexDirection: "row",
188
- flexWrap: "wrap",
189
- alignItems: "center",
190
- gap: 4,
191
- overflow: "visible"
192
- } }, { children: [_jsxs(Box, __assign({ sx: { position: "relative", zIndex: expandedOption === "d" ? 1600 : "auto", display: "inline-flex", alignItems: "center" } }, { children: [_jsx(Tooltip, __assign({ title: "Pen" }, { children: _jsx(IconButton, __assign({ "aria-label": "pen", size: "small", onClick: clickPen, sx: __assign(__assign({}, toolBtnSx), shapeSelectedSx("pen")) }, { children: _jsx(DrawIcon, { sx: { color: "inherit" } }) })) })), _jsx(DrawColorPlatte, { expanded: expandedOption === "d", updateDrawPen: props.updateDrawPen, setDrawingEnabled: props.setDrawingEnabled, showPenModes: true, penDrawMode: props.penDrawMode, onPenDrawModeChange: props.onPenDrawModeChange, polylineVertexCount: props.polylineVertexCount, onCancelPolyline: props.onCancelPolyline, penDraftActive: props.penDraftActive, penDraftKind: props.penDraftKind, penDraftFilled: props.penDraftFilled, onApplyPenDraft: props.onApplyPenDraft, onDeletePenDraft: props.onDeletePenDraft, onFillPenDraft: props.onFillPenDraft, brushSize: props.brushSize, updateBrushSize: props.updateBrushSize })] })), _jsxs(Box, __assign({ sx: { position: "relative", zIndex: expandedOption === "r" ? 1600 : "auto", display: "inline-flex", alignItems: "center" } }, { children: [_jsx(Tooltip, __assign({ title: "Rectangle" }, { children: _jsx(IconButton, __assign({ "aria-label": "rectangle", size: "small", onClick: clickRectangle, sx: __assign(__assign({}, toolBtnSx), shapeSelectedSx("rectangle")) }, { children: _jsx(CropSquareOutlinedIcon, { sx: { color: "inherit" } }) })) })), _jsx(DrawColorPlatte, { expanded: expandedOption === "r", updateDrawPen: props.updateDrawPen, setDrawingEnabled: props.setDrawingEnabled, shapeDraftActive: props.shapeDraftActive && drawShapeTool === "rectangle", onApplyShapeDraft: props.onApplyShapeDraft, onDeleteShapeDraft: props.onDeleteShapeDraft })] })), _jsxs(Box, __assign({ sx: { position: "relative", zIndex: expandedOption === "l" ? 1600 : "auto", display: "inline-flex", alignItems: "center" } }, { children: [_jsx(Tooltip, __assign({ title: "Ellipse" }, { children: _jsx(IconButton, __assign({ "aria-label": "ellipse", size: "small", onClick: clickEllipse, sx: __assign(__assign({}, toolBtnSx), shapeSelectedSx("ellipse")) }, { children: _jsx(CircleOutlinedIcon, { sx: { color: "inherit" } }) })) })), _jsx(DrawColorPlatte, { expanded: expandedOption === "l", updateDrawPen: props.updateDrawPen, setDrawingEnabled: props.setDrawingEnabled, shapeDraftActive: props.shapeDraftActive && drawShapeTool === "ellipse", onApplyShapeDraft: props.onApplyShapeDraft, onDeleteShapeDraft: props.onDeleteShapeDraft })] })), _jsxs(Box, __assign({ sx: { position: "relative", zIndex: expandedOption === "e" ? 1600 : "auto", display: "inline-flex", alignItems: "center" } }, { children: [_jsx(Tooltip, __assign({ title: "Eraser" }, { children: _jsx(IconButton, __assign({ "aria-label": "erase", size: "small", onClick: clickEraser, sx: __assign(__assign({}, toolBtnSx), (eraserActive ? theme.selectedToolSx : {})) }, { children: filled || !eraserActive ? (_jsx(EraserIcon, {})) : (_jsx(AutoFixNormalOutlinedIcon, { sx: { color: ICON_COLOR } })) })) })), _jsx(EraserPlatte, { expandEraseOptions: expandedOption === "e", updateDrawPen: props.updateDrawPen, setDrawingEnabled: props.setDrawingEnabled, eraserSize: props.eraserSize, updateEraserSize: props.updateEraserSize })] })), _jsx(Tooltip, __assign({ title: "Undo" }, { children: _jsx(IconButton, __assign({ "aria-label": "revert", size: "small", onClick: function () { return props.drawUndo(); }, sx: toolBtnSx }, { children: _jsx(ReplyIcon, { sx: { color: ICON_COLOR } }) })) })), _jsx(Tooltip, __assign({ title: "Save screenshot" }, { children: _jsx("span", { children: _jsx(IconButton, __assign({ "aria-label": "capture", size: "small", disabled: !vol, onClick: function () { return vol && props.nv.saveScene("".concat(vol.name, "_drawing.png")); }, sx: toolBtnSx }, { children: _jsx(CameraAltIcon, { sx: { color: ICON_COLOR } }) })) }) })), _jsx(Tooltip, __assign({ title: "Clear drawing" }, { children: _jsx(IconButton, __assign({ "aria-label": "delete", size: "small", onClick: function () {
193
- var _a;
194
- (_a = props.onClearDrawing) === null || _a === void 0 ? void 0 : _a.call(props);
195
- setExpandedOption("n");
196
- setExpandOpacityOptions(false);
197
- }, sx: toolBtnSx }, { children: _jsx(DeleteIcon, { sx: { color: ICON_COLOR } }) })) })), _jsx(Tooltip, __assign({ title: "ROI visibility" }, { children: _jsx(IconButton, __assign({ "aria-label": "visible", size: "small", onClick: function () { return props.toggleROIVisible(); }, sx: toolBtnSx }, { children: props.roiVisible ? (_jsx(VisibilityIcon, { sx: { color: ICON_COLOR } })) : (_jsx(VisibilityOffIcon, { sx: { color: ICON_COLOR, opacity: 0.45 } })) })) })), _jsxs(Box, __assign({ sx: {
198
- position: "relative",
199
- zIndex: expandOpacityOptions ? 1600 : "auto",
200
- display: "inline-flex",
201
- alignItems: "center",
202
- color: ICON_COLOR
203
- } }, { children: [_jsx(Tooltip, __assign({ title: "Drawing opacity" }, { children: _jsx(IconButton, __assign({ "aria-label": "opaque", size: "small", onClick: function () { return setExpandOpacityOptions(!expandOpacityOptions); }, sx: toolBtnSx }, { children: _jsx(OpacityIcon, { sx: { color: ICON_COLOR } }) })) })), _jsx(Typography, __assign({ component: "span", sx: { fontSize: "0.7rem", mr: 0.25, userSelect: "none", color: ICON_COLOR } }, { children: props.drawingOpacity.toFixed(2) })), _jsx(OpacityPlatte, { drawingOpacity: props.drawingOpacity, setDrawingOpacity: props.setDrawingOpacity, expanded: expandOpacityOptions })] })), _jsxs(Box, __assign({ sx: { position: "relative", zIndex: expandedOption === "m" ? 1600 : "auto", display: "inline-flex", alignItems: "center" } }, { children: [_jsx(Tooltip, __assign({ title: "Mask by intensity range" }, { children: _jsx(IconButton, __assign({ "aria-label": "fill", size: "small", onClick: clickMask, sx: toolBtnSx }, { children: _jsx(FormatColorFillIcon, { sx: { color: ICON_COLOR } }) })) })), _jsx(MaskPlatte, { resampleImage: function () {
204
- props.resampleImage();
205
- props.setDrawingChanged(true);
206
- }, expanded: expandedOption === "m", nv: props.nv, setMaskColor: setMaskColor, unfocus: function () { return setExpandedOption("n"); } })] }))] })) })) })) }))] })) })));
198
+ function shouldIgnoreToolClickAway(event) {
199
+ if (clickTargetIsNiivueCanvas(event.target))
200
+ return true;
201
+ if (props.shapeDraftActive || props.penDraftActive)
202
+ return true;
203
+ return false;
204
+ }
205
+ function handleDrawToolClickAway(event) {
206
+ var _a;
207
+ if (shouldIgnoreToolClickAway(event))
208
+ return;
209
+ if (expandedOption === "n" || expandedOption === "m")
210
+ return;
211
+ setExpandedOption("n");
212
+ setExpandOpacityOptions(false);
213
+ (_a = props.onDeactivateDrawTools) === null || _a === void 0 ? void 0 : _a.call(props);
214
+ }
215
+ var toolGroupSx = { position: "relative", display: "inline-flex", alignItems: "center" };
216
+ return (_jsxs("div", __assign({ style: __assign({ width: "100%", position: "relative", zIndex: 1080 }, props.style) }, { children: [_jsx("div", __assign({ className: "title", style: { width: "100%" } }, { children: "ROI Tools" })), _jsx(Card, __assign({ variant: "outlined", sx: {
217
+ mb: 2,
218
+ borderTopLeftRadius: 0,
219
+ borderTopRightRadius: 0,
220
+ overflow: "visible"
221
+ } }, { children: _jsx(CardContent, __assign({ sx: {
222
+ overflow: "visible",
223
+ "&:last-child": { paddingBottom: 2 }
224
+ } }, { children: _jsx("div", __assign({ style: { display: "flex", flexDirection: "column", overflow: "visible" } }, { children: _jsxs("div", __assign({ style: {
225
+ display: "flex",
226
+ flexDirection: "row",
227
+ flexWrap: "wrap",
228
+ alignItems: "center",
229
+ gap: 4,
230
+ overflow: "visible"
231
+ } }, { children: [_jsxs(ToolClickAway, __assign({ active: expandedOption === "d", onClickAway: handleDrawToolClickAway, sx: __assign(__assign({}, toolGroupSx), { zIndex: expandedOption === "d" ? 1600 : "auto" }) }, { children: [_jsx(Tooltip, __assign({ title: "Freehand" }, { children: _jsx(IconButton, __assign({ "aria-label": "freehand", size: "small", onClick: clickPen, sx: __assign(__assign({}, toolBtnSx), shapeSelectedSx("pen")) }, { children: _jsx(DrawIcon, { sx: { color: "inherit" } }) })) })), _jsx(DrawColorPlatte, { expanded: expandedOption === "d", updateDrawPen: props.updateDrawPen, setDrawingEnabled: props.setDrawingEnabled, penToolKind: "freehand", penDraftActive: props.penDraftActive, penDraftKind: props.penDraftKind, onDeletePenDraft: props.onDeletePenDraft, brushSize: props.brushSize, updateBrushSize: props.updateBrushSize })] })), _jsxs(ToolClickAway, __assign({ active: expandedOption === "p", onClickAway: handleDrawToolClickAway, sx: __assign(__assign({}, toolGroupSx), { zIndex: expandedOption === "p" ? 1600 : "auto" }) }, { children: [_jsx(Tooltip, __assign({ title: "Polyline" }, { children: _jsx(IconButton, __assign({ "aria-label": "polyline", size: "small", onClick: clickPolyline, sx: __assign(__assign({}, toolBtnSx), shapeSelectedSx("polyline")) }, { children: _jsx(TimelineIcon, { sx: { color: "inherit" } }) })) })), _jsx(DrawColorPlatte, { expanded: expandedOption === "p", updateDrawPen: props.updateDrawPen, setDrawingEnabled: props.setDrawingEnabled, penToolKind: "polyline", polylineVertexCount: props.polylineVertexCount, penDraftActive: props.penDraftActive, penDraftKind: props.penDraftKind, penDraftFilled: props.penDraftFilled, onApplyPenDraft: props.onApplyPenDraft, onDeletePenDraft: props.onDeletePenDraft, onFillPenDraft: props.onFillPenDraft, brushSize: props.brushSize, updateBrushSize: props.updateBrushSize })] })), _jsxs(ToolClickAway, __assign({ active: expandedOption === "r", onClickAway: handleDrawToolClickAway, sx: __assign(__assign({}, toolGroupSx), { zIndex: expandedOption === "r" ? 1600 : "auto" }) }, { children: [_jsx(Tooltip, __assign({ title: "Rectangle" }, { children: _jsx(IconButton, __assign({ "aria-label": "rectangle", size: "small", onClick: clickRectangle, sx: __assign(__assign({}, toolBtnSx), shapeSelectedSx("rectangle")) }, { children: _jsx(CropSquareOutlinedIcon, { sx: { color: "inherit" } }) })) })), _jsx(DrawColorPlatte, { expanded: expandedOption === "r", updateDrawPen: props.updateDrawPen, setDrawingEnabled: props.setDrawingEnabled, shapeDraftActive: props.shapeDraftActive && drawShapeTool === "rectangle", onApplyShapeDraft: props.onApplyShapeDraft, onDeleteShapeDraft: props.onDeleteShapeDraft })] })), _jsxs(ToolClickAway, __assign({ active: expandedOption === "l", onClickAway: handleDrawToolClickAway, sx: __assign(__assign({}, toolGroupSx), { zIndex: expandedOption === "l" ? 1600 : "auto" }) }, { children: [_jsx(Tooltip, __assign({ title: "Ellipse" }, { children: _jsx(IconButton, __assign({ "aria-label": "ellipse", size: "small", onClick: clickEllipse, sx: __assign(__assign({}, toolBtnSx), shapeSelectedSx("ellipse")) }, { children: _jsx(CircleOutlinedIcon, { sx: { color: "inherit" } }) })) })), _jsx(DrawColorPlatte, { expanded: expandedOption === "l", updateDrawPen: props.updateDrawPen, setDrawingEnabled: props.setDrawingEnabled, shapeDraftActive: props.shapeDraftActive && drawShapeTool === "ellipse", onApplyShapeDraft: props.onApplyShapeDraft, onDeleteShapeDraft: props.onDeleteShapeDraft })] })), _jsxs(ToolClickAway, __assign({ active: expandedOption === "e", onClickAway: handleDrawToolClickAway, sx: __assign(__assign({}, toolGroupSx), { zIndex: expandedOption === "e" ? 1600 : "auto" }) }, { children: [_jsx(Tooltip, __assign({ title: "Eraser" }, { children: _jsx(IconButton, __assign({ "aria-label": "erase", size: "small", onClick: clickEraser, sx: __assign(__assign({}, toolBtnSx), (eraserActive ? theme.selectedToolSx : {})) }, { children: filled || !eraserActive ? (_jsx(EraserIcon, {})) : (_jsx(AutoFixNormalOutlinedIcon, { sx: { color: ICON_COLOR } })) })) })), _jsx(EraserPlatte, { expandEraseOptions: expandedOption === "e", updateDrawPen: props.updateDrawPen, setDrawingEnabled: props.setDrawingEnabled, eraserSize: props.eraserSize, updateEraserSize: props.updateEraserSize })] })), _jsx(Tooltip, __assign({ title: "Undo" }, { children: _jsx(IconButton, __assign({ "aria-label": "revert", size: "small", onClick: function () { return props.drawUndo(); }, sx: toolBtnSx }, { children: _jsx(ReplyIcon, { sx: { color: ICON_COLOR } }) })) })), _jsx(Tooltip, __assign({ title: "Save screenshot" }, { children: _jsx("span", { children: _jsx(IconButton, __assign({ "aria-label": "capture", size: "small", disabled: !vol, onClick: function () { return vol && props.nv.saveScene("".concat(vol.name, "_drawing.png")); }, sx: toolBtnSx }, { children: _jsx(CameraAltIcon, { sx: { color: ICON_COLOR } }) })) }) })), _jsx(Tooltip, __assign({ title: "Clear drawing" }, { children: _jsx(IconButton, __assign({ "aria-label": "delete", size: "small", onClick: function () {
232
+ var _a;
233
+ (_a = props.onClearDrawing) === null || _a === void 0 ? void 0 : _a.call(props);
234
+ setExpandedOption("n");
235
+ setExpandOpacityOptions(false);
236
+ }, sx: toolBtnSx }, { children: _jsx(DeleteIcon, { sx: { color: ICON_COLOR } }) })) })), _jsx(Tooltip, __assign({ title: "ROI visibility" }, { children: _jsx(IconButton, __assign({ "aria-label": "visible", size: "small", onClick: function () { return props.toggleROIVisible(); }, sx: toolBtnSx }, { children: props.roiVisible ? (_jsx(VisibilityIcon, { sx: { color: ICON_COLOR } })) : (_jsx(VisibilityOffIcon, { sx: { color: ICON_COLOR, opacity: 0.45 } })) })) })), _jsxs(Box, __assign({ sx: {
237
+ position: "relative",
238
+ zIndex: expandOpacityOptions ? 1600 : "auto",
239
+ display: "inline-flex",
240
+ alignItems: "center",
241
+ color: ICON_COLOR
242
+ } }, { children: [_jsx(Tooltip, __assign({ title: "Drawing opacity" }, { children: _jsx(IconButton, __assign({ "aria-label": "opaque", size: "small", onClick: function () { return setExpandOpacityOptions(!expandOpacityOptions); }, sx: toolBtnSx }, { children: _jsx(OpacityIcon, { sx: { color: ICON_COLOR } }) })) })), _jsx(Typography, __assign({ component: "span", sx: { fontSize: "0.7rem", mr: 0.25, userSelect: "none", color: ICON_COLOR } }, { children: props.drawingOpacity.toFixed(2) })), _jsx(OpacityPlatte, { drawingOpacity: props.drawingOpacity, setDrawingOpacity: props.setDrawingOpacity, expanded: expandOpacityOptions })] })), _jsxs(Box, __assign({ sx: { position: "relative", zIndex: expandedOption === "m" ? 1600 : "auto", display: "inline-flex", alignItems: "center" } }, { children: [_jsx(Tooltip, __assign({ title: "Mask by intensity range" }, { children: _jsx(IconButton, __assign({ "aria-label": "fill", size: "small", onClick: clickMask, sx: toolBtnSx }, { children: _jsx(FormatColorFillIcon, { sx: { color: ICON_COLOR } }) })) })), _jsx(MaskPlatte, { resampleImage: function () {
243
+ props.resampleImage();
244
+ props.setDrawingChanged(true);
245
+ }, expanded: expandedOption === "m", nv: props.nv, setMaskColor: setMaskColor, unfocus: function () { return setExpandedOption("n"); } })] }))] })) })) })) }))] })));
207
246
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloudmr-ux",
3
- "version": "4.8.2",
3
+ "version": "4.8.3",
4
4
  "author": "erosmontin@gmail.com",
5
5
  "license": "MIT",
6
6
  "repository": "erosmontin/cloudmr-ux",