mirror of
https://github.com/temetro/temetro.git
synced 2026-07-26 11:58:14 +00:00
bd8fdfadda
- Invoice dialog: the Back-date / Due-date toggles now use the COSS Checkbox instead of raw, misshapen native checkboxes. - Patients page: rebuilt the list as a COSS Table in a CardFrame, and moved "Import from a patient app" into a ⋯ overflow menu so the toolbar keeps one primary "Add patient" CTA. - Patient detail sheet: collapsed the five header buttons + delete into a primary Edit action plus a ⋯ More menu (Download summary, Record visit, Transfer, Push to wallet, and a destructive Delete). - Added the COSS table + checkbox primitives; new i18n keys across all locales. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
152 lines
5.3 KiB
TypeScript
152 lines
5.3 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 type TableVariant = "default" | "card";
|
|
|
|
export type TableProps = React.ComponentProps<"table"> & {
|
|
variant?: TableVariant;
|
|
render?: useRender.ComponentProps<"div">["render"];
|
|
};
|
|
|
|
export function Table({
|
|
className,
|
|
variant = "default",
|
|
render,
|
|
...props
|
|
}: TableProps): React.ReactElement {
|
|
const defaultProps = {
|
|
children: (
|
|
<table
|
|
className={cn(
|
|
"w-full caption-bottom in-data-[variant=card]:border-separate in-data-[variant=card]:border-spacing-0 text-sm",
|
|
className,
|
|
)}
|
|
data-slot="table"
|
|
{...props}
|
|
/>
|
|
),
|
|
className: "relative w-full overflow-x-auto",
|
|
"data-slot": "table-container",
|
|
"data-variant": variant,
|
|
};
|
|
|
|
return useRender({
|
|
defaultTagName: "div",
|
|
props: mergeProps<"div">(defaultProps, {}),
|
|
render,
|
|
});
|
|
}
|
|
|
|
export function TableHeader({
|
|
className,
|
|
...props
|
|
}: React.ComponentProps<"thead">): React.ReactElement {
|
|
return (
|
|
<thead
|
|
className={cn("[&_tr]:border-b", className)}
|
|
data-slot="table-header"
|
|
{...props}
|
|
/>
|
|
);
|
|
}
|
|
|
|
export function TableBody({
|
|
className,
|
|
...props
|
|
}: React.ComponentProps<"tbody">): React.ReactElement {
|
|
return (
|
|
<tbody
|
|
className={cn(
|
|
"relative in-data-[variant=card]:rounded-xl in-data-[variant=card]:shadow-xs/5 before:pointer-events-none before:absolute before:inset-px not-in-data-[variant=card]:before:hidden before:rounded-[calc(var(--radius-xl)-1px)] before:shadow-[0_1px_--theme(--color-black/4%)] dark:before:shadow-[0_-1px_--theme(--color-white/8%)] [&_tr:last-child]:border-0 in-data-[variant=card]:*:[tr]:border-0 in-data-[variant=card]:*:[tr]:*:[td]:border-b in-data-[variant=card]:*:[tr]:*:[td]:bg-card in-data-[variant=card]:*:[tr]:first:*:[td]:first:rounded-ss-xl in-data-[variant=card]:*:[tr]:*:[td]:first:border-s in-data-[variant=card]:*:[tr]:first:*:[td]:border-t in-data-[variant=card]:*:[tr]:last:*:[td]:last:rounded-ee-xl in-data-[variant=card]:*:[tr]:*:[td]:last:border-e in-data-[variant=card]:*:[tr]:first:*:[td]:last:rounded-se-xl in-data-[variant=card]:*:[tr]:last:*:[td]:first:rounded-es-xl in-data-[variant=card]:*:[tr]:hover:*:[td]:bg-[color-mix(in_srgb,var(--card),var(--color-black)_2%)] in-data-[variant=card]:*:[tr]:data-[state=selected]:*:[td]:bg-[color-mix(in_srgb,var(--card),var(--color-black)_4%)] dark:in-data-[variant=card]:*:[tr]:data-[state=selected]:*:[td]:bg-[color-mix(in_srgb,var(--card),var(--color-white)_4%)] dark:in-data-[variant=card]:*:[tr]:hover:*:[td]:bg-[color-mix(in_srgb,var(--card),var(--color-white)_2%)]",
|
|
className,
|
|
)}
|
|
data-slot="table-body"
|
|
{...props}
|
|
/>
|
|
);
|
|
}
|
|
|
|
export function TableFooter({
|
|
className,
|
|
...props
|
|
}: React.ComponentProps<"tfoot">): React.ReactElement {
|
|
return (
|
|
<tfoot
|
|
className={cn(
|
|
"border-t in-data-[variant=card]:border-none bg-transparent not-in-data-[variant=card]:bg-[color-mix(in_srgb,var(--card),var(--color-black)_2%)] font-medium dark:not-in-data-[variant=card]:bg-[color-mix(in_srgb,var(--card),var(--color-white)_2%)] [&>tr]:last:border-b-0",
|
|
className,
|
|
)}
|
|
data-slot="table-footer"
|
|
{...props}
|
|
/>
|
|
);
|
|
}
|
|
|
|
export function TableRow({
|
|
className,
|
|
...props
|
|
}: React.ComponentProps<"tr">): React.ReactElement {
|
|
return (
|
|
<tr
|
|
className={cn(
|
|
"relative border-b not-in-data-[variant=card]:hover:bg-[color-mix(in_srgb,var(--background),var(--color-black)_2%)] not-in-data-[variant=card]:data-[state=selected]:bg-[color-mix(in_srgb,var(--background),var(--color-black)_4%)] dark:not-in-data-[variant=card]:data-[state=selected]:bg-[color-mix(in_srgb,var(--background),var(--color-white)_4%)] dark:not-in-data-[variant=card]:hover:bg-[color-mix(in_srgb,var(--background),var(--color-white)_2%)]",
|
|
className,
|
|
)}
|
|
data-slot="table-row"
|
|
{...props}
|
|
/>
|
|
);
|
|
}
|
|
|
|
export function TableHead({
|
|
className,
|
|
...props
|
|
}: React.ComponentProps<"th">): React.ReactElement {
|
|
return (
|
|
<th
|
|
className={cn(
|
|
"h-10 whitespace-nowrap px-2.5 text-left align-middle font-medium text-muted-foreground leading-none has-[[role=checkbox]]:w-px last:has-[[role=checkbox]]:ps-0 first:has-[[role=checkbox]]:pe-0",
|
|
className,
|
|
)}
|
|
data-slot="table-head"
|
|
{...props}
|
|
/>
|
|
);
|
|
}
|
|
|
|
export function TableCell({
|
|
className,
|
|
...props
|
|
}: React.ComponentProps<"td">): React.ReactElement {
|
|
return (
|
|
<td
|
|
className={cn(
|
|
"whitespace-nowrap bg-clip-padding p-2.5 in-data-[slot=table-footer]:py-3.5 align-middle leading-none in-data-[variant=card]:first:ps-[calc(--spacing(2.5)-1px)] in-data-[variant=card]:last:pe-[calc(--spacing(2.5)-1px)] has-[[role=checkbox]]:w-px last:has-[[role=checkbox]]:ps-0 first:has-[[role=checkbox]]:pe-0",
|
|
className,
|
|
)}
|
|
data-slot="table-cell"
|
|
{...props}
|
|
/>
|
|
);
|
|
}
|
|
|
|
export function TableCaption({
|
|
className,
|
|
...props
|
|
}: React.ComponentProps<"caption">): React.ReactElement {
|
|
return (
|
|
<caption
|
|
className={cn(
|
|
"in-data-[variant=card]:my-4 mt-4 text-muted-foreground text-sm",
|
|
className,
|
|
)}
|
|
data-slot="table-caption"
|
|
{...props}
|
|
/>
|
|
);
|
|
}
|