Skip to content

Commit

Permalink
create select by and starting well ui elements
Browse files Browse the repository at this point in the history
  • Loading branch information
brenthagen committed May 23, 2024
1 parent a65fa51 commit b7b9e14
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 57 deletions.
7 changes: 6 additions & 1 deletion app/src/assets/localization/en/quick_transfer.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
{
"add_or_remove_columns": "add or remove columns",
"add_or_remove": "add or remove",
"advanced_settings": "Advanced settings",
"all": "All labware",
"aspirate_volume": "Aspirate volume per well",
"aspirate_volume_µL": "Aspirate volume per well (µL)",
"both_mounts": "Left + Right Mount",
"columns": "columns",
"create_new_transfer": "Create new quick transfer",
"create_transfer": "Create transfer",
"destination": "Destination",
Expand All @@ -21,6 +23,7 @@
"reservoir": "Reservoirs",
"source": "Source",
"select_attached_pipette": "Select attached pipette",
"select_by": "select by",
"select_dest_labware": "Select destination labware",
"select_dest_wells": "Select destination wells",
"select_source_labware": "Select source labware",
Expand All @@ -31,6 +34,7 @@
"set_transfer_volume": "Set transfer volume",
"source_labware": "Source labware",
"source_labware_d2": "Source labware in D2",
"starting_well": "starting well",
"use_deck_slots": "<block>Quick transfers use deck slots B2-D2. These slots hold a tip rack, a source labware, and a destination labware.</block><block>Make sure that your deck configuration is up to date to avoid collisions.</block>",
"tip_management": "Tip management",
"tip_rack": "Tip rack",
Expand All @@ -42,5 +46,6 @@
"pipette_currently_attached": "Quick transfer options depend on the pipettes currently attached to your robot.",
"wellPlate": "Well plates",
"well_selection": "Well selection",
"well_ratio": "Quick transfers with multiple source wells can either be one-to-one (select {{wells}} for this transfer) or consolidate (select 1 destination well)."
"well_ratio": "Quick transfers with multiple source wells can either be one-to-one (select {{wells}} for this transfer) or consolidate (select 1 destination well).",
"wells": "wells"
}
35 changes: 22 additions & 13 deletions app/src/atoms/buttons/IconButton.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
import * as React from 'react'
import { BORDERS, COLORS, Icon, RESPONSIVENESS } from "@opentrons/components"
import { ODD_FOCUS_VISIBLE } from "./constants"
import styled, { css } from "styled-components"
import { css } from 'styled-components'
import {
BORDERS,
Btn,
COLORS,
Icon,
RESPONSIVENESS,
} from '@opentrons/components'
import { ODD_FOCUS_VISIBLE } from './constants'

interface IconButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
interface IconButtonProps
// extends React.ButtonHTMLAttributes<HTMLButtonElement> {
extends React.ComponentProps<typeof Btn> {
iconName: React.ComponentProps<typeof Icon>['name']
hasBackground?: boolean
}

export function IconButton(props: IconButtonProps) {
export function IconButton(props: IconButtonProps): JSX.Element {
const { iconName, hasBackground = false, ...buttonProps } = props
return (
<button
<Btn
css={css`
border-radius: ${hasBackground ? BORDERS.borderRadius8 : '50%'};
max-height: 100%;
Expand All @@ -22,11 +30,15 @@ export function IconButton(props: IconButtonProps) {
}
&:focus-visible {
box-shadow: ${ODD_FOCUS_VISIBLE};
background-color: ${hasBackground ? COLORS.grey35 : COLORS.transparent};
background-color: ${hasBackground
? COLORS.grey35
: COLORS.transparent};
}
&:disabled {
background-color: ${hasBackground ? COLORS.grey35 : COLORS.transparent};
color: ${COLORS.grey35}
background-color: ${hasBackground
? COLORS.grey35
: COLORS.transparent};
color: ${COLORS.grey35};
}
@media ${RESPONSIVENESS.touchscreenMediaQuerySpecs} {
cursor: default;
Expand All @@ -35,9 +47,6 @@ export function IconButton(props: IconButtonProps) {
{...buttonProps}
>
<Icon size="5rem" name={iconName} />
</button>
</Btn>
)
}

const ButtonWrapper = styled('button')

157 changes: 114 additions & 43 deletions app/src/organisms/WellSelection/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import reduce from 'lodash/reduce'

import {
ALIGN_STRETCH,
Box,
Checkbox,
COLORS,
DIRECTION_COLUMN,
Flex,
Expand All @@ -15,6 +16,9 @@ import {
TYPOGRAPHY,
WELL_LABEL_OPTIONS,
} from '@opentrons/components'

import { IconButton } from '../../atoms/buttons/IconButton'
import { RadioButton } from '../../atoms/buttons/RadioButton'
import {
arrayToWellGroup,
getCollidingWells,
Expand All @@ -28,8 +32,6 @@ import type {
PipetteChannels,
} from '@opentrons/shared-data'
import type { GenericRect } from './types'
import { useTranslation } from 'react-i18next'
import { IconButton } from '../../atoms/buttons/IconButton'

interface WellSelectionProps {
definition: LabwareDefinition2
Expand Down Expand Up @@ -108,18 +110,18 @@ export function WellSelection(props: WellSelectionProps): JSX.Element {
const allSelectedWells =
channels === 8 || channels === 96
? reduce<WellGroup, WellGroup>(
selectedPrimaryWells,
(acc, _, wellName): WellGroup => {
const wellSet = getWellSetForMultichannel(
definition,
wellName,
channels
)
if (!wellSet) return acc
return { ...acc, ...arrayToWellGroup(wellSet) }
},
{}
)
selectedPrimaryWells,
(acc, _, wellName): WellGroup => {
const wellSet = getWellSetForMultichannel(
definition,
wellName,
channels
)
if (!wellSet) return acc
return { ...acc, ...arrayToWellGroup(wellSet) }
},
{}
)
: selectedPrimaryWells

const wellFill: WellFill = {}
Expand Down Expand Up @@ -150,48 +152,117 @@ export function WellSelection(props: WellSelectionProps): JSX.Element {
</RobotCoordinateSpace>
)
return definition.parameters.format === '384Standard' ? (
<Flex justifyContent={JUSTIFY_SPACE_BETWEEN} gridGap={SPACING.spacing40} width="100%">
<Flex
justifyContent={JUSTIFY_SPACE_BETWEEN}
gridGap={SPACING.spacing40}
width="100%"
>
<Box flex="2 0 0">{labwareRender}</Box>
<ButtonControls channels={channels} flex="1 0 0" />
<Flex
flex="1 0 0"
flexDirection={DIRECTION_COLUMN}
gridGap={SPACING.spacing32}
>
{channels === 1 ? <SelectBy /> : <StartingWell channels={channels} />}
<ButtonControls channels={channels} />
</Flex>
</Flex>
) : (
<SelectionRect
onSelectionMove={handleSelectionMove}
onSelectionDone={handleSelectionDone}
>
{ labwareRender }
</SelectionRect >
{labwareRender}
</SelectionRect>
)
}

interface ButtonControlsProps {
channels: PipetteChannels
flex: React.ComponentProps<typeof Flex>['flex']
}
function ButtonControls(props: ButtonControlsProps): JSX.Element {
const { channels, flex } = props
function SelectBy(): JSX.Element {
const { t, i18n } = useTranslation('quick_transfer')

const addOrRemoveButtons = channels !== 96 ? (
<Flex flexDirection={DIRECTION_COLUMN} alignItems={ALIGN_STRETCH} gridGap={SPACING.spacing16}>
<StyledText as="p" fontWeight={TYPOGRAPHY.fontWeightSemiBold}>{i18n.format(t('add_or_remove'), 'capitalize')}</StyledText>
<Flex alignSelf={ALIGN_STRETCH} gridGap={SPACING.spacing16}>
<IconButton
onClick={() => { console.log('TODO handle minus') }}
iconName="minus"
hasBackground />
<IconButton
onClick={() => { console.log('TODO handle plus') }}
iconName="plus"
hasBackground />
</Flex>
return (
<Flex flexDirection={DIRECTION_COLUMN} gridGap={SPACING.spacing16}>
<StyledText as="p" fontWeight={TYPOGRAPHY.fontWeightSemiBold}>
{i18n.format(t('select_by'), 'capitalize')}
</StyledText>
<RadioButton
buttonLabel={i18n.format(t('columns'), 'capitalize')}
buttonValue="columns"
onChange={() => console.log('columns')}
radioButtonType="small"
/>
<RadioButton
buttonLabel={i18n.format(t('wells'), 'capitalize')}
buttonValue="wells"
onChange={() => console.log('wells')}
radioButtonType="small"
/>
</Flex>
) : null
)
}

function StartingWell({
channels,
}: {
channels: PipetteChannels
}): JSX.Element {
const { t, i18n } = useTranslation('quick_transfer')

const checkboxWellOptions =
channels === 8 ? ['A1', 'B1'] : ['A1', 'A2', 'B1', 'B2']

return (
<Flex
flex={flex}
flexDirection={DIRECTION_COLUMN}>
{addOrRemoveButtons}
<Flex flexDirection={DIRECTION_COLUMN} gridGap={SPACING.spacing16}>
<StyledText as="p" fontWeight={TYPOGRAPHY.fontWeightSemiBold}>
{i18n.format(t('starting_well'), 'capitalize')}
</StyledText>
{checkboxWellOptions.map(well => (
<Checkbox
key={well}
isChecked
labelText={well}
onClick={() => console.log(well)}
/>
))}
</Flex>
)
}

interface ButtonControlsProps {
channels: PipetteChannels
}
function ButtonControls(props: ButtonControlsProps): JSX.Element {
const { channels } = props
const { t, i18n } = useTranslation('quick_transfer')

const addOrRemoveButtons =
channels !== 96 ? (
<Flex flexDirection={DIRECTION_COLUMN} gridGap={SPACING.spacing16}>
<StyledText as="p" fontWeight={TYPOGRAPHY.fontWeightSemiBold}>
{i18n.format(
t(channels === 8 ? 'add_or_remove_columns' : 'add_or_remove'),
'capitalize'
)}
</StyledText>
<Flex gridGap={SPACING.spacing16}>
<IconButton
onClick={() => {
console.log('TODO handle minus')
}}
iconName="minus"
hasBackground
flex="1"
/>
<IconButton
onClick={() => {
console.log('TODO handle plus')
}}
iconName="plus"
hasBackground
flex="1"
/>
</Flex>
</Flex>
) : null
return <Flex flexDirection={DIRECTION_COLUMN}>{addOrRemoveButtons}</Flex>
}
1 change: 1 addition & 0 deletions components/src/atoms/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './buttons'
export * from './Checkbox'
export * from './CheckboxField'
export * from './Chip'
export * from './StepMeter'
Expand Down

0 comments on commit b7b9e14

Please sign in to comment.