From 2018908a0b7f8b3fa6bb8f289a2dc9df5d8c276c Mon Sep 17 00:00:00 2001 From: Jethary Date: Wed, 7 Feb 2024 13:26:40 -0500 Subject: [PATCH 01/17] refactor a bunch of components --- protocol-designer/package.json | 4 +- .../LabwareOverlays/AdapterControls.tsx | 186 +++++------ .../DeckSetup/LabwareOverlays/DragPreview.tsx | 42 +-- .../DeckSetup/LabwareOverlays/EditLabware.tsx | 215 +++++-------- .../LabwareOverlays/LabwareControls.tsx | 5 +- .../LabwareOverlays/SlotControls.tsx | 158 ++++------ .../src/components/DeckSetup/index.tsx | 4 - .../src/components/ProtocolEditor.tsx | 8 +- .../steplist/DraggableStepItems.tsx | 288 +++++++----------- .../src/containers/ConnectedStepItem.tsx | 6 +- yarn.lock | 117 ++++--- 11 files changed, 392 insertions(+), 641 deletions(-) diff --git a/protocol-designer/package.json b/protocol-designer/package.json index 6730a8da47d..c06cc700b2f 100755 --- a/protocol-designer/package.json +++ b/protocol-designer/package.json @@ -38,8 +38,8 @@ "query-string": "6.2.0", "react": "18.2.0", "react-color": "2.19.3", - "react-dnd": "6.0.0", - "react-dnd-mouse-backend": "0.1.2", + "react-dnd": "16.0.1", + "react-dnd-mouse-backend": "1.0.0-rc.2", "react-dom": "18.2.0", "react-hook-form": "7.49.3", "react-i18next": "14.0.0", diff --git a/protocol-designer/src/components/DeckSetup/LabwareOverlays/AdapterControls.tsx b/protocol-designer/src/components/DeckSetup/LabwareOverlays/AdapterControls.tsx index e01e1b7dcd5..a2b68652f47 100644 --- a/protocol-designer/src/components/DeckSetup/LabwareOverlays/AdapterControls.tsx +++ b/protocol-designer/src/components/DeckSetup/LabwareOverlays/AdapterControls.tsx @@ -1,8 +1,8 @@ import assert from 'assert' import * as React from 'react' -import { DropTarget, DropTargetConnector, DropTargetMonitor } from 'react-dnd' +import { useDispatch, useSelector } from 'react-redux' +import { DropTargetMonitor, useDrop } from 'react-dnd' import cx from 'classnames' -import { connect } from 'react-redux' import noop from 'lodash/noop' import { Icon, RobotCoordsForeignDiv } from '@opentrons/components' import { DND_TYPES } from '../../../constants' @@ -15,27 +15,16 @@ import { moveDeckItem, openAddLabwareModal, } from '../../../labware-ingred/actions' -import { - LabwareDefByDefURI, - selectors as labwareDefSelectors, -} from '../../../labware-defs' +import { selectors as labwareDefSelectors } from '../../../labware-defs' import { START_TERMINAL_ITEM_ID, TerminalItemId } from '../../../steplist' import { BlockedSlot } from './BlockedSlot' import type { CoordinateTuple, Dimensions } from '@opentrons/shared-data' -import type { BaseState, DeckSlot, ThunkDispatch } from '../../../types' import type { LabwareOnDeck } from '../../../step-forms' import styles from './LabwareOverlays.css' -interface DNDP { - isOver: boolean - connectDropTarget: (val: React.ReactNode) => JSX.Element - draggedItem: { labwareOnDeck: LabwareOnDeck } | null - itemType: string -} - -interface OP { +interface AdapterControlsProps { slotPosition: CoordinateTuple slotBoundingBox: Dimensions // labwareId is the adapter's labwareId @@ -43,38 +32,72 @@ interface OP { allLabware: LabwareOnDeck[] onDeck: boolean selectedTerminalItemId?: TerminalItemId | null - handleDragHover?: () => unknown -} -interface DP { - addLabware: (e: React.MouseEvent) => unknown - moveDeckItem: (item1: DeckSlot, item2: DeckSlot) => unknown - deleteLabware: () => void + handleDragHover?: () => void } -interface SP { - customLabwareDefs: LabwareDefByDefURI +interface DroppedItem { + labwareOnDeck: LabwareOnDeck } -export type SlotControlsProps = OP & DP & DNDP & SP - -export const AdapterControlsComponents = ( - props: SlotControlsProps +export const AdapterControls = ( + props: AdapterControlsProps ): JSX.Element | null => { const { slotPosition, slotBoundingBox, - addLabware, selectedTerminalItemId, - isOver, - connectDropTarget, - draggedItem, - itemType, - deleteLabware, labwareId, - customLabwareDefs, onDeck, + handleDragHover, allLabware, } = props + const customLabwareDefs = useSelector( + labwareDefSelectors.getCustomLabwareDefsByURI + ) + + const dispatch = useDispatch() + const adapterName = + allLabware.find(labware => labware.id === labwareId)?.def.metadata + .displayName ?? '' + + const [{ itemType, draggedItem, isOver }, drop] = useDrop(() => ({ + accept: DND_TYPES.LABWARE, + canDrop: (item: DroppedItem) => { + const draggedDef = item.labwareOnDeck?.def + assert(draggedDef, 'no labware def of dragged item, expected it on drop') + + if (draggedDef != null) { + const isCustomLabware = getLabwareIsCustom( + customLabwareDefs, + item.labwareOnDeck + ) + return ( + getAdapterLabwareIsAMatch( + labwareId, + allLabware, + draggedDef.parameters.loadName + ) || isCustomLabware + ) + } + return true + }, + drop: (item: DroppedItem) => { + if (item.labwareOnDeck) { + dispatch(moveDeckItem(item.labwareOnDeck.slot, labwareId)) + } + }, + hover: () => { + if (handleDragHover) { + handleDragHover() + } + }, + collect: (monitor: DropTargetMonitor) => ({ + itemType: monitor.getItemType(), + isOver: !!monitor.isOver(), + draggedItem: monitor.getItem() as DroppedItem, + }), + })) + if ( selectedTerminalItemId !== START_TERMINAL_ITEM_ID || (itemType !== DND_TYPES.LABWARE && itemType !== null) @@ -101,8 +124,8 @@ export const AdapterControlsComponents = ( slotBlocked = 'Labware incompatible with this adapter' } - return connectDropTarget( - + return ( + {slotBlocked ? ( - + dispatch(openAddLabwareModal({ slot: labwareId }))} + > {!isOver && } {isOver ? 'Place Here' : 'Add Labware'} - + { + window.confirm( + `"Are you sure you want to remove this ${adapterName}?` + ) && dispatch(deleteContainer({ labwareId: labwareId })) + }} + > {!isOver && } {'Delete'} @@ -137,80 +170,3 @@ export const AdapterControlsComponents = ( ) } - -const mapStateToProps = (state: BaseState): SP => { - return { - customLabwareDefs: labwareDefSelectors.getCustomLabwareDefsByURI(state), - } -} - -const mapDispatchToProps = (dispatch: ThunkDispatch, ownProps: OP): DP => { - const adapterName = - ownProps.allLabware.find(labware => labware.id === ownProps.labwareId)?.def - .metadata.displayName ?? '' - return { - addLabware: () => - dispatch(openAddLabwareModal({ slot: ownProps.labwareId })), - moveDeckItem: (sourceSlot, destSlot) => - dispatch(moveDeckItem(sourceSlot, destSlot)), - deleteLabware: () => { - window.confirm(`"Are you sure you want to remove this ${adapterName}?`) && - dispatch(deleteContainer({ labwareId: ownProps.labwareId })) - }, - } -} - -const slotTarget = { - drop: (props: SlotControlsProps, monitor: DropTargetMonitor) => { - const draggedItem = monitor.getItem() - if (draggedItem) { - props.moveDeckItem(draggedItem.labwareOnDeck.slot, props.labwareId) - } - }, - hover: (props: SlotControlsProps) => { - if (props.handleDragHover) { - props.handleDragHover() - } - }, - canDrop: (props: SlotControlsProps, monitor: DropTargetMonitor) => { - const draggedItem = monitor.getItem() - const draggedDef = draggedItem?.labwareOnDeck?.def - assert(draggedDef, 'no labware def of dragged item, expected it on drop') - - if (draggedDef != null) { - const isCustomLabware = getLabwareIsCustom( - props.customLabwareDefs, - draggedItem.labwareOnDeck - ) - return ( - getAdapterLabwareIsAMatch( - props.labwareId, - props.allLabware, - draggedDef.parameters.loadName - ) || isCustomLabware - ) - } - return true - }, -} -const collectSlotTarget = ( - connect: DropTargetConnector, - monitor: DropTargetMonitor -): React.ReactNode => ({ - // @ts-expect-error(BC, 12-13-2023): react dnd needs to be updated or removed to include proper type - connectDropTarget: connect.dropTarget(), - isOver: monitor.isOver(), - draggedItem: monitor.getItem(), - itemType: monitor.getItemType(), -}) - -export const AdapterControls = connect( - mapStateToProps, - mapDispatchToProps -)( - DropTarget( - DND_TYPES.LABWARE, - slotTarget, - collectSlotTarget - )(AdapterControlsComponents) -) diff --git a/protocol-designer/src/components/DeckSetup/LabwareOverlays/DragPreview.tsx b/protocol-designer/src/components/DeckSetup/LabwareOverlays/DragPreview.tsx index 700b683c946..6d1dc0c85b7 100644 --- a/protocol-designer/src/components/DeckSetup/LabwareOverlays/DragPreview.tsx +++ b/protocol-designer/src/components/DeckSetup/LabwareOverlays/DragPreview.tsx @@ -1,32 +1,31 @@ import * as React from 'react' -import { DragLayer } from 'react-dnd' +import { useDragLayer, XYCoord } from 'react-dnd' import { LabwareOnDeck } from '../LabwareOnDeck' import { DND_TYPES } from '../../../constants' -import { LabwareOnDeck as LabwareOnDeckType } from '../../../step-forms' import { RobotWorkSpaceRenderProps } from '@opentrons/components' import styles from './DragPreview.css' interface DragPreviewProps { - isDragging: boolean - currentOffset?: { x: number; y: number } - item: { labwareOnDeck: LabwareOnDeckType } - itemType: string getRobotCoordsFromDOMCoords: RobotWorkSpaceRenderProps['getRobotCoordsFromDOMCoords'] } -const LabwareDragPreview = (props: DragPreviewProps): JSX.Element | null => { - const { - item, - itemType, - isDragging, - currentOffset, - getRobotCoordsFromDOMCoords, - } = props - if (itemType !== DND_TYPES.LABWARE || !isDragging || !currentOffset) +export const DragPreview = (props: DragPreviewProps): JSX.Element | null => { + const { getRobotCoordsFromDOMCoords } = props + const { item, itemType, isDragging, currentOffset } = useDragLayer( + monitor => ({ + item: monitor.getItem(), + itemType: monitor.getItemType(), + isDragging: monitor.isDragging(), + currentOffset: monitor.getSourceClientOffset(), + }) + ) + + if (!isDragging || !currentOffset || itemType !== DND_TYPES.LABWARE) { return null - const { x, y } = currentOffset + } - const cursor = getRobotCoordsFromDOMCoords(x, y) + const { x, y } = currentOffset + const cursor: XYCoord = getRobotCoordsFromDOMCoords(x, y) return ( { /> ) } - -export const DragPreview = DragLayer< - Omit ->(monitor => ({ - currentOffset: monitor.getSourceClientOffset(), - isDragging: monitor.isDragging(), - itemType: monitor.getItemType(), - item: monitor.getItem(), -}))(LabwareDragPreview) diff --git a/protocol-designer/src/components/DeckSetup/LabwareOverlays/EditLabware.tsx b/protocol-designer/src/components/DeckSetup/LabwareOverlays/EditLabware.tsx index e4f267114b0..530032c2306 100644 --- a/protocol-designer/src/components/DeckSetup/LabwareOverlays/EditLabware.tsx +++ b/protocol-designer/src/components/DeckSetup/LabwareOverlays/EditLabware.tsx @@ -1,18 +1,10 @@ import * as React from 'react' import { useTranslation } from 'react-i18next' -import { connect } from 'react-redux' +import { useDispatch, useSelector } from 'react-redux' import cx from 'classnames' import { Icon } from '@opentrons/components' import { getLabwareDisplayName } from '@opentrons/shared-data' -import { - DragSource, - DragSourceConnector, - DragSourceMonitor, - DropTarget, - DropTargetConnector, - DropTargetMonitor, - DropTargetSpec, -} from 'react-dnd' +import { DropTargetMonitor, useDrag, useDrop } from 'react-dnd' import { NameThisLabware } from './NameThisLabware' import { DND_TYPES } from '../../../constants' import { @@ -22,50 +14,78 @@ import { openIngredientSelector, } from '../../../labware-ingred/actions' import { selectors as labwareIngredSelectors } from '../../../labware-ingred/selectors' -import { BaseState, DeckSlot, ThunkDispatch } from '../../../types' +import { ThunkDispatch } from '../../../types' import { LabwareOnDeck } from '../../../step-forms' import styles from './LabwareOverlays.css' -interface OP { +interface Props { labwareOnDeck: LabwareOnDeck setHoveredLabware: (val?: LabwareOnDeck | null) => unknown setDraggedLabware: (val?: LabwareOnDeck | null) => unknown swapBlocked: boolean } -interface SP { - isYetUnnamed: boolean -} -interface DP { - editLiquids: () => unknown - duplicateLabware: () => unknown - deleteLabware: () => unknown - moveDeckItem: (item1: DeckSlot, item2: DeckSlot) => unknown -} -interface DNDP { - draggedLabware?: LabwareOnDeck | null - isOver: boolean - connectDragSource: (val: JSX.Element) => JSX.Element - connectDropTarget: (val: JSX.Element) => JSX.Element -} - -type Props = OP & SP & DP & DNDP - -const EditLabwareComponent = (props: Props): JSX.Element => { +export const EditLabware = (props: Props): JSX.Element | null => { const { labwareOnDeck, - isYetUnnamed, - editLiquids, - deleteLabware, - duplicateLabware, - draggedLabware, - isOver, - connectDragSource, - connectDropTarget, swapBlocked, + setDraggedLabware, + setHoveredLabware, } = props + const savedLabware = useSelector(labwareIngredSelectors.getSavedLabware) + const dispatch = useDispatch>() const { t } = useTranslation('deck') + const { isTiprack } = labwareOnDeck.def.parameters + const hasName = savedLabware[labwareOnDeck.id] + const isYetUnnamed = !labwareOnDeck.def.parameters.isTiprack && !hasName + + const editLiquids = (): void => { + dispatch(openIngredientSelector(labwareOnDeck.id)) + } + + const [, drag] = useDrag(() => ({ + type: DND_TYPES.LABWARE, + item: { labwareOnDeck }, + beginDrag: () => { + setDraggedLabware(labwareOnDeck) + return { labwareOnDeck } + }, + endDrag: () => { + setHoveredLabware(null) + setDraggedLabware(null) + }, + })) + + const [{ draggedLabware, isOver }, drop] = useDrop(() => ({ + accept: DND_TYPES.LABWARE, + canDrop: (monitor: DropTargetMonitor) => { + const draggedItem: any = monitor.getItem() + const draggedLabware = draggedItem?.labwareOnDeck + const isDifferentSlot = + draggedLabware && draggedLabware.slot !== props.labwareOnDeck.slot + return isDifferentSlot && !props.swapBlocked + }, + drop: (monitor: DropTargetMonitor) => { + const draggedItem: any = monitor.getItem() + if (draggedItem) { + dispatch( + moveDeckItem(draggedItem.labwareOnDeck.slot, props.labwareOnDeck.slot) + ) + } + }, + + hover: (monitor: DropTargetMonitor) => { + if (monitor.canDrop()) { + props.setHoveredLabware(labwareOnDeck) + } + }, + collect: (monitor: DropTargetMonitor) => ({ + isOver: monitor.isOver(), + draggedLabware: monitor.getItem() as any, + }), + })) + if (isYetUnnamed && !isTiprack) { return ( { ) : (
)} - + dispatch(duplicateLabware(labwareOnDeck.id))} + > {t('overlay.edit.duplicate')} - + { + window.confirm( + `Are you sure you want to permanently delete this ${getLabwareDisplayName( + labwareOnDeck.def + )}?` + ) && dispatch(deleteContainer({ labwareId: labwareOnDeck.id })) + }} + > {t('overlay.edit.delete')} @@ -115,8 +147,8 @@ const EditLabwareComponent = (props: Props): JSX.Element => { ) } - return connectDragSource( - connectDropTarget( + const dragResult = drag( +
{ > {contents}
- ) +
) - } -} - -const labwareSource = { - beginDrag: (props: Props, monitor: DragSourceMonitor, component: any) => { - const { labwareOnDeck } = props - props.setDraggedLabware(labwareOnDeck) - return { labwareOnDeck } - }, - endDrag: (props: Props, monitor: DragSourceMonitor, component: any) => { - props.setHoveredLabware(null) - props.setDraggedLabware(null) - }, -} -const collectLabwareSource = ( - connect: DragSourceConnector, - monitor: DragSourceMonitor -): React.ReactNode => ({ - // @ts-expect-error(BC, 12-13-2023): react dnd needs to be updated or removed to include proper type - connectDragSource: connect.dragSource(), - isDragging: monitor.isDragging(), - draggedItem: monitor.getItem(), -}) -const DragEditLabware = DragSource( - DND_TYPES.LABWARE, - labwareSource, - collectLabwareSource -)(EditLabwareComponent) - -const labwareDropTarget = { - canDrop: (props: Props, monitor: DropTargetMonitor) => { - const draggedItem = monitor.getItem() - const draggedLabware = draggedItem?.labwareOnDeck - const isDifferentSlot = - draggedLabware && draggedLabware.slot !== props.labwareOnDeck.slot - return isDifferentSlot && !props.swapBlocked - }, - hover: (props: Props, monitor: DropTargetSpec, component: any) => { - if (monitor.canDrop) { - props.setHoveredLabware(component.props.labwareOnDeck) - } - }, - drop: (props: Props, monitor: DropTargetMonitor) => { - const draggedItem = monitor.getItem() - if (draggedItem) { - props.moveDeckItem( - draggedItem.labwareOnDeck.slot, - props.labwareOnDeck.slot - ) - } - }, -} -const collectLabwareDropTarget = ( - connect: DropTargetConnector, - monitor: DropTargetMonitor -): React.ReactNode => ({ - // @ts-expect-error(BC, 12-13-2023): react dnd needs to be updated or removed to include proper type - connectDropTarget: connect.dropTarget(), - isOver: monitor.isOver(), - draggedLabware: monitor.getItem()?.labwareOnDeck || null, -}) -const DragDropEditLabware = DropTarget( - DND_TYPES.LABWARE, - labwareDropTarget, - collectLabwareDropTarget -)(DragEditLabware) -const mapStateToProps = (state: BaseState, ownProps: OP): SP => { - const { id } = ownProps.labwareOnDeck - const hasName = labwareIngredSelectors.getSavedLabware(state)[id] - return { - isYetUnnamed: !ownProps.labwareOnDeck.def.parameters.isTiprack && !hasName, + return dragResult !== null ? dragResult : null } } - -const mapDispatchToProps = ( - dispatch: ThunkDispatch, - ownProps: OP -): DP => ({ - editLiquids: () => - dispatch(openIngredientSelector(ownProps.labwareOnDeck.id)), - duplicateLabware: () => dispatch(duplicateLabware(ownProps.labwareOnDeck.id)), - deleteLabware: () => { - window.confirm( - `Are you sure you want to permanently delete this ${getLabwareDisplayName( - ownProps.labwareOnDeck.def - )}?` - ) && dispatch(deleteContainer({ labwareId: ownProps.labwareOnDeck.id })) - }, - moveDeckItem: (sourceSlot, destSlot) => - dispatch(moveDeckItem(sourceSlot, destSlot)), -}) - -export const EditLabware = connect( - mapStateToProps, - mapDispatchToProps -)(DragDropEditLabware) diff --git a/protocol-designer/src/components/DeckSetup/LabwareOverlays/LabwareControls.tsx b/protocol-designer/src/components/DeckSetup/LabwareOverlays/LabwareControls.tsx index 66d84a87ab3..fc7c011811c 100644 --- a/protocol-designer/src/components/DeckSetup/LabwareOverlays/LabwareControls.tsx +++ b/protocol-designer/src/components/DeckSetup/LabwareOverlays/LabwareControls.tsx @@ -16,8 +16,8 @@ import type { CoordinateTuple } from '@opentrons/shared-data' interface LabwareControlsProps { labwareOnDeck: LabwareOnDeck slotPosition: CoordinateTuple - setHoveredLabware: (labware?: LabwareOnDeck | null) => unknown - setDraggedLabware: (labware?: LabwareOnDeck | null) => unknown + setHoveredLabware: (labware?: LabwareOnDeck | null) => void + setDraggedLabware: (labware?: LabwareOnDeck | null) => void swapBlocked: boolean selectedTerminalItemId?: TerminalItemId | null } @@ -48,7 +48,6 @@ export const LabwareControls = (props: LabwareControlsProps): JSX.Element => { > {canEdit ? ( - // @ts-expect-error(sa, 2021-6-21): react dnd type mismatch JSX.Element - draggedItem: { labwareOnDeck: LabwareOnDeck } | null - itemType: string -} - -interface OP { +interface SlotControlsProps { slotPosition: CoordinateTuple | null slotBoundingBox: Dimensions // NOTE: slotId can be either AddressableAreaName or moduleId @@ -49,33 +37,59 @@ interface OP { handleDragHover?: () => unknown } -interface DP { - addLabware: (e: React.MouseEvent) => unknown - moveDeckItem: (item1: DeckSlot, item2: DeckSlot) => unknown -} - -interface SP { - customLabwareDefs: LabwareDefByDefURI +interface DroppedItem { + labwareOnDeck: LabwareOnDeck } -export type SlotControlsProps = OP & DP & DNDP & SP - -export const SlotControlsComponent = ( - props: SlotControlsProps -): JSX.Element | null => { +export const SlotControls = (props: SlotControlsProps): JSX.Element | null => { const { slotBoundingBox, slotPosition, - addLabware, + slotId, selectedTerminalItemId, - isOver, - connectDropTarget, moduleType, - draggedItem, - itemType, - customLabwareDefs, + handleDragHover, } = props + const customLabwareDefs = useSelector( + labwareDefSelectors.getCustomLabwareDefsByURI + ) + const dispatch = useDispatch() const { t } = useTranslation('deck') + + const [{ itemType, draggedItem, isOver }, drop] = useDrop(() => ({ + accept: DND_TYPES.LABWARE, + canDrop: (item: DroppedItem) => { + const draggedDef = item?.labwareOnDeck?.def + assert(draggedDef, 'no labware def of dragged item, expected it on drop') + + if (moduleType != null && draggedDef != null) { + // this is a module slot, prevent drop if the dragged labware is not compatible + const isCustomLabware = getLabwareIsCustom( + customLabwareDefs, + item.labwareOnDeck + ) + + return getLabwareIsCompatible(draggedDef, moduleType) || isCustomLabware + } + return true + }, + drop: (item: DroppedItem) => { + if (item.labwareOnDeck) { + dispatch(moveDeckItem(item.labwareOnDeck.slot, slotId)) + } + }, + hover: () => { + if (handleDragHover) { + handleDragHover() + } + }, + collect: (monitor: DropTargetMonitor) => ({ + itemType: monitor.getItemType(), + isOver: !!monitor.isOver(), + draggedItem: monitor.getItem() as DroppedItem, + }), + })) + if ( selectedTerminalItemId !== START_TERMINAL_ITEM_ID || (itemType !== DND_TYPES.LABWARE && itemType !== null) || @@ -84,6 +98,7 @@ export const SlotControlsComponent = ( return null const draggedDef = draggedItem?.labwareOnDeck?.def + const isCustomLabware = draggedItem ? getLabwareIsCustom(customLabwareDefs, draggedItem.labwareOnDeck) : false @@ -111,8 +126,12 @@ export const SlotControlsComponent = ( overlayText = 'add_labware' } - return connectDropTarget( - + const addLabware = (): void => { + dispatch(openAddLabwareModal({ slot: slotId })) + } + + return ( + {slotBlocked ? ( ) } - -const mapStateToProps = (state: BaseState): SP => { - return { - customLabwareDefs: labwareDefSelectors.getCustomLabwareDefsByURI(state), - } -} - -const mapDispatchToProps = ( - dispatch: ThunkDispatch, - ownProps: OP -): DP => ({ - addLabware: () => dispatch(openAddLabwareModal({ slot: ownProps.slotId })), - moveDeckItem: (sourceSlot, destSlot) => - dispatch(moveDeckItem(sourceSlot, destSlot)), -}) - -const slotTarget = { - drop: (props: SlotControlsProps, monitor: DropTargetMonitor) => { - const draggedItem = monitor.getItem() - if (draggedItem) { - props.moveDeckItem(draggedItem.labwareOnDeck.slot, props.slotId) - } - }, - hover: (props: SlotControlsProps) => { - if (props.handleDragHover) { - props.handleDragHover() - } - }, - canDrop: (props: SlotControlsProps, monitor: DropTargetMonitor) => { - const draggedItem = monitor.getItem() - const draggedDef = draggedItem?.labwareOnDeck?.def - const moduleType = props.moduleType - assert(draggedDef, 'no labware def of dragged item, expected it on drop') - - if (moduleType != null && draggedDef != null) { - // this is a module slot, prevent drop if the dragged labware is not compatible - const isCustomLabware = getLabwareIsCustom( - props.customLabwareDefs, - draggedItem.labwareOnDeck - ) - - return getLabwareIsCompatible(draggedDef, moduleType) || isCustomLabware - } - return true - }, -} -const collectSlotTarget = ( - connect: DropTargetConnector, - monitor: DropTargetMonitor -): React.ReactNode => ({ - // @ts-expect-error(BC, 12-13-2023): react dnd needs to be updated or removed to include proper type - connectDropTarget: connect.dropTarget(), - isOver: monitor.isOver(), - draggedItem: monitor.getItem(), - itemType: monitor.getItemType(), -}) - -export const SlotControls = connect( - mapStateToProps, - mapDispatchToProps -)( - DropTarget( - DND_TYPES.LABWARE, - slotTarget, - collectSlotTarget - )(SlotControlsComponent) -) diff --git a/protocol-designer/src/components/DeckSetup/index.tsx b/protocol-designer/src/components/DeckSetup/index.tsx index 8044c838050..895f46e1954 100644 --- a/protocol-designer/src/components/DeckSetup/index.tsx +++ b/protocol-designer/src/components/DeckSetup/index.tsx @@ -265,7 +265,6 @@ export const DeckSetupContents = (props: ContentsProps): JSX.Element => { labwareOnDeck={labwareLoadedOnModule} /> {isAdapter ? ( - // @ts-expect-error { {labwareLoadedOnModule == null && !shouldHideChildren && !isAdapter ? ( - // @ts-expect-error { }) .map(addressableArea => { return ( - // @ts-expect-error { /> {labwareIsAdapter ? ( - // @ts-expect-error ( + + + ) diff --git a/protocol-designer/src/components/steplist/DraggableStepItems.tsx b/protocol-designer/src/components/steplist/DraggableStepItems.tsx index 62a757cb82a..9532a40bc1f 100644 --- a/protocol-designer/src/components/steplist/DraggableStepItems.tsx +++ b/protocol-designer/src/components/steplist/DraggableStepItems.tsx @@ -1,129 +1,109 @@ import * as React from 'react' -import { connect } from 'react-redux' +import { useSelector } from 'react-redux' import { - DragSource, - DropTarget, - DragLayer, DragLayerMonitor, - DragSourceConnector, - DragSourceMonitor, - DropTargetConnector, DropTargetMonitor, - DragElementWrapper, - DragSourceOptions, - ConnectDropTarget, + useDrop, + useDrag, } from 'react-dnd' import isEqual from 'lodash/isEqual' import { DND_TYPES } from '../../constants' -import { ConnectedStepItem } from '../../containers/ConnectedStepItem' -import { PDTitledList } from '../lists' -import { stepIconsByType, StepIdType, StepType } from '../../form-types' import { selectors as stepFormSelectors } from '../../step-forms' -import { BaseState } from '../../types' +import { stepIconsByType, StepIdType } from '../../form-types' +import { + ConnectedStepItem, + ConnectedStepItemProps, +} from '../../containers/ConnectedStepItem' +import { PDTitledList } from '../lists' import { ContextMenu } from './ContextMenu' + import styles from './StepItem.css' -type DragDropStepItemProps = React.ComponentProps & { - connectDragSource: (val: unknown) => React.ReactElement - connectDropTarget: (val: unknown) => React.ReactElement +interface DragDropStepItemProps extends ConnectedStepItemProps { stepId: StepIdType stepNumber: number - isDragging: boolean findStepIndex: (stepIdType: StepIdType) => number onDrag: () => void moveStep: (stepId: StepIdType, value: number) => void } -const DragSourceStepItem = (props: DragDropStepItemProps): any => - props.connectDragSource( - props.connectDropTarget( -
- +const DragDropStepItem = (props: DragDropStepItemProps): JSX.Element => { + const { onDrag, stepId, findStepIndex, moveStep } = props + console.log('stepId', stepId) + const [{ isDragging }, drag] = useDrag(() => ({ + type: DND_TYPES.STEP_ITEM, + beginDrag: () => { + onDrag() + return { stepId: stepId } + }, + collect: (monitor: DragLayerMonitor) => ({ + isDragging: monitor.isDragging(), + }), + })) + + const [, drop] = useDrop(() => ({ + accept: DND_TYPES.STEP_ITEM, + canDrop: () => { + return false + }, + hover: (item: any) => { + console.log('item', item) + const draggedId = item.stepId + + if (draggedId !== stepId) { + const overIndex = findStepIndex(stepId) + moveStep(draggedId, overIndex) + } + }, + })) + + return ( +
+
+
- ) +
) - -const stepItemSource = { - beginDrag: (props: DragDropStepItemProps) => { - props.onDrag() - return { stepId: props.stepId } - }, } -const collectStepSource = ( - connect: DragSourceConnector, - monitor: DragSourceMonitor -): { - connectDragSource: DragElementWrapper - isDragging: boolean -} => ({ - connectDragSource: connect.dragSource(), - isDragging: monitor.isDragging(), -}) -const DraggableStepItem = DragSource( - DND_TYPES.STEP_ITEM, - stepItemSource, - collectStepSource -)(DragSourceStepItem) - -const stepItemTarget = { - canDrop: () => { - return false - }, - hover: (props: DragDropStepItemProps, monitor: DropTargetMonitor) => { - const { stepId: draggedId } = monitor.getItem() - const { stepId: overId } = props - - if (draggedId !== overId) { - const overIndex = props.findStepIndex(overId) - props.moveStep(draggedId, overIndex) - } - }, -} -const collectStepTarget = ( - connect: DropTargetConnector -): { connectDropTarget: ReturnType } => ({ - connectDropTarget: connect.dropTarget(), -}) -const DragDropStepItem = DropTarget( - DND_TYPES.STEP_ITEM, - stepItemTarget, - collectStepTarget -)(DraggableStepItem) interface StepItemsProps { orderedStepIds: StepIdType[] - reorderSteps: (steps: StepIdType[]) => unknown - isOver: boolean - connectDropTarget: (val: unknown) => React.ReactElement -} -interface StepItemsState { - stepIds: StepIdType[] + reorderSteps: (steps: StepIdType[]) => void } -class StepItems extends React.Component { - constructor(props: StepItemsProps) { - super(props) - this.state = { stepIds: this.props.orderedStepIds } - } +export const DraggableStepItems = (props: StepItemsProps): JSX.Element => { + const { orderedStepIds, reorderSteps } = props + const [stepIds, setStepIds] = React.useState(orderedStepIds) - onDrag = (): void => { - this.setState({ stepIds: this.props.orderedStepIds }) + const onDrag = (): void => { + setStepIds(orderedStepIds) } - submitReordering = (): void => { + const submitReordering = (): void => { if ( confirm( 'Are you sure you want to reorder these steps, it may cause errors?' ) ) { - this.props.reorderSteps(this.state.stepIds) + reorderSteps(stepIds) } } + const [{ isOver }, drop] = useDrop(() => ({ + accept: DND_TYPES.STEP_ITEM, + drop: () => { + if (!isEqual(orderedStepIds, stepIds)) { + submitReordering() + } + }, + collect: (monitor: DropTargetMonitor) => ({ + isOver: !!monitor.isOver(), + }), + })) + // TODO: BC 2018-11-27 make util function for reordering and use it in hotkey implementation too - moveStep = (stepId: StepIdType, targetIndex: number): void => { - const { stepIds } = this.state - const currentIndex = this.findStepIndex(stepId) + const moveStep = (stepId: StepIdType, targetIndex: number): void => { + const currentIndex = findStepIndex(stepId) const currentRemoved = [ ...stepIds.slice(0, currentIndex), ...stepIds.slice(currentIndex + 1, stepIds.length), @@ -133,62 +113,54 @@ class StepItems extends React.Component { stepId, ...currentRemoved.slice(targetIndex, currentRemoved.length), ] - this.setState({ stepIds: currentReinserted }) + setStepIds(currentReinserted) } - findStepIndex = (stepId: StepIdType): number => - this.state.stepIds.findIndex(id => stepId === id) + const findStepIndex = (stepId: StepIdType): number => + stepIds.findIndex(id => stepId === id) - render(): React.ReactNode { - const currentIds = this.props.isOver - ? this.state.stepIds - : this.props.orderedStepIds - return this.props.connectDropTarget( -
- - {({ makeStepOnContextMenu }) => - currentIds.map((stepId: StepIdType, index: number) => ( - - )) - } - - -
- ) - } + const currentIds = isOver ? stepIds : orderedStepIds + console.log('stepIds', stepIds) + console.log('currentStepIds', currentIds) + return ( +
+ + {({ makeStepOnContextMenu }) => + currentIds.map((stepId: StepIdType, index: number) => ( + makeStepOnContextMenu(stepId)} + findStepIndex={findStepIndex} + onDrag={onDrag} + moveStep={moveStep} + /> + )) + } + + +
+ ) } const NAV_OFFSET = 64 -interface StepDragPreviewSP { - stepType: StepType | null | undefined - stepName: string | null | undefined -} - -interface StepDragPreviewOP { - currentOffset?: { y: number; x: number } - itemType: string - isDragging: boolean - item: { stepId: StepIdType } -} - -type StepDragPreviewProps = StepDragPreviewOP & StepDragPreviewSP -type DraggableStepItemProps = Omit< - StepItemsProps, - 'isOver' | 'connectDropTarget' -> +const StepDragPreview = (): JSX.Element | null => { + const [{ isDragging, itemType, item, currentOffset }] = useDrag(() => ({ + type: DND_TYPES.STEP_ITEM, + collect: (monitor: DragLayerMonitor) => ({ + currentOffset: monitor.getSourceClientOffset(), + isDragging: monitor.isDragging(), + itemType: monitor.getItemType(), + item: monitor.getItem() as { stepId: StepIdType }, + }), + })) + + const savedStepForms = useSelector(stepFormSelectors.getSavedStepForms) + const savedForm = item && savedStepForms[item.stepId] + const { stepType, stepName } = savedForm || {} -const StepDragPreview = (props: StepDragPreviewProps): JSX.Element | null => { - const { itemType, isDragging, currentOffset, stepType, stepName } = props if ( itemType !== DND_TYPES.STEP_ITEM || !isDragging || @@ -210,47 +182,3 @@ const StepDragPreview = (props: StepDragPreviewProps): JSX.Element | null => {
) } - -const mapSTPForPreview = ( - state: BaseState, - ownProps: StepDragPreviewProps -): StepDragPreviewSP => { - const savedForm = - ownProps.item && - stepFormSelectors.getSavedStepForms(state)[ownProps.item.stepId] - const { stepType, stepName } = savedForm || {} - return { stepType, stepName } -} - -export const StepDragPreviewLayer = DragLayer((monitor: DragLayerMonitor) => ({ - currentOffset: monitor.getSourceClientOffset(), - isDragging: monitor.isDragging(), - itemType: monitor.getItemType(), - item: monitor.getItem(), -}))(connect(mapSTPForPreview)(StepDragPreview)) - -const listTarget = { - drop: ( - props: DraggableStepItemProps, - monitor: DragLayerMonitor, - component: StepItems - ) => { - if (!isEqual(props.orderedStepIds, component.state.stepIds)) { - component.submitReordering() - } - }, -} -const collectListTarget = ( - connect: DropTargetConnector, - monitor: DropTargetMonitor -): { isOver: boolean; connectDropTarget: ConnectDropTarget } => ({ - isOver: monitor.isOver(), - connectDropTarget: connect.dropTarget(), -}) - -export const DraggableStepItems = DropTarget( - DND_TYPES.STEP_ITEM, - // @ts-expect-error(sa, 2021-6-21): fix when updating react dnd to hooks api - listTarget, - collectListTarget -)(StepItems) diff --git a/protocol-designer/src/containers/ConnectedStepItem.tsx b/protocol-designer/src/containers/ConnectedStepItem.tsx index b871e77c5cc..27ea034b099 100644 --- a/protocol-designer/src/containers/ConnectedStepItem.tsx +++ b/protocol-designer/src/containers/ConnectedStepItem.tsx @@ -45,7 +45,7 @@ import { BaseState, ThunkAction } from '../types' import { getAdditionalEquipmentEntities } from '../step-forms/selectors' import { ThunkDispatch } from 'redux-thunk' -interface Props { +export interface ConnectedStepItemProps { stepId: StepIdType stepNumber: number onStepContextMenu?: () => void @@ -66,7 +66,9 @@ const getMouseClickKeyInfo = ( return { isShiftKeyPressed, isMetaKeyPressed } } -export const ConnectedStepItem = (props: Props): JSX.Element => { +export const ConnectedStepItem = ( + props: ConnectedStepItemProps +): JSX.Element => { const { stepId, stepNumber } = props const step = useSelector(stepFormSelectors.getSavedStepForms)[stepId] diff --git a/yarn.lock b/yarn.lock index b218f7b6a12..d5148bc255a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1726,6 +1726,11 @@ dependencies: "@hapi/hoek" "^9.0.0" +"@hookform/resolvers@3.1.1": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@hookform/resolvers/-/resolvers-3.1.1.tgz#b374d33e356428fff9c6ef3c933441fe15e40784" + integrity sha512-tS16bAUkqjITNSvbJuO1x7MXbn7Oe8ZziDTJdA9mMvsoYthnOOiznOTGBYwbdlYBgU+tgpI/BtTU3paRbCuSlg== + "@humanwhocodes/config-array@^0.11.13": version "0.11.14" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" @@ -1744,10 +1749,6 @@ version "2.0.2" resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz#d9fae00a2d5cb40f92cfe64b47ad749fbc38f917" integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw== -"@hookform/resolvers@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@hookform/resolvers/-/resolvers-3.1.1.tgz#b374d33e356428fff9c6ef3c933441fe15e40784" - integrity sha512-tS16bAUkqjITNSvbJuO1x7MXbn7Oe8ZziDTJdA9mMvsoYthnOOiznOTGBYwbdlYBgU+tgpI/BtTU3paRbCuSlg== "@hutson/parse-repository-url@^3.0.0": version "3.0.2" @@ -2371,6 +2372,21 @@ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.1.1.tgz#12c572ab88ef7345b43f21883fca26631c223085" integrity sha512-sLqWxCzC5/QHLhziXSCAksBxHfOnQlhPRVgPK0egEw+ktWvG75T2k+aYWVjVh9+WKeT3tlG3ZNbZQvZLmfuOIw== +"@react-dnd/asap@^5.0.1": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@react-dnd/asap/-/asap-5.0.2.tgz#1f81f124c1cd6f39511c11a881cfb0f715343488" + integrity sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A== + +"@react-dnd/invariant@^4.0.1": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@react-dnd/invariant/-/invariant-4.0.2.tgz#b92edffca10a26466643349fac7cdfb8799769df" + integrity sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw== + +"@react-dnd/shallowequal@^4.0.1": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz#d1b4befa423f692fa4abf1c79209702e7d8ae4b4" + integrity sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA== + "@react-spring/animated@~9.6.1": version "9.6.1" resolved "https://registry.yarnpkg.com/@react-spring/animated/-/animated-9.6.1.tgz#ccc626d847cbe346f5f8815d0928183c647eb425" @@ -5215,7 +5231,7 @@ arrify@^2.0.1: resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== -asap@^2.0.6, asap@~2.0.3: +asap@~2.0.3: version "2.0.6" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= @@ -6500,11 +6516,6 @@ chalk@^4.0.2, chalk@^4.1.2: ansi-styles "^4.1.0" supports-color "^7.1.0" -change-emitter@^0.1.2: - version "0.1.6" - resolved "https://registry.yarnpkg.com/change-emitter/-/change-emitter-0.1.6.tgz#e8b2fe3d7f1ab7d69a32199aff91ea6931409515" - integrity sha1-6LL+PX8at9aaMhma/5HqaTFAlRU= - char-regex@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" @@ -8492,15 +8503,14 @@ dmg-license@^1.0.11: smart-buffer "^4.0.2" verror "^1.10.0" -dnd-core@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/dnd-core/-/dnd-core-6.0.0.tgz#d347266ebd72f0a2de6ecf5e26e4ef006ebde84b" - integrity sha512-WnnFSbnC3grP/XJ+xfxgM8DyIsts3Q/rfgE6WGRWs6tCQcwILputNNm/Kw+WPS2N1e46hRy5iPl2pYwkP9kK9Q== +dnd-core@^16.0.1: + version "16.0.1" + resolved "https://registry.yarnpkg.com/dnd-core/-/dnd-core-16.0.1.tgz#a1c213ed08961f6bd1959a28bb76f1a868360d19" + integrity sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng== dependencies: - asap "^2.0.6" - invariant "^2.2.4" - lodash "^4.17.11" - redux "^4.0.1" + "@react-dnd/asap" "^5.0.1" + "@react-dnd/invariant" "^4.0.1" + redux "^4.2.0" dns-equal@^1.0.0: version "1.0.0" @@ -10011,7 +10021,7 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" -fbjs@^0.8.0, fbjs@^0.8.1: +fbjs@^0.8.0: version "0.8.17" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd" integrity sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90= @@ -11592,11 +11602,6 @@ hmac-drbg@^1.0.1: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -hoist-non-react-statics@^2.3.1: - version "2.5.5" - resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47" - integrity sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw== - hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" @@ -12199,7 +12204,7 @@ into-stream@^3.1.0: from2 "^2.1.1" p-is-promise "^1.1.0" -invariant@^2.1.0, invariant@^2.2.1, invariant@^2.2.4: +invariant@^2.2.1, invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== @@ -15139,13 +15144,6 @@ node-abi@^3.45.0: dependencies: semver "^7.3.5" -node-abi@^3.45.0: - version "3.54.0" - resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.54.0.tgz#f6386f7548817acac6434c6cba02999c9aebcc69" - integrity sha512-p7eGEiQil0YUV3ItH4/tBb781L5impVmmx2E9FRKF7d18XXzp4PGT2tdYMFY6wQqgxD0IwNZOiSJ0/K0fSi/OA== - dependencies: - semver "^7.3.5" - node-addon-api@^1.6.3: version "1.7.2" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.7.2.tgz#3df30b95720b53c24e59948b49532b662444f54d" @@ -17518,22 +17516,21 @@ react-color@2.19.3: reactcss "^1.2.0" tinycolor2 "^1.4.1" -react-dnd-mouse-backend@0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/react-dnd-mouse-backend/-/react-dnd-mouse-backend-0.1.2.tgz#bf79e5cc20715fb1bc03f3ba20389cc5b062f5da" - integrity sha512-A1kgknzYKysVgqwHnB7aFzNmV0/CBK5rJdsCSAIxZxJYaNqymfFgtDD5KneR4dVEva2nFvJXH5th1uYGH4DZGQ== +react-dnd-mouse-backend@1.0.0-rc.2: + version "1.0.0-rc.2" + resolved "https://registry.yarnpkg.com/react-dnd-mouse-backend/-/react-dnd-mouse-backend-1.0.0-rc.2.tgz#bc114d2e91ee6d079adc6920c22167a9780185af" + integrity sha512-mmbyXvg6YGKxdi7gx+ICFD0pFEMMiG1x7FBSu8ZYXBCqxVPkm8pkhrHfzd8AY89YmcWoUeqL3/DSU0u9lkwbsQ== -react-dnd@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/react-dnd/-/react-dnd-6.0.0.tgz#0780eafaa47293bf12dc8f79cf1e48ede8ba72f1" - integrity sha512-XI14rxF5eeGk8045xh/6KbjfLSzgkfNdQCqwkR5qAvBf0QYvkGAUz1AQfLrQudFs/DVw7WiCoCohRzJR1Kyn9Q== +react-dnd@16.0.1: + version "16.0.1" + resolved "https://registry.yarnpkg.com/react-dnd/-/react-dnd-16.0.1.tgz#2442a3ec67892c60d40a1559eef45498ba26fa37" + integrity sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q== dependencies: - dnd-core "^6.0.0" - hoist-non-react-statics "^3.1.0" - invariant "^2.1.0" - lodash "^4.17.11" - recompose "^0.30.0" - shallowequal "^1.1.0" + "@react-dnd/invariant" "^4.0.1" + "@react-dnd/shallowequal" "^4.0.1" + dnd-core "^16.0.1" + fast-deep-equal "^3.1.3" + hoist-non-react-statics "^3.3.2" react-docgen-typescript@^1.21.0: version "1.22.0" @@ -17640,11 +17637,6 @@ react-is@^18.0.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== -react-lifecycles-compat@^3.0.2: - version "3.0.4" - resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" - integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== - react-popper@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-1.0.0.tgz#b99452144e8fe4acc77fa3d959a8c79e07a65084" @@ -17938,18 +17930,6 @@ rechoir@^0.6.2: dependencies: resolve "^1.1.6" -recompose@^0.30.0: - version "0.30.0" - resolved "https://registry.yarnpkg.com/recompose/-/recompose-0.30.0.tgz#82773641b3927e8c7d24a0d87d65aeeba18aabd0" - integrity sha512-ZTrzzUDa9AqUIhRk4KmVFihH0rapdCSMFXjhHbNrjAWxBuUD/guYlyysMnuHjlZC/KRiOKRtB4jf96yYSkKE8w== - dependencies: - "@babel/runtime" "^7.0.0" - change-emitter "^0.1.2" - fbjs "^0.8.1" - hoist-non-react-statics "^2.3.1" - react-lifecycles-compat "^3.0.2" - symbol-observable "^1.0.4" - redent@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" @@ -18014,13 +17994,20 @@ redux@4.0.5: loose-envify "^1.4.0" symbol-observable "^1.2.0" -redux@^4.0.0, redux@^4.0.1, redux@^4.0.5: +redux@^4.0.0, redux@^4.0.5: version "4.1.0" resolved "https://registry.yarnpkg.com/redux/-/redux-4.1.0.tgz#eb049679f2f523c379f1aff345c8612f294c88d4" integrity sha512-uI2dQN43zqLWCt6B/BMGRMY6db7TTY4qeHHfGeKb3EOhmOKjU3KdWvNLJyqaHRksv/ErdNH7cFZWg9jXtewy4g== dependencies: "@babel/runtime" "^7.9.2" +redux@^4.2.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.1.tgz#c08f4306826c49b5e9dc901dee0452ea8fce6197" + integrity sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w== + dependencies: + "@babel/runtime" "^7.9.2" + regenerate-unicode-properties@^8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec" @@ -20267,7 +20254,7 @@ svgo@^1.0.0: unquote "~1.1.1" util.promisify "~1.0.0" -symbol-observable@^1.0.4, symbol-observable@^1.1.0, symbol-observable@^1.2.0: +symbol-observable@^1.1.0, symbol-observable@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== From 30ad3c10012284f8bebe651b59902199c6da9964 Mon Sep 17 00:00:00 2001 From: Jethary Date: Thu, 8 Feb 2024 11:40:35 -0500 Subject: [PATCH 02/17] some tweaks --- .../steplist/DraggableStepItems.tsx | 51 +++++++++---------- .../src/containers/ConnectedStepItem.tsx | 2 +- 2 files changed, 24 insertions(+), 29 deletions(-) diff --git a/protocol-designer/src/components/steplist/DraggableStepItems.tsx b/protocol-designer/src/components/steplist/DraggableStepItems.tsx index 9532a40bc1f..57c555e8d2a 100644 --- a/protocol-designer/src/components/steplist/DraggableStepItems.tsx +++ b/protocol-designer/src/components/steplist/DraggableStepItems.tsx @@ -25,18 +25,16 @@ interface DragDropStepItemProps extends ConnectedStepItemProps { stepNumber: number findStepIndex: (stepIdType: StepIdType) => number onDrag: () => void - moveStep: (stepId: StepIdType, value: number) => void + moveStep: (draggedId: StepIdType, value: number) => void } const DragDropStepItem = (props: DragDropStepItemProps): JSX.Element => { const { onDrag, stepId, findStepIndex, moveStep } = props - console.log('stepId', stepId) + const ref = React.useRef(null) + const [{ isDragging }, drag] = useDrag(() => ({ type: DND_TYPES.STEP_ITEM, - beginDrag: () => { - onDrag() - return { stepId: stepId } - }, + item: { stepId }, collect: (monitor: DragLayerMonitor) => ({ isDragging: monitor.isDragging(), }), @@ -47,22 +45,24 @@ const DragDropStepItem = (props: DragDropStepItemProps): JSX.Element => { canDrop: () => { return false }, - hover: (item: any) => { - console.log('item', item) - const draggedId = item.stepId + hover: (item: { stepId: StepIdType }) => { + const draggedId: StepIdType = item.stepId + const targetIndex = findStepIndex(stepId) + const draggedIndex = findStepIndex(draggedId) + + const adjustedTargetIndex = + draggedIndex < targetIndex ? targetIndex - 1 : targetIndex if (draggedId !== stepId) { - const overIndex = findStepIndex(stepId) - moveStep(draggedId, overIndex) + moveStep(draggedId, adjustedTargetIndex) } }, })) + drag(drop(ref)) return ( -
-
- -
+
+
) } @@ -79,21 +79,17 @@ export const DraggableStepItems = (props: StepItemsProps): JSX.Element => { setStepIds(orderedStepIds) } - const submitReordering = (): void => { - if ( - confirm( - 'Are you sure you want to reorder these steps, it may cause errors?' - ) - ) { - reorderSteps(stepIds) - } - } - const [{ isOver }, drop] = useDrop(() => ({ accept: DND_TYPES.STEP_ITEM, drop: () => { if (!isEqual(orderedStepIds, stepIds)) { - submitReordering() + if ( + confirm( + 'Are you sure you want to reorder these steps, it may cause errors?' + ) + ) { + reorderSteps(stepIds) + } } }, collect: (monitor: DropTargetMonitor) => ({ @@ -120,8 +116,7 @@ export const DraggableStepItems = (props: StepItemsProps): JSX.Element => { stepIds.findIndex(id => stepId === id) const currentIds = isOver ? stepIds : orderedStepIds - console.log('stepIds', stepIds) - console.log('currentStepIds', currentIds) + return (
diff --git a/protocol-designer/src/containers/ConnectedStepItem.tsx b/protocol-designer/src/containers/ConnectedStepItem.tsx index 27ea034b099..15988747480 100644 --- a/protocol-designer/src/containers/ConnectedStepItem.tsx +++ b/protocol-designer/src/containers/ConnectedStepItem.tsx @@ -70,7 +70,7 @@ export const ConnectedStepItem = ( props: ConnectedStepItemProps ): JSX.Element => { const { stepId, stepNumber } = props - + const step = useSelector(stepFormSelectors.getSavedStepForms)[stepId] const argsAndErrors = useSelector(stepFormSelectors.getArgsAndErrorsByStepId)[ stepId From e41fedd80d030fb6d5ec82c149bb6d94741cd05e Mon Sep 17 00:00:00 2001 From: Jethary Date: Fri, 9 Feb 2024 13:55:41 -0500 Subject: [PATCH 03/17] temp changes to draggableStepitems --- .../steplist/DraggableStepItems.tsx | 97 +++++++++++-------- 1 file changed, 54 insertions(+), 43 deletions(-) diff --git a/protocol-designer/src/components/steplist/DraggableStepItems.tsx b/protocol-designer/src/components/steplist/DraggableStepItems.tsx index 57c555e8d2a..a78ba72e281 100644 --- a/protocol-designer/src/components/steplist/DraggableStepItems.tsx +++ b/protocol-designer/src/components/steplist/DraggableStepItems.tsx @@ -22,40 +22,63 @@ import styles from './StepItem.css' interface DragDropStepItemProps extends ConnectedStepItemProps { stepId: StepIdType - stepNumber: number - findStepIndex: (stepIdType: StepIdType) => number - onDrag: () => void - moveStep: (draggedId: StepIdType, value: number) => void + index: number + moveStep: (dragIndex: number, hoverIndex: number) => void } const DragDropStepItem = (props: DragDropStepItemProps): JSX.Element => { - const { onDrag, stepId, findStepIndex, moveStep } = props - const ref = React.useRef(null) + const { stepId, index, moveStep } = props + const ref = React.useRef(null) - const [{ isDragging }, drag] = useDrag(() => ({ + const [{ isDragging }, drag] = useDrag({ type: DND_TYPES.STEP_ITEM, - item: { stepId }, + item: { stepId, index }, + + // beginDrag: () => { + // console.log('you his the onDrag Begin') + // onDragBegin() + // return { stepId: stepId } // Ensure stepId is captured correctly + // }, collect: (monitor: DragLayerMonitor) => ({ isDragging: monitor.isDragging(), }), - })) + }) const [, drop] = useDrop(() => ({ accept: DND_TYPES.STEP_ITEM, canDrop: () => { return false }, - hover: (item: { stepId: StepIdType }) => { - const draggedId: StepIdType = item.stepId - const targetIndex = findStepIndex(stepId) - const draggedIndex = findStepIndex(draggedId) + hover: (item: { index?: number }, monitor: DropTargetMonitor) => { + if (!ref.current || !item.index) { + return + } + const dragIndex = item.index + const hoverIndex = index - const adjustedTargetIndex = - draggedIndex < targetIndex ? targetIndex - 1 : targetIndex + if (dragIndex === hoverIndex) { + return + } + + const hoverBoundingRect = ref.current.getBoundingClientRect() + const hoverMiddleY = + (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2 + const clientOffset = monitor.getClientOffset() + if (!clientOffset) { + return + } + const hoverClientY = clientOffset.y - hoverBoundingRect.top - if (draggedId !== stepId) { - moveStep(draggedId, adjustedTargetIndex) + if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) { + return } + if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) { + return + } + console.log('dragIndex', dragIndex) + console.log('hoverINdex', hoverIndex) + moveStep(dragIndex, hoverIndex) + item.index = hoverIndex }, })) @@ -71,17 +94,17 @@ interface StepItemsProps { orderedStepIds: StepIdType[] reorderSteps: (steps: StepIdType[]) => void } -export const DraggableStepItems = (props: StepItemsProps): JSX.Element => { +export const DraggableStepItems = ( + props: StepItemsProps +): JSX.Element | null => { const { orderedStepIds, reorderSteps } = props const [stepIds, setStepIds] = React.useState(orderedStepIds) - - const onDrag = (): void => { - setStepIds(orderedStepIds) - } + const ref = React.useRef(null) const [{ isOver }, drop] = useDrop(() => ({ accept: DND_TYPES.STEP_ITEM, drop: () => { + console.log('hit the drop') if (!isEqual(orderedStepIds, stepIds)) { if ( confirm( @@ -97,38 +120,26 @@ export const DraggableStepItems = (props: StepItemsProps): JSX.Element => { }), })) - // TODO: BC 2018-11-27 make util function for reordering and use it in hotkey implementation too - const moveStep = (stepId: StepIdType, targetIndex: number): void => { - const currentIndex = findStepIndex(stepId) - const currentRemoved = [ - ...stepIds.slice(0, currentIndex), - ...stepIds.slice(currentIndex + 1, stepIds.length), - ] - const currentReinserted = [ - ...currentRemoved.slice(0, targetIndex), - stepId, - ...currentRemoved.slice(targetIndex, currentRemoved.length), - ] - setStepIds(currentReinserted) + const moveStep = (dragIndex: number, hoverIndex: number): void => { + const updatedStepIds = [...stepIds] + const draggedStep = updatedStepIds.splice(dragIndex, 1)[0] + updatedStepIds.splice(hoverIndex, 0, draggedStep) + setStepIds(updatedStepIds) } - - const findStepIndex = (stepId: StepIdType): number => - stepIds.findIndex(id => stepId === id) - const currentIds = isOver ? stepIds : orderedStepIds + drop(ref) return ( -
+
{({ makeStepOnContextMenu }) => currentIds.map((stepId: StepIdType, index: number) => ( makeStepOnContextMenu(stepId)} - findStepIndex={findStepIndex} - onDrag={onDrag} moveStep={moveStep} /> )) From 5620e195f057886045b8fe8c43f393f549cf8942 Mon Sep 17 00:00:00 2001 From: Jethary Date: Tue, 13 Feb 2024 11:41:10 -0500 Subject: [PATCH 04/17] fix errors with draggableStepitems --- .../LabwareOverlays/SlotControls.tsx | 5 +- .../steplist/DraggableStepItems.tsx | 126 +++++++----------- .../src/localization/en/shared.json | 1 + 3 files changed, 55 insertions(+), 77 deletions(-) diff --git a/protocol-designer/src/components/DeckSetup/LabwareOverlays/SlotControls.tsx b/protocol-designer/src/components/DeckSetup/LabwareOverlays/SlotControls.tsx index 86731b08b9c..97695f7634e 100644 --- a/protocol-designer/src/components/DeckSetup/LabwareOverlays/SlotControls.tsx +++ b/protocol-designer/src/components/DeckSetup/LabwareOverlays/SlotControls.tsx @@ -70,8 +70,9 @@ export const SlotControls = (props: SlotControlsProps): JSX.Element | null => { ) return getLabwareIsCompatible(draggedDef, moduleType) || isCustomLabware + } else { + return true } - return true }, drop: (item: DroppedItem) => { if (item.labwareOnDeck) { @@ -79,7 +80,7 @@ export const SlotControls = (props: SlotControlsProps): JSX.Element | null => { } }, hover: () => { - if (handleDragHover) { + if (handleDragHover != null) { handleDragHover() } }, diff --git a/protocol-designer/src/components/steplist/DraggableStepItems.tsx b/protocol-designer/src/components/steplist/DraggableStepItems.tsx index a78ba72e281..36f6b86cdbf 100644 --- a/protocol-designer/src/components/steplist/DraggableStepItems.tsx +++ b/protocol-designer/src/components/steplist/DraggableStepItems.tsx @@ -1,11 +1,7 @@ import * as React from 'react' +import { useTranslation } from 'react-i18next' import { useSelector } from 'react-redux' -import { - DragLayerMonitor, - DropTargetMonitor, - useDrop, - useDrag, -} from 'react-dnd' +import { DragLayerMonitor, useDrop, useDrag } from 'react-dnd' import isEqual from 'lodash/isEqual' import { DND_TYPES } from '../../constants' @@ -22,23 +18,22 @@ import styles from './StepItem.css' interface DragDropStepItemProps extends ConnectedStepItemProps { stepId: StepIdType - index: number - moveStep: (dragIndex: number, hoverIndex: number) => void + findStepIndex: (stepIdType: StepIdType) => number + clickDrop: () => void + moveStep: (stepId: StepIdType, value: number) => void +} + +interface DropType { + stepId: StepIdType } const DragDropStepItem = (props: DragDropStepItemProps): JSX.Element => { - const { stepId, index, moveStep } = props + const { stepId, moveStep, clickDrop, findStepIndex } = props const ref = React.useRef(null) const [{ isDragging }, drag] = useDrag({ type: DND_TYPES.STEP_ITEM, - item: { stepId, index }, - - // beginDrag: () => { - // console.log('you his the onDrag Begin') - // onDragBegin() - // return { stepId: stepId } // Ensure stepId is captured correctly - // }, + item: { stepId }, collect: (monitor: DragLayerMonitor) => ({ isDragging: monitor.isDragging(), }), @@ -47,38 +42,18 @@ const DragDropStepItem = (props: DragDropStepItemProps): JSX.Element => { const [, drop] = useDrop(() => ({ accept: DND_TYPES.STEP_ITEM, canDrop: () => { - return false + return true }, - hover: (item: { index?: number }, monitor: DropTargetMonitor) => { - if (!ref.current || !item.index) { - return - } - const dragIndex = item.index - const hoverIndex = index - - if (dragIndex === hoverIndex) { - return - } - - const hoverBoundingRect = ref.current.getBoundingClientRect() - const hoverMiddleY = - (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2 - const clientOffset = monitor.getClientOffset() - if (!clientOffset) { - return - } - const hoverClientY = clientOffset.y - hoverBoundingRect.top + drop: () => { + clickDrop() + }, + hover: (item: DropType) => { + const draggedId = item.stepId - if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) { - return + if (draggedId !== stepId) { + const overIndex = findStepIndex(stepId) + moveStep(draggedId, overIndex) } - if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) { - return - } - console.log('dragIndex', dragIndex) - console.log('hoverINdex', hoverIndex) - moveStep(dragIndex, hoverIndex) - item.index = hoverIndex }, })) @@ -98,55 +73,56 @@ export const DraggableStepItems = ( props: StepItemsProps ): JSX.Element | null => { const { orderedStepIds, reorderSteps } = props + const { t } = useTranslation('shared') const [stepIds, setStepIds] = React.useState(orderedStepIds) - const ref = React.useRef(null) - const [{ isOver }, drop] = useDrop(() => ({ - accept: DND_TYPES.STEP_ITEM, - drop: () => { - console.log('hit the drop') - if (!isEqual(orderedStepIds, stepIds)) { - if ( - confirm( - 'Are you sure you want to reorder these steps, it may cause errors?' - ) - ) { - reorderSteps(stepIds) - } + React.useEffect(() => { + setStepIds(orderedStepIds) + }, [orderedStepIds]) + + const clickDrop = (): void => { + if (!isEqual(orderedStepIds, stepIds)) { + if (confirm(t('confirm_reorder'))) { + reorderSteps(stepIds) } - }, - collect: (monitor: DropTargetMonitor) => ({ - isOver: !!monitor.isOver(), - }), - })) + } + } - const moveStep = (dragIndex: number, hoverIndex: number): void => { - const updatedStepIds = [...stepIds] - const draggedStep = updatedStepIds.splice(dragIndex, 1)[0] - updatedStepIds.splice(hoverIndex, 0, draggedStep) - setStepIds(updatedStepIds) + const moveStep = (stepId: StepIdType, targetIndex: number): void => { + const currentIndex = findStepIndex(stepId) + const currentRemoved = [ + ...stepIds.slice(0, currentIndex), + ...stepIds.slice(currentIndex + 1, stepIds.length), + ] + const currentReinserted = [ + ...currentRemoved.slice(0, targetIndex), + stepId, + ...currentRemoved.slice(targetIndex, currentRemoved.length), + ] + setStepIds(currentReinserted) } - const currentIds = isOver ? stepIds : orderedStepIds + const findStepIndex = (stepId: StepIdType): number => + stepIds.findIndex(id => stepId === id) - drop(ref) return ( -
+ <> {({ makeStepOnContextMenu }) => - currentIds.map((stepId: StepIdType, index: number) => ( + stepIds.map((stepId: StepIdType, index: number) => ( makeStepOnContextMenu(stepId)} moveStep={moveStep} + findStepIndex={findStepIndex} + clickDrop={clickDrop} /> )) } -
+ ) } diff --git a/protocol-designer/src/localization/en/shared.json b/protocol-designer/src/localization/en/shared.json index 433b1eba68a..d69d55ffe32 100644 --- a/protocol-designer/src/localization/en/shared.json +++ b/protocol-designer/src/localization/en/shared.json @@ -1,5 +1,6 @@ { "add": "add", + "confirm_reorder": "Are you sure you want to reorder these steps, it may cause errors?", "edit": "edit", "exit": "exit", "go_back": "go back", From f7a40fb206ae4314085d667cc092c5416f75413b Mon Sep 17 00:00:00 2001 From: Jethary Date: Tue, 13 Feb 2024 12:59:35 -0500 Subject: [PATCH 05/17] feat(protocol-designer): update React DnD to version 16.0.1 closes RAUT-964 --- .../DeckSetup/LabwareOverlays/EditLabware.tsx | 24 +++++++++++-------- .../src/containers/ConnectedStepItem.tsx | 2 +- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/protocol-designer/src/components/DeckSetup/LabwareOverlays/EditLabware.tsx b/protocol-designer/src/components/DeckSetup/LabwareOverlays/EditLabware.tsx index 530032c2306..f6ca329cafa 100644 --- a/protocol-designer/src/components/DeckSetup/LabwareOverlays/EditLabware.tsx +++ b/protocol-designer/src/components/DeckSetup/LabwareOverlays/EditLabware.tsx @@ -25,6 +25,9 @@ interface Props { swapBlocked: boolean } +interface DroppedItem { + labwareOnDeck: LabwareOnDeck +} export const EditLabware = (props: Props): JSX.Element | null => { const { labwareOnDeck, @@ -59,30 +62,30 @@ export const EditLabware = (props: Props): JSX.Element | null => { const [{ draggedLabware, isOver }, drop] = useDrop(() => ({ accept: DND_TYPES.LABWARE, - canDrop: (monitor: DropTargetMonitor) => { - const draggedItem: any = monitor.getItem() + canDrop: (item: any) => { + const draggedItem = item?.labwareOnDeck const draggedLabware = draggedItem?.labwareOnDeck const isDifferentSlot = - draggedLabware && draggedLabware.slot !== props.labwareOnDeck.slot - return isDifferentSlot && !props.swapBlocked + draggedLabware && draggedLabware.slot !== labwareOnDeck.slot + return isDifferentSlot && !swapBlocked }, - drop: (monitor: DropTargetMonitor) => { - const draggedItem: any = monitor.getItem() + drop: (item: any) => { + const draggedItem = item?.labwareOnDeck if (draggedItem) { dispatch( - moveDeckItem(draggedItem.labwareOnDeck.slot, props.labwareOnDeck.slot) + moveDeckItem(draggedItem.labwareOnDeck.slot, labwareOnDeck.slot) ) } }, hover: (monitor: DropTargetMonitor) => { if (monitor.canDrop()) { - props.setHoveredLabware(labwareOnDeck) + setHoveredLabware(labwareOnDeck) } }, collect: (monitor: DropTargetMonitor) => ({ isOver: monitor.isOver(), - draggedLabware: monitor.getItem() as any, + draggedLabware: monitor.getItem() as DroppedItem, }), })) @@ -94,7 +97,8 @@ export const EditLabware = (props: Props): JSX.Element | null => { /> ) } else { - const isBeingDragged = draggedLabware?.slot === labwareOnDeck.slot + const isBeingDragged = + draggedLabware?.labwareOnDeck.slot === labwareOnDeck.slot let contents: React.ReactNode | null = null diff --git a/protocol-designer/src/containers/ConnectedStepItem.tsx b/protocol-designer/src/containers/ConnectedStepItem.tsx index 15988747480..27ea034b099 100644 --- a/protocol-designer/src/containers/ConnectedStepItem.tsx +++ b/protocol-designer/src/containers/ConnectedStepItem.tsx @@ -70,7 +70,7 @@ export const ConnectedStepItem = ( props: ConnectedStepItemProps ): JSX.Element => { const { stepId, stepNumber } = props - + const step = useSelector(stepFormSelectors.getSavedStepForms)[stepId] const argsAndErrors = useSelector(stepFormSelectors.getArgsAndErrorsByStepId)[ stepId From 54487679b04cfd8d798a5de247b14c106c91f8ab Mon Sep 17 00:00:00 2001 From: Jethary Date: Tue, 13 Feb 2024 16:47:38 -0500 Subject: [PATCH 06/17] fix bug with slot and adapter controls --- .../LabwareOverlays/AdapterControls.tsx | 33 ++++++++++++++--- .../LabwareOverlays/SlotControls.tsx | 37 +++++++++++++++---- 2 files changed, 56 insertions(+), 14 deletions(-) diff --git a/protocol-designer/src/components/DeckSetup/LabwareOverlays/AdapterControls.tsx b/protocol-designer/src/components/DeckSetup/LabwareOverlays/AdapterControls.tsx index a2b68652f47..7b0dc5b6dc3 100644 --- a/protocol-designer/src/components/DeckSetup/LabwareOverlays/AdapterControls.tsx +++ b/protocol-designer/src/components/DeckSetup/LabwareOverlays/AdapterControls.tsx @@ -15,6 +15,7 @@ import { moveDeckItem, openAddLabwareModal, } from '../../../labware-ingred/actions' +import { getDeckSetupForActiveItem } from '../../../top-selectors/labware-locations' import { selectors as labwareDefSelectors } from '../../../labware-defs' import { START_TERMINAL_ITEM_ID, TerminalItemId } from '../../../steplist' import { BlockedSlot } from './BlockedSlot' @@ -54,13 +55,17 @@ export const AdapterControls = ( const customLabwareDefs = useSelector( labwareDefSelectors.getCustomLabwareDefsByURI ) - + const activeDeckSetup = useSelector(getDeckSetupForActiveItem) + const labware = activeDeckSetup.labware + const ref = React.useRef(null) + const [newSlot, setSlot] = React.useState(null) const dispatch = useDispatch() + const adapterName = allLabware.find(labware => labware.id === labwareId)?.def.metadata .displayName ?? '' - const [{ itemType, draggedItem, isOver }, drop] = useDrop(() => ({ + const [{ itemType, draggedItem, isOver }, drop] = useDrop({ accept: DND_TYPES.LABWARE, canDrop: (item: DroppedItem) => { const draggedDef = item.labwareOnDeck?.def @@ -82,8 +87,12 @@ export const AdapterControls = ( return true }, drop: (item: DroppedItem) => { - if (item.labwareOnDeck) { - dispatch(moveDeckItem(item.labwareOnDeck.slot, labwareId)) + const droppedLabware = item + if (newSlot != null) { + dispatch(moveDeckItem(newSlot, labwareId)) + } else if (droppedLabware.labwareOnDeck != null) { + const droppedSlot = droppedLabware.labwareOnDeck.slot + dispatch(moveDeckItem(droppedSlot, labwareId)) } }, hover: () => { @@ -96,7 +105,17 @@ export const AdapterControls = ( isOver: !!monitor.isOver(), draggedItem: monitor.getItem() as DroppedItem, }), - })) + }) + + const draggedLabware = Object.values(labware).find( + l => l.id === draggedItem?.labwareOnDeck?.id + ) + + React.useEffect(() => { + if (draggedLabware != null) { + setSlot(draggedLabware.slot) + } + }) if ( selectedTerminalItemId !== START_TERMINAL_ITEM_ID || @@ -124,8 +143,10 @@ export const AdapterControls = ( slotBlocked = 'Labware incompatible with this adapter' } + drop(ref) + return ( - + {slotBlocked ? ( unknown + handleDragHover?: () => void } interface DroppedItem { @@ -53,10 +54,15 @@ export const SlotControls = (props: SlotControlsProps): JSX.Element | null => { const customLabwareDefs = useSelector( labwareDefSelectors.getCustomLabwareDefsByURI ) + const activeDeckSetup = useSelector(getDeckSetupForActiveItem) + const labware = activeDeckSetup.labware + const ref = React.useRef(null) + const [newSlot, setSlot] = React.useState(null) const dispatch = useDispatch() + const { t } = useTranslation('deck') - const [{ itemType, draggedItem, isOver }, drop] = useDrop(() => ({ + const [{ draggedItem, itemType, isOver }, drop] = useDrop({ accept: DND_TYPES.LABWARE, canDrop: (item: DroppedItem) => { const draggedDef = item?.labwareOnDeck?.def @@ -70,13 +76,16 @@ export const SlotControls = (props: SlotControlsProps): JSX.Element | null => { ) return getLabwareIsCompatible(draggedDef, moduleType) || isCustomLabware - } else { - return true } + return true }, drop: (item: DroppedItem) => { - if (item.labwareOnDeck) { - dispatch(moveDeckItem(item.labwareOnDeck.slot, slotId)) + const droppedLabware = item + if (newSlot != null) { + dispatch(moveDeckItem(newSlot, slotId)) + } else if (droppedLabware.labwareOnDeck != null) { + const droppedSlot = droppedLabware.labwareOnDeck.slot + dispatch(moveDeckItem(droppedSlot, slotId)) } }, hover: () => { @@ -89,7 +98,17 @@ export const SlotControls = (props: SlotControlsProps): JSX.Element | null => { isOver: !!monitor.isOver(), draggedItem: monitor.getItem() as DroppedItem, }), - })) + }) + + const draggedLabware = Object.values(labware).find( + l => l.id === draggedItem?.labwareOnDeck?.id + ) + + React.useEffect(() => { + if (draggedLabware != null) { + setSlot(draggedLabware.slot) + } + }) if ( selectedTerminalItemId !== START_TERMINAL_ITEM_ID || @@ -131,8 +150,10 @@ export const SlotControls = (props: SlotControlsProps): JSX.Element | null => { dispatch(openAddLabwareModal({ slot: slotId })) } + drop(ref) + return ( - + {slotBlocked ? ( Date: Wed, 14 Feb 2024 12:39:21 -0500 Subject: [PATCH 07/17] fix deleting steps --- .../src/components/steplist/ContextMenu.tsx | 22 +++++++------ .../steplist/DraggableStepItems.tsx | 32 +++++++++++++++---- 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/protocol-designer/src/components/steplist/ContextMenu.tsx b/protocol-designer/src/components/steplist/ContextMenu.tsx index 24a790575c0..354d8f7113c 100644 --- a/protocol-designer/src/components/steplist/ContextMenu.tsx +++ b/protocol-designer/src/components/steplist/ContextMenu.tsx @@ -8,12 +8,13 @@ import { } from '../modals/ConfirmDeleteModal' import { actions as stepsActions, getIsMultiSelectMode } from '../../ui/steps' import { actions as steplistActions } from '../../steplist' +import { getSavedStepForms } from '../../step-forms/selectors' import { Portal } from '../portals/TopPortal' import styles from './StepItem.css' -import { StepIdType } from '../../form-types' -import { getSavedStepForms } from '../../step-forms/selectors' -import { ThunkDispatch } from 'redux-thunk' -import { BaseState } from '../../types' + +import type { StepIdType } from '../../form-types' +import type { ThunkDispatch } from 'redux-thunk' +import type { BaseState } from '../../types' const MENU_OFFSET_PX = 5 @@ -21,7 +22,7 @@ interface Props { children: (args: { makeStepOnContextMenu: ( stepIdType: StepIdType - ) => (event: MouseEvent) => unknown + ) => (event: MouseEvent) => void }) => React.ReactNode } @@ -33,10 +34,9 @@ interface Position { export const ContextMenu = (props: Props): JSX.Element => { const { t } = useTranslation('context_menu') const dispatch = useDispatch>() - const deleteStep = ( - stepId: StepIdType - ): ReturnType => + const deleteStep = (stepId: StepIdType): void => { dispatch(steplistActions.deleteStep(stepId)) + } const duplicateStep = ( stepId: StepIdType ): ReturnType => @@ -61,6 +61,10 @@ export const ContextMenu = (props: Props): JSX.Element => { }) const makeHandleContextMenu = (stepId: StepIdType) => (event: MouseEvent) => { + console.log( + 'handle context menu called before isMulti SelectMode', + isMultiSelectMode + ) if (isMultiSelectMode) return event.preventDefault() @@ -80,7 +84,7 @@ export const ContextMenu = (props: Props): JSX.Element => { screenH - clickY > rootH ? clickY + MENU_OFFSET_PX : clickY - rootH - MENU_OFFSET_PX - + console.log('handle context menu called') setVisible(true) setStepId(stepId) setPosition({ left, top }) diff --git a/protocol-designer/src/components/steplist/DraggableStepItems.tsx b/protocol-designer/src/components/steplist/DraggableStepItems.tsx index 36f6b86cdbf..63c5f476da0 100644 --- a/protocol-designer/src/components/steplist/DraggableStepItems.tsx +++ b/protocol-designer/src/components/steplist/DraggableStepItems.tsx @@ -1,7 +1,12 @@ import * as React from 'react' import { useTranslation } from 'react-i18next' import { useSelector } from 'react-redux' -import { DragLayerMonitor, useDrop, useDrag } from 'react-dnd' +import { + DragLayerMonitor, + useDrop, + useDrag, + DropTargetOptions, +} from 'react-dnd' import isEqual from 'lodash/isEqual' import { DND_TYPES } from '../../constants' @@ -21,6 +26,7 @@ interface DragDropStepItemProps extends ConnectedStepItemProps { findStepIndex: (stepIdType: StepIdType) => number clickDrop: () => void moveStep: (stepId: StepIdType, value: number) => void + setIsOver: React.Dispatch> } interface DropType { @@ -28,7 +34,7 @@ interface DropType { } const DragDropStepItem = (props: DragDropStepItemProps): JSX.Element => { - const { stepId, moveStep, clickDrop, findStepIndex } = props + const { stepId, moveStep, clickDrop, findStepIndex, setIsOver } = props const ref = React.useRef(null) const [{ isDragging }, drag] = useDrag({ @@ -39,7 +45,7 @@ const DragDropStepItem = (props: DragDropStepItemProps): JSX.Element => { }), }) - const [, drop] = useDrop(() => ({ + const [{ isOver }, drop] = useDrop(() => ({ accept: DND_TYPES.STEP_ITEM, canDrop: () => { return true @@ -55,8 +61,15 @@ const DragDropStepItem = (props: DragDropStepItemProps): JSX.Element => { moveStep(draggedId, overIndex) } }, + collect: (monitor: DropTargetOptions) => ({ + isOver: monitor.isOver(), + }), })) + React.useEffect(() => { + setIsOver(isOver) + }, [isOver]) + drag(drop(ref)) return (
@@ -74,11 +87,13 @@ export const DraggableStepItems = ( ): JSX.Element | null => { const { orderedStepIds, reorderSteps } = props const { t } = useTranslation('shared') + const [isOver, setIsOver] = React.useState(false) const [stepIds, setStepIds] = React.useState(orderedStepIds) - React.useEffect(() => { setStepIds(orderedStepIds) }, [orderedStepIds]) + console.log('orderedStepIds', orderedStepIds) + console.log('stepIds', stepIds) const clickDrop = (): void => { if (!isEqual(orderedStepIds, stepIds)) { @@ -104,19 +119,24 @@ export const DraggableStepItems = ( const findStepIndex = (stepId: StepIdType): number => stepIds.findIndex(id => stepId === id) + const currentIds = isOver ? stepIds : orderedStepIds + console.log('isover', isOver) + console.log('currentIds', currentIds) return ( <> {({ makeStepOnContextMenu }) => - stepIds.map((stepId: StepIdType, index: number) => ( + currentIds.map((stepId: StepIdType, index: number) => ( makeStepOnContextMenu(stepId)} + // @ts-expect-error + onStepContextMenu={makeStepOnContextMenu(stepId)} moveStep={moveStep} findStepIndex={findStepIndex} clickDrop={clickDrop} + setIsOver={setIsOver} /> )) } From 7e4ca7e3037e1e8750b09cd790f1d28e6e23deb1 Mon Sep 17 00:00:00 2001 From: Jethary Date: Wed, 14 Feb 2024 13:33:38 -0500 Subject: [PATCH 08/17] fix editLabware drag and drop --- .../DeckSetup/LabwareOverlays/EditLabware.tsx | 49 +++++++++---------- .../src/components/steplist/ContextMenu.tsx | 5 -- .../steplist/DraggableStepItems.tsx | 5 +- 3 files changed, 24 insertions(+), 35 deletions(-) diff --git a/protocol-designer/src/components/DeckSetup/LabwareOverlays/EditLabware.tsx b/protocol-designer/src/components/DeckSetup/LabwareOverlays/EditLabware.tsx index f6ca329cafa..24b12959d4b 100644 --- a/protocol-designer/src/components/DeckSetup/LabwareOverlays/EditLabware.tsx +++ b/protocol-designer/src/components/DeckSetup/LabwareOverlays/EditLabware.tsx @@ -20,8 +20,8 @@ import styles from './LabwareOverlays.css' interface Props { labwareOnDeck: LabwareOnDeck - setHoveredLabware: (val?: LabwareOnDeck | null) => unknown - setDraggedLabware: (val?: LabwareOnDeck | null) => unknown + setHoveredLabware: (val?: LabwareOnDeck | null) => void + setDraggedLabware: (val?: LabwareOnDeck | null) => void swapBlocked: boolean } @@ -38,6 +38,7 @@ export const EditLabware = (props: Props): JSX.Element | null => { const savedLabware = useSelector(labwareIngredSelectors.getSavedLabware) const dispatch = useDispatch>() const { t } = useTranslation('deck') + const ref = React.useRef(null) const { isTiprack } = labwareOnDeck.def.parameters const hasName = savedLabware[labwareOnDeck.id] @@ -62,26 +63,21 @@ export const EditLabware = (props: Props): JSX.Element | null => { const [{ draggedLabware, isOver }, drop] = useDrop(() => ({ accept: DND_TYPES.LABWARE, - canDrop: (item: any) => { - const draggedItem = item?.labwareOnDeck - const draggedLabware = draggedItem?.labwareOnDeck + canDrop: (item: DroppedItem) => { + const draggedLabware = item?.labwareOnDeck const isDifferentSlot = draggedLabware && draggedLabware.slot !== labwareOnDeck.slot return isDifferentSlot && !swapBlocked }, - drop: (item: any) => { - const draggedItem = item?.labwareOnDeck - if (draggedItem) { - dispatch( - moveDeckItem(draggedItem.labwareOnDeck.slot, labwareOnDeck.slot) - ) + drop: (item: DroppedItem) => { + const draggedLabware = item?.labwareOnDeck + if (draggedLabware) { + dispatch(moveDeckItem(draggedLabware.slot, labwareOnDeck.slot)) } }, - hover: (monitor: DropTargetMonitor) => { - if (monitor.canDrop()) { - setHoveredLabware(labwareOnDeck) - } + hover: () => { + setHoveredLabware(labwareOnDeck) }, collect: (monitor: DropTargetMonitor) => ({ isOver: monitor.isOver(), @@ -151,17 +147,18 @@ export const EditLabware = (props: Props): JSX.Element | null => { ) } - const dragResult = drag( -
-
- {contents} -
+ drag(drop(ref)) + + const dragResult = ( +
+ {contents}
) diff --git a/protocol-designer/src/components/steplist/ContextMenu.tsx b/protocol-designer/src/components/steplist/ContextMenu.tsx index 354d8f7113c..e36344c39cf 100644 --- a/protocol-designer/src/components/steplist/ContextMenu.tsx +++ b/protocol-designer/src/components/steplist/ContextMenu.tsx @@ -61,10 +61,6 @@ export const ContextMenu = (props: Props): JSX.Element => { }) const makeHandleContextMenu = (stepId: StepIdType) => (event: MouseEvent) => { - console.log( - 'handle context menu called before isMulti SelectMode', - isMultiSelectMode - ) if (isMultiSelectMode) return event.preventDefault() @@ -84,7 +80,6 @@ export const ContextMenu = (props: Props): JSX.Element => { screenH - clickY > rootH ? clickY + MENU_OFFSET_PX : clickY - rootH - MENU_OFFSET_PX - console.log('handle context menu called') setVisible(true) setStepId(stepId) setPosition({ left, top }) diff --git a/protocol-designer/src/components/steplist/DraggableStepItems.tsx b/protocol-designer/src/components/steplist/DraggableStepItems.tsx index 63c5f476da0..d383698f0e5 100644 --- a/protocol-designer/src/components/steplist/DraggableStepItems.tsx +++ b/protocol-designer/src/components/steplist/DraggableStepItems.tsx @@ -92,8 +92,6 @@ export const DraggableStepItems = ( React.useEffect(() => { setStepIds(orderedStepIds) }, [orderedStepIds]) - console.log('orderedStepIds', orderedStepIds) - console.log('stepIds', stepIds) const clickDrop = (): void => { if (!isEqual(orderedStepIds, stepIds)) { @@ -120,8 +118,7 @@ export const DraggableStepItems = ( stepIds.findIndex(id => stepId === id) const currentIds = isOver ? stepIds : orderedStepIds - console.log('isover', isOver) - console.log('currentIds', currentIds) + return ( <> From d17a79eae3f2082981b85303cdc7df811cd3dd6a Mon Sep 17 00:00:00 2001 From: Jethary Date: Wed, 14 Feb 2024 16:30:43 -0500 Subject: [PATCH 09/17] fix stepList --- .../src/components/DeckSetup/LabwareOverlays/EditLabware.tsx | 2 +- protocol-designer/src/components/steplist/StepList.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/protocol-designer/src/components/DeckSetup/LabwareOverlays/EditLabware.tsx b/protocol-designer/src/components/DeckSetup/LabwareOverlays/EditLabware.tsx index 24b12959d4b..ecfe704b617 100644 --- a/protocol-designer/src/components/DeckSetup/LabwareOverlays/EditLabware.tsx +++ b/protocol-designer/src/components/DeckSetup/LabwareOverlays/EditLabware.tsx @@ -94,7 +94,7 @@ export const EditLabware = (props: Props): JSX.Element | null => { ) } else { const isBeingDragged = - draggedLabware?.labwareOnDeck.slot === labwareOnDeck.slot + draggedLabware?.labwareOnDeck?.slot === labwareOnDeck.slot let contents: React.ReactNode | null = null diff --git a/protocol-designer/src/components/steplist/StepList.tsx b/protocol-designer/src/components/steplist/StepList.tsx index f496a4a1922..765d0cd12d5 100644 --- a/protocol-designer/src/components/steplist/StepList.tsx +++ b/protocol-designer/src/components/steplist/StepList.tsx @@ -30,7 +30,7 @@ export const StepList = (): JSX.Element => { const orderedStepIds = useSelector(stepFormSelectors.getOrderedStepIds) const isMultiSelectMode = useSelector(getIsMultiSelectMode) const dispatch = useDispatch>() - + console.log('orderedStepIds from stepList', orderedStepIds) const handleKeyDown: (e: KeyboardEvent) => void = e => { const key = e.key const altIsPressed = e.altKey @@ -65,7 +65,7 @@ export const StepList = (): JSX.Element => { { dispatch(steplistActions.reorderSteps(stepIds)) }} From 54e59e41490f233821778374445878ff853cdf9d Mon Sep 17 00:00:00 2001 From: Jethary Date: Thu, 15 Feb 2024 15:20:56 -0500 Subject: [PATCH 10/17] update backend to html5 --- .../RobotCoordinateSpaceWithDOMCoords.tsx | 13 +++++++--- protocol-designer/package.json | 2 +- .../components/DeckSetup/LabwareOnDeck.tsx | 1 + .../DeckSetup/LabwareOverlays/DragPreview.tsx | 4 +-- .../DeckSetup/LabwareOverlays/EditLabware.tsx | 26 ++++++++++++++++--- .../LabwareOverlays/SlotControls.tsx | 9 +++++-- .../src/components/DeckSetup/index.tsx | 1 + .../src/components/ProtocolEditor.tsx | 4 +-- .../src/components/steplist/StepList.tsx | 2 +- yarn.lock | 10 ++++--- 10 files changed, 54 insertions(+), 18 deletions(-) diff --git a/components/src/hardware-sim/RobotCoordinateSpace/RobotCoordinateSpaceWithDOMCoords.tsx b/components/src/hardware-sim/RobotCoordinateSpace/RobotCoordinateSpaceWithDOMCoords.tsx index 5ca8396c5be..d016a44d162 100644 --- a/components/src/hardware-sim/RobotCoordinateSpace/RobotCoordinateSpaceWithDOMCoords.tsx +++ b/components/src/hardware-sim/RobotCoordinateSpace/RobotCoordinateSpaceWithDOMCoords.tsx @@ -34,9 +34,16 @@ export function RobotCoordinateSpaceWithDOMCoords( cursorPoint.x = x cursorPoint.y = y - return cursorPoint.matrixTransform( - wrapperRef.current.getScreenCTM()?.inverse() - ) + const screenCTM = wrapperRef.current.getScreenCTM() + + if (!screenCTM) return { x, y } + + const transformedY = wrapperRef.current.clientHeight - y + + cursorPoint.y = transformedY + const transformedPoint = cursorPoint.matrixTransform(screenCTM.inverse()) + + return transformedPoint } if (deckDef == null && viewBox == null) return null diff --git a/protocol-designer/package.json b/protocol-designer/package.json index c06cc700b2f..a059becbd19 100755 --- a/protocol-designer/package.json +++ b/protocol-designer/package.json @@ -39,7 +39,7 @@ "react": "18.2.0", "react-color": "2.19.3", "react-dnd": "16.0.1", - "react-dnd-mouse-backend": "1.0.0-rc.2", + "react-dnd-html5-backend": "16.0.1", "react-dom": "18.2.0", "react-hook-form": "7.49.3", "react-i18next": "14.0.0", diff --git a/protocol-designer/src/components/DeckSetup/LabwareOnDeck.tsx b/protocol-designer/src/components/DeckSetup/LabwareOnDeck.tsx index 1bc83f7e752..c84239e3b10 100644 --- a/protocol-designer/src/components/DeckSetup/LabwareOnDeck.tsx +++ b/protocol-designer/src/components/DeckSetup/LabwareOnDeck.tsx @@ -35,6 +35,7 @@ export function LabwareOnDeck(props: LabwareOnDeckProps): JSX.Element { const missingTips = missingTipsByLabwareId ? missingTipsByLabwareId[labwareOnDeck.id] : null + return ( { } const { x, y } = currentOffset - const cursor: XYCoord = getRobotCoordsFromDOMCoords(x, y) + const cursor: XYCoord = getRobotCoordsFromDOMCoords(x, -y) return ( ) diff --git a/protocol-designer/src/components/DeckSetup/LabwareOverlays/EditLabware.tsx b/protocol-designer/src/components/DeckSetup/LabwareOverlays/EditLabware.tsx index ecfe704b617..69635f53a40 100644 --- a/protocol-designer/src/components/DeckSetup/LabwareOverlays/EditLabware.tsx +++ b/protocol-designer/src/components/DeckSetup/LabwareOverlays/EditLabware.tsx @@ -7,6 +7,7 @@ import { getLabwareDisplayName } from '@opentrons/shared-data' import { DropTargetMonitor, useDrag, useDrop } from 'react-dnd' import { NameThisLabware } from './NameThisLabware' import { DND_TYPES } from '../../../constants' +import { getDeckSetupForActiveItem } from '../../../top-selectors/labware-locations' import { deleteContainer, duplicateLabware, @@ -38,7 +39,10 @@ export const EditLabware = (props: Props): JSX.Element | null => { const savedLabware = useSelector(labwareIngredSelectors.getSavedLabware) const dispatch = useDispatch>() const { t } = useTranslation('deck') + const activeDeckSetup = useSelector(getDeckSetupForActiveItem) + const labware = activeDeckSetup.labware const ref = React.useRef(null) + const [newSlot, setSlot] = React.useState(null) const { isTiprack } = labwareOnDeck.def.parameters const hasName = savedLabware[labwareOnDeck.id] @@ -71,7 +75,9 @@ export const EditLabware = (props: Props): JSX.Element | null => { }, drop: (item: DroppedItem) => { const draggedLabware = item?.labwareOnDeck - if (draggedLabware) { + if (newSlot != null) { + dispatch(moveDeckItem(newSlot, labwareOnDeck.slot)) + } else if (draggedLabware != null) { dispatch(moveDeckItem(draggedLabware.slot, labwareOnDeck.slot)) } }, @@ -85,6 +91,16 @@ export const EditLabware = (props: Props): JSX.Element | null => { }), })) + const draggedItem = Object.values(labware).find( + l => l.id === draggedLabware?.labwareOnDeck?.id + ) + + React.useEffect(() => { + if (draggedItem != null) { + setSlot(draggedItem.slot) + } + }) + if (isYetUnnamed && !isTiprack) { return ( { /> ) } else { - const isBeingDragged = - draggedLabware?.labwareOnDeck?.slot === labwareOnDeck.slot + const isBeingDragged = draggedItem?.slot === labwareOnDeck.slot let contents: React.ReactNode | null = null @@ -111,6 +126,11 @@ export const EditLabware = (props: Props): JSX.Element | null => { `overlay.slot.${isBeingDragged ? 'drag_to_new_slot' : 'place_here'}` )}
+ // ) } else { contents = ( diff --git a/protocol-designer/src/components/DeckSetup/LabwareOverlays/SlotControls.tsx b/protocol-designer/src/components/DeckSetup/LabwareOverlays/SlotControls.tsx index 79e40c93f87..06871779c2b 100644 --- a/protocol-designer/src/components/DeckSetup/LabwareOverlays/SlotControls.tsx +++ b/protocol-designer/src/components/DeckSetup/LabwareOverlays/SlotControls.tsx @@ -3,7 +3,7 @@ import * as React from 'react' import { useTranslation } from 'react-i18next' import { useSelector, useDispatch } from 'react-redux' import noop from 'lodash/noop' -import { useDrop, DropTargetMonitor } from 'react-dnd' +import { useDrop, DropTargetMonitor, useDrag } from 'react-dnd' import cx from 'classnames' import { Icon, RobotCoordsForeignDiv } from '@opentrons/components' import { DND_TYPES } from '../../../constants' @@ -62,6 +62,11 @@ export const SlotControls = (props: SlotControlsProps): JSX.Element | null => { const { t } = useTranslation('deck') + const [, drag] = useDrag({ + type: DND_TYPES.LABWARE, + item: { labwareOnDeck: null }, + }) + const [{ draggedItem, itemType, isOver }, drop] = useDrop({ accept: DND_TYPES.LABWARE, canDrop: (item: DroppedItem) => { @@ -150,7 +155,7 @@ export const SlotControls = (props: SlotControlsProps): JSX.Element | null => { dispatch(openAddLabwareModal({ slot: slotId })) } - drop(ref) + drag(drop(ref)) return ( diff --git a/protocol-designer/src/components/DeckSetup/index.tsx b/protocol-designer/src/components/DeckSetup/index.tsx index 895f46e1954..9e9a7ebdebf 100644 --- a/protocol-designer/src/components/DeckSetup/index.tsx +++ b/protocol-designer/src/components/DeckSetup/index.tsx @@ -556,6 +556,7 @@ export const DeckSetup = (): JSX.Element => { return (
{drilledDown && } +
( - + ) diff --git a/protocol-designer/src/components/steplist/StepList.tsx b/protocol-designer/src/components/steplist/StepList.tsx index 765d0cd12d5..a6f618bd352 100644 --- a/protocol-designer/src/components/steplist/StepList.tsx +++ b/protocol-designer/src/components/steplist/StepList.tsx @@ -30,7 +30,7 @@ export const StepList = (): JSX.Element => { const orderedStepIds = useSelector(stepFormSelectors.getOrderedStepIds) const isMultiSelectMode = useSelector(getIsMultiSelectMode) const dispatch = useDispatch>() - console.log('orderedStepIds from stepList', orderedStepIds) + const handleKeyDown: (e: KeyboardEvent) => void = e => { const key = e.key const altIsPressed = e.altKey diff --git a/yarn.lock b/yarn.lock index d5148bc255a..e24df6ff354 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17516,10 +17516,12 @@ react-color@2.19.3: reactcss "^1.2.0" tinycolor2 "^1.4.1" -react-dnd-mouse-backend@1.0.0-rc.2: - version "1.0.0-rc.2" - resolved "https://registry.yarnpkg.com/react-dnd-mouse-backend/-/react-dnd-mouse-backend-1.0.0-rc.2.tgz#bc114d2e91ee6d079adc6920c22167a9780185af" - integrity sha512-mmbyXvg6YGKxdi7gx+ICFD0pFEMMiG1x7FBSu8ZYXBCqxVPkm8pkhrHfzd8AY89YmcWoUeqL3/DSU0u9lkwbsQ== +react-dnd-html5-backend@16.0.1: + version "16.0.1" + resolved "https://registry.yarnpkg.com/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz#87faef15845d512a23b3c08d29ecfd34871688b6" + integrity sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw== + dependencies: + dnd-core "^16.0.1" react-dnd@16.0.1: version "16.0.1" From d605ca638b05eb5a139f5de500234ed15dbad81b Mon Sep 17 00:00:00 2001 From: Jethary Date: Thu, 15 Feb 2024 16:12:04 -0500 Subject: [PATCH 11/17] refactor draggableStepItems --- .../steplist/DraggableStepItems.tsx | 74 +++++++++++++------ 1 file changed, 51 insertions(+), 23 deletions(-) diff --git a/protocol-designer/src/components/steplist/DraggableStepItems.tsx b/protocol-designer/src/components/steplist/DraggableStepItems.tsx index d383698f0e5..2836ab01d90 100644 --- a/protocol-designer/src/components/steplist/DraggableStepItems.tsx +++ b/protocol-designer/src/components/steplist/DraggableStepItems.tsx @@ -6,6 +6,7 @@ import { useDrop, useDrag, DropTargetOptions, + XYCoord, } from 'react-dnd' import isEqual from 'lodash/isEqual' @@ -25,27 +26,29 @@ interface DragDropStepItemProps extends ConnectedStepItemProps { stepId: StepIdType findStepIndex: (stepIdType: StepIdType) => number clickDrop: () => void - moveStep: (stepId: StepIdType, value: number) => void + moveStep: (dragIndex: number, value: number) => void setIsOver: React.Dispatch> + index: number } interface DropType { stepId: StepIdType + index: number } const DragDropStepItem = (props: DragDropStepItemProps): JSX.Element => { - const { stepId, moveStep, clickDrop, findStepIndex, setIsOver } = props + const { stepId, moveStep, clickDrop, setIsOver, index } = props const ref = React.useRef(null) const [{ isDragging }, drag] = useDrag({ type: DND_TYPES.STEP_ITEM, - item: { stepId }, + item: { stepId, index }, collect: (monitor: DragLayerMonitor) => ({ isDragging: monitor.isDragging(), }), }) - const [{ isOver }, drop] = useDrop(() => ({ + const [{ isOver, handlerId }, drop] = useDrop(() => ({ accept: DND_TYPES.STEP_ITEM, canDrop: () => { return true @@ -53,16 +56,36 @@ const DragDropStepItem = (props: DragDropStepItemProps): JSX.Element => { drop: () => { clickDrop() }, - hover: (item: DropType) => { - const draggedId = item.stepId + hover(item: DropType, monitor: DropTargetOptions) { + if (!ref.current) { + return + } + const dragIndex = item.index + const hoverIndex = index + + if (dragIndex === hoverIndex) { + return + } - if (draggedId !== stepId) { - const overIndex = findStepIndex(stepId) - moveStep(draggedId, overIndex) + const hoverBoundingRect = ref.current?.getBoundingClientRect() + const hoverMiddleY = + (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2 + const clientOffset = monitor.getClientOffset() + const hoverClientY = (clientOffset as XYCoord).y - hoverBoundingRect.top + + if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) { + return + } + if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) { + return } + moveStep(dragIndex, hoverIndex) + + item.index = hoverIndex }, collect: (monitor: DropTargetOptions) => ({ isOver: monitor.isOver(), + handlerId: monitor.getHandlerId(), }), })) @@ -72,7 +95,11 @@ const DragDropStepItem = (props: DragDropStepItemProps): JSX.Element => { drag(drop(ref)) return ( -
+
) @@ -101,19 +128,19 @@ export const DraggableStepItems = ( } } - const moveStep = (stepId: StepIdType, targetIndex: number): void => { - const currentIndex = findStepIndex(stepId) - const currentRemoved = [ - ...stepIds.slice(0, currentIndex), - ...stepIds.slice(currentIndex + 1, stepIds.length), - ] - const currentReinserted = [ - ...currentRemoved.slice(0, targetIndex), - stepId, - ...currentRemoved.slice(targetIndex, currentRemoved.length), - ] - setStepIds(currentReinserted) - } + const moveStep = React.useCallback( + (dragIndex: number, hoverIndex: number) => { + setStepIds((prevCards: StepIdType[]) => { + const updatedCards = [...prevCards] + const draggedCard = updatedCards[dragIndex] + updatedCards.splice(dragIndex, 1) + updatedCards.splice(hoverIndex, 0, draggedCard) + return updatedCards + }) + }, + [] + ) + const findStepIndex = (stepId: StepIdType): number => stepIds.findIndex(id => stepId === id) @@ -134,6 +161,7 @@ export const DraggableStepItems = ( findStepIndex={findStepIndex} clickDrop={clickDrop} setIsOver={setIsOver} + index={index} /> )) } From bbf89581c6c277a79ce99973d94caaf58279dc42 Mon Sep 17 00:00:00 2001 From: Jethary Date: Thu, 15 Feb 2024 17:56:01 -0500 Subject: [PATCH 12/17] remove dragPreview --- .../RobotCoordinateSpaceWithDOMCoords.tsx | 26 +------------ .../DeckSetup/LabwareOverlays/DragPreview.css | 5 --- .../DeckSetup/LabwareOverlays/DragPreview.tsx | 38 ------------------- .../DeckSetup/LabwareOverlays/EditLabware.tsx | 19 +--------- .../LabwareOverlays/LabwareOverlays.css | 2 +- .../DeckSetup/LabwareOverlays/index.ts | 1 - .../src/components/DeckSetup/index.tsx | 9 +---- .../steplist/DraggableStepItems.tsx | 5 --- 8 files changed, 6 insertions(+), 99 deletions(-) delete mode 100644 protocol-designer/src/components/DeckSetup/LabwareOverlays/DragPreview.css delete mode 100644 protocol-designer/src/components/DeckSetup/LabwareOverlays/DragPreview.tsx diff --git a/components/src/hardware-sim/RobotCoordinateSpace/RobotCoordinateSpaceWithDOMCoords.tsx b/components/src/hardware-sim/RobotCoordinateSpace/RobotCoordinateSpaceWithDOMCoords.tsx index d016a44d162..5daece60d7f 100644 --- a/components/src/hardware-sim/RobotCoordinateSpace/RobotCoordinateSpaceWithDOMCoords.tsx +++ b/components/src/hardware-sim/RobotCoordinateSpace/RobotCoordinateSpaceWithDOMCoords.tsx @@ -4,10 +4,6 @@ import type { DeckDefinition, DeckSlot } from '@opentrons/shared-data' export interface RobotCoordinateSpaceWithDOMCoordsRenderProps { deckSlotsById: { [slotId: string]: DeckSlot } - getRobotCoordsFromDOMCoords: ( - domX: number, - domY: number - ) => { x: number; y: number } } interface RobotCoordinateSpaceWithDOMCoordsProps @@ -19,32 +15,12 @@ interface RobotCoordinateSpaceWithDOMCoordsProps ) => React.ReactNode } -type GetRobotCoordsFromDOMCoords = RobotCoordinateSpaceWithDOMCoordsRenderProps['getRobotCoordsFromDOMCoords'] - export function RobotCoordinateSpaceWithDOMCoords( props: RobotCoordinateSpaceWithDOMCoordsProps ): JSX.Element | null { const { children, deckDef, viewBox, ...restProps } = props const wrapperRef = React.useRef(null) - const getRobotCoordsFromDOMCoords: GetRobotCoordsFromDOMCoords = (x, y) => { - if (wrapperRef.current == null) return { x: 0, y: 0 } - - const cursorPoint = wrapperRef.current.createSVGPoint() - - cursorPoint.x = x - cursorPoint.y = y - - const screenCTM = wrapperRef.current.getScreenCTM() - if (!screenCTM) return { x, y } - - const transformedY = wrapperRef.current.clientHeight - y - - cursorPoint.y = transformedY - const transformedPoint = cursorPoint.matrixTransform(screenCTM.inverse()) - - return transformedPoint - } if (deckDef == null && viewBox == null) return null let wholeDeckViewBox @@ -66,7 +42,7 @@ export function RobotCoordinateSpaceWithDOMCoords( transform="scale(1, -1)" {...restProps} > - {children?.({ deckSlotsById, getRobotCoordsFromDOMCoords })} + {children?.({ deckSlotsById })} ) } diff --git a/protocol-designer/src/components/DeckSetup/LabwareOverlays/DragPreview.css b/protocol-designer/src/components/DeckSetup/LabwareOverlays/DragPreview.css deleted file mode 100644 index 1634b4980ed..00000000000 --- a/protocol-designer/src/components/DeckSetup/LabwareOverlays/DragPreview.css +++ /dev/null @@ -1,5 +0,0 @@ -@import '@opentrons/components'; - -.labware_drag_preview { - opacity: 0.5; -} diff --git a/protocol-designer/src/components/DeckSetup/LabwareOverlays/DragPreview.tsx b/protocol-designer/src/components/DeckSetup/LabwareOverlays/DragPreview.tsx deleted file mode 100644 index 6add5f95569..00000000000 --- a/protocol-designer/src/components/DeckSetup/LabwareOverlays/DragPreview.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import * as React from 'react' -import { useDragLayer, XYCoord } from 'react-dnd' -import { LabwareOnDeck } from '../LabwareOnDeck' -import { DND_TYPES } from '../../../constants' -import { RobotWorkSpaceRenderProps } from '@opentrons/components' -import styles from './DragPreview.css' - -interface DragPreviewProps { - getRobotCoordsFromDOMCoords: RobotWorkSpaceRenderProps['getRobotCoordsFromDOMCoords'] -} - -export const DragPreview = (props: DragPreviewProps): JSX.Element | null => { - const { getRobotCoordsFromDOMCoords } = props - const { item, itemType, isDragging, currentOffset } = useDragLayer( - monitor => ({ - item: monitor.getItem(), - itemType: monitor.getItemType(), - isDragging: monitor.isDragging(), - currentOffset: monitor.getSourceClientOffset(), - }) - ) - - if (!isDragging || !currentOffset || itemType !== DND_TYPES.LABWARE) { - return null - } - - const { x, y } = currentOffset - const cursor: XYCoord = getRobotCoordsFromDOMCoords(x, -y) - - return ( - - ) -} diff --git a/protocol-designer/src/components/DeckSetup/LabwareOverlays/EditLabware.tsx b/protocol-designer/src/components/DeckSetup/LabwareOverlays/EditLabware.tsx index 69635f53a40..9a0db4bab38 100644 --- a/protocol-designer/src/components/DeckSetup/LabwareOverlays/EditLabware.tsx +++ b/protocol-designer/src/components/DeckSetup/LabwareOverlays/EditLabware.tsx @@ -115,23 +115,8 @@ export const EditLabware = (props: Props): JSX.Element | null => { if (swapBlocked) { contents = null - } else if (draggedLabware) { - contents = ( -
- {t( - `overlay.slot.${isBeingDragged ? 'drag_to_new_slot' : 'place_here'}` - )} -
- // - ) + } else if (draggedLabware != null) { + contents = null } else { contents = ( <> diff --git a/protocol-designer/src/components/DeckSetup/LabwareOverlays/LabwareOverlays.css b/protocol-designer/src/components/DeckSetup/LabwareOverlays/LabwareOverlays.css index fbce92ed741..29a069ae2e9 100644 --- a/protocol-designer/src/components/DeckSetup/LabwareOverlays/LabwareOverlays.css +++ b/protocol-designer/src/components/DeckSetup/LabwareOverlays/LabwareOverlays.css @@ -33,7 +33,7 @@ } .slot_overlay.disabled { - background-color: color-mod(var(--c-light-gray) alpha(0.9)); + background-color: color-mod(var(--c-light-gray) alpha(0.7)); color: var(--c-font-dark); } diff --git a/protocol-designer/src/components/DeckSetup/LabwareOverlays/index.ts b/protocol-designer/src/components/DeckSetup/LabwareOverlays/index.ts index 490ee828367..cd857edd17a 100644 --- a/protocol-designer/src/components/DeckSetup/LabwareOverlays/index.ts +++ b/protocol-designer/src/components/DeckSetup/LabwareOverlays/index.ts @@ -1,4 +1,3 @@ export { SlotControls } from './SlotControls' export { AdapterControls } from './AdapterControls' export { LabwareControls } from './LabwareControls' -export { DragPreview } from './DragPreview' diff --git a/protocol-designer/src/components/DeckSetup/index.tsx b/protocol-designer/src/components/DeckSetup/index.tsx index 9e9a7ebdebf..8ee1ebc1bf4 100644 --- a/protocol-designer/src/components/DeckSetup/index.tsx +++ b/protocol-designer/src/components/DeckSetup/index.tsx @@ -8,7 +8,6 @@ import { FlexTrash, Module, RobotCoordinateSpaceWithDOMCoords, - RobotWorkSpaceRenderProps, SingleSlotFixture, StagingAreaFixture, StagingAreaLocation, @@ -64,7 +63,6 @@ import { AdapterControls, SlotControls, LabwareControls, - DragPreview, } from './LabwareOverlays' import { FlexModuleTag } from './FlexModuleTag' import { Ot2ModuleTag } from './Ot2ModuleTag' @@ -102,7 +100,6 @@ const OT2_STANDARD_DECK_VIEW_LAYER_BLOCK_LIST: string[] = [ ] interface ContentsProps { - getRobotCoordsFromDOMCoords: RobotWorkSpaceRenderProps['getRobotCoordsFromDOMCoords'] activeDeckSetup: InitialDeckSetup selectedTerminalItemId?: TerminalItemId | null showGen1MultichannelCollisionWarnings: boolean @@ -118,7 +115,6 @@ const darkFill = COLORS.grey60 export const DeckSetupContents = (props: ContentsProps): JSX.Element => { const { activeDeckSetup, - getRobotCoordsFromDOMCoords, showGen1MultichannelCollisionWarnings, deckDef, robotType, @@ -482,7 +478,6 @@ export const DeckSetupContents = (props: ContentsProps): JSX.Element => { ) })} - ) } @@ -567,7 +562,7 @@ export const DeckSetup = (): JSX.Element => { : deckDef.cornerOffsetFromOrigin[1] } ${deckDef.dimensions[0]} ${deckDef.dimensions[1]}`} > - {({ getRobotCoordsFromDOMCoords }) => ( + {() => ( <> {robotType === OT2_ROBOT_TYPE ? ( { )} {...{ deckDef, - getRobotCoordsFromDOMCoords, + showGen1MultichannelCollisionWarnings, }} /> diff --git a/protocol-designer/src/components/steplist/DraggableStepItems.tsx b/protocol-designer/src/components/steplist/DraggableStepItems.tsx index 2836ab01d90..c34ff040fdf 100644 --- a/protocol-designer/src/components/steplist/DraggableStepItems.tsx +++ b/protocol-designer/src/components/steplist/DraggableStepItems.tsx @@ -24,7 +24,6 @@ import styles from './StepItem.css' interface DragDropStepItemProps extends ConnectedStepItemProps { stepId: StepIdType - findStepIndex: (stepIdType: StepIdType) => number clickDrop: () => void moveStep: (dragIndex: number, value: number) => void setIsOver: React.Dispatch> @@ -141,9 +140,6 @@ export const DraggableStepItems = ( [] ) - const findStepIndex = (stepId: StepIdType): number => - stepIds.findIndex(id => stepId === id) - const currentIds = isOver ? stepIds : orderedStepIds return ( @@ -158,7 +154,6 @@ export const DraggableStepItems = ( // @ts-expect-error onStepContextMenu={makeStepOnContextMenu(stepId)} moveStep={moveStep} - findStepIndex={findStepIndex} clickDrop={clickDrop} setIsOver={setIsOver} index={index} From 2bd2c094ccdaeb1699022f956039f5d3a6dfbd72 Mon Sep 17 00:00:00 2001 From: Jethary Date: Fri, 16 Feb 2024 09:18:09 -0500 Subject: [PATCH 13/17] refactor draggableStepItems --- .../steplist/DraggableStepItems.tsx | 67 +++++++------------ 1 file changed, 26 insertions(+), 41 deletions(-) diff --git a/protocol-designer/src/components/steplist/DraggableStepItems.tsx b/protocol-designer/src/components/steplist/DraggableStepItems.tsx index c34ff040fdf..3d21a493ea9 100644 --- a/protocol-designer/src/components/steplist/DraggableStepItems.tsx +++ b/protocol-designer/src/components/steplist/DraggableStepItems.tsx @@ -6,7 +6,6 @@ import { useDrop, useDrag, DropTargetOptions, - XYCoord, } from 'react-dnd' import isEqual from 'lodash/isEqual' @@ -25,23 +24,22 @@ import styles from './StepItem.css' interface DragDropStepItemProps extends ConnectedStepItemProps { stepId: StepIdType clickDrop: () => void - moveStep: (dragIndex: number, value: number) => void + moveStep: (stepId: StepIdType, value: number) => void setIsOver: React.Dispatch> - index: number + findStepIndex: (stepId: StepIdType) => number } interface DropType { stepId: StepIdType - index: number } const DragDropStepItem = (props: DragDropStepItemProps): JSX.Element => { - const { stepId, moveStep, clickDrop, setIsOver, index } = props + const { stepId, moveStep, clickDrop, setIsOver, findStepIndex } = props const ref = React.useRef(null) const [{ isDragging }, drag] = useDrag({ type: DND_TYPES.STEP_ITEM, - item: { stepId, index }, + item: { stepId }, collect: (monitor: DragLayerMonitor) => ({ isDragging: monitor.isDragging(), }), @@ -55,32 +53,12 @@ const DragDropStepItem = (props: DragDropStepItemProps): JSX.Element => { drop: () => { clickDrop() }, - hover(item: DropType, monitor: DropTargetOptions) { - if (!ref.current) { - return + hover: (item: DropType) => { + const draggedId = item.stepId + if (draggedId !== stepId) { + const overIndex = findStepIndex(stepId) + moveStep(draggedId, overIndex) } - const dragIndex = item.index - const hoverIndex = index - - if (dragIndex === hoverIndex) { - return - } - - const hoverBoundingRect = ref.current?.getBoundingClientRect() - const hoverMiddleY = - (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2 - const clientOffset = monitor.getClientOffset() - const hoverClientY = (clientOffset as XYCoord).y - hoverBoundingRect.top - - if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) { - return - } - if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) { - return - } - moveStep(dragIndex, hoverIndex) - - item.index = hoverIndex }, collect: (monitor: DropTargetOptions) => ({ isOver: monitor.isOver(), @@ -127,17 +105,24 @@ export const DraggableStepItems = ( } } + const findStepIndex = (stepId: StepIdType): number => + stepIds.findIndex(id => stepId === id) + const moveStep = React.useCallback( - (dragIndex: number, hoverIndex: number) => { - setStepIds((prevCards: StepIdType[]) => { - const updatedCards = [...prevCards] - const draggedCard = updatedCards[dragIndex] - updatedCards.splice(dragIndex, 1) - updatedCards.splice(hoverIndex, 0, draggedCard) - return updatedCards - }) + (stepId: StepIdType, targetIndex: number): void => { + const currentIndex = findStepIndex(stepId) + const currentRemoved = [ + ...stepIds.slice(0, currentIndex), + ...stepIds.slice(currentIndex + 1, stepIds.length), + ] + const currentReinserted = [ + ...currentRemoved.slice(0, targetIndex), + stepId, + ...currentRemoved.slice(targetIndex, currentRemoved.length), + ] + setStepIds(currentReinserted) }, - [] + [stepIds, findStepIndex] ) const currentIds = isOver ? stepIds : orderedStepIds @@ -156,7 +141,7 @@ export const DraggableStepItems = ( moveStep={moveStep} clickDrop={clickDrop} setIsOver={setIsOver} - index={index} + findStepIndex={findStepIndex} /> )) } From c5861b3b9efee629d5e12d7f476514963ffc0c9d Mon Sep 17 00:00:00 2001 From: Jethary Date: Fri, 16 Feb 2024 09:29:36 -0500 Subject: [PATCH 14/17] another fix to draggableStepItems --- .../steplist/DraggableStepItems.tsx | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/protocol-designer/src/components/steplist/DraggableStepItems.tsx b/protocol-designer/src/components/steplist/DraggableStepItems.tsx index 3d21a493ea9..821712ba28f 100644 --- a/protocol-designer/src/components/steplist/DraggableStepItems.tsx +++ b/protocol-designer/src/components/steplist/DraggableStepItems.tsx @@ -108,22 +108,19 @@ export const DraggableStepItems = ( const findStepIndex = (stepId: StepIdType): number => stepIds.findIndex(id => stepId === id) - const moveStep = React.useCallback( - (stepId: StepIdType, targetIndex: number): void => { - const currentIndex = findStepIndex(stepId) - const currentRemoved = [ - ...stepIds.slice(0, currentIndex), - ...stepIds.slice(currentIndex + 1, stepIds.length), - ] - const currentReinserted = [ - ...currentRemoved.slice(0, targetIndex), - stepId, - ...currentRemoved.slice(targetIndex, currentRemoved.length), - ] - setStepIds(currentReinserted) - }, - [stepIds, findStepIndex] - ) + const moveStep = (stepId: StepIdType, targetIndex: number): void => { + const currentIndex = findStepIndex(stepId) + const currentRemoved = [ + ...stepIds.slice(0, currentIndex), + ...stepIds.slice(currentIndex + 1, stepIds.length), + ] + const currentReinserted = [ + ...currentRemoved.slice(0, targetIndex), + stepId, + ...currentRemoved.slice(targetIndex, currentRemoved.length), + ] + setStepIds(currentReinserted) + } const currentIds = isOver ? stepIds : orderedStepIds From 0df681476f642c7c7c9daaec3854a81accb914d7 Mon Sep 17 00:00:00 2001 From: Jethary Date: Fri, 16 Feb 2024 10:35:52 -0500 Subject: [PATCH 15/17] rename component --- ...DOMCoords.tsx => RobotCoordinateSpaceWithRef.tsx} | 12 +++++------- .../src/hardware-sim/RobotCoordinateSpace/index.ts | 2 +- protocol-designer/src/components/DeckSetup/index.tsx | 6 +++--- 3 files changed, 9 insertions(+), 11 deletions(-) rename components/src/hardware-sim/RobotCoordinateSpace/{RobotCoordinateSpaceWithDOMCoords.tsx => RobotCoordinateSpaceWithRef.tsx} (78%) diff --git a/components/src/hardware-sim/RobotCoordinateSpace/RobotCoordinateSpaceWithDOMCoords.tsx b/components/src/hardware-sim/RobotCoordinateSpace/RobotCoordinateSpaceWithRef.tsx similarity index 78% rename from components/src/hardware-sim/RobotCoordinateSpace/RobotCoordinateSpaceWithDOMCoords.tsx rename to components/src/hardware-sim/RobotCoordinateSpace/RobotCoordinateSpaceWithRef.tsx index 5daece60d7f..c1986711ed2 100644 --- a/components/src/hardware-sim/RobotCoordinateSpace/RobotCoordinateSpaceWithDOMCoords.tsx +++ b/components/src/hardware-sim/RobotCoordinateSpace/RobotCoordinateSpaceWithRef.tsx @@ -2,21 +2,19 @@ import * as React from 'react' import { Svg } from '../../primitives' import type { DeckDefinition, DeckSlot } from '@opentrons/shared-data' -export interface RobotCoordinateSpaceWithDOMCoordsRenderProps { +export interface RobotCoordinateSpaceWithRefRenderProps { deckSlotsById: { [slotId: string]: DeckSlot } } -interface RobotCoordinateSpaceWithDOMCoordsProps +interface RobotCoordinateSpaceWithRefProps extends React.ComponentProps { viewBox?: string | null deckDef?: DeckDefinition - children?: ( - props: RobotCoordinateSpaceWithDOMCoordsRenderProps - ) => React.ReactNode + children?: (props: RobotCoordinateSpaceWithRefRenderProps) => React.ReactNode } -export function RobotCoordinateSpaceWithDOMCoords( - props: RobotCoordinateSpaceWithDOMCoordsProps +export function RobotCoordinateSpaceWithRef( + props: RobotCoordinateSpaceWithRefProps ): JSX.Element | null { const { children, deckDef, viewBox, ...restProps } = props const wrapperRef = React.useRef(null) diff --git a/components/src/hardware-sim/RobotCoordinateSpace/index.ts b/components/src/hardware-sim/RobotCoordinateSpace/index.ts index 71c518dc39a..07fadd1099a 100644 --- a/components/src/hardware-sim/RobotCoordinateSpace/index.ts +++ b/components/src/hardware-sim/RobotCoordinateSpace/index.ts @@ -1,2 +1,2 @@ -export * from './RobotCoordinateSpaceWithDOMCoords' +export * from './RobotCoordinateSpaceWithRef' export * from './RobotCoordinateSpace' diff --git a/protocol-designer/src/components/DeckSetup/index.tsx b/protocol-designer/src/components/DeckSetup/index.tsx index 8ee1ebc1bf4..dcf7fce2855 100644 --- a/protocol-designer/src/components/DeckSetup/index.tsx +++ b/protocol-designer/src/components/DeckSetup/index.tsx @@ -7,7 +7,7 @@ import { DeckFromLayers, FlexTrash, Module, - RobotCoordinateSpaceWithDOMCoords, + RobotCoordinateSpaceWithRef, SingleSlotFixture, StagingAreaFixture, StagingAreaLocation, @@ -553,7 +553,7 @@ export const DeckSetup = (): JSX.Element => { {drilledDown && }
- { /> )} - +
) From 7cbc73106eb23aee116927317482828212dfad20 Mon Sep 17 00:00:00 2001 From: Jethary Date: Fri, 16 Feb 2024 13:00:58 -0500 Subject: [PATCH 16/17] tweak the moveStep logic --- .../steplist/DraggableStepItems.tsx | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/protocol-designer/src/components/steplist/DraggableStepItems.tsx b/protocol-designer/src/components/steplist/DraggableStepItems.tsx index 821712ba28f..d02a87ee60a 100644 --- a/protocol-designer/src/components/steplist/DraggableStepItems.tsx +++ b/protocol-designer/src/components/steplist/DraggableStepItems.tsx @@ -93,6 +93,8 @@ export const DraggableStepItems = ( const { t } = useTranslation('shared') const [isOver, setIsOver] = React.useState(false) const [stepIds, setStepIds] = React.useState(orderedStepIds) + + // needed to initalize stepIds React.useEffect(() => { setStepIds(orderedStepIds) }, [orderedStepIds]) @@ -109,17 +111,13 @@ export const DraggableStepItems = ( stepIds.findIndex(id => stepId === id) const moveStep = (stepId: StepIdType, targetIndex: number): void => { - const currentIndex = findStepIndex(stepId) - const currentRemoved = [ - ...stepIds.slice(0, currentIndex), - ...stepIds.slice(currentIndex + 1, stepIds.length), - ] - const currentReinserted = [ - ...currentRemoved.slice(0, targetIndex), - stepId, - ...currentRemoved.slice(targetIndex, currentRemoved.length), - ] - setStepIds(currentReinserted) + const currentIndex = orderedStepIds.findIndex(id => id === stepId) + + const newStepIds = [...orderedStepIds] + newStepIds.splice(currentIndex, 1) + newStepIds.splice(targetIndex, 0, stepId) + + setStepIds(newStepIds) } const currentIds = isOver ? stepIds : orderedStepIds From e30036167c38f61af491171ee7f39ebfbda66394 Mon Sep 17 00:00:00 2001 From: Jethary Date: Tue, 20 Feb 2024 11:15:51 -0500 Subject: [PATCH 17/17] fix issue with swapping labware --- .../LabwareOverlays/AdapterControls.tsx | 2 +- .../DeckSetup/LabwareOverlays/EditLabware.tsx | 22 +++++++++---------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/protocol-designer/src/components/DeckSetup/LabwareOverlays/AdapterControls.tsx b/protocol-designer/src/components/DeckSetup/LabwareOverlays/AdapterControls.tsx index 7b0dc5b6dc3..7017e8adfcb 100644 --- a/protocol-designer/src/components/DeckSetup/LabwareOverlays/AdapterControls.tsx +++ b/protocol-designer/src/components/DeckSetup/LabwareOverlays/AdapterControls.tsx @@ -96,7 +96,7 @@ export const AdapterControls = ( } }, hover: () => { - if (handleDragHover) { + if (handleDragHover != null) { handleDragHover() } }, diff --git a/protocol-designer/src/components/DeckSetup/LabwareOverlays/EditLabware.tsx b/protocol-designer/src/components/DeckSetup/LabwareOverlays/EditLabware.tsx index 9a0db4bab38..b51489969ac 100644 --- a/protocol-designer/src/components/DeckSetup/LabwareOverlays/EditLabware.tsx +++ b/protocol-designer/src/components/DeckSetup/LabwareOverlays/EditLabware.tsx @@ -52,18 +52,10 @@ export const EditLabware = (props: Props): JSX.Element | null => { dispatch(openIngredientSelector(labwareOnDeck.id)) } - const [, drag] = useDrag(() => ({ + const [, drag] = useDrag({ type: DND_TYPES.LABWARE, item: { labwareOnDeck }, - beginDrag: () => { - setDraggedLabware(labwareOnDeck) - return { labwareOnDeck } - }, - endDrag: () => { - setHoveredLabware(null) - setDraggedLabware(null) - }, - })) + }) const [{ draggedLabware, isOver }, drop] = useDrop(() => ({ accept: DND_TYPES.LABWARE, @@ -82,8 +74,10 @@ export const EditLabware = (props: Props): JSX.Element | null => { } }, - hover: () => { - setHoveredLabware(labwareOnDeck) + hover: (item: DroppedItem, monitor: DropTargetMonitor) => { + if (monitor.canDrop()) { + setHoveredLabware(labwareOnDeck) + } }, collect: (monitor: DropTargetMonitor) => ({ isOver: monitor.isOver(), @@ -98,6 +92,10 @@ export const EditLabware = (props: Props): JSX.Element | null => { React.useEffect(() => { if (draggedItem != null) { setSlot(draggedItem.slot) + setDraggedLabware(draggedItem) + } else { + setHoveredLabware(null) + setDraggedLabware(null) } })