1--- 2import { Info, TriangleAlert, Skull } from "@lucide/astro"; 3 4interface Props { 5 id?: string; 6 label: string; 7 icon?: "info" | "warning" | "danger"; 8 title?: string; 9 class?: string; 10} 11 12const { id, label, icon, title, class: className, ...rest } = Astro.props; 13--- 14<!-- type button needs to be set here, otherwise it doesn't work inside forms --> 15<button 16 type="button" 17 id={`${id}-trigger`} 18 class:list={[ 19 "btn btn-xs", 20 icon && ["btn-circle", "btn-ghost"], 21 icon && 22 (icon === "info") ? "text-info" : 23 (icon === "warning") ? "text-warning" : 24 (icon === "danger") ? "text-error" : 25 "text-base-content", 26 "popover-btn" 27 ]} 28 aria-describedby={id} 29 popovertarget={id} 30> 31 {icon 32 ? 33 <div class="icon" aria-label={label}> 34 {icon && 35 (icon === "info") ? <Info /> : 36 (icon === "warning") ? <TriangleAlert /> : 37 (icon === "danger") ? <Skull /> : 38 <></> 39 } 40 </div> 41 : <span>{label}</span> 42 } 43</button> 44 45<div 46 {id} 47 class:list={[ 48 "popover-content", 49 className, 50 ]} 51 role="tooltip" 52 popover="auto" 53 {...rest} 54> 55 <div class="card-body"> 56 {title && ( 57 <h3 class="card-title">{title}</h3> 58 )} 59 60 <slot /> 61 </div> 62</div> 63 64<style define:vars={{ anchor: `--${id}-anchor` }}> 65 @reference "../assets/styles/global.css"; 66 67 .popover-btn { 68 @supports (anchor-name: var(--anchor)) { 69 anchor-name: var(--anchor); 70 } 71 } 72 73 .popover-content { 74 @apply dropdown card mx-0 inset-auto bg-base-100 w-72 shadow; 75 76 @supports (position-anchor: var(--anchor)) and (left: anchor(center)) { 77 position-anchor: var(--anchor); 78 left: anchor(center); 79 transform: translateX(-50%); 80 } 81 } 82</style> 83 84<script> 85 import { computePosition, autoUpdate, shift, flip } from "@floating-ui/dom"; 86 const triggers = document.querySelectorAll(".popover-btn"); 87 88 triggers.forEach(trigger => { 89 const btn = trigger as HTMLButtonElement; 90 // triggering button will always end with "-trigger" 91 // so slice that from the id 92 const id = btn.id.slice(0, -8); 93 const popover = document.getElementById(`${id}`) as HTMLElement; 94 95 btn.addEventListener("click", (e) => { 96 e.preventDefault(); 97 popover.togglePopover(); 98 }); 99 100 popover.addEventListener("toggle", (e) => { 101 const cleanup = autoUpdate( 102 btn, 103 popover, 104 () => { 105 computePosition(btn, popover, { 106 middleware: [ 107 flip(), 108 shift({ 109 crossAxis: false, 110 }), 111 ], 112 }).then(({ placement, middlewareData }) => { 113 Object.assign(popover.style, { 114 top: `anchor(${placement})`, 115 ...(placement === "top") && { 116 transform: (middlewareData.shift?.enabled.x) 117 ? `translate(calc(-50% + ${middlewareData.shift.x}px), -100%)` 118 : `translate(-50%, -100%)`, 119 }, 120 }); 121 }); 122 }); 123 if (e.newState === "open") { 124 cleanup; 125 } else { 126 cleanup(); 127 } 128 }); 129 }); 130</script>