fix(webapp): reserve sheet close-button space only inside a sheet

UpsertScheduleForm and ScheduleInspector each render both inside a sheet
and as a standalone page. Reserving room for the sheet's floating close
button unconditionally left an empty gap on the standalone edit route.

SheetContent now publishes whether it rendered the button via context and
the headers that sit under it read that, which also replaces the "has
header actions" stand-in for "am I in a sheet".

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Claude
2026-08-11 15:19:44 +00:00
parent e2a727da5a
commit e83beb4d09
3 changed files with 42 additions and 16 deletions
@@ -54,6 +54,17 @@ interface SheetContentProps
showCloseButton?: boolean;
}
const SheetCloseButtonContext = React.createContext(false);
/**
* True when an enclosing sheet renders its own floating close button, so a header
* rendered inside it has to leave room for it. False outside a sheet, which is why
* components used both in a sheet and as a full page can rely on it.
*/
export function useSheetHasCloseButton() {
return React.useContext(SheetCloseButtonContext);
}
const SheetContent = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Content>,
SheetContentProps
@@ -72,7 +83,9 @@ const SheetContent = React.forwardRef<
</SheetPrimitive.Close>
</div>
)}
{children}
<SheetCloseButtonContext.Provider value={showCloseButton}>
{children}
</SheetCloseButtonContext.Provider>
</SheetPrimitive.Content>
</SheetPortal>
));
@@ -94,18 +107,23 @@ SheetFooter.displayName = "SheetFooter";
const SheetTitle = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
>(({ className, children, ...props }, ref) => (
<SheetPrimitive.Title
ref={ref}
className={cn(
"sticky top-0 flex items-center justify-between border-b border-grid-bright bg-background-dimmed pb-1.5 pl-3 pr-16 pt-2",
className
)}
{...props}
>
{children}
</SheetPrimitive.Title>
));
>(({ className, children, ...props }, ref) => {
const hasCloseButton = useSheetHasCloseButton();
return (
<SheetPrimitive.Title
ref={ref}
className={cn(
"sticky top-0 flex items-center justify-between border-b border-grid-bright bg-background-dimmed pb-1.5 pl-3 pt-2",
hasCloseButton ? "pr-16" : "pr-1.5",
className
)}
{...props}
>
{children}
</SheetPrimitive.Title>
);
});
SheetTitle.displayName = SheetPrimitive.Title.displayName;
const SheetDescription = React.forwardRef<
@@ -23,6 +23,7 @@ import { Header2, Header3 } from "~/components/primitives/Headers";
import { InfoPanel } from "~/components/primitives/InfoPanel";
import { Paragraph } from "~/components/primitives/Paragraph";
import * as Property from "~/components/primitives/PropertyTable";
import { useSheetHasCloseButton } from "~/components/primitives/SheetV3";
import {
Table,
TableBlankRow,
@@ -96,6 +97,7 @@ export function ScheduleInspector({
const organization = useOrganization();
const project = useProject();
const environment = useEnvironment();
const hasSheetCloseButton = useSheetHasCloseButton();
const isUtc = schedule.timezone === "UTC";
const isImperative = schedule.type === "IMPERATIVE";
@@ -110,8 +112,7 @@ export function ScheduleInspector({
<div
className={cn(
"mx-3 flex items-center justify-between gap-2 border-b border-grid-dimmed",
// Without header actions the sheet's own floating close button sits here.
!headerActions && "pr-14"
hasSheetCloseButton && "pr-14"
)}
>
<Header2 className="truncate">{schedule.friendlyId}</Header2>
@@ -40,6 +40,7 @@ import {
TableRow,
} from "~/components/primitives/Table";
import { TextLink } from "~/components/primitives/TextLink";
import { useSheetHasCloseButton } from "~/components/primitives/SheetV3";
import { TimezoneList } from "~/components/scheduled/timezones";
import { prisma } from "~/db.server";
import { useEnvironment } from "~/hooks/useEnvironment";
@@ -156,6 +157,7 @@ export function UpsertScheduleForm({
/** Submits via this fetcher with `_format=json` so the host can toast/close itself. */
submitFetcher?: FetcherWithComponents<unknown>;
}) {
const hasSheetCloseButton = useSheetHasCloseButton();
const actionData = useActionData();
// Only feed conform-shaped data (`status`) to `useForm` — `{ ok, message }`
// envelopes lack it and crash conform.
@@ -231,7 +233,12 @@ export function UpsertScheduleForm({
{...getFormProps(form)}
className="grid h-full max-h-full grid-rows-[2.5rem_1fr_auto] overflow-hidden bg-background-bright"
>
<div className="mx-3 flex min-w-0 items-center justify-between gap-2 overflow-hidden border-b border-grid-dimmed pr-14">
<div
className={cn(
"mx-3 flex min-w-0 items-center justify-between gap-2 overflow-hidden border-b border-grid-dimmed",
hasSheetCloseButton && "pr-14"
)}
>
<Header2 className="truncate">
{schedule?.friendlyId
? "Edit schedule"