mirror of
https://github.com/langgenius/dify.git
synced 2024-11-16 11:42:29 +08:00
feat: app sidebar support collapse (#1997)
This commit is contained in:
parent
eed5fdd768
commit
1372bf784f
|
@ -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' && (
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
/>
|
||||
|
|
|
@ -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 |
|
@ -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 |
|
@ -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"
|
||||
}
|
|
@ -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
|
|
@ -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"
|
||||
}
|
|
@ -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
|
|
@ -1 +1,3 @@
|
|||
export { default as AlignLeft01 } from './AlignLeft01'
|
||||
export { default as AlignRight01 } from './AlignRight01'
|
||||
export { default as Grid01 } from './Grid01'
|
||||
|
|
Loading…
Reference in New Issue
Block a user