@@ -27,6 +27,7 @@ import { HomeDraggableLayer } from '@/app/(home)/home-draggable-layer'
2727import { useSiteAssetUrl } from '@/hooks/use-site-assets'
2828
2929const list = [
30+
3031 {
3132 icon : ScrollOutlineSVG ,
3233 iconActive : ScrollFilledSVG ,
@@ -68,6 +69,11 @@ const list = [
6869const extraSize = 5
6970const APPS_ICON_SCALE = 1.5
7071const DEFAULT_ICON_SIZE_CLASS = 'h-7 w-7'
72+ const NAV_HIGHLIGHT_TRANSITION = {
73+ type : 'spring' as const ,
74+ stiffness : 400 ,
75+ damping : 30
76+ }
7177
7278const AVATAR_URL = '/api/site-assets/avatar'
7379
@@ -79,13 +85,12 @@ export default function NavCard() {
7985 const [ hoveredIndex , setHoveredIndex ] = useState < number > ( 0 )
8086 const [ hoverRect , setHoverRect ] = useState < { centerX : number ; top : number ; width : number ; height : number } | null > ( null )
8187 const itemRefs = useRef < Array < HTMLAnchorElement | null > > ( [ ] )
88+ const iconContainerRef = useRef < HTMLDivElement | null > ( null )
8289 const { siteContent, cardStyles } = useConfigStore ( )
8390 const avatarUrl = useSiteAssetUrl ( 'avatar' )
8491 const styles = cardStyles . navCard
8592 const hiCardStyles = cardStyles . hiCard
8693
87- const hoverLayoutId = useMemo ( ( ) => `nav-hover-${ pathname === '/' ? 'home' : pathname . replace ( / [ ^ a - z 0 - 9 _ - ] / gi, '_' ) } ` , [ pathname ] )
88-
8994 const activeIndex = useMemo ( ( ) => {
9095 const index = list . findIndex ( item => pathname === item . href )
9196 return index >= 0 ? index : undefined
@@ -135,18 +140,22 @@ export default function NavCard() {
135140 }
136141
137142 const activeItem = itemRefs . current [ hoveredIndex ]
138- if ( ! activeItem ) {
143+ const container = iconContainerRef . current
144+ if ( ! activeItem || ! container ) {
139145 setHoverRect ( null )
140146 return
141147 }
142148
149+ const itemRect = activeItem . getBoundingClientRect ( )
150+ const containerRect = container . getBoundingClientRect ( )
151+
143152 setHoverRect ( {
144- centerX : activeItem . offsetLeft + activeItem . offsetWidth / 2 ,
145- top : activeItem . offsetTop ,
146- width : activeItem . offsetWidth ,
147- height : activeItem . offsetHeight
153+ centerX : itemRect . left - containerRect . left + itemRect . width / 2 ,
154+ top : itemRect . top - containerRect . top ,
155+ width : itemRect . width ,
156+ height : itemRect . height
148157 } )
149- } , [ form , hoveredIndex , maxSM , maxXS , pathname ] )
158+ } , [ form , hoveredIndex , maxSM , maxXS , pathname , size . width ] )
150159
151160
152161 if ( maxSM ) position = { x : center . x - size . width / 2 , y : 16 }
@@ -183,14 +192,14 @@ export default function NavCard() {
183192 { form !== 'icons' && < div className = 'text-secondary mt-6 text-sm uppercase' > General</ div > }
184193
185194 < div
195+ ref = { iconContainerRef }
186196 className = { cn ( 'relative mt-2 space-y-2' , form === 'icons' && 'mt-0 flex min-w-0 flex-1 items-center justify-between gap-2 space-y-0 overflow-visible sm:gap-4' ) }
187197 onMouseLeave = { ( ) => {
188198 if ( form === 'icons' ) setHoveredIndex ( activeIndex ?? 0 )
189199 } } >
190200 { ( form !== 'icons' || hoverRect ) && (
191201 < motion . div
192202 className = 'pointer-events-none absolute max-w-[230px] rounded-full border'
193- layoutId = { hoverLayoutId }
194203 initial = { false }
195204 animate = {
196205 form === 'icons'
@@ -202,11 +211,7 @@ export default function NavCard() {
202211 }
203212 : { top : hoveredIndex * ( itemHeight + 8 ) , left : 0 , width : '100%' , height : itemHeight }
204213 }
205- transition = { {
206- type : 'spring' ,
207- stiffness : 400 ,
208- damping : 30
209- } }
214+ transition = { NAV_HIGHLIGHT_TRANSITION }
210215 style = { { backgroundImage : 'linear-gradient(to right bottom, var(--color-border) 60%, var(--color-card) 100%)' } }
211216 />
212217 ) }
0 commit comments