cloudmr-ux 4.8.9 → 4.9.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.
@@ -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';
@@ -78,9 +78,65 @@ export default function Toolbar(props) {
78
78
  { value: "none", label: "Slice and None" }
79
79
  ];
80
80
  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];
81
+ var _a = useState(''), zoomText = _a[0], setZoomText = _a[1];
82
+ var zoomEditingRef = React.useRef(false);
83
+ /** Compute the base pixels-per-mm at zoom=1.0, reading live canvas + volume state. */
84
+ var getBasePxPerMm = function () {
85
+ var _a;
86
+ var nv = props.nv;
87
+ var canvas = (_a = nv === null || nv === void 0 ? void 0 : nv.gl) === null || _a === void 0 ? void 0 : _a.canvas;
88
+ var back = nv === null || nv === void 0 ? void 0 : nv.back;
89
+ if (!canvas || !(back === null || back === void 0 ? void 0 : back.dims) || !(back === null || back === void 0 ? void 0 : back.pixDims))
90
+ return null;
91
+ var imgWmm = back.dims[1] * Math.abs(back.pixDims[1]);
92
+ var imgHmm = back.dims[2] * Math.abs(back.pixDims[2]);
93
+ if (imgWmm <= 0 || imgHmm <= 0)
94
+ return null;
95
+ var w = canvas.clientWidth || canvas.width;
96
+ var h = canvas.clientHeight || canvas.height;
97
+ return Math.min(w / imgWmm, h / imgHmm);
98
+ };
99
+ /** Returns the display value (number only, no unit suffix). */
100
+ var formatPxPerMm = function () {
101
+ var _a, _b, _c;
102
+ 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];
103
+ if (zoom == null)
104
+ return '';
105
+ var base = getBasePxPerMm();
106
+ if (base != null && base > 0)
107
+ return (base * zoom).toFixed(2);
108
+ return (zoom * 100).toFixed(0);
109
+ };
110
+ React.useEffect(function () {
111
+ var rafId;
112
+ var sync = function () {
113
+ if (!zoomEditingRef.current)
114
+ setZoomText(formatPxPerMm());
115
+ rafId = requestAnimationFrame(sync);
116
+ };
117
+ rafId = requestAnimationFrame(sync);
118
+ return function () { return cancelAnimationFrame(rafId); };
119
+ }, [props.nv]);
120
+ var applyZoom = function (text) {
121
+ var num = parseFloat(text.trim());
122
+ if (isNaN(num) || num <= 0)
123
+ return;
124
+ var base = getBasePxPerMm();
125
+ var targetZoom = (base != null && base > 0) ? num / base : num / 100;
126
+ var scene = props.nv.scene;
127
+ var current = scene.pan2Dxyzmm[3];
128
+ var next = Math.max(0.01, targetZoom);
129
+ var delta = current - next;
130
+ scene.pan2Dxyzmm[3] = next;
131
+ var mm = props.nv.frac2mm(scene.crosshairPos);
132
+ scene.pan2Dxyzmm[0] += delta * mm[0];
133
+ scene.pan2Dxyzmm[1] += delta * mm[1];
134
+ scene.pan2Dxyzmm[2] += delta * mm[2];
135
+ props.nv.drawScene();
136
+ };
137
+ var _b = useState(false), roiDeleteOpen = _b[0], setRoiDeleteOpen = _b[1];
138
+ var _c = useState(undefined), roiDeleteMsg = _c[0], setRoiDeleteMsg = _c[1];
139
+ var _d = useState(function () { return function () { }; }), roiDeleteConfirm = _d[0], setRoiDeleteConfirm = _d[1];
84
140
  // const deleteROI= createAsyncThunk('DeleteROI', async (arg: { accessToken: string, jobId: string }) => {
85
141
  // // const data = { jobId: arg.jobId }; // No need to stringify, axios will handle it
86
142
  // const config = {
@@ -270,7 +326,28 @@ export default function Toolbar(props) {
270
326
  border: '1px solid',
271
327
  borderColor: 'divider',
272
328
  borderRadius: 1
273
- } }, { children: _jsx(ZoomOutIcon, {}) })) })), _jsx(Tooltip, __assign({ title: 'Zoom In', placement: 'right' }, { children: _jsx(IconButton, __assign({ onClick: function () {
329
+ } }, { children: _jsx(ZoomOutIcon, {}) })) })), _jsx(TextField, { label: "px/mm", size: "small", value: zoomText, onChange: function (e) { return setZoomText(e.target.value); }, onFocus: function (e) {
330
+ zoomEditingRef.current = true;
331
+ requestAnimationFrame(function () { return e.target.select(); });
332
+ }, onBlur: function () {
333
+ zoomEditingRef.current = false;
334
+ applyZoom(zoomText);
335
+ }, onKeyDown: function (e) {
336
+ if (e.key === 'Enter') {
337
+ e.target.blur();
338
+ }
339
+ else if (e.key === 'Escape') {
340
+ zoomEditingRef.current = false;
341
+ setZoomText(formatPxPerMm());
342
+ e.target.blur();
343
+ }
344
+ }, sx: {
345
+ width: 100,
346
+ '& .MuiInputBase-input': {
347
+ textAlign: 'center',
348
+ fontSize: '0.8rem'
349
+ }
350
+ } }), _jsx(Tooltip, __assign({ title: 'Zoom In', placement: 'right' }, { children: _jsx(IconButton, __assign({ onClick: function () {
274
351
  var scene = props.nv.scene;
275
352
  var current = scene.pan2Dxyzmm[3];
276
353
  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": "4.9.0",
4
4
  "author": "erosmontin@gmail.com",
5
5
  "license": "MIT",
6
6
  "repository": "erosmontin/cloudmr-ux",