forked from pdsls.dev/pdsls
atproto explorer
at main 3.8 kB view raw
1import { A } from "@solidjs/router"; 2import { 3 Accessor, 4 createContext, 5 createSignal, 6 JSX, 7 onCleanup, 8 onMount, 9 Setter, 10 Show, 11 useContext, 12} from "solid-js"; 13import { addToClipboard } from "../utils/copy"; 14 15const MenuContext = createContext<{ 16 showMenu: Accessor<boolean>; 17 setShowMenu: Setter<boolean>; 18}>(); 19 20export const MenuProvider = (props: { children?: JSX.Element }) => { 21 const [showMenu, setShowMenu] = createSignal(false); 22 const value = { showMenu, setShowMenu }; 23 24 return <MenuContext.Provider value={value}>{props.children}</MenuContext.Provider>; 25}; 26 27export const CopyMenu = (props: { copyContent: string; label: string; icon?: string }) => { 28 const ctx = useContext(MenuContext); 29 30 return ( 31 <button 32 onClick={() => { 33 addToClipboard(props.copyContent); 34 ctx?.setShowMenu(false); 35 }} 36 class="flex items-center gap-1.5 rounded-lg p-1 whitespace-nowrap hover:bg-neutral-200/50 active:bg-neutral-200 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 37 > 38 <Show when={props.icon}> 39 <span class={"iconify shrink-0 " + props.icon}></span> 40 </Show> 41 <span class="whitespace-nowrap">{props.label}</span> 42 </button> 43 ); 44}; 45 46export const NavMenu = (props: { href: string; label: string; icon: string; newTab?: boolean }) => { 47 const ctx = useContext(MenuContext); 48 49 return ( 50 <A 51 href={props.href} 52 onClick={() => ctx?.setShowMenu(false)} 53 class="flex items-center gap-1.5 rounded-lg p-1 hover:bg-neutral-200/50 active:bg-neutral-200 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 54 target={props.newTab ? "_blank" : undefined} 55 > 56 <span class={"iconify shrink-0 " + props.icon}></span> 57 <span class="whitespace-nowrap">{props.label}</span> 58 </A> 59 ); 60}; 61 62export const ActionMenu = (props: { 63 label: string; 64 icon: string; 65 onClick: JSX.EventHandlerUnion<HTMLButtonElement, MouseEvent>; 66}) => { 67 return ( 68 <button 69 onClick={props.onClick} 70 class="flex items-center gap-1.5 rounded-lg p-1 whitespace-nowrap hover:bg-neutral-200/50 active:bg-neutral-200 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 71 > 72 <Show when={props.icon}> 73 <span class={"iconify shrink-0 " + props.icon}></span> 74 </Show> 75 <span class="whitespace-nowrap">{props.label}</span> 76 </button> 77 ); 78}; 79 80export const DropdownMenu = (props: { 81 icon: string; 82 buttonClass?: string; 83 menuClass?: string; 84 children?: JSX.Element; 85}) => { 86 const ctx = useContext(MenuContext); 87 const [menu, setMenu] = createSignal<HTMLDivElement>(); 88 const [menuButton, setMenuButton] = createSignal<HTMLButtonElement>(); 89 90 const clickEvent = (event: MouseEvent) => { 91 const target = event.target as Node; 92 if (!menuButton()?.contains(target) && !menu()?.contains(target)) ctx?.setShowMenu(false); 93 }; 94 95 onMount(() => window.addEventListener("click", clickEvent)); 96 onCleanup(() => window.removeEventListener("click", clickEvent)); 97 98 return ( 99 <div class="relative"> 100 <button 101 class={ 102 "flex items-center hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600 " + 103 props.buttonClass 104 } 105 ref={setMenuButton} 106 onClick={() => ctx?.setShowMenu(!ctx?.showMenu())} 107 > 108 <span class={"iconify " + props.icon}></span> 109 </button> 110 <Show when={ctx?.showMenu()}> 111 <div 112 ref={setMenu} 113 class={ 114 "dark:bg-dark-300 dark:shadow-dark-800 absolute right-0 z-40 flex flex-col rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 shadow-md dark:border-neutral-700 " + 115 props.menuClass 116 } 117 > 118 {props.children} 119 </div> 120 </Show> 121 </div> 122 ); 123};