11import { useEffect } from 'react' ;
22import { Link , useLocation , useNavigate } from 'react-router' ;
3+ import logoIcon from 'assets/logos/logo-icon.svg' ;
34import logo from 'assets/logos/logo-white.svg' ;
45import { useAuth , getCurrentEnvironment } from 'auth' ;
56import * as ROUTING from 'constants/routing' ;
@@ -8,22 +9,40 @@ import { useToggleOpen } from 'hooks';
89import { useTranslation } from 'i18n' ;
910import compact from 'lodash/compact' ;
1011import flatMapDeep from 'lodash/flatMapDeep' ;
12+ import { setNavigationCollapsedStorage } from 'storage/navigation' ;
1113import { cn } from 'utils/style' ;
1214import * as IconSystem from '@icons' ;
1315import Divider from 'components/divider' ;
1416import Icon from 'components/icon' ;
17+ import { Tooltip } from 'components/tooltip' ;
1518import SectionMenu from './menu-section' ;
1619import MyProjects from './my-projects' ;
1720import NotificationBell from './notification-bell' ;
1821import SwitchOrganization from './switch-organization' ;
1922import UserMenu from './user-menu' ;
2023
21- const Navigation = ( { onClickNavLink } : { onClickNavLink : ( ) => void } ) => {
24+ type NavigationProps = {
25+ onClickNavLink : ( ) => void ;
26+ isCollapsed : boolean ;
27+ onToggleCollapsed : ( value : boolean ) => void ;
28+ } ;
29+
30+ const Navigation = ( {
31+ onClickNavLink,
32+ isCollapsed,
33+ onToggleCollapsed
34+ } : NavigationProps ) => {
2235 const { t } = useTranslation ( [ 'common' ] ) ;
2336 const { pathname } = useLocation ( ) ;
2437 const navigate = useNavigate ( ) ;
2538 const { consoleAccount } = useAuth ( ) ;
2639
40+ const toggleCollapsed = ( ) => {
41+ const next = ! isCollapsed ;
42+ setNavigationCollapsedStorage ( next ) ;
43+ onToggleCollapsed ( next ) ;
44+ } ;
45+
2746 const currentEnvironment = getCurrentEnvironment ( consoleAccount ! ) ;
2847 const envUrlCode = currentEnvironment . urlCode ;
2948
@@ -161,11 +180,59 @@ const Navigation = ({ onClickNavLink }: { onClickNavLink: () => void }) => {
161180 useToggleOpen ( false ) ;
162181
163182 return (
164- < div className = "fixed h-screen w-[248px] bg-primary-500 z-50 py-8 px-6" >
183+ < div
184+ className = { cn (
185+ 'fixed h-screen bg-primary-500 z-50 py-8 transition-all duration-300 ease-in-out' ,
186+ isCollapsed ? 'w-[60px] px-2' : 'w-[248px] px-6'
187+ ) }
188+ >
165189 < div className = "flex flex-col size-full relative overflow-hidden" >
166- < Link to = { ROUTING . PAGE_PATH_ROOT } onClick = { onCloseSetting } >
167- < img src = { logo } alt = "Bucketer" />
168- </ Link >
190+ < div
191+ className = { cn ( 'group relative flex items-center' , {
192+ 'w-full justify-center' : isCollapsed
193+ } ) }
194+ >
195+ < Link
196+ to = { ROUTING . PAGE_PATH_ROOT }
197+ onClick = { onCloseSetting }
198+ className = { cn (
199+ 'overflow-hidden' ,
200+ isCollapsed && 'flex-center w-full' ,
201+ isCollapsed && 'group-hover:pointer-events-none'
202+ ) }
203+ >
204+ { isCollapsed ? (
205+ < img
206+ src = { logoIcon }
207+ alt = "Bucketeer"
208+ className = "w-8 h-8 shrink-0"
209+ />
210+ ) : (
211+ < img src = { logo } alt = "Bucketeer" />
212+ ) }
213+ </ Link >
214+ < button
215+ type = "button"
216+ onClick = { toggleCollapsed }
217+ className = { cn (
218+ 'flex-center rounded-md text-primary-50 shrink-0' ,
219+ 'opacity-0 group-hover:opacity-100 focus-visible:opacity-100 transition-opacity' ,
220+ isCollapsed
221+ ? 'absolute inset-0 m-auto size-8 bg-primary-400 hover:bg-primary-300 pointer-events-none group-hover:pointer-events-auto focus-visible:pointer-events-auto'
222+ : 'size-6 ml-auto hover:bg-primary-400'
223+ ) }
224+ aria-label = { t (
225+ isCollapsed ? `navigation.expand` : `navigation.collapse`
226+ ) }
227+ >
228+ < Icon
229+ icon = { IconSystem . IconChevronRight }
230+ color = "primary-50"
231+ size = "xs"
232+ className = { cn ( { 'rotate-180' : ! isCollapsed } ) }
233+ />
234+ </ button >
235+ </ div >
169236
170237 < div className = "flex flex-col flex-1 items-center pt-6" >
171238 < div
@@ -174,23 +241,39 @@ const Navigation = ({ onClickNavLink }: { onClickNavLink: () => void }) => {
174241 { 'right-0' : isOpenSetting }
175242 ) }
176243 >
177- < button
178- onClick = { ( ) => {
179- onCloseSetting ( ) ;
180- navigate ( `/${ envUrlCode } ${ ROUTING . PAGE_PATH_FEATURES } ` ) ;
181- } }
182- className = "flex items-center gap-x-2 text-primary-50"
183- >
184- < Icon icon = { IconSystem . IconBackspace } />
185- < span > { t ( `navigation.back-to-main` ) } </ span >
186- </ button >
244+ < Tooltip
245+ hidden = { ! isCollapsed }
246+ content = { t ( `navigation.back-to-main` ) }
247+ side = "right"
248+ trigger = {
249+ < button
250+ onClick = { ( ) => {
251+ onCloseSetting ( ) ;
252+ navigate ( `/${ envUrlCode } ${ ROUTING . PAGE_PATH_FEATURES } ` ) ;
253+ } }
254+ aria-label = {
255+ isCollapsed ? t ( `navigation.back-to-main` ) : undefined
256+ }
257+ className = { cn (
258+ 'flex items-center gap-x-2 text-primary-50 rounded-lg' ,
259+ isCollapsed
260+ ? 'justify-center w-full py-2 hover:bg-primary-400'
261+ : 'px-3'
262+ ) }
263+ >
264+ < Icon icon = { IconSystem . IconBackspace } />
265+ { ! isCollapsed && < span > { t ( `navigation.back-to-main` ) } </ span > }
266+ </ button >
267+ }
268+ />
187269 < Divider className = "my-5 bg-primary-50 opacity-10" />
188270 { settingMenuSections . map ( ( item , index ) => (
189271 < SectionMenu
190272 key = { index }
191273 className = "first:mt-0 mt-4"
192274 title = { item . title }
193275 items = { item . menus }
276+ isCollapsed = { isCollapsed }
194277 />
195278 ) ) }
196279 </ div >
@@ -200,10 +283,12 @@ const Navigation = ({ onClickNavLink }: { onClickNavLink: () => void }) => {
200283 { 'left-0' : ! isOpenSetting }
201284 ) }
202285 >
203- < div className = "px-3 opacity-80 uppercase typo-head-bold-tiny text-primary-50 mb-3" >
204- { t ( `environment` ) }
205- </ div >
206- < MyProjects />
286+ { ! isCollapsed && (
287+ < div className = "px-3 opacity-80 uppercase typo-head-bold-tiny text-primary-50 mb-3" >
288+ { t ( `environment` ) }
289+ </ div >
290+ ) }
291+ < MyProjects isCollapsed = { isCollapsed } />
207292 < Divider className = "my-5 bg-primary-50 opacity-10" />
208293 { mainMenuSections . map ( ( item , index ) => (
209294 < SectionMenu
@@ -212,37 +297,55 @@ const Navigation = ({ onClickNavLink }: { onClickNavLink: () => void }) => {
212297 title = { item . title }
213298 items = { item . menus }
214299 onClickNavLink = { onClickNavLink }
300+ isCollapsed = { isCollapsed }
215301 />
216302 ) ) }
217303 </ div >
218304 </ div >
219305
220306 < Divider className = "mb-3 bg-primary-50 opacity-10" />
221307
222- < div className = "flex items-center justify-between" >
308+ < div
309+ className = { cn ( 'flex items-center justify-between' , {
310+ 'flex-col gap-y-3' : isCollapsed
311+ } ) }
312+ >
223313 < UserMenu onOpenSwitchOrg = { onOpenSwitchOrg } />
224- < div className = "flex items-center justify-center gap-2" >
314+ < div
315+ className = { cn ( 'flex items-center justify-center gap-2' , {
316+ 'flex-col' : isCollapsed
317+ } ) }
318+ >
225319 < NotificationBell envUrlCode = { envUrlCode } />
226- < button
227- type = "button"
228- onClick = { ( ) => {
229- onOpenSetting ( ) ;
230- if ( consoleAccount ?. isSystemAdmin ) {
231- navigate ( ROUTING . PAGE_PATH_ORGANIZATIONS ) ;
232- } else {
233- navigate ( `/${ envUrlCode } ${ ROUTING . PAGE_PATH_SETTINGS } ` ) ;
234- }
235- } }
236- >
237- < Icon icon = { IconSystem . IconSetting } color = "primary-50" />
238- </ button >
320+ < Tooltip
321+ hidden = { ! isCollapsed }
322+ content = { t ( `settings` ) }
323+ side = "right"
324+ trigger = {
325+ < button
326+ type = "button"
327+ aria-label = { isCollapsed ? t ( `settings` ) : undefined }
328+ onClick = { ( ) => {
329+ onOpenSetting ( ) ;
330+ if ( consoleAccount ?. isSystemAdmin ) {
331+ navigate ( ROUTING . PAGE_PATH_ORGANIZATIONS ) ;
332+ } else {
333+ navigate ( `/${ envUrlCode } ${ ROUTING . PAGE_PATH_SETTINGS } ` ) ;
334+ }
335+ } }
336+ >
337+ < Icon icon = { IconSystem . IconSetting } color = "primary-50" />
338+ </ button >
339+ }
340+ />
239341 </ div >
240342 </ div >
241343 </ div >
242344 < SwitchOrganization
243345 isOpen = { isOpenSwitchOrg }
244346 onCloseSwitchOrg = { onCloseSwitchOrg }
245347 onCloseSetting = { onCloseSetting }
348+ isCollapsed = { isCollapsed }
246349 />
247350 </ div >
248351 ) ;
0 commit comments