11import { useEffect , useRef , useState } from "react" ;
22import "./BorderedAppMenu.scss" ;
3- import useSystemSettings from "../../../stores/systemSettingsStore" ;
43import useDetectClickOutside from "../../../hooks/useDetectClickOutside" ;
5-
6- export interface BorderedAppMenuItemProps {
7- title : string ;
8- action ?: ( ) => void ;
9- items ?: Array < BorderedAppMenuItemProps > ;
10- }
4+ import MenuItems , { MenuItemProps } from "../../MenuItems" ;
115
126export interface BorderedAppMenuProps {
137 title : string ;
14- items : Array < BorderedAppMenuItemProps > ;
15- }
16-
17- function BorderedAppMenuItem ( {
18- title,
19- action,
20- items,
21- itemNo,
22- position,
23- } : BorderedAppMenuItemProps & {
24- itemNo : number ;
25- position : { x : number ; y : number } ;
26- } ) {
27- const hoverDelayMs = 400 ;
28- const elementRef = useRef < HTMLDivElement > ( null ) ;
29- const elementPosition = useRef ( { ...position } ) ;
30- const hoverOpenDelayRef = useRef < NodeJS . Timeout > ( ) ;
31- const hoverCloseDelayRef = useRef < NodeJS . Timeout > ( ) ;
32- const [ open , setOpen ] = useState < boolean > ( false ) ;
33-
34- useEffect ( ( ) => {
35- return ( ) => {
36- clearTimeout ( hoverCloseDelayRef . current ) ;
37- clearTimeout ( hoverOpenDelayRef . current ) ;
38- } ;
39- } , [ ] ) ;
40-
41- useEffect ( ( ) => {
42- const rect = elementRef . current ?. getBoundingClientRect ( ) ;
43- elementPosition . current . x = rect ?. width ?? 0 ;
44- elementPosition . current . y = ( rect ?. height ?? 0 ) * ( itemNo - 1 ) ;
45- } , [ elementRef , itemNo , position ] ) ;
46-
47- function handleOnMouseEnter ( ) {
48- // Now that we've entered the item, if it's open
49- // and there's a pending timeout to close it,
50- // we want to cancel that
51- if ( open && hoverCloseDelayRef . current ) {
52- clearTimeout ( hoverCloseDelayRef . current ) ;
53- hoverCloseDelayRef . current = undefined ;
54- }
55-
56- // If it's not open yet and there's no pending
57- // timeout to open it, lets start one.
58- if ( ! open && ! hoverOpenDelayRef . current ) {
59- hoverOpenDelayRef . current = setTimeout ( ( ) => {
60- if ( ! open ) {
61- setOpen ( true ) ;
62- hoverOpenDelayRef . current = undefined ;
63- }
64- } , hoverDelayMs ) ;
65- }
66- }
67- function handleOnMouseLeave ( ) {
68- // Now that we've left the item, if it's closed
69- // and there's a pending timeout to open it,
70- // we want to cancel that
71- if ( ! open && hoverOpenDelayRef . current ) {
72- clearTimeout ( hoverOpenDelayRef . current ) ;
73- hoverOpenDelayRef . current = undefined ;
74- }
75-
76- // If it's not closed yet and there's no pending
77- // timeout to close it, lets start one.
78- if ( open && ! hoverCloseDelayRef . current ) {
79- hoverCloseDelayRef . current = setTimeout ( ( ) => {
80- if ( open ) {
81- setOpen ( false ) ;
82- hoverCloseDelayRef . current = undefined ;
83- }
84- } , hoverDelayMs ) ;
85- }
86- }
87-
88- function handleOnClick ( e : React . MouseEvent < HTMLDivElement > ) {
89- e . stopPropagation ( ) ;
90- if ( ! items ) return ;
91- setOpen ( ! open ) ;
92- }
93-
94- return (
95- < div
96- ref = { elementRef }
97- className = "bordered-app-menu-item"
98- onClick = { handleOnClick }
99- onMouseEnter = { handleOnMouseEnter }
100- onMouseLeave = { handleOnMouseLeave }
101- >
102- < span className = "bordered-app-menu-item__title" onClick = { action } >
103- { title }
104- </ span >
105- { items && open && (
106- < BorderedAppMenuItems
107- items = { items }
108- position = { { ...elementPosition . current } }
109- />
110- ) }
111- </ div >
112- ) ;
113- }
114-
115- function BorderedAppMenuItems ( {
116- items,
117- position,
118- } : {
119- items : Array < BorderedAppMenuItemProps > ;
120- position : { x : number ; y : number } ;
121- } ) {
122- const settings = useSystemSettings ( ) ;
123- return (
124- < div
125- className = "bordered-app-menu-items"
126- style = { {
127- left : position . x ,
128- top : position . y ,
129- backgroundColor : settings . mainColor ,
130- } }
131- >
132- < div className = "bordered-app-menu-items__content" >
133- { items . map ( ( item , i ) => (
134- < BorderedAppMenuItem
135- title = { item . title }
136- action = { item . action }
137- items = { item . items }
138- itemNo = { i + 1 }
139- position = { { ...position } }
140- key = { `${ item . title } -${ i } ` }
141- />
142- ) ) }
143- </ div >
144- </ div >
145- ) ;
8+ items : Array < MenuItemProps > ;
1469}
14710
14811function BorderedAppMenu ( { title, items } : BorderedAppMenuProps ) {
@@ -166,12 +29,7 @@ function BorderedAppMenu({ title, items }: BorderedAppMenuProps) {
16629 onClick = { ( ) => setOpen ( ! open ) }
16730 >
16831 < span className = "bordered-app-menu__title" > { title } </ span >
169- { open && (
170- < BorderedAppMenuItems
171- items = { items }
172- position = { { ...position . current } }
173- />
174- ) }
32+ { open && < MenuItems items = { items } position = { { ...position . current } } /> }
17533 </ div >
17634 ) ;
17735}
0 commit comments