From 018a272d563039e423dd9763f17f59ae4009b33c Mon Sep 17 00:00:00 2001 From: Trevor Manz Date: Sun, 21 Jul 2024 20:34:40 -0400 Subject: [PATCH] Upgrade TypeScript --- src/components/LayerController/AxisSlider.tsx | 8 +++---- src/components/Viewer.tsx | 21 ++++++++++++------- src/gridLayer.ts | 2 +- src/index.tsx | 2 +- src/ome.ts | 6 +++--- src/state.ts | 15 ++++++++----- src/utils.ts | 18 +++++++++------- tsconfig.json | 9 +++----- 8 files changed, 47 insertions(+), 34 deletions(-) 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/*"] } } }