import classNames from "classnames";
import lodash from "lodash";
import { getIsEditorEmpty } from "./../_shared/get-is-editor-empty.js";
import { TOP_LEVEL_NODES } from "./../_shared/top-level-nodes.js";
import { Plugin, PluginKey } from "./../tiptap/pm/state.js";
import { Decoration, DecorationSet } from "./../tiptap/pm/view.js";
import { createRicosFunctionalExtension } from "./create-ricos-extension/index.js";
import styles from "./placeholder-scss.js";
const PLACEHOLDER_UPDATE_META = "placeholder-update";
const PLACEHOLDER_POINTER_META = "placeholder-pointer-update";
const createPlaceholderWidget = (text, isMobile) => () => {
  const wrapper = document.createElement("span");
  wrapper.contentEditable = "false";
  wrapper.setAttribute("data-placeholder", text);
  if (isMobile) {
    wrapper.setAttribute("data-placeholder-multiline", "");
  }
  wrapper.textContent = "​";
  return wrapper;
};
const updatePointerSuppression = (view, pluginKey, suppressWidgets) => {
  if (pluginKey.getState(view.state)?.suppressWidgets === suppressWidgets) {
    return;
  }
  view.dispatch(view.state.tr.setMeta(PLACEHOLDER_POINTER_META, suppressWidgets));
};
const placeholder = createRicosFunctionalExtension({
  type: "extension",
  groups: [],
  name: "placeholder",
  reconfigure: (config, _extensions, ricosProps, _settings, services) => ({
    ...config,
    addOptions() {
      return {
        isMobile: services?.context?.isMobile ?? false,
        placeholder: ricosProps.placeholder || "",
        t: services.t,
        extensionsPlaceholders: lodash.compact(
          _extensions.filter(
            (ext) => ["html-node", "react-node", "extension"].includes(ext.type)
          ).map((ext) => ext.placeholder)
        )
      };
    }
  }),
  createExtensionConfig() {
    return {
      name: this.name,
      addProseMirrorPlugins() {
        const pluginKey = new PluginKey("editor-placeholders");
        const options = this.options;
        const editor = this.editor;
        const pointerState = { isPointerDown: false };
        const sortedPlaceholders = [
          ...options.extensionsPlaceholders
        ].sort(({ priority: a = 0 }, { priority: b = 0 }) => b - a);
        return [
          new Plugin({
            key: pluginKey,
            state: {
              init: () => ({
                placeholder: options.placeholder,
                suppressWidgets: false
              }),
              apply(tr, value) {
                const placeholderOverride = tr.getMeta(PLACEHOLDER_UPDATE_META);
                const suppressWidgetsOverride = tr.getMeta(PLACEHOLDER_POINTER_META);
                if (placeholderOverride === void 0 && suppressWidgetsOverride === void 0) {
                  return value;
                }
                return {
                  placeholder: placeholderOverride !== void 0 ? placeholderOverride : value.placeholder,
                  suppressWidgets: suppressWidgetsOverride !== void 0 ? suppressWidgetsOverride : value.suppressWidgets
                };
              }
            },
            view(view) {
              const requestClearSuppression = () => {
                pointerState.isPointerDown = false;
                const currentWindow2 = view.dom.ownerDocument.defaultView;
                if (!pluginKey.getState(view.state)?.suppressWidgets || !currentWindow2) {
                  return;
                }
                currentWindow2.requestAnimationFrame(() => {
                  if (view.isDestroyed) {
                    return;
                  }
                  updatePointerSuppression(view, pluginKey, false);
                });
              };
              const currentDocument = view.dom.ownerDocument;
              const currentWindow = currentDocument.defaultView;
              currentDocument.addEventListener("mouseup", requestClearSuppression);
              currentDocument.addEventListener("dragend", requestClearSuppression);
              currentWindow?.addEventListener("blur", requestClearSuppression);
              return {
                destroy() {
                  pointerState.isPointerDown = false;
                  currentDocument.removeEventListener("mouseup", requestClearSuppression);
                  currentDocument.removeEventListener("dragend", requestClearSuppression);
                  currentWindow?.removeEventListener("blur", requestClearSuppression);
                }
              };
            },
            props: {
              handleDOMEvents: {
                mousedown(_view, event) {
                  if (event.button !== 0) {
                    return false;
                  }
                  pointerState.isPointerDown = true;
                  return false;
                },
                mousemove(view, event) {
                  if (!pointerState.isPointerDown || event.buttons !== 1) {
                    return false;
                  }
                  updatePointerSuppression(view, pluginKey, true);
                  return false;
                }
              },
              // Computed live, NOT cached in plugin state. Some predicates
              // (notably quick-actions) read `editor.isFocused`, which is
              // not part of editor state and does not flip via a transaction
              // — caching the resulting `DecorationSet` in `apply` would
              // leave the placeholder stale across focus/blur. The work
              // here is small (one `descendants` walk on an empty doc),
              // so the live read is the correct trade-off.
              decorations(state) {
                if (!editor.isEditable) {
                  return null;
                }
                const pluginState = pluginKey.getState(state);
                const placeholderText = pluginState?.placeholder ?? "";
                const shouldRenderWidgets = !pluginState?.suppressWidgets;
                const { doc, selection } = state;
                const decorations = [];
                let editorEmptyTargetPos;
                let editorEmptyTargetNodeSize;
                const nodePlaceholderPositions = /* @__PURE__ */ new Set();
                if (getIsEditorEmpty(doc)) {
                  const { firstChild } = doc;
                  editorEmptyTargetPos = 0;
                  let targetNode = firstChild;
                  while (TOP_LEVEL_NODES.includes(targetNode.type.name) && targetNode.firstChild) {
                    editorEmptyTargetPos += 1;
                    targetNode = targetNode.firstChild;
                  }
                  editorEmptyTargetNodeSize = targetNode.nodeSize;
                }
                if (sortedPlaceholders.length) {
                  doc.descendants((node, pos) => {
                    const isEmpty = !node.isLeaf && !node.childCount;
                    if (!isEmpty) {
                      return;
                    }
                    const { content, alwaysVisible, alignCenter } = sortedPlaceholders.find(
                      ({ predicate }) => predicate({
                        doc,
                        pos,
                        node,
                        selection,
                        editor,
                        isMobile: options.isMobile
                      })
                    ) || {};
                    if (!content) {
                      return;
                    }
                    const placeholderText2 = options.t?.(content) || content;
                    nodePlaceholderPositions.add(pos);
                    decorations.push(
                      Decoration.node(pos, pos + node.nodeSize, {
                        class: classNames("is-node-empty", {
                          [styles.alignCenter]: alignCenter
                        }),
                        ...alwaysVisible ? { "data-placeholder-always-visible": "" } : {}
                      })
                    );
                    if (shouldRenderWidgets) {
                      decorations.push(
                        Decoration.widget(
                          pos + 1,
                          createPlaceholderWidget(placeholderText2, options.isMobile),
                          {
                            side: -1,
                            ignoreSelection: true,
                            key: `node-empty-placeholder:${placeholderText2}`
                          }
                        )
                      );
                    }
                    return true;
                  });
                }
                if (editorEmptyTargetPos !== void 0 && editorEmptyTargetNodeSize !== void 0 && !nodePlaceholderPositions.has(editorEmptyTargetPos)) {
                  decorations.push(
                    Decoration.node(
                      editorEmptyTargetPos,
                      editorEmptyTargetPos + editorEmptyTargetNodeSize,
                      {
                        class: "is-editor-empty"
                      }
                    )
                  );
                  if (shouldRenderWidgets) {
                    decorations.push(
                      Decoration.widget(
                        editorEmptyTargetPos + 1,
                        createPlaceholderWidget(placeholderText, options.isMobile),
                        {
                          side: -1,
                          ignoreSelection: true,
                          key: `editor-empty-placeholder:${placeholderText}`
                        }
                      )
                    );
                  }
                }
                return DecorationSet.create(doc, decorations);
              }
            }
          })
        ];
      }
    };
  }
});
const updatePlaceholder = (editor, placeholder2) => {
  const tr = editor.view.state.tr.setMeta(PLACEHOLDER_UPDATE_META, placeholder2);
  editor.view.dispatch(tr);
};
export {
  placeholder,
  updatePlaceholder
};
//# sourceMappingURL=placeholder.js.map
