cloudmr-ux 4.8.9 → 5.0.0

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.
@@ -10,7 +10,8 @@ var __assign = (this && this.__assign) || function () {
10
10
  return __assign.apply(this, arguments);
11
11
  };
12
12
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
- import { Card, CardContent, Box } from "@mui/material";
13
+ import { Box, Button, Card, CardContent } from "@mui/material";
14
+ import Brightness6Icon from "@mui/icons-material/Brightness6";
14
15
  import TKDualRange from "../tk-dualrange/TKDualRange";
15
16
  import CmrLabel from "../label/Label";
16
17
  import { resolveNiivueAccentColor, useNiivueViewerTheme, } from "../niivue-viewer/NiivueViewerThemeContext";
@@ -52,6 +53,43 @@ export function NiivueContrastAdjustments(_a) {
52
53
  var theme = useNiivueViewerTheme();
53
54
  var accentColor = resolveNiivueAccentColor(accentColorProp, theme);
54
55
  var a = transformFactors.a, b = transformFactors.b;
56
+ var handleResetContrast = function () {
57
+ var _a, _b;
58
+ nv.resetContrast();
59
+ nv.setGamma(1.0);
60
+ (_a = nv.onResetGamma) === null || _a === void 0 ? void 0 : _a.call(nv);
61
+ var vol = (_b = nv.volumes) === null || _b === void 0 ? void 0 : _b[0];
62
+ if (vol) {
63
+ setMin(vol.cal_min);
64
+ setMax(vol.cal_max);
65
+ }
66
+ };
67
+ var resetContrastButtonSx = {
68
+ color: "rgba(0, 0, 0, 0.87)",
69
+ textTransform: "none",
70
+ fontSize: "0.78rem",
71
+ py: 0,
72
+ minWidth: 0,
73
+ background: "none",
74
+ "& .MuiButton-startIcon": {
75
+ color: "inherit"
76
+ },
77
+ "&:hover": {
78
+ color: accentColor,
79
+ background: "none"
80
+ },
81
+ "&:active": {
82
+ color: accentColor,
83
+ opacity: 0.8,
84
+ background: "none"
85
+ },
86
+ "&:focus": {
87
+ background: "none"
88
+ },
89
+ "&.MuiButtonBase-root:hover": {
90
+ backgroundColor: "transparent"
91
+ }
92
+ };
55
93
  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) {
56
94
  var v = nv.volumes[0];
57
95
  if (!v)
@@ -72,6 +110,6 @@ export function NiivueContrastAdjustments(_a) {
72
110
  var val = Number(e.target.value);
73
111
  setGamma(val);
74
112
  nv.setGamma(val);
75
- }, style: { width: "100%", accentColor: accentColor } }, gammaKey)] })), layerList.length > 0 && (_jsx(Box, __assign({ style: { height: "100%" } }, { children: layerList })))] })) })) }))] })));
113
+ }, style: { width: "100%", accentColor: accentColor } }, gammaKey)] })), _jsx(Box, __assign({ sx: { display: "flex", justifyContent: "flex-end", mt: 0.5, mb: 1 } }, { children: _jsx(Button, __assign({ size: "small", disableRipple: true, startIcon: _jsx(Brightness6Icon, { fontSize: "small" }), onClick: handleResetContrast, sx: resetContrastButtonSx }, { children: "Reset Contrast" })) })), layerList.length > 0 && (_jsx(Box, __assign({ style: { height: "100%" } }, { children: layerList })))] })) })) }))] })));
76
114
  }
77
115
  export default NiivueContrastAdjustments;
@@ -46,8 +46,8 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
46
46
  }
47
47
  };
48
48
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
49
- import { Fragment, useState } from 'react';
50
- import { Box, Button, CircularProgress, Stack, Switch, Tooltip, Typography } from "@mui/material";
49
+ import React, { Fragment, useState } from 'react';
50
+ import { Box, Button, CircularProgress, Stack, Switch, TextField, Tooltip, Typography } from "@mui/material";
51
51
  import { IconButton, FormControl, Select, MenuItem, InputLabel } from "@mui/material";
52
52
  import SettingsIcon from '@mui/icons-material/Settings';
53
53
  import HomeIcon from '@mui/icons-material/Home';
@@ -55,7 +55,6 @@ import CenterFocusStrongIcon from '@mui/icons-material/CenterFocusStrong';
55
55
  import ZoomInMapIcon from '@mui/icons-material/ZoomInMap';
56
56
  import ZoomInIcon from '@mui/icons-material/ZoomIn';
57
57
  import ZoomOutIcon from '@mui/icons-material/ZoomOut';
58
- import Brightness6Icon from '@mui/icons-material/Brightness6';
59
58
  import DeleteIcon from "@mui/icons-material/Delete";
60
59
  import CmrConfirmation from "../dialogue/Confirmation";
61
60
  import axios from "axios";
@@ -78,9 +77,65 @@ export default function Toolbar(props) {
78
77
  { value: "none", label: "Slice and None" }
79
78
  ];
80
79
  var displayModes = ["absolute", "real", "imaginary", "phase"];
81
- var _a = useState(false), roiDeleteOpen = _a[0], setRoiDeleteOpen = _a[1];
82
- var _b = useState(undefined), roiDeleteMsg = _b[0], setRoiDeleteMsg = _b[1];
83
- var _c = useState(function () { return function () { }; }), roiDeleteConfirm = _c[0], setRoiDeleteConfirm = _c[1];
80
+ var _a = useState(''), zoomText = _a[0], setZoomText = _a[1];
81
+ var zoomEditingRef = React.useRef(false);
82
+ /** Compute the base pixels-per-mm at zoom=1.0, reading live canvas + volume state. */
83
+ var getBasePxPerMm = function () {
84
+ var _a;
85
+ var nv = props.nv;
86
+ var canvas = (_a = nv === null || nv === void 0 ? void 0 : nv.gl) === null || _a === void 0 ? void 0 : _a.canvas;
87
+ var back = nv === null || nv === void 0 ? void 0 : nv.back;
88
+ if (!canvas || !(back === null || back === void 0 ? void 0 : back.dims) || !(back === null || back === void 0 ? void 0 : back.pixDims))
89
+ return null;
90
+ var imgWmm = back.dims[1] * Math.abs(back.pixDims[1]);
91
+ var imgHmm = back.dims[2] * Math.abs(back.pixDims[2]);
92
+ if (imgWmm <= 0 || imgHmm <= 0)
93
+ return null;
94
+ var w = canvas.clientWidth || canvas.width;
95
+ var h = canvas.clientHeight || canvas.height;
96
+ return Math.min(w / imgWmm, h / imgHmm);
97
+ };
98
+ /** Returns the display value (number only, no unit suffix). */
99
+ var formatPxPerMm = function () {
100
+ var _a, _b, _c;
101
+ var zoom = (_c = (_b = (_a = props.nv) === null || _a === void 0 ? void 0 : _a.scene) === null || _b === void 0 ? void 0 : _b.pan2Dxyzmm) === null || _c === void 0 ? void 0 : _c[3];
102
+ if (zoom == null)
103
+ return '';
104
+ var base = getBasePxPerMm();
105
+ if (base != null && base > 0)
106
+ return (base * zoom).toFixed(2);
107
+ return (zoom * 100).toFixed(0);
108
+ };
109
+ React.useEffect(function () {
110
+ var rafId;
111
+ var sync = function () {
112
+ if (!zoomEditingRef.current)
113
+ setZoomText(formatPxPerMm());
114
+ rafId = requestAnimationFrame(sync);
115
+ };
116
+ rafId = requestAnimationFrame(sync);
117
+ return function () { return cancelAnimationFrame(rafId); };
118
+ }, [props.nv]);
119
+ var applyZoom = function (text) {
120
+ var num = parseFloat(text.trim());
121
+ if (isNaN(num) || num <= 0)
122
+ return;
123
+ var base = getBasePxPerMm();
124
+ var targetZoom = (base != null && base > 0) ? num / base : num / 100;
125
+ var scene = props.nv.scene;
126
+ var current = scene.pan2Dxyzmm[3];
127
+ var next = Math.max(0.01, targetZoom);
128
+ var delta = current - next;
129
+ scene.pan2Dxyzmm[3] = next;
130
+ var mm = props.nv.frac2mm(scene.crosshairPos);
131
+ scene.pan2Dxyzmm[0] += delta * mm[0];
132
+ scene.pan2Dxyzmm[1] += delta * mm[1];
133
+ scene.pan2Dxyzmm[2] += delta * mm[2];
134
+ props.nv.drawScene();
135
+ };
136
+ var _b = useState(false), roiDeleteOpen = _b[0], setRoiDeleteOpen = _b[1];
137
+ var _c = useState(undefined), roiDeleteMsg = _c[0], setRoiDeleteMsg = _c[1];
138
+ var _d = useState(function () { return function () { }; }), roiDeleteConfirm = _d[0], setRoiDeleteConfirm = _d[1];
84
139
  // const deleteROI= createAsyncThunk('DeleteROI', async (arg: { accessToken: string, jobId: string }) => {
85
140
  // // const data = { jobId: arg.jobId }; // No need to stringify, axios will handle it
86
141
  // const config = {
@@ -250,12 +305,7 @@ export default function Toolbar(props) {
250
305
  alignItems: 'center'
251
306
  }, m: 1 }, { children: [_jsx(Typography, __assign({ style: {
252
307
  marginRight: 'auto'
253
- } }, { children: "Labels Visible" })), _jsx(Switch, { defaultChecked: false, checked: props.labelsVisible, onChange: props.toggleLabelsVisible, sx: viewerTheme.muiSwitchSx })] })), _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 () {
254
- var _a, _b;
255
- props.nv.resetContrast();
256
- props.nv.setGamma(1.0); // engine reset
257
- (_b = (_a = props.nv).onResetGamma) === null || _b === void 0 ? void 0 : _b.call(_a); // UI reset: bumps gammaKey + sets gamma=1.0
258
- } }, { children: _jsx(Brightness6Icon, {}) })) })), _jsx(Tooltip, __assign({ title: 'Zoom Out', placement: 'right' }, { children: _jsx(IconButton, __assign({ onClick: function () {
308
+ } }, { children: "Labels Visible" })), _jsx(Switch, { defaultChecked: false, checked: props.labelsVisible, onChange: props.toggleLabelsVisible, sx: viewerTheme.muiSwitchSx })] })), _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: 'Zoom Out', placement: 'right' }, { children: _jsx(IconButton, __assign({ onClick: function () {
259
309
  var scene = props.nv.scene;
260
310
  var current = scene.pan2Dxyzmm[3];
261
311
  var next = Math.max(0.1, current - 0.1);
@@ -270,7 +320,28 @@ export default function Toolbar(props) {
270
320
  border: '1px solid',
271
321
  borderColor: 'divider',
272
322
  borderRadius: 1
273
- } }, { children: _jsx(ZoomOutIcon, {}) })) })), _jsx(Tooltip, __assign({ title: 'Zoom In', placement: 'right' }, { children: _jsx(IconButton, __assign({ onClick: function () {
323
+ } }, { children: _jsx(ZoomOutIcon, {}) })) })), _jsx(TextField, { label: "px/mm", size: "small", value: zoomText, onChange: function (e) { return setZoomText(e.target.value); }, onFocus: function (e) {
324
+ zoomEditingRef.current = true;
325
+ requestAnimationFrame(function () { return e.target.select(); });
326
+ }, onBlur: function () {
327
+ zoomEditingRef.current = false;
328
+ applyZoom(zoomText);
329
+ }, onKeyDown: function (e) {
330
+ if (e.key === 'Enter') {
331
+ e.target.blur();
332
+ }
333
+ else if (e.key === 'Escape') {
334
+ zoomEditingRef.current = false;
335
+ setZoomText(formatPxPerMm());
336
+ e.target.blur();
337
+ }
338
+ }, sx: {
339
+ width: 100,
340
+ '& .MuiInputBase-input': {
341
+ textAlign: 'center',
342
+ fontSize: '0.8rem'
343
+ }
344
+ } }), _jsx(Tooltip, __assign({ title: 'Zoom In', placement: 'right' }, { children: _jsx(IconButton, __assign({ onClick: function () {
274
345
  var scene = props.nv.scene;
275
346
  var current = scene.pan2Dxyzmm[3];
276
347
  var next = current + 0.1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloudmr-ux",
3
- "version": "4.8.9",
3
+ "version": "5.0.0",
4
4
  "author": "erosmontin@gmail.com",
5
5
  "license": "MIT",
6
6
  "repository": "erosmontin/cloudmr-ux",