"use client";
import * as React from "react";
import { cn } from "../../lib/utils";
import { ScrollFade } from "../primitives";
import {
observeBrowserResize,
subscribeBrowserWindowEvent,
} from "../primitives/browser-transport";
import {
sanitizeComposedHostProps,
sanitizeInteractionHostProps,
type SafeComposedHostElementProps,
} from "../primitives/sanitize-composed-host-props";
const panelDividerClassName =
"border-t border-[color:color-mix(in_oklab,var(--border)_8%,transparent)]";
const panelContentScrollFadeHeight = 44;
const panelContentViewportClassName =
"flex min-h-0 flex-col overflow-x-hidden overflow-y-auto overscroll-contain";
const panelSurfaceClassName =
"floating-popup-surface toolcraft-panel-surface isolate border text-[color:var(--popover-foreground)] supports-backdrop-filter:backdrop-blur-2xl supports-backdrop-filter:backdrop-saturate-150";
export const PanelSurface = React.forwardRef<
HTMLDivElement,
SafeComposedHostElementProps<"div">
>(function PanelSurface({ children, className, ...props }, ref) {
return (
{children}
);
});
export type PanelHoverSurfaceProps = SafeComposedHostElementProps<"div"> &
Pick, "onPointerEnter" | "onPointerLeave">;
export const PanelHoverSurface = React.forwardRef<
HTMLDivElement,
PanelHoverSurfaceProps
>(function PanelHoverSurface({ children, className, ...props }, ref) {
return (
{children}
);
});
type PanelCollapsibleBodySurfaceProps = SafeComposedHostElementProps<"div"> &
Pick, "onTransitionEnd">;
export const PanelCollapsibleBodySurface = React.forwardRef<
HTMLDivElement,
PanelCollapsibleBodySurfaceProps
>(function PanelCollapsibleBodySurface({ children, className, ...props }, ref) {
return (
{children}
);
});
export const PanelContentSurface = React.forwardRef<
HTMLDivElement,
SafeComposedHostElementProps<"div"> & {
scrollFadeMode?: "always" | "overflow";
stickyFooterActive?: boolean;
stickyFooterProgress?: number | null;
stickyFooter?: React.ReactNode;
}
>(function PanelContentSurface(
{
children,
className,
scrollFadeMode = "always",
stickyFooter,
stickyFooterActive = false,
stickyFooterProgress = null,
...props
},
ref,
) {
const [viewportElement, setViewportElement] =
React.useState(null);
const [hasOverflow, setHasOverflow] = React.useState(
scrollFadeMode === "always",
);
const attachViewport = React.useCallback(
(node: HTMLDivElement | null) => {
setViewportElement(node);
if (typeof ref === "function") {
ref(node);
return;
}
if (ref) {
ref.current = node;
}
},
[ref],
);
const hasStickyFooter = React.Children.count(stickyFooter) > 0;
React.useLayoutEffect(() => {
if (scrollFadeMode !== "overflow") {
setHasOverflow(true);
return;
}
if (!viewportElement) {
setHasOverflow(false);
return;
}
const updateOverflow = () => {
setHasOverflow(
viewportElement.scrollHeight > viewportElement.clientHeight + 1,
);
};
updateOverflow();
const unsubscribeResize = subscribeBrowserWindowEvent("resize", updateOverflow);
const contentNode = viewportElement.firstElementChild;
const unsubscribeResizeObserver = observeBrowserResize(
contentNode ? [viewportElement, contentNode] : [viewportElement],
updateOverflow,
);
return () => {
unsubscribeResize();
unsubscribeResizeObserver();
};
}, [scrollFadeMode, viewportElement]);
if (scrollFadeMode === "overflow" && !hasOverflow) {
const viewport = (
{children}
);
return hasStickyFooter ? (
{viewport}
) : (
viewport
);
}
const viewport = (
{children}
);
return hasStickyFooter ? (
{viewport}
) : (
viewport
);
});
function PanelContentWithStickyFooter({
children,
stickyFooter,
stickyFooterActive,
stickyFooterProgress,
}: {
children: React.ReactNode;
stickyFooter: React.ReactNode;
stickyFooterActive: boolean;
stickyFooterProgress: number | null;
}): React.JSX.Element {
const stickyFooterStyle =
typeof stickyFooterProgress === "number"
? ({
"--sticky-footer-progress": String(stickyFooterProgress),
} as React.CSSProperties)
: undefined;
return (
{children}
{stickyFooter}
);
}