Skip to content

Commit

Permalink
Replace recoil with jotai (#98)
Browse files Browse the repository at this point in the history
  • Loading branch information
manzt authored Jun 17, 2021
1 parent 213051f commit aba4b97
Show file tree
Hide file tree
Showing 22 changed files with 176 additions and 173 deletions.
10 changes: 5 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
"@material-ui/icons": "^4.9.1",
"deck.gl": "^8.4.3",
"imjoy-rpc": "^0.2.23",
"jotai": "^1.0.0",
"p-map": "^4.0.0",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"recoil": "0.0.13",
"reference-spec-reader": "^0.1.1",
"zarr": "^0.4.0"
},
Expand Down
2 changes: 1 addition & 1 deletion snowpack.config.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/** @type {import("snowpack").SnowpackUserConfig } */
const pkg = require('./package.json');

// pkg version avaiable in app via import.meta.env.SNOWPACK_PUBLIC_PACKAGE_VERSION
process.env.SNOWPACK_PUBLIC_PACKAGE_VERSION = pkg.version;

/** @type {import("snowpack").SnowpackUserConfig } */
module.exports = {
mount: {
public: '/',
Expand Down
11 changes: 5 additions & 6 deletions src/components/LayerController/AcquisitionController.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import React from 'react';
import { Grid, NativeSelect } from '@material-ui/core';
import { useRecoilValue } from 'recoil';
import { useAtomValue } from 'jotai/utils';
import type { ChangeEvent } from 'react';
import type { ControllerProps } from '../../state';

import { sourceInfoState } from '../../state';

function AcquisitionController({ layerId }: { layerId: string }): JSX.Element | null {
const sourceInfo = useRecoilValue(sourceInfoState);
const { acquisitionId, acquisitions } = sourceInfo[layerId];
function AcquisitionController({ sourceAtom }: ControllerProps) {
const sourceData = useAtomValue(sourceAtom);
const { acquisitionId, acquisitions } = sourceData;

if (!acquisitions) {
return null;
Expand Down
20 changes: 10 additions & 10 deletions src/components/LayerController/AddChannelButton.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import React, { useState } from 'react';
import type { MouseEvent, ChangeEvent } from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';
import { useAtom } from 'jotai';
import { useAtomValue } from 'jotai/utils';
import { IconButton, Popover, Paper, Typography, Divider, NativeSelect } from '@material-ui/core';
import { Add } from '@material-ui/icons';

import { layerStateFamily, sourceInfoState } from '../../state';
import { hexToRGB, MAX_CHANNELS } from '../../utils';
import type { ControllerProps } from '../../state';

function AddChannelButton({ layerId }: { layerId: string }): JSX.Element {
const sourceInfo = useRecoilValue(sourceInfoState);
const [layer, setLayer] = useRecoilState(layerStateFamily(layerId));
function AddChannelButton({ sourceAtom, layerAtom }: ControllerProps) {
const sourceData = useAtomValue(sourceAtom);
const [layer, setLayer] = useAtom(layerAtom);
const [anchorEl, setAnchorEl] = useState<null | Element>(null);
const layerInfo = sourceInfo[layerId];

const handleClick = (event: MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
Expand All @@ -27,7 +27,7 @@ function AddChannelButton({ layerId }: { layerId: string }): JSX.Element {
channel_axis,
colors,
contrast_limits,
} = layerInfo;
} = sourceData;
handleClose();
const channelIndex = +event.target.value;
const channelSelection = [...selection];
Expand Down Expand Up @@ -55,9 +55,9 @@ function AddChannelButton({ layerId }: { layerId: string }): JSX.Element {
});
};

const { names } = sourceInfo[layerId];
const { names } = sourceData;
const open = Boolean(anchorEl);
const id = open ? `layer-${layerId}-add-channel` : undefined;
const id = open ? `layer-${sourceData.id}-add-channel` : undefined;
return (
<>
<IconButton
Expand Down Expand Up @@ -93,7 +93,7 @@ function AddChannelButton({ layerId }: { layerId: string }): JSX.Element {
<NativeSelect
fullWidth
style={{ fontSize: '0.7em' }}
id={`layer-${layerId}-channel-select`}
id={`layer-${sourceData.id}-channel-select`}
onChange={handleChange}
>
<option aria-label="None" value="">
Expand Down
18 changes: 12 additions & 6 deletions src/components/LayerController/AxisOptions.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React, { useState } from 'react';
import type { MouseEvent, ChangeEvent } from 'react';
import { useRecoilState } from 'recoil';
import { useAtom } from 'jotai';
import { useAtomValue } from 'jotai/utils';
import { IconButton, Popover, Paper, Typography, Divider, Input } from '@material-ui/core';
import { withStyles } from '@material-ui/styles';
import { MoreHoriz } from '@material-ui/icons';

import { layerStateFamily } from '../../state';
import type { ControllerProps } from '../../state';

const DenseInput = withStyles({
root: {
Expand All @@ -14,8 +14,14 @@ const DenseInput = withStyles({
},
})(Input);

function AxisOptions({ layerId, axisIndex, max }: { layerId: string; axisIndex: number; max: number }): JSX.Element {
const [layer, setLayer] = useRecoilState(layerStateFamily(layerId));
interface Props {
axisIndex: number;
max: number;
}

function AxisOptions({ sourceAtom, layerAtom, axisIndex, max }: ControllerProps<Props>) {
const sourceData = useAtomValue(sourceAtom);
const [layer, setLayer] = useAtom(layerAtom);
const [anchorEl, setAnchorEl] = useState<null | Element>(null);

const handleClick = (event: MouseEvent<HTMLButtonElement>) => {
Expand Down Expand Up @@ -46,7 +52,7 @@ function AxisOptions({ layerId, axisIndex, max }: { layerId: string; axisIndex:
};

const open = Boolean(anchorEl);
const id = open ? `${axisIndex}-index-${layerId}-options` : undefined;
const id = open ? `${axisIndex}-index-${sourceData.id}-options` : undefined;
const value = layer.layerProps.loaderSelection[0] ? layer.layerProps.loaderSelection[0][axisIndex] : 1;

return (
Expand Down
21 changes: 13 additions & 8 deletions src/components/LayerController/AxisSlider.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Grid, Typography, Divider } from '@material-ui/core';
import { useRecoilState, useRecoilValue } from 'recoil';
import { useAtom } from 'jotai';
import { useAtomValue } from 'jotai/utils';
import type { ChangeEvent } from 'react';
import React, { useState, useEffect } from 'react';
import { Slider } from '@material-ui/core';
import { withStyles } from '@material-ui/styles';
import DimensionOptions from './AxisOptions';

import { layerStateFamily, sourceInfoState } from '../../state';
import type { ControllerProps } from '../../state';

const DenseSlider = withStyles({
root: {
Expand All @@ -19,10 +19,15 @@ const DenseSlider = withStyles({
},
})(Slider);

function AxisSlider({ layerId, axisIndex, max }: { layerId: string; axisIndex: number; max: number }): JSX.Element {
const [layer, setLayer] = useRecoilState(layerStateFamily(layerId));
const sourceInfo = useRecoilValue(sourceInfoState);
const { axis_labels } = sourceInfo[layerId];
interface Props {
axisIndex: number;
max: number;
}

function AxisSlider({ sourceAtom, layerAtom, axisIndex, max }: ControllerProps<Props>) {
const [layer, setLayer] = useAtom(layerAtom);
const sourceData = useAtomValue(sourceAtom);
const { axis_labels } = sourceData;
let axisLabel = axis_labels[axisIndex];
if (axisLabel === 't' || axisLabel === 'z') {
axisLabel = axisLabel.toUpperCase();
Expand Down Expand Up @@ -65,7 +70,7 @@ function AxisSlider({ layerId, axisIndex, max }: { layerId: string; axisIndex: n
</div>
</Grid>
<Grid item xs={1}>
<DimensionOptions layerId={layerId} axisIndex={axisIndex} max={max} />
<DimensionOptions sourceAtom={sourceAtom} layerAtom={layerAtom} axisIndex={axisIndex} max={max} />
</Grid>
</Grid>
<Grid container justify="space-between">
Expand Down
14 changes: 8 additions & 6 deletions src/components/LayerController/AxisSliders.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React from 'react';
import { Grid, Divider } from '@material-ui/core';
import { useRecoilValue } from 'recoil';
import { useAtomValue } from 'jotai/utils';
import AxisSlider from './AxisSlider';
import { sourceInfoState } from '../../state';
import type { ControllerProps } from '../../state';

function AxisSliders({ layerId }: { layerId: string }): JSX.Element | null {
const sourceInfo = useRecoilValue(sourceInfoState);
const { axis_labels, channel_axis, loader } = sourceInfo[layerId];
function AxisSliders({ sourceAtom, layerAtom }: ControllerProps) {
const sourceData = useAtomValue(sourceAtom);
const { axis_labels, channel_axis, loader } = sourceData;

const sliders = axis_labels
.slice(0, -2) // ignore last two axes, [y,x]
Expand All @@ -16,7 +16,9 @@ function AxisSliders({ layerId }: { layerId: string }): JSX.Element | null {
if (d[2] > 1) return true; // keep if size > 1
return false; // otherwise ignore as well
})
.map(([name, i, size]) => <AxisSlider key={name} layerId={layerId} axisIndex={i} max={size - 1} />);
.map(([name, i, size]) => (
<AxisSlider key={name} sourceAtom={sourceAtom} layerAtom={layerAtom} axisIndex={i} max={size - 1} />
));

if (sliders.length === 0) return null;
return (
Expand Down
17 changes: 8 additions & 9 deletions src/components/LayerController/ChannelController.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import React from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';
import { useAtom } from 'jotai';
import { useAtomValue } from 'jotai/utils';
import type { ChangeEvent } from 'react';
import { Slider, Typography, Grid, IconButton } from '@material-ui/core';
import { RadioButtonChecked, RadioButtonUnchecked } from '@material-ui/icons';

import ChannelOptions from './ChannelOptions';
import { layerStateFamily, sourceInfoState } from '../../state';
import type { ControllerProps } from '../../state';

interface ChannelConfig {
layerId: string;
channelIndex: number;
}

function ChannelController({ layerId, channelIndex }: ChannelConfig): JSX.Element {
const sourceInfo = useRecoilValue(sourceInfoState);
const [layer, setLayer] = useRecoilState(layerStateFamily(layerId));
function ChannelController({ sourceAtom, layerAtom, channelIndex }: ControllerProps<ChannelConfig>) {
const sourceData = useAtomValue(sourceAtom);
const [layer, setLayer] = useAtom(layerAtom);

const handleContrastChange = (_: ChangeEvent<unknown>, v: number | number[]) => {
setLayer((prev) => {
Expand All @@ -40,7 +39,7 @@ function ChannelController({ layerId, channelIndex }: ChannelConfig): JSX.Elemen
const on = channelIsOn[channelIndex];
const [min, max] = contrastLimits[channelIndex];

const { channel_axis, names } = sourceInfo[layerId];
const { channel_axis, names } = sourceData;
const selection = loaderSelection[channelIndex];
const nameIndex = Number.isInteger(channel_axis) ? selection[channel_axis as number] : 0;
const label = names[nameIndex];
Expand All @@ -55,7 +54,7 @@ function ChannelController({ layerId, channelIndex }: ChannelConfig): JSX.Elemen
</div>
</Grid>
<Grid item xs={1}>
<ChannelOptions layerId={layerId} channelIndex={channelIndex} />
<ChannelOptions sourceAtom={sourceAtom} layerAtom={layerAtom} channelIndex={channelIndex} />
</Grid>
</Grid>
<Grid container justify="space-between">
Expand Down
22 changes: 13 additions & 9 deletions src/components/LayerController/ChannelOptions.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React, { useState } from 'react';
import type { MouseEvent, ChangeEvent } from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';
import { useAtom } from 'jotai';
import { useAtomValue } from 'jotai/utils';
import { IconButton, Popover, Paper, Typography, Divider, Input, NativeSelect } from '@material-ui/core';
import { withStyles } from '@material-ui/styles';
import { MoreHoriz, Remove } from '@material-ui/icons';

import { layerStateFamily, sourceInfoState } from '../../state';
import type { ControllerProps } from '../../state';
import ColorPalette from './ColorPalette';

const DenseInput = withStyles({
Expand All @@ -15,11 +15,15 @@ const DenseInput = withStyles({
},
})(Input);

function ChannelOptions({ layerId, channelIndex }: { layerId: string; channelIndex: number }): JSX.Element {
const sourceInfo = useRecoilValue(sourceInfoState);
const [layer, setLayer] = useRecoilState(layerStateFamily(layerId));
interface Props {
channelIndex: number;
}

function ChannelOptions({ sourceAtom, layerAtom, channelIndex }: ControllerProps<Props>) {
const sourceData = useAtomValue(sourceAtom);
const [layer, setLayer] = useAtom(layerAtom);
const [anchorEl, setAnchorEl] = useState<null | Element>(null);
const { channel_axis, names } = sourceInfo[layerId];
const { channel_axis, names } = sourceData;

const handleClick = (event: MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
Expand Down Expand Up @@ -109,7 +113,7 @@ function ChannelOptions({ layerId, channelIndex }: { layerId: string; channelInd
};

const open = Boolean(anchorEl);
const id = open ? `channel-${channelIndex}-${layerId}-options` : undefined;
const id = open ? `channel-${channelIndex}-${sourceData.name}-options` : undefined;
const [min, max] = layer.layerProps.contrastLimits[channelIndex];

return (
Expand Down Expand Up @@ -153,7 +157,7 @@ function ChannelOptions({ layerId, channelIndex }: { layerId: string; channelInd
<NativeSelect
fullWidth
style={{ fontSize: '0.7em' }}
id={`layer-${layerId}-channel-select`}
id={`layer-${sourceData.name}-channel-select`}
onChange={handleSelectionChange}
value={layer.layerProps.loaderSelection[channelIndex][channel_axis as number]}
>
Expand Down
2 changes: 1 addition & 1 deletion src/components/LayerController/ColorPalette.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const useStyles = makeStyles(() => ({
}));

const RGB_COLORS: [string, number[]][] = Object.entries(COLORS).map(([name, hex]) => [name, hexToRGB(hex)]);
function ColorPalette({ handleChange }: { handleChange: (c: number[]) => void }): JSX.Element {
function ColorPalette({ handleChange }: { handleChange: (c: number[]) => void }) {
const classes = useStyles();
return (
<div className={classes.container} aria-label="color-swatch">
Expand Down
16 changes: 10 additions & 6 deletions src/components/LayerController/Content.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import { useAtomValue } from 'jotai/utils';
import { AccordionDetails, Grid, Typography, Divider } from '@material-ui/core';
import { withStyles } from '@material-ui/styles';

Expand All @@ -9,6 +10,7 @@ import AxisSliders from './AxisSliders';
import ChannelController from './ChannelController';

import { range } from '../../utils';
import type { ControllerProps } from '../../state';

const Details = withStyles({
root: {
Expand All @@ -18,35 +20,37 @@ const Details = withStyles({
},
})(AccordionDetails);

function Content({ layerId, nChannels }: { layerId: string; nChannels: number }): JSX.Element {
function Content({ sourceAtom, layerAtom }: ControllerProps) {
const layer = useAtomValue(layerAtom);
const nChannels = layer.layerProps.loaderSelection.length;
return (
<Details>
<Grid container direction="column">
<AcquisitionController layerId={layerId} />
<AcquisitionController sourceAtom={sourceAtom} layerAtom={layerAtom} />
<Grid>
<Grid container justify="space-between">
<Grid item xs={3}>
<Typography variant="caption">opacity:</Typography>
</Grid>
<Grid item xs={8}>
<OpacitySlider layerId={layerId} />
<OpacitySlider sourceAtom={sourceAtom} layerAtom={layerAtom} />
</Grid>
</Grid>
</Grid>
<Divider />
<AxisSliders layerId={layerId} />
<AxisSliders sourceAtom={sourceAtom} layerAtom={layerAtom} />
<Grid container justify="space-between">
<Grid item xs={3}>
<Typography variant="caption">channels:</Typography>
</Grid>
<Grid item xs={1}>
<AddChannelButton layerId={layerId} />
<AddChannelButton sourceAtom={sourceAtom} layerAtom={layerAtom} />
</Grid>
</Grid>
<Divider />
<Grid>
{range(nChannels).map((i) => (
<ChannelController layerId={layerId} channelIndex={i} key={i + layerId} />
<ChannelController sourceAtom={sourceAtom} layerAtom={layerAtom} channelIndex={i} key={i} />
))}
</Grid>
</Grid>
Expand Down
Loading

0 comments on commit aba4b97

Please sign in to comment.