// Use this selector to set the intersection observer boundary for editor's inline node views
// If this does not exist, it will use the IntersectionObserver's default root
const INTERSECTION_OBSERVER_ROOT_SELECTOR = '[data-editor-scroll-container="true"]';
const editorObservers = new WeakMap();
const callbackMap = new WeakMap();

/**
 * Creates a node visibility manager
 * @param editorElement
 * @returns
 */
export const nodeVisibilityManager = editorElement => {
  // Warning! do not reference editorElement outside of internal functions.
  // editorElement is passed to allow support for multiple editors,

  const unObserveInternal = nodeElement => {
    var _editorObservers$get;
    (_editorObservers$get = editorObservers.get(editorElement)) === null || _editorObservers$get === void 0 ? void 0 : _editorObservers$get.unobserve(nodeElement);
    callbackMap.delete(nodeElement);
  };
  const observe = observerConfig => {
    var _editorObservers$get2;
    callbackMap.set(observerConfig.element, observerConfig);
    (_editorObservers$get2 = editorObservers.get(editorElement)) === null || _editorObservers$get2 === void 0 ? void 0 : _editorObservers$get2.observe(observerConfig.element);

    // return clean up
    return () => {
      // consumer needs to unobserve on destroy if their element
      // was observed but never scrolled into view
      unObserveInternal(observerConfig.element);
    };
  };
  const initialiseNodeObserver = () => {
    if (editorObservers.has(editorElement)) {
      return;
    }
    const intersectionObserverOptions = {
      root: editorElement.closest(INTERSECTION_OBSERVER_ROOT_SELECTOR),
      rootMargin: '0px 0px 100px 0px',
      threshold: 0
    };
    const editorObserver = new IntersectionObserver(entries => entries.map(entry => ({
      entry,
      callback: callbackMap.get(entry.target)
    }))
    // Invoke callbacks together to group browser rendering
    // Avoiding requestAnimationFrame to reduce visual flickering
    .forEach(({
      entry,
      callback
    }) => {
      if (entry.isIntersecting) {
        callback === null || callback === void 0 ? void 0 : callback.onFirstVisible();
        if (entry.target instanceof HTMLElement) {
          // immediately unobserve the element after it is visible
          unObserveInternal(entry.target);
        }
      }
    }), intersectionObserverOptions);
    editorObservers.set(editorElement, editorObserver);
  };
  const disconnect = () => {
    var _editorObservers$get3;
    (_editorObservers$get3 = editorObservers.get(editorElement)) === null || _editorObservers$get3 === void 0 ? void 0 : _editorObservers$get3.disconnect();
    editorObservers.delete(editorElement);
  };
  return {
    initialiseNodeObserver,
    observe,
    disconnect
  };
};