Skip to content

Commit

Permalink
highlight rectangle selected wells
Browse files Browse the repository at this point in the history
  • Loading branch information
brenthagen committed May 13, 2024
1 parent e4df27b commit 832c5b3
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 93 deletions.
9 changes: 1 addition & 8 deletions app/src/organisms/QuickTransferFlow/SelectSourceWells.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,12 @@ export function SelectSourceWells(props: SelectSourceWellsProps): JSX.Element {
>
{state.source != null ? (
<WellSelection
labwareProps={{ definition: state.source }}
definition={state.source}
selectedPrimaryWells={selectedWells}
selectWells={wellGroup => {
setSelectedWells(prevWells => ({ ...prevWells, ...wellGroup }))
}}
deselectWells={wellGroup => {
setSelectedWells(wellGroup)
}}
updateHighlightedWells={wellGroup => {
console.log(wellGroup)
}}
nozzleType={null}
wellContents={{}}
/>
) : null}
</Flex>
Expand Down
8 changes: 4 additions & 4 deletions app/src/organisms/WellSelection/SelectionRect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import * as React from 'react'
import type { DragRect, GenericRect } from './types'

interface SelectionRectProps {
onSelectionMove?: (e: MouseEvent, arg: GenericRect) => void
onSelectionDone?: (e: MouseEvent, arg: GenericRect) => void
onSelectionMove?: (rect: GenericRect) => void
onSelectionDone?: (rect: GenericRect) => void
children?: React.ReactNode
}

Expand Down Expand Up @@ -33,7 +33,7 @@ export function SelectionRect(props: SelectionRectProps): JSX.Element {
yDynamic: e.clientY,
}
const rect = getRect(nextRect)
onSelectionMove && onSelectionMove(e, rect)
onSelectionMove && onSelectionMove(rect)

return nextRect
}
Expand All @@ -50,7 +50,7 @@ export function SelectionRect(props: SelectionRectProps): JSX.Element {
return prevPositions === positions ? null : prevPositions
})
// call onSelectionDone callback with {x0, x1, y0, y1} of final selection rectangle
onSelectionDone && finalRect && onSelectionDone(e, finalRect)
onSelectionDone && finalRect && onSelectionDone(finalRect)
}

const handleMouseDown: React.MouseEventHandler = e => {
Expand Down
120 changes: 41 additions & 79 deletions app/src/organisms/WellSelection/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,16 @@ import {
} from './utils'
import { SelectionRect } from './SelectionRect'

import type { WellMouseEvent, WellFill, WellGroup } from '@opentrons/components'
import type { ContentsByWell, GenericRect, NozzleType } from './types'
import type { WellFill, WellGroup, WellStroke } from '@opentrons/components'
import type { LabwareDefinition2 } from '@opentrons/shared-data'
import type { GenericRect, NozzleType } from './types'

interface WellSelectionProps {
labwareProps: Omit<
React.ComponentProps<typeof LabwareRender>,
'selectedWells'
>
definition: LabwareDefinition2
/** array of primary wells. Overrides labwareProps.selectedWells */
selectedPrimaryWells: WellGroup
selectWells: (wellGroup: WellGroup) => unknown
deselectWells: (wellGroup: WellGroup) => unknown
updateHighlightedWells: (wellGroup: WellGroup) => unknown
nozzleType: NozzleType | null
wellContents: ContentsByWell
}

type ChannelType = 8 | 96
Expand All @@ -43,16 +38,9 @@ const getChannelsFromNozzleType = (nozzleType: NozzleType): ChannelType => {
}

export function WellSelection(props: WellSelectionProps): JSX.Element {
const {
labwareProps,
selectedPrimaryWells,
selectWells,
deselectWells,
updateHighlightedWells,
nozzleType,
wellContents,
} = props
const labwareDef = labwareProps.definition
const { definition, selectedPrimaryWells, selectWells, nozzleType } = props

const [highlightedWells, setHighlightedWells] = React.useState<WellGroup>({})

const _wellsFromSelected: (
selectedWells: WellGroup
Expand All @@ -66,7 +54,7 @@ export function WellSelection(props: WellSelectionProps): JSX.Element {
selectedWells,
(acc: WellGroup, _, wellName: string): WellGroup => {
const wellSet = getWellSetForMultichannel(
labwareDef,
definition,
wellName,
channels
)
Expand All @@ -87,61 +75,36 @@ export function WellSelection(props: WellSelectionProps): JSX.Element {
return _wellsFromSelected(selectedWells)
}

const handleSelectionMove: (e: MouseEvent, rect: GenericRect) => void = (
e,
rect
) => {
if (!e.shiftKey) {
if (nozzleType != null) {
const channels = getChannelsFromNozzleType(nozzleType)
const selectedWells = _getWellsFromRect(rect)
const allWellsForMulti: WellGroup = reduce(
selectedWells,
(acc: WellGroup, _, wellName: string): WellGroup => {
const wellSetForMulti =
getWellSetForMultichannel(labwareDef, wellName, channels) || []
const channelWells = arrayToWellGroup(wellSetForMulti)
return {
...acc,
...channelWells,
}
},
{}
)
updateHighlightedWells(allWellsForMulti)
} else {
updateHighlightedWells(_getWellsFromRect(rect))
}
}
}

const handleSelectionDone: (e: MouseEvent, rect: GenericRect) => void = (
e,
rect
) => {
const wells = _wellsFromSelected(_getWellsFromRect(rect))
if (e.shiftKey) {
deselectWells(wells)
} else {
selectWells(wells)
}
}

const handleMouseEnterWell: (args: WellMouseEvent) => void = args => {
const handleSelectionMove: (rect: GenericRect) => void = rect => {
if (nozzleType != null) {
const channels = getChannelsFromNozzleType(nozzleType)
const wellSet = getWellSetForMultichannel(
labwareDef,
args.wellName,
channels
const selectedWells = _getWellsFromRect(rect)
const allWellsForMulti: WellGroup = reduce(
selectedWells,
(acc: WellGroup, _, wellName: string): WellGroup => {
const wellSetForMulti =
getWellSetForMultichannel(definition, wellName, channels) || []
const channelWells = arrayToWellGroup(wellSetForMulti)
return {
...acc,
...channelWells,
}
},
{}
)
const nextHighlightedWells = arrayToWellGroup(wellSet || [])
nextHighlightedWells && updateHighlightedWells(nextHighlightedWells)
setHighlightedWells(allWellsForMulti)
} else {
updateHighlightedWells({ [args.wellName]: null })
setHighlightedWells(_getWellsFromRect(rect))
}
}

const handleSelectionDone: (rect: GenericRect) => void = rect => {
const wells = _wellsFromSelected(_getWellsFromRect(rect))

selectWells(wells)
setHighlightedWells({})
}

// For rendering, show all wells not just primary wells
const allSelectedWells =
nozzleType != null
Expand All @@ -150,7 +113,7 @@ export function WellSelection(props: WellSelectionProps): JSX.Element {
(acc, _, wellName): WellGroup => {
const channels = getChannelsFromNozzleType(nozzleType)
const wellSet = getWellSetForMultichannel(
labwareDef,
definition,
wellName,
channels
)
Expand All @@ -162,12 +125,17 @@ export function WellSelection(props: WellSelectionProps): JSX.Element {
: selectedPrimaryWells

const wellFill: WellFill = {}
Object.keys(labwareProps.definition.wells).forEach(wellName => {
const wellStroke: WellStroke = {}
Object.keys(definition.wells).forEach(wellName => {
wellFill[wellName] = COLORS.blue35
wellStroke[wellName] = COLORS.transparent
})
Object.keys(allSelectedWells).forEach(wellName => {
wellFill[wellName] = COLORS.blue50
})
Object.keys(highlightedWells).forEach(wellName => {
wellFill[wellName] = COLORS.blue50
})

return (
<SelectionRect
Expand All @@ -176,19 +144,13 @@ export function WellSelection(props: WellSelectionProps): JSX.Element {
>
<RobotCoordinateSpace viewBox="0 0 128 86">
<LabwareRender
{...labwareProps}
definition={definition}
selectedWells={allSelectedWells}
onMouseLeaveWell={() => {
updateHighlightedWells({})
}}
onMouseEnterWell={({ wellName, event }) => {
if (wellContents !== null) {
handleMouseEnterWell({ wellName, event })
}
}}
hideOutline
isInteractive
wellLabelOption={WELL_LABEL_OPTIONS.SHOW_LABEL_INSIDE}
wellFill={wellFill}
wellStroke={wellStroke}
/>
</RobotCoordinateSpace>
</SelectionRect>
Expand Down
6 changes: 5 additions & 1 deletion components/src/hardware-sim/Labware/LabwareRender.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,12 @@ export interface LabwareRenderProps {
onLabwareClick?: () => void
/** Hide labware outline */
hideOutline?: boolean
/** Provides well data attribute */
isInteractive?: boolean
}

export const LabwareRender = (props: LabwareRenderProps): JSX.Element => {
const { gRef, definition, hideOutline } = props
const { gRef, definition, hideOutline, isInteractive } = props

const cornerOffsetFromSlot = definition.cornerOffsetFromSlot
const labwareLoadName = definition.parameters.loadName
Expand Down Expand Up @@ -97,13 +99,15 @@ export const LabwareRender = (props: LabwareRenderProps): JSX.Element => {
transform={`translate(${cornerOffsetFromSlot.x}, ${cornerOffsetFromSlot.y})`}
ref={gRef}
>
{/* TODO(bh, 2024-05-13): refactor rendering of wells - multiple layers of styled wells, DOM ordering determines which are visible */}
<StaticLabware
definition={props.definition}
onMouseEnterWell={props.onMouseEnterWell}
onMouseLeaveWell={props.onMouseLeaveWell}
onLabwareClick={props.onLabwareClick}
highlight={props.highlight}
hideOutline={hideOutline}
isInteractive={isInteractive}
/>
{props.wellStroke != null ? (
<StrokedWells
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ export interface StaticLabwareProps {
onMouseEnterWell?: (e: WellMouseEvent) => unknown
/** Optional callback to be executed when mouse leaves a well element */
onMouseLeaveWell?: (e: WellMouseEvent) => unknown
/** Provides well data attribute */
isInteractive?: boolean
}

const TipDecoration = React.memo(function TipDecoration(props: {
Expand Down Expand Up @@ -55,6 +57,7 @@ export function StaticLabwareComponent(props: StaticLabwareProps): JSX.Element {
definition,
hideOutline = false,
highlight,
isInteractive,
onLabwareClick,
onMouseEnterWell,
onMouseLeaveWell,
Expand All @@ -80,6 +83,7 @@ export function StaticLabwareComponent(props: StaticLabwareProps): JSX.Element {
well={definition.wells[wellName]}
onMouseEnterWell={onMouseEnterWell}
onMouseLeaveWell={onMouseLeaveWell}
isInteractive={isInteractive}
{...(isTiprack
? STYLE_BY_WELL_CONTENTS.tipPresent
: STYLE_BY_WELL_CONTENTS.defaultWell)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export interface WellProps extends StyleProps {
/** Optional callback, called with WellMouseEvent args onMouseOver */
onMouseEnterWell?: (e: WellMouseEvent) => unknown
onMouseLeaveWell?: (e: WellMouseEvent) => unknown
/** Provides well data attribute */
isInteractive?: boolean
}

export function WellComponent(props: WellProps): JSX.Element {
Expand All @@ -27,10 +29,10 @@ export function WellComponent(props: WellProps): JSX.Element {
fill = COLORS.white,
onMouseEnterWell,
onMouseLeaveWell,
isInteractive = onMouseEnterWell != null || onMouseLeaveWell != null,
} = props
const { x, y } = well

const isInteractive = onMouseEnterWell != null || onMouseLeaveWell != null
const pointerEvents: React.CSSProperties['pointerEvents'] = isInteractive
? 'auto'
: 'none'
Expand Down

0 comments on commit 832c5b3

Please sign in to comment.