import {
  TOOLPAD_BRIDGE_GLOBAL
} from "./chunk-6QRXRVYC.mjs";
import {
  CanvasHooksContext,
  IS_RENDERED_IN_CANVAS,
  ToolpadApp,
  queryClient
} from "./chunk-UO6UIBP6.mjs";
import "./chunk-ZUJ4UAKT.mjs";

// src/canvas/index.tsx
import * as React from "react";
import invariant from "invariant";
import { throttle } from "lodash-es";
import { CanvasEventsContext } from "@mui/toolpad-core/runtime";

// src/canvas/getPageViewState.ts
import { RUNTIME_PROP_NODE_ID, RUNTIME_PROP_SLOTS } from "@mui/toolpad-core";

// src/utils/geometry.ts
function getRelativeBoundingRect(containerElm, childElm) {
  const containerRect = containerElm.getBoundingClientRect();
  const childRect = childElm.getBoundingClientRect();
  return {
    x: childRect.x - containerRect.x,
    y: childRect.y - containerRect.y,
    width: childRect.width,
    height: childRect.height
  };
}
function getRelativeOuterRect(containerElm, childElm) {
  const { x, y, width, height } = getRelativeBoundingRect(containerElm, childElm);
  const styles = window.getComputedStyle(childElm);
  const parseMarginStyle = (style) => style === "auto" ? 0 : parseFloat(style);
  let offsetLeft = parseMarginStyle(styles.marginLeft);
  let offsetRight = parseMarginStyle(styles.marginRight);
  let offsetTop = parseMarginStyle(styles.marginTop);
  let offsetBottom = parseMarginStyle(styles.marginBottom);
  if (styles.boxSizing === "content-box") {
    offsetLeft += parseFloat(styles.paddingLeft) + parseFloat(styles.borderLeftWidth);
    offsetRight += parseFloat(styles.paddingRight) + parseFloat(styles.borderRightWidth);
    offsetTop += parseFloat(styles.paddingTop) + parseFloat(styles.borderTopWidth);
    offsetBottom += parseFloat(styles.paddingBottom) + parseFloat(styles.borderBottomWidth);
  }
  return {
    x: x - offsetLeft,
    y: y - offsetTop,
    width: width + offsetLeft + offsetRight,
    height: height + offsetTop + offsetBottom
  };
}
function rectContainsPoint(rect, x, y) {
  return rect.x <= x && rect.x + rect.width >= x && rect.y <= y && rect.y + rect.height >= y;
}

// src/canvas/getPageViewState.ts
function getNodeViewInfo(fiber, viewElm, elm, nodeId, fiberHostProps) {
  if (nodeId) {
    const rect = getRelativeOuterRect(viewElm, elm);
    const props = fiber.child?.memoizedProps ?? {};
    return {
      nodeId,
      error: fiberHostProps.nodeError,
      componentConfig: fiberHostProps.componentConfig,
      rect,
      slots: {},
      props
    };
  }
  return null;
}
function walkFibers(node, visitor) {
  visitor(node);
  if (node.child) {
    walkFibers(node.child, visitor);
  }
  if (node.sibling) {
    walkFibers(node.sibling, visitor);
  }
}
function getNodesViewInfo(rootElm) {
  const devtoolsHook = rootElm.ownerDocument.defaultView?.__REACT_DEVTOOLS_GLOBAL_HOOK__;
  if (!devtoolsHook) {
    console.warn(`Can't read page layout as react devtools are not installed`);
    return { nodes: {} };
  }
  const nodes = {};
  const rendererId = 1;
  const nodeElms = /* @__PURE__ */ new Map();
  Array.from(devtoolsHook.getFiberRoots(rendererId)).forEach((fiberRoot) => {
    if (fiberRoot.current) {
      walkFibers(fiberRoot.current, (fiber) => {
        if (!fiber.memoizedProps) {
          return;
        }
        const nodeIdPropValue = fiber.memoizedProps[RUNTIME_PROP_NODE_ID];
        if (nodeIdPropValue) {
          const fiberHostProps = fiber.memoizedProps;
          const nodeId = fiberHostProps[RUNTIME_PROP_NODE_ID];
          if (nodes[nodeId]) {
            return;
          }
          const elm = devtoolsHook.renderers.get(rendererId)?.findHostInstanceByFiber(fiber);
          if (elm) {
            nodeElms.set(nodeId, elm);
            const info = getNodeViewInfo(fiber, rootElm, elm, nodeId, fiberHostProps);
            if (info) {
              nodes[nodeId] = info;
            }
          }
        }
        const slotNamePropValue = fiber.memoizedProps[RUNTIME_PROP_SLOTS];
        if (slotNamePropValue) {
          const slotType = fiber.memoizedProps.slotType;
          const parentId = fiber.memoizedProps.parentId;
          const nodeSlots = nodes[parentId]?.slots;
          if (nodeSlots && nodeSlots[slotNamePropValue]) {
            return;
          }
          const firstChildElm = devtoolsHook.renderers.get(rendererId)?.findHostInstanceByFiber(fiber);
          const childContainerElm = firstChildElm?.parentElement;
          if (childContainerElm && nodeSlots) {
            const rect = slotType === "single" ? getRelativeBoundingRect(rootElm, firstChildElm) : getRelativeBoundingRect(rootElm, childContainerElm);
            const display = window.getComputedStyle(childContainerElm).display;
            let flowDirection = "row";
            if (slotType === "layout") {
              flowDirection = "column";
            } else if (display === "grid") {
              const gridAutoFlow = window.getComputedStyle(childContainerElm).gridAutoFlow;
              flowDirection = gridAutoFlow === "row" ? "column" : "row";
            } else if (display === "flex") {
              flowDirection = window.getComputedStyle(childContainerElm).flexDirection;
            }
            nodeSlots[slotNamePropValue] = {
              type: slotType,
              rect,
              flowDirection
            };
          }
        }
      });
    }
  });
  return { nodes };
}
function getPageViewState(rootElm) {
  return getNodesViewInfo(rootElm);
}

// src/canvas/ToolpadBridge.tsx
import { Emitter } from "@mui/toolpad-utils/events";
var COMMAND_HANDLERS = Symbol("hidden property to hold the command handlers");
function createCommands(initial = {}) {
  return new Proxy(
    {
      [COMMAND_HANDLERS]: initial
    },
    {
      get(target, prop, receiver) {
        if (typeof prop !== "string") {
          return Reflect.get(target, prop, receiver);
        }
        return (...args) => {
          const handler = target[COMMAND_HANDLERS][prop];
          if (typeof handler !== "function") {
            throw new Error(`Command "${prop}" not recognized.`);
          }
          return handler(...args);
        };
      }
    }
  );
}
function setCommandHandler(commands, name, handler) {
  commands[COMMAND_HANDLERS][name] = handler;
}
var isRenderedInCanvas = typeof window !== "undefined" && window.frameElement?.dataset.toolpadCanvas;
var canvasIsReady = false;
var bridge = isRenderedInCanvas ? {
  editorEvents: new Emitter(),
  editorCommands: createCommands(),
  canvasEvents: new Emitter(),
  canvasCommands: createCommands({
    isReady: () => canvasIsReady,
    getPageViewState: () => {
      throw new Error("Not implemented");
    },
    getViewCoordinates: () => {
      throw new Error("Not implemented");
    },
    invalidateQueries: () => {
      throw new Error("Not implemented");
    },
    update: () => {
      throw new Error("Not implemented");
    }
  })
} : null;
bridge?.canvasEvents.on("ready", () => {
  canvasIsReady = true;
});
if (bridge) {
  if (typeof window[TOOLPAD_BRIDGE_GLOBAL] === "function") {
    window[TOOLPAD_BRIDGE_GLOBAL](bridge);
  }
  window[TOOLPAD_BRIDGE_GLOBAL] = bridge;
}

// src/canvas/index.tsx
var handleScreenUpdate = throttle(
  () => {
    bridge?.canvasEvents.emit("screenUpdate", {});
  },
  50,
  { trailing: true }
);
function AppCanvas({ basename, state: initialState }) {
  const [state, setState] = React.useState(initialState);
  const [readyBridge, setReadyBridge] = React.useState();
  const appRootRef = React.useRef();
  const appRootCleanupRef = React.useRef();
  const onAppRoot = React.useCallback((appRoot) => {
    appRootCleanupRef.current?.();
    appRootCleanupRef.current = void 0;
    if (!appRoot) {
      return;
    }
    appRootRef.current = appRoot;
    const mutationObserver = new MutationObserver(handleScreenUpdate);
    mutationObserver.observe(appRoot, {
      attributes: true,
      childList: true,
      subtree: true,
      characterData: true
    });
    const resizeObserver = new ResizeObserver(handleScreenUpdate);
    resizeObserver.observe(appRoot);
    appRoot.querySelectorAll("*").forEach((elm) => resizeObserver.observe(elm));
    appRootCleanupRef.current = () => {
      handleScreenUpdate.cancel();
      mutationObserver.disconnect();
      resizeObserver.disconnect();
    };
  }, []);
  React.useEffect(
    () => () => {
      appRootCleanupRef.current?.();
      appRootCleanupRef.current = void 0;
    },
    []
  );
  React.useEffect(() => {
    if (appRootRef.current) {
      handleScreenUpdate();
    }
  });
  React.useEffect(() => {
    if (!bridge) {
      return;
    }
    setCommandHandler(bridge.canvasCommands, "getPageViewState", () => {
      invariant(appRootRef.current, "App ref not attached");
      return getPageViewState(appRootRef.current);
    });
    setCommandHandler(bridge.canvasCommands, "getViewCoordinates", (clientX, clientY) => {
      if (!appRootRef.current) {
        return null;
      }
      const rect = appRootRef.current.getBoundingClientRect();
      if (rectContainsPoint(rect, clientX, clientY)) {
        return { x: clientX - rect.x, y: clientY - rect.y };
      }
      return null;
    });
    setCommandHandler(bridge.canvasCommands, "update", (newState) => {
      React.startTransition(() => setState(structuredClone(newState)));
    });
    setCommandHandler(bridge.canvasCommands, "invalidateQueries", () => {
      queryClient.invalidateQueries();
    });
    bridge.canvasEvents.emit("ready", {});
    setReadyBridge(bridge);
  }, []);
  const savedNodes = state?.savedNodes;
  const editorHooks = React.useMemo(() => {
    return {
      savedNodes
    };
  }, [savedNodes]);
  if (IS_RENDERED_IN_CANVAS) {
    return readyBridge ? /* @__PURE__ */ React.createElement(CanvasHooksContext.Provider, { value: editorHooks }, /* @__PURE__ */ React.createElement(CanvasEventsContext.Provider, { value: readyBridge.canvasEvents }, /* @__PURE__ */ React.createElement(ToolpadApp, { rootRef: onAppRoot, basename, state }))) : null;
  }
  return /* @__PURE__ */ React.createElement(ToolpadApp, { basename, state });
}
export {
  AppCanvas as default
};
//# sourceMappingURL=canvas.mjs.map