/**
* Material 3: the surfaces.
*
* Tonal rather than outlined — a widget is told from the ground by being a
* step further toward the primary, which is what lets a palette recolour the
* whole look. Nothing here holds state: the behaviour is in `ui/core`.
*/
import { Link } from "@tanstack/react-router"
import { Lock } from "lucide-react"
import { motion } from "motion/react"
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip"
import { cn } from "@/lib/utils"
import { ICONS } from "../../icons"
import type {
BackdropProps,
FrameProps,
NoticeProps,
RailProps,
} from "../core/contract"
import { TESTID } from "../core/contract"
import { LOOK } from "../core/motion"
/** Material's ground is the surface colour itself; only an image is drawn. */
export function Backdrop({ image }: BackdropProps) {
if (!image) return null
return (
)
}
function Issue({ issue }: { issue: string }) {
return (
{issue}
)
}
export function Frame({
title,
issue,
grip,
scroll,
selected,
onClick,
children,
}: FrameProps) {
return (
// A card is not a control: the click only picks it in edit mode, and every
// interactive element inside keeps its own role and keyboard handling.
// biome-ignore lint/a11y/useKeyWithClickEvents: see above.
// biome-ignore lint/a11y/noStaticElementInteractions: see above.
{title ? (
{title}
{issue ? : null}
) : null}
{children}
{/* No header to sit in, so the fault takes the corner instead — a widget
drawn without its title still says when it is mis-wired. */}
{!title && issue ? (
) : null}
{/* And the editor still needs something to drag it by. */}
{!title && grip ? (
) : null}
)
}
/** Two letters off the title, so a rail of four reads as four different things. */
function initials(label: string): string {
const words = label.split(/[\s_-]+/).filter(Boolean)
if (words.length === 0) return "?"
if (words.length === 1) return words[0].slice(0, 2).toUpperCase()
return (words[0][0] + words[1][0]).toUpperCase()
}
export function Rail({ entries }: RailProps) {
return (
)
}
export function Notice({ children }: NoticeProps) {
return (