// src/Close.tsx
import Dialog from "@corvu/dialog";
var DrawerClose = (props) => {
  return <Dialog.Close
    data-corvu-drawer-close=""
    data-corvu-dialog-close={null}
    {...props}
  />;
};
var Close_default = DrawerClose;

// src/Content.tsx
import { dataIf } from "@corvu/utils";
import {
  batch,
  createEffect,
  createMemo,
  onCleanup,
  splitProps
} from "solid-js";

// src/lib.ts
var resolveSnapPoint = (snapPoint, drawerSize, index, breakPoints) => {
  if (index === void 0 || breakPoints === void 0) {
    return {
      value: snapPoint,
      offset: resolvePoint(snapPoint, drawerSize)
    };
  }
  const upperBreakPoint = breakPoints[index - 1] !== void 0 && breakPoints[index - 1] !== null ? resolvePoint(breakPoints[index - 1], drawerSize) : void 0;
  const lowerBreakPoint = breakPoints[index] !== void 0 && breakPoints[index] !== null ? resolvePoint(breakPoints[index], drawerSize) : void 0;
  return {
    value: snapPoint,
    offset: resolvePoint(snapPoint, drawerSize),
    lowerBreakPoint,
    upperBreakPoint
  };
};
var resolvePoint = (point, drawerSize) => {
  if (typeof point === "number") return drawerSize - point * drawerSize;
  if (!point.endsWith("px")) {
    throw new Error(
      `[corvu] Snap and break points must be a number or a string ending with 'px'. Got ${point}`
    );
  }
  return drawerSize - parseInt(point, 10);
};
var findClosestSnapPoint = (snapPoints, offset, offsetWithVelocity, allowSkippingSnapPoints) => {
  const upperSnapPoint = findNearbySnapPoint(
    "upper",
    snapPoints,
    allowSkippingSnapPoints ? offsetWithVelocity : offset
  );
  const lowerSnapPoint = findNearbySnapPoint(
    "lower",
    snapPoints,
    allowSkippingSnapPoints ? offsetWithVelocity : offset
  );
  if (!upperSnapPoint) return lowerSnapPoint;
  if (!lowerSnapPoint) return upperSnapPoint;
  if (lowerSnapPoint.upperBreakPoint === void 0 || upperSnapPoint.lowerBreakPoint === void 0) {
    return Math.abs(lowerSnapPoint.offset - offsetWithVelocity) < Math.abs(upperSnapPoint.offset - offsetWithVelocity) ? lowerSnapPoint : upperSnapPoint;
  }
  return offsetWithVelocity < upperSnapPoint.lowerBreakPoint ? lowerSnapPoint : upperSnapPoint;
};
var findNearbySnapPoint = (side, snapPoints, offset) => {
  return snapPoints.reduce(
    (previousSnapPoint, currentSnapPoint) => {
      if (side == "upper" && currentSnapPoint.offset >= offset && (!previousSnapPoint || currentSnapPoint.offset < previousSnapPoint.offset)) {
        return currentSnapPoint;
      } else if (side == "lower" && currentSnapPoint.offset <= offset && (!previousSnapPoint || currentSnapPoint.offset > previousSnapPoint.offset)) {
        return currentSnapPoint;
      }
      return previousSnapPoint;
    },
    void 0
  );
};
var locationIsDraggable = (location, stopAt, pointerType) => {
  let currentElement = location;
  let stopReached = false;
  do {
    if (currentElement.hasAttribute("data-corvu-no-drag") || currentElement.type === "range" || currentElement.tagName === "SELECT" && pointerType === "mouse")
      return false;
    if (currentElement === stopAt) {
      stopReached = true;
    } else {
      currentElement = currentElement.parentElement;
    }
  } while (currentElement && !stopReached);
  return true;
};

// src/Content.tsx
import { combineStyle } from "@corvu/utils/dom";
import Dialog2 from "@corvu/dialog";
import { getScrollAtLocation } from "@corvu/utils/scroll";

// src/context.ts
import { createContext, useContext } from "solid-js";
import {
  createKeyedContext,
  useKeyedContext
} from "@corvu/utils/create/keyedContext";
var DrawerContext = createContext();
var createDrawerContext = (contextId) => {
  if (contextId === void 0) return DrawerContext;
  const context = createKeyedContext(`drawer-${contextId}`);
  return context;
};
var useDrawerContext = (contextId) => {
  if (contextId === void 0) {
    const context2 = useContext(DrawerContext);
    if (!context2) {
      throw new Error(
        "[corvu]: Drawer context not found. Make sure to wrap Drawer components in <Drawer.Root>"
      );
    }
    return context2;
  }
  const context = useKeyedContext(`drawer-${contextId}`);
  if (!context) {
    throw new Error(
      `[corvu]: Drawer context with id "${contextId}" not found. Make sure to wrap Drawer components in <Drawer.Root contextId="${contextId}">`
    );
  }
  return context;
};
var InternalDrawerContext = createContext();
var createInternalDrawerContext = (contextId) => {
  if (contextId === void 0) return InternalDrawerContext;
  const context = createKeyedContext(
    `drawer-internal-${contextId}`
  );
  return context;
};
var useInternalDrawerContext = (contextId) => {
  if (contextId === void 0) {
    const context2 = useContext(InternalDrawerContext);
    if (!context2) {
      throw new Error(
        "[corvu]: Drawer context not found. Make sure to wrap Drawer components in <Drawer.Root>"
      );
    }
    return context2;
  }
  const context = useKeyedContext(
    `drawer-internal-${contextId}`
  );
  if (!context) {
    throw new Error(
      `[corvu]: Drawer context with id "${contextId}" not found. Make sure to wrap Drawer components in <Drawer.Root contextId="${contextId}">`
    );
  }
  return context;
};

// src/Content.tsx
var DrawerContent = (props) => {
  const [localProps, otherProps] = splitProps(props, [
    "contextId",
    "style"
  ]);
  let pointerDown = false;
  let dragStartPos = null;
  let currentPointerStart = [0, 0];
  let cachedMoveTimestamp;
  let cachedTranslate = 0;
  const drawerContext = createMemo(
    () => useInternalDrawerContext(localProps.contextId)
  );
  const dialogContext = createMemo(
    () => Dialog2.useContext(localProps.contextId)
  );
  const snapPoints = createMemo(
    () => drawerContext().snapPoints().map(
      (snapPoint, index) => resolveSnapPoint(
        snapPoint,
        drawerContext().drawerSize(),
        index,
        drawerContext().breakPoints()
      )
    )
  );
  const transformValue = createMemo(() => {
    switch (drawerContext().side()) {
      case "top":
        return `translate3d(0, ${-drawerContext().translate()}px, 0)`;
      case "bottom":
        return `translate3d(0, ${drawerContext().translate()}px, 0)`;
      case "right":
        return `translate3d(${drawerContext().translate()}px, 0, 0)`;
      case "left":
        return `translate3d(${-drawerContext().translate()}px, 0, 0)`;
    }
  });
  const transitionHeight = createMemo(() => {
    const transitionSize = drawerContext().transitionSize();
    if (transitionSize === null) return void 0;
    switch (drawerContext().side()) {
      case "top":
      case "bottom":
        return `${transitionSize}px`;
    }
    return void 0;
  });
  const transitionWidth = createMemo(() => {
    const transitionSize = drawerContext().transitionSize();
    if (transitionSize === null) return void 0;
    switch (drawerContext().side()) {
      case "left":
      case "right":
        return `${transitionSize}px`;
    }
    return void 0;
  });
  createEffect(() => {
    if (!dialogContext().open()) return;
    document.addEventListener("pointermove", onPointerMove);
    document.addEventListener("touchmove", onTouchMove);
    document.addEventListener("pointerup", onPointerUp);
    document.addEventListener("touchend", onTouchEnd);
    document.addEventListener("contextmenu", onUp);
    onCleanup(() => {
      document.removeEventListener("pointermove", onPointerMove);
      document.removeEventListener("touchmove", onTouchMove);
      document.removeEventListener("pointerup", onPointerUp);
      document.removeEventListener("touchend", onTouchEnd);
      document.removeEventListener("contextmenu", onUp);
    });
  });
  createEffect(() => {
    if (drawerContext().transitionState() === "closing") {
      drawerContext().setIsDragging(false);
    }
  });
  const onPointerDown = (event) => {
    if (event.button !== 0) return;
    if (!locationIsDraggable(
      event.target,
      dialogContext().contentRef(),
      event.pointerType
    ))
      return;
    if (drawerContext().transitionState() === "closing") return;
    pointerDown = true;
    if (drawerContext().handleScrollableElements()) {
      currentPointerStart = [event.clientX, event.clientY];
    }
  };
  const onPointerMove = (event) => {
    onMove(event.target, event.clientX, event.clientY);
  };
  const onTouchMove = (event) => {
    if (!event.touches[0]) return;
    onMove(
      event.target,
      event.touches[0].clientX,
      event.touches[0].clientY
    );
  };
  const onMove = (target, x, y) => {
    if (!pointerDown) return;
    if (!drawerContext().isDragging() || dragStartPos === null) {
      const selection = window.getSelection();
      if (selection && selection.toString().length > 0) {
        onUp();
        return;
      }
      if (drawerContext().handleScrollableElements()) {
        const delta2 = [x, y].map(
          (pointer, i) => currentPointerStart[i] - pointer
        );
        const axis = Math.abs(delta2[0]) > Math.abs(delta2[1]) ? "x" : "y";
        const axisDelta = axis === "x" ? delta2[0] : delta2[1];
        if (Math.abs(axisDelta) < 0.3) return;
        const wrapper = dialogContext().contentRef();
        const [availableScroll, availableScrollTop] = getScrollAtLocation(
          target,
          axis,
          wrapper
        );
        if (axisDelta > 0 && Math.abs(availableScroll) > 1 || axisDelta < 0 && Math.abs(availableScrollTop) > 0) {
          onUp();
          return;
        }
      }
      switch (drawerContext().side()) {
        case "top":
        case "bottom":
          dragStartPos = y;
          break;
        case "right":
        case "left":
          dragStartPos = x;
      }
      cachedMoveTimestamp = /* @__PURE__ */ new Date();
      cachedTranslate = drawerContext().translate();
      batch(() => {
        drawerContext().setIsDragging(true);
        drawerContext().setTransitionState(null);
      });
    }
    let delta;
    switch (drawerContext().side()) {
      case "top":
        delta = -(dragStartPos - y);
        break;
      case "bottom":
        delta = dragStartPos - y;
        break;
      case "right":
        delta = dragStartPos - x;
        break;
      case "left":
        delta = -(dragStartPos - x);
        break;
    }
    delta -= drawerContext().resolvedActiveSnapPoint().offset;
    if (delta > 0) delta = drawerContext().dampFunction(delta);
    const now = /* @__PURE__ */ new Date();
    if (now.getTime() - cachedMoveTimestamp.getTime() > drawerContext().velocityCacheReset()) {
      cachedMoveTimestamp = now;
      cachedTranslate = drawerContext().translate();
    }
    drawerContext().setTranslate(-delta);
  };
  const onPointerUp = (event) => {
    if (event.pointerType !== "touch") onUp();
  };
  const onTouchEnd = (event) => {
    if (event.touches.length === 0) onUp();
  };
  const onUp = () => {
    pointerDown = false;
    if (!drawerContext().isDragging()) return;
    const now = /* @__PURE__ */ new Date();
    const velocity = drawerContext().velocityFunction(
      -(cachedTranslate - drawerContext().translate()),
      now.getTime() - cachedMoveTimestamp.getTime() || 1
    );
    const translateWithVelocity = drawerContext().translate() * velocity;
    const closestSnapPoint = findClosestSnapPoint(
      snapPoints(),
      drawerContext().translate(),
      translateWithVelocity,
      drawerContext().allowSkippingSnapPoints()
    );
    batch(() => {
      drawerContext().setTransitionState("snapping");
      drawerContext().setIsDragging(false);
    });
    batch(() => {
      drawerContext().setActiveSnapPoint(closestSnapPoint.value);
      if (closestSnapPoint.offset === drawerContext().drawerSize()) {
        dialogContext().setOpen(false);
      } else {
        drawerContext().setTranslate(closestSnapPoint.offset);
        const transitionDuration = parseFloat(
          drawerContext().drawerStyles().transitionDuration
        );
        if (transitionDuration === 0) {
          drawerContext().setTransitionState(null);
        }
      }
    });
  };
  return <Dialog2.Content
    contextId={localProps.contextId}
    style={combineStyle(
      {
        transform: transformValue(),
        "transition-duration": drawerContext().isDragging() ? "0ms" : void 0,
        height: transitionHeight(),
        width: transitionWidth()
      },
      localProps.style
    )}
    onPointerDown={onPointerDown}
    onTouchStart={(e) => {
      if (e.touches.length !== 1) return;
      dragStartPos = null;
    }}
    onTransitionEnd={(e) => {
      if (e.target !== dialogContext().contentRef()) return;
      batch(() => {
        if (drawerContext().transitionState() === "closing") {
          drawerContext().closeDrawer();
        }
        if (drawerContext().transitionState() !== "resizing") {
          drawerContext().setTransitionState(null);
        }
      });
    }}
    data-closing={dataIf(drawerContext().transitionState() === "closing")}
    data-opening={dataIf(drawerContext().transitionState() === "opening")}
    data-resizing={dataIf(drawerContext().transitionState() === "resizing")}
    data-snapping={dataIf(drawerContext().transitionState() === "snapping")}
    data-transitioning={dataIf(drawerContext().isTransitioning())}
    data-corvu-drawer-content=""
    data-corvu-dialog-content={null}
    {...otherProps}
  />;
};
var Content_default = DrawerContent;

// src/Description.tsx
import Dialog3 from "@corvu/dialog";
var DrawerDescription = (props) => {
  return <Dialog3.Description
    data-corvu-drawer-description=""
    data-corvu-dialog-description={null}
    {...props}
  />;
};
var Description_default = DrawerDescription;

// src/index.ts
import {
  Portal,
  useContext as useDialogContext
} from "@corvu/dialog";

// src/Label.tsx
import Dialog4 from "@corvu/dialog";
var DrawerLabel = (props) => {
  return <Dialog4.Label
    data-corvu-drawer-label=""
    data-corvu-dialog-label={null}
    {...props}
  />;
};
var Label_default = DrawerLabel;

// src/Overlay.tsx
import {
  createMemo as createMemo2,
  splitProps as splitProps2
} from "solid-js";
import Dialog5 from "@corvu/dialog";
import { dataIf as dataIf2 } from "@corvu/utils";
var DrawerOverlay = (props) => {
  const [localProps, otherProps] = splitProps2(props, [
    "contextId"
  ]);
  const drawerContext = createMemo2(
    () => useInternalDrawerContext(localProps.contextId)
  );
  return <Dialog5.Overlay
    contextId={localProps.contextId}
    data-closing={dataIf2(drawerContext().transitionState() === "closing")}
    data-opening={dataIf2(drawerContext().transitionState() === "opening")}
    data-resizing={dataIf2(drawerContext().transitionState() === "resizing")}
    data-snapping={dataIf2(drawerContext().transitionState() === "snapping")}
    data-transitioning={dataIf2(drawerContext().isTransitioning())}
    data-corvu-drawer-overlay=""
    data-corvu-dialog-overlay={null}
    {...otherProps}
  />;
};
var Overlay_default = DrawerOverlay;

// src/Root.tsx
import {
  batch as batch2,
  createEffect as createEffect2,
  createMemo as createMemo3,
  createSignal,
  mergeProps,
  splitProps as splitProps3,
  untrack
} from "solid-js";
import { isFunction } from "@corvu/utils";
import { afterPaint } from "@corvu/utils/dom";
import createControllableSignal from "@corvu/utils/create/controllableSignal";
import createOnce from "@corvu/utils/create/once";
import createSize from "@corvu/utils/create/size";
import createTransitionSize from "solid-transition-size";
import { createWritableMemo } from "@solid-primitives/memo";
import Dialog6 from "@corvu/dialog";
var DrawerRoot = (props) => {
  const defaultedProps = mergeProps(
    {
      initialOpen: false,
      snapPoints: [0, 1],
      breakPoints: [null],
      defaultSnapPoint: 1,
      side: "bottom",
      dampFunction: (distance) => 6 * Math.log(distance + 1),
      velocityFunction: (distance, time) => {
        const velocity = distance / time;
        return velocity < 1 && velocity > -1 ? 1 : velocity;
      },
      velocityCacheReset: 200,
      allowSkippingSnapPoints: true,
      handleScrollableElements: true,
      transitionResize: false,
      closeOnOutsidePointer: true,
      allowPinchZoom: false
    },
    props
  );
  const [localProps, otherProps] = splitProps3(defaultedProps, [
    "snapPoints",
    "breakPoints",
    "defaultSnapPoint",
    "activeSnapPoint",
    "onActiveSnapPointChange",
    "side",
    "dampFunction",
    "velocityFunction",
    "velocityCacheReset",
    "allowSkippingSnapPoints",
    "handleScrollableElements",
    "transitionResize",
    "open",
    "initialOpen",
    "onOpenChange",
    "closeOnOutsidePointer",
    "contextId",
    "children"
  ]);
  const [open, setOpen] = createControllableSignal({
    value: () => localProps.open,
    initialValue: localProps.initialOpen,
    onChange: localProps.onOpenChange
  });
  const [activeSnapPoint, setActiveSnapPoint] = createControllableSignal({
    value: () => localProps.activeSnapPoint,
    initialValue: 0,
    onChange: localProps.onActiveSnapPointChange
  });
  const [dialogContext, setDialogContext] = createSignal();
  const { transitioning: sizeTransitioning, transitionSize } = createTransitionSize({
    element: () => dialogContext()?.contentRef() ?? null,
    enabled: () => open() && localProps.transitionResize,
    dimension: () => {
      switch (localProps.side) {
        case "top":
        case "bottom":
          return "height";
        case "left":
        case "right":
          return "width";
      }
    }
  });
  const [isDragging, setIsDragging] = createSignal(false);
  const [transitionState, setTransitionState] = createWritableMemo(() => {
    if (sizeTransitioning()) return "resizing";
    return null;
  });
  const drawerStyles = createMemo3(() => {
    const contentRef = dialogContext()?.contentRef();
    if (!contentRef) return void 0;
    return getComputedStyle(contentRef);
  });
  const [transitionAwareOpen, setTransitionAwareOpen] = createSignal(false);
  createEffect2(() => {
    const _open = open();
    untrack(() => {
      if (transitionAwareOpen() === _open) {
        return;
      }
      if (_open) {
        setTransitionAwareOpen(true);
        afterPaint(() => {
          batch2(() => {
            setTransitionState("opening");
            setActiveSnapPoint(localProps.defaultSnapPoint);
          });
          const transitionDuration = parseFloat(
            drawerStyles().transitionDuration
          );
          if (transitionDuration === 0) {
            setTransitionState(null);
          }
        });
      } else {
        batch2(() => {
          setTransitionState("closing");
          setActiveSnapPoint(0);
        });
        afterPaint(() => {
          const transitionDuration = parseFloat(
            drawerStyles().transitionDuration
          );
          if (transitionDuration === 0) {
            closeDrawer();
          }
        });
      }
    });
  });
  const closeDrawer = () => {
    batch2(() => {
      setTransitionAwareOpen(false);
      setTransitionState(null);
    });
  };
  const drawerSize = createSize({
    element: () => dialogContext()?.contentRef() ?? null,
    dimension: () => {
      switch (localProps.side) {
        case "top":
        case "bottom":
          return "height";
        case "left":
        case "right":
          return "width";
      }
    }
  });
  const resolvedActiveSnapPoint = createMemo3(
    () => resolveSnapPoint(activeSnapPoint(), drawerSize())
  );
  const [translate, setTranslate] = createWritableMemo(
    () => resolvedActiveSnapPoint().offset
  );
  const openPercentage = createMemo3(() => {
    if (!drawerSize()) return 0;
    return (drawerSize() - translate()) / drawerSize();
  });
  const childrenProps = {
    get snapPoints() {
      return localProps.snapPoints;
    },
    get breakPoints() {
      return localProps.breakPoints;
    },
    get defaultSnapPoint() {
      return localProps.defaultSnapPoint;
    },
    get activeSnapPoint() {
      return activeSnapPoint();
    },
    setActiveSnapPoint,
    get side() {
      return localProps.side;
    },
    get isDragging() {
      return isDragging();
    },
    get isTransitioning() {
      return transitionState() !== null;
    },
    get transitionState() {
      return transitionState();
    },
    get openPercentage() {
      return openPercentage();
    },
    get translate() {
      return translate();
    },
    get velocityCacheReset() {
      return localProps.velocityCacheReset;
    },
    get allowSkippingSnapPoints() {
      return localProps.allowSkippingSnapPoints;
    },
    get handleScrollableElements() {
      return localProps.handleScrollableElements;
    },
    get transitionResize() {
      return localProps.transitionResize;
    }
  };
  const memoizedChildren = createOnce(() => localProps.children);
  const resolveChildren = (dialogChildrenProps) => {
    setDialogContext(Dialog6.useContext(localProps.contextId));
    const children = memoizedChildren()();
    if (isFunction(children)) {
      const mergedProps = mergeProps(dialogChildrenProps, childrenProps);
      return children(mergedProps);
    }
    return children;
  };
  const memoizedDrawerRoot = createMemo3(() => {
    const DrawerContext2 = createDrawerContext(localProps.contextId);
    const InternalDrawerContext2 = createInternalDrawerContext(
      localProps.contextId
    );
    return untrack(() => <DrawerContext2.Provider
      value={{
        snapPoints: () => localProps.snapPoints,
        breakPoints: () => localProps.breakPoints,
        defaultSnapPoint: () => localProps.defaultSnapPoint,
        activeSnapPoint,
        setActiveSnapPoint,
        side: () => localProps.side,
        isDragging,
        isTransitioning: () => transitionState() !== null,
        transitionState,
        openPercentage,
        translate,
        velocityCacheReset: () => localProps.velocityCacheReset,
        allowSkippingSnapPoints: () => localProps.allowSkippingSnapPoints,
        handleScrollableElements: () => localProps.handleScrollableElements,
        transitionResize: () => localProps.transitionResize
      }}
    >
        <InternalDrawerContext2.Provider
      value={{
        snapPoints: () => localProps.snapPoints,
        breakPoints: () => localProps.breakPoints,
        defaultSnapPoint: () => localProps.defaultSnapPoint,
        activeSnapPoint,
        setActiveSnapPoint,
        side: () => localProps.side,
        isDragging,
        isTransitioning: () => transitionState() !== null,
        transitionState,
        openPercentage,
        translate,
        velocityCacheReset: () => localProps.velocityCacheReset,
        allowSkippingSnapPoints: () => localProps.allowSkippingSnapPoints,
        handleScrollableElements: () => localProps.handleScrollableElements,
        transitionResize: () => localProps.transitionResize,
        dampFunction: localProps.dampFunction,
        velocityFunction: localProps.velocityFunction,
        setIsDragging,
        setTranslate,
        drawerSize,
        resolvedActiveSnapPoint,
        drawerStyles,
        setTransitionState,
        transitionSize,
        closeDrawer
      }}
    >
          <Dialog6
      open={transitionAwareOpen()}
      onOpenChange={setOpen}
      contextId={localProps.contextId}
      closeOnOutsidePointer={!isDragging() && localProps.closeOnOutsidePointer}
      {...otherProps}
    >
            {(dialogChildrenProps) => resolveChildren(dialogChildrenProps)}
          </Dialog6>
        </InternalDrawerContext2.Provider>
      </DrawerContext2.Provider>);
  });
  return memoizedDrawerRoot;
};
var Root_default = DrawerRoot;

// src/Trigger.tsx
import Dialog7 from "@corvu/dialog";
var DrawerTrigger = (props) => {
  return <Dialog7.Trigger
    data-corvu-drawer-trigger=""
    data-corvu-dialog-trigger={null}
    {...props}
  />;
};
var Trigger_default = DrawerTrigger;

// src/index.ts
var Drawer = Object.assign(Root_default, {
  Trigger: Trigger_default,
  Portal,
  Overlay: Overlay_default,
  Content: Content_default,
  Label: Label_default,
  Description: Description_default,
  Close: Close_default,
  useContext: useDrawerContext,
  useDialogContext
});
var index_default = Drawer;
export {
  Close_default as Close,
  Content_default as Content,
  Description_default as Description,
  Label_default as Label,
  Overlay_default as Overlay,
  Portal,
  Root_default as Root,
  Trigger_default as Trigger,
  index_default as default,
  useDrawerContext as useContext,
  useDialogContext
};
