2
2
'use client' ;
3
3
4
4
import type { Dispatch , SetStateAction } from 'react' ;
5
- import { useState } from 'react' ;
5
+ import { useState , useEffect , useRef } from 'react' ;
6
6
import IconLink from './iconlink' ;
7
7
import SortSelect from './sort-select' ;
8
8
9
9
// Types matching those in Header/Page
10
10
import { SortOption } from './sort-types' ;
11
+
11
12
interface ProfileLink {
12
13
href : string ;
13
14
label : string ;
@@ -24,12 +25,51 @@ interface MoreMenuProps {
24
25
25
26
export default function MoreMenu ( { profileLinks, sort, setSort, query, setQuery } : MoreMenuProps ) {
26
27
const [ open , setOpen ] = useState ( false ) ;
28
+ const menuRef = useRef < HTMLDivElement > ( null ) ;
29
+
30
+ // Close menu when scrolling
31
+ useEffect ( ( ) => {
32
+ function handleScroll ( ) {
33
+ if ( open ) {
34
+ setOpen ( false ) ;
35
+ }
36
+ }
37
+
38
+ // Add scroll event listener
39
+ window . addEventListener ( 'scroll' , handleScroll , { passive : true } ) ;
40
+
41
+ // Clean up
42
+ return ( ) => {
43
+ window . removeEventListener ( 'scroll' , handleScroll ) ;
44
+ } ;
45
+ } , [ open ] ) ;
46
+
47
+ // Close menu when clicking outside
48
+ useEffect ( ( ) => {
49
+ function handleClickOutside ( event : MouseEvent ) {
50
+ if ( menuRef . current && ! menuRef . current . contains ( event . target as Node ) ) {
51
+ setOpen ( false ) ;
52
+ }
53
+ }
54
+
55
+ if ( open ) {
56
+ document . addEventListener ( 'mousedown' , handleClickOutside ) ;
57
+ }
58
+
59
+ return ( ) => {
60
+ document . removeEventListener ( 'mousedown' , handleClickOutside ) ;
61
+ } ;
62
+ } , [ open ] ) ;
27
63
28
64
return (
29
- < div className = "relative flex-shrink-0" >
65
+ < div className = "relative flex-shrink-0" ref = { menuRef } >
30
66
< button
31
- onClick = { ( ) => setOpen ( ! open ) }
32
- aria-label = "Open menu"
67
+ onClick = { ( e ) => {
68
+ e . stopPropagation ( ) ;
69
+ setOpen ( ! open ) ;
70
+ } }
71
+ aria-label = { open ? 'Close menu' : 'Open menu' }
72
+ aria-expanded = { open }
33
73
className = "p-2 rounded-md bg-neutral-800/70 hover:bg-neutral-800 focus:outline-none focus:ring-2 focus:ring-amber-500 lg:hidden"
34
74
>
35
75
< svg viewBox = "0 0 24 24" className = "w-4 h-4 fill-neutral-300" >
@@ -40,9 +80,11 @@ export default function MoreMenu({ profileLinks, sort, setSort, query, setQuery
40
80
</ button >
41
81
42
82
{ open && (
43
- < div className = "absolute right-0 mt-2 w-56 rounded-md bg-neutral-900 shadow-lg ring-1 ring-neutral-700/60 z-50" >
83
+ < div
84
+ className = "absolute right-0 mt-2 w-56 rounded-md bg-neutral-900 shadow-lg ring-1 ring-neutral-700/60 z-50"
85
+ onClick = { ( e ) => e . stopPropagation ( ) }
86
+ >
44
87
< div className = "flex flex-col p-2 gap-2 divide-y divide-neutral-700/60" >
45
-
46
88
{ /* Search Input - Shown below sm */ }
47
89
< div className = "sm:hidden px-2 pt-1 pb-2" >
48
90
< input
0 commit comments