Selection Popover
Showing an inline Popover when a text is selected. This example uses the getAnchorRect
prop to position the flyout relative to the selected text.
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Odio, sed fuga necessitatibus aliquid expedita atque? Doloremque ea sequi totam laudantium laboriosam repellat quasi commodi omnis aut nulla. Numquam, beatae maxime.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
import * as Ariakit from "@ariakit/react";import { useEffect, useRef } from "react";import "./style.css";function hasSelectionWithin(element?: Element | null) {const selection = element?.ownerDocument.getSelection();if (!selection?.rangeCount) return false;const range = selection.getRangeAt(0);if (range.collapsed) return false;return !!element?.contains(range.commonAncestorContainer);}export default function Example() {const popoverRef = useRef<HTMLDivElement>(null);const paragraphRef = useRef<HTMLParagraphElement>(null);useEffect(() => {const popoverContainer = popoverRef.current;const paragraph = paragraphRef.current;if (!popoverContainer) return;if (!paragraph) return;const doc = paragraph.ownerDocument || document;const onMouseUp = () => {if (!hasSelectionWithin(paragraph)) return;popover.render();popover.setOpen(true);};const onSelect = () => {if (popoverContainer.contains(doc.activeElement)) return;if (hasSelectionWithin(paragraph)) {return popover.render();}popover.setOpen(false);};doc.addEventListener("mouseup", onMouseUp);doc.addEventListener("selectionchange", onSelect);return () => {doc.removeEventListener("mouseup", onMouseUp);doc.removeEventListener("selectionchange", onSelect);};}, [popover]);return (<div>!hasSelectionWithin(paragraphRef.current)}ref={popoverRef}className="popover"const selection =paragraphRef.current?.ownerDocument.getSelection();if (!selection?.rangeCount) return null;const range = selection.getRangeAt(0);return range.getBoundingClientRect();}}><p ref={paragraphRef}>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Odio, sed fuganecessitatibus aliquid expedita atque? Doloremque ea sequi totamlaudantium laboriosam repellat quasi commodi omnis aut nulla. Numquam,beatae maxime.</p></div>);}import * as Ariakit from "@ariakit/react";import { useEffect, useRef } from "react";import "./style.css";function hasSelectionWithin(element?: Element | null) {const selection = element?.ownerDocument.getSelection();if (!selection?.rangeCount) return false;const range = selection.getRangeAt(0);if (range.collapsed) return false;return !!element?.contains(range.commonAncestorContainer);}export default function Example() {const popoverRef = useRef<HTMLDivElement>(null);const paragraphRef = useRef<HTMLParagraphElement>(null);useEffect(() => {const popoverContainer = popoverRef.current;const paragraph = paragraphRef.current;if (!popoverContainer) return;if (!paragraph) return;const doc = paragraph.ownerDocument || document;const onMouseUp = () => {if (!hasSelectionWithin(paragraph)) return;popover.render();popover.setOpen(true);};const onSelect = () => {if (popoverContainer.contains(doc.activeElement)) return;if (hasSelectionWithin(paragraph)) {return popover.render();}popover.setOpen(false);};doc.addEventListener("mouseup", onMouseUp);doc.addEventListener("selectionchange", onSelect);return () => {doc.removeEventListener("mouseup", onMouseUp);doc.removeEventListener("selectionchange", onSelect);};}, [popover]);return (<div>!hasSelectionWithin(paragraphRef.current)}ref={popoverRef}className="popover"const selection =paragraphRef.current?.ownerDocument.getSelection();if (!selection?.rangeCount) return null;const range = selection.getRangeAt(0);return range.getBoundingClientRect();}}><p ref={paragraphRef}>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Odio, sed fuganecessitatibus aliquid expedita atque? Doloremque ea sequi totamlaudantium laboriosam repellat quasi commodi omnis aut nulla. Numquam,beatae maxime.</p></div>);}