// src/defineAsCustomElement.tsx
import React2 from "react";
import { render, unmountComponentAtNode } from "react-dom";

// src/hooks/CustomElementProvider.tsx
import React, { createElement, memo, useMemo } from "react";

// src/hooks/private/CustomElementContext.ts
import { createContext } from "react";
var CustomElementContext = createContext(
  new Proxy({}, {
    get() {
      throw new Error("This hook cannot be used outside of the custom element.");
    }
  })
);
var CustomElementContext_default = CustomElementContext;

// src/hooks/CustomElementProvider.tsx
var CustomElementProvider = ({
  componentType,
  customElement,
  props,
  setMethodCallback
}) => {
  const context = useMemo(
    () => ({
      customElementState: Object.freeze([customElement]),
      setMethodCallback
    }),
    [customElement, setMethodCallback]
  );
  return /* @__PURE__ */ React.createElement(CustomElementContext_default.Provider, { value: context }, createElement(componentType, props));
};
CustomElementProvider.displayName = "CustomElementProvider";
var MemoizedCustomElementProvider = memo(CustomElementProvider, (prevProps, nextProps) => {
  return Object.is(prevProps.componentType, nextProps.componentType) && Object.is(prevProps.customElement, nextProps.customElement) && Object.is(prevProps.setMethodCallback, nextProps.setMethodCallback) && Array.from(new Set(Object.keys(prevProps.props)).union(new Set(Object.keys(nextProps.props)))).every(
    (key) => Object.is(prevProps.props[key], nextProps.props[key])
  );
});
var CustomElementProvider_default = MemoizedCustomElementProvider;

// src/private/createReactCustomElement.ts
function createReactCustomElement(customElementConstructor) {
  const registry = new FinalizationRegistry((dispose) => dispose());
  return class ReactCustomElement extends (customElementConstructor || HTMLElement) {
    constructor(attributesMap, shadowRootInit, mountCallback, unmountCallback) {
      super();
      this.#attributesMap = attributesMap;
      this.#mountCallback = mountCallback;
      this.#unmountCallback = unmountCallback;
      this.#element = shadowRootInit ? this.attachShadow(shadowRootInit) : this;
      registry.register(new WeakRef(this), this[Symbol.dispose].bind(this));
    }
    #attributesMap;
    #connected = false;
    #element;
    #methodNonceMap = /* @__PURE__ */ new Map();
    #mountCallback;
    #propsMap = /* @__PURE__ */ new Map();
    #setMethodCallbackBound = this.#setMethodCallback.bind(this);
    #unmountCallback;
    #version = 0;
    [Symbol.dispose]() {
      this.#unmountCallback?.(this.#element);
      this.#unmountCallback = void 0;
    }
    #getProps() {
      return Object.freeze(Object.fromEntries(this.#propsMap.entries()));
    }
    #refresh() {
      if (this.#connected) {
        const version = ++this.#version;
        queueMicrotask(() => {
          if (version === this.#version) {
            const element = this.#element;
            element && this.#mountCallback(this.#getProps(), element, this.#setMethodCallbackBound);
          }
        });
      }
    }
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    #setMethodCallback(name, nonce, fn) {
      const existingNonce = this.#methodNonceMap.get(name);
      if (typeof existingNonce === "number" && existingNonce !== nonce) {
        throw new Error(`useMethodCallback('${name}') is already registered to another function.`);
      }
      this.#methodNonceMap.set(name, nonce);
      if (fn) {
        this[name] = (...args) => {
          return fn(...args);
        };
      } else {
        delete this[name];
      }
    }
    attributeChangedCallback(name, _oldValue, newValue) {
      const propName = this.#attributesMap[name];
      if (propName) {
        this.#propsMap.set(propName, newValue);
        this.#refresh();
      }
    }
    connectedCallback() {
      this.#connected = true;
      this.#refresh();
    }
    disconnectedCallback() {
      this.#connected = false;
    }
  };
}

// src/defineAsCustomElement.tsx
function defineAsCustomElement(componentType, tagName, attributesMap, init) {
  const observedAttributes = Object.freeze(Object.keys(attributesMap));
  customElements.define(
    tagName,
    class extends createReactCustomElement(init?.builtInElement?.customElementConstructor) {
      static get observedAttributes() {
        return observedAttributes;
      }
      constructor() {
        super(
          attributesMap,
          init?.shadowRoot,
          (props, element, setMethodCallback) => render(
            /* @__PURE__ */ React2.createElement(
              CustomElementProvider_default,
              {
                componentType,
                customElement: element,
                props,
                setMethodCallback
              }
            ),
            element
          ),
          (element) => unmountComponentAtNode(element)
        );
      }
    },
    init?.builtInElement?.extends ? { extends: init?.builtInElement?.extends } : {}
  );
}

// src/defineAsCustomElementWithPortal.tsx
import mathRandom from "math-random";
import React3, { Fragment, memo as memo2, useEffect, useState } from "react";
import { createPortal } from "react-dom";

// src/signalingState.ts
function signalingState(initialState) {
  let resolvers = Promise.withResolvers();
  let state = initialState;
  return {
    getState() {
      return state;
    },
    async next() {
      return resolvers.promise;
    },
    patchState(next) {
      state = next(state);
      const resolve = resolvers.resolve.bind(resolvers);
      resolvers = Promise.withResolvers();
      resolve();
    }
  };
}

// src/defineAsCustomElementWithPortal.tsx
function defineAsCustomElement2(componentType, tagName, attributesMap, init) {
  const { getState, next, patchState } = signalingState(/* @__PURE__ */ new Map());
  const observedAttributes = Object.freeze(Object.keys(attributesMap));
  customElements.define(
    tagName,
    class extends createReactCustomElement(init?.builtInElement?.customElementConstructor) {
      static get observedAttributes() {
        return observedAttributes;
      }
      constructor() {
        super(
          attributesMap,
          init?.shadowRoot,
          (props, element, setMethodCallback) => patchState(
            (map) => Object.freeze(new Map(map).set(this.#key, Object.freeze([element, props, setMethodCallback])))
          ),
          () => patchState((map) => {
            const nextMap = new Map(map);
            nextMap.delete(this.#key);
            return Object.freeze(nextMap);
          })
        );
      }
      #key = mathRandom().toString(36).substring(2);
    },
    init?.builtInElement?.extends ? { extends: init?.builtInElement?.extends } : {}
  );
  let portalMounted = false;
  const CustomElementsWrapperPortal = memo2(() => {
    const [instances, setInstances] = useState(/* @__PURE__ */ new Map());
    useEffect(() => {
      if (portalMounted) {
        throw new Error("Portal already mounted.");
      }
      portalMounted = true;
      let unmounted = false;
      (async () => {
        while (!unmounted) {
          const state = getState();
          typeof state !== "undefined" && setInstances(state);
          await next();
        }
      })();
      return () => {
        unmounted = true;
        portalMounted = false;
      };
    }, [setInstances]);
    return /* @__PURE__ */ React3.createElement(Fragment, null, Array.from(
      instances.entries().map(
        ([key, [element, props, setMethodCallback]]) => createPortal(
          /* @__PURE__ */ React3.createElement(
            CustomElementProvider_default,
            {
              componentType,
              customElement: element,
              props,
              setMethodCallback
            }
          ),
          element,
          key
        )
      )
    ));
  });
  CustomElementsWrapperPortal.displayName = "CustomElementsWrapperPortal";
  return { Portal: CustomElementsWrapperPortal };
}

// src/hooks/private/useCustomElementContext.ts
import { useContext } from "react";
function useCustomElementContext() {
  return useContext(CustomElementContext_default);
}

// src/hooks/useCustomElement.ts
function useCustomElement() {
  return useCustomElementContext().customElementState;
}

// src/hooks/useMethodCallback.ts
import mathRandom2 from "math-random";
import { useRef } from "react";
function useMethodCallback(name, fn) {
  const nonceRef = useRef(mathRandom2());
  useCustomElementContext().setMethodCallback(name, nonceRef.current, fn);
}
export {
  defineAsCustomElement,
  defineAsCustomElement2 as defineAsCustomElementWithPortal,
  useCustomElement,
  useMethodCallback
};
//# sourceMappingURL=react-define-as-custom-element.mjs.map