diff --git a/src/components/LayerController/AxisSlider.tsx b/src/components/LayerController/AxisSlider.tsx
index a73600c..9eeea3f 100644
--- a/src/components/LayerController/AxisSlider.tsx
+++ b/src/components/LayerController/AxisSlider.tsx
@@ -2,8 +2,8 @@ import { Divider, Grid, Typography } from "@material-ui/core";
import { Slider } from "@material-ui/core";
import { withStyles } from "@material-ui/styles";
import { useAtom, useAtomValue } from "jotai";
+import * as React from "react";
import type { ChangeEvent } from "react";
-import React, { useState, useEffect } from "react";
import type { ControllerProps } from "../../state";
import DimensionOptions from "./AxisOptions";
@@ -32,13 +32,13 @@ function AxisSlider({ sourceAtom, layerAtom, axisIndex, max }: ControllerProps
{
+ React.useEffect(() => {
// Use first channel to get initial value of slider - can be undefined on first render
setValue(layer.layerProps.selections[0] ? layer.layerProps.selections[0][axisIndex] : 1);
- }, [layer.layerProps.selections]);
+ }, [layer.layerProps.selections, axisIndex]);
const handleRelease = () => {
setLayer((prev) => {
diff --git a/src/components/Viewer.tsx b/src/components/Viewer.tsx
index 93cb5eb..f2fef3f 100644
--- a/src/components/Viewer.tsx
+++ b/src/components/Viewer.tsx
@@ -4,11 +4,16 @@ import { type WritableAtom, useAtom } from "jotai";
import { useAtomValue } from "jotai";
import * as React from "react";
-import type { LayerState, ViewState } from "../state";
+import type { LayerProps } from "@deck.gl/core/lib/layer";
+import type { ZarrPixelSource } from "../ZarrPixelSource";
+import type { ViewState } from "../state";
import { layerAtoms } from "../state";
import { fitBounds, isInterleaved } from "../utils";
-function getLayerSize(props: LayerState["layerProps"]) {
+type Data = { loader: ZarrPixelSource; rows: number; columns: number };
+type VizarrLayer = Layer & Data>;
+
+function getLayerSize(props: Data) {
const { loader } = props;
const [base, maxZoom] = Array.isArray(loader) ? [loader[0], loader.length] : [loader, 0];
const interleaved = isInterleaved(base.shape);
@@ -24,17 +29,18 @@ function getLayerSize(props: LayerState["layerProps"]) {
}
function WrappedViewStateDeck(props: {
- layers: Layer[];
+ layers: Array;
viewStateAtom: WritableAtom;
}) {
const [viewState, setViewState] = useAtom(props.viewStateAtom);
const deckRef = React.useRef(null);
+ const firstLayerProps = props.layers[0]?.props;
// If viewState hasn't been updated, use the first loader to guess viewState
// TODO: There is probably a better place / way to set the intital view and this is a hack.
- if (deckRef.current && !viewState && props.layers[0]?.props?.loader) {
+ if (deckRef.current && !viewState && firstLayerProps?.loader) {
const { deck } = deckRef.current;
- const { width, height, maxZoom } = getLayerSize(props.layers[0].props);
+ const { width, height, maxZoom } = getLayerSize(firstLayerProps);
const padding = deck.width < 400 ? 10 : deck.width < 600 ? 30 : 50; // Adjust depending on viewport width.
const bounds = fitBounds([width, height], [deck.width, deck.height], maxZoom, padding);
setViewState(bounds);
@@ -59,10 +65,11 @@ function WrappedViewStateDeck(props: {
function Viewer({ viewStateAtom }: { viewStateAtom: WritableAtom }) {
const layerConstructors = useAtomValue(layerAtoms);
- const layers = layerConstructors.map((layer) => {
+ // @ts-expect-error - Viv types are giving up an issue
+ const layers: Array = layerConstructors.map((layer) => {
return !layer.on ? null : new layer.Layer(layer.layerProps);
});
- return []} />;
+ return ;
}
export default Viewer;
diff --git a/src/gridLayer.ts b/src/gridLayer.ts
index c438600..7fe0a3f 100644
--- a/src/gridLayer.ts
+++ b/src/gridLayer.ts
@@ -86,7 +86,7 @@ function refreshGridData(props: GridLayerProps) {
return pMap(loaders, mapper, { concurrency });
}
-export default class GridLayer extends CompositeLayer {
+export default class GridLayer extends CompositeLayer & GridLayerProps> {
initializeState() {
this.state = { gridData: [], width: 0, height: 0 };
refreshGridData(this.props).then((gridData) => {
diff --git a/src/index.tsx b/src/index.tsx
index 3f1ca3e..4f2114c 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -46,7 +46,7 @@ export function createViewer(element: HTMLElement, options: { menuOpen?: boolean
on: emitter.on.bind(emitter),
destroy: () => root.unmount(),
}),
- [],
+ [setViewState, addImage],
);
React.useEffect(() => {
if (ref.current) {
diff --git a/src/ome.ts b/src/ome.ts
index c2f23b1..66f0cd9 100644
--- a/src/ome.ts
+++ b/src/ome.ts
@@ -1,7 +1,7 @@
import type { Readable } from "@zarrita/storage";
import pMap from "p-map";
import * as zarr from "zarrita";
-import type { ImageLayerConfig, SourceData } from "./state";
+import type { ImageLayerConfig, OnClickData, SourceData } from "./state";
import { ZarrPixelSource } from "./ZarrPixelSource";
import * as utils from "./utils";
@@ -105,7 +105,7 @@ export async function loadWell(
sourceData.rows = rows;
sourceData.columns = cols;
- sourceData.onClick = (info: Record & { gridCoord?: { row: number; column: number } }) => {
+ sourceData.onClick = (info: OnClickData) => {
let gridCoord = info.gridCoord;
if (!gridCoord) {
return;
@@ -222,7 +222,7 @@ export async function loadPlate(
columns: columns.length,
};
// Us onClick from image config or Open Well in new window
- sourceData.onClick = (info: Record & { gridCoord?: { row: number; column: number } }) => {
+ sourceData.onClick = (info: OnClickData) => {
let gridCoord = info.gridCoord;
if (!gridCoord) {
return;
diff --git a/src/state.ts b/src/state.ts
index 6706f62..4eefd0a 100644
--- a/src/state.ts
+++ b/src/state.ts
@@ -38,7 +38,7 @@ interface BaseConfig {
opacity?: number;
acquisition?: string;
model_matrix?: string | number[];
- onClick?: (e: any) => void;
+ onClick?: (e: unknown) => void;
}
export interface MultichannelConfig extends BaseConfig {
@@ -57,6 +57,10 @@ export interface SingleChannelConfig extends BaseConfig {
export type ImageLayerConfig = MultichannelConfig | SingleChannelConfig;
+export type OnClickData = Record & {
+ gridCoord?: { row: number; column: number };
+};
+
export type SourceData = {
loader: ZarrPixelSource[];
loaders?: GridLoader[]; // for OME plates
@@ -77,7 +81,7 @@ export type SourceData = {
};
model_matrix: Matrix4;
axis_labels: string[];
- onClick?: (e: any) => void;
+ onClick?: (e: OnClickData) => void;
};
export type VivProps = ConstructorParameters[0];
@@ -92,7 +96,7 @@ export interface BaseLayerProps {
selections: number[][];
modelMatrix: Matrix4;
contrastLimitsRange: [min: number, max: number][];
- onClick?: (e: any) => void;
+ onClick?: (e: OnClickData) => void;
}
interface MultiscaleImageLayerProps extends BaseLayerProps {
@@ -109,7 +113,8 @@ type LayerMap = {
grid: [GridLayer, { loader: ZarrPixelSource | ZarrPixelSource[] } & GridLayerProps];
};
-export type LayerCtr = new (...args: any[]) => T;
+// biome-ignore lint/suspicious/noExplicitAny: Need a catch all for layer types
+export type LayerCtr = new (...args: Array) => T;
export type LayerState = {
Layer: LayerCtr;
layerProps: LayerMap[T][1];
@@ -118,7 +123,7 @@ export type LayerState = T & { id: string };
-export type ControllerProps = {
+export type ControllerProps = {
sourceAtom: PrimitiveAtom>;
layerAtom: PrimitiveAtom>;
} & T;
diff --git a/src/utils.ts b/src/utils.ts
index a5ed9c0..7d8e983 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -28,15 +28,14 @@ async function normalizeStore(source: string | Readable): Promise res.json()),
]);
store = ReferenceStore.fromSpec(json);
} else {
const url = new URL(source);
- // @ts-expect-error - pathname always starts with '/'
- path = url.pathname;
+ // grab the path and then set the URL to the root
+ path = ensureAbosolutePath(url.pathname);
url.pathname = "/";
store = new zarr.FetchStore(url.href);
}
@@ -48,6 +47,12 @@ async function normalizeStore(source: string | Readable): Promise rstrip(s as string, "/"))
+ .filter((s) => s !== undefined)
+ .map((s) => rstrip(s, "/"))
.join("/");
}
@@ -226,7 +231,6 @@ export function fitBounds(
return { zoom, target: [width / 2, height / 2] };
}
-// prettier-ignore
type Array16 = [
number,
number,
@@ -319,7 +323,7 @@ export async function calcConstrastLimits(
*/
export function defer() {
let resolve: (value: T | PromiseLike) => void;
- let reject: (reason?: any) => void;
+ let reject: (reason?: unknown) => void;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
diff --git a/tsconfig.json b/tsconfig.json
index e389862..feac920 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -3,18 +3,15 @@
"compilerOptions": {
"module": "esnext",
"target": "esnext",
- "moduleResolution": "node",
- "jsx": "preserve",
- "baseUrl": "./",
+ "moduleResolution": "bundler",
"noEmit": true,
"strict": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
- "resolveJsonModule": true,
- "allowSyntheticDefaultImports": true,
"verbatimModuleSyntax": true,
+ "jsx": "preserve",
"paths": {
- "*": ["*", "node_modules/@danmarshall/deckgl-typings/*"]
+ "*": ["./node_modules/@danmarshall/deckgl-typings/*"]
}
}
}