{
  "version": 3,
  "sources": ["../../src/hook/use-anchor.ts"],
  "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { usePrevious } from '@wordpress/compose';\nimport { useState, useLayoutEffect } from '@wordpress/element';\nimport { getRectangleFromRange } from '@wordpress/dom';\n\n/**\n * Internal dependencies\n */\nimport type { WPFormat } from '../register-format-type';\n\n/**\n * Given a range and a format tag name and class name, returns the closest\n * format element.\n *\n * @param range                  The Range to check.\n * @param editableContentElement The editable wrapper.\n * @param tagName                The tag name of the format element.\n * @param className              The class name of the format element.\n * @return                       The format element, if found.\n */\nfunction getFormatElement(\n\trange: Range,\n\teditableContentElement: HTMLElement,\n\ttagName: string,\n\tclassName: string\n): HTMLElement | undefined {\n\tlet element = range.startContainer;\n\n\t// Even if the active format is defined, the actually DOM range's start\n\t// container may be outside of the format's DOM element:\n\t// `a\u2038<strong>b</strong>` (DOM) while visually it's `a<strong>\u2038b</strong>`.\n\t// So at a given selection index, start with the deepest format DOM element.\n\tif (\n\t\telement.nodeType === element.TEXT_NODE &&\n\t\telement instanceof window.Text &&\n\t\trange.startOffset === element.length &&\n\t\telement.nextSibling\n\t) {\n\t\telement = element.nextSibling;\n\n\t\twhile ( element.firstChild ) {\n\t\t\telement = element.firstChild;\n\t\t}\n\t}\n\n\tif ( element.nodeType !== element.ELEMENT_NODE ) {\n\t\tif ( ! element.parentElement ) {\n\t\t\treturn;\n\t\t}\n\t\telement = element.parentElement;\n\t}\n\n\tif ( element === editableContentElement ) {\n\t\treturn;\n\t}\n\n\tif ( ! editableContentElement.contains( element ) ) {\n\t\treturn;\n\t}\n\n\tconst selector = tagName + ( className ? '.' + className : '' );\n\n\t// Element#matches will throw SyntaxError on an empty selector\n\tif ( ! selector ) {\n\t\treturn;\n\t}\n\n\tif ( ! ( element instanceof window.HTMLElement ) ) {\n\t\treturn;\n\t}\n\n\tlet closestElement: HTMLElement | null = element;\n\n\t// .closest( selector ), but with a boundary. Check if the element matches\n\t// the selector. If it doesn't match, try the parent element if it's not the\n\t// editable wrapper. We don't want to try to match ancestors of the editable\n\t// wrapper, which is what .closest( selector ) would do. When the element is\n\t// the editable wrapper (which is most likely the case because most text is\n\t// unformatted), this never runs.\n\twhile ( closestElement && closestElement !== editableContentElement ) {\n\t\tif ( closestElement.matches( selector ) ) {\n\t\t\treturn closestElement;\n\t\t}\n\n\t\tclosestElement = closestElement.parentElement;\n\t}\n\n\treturn undefined;\n}\n\ninterface VirtualAnchorElement {\n\tgetBoundingClientRect: () => DOMRect;\n\tcontextElement: HTMLElement;\n}\n\n/**\n * Creates a virtual anchor element for a range.\n *\n * @param range                  The range to create a virtual anchor element for.\n * @param editableContentElement The editable wrapper.\n * @return                       The virtual anchor element.\n */\nfunction createVirtualAnchorElement(\n\trange: Range,\n\teditableContentElement: HTMLElement\n): VirtualAnchorElement {\n\treturn {\n\t\tcontextElement: editableContentElement,\n\t\tgetBoundingClientRect() {\n\t\t\tif ( editableContentElement.contains( range.startContainer ) ) {\n\t\t\t\treturn (\n\t\t\t\t\tgetRectangleFromRange( range ) ??\n\t\t\t\t\trange.getBoundingClientRect()\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn editableContentElement.getBoundingClientRect();\n\t\t},\n\t};\n}\n\n/**\n * Get the anchor: a format element if there is a matching one based on the\n * tagName and className or a range otherwise.\n *\n * @param editableContentElement The editable wrapper.\n * @param tagName                The tag name of the format element.\n * @param className              The class name of the format element.\n * @return                       The anchor.\n */\nfunction getAnchor(\n\teditableContentElement: HTMLElement | null,\n\ttagName: string,\n\tclassName: string\n): HTMLElement | VirtualAnchorElement | undefined {\n\tif ( ! editableContentElement ) {\n\t\treturn;\n\t}\n\n\tconst { ownerDocument } = editableContentElement;\n\tconst { defaultView } = ownerDocument;\n\tconst selection = defaultView?.getSelection();\n\n\tif ( ! selection ) {\n\t\treturn;\n\t}\n\tif ( ! selection.rangeCount ) {\n\t\treturn;\n\t}\n\n\tconst range = selection.getRangeAt( 0 );\n\n\tif ( ! range || ! range.startContainer ) {\n\t\treturn;\n\t}\n\n\tif ( ! tagName && ! className ) {\n\t\treturn createVirtualAnchorElement( range, editableContentElement );\n\t}\n\n\treturn (\n\t\tgetFormatElement( range, editableContentElement, tagName, className ) ??\n\t\tcreateVirtualAnchorElement( range, editableContentElement )\n\t);\n}\n\nconst DEFAULT_SETTINGS = {\n\ttagName: '',\n\tclassName: '',\n};\n\n/**\n * This hook, to be used in a format type's Edit component, returns the active\n * element that is formatted, or a virtual element for the selection range if\n * no format is active. The returned value is meant to be used for positioning\n * UI, e.g. by passing it to the `Popover` component via the `anchor` prop.\n *\n * @param obj                        Named parameters.\n * @param obj.editableContentElement The element containing the editable content.\n * @param obj.settings               The format type's settings.\n * @return                           The active element or selection range.\n */\nexport function useAnchor( {\n\teditableContentElement,\n\tsettings,\n}: {\n\teditableContentElement: HTMLElement | null;\n\tsettings?: WPFormat;\n} ): Element | VirtualAnchorElement | undefined | null {\n\tconst { tagName, className } = settings ?? DEFAULT_SETTINGS;\n\n\t// `isActive` is not a property of `WPFormat`, but it has made its way into\n\t// `settings` in certain cases (see `core/link` format). Avoid making this\n\t// exception \"public\" in the function signature: tell TS how to look for it\n\t// dynamically.\n\tconst isActive = !! (\n\t\tsettings &&\n\t\t'isActive' in settings &&\n\t\tsettings.isActive\n\t);\n\n\tconst [ anchor, setAnchor ] = useState( () =>\n\t\tgetAnchor( editableContentElement, tagName, className ?? '' )\n\t);\n\tconst wasActive = usePrevious( isActive );\n\n\tuseLayoutEffect( () => {\n\t\tif ( ! editableContentElement ) {\n\t\t\treturn;\n\t\t}\n\n\t\tfunction callback() {\n\t\t\tsetAnchor(\n\t\t\t\tgetAnchor( editableContentElement, tagName, className ?? '' )\n\t\t\t);\n\t\t}\n\n\t\tfunction attach() {\n\t\t\townerDocument.addEventListener( 'selectionchange', callback );\n\t\t}\n\n\t\tfunction detach() {\n\t\t\townerDocument.removeEventListener( 'selectionchange', callback );\n\t\t}\n\n\t\tconst { ownerDocument } = editableContentElement;\n\n\t\tif (\n\t\t\teditableContentElement === ownerDocument.activeElement ||\n\t\t\t// When a link is created, we need to attach the popover to the newly created anchor.\n\t\t\t( ! wasActive && isActive ) ||\n\t\t\t// Sometimes we're _removing_ an active anchor, such as the inline color popover.\n\t\t\t// When we add the color, it switches from a virtual anchor to a `<mark>` element.\n\t\t\t// When we _remove_ the color, it switches from a `<mark>` element to a virtual anchor.\n\t\t\t( wasActive && ! isActive )\n\t\t) {\n\t\t\tsetAnchor(\n\t\t\t\tgetAnchor( editableContentElement, tagName, className ?? '' )\n\t\t\t);\n\t\t\tattach();\n\t\t}\n\n\t\teditableContentElement.addEventListener( 'focusin', attach );\n\t\teditableContentElement.addEventListener( 'focusout', detach );\n\n\t\treturn () => {\n\t\t\tdetach();\n\n\t\t\teditableContentElement.removeEventListener( 'focusin', attach );\n\t\t\teditableContentElement.removeEventListener( 'focusout', detach );\n\t\t};\n\t}, [ editableContentElement, tagName, className, isActive, wasActive ] );\n\n\treturn anchor;\n}\n"],
  "mappings": "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",
  "names": []
}
