cloudmr-ux 4.7.6 → 4.7.8
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.
- package/dist/CmrComponents/niivue-contrast-adjustments/NiivueContrastAdjustments.d.ts +1 -1
- package/dist/CmrComponents/niivue-contrast-adjustments/NiivueContrastAdjustments.js +4 -1
- package/dist/CmrComponents/niivue-roi-table/NiivueRoiTable.js +3 -1
- package/dist/CmrComponents/niivue-slice-position/NiivueSlicePosition.d.ts +1 -1
- package/dist/CmrComponents/niivue-slice-position/NiivueSlicePosition.js +9 -6
- package/dist/CmrComponents/niivue-viewer/CloudMrNiivuePanel.js +3 -1
- package/dist/CmrComponents/niivue-viewer/CloudMrNiivueViewer.d.ts +5 -0
- package/dist/CmrComponents/niivue-viewer/CloudMrNiivueViewer.js +80 -15
- package/dist/CmrComponents/niivue-viewer/Layer.js +3 -1
- package/dist/CmrComponents/niivue-viewer/NiivueViewerThemeContext.d.ts +14 -0
- package/dist/CmrComponents/niivue-viewer/NiivueViewerThemeContext.js +31 -0
- package/dist/CmrComponents/niivue-viewer/PenDraftOverlay.js +5 -4
- package/dist/CmrComponents/niivue-viewer/ShapeDraftOverlay.js +5 -4
- package/dist/CmrComponents/niivue-viewer/Switch.js +1 -1
- package/dist/CmrComponents/niivue-viewer/Toolbar.js +4 -4
- package/dist/CmrComponents/niivue-viewer/mro-draw-toolkit/DrawColorPlatte.d.ts +3 -1
- package/dist/CmrComponents/niivue-viewer/mro-draw-toolkit/DrawColorPlatte.js +24 -15
- package/dist/CmrComponents/niivue-viewer/mro-draw-toolkit/MroDrawToolkit.js +5 -7
- package/dist/CmrComponents/niivue-viewer/niivueViewerTheme.d.ts +33 -0
- package/dist/CmrComponents/niivue-viewer/niivueViewerTheme.js +90 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/package.json +1 -1
|
@@ -88,5 +88,5 @@ export interface NiivueContrastAdjustmentsProps {
|
|
|
88
88
|
* Pass `title=""` to suppress the heading strip, or `title="Window / Level"` to
|
|
89
89
|
* rename it for a different application.
|
|
90
90
|
*/
|
|
91
|
-
export declare function NiivueContrastAdjustments({ nv, min, max, setMin, setMax, transformFactors, gamma, gammaKey, setGamma, layerList, title, accentColor, style, className, }: NiivueContrastAdjustmentsProps): import("react/jsx-runtime").JSX.Element;
|
|
91
|
+
export declare function NiivueContrastAdjustments({ nv, min, max, setMin, setMax, transformFactors, gamma, gammaKey, setGamma, layerList, title, accentColor: accentColorProp, style, className, }: NiivueContrastAdjustmentsProps): import("react/jsx-runtime").JSX.Element;
|
|
92
92
|
export default NiivueContrastAdjustments;
|
|
@@ -13,6 +13,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
13
13
|
import { Card, CardContent, Box } from "@mui/material";
|
|
14
14
|
import TKDualRange from "../tk-dualrange/TKDualRange";
|
|
15
15
|
import CmrLabel from "../label/Label";
|
|
16
|
+
import { resolveNiivueAccentColor, useNiivueViewerTheme, } from "../niivue-viewer/NiivueViewerThemeContext";
|
|
16
17
|
// ─── Component ────────────────────────────────────────────────────────────────
|
|
17
18
|
/**
|
|
18
19
|
* **NiivueContrastAdjustments**
|
|
@@ -47,7 +48,9 @@ import CmrLabel from "../label/Label";
|
|
|
47
48
|
*/
|
|
48
49
|
export function NiivueContrastAdjustments(_a) {
|
|
49
50
|
var _b, _c, _d, _e;
|
|
50
|
-
var nv = _a.nv, min = _a.min, max = _a.max, setMin = _a.setMin, setMax = _a.setMax, transformFactors = _a.transformFactors, gamma = _a.gamma, gammaKey = _a.gammaKey, setGamma = _a.setGamma, _f = _a.layerList, layerList = _f === void 0 ? [] : _f, _g = _a.title, title = _g === void 0 ? "Contrast Adjustments" : _g,
|
|
51
|
+
var nv = _a.nv, min = _a.min, max = _a.max, setMin = _a.setMin, setMax = _a.setMax, transformFactors = _a.transformFactors, gamma = _a.gamma, gammaKey = _a.gammaKey, setGamma = _a.setGamma, _f = _a.layerList, layerList = _f === void 0 ? [] : _f, _g = _a.title, title = _g === void 0 ? "Contrast Adjustments" : _g, accentColorProp = _a.accentColor, style = _a.style, className = _a.className;
|
|
52
|
+
var theme = useNiivueViewerTheme();
|
|
53
|
+
var accentColor = resolveNiivueAccentColor(accentColorProp, theme);
|
|
51
54
|
var a = transformFactors.a, b = transformFactors.b;
|
|
52
55
|
return (_jsxs("div", __assign({ style: style, className: className }, { children: [title !== "" && (_jsx("div", __assign({ className: "title", style: { width: "100%" } }, { children: title }))), _jsx(Card, __assign({ variant: "outlined", sx: { mb: 2, borderTopLeftRadius: 0, borderTopRightRadius: 0 } }, { children: _jsx(CardContent, __assign({ sx: { "&:last-child": { paddingBottom: 2 } } }, { children: _jsxs(Box, __assign({ style: { display: "flex", flex: 1, minWidth: "245px", flexDirection: "column" } }, { children: [_jsx(TKDualRange, { name: "Values:", minDomain: (_c = (_b = nv.volumes[0]) === null || _b === void 0 ? void 0 : _b.robust_min) !== null && _c !== void 0 ? _c : 0, maxDomain: (_e = (_d = nv.volumes[0]) === null || _d === void 0 ? void 0 : _d.robust_max) !== null && _e !== void 0 ? _e : 1, valueLow: min, valueHigh: max, onChangeLow: function (newMin) {
|
|
53
56
|
var v = nv.volumes[0];
|
|
@@ -58,6 +58,7 @@ import CmrTable from "../../CmrTable/CmrTable";
|
|
|
58
58
|
import CMRUpload from "../upload/Upload";
|
|
59
59
|
import { getEndpoints } from "../../core/config/AppConfig";
|
|
60
60
|
import { AuthenticatedHttpClient } from "../../core/common/utilities/AuthenticatedRequests";
|
|
61
|
+
import { useNiivueViewerTheme } from "../niivue-viewer/NiivueViewerThemeContext";
|
|
61
62
|
/** Default merged ROI label in Niivue patcher `groupLabelsInto` / `groupLabelsFromSelection`. */
|
|
62
63
|
export var DEFAULT_ROI_GROUP_TARGET_LABEL = 7;
|
|
63
64
|
var ROI_TOOLBAR_ICON_SIZE_PX = 24;
|
|
@@ -79,6 +80,7 @@ export function NiivueRoiTable(props) {
|
|
|
79
80
|
var _this = this;
|
|
80
81
|
var _a = props.groupTargetLabel, groupTargetLabel = _a === void 0 ? DEFAULT_ROI_GROUP_TARGET_LABEL : _a, onAfterRoiUpload = props.onAfterRoiUpload;
|
|
81
82
|
var _b = useState(1), uploadKey = _b[0], setUploadKey = _b[1];
|
|
83
|
+
var theme = useNiivueViewerTheme();
|
|
82
84
|
var endpoints = getEndpoints();
|
|
83
85
|
var _c = useState([]), selectedData = _c[0], setSelectedData = _c[1];
|
|
84
86
|
var roiColumns = [
|
|
@@ -151,7 +153,7 @@ export function NiivueRoiTable(props) {
|
|
|
151
153
|
: uniqueSelected.size < 2 || selectedNums.length < 2
|
|
152
154
|
? "Select at least two different ROIs to group"
|
|
153
155
|
: "Group selected ROIs";
|
|
154
|
-
return (_jsxs(Box, __assign({ style: props.style }, { children: [_jsx(CmrTable, { hideFooter: true, getRowId: function (row) { return row.label; }, style: { height: "70%" }, dataSource: props.rois, columns: roiColumns, columnHeaderHeight: 40, rowSelectionModel: selectedData, onRowSelectionModelChange: function (rowSelectionModel) {
|
|
156
|
+
return (_jsxs(Box, __assign({ style: props.style }, { children: [_jsx(CmrTable, { hideFooter: true, getRowId: function (row) { return row.label; }, style: { height: "70%" }, dataSource: props.rois, columns: roiColumns, columnHeaderHeight: 40, headerBgColor: theme.headerBgColor, headerIconColor: theme.headerIconColor, checkboxCheckedColor: theme.checkboxCheckedColor, checkboxUncheckedColor: theme.checkboxUncheckedColor, rowSelectionModel: selectedData, onRowSelectionModelChange: function (rowSelectionModel) {
|
|
155
157
|
setSelectedData(rowSelectionModel);
|
|
156
158
|
} }), _jsxs(Box, __assign({ sx: {
|
|
157
159
|
display: "flex",
|
|
@@ -68,5 +68,5 @@ export interface NiivueSlicePositionProps {
|
|
|
68
68
|
* <NiivueSlicePosition nv={nv} mins={mins} maxs={maxs} mms={mms} />
|
|
69
69
|
* ```
|
|
70
70
|
*/
|
|
71
|
-
export declare function NiivueSlicePosition({ nv, mins, maxs, mms, title, accentColor, style, className, }: NiivueSlicePositionProps): import("react/jsx-runtime").JSX.Element;
|
|
71
|
+
export declare function NiivueSlicePosition({ nv, mins, maxs, mms, title, accentColor: accentColorProp, style, className, }: NiivueSlicePositionProps): import("react/jsx-runtime").JSX.Element;
|
|
72
72
|
export default NiivueSlicePosition;
|
|
@@ -13,6 +13,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
13
13
|
import React from "react";
|
|
14
14
|
import { Card, CardContent } from "@mui/material";
|
|
15
15
|
import CmrLabel from "../label/Label";
|
|
16
|
+
import { resolveNiivueAccentColor, useNiivueViewerTheme, } from "../niivue-viewer/NiivueViewerThemeContext";
|
|
16
17
|
// ─── Helpers (pure, no React) ─────────────────────────────────────────────────
|
|
17
18
|
var safeSpan = function (min, max) { return Math.max(1e-9, max - min); };
|
|
18
19
|
var clamp = function (v, lo, hi) { return Math.max(lo, Math.min(hi, v)); };
|
|
@@ -47,7 +48,9 @@ var round3 = function (v) { return Math.round(v * 1000) / 1000; };
|
|
|
47
48
|
*/
|
|
48
49
|
export function NiivueSlicePosition(_a) {
|
|
49
50
|
var _b, _c, _d, _e, _f;
|
|
50
|
-
var nv = _a.nv, mins = _a.mins, maxs = _a.maxs, mms = _a.mms, _g = _a.title, title = _g === void 0 ? "Slice Position" : _g,
|
|
51
|
+
var nv = _a.nv, mins = _a.mins, maxs = _a.maxs, mms = _a.mms, _g = _a.title, title = _g === void 0 ? "Slice Position" : _g, accentColorProp = _a.accentColor, style = _a.style, className = _a.className;
|
|
52
|
+
var theme = useNiivueViewerTheme();
|
|
53
|
+
var accentColor = resolveNiivueAccentColor(accentColorProp, theme);
|
|
51
54
|
// ── Derive voxel grid from the loaded volume ─────────────────────────────
|
|
52
55
|
var vol = (_b = nv === null || nv === void 0 ? void 0 : nv.volumes) === null || _b === void 0 ? void 0 : _b[0];
|
|
53
56
|
var meta = (_c = vol === null || vol === void 0 ? void 0 : vol.getImageMetadata) === null || _c === void 0 ? void 0 : _c.call(vol);
|
|
@@ -77,7 +80,7 @@ export function NiivueSlicePosition(_a) {
|
|
|
77
80
|
zAtStart = nv.frac2mm([cx, cy, 0.5 / nz])[2];
|
|
78
81
|
zAtEnd = nv.frac2mm([cx, cy, (nz - 0.5) / nz])[2];
|
|
79
82
|
}
|
|
80
|
-
catch (
|
|
83
|
+
catch (_h) {
|
|
81
84
|
var s = safeSpan(mins[2], maxs[2]) / nz;
|
|
82
85
|
zAtStart = mins[2] + 0.5 * s;
|
|
83
86
|
zAtEnd = maxs[2] - 0.5 * s;
|
|
@@ -128,9 +131,9 @@ export function NiivueSlicePosition(_a) {
|
|
|
128
131
|
}
|
|
129
132
|
};
|
|
130
133
|
// ── Local slider state (mirrors mms; synced by useEffect) ────────────────
|
|
131
|
-
var
|
|
132
|
-
var
|
|
133
|
-
var
|
|
134
|
+
var _j = React.useState(round3(mms[0])), xVal = _j[0], setXVal = _j[1];
|
|
135
|
+
var _k = React.useState(round3(mms[1])), yVal = _k[0], setYVal = _k[1];
|
|
136
|
+
var _l = React.useState(round3(mms[2])), zVal = _l[0], setZVal = _l[1];
|
|
134
137
|
// Keep a ref so snapToVoxel can read the *latest* values without stale closure
|
|
135
138
|
var mmsRef = React.useRef([xVal, yVal, zVal]);
|
|
136
139
|
React.useEffect(function () {
|
|
@@ -208,7 +211,7 @@ export function NiivueSlicePosition(_a) {
|
|
|
208
211
|
try {
|
|
209
212
|
fracZ = nv.mm2frac([xVal, yVal, zVal])[2];
|
|
210
213
|
}
|
|
211
|
-
catch (
|
|
214
|
+
catch (_m) {
|
|
212
215
|
fracZ = ratioAxis(zVal, 2);
|
|
213
216
|
}
|
|
214
217
|
zSliceIndex = clamp(Math.round(fracZ * nz - 0.5), 0, nz - 1);
|
|
@@ -57,8 +57,10 @@ import { NiivueRoiTable } from "../niivue-roi-table/NiivueRoiTable";
|
|
|
57
57
|
import { MroDrawToolkit } from "./mro-draw-toolkit/MroDrawToolkit";
|
|
58
58
|
import { ShapeDraftOverlay } from "./ShapeDraftOverlay";
|
|
59
59
|
import { PenDraftOverlay } from "./PenDraftOverlay";
|
|
60
|
+
import { useNiivueViewerTheme } from "./NiivueViewerThemeContext";
|
|
60
61
|
export function CloudMrNiivuePanel(props) {
|
|
61
62
|
var _this = this;
|
|
63
|
+
var theme = useNiivueViewerTheme();
|
|
62
64
|
var canvas = React.useRef(null);
|
|
63
65
|
var histogram = React.useRef(null);
|
|
64
66
|
var mins = props.mins, maxs = props.maxs, mms = props.mms;
|
|
@@ -150,7 +152,7 @@ export function CloudMrNiivuePanel(props) {
|
|
|
150
152
|
md: "row"
|
|
151
153
|
},
|
|
152
154
|
flexWrap: "nowrap"
|
|
153
|
-
} }, { children: [_jsxs(Box, __assign({ sx: {
|
|
155
|
+
}, style: theme.cssVars }, { children: [_jsxs(Box, __assign({ sx: {
|
|
154
156
|
width: {
|
|
155
157
|
xs: "100%",
|
|
156
158
|
md: "63%"
|
|
@@ -13,6 +13,7 @@ export default function CloudMrNiivueViewer(props: any): import("react/jsx-runti
|
|
|
13
13
|
* @property {string} roiDeleteUrl — full URL for `ROI_DELETE` (app resolves from env)
|
|
14
14
|
* @property {() => Promise<void>} refreshPipelineRois — e.g. dispatch getPipelineROI
|
|
15
15
|
* @property {() => any[]} getPipelineRois — returns current ROI list for pipeline (e.g. from Redux)
|
|
16
|
+
* @property {string} [accentColor] — override viewer accent; else host MUI primary if set, else `#580f8b`
|
|
16
17
|
*/
|
|
17
18
|
export const nv: Niivue;
|
|
18
19
|
export type CloudMrNiivueViewerProps = {
|
|
@@ -37,5 +38,9 @@ export type CloudMrNiivueViewerProps = {
|
|
|
37
38
|
* — returns current ROI list for pipeline (e.g. from Redux)
|
|
38
39
|
*/
|
|
39
40
|
getPipelineRois: () => any[];
|
|
41
|
+
/**
|
|
42
|
+
* — override viewer accent; else host MUI primary if set, else `#580f8b`
|
|
43
|
+
*/
|
|
44
|
+
accentColor?: string | undefined;
|
|
40
45
|
};
|
|
41
46
|
import { Niivue } from "./NiivuePatcher";
|
|
@@ -77,6 +77,7 @@ import axios from "axios";
|
|
|
77
77
|
import JSZip from "jszip";
|
|
78
78
|
import { getMax, getMin } from "../../core/common/utilities";
|
|
79
79
|
import { AuthenticatedHttpClient, getEndpoints } from "../../core";
|
|
80
|
+
import { NiivueViewerThemeProvider } from './NiivueViewerThemeContext';
|
|
80
81
|
/**
|
|
81
82
|
* @typedef {Object} CloudMrNiivueViewerProps
|
|
82
83
|
* @property {any[]} niis
|
|
@@ -91,6 +92,7 @@ import { AuthenticatedHttpClient, getEndpoints } from "../../core";
|
|
|
91
92
|
* @property {string} roiDeleteUrl — full URL for `ROI_DELETE` (app resolves from env)
|
|
92
93
|
* @property {() => Promise<void>} refreshPipelineRois — e.g. dispatch getPipelineROI
|
|
93
94
|
* @property {() => any[]} getPipelineRois — returns current ROI list for pipeline (e.g. from Redux)
|
|
95
|
+
* @property {string} [accentColor] — override viewer accent; else host MUI primary if set, else `#580f8b`
|
|
94
96
|
*/
|
|
95
97
|
export var nv = new Niivue({
|
|
96
98
|
loadingText: '',
|
|
@@ -1051,6 +1053,67 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
1051
1053
|
nvSetDrawingEnabled(true);
|
|
1052
1054
|
}
|
|
1053
1055
|
}
|
|
1056
|
+
function deleteShapeDraftHandler() {
|
|
1057
|
+
var draft = shapeDraftRef.current;
|
|
1058
|
+
if (!(draft === null || draft === void 0 ? void 0 : draft.baseBitmap))
|
|
1059
|
+
return;
|
|
1060
|
+
// Clear tool-kind tags for voxels this shape occupied
|
|
1061
|
+
if (nv._cloudMrToolKindBitmap) {
|
|
1062
|
+
for (var i = 0; i < nv.drawBitmap.length; i++) {
|
|
1063
|
+
if (nv.drawBitmap[i] === draft.penValue && draft.baseBitmap[i] !== draft.penValue) {
|
|
1064
|
+
nv._cloudMrToolKindBitmap[i] = 0;
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
nv.drawBitmap.set(draft.baseBitmap);
|
|
1069
|
+
nv.refreshDrawing(true, false);
|
|
1070
|
+
nv.drawScene();
|
|
1071
|
+
nv.drawAddUndoBitmap(nv.drawFillOverwrites);
|
|
1072
|
+
setShapeDraft(null);
|
|
1073
|
+
shapeDraftRef.current = null;
|
|
1074
|
+
nv._cloudMrShapeDraftActive = false;
|
|
1075
|
+
setDrawingChanged(true);
|
|
1076
|
+
setDrawShapeTool(null);
|
|
1077
|
+
nv.opts.deferShapeCommit = false;
|
|
1078
|
+
nv.opts.penType = NI_PEN_TYPE.PEN;
|
|
1079
|
+
nvSetDrawingEnabled(false);
|
|
1080
|
+
resampleImage();
|
|
1081
|
+
}
|
|
1082
|
+
function deletePenDraftHandler() {
|
|
1083
|
+
var _a;
|
|
1084
|
+
var draft = penDraftRef.current;
|
|
1085
|
+
if (!(draft === null || draft === void 0 ? void 0 : draft.baseBitmap))
|
|
1086
|
+
return;
|
|
1087
|
+
// Clear tool-kind tags for voxels this ROI occupied
|
|
1088
|
+
if (nv._cloudMrToolKindBitmap) {
|
|
1089
|
+
for (var i = 0; i < nv.drawBitmap.length; i++) {
|
|
1090
|
+
if (nv.drawBitmap[i] === draft.penValue && draft.baseBitmap[i] !== draft.penValue) {
|
|
1091
|
+
nv._cloudMrToolKindBitmap[i] = 0;
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
nv.drawBitmap.set(draft.baseBitmap);
|
|
1096
|
+
nv.refreshDrawing(true, false);
|
|
1097
|
+
nv.drawScene();
|
|
1098
|
+
nv.drawAddUndoBitmap(nv.drawFillOverwrites);
|
|
1099
|
+
// Remove from polyline registry if this was a registered polyline
|
|
1100
|
+
if (draft._registryId != null && nv._cloudMrPolylineRegistry) {
|
|
1101
|
+
nv._cloudMrPolylineRegistry = nv._cloudMrPolylineRegistry.filter(function (e) { return e.id !== draft._registryId; });
|
|
1102
|
+
}
|
|
1103
|
+
if (draft.kind === "polyline") {
|
|
1104
|
+
(_a = nv.cloudMrResetPolyline) === null || _a === void 0 ? void 0 : _a.call(nv);
|
|
1105
|
+
setPolylineVertexCount(0);
|
|
1106
|
+
}
|
|
1107
|
+
setPenDraft(null);
|
|
1108
|
+
penDraftRef.current = null;
|
|
1109
|
+
nv._cloudMrPenDraftActive = false;
|
|
1110
|
+
setDrawingChanged(true);
|
|
1111
|
+
setDrawShapeTool(null);
|
|
1112
|
+
nv.opts.deferFreehandCommit = false;
|
|
1113
|
+
nv.opts.polylinePenMode = false;
|
|
1114
|
+
nvSetDrawingEnabled(false);
|
|
1115
|
+
resampleImage();
|
|
1116
|
+
}
|
|
1054
1117
|
function ensureToolKindBitmap() {
|
|
1055
1118
|
if (!nv.drawBitmap)
|
|
1056
1119
|
return;
|
|
@@ -1692,6 +1755,8 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
1692
1755
|
onCancelPolyline: cancelPolylineDraft,
|
|
1693
1756
|
onApplyPenDraft: applyPenDraftHandler,
|
|
1694
1757
|
onCancelPenDraft: cancelPenDraftHandler,
|
|
1758
|
+
onDeletePenDraft: deletePenDraftHandler,
|
|
1759
|
+
onDeleteShapeDraft: deleteShapeDraftHandler,
|
|
1695
1760
|
onFillPenDraft: fillPolylineDraftHandler,
|
|
1696
1761
|
penDraftActive: penDraft != null,
|
|
1697
1762
|
penDraftKind: penDraft === null || penDraft === void 0 ? void 0 : penDraft.kind,
|
|
@@ -1702,21 +1767,21 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
1702
1767
|
onDeactivateDrawTools: deactivateDrawTools,
|
|
1703
1768
|
onClearDrawing: clearDrawingHandler
|
|
1704
1769
|
};
|
|
1705
|
-
return (_jsxs(Box, __assign({ sx: {
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1770
|
+
return (_jsx(NiivueViewerThemeProvider, __assign({ accentColor: props.accentColor }, { children: _jsxs(Box, __assign({ sx: {
|
|
1771
|
+
display: 'flex',
|
|
1772
|
+
flexDirection: 'column',
|
|
1773
|
+
height: '100%',
|
|
1774
|
+
width: '100%',
|
|
1775
|
+
alignItems: 'center',
|
|
1776
|
+
justifyContent: 'center'
|
|
1777
|
+
} }, { children: [_jsxs(SettingsPanel, __assign({ open: openSettings, width: 300, toggleMenu: toggleSettings }, { children: [_jsx(ColorPicker, { colorRGB01: backColor, onSetColor: nvUpdateBackColor, title: 'Background color' }), _jsx(ColorPicker, { colorRGB01: clipPlaneColor, onSetColor: nvUpdateClipPlaneColor, title: 'Clip plane color' }), _jsx(NumberPicker, { value: clipPlaneOpacity, onChange: nvUpdateClipPlaneOpacity, title: 'Clip plane opacity', min: 0, max: 1, step: 0.1 }), _jsx(ColorPicker, { colorRGB01: crosshairColor, onSetColor: nvUpdateCrosshairColor, title: 'Crosshair color' }), _jsx(NumberPicker, { value: crosshairOpacity, onChange: nvUpdateCrosshairOpacity, title: 'Crosshair opacity', min: 0, max: 1, step: 0.1 }), _jsx(ColorPicker, { colorRGB01: selectionBoxColor, onSetColor: nvUpdateSelectionBoxColor, title: 'Selection box color' }), _jsx(NumberPicker, { value: nv.opts.crosshairWidth, onChange: nvUpdateCrosshairSize, title: 'Crosshair size', min: 0, max: 10, step: 1 }), _jsx(NumberPicker, { value: textSize, onChange: nvUpdateTextSize, title: 'Text size', min: 0, max: 0.2, step: 0.01 }), _jsx(ColorPicker, { colorRGB01: rulerColor, onSetColor: nvUpdateRulerColor, title: 'Ruler color' }), _jsx(NumberPicker, { value: rulerWidth, onChange: nvUpdateRulerWidth, title: 'Ruler thickness', min: 0, max: 10, step: 1 }), _jsx(NumberPicker, { value: rulerOpacity, onChange: nvUpdateRulerOpacity, title: 'Ruler opacity', min: 0, max: 1, step: 0.1 }), _jsx(NumberPicker, { value: opacity, onChange: nvUpdateOpacity, title: 'Opacity', min: 0, max: 1, step: 0.01 }), _jsx(NumberPicker, { value: drawOpacity, onChange: nvUpdateDrawOpacity, title: 'Draw Opacity', min: 0, max: 1, step: 0.01 }), _jsx("label", __assign({ htmlFor: "drawPen" }, { children: "Draw color:" })), _jsxs("select", __assign({ name: "drawPen", id: "drawPen", onChange: nvUpdateDrawPen, value: drawPen }, { children: [_jsx("option", __assign({ value: "0" }, { children: "Erase" })), _jsx("option", __assign({ value: "1" }, { children: "Red" })), _jsx("option", __assign({ value: "2" }, { children: "Green" })), _jsx("option", __assign({ value: "3" }, { children: "Blue" })), _jsx("option", __assign({ value: "8" }, { children: "Filled Erase" })), _jsx("option", __assign({ value: "9" }, { children: "Filled Red" })), _jsx("option", __assign({ value: "10" }, { children: "Filled Green" })), _jsx("option", __assign({ value: "11" }, { children: "Filled Blue" })), _jsx("option", __assign({ value: "12" }, { children: "Erase Selected Cluster" }))] })), _jsx(Button, __assign({ title: 'Save image', onClick: nvSaveImage }, { children: "Save image" })), _jsx(NVSwitch, { checked: locationTableVisible, title: 'Location table', onChange: toggleLocationTable }), _jsx(NVSwitch, { checked: drawingEnabled, title: 'Enable drawing', onChange: nvUpdateDrawingEnabled }), _jsx(NVSwitch, { checked: orientCube, title: 'Orientation cube', onChange: nvUpdateOrientCube }), _jsx(NVSwitch, { checked: ruler, title: 'Ruler', onChange: nvUpdateRuler }), _jsx(NVSwitch, { checked: clipPlane, title: 'Clip plane', onChange: nvUpdateClipPlane }), _jsx(NVSwitch, { checked: cornerText, title: 'Corner text', onChange: nvUpdateCornerText }), _jsx(NVSwitch, { checked: radiological, title: 'radiological', onChange: nvUpdateRadiological }), _jsx(NVSwitch, { checked: crosshair3D, title: '3D crosshair', onChange: nvUpdateCrosshair3D }), _jsx(NVSwitch, { checked: colorBar, title: 'Show color bar', onChange: nvUpdateColorBar }), _jsx(NVSwitch, { checked: worldSpace, title: 'World space', onChange: nvUpdateWorldSpace }), _jsx(NVSwitch, { checked: sagittalNoseLeft, title: 'Nose left', onChange: nvUpdateSagittalNoseLeft }), _jsx(NVSwitch, { checked: dragToMeasure, title: 'Drag to measure', onChange: nvUpdateDragToMeasure }), _jsx(NVSwitch, { checked: highDPI, title: 'High DPI', onChange: nvUpdateHighDPI }), _jsx(NumberPicker, { value: decimalPrecision, onChange: updateDecimalPrecision, title: 'Decimal precision', min: 0, max: 8, step: 1 }), _jsx(NumberPicker, { value: multiplanarPadPixels, onChange: nvUpdateMultiplanarPadPixels, title: 'Multiplanar padding', min: 0, max: 20, step: 2 }), _jsx(NumberPicker, { value: maxDrawUndoBitmaps, onChange: nvUpdateMaxDrawUndoBitmaps, title: 'Max Draw Undos', min: 8, max: 28, step: 1 }), _jsx(NumberPicker, { value: longTouchTimeout, onChange: nvUpdateLongTouchTimeout, title: 'Long touch timeout msec', min: 1000, max: 5000, step: 100 }), _jsx(NumberPicker, { value: doubleTouchTimeout, onChange: nvUpdateDoubleTouchTimeout, title: 'Double touch timeout msec', min: 500, max: 999, step: 25 })] })), _jsx(LayersPanel, __assign({ open: openLayers, width: 320, onToggleMenu: toggleLayers, onAddLayer: addLayer }, { children: layerList })), _jsx(Toolbar, { nv: nv, nvUpdateSliceType: nvUpdateSliceType, sliceType: sliceType, toggleSettings: toggleSettings, toggleLayers: toggleLayers, volumes: props.niis.map(niiToVolume), selectedVolume: selectedVolume, setSelectedVolume: selectVolume, showColorBar: colorBar, toggleColorBar: nvUpdateColorBar, rois: props.rois, selectedROI: selectedROI, refreshROI: refreshROI, setSelectedROI: selectDrawingLayer, toggleShowCrosshair: nvUpdateCrosshair, showCrosshair: showCrosshair, dragMode: dragMode, setDragMode: nvSetDragMode, toggleRadiological: nvUpdateRadiological, radiological: radiological, saveROI: saveDrawingLayer, complexMode: complexMode, setComplexMode: nvSetDisplayedVoxels, complexOptions: complexOptions, labelsVisible: textsVisible, toggleLabelsVisible: nvToggleLabelVisible, saving: saving, setSaving: setSaving, drawingChanged: drawingChanged, resampleImage: resampleImage, accessToken: props.accessToken, pipelineId: props.pipelineID, roiDeleteUrl: props.roiDeleteUrl, refreshPipelineRois: props.refreshPipelineRois }), _jsx(CmrConfirmation, { name: 'New Changes Made', message: "Consider saving your drawing before switching.", open: confirmationOpen, setOpen: setConfirmationOpen, cancellable: true, confirmCallback: warningConfirmationCallback, cancelCallback: warningCancelCallback, cancelText: "Don't save" }), _jsx(CmrEditConfirmation, { name: 'Save drawings', message: 'Please enter the name of the saved drawing', open: saveDialogOpen, setOpen: setSaveDialogOpen, confirmCallback: saveConfirmCallback, cancellable: true, cancelCallback: function () {
|
|
1778
|
+
},
|
|
1779
|
+
// suffix={'.zip'}
|
|
1780
|
+
defaultText: (props.rois[selectedROI] !== undefined ?
|
|
1781
|
+
props.rois[selectedROI].filename : undefined) }), props.niis[selectedVolume] != undefined && _jsx(CloudMrNiivuePanel, { nv: nv, transformFactors: transformFactors, decimalPrecision: decimalPrecision, locationData: locationData, locationTableVisible: locationTableVisible, pipelineID: props.pipelineID, resampleImage: resampleImage, rois: rois, drawToolkitProps: drawToolkitProps, drawShapeTool: drawShapeTool, setDrawShapeTool: setDrawShapeTool, mins: boundMins, maxs: boundMaxs, mms: mms, min: min, max: max, setMin: setMin, setMax: setMax, unzipAndRenderROI: unpackROI, zipAndSendROI: zipAndSendDrawingLayer, setLabelAlias: setLabelAlias, onAfterRoiUpload: function () {
|
|
1782
|
+
var _a;
|
|
1783
|
+
void ((_a = props.refreshPipelineRois) === null || _a === void 0 ? void 0 : _a.call(props));
|
|
1784
|
+
}, gamma: gamma, gammaKey: gammaKey, setGamma: setGamma, shapeDraft: shapeDraft, onShapeDraftChange: onShapeDraftChange, onApplyShapeDraft: function () { return applyShapeDraft(); }, onApplyShapeDraftKeepTool: function () { return applyShapeDraft({ keepTool: true }); }, onCancelShapeDraft: cancelShapeDraft, penDraft: penDraft, onPenDraftChange: onPenDraftChange, onApplyPenDraft: function () { return applyPenDraftHandler(); }, onApplyPenDraftKeepTool: function () { return applyPenDraftHandler({ keepTool: true }); }, onCancelPenDraft: cancelPenDraftHandler }, "".concat(selectedVolume))] })) })));
|
|
1720
1785
|
}
|
|
1721
1786
|
function niiToVolume(nii) {
|
|
1722
1787
|
return {
|
|
@@ -26,6 +26,7 @@ import KeyboardDoubleArrowUpIcon from '@mui/icons-material/KeyboardDoubleArrowUp
|
|
|
26
26
|
import { NumberPicker } from './NumberPicker';
|
|
27
27
|
import React from 'react';
|
|
28
28
|
import { display } from "@mui/system";
|
|
29
|
+
import { useNiivueViewerTheme } from "./NiivueViewerThemeContext";
|
|
29
30
|
function makeColorGradients(colorMapValues) {
|
|
30
31
|
var gradients = '';
|
|
31
32
|
var c = colorMapValues;
|
|
@@ -41,6 +42,7 @@ function makeColorGradients(colorMapValues) {
|
|
|
41
42
|
}
|
|
42
43
|
export default function Layer(props) {
|
|
43
44
|
var _a;
|
|
45
|
+
var theme = useNiivueViewerTheme();
|
|
44
46
|
var allowedColorMaps = [
|
|
45
47
|
"bone",
|
|
46
48
|
"gray",
|
|
@@ -112,7 +114,7 @@ export default function Layer(props) {
|
|
|
112
114
|
width: '100%',
|
|
113
115
|
alignSelf: 'center',
|
|
114
116
|
margin: '8px 0 16px 0',
|
|
115
|
-
accentColor:
|
|
117
|
+
accentColor: theme.accentColor
|
|
116
118
|
} })] })), _jsx(Box, __assign({ sx: {
|
|
117
119
|
display: 'flex',
|
|
118
120
|
flexDirection: 'row',
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { type NiivueViewerThemeTokens } from "./niivueViewerTheme";
|
|
3
|
+
export interface NiivueViewerThemeProviderProps {
|
|
4
|
+
/**
|
|
5
|
+
* Optional override for viewer accent (sliders, ROI table tints, draw-toolkit, etc.).
|
|
6
|
+
* When omitted: uses host MUI `palette.primary.main` if customized, else package purple.
|
|
7
|
+
*/
|
|
8
|
+
accentColor?: string;
|
|
9
|
+
children: React.ReactNode;
|
|
10
|
+
}
|
|
11
|
+
export declare function NiivueViewerThemeProvider({ accentColor, children, }: NiivueViewerThemeProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
/** Returns the viewer theme from context, or package defaults when outside a provider. */
|
|
13
|
+
export declare function useNiivueViewerTheme(): NiivueViewerThemeTokens;
|
|
14
|
+
export declare function resolveNiivueAccentColor(propAccent: string | undefined, theme: NiivueViewerThemeTokens): string;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
var __assign = (this && this.__assign) || function () {
|
|
2
|
+
__assign = Object.assign || function(t) {
|
|
3
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
+
s = arguments[i];
|
|
5
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
+
t[p] = s[p];
|
|
7
|
+
}
|
|
8
|
+
return t;
|
|
9
|
+
};
|
|
10
|
+
return __assign.apply(this, arguments);
|
|
11
|
+
};
|
|
12
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
13
|
+
import { createContext, useContext, useMemo } from "react";
|
|
14
|
+
import { useTheme } from "@mui/material/styles";
|
|
15
|
+
import { buildNiivueViewerTheme, CLOUDMR_NIIVUE_DEFAULT_ACCENT, resolveViewerAccentColor, } from "./niivueViewerTheme";
|
|
16
|
+
var NiivueViewerThemeContext = createContext(null);
|
|
17
|
+
export function NiivueViewerThemeProvider(_a) {
|
|
18
|
+
var accentColor = _a.accentColor, children = _a.children;
|
|
19
|
+
var muiTheme = useTheme();
|
|
20
|
+
var resolvedAccent = resolveViewerAccentColor(accentColor, muiTheme.palette.primary.main);
|
|
21
|
+
var theme = useMemo(function () { return buildNiivueViewerTheme(resolvedAccent); }, [resolvedAccent]);
|
|
22
|
+
return (_jsx(NiivueViewerThemeContext.Provider, __assign({ value: theme }, { children: children })));
|
|
23
|
+
}
|
|
24
|
+
/** Returns the viewer theme from context, or package defaults when outside a provider. */
|
|
25
|
+
export function useNiivueViewerTheme() {
|
|
26
|
+
var ctx = useContext(NiivueViewerThemeContext);
|
|
27
|
+
return ctx !== null && ctx !== void 0 ? ctx : buildNiivueViewerTheme(CLOUDMR_NIIVUE_DEFAULT_ACCENT);
|
|
28
|
+
}
|
|
29
|
+
export function resolveNiivueAccentColor(propAccent, theme) {
|
|
30
|
+
return propAccent !== null && propAccent !== void 0 ? propAccent : theme.accentColor;
|
|
31
|
+
}
|
|
@@ -22,8 +22,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
22
22
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
23
23
|
import { boundsToFreehandCorners, redrawFreehandDraft, redrawPolylineDraft, syncPolylineDraftToNv, translateFreehandDraft, translatePolylineVertices, updatePolylineVertex, } from "./penDraftUtils";
|
|
24
24
|
import { canvasDeltaToVoxDelta, clientToCanvasPos, voxToOverlayPos, voxUnderClient, } from "./shapeDraftUtils";
|
|
25
|
+
import { useNiivueViewerTheme } from "./NiivueViewerThemeContext";
|
|
25
26
|
var HANDLE_SIZE = 10;
|
|
26
|
-
var ACCENT = "#580f8b";
|
|
27
27
|
function cloneFreehandDraft(draft) {
|
|
28
28
|
return __assign(__assign({}, draft), { strokeVoxels: draft.strokeVoxels.map(function (v) { return __spreadArray([], v, true); }), bounds: draft.bounds ? __assign({}, draft.bounds) : draft.bounds });
|
|
29
29
|
}
|
|
@@ -33,6 +33,7 @@ function cloneFreehandDraft(draft) {
|
|
|
33
33
|
*/
|
|
34
34
|
export function PenDraftOverlay(_a) {
|
|
35
35
|
var nv = _a.nv, draft = _a.draft, onDraftChange = _a.onDraftChange, onApplyDraft = _a.onApplyDraft, overlayKey = _a.overlayKey;
|
|
36
|
+
var theme = useNiivueViewerTheme();
|
|
36
37
|
var dragRef = useRef(null);
|
|
37
38
|
var draftRef = useRef(draft);
|
|
38
39
|
draftRef.current = draft;
|
|
@@ -187,7 +188,7 @@ export function PenDraftOverlay(_a) {
|
|
|
187
188
|
marginTop: -HANDLE_SIZE / 2,
|
|
188
189
|
borderRadius: "50%",
|
|
189
190
|
background: "#fff",
|
|
190
|
-
border: "2px solid ".concat(
|
|
191
|
+
border: "2px solid ".concat(theme.accentColor),
|
|
191
192
|
boxSizing: "border-box",
|
|
192
193
|
cursor: "pointer",
|
|
193
194
|
zIndex: 3,
|
|
@@ -206,8 +207,8 @@ export function PenDraftOverlay(_a) {
|
|
|
206
207
|
top: boxStyle.top,
|
|
207
208
|
width: boxStyle.width,
|
|
208
209
|
height: boxStyle.height,
|
|
209
|
-
border: "2px dashed ".concat(
|
|
210
|
-
background:
|
|
210
|
+
border: "2px dashed ".concat(theme.accentColor),
|
|
211
|
+
background: theme.accentMutedBgLight,
|
|
211
212
|
boxSizing: "border-box",
|
|
212
213
|
pointerEvents: "none"
|
|
213
214
|
} }), centerCss && (_jsx("div", { role: "presentation", onPointerDown: function (e) { return startDrag(e, "move"); }, style: __assign(__assign({}, handleStyle), { left: centerCss.x, top: centerCss.y, cursor: "move", borderRadius: 2, width: 12, height: 12, marginLeft: -6, marginTop: -6 }), title: "Move shape" })), isPolyline &&
|
|
@@ -21,8 +21,8 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
|
21
21
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
22
22
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
23
23
|
import { boundsToCorners, canvasDeltaToVoxDelta, clientToCanvasPos, normalizeBounds, redrawDraftShape, resizeDraftCorner, translatePt, voxToOverlayPos, voxUnderClient, } from "./shapeDraftUtils";
|
|
24
|
+
import { useNiivueViewerTheme } from "./NiivueViewerThemeContext";
|
|
24
25
|
var HANDLE_SIZE = 10;
|
|
25
|
-
var ACCENT = "#580f8b";
|
|
26
26
|
/**
|
|
27
27
|
* Overlay handles for adjusting a rectangle/ellipse draft before commit.
|
|
28
28
|
* @param {{ nv: any, draft: import('./shapeDraftUtils').ShapeDraft, onDraftChange: (d: any) => void, onApplyDraft?: () => void, overlayKey?: unknown }} props
|
|
@@ -30,6 +30,7 @@ var ACCENT = "#580f8b";
|
|
|
30
30
|
export function ShapeDraftOverlay(_a) {
|
|
31
31
|
var _b;
|
|
32
32
|
var nv = _a.nv, draft = _a.draft, onDraftChange = _a.onDraftChange, onApplyDraft = _a.onApplyDraft, overlayKey = _a.overlayKey;
|
|
33
|
+
var theme = useNiivueViewerTheme();
|
|
33
34
|
var dragRef = useRef(null);
|
|
34
35
|
var draftRef = useRef(draft);
|
|
35
36
|
draftRef.current = draft;
|
|
@@ -157,7 +158,7 @@ export function ShapeDraftOverlay(_a) {
|
|
|
157
158
|
marginTop: -HANDLE_SIZE / 2,
|
|
158
159
|
borderRadius: "50%",
|
|
159
160
|
background: "#fff",
|
|
160
|
-
border: "2px solid ".concat(
|
|
161
|
+
border: "2px solid ".concat(theme.accentColor),
|
|
161
162
|
boxSizing: "border-box",
|
|
162
163
|
cursor: "pointer",
|
|
163
164
|
zIndex: 3,
|
|
@@ -176,8 +177,8 @@ export function ShapeDraftOverlay(_a) {
|
|
|
176
177
|
top: boxStyle.top,
|
|
177
178
|
width: boxStyle.width,
|
|
178
179
|
height: boxStyle.height,
|
|
179
|
-
border: "2px dashed ".concat(
|
|
180
|
-
background:
|
|
180
|
+
border: "2px dashed ".concat(theme.accentColor),
|
|
181
|
+
background: theme.accentMutedBgLight,
|
|
181
182
|
boxSizing: "border-box",
|
|
182
183
|
pointerEvents: "none"
|
|
183
184
|
} }), centerCss && (_jsx("div", { role: "presentation", onPointerDown: function (e) { return startDrag(e, "move"); }, style: __assign(__assign({}, handleStyle), { left: centerCss.x, top: centerCss.y, cursor: "move", borderRadius: 2, width: 12, height: 12, marginLeft: -6, marginTop: -6 }), title: "Move shape" })), cornerCss.map(function (pos, i) { return (_jsx("div", { role: "presentation", onPointerDown: function (e) { return startDrag(e, "corner", i); }, style: __assign(__assign({}, handleStyle), { left: pos.x, top: pos.y, cursor: "nwse-resize" }), title: "Resize shape" }, "corner-".concat(i))); })] })));
|
|
@@ -23,5 +23,5 @@ export default function NVSwitch(props) {
|
|
|
23
23
|
alignItems: 'center'
|
|
24
24
|
}, m: 1 }, { children: [_jsx(Typography, __assign({ onClick: handleChange, style: {
|
|
25
25
|
marginRight: 'auto'
|
|
26
|
-
} }, { children: props.title })), _jsx(Switch, { checked: props.checked, onChange: handleChange })] })));
|
|
26
|
+
} }, { children: props.title })), _jsx(Switch, { checked: props.checked, onChange: handleChange, color: "primary" })] })));
|
|
27
27
|
}
|
|
@@ -222,22 +222,22 @@ export default function Toolbar(props) {
|
|
|
222
222
|
alignItems: 'center'
|
|
223
223
|
}, m: 1 }, { children: [_jsx(Typography, __assign({ style: {
|
|
224
224
|
marginRight: 'auto'
|
|
225
|
-
} }, { children: "Neurological" })), _jsx(Switch, { defaultChecked: false, checked: !props.radiological, onChange: props.toggleRadiological })] })), _jsxs(Box, __assign({ sx: {
|
|
225
|
+
} }, { children: "Neurological" })), _jsx(Switch, { defaultChecked: false, checked: !props.radiological, onChange: props.toggleRadiological, color: "primary" })] })), _jsxs(Box, __assign({ sx: {
|
|
226
226
|
display: 'flex',
|
|
227
227
|
alignItems: 'center'
|
|
228
228
|
}, m: 1 }, { children: [_jsx(Typography, __assign({ style: {
|
|
229
229
|
marginRight: 'auto'
|
|
230
|
-
} }, { children: "Show Crosshair" })), _jsx(Switch, { defaultChecked: true, checked: props.showCrosshair, onChange: props.toggleShowCrosshair })] })), _jsxs(Box, __assign({ sx: {
|
|
230
|
+
} }, { children: "Show Crosshair" })), _jsx(Switch, { defaultChecked: true, checked: props.showCrosshair, onChange: props.toggleShowCrosshair, color: "primary" })] })), _jsxs(Box, __assign({ sx: {
|
|
231
231
|
display: 'flex',
|
|
232
232
|
alignItems: 'center'
|
|
233
233
|
}, m: 1 }, { children: [_jsx(Typography, __assign({ style: {
|
|
234
234
|
marginRight: 'auto'
|
|
235
|
-
} }, { children: "Show Color Bar" })), _jsx(Switch, { checked: props.showColorBar, onChange: props.toggleColorBar })] })), _jsxs(Box, __assign({ sx: {
|
|
235
|
+
} }, { children: "Show Color Bar" })), _jsx(Switch, { checked: props.showColorBar, onChange: props.toggleColorBar, color: "primary" })] })), _jsxs(Box, __assign({ sx: {
|
|
236
236
|
display: 'flex',
|
|
237
237
|
alignItems: 'center'
|
|
238
238
|
}, m: 1 }, { children: [_jsx(Typography, __assign({ style: {
|
|
239
239
|
marginRight: 'auto'
|
|
240
|
-
} }, { children: "Labels Visible" })), _jsx(Switch, { defaultChecked: false, checked: props.labelsVisible, onChange: props.toggleLabelsVisible })] })), _jsx(Box, { sx: { flex: 1 } }), _jsxs(Stack, __assign({ flexDirection: 'row', alignItems: 'center', sx: { m: 2, gap: 0.5 } }, { children: [_jsx(Tooltip, __assign({ title: 'Reset Views', placement: 'right' }, { children: _jsx(IconButton, __assign({ onClick: function () { return props.nv.resetScene(); } }, { children: _jsx(HomeIcon, {}) })) })), _jsx(Tooltip, __assign({ title: 'Recenter Views', placement: 'right' }, { children: _jsx(IconButton, __assign({ onClick: function () { return props.nv.recenter(); } }, { children: _jsx(CenterFocusStrongIcon, {}) })) })), _jsx(Tooltip, __assign({ title: 'Reset Zooms', placement: 'right' }, { children: _jsx(IconButton, __assign({ onClick: function () { return props.nv.resetZoom(); } }, { children: _jsx(ZoomInMapIcon, {}) })) })), _jsx(Tooltip, __assign({ title: 'Reset Contrast', placement: 'right' }, { children: _jsx(IconButton, __assign({ onClick: function () {
|
|
240
|
+
} }, { children: "Labels Visible" })), _jsx(Switch, { defaultChecked: false, checked: props.labelsVisible, onChange: props.toggleLabelsVisible, color: "primary" })] })), _jsx(Box, { sx: { flex: 1 } }), _jsxs(Stack, __assign({ flexDirection: 'row', alignItems: 'center', sx: { m: 2, gap: 0.5 } }, { children: [_jsx(Tooltip, __assign({ title: 'Reset Views', placement: 'right' }, { children: _jsx(IconButton, __assign({ onClick: function () { return props.nv.resetScene(); } }, { children: _jsx(HomeIcon, {}) })) })), _jsx(Tooltip, __assign({ title: 'Recenter Views', placement: 'right' }, { children: _jsx(IconButton, __assign({ onClick: function () { return props.nv.recenter(); } }, { children: _jsx(CenterFocusStrongIcon, {}) })) })), _jsx(Tooltip, __assign({ title: 'Reset Zooms', placement: 'right' }, { children: _jsx(IconButton, __assign({ onClick: function () { return props.nv.resetZoom(); } }, { children: _jsx(ZoomInMapIcon, {}) })) })), _jsx(Tooltip, __assign({ title: 'Reset Contrast', placement: 'right' }, { children: _jsx(IconButton, __assign({ onClick: function () {
|
|
241
241
|
var _a, _b;
|
|
242
242
|
props.nv.resetContrast();
|
|
243
243
|
props.nv.setGamma(1.0); // engine reset
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export default function DrawColorPlatte({ expanded, updateDrawPen, setDrawingEnabled, showPenModes, penDrawMode, onPenDrawModeChange, polylineVertexCount, penDraftActive, penDraftKind, penDraftFilled, onApplyPenDraft, onFillPenDraft, brushSize, updateBrushSize, shapeDraftActive, onApplyShapeDraft, }: {
|
|
1
|
+
export default function DrawColorPlatte({ expanded, updateDrawPen, setDrawingEnabled, showPenModes, penDrawMode, onPenDrawModeChange, polylineVertexCount, penDraftActive, penDraftKind, penDraftFilled, onApplyPenDraft, onDeletePenDraft, onFillPenDraft, brushSize, updateBrushSize, shapeDraftActive, onApplyShapeDraft, onDeleteShapeDraft, }: {
|
|
2
2
|
expanded: any;
|
|
3
3
|
updateDrawPen: any;
|
|
4
4
|
setDrawingEnabled: any;
|
|
@@ -10,9 +10,11 @@ export default function DrawColorPlatte({ expanded, updateDrawPen, setDrawingEna
|
|
|
10
10
|
penDraftKind: any;
|
|
11
11
|
penDraftFilled?: boolean | undefined;
|
|
12
12
|
onApplyPenDraft: any;
|
|
13
|
+
onDeletePenDraft: any;
|
|
13
14
|
onFillPenDraft: any;
|
|
14
15
|
brushSize?: number | undefined;
|
|
15
16
|
updateBrushSize: any;
|
|
16
17
|
shapeDraftActive?: boolean | undefined;
|
|
17
18
|
onApplyShapeDraft: any;
|
|
19
|
+
onDeleteShapeDraft: any;
|
|
18
20
|
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -16,6 +16,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
16
16
|
import { Stack, IconButton, Button, Tooltip, Typography } from "@mui/material";
|
|
17
17
|
import FiberManualRecordIcon from "@mui/icons-material/FiberManualRecord";
|
|
18
18
|
import CheckIcon from "@mui/icons-material/Check";
|
|
19
|
+
import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";
|
|
19
20
|
import { BrushSizeSlider } from "./BrushSizeSlider";
|
|
20
21
|
var FILLED_COLORS = [
|
|
21
22
|
{ sx: { color: "red" } },
|
|
@@ -36,7 +37,7 @@ var modeBtnSx = function (active) { return ({
|
|
|
36
37
|
px: 0.75
|
|
37
38
|
}); };
|
|
38
39
|
export default function DrawColorPlatte(_a) {
|
|
39
|
-
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, 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;
|
|
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;
|
|
40
41
|
return (_jsxs(Stack, __assign({ style: {
|
|
41
42
|
position: "absolute",
|
|
42
43
|
top: "100%",
|
|
@@ -56,30 +57,38 @@ export default function DrawColorPlatte(_a) {
|
|
|
56
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 &&
|
|
57
58
|
penDraftActive &&
|
|
58
59
|
(penDrawMode === "polyline" || penDraftKind === "freehand") &&
|
|
59
|
-
expanded && (_jsxs(Stack, __assign({ direction: "row", alignItems: "center", justifyContent: "
|
|
60
|
-
|
|
61
|
-
: "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: {
|
|
62
|
-
color: penDraftFilled ? "#ffb74d" : "#c9a0e8",
|
|
63
|
-
fontSize: ACTION_FONT_SIZE,
|
|
64
|
-
textTransform: "none",
|
|
65
|
-
minWidth: 0,
|
|
66
|
-
py: 0.25,
|
|
67
|
-
px: 0.75
|
|
68
|
-
} }, { children: penDraftFilled ? "Undo Fill" : "Fill" })) }))), _jsx(Tooltip, __assign({ title: "Apply shape (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: {
|
|
69
|
-
color: "#c9a0e8",
|
|
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: {
|
|
61
|
+
color: "#f44336",
|
|
70
62
|
fontSize: ACTION_FONT_SIZE,
|
|
71
63
|
textTransform: "none",
|
|
72
64
|
minWidth: 0,
|
|
73
65
|
py: 0.25,
|
|
74
66
|
px: 0.75,
|
|
75
67
|
"& .MuiButton-startIcon": { mr: 0.5, ml: 0 }
|
|
76
|
-
} }, { children: "
|
|
77
|
-
|
|
68
|
+
} }, { children: "Delete" })) })), _jsxs(Stack, __assign({ direction: "row", spacing: 1, alignItems: "center" }, { children: [penDrawMode === "polyline" && polylineVertexCount >= 3 && (_jsx(Tooltip, __assign({ title: penDraftFilled
|
|
69
|
+
? "Remove fill (keeps outline editable)"
|
|
70
|
+
: "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
|
+
color: penDraftFilled ? "#ffb74d" : "#c9a0e8",
|
|
72
|
+
fontSize: ACTION_FONT_SIZE,
|
|
73
|
+
textTransform: "none",
|
|
74
|
+
minWidth: 0,
|
|
75
|
+
py: 0.25,
|
|
76
|
+
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: {
|
|
78
|
+
color: "#c9a0e8",
|
|
79
|
+
fontSize: ACTION_FONT_SIZE,
|
|
80
|
+
textTransform: "none",
|
|
81
|
+
minWidth: 0,
|
|
82
|
+
py: 0.25,
|
|
83
|
+
px: 0.75,
|
|
84
|
+
"& .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: {
|
|
86
|
+
color: "#f44336",
|
|
78
87
|
fontSize: ACTION_FONT_SIZE,
|
|
79
88
|
textTransform: "none",
|
|
80
89
|
minWidth: 0,
|
|
81
90
|
py: 0.25,
|
|
82
91
|
px: 0.75,
|
|
83
92
|
"& .MuiButton-startIcon": { mr: 0.5, ml: 0 }
|
|
84
|
-
} }, { children: "
|
|
93
|
+
} }, { children: "Delete" })) })) })))] })));
|
|
85
94
|
}
|
|
@@ -35,6 +35,7 @@ import ClickAwayListener from "@mui/material/ClickAwayListener";
|
|
|
35
35
|
import DrawColorPlatte from "./DrawColorPlatte";
|
|
36
36
|
import EraserPlatte from "./EraserPlatte";
|
|
37
37
|
import MaskPlatte from "./MaskPlatte";
|
|
38
|
+
import { useNiivueViewerTheme } from "../NiivueViewerThemeContext";
|
|
38
39
|
/** True if the event target is the main NiiVue canvas (drawing surface). Clicks there must not exit draw mode. */
|
|
39
40
|
function clickTargetIsNiivueCanvas(target) {
|
|
40
41
|
if (typeof document === "undefined" || !(target instanceof Element))
|
|
@@ -61,10 +62,11 @@ function OpacityPlatte(_a) {
|
|
|
61
62
|
borderTopRightRadius: "6pt",
|
|
62
63
|
background: "#333",
|
|
63
64
|
width: 150
|
|
64
|
-
}, direction: "column" }, { children: _jsxs(Stack, __assign({ sx: { mb: 1 }, alignItems: "center" }, { children: [_jsxs(Typography, __assign({ color: "white", noWrap: true, gutterBottom: true, width: "100%", marginLeft: "10pt", fontSize: "11pt", alignItems: "start" }, { children: ["Opacity: ", drawingOpacity] })), _jsx(Slider, { sx: { width: "80%" }, value: drawingOpacity, step: 0.01, min: 0, max: 1, onChange: function (_e, value) { return setDrawingOpacity(value); } })] })) })));
|
|
65
|
+
}, direction: "column" }, { children: _jsxs(Stack, __assign({ sx: { mb: 1 }, alignItems: "center" }, { children: [_jsxs(Typography, __assign({ color: "white", noWrap: true, gutterBottom: true, width: "100%", marginLeft: "10pt", fontSize: "11pt", alignItems: "start" }, { children: ["Opacity: ", drawingOpacity] })), _jsx(Slider, { color: "primary", sx: { width: "80%" }, value: drawingOpacity, step: 0.01, min: 0, max: 1, onChange: function (_e, value) { return setDrawingOpacity(value); } })] })) })));
|
|
65
66
|
}
|
|
66
67
|
export function MroDrawToolkit(props) {
|
|
67
68
|
var _a;
|
|
69
|
+
var theme = useNiivueViewerTheme();
|
|
68
70
|
var drawShapeTool = props.drawShapeTool, onDrawShapeToolChange = props.onDrawShapeToolChange;
|
|
69
71
|
var _b = useState("n"), expandedOption = _b[0], setExpandedOption = _b[1];
|
|
70
72
|
var _c = useState(false), expandOpacityOptions = _c[0], setExpandOpacityOptions = _c[1];
|
|
@@ -86,9 +88,7 @@ export function MroDrawToolkit(props) {
|
|
|
86
88
|
setExpandedOption("d");
|
|
87
89
|
}, [props.shapeDraftActive, props.penDraftActive, drawShapeTool]);
|
|
88
90
|
var shapeSelectedSx = function (shape) {
|
|
89
|
-
return drawShapeTool === shape
|
|
90
|
-
? { backgroundColor: "rgba(88, 15, 139, 0.12)", color: "#580f8b" }
|
|
91
|
-
: {};
|
|
91
|
+
return drawShapeTool === shape ? theme.selectedToolSx : {};
|
|
92
92
|
};
|
|
93
93
|
var eraserActive = expandedOption === "e";
|
|
94
94
|
function leaveEraserIfActive() {
|
|
@@ -189,9 +189,7 @@ export function MroDrawToolkit(props) {
|
|
|
189
189
|
alignItems: "center",
|
|
190
190
|
gap: 4,
|
|
191
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, 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 })] })), _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 })] })), _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
|
|
193
|
-
? { backgroundColor: "rgba(88, 15, 139, 0.12)", color: "#580f8b" }
|
|
194
|
-
: {})) }, { children: filled || !eraserActive ? (_jsx(EraserIcon, {})) : (_jsx(AutoFixNormalOutlinedIcon, { sx: { color: ICON_COLOR } })) })) })), _jsx(EraserPlatte, { expandEraseOptions: expandedOption === "e", updateDrawPen: props.updateDrawPen, setDrawingEnabled: props.setDrawingEnabled, brushSize: props.brushSize, updateBrushSize: props.updateBrushSize })] })), _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 () {
|
|
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, brushSize: props.brushSize, updateBrushSize: props.updateBrushSize })] })), _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 () {
|
|
195
193
|
var _a;
|
|
196
194
|
(_a = props.onClearDrawing) === null || _a === void 0 ? void 0 : _a.call(props);
|
|
197
195
|
setExpandedOption("n");
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { CSSProperties } from "react";
|
|
2
|
+
/** Default MROptimum / package purple accent for the Niivue viewer. */
|
|
3
|
+
export declare const CLOUDMR_NIIVUE_DEFAULT_ACCENT = "#580f8b";
|
|
4
|
+
/**
|
|
5
|
+
* Resolve Niivue viewer accent color:
|
|
6
|
+
* 1. Explicit `accentColor` prop on {@link CloudMrNiivueViewer}
|
|
7
|
+
* 2. Host MUI `palette.primary.main` when it differs from stock MUI primary
|
|
8
|
+
* (e.g. CAMRIE `#1578A1`, MROptimum `#580f8b`)
|
|
9
|
+
* 3. Package default purple {@link CLOUDMR_NIIVUE_DEFAULT_ACCENT}
|
|
10
|
+
*/
|
|
11
|
+
export declare function resolveViewerAccentColor(accentColorProp?: string, muiPrimaryMain?: string): string;
|
|
12
|
+
export interface NiivueViewerThemeTokens {
|
|
13
|
+
accentColor: string;
|
|
14
|
+
accentMutedBg: string;
|
|
15
|
+
accentMutedBgLight: string;
|
|
16
|
+
headerBgColor: string;
|
|
17
|
+
headerIconColor: string;
|
|
18
|
+
checkboxCheckedColor: string;
|
|
19
|
+
checkboxUncheckedColor: string;
|
|
20
|
+
selectedToolSx: {
|
|
21
|
+
backgroundColor: string;
|
|
22
|
+
color: string;
|
|
23
|
+
};
|
|
24
|
+
muiSwitchSx: Record<string, unknown>;
|
|
25
|
+
muiSliderSx: Record<string, unknown>;
|
|
26
|
+
cssVars: CSSProperties;
|
|
27
|
+
}
|
|
28
|
+
export declare function hexToRgb(hex: string): {
|
|
29
|
+
r: number;
|
|
30
|
+
g: number;
|
|
31
|
+
b: number;
|
|
32
|
+
};
|
|
33
|
+
export declare function buildNiivueViewerTheme(accentColor: string): NiivueViewerThemeTokens;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { createTheme } from "@mui/material/styles";
|
|
2
|
+
/** Default MROptimum / package purple accent for the Niivue viewer. */
|
|
3
|
+
export var CLOUDMR_NIIVUE_DEFAULT_ACCENT = "#580f8b";
|
|
4
|
+
/** MUI stock `palette.primary.main` when the host does not customize primary. */
|
|
5
|
+
var muiDefaultPrimaryMain;
|
|
6
|
+
function getMuiDefaultPrimaryMain() {
|
|
7
|
+
if (!muiDefaultPrimaryMain) {
|
|
8
|
+
muiDefaultPrimaryMain = createTheme().palette.primary.main;
|
|
9
|
+
}
|
|
10
|
+
return muiDefaultPrimaryMain;
|
|
11
|
+
}
|
|
12
|
+
function normalizeHex(color) {
|
|
13
|
+
return color.trim().toLowerCase();
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Resolve Niivue viewer accent color:
|
|
17
|
+
* 1. Explicit `accentColor` prop on {@link CloudMrNiivueViewer}
|
|
18
|
+
* 2. Host MUI `palette.primary.main` when it differs from stock MUI primary
|
|
19
|
+
* (e.g. CAMRIE `#1578A1`, MROptimum `#580f8b`)
|
|
20
|
+
* 3. Package default purple {@link CLOUDMR_NIIVUE_DEFAULT_ACCENT}
|
|
21
|
+
*/
|
|
22
|
+
export function resolveViewerAccentColor(accentColorProp, muiPrimaryMain) {
|
|
23
|
+
if (accentColorProp) {
|
|
24
|
+
return accentColorProp;
|
|
25
|
+
}
|
|
26
|
+
var primary = muiPrimaryMain === null || muiPrimaryMain === void 0 ? void 0 : muiPrimaryMain.trim();
|
|
27
|
+
if (primary &&
|
|
28
|
+
normalizeHex(primary) !== normalizeHex(getMuiDefaultPrimaryMain())) {
|
|
29
|
+
return primary;
|
|
30
|
+
}
|
|
31
|
+
return CLOUDMR_NIIVUE_DEFAULT_ACCENT;
|
|
32
|
+
}
|
|
33
|
+
export function hexToRgb(hex) {
|
|
34
|
+
var h = hex.replace("#", "");
|
|
35
|
+
var full = h.length === 3 ? h.split("").map(function (c) { return c + c; }).join("") : h.slice(0, 6);
|
|
36
|
+
var n = parseInt(full, 16);
|
|
37
|
+
if (Number.isNaN(n)) {
|
|
38
|
+
return hexToRgb(CLOUDMR_NIIVUE_DEFAULT_ACCENT);
|
|
39
|
+
}
|
|
40
|
+
return { r: (n >> 16) & 255, g: (n >> 8) & 255, b: n & 255 };
|
|
41
|
+
}
|
|
42
|
+
/** Light header background tinted from accent (~8% accent, 92% white). */
|
|
43
|
+
function accentToHeaderBg(r, g, b) {
|
|
44
|
+
var mix = function (c) {
|
|
45
|
+
return Math.round(c * 0.08 + 255 * 0.92)
|
|
46
|
+
.toString(16)
|
|
47
|
+
.padStart(2, "0");
|
|
48
|
+
};
|
|
49
|
+
return "#".concat(mix(r)).concat(mix(g)).concat(mix(b));
|
|
50
|
+
}
|
|
51
|
+
export function buildNiivueViewerTheme(accentColor) {
|
|
52
|
+
var _a;
|
|
53
|
+
var _b = hexToRgb(accentColor), r = _b.r, g = _b.g, b = _b.b;
|
|
54
|
+
var accentMutedBg = "rgba(".concat(r, ", ").concat(g, ", ").concat(b, ", 0.12)");
|
|
55
|
+
var accentMutedBgLight = "rgba(".concat(r, ", ").concat(g, ", ").concat(b, ", 0.08)");
|
|
56
|
+
var checkboxUncheckedColor = "rgba(".concat(r, ", ").concat(g, ", ").concat(b, ", 0.54)");
|
|
57
|
+
return {
|
|
58
|
+
accentColor: accentColor,
|
|
59
|
+
accentMutedBg: accentMutedBg,
|
|
60
|
+
accentMutedBgLight: accentMutedBgLight,
|
|
61
|
+
headerBgColor: accentToHeaderBg(r, g, b),
|
|
62
|
+
headerIconColor: accentColor,
|
|
63
|
+
checkboxCheckedColor: accentColor,
|
|
64
|
+
checkboxUncheckedColor: checkboxUncheckedColor,
|
|
65
|
+
selectedToolSx: { backgroundColor: accentMutedBg, color: accentColor },
|
|
66
|
+
muiSwitchSx: {
|
|
67
|
+
"& .MuiSwitch-switchBase.Mui-checked": {
|
|
68
|
+
color: accentColor,
|
|
69
|
+
"&:hover": { backgroundColor: accentMutedBgLight },
|
|
70
|
+
"& + .MuiSwitch-track": { backgroundColor: accentColor, opacity: 1 }
|
|
71
|
+
},
|
|
72
|
+
"& .MuiSwitch-switchBase.Mui-checked.Mui-disabled": {
|
|
73
|
+
color: checkboxUncheckedColor,
|
|
74
|
+
"& + .MuiSwitch-track": {
|
|
75
|
+
backgroundColor: checkboxUncheckedColor,
|
|
76
|
+
opacity: 0.5
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
muiSliderSx: {
|
|
81
|
+
color: accentColor,
|
|
82
|
+
"& .MuiSlider-thumb": { backgroundColor: accentColor },
|
|
83
|
+
"& .MuiSlider-track": { backgroundColor: accentColor }
|
|
84
|
+
},
|
|
85
|
+
cssVars: (_a = {},
|
|
86
|
+
_a["--tkdr-accent"] = accentColor,
|
|
87
|
+
_a["--cmr-niivue-accent"] = accentColor,
|
|
88
|
+
_a)
|
|
89
|
+
};
|
|
90
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -35,6 +35,9 @@ export { NiivueRoiTable, DEFAULT_ROI_GROUP_TARGET_LABEL, } from "./CmrComponents
|
|
|
35
35
|
export type { NiivueRoiTableProps } from "./CmrComponents/niivue-roi-table/NiivueRoiTable";
|
|
36
36
|
export { default as CloudMrNiivueViewer, nv } from "./CmrComponents/niivue-viewer/CloudMrNiivueViewer.js";
|
|
37
37
|
export { CloudMrNiivuePanel } from "./CmrComponents/niivue-viewer/CloudMrNiivuePanel";
|
|
38
|
+
export { NiivueViewerThemeProvider, useNiivueViewerTheme, } from "./CmrComponents/niivue-viewer/NiivueViewerThemeContext";
|
|
39
|
+
export { CLOUDMR_NIIVUE_DEFAULT_ACCENT, buildNiivueViewerTheme, resolveViewerAccentColor, } from "./CmrComponents/niivue-viewer/niivueViewerTheme";
|
|
40
|
+
export type { NiivueViewerThemeTokens } from "./CmrComponents/niivue-viewer/niivueViewerTheme";
|
|
38
41
|
export type { CloudMrNiivuePanelProps, CloudMrDrawToolkitProps, } from "./CmrComponents/niivue-viewer/CloudMrNiivuePanel";
|
|
39
42
|
import type { FC } from "react";
|
|
40
43
|
import type { CmrTableProps } from "./CmrTable/CmrTable";
|
package/dist/index.js
CHANGED
|
@@ -31,6 +31,8 @@ export { useNiivueRoiHistogram } from "./CmrComponents/niivue-roi-histogram/useN
|
|
|
31
31
|
export { NiivueRoiTable, DEFAULT_ROI_GROUP_TARGET_LABEL, } from "./CmrComponents/niivue-roi-table/NiivueRoiTable";
|
|
32
32
|
export { default as CloudMrNiivueViewer, nv } from "./CmrComponents/niivue-viewer/CloudMrNiivueViewer.js";
|
|
33
33
|
export { CloudMrNiivuePanel } from "./CmrComponents/niivue-viewer/CloudMrNiivuePanel";
|
|
34
|
+
export { NiivueViewerThemeProvider, useNiivueViewerTheme, } from "./CmrComponents/niivue-viewer/NiivueViewerThemeContext";
|
|
35
|
+
export { CLOUDMR_NIIVUE_DEFAULT_ACCENT, buildNiivueViewerTheme, resolveViewerAccentColor, } from "./CmrComponents/niivue-viewer/niivueViewerTheme";
|
|
34
36
|
import CmrTableComponent, { CMR_TABLE_DEFAULT_HEADER_ICON } from "./CmrTable/CmrTable";
|
|
35
37
|
export var CmrTable = CmrTableComponent;
|
|
36
38
|
export { CMR_TABLE_DEFAULT_HEADER_ICON };
|