cloudmr-ux 4.3.5 → 4.3.7

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.
@@ -0,0 +1,25 @@
1
+ import { CSSProperties } from "react";
2
+ /** Default merged ROI label in Niivue patcher `groupLabelsInto` / `groupLabelsFromSelection`. */
3
+ export declare const DEFAULT_ROI_GROUP_TARGET_LABEL = 7;
4
+ export interface NiivueRoiTableProps {
5
+ pipelineID: string;
6
+ rois: any[];
7
+ resampleImage: () => void;
8
+ zipAndSendROI: (url: string, filename: string, blob: Blob) => Promise<void>;
9
+ unpackROI: (url: string) => Promise<void>;
10
+ setLabelAlias: (label: number | string, alias: string) => void;
11
+ nv: any;
12
+ style?: CSSProperties;
13
+ /**
14
+ * Called after a successful ROI upload handshake, multipart upload, and `unpackROI`.
15
+ * Use this to refresh server-side ROI metadata (e.g. `dispatch(getPipelineROI(...))`).
16
+ */
17
+ onAfterRoiUpload?: () => void | Promise<void>;
18
+ /** Niivue label index ROIs merge into when grouping. Default: {@link DEFAULT_ROI_GROUP_TARGET_LABEL}. */
19
+ groupTargetLabel?: number;
20
+ }
21
+ /**
22
+ * Data grid + toolbar for Niivue ROI labels: edit aliases, visibility, group / ungroup,
23
+ * download subset, delete, and upload replacement mask. No Redux — pass callbacks and `nv`.
24
+ */
25
+ export declare function NiivueRoiTable(props: NiivueRoiTableProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,253 @@
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
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
13
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
14
+ return new (P || (P = Promise))(function (resolve, reject) {
15
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
16
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
17
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
18
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
19
+ });
20
+ };
21
+ var __generator = (this && this.__generator) || function (thisArg, body) {
22
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
23
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
24
+ function verb(n) { return function (v) { return step([n, v]); }; }
25
+ function step(op) {
26
+ if (f) throw new TypeError("Generator is already executing.");
27
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
28
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
29
+ if (y = 0, t) op = [op[0] & 2, t.value];
30
+ switch (op[0]) {
31
+ case 0: case 1: t = op; break;
32
+ case 4: _.label++; return { value: op[1], done: false };
33
+ case 5: _.label++; y = op[1]; op = [0]; continue;
34
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
35
+ default:
36
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
37
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
38
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
39
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
40
+ if (t[2]) _.ops.pop();
41
+ _.trys.pop(); continue;
42
+ }
43
+ op = body.call(thisArg, _);
44
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
45
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
46
+ }
47
+ };
48
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
49
+ import { useState } from "react";
50
+ import { Tooltip, IconButton, Snackbar, Alert, } from "@mui/material";
51
+ import Box from "@mui/material/Box";
52
+ import VisibilityIcon from "@mui/icons-material/Visibility";
53
+ import VisibilityOffIcon from "@mui/icons-material/VisibilityOff";
54
+ import GetAppIcon from "@mui/icons-material/GetApp";
55
+ import DeleteIcon from "@mui/icons-material/Delete";
56
+ import { Icon as WpIcon, group as wpGroup, ungroup as wpUngroup } from "@wordpress/icons";
57
+ import CmrTable from "../../CmrTable/CmrTable";
58
+ import CMRUpload from "../upload/Upload";
59
+ import { getEndpoints } from "../../core/config/AppConfig";
60
+ import { AuthenticatedHttpClient } from "../../core/common/utilities/AuthenticatedRequests";
61
+ /** Default merged ROI label in Niivue patcher `groupLabelsInto` / `groupLabelsFromSelection`. */
62
+ export var DEFAULT_ROI_GROUP_TARGET_LABEL = 7;
63
+ var ROI_TOOLBAR_ICON_SIZE_PX = 24;
64
+ var ROI_TOOLBAR_ICON_BUTTON_SX = {
65
+ color: "action.active",
66
+ "&.Mui-disabled": {
67
+ color: function (theme) { return theme.palette.action.disabled; }
68
+ }
69
+ };
70
+ var ROI_TOOLBAR_MUI_ICON_SX = {
71
+ fontSize: ROI_TOOLBAR_ICON_SIZE_PX,
72
+ color: "inherit"
73
+ };
74
+ /**
75
+ * Data grid + toolbar for Niivue ROI labels: edit aliases, visibility, group / ungroup,
76
+ * download subset, delete, and upload replacement mask. No Redux — pass callbacks and `nv`.
77
+ */
78
+ export function NiivueRoiTable(props) {
79
+ var _this = this;
80
+ var _a = props.groupTargetLabel, groupTargetLabel = _a === void 0 ? DEFAULT_ROI_GROUP_TARGET_LABEL : _a, onAfterRoiUpload = props.onAfterRoiUpload;
81
+ var _b = useState(1), uploadKey = _b[0], setUploadKey = _b[1];
82
+ var endpoints = getEndpoints();
83
+ var _c = useState([]), selectedData = _c[0], setSelectedData = _c[1];
84
+ var roiColumns = [
85
+ {
86
+ headerName: "ROI Label",
87
+ field: "alias",
88
+ flex: 1,
89
+ editable: true,
90
+ valueSetter: function (params) {
91
+ if (params.value !== params.row.alias) {
92
+ props.setLabelAlias(params.row.label, params.value);
93
+ }
94
+ return params.row;
95
+ }
96
+ },
97
+ {
98
+ headerName: "Color",
99
+ field: "color",
100
+ flex: 0.5,
101
+ sortable: false,
102
+ renderCell: function (params) { return (_jsx(Box, __assign({ sx: { width: "100%", display: "flex", justifyContent: "center" } }, { children: _jsx("div", { style: {
103
+ width: "14pt",
104
+ height: "14pt",
105
+ borderRadius: "3pt",
106
+ background: "".concat(params.row.color)
107
+ } }) }))); }
108
+ },
109
+ {
110
+ headerName: "Mean",
111
+ field: "mu",
112
+ flex: 1,
113
+ renderCell: function (params) { return (_jsx("div", { children: "".concat(params.row.mu.toFixed(3)) })); }
114
+ },
115
+ {
116
+ headerName: "SD",
117
+ field: "std",
118
+ flex: 1,
119
+ renderCell: function (params) { return (_jsx("div", { children: "".concat(params.row.std.toFixed(3)) })); }
120
+ },
121
+ {
122
+ headerName: "Visibility",
123
+ field: "visibility",
124
+ flex: 1,
125
+ sortable: false,
126
+ renderCell: function (params) { return (_jsx(Box, __assign({ sx: { width: "100%", display: "flex", justifyContent: "center" } }, { children: _jsx(IconButton, __assign({ onClick: function (event) {
127
+ props.nv.setLabelVisibility(Number(params.row.label), !props.nv.getLabelVisibility(Number(params.row.label)));
128
+ props.resampleImage();
129
+ props.nv.drawScene();
130
+ event.stopPropagation();
131
+ } }, { children: params.row.visibility ? (_jsx(VisibilityIcon, { sx: { color: "#aaa" } })) : (_jsx(VisibilityOffIcon, { sx: { color: "#aaa" } })) })) }))); }
132
+ },
133
+ {
134
+ headerName: "Voxel Count",
135
+ field: "count",
136
+ flex: 1.5
137
+ },
138
+ ];
139
+ var _d = useState(false), warningVisible = _d[0], setWarningVisible = _d[1];
140
+ var _e = useState(""), warningMessage = _e[0], setWarningMessage = _e[1];
141
+ var warnEmptySelection = function (message) {
142
+ setWarningMessage(message);
143
+ setWarningVisible(true);
144
+ };
145
+ var selectedNums = selectedData.map(function (v) { return Number(v); });
146
+ var uniqueSelected = new Set(selectedNums);
147
+ var canGroupSelection = selectedNums.length >= 2 && uniqueSelected.size >= 2;
148
+ var groupButtonDisabled = selectedData.length > 0 && !canGroupSelection;
149
+ var groupTooltip = selectedData.length === 0
150
+ ? "Group selected ROIs"
151
+ : uniqueSelected.size < 2 || selectedNums.length < 2
152
+ ? "Select at least two different ROIs to group"
153
+ : "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) {
155
+ setSelectedData(rowSelectionModel);
156
+ } }), _jsxs(Box, __assign({ sx: {
157
+ display: "flex",
158
+ justifyContent: "flex-start",
159
+ alignItems: "center",
160
+ flexWrap: "wrap",
161
+ gap: 2,
162
+ mt: 0,
163
+ px: 2,
164
+ py: 1,
165
+ backgroundColor: "#f8f9fa",
166
+ border: "1px solid rgba(0, 0, 0, 0.12)",
167
+ borderTop: "none",
168
+ borderRadius: "0 0 4px 4px"
169
+ } }, { children: [_jsx(Tooltip, __assign({ title: groupTooltip }, { children: _jsx("span", { children: _jsx(IconButton, __assign({ disabled: groupButtonDisabled, sx: ROI_TOOLBAR_ICON_BUTTON_SX, onClick: function () {
170
+ if (selectedData.length === 0) {
171
+ warnEmptySelection("Please select an ROI to group");
172
+ return;
173
+ }
174
+ if (!canGroupSelection) {
175
+ warnEmptySelection("Please select at least two different ROIs to group");
176
+ return;
177
+ }
178
+ if (typeof props.nv.groupLabelsFromSelection === "function") {
179
+ props.nv.groupLabelsFromSelection(selectedNums, groupTargetLabel);
180
+ }
181
+ else {
182
+ props.nv.groupLabelsInto(selectedNums, groupTargetLabel);
183
+ }
184
+ props.nv.drawScene();
185
+ props.resampleImage();
186
+ } }, { children: _jsx(WpIcon, { icon: wpGroup, size: 24, fill: "currentColor" }) })) }) })), _jsx(Tooltip, __assign({ title: "Ungroup ROIs" }, { children: _jsx(IconButton, __assign({ sx: ROI_TOOLBAR_ICON_BUTTON_SX, onClick: function () {
187
+ if (selectedData.length === 0) {
188
+ warnEmptySelection("Please select an ROI to ungroup");
189
+ return;
190
+ }
191
+ props.nv.ungroup();
192
+ props.nv.drawScene();
193
+ props.resampleImage();
194
+ } }, { children: _jsx(WpIcon, { icon: wpUngroup, size: ROI_TOOLBAR_ICON_SIZE_PX, fill: "currentColor" }) })) })), _jsx(Tooltip, __assign({ title: "Download" }, { children: _jsx(IconButton, __assign({ sx: ROI_TOOLBAR_ICON_BUTTON_SX, onClick: function () { return __awaiter(_this, void 0, void 0, function () {
195
+ var fileName, selectedLabels, _i, selectedData_1, label;
196
+ return __generator(this, function (_a) {
197
+ switch (_a.label) {
198
+ case 0:
199
+ fileName = "label";
200
+ selectedLabels = [];
201
+ for (_i = 0, selectedData_1 = selectedData; _i < selectedData_1.length; _i++) {
202
+ label = selectedData_1[_i];
203
+ fileName += label;
204
+ selectedLabels.push(Number(label));
205
+ }
206
+ fileName += ".nii";
207
+ if (selectedLabels.length === 0) {
208
+ warnEmptySelection("Please select an ROI to download");
209
+ return [2 /*return*/];
210
+ }
211
+ return [4 /*yield*/, props.nv.saveImageByLabels(fileName, selectedLabels)];
212
+ case 1:
213
+ _a.sent();
214
+ return [2 /*return*/];
215
+ }
216
+ });
217
+ }); } }, { children: _jsx(GetAppIcon, { sx: ROI_TOOLBAR_MUI_ICON_SX }) })) })), _jsx(Tooltip, __assign({ title: "Delete" }, { children: _jsx(IconButton, __assign({ sx: ROI_TOOLBAR_ICON_BUTTON_SX, onClick: function () {
218
+ if (selectedData.length === 0) {
219
+ warnEmptySelection("Please select an ROI to delete");
220
+ return;
221
+ }
222
+ props.nv.deleteDrawingByLabel(selectedData.map(function (value) { return Number(value); }));
223
+ props.resampleImage();
224
+ props.nv.drawScene();
225
+ } }, { children: _jsx(DeleteIcon, { sx: ROI_TOOLBAR_MUI_ICON_SX }) })) })), _jsx(CMRUpload, { changeNameAfterUpload: false, color: "primary", onUploaded: function () { }, uploadHandler: function (file, _fileAlias, _fileDatabase) { return __awaiter(_this, void 0, void 0, function () {
226
+ var filename, response;
227
+ return __generator(this, function (_a) {
228
+ switch (_a.label) {
229
+ case 0:
230
+ filename = file.name;
231
+ return [4 /*yield*/, AuthenticatedHttpClient.post(endpoints.ROI_UPLOAD, {
232
+ filename: filename,
233
+ pipeline_id: props.pipelineID,
234
+ type: "image",
235
+ contentType: "application/octet-stream"
236
+ })];
237
+ case 1:
238
+ response = _a.sent();
239
+ return [4 /*yield*/, props.zipAndSendROI(response.data.upload_url, filename, file)];
240
+ case 2:
241
+ _a.sent();
242
+ return [4 /*yield*/, props.unpackROI(response.data.access_url)];
243
+ case 3:
244
+ _a.sent();
245
+ return [4 /*yield*/, (onAfterRoiUpload === null || onAfterRoiUpload === void 0 ? void 0 : onAfterRoiUpload())];
246
+ case 4:
247
+ _a.sent();
248
+ setUploadKey(function (k) { return k + 1; });
249
+ return [2 /*return*/, 200];
250
+ }
251
+ });
252
+ }); }, maxCount: 1 }, uploadKey)] })), _jsx(Snackbar, __assign({ open: warningVisible, autoHideDuration: 3000, onClose: function () { return setWarningVisible(false); }, anchorOrigin: { vertical: "top", horizontal: "center" } }, { children: _jsx(Alert, __assign({ severity: "warning", sx: { width: "100%" }, onClose: function () { return setWarningVisible(false); } }, { children: warningMessage })) }))] })));
253
+ }
@@ -6,18 +6,42 @@
6
6
  margin-bottom: 0;
7
7
  } */
8
8
 
9
- .MuiDataGrid-columnHeaders{
10
- background: #F3E5F5;
11
- color: #333;
9
+ /**
10
+ * CmrTable root — colors are driven by CSS variables set on `.cmr-table` from props.
11
+ * Defaults preserve the package purple accent (#580f8b). Host apps can override per instance
12
+ * via `headerIconColor` / `checkboxCheckedColor` / `checkboxUncheckedColor`, or by setting
13
+ * variables on a parent: `--cmr-table-header-icon`, etc.
14
+ */
15
+ .cmr-table {
16
+ /* Fallbacks = package defaults */
17
+ --cmr-table-header-bg: #f3e5f5;
18
+ --cmr-table-header-text: #333;
19
+ --cmr-table-header-icon: #580f8b;
20
+ --cmr-table-checkbox-unchecked: rgba(88, 15, 139, 0.54);
21
+ --cmr-table-checkbox-checked: #580f8b;
22
+ }
23
+
24
+ .cmr-table .MuiDataGrid-columnHeaders {
25
+ background: var(--cmr-table-header-bg);
26
+ color: var(--cmr-table-header-text);
12
27
  font-weight: bold;
13
28
  }
14
29
 
15
- .MuiDataGrid-columnHeaders .MuiSvgIcon-root {
16
- color: #580f8b !important;
30
+ .cmr-table .MuiDataGrid-columnHeaders .MuiSvgIcon-root {
31
+ color: var(--cmr-table-header-icon) !important;
32
+ }
33
+
34
+ .cmr-table .MuiCheckbox-root {
35
+ color: var(--cmr-table-checkbox-unchecked);
36
+ }
37
+
38
+ .cmr-table .MuiCheckbox-root.Mui-checked,
39
+ .cmr-table .MuiCheckbox-root.MuiCheckbox-indeterminate {
40
+ color: var(--cmr-table-checkbox-checked) !important;
17
41
  }
18
42
 
19
- /* // Religning MUI checkbox in the header */
20
- .MuiDataGrid-columnHeaderCheckbox .MuiDataGrid-columnHeaderTitleContainerContent {
43
+ /* Religning MUI checkbox in the header */
44
+ .cmr-table .MuiDataGrid-columnHeaderCheckbox .MuiDataGrid-columnHeaderTitleContainerContent {
21
45
  padding: 0 !important;
22
46
  margin: 0 !important;
23
47
  display: flex;
@@ -2,15 +2,34 @@ import React from 'react';
2
2
  import './CmrTable.css';
3
3
  import { DataGridProps } from '@mui/x-data-grid';
4
4
  import { CSSProperties } from 'react';
5
+ /** Default header icon / selection accent (purple). Exposed for host apps that want the package default explicitly. */
6
+ export declare const CMR_TABLE_DEFAULT_HEADER_ICON = "#580f8b";
5
7
  export interface CmrTableProps extends Omit<DataGridProps, 'rows'> {
6
8
  dataSource: any[];
7
9
  idAlias?: string;
8
10
  name?: string;
9
11
  style?: CSSProperties;
10
12
  showCheckbox?: boolean;
13
+ /** Column header background. @default `#F3E5F5` */
11
14
  headerBgColor?: string;
15
+ /** Column header text color. @default `#333` */
12
16
  headerTextColor?: string;
17
+ /**
18
+ * Sort icons, menu icons, and header “select all” checkbox accent.
19
+ * Also the default for {@link checkboxCheckedColor} when that prop is omitted.
20
+ * @default `#580f8b`
21
+ */
13
22
  headerIconColor?: string;
23
+ /**
24
+ * Row / header checkbox color when checked or indeterminate.
25
+ * @default same as `headerIconColor` (`#580f8b` when defaults apply)
26
+ */
27
+ checkboxCheckedColor?: string;
28
+ /**
29
+ * Row / header checkbox color when unchecked (non-checked outline state).
30
+ * @default `rgba(88, 15, 139, 0.54)` (pairs with default purple accent)
31
+ */
32
+ checkboxUncheckedColor?: string;
14
33
  /**
15
34
  * Optional function to customize how rows are filtered/sorted before display.
16
35
  * Receives the mapped rows; return a new array in the desired order.
@@ -32,6 +32,12 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
32
32
  import { jsx as _jsx } from "react/jsx-runtime";
33
33
  import './CmrTable.css';
34
34
  import { DataGrid } from '@mui/x-data-grid';
35
+ /** Default header icon / selection accent (purple). Exposed for host apps that want the package default explicitly. */
36
+ export var CMR_TABLE_DEFAULT_HEADER_ICON = '#580f8b';
37
+ var DEFAULT_HEADER_BG = '#F3E5F5';
38
+ var DEFAULT_HEADER_TEXT = '#333';
39
+ /** Unchecked row/header checkbox tint that pairs with {@link CMR_TABLE_DEFAULT_HEADER_ICON}. */
40
+ var DEFAULT_CHECKBOX_UNCHECKED = 'rgba(88, 15, 139, 0.54)';
35
41
  /** Extracts a comparable timestamp from a row for sorting. Supports createdAt, created_at, or similar date fields. */
36
42
  function getSubmittedTimestamp(row) {
37
43
  var _a, _b, _c;
@@ -46,38 +52,45 @@ function defaultSortByRecent(rows) {
46
52
  return __spreadArray([], rows, true).sort(function (a, b) { return getSubmittedTimestamp(b) - getSubmittedTimestamp(a); });
47
53
  }
48
54
  var CmrTable = function (props) {
49
- var dataSource = props.dataSource, columns = props.columns, idAlias = props.idAlias, className = props.className, onRowSelectionModelChange = props.onRowSelectionModelChange, style = props.style, _a = props.showCheckbox, showCheckbox = _a === void 0 ? true : _a, _b = props.headerBgColor, headerBgColor = _b === void 0 ? '#F3E5F5' : _b, _c = props.headerTextColor, headerTextColor = _c === void 0 ? '#333' : _c, _d = props.headerIconColor, headerIconColor = _d === void 0 ? '#580f8b' : _d, processRows = props.processRows, rest = __rest(props, ["dataSource", "columns", "idAlias", "className", "onRowSelectionModelChange", "style", "showCheckbox", "headerBgColor", "headerTextColor", "headerIconColor", "processRows"]);
55
+ var _a;
56
+ var dataSource = props.dataSource, columns = props.columns, idAlias = props.idAlias, className = props.className, onRowSelectionModelChange = props.onRowSelectionModelChange, style = props.style, _b = props.showCheckbox, showCheckbox = _b === void 0 ? true : _b, _c = props.headerBgColor, headerBgColor = _c === void 0 ? DEFAULT_HEADER_BG : _c, _d = props.headerTextColor, headerTextColor = _d === void 0 ? DEFAULT_HEADER_TEXT : _d, _e = props.headerIconColor, headerIconColor = _e === void 0 ? CMR_TABLE_DEFAULT_HEADER_ICON : _e, checkboxCheckedColor = props.checkboxCheckedColor, checkboxUncheckedColor = props.checkboxUncheckedColor, processRows = props.processRows, userSx = props.sx, rest = __rest(props, ["dataSource", "columns", "idAlias", "className", "onRowSelectionModelChange", "style", "showCheckbox", "headerBgColor", "headerTextColor", "headerIconColor", "checkboxCheckedColor", "checkboxUncheckedColor", "processRows", "sx"]);
57
+ var resolvedCheckboxChecked = checkboxCheckedColor !== null && checkboxCheckedColor !== void 0 ? checkboxCheckedColor : headerIconColor;
58
+ var resolvedCheckboxUnchecked = checkboxUncheckedColor !== null && checkboxUncheckedColor !== void 0 ? checkboxUncheckedColor : DEFAULT_CHECKBOX_UNCHECKED;
59
+ var wrapperStyle = __assign(__assign({ height: '400px', width: '100%' }, (style !== null && style !== void 0 ? style : {})), (_a = {}, _a['--cmr-table-header-bg'] = headerBgColor, _a['--cmr-table-header-text'] = headerTextColor, _a['--cmr-table-header-icon'] = headerIconColor, _a['--cmr-table-checkbox-checked'] = resolvedCheckboxChecked, _a['--cmr-table-checkbox-unchecked'] = resolvedCheckboxUnchecked, _a));
60
+ var tableClassName = ['cmr-table', className].filter(Boolean).join(' ');
50
61
  var mappedRows = dataSource
51
62
  ? dataSource.map(function (row) { return (__assign({ id: idAlias ? row[idAlias] : row['id'] }, row)); })
52
63
  : [];
53
64
  var displayRows = processRows ? processRows(mappedRows) : defaultSortByRecent(mappedRows);
54
- return (_jsx("div", __assign({ style: style !== null && style !== void 0 ? style : { height: '400px', width: '100%' }, className: className !== null && className !== void 0 ? className : '' }, { children: _jsx(DataGrid, __assign({ rows: displayRows, columns: columns, checkboxSelection: showCheckbox, onRowSelectionModelChange: onRowSelectionModelChange, initialState: {
65
+ var baseSx = {
66
+ '& .MuiDataGrid-columnHeaders': {
67
+ backgroundColor: headerBgColor,
68
+ color: headerTextColor
69
+ },
70
+ '&& .MuiDataGrid-columnHeader .MuiSvgIcon-root': {
71
+ color: "".concat(headerIconColor, " !important")
72
+ },
73
+ '&& .MuiDataGrid-columnHeader .MuiDataGrid-sortIcon': {
74
+ color: "".concat(headerIconColor, " !important")
75
+ },
76
+ '&& .MuiDataGrid-columnHeader .MuiDataGrid-menuIconButton .MuiSvgIcon-root': {
77
+ color: "".concat(headerIconColor, " !important")
78
+ },
79
+ '&& .MuiDataGrid-columnHeader .MuiDataGrid-iconButtonContainer .MuiSvgIcon-root': {
80
+ color: "".concat(headerIconColor, " !important")
81
+ },
82
+ '& .MuiDataGrid-columnHeaderTitle': {
83
+ fontWeight: 'bold'
84
+ }
85
+ };
86
+ var mergedSx = userSx == null
87
+ ? baseSx
88
+ : Array.isArray(userSx)
89
+ ? __spreadArray([baseSx], userSx, true) : [baseSx, userSx];
90
+ return (_jsx("div", __assign({ style: wrapperStyle, className: tableClassName }, { children: _jsx(DataGrid, __assign({ rows: displayRows, columns: columns, checkboxSelection: showCheckbox, onRowSelectionModelChange: onRowSelectionModelChange, initialState: {
55
91
  pagination: {
56
92
  paginationModel: { pageSize: 50, page: 0 }
57
93
  }
58
- }, sx: {
59
- '& .MuiDataGrid-columnHeaders': {
60
- backgroundColor: headerBgColor,
61
- color: headerTextColor
62
- },
63
- // '& .MuiDataGrid-columnHeaders .MuiSvgIcon-root': {
64
- // color: headerIconColor,
65
- // },
66
- '&& .MuiDataGrid-columnHeader .MuiSvgIcon-root': {
67
- color: "".concat(headerIconColor, " !important")
68
- },
69
- '&& .MuiDataGrid-columnHeader .MuiDataGrid-sortIcon': {
70
- color: "".concat(headerIconColor, " !important")
71
- },
72
- '&& .MuiDataGrid-columnHeader .MuiDataGrid-menuIconButton .MuiSvgIcon-root': {
73
- color: "".concat(headerIconColor, " !important")
74
- },
75
- '&& .MuiDataGrid-columnHeader .MuiDataGrid-iconButtonContainer .MuiSvgIcon-root': {
76
- color: "".concat(headerIconColor, " !important")
77
- },
78
- '& .MuiDataGrid-columnHeaderTitle': {
79
- fontWeight: 'bold'
80
- }
81
- }, localeText: { noRowsLabel: 'No Rows' } }, rest)) })));
94
+ }, sx: mergedSx, localeText: { noRowsLabel: 'No Rows' } }, rest)) })));
82
95
  };
83
96
  export default CmrTable;
@@ -1,11 +1,8 @@
1
- import { CSSProperties } from "react";
2
- export declare const ROITable: (props: {
3
- pipelineID: string;
4
- rois: any[];
5
- resampleImage: () => void;
6
- zipAndSendROI: (url: string, filename: string, blob: Blob) => Promise<void>;
7
- unpackROI: (url: string) => Promise<void>;
8
- setLabelAlias: (label: number | string, alias: string) => void;
9
- style?: CSSProperties | undefined;
10
- nv: any;
11
- }) => import("react/jsx-runtime").JSX.Element;
1
+ import type { NiivueRoiTableProps } from "../../../CmrComponents/niivue-roi-table/NiivueRoiTable";
2
+ export type { NiivueRoiTableProps };
3
+ type ReduxRoiTableProps = Omit<NiivueRoiTableProps, "onAfterRoiUpload">;
4
+ /**
5
+ * ROI table wired to the core Redux store: refreshes pipeline ROI metadata after upload.
6
+ * Prefer {@link NiivueRoiTable} from `cloudmr-ux` when building screens without this store.
7
+ */
8
+ export declare function ROITable(props: ReduxRoiTableProps): import("react/jsx-runtime").JSX.Element;
@@ -9,298 +9,21 @@ var __assign = (this && this.__assign) || function () {
9
9
  };
10
10
  return __assign.apply(this, arguments);
11
11
  };
12
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
13
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
14
- return new (P || (P = Promise))(function (resolve, reject) {
15
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
16
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
17
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
18
- step((generator = generator.apply(thisArg, _arguments || [])).next());
19
- });
20
- };
21
- var __generator = (this && this.__generator) || function (thisArg, body) {
22
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
23
- return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
24
- function verb(n) { return function (v) { return step([n, v]); }; }
25
- function step(op) {
26
- if (f) throw new TypeError("Generator is already executing.");
27
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
28
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
29
- if (y = 0, t) op = [op[0] & 2, t.value];
30
- switch (op[0]) {
31
- case 0: case 1: t = op; break;
32
- case 4: _.label++; return { value: op[1], done: false };
33
- case 5: _.label++; y = op[1]; op = [0]; continue;
34
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
35
- default:
36
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
37
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
38
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
39
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
40
- if (t[2]) _.ops.pop();
41
- _.trys.pop(); continue;
42
- }
43
- op = body.call(thisArg, _);
44
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
45
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
46
- }
47
- };
48
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
49
- import { CmrTable, CMRUpload, CmrConfirmation } from "../../../index";
50
- import { useState } from "react";
51
- import { Tooltip, IconButton, } from "@mui/material";
52
- import { getEndpoints } from "../../config/AppConfig";
12
+ import { jsx as _jsx } from "react/jsx-runtime";
13
+ import { NiivueRoiTable } from "../../../CmrComponents/niivue-roi-table/NiivueRoiTable";
53
14
  import { useAppDispatch, useAppSelector } from "../../store/hooks";
54
- import Box from "@mui/material/Box";
55
- import VisibilityIcon from "@mui/icons-material/Visibility";
56
- import VisibilityOffIcon from "@mui/icons-material/VisibilityOff";
57
- import GetAppIcon from "@mui/icons-material/GetApp";
58
- import DeleteIcon from "@mui/icons-material/Delete";
59
- import { Icon as WpIcon, group as wpGroup, ungroup as wpUngroup } from "@wordpress/icons";
60
15
  import { getPipelineROI } from "../../features/rois/resultActionCreation";
61
- import { AuthenticatedHttpClient } from "../../common/utilities/AuthenticatedRequests";
62
- /** Default merged ROI label in NiivuePatcher `groupLabelsInto` keep in sync with patcher */
63
- var GROUP_TARGET_LABEL = 7;
64
- /** Toolbar action icons: `action.active` → rgba(0, 0, 0, 0.54) in default MUI light theme */
65
- var ROI_TOOLBAR_ICON_SIZE_PX = 24;
66
- var ROI_TOOLBAR_ICON_BUTTON_SX = {
67
- color: "action.active",
68
- "&.Mui-disabled": {
69
- color: function (theme) { return theme.palette.action.disabled; }
70
- }
71
- };
72
- var ROI_TOOLBAR_MUI_ICON_SX = {
73
- fontSize: ROI_TOOLBAR_ICON_SIZE_PX,
74
- color: "inherit"
75
- };
76
- export var ROITable = function (props) {
77
- // const rois:ROI[] = useAppSelector(state=>{
78
- // return (state.roi.rois[props.pipelineID]==undefined)?[]:state.roi.rois[props.pipelineID];
79
- // })
80
- // console.log(rois);
81
- var endpoints = getEndpoints();
82
- var _a = useState(1), uploadKey = _a[0], setUploadKey = _a[1];
83
- var pipeline = useAppSelector(function (state) { var _a; return (_a = state.result.activeJob) === null || _a === void 0 ? void 0 : _a.pipeline_id; });
84
- var _b = useState([]), selectedData = _b[0], setSelectedData = _b[1];
16
+ /**
17
+ * ROI table wired to the core Redux store: refreshes pipeline ROI metadata after upload.
18
+ * Prefer {@link NiivueRoiTable} from `cloudmr-ux` when building screens without this store.
19
+ */
20
+ export function ROITable(props) {
21
+ var pipeline = useAppSelector(function (s) { var _a; return (_a = s.result.activeJob) === null || _a === void 0 ? void 0 : _a.pipeline_id; });
85
22
  var dispatch = useAppDispatch();
86
- var roiColumns = [
87
- {
88
- headerName: "ROI Label",
89
- field: "alias",
90
- flex: 1,
91
- editable: true,
92
- valueSetter: function (params) {
93
- var value = params.value;
94
- // console.log(params);
95
- var newAlias = params.value; // Value entered by the user
96
- // console.log(newAlias);
97
- if (newAlias != params.row.alias) {
98
- props.setLabelAlias(params.row.label, newAlias);
99
- }
100
- return params.row;
101
- }
102
- },
103
- {
104
- headerName: "Color",
105
- field: "color",
106
- flex: 0.5,
107
- sortable: false,
108
- // renderHeader: (params: any) => {
109
- // return (
110
- // <Box sx={{ display: 'flex', justifyContent: 'center', width: '100%' }}>
111
- // {params.colDef.headerName}
112
- // </Box>
113
- // );
114
- // },
115
- renderCell: function (params) {
116
- return (_jsx(Box, __assign({ sx: {
117
- width: "100%",
118
- display: "flex",
119
- justifyContent: "center"
120
- } }, { children: _jsx("div", { style: {
121
- width: "14pt",
122
- height: "14pt",
123
- borderRadius: "3pt",
124
- background: "".concat(params.row.color)
125
- } }) })));
23
+ return (_jsx(NiivueRoiTable, __assign({}, props, { onAfterRoiUpload: function () {
24
+ var p = pipeline !== null && pipeline !== void 0 ? pipeline : props.pipelineID;
25
+ if (p) {
26
+ void dispatch(getPipelineROI({ pipeline: p }));
126
27
  }
127
- },
128
- {
129
- headerName: "Mean",
130
- field: "mu",
131
- flex: 1,
132
- renderCell: function (params) {
133
- return _jsx("div", { children: "".concat(params.row.mu.toFixed(3)) });
134
- }
135
- },
136
- {
137
- headerName: "SD",
138
- field: "std",
139
- flex: 1,
140
- renderCell: function (params) {
141
- return _jsx("div", { children: "".concat(params.row.std.toFixed(3)) });
142
- }
143
- },
144
- {
145
- headerName: "Visibility",
146
- field: "visibility",
147
- flex: 1,
148
- sortable: false,
149
- // renderHeader: (params: any) => {
150
- // return (
151
- // <Box sx={{ display: 'flex', justifyContent: 'center', width: '100%' }}>
152
- // {params.colDef.headerName}
153
- // </Box>
154
- // );
155
- // },
156
- renderCell: function (params) {
157
- return (_jsx(Box, __assign({ sx: {
158
- width: "100%",
159
- display: "flex",
160
- justifyContent: "center"
161
- } }, { children: _jsx(IconButton, __assign({ onClick: function (event) {
162
- props.nv.setLabelVisibility(Number(params.row.label), !props.nv.getLabelVisibility(Number(params.row.label)));
163
- props.resampleImage();
164
- props.nv.drawScene();
165
- event.stopPropagation();
166
- } }, { children: params.row.visibility ? (_jsx(VisibilityIcon, { sx: { color: "#aaa" } })) : (_jsx(VisibilityOffIcon, { sx: { color: "#aaa" } })) })) })));
167
- }
168
- },
169
- {
170
- headerName: "Voxel Count",
171
- field: "count",
172
- flex: 1.5
173
- },
174
- ];
175
- var _c = useState(false), warningVisible = _c[0], setWarningVisible = _c[1];
176
- var _d = useState(""), warningMessage = _d[0], setWarningMessage = _d[1];
177
- var warnEmptySelection = function (message) {
178
- setWarningMessage(message);
179
- setWarningVisible(true);
180
- };
181
- var selectedNums = selectedData.map(function (v) { return Number(v); });
182
- var uniqueSelected = new Set(selectedNums);
183
- var canGroupSelection = selectedNums.length >= 2 && uniqueSelected.size >= 2;
184
- var groupButtonDisabled = selectedData.length > 0 && !canGroupSelection;
185
- var groupTooltip = selectedData.length === 0
186
- ? "Group selected ROIs"
187
- : uniqueSelected.size < 2 || selectedNums.length < 2
188
- ? "Select at least two different ROIs to group"
189
- : "Group selected ROIs";
190
- return (_jsxs(Box, __assign({ style: props.style }, { children: [_jsx(CmrTable
191
- // sx={{
192
- // borderBottomLeftRadius: 0,
193
- // borderBottomRightRadius: 0,
194
- // marginBottom: 0,
195
- // paddingBottom: 0,
196
- // }}
197
- , {
198
- // sx={{
199
- // borderBottomLeftRadius: 0,
200
- // borderBottomRightRadius: 0,
201
- // marginBottom: 0,
202
- // paddingBottom: 0,
203
- // }}
204
- hideFooter: true, getRowId: function (row) { return row.label; }, style: { height: "70%" }, dataSource: props.rois, columns: roiColumns, columnHeaderHeight: 40, rowSelectionModel: selectedData, onRowSelectionModelChange: function (rowSelectionModel) {
205
- setSelectedData(rowSelectionModel);
206
- } }), _jsxs(Box, __assign({ sx: {
207
- display: "flex",
208
- justifyContent: "flex-start",
209
- alignItems: "center",
210
- flexWrap: "wrap",
211
- gap: 2,
212
- mt: 0,
213
- px: 2,
214
- py: 1,
215
- backgroundColor: "#f8f9fa",
216
- border: "1px solid rgba(0, 0, 0, 0.12)",
217
- borderTop: "none",
218
- borderRadius: "0 0 4px 4px"
219
- } }, { children: [_jsx(Tooltip, __assign({ title: groupTooltip }, { children: _jsx("span", { children: _jsx(IconButton, __assign({ disabled: groupButtonDisabled, sx: ROI_TOOLBAR_ICON_BUTTON_SX, onClick: function () {
220
- if (selectedData.length === 0) {
221
- warnEmptySelection("Please select an ROI to group");
222
- return;
223
- }
224
- if (!canGroupSelection) {
225
- warnEmptySelection("Please select at least two different ROIs to group");
226
- return;
227
- }
228
- if (typeof props.nv.groupLabelsFromSelection === "function") {
229
- props.nv.groupLabelsFromSelection(selectedNums, GROUP_TARGET_LABEL);
230
- }
231
- else {
232
- props.nv.groupLabelsInto(selectedNums, GROUP_TARGET_LABEL);
233
- }
234
- props.nv.drawScene();
235
- props.resampleImage();
236
- } }, { children: _jsx(WpIcon, { icon: wpGroup, size: ROI_TOOLBAR_ICON_SIZE_PX, fill: "currentColor" }) })) }) })), _jsx(Tooltip, __assign({ title: "Ungroup" }, { children: _jsx(IconButton, __assign({ sx: ROI_TOOLBAR_ICON_BUTTON_SX, onClick: function () {
237
- props.nv.ungroup();
238
- props.nv.drawScene();
239
- props.resampleImage();
240
- } }, { children: _jsx(WpIcon, { icon: wpUngroup, size: 16, fill: "currentColor" }) })) })), _jsx(Tooltip, __assign({ title: "Download" }, { children: _jsx(IconButton, __assign({ sx: ROI_TOOLBAR_ICON_BUTTON_SX, onClick: function () { return __awaiter(void 0, void 0, void 0, function () {
241
- var fileName, selectedLabels, _i, selectedData_1, label;
242
- return __generator(this, function (_a) {
243
- switch (_a.label) {
244
- case 0:
245
- fileName = "label";
246
- selectedLabels = [];
247
- for (_i = 0, selectedData_1 = selectedData; _i < selectedData_1.length; _i++) {
248
- label = selectedData_1[_i];
249
- fileName += label;
250
- selectedLabels.push(Number(label));
251
- }
252
- fileName += ".nii";
253
- if (selectedLabels.length == 0) {
254
- warnEmptySelection("No ROI selected for download");
255
- return [2 /*return*/];
256
- }
257
- return [4 /*yield*/, props.nv.saveImageByLabels(fileName, selectedLabels)];
258
- case 1:
259
- _a.sent();
260
- return [2 /*return*/];
261
- }
262
- });
263
- }); } }, { children: _jsx(GetAppIcon, { sx: ROI_TOOLBAR_MUI_ICON_SX }) })) })), _jsx(Tooltip, __assign({ title: "Delete" }, { children: _jsx(IconButton, __assign({ sx: ROI_TOOLBAR_ICON_BUTTON_SX, onClick: function () {
264
- props.nv.deleteDrawingByLabel(selectedData.map(function (value) { return Number(value); }));
265
- props.resampleImage();
266
- props.nv.drawScene();
267
- } }, { children: _jsx(DeleteIcon, { sx: ROI_TOOLBAR_MUI_ICON_SX }) })) })), _jsx(CMRUpload, { changeNameAfterUpload: false, color: "primary", onUploaded: function (res, file) { }, uploadHandler: function (file) { return __awaiter(void 0, void 0, void 0, function () {
268
- var filename, response;
269
- return __generator(this, function (_a) {
270
- switch (_a.label) {
271
- case 0:
272
- console.log(props);
273
- filename = file.name;
274
- filename.split(".").pop();
275
- return [4 /*yield*/, AuthenticatedHttpClient.post(endpoints.ROI_UPLOAD, {
276
- filename: filename,
277
- pipeline_id: props.pipelineID,
278
- type: "image",
279
- contentType: "application/octet-stream"
280
- })];
281
- case 1:
282
- response = _a.sent();
283
- console.log(response);
284
- return [4 /*yield*/, props
285
- .zipAndSendROI(response.data.upload_url, filename, file)
286
- .then(function () { return __awaiter(void 0, void 0, void 0, function () {
287
- return __generator(this, function (_a) {
288
- switch (_a.label) {
289
- case 0: return [4 /*yield*/, props.unpackROI(response.data.access_url)];
290
- case 1:
291
- _a.sent();
292
- // @ts-ignore
293
- dispatch(getPipelineROI({ pipeline: pipeline }));
294
- return [2 /*return*/];
295
- }
296
- });
297
- }); })];
298
- case 2:
299
- _a.sent();
300
- return [2 /*return*/, 200];
301
- }
302
- });
303
- }); },
304
- // createPayload={createPayload}
305
- maxCount: 1 }, uploadKey)] })), _jsx(CmrConfirmation, { name: "Warning", message: warningMessage, color: "error", width: 400, open: warningVisible, setOpen: function (open) { return setWarningVisible(open); }, confirmText: "OK" })] })));
306
- };
28
+ } })));
29
+ }
@@ -43,7 +43,7 @@ export var resultSlice = createSlice({
43
43
  state.resultLoading = action.meta.jobId;
44
44
  }),
45
45
  builder.addCase(loadResult.fulfilled, function (state, action) {
46
- var _a;
46
+ var _a, _b, _c, _d, _e;
47
47
  // console.log(action.payload);
48
48
  state.activeJob = action.payload.job;
49
49
  //@ts-ignore
@@ -52,9 +52,9 @@ export var resultSlice = createSlice({
52
52
  state.activeJob.setup.task = action.payload.result.headers.options;
53
53
  //@ts-ignore
54
54
  state.activeJob.logs =
55
- action.payload.result.log || action.payload.result.headers.log;
55
+ (_c = (_a = action.payload.result.log) !== null && _a !== void 0 ? _a : (_b = action.payload.result.headers) === null || _b === void 0 ? void 0 : _b.log) !== null && _c !== void 0 ? _c : (_d = action.payload.result.headers) === null || _d === void 0 ? void 0 : _d.logs;
56
56
  //@ts-ignore
57
- state.activeJob.slices = (_a = action.payload.result.info) === null || _a === void 0 ? void 0 : _a.slices;
57
+ state.activeJob.slices = (_e = action.payload.result.info) === null || _e === void 0 ? void 0 : _e.slices;
58
58
  //@ts-ignore
59
59
  state.niis[state.activeJob.pipeline_id] = action.payload.niis;
60
60
  state.selectedVolume = 1;
package/dist/index.d.ts CHANGED
@@ -31,8 +31,12 @@ export { resampleNiivueRoiHistogram, getDefaultRoiHistogramLayout, } from "./Cmr
31
31
  export type { NiivueRoiHistogramRow, RoiLabelMapping, } from "./CmrComponents/niivue-roi-histogram/resampleNiivueRoiHistogram";
32
32
  export { NiivueRoiHistogram } from "./CmrComponents/niivue-roi-histogram/NiivueRoiHistogram";
33
33
  export { useNiivueRoiHistogram } from "./CmrComponents/niivue-roi-histogram/useNiivueRoiHistogram";
34
+ export { NiivueRoiTable, DEFAULT_ROI_GROUP_TARGET_LABEL, } from "./CmrComponents/niivue-roi-table/NiivueRoiTable";
35
+ export type { NiivueRoiTableProps } from "./CmrComponents/niivue-roi-table/NiivueRoiTable";
34
36
  import type { FC } from "react";
35
37
  import type { CmrTableProps } from "./CmrTable/CmrTable";
38
+ import { CMR_TABLE_DEFAULT_HEADER_ICON } from "./CmrTable/CmrTable";
36
39
  export declare const CmrTable: FC<CmrTableProps>;
37
40
  export type { CmrTableProps };
41
+ export { CMR_TABLE_DEFAULT_HEADER_ICON };
38
42
  export * from "./core";
package/dist/index.js CHANGED
@@ -28,6 +28,8 @@ export { DrawToolkit } from "./CmrComponents/draw-toolkit/DrawToolkit";
28
28
  export { resampleNiivueRoiHistogram, getDefaultRoiHistogramLayout, } from "./CmrComponents/niivue-roi-histogram/resampleNiivueRoiHistogram";
29
29
  export { NiivueRoiHistogram } from "./CmrComponents/niivue-roi-histogram/NiivueRoiHistogram";
30
30
  export { useNiivueRoiHistogram } from "./CmrComponents/niivue-roi-histogram/useNiivueRoiHistogram";
31
- import CmrTableComponent from "./CmrTable/CmrTable";
31
+ export { NiivueRoiTable, DEFAULT_ROI_GROUP_TARGET_LABEL, } from "./CmrComponents/niivue-roi-table/NiivueRoiTable";
32
+ import CmrTableComponent, { CMR_TABLE_DEFAULT_HEADER_ICON } from "./CmrTable/CmrTable";
32
33
  export var CmrTable = CmrTableComponent;
34
+ export { CMR_TABLE_DEFAULT_HEADER_ICON };
33
35
  export * from "./core";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloudmr-ux",
3
- "version": "4.3.5",
3
+ "version": "4.3.7",
4
4
  "author": "erosmontin@gmail.com",
5
5
  "license": "MIT",
6
6
  "repository": "erosmontin/cloudmr-ux",