cloudmr-ux 5.0.0 → 5.0.1

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,7 @@ 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 React from "react";
13
+ import React, { useEffect, useState } from "react";
14
14
  import { Card, CardContent } from "@mui/material";
15
15
  import CmrLabel from "../label/Label";
16
16
  import { resolveNiivueAccentColor, useNiivueViewerTheme, } from "../niivue-viewer/NiivueViewerThemeContext";
@@ -18,6 +18,50 @@ import { resolveNiivueAccentColor, useNiivueViewerTheme, } from "../niivue-viewe
18
18
  var safeSpan = function (min, max) { return Math.max(1e-9, max - min); };
19
19
  var clamp = function (v, lo, hi) { return Math.max(lo, Math.min(hi, v)); };
20
20
  var round3 = function (v) { return Math.round(v * 1000) / 1000; };
21
+ // ─── Deferred number input ────────────────────────────────────────────────────
22
+ /**
23
+ * A text input that holds a local string draft while the user is typing,
24
+ * committing only on blur or Enter. This prevents mid-entry snapping when the
25
+ * parent immediately re-renders with the snapped Niivue position.
26
+ * Arrow Up / Arrow Down step by one voxel via `onStep`.
27
+ */
28
+ function SliceInput(_a) {
29
+ var value = _a.value, inputStyle = _a.inputStyle, onCommit = _a.onCommit, onStep = _a.onStep;
30
+ var committed = value.toFixed(3);
31
+ var _b = useState(committed), draft = _b[0], setDraft = _b[1];
32
+ var _c = useState(false), focused = _c[0], setFocused = _c[1];
33
+ // Keep draft in sync with parent while the field is not being edited.
34
+ useEffect(function () {
35
+ if (!focused)
36
+ setDraft(committed);
37
+ }, [committed, focused]);
38
+ var commit = function (raw) {
39
+ var n = Number(raw);
40
+ if (Number.isFinite(n)) {
41
+ onCommit(n);
42
+ }
43
+ else {
44
+ // Revert to last known good value.
45
+ setDraft(committed);
46
+ }
47
+ };
48
+ return (_jsx("input", { type: "text", inputMode: "decimal", value: draft, style: inputStyle, onChange: function (e) { return setDraft(e.target.value); }, onFocus: function () { return setFocused(true); }, onBlur: function (e) {
49
+ setFocused(false);
50
+ commit(e.target.value);
51
+ }, onKeyDown: function (e) {
52
+ if (e.key === "Enter") {
53
+ e.target.blur();
54
+ }
55
+ else if (e.key === "ArrowUp") {
56
+ e.preventDefault();
57
+ onStep(1);
58
+ }
59
+ else if (e.key === "ArrowDown") {
60
+ e.preventDefault();
61
+ onStep(-1);
62
+ }
63
+ } }));
64
+ }
21
65
  // ─── Component ────────────────────────────────────────────────────────────────
22
66
  /**
23
67
  * **NiivueSlicePosition**
@@ -235,24 +279,6 @@ export function NiivueSlicePosition(_a) {
235
279
  accentColor: accentColor
236
280
  };
237
281
  // ── Render ────────────────────────────────────────────────────────────────
238
- 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("div", __assign({ style: { display: "flex", flexDirection: "column" } }, { children: [_jsxs("div", __assign({ style: { marginBottom: 20 } }, { children: [_jsxs("div", __assign({ style: rowStyle }, { children: [_jsx(CmrLabel, { children: "X:" }), _jsx("input", { type: "number", value: xVal.toFixed(3), min: sliderMinX, max: sliderMaxX, step: stepX, style: inputStyle, onChange: function (e) {
239
- var next = Number(e.target.value);
240
- if (Number.isFinite(next))
241
- applyX(next);
242
- }, onBlur: function (e) {
243
- applyX(clamp(Number(e.target.value), sliderMinX, sliderMaxX));
244
- } })] })), _jsx("input", { id: "xSlice", type: "range", min: sliderMinX, max: sliderMaxX, step: stepX, value: clamp(xVal, sliderMinX, sliderMaxX), style: sliderStyle, onChange: function (e) { return applyX(Number(e.target.value)); } })] })), _jsxs("div", __assign({ style: { marginBottom: 20 } }, { children: [_jsxs("div", __assign({ style: rowStyle }, { children: [_jsx(CmrLabel, { children: "Y:" }), _jsx("input", { type: "number", value: yVal.toFixed(3), min: sliderMinY, max: sliderMaxY, step: stepY, style: inputStyle, onChange: function (e) {
245
- var next = Number(e.target.value);
246
- if (Number.isFinite(next))
247
- applyY(next);
248
- }, onBlur: function (e) {
249
- applyY(clamp(Number(e.target.value), sliderMinY, sliderMaxY));
250
- } })] })), _jsx("input", { id: "ySlice", type: "range", min: sliderMinY, max: sliderMaxY, step: stepY, value: clamp(yVal, sliderMinY, sliderMaxY), style: sliderStyle, onChange: function (e) { return applyY(Number(e.target.value)); } })] })), _jsxs("div", { children: [_jsxs("div", __assign({ style: rowStyle }, { children: [_jsx(CmrLabel, { children: "Z:" }), _jsx("input", { type: "number", value: zVal.toFixed(3), min: sliderMinZ, max: sliderMaxZ, step: stepZ, style: inputStyle, onChange: function (e) {
251
- var next = Number(e.target.value);
252
- if (Number.isFinite(next))
253
- applyZ(next);
254
- }, onBlur: function (e) {
255
- applyZ(clamp(Number(e.target.value), sliderMinZ, sliderMaxZ));
256
- } })] })), _jsx("input", { id: "zSlice", type: "range", min: 0, max: Math.max(0, nz - 1), step: 1, value: zSliceIndex, style: sliderStyle, onChange: function (e) { return applyZBySliceIndex(Number(e.target.value)); } })] })] })) })) }))] })));
282
+ 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("div", __assign({ style: { display: "flex", flexDirection: "column" } }, { children: [_jsxs("div", __assign({ style: { marginBottom: 20 } }, { children: [_jsxs("div", __assign({ style: rowStyle }, { children: [_jsx(CmrLabel, { children: "X:" }), _jsx(SliceInput, { value: xVal, inputStyle: inputStyle, onCommit: function (mm) { return applyX(clamp(mm, sliderMinX, sliderMaxX)); }, onStep: function (dir) { return applyX(clamp(xVal + dir * stepX, sliderMinX, sliderMaxX)); } })] })), _jsx("input", { id: "xSlice", type: "range", min: sliderMinX, max: sliderMaxX, step: stepX, value: clamp(xVal, sliderMinX, sliderMaxX), style: sliderStyle, onChange: function (e) { return applyX(Number(e.target.value)); } })] })), _jsxs("div", __assign({ style: { marginBottom: 20 } }, { children: [_jsxs("div", __assign({ style: rowStyle }, { children: [_jsx(CmrLabel, { children: "Y:" }), _jsx(SliceInput, { value: yVal, inputStyle: inputStyle, onCommit: function (mm) { return applyY(clamp(mm, sliderMinY, sliderMaxY)); }, onStep: function (dir) { return applyY(clamp(yVal + dir * stepY, sliderMinY, sliderMaxY)); } })] })), _jsx("input", { id: "ySlice", type: "range", min: sliderMinY, max: sliderMaxY, step: stepY, value: clamp(yVal, sliderMinY, sliderMaxY), style: sliderStyle, onChange: function (e) { return applyY(Number(e.target.value)); } })] })), _jsxs("div", { children: [_jsxs("div", __assign({ style: rowStyle }, { children: [_jsx(CmrLabel, { children: "Z:" }), _jsx(SliceInput, { value: zVal, inputStyle: inputStyle, onCommit: function (mm) { return applyZ(mm); }, onStep: function (dir) { return applyZBySliceIndex(zSliceIndex + dir); } })] })), _jsx("input", { id: "zSlice", type: "range", min: 0, max: Math.max(0, nz - 1), step: 1, value: zSliceIndex, style: sliderStyle, onChange: function (e) { return applyZBySliceIndex(Number(e.target.value)); } })] })] })) })) }))] })));
257
283
  }
258
284
  export default NiivueSlicePosition;
@@ -82,6 +82,13 @@ export default function TKDualRange(_a) {
82
82
  var span = Math.max(1e-12, tMax - tMin);
83
83
  var pct = function (t) { return ((t - tMin) / span) * 100; };
84
84
  var s = step !== null && step !== void 0 ? step : Math.max(span * 0.001, Number.EPSILON);
85
+ // Whether either value is currently outside the domain (from manual text input).
86
+ var isOutOfBounds = valueLow < minDomain || valueLow > maxDomain ||
87
+ valueHigh < minDomain || valueHigh > maxDomain;
88
+ // Clamped render-space values used only for the fill bar and slider thumbs,
89
+ // so they don't go offscreen when the real value is outside the domain.
90
+ var tLowDisplay = clamp(tLow, tMin, tMax);
91
+ var tHighDisplay = clamp(tHigh, tMin, tMax);
85
92
  // Keep ends from crossing; clamp in REAL space against the other end.
86
93
  var handleLowRender = function (nextRender) {
87
94
  var nextReal = clamp(inverse(nextRender), minDomain, valueHigh);
@@ -91,18 +98,22 @@ export default function TKDualRange(_a) {
91
98
  var nextReal = clamp(inverse(nextRender), valueLow, maxDomain);
92
99
  onChangeHigh(nextReal);
93
100
  };
94
- return (_jsxs("div", __assign({ className: "tkdr" }, { children: [_jsxs("div", __assign({ className: "tkdr__row tkdr__row--ends" }, { children: [_jsxs("div", __assign({ className: "tkdr__group" }, { children: [_jsx("span", __assign({ className: "tkdr__hint" }, { children: "Min" })), _jsx(DeferredInput, { className: "tkdr__num", committed: fmt(valueLow, precision), lo: minDomain, hi: valueHigh, onCommit: function (raw) {
101
+ return (_jsxs("div", __assign({ className: "tkdr" }, { children: [_jsxs("div", __assign({ className: "tkdr__row tkdr__row--ends" }, { children: [_jsxs("div", __assign({ className: "tkdr__group" }, { children: [_jsx("span", __assign({ className: "tkdr__hint" }, { children: "Min" })), _jsx(DeferredInput, { className: "tkdr__num", committed: fmt(valueLow, precision), lo: minDomain, hi: maxDomain, onCommit: function (raw) {
95
102
  var n = parse(raw);
96
103
  if (!Number.isFinite(n))
97
104
  return;
98
- onChangeLow(clamp(n, minDomain, valueHigh));
99
- } })] })), _jsxs("div", __assign({ className: "tkdr__group" }, { children: [_jsx("span", __assign({ className: "tkdr__hint" }, { children: "Max" })), _jsx(DeferredInput, { className: "tkdr__num", committed: fmt(valueHigh, precision), lo: valueLow, hi: maxDomain, onCommit: function (raw) {
105
+ onChangeLow(n);
106
+ } })] })), _jsxs("div", __assign({ className: "tkdr__group" }, { children: [_jsx("span", __assign({ className: "tkdr__hint" }, { children: "Max" })), _jsx(DeferredInput, { className: "tkdr__num", committed: fmt(valueHigh, precision), lo: minDomain, hi: maxDomain, onCommit: function (raw) {
100
107
  var n = parse(raw);
101
108
  if (!Number.isFinite(n))
102
109
  return;
103
- onChangeHigh(clamp(n, valueLow, maxDomain));
104
- } })] }))] })), _jsxs("div", __assign({ className: "tkdr__track", style: (_b = {}, _b["--tkdr-accent"] = accentColor, _b) }, { children: [_jsx("div", { className: "tkdr__range-fill", style: {
105
- left: "".concat(pct(Math.min(tLow, tHigh)), "%"),
106
- width: "".concat(Math.abs(pct(tHigh) - pct(tLow)), "%")
107
- }, "aria-hidden": true }), _jsx("input", { className: "tkdr__range", type: "range", min: tMin, max: tMax, step: s, value: tLow, onChange: function (e) { return handleLowRender(Number(e.target.value)); } }), _jsx("input", { className: "tkdr__range tkdr__range--top", type: "range", min: tMin, max: tMax, step: s, value: tHigh, onChange: function (e) { return handleHighRender(Number(e.target.value)); } })] }))] })));
110
+ onChangeHigh(n);
111
+ } })] }))] })), _jsxs("div", __assign({ className: "tkdr__track", style: __assign((_b = {}, _b["--tkdr-accent"] = accentColor, _b), (isOutOfBounds && {
112
+ opacity: 0.35,
113
+ filter: "grayscale(1)",
114
+ transition: "opacity 0.15s, filter 0.15s"
115
+ })) }, { children: [_jsx("div", { className: "tkdr__range-fill", style: {
116
+ left: "".concat(pct(Math.min(tLowDisplay, tHighDisplay)), "%"),
117
+ width: "".concat(Math.abs(pct(tHighDisplay) - pct(tLowDisplay)), "%")
118
+ }, "aria-hidden": true }), _jsx("input", { className: "tkdr__range", type: "range", min: tMin, max: tMax, step: s, value: tLowDisplay, onChange: function (e) { return handleLowRender(Number(e.target.value)); } }), _jsx("input", { className: "tkdr__range tkdr__range--top", type: "range", min: tMin, max: tMax, step: s, value: tHighDisplay, onChange: function (e) { return handleHighRender(Number(e.target.value)); } })] }))] })));
108
119
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloudmr-ux",
3
- "version": "5.0.0",
3
+ "version": "5.0.1",
4
4
  "author": "erosmontin@gmail.com",
5
5
  "license": "MIT",
6
6
  "repository": "erosmontin/cloudmr-ux",