Skip to content
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

Merged
merged 28 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
a3a0bae
add draggable sidebar component for timeline
koji Dec 6, 2024
31e12fd
Merge branch 'edge' into feat_add-resizeable-sidebar
koji Dec 6, 2024
6f81ca4
update add step button
koji Dec 11, 2024
f7d013a
Merge branch 'edge' into feat_add-resizeable-sidebar
koji Dec 11, 2024
041bda1
modify icon position with sidebarWidth
koji Dec 11, 2024
7e7127e
fix hotkey info position issue
koji Dec 13, 2024
37a66db
fix empty spacing issue
koji Dec 13, 2024
892b2cc
restore flex to keep the fixed two col
koji Dec 13, 2024
1856da6
Merge branch 'edge' into feat_add-resizeable-sidebar
koji Dec 13, 2024
d79b7ee
fix check-js errors
koji Dec 13, 2024
9b88d76
modify a component name
koji Dec 13, 2024
8c96b57
fix test error
koji Dec 13, 2024
e5dcda6
add test
koji Dec 13, 2024
5daede5
fix check-js errors
koji Dec 14, 2024
996fde6
added a base test
koji Dec 14, 2024
7b99baf
fix format errors
koji Dec 14, 2024
ea625d2
clean up draggable sidebar a little bit
koji Dec 16, 2024
ad6b69e
Merge branch 'feat_add-resizeable-sidebar' of https://github.com/Open…
koji Dec 16, 2024
3671140
address feedback
koji Dec 18, 2024
0cf372b
fix check-js errors
koji Dec 18, 2024
e2711d9
Merge branch 'edge' into feat_add-resizeable-sidebar
koji Dec 18, 2024
eca5df7
fix check-js error
koji Dec 18, 2024
25bc420
resolve the pointed-out UI issues
koji Dec 19, 2024
7d2fbed
Merge branch 'edge' into feat_add-resizeable-sidebar
koji Dec 20, 2024
22538f2
remove unused lines
koji Dec 20, 2024
8237107
fix lint errors
koji Dec 20, 2024
bcaf94b
fix check-js errors
koji Dec 20, 2024
f6525ac
modify styles a little bit
koji Dec 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion protocol-designer/src/assets/localization/en/button.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"add_step": "+ Add Step",
"add_step": "Add Step",
"add_off_deck": "+ Off-deck labware",
"cancel": "cancel",
"close": "close",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { useState, useRef, useCallback, useEffect } from 'react'
import styled from 'styled-components'
import {
Box,
COLORS,
DIRECTION_COLUMN,
DISPLAY_FLEX,
Flex,
JUSTIFY_SPACE_BETWEEN,
} from '@opentrons/components'
import { TimelineToolbox } from './Timeline/TimelineToolbox'

const INITIAL_SIDEBAR_WIDTH = 276
const MIN_SIDEBAR_WIDTH = 80
const MAX_SIDEBAR_WIDTH = 350

interface DraggableSidebarProps {
setTargetWidth: (width: number) => void
}

// Note (kk:2024/12/20 the designer will revisit responsive sidebar design in 2025
// we will need to update the details to align with the updated design
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)`
display: ${DISPLAY_FLEX};
flex-direction: ${DIRECTION_COLUMN};
border-right: 1px solid #ccc;
position: relative;
/* overflow: hidden; */
height: 100%;
`

const SidebarContent = styled(Flex)`
flex: 1;
`

interface SidebarResizerProps {
dragging: boolean
}

const SidebarResizer = styled(Flex)<SidebarResizerProps>`
user-select: none;
width: 2px;
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: ${COLORS.blue50}; /* Hover state */
}

${props =>
props.dragging === true &&
`
background-color: ${COLORS.blue55}; /* Dragging state */
`}
`
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,27 @@ 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,
DISPLAY_FLEX,
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 {
ABSORBANCE_READER_TYPE,
Expand All @@ -23,6 +31,7 @@ import {
TEMPERATURE_MODULE_TYPE,
THERMOCYCLER_MODULE_TYPE,
} from '@opentrons/shared-data'

import {
actions as stepsActions,
getIsMultiSelectMode,
Expand All @@ -40,15 +49,18 @@ import {
getEnableAbsorbanceReader,
getEnableComment,
} from '../../../../feature-flags/selectors'

import { AddStepOverflowButton } from './AddStepOverflowButton'

import type { MouseEvent } from 'react'
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>>()
Expand Down Expand Up @@ -151,16 +163,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()
Expand All @@ -176,6 +180,10 @@ export function AddStepButton(): JSX.Element {
</Tooltip>
)}
<SecondaryButton
display={DISPLAY_FLEX}
justifyContent={JUSTIFY_CENTER}
alignItems={ALIGN_CENTER}
gridGap={SPACING.spacing10}
width="100%"
{...targetProps}
id="AddStepButton"
Expand All @@ -184,8 +192,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;
right: -7.75rem;
white-space: ${NO_WRAP};
bottom: 4.2rem;
border-radius: ${BORDERS.borderRadius8};
box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.2);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have a constant for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you mean box-shadow or the entire style?
i think we don't have the constant for this part.

background-color: ${COLORS.white};
flex-direction: ${DIRECTION_COLUMN};
`
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export interface ConnectedStepInfoProps {
dragHovered?: boolean
openedOverflowMenuId?: string | null
setOpenedOverflowMenuId?: Dispatch<SetStateAction<string | null>>
sidebarWidth: number
}

export function ConnectedStepInfo(props: ConnectedStepInfoProps): JSX.Element {
Expand All @@ -58,6 +59,7 @@ export function ConnectedStepInfo(props: ConnectedStepInfoProps): JSX.Element {
dragHovered = false,
openedOverflowMenuId,
setOpenedOverflowMenuId,
sidebarWidth,
} = props
const { t } = useTranslation('application')
const dispatch = useDispatch<ThunkDispatch<BaseState, any, any>>()
Expand Down Expand Up @@ -227,6 +229,7 @@ export function ConnectedStepInfo(props: ConnectedStepInfoProps): JSX.Element {
step.stepName || t(`stepType.${step.stepType}`)
}`}
dragHovered={dragHovered}
sidebarWidth={sidebarWidth}
/>
</>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ interface DragDropStepProps extends ConnectedStepItemProps {
orderedStepIds: string[]
openedOverflowMenuId?: string | null
setOpenedOverflowMenuId?: Dispatch<SetStateAction<string | null>>
sidebarWidth: number
}

interface DropType {
Expand All @@ -46,6 +47,7 @@ function DragDropStep(props: DragDropStepProps): JSX.Element {
stepNumber,
openedOverflowMenuId,
setOpenedOverflowMenuId,
sidebarWidth,
} = props
const stepRef = useRef<HTMLDivElement>(null)

Expand Down Expand Up @@ -94,6 +96,7 @@ function DragDropStep(props: DragDropStepProps): JSX.Element {
stepNumber={stepNumber}
stepId={stepId}
dragHovered={hovered}
sidebarWidth={sidebarWidth}
/>
</Box>
)
Expand All @@ -102,9 +105,10 @@ function DragDropStep(props: DragDropStepProps): JSX.Element {
interface DraggableStepsProps {
orderedStepIds: StepIdType[]
reorderSteps: (steps: StepIdType[]) => void
sidebarWidth: number
}
export function DraggableSteps(props: DraggableStepsProps): JSX.Element | null {
const { orderedStepIds, reorderSteps } = props
const { orderedStepIds, reorderSteps, sidebarWidth } = props
const { t } = useTranslation('shared')
const [openedOverflowMenuId, setOpenedOverflowMenuId] = useState<
string | null
Expand Down Expand Up @@ -146,14 +150,21 @@ export function DraggableSteps(props: DraggableStepsProps): JSX.Element | null {
orderedStepIds={orderedStepIds}
openedOverflowMenuId={openedOverflowMenuId}
setOpenedOverflowMenuId={setOpenedOverflowMenuId}
sidebarWidth={sidebarWidth}
/>
))}
<StepDragPreview />
<StepDragPreview sidebarWidth={sidebarWidth} />
</Flex>
)
}

function StepDragPreview(): JSX.Element | null {
interface StepDragPreviewProps {
sidebarWidth: number
}

function StepDragPreview({
sidebarWidth,
}: StepDragPreviewProps): JSX.Element | null {
const [{ isDragging, itemType, item, currentOffset }] = useDrag(() => ({
type: DND_TYPES.STEP_ITEM,
collect: (monitor: DragLayerMonitor) => ({
Expand Down Expand Up @@ -182,6 +193,7 @@ function StepDragPreview(): JSX.Element | null {
<StepContainer
iconName={stepIconsByType[stepType]}
title={stepName || ''}
sidebarWidth={sidebarWidth}
/>
</Flex>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ import {
} from '../../../../ui/steps'
import { StepContainer } from './StepContainer'

export function PresavedStep(): JSX.Element | null {
interface PresavedStepProps {
sidebarWidth: number
}

export function PresavedStep({
sidebarWidth,
}: PresavedStepProps): JSX.Element | null {
const { t } = useTranslation('application')
const presavedStepForm = useSelector(stepFormSelectors.getPresavedStepForm)
const stepNumber = useSelector(stepFormSelectors.getOrderedStepIds).length + 1
Expand Down Expand Up @@ -39,6 +45,7 @@ export function PresavedStep(): JSX.Element | null {
hovered={hovered}
iconName={stepIconsByType[stepType]}
title={`${stepNumber}. ${t(`stepType.${stepType}`)}`}
sidebarWidth={sidebarWidth}
/>
)
}
Loading
Loading