Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 15 additions & 10 deletions src/Plate/EmptyWell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,25 @@ import React from 'react';

import { PALETTE } from '../theme';

import { PLATE_FLOW } from './constants';
import { columnForPosition, rowForPosition } from './utils';
import { CoordinateSystem } from './coordinateSystem';
import { GENERAL_WELL_STYLE } from './wellUtils';

export function EmptyWell(props: { position: number }) {
export function EmptyWell({
position,
coordinateSystem,
}: {
position: number;
coordinateSystem: CoordinateSystem;
}) {
const row = coordinateSystem.rowForRowFlowPosition(position);
const column = coordinateSystem.columnForRowFlowPosition(position);

const { setNodeRef, isOver } = useDroppable({
id: props.position,
id: position,
data: {
coordinates: {
row: rowForPosition(props.position, PLATE_FLOW),
column: columnForPosition(props.position, PLATE_FLOW),
row,
column,
},
},
});
Expand All @@ -30,10 +38,7 @@ export function EmptyWell(props: { position: number }) {
alignItems: 'center',
}}
>
<small>
{rowForPosition(props.position, PLATE_FLOW) +
columnForPosition(props.position, PLATE_FLOW)}
</small>
<small>{row + column}</small>
</div>
);
}
8 changes: 4 additions & 4 deletions src/Plate/FilledWell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@ import React from 'react';

import { PALETTE } from '../theme';

import { PLATE_FLOW } from './constants';
import { CoordinateSystem } from './coordinateSystem';
import { PlateWell } from './types';
import { columnForPosition, rowForPosition } from './utils';
import { GENERAL_WELL_STYLE } from './wellUtils';

export function FilledWell(props: {
coordinateSystem: CoordinateSystem;
well: PlateWell;
position: number;
isDraggable: boolean;
}) {
const data = {
coordinates: {
row: rowForPosition(props.position, PLATE_FLOW),
column: columnForPosition(props.position, PLATE_FLOW),
row: props.coordinateSystem.rowForRowFlowPosition(props.position),
column: props.coordinateSystem.columnForRowFlowPosition(props.position),
},
};

Expand Down
12 changes: 8 additions & 4 deletions src/Plate/RowLabel.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import React from 'react';

import { PLATE_FLOW } from './constants';
import { rowForPosition } from './utils';
import { CoordinateSystem } from './coordinateSystem';

export function RowLabel(props: { position: number }) {
export function RowLabel(props: {
position: number;
coordinateSystem: CoordinateSystem;
}) {
return (
<span
style={{
Expand All @@ -12,7 +14,9 @@ export function RowLabel(props: { position: number }) {
alignItems: 'center',
}}
>
<strong>{rowForPosition(props.position, PLATE_FLOW)}</strong>
<strong>
{props.coordinateSystem.rowForRowFlowPosition(props.position)}
</strong>
</span>
);
}
23 changes: 16 additions & 7 deletions src/Plate/Well.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
import { Maybe } from '@mll-lab/js-utils';
import React from 'react';

import { EmptyWell } from './EmptyWell';
import { FilledWell } from './FilledWell';
import { CoordinateSystem } from './coordinateSystem';
import { PlateWell } from './types';

export function Well(props: {
export function Well({
coordinateSystem,
position,
well,
isDraggable,
}: {
coordinateSystem: CoordinateSystem;
position: number;
well?: PlateWell;
well: Maybe<PlateWell>;
isDraggable: boolean;
}) {
return props.well?.content ? (
return well?.content ? (
<FilledWell
well={props.well}
position={props.position}
isDraggable={props.isDraggable}
well={well}
position={position}
isDraggable={isDraggable}
coordinateSystem={coordinateSystem}
/>
) : (
<EmptyWell position={props.position} />
<EmptyWell position={position} coordinateSystem={coordinateSystem} />
);
}
4 changes: 3 additions & 1 deletion src/Plate/constants.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { range } from 'lodash';

import { Coordinates, FlowDirection } from './types';
import { Coordinates } from '../../types';

Check failure on line 3 in src/Plate/constants.ts

View workflow job for this annotation

GitHub Actions / typecheck

Cannot find module '../../types' or its corresponding type declarations.

import { FlowDirection } from './types';

const TUBE_COUNT = 96;
export const WELLS = range(1, TUBE_COUNT + 1);
Expand Down
64 changes: 64 additions & 0 deletions src/Plate/coordinateSystem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { range } from 'lodash';

import {
CoordinateColumn,
CoordinateColumns,
PlateWell,
CoordinateRow,
CoordinateRows,
} from './types';

export abstract class CoordinateSystem {
abstract rows(): CoordinateRows;

abstract columns(): CoordinateColumns;

rowForRowFlowPosition(position: number): CoordinateRow {
return this.rows()[Math.floor((position - 1) / this.columnsCount())];

Check failure on line 17 in src/Plate/coordinateSystem.ts

View workflow job for this annotation

GitHub Actions / typecheck

Type 'CoordinateRow | undefined' is not assignable to type 'CoordinateRow'.
}

rowForColumnFlowPosition(position: number): CoordinateRow {
return this.rows()[(position - 1) % this.rowsCount()];

Check failure on line 21 in src/Plate/coordinateSystem.ts

View workflow job for this annotation

GitHub Actions / typecheck

Type 'CoordinateRow | undefined' is not assignable to type 'CoordinateRow'.
}

columnForRowFlowPosition(position: number): CoordinateColumn {
return this.columns()[(position - 1) % this.columnsCount()];

Check failure on line 25 in src/Plate/coordinateSystem.ts

View workflow job for this annotation

GitHub Actions / typecheck

Type 'CoordinateColumn | undefined' is not assignable to type 'CoordinateColumn'.
}

columnForColumnFlowPosition(position: number): CoordinateColumn {
return this.columns()[Math.floor((position - 1) / this.rowsCount())];

Check failure on line 29 in src/Plate/coordinateSystem.ts

View workflow job for this annotation

GitHub Actions / typecheck

Type 'CoordinateColumn | undefined' is not assignable to type 'CoordinateColumn'.
}

positionsCount(): number {
return this.columnsCount() * this.rowsCount();
}

rowsCount(): number {
return this.rows().length;
}

columnsCount(): number {
return this.columns().length;
}

/** Returns every possible position in the coordinate system. */
allPositions(): Array<number> {
return range(1, this.rowsCount() * this.columnsCount() + 1);
}

wellAtPosition(
position: number,
data: Array<PlateWell>,
): PlateWell | undefined {
return data.find(
(well) =>
well.coordinates.row === this.rowForRowFlowPosition(position) &&
well.coordinates.column === this.columnForRowFlowPosition(position),
);
}

/** List of columns, 0-padded to all have the same length. */
paddedColumns(): Array<string> {
return this.columns().map((column) => column.toString().padStart(2, '0'));
}
}
12 changes: 12 additions & 0 deletions src/Plate/coordinateSystem12Well.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { CoordinateSystem } from './coordinateSystem';
import { CoordinateColumns, CoordinateRows } from './types';

export class CoordinateSystem12Well extends CoordinateSystem {
rows(): CoordinateRows {
return ['A', 'B', 'C'];
}

columns(): CoordinateColumns {
return [1, 2, 3, 4];
}
}
12 changes: 12 additions & 0 deletions src/Plate/coordinateSystem96Well.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { CoordinateSystem } from './coordinateSystem';
import { CoordinateColumns, CoordinateRows } from './types';

export class CoordinateSystem96Well extends CoordinateSystem {
rows(): CoordinateRows {
return ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'];
}

columns(): CoordinateColumns {
return [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
}
}
127 changes: 127 additions & 0 deletions src/Plate/coordinates.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { CoordinateSystem } from './coordinateSystem';
import { CoordinatesXXXX, FlowDirection } from './types';

export class Coordinates {
public row: CoordinatesXXXX['row'];

public column: CoordinatesXXXX['column'];

public coordinateSystem: CoordinateSystem;

constructor(
row: CoordinatesXXXX['row'],
column: CoordinatesXXXX['column'],
coordinateSystem: CoordinateSystem,
) {
if (!coordinateSystem.rows().includes(row)) {
throw new Error(
`Expected a row with value of ${coordinateSystem
.rows()
.join(',')}, got ${row}.`,
);
}
this.row = row;

if (!coordinateSystem.columns().includes(column)) {
throw new Error(
`Expected a column with value of ${coordinateSystem
.columns()
.join(',')}, got ${column}.`,
);
}
this.column = column;

this.coordinateSystem = coordinateSystem;
}

static fromString(
coordinateString: string,
coordinateSystem: CoordinateSystem,
): Coordinates {
const rows = coordinateSystem.rows();
const rowsOptions = rows.join('|');

const columns = [
...coordinateSystem.columns(),
...coordinateSystem.paddedColumns(),
];
const columnsOptions = columns.join('|');

const matches = coordinateString.match(
new RegExp(`^(${rowsOptions})(${columnsOptions})`),
);

if (matches === null || matches.length === 0) {
const coordinateSystemClass = coordinateSystem.constructor.name;
throw new Error(
`Expected a coordinate with rows ${JSON.stringify(
rows,
)} and columns ${JSON.stringify(
columns,
)} for ${coordinateSystemClass}, got: ${coordinateString}.`,
);
}

return new this(
matches[1] as CoordinatesXXXX['row'],
Number(matches[2]) as CoordinatesXXXX['column'],
coordinateSystem,
);
}

toString(): string {
return this.row + this.column;
}

static fromPosition(
position: number,
direction: FlowDirection,
coordinateSystem: CoordinateSystem,
): Coordinates {
this.assertPositionInRange(coordinateSystem, position);

switch (direction) {
case 'column':
return new this(
coordinateSystem.rowForColumnFlowPosition(position),
coordinateSystem.columnForColumnFlowPosition(position),
coordinateSystem,
);

case 'row':
return new this(
coordinateSystem.rowForRowFlowPosition(position),
coordinateSystem.columnForRowFlowPosition(position),
coordinateSystem,
);
}
}

position(direction: FlowDirection): number {
const rowIndex = this.coordinateSystem.rows().indexOf(this.row);

const columnIndex = this.coordinateSystem.columns().indexOf(this.column);

switch (direction) {
case 'row':
return (
rowIndex * this.coordinateSystem.columns().length + columnIndex + 1
);
case 'column':
return columnIndex * this.coordinateSystem.rows().length + rowIndex + 1;
}
}

static assertPositionInRange(
coordinateSystem: CoordinateSystem,
position: number,
): void {
if (!coordinateSystem.allPositions().includes(position)) {
throw new Error(
`Expected a position between ${
coordinateSystem.allPositions()[0]
} - ${coordinateSystem.positionsCount()}, got: ${position}.`,
);
}
}
}
Loading
Loading