raft-ui
Version:
React UI components for Raft.
1,541 lines (1,536 loc) • 680 kB
JavaScript
import { cn } from "./cn.mjs";
import { Dialog as Dialog$1 } from "@base-ui/react/dialog";
import { AlertTriangle, Bell, Check, CheckCircle2, ChevronDown, ChevronLeft, ChevronRight, Copy, FileText, Image, Info, Minus, Upload, X } from "lucide-react";
import { tv } from "tailwind-variants/lite";
import { Children, Fragment, cloneElement, createContext, isValidElement, use, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
import { Button as Button$1 } from "@base-ui/react/button";
import { mergeProps } from "@base-ui/react/merge-props";
import { useRender } from "@base-ui/react/use-render";
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
import { Drawer as Drawer$1 } from "@base-ui/react/drawer";
import { useClipboard } from "foxact/use-clipboard";
import { Checkbox as Checkbox$1 } from "@base-ui/react/checkbox";
import { ScrollArea as ScrollArea$1 } from "@base-ui/react/scroll-area";
import { Collapsible } from "@base-ui/react/collapsible";
import { Avatar as Avatar$1, ContextMenu as ContextMenu$1 } from "@base-ui/react";
import { Menu } from "@base-ui/react/menu";
import { useHotkey } from "@tanstack/react-hotkeys";
import { Tooltip as Tooltip$1 } from "@base-ui/react/tooltip";
import { Popover as Popover$1 } from "@base-ui/react/popover";
import { PreviewCard as PreviewCard$1 } from "@base-ui/react/preview-card";
import { Input as Input$1 } from "@base-ui/react/input";
import { Field as Field$1 } from "@base-ui/react/field";
import { Combobox as Combobox$1 } from "@base-ui/react/combobox";
import { Select as Select$1 } from "@base-ui/react/select";
import { Radio } from "@base-ui/react/radio";
import { RadioGroup as RadioGroup$1 } from "@base-ui/react/radio-group";
import { Switch as Switch$1 } from "@base-ui/react/switch";
import { Toggle } from "@base-ui/react/toggle";
import { ToggleGroup as ToggleGroup$1 } from "@base-ui/react/toggle-group";
import { Toolbar } from "@base-ui/react/toolbar";
import { Progress as Progress$1 } from "@base-ui/react/progress";
import { DndContext, DragOverlay, KeyboardSensor, PointerSensor, TouchSensor, closestCenter, pointerWithin, useDraggable, useDroppable, useSensor, useSensors } from "@dnd-kit/core";
import { SortableContext, arrayMove, defaultAnimateLayoutChanges, horizontalListSortingStrategy, sortableKeyboardCoordinates, useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { Toast } from "@base-ui/react/toast";
import { Tabs as Tabs$1 } from "@base-ui/react/tabs";
import { Separator as Separator$1 } from "@base-ui/react/separator";
import { Group, Panel as Panel$1, Separator as Separator$2 } from "react-resizable-panels";
//#region src/lib/tv.ts
const tv$1 = ((options) => {
const recipe = tv(options);
const wrappedRecipe = ((props) => {
const result = recipe(props);
if (result == null || typeof result === "string") return cn(result);
return Object.fromEntries(Object.entries(result).map(([slotName, slot]) => [slotName, (slotProps) => cn(slot(slotProps))]));
});
return Object.assign(wrappedRecipe, recipe);
});
//#endregion
//#region src/lib/theme/theme-context.ts
const ThemeContext = createContext("brutal");
const ThemeStateContext = createContext({
theme: "brutal",
setTheme: () => void 0,
mode: "light",
resolvedMode: "light"
});
//#endregion
//#region src/lib/theme/use-theme.ts
/** Read the active style family for theme-aware component recipes. */
function useThemeFamily() {
return use(ThemeContext);
}
/** Read and update the application-level RUI theme selection. */
function useTheme() {
return use(ThemeStateContext);
}
//#endregion
//#region src/lib/theme/theme-scope.ts
function resolveThemeScopeProps({ className, "data-theme": _dataTheme, ...props }, { theme, resolvedMode }) {
const modeClassName = theme === "elegant" ? resolvedMode : void 0;
const resolvedClassName = typeof className === "function" && modeClassName ? (state) => cn(className(state), modeClassName) : typeof className === "string" && modeClassName ? cn(className, modeClassName) : className ?? modeClassName;
return {
...props,
"data-theme": theme,
className: resolvedClassName
};
}
function useThemeScopeProps(props) {
return resolveThemeScopeProps(props, useTheme());
}
//#endregion
//#region src/lib/default-native-button.ts
function defaultNativeButton(render) {
if (!render || typeof render !== "object" || !("type" in render)) return;
const element = render;
if (typeof element.type === "string") return element.type === "button";
}
//#endregion
//#region src/components/spinner/spinner.tsx
const ELEGANT_SPINNER_STYLE = `
@keyframes raft-elegant-spinner-spin {
to {
transform: rotate(360deg);
}
}
@keyframes raft-elegant-spinner-dash {
0% {
stroke-dasharray: 1 150;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 90 150;
stroke-dashoffset: -35;
}
100% {
stroke-dasharray: 90 150;
stroke-dashoffset: -124;
}
}
[data-slot="spinner"] [data-spinner-ring] {
animation: raft-elegant-spinner-spin 1600ms linear infinite;
transform-box: fill-box;
transform-origin: center;
will-change: transform;
}
[data-slot="spinner"] [data-spinner-arc] {
animation: raft-elegant-spinner-dash 1400ms cubic-bezier(0.42, 0, 0.58, 1) infinite;
stroke-dasharray: 1 150;
stroke-dashoffset: 0;
}
@media (prefers-reduced-motion: reduce) {
[data-slot="spinner"] [data-spinner-ring] {
animation-duration: 2400ms;
will-change: auto;
}
[data-slot="spinner"] [data-spinner-arc] {
animation: none;
stroke-dasharray: 72 150;
stroke-dashoffset: -30;
}
}
`;
const spinner = tv$1({
slots: {
root: ["relative inline-flex shrink-0 items-center justify-center", "leading-none text-current"],
ring: "block size-full shrink-0"
},
variants: {
theme: {
brutal: {},
elegant: {}
},
size: {
xs: {},
sm: {},
md: {},
lg: {}
},
variant: {
default: {},
inverse: {}
}
},
defaultVariants: { size: "sm" },
compoundVariants: [
{
theme: "brutal",
size: "xs",
class: { root: "size-2.5" }
},
{
theme: "brutal",
size: "sm",
class: { root: "size-4" }
},
{
theme: "brutal",
size: "md",
class: { root: "size-5" }
},
{
theme: "brutal",
size: "lg",
class: { root: "size-8" }
},
{
theme: "brutal",
variant: "default",
class: {
root: "text-black",
ring: "animate-spin rounded-full border-2 border-current/20 border-t-current"
}
},
{
theme: "brutal",
variant: "inverse",
class: {
root: "text-white",
ring: "animate-spin rounded-full border-2 border-current/40 border-t-current"
}
},
{
theme: "elegant",
size: "xs",
class: { root: "size-3.5" }
},
{
theme: "elegant",
size: "sm",
class: { root: "size-3.5" }
},
{
theme: "elegant",
size: "md",
class: { root: "size-4.5" }
},
{
theme: "elegant",
size: "lg",
class: { root: "size-4.5" }
}
]
});
const spinnerStyle = /* @__PURE__ */ jsx("style", {
href: "raft-ui-spinner",
precedence: "default",
children: ELEGANT_SPINNER_STYLE
});
function Spinner({ size = "sm", variant = "default", className, render, ...props }) {
const theme = useThemeFamily();
const { root, ring } = spinner({
theme,
size,
variant
});
const children = theme === "elegant" ? /* @__PURE__ */ jsxs(Fragment$1, { children: [spinnerStyle, /* @__PURE__ */ jsxs("svg", {
"data-spinner-ring": "",
"aria-hidden": "true",
className: ring(),
xmlns: "http://www.w3.org/2000/svg",
fill: "none",
viewBox: "0 0 24 24",
children: [/* @__PURE__ */ jsx("circle", {
className: "opacity-20",
cx: "12",
cy: "12",
r: "10",
stroke: "currentColor",
strokeWidth: "2.5"
}), /* @__PURE__ */ jsx("circle", {
"data-spinner-arc": "",
className: "opacity-80",
cx: "12",
cy: "12",
r: "10",
fill: "none",
pathLength: "150",
stroke: "currentColor",
strokeLinecap: "round",
strokeWidth: "2.5"
})]
})] }) : /* @__PURE__ */ jsx("span", {
"data-spinner-ring": "",
"aria-hidden": "true",
className: ring()
});
return useRender({
defaultTagName: "span",
render,
props: mergeProps({
role: "status",
"aria-label": "Loading",
className: root({ className }),
children
}, props),
state: {
slot: "spinner",
size
}
});
}
//#endregion
//#region src/components/button/button.tsx
const buttonVariants = tv$1({
slots: {
root: [
"relative inline-flex shrink-0 items-center justify-center",
"font-heading whitespace-nowrap select-none",
"rounded-none",
"shadow-raft-sm transition-all duration-100",
"outline-[1px] outline-offset-2 outline-transparent focus-visible:outline-transparent",
"data-[loading=true]:cursor-wait",
"disabled:pointer-events-none disabled:opacity-40 data-[loading=true]:opacity-100",
"disabled:transform-none",
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
],
content: "relative z-10 inline-flex items-center justify-center gap-[inherit] data-[loading=true]:invisible",
loadingIndicator: "pointer-events-none absolute inset-0 z-10 flex items-center justify-center"
},
variants: {
theme: {
brutal: { root: ["border-2 font-bold"] },
elegant: { root: [
"overflow-clip border-0 font-sans font-medium antialiased",
"focus-visible:ring-2 focus-visible:ring-[color-mix(in_oklch,var(--button-focus-ring,var(--primary-400))_80%,transparent)] focus-visible:ring-offset-1 focus-visible:ring-offset-layer-canvas-muted",
"shadow-[inset_0_1px_0.5px_oklch(1_0_0/0.05),0_0_0_1px_var(--line-muted)]",
"before:pointer-events-none before:absolute before:inset-0 before:z-0 before:rounded-[inherit]",
"active:scale-[0.985]"
] }
},
variant: {
primary: { root: ["border-line-strong bg-primary-400 text-primary-950", "hover:bg-primary-500"] },
default: { root: ["border-line-strong bg-layer-panel text-foreground", "hover:bg-layer-panel"] },
information: { root: ["border-line-strong bg-info text-foreground-inverse", "hover:bg-info"] },
muted: { root: ["border-line-strong bg-secondary-400 text-secondary-950", "hover:bg-secondary-400"] },
accent: { root: ["border-line-strong bg-accent-400 text-accent-950", "hover:bg-accent-500"] },
warning: { root: ["border-line-strong bg-warning text-warning-foreground", "hover:bg-warning"] },
outline: { root: [
"border-line-strong bg-transparent text-foreground",
"hover:bg-fill-muted",
"shadow-none"
] },
ghost: { root: ["border-transparent bg-transparent text-foreground-strong shadow-none", "hover:border-line-strong hover:bg-fill-muted"] },
link: { root: ["border-transparent bg-transparent text-foreground shadow-none underline-offset-4", "hover:underline"] },
"danger-secondary": { root: ["border-line-strong bg-danger-soft text-danger", "hover:bg-danger-soft"] },
danger: { root: ["border-line-strong bg-danger text-danger-foreground", "hover:border-danger-hover hover:bg-danger-hover"] },
"danger-outline": { root: [
"border-line-strong bg-transparent text-danger",
"hover:bg-fill-muted",
"shadow-none"
] }
},
size: {
xs: { root: [
"h-6 gap-1 px-2 py-0 text-[11px]",
"has-data-[icon=inline-start]:pl-1.5 has-data-[icon=inline-end]:pr-1.5",
"**:data-icon:size-[1em]",
"[&_svg:not([class*='size-'])]:size-3"
] },
sm: { root: [
"h-7 gap-[5px] px-2.5 py-0 text-xs",
"has-data-[icon=inline-start]:pl-2 has-data-[icon=inline-end]:pr-2",
"**:data-icon:size-[1em]",
"[&_svg:not([class*='size-'])]:size-3.5"
] },
md: { root: [
"h-8 gap-[5.5px] px-3 py-0 text-sm",
"has-data-[icon=inline-start]:pl-2.5 has-data-[icon=inline-end]:pr-2.5",
"**:data-icon:size-[1em]"
] },
lg: { root: [
"gap-1.5 px-3.5 py-0 text-base",
"has-data-[icon=inline-start]:pl-3 has-data-[icon=inline-end]:pr-3",
"**:data-icon:size-[1em]"
] },
"icon-xs": { root: ["size-6 p-0 text-[11px]", "[&_svg:not([class*='size-'])]:size-3"] },
"icon-sm": { root: ["size-7 p-0 text-xs", "[&_svg:not([class*='size-'])]:size-3.5"] },
"icon-md": { root: "size-8 p-0 text-sm" },
"icon-lg": { root: "p-0 text-sm" }
}
},
defaultVariants: {
variant: "default",
size: "md"
},
compoundVariants: [
{
theme: "elegant",
size: "xs",
class: { root: "rounded-sm" }
},
{
theme: "elegant",
size: "icon-xs",
class: { root: "rounded-sm" }
},
{
theme: "elegant",
size: "sm",
class: { root: "rounded-sm" }
},
{
theme: "elegant",
size: "icon-sm",
class: { root: "rounded-sm" }
},
{
theme: "elegant",
size: "md",
class: { root: "rounded-md" }
},
{
theme: "elegant",
size: "icon-md",
class: { root: "rounded-md" }
},
{
theme: "elegant",
size: "lg",
class: { root: "h-9 rounded-md" }
},
{
theme: "elegant",
size: "icon-lg",
class: { root: "size-9 rounded-md" }
},
{
theme: "elegant",
variant: [
"default",
"primary",
"outline",
"information",
"muted",
"accent",
"warning",
"danger",
"danger-secondary",
"danger-outline"
],
class: { root: "dark:shadow-raft-xs" }
},
{
theme: "elegant",
variant: ["default", "outline"],
class: { root: "[--button-focus-ring:var(--foreground)]" }
},
{
theme: "elegant",
variant: [
"ghost",
"link",
"muted"
],
class: { root: "[--button-focus-ring:var(--line-hairline)]" }
},
{
theme: "elegant",
variant: [
"danger-secondary",
"danger",
"danger-outline"
],
class: { root: "[--button-focus-ring:var(--danger)]" }
},
{
theme: "brutal",
variant: "primary",
class: { root: "bg-brutal-yellow text-foreground-strong hover:bg-brutal-yellow" }
},
{
theme: "brutal",
variant: "default",
class: { root: "bg-layer-panel text-foreground-strong hover:bg-layer-panel" }
},
{
theme: "brutal",
variant: "outline",
class: { root: "bg-layer-panel text-foreground-strong shadow-raft-sm hover:bg-layer-panel" }
},
{
theme: "brutal",
variant: "information",
class: { root: "bg-brutal-cyan text-foreground-strong hover:bg-brutal-cyan" }
},
{
theme: "brutal",
variant: "muted",
class: { root: "bg-brutal-stone text-foreground-strong hover:bg-brutal-stone" }
},
{
theme: "brutal",
variant: "accent",
class: { root: "bg-brutal-pink text-foreground-strong hover:bg-brutal-pink" }
},
{
theme: "brutal",
variant: "danger",
class: { root: "bg-brutal-red text-foreground-strong hover:border-line-strong hover:bg-brutal-red" }
},
{
theme: "brutal",
variant: "danger-secondary",
class: { root: "text-[color-mix(in_oklab,var(--danger)_72%,transparent)] hover:border-line-strong" }
},
{
theme: "brutal",
variant: "warning",
class: { root: "bg-brutal-orange text-foreground-strong hover:bg-brutal-orange" }
},
{
theme: "brutal",
size: "lg",
class: { root: ["h-10 px-4 text-sm", "has-data-[icon=inline-start]:pl-3.5 has-data-[icon=inline-end]:pr-3.5"] }
},
{
theme: "brutal",
size: "icon-lg",
class: { root: "size-10" }
},
{
theme: "elegant",
variant: "default",
class: { root: [
"bg-foreground text-foreground-inverse",
"dark:bg-ink",
"shadow-[inset_0_1px_0.5px_oklch(1_0_0/0.12),0_0_0_1px_var(--foreground)]",
"before:bg-[linear-gradient(in_oklab_180deg,oklab(100%_0_0/8%)_0%,oklab(0%_0_0/0%)_100%)]",
"hover:bg-[oklch(0.31_0.006_106.42)] dark:hover:bg-foreground-strong"
] }
},
{
theme: "elegant",
variant: "primary",
class: { root: [
"bg-primary-400 text-primary-950",
"[--button-focus-ring:var(--primary-400)]",
"shadow-[inset_0_1px_0.5px_oklch(1_0_0/0.15),0_0_0_1px_var(--primary-edge)]",
"before:bg-[linear-gradient(in_oklab_180deg,oklab(100%_0_0/11%)_0%,oklab(100%_0_0/0%)_0%,oklab(0%_0_0/0%)_100%)]",
"hover:bg-primary-hover",
"dark:hover:bg-primary-500"
] }
},
{
theme: "elegant",
variant: "outline",
class: { root: [
"bg-layer-panel text-foreground dark:text-foreground-muted",
"shadow-[inset_0_1px_0.5px_oklch(1_0_0/0.05),0_0_0_1px_var(--line-muted)]",
"before:bg-[linear-gradient(in_oklab_180deg,oklab(0%_0_0/0%)_30%,oklab(0%_0_0/1%)_100%)]",
"hover:bg-fill-muted",
"dark:bg-fill-muted dark:hover:bg-fill-strong"
] }
},
{
theme: "elegant",
variant: "ghost",
class: { root: ["bg-transparent text-foreground-muted shadow-none", "hover:bg-ink-4"] }
},
{
theme: "elegant",
variant: "danger-secondary",
class: { root: [
"bg-danger-soft text-danger",
"dark:bg-[oklch(0.37_0.06_27)] dark:text-[oklch(0.8_0.17_22)]",
"shadow-[inset_0_1px_0.5px_oklch(1_0_0/0.08),0_0_0_1px_var(--danger-muted)]",
"before:bg-[linear-gradient(in_oklab_180deg,oklab(100%_0_0/4%)_0%,oklab(0%_0_0/0%)_100%)]",
"hover:bg-danger-muted",
"dark:hover:bg-[oklch(0.4_0.07_27)]"
] }
},
{
theme: "elegant",
variant: "danger",
class: { root: [
"bg-danger text-danger-foreground",
"dark:bg-[oklch(0.58_0.21_27)] dark:text-[oklch(0.985_0.004_106.42)]",
"shadow-[inset_0_1px_0.5px_oklch(1_0_0/0.12),0_0_0_1px_var(--danger)]",
"before:bg-[linear-gradient(in_oklab_180deg,oklab(100%_0_0/10%)_0%,oklab(0%_0_0/0%)_100%)]",
"hover:bg-danger-hover",
"dark:hover:bg-[oklch(0.61_0.21_27)]"
] }
},
{
theme: "elegant",
variant: "danger-outline",
class: { root: [
"bg-layer-panel text-danger",
"dark:bg-fill-muted dark:text-[oklch(0.78_0.19_25)]",
"shadow-[inset_0_1px_0.5px_oklch(1_0_0/0.025),0_0_0_1px_var(--danger-muted)]",
"hover:bg-danger-soft",
"dark:hover:bg-fill-strong"
] }
},
{
theme: "elegant",
variant: "accent",
class: { root: [
"bg-accent-400 text-accent-950",
"dark:text-foreground-inverse",
"[--button-focus-ring:var(--accent-400)]",
"shadow-[inset_0_1px_0.5px_oklch(1_0_0/0.12),0_0_0_1px_var(--accent-400)]",
"before:bg-[linear-gradient(in_oklab_180deg,oklab(100%_0_0/8%)_0%,oklab(0%_0_0/0%)_100%)]",
"hover:bg-accent-hover"
] }
},
{
theme: "elegant",
variant: "warning",
class: { root: [
"bg-warning text-[oklch(0.25_0.05_50)]",
"dark:bg-[oklch(0.66_0.17_46)]",
"[--button-focus-ring:var(--warning)]",
"shadow-[inset_0_1px_0.5px_oklch(1_0_0/0.14),0_0_0_1px_var(--warning)]",
"before:bg-[linear-gradient(in_oklab_180deg,oklab(100%_0_0/10%)_0%,oklab(0%_0_0/0%)_100%)]",
"hover:bg-warning-hover",
"dark:hover:bg-[oklch(0.69_0.17_46)]"
] }
},
{
theme: "elegant",
variant: "link",
class: { root: "bg-transparent text-foreground-muted shadow-none" }
},
{
theme: "elegant",
variant: "information",
class: { root: [
"bg-info text-foreground-inverse",
"dark:bg-info-strong",
"[--button-focus-ring:var(--info)]",
"shadow-[inset_0_1px_0.5px_oklch(1_0_0/0.12),0_0_0_1px_var(--info)]",
"before:bg-[linear-gradient(in_oklab_180deg,oklab(100%_0_0/8%)_0%,oklab(0%_0_0/0%)_100%)]",
"hover:bg-info-hover",
"dark:hover:bg-info"
] }
},
{
theme: "elegant",
variant: "muted",
class: { root: [
"bg-fill-muted text-foreground-muted dark:text-foreground-placeholder",
"shadow-[inset_0_1px_0.5px_oklch(1_0_0/0.05),0_0_0_1px_var(--line-muted)]",
"before:bg-[linear-gradient(in_oklab_180deg,oklab(100%_0_0/5%)_0%,oklab(0%_0_0/1%)_100%)]",
"hover:bg-fill-strong",
"dark:bg-layer-panel",
"dark:hover:bg-fill-muted"
] }
},
{
theme: "brutal",
variant: [
"primary",
"default",
"information",
"muted",
"accent",
"warning",
"outline",
"danger-secondary",
"danger"
],
class: { root: [
"hover:-translate-y-px hover:shadow-raft-md",
"active:translate-px",
"active:shadow-raft-xs"
] }
},
{
theme: "brutal",
variant: "danger-outline",
class: { root: ["hover:-translate-y-px", "active:translate-px"] }
}
]
});
function Button({ variant, size, loading = false, loadingLabel = "Loading", disabled, className, children, render, nativeButton, ...props }) {
const theme = useThemeFamily();
const { root, content, loadingIndicator } = buttonVariants({
theme,
variant,
size
});
return /* @__PURE__ */ jsxs(Button$1, {
...props,
render,
nativeButton: nativeButton ?? defaultNativeButton(render),
"data-slot": "button",
className: root({ className }),
"data-loading": loading ? "true" : void 0,
disabled: disabled || loading,
"aria-disabled": loading || void 0,
children: [loading ? /* @__PURE__ */ jsx("span", {
"data-slot": "button-loading-indicator",
className: loadingIndicator(),
children: /* @__PURE__ */ jsx(Spinner, { "aria-label": loadingLabel })
}) : null, /* @__PURE__ */ jsx("span", {
"data-slot": "button-content",
"data-loading": loading ? "true" : void 0,
className: content(),
children
})]
});
}
//#endregion
//#region src/components/alert-dialog/alert-dialog.tsx
const alertDialog = tv$1({
slots: {
overlay: [
"fixed inset-0 z-50",
"transition-opacity duration-150 ease-out",
"data-starting-style:opacity-0 data-ending-style:opacity-0"
],
content: [
"fixed top-1/2 left-1/2 z-50 grid w-[calc(100%-2rem)] max-w-[408px] -translate-1/2 ",
"origin-center transform-gpu outline-none transition-[opacity,transform] duration-150 ease-out",
"data-starting-style:scale-[.98] data-starting-style:opacity-0",
"data-ending-style:opacity-0"
],
header: ["grid grid-cols-[minmax(0,1fr)_max-content] items-center gap-3 min-w-0"],
body: ["min-w-0"],
footer: ["flex items-center justify-end gap-3"],
title: ["m-0 min-w-0 text-foreground"],
description: ["m-0 text-foreground-muted"]
},
variants: { theme: {
brutal: {
overlay: ["bg-layer-backdrop"],
content: ["gap-4 border-2 border-line-strong bg-layer-panel px-6 py-4 shadow-raft-xl"],
body: ["py-2 font-medium"],
title: ["font-heading text-lg leading-none font-bold uppercase"],
description: ["font-sans text-sm leading-5 text-foreground-strong/60"]
},
elegant: {
overlay: ["bg-layer-backdrop backdrop-blur-[2px]"],
content: ["overflow-clip rounded-lg bg-layer-popover shadow-raft-xl"],
header: ["border-b border-line-muted/75 bg-layer-inset px-5 py-3", "dark:border-transparent dark:bg-transparent"],
body: ["px-6 py-5 dark:pt-2.5"],
footer: ["border-t border-line-muted/75 bg-layer-inset px-5 py-3", "dark:border-transparent dark:bg-transparent"],
title: ["font-sans text-sm leading-5 font-medium tracking-[-0.005em]"],
description: ["font-sans text-sm leading-5 text-foreground-hint"]
}
} }
});
function AlertDialog(props) {
return /* @__PURE__ */ jsx(Dialog$1.Root, {
"data-slot": "alert-dialog",
...props
});
}
function AlertDialogTrigger(props) {
return /* @__PURE__ */ jsx(Dialog$1.Trigger, {
"data-slot": "alert-dialog-trigger",
...props
});
}
function AlertDialogPortal(props) {
return /* @__PURE__ */ jsx(Dialog$1.Portal, {
"data-slot": "alert-dialog-portal",
...useThemeScopeProps(props)
});
}
function AlertDialogClose({ className, variant, size, children, ...props }) {
const isBrutal = useThemeFamily() === "brutal";
return /* @__PURE__ */ jsx(Dialog$1.Close, {
"data-slot": "alert-dialog-close",
render: /* @__PURE__ */ jsx(Button, {
variant: variant ?? (isBrutal ? "outline" : "ghost"),
size: size ?? (isBrutal ? "icon-md" : "icon-sm"),
"aria-label": "Close",
className: isBrutal ? className : cn("p-0 text-foreground-placeholder hover:border-transparent hover:bg-transparent hover:text-foreground-strong", className)
}),
...props,
children: children ?? /* @__PURE__ */ jsx(X, { className: isBrutal ? "size-5" : "size-4" })
});
}
function AlertDialogOverlay({ className, ...props }) {
const theme = useThemeFamily();
const { overlay } = alertDialog({ theme });
return /* @__PURE__ */ jsx(Dialog$1.Backdrop, {
"data-slot": "alert-dialog-overlay",
className: overlay({ className }),
...props
});
}
function AlertDialogContent({ className, children, portalProps, overlay, ...props }) {
const theme = useThemeFamily();
const { content } = alertDialog({ theme });
return /* @__PURE__ */ jsxs(AlertDialogPortal, {
...portalProps,
children: [overlay === false ? null : /* @__PURE__ */ jsx(AlertDialogOverlay, { ...overlay }), /* @__PURE__ */ jsx(Dialog$1.Popup, {
"data-slot": "alert-dialog-content",
className: content({ className }),
...props,
role: "alertdialog",
children
})]
});
}
function AlertDialogHeader({ className, ...props }) {
const theme = useThemeFamily();
const { header } = alertDialog({ theme });
return /* @__PURE__ */ jsx("div", {
"data-slot": "alert-dialog-header",
className: header({ className }),
...props
});
}
function AlertDialogBody({ className, ...props }) {
const theme = useThemeFamily();
const { body } = alertDialog({ theme });
return /* @__PURE__ */ jsx("div", {
"data-slot": "alert-dialog-body",
className: body({ className }),
...props
});
}
function AlertDialogFooter({ className, ...props }) {
const theme = useThemeFamily();
const { footer } = alertDialog({ theme });
return /* @__PURE__ */ jsx("div", {
"data-slot": "alert-dialog-footer",
className: footer({ className }),
...props
});
}
function AlertDialogTitle({ className, ...props }) {
const theme = useThemeFamily();
const { title } = alertDialog({ theme });
return /* @__PURE__ */ jsx(Dialog$1.Title, {
"data-slot": "alert-dialog-title",
className: title({ className }),
...props
});
}
function AlertDialogDescription({ className, ...props }) {
const theme = useThemeFamily();
const { description } = alertDialog({ theme });
return /* @__PURE__ */ jsx(Dialog$1.Description, {
"data-slot": "alert-dialog-description",
className: description({ className }),
...props
});
}
function AlertDialogAction(props) {
return /* @__PURE__ */ jsx(Button, {
"data-slot": "alert-dialog-action",
...props
});
}
function AlertDialogCancel({ className, variant = "outline", size = "sm", ...props }) {
return /* @__PURE__ */ jsx(Dialog$1.Close, {
"data-slot": "alert-dialog-cancel",
render: /* @__PURE__ */ jsx(Button, {
className,
variant,
size
}),
...props
});
}
//#endregion
//#region src/components/dialog/dialog.tsx
const dialog = tv$1({
slots: {
overlay: [
"fixed inset-0 z-50",
"transition-opacity duration-150 ease-out",
"data-starting-style:opacity-0 data-ending-style:opacity-0"
],
content: [
"fixed top-1/2 left-1/2 z-50 grid w-[calc(100%-2rem)] max-w-md -translate-1/2 ",
"origin-center transform-gpu outline-none transition-[opacity,transform] duration-150 ease-out",
"data-starting-style:scale-[.98] data-starting-style:opacity-0",
"data-ending-style:opacity-0"
],
header: ["grid grid-cols-[minmax(0,1fr)_max-content] items-center gap-3 min-w-0"],
body: ["min-w-0"],
footer: ["flex items-center justify-end gap-3"],
title: ["m-0 min-w-0 text-foreground"],
description: ["m-0 text-foreground-muted"]
},
variants: { theme: {
brutal: {
overlay: ["bg-layer-backdrop"],
content: ["gap-4 border-2 border-line-strong bg-layer-panel px-6 py-4 shadow-raft-xl"],
body: ["py-2 font-medium"],
title: ["font-heading text-lg leading-none font-bold uppercase"],
description: ["font-sans text-sm leading-5 text-foreground-strong/60"]
},
elegant: {
overlay: ["bg-layer-backdrop backdrop-blur-[2px]"],
content: ["overflow-clip rounded-lg bg-layer-popover shadow-raft-xl"],
header: ["border-b border-line-muted/75 bg-layer-inset px-6 py-3", "dark:border-transparent dark:bg-transparent"],
body: ["px-6 py-5 dark:pt-2.5"],
footer: ["border-t border-line-muted/75 bg-layer-inset px-6 py-3", "dark:border-transparent dark:bg-transparent"],
title: ["font-sans text-sm leading-5 font-medium tracking-[-0.005em]"],
description: ["font-sans text-sm leading-5 text-foreground-hint"]
}
} }
});
function Dialog(props) {
return /* @__PURE__ */ jsx(Dialog$1.Root, {
"data-slot": "dialog",
...props
});
}
function DialogTrigger(props) {
return /* @__PURE__ */ jsx(Dialog$1.Trigger, {
"data-slot": "dialog-trigger",
...props
});
}
function DialogPortal(props) {
return /* @__PURE__ */ jsx(Dialog$1.Portal, {
"data-slot": "dialog-portal",
...useThemeScopeProps(props)
});
}
function DialogOverlay({ className, ...props }) {
const theme = useThemeFamily();
const { overlay } = dialog({ theme });
return /* @__PURE__ */ jsx(Dialog$1.Backdrop, {
"data-slot": "dialog-overlay",
className: overlay({ className }),
...props
});
}
function DialogContent({ className, children, portalProps, overlay, ...props }) {
const theme = useThemeFamily();
const { content } = dialog({ theme });
return /* @__PURE__ */ jsxs(DialogPortal, {
...portalProps,
children: [overlay === false ? null : /* @__PURE__ */ jsx(DialogOverlay, { ...overlay }), /* @__PURE__ */ jsx(Dialog$1.Popup, {
"data-slot": "dialog-content",
className: content({ className }),
...props,
children
})]
});
}
function DialogClose({ className, variant, size, children, render, ...props }) {
const isBrutal = useThemeFamily() === "brutal";
if (render) return /* @__PURE__ */ jsx(Dialog$1.Close, {
"data-slot": "dialog-close",
render,
...props,
children
});
return /* @__PURE__ */ jsx(Dialog$1.Close, {
"data-slot": "dialog-close",
render: /* @__PURE__ */ jsx(Button, {
variant: variant ?? (isBrutal ? "outline" : "ghost"),
size: size ?? (isBrutal ? "icon-md" : "icon-sm"),
"aria-label": "Close",
className: isBrutal ? className : cn("p-0 text-foreground-placeholder hover:border-transparent hover:bg-transparent hover:text-foreground-strong", className)
}),
...props,
children: children ?? /* @__PURE__ */ jsx(X, { className: isBrutal ? "size-5" : "size-4" })
});
}
function DialogHeader({ className, ...props }) {
const theme = useThemeFamily();
const { header } = dialog({ theme });
return /* @__PURE__ */ jsx("div", {
"data-slot": "dialog-header",
className: header({ className }),
...props
});
}
function DialogBody({ className, ...props }) {
const theme = useThemeFamily();
const { body } = dialog({ theme });
return /* @__PURE__ */ jsx("div", {
"data-slot": "dialog-body",
className: body({ className }),
...props
});
}
function DialogFooter({ className, ...props }) {
const theme = useThemeFamily();
const { footer } = dialog({ theme });
return /* @__PURE__ */ jsx("div", {
"data-slot": "dialog-footer",
className: footer({ className }),
...props
});
}
function DialogTitle({ className, ...props }) {
const theme = useThemeFamily();
const { title } = dialog({ theme });
return /* @__PURE__ */ jsx(Dialog$1.Title, {
"data-slot": "dialog-title",
className: title({ className }),
...props
});
}
function DialogDescription({ className, ...props }) {
const theme = useThemeFamily();
const { description } = dialog({ theme });
return /* @__PURE__ */ jsx(Dialog$1.Description, {
"data-slot": "dialog-description",
className: description({ className }),
...props
});
}
const drawer = tv$1({
extend: tv$1({
slots: {
overlay: [
"opacity-[max(var(--drawer-overlay-min-opacity,0),calc(1-var(--drawer-swipe-progress)))]",
"transition-opacity duration-200 ease-slide",
"data-starting-style:opacity-0 data-ending-style:pointer-events-none data-ending-style:opacity-0 data-ending-style:duration-150",
"motion-reduce:transition-none"
],
popup: [
"[--drawer-blur:2px] [--drawer-ease:var(--ease-slide)]",
"[--closed-transform:translate3d(var(--drawer-exit-x,0px),var(--drawer-exit-y,0px),0)]",
"[--drawer-stack-peek:16px] [--drawer-stack-step:0.05]",
"[--drawer-stack-progress:clamp(0,var(--drawer-swipe-progress),1)]",
"[--drawer-stack-offset:max(0px,calc((var(--nested-drawers)-var(--drawer-stack-progress))*var(--drawer-stack-peek)))]",
"[--drawer-stack-scale-base:calc(max(0,1-(var(--nested-drawers)*var(--drawer-stack-step))))]",
"[--drawer-stack-scale:clamp(0,calc(var(--drawer-stack-scale-base)+(var(--drawer-stack-step)*var(--drawer-stack-progress))),1)]",
"[--drawer-stack-shrink:calc(1-var(--drawer-stack-scale))]",
"[--drawer-stack-shrink-offset:calc(var(--drawer-stack-shrink)*var(--drawer-stack-size))]",
"origin-(--drawer-transform-origin)",
"transform-[translate3d(calc(var(--drawer-swipe-x,0px)+var(--drawer-stack-x,0px)),calc(var(--drawer-swipe-y,0px)+var(--drawer-stack-y,0px)),0)_scale(var(--drawer-stack-scale))]",
"opacity-100 blur-0",
"transition-[transform,height,opacity,filter] duration-240 ease-(--drawer-ease) will-change-transform",
"data-ending-style:duration-[calc(var(--drawer-swipe-strength,1)*180ms)]",
"data-starting-style:transform-(--closed-transform) data-starting-style:opacity-0 data-starting-style:blur-(--drawer-blur)",
"data-ending-style:transform-(--closed-transform) data-ending-style:opacity-0 data-ending-style:blur-(--drawer-blur)",
"data-swiping:duration-0 data-nested-drawer-swiping:duration-0 motion-reduce:transition-none"
],
content: ["transition-opacity duration-150 ease-slide motion-reduce:transition-none", "group-data-nested-drawer-open/drawer-popup:opacity-0 group-data-nested-drawer-swiping/drawer-popup:opacity-100"],
swipeHandle: ["transition-opacity duration-150 motion-reduce:transition-none", "group-data-nested-drawer-open/drawer-popup:opacity-0 group-data-nested-drawer-swiping/drawer-popup:opacity-100"]
},
variants: {
side: {
down: { popup: [
"[--drawer-exit-y:calc(100%+var(--drawer-inset)+2px)]",
"[--drawer-stack-y:calc(0px-var(--drawer-stack-offset)-var(--drawer-stack-shrink-offset))]",
"[--drawer-transform-origin:50%_100%]"
] },
up: { popup: [
"[--drawer-exit-y:calc(-100%-var(--drawer-inset)-2px)]",
"[--drawer-stack-y:calc(var(--drawer-stack-offset)+var(--drawer-stack-shrink-offset))]",
"[--drawer-transform-origin:50%_0%]"
] },
left: { popup: [
"[--drawer-exit-x:calc(-100%-var(--drawer-inset)-2px)]",
"[--drawer-stack-x:calc(var(--drawer-stack-offset)+var(--drawer-stack-shrink-offset))]",
"[--drawer-transform-origin:0%_50%]"
] },
right: { popup: [
"[--drawer-exit-x:calc(100%+var(--drawer-inset)+2px)]",
"[--drawer-stack-x:calc(0px-var(--drawer-stack-offset)-var(--drawer-stack-shrink-offset))]",
"[--drawer-transform-origin:100%_50%]"
] }
},
axis: {
x: { popup: ["[--drawer-stack-size:100%]", "[--drawer-swipe-x:var(--drawer-swipe-movement-x)]"] },
y: { popup: ["[--drawer-stack-size:max(0px,var(--drawer-frontmost-height,var(--drawer-height)))]", "[--drawer-swipe-y:calc(var(--drawer-snap-point-offset,0px)+var(--drawer-swipe-movement-y))]"] }
},
snapPoints: { true: { overlay: ["[--drawer-overlay-min-opacity:0.5]"] } }
}
}),
slots: {
overlay: [
"fixed inset-0 z-50 min-h-dvh select-none",
"bg-layer-backdrop",
"supports-[-webkit-touch-callout:none]:absolute"
],
viewport: ["pointer-events-none fixed inset-0 z-50 select-none"],
popup: [
"group/drawer-popup pointer-events-auto fixed z-50 flex h-(--drawer-content-height) flex-col outline-none",
"text-foreground-strong",
"[--drawer-inset:12px] [--drawer-content-height:var(--drawer-height,auto)]",
"data-nested-drawer-open:overflow-hidden"
],
content: ["flex min-h-0 flex-1 flex-col rounded-[inherit] select-text", "group-data-swiping/drawer-popup:select-none"],
swipeHandle: ["relative z-10 shrink-0 cursor-grab select-none active:cursor-grabbing", "before:absolute before:rounded-full before:bg-ink-20 before:content-['']"],
close: ["shrink-0"],
closeIcon: [],
header: ["flex flex-col items-center px-6 pt-8 pb-2 text-center"],
body: ["flex flex-1 flex-col items-center px-6 pt-4 pb-6 text-center"],
footer: ["mt-auto px-6 py-5"],
actions: ["flex w-full flex-col items-stretch gap-2.5", "**:data-[slot=button]:w-full"],
title: ["max-w-sm text-pretty text-center text-2xl leading-8 text-foreground"],
description: ["max-w-sm text-pretty text-center text-sm leading-5 text-foreground-strong/60"]
},
variants: {
theme: {
brutal: {
title: ["font-bold"],
swipeHandle: ["before:rounded-none before:bg-ink-40"],
popup: ["border-2 border-line-strong bg-layer-panel shadow-raft-xl"],
close: ["bg-layer-panel"],
closeIcon: ["size-5"]
},
elegant: {
overlay: ["backdrop-blur-[2px]"],
popup: ["overflow-clip rounded-lg bg-layer-popover shadow-raft-xl md:rounded-xl"],
close: ["p-0 text-foreground-placeholder hover:bg-transparent hover:text-foreground-strong"],
closeIcon: ["size-4"],
header: ["bg-layer-panel px-8 pt-9 pb-2", "dark:bg-transparent"],
body: ["bg-layer-panel px-8 pt-4 pb-7", "dark:bg-transparent"],
footer: ["mt-0 px-8 py-5"],
title: ["font-sans text-2xl leading-8 font-semibold"],
description: ["font-sans text-sm leading-5 text-foreground-muted"]
}
},
side: {
down: {
popup: ["[--drawer-inset:max(12px,env(safe-area-inset-bottom,0px))]", "bottom-(--drawer-inset) min-h-64 max-h-[82vh]"],
swipeHandle: ["before:top-3"]
},
up: {
popup: ["[--drawer-inset:max(12px,env(safe-area-inset-top,0px))]", "top-(--drawer-inset) max-h-[80vh]"],
swipeHandle: ["order-last", "before:bottom-3"]
},
left: {
popup: ["left-(--drawer-inset)"],
swipeHandle: ["order-last", "before:right-3"]
},
right: {
popup: ["right-(--drawer-inset)"],
swipeHandle: ["before:left-3"]
}
},
axis: {
x: {
popup: ["inset-y-3", "flex-row w-3/4 max-w-sm"],
swipeHandle: ["my-auto h-16 w-11", "before:top-1/2 before:h-12 before:w-1.5 before:-translate-y-1/2"],
header: ["flex-row justify-between gap-3 pt-6 text-left"],
body: ["items-stretch text-left"],
footer: ["py-4"],
title: ["min-w-0 max-w-none flex-1 text-left"],
description: ["max-w-none text-left"]
},
y: {
popup: ["data-nested-drawer-open:h-(--drawer-frontmost-height)", "inset-x-3 mx-auto md:max-w-md"],
swipeHandle: ["mx-auto h-11 w-16", "before:left-1/2 before:h-1.5 before:w-12 before:-translate-x-1/2"]
}
}
},
compoundVariants: [
{
theme: "brutal",
side: "right",
class: { popup: ["[--drawer-inset:20px]"] }
},
{
theme: "brutal",
side: "down",
class: { popup: ["[--drawer-inset:max(20px,env(safe-area-inset-bottom,0px))]"] }
},
{
theme: "elegant",
axis: "x",
class: { popup: ["md:max-w-md"] }
},
{
side: "down",
snapPoints: true,
class: { header: ["pt-2"] }
},
{
axis: "y",
snapPoints: true,
class: { popup: ["[--drawer-content-height:100dvh]"] }
}
]
});
//#endregion
//#region src/components/drawer/drawer.tsx
const DrawerConfigContext = createContext(null);
function useDrawerConfig() {
const context = useContext(DrawerConfigContext);
if (!context) throw new Error("Drawer parts must be used within Drawer.");
return context;
}
function Drawer({ modal = true, snapPoints, swipeDirection = "down", ...props }) {
const hasSnapPoints = snapPoints != null && snapPoints.length > 0;
const swipeAxis = swipeDirection === "down" || swipeDirection === "up" ? "y" : "x";
const contextValue = useMemo(() => ({
hasSnapPoints,
modal,
swipeAxis,
swipeDirection
}), [
hasSnapPoints,
modal,
swipeAxis,
swipeDirection
]);
return /* @__PURE__ */ jsx(DrawerConfigContext.Provider, {
value: contextValue,
children: /* @__PURE__ */ jsx(Drawer$1.Root, {
modal,
snapPoints,
swipeDirection,
...props
})
});
}
function DrawerTrigger(props) {
return /* @__PURE__ */ jsx(Drawer$1.Trigger, {
"data-slot": "drawer-trigger",
...props
});
}
function DrawerPortal(props) {
return /* @__PURE__ */ jsx(Drawer$1.Portal, {
"data-slot": "drawer-portal",
...useThemeScopeProps(props)
});
}
function DrawerOverlay({ className, ...props }) {
const { hasSnapPoints } = useDrawerConfig();
const { overlay } = drawer({
theme: useThemeFamily(),
snapPoints: hasSnapPoints
});
return /* @__PURE__ */ jsx(Drawer$1.Backdrop, {
"data-slot": "drawer-overlay",
className: overlay({ className }),
...props
});
}
function DrawerSwipeHandle() {
const { swipeAxis, swipeDirection } = useDrawerConfig();
const { swipeHandle } = drawer({
theme: useThemeFamily(),
side: swipeDirection,
axis: swipeAxis
});
return /* @__PURE__ */ jsx("div", {
"data-slot": "drawer-swipe-handle",
"aria-hidden": "true",
className: swipeHandle()
});
}
function DrawerContent({ className, children, portalProps, ...props }) {
const { hasSnapPoints, modal, swipeAxis, swipeDirection } = useDrawerConfig();
const { content, popup, viewport } = drawer({
theme: useThemeFamily(),
side: swipeDirection,
axis: swipeAxis,
snapPoints: hasSnapPoints
});
return /* @__PURE__ */ jsxs(DrawerPortal, {
...portalProps,
children: [modal === true ? /* @__PURE__ */ jsx(DrawerOverlay, {}) : null, /* @__PURE__ */ jsx(Drawer$1.Viewport, {
"data-slot": "drawer-viewport",
className: cn(viewport(), modal === true && "pointer-events-auto"),
children: /* @__PURE__ */ jsxs(Drawer$1.Popup, {
"data-slot": "drawer-popup",
className: popup({ className }),
...props,
children: [hasSnapPoints ? /* @__PURE__ */ jsx(DrawerSwipeHandle, {}) : null, /* @__PURE__ */ jsx(Drawer$1.Content, {
"data-slot": "drawer-content",
className: content(),
children
})]
})
})]
});
}
function DrawerClose({ className, variant, size, children, render, ...props }) {
const theme = useThemeFamily();
const isBrutal = theme === "brutal";
const { close, closeIcon } = drawer({ theme });
if (render) return /* @__PURE__ */ jsx(Drawer$1.Close, {
"data-slot": "drawer-close",
className,
render,
...props,
children
});
return /* @__PURE__ */ jsx(Drawer$1.Close, {
"data-slot": "drawer-close",
render: /* @__PURE__ */ jsx(Button, {
variant: variant ?? (isBrutal ? "outline" : "ghost"),
size: size ?? (isBrutal ? "icon-md" : "icon-sm"),
"aria-label": "Close"
}),
className: close({ className }),
...props,
children: children ?? /* @__PURE__ */ jsx(X, { className: closeIcon() })
});
}
function DrawerHeader({ className, render, ...props }) {
const { hasSnapPoints, swipeAxis, swipeDirection } = useDrawerConfig();
const { header } = drawer({
theme: useThemeFamily(),
side: swipeDirection,
axis: swipeAxis,
snapPoints: hasSnapPoints
});
return useRender({
defaultTagName: "div",
render,
props: mergeProps({ className: header({ className }) }, props),
state: { slot: "drawer-header" }
});
}
function DrawerFooter({ className, render, ...props }) {
const { swipeAxis } = useDrawerConfig();
const { footer } = drawer({
theme: useThemeFamily(),
axis: swipeAxis
});
return useRender({
defaultTagName: "div",
render,
props: mergeProps({ className: footer({ className }) }, props),
state: { slot: "drawer-footer" }
});
}
function DrawerBody({ className, render, ...props }) {
const { swipeAxis } = useDrawerConfig();
const { body } = drawer({
theme: useThemeFamily(),
axis: swipeAxis
});
return useRender({
defaultTagName: "div",
render,
props: mergeProps({ className: body({ className }) }, props),
state: { slot: "drawer-body" }
});
}
function DrawerActions({ className, render, children, ...props }) {
const { actions } = drawer({ theme: useThemeFamily() });
return useRender({
defaultTagName: "div",
render,
props: mergeProps({
className: actions({ className }),
children
}, props),
state: { slot: "drawer-actions" }
});
}
function DrawerTitle({ className, ...props }) {
const { swipeAxis } = useDrawerConfig();
const { title } = drawer({
theme: useThemeFamily(),
axis: swipeAxis
});
return /* @__PURE__ */ jsx(Drawer$1.Title, {
"data-slot": "drawer-title",
className: title({ className }),
...props
});
}
function DrawerDescription({ className, ...props }) {
const { swipeAxis } = useDrawerConfig();
const { description } = drawer({
theme: useThemeFamily(),
axis: swipeAxis
});
return /* @__PURE__ */ jsx(Drawer$1.Description, {
"data-slot": "drawer-description",
className: description({ className }),
...props
});
}
//#endregion
//#region src/components/card/card.recipe.ts
const card = tv$1({
slots: {
root: ["flex min-w-0 flex-col overflow-hidden", "text-foreground"],
header: [
"group/card-header grid min-w-0 auto-rows-min grid-cols-1 items-start gap-x-4 gap-y-1",
"has-[>[data-slot=card-leading]]:grid-cols-[auto_minmax(0,1fr)]",
"has-[>[data-slot=card-trailing]]:grid-cols-[minmax(0,1fr)_auto]",
"has-[>[data-slot=card-leading]]:has-[>[data-slot=card-trailing]]:grid-cols-[auto_minmax(0,1fr)_auto]"
],
title: ["col-start-1 row-start-1 min-w-0", "group-has-[>[data-slot=card-leading]]/card-header:col-start-2"],
leading: [
"col-start-1 row-span-2 row-start-1",
"flex shrink-0 items-center self-start",
"[&>svg]:size-4 [&>svg]:shrink-0"
],
description: ["col-start-1 row-start-2 min-w-0", "group-has-[>[data-slot=card-leading]]/card-header:col-start-2"],
trailing: [
"col-start-2 row-start-1",
"flex shrink-0 items-center self-center justify-self-end",
"group-has-[>[data-slot=card-leading]]/card-header:col-start-3"
],
content: ["min-w-0"],
footer: ["flex min-w-0 flex-wrap items-center gap-2"]
},
variants: {
theme: {
brutal: {
leading: ["min-h-5"],
title: ["font-heading text-lg leading-5 font-bold"],
description: ["font-sans text-sm leading-5 text-foreground-strong/60"],
footer: [
"flex-nowrap items-center",
"font-sans text-xs leading-4 text-foreground-muted",
"[&>svg]:size-3.5 [&>svg]:shrink-0",
"[&>:not(svg)]:min-w-0"
]
},
elegant: {
leading: ["min-h-[22px]"],
title: ["font-sans text-base leading-[22px] font-medium"],
description: ["font-sans text-xs leading-4 tracking-[0.01em] text-pretty", "text-foreground-muted"],
footer: [
"flex-nowrap items-center",
"font-sans text-xs leading-4 text-foreground-hint",
"[&>svg]:size-3.5 [&>svg]:shrink-0",
"[&>:not(svg)]:min-w-0"
]
}
},
variant: {
default: {},
inset: {}
}
},
defaultVariants: { variant: "default" },
compoundVariants: [
{
theme: "brutal",
variant: "default",
class: {
root: ["border-2 border-line-strong bg-layer-panel", "shadow-raft-md"],
header: "border-b-2 border-line-strong p-4",
content: "p-4",
footer: "border-t-2 border-line-strong bg-layer-inset px-4 py-3"
}
},
{
theme: "brutal",
variant: "inset",
class: {
root: ["border-2 border-line-strong bg-layer-panel px-2", "shadow-raft-md"],
header: "px-4 py-3",
content: "-mx-2 border-y-2 border-line-strong bg-layer-panel px-6 py-4",
footer: "px-4 py-3"
}
},
{
theme: "elegant",
variant: "default",
class: {
root: ["rounded-lg border-[0.5px] border-line-muted bg-layer-panel", "shadow-raft-xs dark:border-transparent"],
header: "p-5 has-[+[data-slot=card-content]]:pb-0",
content: "p-5",
footer: ["border-t border-line-hairline bg-layer-inset px-5 py-3.5", "dark:border-transparent dark:bg-transparent"]
}
},
{
theme: "elegant",
variant: "inset",
class: {
root: ["rounded-lg border-[0.5px] border-line-muted bg-layer-card p-[2px]", "shadow-raft-xs dark:border-transparent"],
header: "px-5 py-3.5",
content: ["overflow-clip rounded-[5.5px] border-[0.5px] border-line-hairline bg-layer-panel p-5", "dark:border-transparent dark:shadow-[inset_0_1px_0_oklch(1_0_0/0.04),0_0_0_1px_oklch(0_0_0/0.35)]"],
footer: "px-5 py-3"
}
}
]
});
//#endregion
//#region src/components/card/card.tsx
const CardVariantContext = createContext("default");
function useCardRecipe() {
return card({
theme: useThemeFamily(),
variant: use(CardVariantContext)
});
}
function Card({ className, render, variant = "default", ...props }) {
const { root } = card({
theme: useThemeFamily(),
variant
});
const element = useRender({
defaultTagName: "div",
render,
props: mergeProps({
"data-slot": "card",
"data-variant": variant,
className: root({ className })
}, props)
});
return /* @__PURE__ */ jsx(CardVariantContext, {
value: variant,
children: element
});
}
function CardHeader({ className, render, ...props }) {
const { header } = useCardRecipe();
return useRender({
defaultTagName: "div",
render,
props: mergeProps({
"data-slot": "card-header",
className: header({ className })
}, props)
});
}
function CardTitle({ className, render, ...props }) {
const { title } = useCardRecipe();
return useRender({
defaultTagName: "div",
render,
props: mergeProps({
"data-slot": "card-title",
className: title({ className })
}, props)
});
}
function CardLeading({ className, render, ...props }) {
const { leading } = useCardRecipe();
return useRender({
defaultTagName: "div",
render,
props: mergeProps({
"data-slot": "card-leading",
className: leading({ className })
}, props)
});
}
function CardDescription({ className, render, ...props }) {
const { description } = useCardRecipe();
return useRender({
defaultTagName: "div",
render,
props: mergeProps({
"data-slot": "card-description",
className: description({ className })
}, props)
});
}
function CardTrailing({ className, render, ...props }) {
const { trailing } = useCardRecipe();
return useRender({
defaultTagName: "div",
render,
props: mergeProps({
"data-slot": "card-trailing",
className: trailing({ className })
}, props)
});
}
function CardContent({ className, render, ...props }) {
const { content } = useCardRecipe();
return useRender({
defaultTagName: "div",
render,
props: mergeProps({
"data-slot": "card-content",
className: content({ className })
}, props)
});
}
function CardFooter({ className, render, ...props }) {
const { footer } = useCardRecipe();
return useRender({