atproto explorer pdsls.dev
atproto tool
at v1.0.0 4.1 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: { content: string; label: string; icon?: string }) => { 28 const ctx = useContext(MenuContext); 29 30 return ( 31 <button 32 onClick={() => { 33 addToClipboard(props.content); 34 ctx?.setShowMenu(false); 35 }} 36 class="flex items-center gap-1.5 rounded-md 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: { 47 href: string; 48 label: string; 49 icon?: string; 50 newTab?: boolean; 51 external?: boolean; 52}) => { 53 const ctx = useContext(MenuContext); 54 55 return ( 56 <A 57 href={props.href} 58 onClick={() => ctx?.setShowMenu(false)} 59 class="flex items-center gap-1.5 rounded-md p-1 hover:bg-neutral-200/50 active:bg-neutral-200 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 60 classList={{ "justify-between": props.external }} 61 target={props.newTab ? "_blank" : undefined} 62 > 63 <Show when={props.icon}> 64 <span class={"iconify shrink-0 " + props.icon}></span> 65 </Show> 66 <span class="whitespace-nowrap">{props.label}</span> 67 <Show when={props.external}> 68 <span class="iconify lucide--external-link"></span> 69 </Show> 70 </A> 71 ); 72}; 73 74export const ActionMenu = (props: { 75 label: string; 76 icon: string; 77 onClick: JSX.EventHandlerUnion<HTMLButtonElement, MouseEvent>; 78}) => { 79 return ( 80 <button 81 onClick={props.onClick} 82 class="flex items-center gap-1.5 rounded-md p-1 whitespace-nowrap hover:bg-neutral-200/50 active:bg-neutral-200 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 83 > 84 <Show when={props.icon}> 85 <span class={"iconify shrink-0 " + props.icon}></span> 86 </Show> 87 <span class="whitespace-nowrap">{props.label}</span> 88 </button> 89 ); 90}; 91 92export const MenuSeparator = () => { 93 return <div class="my-1 h-[0.5px] bg-neutral-300 dark:bg-neutral-600" />; 94}; 95 96export const DropdownMenu = (props: { 97 icon: string; 98 buttonClass?: string; 99 menuClass?: string; 100 children?: JSX.Element; 101}) => { 102 const ctx = useContext(MenuContext); 103 const [menu, setMenu] = createSignal<HTMLDivElement>(); 104 const [menuButton, setMenuButton] = createSignal<HTMLButtonElement>(); 105 106 const clickEvent = (event: MouseEvent) => { 107 const target = event.target as Node; 108 if (!menuButton()?.contains(target) && !menu()?.contains(target)) ctx?.setShowMenu(false); 109 }; 110 111 onMount(() => window.addEventListener("click", clickEvent)); 112 onCleanup(() => window.removeEventListener("click", clickEvent)); 113 114 return ( 115 <div class="relative"> 116 <button 117 class={ 118 "flex items-center hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600 " + 119 props.buttonClass 120 } 121 ref={setMenuButton} 122 onClick={() => ctx?.setShowMenu(!ctx?.showMenu())} 123 > 124 <span class={"iconify " + props.icon}></span> 125 </button> 126 <Show when={ctx?.showMenu()}> 127 <div 128 ref={setMenu} 129 class={ 130 "dark:bg-dark-300 dark:shadow-dark-700 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 " + 131 props.menuClass 132 } 133 > 134 {props.children} 135 </div> 136 </Show> 137 </div> 138 ); 139};