-
Notifications
You must be signed in to change notification settings - Fork 179
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(protocol-designer): make timeline responsive #17109
Changes from 8 commits
a3a0bae
31e12fd
6f81ca4
f7d013a
041bda1
7e7127e
37a66db
892b2cc
1856da6
d79b7ee
9b88d76
8c96b57
e5dcda6
5daede5
996fde6
7b99baf
ea625d2
ad6b69e
3671140
0cf372b
e2711d9
eca5df7
25bc420
7d2fbed
22538f2
8237107
bcaf94b
f6525ac
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
import { useState, useRef, useCallback, useEffect } from 'react' | ||
import styled from 'styled-components' | ||
import { | ||
DIRECTION_COLUMN, | ||
DISPLAY_FLEX, | ||
Flex, | ||
JUSTIFY_SPACE_BETWEEN, | ||
Box, | ||
} from '@opentrons/components' | ||
import { TimelineToolbox } from './Timeline/TimelineToolbox' | ||
|
||
const INITIAL_SIDEBAR_WIDTH = 350 | ||
const MIN_SIDEBAR_WIDTH = 80 | ||
const MAX_SIDEBAR_WIDTH = 350 | ||
|
||
interface DraggableSidebarProps { | ||
setTargetWidth: (width: number) => void | ||
} | ||
|
||
export function DraggableSidebar({ | ||
setTargetWidth, | ||
}: DraggableSidebarProps): JSX.Element { | ||
const sidebarRef = useRef<HTMLDivElement>(null) | ||
const [isResizing, setIsResizing] = useState(false) | ||
const [sidebarWidth, setSidebarWidth] = useState(INITIAL_SIDEBAR_WIDTH) | ||
|
||
const startResizing = useCallback(() => { | ||
setIsResizing(true) | ||
}, []) | ||
|
||
const stopResizing = useCallback(() => { | ||
setIsResizing(false) | ||
}, []) | ||
|
||
const resize = useCallback( | ||
(mouseMoveEvent: MouseEvent) => { | ||
if (isResizing && sidebarRef.current != null) { | ||
const newWidth = | ||
mouseMoveEvent.clientX - | ||
sidebarRef.current.getBoundingClientRect().left | ||
|
||
if (newWidth >= MIN_SIDEBAR_WIDTH && newWidth <= MAX_SIDEBAR_WIDTH) { | ||
setSidebarWidth(newWidth) | ||
setTargetWidth(newWidth) | ||
} | ||
} | ||
}, | ||
[isResizing, setTargetWidth] | ||
) | ||
|
||
useEffect(() => { | ||
window.addEventListener('mousemove', resize) | ||
window.addEventListener('mouseup', stopResizing) | ||
|
||
return () => { | ||
window.removeEventListener('mousemove', resize) | ||
window.removeEventListener('mouseup', stopResizing) | ||
} | ||
}, [resize, stopResizing]) | ||
|
||
return ( | ||
<Flex | ||
flexDirection={DIRECTION_COLUMN} | ||
justifyContent={JUSTIFY_SPACE_BETWEEN} | ||
height="100%" | ||
> | ||
<SidebarContainer ref={sidebarRef} resizedWidth={sidebarWidth}> | ||
<SidebarContent> | ||
<TimelineToolbox sidebarWidth={sidebarWidth} /> | ||
</SidebarContent> | ||
<SidebarResizer dragging={isResizing} onMouseDown={startResizing} /> | ||
</SidebarContainer> | ||
</Flex> | ||
) | ||
} | ||
|
||
const SidebarContainer = styled(Box)<{ resizedWidth: number }>` | ||
display: ${DISPLAY_FLEX}; | ||
flex-direction: ${DIRECTION_COLUMN}; | ||
/* background-color: #f4f4f4; */ | ||
background-color: #ff0000; | ||
border-right: 1px solid #ccc; | ||
position: relative; | ||
width: ${props => props.resizedWidth}px; | ||
overflow: hidden; /* Prevent content overflow */ | ||
height: 100%; | ||
` | ||
|
||
const SidebarContent = styled(Flex)` | ||
flex: 1; | ||
` | ||
|
||
const SidebarResizer = styled(Flex)<{ dragging: boolean }>` | ||
width: 0.3125rem; | ||
cursor: ew-resize; | ||
background-color: #ddd; | ||
position: absolute; | ||
top: 0; | ||
right: 0; | ||
bottom: 0; | ||
margin: 0; | ||
padding: 0; | ||
transition: background-color 0.2s ease; | ||
|
||
&:hover { | ||
background-color: blue; /* Hover state */ | ||
} | ||
|
||
${props => | ||
props.dragging === true && | ||
` | ||
background-color: darkblue; /* Dragging state */ | ||
`} | ||
` |
Original file line number | Diff line number | Diff line change | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -2,40 +2,38 @@ import { useState } from 'react' | |||||||||||||||
import { useDispatch, useSelector } from 'react-redux' | ||||||||||||||||
import { createPortal } from 'react-dom' | ||||||||||||||||
import { useTranslation } from 'react-i18next' | ||||||||||||||||
import { css } from 'styled-components' | ||||||||||||||||
import { | ||||||||||||||||
useHoverTooltip, | ||||||||||||||||
TOOLTIP_TOP, | ||||||||||||||||
TOOLTIP_FIXED, | ||||||||||||||||
Tooltip, | ||||||||||||||||
ALIGN_CENTER, | ||||||||||||||||
BORDERS, | ||||||||||||||||
COLORS, | ||||||||||||||||
DIRECTION_COLUMN, | ||||||||||||||||
Flex, | ||||||||||||||||
POSITION_ABSOLUTE, | ||||||||||||||||
BORDERS, | ||||||||||||||||
Icon, | ||||||||||||||||
JUSTIFY_CENTER, | ||||||||||||||||
NO_WRAP, | ||||||||||||||||
useOnClickOutside, | ||||||||||||||||
POSITION_ABSOLUTE, | ||||||||||||||||
SecondaryButton, | ||||||||||||||||
SPACING, | ||||||||||||||||
StyledText, | ||||||||||||||||
TOOLTIP_FIXED, | ||||||||||||||||
TOOLTIP_TOP, | ||||||||||||||||
Tooltip, | ||||||||||||||||
useHoverTooltip, | ||||||||||||||||
useOnClickOutside, | ||||||||||||||||
} from '@opentrons/components' | ||||||||||||||||
import { | ||||||||||||||||
HEATERSHAKER_MODULE_TYPE, | ||||||||||||||||
MAGNETIC_MODULE_TYPE, | ||||||||||||||||
TEMPERATURE_MODULE_TYPE, | ||||||||||||||||
THERMOCYCLER_MODULE_TYPE, | ||||||||||||||||
} from '@opentrons/shared-data' | ||||||||||||||||
import { | ||||||||||||||||
actions as stepsActions, | ||||||||||||||||
getIsMultiSelectMode, | ||||||||||||||||
} from '../../../../ui/steps' | ||||||||||||||||
import { | ||||||||||||||||
selectors as stepFormSelectors, | ||||||||||||||||
getIsModuleOnDeck, | ||||||||||||||||
} from '../../../../step-forms' | ||||||||||||||||
import { selectors as stepFormSelectors } from '../../../../step-forms' | ||||||||||||||||
import { | ||||||||||||||||
CLOSE_UNSAVED_STEP_FORM, | ||||||||||||||||
ConfirmDeleteModal, | ||||||||||||||||
getMainPagePortalEl, | ||||||||||||||||
} from '../../../../organisms' | ||||||||||||||||
import { getEnableComment } from '../../../../feature-flags/selectors' | ||||||||||||||||
import { getIsStepTypeEnabled, getSupportedSteps } from './utils' | ||||||||||||||||
|
||||||||||||||||
import { AddStepOverflowButton } from './AddStepOverflowButton' | ||||||||||||||||
|
||||||||||||||||
|
@@ -44,7 +42,11 @@ import type { ThunkDispatch } from 'redux-thunk' | |||||||||||||||
import type { BaseState } from '../../../../types' | ||||||||||||||||
import type { StepType } from '../../../../form-types' | ||||||||||||||||
|
||||||||||||||||
export function AddStepButton(): JSX.Element { | ||||||||||||||||
interface AddStepButtonProps { | ||||||||||||||||
hasText: boolean | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
export function AddStepButton({ hasText }: AddStepButtonProps): JSX.Element { | ||||||||||||||||
const { t } = useTranslation(['tooltip', 'button']) | ||||||||||||||||
const enableComment = useSelector(getEnableComment) | ||||||||||||||||
const dispatch = useDispatch<ThunkDispatch<BaseState, any, any>>() | ||||||||||||||||
|
@@ -72,51 +74,11 @@ export function AddStepButton(): JSX.Element { | |||||||||||||||
null | ||||||||||||||||
) | ||||||||||||||||
|
||||||||||||||||
const getSupportedSteps = (): Array< | ||||||||||||||||
Exclude<StepType, 'manualIntervention'> | ||||||||||||||||
> => | ||||||||||||||||
enableComment | ||||||||||||||||
? [ | ||||||||||||||||
'comment', | ||||||||||||||||
'moveLabware', | ||||||||||||||||
'moveLiquid', | ||||||||||||||||
'mix', | ||||||||||||||||
'pause', | ||||||||||||||||
'heaterShaker', | ||||||||||||||||
'magnet', | ||||||||||||||||
'temperature', | ||||||||||||||||
'thermocycler', | ||||||||||||||||
] | ||||||||||||||||
: [ | ||||||||||||||||
'moveLabware', | ||||||||||||||||
'moveLiquid', | ||||||||||||||||
'mix', | ||||||||||||||||
'pause', | ||||||||||||||||
'heaterShaker', | ||||||||||||||||
'magnet', | ||||||||||||||||
'temperature', | ||||||||||||||||
'thermocycler', | ||||||||||||||||
] | ||||||||||||||||
const isStepTypeEnabled: Record< | ||||||||||||||||
Exclude<StepType, 'manualIntervention'>, | ||||||||||||||||
boolean | ||||||||||||||||
> = { | ||||||||||||||||
comment: enableComment, | ||||||||||||||||
moveLabware: true, | ||||||||||||||||
moveLiquid: true, | ||||||||||||||||
mix: true, | ||||||||||||||||
pause: true, | ||||||||||||||||
magnet: getIsModuleOnDeck(modules, MAGNETIC_MODULE_TYPE), | ||||||||||||||||
temperature: getIsModuleOnDeck(modules, TEMPERATURE_MODULE_TYPE), | ||||||||||||||||
thermocycler: getIsModuleOnDeck(modules, THERMOCYCLER_MODULE_TYPE), | ||||||||||||||||
heaterShaker: getIsModuleOnDeck(modules, HEATERSHAKER_MODULE_TYPE), | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
const addStep = (stepType: StepType): ReturnType<any> => | ||||||||||||||||
dispatch(stepsActions.addAndSelectStep({ stepType })) | ||||||||||||||||
|
||||||||||||||||
const items = getSupportedSteps() | ||||||||||||||||
.filter(stepType => isStepTypeEnabled[stepType]) | ||||||||||||||||
const items = getSupportedSteps(enableComment) | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe
Suggested change
|
||||||||||||||||
.filter(stepType => getIsStepTypeEnabled(enableComment, modules)[stepType]) | ||||||||||||||||
.map(stepType => ( | ||||||||||||||||
<AddStepOverflowButton | ||||||||||||||||
key={stepType} | ||||||||||||||||
|
@@ -154,16 +116,8 @@ export function AddStepButton(): JSX.Element { | |||||||||||||||
|
||||||||||||||||
{showStepOverflowMenu ? ( | ||||||||||||||||
<Flex | ||||||||||||||||
position={POSITION_ABSOLUTE} | ||||||||||||||||
zIndex={5} | ||||||||||||||||
css={STEP_OVERFLOW_MENU_STYLE} | ||||||||||||||||
ref={overflowWrapperRef} | ||||||||||||||||
left="19.5rem" | ||||||||||||||||
whiteSpace={NO_WRAP} | ||||||||||||||||
bottom="4.2rem" | ||||||||||||||||
borderRadius={BORDERS.borderRadius8} | ||||||||||||||||
boxShadow="0px 1px 3px rgba(0, 0, 0, 0.2)" | ||||||||||||||||
backgroundColor={COLORS.white} | ||||||||||||||||
flexDirection={DIRECTION_COLUMN} | ||||||||||||||||
onClick={(e: MouseEvent) => { | ||||||||||||||||
e.preventDefault() | ||||||||||||||||
e.stopPropagation() | ||||||||||||||||
|
@@ -179,6 +133,10 @@ export function AddStepButton(): JSX.Element { | |||||||||||||||
</Tooltip> | ||||||||||||||||
)} | ||||||||||||||||
<SecondaryButton | ||||||||||||||||
display="flex" | ||||||||||||||||
koji marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||
justifyContent={JUSTIFY_CENTER} | ||||||||||||||||
alignItems={ALIGN_CENTER} | ||||||||||||||||
gridGap={SPACING.spacing10} | ||||||||||||||||
width="100%" | ||||||||||||||||
{...targetProps} | ||||||||||||||||
id="AddStepButton" | ||||||||||||||||
|
@@ -187,8 +145,21 @@ export function AddStepButton(): JSX.Element { | |||||||||||||||
}} | ||||||||||||||||
disabled={isStepCreationDisabled} | ||||||||||||||||
> | ||||||||||||||||
{t('button:add_step')} | ||||||||||||||||
<Icon name="plus" size="1rem" /> | ||||||||||||||||
{hasText ? <StyledText>{t('button:add_step')}</StyledText> : null} | ||||||||||||||||
</SecondaryButton> | ||||||||||||||||
</> | ||||||||||||||||
) | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
const STEP_OVERFLOW_MENU_STYLE = css` | ||||||||||||||||
position: ${POSITION_ABSOLUTE}; | ||||||||||||||||
z-index: 5; | ||||||||||||||||
left: 19.5rem; | ||||||||||||||||
white-space: ${NO_WRAP}; | ||||||||||||||||
bottom: 4.2rem; | ||||||||||||||||
border-radius: ${BORDERS.borderRadius8}; | ||||||||||||||||
box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.2); | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we have a constant for this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you mean box-shadow or the entire style? |
||||||||||||||||
background-color: ${COLORS.white}; | ||||||||||||||||
flex-direction: ${DIRECTION_COLUMN}; | ||||||||||||||||
` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'll get the right value from Mel next week