feat: app sidebar support collapse (#1997)

This commit is contained in:
zxhlyh 2024-01-11 13:26:34 +08:00 committed by GitHub
parent eed5fdd768
commit 1372bf784f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 193 additions and 11 deletions

View File

@ -15,7 +15,7 @@ export type IAppBasicProps = {
hoverTip?: string
textStyle?: { main?: string; extra?: string }
isExtraInLine?: boolean
mode?: 'expand' | 'collapse'
mode?: string
}
const ApiSvg = <svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
@ -56,7 +56,7 @@ const ICON_MAP = {
notion: <AppIcon innerIcon={NotionSvg} className='!border-[0.5px] !border-indigo-100 !bg-white' />,
}
export default function AppBasic({ icon, icon_background, name, type, hoverTip, textStyle, mode = 'expand', iconType = 'app', isExtraInLine }: IAppBasicProps) {
export default function AppBasic({ icon, icon_background, name, type, hoverTip, textStyle, mode = 'expand', iconType = 'app' }: IAppBasicProps) {
return (
<div className="flex items-start">
{icon && icon_background && iconType === 'app' && (

View File

@ -1,8 +1,12 @@
import React from 'react'
import React, { useCallback, useState } from 'react'
import NavLink from './navLink'
import type { NavIcon } from './navLink'
import AppBasic from './basic'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import {
AlignLeft01,
AlignRight01,
} from '@/app/components/base/icons/src/vender/line/layout'
export type IAppDetailNavProps = {
iconType?: 'app' | 'dataset' | 'notion'
@ -20,23 +24,77 @@ export type IAppDetailNavProps = {
}
const AppDetailNav = ({ title, desc, icon, icon_background, navigation, extraInfo, iconType = 'app' }: IAppDetailNavProps) => {
const localeMode = localStorage.getItem('app-detail-collapse-or-expand') || 'expand'
const media = useBreakpoints()
const isMobile = media === MediaType.mobile
const mode = isMobile ? 'collapse' : 'expand'
const [modeState, setModeState] = useState(isMobile ? mode : localeMode)
const expand = modeState === 'expand'
const handleToggle = useCallback(() => {
setModeState((prev) => {
const next = prev === 'expand' ? 'collapse' : 'expand'
localStorage.setItem('app-detail-collapse-or-expand', next)
return next
})
}, [])
return (
<div className="flex flex-col sm:w-56 w-16 overflow-y-auto bg-white border-r border-gray-200 shrink-0 mobile:h-screen">
<div className="flex flex-shrink-0 p-4">
<AppBasic mode={mode} iconType={iconType} icon={icon} icon_background={icon_background} name={title} type={desc} />
<div
className={`
shrink-0 flex flex-col bg-white border-r border-gray-200 transition-all
${expand ? 'w-[216px]' : 'w-14'}
`}
>
<div
className={`
shrink-0
${expand ? 'p-4' : 'p-2'}
`}
>
<AppBasic
mode={modeState}
iconType={iconType}
icon={icon}
icon_background={icon_background}
name={title}
type={desc}
/>
</div>
<nav className="flex-1 p-4 space-y-1 bg-white">
<nav
className={`
grow space-y-1 bg-white
${expand ? 'p-4' : 'px-2.5 py-4'}
`}
>
{navigation.map((item, index) => {
return (
<NavLink key={index} mode={mode} iconMap={{ selected: item.selectedIcon, normal: item.icon }} name={item.name} href={item.href} />
<NavLink key={index} mode={modeState} iconMap={{ selected: item.selectedIcon, normal: item.icon }} name={item.name} href={item.href} />
)
})}
{extraInfo ?? null}
</nav>
{
!isMobile && (
<div
className={`
shrink-0 py-3
${expand ? 'px-6' : 'px-4'}
`}
>
<div
className='flex items-center justify-center w-6 h-6 text-gray-500 cursor-pointer'
onClick={handleToggle}
>
{
expand
? <AlignLeft01 className='w-[14px] h-[14px]' />
: <AlignRight01 className='w-[14px] h-[14px]' />
}
</div>
</div>
)
}
</div>
)
}

View File

@ -18,7 +18,7 @@ export type NavLinkProps = {
selected: NavIcon
normal: NavIcon
}
mode?: 'expand' | 'collapse'
mode?: string
}
export default function NavLink({
@ -45,13 +45,15 @@ export default function NavLink({
href={href}
className={classNames(
isActive ? 'bg-primary-50 text-primary-600 font-semibold' : 'text-gray-700 hover:bg-gray-100 hover:text-gray-700',
'group flex items-center rounded-md px-2 py-2 text-sm font-normal',
'group flex items-center h-9 rounded-md py-2 text-sm font-normal',
mode === 'expand' ? 'px-3' : 'px-2.5',
)}
>
<NavIcon
className={classNames(
'mr-2 h-4 w-4 flex-shrink-0',
'h-4 w-4 flex-shrink-0',
isActive ? 'text-primary-600' : 'text-gray-700',
mode === 'expand' ? 'mr-2' : 'mr-0',
)}
aria-hidden="true"
/>

View File

@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="align-left-01">
<path id="Icon" d="M3 3V21M21 12H7M7 12L14 19M7 12L14 5" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 269 B

View File

@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="align-right-01">
<path id="Icon" d="M21 21V3M3 12H17M17 12L10 5M17 12L10 19" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 273 B

View File

@ -0,0 +1,39 @@
{
"icon": {
"type": "element",
"isRootNode": true,
"name": "svg",
"attributes": {
"width": "24",
"height": "24",
"viewBox": "0 0 24 24",
"fill": "none",
"xmlns": "http://www.w3.org/2000/svg"
},
"children": [
{
"type": "element",
"name": "g",
"attributes": {
"id": "align-left-01"
},
"children": [
{
"type": "element",
"name": "path",
"attributes": {
"id": "Icon",
"d": "M3 3V21M21 12H7M7 12L14 19M7 12L14 5",
"stroke": "currentColor",
"stroke-width": "2",
"stroke-linecap": "round",
"stroke-linejoin": "round"
},
"children": []
}
]
}
]
},
"name": "AlignLeft01"
}

View File

@ -0,0 +1,16 @@
// GENERATE BY script
// DON NOT EDIT IT MANUALLY
import * as React from 'react'
import data from './AlignLeft01.json'
import IconBase from '@/app/components/base/icons/IconBase'
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
props,
ref,
) => <IconBase {...props} ref={ref} data={data as IconData} />)
Icon.displayName = 'AlignLeft01'
export default Icon

View File

@ -0,0 +1,39 @@
{
"icon": {
"type": "element",
"isRootNode": true,
"name": "svg",
"attributes": {
"width": "24",
"height": "24",
"viewBox": "0 0 24 24",
"fill": "none",
"xmlns": "http://www.w3.org/2000/svg"
},
"children": [
{
"type": "element",
"name": "g",
"attributes": {
"id": "align-right-01"
},
"children": [
{
"type": "element",
"name": "path",
"attributes": {
"id": "Icon",
"d": "M21 21V3M3 12H17M17 12L10 5M17 12L10 19",
"stroke": "currentColor",
"stroke-width": "2",
"stroke-linecap": "round",
"stroke-linejoin": "round"
},
"children": []
}
]
}
]
},
"name": "AlignRight01"
}

View File

@ -0,0 +1,16 @@
// GENERATE BY script
// DON NOT EDIT IT MANUALLY
import * as React from 'react'
import data from './AlignRight01.json'
import IconBase from '@/app/components/base/icons/IconBase'
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
props,
ref,
) => <IconBase {...props} ref={ref} data={data as IconData} />)
Icon.displayName = 'AlignRight01'
export default Icon

View File

@ -1 +1,3 @@
export { default as AlignLeft01 } from './AlignLeft01'
export { default as AlignRight01 } from './AlignRight01'
export { default as Grid01 } from './Grid01'