chore: initial Untitled UI Vite scaffold with FontAwesome Pro

This commit is contained in:
2026-03-16 14:23:23 +05:30
commit 3a338b33dd
163 changed files with 27081 additions and 0 deletions

View File

@@ -0,0 +1,298 @@
import type { ComponentPropsWithRef, HTMLAttributes, ReactNode, Ref, TdHTMLAttributes, ThHTMLAttributes } from "react";
import { createContext, isValidElement, useContext } from "react";
import { ArrowDown, ChevronSelectorVertical, Copy01, Edit01, HelpCircle, Trash01 } from "@untitledui/icons";
import type {
CellProps as AriaCellProps,
ColumnProps as AriaColumnProps,
RowProps as AriaRowProps,
TableHeaderProps as AriaTableHeaderProps,
TableProps as AriaTableProps,
} from "react-aria-components";
import {
Cell as AriaCell,
Collection as AriaCollection,
Column as AriaColumn,
Group as AriaGroup,
Row as AriaRow,
Table as AriaTable,
TableBody as AriaTableBody,
TableHeader as AriaTableHeader,
useTableOptions,
} from "react-aria-components";
import { Badge } from "@/components/base/badges/badges";
import { Checkbox } from "@/components/base/checkbox/checkbox";
import { Dropdown } from "@/components/base/dropdown/dropdown";
import { Tooltip, TooltipTrigger } from "@/components/base/tooltip/tooltip";
import { cx } from "@/utils/cx";
export const TableRowActionsDropdown = () => (
<Dropdown.Root>
<Dropdown.DotsButton />
<Dropdown.Popover className="w-min">
<Dropdown.Menu>
<Dropdown.Item icon={Edit01}>
<span className="pr-4">Edit</span>
</Dropdown.Item>
<Dropdown.Item icon={Copy01}>
<span className="pr-4">Copy link</span>
</Dropdown.Item>
<Dropdown.Item icon={Trash01}>
<span className="pr-4">Delete</span>
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown.Popover>
</Dropdown.Root>
);
const TableContext = createContext<{ size: "sm" | "md" }>({ size: "md" });
const TableCardRoot = ({ children, className, size = "md", ...props }: HTMLAttributes<HTMLDivElement> & { size?: "sm" | "md" }) => {
return (
<TableContext.Provider value={{ size }}>
<div {...props} className={cx("overflow-hidden rounded-xl bg-primary shadow-xs ring-1 ring-secondary", className)}>
{children}
</div>
</TableContext.Provider>
);
};
interface TableCardHeaderProps {
/** The title of the table card header. */
title: string;
/** The badge displayed next to the title. */
badge?: ReactNode;
/** The description of the table card header. */
description?: string;
/** The content displayed after the title and badge. */
contentTrailing?: ReactNode;
/** The class name of the table card header. */
className?: string;
}
const TableCardHeader = ({ title, badge, description, contentTrailing, className }: TableCardHeaderProps) => {
const { size } = useContext(TableContext);
return (
<div
className={cx(
"relative flex flex-col items-start gap-4 border-b border-secondary bg-primary px-4 md:flex-row",
size === "sm" ? "py-4 md:px-5" : "py-5 md:px-6",
className,
)}
>
<div className="flex flex-1 flex-col gap-0.5">
<div className="flex items-center gap-2">
<h2 className={cx("font-semibold text-primary", size === "sm" ? "text-md" : "text-lg")}>{title}</h2>
{badge ? (
isValidElement(badge) ? (
badge
) : (
<Badge color="brand" size="sm">
{badge}
</Badge>
)
) : null}
</div>
{description && <p className="text-sm text-tertiary">{description}</p>}
</div>
{contentTrailing}
</div>
);
};
interface TableRootProps extends AriaTableProps, Omit<ComponentPropsWithRef<"table">, "className" | "slot" | "style"> {
size?: "sm" | "md";
}
const TableRoot = ({ className, size = "md", ...props }: TableRootProps) => {
const context = useContext(TableContext);
return (
<TableContext.Provider value={{ size: context?.size ?? size }}>
<div className="overflow-x-auto">
<AriaTable className={(state) => cx("w-full overflow-x-hidden", typeof className === "function" ? className(state) : className)} {...props} />
</div>
</TableContext.Provider>
);
};
TableRoot.displayName = "Table";
interface TableHeaderProps<T extends object>
extends AriaTableHeaderProps<T>,
Omit<ComponentPropsWithRef<"thead">, "children" | "className" | "slot" | "style"> {
bordered?: boolean;
}
const TableHeader = <T extends object>({ columns, children, bordered = true, className, ...props }: TableHeaderProps<T>) => {
const { size } = useContext(TableContext);
const { selectionBehavior, selectionMode } = useTableOptions();
return (
<AriaTableHeader
{...props}
className={(state) =>
cx(
"relative bg-secondary",
size === "sm" ? "h-9" : "h-11",
// Row border—using an "after" pseudo-element to avoid the border taking up space.
bordered &&
"[&>tr>th]:after:pointer-events-none [&>tr>th]:after:absolute [&>tr>th]:after:inset-x-0 [&>tr>th]:after:bottom-0 [&>tr>th]:after:h-px [&>tr>th]:after:bg-border-secondary [&>tr>th]:focus-visible:after:bg-transparent",
typeof className === "function" ? className(state) : className,
)
}
>
{selectionBehavior === "toggle" && (
<AriaColumn className={cx("relative py-2 pr-0 pl-4", size === "sm" ? "w-9 md:pl-5" : "w-11 md:pl-6")}>
{selectionMode === "multiple" && (
<div className="flex items-start">
<Checkbox slot="selection" size={size} />
</div>
)}
</AriaColumn>
)}
<AriaCollection items={columns}>{children}</AriaCollection>
</AriaTableHeader>
);
};
TableHeader.displayName = "TableHeader";
interface TableHeadProps extends AriaColumnProps, Omit<ThHTMLAttributes<HTMLTableCellElement>, "children" | "className" | "style" | "id"> {
label?: string;
tooltip?: string;
}
const TableHead = ({ className, tooltip, label, children, ...props }: TableHeadProps) => {
const { selectionBehavior } = useTableOptions();
return (
<AriaColumn
{...props}
className={(state) =>
cx(
"relative p-0 px-6 py-2 outline-hidden focus-visible:z-1 focus-visible:ring-2 focus-visible:ring-focus-ring focus-visible:ring-offset-bg-primary focus-visible:ring-inset",
selectionBehavior === "toggle" && "nth-2:pl-3",
state.allowsSorting && "cursor-pointer",
typeof className === "function" ? className(state) : className,
)
}
>
{(state) => (
<AriaGroup className="flex items-center gap-1">
<div className="flex items-center gap-1">
{label && <span className="text-xs font-semibold whitespace-nowrap text-quaternary">{label}</span>}
{typeof children === "function" ? children(state) : children}
</div>
{tooltip && (
<Tooltip title={tooltip} placement="top">
<TooltipTrigger className="cursor-pointer text-fg-quaternary transition duration-100 ease-linear hover:text-fg-quaternary_hover focus:text-fg-quaternary_hover">
<HelpCircle className="size-4" />
</TooltipTrigger>
</Tooltip>
)}
{state.allowsSorting &&
(state.sortDirection ? (
<ArrowDown className={cx("size-3 stroke-[3px] text-fg-quaternary", state.sortDirection === "ascending" && "rotate-180")} />
) : (
<ChevronSelectorVertical size={12} strokeWidth={3} className="text-fg-quaternary" />
))}
</AriaGroup>
)}
</AriaColumn>
);
};
TableHead.displayName = "TableHead";
interface TableRowProps<T extends object>
extends AriaRowProps<T>,
Omit<ComponentPropsWithRef<"tr">, "children" | "className" | "onClick" | "slot" | "style" | "id"> {
highlightSelectedRow?: boolean;
}
const TableRow = <T extends object>({ columns, children, className, highlightSelectedRow = true, ...props }: TableRowProps<T>) => {
const { size } = useContext(TableContext);
const { selectionBehavior } = useTableOptions();
return (
<AriaRow
{...props}
className={(state) =>
cx(
"relative outline-focus-ring transition-colors after:pointer-events-none hover:bg-secondary focus-visible:outline-2 focus-visible:-outline-offset-2",
size === "sm" ? "h-14" : "h-18",
highlightSelectedRow && "selected:bg-secondary",
// Row border—using an "after" pseudo-element to avoid the border taking up space.
"[&>td]:after:absolute [&>td]:after:inset-x-0 [&>td]:after:bottom-0 [&>td]:after:h-px [&>td]:after:w-full [&>td]:after:bg-border-secondary last:[&>td]:after:hidden [&>td]:focus-visible:after:opacity-0 focus-visible:[&>td]:after:opacity-0",
typeof className === "function" ? className(state) : className,
)
}
>
{selectionBehavior === "toggle" && (
<AriaCell className={cx("relative py-2 pr-0 pl-4", size === "sm" ? "md:pl-5" : "md:pl-6")}>
<div className="flex items-end">
<Checkbox slot="selection" size={size} />
</div>
</AriaCell>
)}
<AriaCollection items={columns}>{children}</AriaCollection>
</AriaRow>
);
};
TableRow.displayName = "TableRow";
interface TableCellProps extends AriaCellProps, Omit<TdHTMLAttributes<HTMLTableCellElement>, "children" | "className" | "style" | "id"> {
ref?: Ref<HTMLTableCellElement>;
}
const TableCell = ({ className, children, ...props }: TableCellProps) => {
const { size } = useContext(TableContext);
const { selectionBehavior } = useTableOptions();
return (
<AriaCell
{...props}
className={(state) =>
cx(
"relative text-sm text-tertiary outline-focus-ring focus-visible:z-1 focus-visible:outline-2 focus-visible:-outline-offset-2",
size === "sm" && "px-5 py-3",
size === "md" && "px-6 py-4",
selectionBehavior === "toggle" && "nth-2:pl-3",
typeof className === "function" ? className(state) : className,
)
}
>
{children}
</AriaCell>
);
};
TableCell.displayName = "TableCell";
const TableCard = {
Root: TableCardRoot,
Header: TableCardHeader,
};
const Table = TableRoot as typeof TableRoot & {
Body: typeof AriaTableBody;
Cell: typeof TableCell;
Head: typeof TableHead;
Header: typeof TableHeader;
Row: typeof TableRow;
};
Table.Body = AriaTableBody;
Table.Cell = TableCell;
Table.Head = TableHead;
Table.Header = TableHeader;
Table.Row = TableRow;
export { Table, TableCard };