Dimensions
Set the dimensions for the layer.
package showcase
import (
"github.com/axzilla/templui/internal/components/button"
"github.com/axzilla/templui/internal/components/input"
"github.com/axzilla/templui/internal/components/label"
"github.com/axzilla/templui/internal/components/popover"
"github.com/axzilla/templui/internal/utils"
)
templ PopoverDefault() {
@popover.Popover() {
@popover.Trigger(popover.TriggerProps{
For: "default-popover",
}) {
@button.Button(button.Props{
Variant: button.VariantOutline,
}) {
Open Popover
}
}
@popover.Content(popover.ContentProps{
ID: "default-popover",
}) {
@PopoverContent()
}
}
}
templ PopoverContent() {
{{ var id = utils.RandomID() }}
<div class="p-4 space-y-4">
<div>
<h3 class="text-lg font-semibold">Dimensions</h3>
<p>Set the dimensions for the layer.</p>
</div>
<div class="flex flex-col gap-2 max-w-fit">
<div class="flex items-center gap-2">
@label.Label(label.Props{
For: "width" + id,
Class: "w-24",
}) {
Width
}
@input.Input(input.Props{
ID: "width" + id,
Placeholder: "Width",
Value: "100%",
Class: "flex-1",
})
</div>
<div class="flex items-center gap-2">
@label.Label(label.Props{
For: "height" + id,
Class: "w-24",
}) {
Height
}
@input.Input(input.Props{
ID: "height" + id,
Placeholder: "Height",
Value: "100%",
Class: "flex-1",
})
</div>
</div>
</div>
}
Installation
templui add popover
Copy and paste the following code into your project:
package popover import ( "github.com/axzilla/templui/internal/utils" "strconv" ) type Placement string const ( PlacementTop Placement = "top" PlacementTopStart Placement = "top-start" PlacementTopEnd Placement = "top-end" PlacementRight Placement = "right" PlacementRightStart Placement = "right-start" PlacementRightEnd Placement = "right-end" PlacementBottom Placement = "bottom" PlacementBottomStart Placement = "bottom-start" PlacementBottomEnd Placement = "bottom-end" PlacementLeft Placement = "left" PlacementLeftStart Placement = "left-start" PlacementLeftEnd Placement = "left-end" ) type TriggerType string const ( TriggerTypeHover TriggerType = "hover" TriggerTypeClick TriggerType = "click" ) type Props struct { Class string } type TriggerProps struct { ID string For string TriggerType TriggerType } type ContentProps struct { ID string Class string Attributes templ.Attributes Placement Placement Offset int DisableClickAway bool DisableESC bool ShowArrow bool HoverDelay int HoverOutDelay int MatchWidth bool } templ Popover(props ...Props) { @Script() {{ var p Props }} if len(props) > 0 { {{ p = props[0] }} } <div class={ p.Class }> { children... } </div> } templ Trigger(props ...TriggerProps) { {{ var p TriggerProps }} if len(props) > 0 { {{ p = props[0] }} } if p.TriggerType == "" { {{ p.TriggerType = TriggerTypeClick }} } <span if p.ID != "" { id={ p.ID } } data-popover-trigger data-popover-for={ p.For } data-popover-type={ string(p.TriggerType) } > { children... } </span> } templ Content(props ...ContentProps) { {{ var p ContentProps }} if len(props) > 0 { {{ p = props[0] }} } if p.Placement == "" { {{ p.Placement = PlacementBottom }} } if p.Offset == 0 { if p.ShowArrow { {{ p.Offset = 8 }} } else { {{ p.Offset = 4 }} } } <div id={ p.ID } data-popover-id={ p.ID } data-popover-placement={ string(p.Placement) } data-popover-offset={ strconv.Itoa(p.Offset) } data-popover-disable-clickaway={ strconv.FormatBool(p.DisableClickAway) } data-popover-disable-esc={ strconv.FormatBool(p.DisableESC) } data-popover-show-arrow={ strconv.FormatBool(p.ShowArrow) } data-popover-hover-delay={ strconv.Itoa(p.HoverDelay) } data-popover-hover-out-delay={ strconv.Itoa(p.HoverOutDelay) } if p.MatchWidth { data-popover-match-width="true" } class={ utils.TwMerge( "bg-background rounded-lg border text-sm shadow-lg pointer-events-auto absolute z-[9999] hidden top-0 left-0", p.Class, ) } { p.Attributes... } > <div class="w-full overflow-hidden"> { children... } </div> if p.ShowArrow { <div data-popover-arrow class="absolute h-2.5 w-2.5 rotate-45 bg-background border"></div> } </div> } var handle = templ.NewOnceHandle() templ Script() { @handle.Once() { @FloatingUICore() @FloatingUIDom() <script nonce={ templ.GetNonce(ctx) }> if (typeof window.popoverState === 'undefined') { window.popoverState = new Map(); } (function() { // IIFE Start if (window.popoverSystemInitialized) return; // --- Ensure Global Portal Container --- let portalContainer = document.querySelector('[data-popover-portal-container]'); if (!portalContainer) { portalContainer = document.createElement('div'); portalContainer.setAttribute('data-popover-portal-container', ''); portalContainer.className = 'fixed inset-0 z-[9999] pointer-events-none'; document.body.appendChild(portalContainer); } // --- End Ensure Global Portal Container --- // --- Floating UI Check & Helper --- let FloatingUIDOM = null; function whenFloatingUiReady(callback, attempt = 1) { if (window.FloatingUIDOM) { FloatingUIDOM = window.FloatingUIDOM; callback(); } else if (attempt < 40) { setTimeout(() => whenFloatingUiReady(callback, attempt + 1), 50); } else { console.error("Floating UI DOM failed to load after several attempts."); } } // --- Helper Functions --- function findReferenceElement(triggerSpan) { const children = triggerSpan.children; if (children.length === 0) return triggerSpan; let bestElement = triggerSpan; let largestArea = 0; for (const child of children) { if (typeof child.getBoundingClientRect !== 'function') continue; const rect = child.getBoundingClientRect(); const area = rect.width * rect.height; if (area > largestArea) { largestArea = area; bestElement = child; } } return bestElement; } function positionArrow(arrowElement, placement, arrowData, content) { const { x: arrowX, y: arrowY } = arrowData; const staticSide = { top: 'bottom', right: 'left', bottom: 'top', left: 'right' }[placement.split('-')[0]]; Object.assign(arrowElement.style, { left: arrowX != null ? `${arrowX}px` : '', top: arrowY != null ? `${arrowY}px` : '', right: '', bottom: '', [staticSide]: '-5px' }); const popoverStyle = window.getComputedStyle(content); const popoverBorderColor = popoverStyle.borderColor; arrowElement.style.backgroundColor = popoverStyle.backgroundColor; arrowElement.style.borderTopColor = popoverBorderColor; arrowElement.style.borderRightColor = popoverBorderColor; arrowElement.style.borderBottomColor = popoverBorderColor; arrowElement.style.borderLeftColor = popoverBorderColor; switch (staticSide) { case 'top': arrowElement.style.borderBottomColor = 'transparent'; arrowElement.style.borderRightColor = 'transparent'; break; case 'bottom': arrowElement.style.borderTopColor = 'transparent'; arrowElement.style.borderLeftColor = 'transparent'; break; case 'left': arrowElement.style.borderTopColor = 'transparent'; arrowElement.style.borderRightColor = 'transparent'; break; case 'right': arrowElement.style.borderBottomColor = 'transparent'; arrowElement.style.borderLeftColor = 'transparent'; break; } } function addAnimationStyles() { if (document.getElementById('popover-animations')) return; const style = document.createElement('style'); style.id = 'popover-animations'; style.textContent = ` @keyframes popover-in { 0% { opacity: 0; transform: scale(0.95); } 100% { opacity: 1; transform: scale(1); } } @keyframes popover-out { 0% { opacity: 1; transform: scale(1); } 100% { opacity: 0; transform: scale(0.95); } } [data-popover-id].popover-animate-in { animation: popover-in 0.15s cubic-bezier(0.16, 1, 0.3, 1); } [data-popover-id].popover-animate-out { animation: popover-out 0.1s cubic-bezier(0.16, 1, 0.3, 1) forwards; } `; document.head.appendChild(style); } // --- Core Popover Logic --- function updatePosition(state) { if (!FloatingUIDOM || !state || !state.trigger || !state.content) return; const { computePosition, offset, flip, shift, arrow } = FloatingUIDOM; const referenceElement = findReferenceElement(state.trigger); const arrowElement = state.content.querySelector('[data-popover-arrow]'); const placement = state.content.dataset.popoverPlacement || 'bottom'; const offsetValue = parseInt(state.content.dataset.popoverOffset) || (arrowElement ? 8 : 4); const shouldMatchWidth = state.content.dataset.popoverMatchWidth === 'true'; const middleware = [offset(offsetValue), flip({ padding: 10 }), shift({ padding: 10 })]; if (arrowElement) middleware.push(arrow({ element: arrowElement, padding: 5 })); computePosition(referenceElement, state.content, { placement, middleware }).then(({ x, y, placement, middlewareData }) => { Object.assign(state.content.style, { left: `${x}px`, top: `${y}px` }); if (shouldMatchWidth) { const triggerWidth = referenceElement.offsetWidth; state.content.style.setProperty('--popover-trigger-width', `${triggerWidth}px`); } if (arrowElement && middlewareData.arrow) { positionArrow(arrowElement, placement, middlewareData.arrow, state.content); } }); } function addGlobalListeners(popoverId, state) { removeGlobalListeners(state); // Ensure no duplicates if (state.content.dataset.popoverDisableClickaway !== 'true') { const handler = (e) => { // Close if click is outside trigger and content if (!state.trigger.contains(e.target) && !state.content.contains(e.target)) { closePopover(popoverId); } }; // Use setTimeout to avoid capturing the click that opened the popover setTimeout(() => document.addEventListener('click', handler), 0); state.eventListeners.clickAway = handler; } if (state.content.dataset.popoverDisableEsc !== 'true') { const handler = (e) => { if (e.key === 'Escape') closePopover(popoverId); }; document.addEventListener('keydown', handler); state.eventListeners.esc = handler; } } function removeGlobalListeners(state) { if (state.eventListeners.clickAway) document.removeEventListener('click', state.eventListeners.clickAway); if (state.eventListeners.esc) document.removeEventListener('keydown', state.eventListeners.esc); state.eventListeners = {}; // Clear stored handlers } function openPopover(popoverId, trigger) { if (!FloatingUIDOM) return; const { autoUpdate } = FloatingUIDOM; const content = document.getElementById(popoverId); if (!content) return; let state = window.popoverState.get(popoverId); if (!state) { // Should be created by initTrigger, but as a fallback state = { trigger, content, isOpen: false, cleanup: null, hoverState: {}, eventListeners: {} }; window.popoverState.set(popoverId, state); } else if (state.isOpen) return; state.trigger = trigger; // Ensure trigger reference is current state.content = content; // Ensure content reference is current const portal = document.querySelector('[data-popover-portal-container]'); if (portal && content.parentNode !== portal) portal.appendChild(content); content.style.display = 'block'; content.classList.remove('popover-animate-out'); content.classList.add('popover-animate-in'); // Initial position update before autoUpdate starts updatePosition(state); if (state.cleanup) state.cleanup(); state.cleanup = autoUpdate(findReferenceElement(trigger), content, () => updatePosition(state), { animationFrame: true }); // Use animationFrame for smoother updates addGlobalListeners(popoverId, state); state.isOpen = true; } function closePopover(popoverId, immediate = false) { const state = window.popoverState.get(popoverId); if (!state || !state.isOpen) return; if (state.cleanup) { state.cleanup(); state.cleanup = null; } removeGlobalListeners(state); const content = state.content; function hideContent() { content.style.display = 'none'; content.classList.remove('popover-animate-in', 'popover-animate-out'); } if (immediate) hideContent(); else { content.classList.remove('popover-animate-in'); content.classList.add('popover-animate-out'); setTimeout(hideContent, 150); // Match animation duration } state.isOpen = false; } // Expose closePopover globally window.closePopover = closePopover; // --- Trigger Initialization & Handling --- function attachClickTrigger(trigger, popoverId) { const handler = (e) => { e.stopPropagation(); const state = window.popoverState.get(popoverId); if (state?.isOpen) closePopover(popoverId); else openPopover(popoverId, trigger); }; trigger.addEventListener('click', handler); trigger._popoverListener = handler; } function attachHoverTrigger(trigger, popoverId) { const content = document.getElementById(popoverId); if (!content) return; let state = window.popoverState.get(popoverId); if (!state) return; // State should exist from initTrigger const hoverDelay = parseInt(content.dataset.popoverHoverDelay) || 100; const hoverOutDelay = parseInt(content.dataset.popoverHoverOutDelay) || 200; const handleTriggerEnter = () => { clearTimeout(state.hoverState.leaveTimeout); state.hoverState.enterTimeout = setTimeout(() => openPopover(popoverId, trigger), hoverDelay); }; const handleTriggerLeave = (e) => { clearTimeout(state.hoverState.enterTimeout); state.hoverState.leaveTimeout = setTimeout(() => { if (!content.contains(e.relatedTarget)) closePopover(popoverId); }, hoverOutDelay); }; const handleContentEnter = () => clearTimeout(state.hoverState.leaveTimeout); const handleContentLeave = (e) => { state.hoverState.leaveTimeout = setTimeout(() => { if (!trigger.contains(e.relatedTarget)) closePopover(popoverId); }, hoverOutDelay); }; trigger.addEventListener('mouseenter', handleTriggerEnter); trigger.addEventListener('mouseleave', handleTriggerLeave); content.addEventListener('mouseenter', handleContentEnter); content.addEventListener('mouseleave', handleContentLeave); // Store handlers for cleanup trigger._popoverHoverListeners = { handleTriggerEnter, handleTriggerLeave }; content._popoverHoverListeners = { handleContentEnter, handleContentLeave }; } function initTrigger(trigger) { const popoverId = trigger.dataset.popoverFor; const content = document.getElementById(popoverId); if (!popoverId || !content) return; // Prevent re-attaching listeners to the same DOM element instance if (trigger._popoverListenerAttached) return; // Ensure state object exists if (!window.popoverState.has(popoverId)) { window.popoverState.set(popoverId, { trigger, content, isOpen: false, cleanup: null, hoverState: {}, eventListeners: {} }); } else { // Update refs in existing state if trigger persisted const state = window.popoverState.get(popoverId); state.trigger = trigger; state.content = content; // Ensure closed state after potential swap/cleanup if (state.isOpen) closePopover(popoverId, true); } // Cleanup any stray listeners before attaching new ones if (trigger._popoverListener) trigger.removeEventListener('click', trigger._popoverListener); if (trigger._popoverHoverListeners) { trigger.removeEventListener('mouseenter', trigger._popoverHoverListeners.handleTriggerEnter); trigger.removeEventListener('mouseleave', trigger._popoverHoverListeners.handleTriggerLeave); } if (content._popoverHoverListeners) { content.removeEventListener('mouseenter', content._popoverHoverListeners.handleContentEnter); content.removeEventListener('mouseleave', content._popoverHoverListeners.handleContentLeave); } delete trigger._popoverListener; delete trigger._popoverHoverListeners; if (content) delete content._popoverHoverListeners; // Attach the correct listener type const triggerType = trigger.dataset.popoverType || 'click'; if (triggerType === 'click') { attachClickTrigger(trigger, popoverId); } else if (triggerType === 'hover') { attachHoverTrigger(trigger, popoverId); } trigger._popoverListenerAttached = true; } // --- Cleanup --- function cleanupPopovers(element) { const cleanupTrigger = (trigger) => { const popoverId = trigger.dataset.popoverFor; if (popoverId) { closePopover(popoverId, true); // Close popover, remove global listeners, stop Floating UI } // Remove listeners directly attached to the trigger if (trigger._popoverListener) trigger.removeEventListener('click', trigger._popoverListener); if (trigger._popoverHoverListeners) { trigger.removeEventListener('mouseenter', trigger._popoverHoverListeners.handleTriggerEnter); trigger.removeEventListener('mouseleave', trigger._popoverHoverListeners.handleTriggerLeave); } // Remove listeners attached to the content (for hover) const content = document.getElementById(popoverId); if (content && content._popoverHoverListeners) { content.removeEventListener('mouseenter', content._popoverHoverListeners.handleContentEnter); content.removeEventListener('mouseleave', content._popoverHoverListeners.handleContentLeave); delete content._popoverHoverListeners; } // Clean up stored references and flags on the trigger delete trigger._popoverListener; delete trigger._popoverHoverListeners; delete trigger._popoverListenerAttached; // Optionally remove state - might be desired if the element is definitely gone // window.popoverState.delete(popoverId); }; // Cleanup element itself if it's a trigger if (element.matches && element.matches('[data-popover-trigger]')) { cleanupTrigger(element); } // Cleanup descendants if (element.querySelectorAll) { element.querySelectorAll('[data-popover-trigger]').forEach(cleanupTrigger); } } function initAllComponents(root = document) { if (!FloatingUIDOM) return; // Don't init if library isn't ready if (root instanceof Element && root.matches('[data-popover-trigger]')) { initTrigger(root); } if (root && typeof root.querySelectorAll === 'function') { for (const trigger of root.querySelectorAll('[data-popover-trigger]')) { initTrigger(trigger); } } } const handleHtmxSwap = (event) => { const target = event.detail.elt if (target instanceof Element) { whenFloatingUiReady(() => initAllComponents(target)); } }; initAllComponents(); document.addEventListener('DOMContentLoaded', () => { whenFloatingUiReady(() => { addAnimationStyles(); initAllComponents(); }); }); document.body.addEventListener('htmx:beforeSwap', (event) => { const target = event.detail.elt; if (target instanceof Element) { cleanupPopovers(target); } }); document.body.addEventListener('htmx:afterSwap', handleHtmxSwap); document.body.addEventListener('htmx:oobAfterSwap', handleHtmxSwap); window.popoverSystemInitialized = true; })(); // IIFE End </script> } }
Update the import paths to match your project setup.
Examples
Trigger & Closing
Dimensions
Set the dimensions for the layer.
Dimensions
Set the dimensions for the layer.
Dimensions
Set the dimensions for the layer.
Dimensions
Set the dimensions for the layer.
package showcase
import (
"github.com/axzilla/templui/internal/components/button"
"github.com/axzilla/templui/internal/components/popover"
)
templ PopoverTriggers() {
<div class="flex gap-2 flex-wrap">
@popover.Popover() {
@popover.Trigger(popover.TriggerProps{
For: "hover-popover",
TriggerType: popover.TriggerTypeHover,
}) {
@button.Button(button.Props{Variant: button.VariantOutline}) {
Hover
}
}
@popover.Content(popover.ContentProps{
ID: "hover-popover",
HoverDelay: 300,
HoverOutDelay: 500,
}) {
@PopoverContent()
}
}
@popover.Popover() {
@popover.Trigger(popover.TriggerProps{
For: "click-popover",
}) {
@button.Button(button.Props{Variant: button.VariantOutline}) {
Click
}
}
@popover.Content(popover.ContentProps{
ID: "click-popover",
}) {
@PopoverContent()
}
}
@popover.Popover() {
@popover.Trigger(popover.TriggerProps{
For: "no-clickaway-popover",
}) {
@button.Button(button.Props{Variant: button.VariantOutline}) {
No ClickAway
}
}
@popover.Content(popover.ContentProps{
ID: "no-clickaway-popover",
DisableClickAway: true,
}) {
@PopoverContent()
}
}
@popover.Popover() {
@popover.Trigger(popover.TriggerProps{
For: "no-clickaway-esc",
}) {
@button.Button(button.Props{Variant: button.VariantOutline}) {
No ClickAway-ESC
}
}
@popover.Content(popover.ContentProps{
ID: "no-clickaway-esc",
DisableClickAway: true,
DisableESC: true,
}) {
@PopoverContent()
}
}
</div>
}
Positions
Dimensions
Set the dimensions for the layer.
Dimensions
Set the dimensions for the layer.
Dimensions
Set the dimensions for the layer.
Dimensions
Set the dimensions for the layer.
Dimensions
Set the dimensions for the layer.
Dimensions
Set the dimensions for the layer.
Dimensions
Set the dimensions for the layer.
Dimensions
Set the dimensions for the layer.
Dimensions
Set the dimensions for the layer.
Dimensions
Set the dimensions for the layer.
Dimensions
Set the dimensions for the layer.
Dimensions
Set the dimensions for the layer.
package showcase
import (
"github.com/axzilla/templui/internal/components/button"
"github.com/axzilla/templui/internal/components/popover"
)
templ PopoverPositions() {
<div class="flex flex-col w-full max-w-md">
<div class="grid grid-cols-3 gap-2">
@popover.Popover() {
@popover.Trigger(popover.TriggerProps{
For: "top-start-popover",
TriggerType: popover.TriggerTypeHover,
}) {
@button.Button(button.Props{
Class: "w-full",
Variant: button.VariantOutline,
}) {
Top Start
}
}
@popover.Content(popover.ContentProps{
ID: "top-start-popover",
Placement: popover.PlacementTopStart,
ShowArrow: true,
}) {
@PopoverContent()
}
}
@popover.Popover() {
@popover.Trigger(popover.TriggerProps{
For: "top-popover",
TriggerType: popover.TriggerTypeHover,
}) {
@button.Button(button.Props{
Class: "w-full",
Variant: button.VariantOutline,
}) {
Top
}
}
@popover.Content(popover.ContentProps{
ID: "top-popover",
Placement: popover.PlacementTop,
ShowArrow: true,
}) {
@PopoverContent()
}
}
@popover.Popover() {
@popover.Trigger(popover.TriggerProps{
For: "top-end-popover",
TriggerType: popover.TriggerTypeHover,
}) {
@button.Button(button.Props{
Class: "w-full",
Variant: button.VariantOutline,
}) {
Top End
}
}
@popover.Content(popover.ContentProps{
ID: "top-end-popover",
Placement: popover.PlacementTopEnd,
ShowArrow: true,
}) {
@PopoverContent()
}
}
@popover.Popover() {
@popover.Trigger(popover.TriggerProps{
For: "right-start-popover",
TriggerType: popover.TriggerTypeHover,
}) {
@button.Button(button.Props{
Class: "w-full",
Variant: button.VariantOutline,
}) {
Right Start
}
}
@popover.Content(popover.ContentProps{
ID: "right-start-popover",
Placement: popover.PlacementRightStart,
ShowArrow: true,
}) {
@PopoverContent()
}
}
@popover.Popover() {
@popover.Trigger(popover.TriggerProps{
For: "right-popover",
TriggerType: popover.TriggerTypeHover,
}) {
@button.Button(button.Props{
Class: "w-full",
Variant: button.VariantOutline,
}) {
Right
}
}
@popover.Content(popover.ContentProps{
ID: "right-popover",
Placement: popover.PlacementRight,
ShowArrow: true,
}) {
@PopoverContent()
}
}
@popover.Popover() {
@popover.Trigger(popover.TriggerProps{
For: "right-end-popover",
TriggerType: popover.TriggerTypeHover,
}) {
@button.Button(button.Props{
Class: "w-full",
Variant: button.VariantOutline,
}) {
Right End
}
}
@popover.Content(popover.ContentProps{
ID: "right-end-popover",
Placement: popover.PlacementRightEnd,
ShowArrow: true,
}) {
@PopoverContent()
}
}
@popover.Popover() {
@popover.Trigger(popover.TriggerProps{
For: "bottom-start-popover",
TriggerType: popover.TriggerTypeHover,
}) {
@button.Button(button.Props{
Class: "w-full",
Variant: button.VariantOutline,
}) {
Bottom Start
}
}
@popover.Content(popover.ContentProps{
ID: "bottom-start-popover",
Placement: popover.PlacementBottomStart,
ShowArrow: true,
}) {
@PopoverContent()
}
}
@popover.Popover() {
@popover.Trigger(popover.TriggerProps{
For: "bottom-popover",
TriggerType: popover.TriggerTypeHover,
}) {
@button.Button(button.Props{
Class: "w-full",
Variant: button.VariantOutline,
}) {
Bottom
}
}
@popover.Content(popover.ContentProps{
ID: "bottom-popover",
Placement: popover.PlacementBottom,
ShowArrow: true,
}) {
@PopoverContent()
}
}
@popover.Popover() {
@popover.Trigger(popover.TriggerProps{
For: "bottom-end-popover",
TriggerType: popover.TriggerTypeHover,
}) {
@button.Button(button.Props{
Class: "w-full",
Variant: button.VariantOutline,
}) {
Bottom End
}
}
@popover.Content(popover.ContentProps{
ID: "bottom-end-popover",
Placement: popover.PlacementBottomEnd,
ShowArrow: true,
}) {
@PopoverContent()
}
}
@popover.Popover() {
@popover.Trigger(popover.TriggerProps{
For: "left-start-popover",
TriggerType: popover.TriggerTypeHover,
}) {
@button.Button(button.Props{
Class: "w-full",
Variant: button.VariantOutline,
}) {
Left Start
}
}
@popover.Content(popover.ContentProps{
ID: "left-start-popover",
Placement: popover.PlacementLeftStart,
ShowArrow: true,
}) {
@PopoverContent()
}
}
@popover.Popover() {
@popover.Trigger(popover.TriggerProps{
For: "left-popover",
TriggerType: popover.TriggerTypeHover,
}) {
@button.Button(button.Props{
Class: "w-full",
Variant: button.VariantOutline,
}) {
Left
}
}
@popover.Content(popover.ContentProps{
ID: "left-popover",
Placement: popover.PlacementLeft,
ShowArrow: true,
}) {
@PopoverContent()
}
}
@popover.Popover() {
@popover.Trigger(popover.TriggerProps{
For: "left-end-popover",
TriggerType: popover.TriggerTypeHover,
}) {
@button.Button(button.Props{
Class: "w-full",
Variant: button.VariantOutline,
}) {
Left End
}
}
@popover.Content(popover.ContentProps{
ID: "left-end-popover",
Placement: popover.PlacementLeftEnd,
ShowArrow: true,
}) {
@PopoverContent()
}
}
</div>
</div>
}