Skip to content

Commit 7485874

Browse files
committed
feat: implement draggable side panel with support to open doc links in iframe
1 parent f94f49b commit 7485874

File tree

6 files changed

+343
-136
lines changed

6 files changed

+343
-136
lines changed
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { useState } from 'react'
2+
// eslint-disable-next-line import/no-extraneous-dependencies
3+
import Draggable, { DraggableEventHandler } from 'react-draggable'
4+
5+
import { AnimatePresence, motion, useMainContext } from '@devtron-labs/devtron-fe-common-lib'
6+
7+
import { SidePanelDocumentation } from './SidePanelDocumentation'
8+
import { SidePanelProps } from './types'
9+
10+
const MAX_ASIDE_WIDTH = 525
11+
const MIN_ASIDE_WIDTH = 350
12+
13+
export const SidePanel = ({ asideWidth, setAsideWidth }: SidePanelProps) => {
14+
// STATES
15+
const [contentOverlay, setContentOverlay] = useState(false)
16+
17+
// HOOKS
18+
const { sidePanelConfig, setSidePanelConfig } = useMainContext()
19+
20+
const { open } = sidePanelConfig
21+
22+
// HANDLERS
23+
const handleClose = () => {
24+
setAsideWidth(MIN_ASIDE_WIDTH)
25+
setSidePanelConfig({ open: false })
26+
}
27+
28+
const handleDrag: DraggableEventHandler = (_, data) => {
29+
const newWidth = asideWidth - data.deltaX
30+
const clamped = Math.max(MIN_ASIDE_WIDTH, Math.min(MAX_ASIDE_WIDTH, newWidth))
31+
setAsideWidth(clamped)
32+
}
33+
34+
const handleDragStart = () => setContentOverlay(true)
35+
36+
const handleDragStop = () => setContentOverlay(false)
37+
38+
return (
39+
<AnimatePresence>
40+
{open && (
41+
<>
42+
<Draggable
43+
handle=".aside-drag"
44+
defaultClassNameDragging="aside-drag--dragging"
45+
axis="none"
46+
position={{
47+
x: 0,
48+
y: 0,
49+
}}
50+
bounds={{
51+
top: 0,
52+
bottom: 0,
53+
}}
54+
onDrag={handleDrag}
55+
onStart={handleDragStart}
56+
onStop={handleDragStop}
57+
>
58+
<div className="aside-drag flex dc__cursor-col-resize dc__zi-10">
59+
<div className="aside-drag__handle px-1 br-1" />
60+
</div>
61+
</Draggable>
62+
<motion.aside
63+
initial={{ x: 350, opacity: 0 }}
64+
animate={{ x: 0, opacity: 1 }}
65+
exit={{ x: 350, opacity: 0 }}
66+
transition={{
67+
duration: 0.2,
68+
type: 'spring',
69+
stiffness: 300,
70+
damping: 30,
71+
}}
72+
className="dc__position-rel mt-8 mr-8 mb-8 border__primary br-6 bg__primary flexbox-col dc__overflow-hidden"
73+
>
74+
{contentOverlay && <div className="dc__position-abs w-100 h-100 dc__zi-1" />}
75+
<SidePanelDocumentation onClose={handleClose} />
76+
</motion.aside>
77+
</>
78+
)}
79+
</AnimatePresence>
80+
)
81+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import {
2+
Button,
3+
ButtonComponentType,
4+
ButtonStyleType,
5+
ButtonVariantType,
6+
ComponentSizeType,
7+
Icon,
8+
useMainContext,
9+
useTheme,
10+
} from '@devtron-labs/devtron-fe-common-lib'
11+
12+
import { SidePanelDocumentationProps } from './types'
13+
14+
export const SidePanelDocumentation = ({ onClose }: SidePanelDocumentationProps) => {
15+
// HOOKS
16+
const { appTheme } = useTheme()
17+
const {
18+
sidePanelConfig: { docLink },
19+
} = useMainContext()
20+
21+
return (
22+
<>
23+
<div className="px-16 pt-14 pb-13 border__primary--bottom flex dc__gap-12">
24+
<Icon name="ic-book-open" color="N900" />
25+
<h2 className="m-0 fs-16 lh-1-5 fw-6 cn-9 flex-grow-1">Documentation</h2>
26+
<div className="flex dc__gap-8">
27+
<Button
28+
dataTestId="side-doc-open-link"
29+
ariaLabel="side-doc-open-link"
30+
showAriaLabelInTippy={false}
31+
icon={<Icon name="ic-arrow-square-out" color={null} />}
32+
variant={ButtonVariantType.borderLess}
33+
style={ButtonStyleType.neutral}
34+
size={ComponentSizeType.xs}
35+
component={ButtonComponentType.anchor}
36+
anchorProps={{
37+
href: docLink,
38+
}}
39+
/>
40+
<Button
41+
dataTestId="side-doc-close-btn"
42+
ariaLabel="side-doc-close-btn"
43+
showAriaLabelInTippy={false}
44+
icon={<Icon name="ic-close-large" color={null} />}
45+
variant={ButtonVariantType.borderLess}
46+
style={ButtonStyleType.negativeGrey}
47+
size={ComponentSizeType.xs}
48+
onClick={onClose}
49+
/>
50+
</div>
51+
</div>
52+
<div className="flex-grow-1">
53+
{docLink && (
54+
<iframe
55+
title="side-panel-documentation"
56+
loading="lazy"
57+
className="dc__no-border"
58+
src={`${docLink}?theme=${appTheme}`}
59+
width="100%"
60+
height="100%"
61+
/>
62+
)}
63+
</div>
64+
</>
65+
)
66+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './SidePanel'
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { Dispatch, SetStateAction } from 'react'
2+
3+
export interface SidePanelProps {
4+
asideWidth: number
5+
setAsideWidth: Dispatch<SetStateAction<number>>
6+
}
7+
8+
export interface SidePanelDocumentationProps {
9+
onClose: () => void
10+
}

0 commit comments

Comments
 (0)