Skip to content

A simple popover component for SolidJS based on floating-ui

License

Notifications You must be signed in to change notification settings

elite174/solid-simple-popover

Repository files navigation

solid-simple-popover

version npm

A really simple and minimalistic popover component for your apps.

IMPORTANT:

  • You may add width: max-content to the content element by yourself to avoid layout interference as it described here.

Features

  • Minimalistic - no wrapper DOM nodes!
  • Popover API support
  • Full control over position
  • Works with SSR and Astro
  • Multiple trigger events with vue-style modifiers
  • Custom anchor element

No wrapper nodes

No extra DOM nodes. Trigger node will have data-popover-open attribute, so you can use it in your CSS styles.

<button id="trigger-element">Toggle popover!</button>
<Popover triggerElement="#trigger-element">
  <div>Nice content here</div>
</Popover>

Popover API support

This component uses Popover API by default.

Don't forget to reset default browser styles for [popover]:

[popover] {
  margin: 0;
  background-color: transparent;
  padding: 0;
  border: none;
}

Full control over position

You can pass all the options for positioning. See docs for computePosition.

import { flip } from "@floating-ui/dom";

<button id="trigger-element">Toggle popover!</button>
<Popover
  triggerElement="#trigger-element"
  // Full control over position
  autoUpdate
  computePositionOptions={{ placement: "bottom-start", middleware: [flip()] }}
>
  <div>I'm a content</div>
</Popover>;

Multiple trigger events with vue-style modifiers

You can pass multiple trigger events with modifiers:

Events support the following modifiers:

  • capture
  • once
  • prevent
  • stop
  • passive
<button id="trigger-element">Toggle popover!</button>
<Popover
  triggerElement="#trigger-element"
  triggerEvents="click.capture|pointerdown"
>
  <div>I'm a content</div>
</Popover>

Custom anchor element

Sometimes it's necessary the anchor element to be different from trigger element. You may pass optional selector to find anchor element:

<div id="anchor-element"></div>
<button id="trigger-element">Toggle popover!</button>
<Popover
  triggerElement="#trigger-element"
  // Here you can pass CSS selector or HTML element
  anchorElement="#anchor-element"
>
  <div>
    <button autofocus>hi</button>
    This div is visible when popover is open!
  </div>
</Popover>

Installation

This package has the following peer dependencies:

"@floating-ui/dom": "^1.5",
"solid-js": "^1.8"

so you need to install required packages by yourself.

pnpm i solid-js @floating-ui/dom solid-simple-popover

Usage

import { Popover } from "solid-simple-popover";
import { flip } from "@floating-ui/dom";

<button id="trigger-button">Toggle popover</button>
<Popover
  triggerElement="trigger-button"
  // Full control over position
  autoUpdate
  computePositionOptions={{ placement: "bottom-start", middleware: [flip()] }}
  // Highly customizable
  sameWidth
  dataAttributeName="data-open"
  // You may pass custom selector here
  anchorElement="#trigger-button"
  // Astro support
  contentElementSelector="div"
>
  <div>This div is visible when popover is open!</div>
</Popover>;

Types

import { type ComputePositionConfig, type AutoUpdateOptions, type ComputePositionReturn } from "@floating-ui/dom";
import { type JSXElement, type ParentComponent } from "solid-js";

export type PopoverProps = {
  /**
   * HTML Element or CSS selector to find trigger element which triggers popover
   */
  triggerElement?: JSXElement;
  /**
   * HTML element or CSS selector to find anchor element which is used for positioning
   * Can be used with Astro, because astro wraps trigger element into astro-slot
   * and position breaks
   */
  anchorElement?: string | HTMLElement;
  open?: boolean;
  defaultOpen?: boolean;
  /**
   * Disables listening to trigger events
   * Note: if your trigger element has `disabled` state (like button or input), popover also won't be triggered
   */
  disabled?: boolean;
  /** Should content have the same width as trigger */
  sameWidth?: boolean;
  /** Options for floating-ui computePosition function */
  computePositionOptions?: ComputePositionConfig;
  /**
   * @default "pointerdown"
   * If set to null no event would trigger popover,
   * so you need to trigger it mannually.
   * Event name or list of event names separated by "|" which triggers popover.
   * You may also add modifiers like "capture", "passive", "once", "prevent", "stop" to the event separated by ".":
   * @example "pointerdown.capture.once.prevent|click"
   */
  triggerEvents?: string | null;
  /**
   * Close popover on interaction outside
   * @default true
   * By default when popover is open it will listen to "pointerdown" event outside of popover content and trigger
   */
  closeOnOutsideInteraction?: boolean;
  /**
   * Data attribute name to set on trigger element
   * @default "data-popover-open"
   */
  dataAttributeName?: string;
  /**
   * CSS selector to find html element inside content
   * Can be used with Astro, because astro wraps element into astro-slot
   * and position breaks
   */
  contentElementSelector?: string;
  /**
   * autoUpdate option for floating ui
   * @see https://floating-ui.com/docs/autoupdate
   */
  autoUpdate?: boolean;
  /**
   * Applies only if autoUpdate is true
   * @see https://floating-ui.com/docs/autoupdate#options
   */
  autoUpdateOptions?: AutoUpdateOptions;
  /**
   * Close popover on escape key press.
   * Uses 'keydown' event with 'Escape' key.
   * @default true
   */
  closeOnEscape?: boolean;
  onOpenChange?: (open: boolean) => void;
  onComputePosition?: (data: ComputePositionReturn) => void;
};

export declare const Popover: ParentComponent<PopoverProps>;

License

MIT