@cytario/web 2.1.4 → 2.1.5
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/README.md +20 -20
- package/app/.server/auth/README.md +8 -8
- package/app/.server/auth/authMiddleware.ts +9 -25
- package/app/.server/auth/exchangeAuthCode.ts +2 -6
- package/app/.server/auth/getS3Client.ts +3 -13
- package/app/.server/auth/getSessionCredentials.ts +6 -20
- package/app/.server/auth/getUserInfo.ts +2 -6
- package/app/.server/auth/keycloakAdmin/client.ts +2 -9
- package/app/.server/auth/keycloakAdmin/groups.ts +9 -26
- package/app/.server/auth/keycloakAdmin/users.ts +7 -23
- package/app/.server/auth/oauthState.ts +4 -13
- package/app/.server/auth/redirectIfAuthenticated.ts +1 -3
- package/app/.server/auth/refreshAuthTokens.ts +5 -19
- package/app/.server/auth/sessionMiddleware.ts +1 -4
- package/app/.server/auth/sessionStorage.ts +1 -4
- package/app/.server/auth/verifyIdToken.ts +1 -3
- package/app/.server/auth/wellKnownEndpoints.ts +1 -4
- package/app/.server/db/redis.ts +5 -1
- package/app/.server/logging.ts +1 -4
- package/app/.server/requestDurationMiddleware.ts +1 -4
- package/app/components/.client/ImageViewer/README.md +5 -5
- package/app/components/.client/ImageViewer/components/ChannelsController/ChannelsController.tsx +7 -9
- package/app/components/.client/ImageViewer/components/ChannelsController/ChannelsControllerBrightfieldItem.tsx +1 -2
- package/app/components/.client/ImageViewer/components/ChannelsController/ChannelsControllerItem.tsx +2 -5
- package/app/components/.client/ImageViewer/components/ChannelsController/ChannelsControllerItemList.tsx +7 -15
- package/app/components/.client/ImageViewer/components/ChannelsController/ColorPicker/ColorPicker.tsx +2 -9
- package/app/components/.client/ImageViewer/components/ChannelsController/ColorPicker/ColorSwatch.tsx +1 -4
- package/app/components/.client/ImageViewer/components/ChannelsController/DomainSlider.tsx +1 -3
- package/app/components/.client/ImageViewer/components/ChannelsController/Histogram.tsx +16 -18
- package/app/components/.client/ImageViewer/components/ChannelsController/HistogramChannel.tsx +2 -8
- package/app/components/.client/ImageViewer/components/ChannelsController/MinMaxSettings.tsx +6 -15
- package/app/components/.client/ImageViewer/components/FeatureBar/FeatureBarDragHandle.tsx +1 -5
- package/app/components/.client/ImageViewer/components/FeatureBar/FeatureBarToggle.tsx +1 -5
- package/app/components/.client/ImageViewer/components/FeatureBar/FeatureItem.tsx +1 -5
- package/app/components/.client/ImageViewer/components/FeatureBar/Presets.tsx +3 -11
- package/app/components/.client/ImageViewer/components/FeatureBar/useFeatureBar.tsx +16 -25
- package/app/components/.client/ImageViewer/components/Image/Channels/useChannelsLayer.ts +7 -18
- package/app/components/.client/ImageViewer/components/Image/ImageContainer.tsx +1 -1
- package/app/components/.client/ImageViewer/components/Image/ImagePanel.tsx +6 -26
- package/app/components/.client/ImageViewer/components/Image/ImagePreview.tsx +2 -9
- package/app/components/.client/ImageViewer/components/Image/Overlays/AdditivePolygonLayer.tsx +1 -5
- package/app/components/.client/ImageViewer/components/Image/Overlays/AdditiveScatterplotLayer.tsx +1 -5
- package/app/components/.client/ImageViewer/components/Image/Overlays/OverlaysLayer.tsx +6 -24
- package/app/components/.client/ImageViewer/components/Image/Overlays/markerUniforms.ts +2 -5
- package/app/components/.client/ImageViewer/components/Image/Overlays/useOverlaysLayer.tsx +7 -21
- package/app/components/.client/ImageViewer/components/Image/useInitializeChannels.ts +1 -7
- package/app/components/.client/ImageViewer/components/Image/useResizeObserver.ts +1 -1
- package/app/components/.client/ImageViewer/components/Magnifier.tsx +5 -13
- package/app/components/.client/ImageViewer/components/Measurements/ActiveViewStatePreview.tsx +3 -8
- package/app/components/.client/ImageViewer/components/Measurements/CursorTick.tsx +2 -7
- package/app/components/.client/ImageViewer/components/Measurements/Ruler.tsx +1 -8
- package/app/components/.client/ImageViewer/components/Measurements/SlideCarrier.tsx +3 -13
- package/app/components/.client/ImageViewer/components/Measurements/Tick.tsx +1 -1
- package/app/components/.client/ImageViewer/components/Measurements/calculateViewStateToFit.ts +1 -1
- package/app/components/.client/ImageViewer/components/Measurements/useMeasurements.ts +9 -28
- package/app/components/.client/ImageViewer/components/OverlaysController/AddOverlay.tsx +4 -13
- package/app/components/.client/ImageViewer/components/OverlaysController/OverlayPicker.modal.tsx +1 -6
- package/app/components/.client/ImageViewer/components/OverlaysController/OverlaysController.Item.tsx +24 -54
- package/app/components/.client/ImageViewer/components/OverlaysController/OverlaysController.tsx +1 -3
- package/app/components/.client/ImageViewer/components/SplitViewToggle.tsx +1 -3
- package/app/components/.client/ImageViewer/components/ViewerHeader.tsx +1 -3
- package/app/components/.client/ImageViewer/state/decoders/decodeJPEG2000.d.ts +9 -11
- package/app/components/.client/ImageViewer/state/decoders/decodeJPEG2000.js +11 -11
- package/app/components/.client/ImageViewer/state/decoders/decoder.worker.js +49 -49
- package/app/components/.client/ImageViewer/state/decoders/genericDecoder.ts +76 -81
- package/app/components/.client/ImageViewer/state/decoders/jp2k-decoder.ts +9 -9
- package/app/components/.client/ImageViewer/state/decoders/lzwDecoder.ts +9 -9
- package/app/components/.client/ImageViewer/state/loaders/loadBioformatsZarrWithCredentials.ts +10 -22
- package/app/components/.client/ImageViewer/state/store/ViewerStoreContext.tsx +4 -18
- package/app/components/.client/ImageViewer/state/store/createViewerStore.ts +110 -194
- package/app/components/.client/ImageViewer/state/store/getInitialChannelsState.ts +2 -6
- package/app/components/.client/ImageViewer/state/store/selectors.ts +9 -9
- package/app/components/.client/ImageViewer/state/store/types.ts +3 -12
- package/app/components/.client/ImageViewer/state/transport/CredentialedHTTPStore.ts +1 -5
- package/app/components/.client/ImageViewer/state/transport/SigV4TiffClient.ts +2 -9
- package/app/components/.client/ImageViewer/utils/getSelectionStats.ts +1 -4
- package/app/components/.client/ImageViewer/utils/handleImageViewerHover.ts +1 -1
- package/app/components/.client/ImageViewer/utils/mapChannelConfigsToState.ts +2 -4
- package/app/components/.client/ImageViewer/utils/useTilesLoading.ts +3 -3
- package/app/components/AppHeader.tsx +1 -4
- package/app/components/Breadcrumbs/Breadcrumbs.tsx +4 -13
- package/app/components/Breadcrumbs/getCrumbs.tsx +1 -1
- package/app/components/ClientOnly.tsx +1 -1
- package/app/components/Container.tsx +3 -15
- package/app/components/DataGrid/ConvertOverlay.modal.tsx +1 -6
- package/app/components/DataGrid/DataGrid.tsx +7 -27
- package/app/components/DataGrid/WktSvg.tsx +2 -4
- package/app/components/DataGrid/getParquetSchema.ts +1 -4
- package/app/components/DescriptionList.tsx +1 -3
- package/app/components/DirectoryView/ConnectionMenu.tsx +8 -22
- package/app/components/DirectoryView/DirectoryView.tsx +10 -46
- package/app/components/DirectoryView/DirectoryViewGrid.tsx +16 -49
- package/app/components/DirectoryView/DirectoryViewTableConnection.tsx +1 -4
- package/app/components/DirectoryView/DirectoryViewTableDirectory.tsx +2 -7
- package/app/components/DirectoryView/DirectoryViewTree.tsx +5 -21
- package/app/components/DirectoryView/FilterBar.tsx +9 -48
- package/app/components/DirectoryView/buildDirectoryTree.ts +6 -25
- package/app/components/DirectoryView/filterNodes.ts +4 -11
- package/app/components/DirectoryView/modals/Cyberduck.modal.tsx +6 -15
- package/app/components/DirectoryView/modals/FileInfo.modal.tsx +1 -5
- package/app/components/DirectoryView/useLayoutStore.ts +5 -25
- package/app/components/GlobalSearch/GlobalSearch.tsx +1 -4
- package/app/components/GlobalSearch/SearchBar.tsx +1 -7
- package/app/components/GlobalSearch/Suggestions.tsx +0 -1
- package/app/components/ImageViewer/state/formatRegistry.ts +5 -18
- package/app/components/LavaLoader.tsx +4 -11
- package/app/components/Layout/Footer.tsx +1 -4
- package/app/components/Pills/ScopePill.tsx +2 -8
- package/app/components/Table/ColumnFilterInput.tsx +5 -20
- package/app/components/Table/ColumnResizeHandle.tsx +1 -5
- package/app/components/Table/ColumnSortButton.tsx +1 -5
- package/app/components/Table/SelectionFooter.tsx +3 -5
- package/app/components/Table/Table.tsx +5 -21
- package/app/components/Table/TableBodyRow.tsx +19 -31
- package/app/components/Table/TableHeaderRow.tsx +7 -28
- package/app/components/Table/TableMenu.tsx +4 -20
- package/app/components/Table/state/createTableStore.ts +3 -9
- package/app/components/Table/state/useTableStore.ts +1 -3
- package/app/components/Table/types.ts +4 -10
- package/app/components/Table/useColumnFilters.ts +1 -4
- package/app/components/Table/useColumnVisibility.ts +4 -11
- package/app/components/Table/useColumnWidths.ts +1 -3
- package/app/components/Table/useTableSorting.ts +4 -12
- package/app/components/Tooltip/Tooltip.tsx +4 -17
- package/app/components/Tooltip/TooltipSpan.tsx +5 -28
- package/app/components/Tooltip/useCopyToClipboard.ts +1 -3
- package/app/components/Tooltip/useMiddleEllipsis.ts +2 -7
- package/app/components/Tooltip/useOverflowDetection.ts +2 -5
- package/app/components/UserMenu.tsx +2 -9
- package/app/entry.server.tsx +9 -19
- package/app/hooks/useSearchParam.ts +2 -4
- package/app/lib/bootstrapPluginsCore.ts +4 -9
- package/app/root.tsx +4 -15
- package/app/routes/admin/assertAdminScope.ts +1 -3
- package/app/routes/admin/assertGroupPathsInScope.ts +3 -11
- package/app/routes/admin/assertGroupsInScope.ts +3 -11
- package/app/routes/admin/assertUsersInScope.ts +2 -8
- package/app/routes/admin/bulkInvite/bulkInvite.action.ts +2 -13
- package/app/routes/admin/bulkInvite/bulkInvite.form.tsx +18 -35
- package/app/routes/admin/createGroup/createGroup.action.ts +3 -10
- package/app/routes/admin/createGroup/createGroup.form.tsx +2 -9
- package/app/routes/admin/createGroup/createGroup.modal.tsx +1 -5
- package/app/routes/admin/inviteUser/inviteUser.action.ts +1 -4
- package/app/routes/admin/inviteUser/inviteUser.form.tsx +2 -8
- package/app/routes/admin/inviteUser/inviteUser.loader.ts +1 -4
- package/app/routes/admin/inviteUser/inviteUser.modal.tsx +3 -16
- package/app/routes/admin/updateUser/updateUser.form.tsx +4 -15
- package/app/routes/admin/updateUser/updateUser.modal.tsx +5 -23
- package/app/routes/admin/updateUser/userDetail.action.ts +2 -10
- package/app/routes/admin/users/BulkActions.tsx +15 -38
- package/app/routes/admin/users/bulkUsers.action.ts +2 -9
- package/app/routes/admin/users/bulkUsers.schema.ts +1 -6
- package/app/routes/admin/users/users.route.tsx +14 -63
- package/app/routes/api/cyberduck-profile.$name.ts +6 -2
- package/app/routes/auth/callback.route.tsx +8 -33
- package/app/routes/auth/login.route.tsx +8 -11
- package/app/routes/auth/logout.route.tsx +4 -14
- package/app/routes/config.route.tsx +1 -5
- package/app/routes/connections/connection.form.tsx +5 -14
- package/app/routes/connections/connection.schema.ts +4 -16
- package/app/routes/connections/connections.loader.ts +11 -23
- package/app/routes/connections/connections.route.tsx +1 -5
- package/app/routes/connections/connections.server.ts +1 -3
- package/app/routes/connections/createConnection.action.ts +2 -8
- package/app/routes/connections/createConnection.modal.tsx +3 -13
- package/app/routes/connections/deleteConnection.action.ts +1 -4
- package/app/routes/connections/updateConnection.action.ts +2 -8
- package/app/routes/connections/updateConnection.modal.tsx +4 -14
- package/app/routes/home/home.route.tsx +8 -33
- package/app/routes/layouts/ModalOutlet.tsx +6 -18
- package/app/routes/objects/objects.loader.ts +5 -19
- package/app/routes/objects/objects.route.tsx +11 -30
- package/app/routes/presign.route.tsx +5 -18
- package/app/routes/recent.route.tsx +1 -4
- package/app/routes/search.route.tsx +4 -17
- package/app/routes.ts +1 -4
- package/app/tailwind.css +17 -12
- package/app/types/cornerstone-codecs.d.ts +25 -29
- package/app/utils/connectionsStore/selectors.ts +2 -6
- package/app/utils/connectionsStore/useConnectionsStore.ts +2 -6
- package/app/utils/db/convertCsvToParquet.ts +1 -3
- package/app/utils/db/createDatabase.ts +1 -3
- package/app/utils/db/createSingleton.ts +1 -1
- package/app/utils/db/getBlobFromObjectNode.ts +3 -9
- package/app/utils/db/getGeomQuery.ts +1 -1
- package/app/utils/db/getMarkerInfoWasm.ts +1 -3
- package/app/utils/db/getTileBoundingBox.ts +1 -4
- package/app/utils/db/sqlQueries.ts +1 -4
- package/app/utils/fileType.ts +80 -10
- package/app/utils/filterObjects.ts +2 -5
- package/app/utils/localFilesStore/useFileStore.ts +7 -7
- package/app/utils/recentlyViewed.server.ts +1 -4
- package/app/utils/resourceId.ts +3 -11
- package/app/utils/s3Provider.ts +3 -7
- package/app/utils/signedFetch.ts +4 -13
- package/bin-src/codegen.ts +4 -1
- package/package.json +5 -1
- package/prisma/seed.ts +1 -2
- package/public/favicon/site.webmanifest +1 -1
- package/server.js +1 -4
- package/server.js.map +1 -1
- package/vite-plugins/cytario-plugins.ts +2 -8
|
@@ -186,8 +186,8 @@ GeoTIFF decoders are registered at the module level in `ImageViewer.tsx`:
|
|
|
186
186
|
|
|
187
187
|
```typescript
|
|
188
188
|
import { addDecoder } from "geotiff";
|
|
189
|
-
addDecoder(5, () => LZWDecoder);
|
|
190
|
-
addDecoder(33005, () => JP2KDecoder);
|
|
189
|
+
addDecoder(5, () => LZWDecoder); // LZW compression
|
|
190
|
+
addDecoder(33005, () => JP2KDecoder); // JPEG2000 compression
|
|
191
191
|
```
|
|
192
192
|
|
|
193
193
|
This runs client-side only — decoders use Web Workers and must not be imported during SSR. The `ImageViewer` component is lazy-loaded from the route via `React.lazy()` + `<ClientOnly>` + `<Suspense>`, ensuring decoders are never evaluated on the server.
|
|
@@ -215,9 +215,9 @@ ImageViewer (client)
|
|
|
215
215
|
|
|
216
216
|
### Naming Convention
|
|
217
217
|
|
|
218
|
-
| Image key
|
|
219
|
-
|
|
220
|
-
| `data/image.ome.tif`
|
|
218
|
+
| Image key | Offsets key |
|
|
219
|
+
| --------------------- | ------------------------- |
|
|
220
|
+
| `data/image.ome.tif` | `data/image.offsets.json` |
|
|
221
221
|
| `data/image.ome.tiff` | `data/image.offsets.json` |
|
|
222
222
|
|
|
223
223
|
Offsets are generated with [`generate-tiff-offsets`](https://github.com/hms-dbmi/generate-tiff-offsets) and placed alongside the OME-TIFF in S3. If the file doesn't exist, the viewer loads normally via sequential IFD traversal.
|
package/app/components/.client/ImageViewer/components/ChannelsController/ChannelsController.tsx
CHANGED
|
@@ -20,11 +20,12 @@ export function ChannelsController() {
|
|
|
20
20
|
|
|
21
21
|
// Show grouped counts: brightfield R/G/B counts as 1 item in the UI
|
|
22
22
|
const groupOffset = brightfieldGroup ? 2 : 0; // 3 channels → 1 item = -2
|
|
23
|
-
const isBrightfieldVisible =
|
|
24
|
-
|
|
25
|
-
channelsState[brightfieldGroup.
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
const isBrightfieldVisible =
|
|
24
|
+
brightfieldGroup && channelsState
|
|
25
|
+
? channelsState[brightfieldGroup.red]?.isVisible &&
|
|
26
|
+
channelsState[brightfieldGroup.green]?.isVisible &&
|
|
27
|
+
channelsState[brightfieldGroup.blue]?.isVisible
|
|
28
|
+
: false;
|
|
28
29
|
const visibleGrouped = visibleChannelCount - (isBrightfieldVisible ? 2 : 0);
|
|
29
30
|
const totalGrouped = channelIds.length - groupOffset;
|
|
30
31
|
const badge = `${visibleGrouped}/${totalGrouped}`;
|
|
@@ -39,10 +40,7 @@ export function ChannelsController() {
|
|
|
39
40
|
>
|
|
40
41
|
{layersStates.map((_, index) => (
|
|
41
42
|
<TabPanel key={index} id={String(index)}>
|
|
42
|
-
<ChannelsControllerItemList
|
|
43
|
-
isExpanded={isExpanded}
|
|
44
|
-
setIsExpanded={setIsExpanded}
|
|
45
|
-
/>
|
|
43
|
+
<ChannelsControllerItemList isExpanded={isExpanded} setIsExpanded={setIsExpanded} />
|
|
46
44
|
</TabPanel>
|
|
47
45
|
))}
|
|
48
46
|
</FeatureItem>
|
|
@@ -50,8 +50,7 @@ export function ChannelsControllerBrightfieldItem({
|
|
|
50
50
|
|
|
51
51
|
// Brightfield needs 3 channel slots
|
|
52
52
|
const disabled =
|
|
53
|
-
!isVisible &&
|
|
54
|
-
visibleChannelCount + BRIGHTFIELD_CHANNEL_COUNT > MAX_VISIBLE_CHANNELS;
|
|
53
|
+
!isVisible && visibleChannelCount + BRIGHTFIELD_CHANNEL_COUNT > MAX_VISIBLE_CHANNELS;
|
|
55
54
|
|
|
56
55
|
let tooltip = `${isVisible ? "Hide" : "Show"} Brightfield`;
|
|
57
56
|
if (disabled)
|
package/app/components/.client/ImageViewer/components/ChannelsController/ChannelsControllerItem.tsx
CHANGED
|
@@ -62,8 +62,7 @@ export function ChannelsControllerItem({
|
|
|
62
62
|
const disabled = !isVisible && visibleChannelCount >= MAX_VISIBLE_CHANNELS;
|
|
63
63
|
let tooltip = `${isVisible ? "Hide" : "Show"} ${name}`;
|
|
64
64
|
|
|
65
|
-
if (disabled)
|
|
66
|
-
tooltip = `Only ${MAX_VISIBLE_CHANNELS} channels can be visible at once`;
|
|
65
|
+
if (disabled) tooltip = `Only ${MAX_VISIBLE_CHANNELS} channels can be visible at once`;
|
|
67
66
|
|
|
68
67
|
return (
|
|
69
68
|
<Radio key={name} value={name} className={cx}>
|
|
@@ -88,9 +87,7 @@ export function ChannelsControllerItem({
|
|
|
88
87
|
|
|
89
88
|
{/* Pixel Value */}
|
|
90
89
|
{pixelValue > 0 && (
|
|
91
|
-
<span className="text-xs tabular-nums text-(--color-text-secondary)">
|
|
92
|
-
{pixelValue}
|
|
93
|
-
</span>
|
|
90
|
+
<span className="text-xs tabular-nums text-(--color-text-secondary)">{pixelValue}</span>
|
|
94
91
|
)}
|
|
95
92
|
|
|
96
93
|
{/* Visibility Toggle */}
|
|
@@ -31,11 +31,7 @@ export function ChannelsControllerItemList({
|
|
|
31
31
|
|
|
32
32
|
const brightfieldChannelIds = useMemo(() => {
|
|
33
33
|
if (!brightfieldGroup) return new Set<string>();
|
|
34
|
-
return new Set([
|
|
35
|
-
brightfieldGroup.red,
|
|
36
|
-
brightfieldGroup.green,
|
|
37
|
-
brightfieldGroup.blue,
|
|
38
|
-
]);
|
|
34
|
+
return new Set([brightfieldGroup.red, brightfieldGroup.green, brightfieldGroup.blue]);
|
|
39
35
|
}, [brightfieldGroup]);
|
|
40
36
|
|
|
41
37
|
const isBrightfieldVisible = useMemo(() => {
|
|
@@ -50,16 +46,13 @@ export function ChannelsControllerItemList({
|
|
|
50
46
|
const visibleChannelIds = useMemo(
|
|
51
47
|
() =>
|
|
52
48
|
channelIds.filter(
|
|
53
|
-
(id) =>
|
|
54
|
-
!brightfieldChannelIds.has(id) &&
|
|
55
|
-
(isExpanded || channelsState?.[id]?.isVisible),
|
|
49
|
+
(id) => !brightfieldChannelIds.has(id) && (isExpanded || channelsState?.[id]?.isVisible),
|
|
56
50
|
),
|
|
57
51
|
[channelIds, channelsState, isExpanded, brightfieldChannelIds],
|
|
58
52
|
);
|
|
59
53
|
|
|
60
54
|
// Show brightfield item when expanded or when it's visible
|
|
61
|
-
const showBrightfield =
|
|
62
|
-
brightfieldGroup && (isExpanded || isBrightfieldVisible);
|
|
55
|
+
const showBrightfield = brightfieldGroup && (isExpanded || isBrightfieldVisible);
|
|
63
56
|
|
|
64
57
|
useEffect(() => {
|
|
65
58
|
if (visibleChannelIds.length === 0 && !showBrightfield) {
|
|
@@ -77,7 +70,9 @@ export function ChannelsControllerItemList({
|
|
|
77
70
|
// Check if enabling this channel/group would exceed the viv limit
|
|
78
71
|
const isBrightfield = name === BRIGHTFIELD_GROUP_ID;
|
|
79
72
|
const slotsNeeded = isBrightfield ? 3 : 1;
|
|
80
|
-
const alreadyVisible = isBrightfield
|
|
73
|
+
const alreadyVisible = isBrightfield
|
|
74
|
+
? isBrightfieldVisible
|
|
75
|
+
: channelsState?.[name]?.isVisible;
|
|
81
76
|
if (!alreadyVisible && visibleChannelCount + slotsNeeded > MAX_VISIBLE_CHANNELS) return;
|
|
82
77
|
|
|
83
78
|
setSelectedChannelId(name);
|
|
@@ -129,10 +124,7 @@ export function ChannelsControllerItemList({
|
|
|
129
124
|
visibleChannelCount={visibleChannelCount}
|
|
130
125
|
toggleVisibility={() => {
|
|
131
126
|
const newVisible = !isBrightfieldVisible;
|
|
132
|
-
setChannelVisibility(
|
|
133
|
-
BRIGHTFIELD_GROUP_ID as keyof ChannelsStateColumns,
|
|
134
|
-
newVisible,
|
|
135
|
-
);
|
|
127
|
+
setChannelVisibility(BRIGHTFIELD_GROUP_ID as keyof ChannelsStateColumns, newVisible);
|
|
136
128
|
|
|
137
129
|
if (!newVisible && selectedChannelId === BRIGHTFIELD_GROUP_ID) {
|
|
138
130
|
setSelectedChannelId(null);
|
package/app/components/.client/ImageViewer/components/ChannelsController/ColorPicker/ColorPicker.tsx
CHANGED
|
@@ -43,9 +43,7 @@ export function ColorPicker({ color, onColorChange }: ColorPickerProps) {
|
|
|
43
43
|
|
|
44
44
|
<PopoverContent placement="bottom start" data-theme="dark">
|
|
45
45
|
<RacColorPicker
|
|
46
|
-
value={parseColor(
|
|
47
|
-
`rgb(${color[0]}, ${color[1]}, ${color[2]})`,
|
|
48
|
-
).toFormat("hsb")}
|
|
46
|
+
value={parseColor(`rgb(${color[0]}, ${color[1]}, ${color[2]})`).toFormat("hsb")}
|
|
49
47
|
onChange={(color) => {
|
|
50
48
|
const rgb = color.toFormat("rgb");
|
|
51
49
|
onColorChange([
|
|
@@ -78,12 +76,7 @@ export function ColorPicker({ color, onColorChange }: ColorPickerProps) {
|
|
|
78
76
|
<ColorThumb className="block w-4 h-4 rounded-full border-2 border-white shadow-md focus-visible:outline-2 focus-visible:outline-(--color-border-focus)" />
|
|
79
77
|
</ColorArea>
|
|
80
78
|
|
|
81
|
-
<ColorSlider
|
|
82
|
-
colorSpace="hsb"
|
|
83
|
-
channel="hue"
|
|
84
|
-
className="w-full"
|
|
85
|
-
aria-label="Hue"
|
|
86
|
-
>
|
|
79
|
+
<ColorSlider colorSpace="hsb" channel="hue" className="w-full" aria-label="Hue">
|
|
87
80
|
<SliderTrack className="relative h-3 rounded touch-none">
|
|
88
81
|
<ColorThumb className="block w-4 h-4 rounded-full border-2 border-white shadow-md top-1/2 focus-visible:outline-2 focus-visible:outline-(--color-border-focus)" />
|
|
89
82
|
</SliderTrack>
|
package/app/components/.client/ImageViewer/components/ChannelsController/ColorPicker/ColorSwatch.tsx
CHANGED
|
@@ -9,10 +9,7 @@ interface ColorSwatchProps extends ButtonProps {
|
|
|
9
9
|
|
|
10
10
|
export function ColorSwatch({ color, ...props }: ColorSwatchProps) {
|
|
11
11
|
return (
|
|
12
|
-
<Button
|
|
13
|
-
className="group cursor-pointer flex items-center justify-center"
|
|
14
|
-
{...props}
|
|
15
|
-
>
|
|
12
|
+
<Button className="group cursor-pointer flex items-center justify-center" {...props}>
|
|
16
13
|
<div
|
|
17
14
|
className={`
|
|
18
15
|
w-5 h-5 m-1 rounded-full border-2
|
|
@@ -11,9 +11,7 @@ export const DomainSlider = ({ domain }: { domain: ByteDomain }) => {
|
|
|
11
11
|
const setContrastLimits = useViewerStore(select.setContrastLimits);
|
|
12
12
|
const [min, max] = domain;
|
|
13
13
|
|
|
14
|
-
const color = selectedChannel
|
|
15
|
-
? rgb(selectedChannel.color)
|
|
16
|
-
: "var(--color-text-tertiary)";
|
|
14
|
+
const color = selectedChannel ? rgb(selectedChannel.color) : "var(--color-text-tertiary)";
|
|
17
15
|
|
|
18
16
|
return (
|
|
19
17
|
<div className="h-0">
|
|
@@ -45,25 +45,23 @@ export function Histogram() {
|
|
|
45
45
|
<div ref={ref} className="top-0 overflow-hidden px-3 pt-3">
|
|
46
46
|
<div className="p-2 pb-0 bg-[var(--color-surface-subtle)] rounded overflow-visible">
|
|
47
47
|
<svg width={width} height={height}>
|
|
48
|
-
{channelConfigs.map(
|
|
49
|
-
(
|
|
50
|
-
if (!isVisible) return null;
|
|
48
|
+
{channelConfigs.map(({ histogram, color, contrastLimits, isVisible }, channelIndex) => {
|
|
49
|
+
if (!isVisible) return null;
|
|
51
50
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
)}
|
|
51
|
+
return (
|
|
52
|
+
<HistogramChannel
|
|
53
|
+
key={channelIndex}
|
|
54
|
+
channelIndex={channelIndex}
|
|
55
|
+
maxLogValue={maxLogValue}
|
|
56
|
+
width={width}
|
|
57
|
+
height={height}
|
|
58
|
+
range={maxDomain}
|
|
59
|
+
histogram={histogram}
|
|
60
|
+
color={rgb(color)}
|
|
61
|
+
contrastLimit={contrastLimits}
|
|
62
|
+
/>
|
|
63
|
+
);
|
|
64
|
+
})}
|
|
67
65
|
</svg>
|
|
68
66
|
|
|
69
67
|
<DomainSlider domain={[0, maxDomain]} />
|
package/app/components/.client/ImageViewer/components/ChannelsController/HistogramChannel.tsx
CHANGED
|
@@ -6,8 +6,7 @@ const normalizeY = (value: number, maxLogValue: number, height: number) => {
|
|
|
6
6
|
return height - normalizedY;
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
-
const normalizeX = (index: number, length: number, width: number) =>
|
|
10
|
-
(index / (length - 1)) * width;
|
|
9
|
+
const normalizeX = (index: number, length: number, width: number) => (index / (length - 1)) * width;
|
|
11
10
|
|
|
12
11
|
interface HistogramChannelProps {
|
|
13
12
|
channelIndex: number;
|
|
@@ -76,12 +75,7 @@ export const HistogramChannel = ({
|
|
|
76
75
|
fill={color}
|
|
77
76
|
fillOpacity={0.1}
|
|
78
77
|
/>
|
|
79
|
-
<polygon
|
|
80
|
-
points={points}
|
|
81
|
-
fill={color}
|
|
82
|
-
fillOpacity={0.5}
|
|
83
|
-
clipPath={`url(#${id})`}
|
|
84
|
-
/>
|
|
78
|
+
<polygon points={points} fill={color} fillOpacity={0.5} clipPath={`url(#${id})`} />
|
|
85
79
|
</g>
|
|
86
80
|
);
|
|
87
81
|
};
|
|
@@ -9,18 +9,14 @@ import { useViewerStore } from "../../state/store/ViewerStoreContext";
|
|
|
9
9
|
export function MinMaxSettings() {
|
|
10
10
|
const selectedChannel = useViewerStore(select.selectedChannel);
|
|
11
11
|
const setContrastLimits = useViewerStore(select.setContrastLimits);
|
|
12
|
-
const resetContrastLimits = useViewerStore(
|
|
13
|
-
(state) => state.resetContrastLimits,
|
|
14
|
-
);
|
|
12
|
+
const resetContrastLimits = useViewerStore((state) => state.resetContrastLimits);
|
|
15
13
|
|
|
16
14
|
// Local state only used while editing (null = not editing, use store value)
|
|
17
15
|
const [editingMin, setEditingMin] = useState<string | null>(null);
|
|
18
16
|
const [editingMax, setEditingMax] = useState<string | null>(null);
|
|
19
17
|
|
|
20
|
-
const minValue =
|
|
21
|
-
|
|
22
|
-
const maxValue =
|
|
23
|
-
editingMax ?? String(selectedChannel?.contrastLimits[1] ?? 0);
|
|
18
|
+
const minValue = editingMin ?? String(selectedChannel?.contrastLimits[0] ?? 0);
|
|
19
|
+
const maxValue = editingMax ?? String(selectedChannel?.contrastLimits[1] ?? 0);
|
|
24
20
|
|
|
25
21
|
const commitValue = (type: "min" | "max", value: string) => {
|
|
26
22
|
// Clear editing state first
|
|
@@ -54,10 +50,7 @@ export function MinMaxSettings() {
|
|
|
54
50
|
setContrastLimits(newLimits);
|
|
55
51
|
};
|
|
56
52
|
|
|
57
|
-
const handleKeyDown = (
|
|
58
|
-
e: React.KeyboardEvent<HTMLInputElement>,
|
|
59
|
-
type: "min" | "max",
|
|
60
|
-
) => {
|
|
53
|
+
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>, type: "min" | "max") => {
|
|
61
54
|
if (e.key === "Enter") {
|
|
62
55
|
e.currentTarget.blur();
|
|
63
56
|
} else if (e.key === "Escape") {
|
|
@@ -73,10 +66,8 @@ export function MinMaxSettings() {
|
|
|
73
66
|
|
|
74
67
|
const isResetDisabled =
|
|
75
68
|
!selectedChannel ||
|
|
76
|
-
(selectedChannel.contrastLimits[0] ===
|
|
77
|
-
selectedChannel.contrastLimitsInitial[
|
|
78
|
-
selectedChannel.contrastLimits[1] ===
|
|
79
|
-
selectedChannel.contrastLimitsInitial[1]);
|
|
69
|
+
(selectedChannel.contrastLimits[0] === selectedChannel.contrastLimitsInitial[0] &&
|
|
70
|
+
selectedChannel.contrastLimits[1] === selectedChannel.contrastLimitsInitial[1]);
|
|
80
71
|
|
|
81
72
|
return (
|
|
82
73
|
<div className="m-2 flex gap-2 items-center">
|
|
@@ -2,11 +2,7 @@ import { motion, MotionValue } from "motion/react";
|
|
|
2
2
|
|
|
3
3
|
import { useFeatureBarStore } from "./useFeatureBar";
|
|
4
4
|
|
|
5
|
-
export const FeatureBarDragHandle = ({
|
|
6
|
-
motionWidth,
|
|
7
|
-
}: {
|
|
8
|
-
motionWidth: MotionValue<number>;
|
|
9
|
-
}) => {
|
|
5
|
+
export const FeatureBarDragHandle = ({ motionWidth }: { motionWidth: MotionValue<number> }) => {
|
|
10
6
|
const { minWidth, maxWidth } = useFeatureBarStore();
|
|
11
7
|
|
|
12
8
|
return (
|
|
@@ -4,11 +4,7 @@ import { MotionValue } from "motion/react";
|
|
|
4
4
|
|
|
5
5
|
import { useFeatureBarStore } from "./useFeatureBar";
|
|
6
6
|
|
|
7
|
-
export const FeatureBarToggle = ({
|
|
8
|
-
motionWidth,
|
|
9
|
-
}: {
|
|
10
|
-
motionWidth: MotionValue<number>;
|
|
11
|
-
}) => {
|
|
7
|
+
export const FeatureBarToggle = ({ motionWidth }: { motionWidth: MotionValue<number> }) => {
|
|
12
8
|
const { minWidth, setWidth, width } = useFeatureBarStore();
|
|
13
9
|
|
|
14
10
|
return (
|
|
@@ -53,11 +53,7 @@ function FeatureItemInner({
|
|
|
53
53
|
</span>
|
|
54
54
|
</button>
|
|
55
55
|
|
|
56
|
-
{badge && (
|
|
57
|
-
<span className="text-xs text-[var(--color-text-tertiary)]">
|
|
58
|
-
{badge}
|
|
59
|
-
</span>
|
|
60
|
-
)}
|
|
56
|
+
{badge && <span className="text-xs text-[var(--color-text-tertiary)]">{badge}</span>}
|
|
61
57
|
|
|
62
58
|
{onToggleChange && !toggleHidden && (
|
|
63
59
|
<IconButton
|
|
@@ -6,12 +6,8 @@ import { rgb } from "../ChannelsController/ColorPicker/ColorPicker";
|
|
|
6
6
|
import { SplitViewToggle } from "../SplitViewToggle";
|
|
7
7
|
|
|
8
8
|
export function Presets({ children }: { children: React.ReactNode }) {
|
|
9
|
-
const activeChannelsStateIndex = useViewerStore(
|
|
10
|
-
|
|
11
|
-
);
|
|
12
|
-
const setActiveChannelsStateIndex = useViewerStore(
|
|
13
|
-
select.setActiveChannelsStateIndex,
|
|
14
|
-
);
|
|
9
|
+
const activeChannelsStateIndex = useViewerStore(select.activeChannelsStateIndex);
|
|
10
|
+
const setActiveChannelsStateIndex = useViewerStore(select.setActiveChannelsStateIndex);
|
|
15
11
|
|
|
16
12
|
const handleSelectionChange = (key: Key) => {
|
|
17
13
|
setActiveChannelsStateIndex(Number(key));
|
|
@@ -121,11 +117,7 @@ export const PresetLabel = ({ index }: { index: number }) => {
|
|
|
121
117
|
{colors.length > 0 && (
|
|
122
118
|
<div className="mt-auto flex w-full">
|
|
123
119
|
{colors.map((color, i) => (
|
|
124
|
-
<div
|
|
125
|
-
key={i}
|
|
126
|
-
className="h-1.5 flex-1"
|
|
127
|
-
style={{ backgroundColor: color }}
|
|
128
|
-
/>
|
|
120
|
+
<div key={i} className="h-1.5 flex-1" style={{ backgroundColor: color }} />
|
|
129
121
|
))}
|
|
130
122
|
</div>
|
|
131
123
|
)}
|
|
@@ -35,8 +35,7 @@ export const useFeatureBarStore = create<ControlBarStore>()(
|
|
|
35
35
|
maxWidth: 720,
|
|
36
36
|
setWidth: (width: number) => set({ width }, false, "setWidth"),
|
|
37
37
|
listStyle: "grid",
|
|
38
|
-
setListStyle: (listStyle: FeatureBarListStyle) =>
|
|
39
|
-
set({ listStyle }, false, "setListStyle"),
|
|
38
|
+
setListStyle: (listStyle: FeatureBarListStyle) => set({ listStyle }, false, "setListStyle"),
|
|
40
39
|
showCellOutline: true,
|
|
41
40
|
setShowCellOutline: (showCellOutline: boolean) =>
|
|
42
41
|
set({ showCellOutline }, false, "setShowCellOutline"),
|
|
@@ -49,20 +48,19 @@ export const useFeatureBarStore = create<ControlBarStore>()(
|
|
|
49
48
|
acc[id] = values[index];
|
|
50
49
|
return acc;
|
|
51
50
|
},
|
|
52
|
-
{ ...state.pixelValues }
|
|
51
|
+
{ ...state.pixelValues },
|
|
53
52
|
),
|
|
54
53
|
}),
|
|
55
54
|
false,
|
|
56
|
-
"setPixelValues"
|
|
55
|
+
"setPixelValues",
|
|
57
56
|
),
|
|
58
57
|
isExpanded: true,
|
|
59
|
-
setIsExpanded: (isExpanded: boolean) =>
|
|
60
|
-
set({ isExpanded }, false, "setIsExpanded"),
|
|
58
|
+
setIsExpanded: (isExpanded: boolean) => set({ isExpanded }, false, "setIsExpanded"),
|
|
61
59
|
}),
|
|
62
|
-
{ name }
|
|
60
|
+
{ name },
|
|
63
61
|
),
|
|
64
|
-
{ name }
|
|
65
|
-
)
|
|
62
|
+
{ name },
|
|
63
|
+
),
|
|
66
64
|
);
|
|
67
65
|
|
|
68
66
|
export function createFeatureItemStore(name: string) {
|
|
@@ -73,16 +71,16 @@ export function createFeatureItemStore(name: string) {
|
|
|
73
71
|
isOpen: false,
|
|
74
72
|
setIsOpen: (isOpen: boolean) => set({ isOpen }),
|
|
75
73
|
}),
|
|
76
|
-
{ name }
|
|
74
|
+
{ name },
|
|
77
75
|
),
|
|
78
|
-
{ name }
|
|
79
|
-
)
|
|
76
|
+
{ name },
|
|
77
|
+
),
|
|
80
78
|
);
|
|
81
79
|
}
|
|
82
80
|
|
|
83
|
-
const FeatureItemStoreContext = createContext<UseBoundStore<
|
|
84
|
-
|
|
85
|
-
|
|
81
|
+
const FeatureItemStoreContext = createContext<UseBoundStore<StoreApi<FeatureItemStore>> | null>(
|
|
82
|
+
null,
|
|
83
|
+
);
|
|
86
84
|
|
|
87
85
|
export function FeatureItemStoreProvider({
|
|
88
86
|
name,
|
|
@@ -93,20 +91,13 @@ export function FeatureItemStoreProvider({
|
|
|
93
91
|
}) {
|
|
94
92
|
const [store] = useState(() => createFeatureItemStore(name));
|
|
95
93
|
return (
|
|
96
|
-
<FeatureItemStoreContext.Provider value={store}>
|
|
97
|
-
{children}
|
|
98
|
-
</FeatureItemStoreContext.Provider>
|
|
94
|
+
<FeatureItemStoreContext.Provider value={store}>{children}</FeatureItemStoreContext.Provider>
|
|
99
95
|
);
|
|
100
96
|
}
|
|
101
97
|
|
|
102
98
|
// export function useFeatureItemStore() {
|
|
103
|
-
export const useFeatureItemStore = <T,>(
|
|
104
|
-
selector: (state: FeatureItemStore) => T
|
|
105
|
-
): T => {
|
|
99
|
+
export const useFeatureItemStore = <T,>(selector: (state: FeatureItemStore) => T): T => {
|
|
106
100
|
const store = useContext(FeatureItemStoreContext);
|
|
107
|
-
if (!store)
|
|
108
|
-
throw new Error(
|
|
109
|
-
"useFeatureItemStore must be used within a FeatureItemStoreProvider"
|
|
110
|
-
);
|
|
101
|
+
if (!store) throw new Error("useFeatureItemStore must be used within a FeatureItemStoreProvider");
|
|
111
102
|
return useStore(store, selector);
|
|
112
103
|
};
|
|
@@ -9,14 +9,9 @@ import { useTilesLoading } from "../../../utils/useTilesLoading";
|
|
|
9
9
|
|
|
10
10
|
const EMPTY_OBJECT = Object.freeze({});
|
|
11
11
|
|
|
12
|
-
type MultiscaleImageLayerProps = ConstructorParameters<
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
export const useChannelsLayer = (
|
|
17
|
-
imagePanelId: number,
|
|
18
|
-
onHover?: (info: PickingInfo) => void
|
|
19
|
-
) => {
|
|
12
|
+
type MultiscaleImageLayerProps = ConstructorParameters<typeof MultiscaleImageLayer>[0];
|
|
13
|
+
|
|
14
|
+
export const useChannelsLayer = (imagePanelId: number, onHover?: (info: PickingInfo) => void) => {
|
|
20
15
|
const dtype = useViewerStore((state) => {
|
|
21
16
|
const type = state.metadata?.Pixels.Type ?? "Uint8";
|
|
22
17
|
return type;
|
|
@@ -31,20 +26,16 @@ export const useChannelsLayer = (
|
|
|
31
26
|
|
|
32
27
|
const channelsStateColumns = useMemo(
|
|
33
28
|
() => mapChannelConfigsToState(channelsState ?? {}),
|
|
34
|
-
[channelsState]
|
|
29
|
+
[channelsState],
|
|
35
30
|
);
|
|
36
31
|
|
|
37
32
|
const extensions = useMemo(() => [new ColorPaletteExtension()], []);
|
|
38
33
|
|
|
39
|
-
const { selections, contrastLimits, colors, channelsVisible } =
|
|
40
|
-
channelsStateColumns;
|
|
34
|
+
const { selections, contrastLimits, colors, channelsVisible } = channelsStateColumns;
|
|
41
35
|
|
|
42
36
|
const rawLoader = useViewerStore(select.loader);
|
|
43
37
|
const setIsChannelsLoading = useViewerStore(select.setIsChannelsLoading);
|
|
44
|
-
const { loadTile, finishTile } = useTilesLoading(
|
|
45
|
-
imagePanelId,
|
|
46
|
-
setIsChannelsLoading
|
|
47
|
-
);
|
|
38
|
+
const { loadTile, finishTile } = useTilesLoading(imagePanelId, setIsChannelsLoading);
|
|
48
39
|
const channelsOpacity = useViewerStore((state) => {
|
|
49
40
|
const channelsStateIndex = state.imagePanels[imagePanelId];
|
|
50
41
|
return state.layersStates[channelsStateIndex]?.channelsOpacity ?? 1;
|
|
@@ -62,9 +53,7 @@ export const useChannelsLayer = (
|
|
|
62
53
|
const wrappedLoader = Object.create(Object.getPrototypeOf(loaderLevel));
|
|
63
54
|
Object.assign(wrappedLoader, loaderLevel);
|
|
64
55
|
|
|
65
|
-
wrappedLoader.getTile = async (
|
|
66
|
-
params: Parameters<typeof originalGetTile>[0]
|
|
67
|
-
) => {
|
|
56
|
+
wrappedLoader.getTile = async (params: Parameters<typeof originalGetTile>[0]) => {
|
|
68
57
|
const tileId = `${params.x}-${params.y}-${params.selection?.z || 0}`;
|
|
69
58
|
|
|
70
59
|
loadTile(tileId);
|
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
InteractionState,
|
|
3
|
-
OrthographicViewState,
|
|
4
|
-
PickingInfo,
|
|
5
|
-
} from "@deck.gl/core";
|
|
1
|
+
import { InteractionState, OrthographicViewState, PickingInfo } from "@deck.gl/core";
|
|
6
2
|
import DeckGL from "@deck.gl/react";
|
|
7
3
|
import { ReactNode, useCallback, useEffect, useMemo, useState } from "react";
|
|
8
4
|
|
|
@@ -57,10 +53,7 @@ const ImagePanelInner = ({
|
|
|
57
53
|
);
|
|
58
54
|
|
|
59
55
|
/** Setup Orthographic View */
|
|
60
|
-
const { ids } = useMemo(
|
|
61
|
-
() => mapChannelConfigsToState(channelsState ?? {}),
|
|
62
|
-
[channelsState],
|
|
63
|
-
);
|
|
56
|
+
const { ids } = useMemo(() => mapChannelConfigsToState(channelsState ?? {}), [channelsState]);
|
|
64
57
|
const setPixelValues = useFeatureBarStore((state) => state.setPixelValues);
|
|
65
58
|
|
|
66
59
|
const onMultiscaleLayerHover = useCallback(
|
|
@@ -73,10 +66,7 @@ const ImagePanelInner = ({
|
|
|
73
66
|
);
|
|
74
67
|
|
|
75
68
|
/* Setup Layers */
|
|
76
|
-
const multiscaleLayer = useChannelsLayer(
|
|
77
|
-
imagePanelId,
|
|
78
|
-
onMultiscaleLayerHover,
|
|
79
|
-
);
|
|
69
|
+
const multiscaleLayer = useChannelsLayer(imagePanelId, onMultiscaleLayerHover);
|
|
80
70
|
const markersLayers = useOverlaysLayers(imagePanelId, setTooltip);
|
|
81
71
|
const layers = [multiscaleLayer, ...markersLayers];
|
|
82
72
|
|
|
@@ -84,17 +74,10 @@ const ImagePanelInner = ({
|
|
|
84
74
|
if (!metadata || !width || !height) return;
|
|
85
75
|
|
|
86
76
|
if (!viewStateActive) {
|
|
87
|
-
const initViewState = calculateViewStateToFit(
|
|
88
|
-
metadata,
|
|
89
|
-
{ width, height },
|
|
90
|
-
{ padding },
|
|
91
|
-
);
|
|
77
|
+
const initViewState = calculateViewStateToFit(metadata, { width, height }, { padding });
|
|
92
78
|
|
|
93
79
|
setViewStateActive(initViewState);
|
|
94
|
-
} else if (
|
|
95
|
-
viewStateActive.width !== width ||
|
|
96
|
-
viewStateActive.height !== height
|
|
97
|
-
) {
|
|
80
|
+
} else if (viewStateActive.width !== width || viewStateActive.height !== height) {
|
|
98
81
|
setViewStateActive({ ...viewStateActive, width, height });
|
|
99
82
|
}
|
|
100
83
|
}, [metadata, padding, setViewStateActive, width, height, viewStateActive]);
|
|
@@ -109,10 +92,7 @@ const ImagePanelInner = ({
|
|
|
109
92
|
const handleInteractionStateChange = useCallback(
|
|
110
93
|
(event: InteractionState) => {
|
|
111
94
|
const { isDragging, isPanning, isZooming } = event;
|
|
112
|
-
if (
|
|
113
|
-
(isDragging || isPanning || isZooming) &&
|
|
114
|
-
activeImagePanelId !== imagePanelId
|
|
115
|
-
) {
|
|
95
|
+
if ((isDragging || isPanning || isZooming) && activeImagePanelId !== imagePanelId) {
|
|
116
96
|
setActiveImagePanelId(imagePanelId);
|
|
117
97
|
}
|
|
118
98
|
},
|
|
@@ -88,19 +88,12 @@ const ImagePreviewInner = ({ viewPort, isInteractive }: ViewProps) => {
|
|
|
88
88
|
);
|
|
89
89
|
};
|
|
90
90
|
|
|
91
|
-
export const ImagePreview = ({
|
|
92
|
-
isInteractive = false,
|
|
93
|
-
}: {
|
|
94
|
-
isInteractive?: boolean;
|
|
95
|
-
}) => {
|
|
91
|
+
export const ImagePreview = ({ isInteractive = false }: { isInteractive?: boolean }) => {
|
|
96
92
|
return (
|
|
97
93
|
<ImageContainer isPreview>
|
|
98
94
|
{(viewPort) => (
|
|
99
95
|
<>
|
|
100
|
-
<ImagePreviewInner
|
|
101
|
-
viewPort={viewPort}
|
|
102
|
-
isInteractive={isInteractive}
|
|
103
|
-
/>
|
|
96
|
+
<ImagePreviewInner viewPort={viewPort} isInteractive={isInteractive} />
|
|
104
97
|
<ActiveViewStatePreview />
|
|
105
98
|
</>
|
|
106
99
|
)}
|
package/app/components/.client/ImageViewer/components/Image/Overlays/AdditivePolygonLayer.tsx
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { SolidPolygonLayer } from "@deck.gl/layers";
|
|
2
2
|
|
|
3
3
|
import { shadersInject } from "./additiveBlending.glsl";
|
|
4
|
-
import {
|
|
5
|
-
type MarkerProps,
|
|
6
|
-
type MarkerLayerProps,
|
|
7
|
-
markerUniforms,
|
|
8
|
-
} from "./markerUniforms";
|
|
4
|
+
import { type MarkerProps, type MarkerLayerProps, markerUniforms } from "./markerUniforms";
|
|
9
5
|
|
|
10
6
|
export class AdditivePolygonLayer extends SolidPolygonLayer<MarkerLayerProps> {
|
|
11
7
|
static layerName = "AdditivePolygonLayer";
|
package/app/components/.client/ImageViewer/components/Image/Overlays/AdditiveScatterplotLayer.tsx
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { ScatterplotLayer } from "@deck.gl/layers";
|
|
2
2
|
|
|
3
3
|
import { shadersInject } from "./additiveBlending.glsl";
|
|
4
|
-
import {
|
|
5
|
-
type MarkerProps,
|
|
6
|
-
type MarkerLayerProps,
|
|
7
|
-
markerUniforms,
|
|
8
|
-
} from "./markerUniforms";
|
|
4
|
+
import { type MarkerProps, type MarkerLayerProps, markerUniforms } from "./markerUniforms";
|
|
9
5
|
|
|
10
6
|
export class AdditiveScatterplotLayer extends ScatterplotLayer<MarkerLayerProps> {
|
|
11
7
|
static layerName = "AdditiveScatterplotLayer";
|