"use client" import type { CSSProperties } from "react" import { useIsMobile } from "../hooks/use-mobile" import { useSidebarReflowZoom } from "../hooks/use-sidebar-reflow-zoom" import { cn } from "./utils" export type FloatingSheetSide = "top" | "right" | "bottom" | "left" /** * Starting width of a rail: `sm` 24rem, `md` 32rem, `lg` 40rem. * * This is only where the rail opens. The user can change it from the toolbar's * size menu or by dragging the inner edge, and both choices are remembered. */ export type FloatingSheetSize = "sm" | "md" | "lg" /** * Per-size desktop widths. Written out in full rather than composed, because * Tailwind only generates arbitrary values it can find as literal strings. */ export const FLOATING_SHEET_SIZE_WIDTH: Record = { // 24rem — filters, properties, a short option list. sm: "w-[min(24rem,calc(100vw-1rem))] max-w-none sm:max-w-none", // 32rem — single-column forms with more than a handful of fields. md: "w-[min(32rem,calc(100vw-1rem))] max-w-none sm:max-w-none", // 40rem — dense tables or side-by-side comparison. Past this, use a route. lg: "w-[min(40rem,calc(100vw-1rem))] max-w-none sm:max-w-none", } /** Labels for the toolbar's size menu. */ export const FLOATING_SHEET_SIZE_LABEL: Record = { sm: "Small", md: "Medium", lg: "Large", } export const FLOATING_SHEET_SIZES: readonly FloatingSheetSize[] = ["sm", "md", "lg"] /** * Default rail width, as a class. Kept for surfaces that borrow the rail * footprint without being a rail (`MarketingBanner`). */ export const FLOATING_SHEET_DESKTOP_WIDTH = FLOATING_SHEET_SIZE_WIDTH.sm /** * The same widths as inline `style` values. * * A utility class cannot win here: `SheetContent`'s base carries * `data-[side=right]:w-3/4`, which outranks a plain `w-[…]` on specificity, so * the rail silently rendered at the `size` max-width cap instead of its own * width. Inline beats both, and `maxWidth: none` clears the cap so `md` and * `lg` are not clipped back to the default. */ export const FLOATING_SHEET_SIZE_INLINE_WIDTH: Record = { sm: "min(24rem, calc(100vw - 1rem))", md: "min(32rem, calc(100vw - 1rem))", lg: "min(40rem, calc(100vw - 1rem))", } /** Mobile / ≥200% zoom — nearly full viewport; `!` beats SheetContent default `w-3/4`. */ export const FLOATING_SHEET_COMPACT_WIDTH = "!w-[calc(100vw-1rem)] max-w-none sm:max-w-none" export const FLOATING_SHEET_CHROME_CLASS = "flex flex-col border border-border bg-background shadow-[var(--shadow-sheet-panel)] rounded-xl overflow-hidden p-0 gap-0" export function floatingSheetWidthClass( compact: boolean, size: FloatingSheetSize = "sm", ): string { return compact ? FLOATING_SHEET_COMPACT_WIDTH : FLOATING_SHEET_SIZE_WIDTH[size] } /** * Storage key for a rail's dragged width, keyed by its `contentSlot`. * * Panel width is a shell-level display preference, not product data, so it * sits in the same unnamespaced `shell:` family as the secondary panel and * Ask Leo rail widths rather than under a product prefix. */ export function floatingSheetWidthStorageKey( contentSlot: string | undefined, ): string | undefined { return contentSlot ? `shell:sheet:${contentSlot}:width` : undefined } /** Storage key for a rail's chosen size, alongside its dragged width. */ export function floatingSheetSizeStorageKey( contentSlot: string | undefined, ): string | undefined { return contentSlot ? `shell:sheet:${contentSlot}:size` : undefined } /** True when floating sheets should expand to nearly full viewport width. */ export function useCompactFloatingSheet(): boolean { const isMobile = useIsMobile() const reflowZoom = useSidebarReflowZoom() return isMobile || reflowZoom } /** * Inset + sizing for floating product sheets (Export, Properties, invite, folder create): * `Sheet` + `showOverlay={false}` — no dimming layer; panel is inset with rounded corners. */ export function getFloatingSheetInsetProps( side: FloatingSheetSide, compact = false, size: FloatingSheetSize = "sm", ): { side: FloatingSheetSide className: string style: CSSProperties } { const widthClass = floatingSheetWidthClass(compact, size) // Compact rails pin their width with `!w-…`, which an inline value cannot // override, so leave the class in charge there. const widthStyle: CSSProperties = compact ? {} : { width: FLOATING_SHEET_SIZE_INLINE_WIDTH[size], maxWidth: "none" } switch (side) { case "right": return { side: "right", className: cn(FLOATING_SHEET_CHROME_CLASS, widthClass), style: { top: "0.5rem", bottom: "0.5rem", right: "0.5rem", left: "auto", height: "calc(100svh - 1rem)", ...widthStyle, }, } case "left": return { side: "left", className: cn(FLOATING_SHEET_CHROME_CLASS, widthClass), style: { top: "0.5rem", bottom: "0.5rem", left: "0.5rem", right: "auto", height: "calc(100svh - 1rem)", ...widthStyle, }, } case "bottom": return { side: "bottom", className: cn(FLOATING_SHEET_CHROME_CLASS, "max-h-[min(80vh,32rem)]", widthClass), style: { left: "0.5rem", right: "0.5rem", bottom: "0.5rem", top: "auto", height: "auto", }, } case "top": return { side: "top", className: cn(FLOATING_SHEET_CHROME_CLASS, "max-h-[min(80vh,32rem)]", widthClass), style: { left: "0.5rem", right: "0.5rem", top: "0.5rem", bottom: "auto", height: "auto", }, } } }