mirror of
https://github.com/block/buzz.git
synced 2026-10-30 05:51:46 +00:00
Signed-off-by: Taylor Ho <taylorkmho@gmail.com> Co-authored-by: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@sprout-oss.stage.blox.sqprod.co>
157 lines
4.3 KiB
TypeScript
157 lines
4.3 KiB
TypeScript
import * as React from "react";
|
|
|
|
import { useIsAuxiliaryPanelOverlay } from "@/shared/hooks/use-mobile";
|
|
import { AUXILIARY_PANEL_MIN_WIDTH_PX } from "@/shared/layout/auxiliaryPanelLayout";
|
|
import {
|
|
AuxiliaryPanelContext,
|
|
type AuxiliaryPanelLayout,
|
|
} from "@/shared/layout/auxiliaryPanelContext";
|
|
import { getAuxiliaryPanelMode } from "@/shared/layout/AuxiliaryPanelHeader";
|
|
import { cn } from "@/shared/lib/cn";
|
|
import {
|
|
OverlayPanelBackdrop,
|
|
PANEL_ENTER_BASE_CLASS,
|
|
PANEL_OVERLAY_CLASS,
|
|
} from "@/shared/ui/OverlayPanelBackdrop";
|
|
|
|
export type {
|
|
AuxiliaryPanelContextValue,
|
|
AuxiliaryPanelLayout,
|
|
} from "@/shared/layout/auxiliaryPanelContext";
|
|
export { useAuxiliaryPanel } from "@/shared/layout/auxiliaryPanelContext";
|
|
|
|
type AuxiliaryPanelProps = {
|
|
canResetWidth?: boolean;
|
|
children: React.ReactNode;
|
|
className?: string;
|
|
footer?: React.ReactNode;
|
|
header?: React.ReactNode;
|
|
isSinglePanelView?: boolean;
|
|
layout?: AuxiliaryPanelLayout;
|
|
onClose: () => void;
|
|
onResetWidth?: () => void;
|
|
onResizeStart?: React.PointerEventHandler<HTMLButtonElement>;
|
|
resizeHandleAriaLabel?: string;
|
|
resizeHandleTestId?: string;
|
|
siblings?: React.ReactNode;
|
|
/** When false, standalone width uses `widthPx` without min-width clamp. */
|
|
splitPaneClamp?: boolean;
|
|
testId?: string;
|
|
transparentChrome?: boolean;
|
|
widthPx: number;
|
|
};
|
|
|
|
/** Right-side auxiliary panel shell for split and standalone overlay layouts. */
|
|
export function AuxiliaryPanel({
|
|
canResetWidth,
|
|
children,
|
|
className,
|
|
footer,
|
|
header,
|
|
isSinglePanelView = false,
|
|
layout = "standalone",
|
|
onClose,
|
|
onResetWidth,
|
|
onResizeStart,
|
|
resizeHandleAriaLabel = "Resize panel",
|
|
resizeHandleTestId,
|
|
siblings,
|
|
splitPaneClamp = true,
|
|
testId,
|
|
transparentChrome = false,
|
|
widthPx,
|
|
}: AuxiliaryPanelProps) {
|
|
const isOverlay = useIsAuxiliaryPanelOverlay();
|
|
const isFloatingOverlay = isOverlay && !isSinglePanelView;
|
|
const isSplitLayout = layout === "split";
|
|
const mode = getAuxiliaryPanelMode(isSplitLayout, isFloatingOverlay);
|
|
|
|
const contextValue = React.useMemo(
|
|
() => ({
|
|
isFloatingOverlay,
|
|
isOverlay,
|
|
isSinglePanelView,
|
|
isSplitLayout,
|
|
layout,
|
|
mode,
|
|
onClose,
|
|
transparentChrome,
|
|
widthPx,
|
|
}),
|
|
[
|
|
isFloatingOverlay,
|
|
isOverlay,
|
|
isSinglePanelView,
|
|
isSplitLayout,
|
|
layout,
|
|
mode,
|
|
onClose,
|
|
transparentChrome,
|
|
widthPx,
|
|
],
|
|
);
|
|
|
|
const panelWidth = isSinglePanelView
|
|
? "100%"
|
|
: splitPaneClamp
|
|
? `min(${widthPx}px, calc(100% - ${AUXILIARY_PANEL_MIN_WIDTH_PX}px))`
|
|
: `${widthPx}px`;
|
|
|
|
const resizeHandle =
|
|
!isSplitLayout &&
|
|
!isOverlay &&
|
|
!isSinglePanelView &&
|
|
onResizeStart != null ? (
|
|
<button
|
|
aria-label={resizeHandleAriaLabel}
|
|
className="peer/auxiliary-panel-resize group/auxiliary-panel-resize absolute inset-y-0 left-0 z-40 w-3 -translate-x-1/2 cursor-col-resize"
|
|
data-testid={resizeHandleTestId}
|
|
onDoubleClick={canResetWidth ? onResetWidth : undefined}
|
|
onPointerDown={onResizeStart}
|
|
title={
|
|
canResetWidth
|
|
? "Drag to resize. Double-click to reset width."
|
|
: "Drag to resize."
|
|
}
|
|
type="button"
|
|
>
|
|
<span className="absolute bottom-0 left-1/2 top-10 w-px -translate-x-1/2 bg-transparent transition-colors group-hover/auxiliary-panel-resize:bg-border/80 group-focus-visible/auxiliary-panel-resize:bg-border/80" />
|
|
</button>
|
|
) : null;
|
|
|
|
if (isSplitLayout) {
|
|
return (
|
|
<AuxiliaryPanelContext.Provider value={contextValue}>
|
|
<div className={cn("flex min-h-0 flex-1 flex-col", className)}>
|
|
{header}
|
|
{children}
|
|
{footer}
|
|
</div>
|
|
{siblings}
|
|
</AuxiliaryPanelContext.Provider>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<AuxiliaryPanelContext.Provider value={contextValue}>
|
|
{isFloatingOverlay ? <OverlayPanelBackdrop onClose={onClose} /> : null}
|
|
<aside
|
|
className={cn(
|
|
PANEL_ENTER_BASE_CLASS,
|
|
isSinglePanelView && "border-l-0",
|
|
isFloatingOverlay && PANEL_OVERLAY_CLASS,
|
|
className,
|
|
)}
|
|
data-testid={testId}
|
|
style={{ width: panelWidth }}
|
|
>
|
|
{resizeHandle}
|
|
{header}
|
|
{children}
|
|
{footer}
|
|
</aside>
|
|
{siblings}
|
|
</AuxiliaryPanelContext.Provider>
|
|
);
|
|
}
|