1- import { Accessor , Show } from "solid-js" ;
1+ import { Accessor , createSignal , For , onCleanup , Show } from "solid-js" ;
2+ import MenuIcon from "@/components/icon/MenuIcon" ;
23
34export interface NavbarProps {
45 tabDefs : NavbarButtonDef [ ] ;
56 activeTabKey : Accessor < string > ;
67 titleDef : NavbarTitleDef | null ;
8+ menuDef ?: NavbarMenuDef ;
79}
810interface NavbarTitleDef {
911 label : string ;
@@ -12,30 +14,83 @@ interface NavbarButtonDef {
1214 key : string ;
1315 label : string ;
1416 onClick : ( ) => void ;
15- } ;
17+ }
18+ interface NavbarMenuDef {
19+ items : NavbarMenuItem [ ] ;
20+ }
21+ interface NavbarMenuItem {
22+ key : string ;
23+ label : string ;
24+ onClick : ( ) => void ;
25+ }
26+
27+ const BdtNavbar = ( { navProps } : { navProps : Accessor < NavbarProps > } ) => {
28+ const [ menuOpen , setMenuOpen ] = createSignal ( false ) ;
29+
30+ const handleOutsideClick = ( e : MouseEvent ) => {
31+ const target = e . target as HTMLElement ;
32+ if ( ! target . closest ( "[data-navbar-menu]" ) ) {
33+ setMenuOpen ( false ) ;
34+ }
35+ } ;
36+
37+ document . addEventListener ( "click" , handleOutsideClick ) ;
38+ onCleanup ( ( ) => document . removeEventListener ( "click" , handleOutsideClick ) ) ;
1639
17- const BdtNavbar = ( { navProps} : { navProps : Accessor < NavbarProps > } ) => {
1840 return (
1941 < div class = "flex border-b border-gray-300" >
2042 < Show when = { navProps ( ) . titleDef !== null } >
2143 < div class = "py-2 px-4 font-bold text-gray-700 cursor-default" >
2244 { navProps ( ) . titleDef ! . label }
2345 </ div >
2446 </ Show >
25- { navProps ( ) . tabDefs . map ( ( tab ) => (
26- < button
27- class = { `px-4 py-2 text-sm font-medium border-b-2 transition-colors ${
28- navProps ( ) . activeTabKey ( ) === tab . key
29- ? "border-b border-gray-700 text-gray-700 hover:bg-gray-200"
30- : "border-transparent text-gray-500 hover:text-gray-700 hover:bg-gray-200"
31- } `}
32- onClick = { tab . onClick }
33- >
34- { tab . label . charAt ( 0 ) . toUpperCase ( ) + tab . label . slice ( 1 ) }
35- </ button >
36- ) ) }
47+ < For each = { navProps ( ) . tabDefs } >
48+ { ( tab ) => (
49+ < button
50+ class = { `px-4 py-2 text-sm font-medium border-b-2 transition-colors ${
51+ navProps ( ) . activeTabKey ( ) === tab . key
52+ ? "border-b border-gray-700 text-gray-700 hover:bg-gray-200"
53+ : "border-transparent text-gray-500 hover:text-gray-700 hover:bg-gray-200"
54+ } `}
55+ onClick = { tab . onClick }
56+ >
57+ { tab . label . charAt ( 0 ) . toUpperCase ( ) + tab . label . slice ( 1 ) }
58+ </ button >
59+ ) }
60+ </ For >
61+ < Show when = { navProps ( ) . menuDef !== undefined } >
62+ < div class = "relative ml-auto" data-navbar-menu >
63+ < button
64+ class = "px-3 py-2 text-gray-500 hover:text-gray-700 hover:bg-gray-200 rounded transition-colors"
65+ onClick = { ( e ) => {
66+ e . stopPropagation ( ) ;
67+ setMenuOpen ( ( o ) => ! o ) ;
68+ } }
69+ aria-label = "More options"
70+ >
71+ < MenuIcon />
72+ </ button >
73+ < Show when = { menuOpen ( ) } >
74+ < div class = "absolute right-0 top-full mt-1 w-48 bg-white border border-gray-200 rounded shadow-md z-10" >
75+ < For each = { navProps ( ) . menuDef ! . items } >
76+ { ( item ) => (
77+ < button
78+ class = "w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 transition-colors"
79+ onClick = { ( ) => {
80+ item . onClick ( ) ;
81+ setMenuOpen ( false ) ;
82+ } }
83+ >
84+ { item . label }
85+ </ button >
86+ ) }
87+ </ For >
88+ </ div >
89+ </ Show >
90+ </ div >
91+ </ Show >
3792 </ div >
3893 ) ;
39- }
94+ } ;
4095
4196export default BdtNavbar ;
0 commit comments