Skip to content

Commit c1fd796

Browse files
committed
feat: implement animated resizing for SidePanel with motionValue and refactor related components
1 parent 580f0f6 commit c1fd796

File tree

4 files changed

+62
-41
lines changed

4 files changed

+62
-41
lines changed

src/components/common/SidePanel/SidePanel.tsx

Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
1-
import { useState } from 'react'
1+
import { useEffect, useState } from 'react'
22
// eslint-disable-next-line import/no-extraneous-dependencies
33
import Draggable, { DraggableEventHandler } from 'react-draggable'
44

5-
import { AnimatePresence, AppThemeType, motion, useMainContext, useTheme } from '@devtron-labs/devtron-fe-common-lib'
5+
import {
6+
animate,
7+
AnimatePresence,
8+
AppThemeType,
9+
motion,
10+
useMainContext,
11+
useTheme,
12+
} from '@devtron-labs/devtron-fe-common-lib'
613

14+
import { SIDE_PANEL_MAX_ASIDE_WIDTH, SIDE_PANEL_MIN_ASIDE_WIDTH } from './constants'
715
import { SidePanelDocumentation } from './SidePanelDocumentation'
816
import { SidePanelProps } from './types'
917

1018
import './SidePanel.scss'
1119

12-
const MAX_ASIDE_WIDTH = 525
13-
const MIN_ASIDE_WIDTH = 350
14-
15-
export const SidePanel = ({ asideWidth, setAsideWidth }: SidePanelProps) => {
20+
export const SidePanel = ({ asideWidth }: SidePanelProps) => {
1621
// STATES
1722
const [contentOverlay, setContentOverlay] = useState(false)
1823

@@ -22,16 +27,32 @@ export const SidePanel = ({ asideWidth, setAsideWidth }: SidePanelProps) => {
2227

2328
const { open } = sidePanelConfig
2429

30+
useEffect(() => {
31+
if (open) {
32+
const controls = animate(asideWidth, SIDE_PANEL_MIN_ASIDE_WIDTH, {
33+
duration: 0.2,
34+
ease: 'easeInOut',
35+
})
36+
return controls.stop
37+
}
38+
39+
const controls = animate(asideWidth, 0, {
40+
duration: 0.3,
41+
ease: 'easeInOut',
42+
})
43+
return controls.stop
44+
}, [open])
45+
2546
// HANDLERS
2647
const handleClose = () => {
27-
setAsideWidth(MIN_ASIDE_WIDTH)
48+
asideWidth.set(SIDE_PANEL_MIN_ASIDE_WIDTH)
2849
setSidePanelConfig({ open: false })
2950
}
3051

3152
const handleDrag: DraggableEventHandler = (_, data) => {
32-
const newWidth = asideWidth - data.deltaX
33-
const clamped = Math.max(MIN_ASIDE_WIDTH, Math.min(MAX_ASIDE_WIDTH, newWidth))
34-
setAsideWidth(clamped)
53+
const newWidth = asideWidth.get() - data.deltaX
54+
const clamped = Math.max(SIDE_PANEL_MIN_ASIDE_WIDTH, Math.min(SIDE_PANEL_MAX_ASIDE_WIDTH, newWidth))
55+
asideWidth.set(clamped)
3556
}
3657

3758
const handleDragStart = () => setContentOverlay(true)
@@ -41,7 +62,13 @@ export const SidePanel = ({ asideWidth, setAsideWidth }: SidePanelProps) => {
4162
return (
4263
<AnimatePresence>
4364
{open && (
44-
<>
65+
<motion.aside
66+
initial={{ x: SIDE_PANEL_MIN_ASIDE_WIDTH, opacity: 0 }}
67+
animate={{ x: 0, opacity: 1 }}
68+
exit={{ x: SIDE_PANEL_MIN_ASIDE_WIDTH, opacity: 0 }}
69+
transition={{ duration: 0.2, ease: 'easeInOut' }}
70+
className="flexbox"
71+
>
4572
<Draggable
4673
handle=".aside-drag"
4774
defaultClassNameDragging="aside-drag--dragging"
@@ -58,26 +85,17 @@ export const SidePanel = ({ asideWidth, setAsideWidth }: SidePanelProps) => {
5885
onStart={handleDragStart}
5986
onStop={handleDragStop}
6087
>
61-
<div className="aside-drag flex dc__cursor-col-resize dc__zi-10">
88+
<div className="aside-drag flex px-7 dc__cursor-col-resize dc__zi-10">
6289
<div className="aside-drag__handle px-1 br-1" />
6390
</div>
6491
</Draggable>
65-
<motion.aside
66-
initial={{ x: 350, opacity: 0 }}
67-
animate={{ x: 0, opacity: 1 }}
68-
exit={{ x: 350, opacity: 0 }}
69-
transition={{
70-
duration: 0.2,
71-
type: 'spring',
72-
stiffness: 300,
73-
damping: 30,
74-
}}
75-
className={`dc__position-rel mt-8 mr-8 mb-8 br-6 bg__primary flexbox-col dc__overflow-hidden ${appTheme === AppThemeType.dark ? 'border__primary-translucent' : ''}`}
92+
<div
93+
className={`flex-grow-1 dc__position-rel mt-8 mr-8 mb-8 br-6 bg__primary flexbox-col dc__overflow-hidden ${appTheme === AppThemeType.dark ? 'border__primary-translucent' : ''}`}
7694
>
7795
{contentOverlay && <div className="dc__position-abs w-100 h-100 dc__zi-1" />}
7896
<SidePanelDocumentation onClose={handleClose} />
79-
</motion.aside>
80-
</>
97+
</div>
98+
</motion.aside>
8199
)}
82100
</AnimatePresence>
83101
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const SIDE_PANEL_MIN_ASIDE_WIDTH = 364
2+
3+
export const SIDE_PANEL_MAX_ASIDE_WIDTH = 525

src/components/common/SidePanel/types.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import { Dispatch, SetStateAction } from 'react'
1+
import { useMotionValue } from '@devtron-labs/devtron-fe-common-lib'
22

33
export interface SidePanelProps {
4-
asideWidth: number
5-
setAsideWidth: Dispatch<SetStateAction<number>>
4+
asideWidth: ReturnType<typeof useMotionValue<number>>
65
}
76

87
export interface SidePanelDocumentationProps {

src/components/common/navigation/NavigationRoutes.tsx

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ import {
4545
motion,
4646
SidePanelConfig,
4747
InstallationType,
48+
useMotionValue,
49+
useMotionTemplate,
4850
} from '@devtron-labs/devtron-fe-common-lib'
4951
import { Route, Switch, useRouteMatch, useHistory, useLocation } from 'react-router-dom'
5052
import * as Sentry from '@sentry/browser'
@@ -151,8 +153,8 @@ export default function NavigationRoutes({ reloadVersionConfig }: Readonly<Navig
151153
const [licenseInfoDialogType, setLicenseInfoDialogType] = useState<LicenseInfoDialogType>(null)
152154
const [intelligenceConfig, setIntelligenceConfig] = useState<IntelligenceConfig>(null)
153155

154-
const [asideWidth, setAsideWidth] = useState(350)
155156
const [sidePanelConfig, setSidePanelConfig] = useState<SidePanelConfig>({ open: false })
157+
const asideWidth = useMotionValue(0)
156158

157159
const {
158160
userPreferences,
@@ -410,23 +412,29 @@ export default function NavigationRoutes({ reloadVersionConfig }: Readonly<Navig
410412
}
411413
}, [location.pathname])
412414

413-
const isOnboardingPage = () => {
415+
const getIsOnboardingPage = () => {
414416
const _pathname = location.pathname.endsWith('/') ? location.pathname.slice(0, -1) : location.pathname
415417
return _pathname === `/${URLS.GETTING_STARTED}` || _pathname === `/dashboard/${URLS.GETTING_STARTED}`
416418
}
417419

420+
const isOnboardingPage = getIsOnboardingPage()
421+
422+
const gridTemplateColumns = !isOnboardingPage
423+
? useMotionTemplate`56px 1fr ${asideWidth}px`
424+
: useMotionTemplate`1fr ${asideWidth}px`
425+
418426
if (pageState === ViewType.LOADING) {
419427
return (
420428
<div className="full-height-width">
421429
<DevtronProgressing parentClasses="h-100 flex bg__primary" classes="icon-dim-80" />
422430
</div>
423431
)
424432
}
433+
425434
if (pageState === ViewType.ERROR) {
426435
// 100vh is required for covering the full height of the page as this is the top level component
427436
return <Reload className="h-100vh bg__tertiary" />
428437
}
429-
const _isOnboardingPage = isOnboardingPage()
430438

431439
const handleOpenLicenseInfoDialog = (
432440
initialDialogTab?: LicenseInfoDialogType.ABOUT | LicenseInfoDialogType.LICENSE,
@@ -498,14 +506,7 @@ export default function NavigationRoutes({ reloadVersionConfig }: Readonly<Navig
498506
isEnterprise: currentServerInfo?.serverInfo?.installationType === InstallationType.ENTERPRISE,
499507
}}
500508
>
501-
<motion.main
502-
id={DEVTRON_BASE_MAIN_ID}
503-
animate={{
504-
gridTemplateColumns: `${!_isOnboardingPage ? '56px' : ''} 1fr ${sidePanelConfig.open ? `14px ${asideWidth}px` : '0px 0px'}`,
505-
}}
506-
style={{ gridTemplateColumns: `${!_isOnboardingPage ? '56px 1fr 0px 0px' : '1fr 0px 0px'}` }}
507-
transition={{ duration: 0.2, type: 'spring', stiffness: 300, damping: 30 }}
508-
>
509+
<motion.main id={DEVTRON_BASE_MAIN_ID} style={{ gridTemplateColumns }}>
509510
{showThemeSwitcherDialog && (
510511
<SwitchThemeDialog
511512
initialThemePreference={userPreferences?.themePreference}
@@ -514,7 +515,7 @@ export default function NavigationRoutes({ reloadVersionConfig }: Readonly<Navig
514515
/>
515516
)}
516517
{renderAboutDevtronDialog()}
517-
{!_isOnboardingPage && (
518+
{!isOnboardingPage && (
518519
<Navigation
519520
currentServerInfo={currentServerInfo}
520521
history={history}
@@ -675,7 +676,7 @@ export default function NavigationRoutes({ reloadVersionConfig }: Readonly<Navig
675676
</Suspense>
676677
</div>
677678
</div>
678-
<SidePanel asideWidth={asideWidth} setAsideWidth={setAsideWidth} />
679+
<SidePanel asideWidth={asideWidth} />
679680
</>
680681
)}
681682
</motion.main>

0 commit comments

Comments
 (0)