@@ -8,21 +8,40 @@ import { useToggleOpen } from 'hooks';
88import { useTranslation } from 'i18n' ;
99import compact from 'lodash/compact' ;
1010import flatMapDeep from 'lodash/flatMapDeep' ;
11+ import { setNavigationCollapsedStorage } from 'storage/navigation' ;
1112import { cn } from 'utils/style' ;
1213import * as IconSystem from '@icons' ;
1314import Divider from 'components/divider' ;
1415import Icon from 'components/icon' ;
16+ import { Tooltip } from 'components/tooltip' ;
1517import SectionMenu from './menu-section' ;
1618import MyProjects from './my-projects' ;
1719import SwitchOrganization from './switch-organization' ;
1820import UserMenu from './user-menu' ;
21+ import logoIcon from '/img/bucketeer-logo-icon.png' ;
1922
20- const Navigation = ( { onClickNavLink } : { onClickNavLink : ( ) => void } ) => {
23+ type NavigationProps = {
24+ onClickNavLink : ( ) => void ;
25+ isCollapsed : boolean ;
26+ onToggleCollapsed : ( value : boolean ) => void ;
27+ } ;
28+
29+ const Navigation = ( {
30+ onClickNavLink,
31+ isCollapsed,
32+ onToggleCollapsed
33+ } : NavigationProps ) => {
2134 const { t } = useTranslation ( [ 'common' ] ) ;
2235 const { pathname } = useLocation ( ) ;
2336 const navigate = useNavigate ( ) ;
2437 const { consoleAccount } = useAuth ( ) ;
2538
39+ const toggleCollapsed = ( ) => {
40+ const next = ! isCollapsed ;
41+ setNavigationCollapsedStorage ( next ) ;
42+ onToggleCollapsed ( next ) ;
43+ } ;
44+
2645 const currentEnvironment = getCurrentEnvironment ( consoleAccount ! ) ;
2746 const envUrlCode = currentEnvironment . urlCode ;
2847
@@ -152,11 +171,58 @@ const Navigation = ({ onClickNavLink }: { onClickNavLink: () => void }) => {
152171 useToggleOpen ( false ) ;
153172
154173 return (
155- < div className = "fixed h-screen w-[248px] bg-primary-500 z-50 py-8 px-6" >
174+ < div
175+ className = { cn (
176+ 'fixed h-screen bg-primary-500 z-50 py-8 transition-all duration-300 ease-in-out' ,
177+ isCollapsed ? 'w-[60px] px-2' : 'w-[248px] px-6'
178+ ) }
179+ >
156180 < div className = "flex flex-col size-full relative overflow-hidden" >
157- < Link to = { ROUTING . PAGE_PATH_ROOT } onClick = { onCloseSetting } >
158- < img src = { logo } alt = "Bucketer" />
159- </ Link >
181+ < div
182+ className = { cn ( 'group relative flex items-center' , {
183+ 'w-full justify-center' : isCollapsed
184+ } ) }
185+ >
186+ < Link
187+ to = { ROUTING . PAGE_PATH_ROOT }
188+ onClick = { onCloseSetting }
189+ className = { cn (
190+ 'overflow-hidden' ,
191+ isCollapsed && 'flex-center w-full'
192+ ) }
193+ >
194+ { isCollapsed ? (
195+ < img
196+ src = { logoIcon }
197+ alt = "Bucketeer"
198+ className = "w-11 h-11 shrink-0"
199+ />
200+ ) : (
201+ < img src = { logo } alt = "Bucketer" />
202+ ) }
203+ </ Link >
204+ < button
205+ type = "button"
206+ onClick = { toggleCollapsed }
207+ className = { cn (
208+ 'flex-center size-6 rounded-md text-primary-50 shrink-0' ,
209+ 'opacity-0 group-hover:opacity-100 focus-visible:opacity-100 transition-opacity' ,
210+ isCollapsed
211+ ? 'absolute inset-0 mx-auto w-11 h-11 bg-primary-500/80 hover:bg-primary-400 pointer-events-none group-hover:pointer-events-auto focus-visible:pointer-events-auto'
212+ : 'ml-auto hover:bg-primary-400'
213+ ) }
214+ aria-label = { t (
215+ isCollapsed ? `navigation.expand` : `navigation.collapse`
216+ ) }
217+ >
218+ < Icon
219+ icon = { IconSystem . IconChevronRight }
220+ color = "primary-50"
221+ size = "xs"
222+ className = { cn ( { 'rotate-180' : ! isCollapsed } ) }
223+ />
224+ </ button >
225+ </ div >
160226
161227 < div className = "flex flex-col flex-1 items-center pt-6" >
162228 < div
@@ -165,23 +231,39 @@ const Navigation = ({ onClickNavLink }: { onClickNavLink: () => void }) => {
165231 { 'right-0' : isOpenSetting }
166232 ) }
167233 >
168- < button
169- onClick = { ( ) => {
170- onCloseSetting ( ) ;
171- navigate ( `/${ envUrlCode } ${ ROUTING . PAGE_PATH_FEATURES } ` ) ;
172- } }
173- className = "flex items-center gap-x-2 text-primary-50"
174- >
175- < Icon icon = { IconSystem . IconBackspace } />
176- < span > { t ( `navigation.back-to-main` ) } </ span >
177- </ button >
234+ < Tooltip
235+ hidden = { ! isCollapsed }
236+ content = { t ( `navigation.back-to-main` ) }
237+ side = "right"
238+ trigger = {
239+ < button
240+ onClick = { ( ) => {
241+ onCloseSetting ( ) ;
242+ navigate ( `/${ envUrlCode } ${ ROUTING . PAGE_PATH_FEATURES } ` ) ;
243+ } }
244+ aria-label = {
245+ isCollapsed ? t ( `navigation.back-to-main` ) : undefined
246+ }
247+ className = { cn (
248+ 'flex items-center gap-x-2 text-primary-50 rounded-lg' ,
249+ isCollapsed
250+ ? 'justify-center w-full py-2 hover:bg-primary-400'
251+ : 'px-3'
252+ ) }
253+ >
254+ < Icon icon = { IconSystem . IconBackspace } />
255+ { ! isCollapsed && < span > { t ( `navigation.back-to-main` ) } </ span > }
256+ </ button >
257+ }
258+ />
178259 < Divider className = "my-5 bg-primary-50 opacity-10" />
179260 { settingMenuSections . map ( ( item , index ) => (
180261 < SectionMenu
181262 key = { index }
182263 className = "first:mt-0 mt-4"
183264 title = { item . title }
184265 items = { item . menus }
266+ isCollapsed = { isCollapsed }
185267 />
186268 ) ) }
187269 </ div >
@@ -191,10 +273,12 @@ const Navigation = ({ onClickNavLink }: { onClickNavLink: () => void }) => {
191273 { 'left-0' : ! isOpenSetting }
192274 ) }
193275 >
194- < div className = "px-3 opacity-80 uppercase typo-head-bold-tiny text-primary-50 mb-3" >
195- { t ( `environment` ) }
196- </ div >
197- < MyProjects />
276+ { ! isCollapsed && (
277+ < div className = "px-3 opacity-80 uppercase typo-head-bold-tiny text-primary-50 mb-3" >
278+ { t ( `environment` ) }
279+ </ div >
280+ ) }
281+ < MyProjects isCollapsed = { isCollapsed } />
198282 < Divider className = "my-5 bg-primary-50 opacity-10" />
199283 { mainMenuSections . map ( ( item , index ) => (
200284 < SectionMenu
@@ -203,34 +287,48 @@ const Navigation = ({ onClickNavLink }: { onClickNavLink: () => void }) => {
203287 title = { item . title }
204288 items = { item . menus }
205289 onClickNavLink = { onClickNavLink }
290+ isCollapsed = { isCollapsed }
206291 />
207292 ) ) }
208293 </ div >
209294 </ div >
210295
211296 < Divider className = "mb-3 bg-primary-50 opacity-10" />
212297
213- < div className = "flex items-center justify-between" >
298+ < div
299+ className = { cn ( 'flex items-center justify-between' , {
300+ 'flex-col gap-y-3' : isCollapsed
301+ } ) }
302+ >
214303 < UserMenu onOpenSwitchOrg = { onOpenSwitchOrg } />
215- < button
216- type = "button"
217- onClick = { ( ) => {
218- onOpenSetting ( ) ;
219- if ( consoleAccount ?. isSystemAdmin ) {
220- navigate ( ROUTING . PAGE_PATH_ORGANIZATIONS ) ;
221- } else {
222- navigate ( `/${ envUrlCode } ${ ROUTING . PAGE_PATH_SETTINGS } ` ) ;
223- }
224- } }
225- >
226- < Icon icon = { IconSystem . IconSetting } color = "primary-50" />
227- </ button >
304+ < Tooltip
305+ hidden = { ! isCollapsed }
306+ content = { t ( `settings` ) }
307+ side = "right"
308+ trigger = {
309+ < button
310+ type = "button"
311+ aria-label = { isCollapsed ? t ( `settings` ) : undefined }
312+ onClick = { ( ) => {
313+ onOpenSetting ( ) ;
314+ if ( consoleAccount ?. isSystemAdmin ) {
315+ navigate ( ROUTING . PAGE_PATH_ORGANIZATIONS ) ;
316+ } else {
317+ navigate ( `/${ envUrlCode } ${ ROUTING . PAGE_PATH_SETTINGS } ` ) ;
318+ }
319+ } }
320+ >
321+ < Icon icon = { IconSystem . IconSetting } color = "primary-50" />
322+ </ button >
323+ }
324+ />
228325 </ div >
229326 </ div >
230327 < SwitchOrganization
231328 isOpen = { isOpenSwitchOrg }
232329 onCloseSwitchOrg = { onCloseSwitchOrg }
233330 onCloseSetting = { onCloseSetting }
331+ isCollapsed = { isCollapsed }
234332 />
235333 </ div >
236334 ) ;
0 commit comments