// packages/block-editor/src/components/writing-flow/use-selection-observer.js
import { useSelect, useDispatch } from "@wordpress/data";
import { useRefEffect } from "@wordpress/compose";
import { create } from "@wordpress/rich-text";
import { isSelectionForward } from "@wordpress/dom";
import { store as blockEditorStore } from "../../store";
import { getBlockClientId } from "../../utils/dom";
function extractSelectionStartNode(selection) {
  const { anchorNode, anchorOffset } = selection;
  if (anchorNode.nodeType === anchorNode.TEXT_NODE) {
    return anchorNode;
  }
  if (anchorOffset === 0) {
    return anchorNode;
  }
  return anchorNode.childNodes[anchorOffset - 1];
}
function extractSelectionEndNode(selection) {
  const { focusNode, focusOffset } = selection;
  if (focusNode.nodeType === focusNode.TEXT_NODE) {
    return focusNode;
  }
  if (focusOffset === focusNode.childNodes.length) {
    return focusNode;
  }
  if (focusOffset === 0 && isSelectionForward(selection)) {
    return focusNode.previousSibling ?? focusNode.parentElement;
  }
  return focusNode.childNodes[focusOffset];
}
function findDepth(a, b) {
  let depth = 0;
  while (a[depth] === b[depth]) {
    depth++;
  }
  return depth;
}
function setContentEditableWrapper(node, value) {
  if (node.contentEditable !== String(value)) {
    node.contentEditable = value;
    if (value) {
      node.focus();
    }
  }
}
function getRichTextElement(node) {
  const element = node.nodeType === node.ELEMENT_NODE ? node : node.parentElement;
  return element?.closest("[data-wp-block-attribute-key]");
}
function useSelectionObserver() {
  const { multiSelect, selectBlock, selectionChange } = useDispatch(blockEditorStore);
  const { getBlockParents, getBlockSelectionStart, isMultiSelecting } = useSelect(blockEditorStore);
  return useRefEffect(
    (node) => {
      const { ownerDocument } = node;
      const { defaultView } = ownerDocument;
      function onSelectionChange(event) {
        const selection = defaultView.getSelection();
        if (!selection.rangeCount) {
          return;
        }
        const startNode = extractSelectionStartNode(selection);
        const endNode = extractSelectionEndNode(selection);
        if (!node.contains(startNode) || !node.contains(endNode)) {
          return;
        }
        const isClickShift = event.shiftKey && event.type === "mouseup";
        if (selection.isCollapsed && !isClickShift) {
          if (node.contentEditable === "true" && !isMultiSelecting()) {
            setContentEditableWrapper(node, false);
            let element = startNode.nodeType === startNode.ELEMENT_NODE ? startNode : startNode.parentElement;
            element = element?.closest("[contenteditable]");
            element?.focus();
          }
          return;
        }
        let startClientId = getBlockClientId(startNode);
        let endClientId = getBlockClientId(endNode);
        if (isClickShift) {
          const selectedClientId = getBlockSelectionStart();
          const clickedClientId = getBlockClientId(event.target);
          const focusNodeIsNonSelectable = clickedClientId !== endClientId;
          if (startClientId === endClientId && selection.isCollapsed || !endClientId || focusNodeIsNonSelectable) {
            endClientId = clickedClientId;
          }
          if (startClientId !== selectedClientId) {
            startClientId = selectedClientId;
          }
        }
        if (startClientId === void 0 && endClientId === void 0) {
          setContentEditableWrapper(node, false);
          return;
        }
        const isSingularSelection = startClientId === endClientId;
        if (isSingularSelection) {
          if (!isMultiSelecting()) {
            selectBlock(startClientId);
          } else {
            multiSelect(startClientId, startClientId);
          }
        } else {
          const startPath = [
            ...getBlockParents(startClientId),
            startClientId
          ];
          const endPath = [
            ...getBlockParents(endClientId),
            endClientId
          ];
          const depth = findDepth(startPath, endPath);
          if (startPath[depth] !== startClientId || endPath[depth] !== endClientId) {
            multiSelect(startPath[depth], endPath[depth]);
            return;
          }
          const richTextElementStart = getRichTextElement(startNode);
          const richTextElementEnd = getRichTextElement(endNode);
          if (richTextElementStart && richTextElementEnd) {
            const range = selection.getRangeAt(0);
            const richTextDataStart = create({
              element: richTextElementStart,
              range,
              __unstableIsEditableTree: true
            });
            const richTextDataEnd = create({
              element: richTextElementEnd,
              range,
              __unstableIsEditableTree: true
            });
            const startOffset = richTextDataStart.start ?? richTextDataStart.end;
            const endOffset = richTextDataEnd.start ?? richTextDataEnd.end;
            selectionChange({
              start: {
                clientId: startClientId,
                attributeKey: richTextElementStart.dataset.wpBlockAttributeKey,
                offset: startOffset
              },
              end: {
                clientId: endClientId,
                attributeKey: richTextElementEnd.dataset.wpBlockAttributeKey,
                offset: endOffset
              }
            });
          } else {
            multiSelect(startClientId, endClientId);
          }
        }
      }
      ownerDocument.addEventListener(
        "selectionchange",
        onSelectionChange
      );
      defaultView.addEventListener("mouseup", onSelectionChange);
      return () => {
        ownerDocument.removeEventListener(
          "selectionchange",
          onSelectionChange
        );
        defaultView.removeEventListener("mouseup", onSelectionChange);
      };
    },
    [multiSelect, selectBlock, selectionChange, getBlockParents]
  );
}
export {
  useSelectionObserver as default
};
//# sourceMappingURL=use-selection-observer.js.map
