mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-11-01 12:02:07 +08:00
Remove the negative text offset so labels remain centered within the list item text area.
131 lines
3.6 KiB
TypeScript
131 lines
3.6 KiB
TypeScript
import type {
|
|
DraggableAttributes,
|
|
DraggableSyntheticListeners,
|
|
} from '@dnd-kit/core'
|
|
import {
|
|
alpha,
|
|
ListItem,
|
|
ListItemButton,
|
|
ListItemIcon,
|
|
ListItemText,
|
|
} from '@mui/material'
|
|
import type { CSSProperties, PointerEvent, ReactNode } from 'react'
|
|
import { useCallback } from 'react'
|
|
import { useMatch, useNavigate, useResolvedPath } from 'react-router'
|
|
|
|
import { useVerge } from '@/hooks/use-verge'
|
|
|
|
interface SortableProps {
|
|
setNodeRef?: (element: HTMLElement | null) => void
|
|
attributes?: DraggableAttributes
|
|
listeners?: DraggableSyntheticListeners
|
|
style?: CSSProperties
|
|
isDragging?: boolean
|
|
disabled?: boolean
|
|
}
|
|
|
|
interface Props {
|
|
to: string
|
|
children: string
|
|
icon: ReactNode[]
|
|
sortable?: SortableProps
|
|
}
|
|
export const LayoutItem = (props: Props) => {
|
|
const { to, children, icon, sortable } = props
|
|
const { verge } = useVerge()
|
|
const { menu_icon } = verge ?? {}
|
|
const navCollapsed = verge?.collapse_navbar ?? false
|
|
const resolved = useResolvedPath(to)
|
|
const match = useMatch({ path: resolved.pathname, end: true })
|
|
const navigate = useNavigate()
|
|
|
|
const effectiveMenuIcon =
|
|
navCollapsed && menu_icon === 'disable' ? 'monochrome' : menu_icon
|
|
|
|
const { setNodeRef, attributes, listeners, style, isDragging, disabled } =
|
|
sortable ?? {}
|
|
|
|
const draggable = Boolean(sortable) && !disabled
|
|
const { onPointerDown, ...otherListeners } = draggable
|
|
? (listeners ?? {})
|
|
: {}
|
|
|
|
const handlePointerDown = useCallback(
|
|
(event: PointerEvent<HTMLDivElement>) => {
|
|
onPointerDown?.(event)
|
|
},
|
|
[onPointerDown],
|
|
)
|
|
|
|
return (
|
|
<ListItem
|
|
ref={setNodeRef}
|
|
style={style}
|
|
sx={[
|
|
{ py: 0.5, maxWidth: 250, mx: 'auto', padding: '4px 0px' },
|
|
isDragging ? { opacity: 0.78 } : {},
|
|
]}
|
|
>
|
|
<ListItemButton
|
|
selected={!!match}
|
|
{...(draggable ? (attributes ?? {}) : {})}
|
|
{...(draggable ? otherListeners : {})}
|
|
sx={[
|
|
{
|
|
borderRadius: 2,
|
|
marginLeft: 1.25,
|
|
paddingLeft: 1,
|
|
paddingRight: 1,
|
|
marginRight: 1.25,
|
|
cursor: draggable ? 'grab' : 'pointer',
|
|
'&:active': draggable ? { cursor: 'grabbing' } : {},
|
|
'& .MuiListItemText-primary': {
|
|
color: 'text.primary',
|
|
fontWeight: '700',
|
|
},
|
|
},
|
|
({ palette: { mode, primary } }) => {
|
|
const bgcolor =
|
|
mode === 'light'
|
|
? alpha(primary.main, 0.15)
|
|
: alpha(primary.main, 0.35)
|
|
const color = mode === 'light' ? '#1f1f1f' : '#ffffff'
|
|
return {
|
|
'&.Mui-selected': { bgcolor },
|
|
'&.Mui-selected:hover': { bgcolor },
|
|
'&.Mui-selected .MuiListItemText-primary': { color },
|
|
}
|
|
},
|
|
]}
|
|
title={navCollapsed ? children : undefined}
|
|
aria-label={navCollapsed ? children : undefined}
|
|
onPointerDown={handlePointerDown}
|
|
onClick={() => navigate(to)}
|
|
>
|
|
{(effectiveMenuIcon === 'monochrome' || !effectiveMenuIcon) && (
|
|
<ListItemIcon
|
|
sx={{
|
|
color: 'text.primary',
|
|
marginLeft: '6px',
|
|
cursor: draggable ? 'grab' : 'inherit',
|
|
}}
|
|
>
|
|
{icon[0]}
|
|
</ListItemIcon>
|
|
)}
|
|
{effectiveMenuIcon === 'colorful' && (
|
|
<ListItemIcon sx={{ cursor: draggable ? 'grab' : 'inherit' }}>
|
|
{icon[1]}
|
|
</ListItemIcon>
|
|
)}
|
|
<ListItemText
|
|
sx={{
|
|
textAlign: 'center',
|
|
}}
|
|
primary={children}
|
|
/>
|
|
</ListItemButton>
|
|
</ListItem>
|
|
)
|
|
}
|