Skip to main content
Glama
static.tsx8.63 kB
import type { DetailedHTMLProps, FC, HTMLAttributes } from 'react'; import { cn } from '../../utils/cn'; import { Container } from '../Container'; /** * Props for the main Popover component * Extends HTMLDivElement attributes for full DOM compatibility */ export type PopoverProps = DetailedHTMLProps< HTMLAttributes<HTMLDivElement>, HTMLDivElement > & { /** Unique identifier linking the trigger to its popover content for accessibility */ identifier: string; }; /** * Composite type for the Popover component with Detail subcomponent * Allows for Popover.Detail usage pattern */ export type PopoverType = FC<PopoverProps> & { Detail: FC<DetailProps>; }; /** * Horizontal alignment options for popover positioning */ export enum PopoverXAlign { /** Align popover to start (left) of trigger */ START = 'start', /** Align popover to end (right) of trigger */ END = 'end', } /** * Vertical alignment options for popover positioning */ export enum PopoverYAlign { /** Position popover below the trigger */ BELOW = 'bellow', /** Position popover above the trigger */ ABOVE = 'above', } /** * Popover Component * * A versatile popover container that displays contextual content when triggered by hover * or focus interactions. Built with accessibility in mind and supports multiple positioning * options with smooth animations. * * Features: * - Hover and focus-based triggering * - Multiple positioning options (above/below, start/end) * - Accessibility compliant with ARIA attributes * - Smooth animations with configurable delays * - Optional directional arrows * - Automatic z-index management * - Responsive design support * * Architecture: * - Main Popover acts as trigger container * - Popover.Detail renders the actual popover content * - Uses CSS groups for coordinated hover/focus states * - Unique identifier system prevents conflicts * * @example * Basic hover popover: * ```jsx * <Popover identifier="help-tooltip"> * <button>Need Help?</button> * * <Popover.Detail identifier="help-tooltip"> * <div>This is helpful information!</div> * </Popover.Detail> * </Popover> * ``` * * @example * Focus-triggered popover: * ```jsx * <Popover identifier="focus-menu"> * <input placeholder="Focus me" /> * * <Popover.Detail * identifier="focus-menu" * isFocusable * isOverable={false} * > * <div>Focus-only menu content</div> * </Popover.Detail> * </Popover> * ``` * * @example * Positioned popover with custom alignment: * ```jsx * <Popover identifier="positioned"> * <span>Hover me</span> * * <Popover.Detail * identifier="positioned" * xAlign={PopoverXAlign.END} * yAlign={PopoverYAlign.ABOVE} * displayArrow={false} * > * <div>Above and right-aligned</div> * </Popover.Detail> * </Popover> * ``` * * Accessibility Features: * - Proper ARIA labeling and relationships * - Keyboard navigation support * - Screen reader compatibility * - Focus management * * Performance Considerations: * - CSS-only animations for smooth transitions * - Efficient group-based state management * - Minimal DOM updates during interactions * * @param props - Popover component props * @returns Trigger container with popover functionality */ export const PopoverStatic: PopoverType = ({ children, className, identifier, ...props }) => ( <div className={cn('group/popover relative flex cursor-pointer', className)} id={`unrollable-panel-button-${identifier}`} aria-haspopup {...props} > {children} </div> ); /** * Props for the Popover.Detail component * Extends HTMLDivElement attributes for styling flexibility */ export type DetailProps = HTMLAttributes<HTMLDivElement> & { /** Whether the popover responds to focus events on the trigger */ isFocusable?: boolean; /** Controls visibility state - undefined allows automatic hover/focus control */ isHidden?: boolean; /** Whether the popover responds to hover events on the trigger */ isOverable?: boolean; /** Unique identifier matching the trigger's identifier for accessibility */ identifier: string; /** Horizontal positioning relative to trigger */ xAlign?: PopoverXAlign | `${PopoverXAlign}`; /** Vertical positioning relative to trigger */ yAlign?: PopoverYAlign | `${PopoverYAlign}`; /** Whether to display the directional arrow indicator */ displayArrow?: boolean; }; /** * Popover Detail Component * * The actual popover content container with advanced positioning, animation, and * accessibility features. Automatically manages visibility based on trigger interactions. * * Features: * - Precise positioning with alignment options * - Smooth fade and slide animations * - Configurable directional arrows * - Hover and focus interaction support * - Accessibility-compliant ARIA attributes * - High z-index for overlay behavior * - Automatic visibility management * * Positioning System: * - X-axis: START (left-aligned) or END (right-aligned) * - Y-axis: BELOW (underneath) or ABOVE (on top) * - Automatic spacing with 1rem gap from trigger * - Responsive minimum width matching trigger * * Arrow Indicators: * - CSS-generated triangular arrows * - Positioned based on alignment settings * - Points toward trigger for visual connection * - Can be disabled for clean, minimal appearance * * Animation Behavior: * - Starts invisible with opacity: 0 * - Smooth 400ms transitions with easing * - 800ms delay for hover states (prevents flicker) * - Immediate hiding when trigger loses focus/hover * * @example * Rich content popover: * ```jsx * <Popover.Detail identifier="rich-content"> * <div className="p-4"> * <h3>Popover Title</h3> * <p>Detailed information with multiple paragraphs.</p> * <button>Action Button</button> * </div> * </Popover.Detail> * ``` * * @example * Menu-style popover: * ```jsx * <Popover.Detail * identifier="context-menu" * displayArrow={false} * xAlign={PopoverXAlign.END} * > * <ul className="py-2"> * <li><button className="w-full px-4 py-2">Edit</button></li> * <li><button className="w-full px-4 py-2">Delete</button></li> * </ul> * </Popover.Detail> * ``` * * @param props - Popover Detail component props * @returns Positioned popover content with animations and accessibility */ const Detail: FC<DetailProps> = ({ children, isHidden = undefined, isOverable = true, isFocusable = false, xAlign = PopoverXAlign.START, yAlign = PopoverYAlign.BELOW, identifier, className, displayArrow = true, ...props }) => ( <Container transparency="sm" role="group" aria-hidden={isHidden} aria-labelledby={`unrollable-panel-button-${identifier}`} id={`unrollable-panel-${identifier}`} className={cn( 'absolute z-50 min-w-full rounded-md ring-1 ring-neutral', /* Positioning */ xAlign === 'start' && 'left-0', xAlign === 'end' && 'right-0', yAlign === 'bellow' && 'top-[calc(100%+1rem)]', yAlign === 'above' && 'bottom-[calc(100%+1rem)]', /* Arrow indicator */ displayArrow && 'before:absolute before:z-[999] before:h-0 before:w-0 before:content-[""]', /* Horizontal positioning */ displayArrow && xAlign === 'start' && 'before:left-2', displayArrow && xAlign === 'end' && 'before:right-2', /* Arrow pointing up (when popover is below trigger) */ displayArrow && yAlign === 'bellow' && 'before:-top-[10px] before:border-r-[10px] before:border-r-transparent before:border-b-[10px] before:border-b-neutral before:border-l-[10px] before:border-l-transparent', /* Arrow pointing down (when popover is above trigger) */ displayArrow && yAlign === 'above' && 'before:-bottom-[10px] before:border-t-[10px] before:border-t-neutral before:border-r-[10px] before:border-r-transparent before:border-l-[10px] before:border-l-transparent', /* Visibility management */ 'overflow-x-visible opacity-0 transition-all duration-400 ease-in-out', isHidden !== false ? 'invisible' : 'visible opacity-100 delay-800', isOverable && 'group-hover/popover:visible group-hover/popover:opacity-100 group-hover/popover:delay-800', isFocusable && 'group-focus-within/popover:visible group-focus-within/popover:opacity-100 group-focus-within/popover:delay-800', className )} {...props} > {children} </Container> ); PopoverStatic.Detail = Detail; // Export Detail for use in dynamic version export { Detail };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/aymericzip/intlayer'

If you have feedback or need assistance with the MCP directory API, please join our Discord server