mirror of
https://github.com/temetro/temetro.git
synced 2026-07-26 11:58:14 +00:00
5961d969b1
The Clinician profile frame inset its content 40px and drew a second
border around it — a box inside a box. Both came from CardFramePanel,
added in 44d653f. It doesn't exist in the COSS registry: upstream
@coss/card ships Card, CardFrame, CardFrameHeader/Title/Description/
Action/Footer and nothing else. There is no body part because the frame
isn't meant to pad its body.
CardFrame styles its cards through *direct-child* selectors — -m-px,
the clip-path, rounded-t/b-xl, shadow-none, before:hidden — pulling each
card out by a pixel so it sits flush inside the frame's own border. The
panel wrapper made every card a grandchild, so none of those selectors
matched: the 20px came from the panel's p-5, the second border came from
the card keeping the treatment the frame was supposed to suppress. The
commit that added it claimed the cards would "pick up the frame's card
treatment"; they never did.
Drop the panel and render children directly inside the frame, which is
the COSS default. Body padding goes on the Card, where COSS puts it, so
content is inset once rather than twice. Card-bearing bodies now get the
frame treatment they were meant to have, and the divide-y lists in
Records, Care team and Signing stack flush, which is what -m-px and
rounded-t-xl were for. The notification ToggleRows lose their space-y
wrapper and read as one list.
Frames holding raw form content (the AI panel, Care team's error and
toolbar) get an explicit padded Card instead of leaning on the frame.
card.tsx is now upstream @coss/card verbatim, minus font-heading on
CardTitle.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
254 lines
6.9 KiB
TypeScript
254 lines
6.9 KiB
TypeScript
"use client";
|
|
|
|
import { mergeProps } from "@base-ui/react/merge-props";
|
|
import { useRender } from "@base-ui/react/use-render";
|
|
import type React from "react";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
export function Card({
|
|
className,
|
|
render,
|
|
...props
|
|
}: useRender.ComponentProps<"div">): React.ReactElement {
|
|
const defaultProps = {
|
|
className: cn(
|
|
"relative flex flex-col rounded-2xl border bg-card not-dark:bg-clip-padding text-card-foreground shadow-xs/5 before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-2xl)-1px)] before:shadow-[0_1px_--theme(--color-black/4%)] dark:before:shadow-[0_-1px_--theme(--color-white/6%)]",
|
|
className,
|
|
),
|
|
"data-slot": "card",
|
|
};
|
|
|
|
return useRender({
|
|
defaultTagName: "div",
|
|
props: mergeProps<"div">(defaultProps, props),
|
|
render,
|
|
});
|
|
}
|
|
|
|
export function CardFrame({
|
|
className,
|
|
render,
|
|
...props
|
|
}: useRender.ComponentProps<"div">): React.ReactElement {
|
|
const defaultProps = {
|
|
className: cn(
|
|
"relative flex flex-col rounded-2xl border bg-card not-dark:bg-clip-padding text-card-foreground shadow-xs/5 [--clip-bottom:-1rem] [--clip-top:-1rem] before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-2xl)-1px)] before:bg-muted/72 before:shadow-[0_1px_--theme(--color-black/4%)] has-data-[slot=table-container]:overflow-hidden *:data-[slot=card]:-m-px *:data-[slot=table-container]:-m-px *:data-[slot=table-container]:w-[calc(100%+2px)] *:not-first:data-[slot=card]:rounded-t-xl *:not-last:data-[slot=card]:rounded-b-xl *:data-[slot=card]:bg-clip-padding *:data-[slot=card]:shadow-none *:data-[slot=card]:before:hidden *:not-first:data-[slot=card]:before:rounded-t-[calc(var(--radius-xl)-1px)] *:not-last:data-[slot=card]:before:rounded-b-[calc(var(--radius-xl)-1px)] dark:before:shadow-[0_-1px_--theme(--color-white/6%)] *:data-[slot=card]:[clip-path:inset(var(--clip-top)_1px_var(--clip-bottom)_1px_round_calc(var(--radius-2xl)-1px))] *:data-[slot=card]:last:[--clip-bottom:1px] *:data-[slot=card]:first:[--clip-top:1px]",
|
|
className,
|
|
),
|
|
"data-slot": "card-frame",
|
|
};
|
|
|
|
return useRender({
|
|
defaultTagName: "div",
|
|
props: mergeProps<"div">(defaultProps, props),
|
|
render,
|
|
});
|
|
}
|
|
|
|
export function CardFrameHeader({
|
|
className,
|
|
render,
|
|
...props
|
|
}: useRender.ComponentProps<"div">): React.ReactElement {
|
|
const defaultProps = {
|
|
className: cn(
|
|
"relative flex grid auto-rows-min grid-rows-[auto_auto] flex-col items-start gap-x-4 px-6 py-4 has-data-[slot=card-frame-action]:grid-cols-[1fr_auto]",
|
|
className,
|
|
),
|
|
"data-slot": "card-frame-header",
|
|
};
|
|
|
|
return useRender({
|
|
defaultTagName: "div",
|
|
props: mergeProps<"div">(defaultProps, props),
|
|
render,
|
|
});
|
|
}
|
|
|
|
export function CardFrameTitle({
|
|
className,
|
|
render,
|
|
...props
|
|
}: useRender.ComponentProps<"div">): React.ReactElement {
|
|
const defaultProps = {
|
|
className: cn("self-center font-semibold text-sm", className),
|
|
"data-slot": "card-frame-title",
|
|
};
|
|
|
|
return useRender({
|
|
defaultTagName: "div",
|
|
props: mergeProps<"div">(defaultProps, props),
|
|
render,
|
|
});
|
|
}
|
|
|
|
export function CardFrameDescription({
|
|
className,
|
|
render,
|
|
...props
|
|
}: useRender.ComponentProps<"div">): React.ReactElement {
|
|
const defaultProps = {
|
|
className: cn("self-center text-muted-foreground text-sm", className),
|
|
"data-slot": "card-frame-description",
|
|
};
|
|
|
|
return useRender({
|
|
defaultTagName: "div",
|
|
props: mergeProps<"div">(defaultProps, props),
|
|
render,
|
|
});
|
|
}
|
|
|
|
export function CardFrameAction({
|
|
className,
|
|
render,
|
|
...props
|
|
}: useRender.ComponentProps<"div">): React.ReactElement {
|
|
const defaultProps = {
|
|
className: cn(
|
|
"col-start-2 nth-3:row-span-2 nth-3:row-start-1 inline-flex self-center justify-self-end",
|
|
className,
|
|
),
|
|
"data-slot": "card-frame-action",
|
|
};
|
|
|
|
return useRender({
|
|
defaultTagName: "div",
|
|
props: mergeProps<"div">(defaultProps, props),
|
|
render,
|
|
});
|
|
}
|
|
|
|
export function CardFrameFooter({
|
|
className,
|
|
render,
|
|
...props
|
|
}: useRender.ComponentProps<"div">): React.ReactElement {
|
|
const defaultProps = {
|
|
className: cn("px-6 py-4", className),
|
|
"data-slot": "card-frame-footer",
|
|
};
|
|
|
|
return useRender({
|
|
defaultTagName: "div",
|
|
props: mergeProps<"div">(defaultProps, props),
|
|
render,
|
|
});
|
|
}
|
|
|
|
export function CardHeader({
|
|
className,
|
|
render,
|
|
...props
|
|
}: useRender.ComponentProps<"div">): React.ReactElement {
|
|
const defaultProps = {
|
|
className: cn(
|
|
"grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 p-6 in-[[data-slot=card]:has(>[data-slot=card-panel])]:pb-4 has-data-[slot=card-action]:grid-cols-[1fr_auto]",
|
|
className,
|
|
),
|
|
"data-slot": "card-header",
|
|
};
|
|
|
|
return useRender({
|
|
defaultTagName: "div",
|
|
props: mergeProps<"div">(defaultProps, props),
|
|
render,
|
|
});
|
|
}
|
|
|
|
export function CardTitle({
|
|
className,
|
|
render,
|
|
...props
|
|
}: useRender.ComponentProps<"div">): React.ReactElement {
|
|
const defaultProps = {
|
|
className: cn("font-semibold text-lg leading-none", className),
|
|
"data-slot": "card-title",
|
|
};
|
|
|
|
return useRender({
|
|
defaultTagName: "div",
|
|
props: mergeProps<"div">(defaultProps, props),
|
|
render,
|
|
});
|
|
}
|
|
|
|
export function CardDescription({
|
|
className,
|
|
render,
|
|
...props
|
|
}: useRender.ComponentProps<"div">): React.ReactElement {
|
|
const defaultProps = {
|
|
className: cn("text-muted-foreground text-sm", className),
|
|
"data-slot": "card-description",
|
|
};
|
|
|
|
return useRender({
|
|
defaultTagName: "div",
|
|
props: mergeProps<"div">(defaultProps, props),
|
|
render,
|
|
});
|
|
}
|
|
|
|
export function CardAction({
|
|
className,
|
|
render,
|
|
...props
|
|
}: useRender.ComponentProps<"div">): React.ReactElement {
|
|
const defaultProps = {
|
|
className: cn(
|
|
"col-start-2 row-span-2 row-start-1 inline-flex self-start justify-self-end",
|
|
className,
|
|
),
|
|
"data-slot": "card-action",
|
|
};
|
|
|
|
return useRender({
|
|
defaultTagName: "div",
|
|
props: mergeProps<"div">(defaultProps, props),
|
|
render,
|
|
});
|
|
}
|
|
|
|
export function CardPanel({
|
|
className,
|
|
render,
|
|
...props
|
|
}: useRender.ComponentProps<"div">): React.ReactElement {
|
|
const defaultProps = {
|
|
className: cn(
|
|
"flex-1 p-6 in-[[data-slot=card]:has(>[data-slot=card-header]:not(.border-b))]:pt-0 in-[[data-slot=card]:has(>[data-slot=card-footer]:not(.border-t))]:pb-0",
|
|
className,
|
|
),
|
|
"data-slot": "card-panel",
|
|
};
|
|
|
|
return useRender({
|
|
defaultTagName: "div",
|
|
props: mergeProps<"div">(defaultProps, props),
|
|
render,
|
|
});
|
|
}
|
|
|
|
export function CardFooter({
|
|
className,
|
|
render,
|
|
...props
|
|
}: useRender.ComponentProps<"div">): React.ReactElement {
|
|
const defaultProps = {
|
|
className: cn(
|
|
"flex items-center p-6 in-[[data-slot=card]:has(>[data-slot=card-panel])]:pt-4",
|
|
className,
|
|
),
|
|
"data-slot": "card-footer",
|
|
};
|
|
|
|
return useRender({
|
|
defaultTagName: "div",
|
|
props: mergeProps<"div">(defaultProps, props),
|
|
render,
|
|
});
|
|
}
|
|
|
|
export { CardPanel as CardContent };
|