Skip to content

Commit

Permalink
feat(RPS-234): Jogging panel layout update (#114)
Browse files Browse the repository at this point in the history
* fix: Fixed divider height

* feat: Updated jogging panel controls/layout to new design

* fix: Fixed cartesian values not copyable

* feat: Added copy popup to copyable text

* feat: Added PoseCartesianValues component (to export)

* feat: Added orientation translation

* chore: Moved experimental to components

* fix: Fixed velocity label box size change

* chore: Unified stopJogging calls in axis control

* feat: Added jogging panel children render based on active tab

* chore: Rolled back tab based renderer
  • Loading branch information
vgerber authored Oct 11, 2024
1 parent 2f8d1b2 commit c20289f
Show file tree
Hide file tree
Showing 27 changed files with 806 additions and 534 deletions.
81 changes: 69 additions & 12 deletions src/components/CopyableText.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Chip, InputLabel } from "@mui/material"
import { forwardRef } from "react"
import { Stack, Tooltip, Typography, useTheme } from "@mui/material"
import { forwardRef, useEffect, useState } from "react"

export const CopyableText = forwardRef(
(
Expand All @@ -12,19 +12,76 @@ export const CopyableText = forwardRef(
},
ref: React.ForwardedRef<HTMLDivElement>,
) => {
const theme = useTheme()
const [tooltipOpen, setTooltipOpen] = useState(false)

async function onCopyText(): Promise<boolean> {
if (!ref || !("current" in ref)) {
console.warn("No copy target found")
return false
}
try {
await navigator.clipboard.writeText(value)
console.log("Copied!")
setTooltipOpen(true)
return true
} catch (err) {
// Direct clipboard copy is not available in non-secure contexts
console.error(err)

// Let's fall back to selecting the text so the user can manually copy easily
const selection = window.getSelection()!
const range = document.createRange()
range.selectNodeContents(ref.current as any)
selection.removeAllRanges()
selection.addRange(range)
}
return false
}

useEffect(() => {
if (!tooltipOpen) {
return
}
const timeoutId = setTimeout(() => setTooltipOpen(false), 3000)
return () => {
timeoutId ? clearTimeout(timeoutId) : {}
}
}, [tooltipOpen])

return (
<>
{label && <InputLabel>{label}</InputLabel>}
<Chip
ref={ref}
<Tooltip open={tooltipOpen} title="Copied!">
<Stack
justifyContent="center"
sx={{
fontSize: "14px",
opacity: 0.8,
marginTop: "4px !important",
height: "100%",
boxSizing: "border-box",
padding: "6px 12px",
background: theme.palette.backgroundPaperElevation?.[8],
borderRadius: "10px",
minWidth: "0",
cursor: "pointer",
}}
value={value}
/>
</>
onClick={() => onCopyText()}
>
<Typography
ref={ref}
align="center"
sx={{
pointerEvents: "none",
fontSize: "12px",
color: theme.palette.text.primary,
whiteSpace: "nowrap",
minWidth: 0,
textOverflow: "ellipsis",
width: "100%",
overflow: "hidden",
}}
>
{value}
</Typography>
</Stack>
</Tooltip>
)
},
)
6 changes: 4 additions & 2 deletions src/components/LoadingCover.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { makeErrorMessage } from "./utils/errorHandling"
import { capitalize, CircularProgress, Stack, useTheme } from "@mui/material"
import { lowerFirst } from "lodash-es"
import { useEffect, useState } from "react"
import { makeErrorMessage } from "./utils/errorHandling"

export const LoadingCover = (props: {
message?: string
Expand All @@ -11,6 +11,7 @@ export const LoadingCover = (props: {
const softTimeout = props.softTimeout || 3000

const [showSlowLoadingMessage, setShowSlowLoadingMessage] = useState(false)
const theme = useTheme()

useEffect(() => {
const timeout = setTimeout(() => {
Expand All @@ -26,6 +27,7 @@ export const LoadingCover = (props: {
height="100%"
alignItems="center"
justifyContent="center"
sx={{ color: theme.palette.text.primary }}
>
{props.error ? (
<LoadingErrorMessage
Expand All @@ -40,7 +42,7 @@ export const LoadingCover = (props: {
sx={{
visibility: showSlowLoadingMessage ? "visible" : "hidden",
marginTop: "1rem",
color: "gray",
color: theme.palette.text.secondary,
}}
>
{"This is taking longer than expected..."}
Expand Down
65 changes: 47 additions & 18 deletions src/components/VelocitySlider.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,30 @@
import { Typography, useTheme } from "@mui/material"
import { Stack, Typography, useTheme, type SxProps } from "@mui/material"
import Slider from "@mui/material/Slider"
import isNumber from "lodash-es/isNumber"
import { observer } from "mobx-react-lite"
import type { ReactNode } from "react"

type VelocitySliderProps = {
min: number
max: number
velocity: number
onVelocityChange: (newVelocity: number) => void
disabled?: boolean
valueLabelFormat?: (value: number) => string
renderValue?: (value: number) => ReactNode
}

/** A slider for controlling the movement velocity of a robot */
export const VelocitySlider = observer((props: VelocitySliderProps) => {
const theme = useTheme()

const valueLabelFormat =
props.valueLabelFormat || ((value: number) => `${value}`)

function onSliderChange(_event: Event, newVelocity: number | number[]) {
if (newVelocity === props.velocity || !isNumber(newVelocity)) return

props.onVelocityChange(newVelocity)
}

return (
<>
<Typography
sx={{
textAlign: "center",
fontSize: "14px",
opacity: 0.8,
lineHeight: 1,
color: theme.palette.text.primary,
}}
>
{valueLabelFormat(props.velocity)}
</Typography>
<Stack direction="row" gap={2}>
<Slider
value={props.velocity}
color="secondary"
Expand All @@ -54,6 +41,48 @@ export const VelocitySlider = observer((props: VelocitySliderProps) => {
},
}}
/>
</>
{props.renderValue ? (
props.renderValue(props.velocity)
) : (
<VelocitySliderLabel value={props.velocity.toString()} />
)}
</Stack>
)
})

type VelocitySliderLabelProps = {
value: string
sx?: SxProps
}

export function VelocitySliderLabel({ value, sx }: VelocitySliderLabelProps) {
const theme = useTheme()
return (
<Stack
direction={"row"}
justifyContent={"center"}
gap={"5px"}
sx={{
padding: "6px 12px",
background: theme.palette.backgroundPaperElevation?.[8],
borderRadius: "10px",
minWidth: "111px",
...sx,
}}
>
<Typography
component="span"
sx={{
textAlign: "right",
fontSize: "14px",
opacity: 0.8,
lineHeight: 1,
color: theme.palette.text.primary,
whiteSpace: "nowrap",
}}
>
{value}
</Typography>
</Stack>
)
}
42 changes: 42 additions & 0 deletions src/components/experimental/utils/AdornedSelect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import {
FormControl,
InputLabel,
Select,
styled,
type SelectProps,
} from "@mui/material"

const AdornedFormControl = styled(FormControl)(({ theme }) => ({
"&.MuiFormControl-root": {
".MuiSelect-select": {
paddingTop: "20px",
paddingLeft: "12px",
},
label: {
pointerEvents: "none",
fontSize: "16px",
},
".MuiInputLabel-root": {
"&.Mui-focused": {
color: theme.palette.text.primary,
},
},
},
}))

type AdornedSelectProps = {
labelValue: string
labelId: string
} & SelectProps

export default function AdornedSelect({
labelValue,
...props
}: AdornedSelectProps) {
return (
<AdornedFormControl fullWidth variant="filled">
<InputLabel id={props.labelId}>{labelValue}</InputLabel>
<Select {...props} />
</AdornedFormControl>
)
}
Loading

0 comments on commit c20289f

Please sign in to comment.