mirror of
https://github.com/foomo/ui.git
synced 2026-10-04 04:56:53 +00:00
refactor!: rename the tailwind prefix from lib: to fui:
BREAKING: every utility this package emits is now fui:* instead of lib:*. Consumers who wrote lib:-prefixed classes against @foomo/ui must rename them. "lib" is a plausible prefix for any in-house library, so it risked colliding in apps that consume more than one. 3658 occurrences across 59 files, plus tailwind.config.js and components.json. The ./lib/* export path is untouched — only the prefix token moved. Also drops the ASCII banner comments and the explanatory docblocks I had added to data-table and the dashboard story. Verified: 19 stories x 2 themes, 194 overlays opened, 0 console errors and 0 elements left unstyled.
This commit is contained in:
@@ -19,7 +19,7 @@ const withTheme: Decorator = (Story, context) => {
|
||||
document.documentElement.style.backgroundColor = "var(--background)";
|
||||
document.body.style.backgroundColor = "var(--background)";
|
||||
document.body.style.color = "var(--foreground)";
|
||||
document.body.classList.add("lib:bg-background", "lib:text-foreground");
|
||||
document.body.classList.add("fui:bg-background", "fui:text-foreground");
|
||||
|
||||
return Story();
|
||||
};
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
"css": "src/index.css",
|
||||
"baseColor": "mist",
|
||||
"cssVariables": true,
|
||||
"prefix": "lib"
|
||||
"prefix": "fui"
|
||||
},
|
||||
"aliases": {
|
||||
"components": "@/components",
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@foomo/ui",
|
||||
"version": "0.1.0-alpha.4",
|
||||
"version": "0.1.0-alpha.5",
|
||||
"type": "module",
|
||||
"files": [
|
||||
"dist",
|
||||
@@ -45,7 +45,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "rm -rf ./dist && tsc -p tsconfig.build.json && vite build",
|
||||
"build": "rm -rf ./dist && tsc -p tsconfig.build.json && node scripts/rewrite-dts-aliases.mjs && vite build",
|
||||
"lint": "biome lint --write ./src",
|
||||
"format": "biome format --write ./src && biome check --fix --unsafe ./src",
|
||||
"preview": "vite preview",
|
||||
@@ -123,4 +123,4 @@
|
||||
"vite-plugin-static-copy": "^3.1.4",
|
||||
"vitest": "3.2.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Rewrite `@/…` path aliases in the emitted declaration files to relative
|
||||
* specifiers.
|
||||
*
|
||||
* `tsc` resolves the alias when type-checking but emits it verbatim into the
|
||||
* `.d.ts` output, and consumers have no `@/*` mapping — so every such import
|
||||
* silently degrades to `any` on their side. That is not a cosmetic problem:
|
||||
* `alert-dialog.d.ts` importing `Button` this way turns
|
||||
* `Pick<ComponentProps<typeof Button>, "variant" | "size">` into
|
||||
* `Pick<any, …>`, which makes both props *required* for callers.
|
||||
*
|
||||
* Vite already rewrites the alias in the `.js` output, so this only has to
|
||||
* catch the declarations. It runs as a build step rather than a source-level
|
||||
* change because `regenerate-components` re-runs the shadcn CLI, which always
|
||||
* emits `@/` imports.
|
||||
*/
|
||||
import { readdir, readFile, writeFile } from "node:fs/promises";
|
||||
import { join, relative, dirname, sep } from "node:path";
|
||||
|
||||
const DIST = new URL("../dist/", import.meta.url).pathname;
|
||||
const ALIAS = /(from\s*")@\/([^"]+)(")/g;
|
||||
|
||||
async function* declarations(dir) {
|
||||
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
||||
const path = join(dir, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
yield* declarations(path);
|
||||
} else if (entry.name.endsWith(".d.ts")) {
|
||||
yield path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let rewritten = 0;
|
||||
let files = 0;
|
||||
|
||||
for await (const file of declarations(DIST)) {
|
||||
const source = await readFile(file, "utf8");
|
||||
let count = 0;
|
||||
|
||||
const output = source.replace(ALIAS, (_match, open, target, close) => {
|
||||
count += 1;
|
||||
// `dist/components/x.d.ts` -> `dist/`, so `@/components/button`
|
||||
// becomes `./button`.
|
||||
let path = relative(dirname(file), join(DIST, target)).split(sep).join("/");
|
||||
if (!path.startsWith(".")) {
|
||||
path = `./${path}`;
|
||||
}
|
||||
return `${open}${path}${close}`;
|
||||
});
|
||||
|
||||
if (count > 0) {
|
||||
await writeFile(file, output);
|
||||
rewritten += count;
|
||||
files += 1;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(
|
||||
`rewrite-dts-aliases: ${rewritten} import(s) across ${files} declaration file(s)`,
|
||||
);
|
||||
@@ -7,7 +7,7 @@ function Accordion({ className, ...props }: AccordionPrimitive.Root.Props) {
|
||||
<AccordionPrimitive.Root
|
||||
data-slot="accordion"
|
||||
className={cn(
|
||||
"lib:flex lib:w-full lib:flex-col lib:overflow-hidden lib:rounded-2xl lib:border",
|
||||
"fui:flex fui:w-full fui:flex-col fui:overflow-hidden fui:rounded-2xl fui:border",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -20,7 +20,7 @@ function AccordionItem({ className, ...props }: AccordionPrimitive.Item.Props) {
|
||||
<AccordionPrimitive.Item
|
||||
data-slot="accordion-item"
|
||||
className={cn(
|
||||
"lib:not-last:border-b lib:data-open:bg-muted/50",
|
||||
"fui:not-last:border-b fui:data-open:bg-muted/50",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -34,11 +34,11 @@ function AccordionTrigger({
|
||||
...props
|
||||
}: AccordionPrimitive.Trigger.Props) {
|
||||
return (
|
||||
<AccordionPrimitive.Header className="lib:flex">
|
||||
<AccordionPrimitive.Header className="fui:flex">
|
||||
<AccordionPrimitive.Trigger
|
||||
data-slot="accordion-trigger"
|
||||
className={cn(
|
||||
"lib:group/accordion-trigger lib:relative lib:flex lib:flex-1 lib:items-start lib:justify-between lib:gap-6 lib:border lib:border-transparent lib:p-4 lib:text-left lib:text-sm lib:font-medium lib:transition-all lib:outline-none lib:hover:underline lib:aria-disabled:pointer-events-none lib:aria-disabled:opacity-50 lib:**:data-[slot=accordion-trigger-icon]:ml-auto lib:**:data-[slot=accordion-trigger-icon]:size-4 lib:**:data-[slot=accordion-trigger-icon]:text-muted-foreground",
|
||||
"fui:group/accordion-trigger fui:relative fui:flex fui:flex-1 fui:items-start fui:justify-between fui:gap-6 fui:border fui:border-transparent fui:p-4 fui:text-left fui:text-sm fui:font-medium fui:transition-all fui:outline-none fui:hover:underline fui:aria-disabled:pointer-events-none fui:aria-disabled:opacity-50 fui:**:data-[slot=accordion-trigger-icon]:ml-auto fui:**:data-[slot=accordion-trigger-icon]:size-4 fui:**:data-[slot=accordion-trigger-icon]:text-muted-foreground",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -46,11 +46,11 @@ function AccordionTrigger({
|
||||
{children}
|
||||
<ChevronDownIcon
|
||||
data-slot="accordion-trigger-icon"
|
||||
className="lib:pointer-events-none lib:shrink-0 lib:group-aria-expanded/accordion-trigger:hidden"
|
||||
className="fui:pointer-events-none fui:shrink-0 fui:group-aria-expanded/accordion-trigger:hidden"
|
||||
/>
|
||||
<ChevronUpIcon
|
||||
data-slot="accordion-trigger-icon"
|
||||
className="lib:pointer-events-none lib:hidden lib:shrink-0 lib:group-aria-expanded/accordion-trigger:inline"
|
||||
className="fui:pointer-events-none fui:hidden fui:shrink-0 fui:group-aria-expanded/accordion-trigger:inline"
|
||||
/>
|
||||
</AccordionPrimitive.Trigger>
|
||||
</AccordionPrimitive.Header>
|
||||
@@ -65,12 +65,12 @@ function AccordionContent({
|
||||
return (
|
||||
<AccordionPrimitive.Panel
|
||||
data-slot="accordion-content"
|
||||
className="lib:overflow-hidden lib:px-4 lib:text-sm lib:data-open:animate-accordion-down lib:data-closed:animate-accordion-up"
|
||||
className="fui:overflow-hidden fui:px-4 fui:text-sm fui:data-open:animate-accordion-down fui:data-closed:animate-accordion-up"
|
||||
{...props}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"lib:h-(--accordion-panel-height) lib:pt-0 lib:pb-4 lib:data-ending-style:h-0 lib:data-starting-style:h-0 lib:[&_a]:underline lib:[&_a]:underline-offset-3 lib:[&_a]:hover:text-foreground lib:[&_p:not(:last-child)]:mb-4",
|
||||
"fui:h-(--accordion-panel-height) fui:pt-0 fui:pb-4 fui:data-ending-style:h-0 fui:data-starting-style:h-0 fui:[&_a]:underline fui:[&_a]:underline-offset-3 fui:[&_a]:hover:text-foreground fui:[&_p:not(:last-child)]:mb-4",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -28,7 +28,7 @@ function AlertDialogOverlay({
|
||||
<AlertDialogPrimitive.Backdrop
|
||||
data-slot="alert-dialog-overlay"
|
||||
className={cn(
|
||||
"lib:fixed lib:inset-0 lib:isolate lib:z-50 lib:bg-black/80 lib:duration-100 lib:supports-backdrop-filter:backdrop-blur-xs lib:data-open:animate-in lib:data-open:fade-in-0 lib:data-closed:animate-out lib:data-closed:fade-out-0",
|
||||
"fui:fixed fui:inset-0 fui:isolate fui:z-50 fui:bg-black/80 fui:duration-100 fui:supports-backdrop-filter:backdrop-blur-xs fui:data-open:animate-in fui:data-open:fade-in-0 fui:data-closed:animate-out fui:data-closed:fade-out-0",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -50,7 +50,7 @@ function AlertDialogContent({
|
||||
data-slot="alert-dialog-content"
|
||||
data-size={size}
|
||||
className={cn(
|
||||
"lib:group/alert-dialog-content lib:fixed lib:top-1/2 lib:left-1/2 lib:z-50 lib:grid lib:w-full lib:-translate-x-1/2 lib:-translate-y-1/2 lib:gap-6 lib:rounded-4xl lib:bg-popover lib:p-6 lib:text-popover-foreground lib:ring-1 lib:ring-foreground/5 lib:duration-100 lib:outline-none lib:data-[size=default]:max-w-xs lib:data-[size=sm]:max-w-xs lib:data-[size=default]:sm:max-w-md lib:data-open:animate-in lib:data-open:fade-in-0 lib:data-open:zoom-in-95 lib:data-closed:animate-out lib:data-closed:fade-out-0 lib:data-closed:zoom-out-95",
|
||||
"fui:group/alert-dialog-content fui:fixed fui:top-1/2 fui:left-1/2 fui:z-50 fui:grid fui:w-full fui:-translate-x-1/2 fui:-translate-y-1/2 fui:gap-6 fui:rounded-4xl fui:bg-popover fui:p-6 fui:text-popover-foreground fui:ring-1 fui:ring-foreground/5 fui:duration-100 fui:outline-none fui:data-[size=default]:max-w-xs fui:data-[size=sm]:max-w-xs fui:data-[size=default]:sm:max-w-md fui:data-open:animate-in fui:data-open:fade-in-0 fui:data-open:zoom-in-95 fui:data-closed:animate-out fui:data-closed:fade-out-0 fui:data-closed:zoom-out-95",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -67,7 +67,7 @@ function AlertDialogHeader({
|
||||
<div
|
||||
data-slot="alert-dialog-header"
|
||||
className={cn(
|
||||
"lib:grid lib:grid-rows-[auto_1fr] lib:place-items-center lib:gap-1.5 lib:text-center lib:has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] lib:has-data-[slot=alert-dialog-media]:gap-x-6 lib:sm:group-data-[size=default]/alert-dialog-content:place-items-start lib:sm:group-data-[size=default]/alert-dialog-content:text-left lib:sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]",
|
||||
"fui:grid fui:grid-rows-[auto_1fr] fui:place-items-center fui:gap-1.5 fui:text-center fui:has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] fui:has-data-[slot=alert-dialog-media]:gap-x-6 fui:sm:group-data-[size=default]/alert-dialog-content:place-items-start fui:sm:group-data-[size=default]/alert-dialog-content:text-left fui:sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -83,7 +83,7 @@ function AlertDialogFooter({
|
||||
<div
|
||||
data-slot="alert-dialog-footer"
|
||||
className={cn(
|
||||
"lib:flex lib:flex-col-reverse lib:gap-2 lib:group-data-[size=sm]/alert-dialog-content:grid lib:group-data-[size=sm]/alert-dialog-content:grid-cols-2 lib:sm:flex-row lib:sm:justify-end",
|
||||
"fui:flex fui:flex-col-reverse fui:gap-2 fui:group-data-[size=sm]/alert-dialog-content:grid fui:group-data-[size=sm]/alert-dialog-content:grid-cols-2 fui:sm:flex-row fui:sm:justify-end",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -99,7 +99,7 @@ function AlertDialogMedia({
|
||||
<div
|
||||
data-slot="alert-dialog-media"
|
||||
className={cn(
|
||||
"lib:mb-2 lib:inline-flex lib:size-16 lib:items-center lib:justify-center lib:rounded-full lib:bg-muted lib:sm:group-data-[size=default]/alert-dialog-content:row-span-2 lib:*:[svg:not([class*=size-])]:size-8",
|
||||
"fui:mb-2 fui:inline-flex fui:size-16 fui:items-center fui:justify-center fui:rounded-full fui:bg-muted fui:sm:group-data-[size=default]/alert-dialog-content:row-span-2 fui:*:[svg:not([class*=size-])]:size-8",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -115,7 +115,7 @@ function AlertDialogTitle({
|
||||
<AlertDialogPrimitive.Title
|
||||
data-slot="alert-dialog-title"
|
||||
className={cn(
|
||||
"lib:font-heading lib:text-lg lib:font-medium lib:sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2",
|
||||
"fui:font-heading fui:text-lg fui:font-medium fui:sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -131,7 +131,7 @@ function AlertDialogDescription({
|
||||
<AlertDialogPrimitive.Description
|
||||
data-slot="alert-dialog-description"
|
||||
className={cn(
|
||||
"lib:text-sm lib:text-balance lib:text-muted-foreground lib:md:text-pretty lib:*:[a]:underline lib:*:[a]:underline-offset-3 lib:*:[a]:hover:text-foreground",
|
||||
"fui:text-sm fui:text-balance fui:text-muted-foreground fui:md:text-pretty fui:*:[a]:underline fui:*:[a]:underline-offset-3 fui:*:[a]:hover:text-foreground",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -3,13 +3,13 @@ import { cn } from "cn";
|
||||
import type * as React from "react";
|
||||
|
||||
const alertVariants = cva(
|
||||
"lib:group/alert lib:relative lib:grid lib:w-full lib:gap-0.5 lib:rounded-lg lib:border lib:px-4 lib:py-3 lib:text-left lib:text-sm lib:has-data-[slot=alert-action]:relative lib:has-data-[slot=alert-action]:pr-18 lib:has-[>svg]:grid-cols-[auto_1fr] lib:has-[>svg]:gap-x-2.5 lib:*:[svg]:row-span-2 lib:*:[svg]:translate-y-0.5 lib:*:[svg]:text-current lib:*:[svg:not([class*=size-])]:size-4",
|
||||
"fui:group/alert fui:relative fui:grid fui:w-full fui:gap-0.5 fui:rounded-lg fui:border fui:px-4 fui:py-3 fui:text-left fui:text-sm fui:has-data-[slot=alert-action]:relative fui:has-data-[slot=alert-action]:pr-18 fui:has-[>svg]:grid-cols-[auto_1fr] fui:has-[>svg]:gap-x-2.5 fui:*:[svg]:row-span-2 fui:*:[svg]:translate-y-0.5 fui:*:[svg]:text-current fui:*:[svg:not([class*=size-])]:size-4",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "lib:bg-card lib:text-card-foreground",
|
||||
default: "fui:bg-card fui:text-card-foreground",
|
||||
destructive:
|
||||
"lib:bg-card lib:text-destructive lib:*:data-[slot=alert-description]:text-destructive/90 lib:*:[svg]:text-current",
|
||||
"fui:bg-card fui:text-destructive fui:*:data-[slot=alert-description]:text-destructive/90 fui:*:[svg]:text-current",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
@@ -38,7 +38,7 @@ function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
|
||||
<div
|
||||
data-slot="alert-title"
|
||||
className={cn(
|
||||
"lib:font-medium lib:group-has-[>svg]/alert:col-start-2 lib:[&_a]:underline lib:[&_a]:underline-offset-3 lib:[&_a]:hover:text-foreground",
|
||||
"fui:font-medium fui:group-has-[>svg]/alert:col-start-2 fui:[&_a]:underline fui:[&_a]:underline-offset-3 fui:[&_a]:hover:text-foreground",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -54,7 +54,7 @@ function AlertDescription({
|
||||
<div
|
||||
data-slot="alert-description"
|
||||
className={cn(
|
||||
"lib:text-sm lib:text-balance lib:text-muted-foreground lib:md:text-pretty lib:[&_a]:underline lib:[&_a]:underline-offset-3 lib:[&_a]:hover:text-foreground lib:[&_p:not(:last-child)]:mb-4",
|
||||
"fui:text-sm fui:text-balance fui:text-muted-foreground fui:md:text-pretty fui:[&_a]:underline fui:[&_a]:underline-offset-3 fui:[&_a]:hover:text-foreground fui:[&_p:not(:last-child)]:mb-4",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -66,7 +66,7 @@ function AlertAction({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="alert-action"
|
||||
className={cn("lib:absolute lib:top-2.5 lib:right-3", className)}
|
||||
className={cn("fui:absolute fui:top-2.5 fui:right-3", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -14,7 +14,7 @@ function AspectRatio({
|
||||
"--ratio": ratio,
|
||||
} as React.CSSProperties
|
||||
}
|
||||
className={cn("lib:relative lib:aspect-(--ratio)", className)}
|
||||
className={cn("fui:relative fui:aspect-(--ratio)", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -14,7 +14,7 @@ function Avatar({
|
||||
data-slot="avatar"
|
||||
data-size={size}
|
||||
className={cn(
|
||||
"lib:group/avatar lib:relative lib:flex lib:size-8 lib:shrink-0 lib:rounded-full lib:select-none lib:after:absolute lib:after:inset-0 lib:after:rounded-full lib:after:border lib:after:border-border lib:after:mix-blend-darken lib:data-[size=lg]:size-10 lib:data-[size=sm]:size-6 lib:dark:after:mix-blend-lighten",
|
||||
"fui:group/avatar fui:relative fui:flex fui:size-8 fui:shrink-0 fui:rounded-full fui:select-none fui:after:absolute fui:after:inset-0 fui:after:rounded-full fui:after:border fui:after:border-border fui:after:mix-blend-darken fui:data-[size=lg]:size-10 fui:data-[size=sm]:size-6 fui:dark:after:mix-blend-lighten",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -27,7 +27,7 @@ function AvatarImage({ className, ...props }: AvatarPrimitive.Image.Props) {
|
||||
<AvatarPrimitive.Image
|
||||
data-slot="avatar-image"
|
||||
className={cn(
|
||||
"lib:aspect-square lib:size-full lib:rounded-full lib:object-cover",
|
||||
"fui:aspect-square fui:size-full fui:rounded-full fui:object-cover",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -43,7 +43,7 @@ function AvatarFallback({
|
||||
<AvatarPrimitive.Fallback
|
||||
data-slot="avatar-fallback"
|
||||
className={cn(
|
||||
"lib:flex lib:size-full lib:items-center lib:justify-center lib:rounded-full lib:bg-muted lib:text-sm lib:text-muted-foreground lib:group-data-[size=sm]/avatar:text-xs",
|
||||
"fui:flex fui:size-full fui:items-center fui:justify-center fui:rounded-full fui:bg-muted fui:text-sm fui:text-muted-foreground fui:group-data-[size=sm]/avatar:text-xs",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -56,10 +56,10 @@ function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) {
|
||||
<span
|
||||
data-slot="avatar-badge"
|
||||
className={cn(
|
||||
"lib:absolute lib:right-0 lib:bottom-0 lib:z-10 lib:inline-flex lib:items-center lib:justify-center lib:rounded-full lib:bg-primary lib:text-primary-foreground lib:bg-blend-color lib:ring-2 lib:ring-background lib:select-none",
|
||||
"lib:group-data-[size=sm]/avatar:size-2 lib:group-data-[size=sm]/avatar:[&>svg]:hidden",
|
||||
"lib:group-data-[size=default]/avatar:size-2.5 lib:group-data-[size=default]/avatar:[&>svg]:size-2",
|
||||
"lib:group-data-[size=lg]/avatar:size-3 lib:group-data-[size=lg]/avatar:[&>svg]:size-2",
|
||||
"fui:absolute fui:right-0 fui:bottom-0 fui:z-10 fui:inline-flex fui:items-center fui:justify-center fui:rounded-full fui:bg-primary fui:text-primary-foreground fui:bg-blend-color fui:ring-2 fui:ring-background fui:select-none",
|
||||
"fui:group-data-[size=sm]/avatar:size-2 fui:group-data-[size=sm]/avatar:[&>svg]:hidden",
|
||||
"fui:group-data-[size=default]/avatar:size-2.5 fui:group-data-[size=default]/avatar:[&>svg]:size-2",
|
||||
"fui:group-data-[size=lg]/avatar:size-3 fui:group-data-[size=lg]/avatar:[&>svg]:size-2",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -72,7 +72,7 @@ function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) {
|
||||
<div
|
||||
data-slot="avatar-group"
|
||||
className={cn(
|
||||
"lib:group/avatar-group lib:flex lib:-space-x-2 lib:*:data-[slot=avatar]:ring-2 lib:*:data-[slot=avatar]:ring-background",
|
||||
"fui:group/avatar-group fui:flex fui:-space-x-2 fui:*:data-[slot=avatar]:ring-2 fui:*:data-[slot=avatar]:ring-background",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -88,7 +88,7 @@ function AvatarGroupCount({
|
||||
<div
|
||||
data-slot="avatar-group-count"
|
||||
className={cn(
|
||||
"lib:relative lib:flex lib:size-8 lib:shrink-0 lib:items-center lib:justify-center lib:rounded-full lib:bg-muted lib:text-sm lib:text-muted-foreground lib:ring-2 lib:ring-background lib:group-has-data-[size=lg]/avatar-group:size-10 lib:group-has-data-[size=sm]/avatar-group:size-6 lib:[&>svg]:size-4 lib:group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 lib:group-has-data-[size=sm]/avatar-group:[&>svg]:size-3",
|
||||
"fui:relative fui:flex fui:size-8 fui:shrink-0 fui:items-center fui:justify-center fui:rounded-full fui:bg-muted fui:text-sm fui:text-muted-foreground fui:ring-2 fui:ring-background fui:group-has-data-[size=lg]/avatar-group:size-10 fui:group-has-data-[size=sm]/avatar-group:size-6 fui:[&>svg]:size-4 fui:group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 fui:group-has-data-[size=sm]/avatar-group:[&>svg]:size-3",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -4,21 +4,21 @@ import { cva, type VariantProps } from "class-variance-authority";
|
||||
import { cn } from "cn";
|
||||
|
||||
const badgeVariants = cva(
|
||||
"lib:group/badge lib:inline-flex lib:h-5 lib:w-fit lib:shrink-0 lib:items-center lib:justify-center lib:gap-1 lib:overflow-hidden lib:rounded-4xl lib:border lib:border-transparent lib:px-2 lib:py-0.5 lib:text-xs lib:font-medium lib:whitespace-nowrap lib:transition-all lib:focus-visible:border-ring lib:focus-visible:ring-[3px] lib:focus-visible:ring-ring/50 lib:has-data-[icon=inline-end]:pr-1.5 lib:has-data-[icon=inline-start]:pl-1.5 lib:aria-invalid:border-destructive lib:aria-invalid:ring-destructive/20 lib:dark:aria-invalid:ring-destructive/40 lib:[&>svg]:pointer-events-none lib:[&>svg]:size-3!",
|
||||
"fui:group/badge fui:inline-flex fui:h-5 fui:w-fit fui:shrink-0 fui:items-center fui:justify-center fui:gap-1 fui:overflow-hidden fui:rounded-4xl fui:border fui:border-transparent fui:px-2 fui:py-0.5 fui:text-xs fui:font-medium fui:whitespace-nowrap fui:transition-all fui:focus-visible:border-ring fui:focus-visible:ring-[3px] fui:focus-visible:ring-ring/50 fui:has-data-[icon=inline-end]:pr-1.5 fui:has-data-[icon=inline-start]:pl-1.5 fui:aria-invalid:border-destructive fui:aria-invalid:ring-destructive/20 fui:dark:aria-invalid:ring-destructive/40 fui:[&>svg]:pointer-events-none fui:[&>svg]:size-3!",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"lib:bg-primary lib:text-primary-foreground lib:[a]:hover:bg-primary/80",
|
||||
"fui:bg-primary fui:text-primary-foreground fui:[a]:hover:bg-primary/80",
|
||||
secondary:
|
||||
"lib:bg-secondary lib:text-secondary-foreground lib:[a]:hover:bg-secondary/80",
|
||||
"fui:bg-secondary fui:text-secondary-foreground fui:[a]:hover:bg-secondary/80",
|
||||
destructive:
|
||||
"lib:bg-destructive/10 lib:text-destructive lib:focus-visible:ring-destructive/20 lib:dark:bg-destructive/20 lib:dark:focus-visible:ring-destructive/40 lib:[a]:hover:bg-destructive/20",
|
||||
"fui:bg-destructive/10 fui:text-destructive fui:focus-visible:ring-destructive/20 fui:dark:bg-destructive/20 fui:dark:focus-visible:ring-destructive/40 fui:[a]:hover:bg-destructive/20",
|
||||
outline:
|
||||
"lib:border-border lib:bg-input/30 lib:text-foreground lib:[a]:hover:bg-muted lib:[a]:hover:text-muted-foreground",
|
||||
"fui:border-border fui:bg-input/30 fui:text-foreground fui:[a]:hover:bg-muted fui:[a]:hover:text-muted-foreground",
|
||||
ghost:
|
||||
"lib:hover:bg-muted lib:hover:text-muted-foreground lib:dark:hover:bg-muted/50",
|
||||
link: "lib:text-primary lib:underline-offset-4 lib:hover:underline",
|
||||
"fui:hover:bg-muted fui:hover:text-muted-foreground fui:dark:hover:bg-muted/50",
|
||||
link: "fui:text-link fui:underline-offset-4 fui:hover:underline",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
|
||||
@@ -20,7 +20,7 @@ function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
|
||||
<ol
|
||||
data-slot="breadcrumb-list"
|
||||
className={cn(
|
||||
"lib:flex lib:flex-wrap lib:items-center lib:gap-1.5 lib:text-sm lib:wrap-break-word lib:text-muted-foreground lib:sm:gap-2.5",
|
||||
"fui:flex fui:flex-wrap fui:items-center fui:gap-1.5 fui:text-sm fui:wrap-break-word fui:text-muted-foreground fui:sm:gap-2.5",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -32,7 +32,7 @@ function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
|
||||
return (
|
||||
<li
|
||||
data-slot="breadcrumb-item"
|
||||
className={cn("lib:inline-flex lib:items-center lib:gap-1.5", className)}
|
||||
className={cn("fui:inline-flex fui:items-center fui:gap-1.5", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -48,7 +48,7 @@ function BreadcrumbLink({
|
||||
props: mergeProps<"a">(
|
||||
{
|
||||
className: cn(
|
||||
"lib:transition-colors lib:hover:text-foreground",
|
||||
"fui:transition-colors fui:hover:text-foreground",
|
||||
className,
|
||||
),
|
||||
},
|
||||
@@ -68,7 +68,7 @@ function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
|
||||
role="link"
|
||||
aria-disabled="true"
|
||||
aria-current="page"
|
||||
className={cn("lib:font-normal lib:text-foreground", className)}
|
||||
className={cn("fui:font-normal fui:text-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -84,7 +84,7 @@ function BreadcrumbSeparator({
|
||||
data-slot="breadcrumb-separator"
|
||||
role="presentation"
|
||||
aria-hidden="true"
|
||||
className={cn("lib:[&>svg]:size-3.5", className)}
|
||||
className={cn("fui:[&>svg]:size-3.5", className)}
|
||||
{...props}
|
||||
>
|
||||
{children ?? <ChevronRightIcon />}
|
||||
@@ -102,13 +102,13 @@ function BreadcrumbEllipsis({
|
||||
role="presentation"
|
||||
aria-hidden="true"
|
||||
className={cn(
|
||||
"lib:flex lib:size-5 lib:items-center lib:justify-center lib:[&>svg]:size-4",
|
||||
"fui:flex fui:size-5 fui:items-center fui:justify-center fui:[&>svg]:size-4",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<MoreHorizontalIcon />
|
||||
<span className="lib:sr-only">More</span>
|
||||
<span className="fui:sr-only">More</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,14 +6,14 @@ import { cn } from "cn";
|
||||
import { Separator } from "@/components/separator";
|
||||
|
||||
const buttonGroupVariants = cva(
|
||||
"lib:flex lib:w-fit lib:items-stretch lib:*:focus-visible:relative lib:*:focus-visible:z-10 lib:has-[>[data-slot=button-group]]:gap-2 lib:has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-4xl lib:[&>[data-slot=select-trigger]:not([class*=w-])]:w-fit lib:[&>input]:flex-1",
|
||||
"fui:flex fui:w-fit fui:items-stretch fui:*:focus-visible:relative fui:*:focus-visible:z-10 fui:has-[>[data-slot=button-group]]:gap-2 fui:has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-4xl fui:[&>[data-slot=select-trigger]:not([class*=w-])]:w-fit fui:[&>input]:flex-1",
|
||||
{
|
||||
variants: {
|
||||
orientation: {
|
||||
horizontal:
|
||||
"lib:*:data-slot:rounded-r-none lib:[&>[data-slot]:not(:has(~[data-slot]))]:rounded-r-4xl! lib:[&>[data-slot]~[data-slot]]:rounded-l-none lib:[&>[data-slot]~[data-slot]]:border-l-0",
|
||||
"fui:*:data-slot:rounded-r-none fui:[&>[data-slot]:not(:has(~[data-slot]))]:rounded-r-4xl! fui:[&>[data-slot]~[data-slot]]:rounded-l-none fui:[&>[data-slot]~[data-slot]]:border-l-0",
|
||||
vertical:
|
||||
"lib:flex-col lib:*:data-slot:rounded-b-none lib:[&>[data-slot]:not(:has(~[data-slot]))]:rounded-b-4xl! lib:[&>[data-slot]~[data-slot]]:rounded-t-none lib:[&>[data-slot]~[data-slot]]:border-t-0",
|
||||
"fui:flex-col fui:*:data-slot:rounded-b-none fui:[&>[data-slot]:not(:has(~[data-slot]))]:rounded-b-4xl! fui:[&>[data-slot]~[data-slot]]:rounded-t-none fui:[&>[data-slot]~[data-slot]]:border-t-0",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
@@ -48,7 +48,7 @@ function ButtonGroupText({
|
||||
props: mergeProps<"div">(
|
||||
{
|
||||
className: cn(
|
||||
"flex items-center gap-2 rounded-4xl border bg-muted px-2.5 text-sm font-medium [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
|
||||
"fui:flex fui:items-center fui:gap-2 fui:rounded-4xl fui:border fui:bg-muted fui:px-2.5 fui:text-sm fui:font-medium fui:[&_svg]:pointer-events-none fui:[&_svg:not([class*='size-'])]:size-4",
|
||||
className,
|
||||
),
|
||||
},
|
||||
@@ -71,7 +71,7 @@ function ButtonGroupSeparator({
|
||||
data-slot="button-group-separator"
|
||||
orientation={orientation}
|
||||
className={cn(
|
||||
"lib:relative lib:self-stretch lib:bg-input lib:data-horizontal:mx-px lib:data-horizontal:w-auto lib:data-vertical:my-px lib:data-vertical:h-auto",
|
||||
"fui:relative fui:self-stretch fui:bg-input fui:data-horizontal:mx-px fui:data-horizontal:w-auto fui:data-vertical:my-px fui:data-vertical:h-auto",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
+15
-15
@@ -3,32 +3,32 @@ import { cva, type VariantProps } from "class-variance-authority";
|
||||
import { cn } from "cn";
|
||||
|
||||
const buttonVariants = cva(
|
||||
"lib:group/button lib:inline-flex lib:shrink-0 lib:items-center lib:justify-center lib:rounded-4xl lib:border lib:border-transparent lib:bg-clip-padding lib:text-sm lib:font-medium lib:whitespace-nowrap lib:transition-all lib:outline-none lib:select-none lib:focus-visible:border-ring lib:focus-visible:ring-[3px] lib:focus-visible:ring-ring/50 lib:active:not-aria-[haspopup]:translate-y-px lib:disabled:pointer-events-none lib:disabled:opacity-50 lib:aria-invalid:border-destructive lib:aria-invalid:ring-[3px] lib:aria-invalid:ring-destructive/20 lib:dark:aria-invalid:border-destructive/50 lib:dark:aria-invalid:ring-destructive/40 lib:[&_svg]:pointer-events-none lib:[&_svg]:shrink-0 lib:[&_svg:not([class*=size-])]:size-4",
|
||||
"fui:group/button fui:inline-flex fui:shrink-0 fui:items-center fui:justify-center fui:rounded-4xl fui:border fui:border-transparent fui:bg-clip-padding fui:text-sm fui:font-medium fui:whitespace-nowrap fui:transition-all fui:outline-none fui:select-none fui:focus-visible:border-ring fui:focus-visible:ring-[3px] fui:focus-visible:ring-ring/50 fui:active:not-aria-[haspopup]:translate-y-px fui:disabled:pointer-events-none fui:disabled:opacity-50 fui:aria-invalid:border-destructive fui:aria-invalid:ring-[3px] fui:aria-invalid:ring-destructive/20 fui:dark:aria-invalid:border-destructive/50 fui:dark:aria-invalid:ring-destructive/40 fui:[&_svg]:pointer-events-none fui:[&_svg]:shrink-0 fui:[&_svg:not([class*=size-])]:size-4",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"lib:bg-primary lib:text-primary-foreground lib:hover:bg-primary/80",
|
||||
"fui:bg-primary fui:text-primary-foreground fui:hover:bg-primary/80",
|
||||
outline:
|
||||
"lib:border-border lib:bg-input/30 lib:hover:bg-input/50 lib:hover:text-foreground lib:aria-expanded:bg-muted lib:aria-expanded:text-foreground",
|
||||
"fui:border-border fui:bg-input/30 fui:hover:bg-input/50 fui:hover:text-foreground fui:aria-expanded:bg-muted fui:aria-expanded:text-foreground",
|
||||
secondary:
|
||||
"lib:bg-secondary lib:text-secondary-foreground lib:hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] lib:aria-expanded:bg-secondary lib:aria-expanded:text-secondary-foreground",
|
||||
"fui:bg-secondary fui:text-secondary-foreground fui:hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] fui:aria-expanded:bg-secondary fui:aria-expanded:text-secondary-foreground",
|
||||
ghost:
|
||||
"lib:hover:bg-muted lib:hover:text-foreground lib:aria-expanded:bg-muted lib:aria-expanded:text-foreground lib:dark:hover:bg-muted/50",
|
||||
"fui:hover:bg-muted fui:hover:text-foreground fui:aria-expanded:bg-muted fui:aria-expanded:text-foreground fui:dark:hover:bg-muted/50",
|
||||
destructive:
|
||||
"lib:bg-destructive/10 lib:text-destructive lib:hover:bg-destructive/20 lib:focus-visible:border-destructive/40 lib:focus-visible:ring-destructive/20 lib:dark:bg-destructive/20 lib:dark:hover:bg-destructive/30 lib:dark:focus-visible:ring-destructive/40",
|
||||
link: "lib:text-primary lib:underline-offset-4 lib:hover:underline",
|
||||
"fui:bg-destructive/10 fui:text-destructive fui:hover:bg-destructive/20 fui:focus-visible:border-destructive/40 fui:focus-visible:ring-destructive/20 fui:dark:bg-destructive/20 fui:dark:hover:bg-destructive/30 fui:dark:focus-visible:ring-destructive/40",
|
||||
link: "fui:text-link fui:underline-offset-4 fui:hover:underline",
|
||||
},
|
||||
size: {
|
||||
default:
|
||||
"lib:h-9 lib:gap-1.5 lib:px-3 lib:has-data-[icon=inline-end]:pr-2.5 lib:has-data-[icon=inline-start]:pl-2.5",
|
||||
xs: "lib:h-6 lib:gap-1 lib:px-2.5 lib:text-xs lib:has-data-[icon=inline-end]:pr-2 lib:has-data-[icon=inline-start]:pl-2 lib:[&_svg:not([class*=size-])]:size-3",
|
||||
sm: "lib:h-8 lib:gap-1 lib:px-3 lib:has-data-[icon=inline-end]:pr-2 lib:has-data-[icon=inline-start]:pl-2",
|
||||
lg: "lib:h-10 lib:gap-1.5 lib:px-4 lib:has-data-[icon=inline-end]:pr-3 lib:has-data-[icon=inline-start]:pl-3",
|
||||
icon: "lib:size-9",
|
||||
"icon-xs": "lib:size-6 lib:[&_svg:not([class*=size-])]:size-3",
|
||||
"icon-sm": "lib:size-8",
|
||||
"icon-lg": "lib:size-10",
|
||||
"fui:h-9 fui:gap-1.5 fui:px-3 fui:has-data-[icon=inline-end]:pr-2.5 fui:has-data-[icon=inline-start]:pl-2.5",
|
||||
xs: "fui:h-6 fui:gap-1 fui:px-2.5 fui:text-xs fui:has-data-[icon=inline-end]:pr-2 fui:has-data-[icon=inline-start]:pl-2 fui:[&_svg:not([class*=size-])]:size-3",
|
||||
sm: "fui:h-8 fui:gap-1 fui:px-3 fui:has-data-[icon=inline-end]:pr-2 fui:has-data-[icon=inline-start]:pl-2",
|
||||
lg: "fui:h-10 fui:gap-1.5 fui:px-4 fui:has-data-[icon=inline-end]:pr-3 fui:has-data-[icon=inline-start]:pl-3",
|
||||
icon: "fui:size-9",
|
||||
"icon-xs": "fui:size-6 fui:[&_svg:not([class*=size-])]:size-3",
|
||||
"icon-sm": "fui:size-8",
|
||||
"icon-lg": "fui:size-10",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
|
||||
+35
-35
@@ -32,7 +32,7 @@ function Calendar({
|
||||
<DayPicker
|
||||
showOutsideDays={showOutsideDays}
|
||||
className={cn(
|
||||
"lib:group/calendar lib:bg-background lib:p-3 lib:[--cell-radius:var(--radius-4xl)] lib:[--cell-size:--spacing(8)] lib:in-data-[slot=card-content]:bg-transparent lib:in-data-[slot=popover-content]:bg-transparent",
|
||||
"fui:group/calendar fui:bg-background fui:p-3 fui:[--cell-radius:var(--radius-4xl)] fui:[--cell-size:--spacing(8)] fui:in-data-[slot=card-content]:bg-transparent fui:in-data-[slot=popover-content]:bg-transparent",
|
||||
String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
|
||||
String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
|
||||
className,
|
||||
@@ -45,99 +45,99 @@ function Calendar({
|
||||
...formatters,
|
||||
}}
|
||||
classNames={{
|
||||
root: cn("lib:w-fit", defaultClassNames.root),
|
||||
root: cn("fui:w-fit", defaultClassNames.root),
|
||||
months: cn(
|
||||
"lib:relative lib:flex lib:flex-col lib:gap-4 lib:md:flex-row",
|
||||
"fui:relative fui:flex fui:flex-col fui:gap-4 fui:md:flex-row",
|
||||
defaultClassNames.months,
|
||||
),
|
||||
month: cn(
|
||||
"lib:flex lib:w-full lib:flex-col lib:gap-4",
|
||||
"fui:flex fui:w-full fui:flex-col fui:gap-4",
|
||||
defaultClassNames.month,
|
||||
),
|
||||
nav: cn(
|
||||
"lib:absolute lib:inset-x-0 lib:top-0 lib:flex lib:w-full lib:items-center lib:justify-between lib:gap-1",
|
||||
"fui:absolute fui:inset-x-0 fui:top-0 fui:flex fui:w-full fui:items-center fui:justify-between fui:gap-1",
|
||||
defaultClassNames.nav,
|
||||
),
|
||||
button_previous: cn(
|
||||
buttonVariants({ variant: buttonVariant }),
|
||||
"lib:size-(--cell-size) lib:p-0 lib:select-none lib:aria-disabled:opacity-50",
|
||||
"fui:size-(--cell-size) fui:p-0 fui:select-none fui:aria-disabled:opacity-50",
|
||||
defaultClassNames.button_previous,
|
||||
),
|
||||
button_next: cn(
|
||||
buttonVariants({ variant: buttonVariant }),
|
||||
"lib:size-(--cell-size) lib:p-0 lib:select-none lib:aria-disabled:opacity-50",
|
||||
"fui:size-(--cell-size) fui:p-0 fui:select-none fui:aria-disabled:opacity-50",
|
||||
defaultClassNames.button_next,
|
||||
),
|
||||
month_caption: cn(
|
||||
"lib:flex lib:h-(--cell-size) lib:w-full lib:items-center lib:justify-center lib:px-(--cell-size)",
|
||||
"fui:flex fui:h-(--cell-size) fui:w-full fui:items-center fui:justify-center fui:px-(--cell-size)",
|
||||
defaultClassNames.month_caption,
|
||||
),
|
||||
dropdowns: cn(
|
||||
"lib:flex lib:h-(--cell-size) lib:w-full lib:items-center lib:justify-center lib:gap-1.5 lib:text-sm lib:font-medium",
|
||||
"fui:flex fui:h-(--cell-size) fui:w-full fui:items-center fui:justify-center fui:gap-1.5 fui:text-sm fui:font-medium",
|
||||
defaultClassNames.dropdowns,
|
||||
),
|
||||
dropdown_root: cn(
|
||||
"lib:relative lib:rounded-(--cell-radius)",
|
||||
"fui:relative fui:rounded-(--cell-radius)",
|
||||
defaultClassNames.dropdown_root,
|
||||
),
|
||||
dropdown: cn(
|
||||
"lib:absolute lib:inset-0 lib:bg-popover lib:opacity-0",
|
||||
"fui:absolute fui:inset-0 fui:bg-popover fui:opacity-0",
|
||||
defaultClassNames.dropdown,
|
||||
),
|
||||
caption_label: cn(
|
||||
"lib:font-medium lib:select-none",
|
||||
"fui:font-medium fui:select-none",
|
||||
captionLayout === "label"
|
||||
? "lib:text-sm"
|
||||
: "lib:flex lib:items-center lib:gap-1 lib:rounded-(--cell-radius) lib:text-sm lib:[&>svg]:size-3.5 lib:[&>svg]:text-muted-foreground",
|
||||
? "fui:text-sm"
|
||||
: "fui:flex fui:items-center fui:gap-1 fui:rounded-(--cell-radius) fui:text-sm fui:[&>svg]:size-3.5 fui:[&>svg]:text-muted-foreground",
|
||||
defaultClassNames.caption_label,
|
||||
),
|
||||
month_grid: cn(
|
||||
"lib:w-full lib:border-collapse",
|
||||
"fui:w-full fui:border-collapse",
|
||||
defaultClassNames.month_grid,
|
||||
),
|
||||
weekdays: cn("lib:flex", defaultClassNames.weekdays),
|
||||
weekdays: cn("fui:flex", defaultClassNames.weekdays),
|
||||
weekday: cn(
|
||||
"lib:flex-1 lib:rounded-(--cell-radius) lib:text-[0.8rem] lib:font-normal lib:text-muted-foreground lib:select-none",
|
||||
"fui:flex-1 fui:rounded-(--cell-radius) fui:text-[0.8rem] fui:font-normal fui:text-muted-foreground fui:select-none",
|
||||
defaultClassNames.weekday,
|
||||
),
|
||||
week: cn("lib:mt-2 lib:flex lib:w-full", defaultClassNames.week),
|
||||
week: cn("fui:mt-2 fui:flex fui:w-full", defaultClassNames.week),
|
||||
week_number_header: cn(
|
||||
"lib:w-(--cell-size) lib:select-none",
|
||||
"fui:w-(--cell-size) fui:select-none",
|
||||
defaultClassNames.week_number_header,
|
||||
),
|
||||
week_number: cn(
|
||||
"lib:text-[0.8rem] lib:text-muted-foreground lib:select-none",
|
||||
"fui:text-[0.8rem] fui:text-muted-foreground fui:select-none",
|
||||
defaultClassNames.week_number,
|
||||
),
|
||||
day: cn(
|
||||
"lib:group/day lib:relative lib:aspect-square lib:h-full lib:w-full lib:rounded-(--cell-radius) lib:p-0 lib:text-center lib:select-none lib:[&:last-child[data-selected=true]_button]:rounded-r-(--cell-radius)",
|
||||
"fui:group/day fui:relative fui:aspect-square fui:h-full fui:w-full fui:rounded-(--cell-radius) fui:p-0 fui:text-center fui:select-none fui:[&:last-child[data-selected=true]_button]:rounded-r-(--cell-radius)",
|
||||
props.showWeekNumber
|
||||
? "lib:[&:nth-child(2)[data-selected=true]_button]:rounded-l-(--cell-radius)"
|
||||
: "lib:[&:first-child[data-selected=true]_button]:rounded-l-(--cell-radius)",
|
||||
? "fui:[&:nth-child(2)[data-selected=true]_button]:rounded-l-(--cell-radius)"
|
||||
: "fui:[&:first-child[data-selected=true]_button]:rounded-l-(--cell-radius)",
|
||||
defaultClassNames.day,
|
||||
),
|
||||
range_start: cn(
|
||||
"lib:relative lib:isolate lib:z-0 lib:rounded-l-(--cell-radius) lib:bg-muted lib:after:absolute lib:after:inset-y-0 lib:after:right-0 lib:after:w-4 lib:after:bg-muted",
|
||||
"fui:relative fui:isolate fui:z-0 fui:rounded-l-(--cell-radius) fui:bg-muted fui:after:absolute fui:after:inset-y-0 fui:after:right-0 fui:after:w-4 fui:after:bg-muted",
|
||||
defaultClassNames.range_start,
|
||||
),
|
||||
range_middle: cn("lib:rounded-none", defaultClassNames.range_middle),
|
||||
range_middle: cn("fui:rounded-none", defaultClassNames.range_middle),
|
||||
range_end: cn(
|
||||
"lib:relative lib:isolate lib:z-0 lib:rounded-r-(--cell-radius) lib:bg-muted lib:after:absolute lib:after:inset-y-0 lib:after:left-0 lib:after:w-4 lib:after:bg-muted",
|
||||
"fui:relative fui:isolate fui:z-0 fui:rounded-r-(--cell-radius) fui:bg-muted fui:after:absolute fui:after:inset-y-0 fui:after:left-0 fui:after:w-4 fui:after:bg-muted",
|
||||
defaultClassNames.range_end,
|
||||
),
|
||||
today: cn(
|
||||
"lib:rounded-(--cell-radius) lib:bg-muted lib:text-foreground lib:data-[selected=true]:rounded-none",
|
||||
"fui:rounded-(--cell-radius) fui:bg-muted fui:text-foreground fui:data-[selected=true]:rounded-none",
|
||||
defaultClassNames.today,
|
||||
),
|
||||
outside: cn(
|
||||
"lib:text-muted-foreground lib:aria-selected:text-muted-foreground",
|
||||
"fui:text-muted-foreground fui:aria-selected:text-muted-foreground",
|
||||
defaultClassNames.outside,
|
||||
),
|
||||
disabled: cn(
|
||||
"lib:text-muted-foreground lib:opacity-50",
|
||||
"fui:text-muted-foreground fui:opacity-50",
|
||||
defaultClassNames.disabled,
|
||||
),
|
||||
hidden: cn("lib:invisible", defaultClassNames.hidden),
|
||||
hidden: cn("fui:invisible", defaultClassNames.hidden),
|
||||
...classNames,
|
||||
}}
|
||||
components={{
|
||||
@@ -155,7 +155,7 @@ function Calendar({
|
||||
if (orientation === "left") {
|
||||
return (
|
||||
<ChevronLeftIcon
|
||||
className={cn("lib:size-4", className)}
|
||||
className={cn("fui:size-4", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -164,7 +164,7 @@ function Calendar({
|
||||
if (orientation === "right") {
|
||||
return (
|
||||
<ChevronRightIcon
|
||||
className={cn("lib:size-4", className)}
|
||||
className={cn("fui:size-4", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -172,7 +172,7 @@ function Calendar({
|
||||
|
||||
return (
|
||||
<ChevronDownIcon
|
||||
className={cn("lib:size-4", className)}
|
||||
className={cn("fui:size-4", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -183,7 +183,7 @@ function Calendar({
|
||||
WeekNumber: ({ children, ...props }) => {
|
||||
return (
|
||||
<td {...props}>
|
||||
<div className="lib:flex lib:size-(--cell-size) lib:items-center lib:justify-center lib:text-center">
|
||||
<div className="fui:flex fui:size-(--cell-size) fui:items-center fui:justify-center fui:text-center">
|
||||
{children}
|
||||
</div>
|
||||
</td>
|
||||
@@ -226,7 +226,7 @@ function CalendarDayButton({
|
||||
data-range-end={modifiers.range_end}
|
||||
data-range-middle={modifiers.range_middle}
|
||||
className={cn(
|
||||
"lib:relative lib:isolate lib:z-10 lib:flex lib:aspect-square lib:size-auto lib:w-full lib:min-w-(--cell-size) lib:flex-col lib:gap-1 lib:border-0 lib:leading-none lib:font-normal lib:group-data-[focused=true]/day:relative lib:group-data-[focused=true]/day:z-10 lib:group-data-[focused=true]/day:border-ring lib:group-data-[focused=true]/day:ring-[3px] lib:group-data-[focused=true]/day:ring-ring/50 lib:data-[range-end=true]:rounded-(--cell-radius) lib:data-[range-end=true]:rounded-r-(--cell-radius) lib:data-[range-end=true]:bg-primary lib:data-[range-end=true]:text-primary-foreground lib:data-[range-middle=true]:rounded-none lib:data-[range-middle=true]:bg-muted lib:data-[range-middle=true]:text-foreground lib:data-[range-start=true]:rounded-(--cell-radius) lib:data-[range-start=true]:rounded-l-(--cell-radius) lib:data-[range-start=true]:bg-primary lib:data-[range-start=true]:text-primary-foreground lib:data-[selected-single=true]:bg-primary lib:data-[selected-single=true]:text-primary-foreground lib:dark:hover:text-foreground lib:[&>span]:text-xs lib:[&>span]:opacity-70",
|
||||
"fui:relative fui:isolate fui:z-10 fui:flex fui:aspect-square fui:size-auto fui:w-full fui:min-w-(--cell-size) fui:flex-col fui:gap-1 fui:border-0 fui:leading-none fui:font-normal fui:group-data-[focused=true]/day:relative fui:group-data-[focused=true]/day:z-10 fui:group-data-[focused=true]/day:border-ring fui:group-data-[focused=true]/day:ring-[3px] fui:group-data-[focused=true]/day:ring-ring/50 fui:data-[range-end=true]:rounded-(--cell-radius) fui:data-[range-end=true]:rounded-r-(--cell-radius) fui:data-[range-end=true]:bg-primary fui:data-[range-end=true]:text-primary-foreground fui:data-[range-middle=true]:rounded-none fui:data-[range-middle=true]:bg-muted fui:data-[range-middle=true]:text-foreground fui:data-[range-start=true]:rounded-(--cell-radius) fui:data-[range-start=true]:rounded-l-(--cell-radius) fui:data-[range-start=true]:bg-primary fui:data-[range-start=true]:text-primary-foreground fui:data-[selected-single=true]:bg-primary fui:data-[selected-single=true]:text-primary-foreground fui:dark:hover:text-foreground fui:[&>span]:text-xs fui:[&>span]:opacity-70",
|
||||
defaultClassNames.day,
|
||||
className,
|
||||
)}
|
||||
|
||||
@@ -11,7 +11,7 @@ function Card({
|
||||
data-slot="card"
|
||||
data-size={size}
|
||||
className={cn(
|
||||
"lib:group/card lib:flex lib:flex-col lib:gap-(--card-spacing) lib:overflow-hidden lib:rounded-2xl lib:bg-card lib:py-(--card-spacing) lib:text-sm lib:text-card-foreground lib:ring-1 lib:ring-foreground/10 lib:[--card-spacing:--spacing(6)] lib:has-[>img:first-child]:pt-0 lib:data-[size=sm]:[--card-spacing:--spacing(4)] lib:*:[img:first-child]:rounded-t-xl lib:*:[img:last-child]:rounded-b-xl",
|
||||
"fui:group/card fui:flex fui:flex-col fui:gap-(--card-spacing) fui:overflow-hidden fui:rounded-2xl fui:bg-card fui:py-(--card-spacing) fui:text-sm fui:text-card-foreground fui:ring-1 fui:ring-foreground/10 fui:[--card-spacing:--spacing(6)] fui:has-[>img:first-child]:pt-0 fui:data-[size=sm]:[--card-spacing:--spacing(4)] fui:*:[img:first-child]:rounded-t-xl fui:*:[img:last-child]:rounded-b-xl",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -24,7 +24,7 @@ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||
<div
|
||||
data-slot="card-header"
|
||||
className={cn(
|
||||
"lib:group/card-header lib:@container/card-header lib:grid lib:auto-rows-min lib:items-start lib:gap-2 lib:rounded-t-xl lib:px-(--card-spacing) lib:has-data-[slot=card-action]:grid-cols-[1fr_auto] lib:has-data-[slot=card-description]:grid-rows-[auto_auto] lib:[.border-b]:pb-(--card-spacing)",
|
||||
"fui:group/card-header fui:@container/card-header fui:grid fui:auto-rows-min fui:items-start fui:gap-2 fui:rounded-t-xl fui:px-(--card-spacing) fui:has-data-[slot=card-action]:grid-cols-[1fr_auto] fui:has-data-[slot=card-description]:grid-rows-[auto_auto] fui:[.border-b]:pb-(--card-spacing)",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -37,7 +37,7 @@ function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
|
||||
<div
|
||||
data-slot="card-title"
|
||||
className={cn(
|
||||
"lib:font-heading lib:text-base lib:font-medium",
|
||||
"fui:font-heading fui:text-base fui:font-medium",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -49,7 +49,7 @@ function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-description"
|
||||
className={cn("lib:text-sm lib:text-muted-foreground", className)}
|
||||
className={cn("fui:text-sm fui:text-muted-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -60,7 +60,7 @@ function CardAction({ className, ...props }: React.ComponentProps<"div">) {
|
||||
<div
|
||||
data-slot="card-action"
|
||||
className={cn(
|
||||
"lib:col-start-2 lib:row-span-2 lib:row-start-1 lib:self-start lib:justify-self-end",
|
||||
"fui:col-start-2 fui:row-span-2 fui:row-start-1 fui:self-start fui:justify-self-end",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -72,7 +72,7 @@ function CardContent({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-content"
|
||||
className={cn("lib:px-(--card-spacing)", className)}
|
||||
className={cn("fui:px-(--card-spacing)", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -83,7 +83,7 @@ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||
<div
|
||||
data-slot="card-footer"
|
||||
className={cn(
|
||||
"lib:flex lib:items-center lib:rounded-b-xl lib:px-(--card-spacing) lib:[.border-t]:pt-(--card-spacing)",
|
||||
"fui:flex fui:items-center fui:rounded-b-xl fui:px-(--card-spacing) fui:[.border-t]:pt-(--card-spacing)",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
+14
-14
@@ -117,7 +117,7 @@ function Carousel({
|
||||
>
|
||||
<div
|
||||
onKeyDownCapture={handleKeyDown}
|
||||
className={cn("lib:relative", className)}
|
||||
className={cn("fui:relative", className)}
|
||||
role="region"
|
||||
aria-roledescription="carousel"
|
||||
data-slot="carousel"
|
||||
@@ -135,13 +135,13 @@ function CarouselContent({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
ref={carouselRef}
|
||||
className="lib:overflow-hidden"
|
||||
className="fui:overflow-hidden"
|
||||
data-slot="carousel-content"
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"lib:flex",
|
||||
orientation === "horizontal" ? "lib:-ml-4" : "lib:-mt-4 lib:flex-col",
|
||||
"fui:flex",
|
||||
orientation === "horizontal" ? "fui:-ml-4" : "fui:-mt-4 fui:flex-col",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -159,8 +159,8 @@ function CarouselItem({ className, ...props }: React.ComponentProps<"div">) {
|
||||
aria-roledescription="slide"
|
||||
data-slot="carousel-item"
|
||||
className={cn(
|
||||
"lib:min-w-0 lib:shrink-0 lib:grow-0 lib:basis-full",
|
||||
orientation === "horizontal" ? "lib:pl-4" : "lib:pt-4",
|
||||
"fui:min-w-0 fui:shrink-0 fui:grow-0 fui:basis-full",
|
||||
orientation === "horizontal" ? "fui:pl-4" : "fui:pt-4",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -182,10 +182,10 @@ function CarouselPrevious({
|
||||
variant={variant}
|
||||
size={size}
|
||||
className={cn(
|
||||
"lib:absolute lib:touch-manipulation lib:rounded-full",
|
||||
"fui:absolute fui:touch-manipulation fui:rounded-full",
|
||||
orientation === "horizontal"
|
||||
? "lib:inset-y-0 lib:-left-12 lib:my-auto"
|
||||
: "lib:-top-12 lib:left-1/2 lib:-translate-x-1/2 lib:rotate-90",
|
||||
? "fui:inset-y-0 fui:-left-12 fui:my-auto"
|
||||
: "fui:-top-12 fui:left-1/2 fui:-translate-x-1/2 fui:rotate-90",
|
||||
className,
|
||||
)}
|
||||
disabled={!canScrollPrev}
|
||||
@@ -193,7 +193,7 @@ function CarouselPrevious({
|
||||
{...props}
|
||||
>
|
||||
<ChevronLeftIcon />
|
||||
<span className="lib:sr-only">Previous slide</span>
|
||||
<span className="fui:sr-only">Previous slide</span>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
@@ -212,10 +212,10 @@ function CarouselNext({
|
||||
variant={variant}
|
||||
size={size}
|
||||
className={cn(
|
||||
"lib:absolute lib:touch-manipulation lib:rounded-full",
|
||||
"fui:absolute fui:touch-manipulation fui:rounded-full",
|
||||
orientation === "horizontal"
|
||||
? "lib:inset-y-0 lib:-right-12 lib:my-auto"
|
||||
: "lib:-bottom-12 lib:left-1/2 lib:-translate-x-1/2 lib:rotate-90",
|
||||
? "fui:inset-y-0 fui:-right-12 fui:my-auto"
|
||||
: "fui:-bottom-12 fui:left-1/2 fui:-translate-x-1/2 fui:rotate-90",
|
||||
className,
|
||||
)}
|
||||
disabled={!canScrollNext}
|
||||
@@ -223,7 +223,7 @@ function CarouselNext({
|
||||
{...props}
|
||||
>
|
||||
<ChevronRightIcon />
|
||||
<span className="lib:sr-only">Next slide</span>
|
||||
<span className="fui:sr-only">Next slide</span>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
+17
-17
@@ -64,7 +64,7 @@ function ChartContainer({
|
||||
data-slot="chart"
|
||||
data-chart={chartId}
|
||||
className={cn(
|
||||
"lib:flex lib:aspect-video lib:justify-center lib:text-xs lib:[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground lib:[&_.recharts-cartesian-grid_line[stroke=#ccc]]:stroke-border/50 lib:[&_.recharts-curve.recharts-tooltip-cursor]:stroke-border lib:[&_.recharts-dot[stroke=#fff]]:stroke-transparent lib:[&_.recharts-layer]:outline-hidden lib:[&_.recharts-polar-grid_[stroke=#ccc]]:stroke-border lib:[&_.recharts-radial-bar-background-sector]:fill-muted lib:[&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted lib:[&_.recharts-reference-line_[stroke=#ccc]]:stroke-border lib:[&_.recharts-sector]:outline-hidden lib:[&_.recharts-sector[stroke=#fff]]:stroke-transparent lib:[&_.recharts-surface]:outline-hidden",
|
||||
"fui:flex fui:aspect-video fui:justify-center fui:text-xs fui:[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground fui:[&_.recharts-cartesian-grid_line[stroke=#ccc]]:stroke-border/50 fui:[&_.recharts-curve.recharts-tooltip-cursor]:stroke-border fui:[&_.recharts-dot[stroke=#fff]]:stroke-transparent fui:[&_.recharts-layer]:outline-hidden fui:[&_.recharts-polar-grid_[stroke=#ccc]]:stroke-border fui:[&_.recharts-radial-bar-background-sector]:fill-muted fui:[&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted fui:[&_.recharts-reference-line_[stroke=#ccc]]:stroke-border fui:[&_.recharts-sector]:outline-hidden fui:[&_.recharts-sector[stroke=#fff]]:stroke-transparent fui:[&_.recharts-surface]:outline-hidden",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -160,7 +160,7 @@ function ChartTooltipContent({
|
||||
|
||||
if (labelFormatter) {
|
||||
return (
|
||||
<div className={cn("lib:font-medium", labelClassName)}>
|
||||
<div className={cn("fui:font-medium", labelClassName)}>
|
||||
{labelFormatter(value, payload)}
|
||||
</div>
|
||||
);
|
||||
@@ -170,7 +170,7 @@ function ChartTooltipContent({
|
||||
return null;
|
||||
}
|
||||
|
||||
return <div className={cn("lib:font-medium", labelClassName)}>{value}</div>;
|
||||
return <div className={cn("fui:font-medium", labelClassName)}>{value}</div>;
|
||||
}, [
|
||||
label,
|
||||
labelFormatter,
|
||||
@@ -190,12 +190,12 @@ function ChartTooltipContent({
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"lib:grid lib:min-w-32 lib:items-start lib:gap-1.5 lib:rounded-lg lib:border lib:border-border/50 lib:bg-background lib:px-2.5 lib:py-1.5 lib:text-xs lib:shadow-xl",
|
||||
"fui:grid fui:min-w-32 fui:items-start fui:gap-1.5 fui:rounded-lg fui:border fui:border-border/50 fui:bg-background fui:px-2.5 fui:py-1.5 fui:text-xs fui:shadow-xl",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{!nestLabel ? tooltipLabel : null}
|
||||
<div className="lib:grid lib:gap-1.5">
|
||||
<div className="fui:grid fui:gap-1.5">
|
||||
{payload
|
||||
.filter((item) => item.type !== "none")
|
||||
.map((item, index) => {
|
||||
@@ -207,8 +207,8 @@ function ChartTooltipContent({
|
||||
<div
|
||||
key={index}
|
||||
className={cn(
|
||||
"lib:flex lib:w-full lib:flex-wrap lib:items-stretch lib:gap-2 lib:[&>svg]:h-2.5 lib:[&>svg]:w-2.5 lib:[&>svg]:text-muted-foreground",
|
||||
indicator === "dot" && "lib:items-center",
|
||||
"fui:flex fui:w-full fui:flex-wrap fui:items-stretch fui:gap-2 fui:[&>svg]:h-2.5 fui:[&>svg]:w-2.5 fui:[&>svg]:text-muted-foreground",
|
||||
indicator === "dot" && "fui:items-center",
|
||||
)}
|
||||
>
|
||||
{formatter && item?.value !== undefined && item.name ? (
|
||||
@@ -221,7 +221,7 @@ function ChartTooltipContent({
|
||||
!hideIndicator && (
|
||||
<div
|
||||
className={cn(
|
||||
"lib:shrink-0 lib:rounded-[2px] lib:border-(--color-border) lib:bg-(--color-bg)",
|
||||
"fui:shrink-0 fui:rounded-[2px] fui:border-(--color-border) fui:bg-(--color-bg)",
|
||||
{
|
||||
"h-2.5 w-2.5": indicator === "dot",
|
||||
"w-1": indicator === "line",
|
||||
@@ -241,18 +241,18 @@ function ChartTooltipContent({
|
||||
)}
|
||||
<div
|
||||
className={cn(
|
||||
"lib:flex lib:flex-1 lib:justify-between lib:leading-none",
|
||||
nestLabel ? "lib:items-end" : "lib:items-center",
|
||||
"fui:flex fui:flex-1 fui:justify-between fui:leading-none",
|
||||
nestLabel ? "fui:items-end" : "fui:items-center",
|
||||
)}
|
||||
>
|
||||
<div className="lib:grid lib:gap-1.5">
|
||||
<div className="fui:grid fui:gap-1.5">
|
||||
{nestLabel ? tooltipLabel : null}
|
||||
<span className="lib:text-muted-foreground">
|
||||
<span className="fui:text-muted-foreground">
|
||||
{itemConfig?.label ?? item.name}
|
||||
</span>
|
||||
</div>
|
||||
{item.value != null && (
|
||||
<span className="lib:font-mono lib:font-medium lib:text-foreground lib:tabular-nums">
|
||||
<span className="fui:font-mono fui:font-medium fui:text-foreground fui:tabular-nums">
|
||||
{typeof item.value === "number"
|
||||
? item.value.toLocaleString()
|
||||
: String(item.value)}
|
||||
@@ -290,8 +290,8 @@ function ChartLegendContent({
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"lib:flex lib:items-center lib:justify-center lib:gap-4",
|
||||
verticalAlign === "top" ? "lib:pb-3" : "lib:pt-3",
|
||||
"fui:flex fui:items-center fui:justify-center fui:gap-4",
|
||||
verticalAlign === "top" ? "fui:pb-3" : "fui:pt-3",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
@@ -305,14 +305,14 @@ function ChartLegendContent({
|
||||
<div
|
||||
key={index}
|
||||
className={cn(
|
||||
"lib:flex lib:items-center lib:gap-1.5 lib:[&>svg]:h-3 lib:[&>svg]:w-3 lib:[&>svg]:text-muted-foreground",
|
||||
"fui:flex fui:items-center fui:gap-1.5 fui:[&>svg]:h-3 fui:[&>svg]:w-3 fui:[&>svg]:text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
{itemConfig?.icon && !hideIcon ? (
|
||||
<itemConfig.icon />
|
||||
) : (
|
||||
<div
|
||||
className="lib:h-2 lib:w-2 lib:shrink-0 lib:rounded-[2px]"
|
||||
className="fui:h-2 fui:w-2 fui:shrink-0 fui:rounded-[2px]"
|
||||
style={{
|
||||
backgroundColor: item.color,
|
||||
}}
|
||||
|
||||
@@ -3,20 +3,22 @@
|
||||
import { Checkbox as CheckboxPrimitive } from "@base-ui/react/checkbox";
|
||||
import { cn } from "cn";
|
||||
import { CheckIcon } from "lucide-react";
|
||||
import * as React from "react";
|
||||
import { Label } from "@/components/label";
|
||||
|
||||
function Checkbox({ className, ...props }: CheckboxPrimitive.Root.Props) {
|
||||
return (
|
||||
<CheckboxPrimitive.Root
|
||||
data-slot="checkbox"
|
||||
className={cn(
|
||||
"lib:peer lib:relative lib:flex lib:size-4 lib:shrink-0 lib:items-center lib:justify-center lib:rounded-[6px] lib:border lib:border-input lib:transition-shadow lib:outline-none lib:group-has-disabled/field:opacity-50 lib:group-has-[:focus-visible]/field-label:ring-0 lib:group-has-[:focus-visible]/field-label:not-data-checked:border-input lib:after:absolute lib:after:-inset-x-3 lib:after:-inset-y-2 lib:focus-visible:border-ring lib:focus-visible:ring-[3px] lib:focus-visible:ring-ring/50 lib:disabled:cursor-not-allowed lib:disabled:opacity-50 lib:aria-invalid:border-destructive lib:aria-invalid:ring-[3px] lib:aria-invalid:ring-destructive/20 lib:aria-invalid:aria-checked:border-primary lib:dark:bg-input/30 lib:dark:aria-invalid:border-destructive/50 lib:dark:aria-invalid:ring-destructive/40 lib:data-checked:border-primary lib:data-checked:bg-primary lib:data-checked:text-primary-foreground lib:group-has-[:focus-visible]/field-label:data-checked:border-primary lib:dark:data-checked:bg-primary",
|
||||
"fui:peer fui:relative fui:flex fui:size-4 fui:shrink-0 fui:items-center fui:justify-center fui:rounded-[6px] fui:border fui:border-input fui:transition-shadow fui:outline-none fui:group-has-disabled/field:opacity-50 fui:group-has-[:focus-visible]/field-label:ring-0 fui:group-has-[:focus-visible]/field-label:not-data-checked:border-input fui:after:absolute fui:after:-inset-x-3 fui:after:-inset-y-2 fui:focus-visible:border-ring fui:focus-visible:ring-[3px] fui:focus-visible:ring-ring/50 fui:disabled:cursor-not-allowed fui:disabled:opacity-50 fui:aria-invalid:border-destructive fui:aria-invalid:ring-[3px] fui:aria-invalid:ring-destructive/20 fui:aria-invalid:aria-checked:border-primary fui:dark:bg-input/30 fui:dark:aria-invalid:border-destructive/50 fui:dark:aria-invalid:ring-destructive/40 fui:data-checked:border-primary fui:data-checked:bg-primary fui:data-checked:text-primary-foreground fui:group-has-[:focus-visible]/field-label:data-checked:border-primary fui:dark:data-checked:bg-primary",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<CheckboxPrimitive.Indicator
|
||||
data-slot="checkbox-indicator"
|
||||
className="lib:grid lib:place-content-center lib:text-current lib:transition-none lib:[&>svg]:size-3.5"
|
||||
className="fui:grid fui:place-content-center fui:text-current fui:transition-none fui:[&>svg]:size-3.5"
|
||||
>
|
||||
<CheckIcon />
|
||||
</CheckboxPrimitive.Indicator>
|
||||
@@ -24,4 +26,53 @@ function Checkbox({ className, ...props }: CheckboxPrimitive.Root.Props) {
|
||||
);
|
||||
}
|
||||
|
||||
export { Checkbox };
|
||||
type CheckboxWithLabelProps = CheckboxPrimitive.Root.Props & {
|
||||
label: React.ReactNode;
|
||||
labelClassName?: string;
|
||||
wrapperClassName?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* A checkbox and its caption as one labelled control.
|
||||
*
|
||||
* Keeps the id wiring — a generated id tying the label to the input — in one
|
||||
* place instead of at every call site.
|
||||
*
|
||||
* Deliberately not built on `Field`: a caller may already be inside one, and
|
||||
* a `Field` nested in a `Field` picks up the bordered card treatment.
|
||||
*/
|
||||
function CheckboxWithLabel({
|
||||
id,
|
||||
label,
|
||||
disabled,
|
||||
className,
|
||||
labelClassName,
|
||||
wrapperClassName,
|
||||
...props
|
||||
}: CheckboxWithLabelProps) {
|
||||
const reactId = React.useId();
|
||||
const inputId = id ?? reactId;
|
||||
|
||||
return (
|
||||
<div
|
||||
data-slot="checkbox-with-label"
|
||||
data-disabled={disabled ? "" : undefined}
|
||||
className={cn(
|
||||
"fui:group fui:inline-flex fui:items-center fui:gap-2",
|
||||
wrapperClassName,
|
||||
)}
|
||||
>
|
||||
<Checkbox
|
||||
id={inputId}
|
||||
disabled={disabled}
|
||||
className={className}
|
||||
{...props}
|
||||
/>
|
||||
<Label htmlFor={inputId} className={labelClassName}>
|
||||
{label}
|
||||
</Label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { Checkbox, CheckboxWithLabel, type CheckboxWithLabelProps };
|
||||
|
||||
+14
-14
@@ -19,7 +19,7 @@ function Command({
|
||||
<CommandPrimitive
|
||||
data-slot="command"
|
||||
className={cn(
|
||||
"lib:flex lib:size-full lib:flex-col lib:overflow-hidden lib:rounded-4xl lib:bg-popover lib:p-1 lib:text-popover-foreground",
|
||||
"fui:flex fui:size-full fui:flex-col fui:overflow-hidden fui:rounded-4xl fui:bg-popover fui:p-1 fui:text-popover-foreground",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -43,13 +43,13 @@ function CommandDialog({
|
||||
}) {
|
||||
return (
|
||||
<Dialog {...props}>
|
||||
<DialogHeader className="lib:sr-only">
|
||||
<DialogHeader className="fui:sr-only">
|
||||
<DialogTitle>{title}</DialogTitle>
|
||||
<DialogDescription>{description}</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogContent
|
||||
className={cn(
|
||||
"lib:top-1/3 lib:translate-y-0 lib:overflow-hidden lib:rounded-4xl! lib:p-0",
|
||||
"fui:top-1/3 fui:translate-y-0 fui:overflow-hidden fui:rounded-4xl! fui:p-0",
|
||||
className,
|
||||
)}
|
||||
showCloseButton={showCloseButton}
|
||||
@@ -65,18 +65,18 @@ function CommandInput({
|
||||
...props
|
||||
}: React.ComponentProps<typeof CommandPrimitive.Input>) {
|
||||
return (
|
||||
<div data-slot="command-input-wrapper" className="lib:p-1 lib:pb-0">
|
||||
<InputGroup className="lib:h-9 lib:bg-input/30">
|
||||
<div data-slot="command-input-wrapper" className="fui:p-1 fui:pb-0">
|
||||
<InputGroup className="fui:h-9 fui:bg-input/30">
|
||||
<CommandPrimitive.Input
|
||||
data-slot="command-input"
|
||||
className={cn(
|
||||
"lib:w-full lib:text-sm lib:outline-hidden lib:disabled:cursor-not-allowed lib:disabled:opacity-50",
|
||||
"fui:w-full fui:text-sm fui:outline-hidden fui:disabled:cursor-not-allowed fui:disabled:opacity-50",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
<InputGroupAddon>
|
||||
<SearchIcon className="lib:size-4 lib:shrink-0 lib:opacity-50" />
|
||||
<SearchIcon className="fui:size-4 fui:shrink-0 fui:opacity-50" />
|
||||
</InputGroupAddon>
|
||||
</InputGroup>
|
||||
</div>
|
||||
@@ -91,7 +91,7 @@ function CommandList({
|
||||
<CommandPrimitive.List
|
||||
data-slot="command-list"
|
||||
className={cn(
|
||||
"lib:no-scrollbar lib:max-h-72 lib:scroll-py-1 lib:overflow-x-hidden lib:overflow-y-auto lib:outline-none",
|
||||
"fui:no-scrollbar fui:max-h-72 fui:scroll-py-1 fui:overflow-x-hidden fui:overflow-y-auto fui:outline-none",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -106,7 +106,7 @@ function CommandEmpty({
|
||||
return (
|
||||
<CommandPrimitive.Empty
|
||||
data-slot="command-empty"
|
||||
className={cn("lib:py-6 lib:text-center lib:text-sm", className)}
|
||||
className={cn("fui:py-6 fui:text-center fui:text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -120,7 +120,7 @@ function CommandGroup({
|
||||
<CommandPrimitive.Group
|
||||
data-slot="command-group"
|
||||
className={cn(
|
||||
"lib:overflow-hidden lib:p-1 lib:text-foreground lib:**:[[cmdk-group-heading]]:px-3 lib:**:[[cmdk-group-heading]]:py-2 lib:**:[[cmdk-group-heading]]:text-xs lib:**:[[cmdk-group-heading]]:font-medium lib:**:[[cmdk-group-heading]]:text-muted-foreground",
|
||||
"fui:overflow-hidden fui:p-1 fui:text-foreground fui:**:[[cmdk-group-heading]]:px-3 fui:**:[[cmdk-group-heading]]:py-2 fui:**:[[cmdk-group-heading]]:text-xs fui:**:[[cmdk-group-heading]]:font-medium fui:**:[[cmdk-group-heading]]:text-muted-foreground",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -135,7 +135,7 @@ function CommandSeparator({
|
||||
return (
|
||||
<CommandPrimitive.Separator
|
||||
data-slot="command-separator"
|
||||
className={cn("lib:my-1 lib:h-px lib:bg-border/50", className)}
|
||||
className={cn("fui:my-1 fui:h-px fui:bg-border/50", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -150,13 +150,13 @@ function CommandItem({
|
||||
<CommandPrimitive.Item
|
||||
data-slot="command-item"
|
||||
className={cn(
|
||||
"lib:group/command-item lib:relative lib:flex lib:cursor-default lib:items-center lib:gap-2 lib:rounded-lg lib:px-3 lib:py-2 lib:text-sm lib:outline-hidden lib:select-none lib:in-data-[slot=dialog-content]:rounded-2xl lib:data-[disabled=true]:pointer-events-none lib:data-[disabled=true]:opacity-50 lib:data-selected:bg-muted lib:data-selected:text-foreground lib:[&_svg]:pointer-events-none lib:[&_svg]:shrink-0 lib:[&_svg:not([class*=size-])]:size-4 lib:data-selected:*:[svg]:text-foreground",
|
||||
"fui:group/command-item fui:relative fui:flex fui:cursor-default fui:items-center fui:gap-2 fui:rounded-lg fui:px-3 fui:py-2 fui:text-sm fui:outline-hidden fui:select-none fui:in-data-[slot=dialog-content]:rounded-2xl fui:data-[disabled=true]:pointer-events-none fui:data-[disabled=true]:opacity-50 fui:data-selected:bg-muted fui:data-selected:text-foreground fui:[&_svg]:pointer-events-none fui:[&_svg]:shrink-0 fui:[&_svg:not([class*=size-])]:size-4 fui:data-selected:*:[svg]:text-foreground",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<CheckIcon className="lib:ml-auto lib:opacity-0 lib:group-has-data-[slot=command-shortcut]/command-item:hidden lib:group-data-[checked=true]/command-item:opacity-100" />
|
||||
<CheckIcon className="fui:ml-auto fui:opacity-0 fui:group-has-data-[slot=command-shortcut]/command-item:hidden fui:group-data-[checked=true]/command-item:opacity-100" />
|
||||
</CommandPrimitive.Item>
|
||||
);
|
||||
}
|
||||
@@ -169,7 +169,7 @@ function CommandShortcut({
|
||||
<span
|
||||
data-slot="command-shortcut"
|
||||
className={cn(
|
||||
"lib:ml-auto lib:text-xs lib:tracking-widest lib:text-muted-foreground lib:group-data-selected/command-item:text-foreground",
|
||||
"fui:ml-auto fui:text-xs fui:tracking-widest fui:text-muted-foreground fui:group-data-selected/command-item:text-foreground",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -20,7 +20,7 @@ function ContextMenuTrigger({
|
||||
return (
|
||||
<ContextMenuPrimitive.Trigger
|
||||
data-slot="context-menu-trigger"
|
||||
className={cn("lib:select-none", className)}
|
||||
className={cn("fui:select-none", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -41,7 +41,7 @@ function ContextMenuContent({
|
||||
return (
|
||||
<ContextMenuPrimitive.Portal>
|
||||
<ContextMenuPrimitive.Positioner
|
||||
className="lib:isolate lib:z-50 lib:outline-none"
|
||||
className="fui:isolate fui:z-50 fui:outline-none"
|
||||
align={align}
|
||||
alignOffset={alignOffset}
|
||||
side={side}
|
||||
@@ -50,7 +50,7 @@ function ContextMenuContent({
|
||||
<ContextMenuPrimitive.Popup
|
||||
data-slot="context-menu-content"
|
||||
className={cn(
|
||||
"lib: lib: lib:z-50 lib:max-h-(--available-height) lib:min-w-48 lib:origin-(--transform-origin) lib:overflow-x-hidden lib:overflow-y-auto lib:rounded-2xl lib:bg-popover lib:p-1 lib:text-popover-foreground lib:shadow-2xl lib:ring-1 lib:ring-foreground/5 lib:duration-100 lib:outline-none lib:data-[side=bottom]:slide-in-from-top-2 lib:data-[side=inline-end]:slide-in-from-left-2 lib:data-[side=inline-start]:slide-in-from-right-2 lib:data-[side=left]:slide-in-from-right-2 lib:data-[side=right]:slide-in-from-left-2 lib:data-[side=top]:slide-in-from-bottom-2 lib:data-open:animate-in lib:data-open:fade-in-0 lib:data-open:zoom-in-95 lib:data-closed:animate-out lib:data-closed:fade-out-0 lib:data-closed:zoom-out-95 animate-none! relative bg-popover/70 before:pointer-events-none before:absolute before:inset-0 before:-z-1 before:rounded-[inherit] before:backdrop-blur-2xl before:backdrop-saturate-150 **:data-[slot$=-item]:focus:bg-foreground/10 **:data-[slot$=-item]:data-highlighted:bg-foreground/10 **:data-[slot$=-separator]:bg-foreground/5 **:data-[slot$=-trigger]:focus:bg-foreground/10 **:data-[slot$=-trigger]:aria-expanded:bg-foreground/10! **:data-[variant=destructive]:focus:bg-foreground/10! **:data-[variant=destructive]:text-accent-foreground! **:data-[variant=destructive]:**:text-accent-foreground!",
|
||||
"fui: fui: fui:z-50 fui:max-h-(--available-height) fui:min-w-48 fui:origin-(--transform-origin) fui:overflow-x-hidden fui:overflow-y-auto fui:rounded-2xl fui:bg-popover fui:p-1 fui:text-popover-foreground fui:shadow-2xl fui:ring-1 fui:ring-foreground/5 fui:duration-100 fui:outline-none fui:data-[side=bottom]:slide-in-from-top-2 fui:data-[side=inline-end]:slide-in-from-left-2 fui:data-[side=inline-start]:slide-in-from-right-2 fui:data-[side=left]:slide-in-from-right-2 fui:data-[side=right]:slide-in-from-left-2 fui:data-[side=top]:slide-in-from-bottom-2 fui:data-open:animate-in fui:data-open:fade-in-0 fui:data-open:zoom-in-95 fui:data-closed:animate-out fui:data-closed:fade-out-0 fui:data-closed:zoom-out-95 animate-none! relative bg-popover/70 before:pointer-events-none before:absolute before:inset-0 before:-z-1 before:rounded-[inherit] before:backdrop-blur-2xl before:backdrop-saturate-150 **:data-[slot$=-item]:focus:bg-foreground/10 **:data-[slot$=-item]:data-highlighted:bg-foreground/10 **:data-[slot$=-separator]:bg-foreground/5 **:data-[slot$=-trigger]:focus:bg-foreground/10 **:data-[slot$=-trigger]:aria-expanded:bg-foreground/10! **:data-[variant=destructive]:focus:bg-foreground/10! **:data-[variant=destructive]:text-accent-foreground! **:data-[variant=destructive]:**:text-accent-foreground!",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -78,7 +78,7 @@ function ContextMenuLabel({
|
||||
data-slot="context-menu-label"
|
||||
data-inset={inset}
|
||||
className={cn(
|
||||
"lib:px-3 lib:py-2.5 lib:text-xs lib:text-muted-foreground lib:data-inset:pl-9.5",
|
||||
"fui:px-3 fui:py-2.5 fui:text-xs fui:text-muted-foreground fui:data-inset:pl-9.5",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -101,7 +101,7 @@ function ContextMenuItem({
|
||||
data-inset={inset}
|
||||
data-variant={variant}
|
||||
className={cn(
|
||||
"lib:group/context-menu-item lib:relative lib:flex lib:cursor-default lib:items-center lib:gap-2.5 lib:rounded-xl lib:px-3 lib:py-2 lib:text-sm lib:outline-hidden lib:select-none lib:focus:bg-accent lib:focus:text-accent-foreground lib:data-inset:pl-9.5 lib:data-[variant=destructive]:text-destructive lib:data-[variant=destructive]:focus:bg-destructive/10 lib:data-[variant=destructive]:focus:text-destructive lib:dark:data-[variant=destructive]:focus:bg-destructive/20 lib:data-disabled:pointer-events-none lib:data-disabled:opacity-50 lib:[&_svg]:pointer-events-none lib:[&_svg]:shrink-0 lib:[&_svg:not([class*=size-])]:size-4 lib:focus:*:[svg]:text-accent-foreground lib:data-[variant=destructive]:*:[svg]:text-destructive",
|
||||
"fui:group/context-menu-item fui:relative fui:flex fui:cursor-default fui:items-center fui:gap-2.5 fui:rounded-xl fui:px-3 fui:py-2 fui:text-sm fui:outline-hidden fui:select-none fui:focus:bg-accent fui:focus:text-accent-foreground fui:data-inset:pl-9.5 fui:data-[variant=destructive]:text-destructive fui:data-[variant=destructive]:focus:bg-destructive/10 fui:data-[variant=destructive]:focus:text-destructive fui:dark:data-[variant=destructive]:focus:bg-destructive/20 fui:data-disabled:pointer-events-none fui:data-disabled:opacity-50 fui:[&_svg]:pointer-events-none fui:[&_svg]:shrink-0 fui:[&_svg:not([class*=size-])]:size-4 fui:focus:*:[svg]:text-accent-foreground fui:data-[variant=destructive]:*:[svg]:text-destructive",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -128,13 +128,13 @@ function ContextMenuSubTrigger({
|
||||
data-slot="context-menu-sub-trigger"
|
||||
data-inset={inset}
|
||||
className={cn(
|
||||
"lib:flex lib:cursor-default lib:items-center lib:rounded-xl lib:px-3 lib:py-2 lib:text-sm lib:outline-hidden lib:select-none lib:focus:bg-accent lib:focus:text-accent-foreground lib:data-inset:pl-9.5 lib:data-open:bg-accent lib:data-open:text-accent-foreground lib:[&_svg]:pointer-events-none lib:[&_svg]:shrink-0 lib:[&_svg:not([class*=size-])]:size-4",
|
||||
"fui:flex fui:cursor-default fui:items-center fui:rounded-xl fui:px-3 fui:py-2 fui:text-sm fui:outline-hidden fui:select-none fui:focus:bg-accent fui:focus:text-accent-foreground fui:data-inset:pl-9.5 fui:data-open:bg-accent fui:data-open:text-accent-foreground fui:[&_svg]:pointer-events-none fui:[&_svg]:shrink-0 fui:[&_svg:not([class*=size-])]:size-4",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronRightIcon className="lib:ml-auto" />
|
||||
<ChevronRightIcon className="fui:ml-auto" />
|
||||
</ContextMenuPrimitive.SubmenuTrigger>
|
||||
);
|
||||
}
|
||||
@@ -145,7 +145,7 @@ function ContextMenuSubContent({
|
||||
return (
|
||||
<ContextMenuContent
|
||||
data-slot="context-menu-sub-content"
|
||||
className="lib: lib: lib:shadow-lg animate-none! relative bg-popover/70 before:pointer-events-none before:absolute before:inset-0 before:-z-1 before:rounded-[inherit] before:backdrop-blur-2xl before:backdrop-saturate-150 **:data-[slot$=-item]:focus:bg-foreground/10 **:data-[slot$=-item]:data-highlighted:bg-foreground/10 **:data-[slot$=-separator]:bg-foreground/5 **:data-[slot$=-trigger]:focus:bg-foreground/10 **:data-[slot$=-trigger]:aria-expanded:bg-foreground/10! **:data-[variant=destructive]:focus:bg-foreground/10! **:data-[variant=destructive]:text-accent-foreground! **:data-[variant=destructive]:**:text-accent-foreground!"
|
||||
className="fui: fui: fui:shadow-lg animate-none! relative bg-popover/70 before:pointer-events-none before:absolute before:inset-0 before:-z-1 before:rounded-[inherit] before:backdrop-blur-2xl before:backdrop-saturate-150 **:data-[slot$=-item]:focus:bg-foreground/10 **:data-[slot$=-item]:data-highlighted:bg-foreground/10 **:data-[slot$=-separator]:bg-foreground/5 **:data-[slot$=-trigger]:focus:bg-foreground/10 **:data-[slot$=-trigger]:aria-expanded:bg-foreground/10! **:data-[variant=destructive]:focus:bg-foreground/10! **:data-[variant=destructive]:text-accent-foreground! **:data-[variant=destructive]:**:text-accent-foreground!"
|
||||
side="right"
|
||||
{...props}
|
||||
/>
|
||||
@@ -166,13 +166,13 @@ function ContextMenuCheckboxItem({
|
||||
data-slot="context-menu-checkbox-item"
|
||||
data-inset={inset}
|
||||
className={cn(
|
||||
"lib:relative lib:flex lib:cursor-default lib:items-center lib:gap-2 lib:rounded-xl lib:py-2 lib:pr-8 lib:pl-3 lib:text-sm lib:outline-hidden lib:select-none lib:focus:bg-accent lib:focus:text-accent-foreground lib:data-inset:pl-9.5 lib:data-disabled:pointer-events-none lib:data-disabled:opacity-50 lib:[&_svg]:pointer-events-none lib:[&_svg]:shrink-0 lib:[&_svg:not([class*=size-])]:size-4",
|
||||
"fui:relative fui:flex fui:cursor-default fui:items-center fui:gap-2 fui:rounded-xl fui:py-2 fui:pr-8 fui:pl-3 fui:text-sm fui:outline-hidden fui:select-none fui:focus:bg-accent fui:focus:text-accent-foreground fui:data-inset:pl-9.5 fui:data-disabled:pointer-events-none fui:data-disabled:opacity-50 fui:[&_svg]:pointer-events-none fui:[&_svg]:shrink-0 fui:[&_svg:not([class*=size-])]:size-4",
|
||||
className,
|
||||
)}
|
||||
checked={checked}
|
||||
{...props}
|
||||
>
|
||||
<span className="lib:pointer-events-none lib:absolute lib:right-2">
|
||||
<span className="fui:pointer-events-none fui:absolute fui:right-2">
|
||||
<ContextMenuPrimitive.CheckboxItemIndicator>
|
||||
<CheckIcon />
|
||||
</ContextMenuPrimitive.CheckboxItemIndicator>
|
||||
@@ -206,12 +206,12 @@ function ContextMenuRadioItem({
|
||||
data-slot="context-menu-radio-item"
|
||||
data-inset={inset}
|
||||
className={cn(
|
||||
"lib:relative lib:flex lib:cursor-default lib:items-center lib:gap-2 lib:rounded-xl lib:py-2 lib:pr-8 lib:pl-3 lib:text-sm lib:outline-hidden lib:select-none lib:focus:bg-accent lib:focus:text-accent-foreground lib:data-inset:pl-9.5 lib:data-disabled:pointer-events-none lib:data-disabled:opacity-50 lib:[&_svg]:pointer-events-none lib:[&_svg]:shrink-0 lib:[&_svg:not([class*=size-])]:size-4",
|
||||
"fui:relative fui:flex fui:cursor-default fui:items-center fui:gap-2 fui:rounded-xl fui:py-2 fui:pr-8 fui:pl-3 fui:text-sm fui:outline-hidden fui:select-none fui:focus:bg-accent fui:focus:text-accent-foreground fui:data-inset:pl-9.5 fui:data-disabled:pointer-events-none fui:data-disabled:opacity-50 fui:[&_svg]:pointer-events-none fui:[&_svg]:shrink-0 fui:[&_svg:not([class*=size-])]:size-4",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className="lib:pointer-events-none lib:absolute lib:right-2">
|
||||
<span className="fui:pointer-events-none fui:absolute fui:right-2">
|
||||
<ContextMenuPrimitive.RadioItemIndicator>
|
||||
<CheckIcon />
|
||||
</ContextMenuPrimitive.RadioItemIndicator>
|
||||
@@ -228,7 +228,7 @@ function ContextMenuSeparator({
|
||||
return (
|
||||
<ContextMenuPrimitive.Separator
|
||||
data-slot="context-menu-separator"
|
||||
className={cn("lib:-mx-1 lib:my-1 lib:h-px lib:bg-border/50", className)}
|
||||
className={cn("fui:-mx-1 fui:my-1 fui:h-px fui:bg-border/50", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -242,7 +242,7 @@ function ContextMenuShortcut({
|
||||
<span
|
||||
data-slot="context-menu-shortcut"
|
||||
className={cn(
|
||||
"lib:ml-auto lib:text-xs lib:tracking-widest lib:text-muted-foreground lib:group-focus/context-menu-item:text-accent-foreground",
|
||||
"fui:ml-auto fui:text-xs fui:tracking-widest fui:text-muted-foreground fui:group-focus/context-menu-item:text-accent-foreground",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
+171
-81
@@ -74,12 +74,6 @@ import {
|
||||
TableRow,
|
||||
} from "@/components/table";
|
||||
|
||||
/**
|
||||
* Feature set for `DataTable`. TanStack Table v9 is opt-in: features and their
|
||||
* row models are registered here, and the resulting type parameterises every
|
||||
* column definition. Row-model factories take no arguments, and the core row
|
||||
* model is implicit — never register it.
|
||||
*/
|
||||
const dataTableFeatures = tableFeatures({
|
||||
columnFilteringFeature,
|
||||
columnVisibilityFeature,
|
||||
@@ -120,39 +114,21 @@ type DataTableInstance<TData extends Record<string, any>> = ReactTable<
|
||||
TData
|
||||
>;
|
||||
|
||||
/**
|
||||
* Typed column helper bound to the `DataTable` feature set.
|
||||
*
|
||||
* @example
|
||||
* const column = createDataTableColumnHelper<Invoice>();
|
||||
* const columns = column.columns([
|
||||
* column.accessor("amount", { header: "Amount", sortFn: "alphanumeric" }),
|
||||
* ]);
|
||||
*/
|
||||
// biome-ignore lint/suspicious/noExplicitAny: mirrors TanStack's RowData constraint.
|
||||
function createDataTableColumnHelper<TData extends Record<string, any>>() {
|
||||
return createColumnHelper<DataTableFeatures, TData>();
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* column header */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
type DataTableColumnHeaderProps<
|
||||
// biome-ignore lint/suspicious/noExplicitAny: mirrors TanStack's RowData constraint.
|
||||
TData extends Record<string, any>,
|
||||
TValue,
|
||||
> = React.ComponentProps<"div"> & {
|
||||
/** `Column` is invariant in `TData`/`TValue`, so both must be generic here. */
|
||||
column: Column<DataTableFeatures, TData, TValue>;
|
||||
title: string;
|
||||
align?: "start" | "end";
|
||||
};
|
||||
|
||||
/**
|
||||
* Sortable header cell. Renders plain text when the column cannot be sorted so
|
||||
* it stays out of the tab order.
|
||||
*/
|
||||
function DataTableColumnHeader<
|
||||
// biome-ignore lint/suspicious/noExplicitAny: mirrors TanStack's RowData constraint.
|
||||
TData extends Record<string, any>,
|
||||
@@ -170,7 +146,7 @@ function DataTableColumnHeader<
|
||||
return (
|
||||
<div
|
||||
data-slot="data-table-column-header"
|
||||
className={cn(align === "end" && "lib:text-right", className)}
|
||||
className={cn(align === "end" && "fui:text-right", className)}
|
||||
{...props}
|
||||
>
|
||||
{title}
|
||||
@@ -189,8 +165,8 @@ function DataTableColumnHeader<
|
||||
<div
|
||||
data-slot="data-table-column-header"
|
||||
className={cn(
|
||||
"lib:flex lib:items-center",
|
||||
align === "end" && "lib:justify-end",
|
||||
"fui:flex fui:items-center",
|
||||
align === "end" && "fui:justify-end",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -206,26 +182,20 @@ function DataTableColumnHeader<
|
||||
? `${title}, sorted descending`
|
||||
: `${title}, not sorted`
|
||||
}
|
||||
className="lib:-mx-2 lib:h-7 lib:gap-1 lib:font-medium lib:data-[sorted=true]:text-foreground"
|
||||
className="fui:-mx-2 fui:h-7 fui:gap-1 fui:font-medium fui:data-[sorted=true]:text-foreground"
|
||||
data-sorted={sorted !== false}
|
||||
>
|
||||
{title}
|
||||
<Icon
|
||||
data-icon="inline-end"
|
||||
className={cn(sorted === false && "lib:opacity-50")}
|
||||
className={cn(sorted === false && "fui:opacity-50")}
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* selection column */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Ready-made checkbox column. Spread it as the first entry of `columns`.
|
||||
*/
|
||||
/** Checkbox column. Spread it as the first entry of `columns`. */
|
||||
function dataTableSelectColumn<
|
||||
// biome-ignore lint/suspicious/noExplicitAny: mirrors TanStack's RowData constraint.
|
||||
TData extends Record<string, any>,
|
||||
@@ -257,10 +227,6 @@ function dataTableSelectColumn<
|
||||
};
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* data table */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
// biome-ignore lint/suspicious/noExplicitAny: mirrors TanStack's RowData constraint.
|
||||
type DataTableProps<TData extends Record<string, any>> = Omit<
|
||||
React.ComponentProps<"div">,
|
||||
@@ -283,6 +249,59 @@ type DataTableProps<TData extends Record<string, any>> = Omit<
|
||||
empty?: React.ReactNode;
|
||||
/** Extra controls rendered in the toolbar, after the search field. */
|
||||
toolbar?: React.ReactNode;
|
||||
/** Optional detail row rendered directly below each data row. */
|
||||
renderSubComponent?: (row: DataTableRow<TData>) => React.ReactNode;
|
||||
|
||||
/* ------------------------------ server-driven ----------------------------- */
|
||||
/*
|
||||
* Each of the three concerns below is controlled when its state prop is
|
||||
* passed, and the table then stops doing that work itself — TanStack's
|
||||
* `manualSorting` / `manualPagination` / `manualFiltering`. Leave them out
|
||||
* and everything happens client-side over `data`, as before.
|
||||
*
|
||||
* This is what a table whose rows come back a page at a time needs: sorting
|
||||
* or filtering the rows already fetched would only reorder the current
|
||||
* window while presenting it as the whole result.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sort state owned by the caller. Implies the sort is the server's, unless
|
||||
* `manualSorting` says otherwise.
|
||||
*/
|
||||
sorting?: SortingState;
|
||||
onSortingChange?: (sorting: SortingState) => void;
|
||||
/**
|
||||
* Page state owned by the caller. Implies paging is the server's, unless
|
||||
* `manualPagination` says otherwise.
|
||||
*/
|
||||
pagination?: PaginationState;
|
||||
onPaginationChange?: (pagination: PaginationState) => void;
|
||||
/**
|
||||
* Total unpaged rows, as reported by the server. Required alongside
|
||||
* `pagination` — without it the table cannot know how many pages exist, and
|
||||
* "Next" would stop working after the first one.
|
||||
*/
|
||||
rowCount?: number;
|
||||
/**
|
||||
* Search term owned by the caller. Implies filtering is the server's, unless
|
||||
* `manualFiltering` says otherwise.
|
||||
*/
|
||||
globalFilter?: string;
|
||||
onGlobalFilterChange?: (value: string) => void;
|
||||
|
||||
/*
|
||||
* Who does the work, when that is not the same question as who holds the
|
||||
* state. Each defaults to "the server" when the matching state prop is
|
||||
* passed, which is the common case.
|
||||
*
|
||||
* Set one to `false` alongside its state prop for a table that holds the
|
||||
* complete result but keeps the state elsewhere — a listing that persists
|
||||
* its search in the URL, say. The table then still sorts, filters and pages
|
||||
* its rows itself, and the caller only stores the state.
|
||||
*/
|
||||
manualSorting?: boolean;
|
||||
manualPagination?: boolean;
|
||||
manualFiltering?: boolean;
|
||||
};
|
||||
|
||||
function DataTable<
|
||||
@@ -300,7 +319,18 @@ function DataTable<
|
||||
onRowSelectionChange,
|
||||
empty = "No results.",
|
||||
toolbar,
|
||||
renderSubComponent,
|
||||
className,
|
||||
sorting: sortingProp,
|
||||
onSortingChange: onSortingChangeProp,
|
||||
pagination: paginationProp,
|
||||
onPaginationChange: onPaginationChangeProp,
|
||||
rowCount,
|
||||
globalFilter: globalFilterProp,
|
||||
onGlobalFilterChange: onGlobalFilterChangeProp,
|
||||
manualSorting: manualSortingProp,
|
||||
manualPagination: manualPaginationProp,
|
||||
manualFiltering: manualFilteringProp,
|
||||
...props
|
||||
}: DataTableProps<TData>) {
|
||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||
@@ -316,26 +346,65 @@ function DataTable<
|
||||
pageSize: pageSize === false ? Infinity : pageSize,
|
||||
});
|
||||
|
||||
// Server-driven by default whenever the caller holds the state, but a
|
||||
// caller with the complete result can opt back out.
|
||||
const manualSorting = manualSortingProp ?? sortingProp !== undefined;
|
||||
const manualPagination = manualPaginationProp ?? paginationProp !== undefined;
|
||||
const manualFiltering = manualFilteringProp ?? globalFilterProp !== undefined;
|
||||
|
||||
/**
|
||||
* TanStack hands updaters through as `T | ((old: T) => T)`, so resolve them
|
||||
* against the current value before calling the caller's plain setter.
|
||||
*/
|
||||
const resolve = <T,>(updater: T | ((old: T) => T), current: T): T =>
|
||||
typeof updater === "function"
|
||||
? (updater as (old: T) => T)(current)
|
||||
: updater;
|
||||
|
||||
const table = useTable({
|
||||
features: dataTableFeatures,
|
||||
columns,
|
||||
data,
|
||||
getRowId,
|
||||
enableRowSelection,
|
||||
manualSorting,
|
||||
manualPagination,
|
||||
manualFiltering,
|
||||
// Only meaningful under `manualPagination`; TanStack derives it from the
|
||||
// row count otherwise.
|
||||
rowCount: manualPagination ? rowCount : undefined,
|
||||
state: {
|
||||
sorting,
|
||||
sorting: sortingProp ?? sorting,
|
||||
columnFilters,
|
||||
globalFilter,
|
||||
globalFilter: globalFilterProp ?? globalFilter,
|
||||
columnVisibility,
|
||||
rowSelection,
|
||||
pagination,
|
||||
pagination: paginationProp ?? pagination,
|
||||
},
|
||||
onSortingChange: (updater) => {
|
||||
if (onSortingChangeProp) {
|
||||
onSortingChangeProp(resolve(updater, sortingProp ?? sorting));
|
||||
return;
|
||||
}
|
||||
setSorting(updater);
|
||||
},
|
||||
onSortingChange: setSorting,
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
onGlobalFilterChange: setGlobalFilter,
|
||||
onGlobalFilterChange: (updater) => {
|
||||
if (onGlobalFilterChangeProp) {
|
||||
onGlobalFilterChangeProp(resolve(updater, globalFilterProp ?? ""));
|
||||
return;
|
||||
}
|
||||
setGlobalFilter(updater);
|
||||
},
|
||||
onColumnVisibilityChange: setColumnVisibility,
|
||||
onRowSelectionChange: setRowSelection,
|
||||
onPaginationChange: setPagination,
|
||||
onPaginationChange: (updater) => {
|
||||
if (onPaginationChangeProp) {
|
||||
onPaginationChangeProp(resolve(updater, paginationProp ?? pagination));
|
||||
return;
|
||||
}
|
||||
setPagination(updater);
|
||||
},
|
||||
});
|
||||
|
||||
const selectedRows = table.getSelectedRowModel().rows;
|
||||
@@ -349,17 +418,21 @@ function DataTable<
|
||||
|
||||
const rows = table.getRowModel().rows;
|
||||
const showToolbar = searchable !== false || hideableColumns || !!toolbar;
|
||||
const totalRows =
|
||||
manualPagination && rowCount !== undefined
|
||||
? rowCount
|
||||
: table.getFilteredRowModel().rows.length;
|
||||
|
||||
return (
|
||||
<div
|
||||
data-slot="data-table"
|
||||
className={cn("lib:flex lib:flex-col lib:gap-3", className)}
|
||||
className={cn("fui:flex fui:flex-col fui:gap-3", className)}
|
||||
{...props}
|
||||
>
|
||||
{showToolbar && (
|
||||
<div className="lib:flex lib:flex-wrap lib:items-center lib:gap-2">
|
||||
<div className="fui:flex fui:flex-wrap fui:items-end fui:gap-2">
|
||||
{searchable !== false && (
|
||||
<InputGroup className="lib:w-full lib:sm:w-64">
|
||||
<InputGroup className="fui:w-full fui:sm:w-64">
|
||||
<InputGroupAddon>
|
||||
<SearchIcon />
|
||||
</InputGroupAddon>
|
||||
@@ -378,13 +451,13 @@ function DataTable<
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
render={
|
||||
<Button variant="outline" size="sm" className="lib:ml-auto">
|
||||
<Button variant="outline" size="sm" className="fui:ml-auto">
|
||||
<Settings2Icon data-icon="inline-start" />
|
||||
Columns
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<DropdownMenuContent align="end" className="lib:w-44">
|
||||
<DropdownMenuContent align="end" className="fui:w-44">
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuLabel>Toggle columns</DropdownMenuLabel>
|
||||
</DropdownMenuGroup>
|
||||
@@ -401,7 +474,7 @@ function DataTable<
|
||||
column.toggleVisibility(!!value)
|
||||
}
|
||||
onSelect={(event) => event.preventDefault()}
|
||||
className="lib:capitalize"
|
||||
className="fui:capitalize"
|
||||
>
|
||||
{column.id}
|
||||
</DropdownMenuCheckboxItem>
|
||||
@@ -413,7 +486,7 @@ function DataTable<
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="lib:overflow-hidden lib:rounded-lg lib:border">
|
||||
<div className="fui:overflow-hidden fui:rounded-lg fui:border fui:bg-card">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
@@ -440,50 +513,67 @@ function DataTable<
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{rows.length === 0 ? (
|
||||
<TableRow className="lib:hover:bg-transparent">
|
||||
<TableRow className="fui:hover:bg-transparent">
|
||||
<TableCell
|
||||
colSpan={table.getAllLeafColumns().length}
|
||||
className="lib:h-28 lib:text-center lib:text-muted-foreground"
|
||||
className="fui:h-28 fui:text-center fui:text-muted-foreground"
|
||||
>
|
||||
{empty}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
rows.map((row: DataTableRow<TData>) => (
|
||||
<TableRow
|
||||
key={row.id}
|
||||
data-state={row.getIsSelected() ? "selected" : undefined}
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
<table.FlexRender cell={cell} />
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
rows.map((row: DataTableRow<TData>) => {
|
||||
const subComponent = renderSubComponent?.(row);
|
||||
|
||||
return (
|
||||
<React.Fragment key={row.id}>
|
||||
<TableRow
|
||||
data-state={row.getIsSelected() ? "selected" : undefined}
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
<table.FlexRender cell={cell} />
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
{subComponent && (
|
||||
<TableRow className="fui:hover:bg-transparent">
|
||||
<TableCell
|
||||
colSpan={row.getVisibleCells().length}
|
||||
className="fui:bg-muted/50 fui:p-0"
|
||||
>
|
||||
{subComponent}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
{pageSize !== false && (
|
||||
<div className="lib:flex lib:flex-wrap lib:items-center lib:justify-between lib:gap-3">
|
||||
<p className="lib:text-sm lib:text-muted-foreground">
|
||||
<div className="fui:flex fui:flex-wrap fui:items-center fui:justify-between fui:gap-3">
|
||||
<p className="fui:text-sm fui:text-muted-foreground">
|
||||
{/* Under `manualPagination` the row model holds only the page that
|
||||
was fetched, so the server's total is the honest figure. */}
|
||||
{enableRowSelection
|
||||
? `${selectedRows.length} of ${table.getFilteredRowModel().rows.length} row(s) selected`
|
||||
: `${table.getFilteredRowModel().rows.length} row(s)`}
|
||||
? `${selectedRows.length} of ${totalRows} row(s) selected`
|
||||
: `${totalRows} row(s)`}
|
||||
</p>
|
||||
|
||||
<div className="lib:flex lib:items-center lib:gap-4">
|
||||
<div className="lib:flex lib:items-center lib:gap-2">
|
||||
<span className="lib:text-sm lib:text-muted-foreground">
|
||||
<div className="fui:flex fui:items-center fui:gap-4">
|
||||
<div className="fui:flex fui:items-center fui:gap-2">
|
||||
<span className="fui:text-sm fui:text-muted-foreground">
|
||||
Rows per page
|
||||
</span>
|
||||
<Select
|
||||
value={String(table.state.pagination.pageSize)}
|
||||
onValueChange={(value) => table.setPageSize(Number(value))}
|
||||
>
|
||||
<SelectTrigger size="sm" className="lib:w-18">
|
||||
<SelectTrigger size="sm" className="fui:w-18">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -498,12 +588,12 @@ function DataTable<
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<span className="lib:text-sm lib:tabular-nums lib:text-muted-foreground">
|
||||
<span className="fui:text-sm fui:tabular-nums fui:text-muted-foreground">
|
||||
Page {table.state.pagination.pageIndex + 1} of{" "}
|
||||
{Math.max(table.getPageCount(), 1)}
|
||||
</span>
|
||||
|
||||
<div className="lib:flex lib:items-center lib:gap-1">
|
||||
<div className="fui:flex fui:items-center fui:gap-1">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon-sm"
|
||||
@@ -511,7 +601,7 @@ function DataTable<
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
<ChevronsLeftIcon />
|
||||
<span className="lib:sr-only">First page</span>
|
||||
<span className="fui:sr-only">First page</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
@@ -520,7 +610,7 @@ function DataTable<
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
<ChevronLeftIcon />
|
||||
<span className="lib:sr-only">Previous page</span>
|
||||
<span className="fui:sr-only">Previous page</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
@@ -529,7 +619,7 @@ function DataTable<
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
<ChevronRightIcon />
|
||||
<span className="lib:sr-only">Next page</span>
|
||||
<span className="fui:sr-only">Next page</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
@@ -538,7 +628,7 @@ function DataTable<
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
<ChevronsRightIcon />
|
||||
<span className="lib:sr-only">Last page</span>
|
||||
<span className="fui:sr-only">Last page</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -30,7 +30,7 @@ function DialogOverlay({
|
||||
<DialogPrimitive.Backdrop
|
||||
data-slot="dialog-overlay"
|
||||
className={cn(
|
||||
"lib:fixed lib:inset-0 lib:isolate lib:z-50 lib:bg-black/80 lib:duration-100 lib:supports-backdrop-filter:backdrop-blur-xs lib:data-open:animate-in lib:data-open:fade-in-0 lib:data-closed:animate-out lib:data-closed:fade-out-0",
|
||||
"fui:fixed fui:inset-0 fui:isolate fui:z-50 fui:bg-black/80 fui:duration-100 fui:supports-backdrop-filter:backdrop-blur-xs fui:data-open:animate-in fui:data-open:fade-in-0 fui:data-closed:animate-out fui:data-closed:fade-out-0",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -52,7 +52,7 @@ function DialogContent({
|
||||
<DialogPrimitive.Popup
|
||||
data-slot="dialog-content"
|
||||
className={cn(
|
||||
"lib:fixed lib:top-1/2 lib:left-1/2 lib:z-50 lib:grid lib:w-full lib:max-w-[calc(100%-2rem)] lib:-translate-x-1/2 lib:-translate-y-1/2 lib:gap-6 lib:rounded-4xl lib:bg-popover lib:p-6 lib:text-sm lib:text-popover-foreground lib:ring-1 lib:ring-foreground/5 lib:duration-100 lib:outline-none lib:sm:max-w-md lib:data-open:animate-in lib:data-open:fade-in-0 lib:data-open:zoom-in-95 lib:data-closed:animate-out lib:data-closed:fade-out-0 lib:data-closed:zoom-out-95",
|
||||
"fui:fixed fui:top-1/2 fui:left-1/2 fui:z-50 fui:grid fui:w-full fui:max-w-[calc(100%-2rem)] fui:-translate-x-1/2 fui:-translate-y-1/2 fui:gap-6 fui:rounded-4xl fui:bg-popover fui:p-6 fui:text-sm fui:text-popover-foreground fui:ring-1 fui:ring-foreground/5 fui:duration-100 fui:outline-none fui:sm:max-w-md fui:data-open:animate-in fui:data-open:fade-in-0 fui:data-open:zoom-in-95 fui:data-closed:animate-out fui:data-closed:fade-out-0 fui:data-closed:zoom-out-95",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -64,13 +64,13 @@ function DialogContent({
|
||||
render={
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="lib:absolute lib:top-4 lib:right-4"
|
||||
className="fui:absolute fui:top-4 fui:right-4"
|
||||
size="icon-sm"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<XIcon />
|
||||
<span className="lib:sr-only">Close</span>
|
||||
<span className="fui:sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
)}
|
||||
</DialogPrimitive.Popup>
|
||||
@@ -82,7 +82,7 @@ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="dialog-header"
|
||||
className={cn("lib:flex lib:flex-col lib:gap-2", className)}
|
||||
className={cn("fui:flex fui:flex-col fui:gap-2", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -100,7 +100,7 @@ function DialogFooter({
|
||||
<div
|
||||
data-slot="dialog-footer"
|
||||
className={cn(
|
||||
"lib:flex lib:flex-col-reverse lib:gap-2 lib:sm:flex-row lib:sm:justify-end",
|
||||
"fui:flex fui:flex-col-reverse fui:gap-2 fui:sm:flex-row fui:sm:justify-end",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -120,7 +120,7 @@ function DialogTitle({ className, ...props }: DialogPrimitive.Title.Props) {
|
||||
<DialogPrimitive.Title
|
||||
data-slot="dialog-title"
|
||||
className={cn(
|
||||
"lib:font-heading lib:text-base lib:leading-none lib:font-medium",
|
||||
"fui:font-heading fui:text-base fui:leading-none fui:font-medium",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -136,7 +136,7 @@ function DialogDescription({
|
||||
<DialogPrimitive.Description
|
||||
data-slot="dialog-description"
|
||||
className={cn(
|
||||
"lib:text-sm lib:text-muted-foreground lib:*:[a]:underline lib:*:[a]:underline-offset-3 lib:*:[a]:hover:text-foreground",
|
||||
"fui:text-sm fui:text-muted-foreground fui:*:[a]:underline fui:*:[a]:underline-offset-3 fui:*:[a]:hover:text-foreground",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -36,7 +36,7 @@ function DrawerOverlay({
|
||||
<DrawerPrimitive.Overlay
|
||||
data-slot="drawer-overlay"
|
||||
className={cn(
|
||||
"lib:fixed lib:inset-0 lib:z-50 lib:bg-black/80 lib:supports-backdrop-filter:backdrop-blur-xs lib:data-open:animate-in lib:data-open:fade-in-0 lib:data-closed:animate-out lib:data-closed:fade-out-0",
|
||||
"fui:fixed fui:inset-0 fui:z-50 fui:bg-black/80 fui:supports-backdrop-filter:backdrop-blur-xs fui:data-open:animate-in fui:data-open:fade-in-0 fui:data-closed:animate-out fui:data-closed:fade-out-0",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -55,12 +55,12 @@ function DrawerContent({
|
||||
<DrawerPrimitive.Content
|
||||
data-slot="drawer-content"
|
||||
className={cn(
|
||||
"lib:group/drawer-content lib:fixed lib:z-50 lib:flex lib:h-auto lib:flex-col lib:bg-transparent lib:p-4 lib:text-sm lib:text-popover-foreground lib:before:absolute lib:before:inset-2 lib:before:-z-10 lib:before:rounded-4xl lib:before:border lib:before:border-border lib:before:bg-popover lib:data-[vaul-drawer-direction=bottom]:inset-x-0 lib:data-[vaul-drawer-direction=bottom]:bottom-0 lib:data-[vaul-drawer-direction=bottom]:mt-24 lib:data-[vaul-drawer-direction=bottom]:max-h-[80vh] lib:data-[vaul-drawer-direction=left]:inset-y-0 lib:data-[vaul-drawer-direction=left]:left-0 lib:data-[vaul-drawer-direction=left]:w-3/4 lib:data-[vaul-drawer-direction=right]:inset-y-0 lib:data-[vaul-drawer-direction=right]:right-0 lib:data-[vaul-drawer-direction=right]:w-3/4 lib:data-[vaul-drawer-direction=top]:inset-x-0 lib:data-[vaul-drawer-direction=top]:top-0 lib:data-[vaul-drawer-direction=top]:mb-24 lib:data-[vaul-drawer-direction=top]:max-h-[80vh] lib:data-[vaul-drawer-direction=left]:sm:max-w-sm lib:data-[vaul-drawer-direction=right]:sm:max-w-sm",
|
||||
"fui:group/drawer-content fui:fixed fui:z-50 fui:flex fui:h-auto fui:flex-col fui:bg-transparent fui:p-4 fui:text-sm fui:text-popover-foreground fui:before:absolute fui:before:inset-2 fui:before:-z-10 fui:before:rounded-4xl fui:before:border fui:before:border-border fui:before:bg-popover fui:data-[vaul-drawer-direction=bottom]:inset-x-0 fui:data-[vaul-drawer-direction=bottom]:bottom-0 fui:data-[vaul-drawer-direction=bottom]:mt-24 fui:data-[vaul-drawer-direction=bottom]:max-h-[80vh] fui:data-[vaul-drawer-direction=left]:inset-y-0 fui:data-[vaul-drawer-direction=left]:left-0 fui:data-[vaul-drawer-direction=left]:w-3/4 fui:data-[vaul-drawer-direction=right]:inset-y-0 fui:data-[vaul-drawer-direction=right]:right-0 fui:data-[vaul-drawer-direction=right]:w-3/4 fui:data-[vaul-drawer-direction=top]:inset-x-0 fui:data-[vaul-drawer-direction=top]:top-0 fui:data-[vaul-drawer-direction=top]:mb-24 fui:data-[vaul-drawer-direction=top]:max-h-[80vh] fui:data-[vaul-drawer-direction=left]:sm:max-w-sm fui:data-[vaul-drawer-direction=right]:sm:max-w-sm",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<div className="lib:mx-auto lib:mt-4 lib:hidden lib:h-1.5 lib:w-[100px] lib:shrink-0 lib:rounded-full lib:bg-muted lib:group-data-[vaul-drawer-direction=bottom]/drawer-content:block" />
|
||||
<div className="fui:mx-auto fui:mt-4 fui:hidden fui:h-1.5 fui:w-[100px] fui:shrink-0 fui:rounded-full fui:bg-muted fui:group-data-[vaul-drawer-direction=bottom]/drawer-content:block" />
|
||||
{children}
|
||||
</DrawerPrimitive.Content>
|
||||
</DrawerPortal>
|
||||
@@ -72,7 +72,7 @@ function DrawerHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||
<div
|
||||
data-slot="drawer-header"
|
||||
className={cn(
|
||||
"lib:flex lib:flex-col lib:gap-0.5 lib:p-4 lib:group-data-[vaul-drawer-direction=bottom]/drawer-content:text-center lib:group-data-[vaul-drawer-direction=top]/drawer-content:text-center lib:md:gap-1.5 lib:md:text-left",
|
||||
"fui:flex fui:flex-col fui:gap-0.5 fui:p-4 fui:group-data-[vaul-drawer-direction=bottom]/drawer-content:text-center fui:group-data-[vaul-drawer-direction=top]/drawer-content:text-center fui:md:gap-1.5 fui:md:text-left",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -85,7 +85,7 @@ function DrawerFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||
<div
|
||||
data-slot="drawer-footer"
|
||||
className={cn(
|
||||
"lib:mt-auto lib:flex lib:flex-col lib:gap-2 lib:p-4",
|
||||
"fui:mt-auto fui:flex fui:flex-col fui:gap-2 fui:p-4",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -101,7 +101,7 @@ function DrawerTitle({
|
||||
<DrawerPrimitive.Title
|
||||
data-slot="drawer-title"
|
||||
className={cn(
|
||||
"lib:font-heading lib:text-base lib:font-medium lib:text-foreground",
|
||||
"fui:font-heading fui:text-base fui:font-medium fui:text-foreground",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -116,7 +116,7 @@ function DrawerDescription({
|
||||
return (
|
||||
<DrawerPrimitive.Description
|
||||
data-slot="drawer-description"
|
||||
className={cn("lib:text-sm lib:text-muted-foreground", className)}
|
||||
className={cn("fui:text-sm fui:text-muted-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -30,7 +30,7 @@ function DropdownMenuContent({
|
||||
return (
|
||||
<MenuPrimitive.Portal>
|
||||
<MenuPrimitive.Positioner
|
||||
className="lib:isolate lib:z-50 lib:outline-none"
|
||||
className="fui:isolate fui:z-50 fui:outline-none"
|
||||
align={align}
|
||||
alignOffset={alignOffset}
|
||||
side={side}
|
||||
@@ -39,7 +39,7 @@ function DropdownMenuContent({
|
||||
<MenuPrimitive.Popup
|
||||
data-slot="dropdown-menu-content"
|
||||
className={cn(
|
||||
"lib: lib: lib:z-50 lib:max-h-(--available-height) lib:w-(--anchor-width) lib:min-w-48 lib:origin-(--transform-origin) lib:overflow-x-hidden lib:overflow-y-auto lib:rounded-2xl lib:bg-popover lib:p-1 lib:text-popover-foreground lib:shadow-2xl lib:ring-1 lib:ring-foreground/5 lib:duration-100 lib:outline-none lib:data-[side=bottom]:slide-in-from-top-2 lib:data-[side=inline-end]:slide-in-from-left-2 lib:data-[side=inline-start]:slide-in-from-right-2 lib:data-[side=left]:slide-in-from-right-2 lib:data-[side=right]:slide-in-from-left-2 lib:data-[side=top]:slide-in-from-bottom-2 lib:dark:ring-foreground/10 lib:data-open:animate-in lib:data-open:fade-in-0 lib:data-open:zoom-in-95 lib:data-closed:animate-out lib:data-closed:overflow-hidden lib:data-closed:fade-out-0 lib:data-closed:zoom-out-95 animate-none! relative bg-popover/70 before:pointer-events-none before:absolute before:inset-0 before:-z-1 before:rounded-[inherit] before:backdrop-blur-2xl before:backdrop-saturate-150 **:data-[slot$=-item]:focus:bg-foreground/10 **:data-[slot$=-item]:data-highlighted:bg-foreground/10 **:data-[slot$=-separator]:bg-foreground/5 **:data-[slot$=-trigger]:focus:bg-foreground/10 **:data-[slot$=-trigger]:aria-expanded:bg-foreground/10! **:data-[variant=destructive]:focus:bg-foreground/10! **:data-[variant=destructive]:text-accent-foreground! **:data-[variant=destructive]:**:text-accent-foreground!",
|
||||
"fui: fui: fui:z-50 fui:max-h-(--available-height) fui:w-(--anchor-width) fui:min-w-48 fui:origin-(--transform-origin) fui:overflow-x-hidden fui:overflow-y-auto fui:rounded-2xl fui:bg-popover fui:p-1 fui:text-popover-foreground fui:shadow-2xl fui:ring-1 fui:ring-foreground/5 fui:duration-100 fui:outline-none fui:data-[side=bottom]:slide-in-from-top-2 fui:data-[side=inline-end]:slide-in-from-left-2 fui:data-[side=inline-start]:slide-in-from-right-2 fui:data-[side=left]:slide-in-from-right-2 fui:data-[side=right]:slide-in-from-left-2 fui:data-[side=top]:slide-in-from-bottom-2 fui:dark:ring-foreground/10 fui:data-open:animate-in fui:data-open:fade-in-0 fui:data-open:zoom-in-95 fui:data-closed:animate-out fui:data-closed:overflow-hidden fui:data-closed:fade-out-0 fui:data-closed:zoom-out-95 animate-none! relative bg-popover/70 before:pointer-events-none before:absolute before:inset-0 before:-z-1 before:rounded-[inherit] before:backdrop-blur-2xl before:backdrop-saturate-150 **:data-[slot$=-item]:focus:bg-foreground/10 **:data-[slot$=-item]:data-highlighted:bg-foreground/10 **:data-[slot$=-separator]:bg-foreground/5 **:data-[slot$=-trigger]:focus:bg-foreground/10 **:data-[slot$=-trigger]:aria-expanded:bg-foreground/10! **:data-[variant=destructive]:focus:bg-foreground/10! **:data-[variant=destructive]:text-accent-foreground! **:data-[variant=destructive]:**:text-accent-foreground!",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -65,7 +65,7 @@ function DropdownMenuLabel({
|
||||
data-slot="dropdown-menu-label"
|
||||
data-inset={inset}
|
||||
className={cn(
|
||||
"lib:px-3 lib:py-2.5 lib:text-xs lib:text-muted-foreground lib:data-inset:pl-9.5",
|
||||
"fui:px-3 fui:py-2.5 fui:text-xs fui:text-muted-foreground fui:data-inset:pl-9.5",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -88,7 +88,7 @@ function DropdownMenuItem({
|
||||
data-inset={inset}
|
||||
data-variant={variant}
|
||||
className={cn(
|
||||
"lib:group/dropdown-menu-item lib:relative lib:flex lib:cursor-default lib:items-center lib:gap-2.5 lib:rounded-xl lib:px-3 lib:py-2 lib:text-sm lib:outline-hidden lib:select-none lib:focus:bg-accent lib:focus:text-accent-foreground lib:not-data-[variant=destructive]:focus:**:text-accent-foreground lib:data-inset:pl-9.5 lib:data-[variant=destructive]:text-destructive lib:data-[variant=destructive]:focus:bg-destructive/10 lib:data-[variant=destructive]:focus:text-destructive lib:dark:data-[variant=destructive]:focus:bg-destructive/20 lib:data-disabled:pointer-events-none lib:data-disabled:opacity-50 lib:[&_svg]:pointer-events-none lib:[&_svg]:shrink-0 lib:[&_svg:not([class*=size-])]:size-4 lib:data-[variant=destructive]:*:[svg]:text-destructive",
|
||||
"fui:group/dropdown-menu-item fui:relative fui:flex fui:cursor-default fui:items-center fui:gap-2.5 fui:rounded-xl fui:px-3 fui:py-2 fui:text-sm fui:outline-hidden fui:select-none fui:focus:bg-accent fui:focus:text-accent-foreground fui:not-data-[variant=destructive]:focus:**:text-accent-foreground fui:data-inset:pl-9.5 fui:data-[variant=destructive]:text-destructive fui:data-[variant=destructive]:focus:bg-destructive/10 fui:data-[variant=destructive]:focus:text-destructive fui:dark:data-[variant=destructive]:focus:bg-destructive/20 fui:data-disabled:pointer-events-none fui:data-disabled:opacity-50 fui:[&_svg]:pointer-events-none fui:[&_svg]:shrink-0 fui:[&_svg:not([class*=size-])]:size-4 fui:data-[variant=destructive]:*:[svg]:text-destructive",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -113,13 +113,13 @@ function DropdownMenuSubTrigger({
|
||||
data-slot="dropdown-menu-sub-trigger"
|
||||
data-inset={inset}
|
||||
className={cn(
|
||||
"lib:flex lib:cursor-default lib:items-center lib:gap-2 lib:rounded-xl lib:px-3 lib:py-2 lib:text-sm lib:outline-hidden lib:select-none lib:focus:bg-accent lib:focus:text-accent-foreground lib:not-data-[variant=destructive]:focus:**:text-accent-foreground lib:data-inset:pl-9.5 lib:data-popup-open:bg-accent lib:data-popup-open:text-accent-foreground lib:data-open:bg-accent lib:data-open:text-accent-foreground lib:[&_svg]:pointer-events-none lib:[&_svg]:shrink-0 lib:[&_svg:not([class*=size-])]:size-4",
|
||||
"fui:flex fui:cursor-default fui:items-center fui:gap-2 fui:rounded-xl fui:px-3 fui:py-2 fui:text-sm fui:outline-hidden fui:select-none fui:focus:bg-accent fui:focus:text-accent-foreground fui:not-data-[variant=destructive]:focus:**:text-accent-foreground fui:data-inset:pl-9.5 fui:data-popup-open:bg-accent fui:data-popup-open:text-accent-foreground fui:data-open:bg-accent fui:data-open:text-accent-foreground fui:[&_svg]:pointer-events-none fui:[&_svg]:shrink-0 fui:[&_svg:not([class*=size-])]:size-4",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronRightIcon className="lib:ml-auto" />
|
||||
<ChevronRightIcon className="fui:ml-auto" />
|
||||
</MenuPrimitive.SubmenuTrigger>
|
||||
);
|
||||
}
|
||||
@@ -136,7 +136,7 @@ function DropdownMenuSubContent({
|
||||
<DropdownMenuContent
|
||||
data-slot="dropdown-menu-sub-content"
|
||||
className={cn(
|
||||
"lib: lib: lib:w-auto lib:min-w-36 lib:rounded-2xl lib:bg-popover lib:p-1 lib:text-popover-foreground lib:shadow-2xl lib:ring-1 lib:ring-foreground/5 lib:duration-100 lib:data-[side=bottom]:slide-in-from-top-2 lib:data-[side=left]:slide-in-from-right-2 lib:data-[side=right]:slide-in-from-left-2 lib:data-[side=top]:slide-in-from-bottom-2 lib:data-open:animate-in lib:data-open:fade-in-0 lib:data-open:zoom-in-95 lib:data-closed:animate-out lib:data-closed:fade-out-0 lib:data-closed:zoom-out-95 animate-none! relative bg-popover/70 before:pointer-events-none before:absolute before:inset-0 before:-z-1 before:rounded-[inherit] before:backdrop-blur-2xl before:backdrop-saturate-150 **:data-[slot$=-item]:focus:bg-foreground/10 **:data-[slot$=-item]:data-highlighted:bg-foreground/10 **:data-[slot$=-separator]:bg-foreground/5 **:data-[slot$=-trigger]:focus:bg-foreground/10 **:data-[slot$=-trigger]:aria-expanded:bg-foreground/10! **:data-[variant=destructive]:focus:bg-foreground/10! **:data-[variant=destructive]:text-accent-foreground! **:data-[variant=destructive]:**:text-accent-foreground!",
|
||||
"fui: fui: fui:w-auto fui:min-w-36 fui:rounded-2xl fui:bg-popover fui:p-1 fui:text-popover-foreground fui:shadow-2xl fui:ring-1 fui:ring-foreground/5 fui:duration-100 fui:data-[side=bottom]:slide-in-from-top-2 fui:data-[side=left]:slide-in-from-right-2 fui:data-[side=right]:slide-in-from-left-2 fui:data-[side=top]:slide-in-from-bottom-2 fui:data-open:animate-in fui:data-open:fade-in-0 fui:data-open:zoom-in-95 fui:data-closed:animate-out fui:data-closed:fade-out-0 fui:data-closed:zoom-out-95 animate-none! relative bg-popover/70 before:pointer-events-none before:absolute before:inset-0 before:-z-1 before:rounded-[inherit] before:backdrop-blur-2xl before:backdrop-saturate-150 **:data-[slot$=-item]:focus:bg-foreground/10 **:data-[slot$=-item]:data-highlighted:bg-foreground/10 **:data-[slot$=-separator]:bg-foreground/5 **:data-[slot$=-trigger]:focus:bg-foreground/10 **:data-[slot$=-trigger]:aria-expanded:bg-foreground/10! **:data-[variant=destructive]:focus:bg-foreground/10! **:data-[variant=destructive]:text-accent-foreground! **:data-[variant=destructive]:**:text-accent-foreground!",
|
||||
className,
|
||||
)}
|
||||
align={align}
|
||||
@@ -162,14 +162,14 @@ function DropdownMenuCheckboxItem({
|
||||
data-slot="dropdown-menu-checkbox-item"
|
||||
data-inset={inset}
|
||||
className={cn(
|
||||
"lib:relative lib:flex lib:cursor-default lib:items-center lib:gap-2.5 lib:rounded-xl lib:py-2 lib:pr-8 lib:pl-3 lib:text-sm lib:outline-hidden lib:select-none lib:focus:bg-accent lib:focus:text-accent-foreground lib:focus:**:text-accent-foreground lib:data-inset:pl-9.5 lib:data-disabled:pointer-events-none lib:data-disabled:opacity-50 lib:[&_svg]:pointer-events-none lib:[&_svg]:shrink-0 lib:[&_svg:not([class*=size-])]:size-4",
|
||||
"fui:relative fui:flex fui:cursor-default fui:items-center fui:gap-2.5 fui:rounded-xl fui:py-2 fui:pr-8 fui:pl-3 fui:text-sm fui:outline-hidden fui:select-none fui:focus:bg-accent fui:focus:text-accent-foreground fui:focus:**:text-accent-foreground fui:data-inset:pl-9.5 fui:data-disabled:pointer-events-none fui:data-disabled:opacity-50 fui:[&_svg]:pointer-events-none fui:[&_svg]:shrink-0 fui:[&_svg:not([class*=size-])]:size-4",
|
||||
className,
|
||||
)}
|
||||
checked={checked}
|
||||
{...props}
|
||||
>
|
||||
<span
|
||||
className="lib:pointer-events-none lib:absolute lib:right-2 lib:flex lib:items-center lib:justify-center"
|
||||
className="fui:pointer-events-none fui:absolute fui:right-2 fui:flex fui:items-center fui:justify-center"
|
||||
data-slot="dropdown-menu-checkbox-item-indicator"
|
||||
>
|
||||
<MenuPrimitive.CheckboxItemIndicator>
|
||||
@@ -203,13 +203,13 @@ function DropdownMenuRadioItem({
|
||||
data-slot="dropdown-menu-radio-item"
|
||||
data-inset={inset}
|
||||
className={cn(
|
||||
"lib:relative lib:flex lib:cursor-default lib:items-center lib:gap-2.5 lib:rounded-xl lib:py-2 lib:pr-8 lib:pl-3 lib:text-sm lib:outline-hidden lib:select-none lib:focus:bg-accent lib:focus:text-accent-foreground lib:focus:**:text-accent-foreground lib:data-inset:pl-9.5 lib:data-disabled:pointer-events-none lib:data-disabled:opacity-50 lib:[&_svg]:pointer-events-none lib:[&_svg]:shrink-0 lib:[&_svg:not([class*=size-])]:size-4",
|
||||
"fui:relative fui:flex fui:cursor-default fui:items-center fui:gap-2.5 fui:rounded-xl fui:py-2 fui:pr-8 fui:pl-3 fui:text-sm fui:outline-hidden fui:select-none fui:focus:bg-accent fui:focus:text-accent-foreground fui:focus:**:text-accent-foreground fui:data-inset:pl-9.5 fui:data-disabled:pointer-events-none fui:data-disabled:opacity-50 fui:[&_svg]:pointer-events-none fui:[&_svg]:shrink-0 fui:[&_svg:not([class*=size-])]:size-4",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span
|
||||
className="lib:pointer-events-none lib:absolute lib:right-2 lib:flex lib:items-center lib:justify-center"
|
||||
className="fui:pointer-events-none fui:absolute fui:right-2 fui:flex fui:items-center fui:justify-center"
|
||||
data-slot="dropdown-menu-radio-item-indicator"
|
||||
>
|
||||
<MenuPrimitive.RadioItemIndicator>
|
||||
@@ -228,7 +228,7 @@ function DropdownMenuSeparator({
|
||||
return (
|
||||
<MenuPrimitive.Separator
|
||||
data-slot="dropdown-menu-separator"
|
||||
className={cn("lib:-mx-1 lib:my-1 lib:h-px lib:bg-border/50", className)}
|
||||
className={cn("fui:-mx-1 fui:my-1 fui:h-px fui:bg-border/50", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -242,7 +242,7 @@ function DropdownMenuShortcut({
|
||||
<span
|
||||
data-slot="dropdown-menu-shortcut"
|
||||
className={cn(
|
||||
"lib:ml-auto lib:text-xs lib:tracking-widest lib:text-muted-foreground lib:group-focus/dropdown-menu-item:text-accent-foreground",
|
||||
"fui:ml-auto fui:text-xs fui:tracking-widest fui:text-muted-foreground fui:group-focus/dropdown-menu-item:text-accent-foreground",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -6,7 +6,7 @@ function Empty({ className, ...props }: React.ComponentProps<"div">) {
|
||||
<div
|
||||
data-slot="empty"
|
||||
className={cn(
|
||||
"lib:flex lib:w-full lib:min-w-0 lib:flex-1 lib:flex-col lib:items-center lib:justify-center lib:gap-4 lib:rounded-lg lib:border-dashed lib:p-12 lib:text-center lib:text-balance",
|
||||
"fui:flex fui:w-full fui:min-w-0 fui:flex-1 fui:flex-col fui:items-center fui:justify-center fui:gap-4 fui:rounded-lg fui:border-dashed fui:p-12 fui:text-center fui:text-balance",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -19,7 +19,7 @@ function EmptyHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||
<div
|
||||
data-slot="empty-header"
|
||||
className={cn(
|
||||
"lib:flex lib:max-w-sm lib:flex-col lib:items-center lib:gap-2",
|
||||
"fui:flex fui:max-w-sm fui:flex-col fui:items-center fui:gap-2",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -28,12 +28,12 @@ function EmptyHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||
}
|
||||
|
||||
const emptyMediaVariants = cva(
|
||||
"lib:mb-2 lib:flex lib:shrink-0 lib:items-center lib:justify-center lib:[&_svg]:pointer-events-none lib:[&_svg]:shrink-0",
|
||||
"fui:mb-2 fui:flex fui:shrink-0 fui:items-center fui:justify-center fui:[&_svg]:pointer-events-none fui:[&_svg]:shrink-0",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "lib:bg-transparent",
|
||||
icon: "lib:flex lib:size-10 lib:shrink-0 lib:items-center lib:justify-center lib:rounded-lg lib:bg-muted lib:text-foreground lib:[&_svg:not([class*=size-])]:size-6",
|
||||
default: "fui:bg-transparent",
|
||||
icon: "fui:flex fui:size-10 fui:shrink-0 fui:items-center fui:justify-center fui:rounded-lg fui:bg-muted fui:text-foreground fui:[&_svg:not([class*=size-])]:size-6",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
@@ -62,7 +62,7 @@ function EmptyTitle({ className, ...props }: React.ComponentProps<"div">) {
|
||||
<div
|
||||
data-slot="empty-title"
|
||||
className={cn(
|
||||
"lib:font-heading lib:text-lg lib:font-medium lib:tracking-tight",
|
||||
"fui:font-heading fui:text-lg fui:font-medium fui:tracking-tight",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -75,7 +75,7 @@ function EmptyDescription({ className, ...props }: React.ComponentProps<"p">) {
|
||||
<div
|
||||
data-slot="empty-description"
|
||||
className={cn(
|
||||
"lib:text-sm/relaxed lib:text-muted-foreground lib:[&>a]:underline lib:[&>a]:underline-offset-4 lib:[&>a:hover]:text-primary",
|
||||
"fui:text-sm/relaxed fui:text-muted-foreground fui:[&>a]:underline fui:[&>a]:underline-offset-4 fui:[&>a:hover]:text-primary",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -88,7 +88,7 @@ function EmptyContent({ className, ...props }: React.ComponentProps<"div">) {
|
||||
<div
|
||||
data-slot="empty-content"
|
||||
className={cn(
|
||||
"lib:flex lib:w-full lib:max-w-sm lib:min-w-0 lib:flex-col lib:items-center lib:gap-4 lib:text-sm lib:text-balance",
|
||||
"fui:flex fui:w-full fui:max-w-sm fui:min-w-0 fui:flex-col fui:items-center fui:gap-4 fui:text-sm fui:text-balance",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
+19
-19
@@ -12,7 +12,7 @@ function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) {
|
||||
<fieldset
|
||||
data-slot="field-set"
|
||||
className={cn(
|
||||
"lib:flex lib:flex-col lib:gap-6 lib:has-[>[data-slot=checkbox-group]]:gap-3 lib:has-[>[data-slot=radio-group]]:gap-3",
|
||||
"fui:flex fui:flex-col fui:gap-6 fui:has-[>[data-slot=checkbox-group]]:gap-3 fui:has-[>[data-slot=radio-group]]:gap-3",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -30,7 +30,7 @@ function FieldLegend({
|
||||
data-slot="field-legend"
|
||||
data-variant={variant}
|
||||
className={cn(
|
||||
"lib:mb-3 lib:font-medium lib:data-[variant=label]:text-sm lib:data-[variant=legend]:text-base",
|
||||
"fui:mb-3 fui:font-medium fui:data-[variant=label]:text-sm fui:data-[variant=legend]:text-base",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -43,7 +43,7 @@ function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
|
||||
<div
|
||||
data-slot="field-group"
|
||||
className={cn(
|
||||
"lib:group/field-group lib:@container/field-group lib:flex lib:w-full lib:flex-col lib:gap-7 lib:data-[slot=checkbox-group]:gap-3 lib:*:data-[slot=field-group]:gap-4",
|
||||
"fui:group/field-group fui:@container/field-group fui:flex fui:w-full fui:flex-col fui:gap-7 fui:data-[slot=checkbox-group]:gap-3 fui:*:data-[slot=field-group]:gap-4",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -52,15 +52,15 @@ function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
|
||||
}
|
||||
|
||||
const fieldVariants = cva(
|
||||
"lib:group/field lib:flex lib:w-full lib:gap-3 lib:data-[invalid=true]:text-destructive",
|
||||
"fui:group/field fui:flex fui:w-full fui:gap-3 fui:data-[invalid=true]:text-destructive",
|
||||
{
|
||||
variants: {
|
||||
orientation: {
|
||||
vertical: "lib:flex-col lib:*:w-full lib:[&>.sr-only]:w-auto",
|
||||
vertical: "fui:flex-col fui:*:w-full fui:[&>.sr-only]:w-auto",
|
||||
horizontal:
|
||||
"lib:flex-row lib:items-center lib:has-[>[data-slot=field-content]]:items-start lib:*:data-[slot=field-label]:flex-auto lib:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
|
||||
"fui:flex-row fui:items-center fui:has-[>[data-slot=field-content]]:items-start fui:*:data-[slot=field-label]:flex-auto fui:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
|
||||
responsive:
|
||||
"lib:flex-col lib:*:w-full lib:@md/field-group:flex-row lib:@md/field-group:items-center lib:@md/field-group:*:w-auto lib:@md/field-group:has-[>[data-slot=field-content]]:items-start lib:@md/field-group:*:data-[slot=field-label]:flex-auto lib:[&>.sr-only]:w-auto lib:@md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
|
||||
"fui:flex-col fui:*:w-full fui:@md/field-group:flex-row fui:@md/field-group:items-center fui:@md/field-group:*:w-auto fui:@md/field-group:has-[>[data-slot=field-content]]:items-start fui:@md/field-group:*:data-[slot=field-label]:flex-auto fui:[&>.sr-only]:w-auto fui:@md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
@@ -90,7 +90,7 @@ function FieldContent({ className, ...props }: React.ComponentProps<"div">) {
|
||||
<div
|
||||
data-slot="field-content"
|
||||
className={cn(
|
||||
"lib:group/field-content lib:flex lib:flex-1 lib:flex-col lib:gap-1 lib:leading-snug",
|
||||
"fui:group/field-content fui:flex fui:flex-1 fui:flex-col fui:gap-1 fui:leading-snug",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -106,8 +106,8 @@ function FieldLabel({
|
||||
<Label
|
||||
data-slot="field-label"
|
||||
className={cn(
|
||||
"lib:group/field-label lib:peer/field-label lib:flex lib:w-fit lib:gap-2 lib:leading-snug lib:group-data-[disabled=true]/field:opacity-50 lib:has-data-checked:border-primary/30 lib:has-data-checked:bg-primary/5 lib:has-[>[data-slot=field]]:rounded-xl lib:has-[>[data-slot=field]]:border lib:has-[>[data-slot=field]]:not-has-[:disabled,[data-disabled]]:hover:bg-input/40 lib:has-[>[data-slot=field]]:has-[:focus-visible]:border-ring lib:has-[>[data-slot=field]]:has-[:focus-visible]:ring-[3px] lib:has-[>[data-slot=field]]:has-[:focus-visible]:ring-ring/50 lib:*:data-[slot=field]:p-4 lib:dark:has-data-checked:border-primary/20 lib:dark:has-data-checked:bg-primary/10",
|
||||
"lib:has-[>[data-slot=field]]:w-full lib:has-[>[data-slot=field]]:flex-col",
|
||||
"fui:group/field-label fui:peer/field-label fui:flex fui:w-fit fui:gap-2 fui:leading-snug fui:group-data-[disabled=true]/field:opacity-50 fui:has-data-checked:border-primary/30 fui:has-data-checked:bg-primary/5 fui:has-[>[data-slot=field]]:rounded-xl fui:has-[>[data-slot=field]]:border fui:has-[>[data-slot=field]]:not-has-[:disabled,[data-disabled]]:hover:bg-input/40 fui:has-[>[data-slot=field]]:has-[:focus-visible]:border-ring fui:has-[>[data-slot=field]]:has-[:focus-visible]:ring-[3px] fui:has-[>[data-slot=field]]:has-[:focus-visible]:ring-ring/50 fui:*:data-[slot=field]:p-4 fui:dark:has-data-checked:border-primary/20 fui:dark:has-data-checked:bg-primary/10",
|
||||
"fui:has-[>[data-slot=field]]:w-full fui:has-[>[data-slot=field]]:flex-col",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -120,7 +120,7 @@ function FieldTitle({ className, ...props }: React.ComponentProps<"div">) {
|
||||
<div
|
||||
data-slot="field-label"
|
||||
className={cn(
|
||||
"lib:flex lib:w-fit lib:items-center lib:gap-2 lib:text-sm lib:font-medium lib:group-data-[disabled=true]/field:opacity-50",
|
||||
"fui:flex fui:w-fit fui:items-center fui:gap-2 fui:text-sm fui:font-medium fui:group-data-[disabled=true]/field:opacity-50",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -133,9 +133,9 @@ function FieldDescription({ className, ...props }: React.ComponentProps<"p">) {
|
||||
<p
|
||||
data-slot="field-description"
|
||||
className={cn(
|
||||
"lib:text-left lib:text-sm lib:leading-normal lib:font-normal lib:text-muted-foreground lib:group-has-data-horizontal/field:text-balance lib:[[data-variant=legend]+&]:-mt-1.5",
|
||||
"lib:last:mt-0 lib:nth-last-2:-mt-1",
|
||||
"lib:[&>a]:underline lib:[&>a]:underline-offset-4 lib:[&>a:hover]:text-primary",
|
||||
"fui:text-left fui:text-sm fui:leading-normal fui:font-normal fui:text-muted-foreground fui:group-has-data-horizontal/field:text-balance fui:[[data-variant=legend]+&]:-mt-1.5",
|
||||
"fui:last:mt-0 fui:nth-last-2:-mt-1",
|
||||
"fui:[&>a]:underline fui:[&>a]:underline-offset-4 fui:[&>a:hover]:text-primary",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -155,15 +155,15 @@ function FieldSeparator({
|
||||
data-slot="field-separator"
|
||||
data-content={!!children}
|
||||
className={cn(
|
||||
"lib:relative lib:-my-2 lib:h-5 lib:text-sm lib:group-data-[variant=outline]/field-group:-mb-2",
|
||||
"fui:relative fui:-my-2 fui:h-5 fui:text-sm fui:group-data-[variant=outline]/field-group:-mb-2",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<Separator className="lib:absolute lib:inset-0 lib:top-1/2" />
|
||||
<Separator className="fui:absolute fui:inset-0 fui:top-1/2" />
|
||||
{children && (
|
||||
<span
|
||||
className="lib:relative lib:mx-auto lib:block lib:w-fit lib:bg-background lib:px-2 lib:text-muted-foreground"
|
||||
className="fui:relative fui:mx-auto fui:block fui:w-fit fui:bg-background fui:px-2 fui:text-muted-foreground"
|
||||
data-slot="field-separator-content"
|
||||
>
|
||||
{children}
|
||||
@@ -199,7 +199,7 @@ function FieldError({
|
||||
}
|
||||
|
||||
return (
|
||||
<ul className="lib:ml-4 lib:flex lib:list-disc lib:flex-col lib:gap-1">
|
||||
<ul className="fui:ml-4 fui:flex fui:list-disc fui:flex-col fui:gap-1">
|
||||
{uniqueErrors.map(
|
||||
(error, index) =>
|
||||
error?.message && <li key={index}>{error.message}</li>,
|
||||
@@ -217,7 +217,7 @@ function FieldError({
|
||||
role="alert"
|
||||
data-slot="field-error"
|
||||
className={cn(
|
||||
"lib:text-sm lib:font-normal lib:text-destructive",
|
||||
"fui:text-sm fui:font-normal fui:text-destructive",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -0,0 +1,427 @@
|
||||
import { cn } from "cn";
|
||||
import { format } from "date-fns";
|
||||
import { CalendarIcon, SearchIcon, XIcon } from "lucide-react";
|
||||
import * as React from "react";
|
||||
import { Button } from "@/components/button";
|
||||
import { Calendar } from "@/components/calendar";
|
||||
import { Checkbox } from "@/components/checkbox";
|
||||
import {
|
||||
InputGroup,
|
||||
InputGroupAddon,
|
||||
InputGroupInput,
|
||||
} from "@/components/input-group";
|
||||
import { Label } from "@/components/label";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/popover";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/select";
|
||||
|
||||
/**
|
||||
* Filter controls for a listing, meant for `DataTable`'s `toolbar` slot.
|
||||
*
|
||||
* Every control is named by a label above it. A filter's *value* is a poor
|
||||
* substitute for its name — a row of dropdowns all reading "Any" says nothing
|
||||
* about what any of them narrow — so the label carries the name and the
|
||||
* control carries only the value.
|
||||
*
|
||||
* The controls are uncontrolled as to *when* they apply: each calls back on
|
||||
* change, and the caller decides whether that hits a server immediately or is
|
||||
* debounced. Text input almost always wants debouncing.
|
||||
*/
|
||||
|
||||
type FilterOption<T extends string = string> = {
|
||||
value: T;
|
||||
label: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* The row of filters.
|
||||
*
|
||||
* Belongs in `DataTable`'s `toolbar` so the filters share a line with the
|
||||
* table's column menu: given a `toolbar`, `DataTable` renders that row whether
|
||||
* or not anything else is in it, and a row holding only the column button
|
||||
* reads as a stray gap.
|
||||
*
|
||||
* `flex-1` claims the space beside it, pushing the column menu to the far end.
|
||||
* `items-end` puts every control on a shared bottom edge, so a checkbox with
|
||||
* no label above it still lines up with the labelled selects.
|
||||
*/
|
||||
function FilterBar({
|
||||
className,
|
||||
children,
|
||||
actions,
|
||||
}: {
|
||||
className?: string;
|
||||
children: React.ReactNode;
|
||||
actions?: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
data-slot="filter-bar"
|
||||
className={cn(
|
||||
"fui:flex fui:flex-1 fui:flex-wrap fui:items-end fui:gap-3",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
{actions ? (
|
||||
<div className="fui:ml-auto fui:flex fui:items-end fui:gap-2">
|
||||
{actions}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/** A labelled filter control. The label sits above and is bound to the input. */
|
||||
function FilterField({
|
||||
label,
|
||||
htmlFor,
|
||||
className,
|
||||
children,
|
||||
}: {
|
||||
label: string;
|
||||
htmlFor?: string;
|
||||
className?: string;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
data-slot="filter-field"
|
||||
className={cn("fui:flex fui:flex-col fui:gap-1", className)}
|
||||
>
|
||||
<Label
|
||||
htmlFor={htmlFor}
|
||||
className="fui:text-xs fui:font-medium fui:whitespace-nowrap fui:text-muted-foreground"
|
||||
>
|
||||
{label}
|
||||
</Label>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/** Free-text filter, with the same search affordance `DataTable` uses. */
|
||||
function FilterSearch({
|
||||
label = "Search",
|
||||
value,
|
||||
onValueChange,
|
||||
placeholder,
|
||||
className,
|
||||
}: {
|
||||
label?: string;
|
||||
value: string;
|
||||
onValueChange: (value: string) => void;
|
||||
placeholder?: string;
|
||||
className?: string;
|
||||
}) {
|
||||
const id = React.useId();
|
||||
|
||||
return (
|
||||
<FilterField
|
||||
label={label}
|
||||
htmlFor={id}
|
||||
className={cn("fui:w-64", className)}
|
||||
>
|
||||
<InputGroup>
|
||||
<InputGroupAddon>
|
||||
<SearchIcon />
|
||||
</InputGroupAddon>
|
||||
<InputGroupInput
|
||||
id={id}
|
||||
value={value}
|
||||
placeholder={placeholder}
|
||||
onChange={(event) => onValueChange(event.target.value)}
|
||||
/>
|
||||
</InputGroup>
|
||||
</FilterField>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Single-choice filter.
|
||||
*
|
||||
* Derives base-ui's `items` map from `options`, which is what makes the
|
||||
* trigger show the selected option's *label*. Without it `Select.Value`
|
||||
* renders the raw value, so an enum filter reads "multibuy" rather than
|
||||
* "Multibuy".
|
||||
*/
|
||||
function FilterSelect<T extends string>({
|
||||
label,
|
||||
value,
|
||||
onValueChange,
|
||||
options,
|
||||
placeholder = "Any",
|
||||
className,
|
||||
}: {
|
||||
label: string;
|
||||
value: T;
|
||||
onValueChange: (value: T) => void;
|
||||
options: ReadonlyArray<FilterOption<T>>;
|
||||
placeholder?: string;
|
||||
className?: string;
|
||||
}) {
|
||||
const id = React.useId();
|
||||
const items = React.useMemo(
|
||||
() =>
|
||||
Object.fromEntries(options.map((option) => [option.value, option.label])),
|
||||
[options],
|
||||
);
|
||||
|
||||
return (
|
||||
<FilterField label={label} htmlFor={id}>
|
||||
<Select
|
||||
items={items}
|
||||
value={value}
|
||||
onValueChange={(next) => onValueChange(next as T)}
|
||||
>
|
||||
<SelectTrigger id={id} size="sm" className={cn("fui:w-44", className)}>
|
||||
<SelectValue placeholder={placeholder} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
{options.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FilterField>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* A period, with either bound optional.
|
||||
*
|
||||
* Both ends are independent: "since 1 March", "up to 31 March" and "1–31
|
||||
* March" are all expressible, and so is neither.
|
||||
*/
|
||||
type FilterPeriod = {
|
||||
from?: Date;
|
||||
to?: Date;
|
||||
};
|
||||
|
||||
function formatBound(date: Date) {
|
||||
return format(date, "d MMM yyyy");
|
||||
}
|
||||
|
||||
function describePeriod(period: FilterPeriod | undefined, placeholder: string) {
|
||||
if (period?.from && period.to) {
|
||||
return `${formatBound(period.from)} – ${formatBound(period.to)}`;
|
||||
}
|
||||
if (period?.from) return `From ${formatBound(period.from)}`;
|
||||
if (period?.to) return `Until ${formatBound(period.to)}`;
|
||||
return placeholder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Period filter: one control for a span, in place of a "from" box and a "to"
|
||||
* box.
|
||||
*
|
||||
* Two calendars in single-date mode rather than one in range mode, because a
|
||||
* range calendar can only be drawn start-then-end: it cannot express "up to
|
||||
* this date" with no start, and it forces a start-only pick to collapse into a
|
||||
* single day. Here each bound is set and cleared on its own.
|
||||
*
|
||||
* Each calendar disables the days that would invert the period, so an
|
||||
* unusable span cannot be selected in the first place.
|
||||
*
|
||||
* Values are exchanged as `Date`s. A caller storing the period in a URL or a
|
||||
* query converts at its own boundary, since the format that belongs there is
|
||||
* the caller's concern.
|
||||
*/
|
||||
function FilterDateRange({
|
||||
label,
|
||||
value,
|
||||
onValueChange,
|
||||
placeholder = "Any time",
|
||||
className,
|
||||
}: {
|
||||
label: string;
|
||||
value: FilterPeriod | undefined;
|
||||
onValueChange: (period: FilterPeriod | undefined) => void;
|
||||
placeholder?: string;
|
||||
className?: string;
|
||||
}) {
|
||||
const id = React.useId();
|
||||
|
||||
const update = (next: FilterPeriod) =>
|
||||
onValueChange(next.from || next.to ? next : undefined);
|
||||
|
||||
return (
|
||||
<FilterField label={label} htmlFor={id}>
|
||||
<Popover>
|
||||
<PopoverTrigger
|
||||
render={
|
||||
<Button
|
||||
id={id}
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
// `font-normal` and the muted empty state make it read as a
|
||||
// field showing a value, not as an action.
|
||||
className={cn(
|
||||
"fui:w-64 fui:justify-start fui:font-normal",
|
||||
!value?.from && !value?.to && "fui:text-muted-foreground",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<CalendarIcon />
|
||||
{describePeriod(value, placeholder)}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<PopoverContent align="start" className="fui:w-auto fui:p-0">
|
||||
<div className="fui:flex fui:flex-col fui:divide-y fui:sm:flex-row fui:sm:divide-x fui:sm:divide-y-0">
|
||||
<PeriodBound
|
||||
title="From"
|
||||
selected={value?.from}
|
||||
defaultMonth={value?.from ?? value?.to}
|
||||
disabled={value?.to ? { after: value.to } : undefined}
|
||||
onSelect={(from) => update({ from, to: value?.to })}
|
||||
/>
|
||||
<PeriodBound
|
||||
title="To"
|
||||
selected={value?.to}
|
||||
defaultMonth={value?.to ?? value?.from}
|
||||
disabled={value?.from ? { before: value.from } : undefined}
|
||||
onSelect={(to) => update({ from: value?.from, to })}
|
||||
/>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</FilterField>
|
||||
);
|
||||
}
|
||||
|
||||
function PeriodBound({
|
||||
title,
|
||||
selected,
|
||||
defaultMonth,
|
||||
disabled,
|
||||
onSelect,
|
||||
}: {
|
||||
title: string;
|
||||
selected: Date | undefined;
|
||||
defaultMonth: Date | undefined;
|
||||
disabled: React.ComponentProps<typeof Calendar>["disabled"];
|
||||
onSelect: (date: Date | undefined) => void;
|
||||
}) {
|
||||
return (
|
||||
<div className="fui:flex fui:flex-col">
|
||||
<div className="fui:flex fui:items-center fui:justify-between fui:gap-2 fui:px-3 fui:pt-3">
|
||||
<span className="fui:text-xs fui:font-medium fui:text-muted-foreground">
|
||||
{title}
|
||||
</span>
|
||||
{selected ? (
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="xs"
|
||||
onClick={() => onSelect(undefined)}
|
||||
>
|
||||
<XIcon />
|
||||
Clear
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
<Calendar
|
||||
mode="single"
|
||||
selected={selected}
|
||||
defaultMonth={defaultMonth}
|
||||
disabled={disabled}
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Boolean filter.
|
||||
*
|
||||
* Its caption follows the box rather than sitting above it: a checkbox reads
|
||||
* as "[x] Active only", and a label overhead would detach the words from the
|
||||
* control they toggle. The height matches the controls beside it so the shared
|
||||
* bottom edge still holds.
|
||||
*/
|
||||
function FilterToggle({
|
||||
label,
|
||||
checked,
|
||||
onCheckedChange,
|
||||
}: {
|
||||
label: string;
|
||||
checked: boolean;
|
||||
onCheckedChange: (checked: boolean) => void;
|
||||
}) {
|
||||
const id = React.useId();
|
||||
|
||||
return (
|
||||
<div
|
||||
data-slot="filter-toggle"
|
||||
className="fui:flex fui:h-8 fui:items-center fui:gap-2"
|
||||
>
|
||||
<Checkbox
|
||||
id={id}
|
||||
checked={checked}
|
||||
onCheckedChange={(next) => onCheckedChange(!!next)}
|
||||
/>
|
||||
<Label
|
||||
htmlFor={id}
|
||||
className="fui:text-sm fui:font-normal fui:whitespace-nowrap"
|
||||
>
|
||||
{label}
|
||||
</Label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears every filter at once.
|
||||
*
|
||||
* Rendered only when something is actually filtered: where filters apply on
|
||||
* change it is easy to narrow a result and have no obvious way back, while a
|
||||
* permanently visible "Clear" is another dead control in the row.
|
||||
*/
|
||||
function FilterReset({
|
||||
active,
|
||||
onReset,
|
||||
label = "Clear filters",
|
||||
}: {
|
||||
active: boolean;
|
||||
onReset: () => void;
|
||||
label?: string;
|
||||
}) {
|
||||
if (!active) return null;
|
||||
|
||||
return (
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={onReset}
|
||||
className="fui:h-8"
|
||||
>
|
||||
<XIcon />
|
||||
{label}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
FilterBar,
|
||||
FilterDateRange,
|
||||
FilterField,
|
||||
type FilterOption,
|
||||
type FilterPeriod,
|
||||
FilterReset,
|
||||
FilterSearch,
|
||||
FilterSelect,
|
||||
FilterToggle,
|
||||
};
|
||||
@@ -104,7 +104,7 @@ function FormLabel({
|
||||
<FieldLabel
|
||||
data-slot="form-label"
|
||||
data-error={!!error}
|
||||
className={cn("lib:data-[error=true]:text-destructive", className)}
|
||||
className={cn("fui:data-[error=true]:text-destructive", className)}
|
||||
htmlFor={formItemId}
|
||||
{...props}
|
||||
/>
|
||||
|
||||
@@ -30,12 +30,12 @@ function HoverCardContent({
|
||||
alignOffset={alignOffset}
|
||||
side={side}
|
||||
sideOffset={sideOffset}
|
||||
className="lib:isolate lib:z-50"
|
||||
className="fui:isolate fui:z-50"
|
||||
>
|
||||
<PreviewCardPrimitive.Popup
|
||||
data-slot="hover-card-content"
|
||||
className={cn(
|
||||
"lib:z-50 lib:w-72 lib:origin-(--transform-origin) lib:rounded-2xl lib:bg-popover lib:p-4 lib:text-sm lib:text-popover-foreground lib:shadow-2xl lib:ring-1 lib:ring-foreground/5 lib:outline-hidden lib:duration-100 lib:data-[side=bottom]:slide-in-from-top-2 lib:data-[side=inline-end]:slide-in-from-left-2 lib:data-[side=inline-start]:slide-in-from-right-2 lib:data-[side=left]:slide-in-from-right-2 lib:data-[side=right]:slide-in-from-left-2 lib:data-[side=top]:slide-in-from-bottom-2 lib:data-open:animate-in lib:data-open:fade-in-0 lib:data-open:zoom-in-95 lib:data-closed:animate-out lib:data-closed:fade-out-0 lib:data-closed:zoom-out-95",
|
||||
"fui:z-50 fui:w-72 fui:origin-(--transform-origin) fui:rounded-2xl fui:bg-popover fui:p-4 fui:text-sm fui:text-popover-foreground fui:shadow-2xl fui:ring-1 fui:ring-foreground/5 fui:outline-hidden fui:duration-100 fui:data-[side=bottom]:slide-in-from-top-2 fui:data-[side=inline-end]:slide-in-from-left-2 fui:data-[side=inline-start]:slide-in-from-right-2 fui:data-[side=left]:slide-in-from-right-2 fui:data-[side=right]:slide-in-from-left-2 fui:data-[side=top]:slide-in-from-bottom-2 fui:data-open:animate-in fui:data-open:fade-in-0 fui:data-open:zoom-in-95 fui:data-closed:animate-out fui:data-closed:fade-out-0 fui:data-closed:zoom-out-95",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -14,7 +14,7 @@ function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
|
||||
data-slot="input-group"
|
||||
role="group"
|
||||
className={cn(
|
||||
"lib:group/input-group lib:relative lib:flex lib:h-9 lib:w-full lib:min-w-0 lib:items-center lib:rounded-4xl lib:border lib:border-input lib:bg-input/30 lib:transition-colors lib:outline-none lib:in-data-[slot=combobox-content]:focus-within:border-inherit lib:in-data-[slot=combobox-content]:focus-within:ring-0 lib:has-data-[align=block-end]:rounded-2xl lib:has-data-[align=block-start]:rounded-2xl lib:has-[[data-slot=input-group-control]:focus-visible]:border-ring lib:has-[[data-slot=input-group-control]:focus-visible]:ring-[3px] lib:has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 lib:has-[[data-slot][aria-invalid=true]]:border-destructive lib:has-[[data-slot][aria-invalid=true]]:ring-[3px] lib:has-[[data-slot][aria-invalid=true]]:ring-destructive/20 lib:has-[textarea]:rounded-xl lib:has-[>[data-align=block-end]]:h-auto lib:has-[>[data-align=block-end]]:flex-col lib:has-[>[data-align=block-start]]:h-auto lib:has-[>[data-align=block-start]]:flex-col lib:has-[>textarea]:h-auto lib:dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 lib:has-[>[data-align=block-end]]:[&>input]:pt-3 lib:has-[>[data-align=block-start]]:[&>input]:pb-3 lib:has-[>[data-align=inline-end]]:[&>input]:pr-1.5 lib:has-[>[data-align=inline-start]]:[&>input]:pl-1.5",
|
||||
"fui:group/input-group fui:relative fui:flex fui:h-9 fui:w-full fui:min-w-0 fui:items-center fui:rounded-4xl fui:border fui:border-input fui:bg-input/30 fui:transition-colors fui:outline-none fui:in-data-[slot=combobox-content]:focus-within:border-inherit fui:in-data-[slot=combobox-content]:focus-within:ring-0 fui:has-data-[align=block-end]:rounded-2xl fui:has-data-[align=block-start]:rounded-2xl fui:has-[[data-slot=input-group-control]:focus-visible]:border-ring fui:has-[[data-slot=input-group-control]:focus-visible]:ring-[3px] fui:has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 fui:has-[[data-slot][aria-invalid=true]]:border-destructive fui:has-[[data-slot][aria-invalid=true]]:ring-[3px] fui:has-[[data-slot][aria-invalid=true]]:ring-destructive/20 fui:has-[textarea]:rounded-xl fui:has-[>[data-align=block-end]]:h-auto fui:has-[>[data-align=block-end]]:flex-col fui:has-[>[data-align=block-start]]:h-auto fui:has-[>[data-align=block-start]]:flex-col fui:has-[>textarea]:h-auto fui:dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 fui:has-[>[data-align=block-end]]:[&>input]:pt-3 fui:has-[>[data-align=block-start]]:[&>input]:pb-3 fui:has-[>[data-align=inline-end]]:[&>input]:pr-1.5 fui:has-[>[data-align=inline-start]]:[&>input]:pl-1.5",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -23,18 +23,18 @@ function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
|
||||
}
|
||||
|
||||
const inputGroupAddonVariants = cva(
|
||||
"lib:flex lib:h-auto lib:cursor-text lib:items-center lib:justify-center lib:gap-2 lib:py-2 lib:text-sm lib:font-medium lib:text-muted-foreground lib:select-none lib:group-data-[disabled=true]/input-group:opacity-50 lib:**:data-[slot=kbd]:rounded-4xl lib:**:data-[slot=kbd]:bg-muted-foreground/10 lib:**:data-[slot=kbd]:px-1.5 lib:[&>svg:not([class*=size-])]:size-4",
|
||||
"fui:flex fui:h-auto fui:cursor-text fui:items-center fui:justify-center fui:gap-2 fui:py-2 fui:text-sm fui:font-medium fui:text-muted-foreground fui:select-none fui:group-data-[disabled=true]/input-group:opacity-50 fui:**:data-[slot=kbd]:rounded-4xl fui:**:data-[slot=kbd]:bg-muted-foreground/10 fui:**:data-[slot=kbd]:px-1.5 fui:[&>svg:not([class*=size-])]:size-4",
|
||||
{
|
||||
variants: {
|
||||
align: {
|
||||
"inline-start":
|
||||
"lib:order-first lib:pl-3 lib:has-[>button]:-ml-1 lib:has-[>kbd]:ml-[-0.15rem]",
|
||||
"fui:order-first fui:pl-3 fui:has-[>button]:-ml-1 fui:has-[>kbd]:ml-[-0.15rem]",
|
||||
"inline-end":
|
||||
"lib:order-last lib:pr-3 lib:has-[>button]:-mr-1 lib:has-[>kbd]:mr-[-0.15rem]",
|
||||
"fui:order-last fui:pr-3 fui:has-[>button]:-mr-1 fui:has-[>kbd]:mr-[-0.15rem]",
|
||||
"block-start":
|
||||
"lib:order-first lib:w-full lib:justify-start lib:px-3 lib:pt-3 lib:group-has-[>input]/input-group:pt-3 lib:[.border-b]:pb-3",
|
||||
"fui:order-first fui:w-full fui:justify-start fui:px-3 fui:pt-3 fui:group-has-[>input]/input-group:pt-3 fui:[.border-b]:pb-3",
|
||||
"block-end":
|
||||
"lib:order-last lib:w-full lib:justify-start lib:px-3 lib:pb-3 lib:group-has-[>input]/input-group:pb-3 lib:[.border-t]:pt-3",
|
||||
"fui:order-last fui:w-full fui:justify-start fui:px-3 fui:pb-3 fui:group-has-[>input]/input-group:pb-3 fui:[.border-t]:pt-3",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
@@ -66,14 +66,14 @@ function InputGroupAddon({
|
||||
}
|
||||
|
||||
const inputGroupButtonVariants = cva(
|
||||
"lib:flex lib:items-center lib:gap-2 lib:rounded-4xl lib:text-sm lib:shadow-none",
|
||||
"fui:flex fui:items-center fui:gap-2 fui:rounded-4xl fui:text-sm fui:shadow-none",
|
||||
{
|
||||
variants: {
|
||||
size: {
|
||||
xs: "lib:h-6 lib:gap-1 lib:px-1.5 lib:[&>svg:not([class*=size-])]:size-3.5",
|
||||
sm: "lib:",
|
||||
"icon-xs": "lib:size-6 lib:p-0 lib:has-[>svg]:p-0",
|
||||
"icon-sm": "lib:size-8 lib:p-0 lib:has-[>svg]:p-0",
|
||||
xs: "fui:h-6 fui:gap-1 fui:px-1.5 fui:[&>svg:not([class*=size-])]:size-3.5",
|
||||
sm: "fui:",
|
||||
"icon-xs": "fui:size-6 fui:p-0 fui:has-[>svg]:p-0",
|
||||
"icon-sm": "fui:size-8 fui:p-0 fui:has-[>svg]:p-0",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
@@ -105,7 +105,7 @@ function InputGroupText({ className, ...props }: React.ComponentProps<"span">) {
|
||||
return (
|
||||
<span
|
||||
className={cn(
|
||||
"lib:flex lib:items-center lib:gap-2 lib:text-sm lib:text-muted-foreground lib:[&_svg]:pointer-events-none lib:[&_svg:not([class*=size-])]:size-4",
|
||||
"fui:flex fui:items-center fui:gap-2 fui:text-sm fui:text-muted-foreground fui:[&_svg]:pointer-events-none fui:[&_svg:not([class*=size-])]:size-4",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -121,7 +121,7 @@ function InputGroupInput({
|
||||
<Input
|
||||
data-slot="input-group-control"
|
||||
className={cn(
|
||||
"lib:flex-1 lib:rounded-none lib:border-0 lib:bg-transparent lib:shadow-none lib:ring-0 lib:focus-visible:ring-0 lib:aria-invalid:ring-0 lib:dark:bg-transparent",
|
||||
"fui:flex-1 fui:rounded-none fui:border-0 fui:bg-transparent fui:shadow-none fui:ring-0 fui:focus-visible:ring-0 fui:aria-invalid:ring-0 fui:dark:bg-transparent",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -137,7 +137,7 @@ function InputGroupTextarea({
|
||||
<Textarea
|
||||
data-slot="input-group-control"
|
||||
className={cn(
|
||||
"lib:flex-1 lib:resize-none lib:rounded-none lib:border-0 lib:bg-transparent lib:py-2 lib:shadow-none lib:ring-0 lib:focus-visible:ring-0 lib:aria-invalid:ring-0 lib:dark:bg-transparent",
|
||||
"fui:flex-1 fui:resize-none fui:rounded-none fui:border-0 fui:bg-transparent fui:py-2 fui:shadow-none fui:ring-0 fui:focus-visible:ring-0 fui:aria-invalid:ring-0 fui:dark:bg-transparent",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -18,7 +18,7 @@ function InputOTP({
|
||||
containerClassName,
|
||||
)}
|
||||
spellCheck={false}
|
||||
className={cn("lib:disabled:cursor-not-allowed", className)}
|
||||
className={cn("fui:disabled:cursor-not-allowed", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -29,7 +29,7 @@ function InputOTPGroup({ className, ...props }: React.ComponentProps<"div">) {
|
||||
<div
|
||||
data-slot="input-otp-group"
|
||||
className={cn(
|
||||
"lib:flex lib:items-center lib:rounded-4xl lib:has-aria-invalid:border-destructive lib:has-aria-invalid:ring-[3px] lib:has-aria-invalid:ring-destructive/20 lib:dark:has-aria-invalid:ring-destructive/40",
|
||||
"fui:flex fui:items-center fui:rounded-4xl fui:has-aria-invalid:border-destructive fui:has-aria-invalid:ring-[3px] fui:has-aria-invalid:ring-destructive/20 fui:dark:has-aria-invalid:ring-destructive/40",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -52,15 +52,15 @@ function InputOTPSlot({
|
||||
data-slot="input-otp-slot"
|
||||
data-active={isActive}
|
||||
className={cn(
|
||||
"lib:relative lib:flex lib:size-9 lib:items-center lib:justify-center lib:border-y lib:border-r lib:border-input lib:bg-input/30 lib:text-sm lib:transition-all lib:outline-none lib:first:rounded-l-4xl lib:first:border-l lib:last:rounded-r-4xl lib:aria-invalid:border-destructive lib:data-[active=true]:z-10 lib:data-[active=true]:border-ring lib:data-[active=true]:ring-[3px] lib:data-[active=true]:ring-ring/50 lib:data-[active=true]:aria-invalid:border-destructive lib:data-[active=true]:aria-invalid:ring-destructive/20 lib:dark:data-[active=true]:aria-invalid:ring-destructive/40",
|
||||
"fui:relative fui:flex fui:size-9 fui:items-center fui:justify-center fui:border-y fui:border-r fui:border-input fui:bg-input/30 fui:text-sm fui:transition-all fui:outline-none fui:first:rounded-l-4xl fui:first:border-l fui:last:rounded-r-4xl fui:aria-invalid:border-destructive fui:data-[active=true]:z-10 fui:data-[active=true]:border-ring fui:data-[active=true]:ring-[3px] fui:data-[active=true]:ring-ring/50 fui:data-[active=true]:aria-invalid:border-destructive fui:data-[active=true]:aria-invalid:ring-destructive/20 fui:dark:data-[active=true]:aria-invalid:ring-destructive/40",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{char}
|
||||
{hasFakeCaret && (
|
||||
<div className="lib:pointer-events-none lib:absolute lib:inset-0 lib:flex lib:items-center lib:justify-center">
|
||||
<div className="lib:h-4 lib:w-px lib:animate-caret-blink lib:bg-foreground lib:duration-1000" />
|
||||
<div className="fui:pointer-events-none fui:absolute fui:inset-0 fui:flex fui:items-center fui:justify-center">
|
||||
<div className="fui:h-4 fui:w-px fui:animate-caret-blink fui:bg-foreground fui:duration-1000" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -71,7 +71,7 @@ function InputOTPSeparator({ ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="input-otp-separator"
|
||||
className="lib:flex lib:items-center lib:[&_svg:not([class*=size-])]:size-4"
|
||||
className="fui:flex fui:items-center fui:[&_svg:not([class*=size-])]:size-4"
|
||||
role="separator"
|
||||
{...props}
|
||||
>
|
||||
|
||||
@@ -8,7 +8,7 @@ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
|
||||
type={type}
|
||||
data-slot="input"
|
||||
className={cn(
|
||||
"lib:h-9 lib:w-full lib:min-w-0 lib:rounded-4xl lib:border lib:border-input lib:bg-input/30 lib:px-3 lib:py-1 lib:text-base lib:transition-colors lib:outline-none lib:file:inline-flex lib:file:h-7 lib:file:border-0 lib:file:bg-transparent lib:file:text-sm lib:file:font-medium lib:file:text-foreground lib:placeholder:text-muted-foreground lib:focus-visible:border-ring lib:focus-visible:ring-[3px] lib:focus-visible:ring-ring/50 lib:disabled:pointer-events-none lib:disabled:cursor-not-allowed lib:disabled:opacity-50 lib:aria-invalid:border-destructive lib:aria-invalid:ring-[3px] lib:aria-invalid:ring-destructive/20 lib:md:text-sm lib:dark:aria-invalid:border-destructive/50 lib:dark:aria-invalid:ring-destructive/40",
|
||||
"fui:h-9 fui:w-full fui:min-w-0 fui:rounded-4xl fui:border fui:border-input fui:bg-input/30 fui:px-3 fui:py-1 fui:text-base fui:transition-colors fui:outline-none fui:file:inline-flex fui:file:h-7 fui:file:border-0 fui:file:bg-transparent fui:file:text-sm fui:file:font-medium fui:file:text-foreground fui:placeholder:text-muted-foreground fui:focus-visible:border-ring fui:focus-visible:ring-[3px] fui:focus-visible:ring-ring/50 fui:disabled:pointer-events-none fui:disabled:cursor-not-allowed fui:disabled:opacity-50 fui:aria-invalid:border-destructive fui:aria-invalid:ring-[3px] fui:aria-invalid:ring-destructive/20 fui:md:text-sm fui:dark:aria-invalid:border-destructive/50 fui:dark:aria-invalid:ring-destructive/40",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
+19
-19
@@ -12,7 +12,7 @@ function ItemGroup({ className, ...props }: React.ComponentProps<"div">) {
|
||||
role="list"
|
||||
data-slot="item-group"
|
||||
className={cn(
|
||||
"lib:group/item-group lib:flex lib:w-full lib:flex-col lib:gap-4 lib:has-data-[size=sm]:gap-2.5 lib:has-data-[size=xs]:gap-2",
|
||||
"fui:group/item-group fui:flex fui:w-full fui:flex-col fui:gap-4 fui:has-data-[size=sm]:gap-2.5 fui:has-data-[size=xs]:gap-2",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -28,25 +28,25 @@ function ItemSeparator({
|
||||
<Separator
|
||||
data-slot="item-separator"
|
||||
orientation="horizontal"
|
||||
className={cn("lib:my-2", className)}
|
||||
className={cn("fui:my-2", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const itemVariants = cva(
|
||||
"lib:group/item lib:flex lib:w-full lib:flex-wrap lib:items-center lib:rounded-2xl lib:border lib:text-sm lib:transition-colors lib:duration-100 lib:outline-none lib:focus-visible:border-ring lib:focus-visible:ring-[3px] lib:focus-visible:ring-ring/50 lib:[a]:transition-colors lib:[a]:hover:bg-muted",
|
||||
"fui:group/item fui:flex fui:w-full fui:flex-wrap fui:items-center fui:rounded-2xl fui:border fui:text-sm fui:transition-colors fui:duration-100 fui:outline-none fui:focus-visible:border-ring fui:focus-visible:ring-[3px] fui:focus-visible:ring-ring/50 fui:[a]:transition-colors fui:[a]:hover:bg-muted",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "lib:border-transparent",
|
||||
outline: "lib:border-border",
|
||||
muted: "lib:border-transparent lib:bg-muted/50",
|
||||
default: "fui:border-transparent",
|
||||
outline: "fui:border-border",
|
||||
muted: "fui:border-transparent fui:bg-muted/50",
|
||||
},
|
||||
size: {
|
||||
default: "lib:gap-3.5 lib:px-4 lib:py-3.5",
|
||||
sm: "lib:gap-3.5 lib:px-3.5 lib:py-3",
|
||||
xs: "lib:gap-2.5 lib:px-3 lib:py-2.5 lib:in-data-[slot=dropdown-menu-content]:p-0",
|
||||
default: "fui:gap-3.5 fui:px-4 fui:py-3.5",
|
||||
sm: "fui:gap-3.5 fui:px-3.5 fui:py-3",
|
||||
xs: "fui:gap-2.5 fui:px-3 fui:py-2.5 fui:in-data-[slot=dropdown-menu-content]:p-0",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
@@ -81,14 +81,14 @@ function Item({
|
||||
}
|
||||
|
||||
const itemMediaVariants = cva(
|
||||
"lib:flex lib:shrink-0 lib:items-center lib:justify-center lib:gap-2 lib:group-has-data-[slot=item-description]/item:translate-y-0.5 lib:group-has-data-[slot=item-description]/item:self-start lib:[&_svg]:pointer-events-none",
|
||||
"fui:flex fui:shrink-0 fui:items-center fui:justify-center fui:gap-2 fui:group-has-data-[slot=item-description]/item:translate-y-0.5 fui:group-has-data-[slot=item-description]/item:self-start fui:[&_svg]:pointer-events-none",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "lib:bg-transparent",
|
||||
icon: "lib:[&_svg:not([class*=size-])]:size-4",
|
||||
default: "fui:bg-transparent",
|
||||
icon: "fui:[&_svg:not([class*=size-])]:size-4",
|
||||
image:
|
||||
"lib:size-10 lib:overflow-hidden lib:rounded-lg lib:group-data-[size=sm]/item:size-8 lib:group-data-[size=xs]/item:size-6 lib:group-data-[size=xs]/item:rounded-md lib:[&_img]:size-full lib:[&_img]:object-cover",
|
||||
"fui:size-10 fui:overflow-hidden fui:rounded-lg fui:group-data-[size=sm]/item:size-8 fui:group-data-[size=xs]/item:size-6 fui:group-data-[size=xs]/item:rounded-md fui:[&_img]:size-full fui:[&_img]:object-cover",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
@@ -117,7 +117,7 @@ function ItemContent({ className, ...props }: React.ComponentProps<"div">) {
|
||||
<div
|
||||
data-slot="item-content"
|
||||
className={cn(
|
||||
"lib:flex lib:flex-1 lib:flex-col lib:gap-1 lib:group-data-[size=xs]/item:gap-0.5 lib:[&+[data-slot=item-content]]:flex-none",
|
||||
"fui:flex fui:flex-1 fui:flex-col fui:gap-1 fui:group-data-[size=xs]/item:gap-0.5 fui:[&+[data-slot=item-content]]:flex-none",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -130,7 +130,7 @@ function ItemTitle({ className, ...props }: React.ComponentProps<"div">) {
|
||||
<div
|
||||
data-slot="item-title"
|
||||
className={cn(
|
||||
"lib:line-clamp-1 lib:flex lib:w-fit lib:items-center lib:gap-2 lib:text-sm lib:leading-snug lib:font-medium lib:underline-offset-4",
|
||||
"fui:line-clamp-1 fui:flex fui:w-fit fui:items-center fui:gap-2 fui:text-sm fui:leading-snug fui:font-medium fui:underline-offset-4",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -143,7 +143,7 @@ function ItemDescription({ className, ...props }: React.ComponentProps<"p">) {
|
||||
<p
|
||||
data-slot="item-description"
|
||||
className={cn(
|
||||
"lib:line-clamp-2 lib:text-left lib:text-sm lib:font-normal lib:text-muted-foreground lib:[&>a]:underline lib:[&>a]:underline-offset-4 lib:[&>a:hover]:text-primary",
|
||||
"fui:line-clamp-2 fui:text-left fui:text-sm fui:font-normal fui:text-muted-foreground fui:[&>a]:underline fui:[&>a]:underline-offset-4 fui:[&>a:hover]:text-primary",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -155,7 +155,7 @@ function ItemActions({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="item-actions"
|
||||
className={cn("lib:flex lib:items-center lib:gap-2", className)}
|
||||
className={cn("fui:flex fui:items-center fui:gap-2", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -166,7 +166,7 @@ function ItemHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||
<div
|
||||
data-slot="item-header"
|
||||
className={cn(
|
||||
"lib:flex lib:basis-full lib:items-center lib:justify-between lib:gap-2",
|
||||
"fui:flex fui:basis-full fui:items-center fui:justify-between fui:gap-2",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -179,7 +179,7 @@ function ItemFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||
<div
|
||||
data-slot="item-footer"
|
||||
className={cn(
|
||||
"lib:flex lib:basis-full lib:items-center lib:justify-between lib:gap-2",
|
||||
"fui:flex fui:basis-full fui:items-center fui:justify-between fui:gap-2",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -5,7 +5,7 @@ function Kbd({ className, ...props }: React.ComponentProps<"kbd">) {
|
||||
<kbd
|
||||
data-slot="kbd"
|
||||
className={cn(
|
||||
"lib:pointer-events-none lib:inline-flex lib:h-5 lib:w-fit lib:min-w-5 lib:items-center lib:justify-center lib:gap-1 lib:rounded-sm lib:bg-muted lib:px-1 lib:font-sans lib:text-xs lib:font-medium lib:text-muted-foreground lib:select-none lib:in-data-[slot=tooltip-content]:bg-background/20 lib:in-data-[slot=tooltip-content]:text-background lib:dark:in-data-[slot=tooltip-content]:bg-background/10 lib:[&_svg:not([class*=size-])]:size-3",
|
||||
"fui:pointer-events-none fui:inline-flex fui:h-5 fui:w-fit fui:min-w-5 fui:items-center fui:justify-center fui:gap-1 fui:rounded-sm fui:bg-muted fui:px-1 fui:font-sans fui:text-xs fui:font-medium fui:text-muted-foreground fui:select-none fui:in-data-[slot=tooltip-content]:bg-background/20 fui:in-data-[slot=tooltip-content]:text-background fui:dark:in-data-[slot=tooltip-content]:bg-background/10 fui:[&_svg:not([class*=size-])]:size-3",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -17,7 +17,7 @@ function KbdGroup({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<kbd
|
||||
data-slot="kbd-group"
|
||||
className={cn("lib:inline-flex lib:items-center lib:gap-1", className)}
|
||||
className={cn("fui:inline-flex fui:items-center fui:gap-1", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -8,7 +8,7 @@ function Label({ className, ...props }: React.ComponentProps<"label">) {
|
||||
<label
|
||||
data-slot="label"
|
||||
className={cn(
|
||||
"lib:flex lib:items-center lib:gap-2 lib:text-sm lib:leading-none lib:font-medium lib:select-none lib:group-data-[disabled=true]:pointer-events-none lib:group-data-[disabled=true]:opacity-50 lib:peer-disabled:cursor-not-allowed lib:peer-disabled:opacity-50",
|
||||
"fui:flex fui:items-center fui:gap-2 fui:text-sm fui:leading-none fui:font-medium fui:select-none fui:group-data-[disabled=true]:pointer-events-none fui:group-data-[disabled=true]:opacity-50 fui:peer-disabled:cursor-not-allowed fui:peer-disabled:opacity-50",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
+13
-13
@@ -26,7 +26,7 @@ function Menubar({ className, ...props }: MenubarPrimitive.Props) {
|
||||
<MenubarPrimitive
|
||||
data-slot="menubar"
|
||||
className={cn(
|
||||
"lib:flex lib:h-9 lib:items-center lib:rounded-4xl lib:border lib:p-1",
|
||||
"fui:flex fui:h-9 fui:items-center fui:rounded-4xl fui:border fui:p-1",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -58,7 +58,7 @@ function MenubarTrigger({
|
||||
<DropdownMenuTrigger
|
||||
data-slot="menubar-trigger"
|
||||
className={cn(
|
||||
"lib:flex lib:items-center lib:rounded-xl lib:px-2 lib:py-0.75 lib:text-sm lib:font-medium lib:outline-hidden lib:select-none lib:hover:bg-muted lib:aria-expanded:bg-muted",
|
||||
"fui:flex fui:items-center fui:rounded-xl fui:px-2 fui:py-0.75 fui:text-sm fui:font-medium fui:outline-hidden fui:select-none fui:hover:bg-muted fui:aria-expanded:bg-muted",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -80,7 +80,7 @@ function MenubarContent({
|
||||
alignOffset={alignOffset}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"lib: lib: lib:min-w-48 lib:rounded-2xl lib:bg-popover lib:p-1 lib:text-popover-foreground lib:shadow-2xl lib:ring-1 lib:ring-foreground/5 lib:duration-100 lib:data-[side=bottom]:slide-in-from-top-2 lib:data-[side=inline-end]:slide-in-from-left-2 lib:data-[side=inline-start]:slide-in-from-right-2 lib:data-[side=left]:slide-in-from-right-2 lib:data-[side=right]:slide-in-from-left-2 lib:data-[side=top]:slide-in-from-bottom-2 lib:data-open:animate-in lib:data-open:fade-in-0 lib:data-open:zoom-in-95 animate-none! relative bg-popover/70 before:pointer-events-none before:absolute before:inset-0 before:-z-1 before:rounded-[inherit] before:backdrop-blur-2xl before:backdrop-saturate-150 **:data-[slot$=-item]:focus:bg-foreground/10 **:data-[slot$=-item]:data-highlighted:bg-foreground/10 **:data-[slot$=-separator]:bg-foreground/5 **:data-[slot$=-trigger]:focus:bg-foreground/10 **:data-[slot$=-trigger]:aria-expanded:bg-foreground/10! **:data-[variant=destructive]:focus:bg-foreground/10! **:data-[variant=destructive]:text-accent-foreground! **:data-[variant=destructive]:**:text-accent-foreground!",
|
||||
"fui: fui: fui:min-w-48 fui:rounded-2xl fui:bg-popover fui:p-1 fui:text-popover-foreground fui:shadow-2xl fui:ring-1 fui:ring-foreground/5 fui:duration-100 fui:data-[side=bottom]:slide-in-from-top-2 fui:data-[side=inline-end]:slide-in-from-left-2 fui:data-[side=inline-start]:slide-in-from-right-2 fui:data-[side=left]:slide-in-from-right-2 fui:data-[side=right]:slide-in-from-left-2 fui:data-[side=top]:slide-in-from-bottom-2 fui:data-open:animate-in fui:data-open:fade-in-0 fui:data-open:zoom-in-95 animate-none! relative bg-popover/70 before:pointer-events-none before:absolute before:inset-0 before:-z-1 before:rounded-[inherit] before:backdrop-blur-2xl before:backdrop-saturate-150 **:data-[slot$=-item]:focus:bg-foreground/10 **:data-[slot$=-item]:data-highlighted:bg-foreground/10 **:data-[slot$=-separator]:bg-foreground/5 **:data-[slot$=-trigger]:focus:bg-foreground/10 **:data-[slot$=-trigger]:aria-expanded:bg-foreground/10! **:data-[variant=destructive]:focus:bg-foreground/10! **:data-[variant=destructive]:text-accent-foreground! **:data-[variant=destructive]:**:text-accent-foreground!",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -100,7 +100,7 @@ function MenubarItem({
|
||||
data-inset={inset}
|
||||
data-variant={variant}
|
||||
className={cn(
|
||||
"lib:group/menubar-item lib:gap-2.5 lib:rounded-xl lib:px-3 lib:py-2 lib:text-sm lib:focus:bg-accent lib:focus:text-accent-foreground lib:not-data-[variant=destructive]:focus:**:text-accent-foreground lib:data-inset:pl-9.5 lib:data-[variant=destructive]:text-destructive lib:data-[variant=destructive]:focus:bg-destructive/10 lib:data-[variant=destructive]:focus:text-destructive lib:dark:data-[variant=destructive]:focus:bg-destructive/20 lib:data-disabled:opacity-50 lib:[&_svg:not([class*=size-])]:size-4 lib:data-[variant=destructive]:*:[svg]:text-destructive!",
|
||||
"fui:group/menubar-item fui:gap-2.5 fui:rounded-xl fui:px-3 fui:py-2 fui:text-sm fui:focus:bg-accent fui:focus:text-accent-foreground fui:not-data-[variant=destructive]:focus:**:text-accent-foreground fui:data-inset:pl-9.5 fui:data-[variant=destructive]:text-destructive fui:data-[variant=destructive]:focus:bg-destructive/10 fui:data-[variant=destructive]:focus:text-destructive fui:dark:data-[variant=destructive]:focus:bg-destructive/20 fui:data-disabled:opacity-50 fui:[&_svg:not([class*=size-])]:size-4 fui:data-[variant=destructive]:*:[svg]:text-destructive!",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -122,13 +122,13 @@ function MenubarCheckboxItem({
|
||||
data-slot="menubar-checkbox-item"
|
||||
data-inset={inset}
|
||||
className={cn(
|
||||
"lib:relative lib:flex lib:cursor-default lib:items-center lib:gap-2.5 lib:rounded-xl lib:py-2 lib:pr-3 lib:pl-9.5 lib:text-sm lib:outline-hidden lib:select-none lib:focus:bg-accent lib:focus:text-accent-foreground lib:focus:**:text-accent-foreground lib:data-inset:pl-9.5 lib:data-disabled:pointer-events-none lib:data-disabled:opacity-50 lib:[&_svg]:pointer-events-none lib:[&_svg]:shrink-0",
|
||||
"fui:relative fui:flex fui:cursor-default fui:items-center fui:gap-2.5 fui:rounded-xl fui:py-2 fui:pr-3 fui:pl-9.5 fui:text-sm fui:outline-hidden fui:select-none fui:focus:bg-accent fui:focus:text-accent-foreground fui:focus:**:text-accent-foreground fui:data-inset:pl-9.5 fui:data-disabled:pointer-events-none fui:data-disabled:opacity-50 fui:[&_svg]:pointer-events-none fui:[&_svg]:shrink-0",
|
||||
className,
|
||||
)}
|
||||
checked={checked}
|
||||
{...props}
|
||||
>
|
||||
<span className="lib:pointer-events-none lib:absolute lib:left-3 lib:flex lib:size-4 lib:items-center lib:justify-center lib:[&_svg:not([class*=size-])]:size-4">
|
||||
<span className="fui:pointer-events-none fui:absolute fui:left-3 fui:flex fui:size-4 fui:items-center fui:justify-center fui:[&_svg:not([class*=size-])]:size-4">
|
||||
<MenuPrimitive.CheckboxItemIndicator>
|
||||
<CheckIcon />
|
||||
</MenuPrimitive.CheckboxItemIndicator>
|
||||
@@ -157,12 +157,12 @@ function MenubarRadioItem({
|
||||
data-slot="menubar-radio-item"
|
||||
data-inset={inset}
|
||||
className={cn(
|
||||
"lib:relative lib:flex lib:cursor-default lib:items-center lib:gap-2.5 lib:rounded-xl lib:py-2 lib:pr-3 lib:pl-9.5 lib:text-sm lib:outline-hidden lib:select-none lib:focus:bg-accent lib:focus:text-accent-foreground lib:focus:**:text-accent-foreground lib:data-inset:pl-9.5 lib:data-disabled:pointer-events-none lib:data-disabled:opacity-50 lib:[&_svg]:pointer-events-none lib:[&_svg]:shrink-0 lib:[&_svg:not([class*=size-])]:size-4",
|
||||
"fui:relative fui:flex fui:cursor-default fui:items-center fui:gap-2.5 fui:rounded-xl fui:py-2 fui:pr-3 fui:pl-9.5 fui:text-sm fui:outline-hidden fui:select-none fui:focus:bg-accent fui:focus:text-accent-foreground fui:focus:**:text-accent-foreground fui:data-inset:pl-9.5 fui:data-disabled:pointer-events-none fui:data-disabled:opacity-50 fui:[&_svg]:pointer-events-none fui:[&_svg]:shrink-0 fui:[&_svg:not([class*=size-])]:size-4",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className="lib:pointer-events-none lib:absolute lib:left-3 lib:flex lib:size-4 lib:items-center lib:justify-center lib:[&_svg:not([class*=size-])]:size-4">
|
||||
<span className="fui:pointer-events-none fui:absolute fui:left-3 fui:flex fui:size-4 fui:items-center fui:justify-center fui:[&_svg:not([class*=size-])]:size-4">
|
||||
<MenuPrimitive.RadioItemIndicator>
|
||||
<CheckIcon />
|
||||
</MenuPrimitive.RadioItemIndicator>
|
||||
@@ -184,7 +184,7 @@ function MenubarLabel({
|
||||
data-slot="menubar-label"
|
||||
data-inset={inset}
|
||||
className={cn(
|
||||
"lib:px-3.5 lib:py-2.5 lib:text-xs lib:text-muted-foreground lib:data-inset:pl-9.5",
|
||||
"fui:px-3.5 fui:py-2.5 fui:text-xs fui:text-muted-foreground fui:data-inset:pl-9.5",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -199,7 +199,7 @@ function MenubarSeparator({
|
||||
return (
|
||||
<DropdownMenuSeparator
|
||||
data-slot="menubar-separator"
|
||||
className={cn("lib:-mx-1 lib:my-1 lib:h-px lib:bg-border/50", className)}
|
||||
className={cn("fui:-mx-1 fui:my-1 fui:h-px fui:bg-border/50", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -213,7 +213,7 @@ function MenubarShortcut({
|
||||
<DropdownMenuShortcut
|
||||
data-slot="menubar-shortcut"
|
||||
className={cn(
|
||||
"lib:ml-auto lib:text-xs lib:tracking-widest lib:text-muted-foreground lib:group-focus/menubar-item:text-accent-foreground",
|
||||
"fui:ml-auto fui:text-xs fui:tracking-widest fui:text-muted-foreground fui:group-focus/menubar-item:text-accent-foreground",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -239,7 +239,7 @@ function MenubarSubTrigger({
|
||||
data-slot="menubar-sub-trigger"
|
||||
data-inset={inset}
|
||||
className={cn(
|
||||
"lib:gap-2 lib:rounded-xl lib:px-3 lib:py-2 lib:text-sm lib:focus:bg-accent lib:focus:text-accent-foreground lib:data-inset:pl-9.5 lib:data-open:bg-accent lib:data-open:text-accent-foreground lib:[&_svg:not([class*=size-])]:size-4",
|
||||
"fui:gap-2 fui:rounded-xl fui:px-3 fui:py-2 fui:text-sm fui:focus:bg-accent fui:focus:text-accent-foreground fui:data-inset:pl-9.5 fui:data-open:bg-accent fui:data-open:text-accent-foreground fui:[&_svg:not([class*=size-])]:size-4",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -255,7 +255,7 @@ function MenubarSubContent({
|
||||
<DropdownMenuSubContent
|
||||
data-slot="menubar-sub-content"
|
||||
className={cn(
|
||||
"lib: lib: lib:min-w-32 lib:rounded-2xl lib:bg-popover lib:p-1 lib:text-popover-foreground lib:shadow-2xl lib:ring-1 lib:ring-foreground/5 lib:duration-100 lib:data-[side=bottom]:slide-in-from-top-2 lib:data-[side=left]:slide-in-from-right-2 lib:data-[side=right]:slide-in-from-left-2 lib:data-[side=top]:slide-in-from-bottom-2 lib:data-open:animate-in lib:data-open:fade-in-0 lib:data-open:zoom-in-95 lib:data-closed:animate-out lib:data-closed:fade-out-0 lib:data-closed:zoom-out-95 animate-none! relative bg-popover/70 before:pointer-events-none before:absolute before:inset-0 before:-z-1 before:rounded-[inherit] before:backdrop-blur-2xl before:backdrop-saturate-150 **:data-[slot$=-item]:focus:bg-foreground/10 **:data-[slot$=-item]:data-highlighted:bg-foreground/10 **:data-[slot$=-separator]:bg-foreground/5 **:data-[slot$=-trigger]:focus:bg-foreground/10 **:data-[slot$=-trigger]:aria-expanded:bg-foreground/10! **:data-[variant=destructive]:focus:bg-foreground/10! **:data-[variant=destructive]:text-accent-foreground! **:data-[variant=destructive]:**:text-accent-foreground!",
|
||||
"fui: fui: fui:min-w-32 fui:rounded-2xl fui:bg-popover fui:p-1 fui:text-popover-foreground fui:shadow-2xl fui:ring-1 fui:ring-foreground/5 fui:duration-100 fui:data-[side=bottom]:slide-in-from-top-2 fui:data-[side=left]:slide-in-from-right-2 fui:data-[side=right]:slide-in-from-left-2 fui:data-[side=top]:slide-in-from-bottom-2 fui:data-open:animate-in fui:data-open:fade-in-0 fui:data-open:zoom-in-95 fui:data-closed:animate-out fui:data-closed:fade-out-0 fui:data-closed:zoom-out-95 animate-none! relative bg-popover/70 before:pointer-events-none before:absolute before:inset-0 before:-z-1 before:rounded-[inherit] before:backdrop-blur-2xl before:backdrop-saturate-150 **:data-[slot$=-item]:focus:bg-foreground/10 **:data-[slot$=-item]:data-highlighted:bg-foreground/10 **:data-[slot$=-separator]:bg-foreground/5 **:data-[slot$=-trigger]:focus:bg-foreground/10 **:data-[slot$=-trigger]:aria-expanded:bg-foreground/10! **:data-[variant=destructive]:focus:bg-foreground/10! **:data-[variant=destructive]:text-accent-foreground! **:data-[variant=destructive]:**:text-accent-foreground!",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -14,7 +14,7 @@ function NavigationMenu({
|
||||
<NavigationMenuPrimitive.Root
|
||||
data-slot="navigation-menu"
|
||||
className={cn(
|
||||
"lib:group/navigation-menu lib:relative lib:flex lib:max-w-max lib:flex-1 lib:items-center lib:justify-center",
|
||||
"fui:group/navigation-menu fui:relative fui:flex fui:max-w-max fui:flex-1 fui:items-center fui:justify-center",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -33,7 +33,7 @@ function NavigationMenuList({
|
||||
<NavigationMenuPrimitive.List
|
||||
data-slot="navigation-menu-list"
|
||||
className={cn(
|
||||
"lib:group lib:flex lib:flex-1 lib:list-none lib:items-center lib:justify-center lib:gap-0",
|
||||
"fui:group fui:flex fui:flex-1 fui:list-none fui:items-center fui:justify-center fui:gap-0",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -48,14 +48,14 @@ function NavigationMenuItem({
|
||||
return (
|
||||
<NavigationMenuPrimitive.Item
|
||||
data-slot="navigation-menu-item"
|
||||
className={cn("lib:relative", className)}
|
||||
className={cn("fui:relative", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const navigationMenuTriggerStyle = cva(
|
||||
"lib:group/navigation-menu-trigger lib:inline-flex lib:h-9 lib:w-max lib:items-center lib:justify-center lib:rounded-2xl lib:px-4.5 lib:py-2.5 lib:text-sm lib:font-medium lib:transition-all lib:outline-none lib:hover:bg-muted lib:focus:bg-muted lib:focus-visible:ring-[3px] lib:focus-visible:ring-ring/50 lib:focus-visible:outline-1 lib:disabled:pointer-events-none lib:disabled:opacity-50 lib:data-popup-open:bg-muted/50 lib:data-popup-open:hover:bg-muted lib:data-open:bg-muted/50 lib:data-open:hover:bg-muted lib:data-open:focus:bg-muted",
|
||||
"fui:group/navigation-menu-trigger fui:inline-flex fui:h-9 fui:w-max fui:items-center fui:justify-center fui:rounded-2xl fui:px-4.5 fui:py-2.5 fui:text-sm fui:font-medium fui:transition-all fui:outline-none fui:hover:bg-muted fui:focus:bg-muted fui:focus-visible:ring-[3px] fui:focus-visible:ring-ring/50 fui:focus-visible:outline-1 fui:disabled:pointer-events-none fui:disabled:opacity-50 fui:data-popup-open:bg-muted/50 fui:data-popup-open:hover:bg-muted fui:data-open:bg-muted/50 fui:data-open:hover:bg-muted fui:data-open:focus:bg-muted",
|
||||
);
|
||||
|
||||
function NavigationMenuTrigger({
|
||||
@@ -66,12 +66,12 @@ function NavigationMenuTrigger({
|
||||
return (
|
||||
<NavigationMenuPrimitive.Trigger
|
||||
data-slot="navigation-menu-trigger"
|
||||
className={cn(navigationMenuTriggerStyle(), "lib:group", className)}
|
||||
className={cn(navigationMenuTriggerStyle(), "fui:group", className)}
|
||||
{...props}
|
||||
>
|
||||
{children}{" "}
|
||||
<ChevronDownIcon
|
||||
className="lib:relative lib:top-px lib:ml-1 lib:size-3 lib:transition lib:duration-300 lib:group-data-popup-open/navigation-menu-trigger:rotate-180 lib:group-data-open/navigation-menu-trigger:rotate-180"
|
||||
className="fui:relative fui:top-px fui:ml-1 fui:size-3 fui:transition fui:duration-300 fui:group-data-popup-open/navigation-menu-trigger:rotate-180 fui:group-data-open/navigation-menu-trigger:rotate-180"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</NavigationMenuPrimitive.Trigger>
|
||||
@@ -86,7 +86,7 @@ function NavigationMenuContent({
|
||||
<NavigationMenuPrimitive.Content
|
||||
data-slot="navigation-menu-content"
|
||||
className={cn(
|
||||
"lib:data-ending-style:data-activation-direction=left:translate-x-[50%] lib:data-ending-style:data-activation-direction=right:translate-x-[-50%] lib:data-starting-style:data-activation-direction=left:translate-x-[-50%] lib:data-starting-style:data-activation-direction=right:translate-x-[50%] lib:isolate lib:z-50 lib:h-full lib:w-auto lib:p-2.5 lib:pr-3 lib:transition-[opacity,transform,translate] lib:duration-[0.35s] lib:ease-[cubic-bezier(0.22,1,0.36,1)] lib:group-data-[viewport=false]/navigation-menu:rounded-2xl lib:group-data-[viewport=false]/navigation-menu:bg-popover lib:group-data-[viewport=false]/navigation-menu:text-popover-foreground lib:group-data-[viewport=false]/navigation-menu:shadow-2xl lib:group-data-[viewport=false]/navigation-menu:ring-1 lib:group-data-[viewport=false]/navigation-menu:ring-foreground/5 lib:group-data-[viewport=false]/navigation-menu:duration-300 lib:data-ending-style:opacity-0 lib:data-starting-style:opacity-0 lib:data-[motion=from-end]:slide-in-from-right-52 lib:data-[motion=from-start]:slide-in-from-left-52 lib:data-[motion=to-end]:slide-out-to-right-52 lib:data-[motion=to-start]:slide-out-to-left-52 lib:data-[motion^=from-]:animate-in lib:data-[motion^=from-]:fade-in lib:data-[motion^=to-]:animate-out lib:data-[motion^=to-]:fade-out lib:**:data-[slot=navigation-menu-link]:focus:ring-0 lib:**:data-[slot=navigation-menu-link]:focus:outline-none lib:group-data-[viewport=false]/navigation-menu:data-open:animate-in lib:group-data-[viewport=false]/navigation-menu:data-open:fade-in-0 lib:group-data-[viewport=false]/navigation-menu:data-open:zoom-in-95 lib:group-data-[viewport=false]/navigation-menu:data-closed:animate-out lib:group-data-[viewport=false]/navigation-menu:data-closed:fade-out-0 lib:group-data-[viewport=false]/navigation-menu:data-closed:zoom-out-95",
|
||||
"fui:data-ending-style:data-activation-direction=left:translate-x-[50%] fui:data-ending-style:data-activation-direction=right:translate-x-[-50%] fui:data-starting-style:data-activation-direction=left:translate-x-[-50%] fui:data-starting-style:data-activation-direction=right:translate-x-[50%] fui:isolate fui:z-50 fui:h-full fui:w-auto fui:p-2.5 fui:pr-3 fui:transition-[opacity,transform,translate] fui:duration-[0.35s] fui:ease-[cubic-bezier(0.22,1,0.36,1)] fui:group-data-[viewport=false]/navigation-menu:rounded-2xl fui:group-data-[viewport=false]/navigation-menu:bg-popover fui:group-data-[viewport=false]/navigation-menu:text-popover-foreground fui:group-data-[viewport=false]/navigation-menu:shadow-2xl fui:group-data-[viewport=false]/navigation-menu:ring-1 fui:group-data-[viewport=false]/navigation-menu:ring-foreground/5 fui:group-data-[viewport=false]/navigation-menu:duration-300 fui:data-ending-style:opacity-0 fui:data-starting-style:opacity-0 fui:data-[motion=from-end]:slide-in-from-right-52 fui:data-[motion=from-start]:slide-in-from-left-52 fui:data-[motion=to-end]:slide-out-to-right-52 fui:data-[motion=to-start]:slide-out-to-left-52 fui:data-[motion^=from-]:animate-in fui:data-[motion^=from-]:fade-in fui:data-[motion^=to-]:animate-out fui:data-[motion^=to-]:fade-out fui:**:data-[slot=navigation-menu-link]:focus:ring-0 fui:**:data-[slot=navigation-menu-link]:focus:outline-none fui:group-data-[viewport=false]/navigation-menu:data-open:animate-in fui:group-data-[viewport=false]/navigation-menu:data-open:fade-in-0 fui:group-data-[viewport=false]/navigation-menu:data-open:zoom-in-95 fui:group-data-[viewport=false]/navigation-menu:data-closed:animate-out fui:group-data-[viewport=false]/navigation-menu:data-closed:fade-out-0 fui:group-data-[viewport=false]/navigation-menu:data-closed:zoom-out-95",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -110,13 +110,13 @@ function NavigationMenuPositioner({
|
||||
align={align}
|
||||
alignOffset={alignOffset}
|
||||
className={cn(
|
||||
"lib:isolate lib:z-50 lib:h-(--positioner-height) lib:w-(--positioner-width) lib:max-w-(--available-width) lib:transition-[top,left,right,bottom] lib:duration-[0.35s] lib:ease-[cubic-bezier(0.22,1,0.36,1)] lib:data-instant:transition-none lib:data-[side=bottom]:before:top-[-10px] lib:data-[side=bottom]:before:right-0 lib:data-[side=bottom]:before:left-0",
|
||||
"fui:isolate fui:z-50 fui:h-(--positioner-height) fui:w-(--positioner-width) fui:max-w-(--available-width) fui:transition-[top,left,right,bottom] fui:duration-[0.35s] fui:ease-[cubic-bezier(0.22,1,0.36,1)] fui:data-instant:transition-none fui:data-[side=bottom]:before:top-[-10px] fui:data-[side=bottom]:before:right-0 fui:data-[side=bottom]:before:left-0",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<NavigationMenuPrimitive.Popup className="lib:data-[ending-style]:easing-[ease] lib:xs:w-(--popup-width) lib:relative lib:h-(--popup-height) lib:w-(--popup-width) lib:origin-(--transform-origin) lib:rounded-2xl lib:bg-popover lib:text-popover-foreground lib:shadow lib:ring-1 lib:ring-foreground/5 lib:transition-[opacity,transform,width,height,scale,translate] lib:duration-[0.35s] lib:ease-[cubic-bezier(0.22,1,0.36,1)] lib:outline-none lib:data-ending-style:scale-90 lib:data-ending-style:opacity-0 lib:data-ending-style:duration-150 lib:data-starting-style:scale-90 lib:data-starting-style:opacity-0">
|
||||
<NavigationMenuPrimitive.Viewport className="lib:relative lib:size-full lib:overflow-hidden" />
|
||||
<NavigationMenuPrimitive.Popup className="fui:data-[ending-style]:easing-[ease] fui:xs:w-(--popup-width) fui:relative fui:h-(--popup-height) fui:w-(--popup-width) fui:origin-(--transform-origin) fui:rounded-2xl fui:bg-popover fui:text-popover-foreground fui:shadow fui:ring-1 fui:ring-foreground/5 fui:transition-[opacity,transform,width,height,scale,translate] fui:duration-[0.35s] fui:ease-[cubic-bezier(0.22,1,0.36,1)] fui:outline-none fui:data-ending-style:scale-90 fui:data-ending-style:opacity-0 fui:data-ending-style:duration-150 fui:data-starting-style:scale-90 fui:data-starting-style:opacity-0">
|
||||
<NavigationMenuPrimitive.Viewport className="fui:relative fui:size-full fui:overflow-hidden" />
|
||||
</NavigationMenuPrimitive.Popup>
|
||||
</NavigationMenuPrimitive.Positioner>
|
||||
</NavigationMenuPrimitive.Portal>
|
||||
@@ -131,7 +131,7 @@ function NavigationMenuLink({
|
||||
<NavigationMenuPrimitive.Link
|
||||
data-slot="navigation-menu-link"
|
||||
className={cn(
|
||||
"lib:flex lib:items-center lib:gap-1.5 lib:rounded-2xl lib:p-3 lib:text-sm lib:transition-all lib:outline-none lib:hover:bg-muted lib:focus:bg-muted lib:focus-visible:ring-[3px] lib:focus-visible:ring-ring/50 lib:focus-visible:outline-1 lib:in-data-[slot=navigation-menu-content]:rounded-xl lib:data-[active=true]:bg-muted/50 lib:data-[active=true]:hover:bg-muted lib:data-[active=true]:focus:bg-muted lib:[&_svg:not([class*=size-])]:size-4",
|
||||
"fui:flex fui:items-center fui:gap-1.5 fui:rounded-2xl fui:p-3 fui:text-sm fui:transition-all fui:outline-none fui:hover:bg-muted fui:focus:bg-muted fui:focus-visible:ring-[3px] fui:focus-visible:ring-ring/50 fui:focus-visible:outline-1 fui:in-data-[slot=navigation-menu-content]:rounded-xl fui:data-[active=true]:bg-muted/50 fui:data-[active=true]:hover:bg-muted fui:data-[active=true]:focus:bg-muted fui:[&_svg:not([class*=size-])]:size-4",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -147,12 +147,12 @@ function NavigationMenuIndicator({
|
||||
<NavigationMenuPrimitive.Icon
|
||||
data-slot="navigation-menu-indicator"
|
||||
className={cn(
|
||||
"lib:top-full lib:z-1 lib:flex lib:h-1.5 lib:items-end lib:justify-center lib:overflow-hidden lib:data-[state=hidden]:animate-out lib:data-[state=hidden]:fade-out lib:data-[state=visible]:animate-in lib:data-[state=visible]:fade-in",
|
||||
"fui:top-full fui:z-1 fui:flex fui:h-1.5 fui:items-end fui:justify-center fui:overflow-hidden fui:data-[state=hidden]:animate-out fui:data-[state=hidden]:fade-out fui:data-[state=visible]:animate-in fui:data-[state=visible]:fade-in",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<div className="lib:relative lib:top-[60%] lib:h-2 lib:w-2 lib:rotate-45 lib:rounded-tl-sm lib:bg-border lib:shadow-md" />
|
||||
<div className="fui:relative fui:top-[60%] fui:h-2 fui:w-2 fui:rotate-45 fui:rounded-tl-sm fui:bg-border fui:shadow-md" />
|
||||
</NavigationMenuPrimitive.Icon>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ function Pagination({ className, ...props }: React.ComponentProps<"nav">) {
|
||||
aria-label="pagination"
|
||||
data-slot="pagination"
|
||||
className={cn(
|
||||
"lib:mx-auto lib:flex lib:w-full lib:justify-center",
|
||||
"fui:mx-auto fui:flex fui:w-full fui:justify-center",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -28,7 +28,7 @@ function PaginationContent({
|
||||
return (
|
||||
<ul
|
||||
data-slot="pagination-content"
|
||||
className={cn("lib:flex lib:items-center lib:gap-1", className)}
|
||||
className={cn("fui:flex fui:items-center fui:gap-1", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -75,11 +75,11 @@ function PaginationPrevious({
|
||||
<PaginationLink
|
||||
aria-label="Go to previous page"
|
||||
size="default"
|
||||
className={cn("lib:pl-2!", className)}
|
||||
className={cn("fui:pl-2!", className)}
|
||||
{...props}
|
||||
>
|
||||
<ChevronLeftIcon data-icon="inline-start" />
|
||||
<span className="lib:hidden lib:sm:block">{text}</span>
|
||||
<span className="fui:hidden fui:sm:block">{text}</span>
|
||||
</PaginationLink>
|
||||
);
|
||||
}
|
||||
@@ -93,10 +93,10 @@ function PaginationNext({
|
||||
<PaginationLink
|
||||
aria-label="Go to next page"
|
||||
size="default"
|
||||
className={cn("lib:pr-2!", className)}
|
||||
className={cn("fui:pr-2!", className)}
|
||||
{...props}
|
||||
>
|
||||
<span className="lib:hidden lib:sm:block">{text}</span>
|
||||
<span className="fui:hidden fui:sm:block">{text}</span>
|
||||
<ChevronRightIcon data-icon="inline-end" />
|
||||
</PaginationLink>
|
||||
);
|
||||
@@ -111,13 +111,13 @@ function PaginationEllipsis({
|
||||
aria-hidden
|
||||
data-slot="pagination-ellipsis"
|
||||
className={cn(
|
||||
"lib:flex lib:size-9 lib:items-center lib:justify-center lib:[&_svg:not([class*=size-])]:size-4",
|
||||
"fui:flex fui:size-9 fui:items-center fui:justify-center fui:[&_svg:not([class*=size-])]:size-4",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<MoreHorizontalIcon />
|
||||
<span className="lib:sr-only">More pages</span>
|
||||
<span className="fui:sr-only">More pages</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -29,12 +29,12 @@ function PopoverContent({
|
||||
alignOffset={alignOffset}
|
||||
side={side}
|
||||
sideOffset={sideOffset}
|
||||
className="lib:isolate lib:z-50"
|
||||
className="fui:isolate fui:z-50"
|
||||
>
|
||||
<PopoverPrimitive.Popup
|
||||
data-slot="popover-content"
|
||||
className={cn(
|
||||
"lib:z-50 lib:flex lib:w-72 lib:origin-(--transform-origin) lib:flex-col lib:gap-4 lib:rounded-2xl lib:bg-popover lib:p-4 lib:text-sm lib:text-popover-foreground lib:shadow-2xl lib:ring-1 lib:ring-foreground/5 lib:outline-hidden lib:duration-100 lib:data-[side=bottom]:slide-in-from-top-2 lib:data-[side=inline-end]:slide-in-from-left-2 lib:data-[side=inline-start]:slide-in-from-right-2 lib:data-[side=left]:slide-in-from-right-2 lib:data-[side=right]:slide-in-from-left-2 lib:data-[side=top]:slide-in-from-bottom-2 lib:data-open:animate-in lib:data-open:fade-in-0 lib:data-open:zoom-in-95 lib:data-closed:animate-out lib:data-closed:fade-out-0 lib:data-closed:zoom-out-95",
|
||||
"fui:z-50 fui:flex fui:w-72 fui:origin-(--transform-origin) fui:flex-col fui:gap-4 fui:rounded-2xl fui:bg-popover fui:p-4 fui:text-sm fui:text-popover-foreground fui:shadow-2xl fui:ring-1 fui:ring-foreground/5 fui:outline-hidden fui:duration-100 fui:data-[side=bottom]:slide-in-from-top-2 fui:data-[side=inline-end]:slide-in-from-left-2 fui:data-[side=inline-start]:slide-in-from-right-2 fui:data-[side=left]:slide-in-from-right-2 fui:data-[side=right]:slide-in-from-left-2 fui:data-[side=top]:slide-in-from-bottom-2 fui:data-open:animate-in fui:data-open:fade-in-0 fui:data-open:zoom-in-95 fui:data-closed:animate-out fui:data-closed:fade-out-0 fui:data-closed:zoom-out-95",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -48,7 +48,7 @@ function PopoverHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="popover-header"
|
||||
className={cn("lib:flex lib:flex-col lib:gap-1 lib:text-sm", className)}
|
||||
className={cn("fui:flex fui:flex-col fui:gap-1 fui:text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -58,7 +58,7 @@ function PopoverTitle({ className, ...props }: PopoverPrimitive.Title.Props) {
|
||||
return (
|
||||
<PopoverPrimitive.Title
|
||||
data-slot="popover-title"
|
||||
className={cn("lib:text-base lib:font-medium", className)}
|
||||
className={cn("fui:text-base fui:font-medium", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -71,7 +71,7 @@ function PopoverDescription({
|
||||
return (
|
||||
<PopoverPrimitive.Description
|
||||
data-slot="popover-description"
|
||||
className={cn("lib:text-muted-foreground", className)}
|
||||
className={cn("fui:text-muted-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -11,7 +11,7 @@ function Progress({
|
||||
<ProgressPrimitive.Root
|
||||
value={value}
|
||||
data-slot="progress"
|
||||
className={cn("lib:flex lib:flex-wrap lib:gap-3", className)}
|
||||
className={cn("fui:flex fui:flex-wrap fui:gap-3", className)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
@@ -26,7 +26,7 @@ function ProgressTrack({ className, ...props }: ProgressPrimitive.Track.Props) {
|
||||
return (
|
||||
<ProgressPrimitive.Track
|
||||
className={cn(
|
||||
"lib:relative lib:flex lib:h-3 lib:w-full lib:items-center lib:overflow-x-hidden lib:rounded-4xl lib:bg-muted",
|
||||
"fui:relative fui:flex fui:h-3 fui:w-full fui:items-center fui:overflow-x-hidden fui:rounded-4xl fui:bg-muted",
|
||||
className,
|
||||
)}
|
||||
data-slot="progress-track"
|
||||
@@ -42,7 +42,7 @@ function ProgressIndicator({
|
||||
return (
|
||||
<ProgressPrimitive.Indicator
|
||||
data-slot="progress-indicator"
|
||||
className={cn("lib:h-full lib:bg-primary lib:transition-all", className)}
|
||||
className={cn("fui:h-full fui:bg-primary fui:transition-all", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -51,7 +51,7 @@ function ProgressIndicator({
|
||||
function ProgressLabel({ className, ...props }: ProgressPrimitive.Label.Props) {
|
||||
return (
|
||||
<ProgressPrimitive.Label
|
||||
className={cn("lib:text-sm lib:font-medium", className)}
|
||||
className={cn("fui:text-sm fui:font-medium", className)}
|
||||
data-slot="progress-label"
|
||||
{...props}
|
||||
/>
|
||||
@@ -62,7 +62,7 @@ function ProgressValue({ className, ...props }: ProgressPrimitive.Value.Props) {
|
||||
return (
|
||||
<ProgressPrimitive.Value
|
||||
className={cn(
|
||||
"lib:ml-auto lib:text-sm lib:text-muted-foreground lib:tabular-nums",
|
||||
"fui:ml-auto fui:text-sm fui:text-muted-foreground fui:tabular-nums",
|
||||
className,
|
||||
)}
|
||||
data-slot="progress-value"
|
||||
|
||||
@@ -8,7 +8,7 @@ function RadioGroup({ className, ...props }: RadioGroupPrimitive.Props) {
|
||||
return (
|
||||
<RadioGroupPrimitive
|
||||
data-slot="radio-group"
|
||||
className={cn("lib:grid lib:w-full lib:gap-3", className)}
|
||||
className={cn("fui:grid fui:w-full fui:gap-3", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -19,16 +19,16 @@ function RadioGroupItem({ className, ...props }: RadioPrimitive.Root.Props) {
|
||||
<RadioPrimitive.Root
|
||||
data-slot="radio-group-item"
|
||||
className={cn(
|
||||
"lib:group/radio-group-item lib:peer lib:relative lib:flex lib:aspect-square lib:size-4 lib:shrink-0 lib:rounded-full lib:border lib:border-input lib:outline-none lib:group-has-[:focus-visible]/field-label:ring-0 lib:group-has-[:focus-visible]/field-label:not-data-checked:border-input lib:after:absolute lib:after:-inset-x-3 lib:after:-inset-y-2 lib:focus-visible:border-ring lib:focus-visible:ring-3 lib:focus-visible:ring-ring/50 lib:disabled:cursor-not-allowed lib:disabled:opacity-50 lib:aria-invalid:border-destructive lib:aria-invalid:ring-3 lib:aria-invalid:ring-destructive/20 lib:aria-invalid:aria-checked:border-primary lib:dark:bg-input/30 lib:dark:aria-invalid:border-destructive/50 lib:dark:aria-invalid:ring-destructive/40 lib:data-checked:border-primary lib:data-checked:bg-primary lib:data-checked:text-primary-foreground lib:group-has-[:focus-visible]/field-label:data-checked:border-primary lib:dark:data-checked:bg-primary",
|
||||
"fui:group/radio-group-item fui:peer fui:relative fui:flex fui:aspect-square fui:size-4 fui:shrink-0 fui:rounded-full fui:border fui:border-input fui:outline-none fui:group-has-[:focus-visible]/field-label:ring-0 fui:group-has-[:focus-visible]/field-label:not-data-checked:border-input fui:after:absolute fui:after:-inset-x-3 fui:after:-inset-y-2 fui:focus-visible:border-ring fui:focus-visible:ring-3 fui:focus-visible:ring-ring/50 fui:disabled:cursor-not-allowed fui:disabled:opacity-50 fui:aria-invalid:border-destructive fui:aria-invalid:ring-3 fui:aria-invalid:ring-destructive/20 fui:aria-invalid:aria-checked:border-primary fui:dark:bg-input/30 fui:dark:aria-invalid:border-destructive/50 fui:dark:aria-invalid:ring-destructive/40 fui:data-checked:border-primary fui:data-checked:bg-primary fui:data-checked:text-primary-foreground fui:group-has-[:focus-visible]/field-label:data-checked:border-primary fui:dark:data-checked:bg-primary",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<RadioPrimitive.Indicator
|
||||
data-slot="radio-group-indicator"
|
||||
className="lib:flex lib:size-4 lib:items-center lib:justify-center"
|
||||
className="fui:flex fui:size-4 fui:items-center fui:justify-center"
|
||||
>
|
||||
<span className="lib:absolute lib:top-1/2 lib:left-1/2 lib:size-2 lib:-translate-x-1/2 lib:-translate-y-1/2 lib:rounded-full lib:bg-primary-foreground" />
|
||||
<span className="fui:absolute fui:top-1/2 fui:left-1/2 fui:size-2 fui:-translate-x-1/2 fui:-translate-y-1/2 fui:rounded-full fui:bg-primary-foreground" />
|
||||
</RadioPrimitive.Indicator>
|
||||
</RadioPrimitive.Root>
|
||||
);
|
||||
|
||||
@@ -9,7 +9,7 @@ function ResizablePanelGroup({
|
||||
<ResizablePrimitive.Group
|
||||
data-slot="resizable-panel-group"
|
||||
className={cn(
|
||||
"lib:flex lib:h-full lib:w-full lib:aria-[orientation=vertical]:flex-col",
|
||||
"fui:flex fui:h-full fui:w-full fui:aria-[orientation=vertical]:flex-col",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -32,13 +32,13 @@ function ResizableHandle({
|
||||
<ResizablePrimitive.Separator
|
||||
data-slot="resizable-handle"
|
||||
className={cn(
|
||||
"lib:relative lib:flex lib:w-px lib:items-center lib:justify-center lib:bg-border lib:ring-offset-background lib:after:absolute lib:after:inset-y-0 lib:after:left-1/2 lib:after:w-1 lib:after:-translate-x-1/2 lib:focus-visible:ring-1 lib:focus-visible:ring-ring lib:focus-visible:outline-hidden lib:aria-[orientation=horizontal]:h-px lib:aria-[orientation=horizontal]:w-full lib:aria-[orientation=horizontal]:after:left-0 lib:aria-[orientation=horizontal]:after:h-1 lib:aria-[orientation=horizontal]:after:w-full lib:aria-[orientation=horizontal]:after:translate-x-0 lib:aria-[orientation=horizontal]:after:-translate-y-1/2 lib:[&[aria-orientation=horizontal]>div]:rotate-90",
|
||||
"fui:relative fui:flex fui:w-px fui:items-center fui:justify-center fui:bg-border fui:ring-offset-background fui:after:absolute fui:after:inset-y-0 fui:after:left-1/2 fui:after:w-1 fui:after:-translate-x-1/2 fui:focus-visible:ring-1 fui:focus-visible:ring-ring fui:focus-visible:outline-hidden fui:aria-[orientation=horizontal]:h-px fui:aria-[orientation=horizontal]:w-full fui:aria-[orientation=horizontal]:after:left-0 fui:aria-[orientation=horizontal]:after:h-1 fui:aria-[orientation=horizontal]:after:w-full fui:aria-[orientation=horizontal]:after:translate-x-0 fui:aria-[orientation=horizontal]:after:-translate-y-1/2 fui:[&[aria-orientation=horizontal]>div]:rotate-90",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{withHandle && (
|
||||
<div className="lib:z-10 lib:flex lib:h-6 lib:w-1 lib:shrink-0 lib:rounded-lg lib:bg-border" />
|
||||
<div className="fui:z-10 fui:flex fui:h-6 fui:w-1 fui:shrink-0 fui:rounded-lg fui:bg-border" />
|
||||
)}
|
||||
</ResizablePrimitive.Separator>
|
||||
);
|
||||
|
||||
@@ -9,12 +9,12 @@ function ScrollArea({
|
||||
return (
|
||||
<ScrollAreaPrimitive.Root
|
||||
data-slot="scroll-area"
|
||||
className={cn("lib:relative", className)}
|
||||
className={cn("fui:relative", className)}
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.Viewport
|
||||
data-slot="scroll-area-viewport"
|
||||
className="lib:size-full lib:rounded-[inherit] lib:transition-[color,box-shadow] lib:outline-none lib:focus-visible:ring-[3px] lib:focus-visible:ring-ring/50 lib:focus-visible:outline-1"
|
||||
className="fui:size-full fui:rounded-[inherit] fui:transition-[color,box-shadow] fui:outline-none fui:focus-visible:ring-[3px] fui:focus-visible:ring-ring/50 fui:focus-visible:outline-1"
|
||||
>
|
||||
{children}
|
||||
</ScrollAreaPrimitive.Viewport>
|
||||
@@ -35,14 +35,14 @@ function ScrollBar({
|
||||
data-orientation={orientation}
|
||||
orientation={orientation}
|
||||
className={cn(
|
||||
"lib:flex lib:touch-none lib:p-px lib:transition-colors lib:select-none lib:data-horizontal:h-2.5 lib:data-horizontal:flex-col lib:data-horizontal:border-t lib:data-horizontal:border-t-transparent lib:data-vertical:h-full lib:data-vertical:w-2.5 lib:data-vertical:border-l lib:data-vertical:border-l-transparent",
|
||||
"fui:flex fui:touch-none fui:p-px fui:transition-colors fui:select-none fui:data-horizontal:h-2.5 fui:data-horizontal:flex-col fui:data-horizontal:border-t fui:data-horizontal:border-t-transparent fui:data-vertical:h-full fui:data-vertical:w-2.5 fui:data-vertical:border-l fui:data-vertical:border-l-transparent",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.Thumb
|
||||
data-slot="scroll-area-thumb"
|
||||
className="lib:relative lib:flex-1 lib:rounded-full lib:bg-border"
|
||||
className="fui:relative fui:flex-1 fui:rounded-full fui:bg-border"
|
||||
/>
|
||||
</ScrollAreaPrimitive.Scrollbar>
|
||||
);
|
||||
|
||||
+14
-14
@@ -11,7 +11,7 @@ function SelectGroup({ className, ...props }: SelectPrimitive.Group.Props) {
|
||||
return (
|
||||
<SelectPrimitive.Group
|
||||
data-slot="select-group"
|
||||
className={cn("lib:scroll-my-1 lib:p-1", className)}
|
||||
className={cn("fui:scroll-my-1 fui:p-1", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -21,7 +21,7 @@ function SelectValue({ className, ...props }: SelectPrimitive.Value.Props) {
|
||||
return (
|
||||
<SelectPrimitive.Value
|
||||
data-slot="select-value"
|
||||
className={cn("lib:flex lib:flex-1 lib:text-left", className)}
|
||||
className={cn("fui:flex fui:flex-1 fui:text-left", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -40,7 +40,7 @@ function SelectTrigger({
|
||||
data-slot="select-trigger"
|
||||
data-size={size}
|
||||
className={cn(
|
||||
"lib:flex lib:w-fit lib:items-center lib:justify-between lib:gap-1.5 lib:rounded-4xl lib:border lib:border-input lib:bg-input/30 lib:px-3 lib:py-2 lib:text-sm lib:whitespace-nowrap lib:transition-colors lib:outline-none lib:focus-visible:border-ring lib:focus-visible:ring-[3px] lib:focus-visible:ring-ring/50 lib:disabled:cursor-not-allowed lib:disabled:opacity-50 lib:aria-invalid:border-destructive lib:aria-invalid:ring-[3px] lib:aria-invalid:ring-destructive/20 lib:data-placeholder:text-muted-foreground lib:data-[size=default]:h-9 lib:data-[size=sm]:h-8 lib:*:data-[slot=select-value]:line-clamp-1 lib:*:data-[slot=select-value]:flex lib:*:data-[slot=select-value]:items-center lib:*:data-[slot=select-value]:gap-1.5 lib:dark:hover:bg-input/50 lib:dark:aria-invalid:border-destructive/50 lib:dark:aria-invalid:ring-destructive/40 lib:[&_svg]:pointer-events-none lib:[&_svg]:shrink-0 lib:[&_svg:not([class*=size-])]:size-4",
|
||||
"fui:flex fui:w-fit fui:items-center fui:justify-between fui:gap-1.5 fui:rounded-4xl fui:border fui:border-input fui:bg-input/30 fui:px-3 fui:py-2 fui:text-sm fui:whitespace-nowrap fui:transition-colors fui:outline-none fui:focus-visible:border-ring fui:focus-visible:ring-[3px] fui:focus-visible:ring-ring/50 fui:disabled:cursor-not-allowed fui:disabled:opacity-50 fui:aria-invalid:border-destructive fui:aria-invalid:ring-[3px] fui:aria-invalid:ring-destructive/20 fui:data-placeholder:text-muted-foreground fui:data-[size=default]:h-9 fui:data-[size=sm]:h-8 fui:*:data-[slot=select-value]:line-clamp-1 fui:*:data-[slot=select-value]:flex fui:*:data-[slot=select-value]:items-center fui:*:data-[slot=select-value]:gap-1.5 fui:dark:hover:bg-input/50 fui:dark:aria-invalid:border-destructive/50 fui:dark:aria-invalid:ring-destructive/40 fui:[&_svg]:pointer-events-none fui:[&_svg]:shrink-0 fui:[&_svg:not([class*=size-])]:size-4",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -48,7 +48,7 @@ function SelectTrigger({
|
||||
{children}
|
||||
<SelectPrimitive.Icon
|
||||
render={
|
||||
<ChevronDownIcon className="lib:pointer-events-none lib:size-4 lib:text-muted-foreground" />
|
||||
<ChevronDownIcon className="fui:pointer-events-none fui:size-4 fui:text-muted-foreground" />
|
||||
}
|
||||
/>
|
||||
</SelectPrimitive.Trigger>
|
||||
@@ -77,13 +77,13 @@ function SelectContent({
|
||||
align={align}
|
||||
alignOffset={alignOffset}
|
||||
alignItemWithTrigger={alignItemWithTrigger}
|
||||
className="lib:isolate lib:z-50"
|
||||
className="fui:isolate fui:z-50"
|
||||
>
|
||||
<SelectPrimitive.Popup
|
||||
data-slot="select-content"
|
||||
data-align-trigger={alignItemWithTrigger}
|
||||
className={cn(
|
||||
"lib: lib: lib:relative lib:isolate lib:z-50 lib:max-h-(--available-height) lib:w-(--anchor-width) lib:min-w-36 lib:origin-(--transform-origin) lib:overflow-x-hidden lib:overflow-y-auto lib:rounded-2xl lib:bg-popover lib:text-popover-foreground lib:shadow-2xl lib:ring-1 lib:ring-foreground/5 lib:duration-100 lib:data-[align-trigger=true]:animate-none lib:data-[side=bottom]:slide-in-from-top-2 lib:data-[side=inline-end]:slide-in-from-left-2 lib:data-[side=inline-start]:slide-in-from-right-2 lib:data-[side=left]:slide-in-from-right-2 lib:data-[side=right]:slide-in-from-left-2 lib:data-[side=top]:slide-in-from-bottom-2 lib:data-open:animate-in lib:data-open:fade-in-0 lib:data-open:zoom-in-95 lib:data-closed:animate-out lib:data-closed:fade-out-0 lib:data-closed:zoom-out-95 animate-none! relative bg-popover/70 before:pointer-events-none before:absolute before:inset-0 before:-z-1 before:rounded-[inherit] before:backdrop-blur-2xl before:backdrop-saturate-150 **:data-[slot$=-item]:focus:bg-foreground/10 **:data-[slot$=-item]:data-highlighted:bg-foreground/10 **:data-[slot$=-separator]:bg-foreground/5 **:data-[slot$=-trigger]:focus:bg-foreground/10 **:data-[slot$=-trigger]:aria-expanded:bg-foreground/10! **:data-[variant=destructive]:focus:bg-foreground/10! **:data-[variant=destructive]:text-accent-foreground! **:data-[variant=destructive]:**:text-accent-foreground!",
|
||||
"fui: fui: fui:relative fui:isolate fui:z-50 fui:max-h-(--available-height) fui:w-(--anchor-width) fui:min-w-36 fui:origin-(--transform-origin) fui:overflow-x-hidden fui:overflow-y-auto fui:rounded-2xl fui:bg-popover fui:text-popover-foreground fui:shadow-2xl fui:ring-1 fui:ring-foreground/5 fui:duration-100 fui:data-[align-trigger=true]:animate-none fui:data-[side=bottom]:slide-in-from-top-2 fui:data-[side=inline-end]:slide-in-from-left-2 fui:data-[side=inline-start]:slide-in-from-right-2 fui:data-[side=left]:slide-in-from-right-2 fui:data-[side=right]:slide-in-from-left-2 fui:data-[side=top]:slide-in-from-bottom-2 fui:data-open:animate-in fui:data-open:fade-in-0 fui:data-open:zoom-in-95 fui:data-closed:animate-out fui:data-closed:fade-out-0 fui:data-closed:zoom-out-95 animate-none! relative bg-popover/70 before:pointer-events-none before:absolute before:inset-0 before:-z-1 before:rounded-[inherit] before:backdrop-blur-2xl before:backdrop-saturate-150 **:data-[slot$=-item]:focus:bg-foreground/10 **:data-[slot$=-item]:data-highlighted:bg-foreground/10 **:data-[slot$=-separator]:bg-foreground/5 **:data-[slot$=-trigger]:focus:bg-foreground/10 **:data-[slot$=-trigger]:aria-expanded:bg-foreground/10! **:data-[variant=destructive]:focus:bg-foreground/10! **:data-[variant=destructive]:text-accent-foreground! **:data-[variant=destructive]:**:text-accent-foreground!",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -105,7 +105,7 @@ function SelectLabel({
|
||||
<SelectPrimitive.GroupLabel
|
||||
data-slot="select-label"
|
||||
className={cn(
|
||||
"lib:px-3 lib:py-2.5 lib:text-xs lib:text-muted-foreground",
|
||||
"fui:px-3 fui:py-2.5 fui:text-xs fui:text-muted-foreground",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -122,20 +122,20 @@ function SelectItem({
|
||||
<SelectPrimitive.Item
|
||||
data-slot="select-item"
|
||||
className={cn(
|
||||
"lib:relative lib:flex lib:w-full lib:cursor-default lib:items-center lib:gap-2.5 lib:rounded-xl lib:py-2 lib:pr-8 lib:pl-3 lib:text-sm lib:outline-hidden lib:select-none lib:focus:bg-accent lib:focus:text-accent-foreground lib:not-data-[variant=destructive]:focus:**:text-accent-foreground lib:data-disabled:pointer-events-none lib:data-disabled:opacity-50 lib:[&_svg]:pointer-events-none lib:[&_svg]:shrink-0 lib:[&_svg:not([class*=size-])]:size-4 lib:*:[span]:last:flex lib:*:[span]:last:items-center lib:*:[span]:last:gap-2",
|
||||
"fui:relative fui:flex fui:w-full fui:cursor-default fui:items-center fui:gap-2.5 fui:rounded-xl fui:py-2 fui:pr-8 fui:pl-3 fui:text-sm fui:outline-hidden fui:select-none fui:focus:bg-accent fui:focus:text-accent-foreground fui:not-data-[variant=destructive]:focus:**:text-accent-foreground fui:data-disabled:pointer-events-none fui:data-disabled:opacity-50 fui:[&_svg]:pointer-events-none fui:[&_svg]:shrink-0 fui:[&_svg:not([class*=size-])]:size-4 fui:*:[span]:last:flex fui:*:[span]:last:items-center fui:*:[span]:last:gap-2",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<SelectPrimitive.ItemText className="lib:flex lib:flex-1 lib:shrink-0 lib:gap-2 lib:whitespace-nowrap">
|
||||
<SelectPrimitive.ItemText className="fui:flex fui:flex-1 fui:shrink-0 fui:gap-2 fui:whitespace-nowrap">
|
||||
{children}
|
||||
</SelectPrimitive.ItemText>
|
||||
<SelectPrimitive.ItemIndicator
|
||||
render={
|
||||
<span className="lib:pointer-events-none lib:absolute lib:right-2 lib:flex lib:size-4 lib:items-center lib:justify-center" />
|
||||
<span className="fui:pointer-events-none fui:absolute fui:right-2 fui:flex fui:size-4 fui:items-center fui:justify-center" />
|
||||
}
|
||||
>
|
||||
<CheckIcon className="lib:pointer-events-none" />
|
||||
<CheckIcon className="fui:pointer-events-none" />
|
||||
</SelectPrimitive.ItemIndicator>
|
||||
</SelectPrimitive.Item>
|
||||
);
|
||||
@@ -149,7 +149,7 @@ function SelectSeparator({
|
||||
<SelectPrimitive.Separator
|
||||
data-slot="select-separator"
|
||||
className={cn(
|
||||
"lib:pointer-events-none lib:-mx-1 lib:my-1 lib:h-px lib:bg-border/50",
|
||||
"fui:pointer-events-none fui:-mx-1 fui:my-1 fui:h-px fui:bg-border/50",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -165,7 +165,7 @@ function SelectScrollUpButton({
|
||||
<SelectPrimitive.ScrollUpArrow
|
||||
data-slot="select-scroll-up-button"
|
||||
className={cn(
|
||||
"lib:top-0 lib:z-10 lib:flex lib:w-full lib:cursor-default lib:items-center lib:justify-center lib:bg-popover lib:py-1 lib:[&_svg:not([class*=size-])]:size-4",
|
||||
"fui:top-0 fui:z-10 fui:flex fui:w-full fui:cursor-default fui:items-center fui:justify-center fui:bg-popover fui:py-1 fui:[&_svg:not([class*=size-])]:size-4",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -183,7 +183,7 @@ function SelectScrollDownButton({
|
||||
<SelectPrimitive.ScrollDownArrow
|
||||
data-slot="select-scroll-down-button"
|
||||
className={cn(
|
||||
"lib:bottom-0 lib:z-10 lib:flex lib:w-full lib:cursor-default lib:items-center lib:justify-center lib:bg-popover lib:py-1 lib:[&_svg:not([class*=size-])]:size-4",
|
||||
"fui:bottom-0 fui:z-10 fui:flex fui:w-full fui:cursor-default fui:items-center fui:justify-center fui:bg-popover fui:py-1 fui:[&_svg:not([class*=size-])]:size-4",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -13,7 +13,7 @@ function Separator({
|
||||
data-slot="separator"
|
||||
orientation={orientation}
|
||||
className={cn(
|
||||
"lib:shrink-0 lib:bg-border lib:data-horizontal:h-px lib:data-horizontal:w-full lib:data-vertical:w-px lib:data-vertical:self-stretch",
|
||||
"fui:shrink-0 fui:bg-border fui:data-horizontal:h-px fui:data-horizontal:w-full fui:data-vertical:w-px fui:data-vertical:self-stretch",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -25,7 +25,7 @@ function SheetOverlay({ className, ...props }: SheetPrimitive.Backdrop.Props) {
|
||||
<SheetPrimitive.Backdrop
|
||||
data-slot="sheet-overlay"
|
||||
className={cn(
|
||||
"lib:fixed lib:inset-0 lib:z-50 lib:bg-black/80 lib:transition-opacity lib:duration-150 lib:data-ending-style:opacity-0 lib:data-starting-style:opacity-0 lib:supports-backdrop-filter:backdrop-blur-xs",
|
||||
"fui:fixed fui:inset-0 fui:z-50 fui:bg-black/80 fui:transition-opacity fui:duration-150 fui:data-ending-style:opacity-0 fui:data-starting-style:opacity-0 fui:supports-backdrop-filter:backdrop-blur-xs",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -50,7 +50,7 @@ function SheetContent({
|
||||
data-slot="sheet-content"
|
||||
data-side={side}
|
||||
className={cn(
|
||||
"lib:fixed lib:z-50 lib:flex lib:flex-col lib:bg-popover lib:bg-clip-padding lib:text-sm lib:text-popover-foreground lib:shadow-lg lib:transition lib:duration-200 lib:ease-in-out lib:data-ending-style:opacity-0 lib:data-starting-style:opacity-0 lib:data-[side=bottom]:inset-x-0 lib:data-[side=bottom]:bottom-0 lib:data-[side=bottom]:h-auto lib:data-[side=bottom]:border-t lib:data-[side=bottom]:data-ending-style:translate-y-[2.5rem] lib:data-[side=bottom]:data-starting-style:translate-y-[2.5rem] lib:data-[side=left]:inset-y-0 lib:data-[side=left]:left-0 lib:data-[side=left]:h-full lib:data-[side=left]:w-3/4 lib:data-[side=left]:border-r lib:data-[side=left]:data-ending-style:translate-x-[-2.5rem] lib:data-[side=left]:data-starting-style:translate-x-[-2.5rem] lib:data-[side=right]:inset-y-0 lib:data-[side=right]:right-0 lib:data-[side=right]:h-full lib:data-[side=right]:w-3/4 lib:data-[side=right]:border-l lib:data-[side=right]:data-ending-style:translate-x-[2.5rem] lib:data-[side=right]:data-starting-style:translate-x-[2.5rem] lib:data-[side=top]:inset-x-0 lib:data-[side=top]:top-0 lib:data-[side=top]:h-auto lib:data-[side=top]:border-b lib:data-[side=top]:data-ending-style:translate-y-[-2.5rem] lib:data-[side=top]:data-starting-style:translate-y-[-2.5rem] lib:data-[side=left]:sm:max-w-sm lib:data-[side=right]:sm:max-w-sm",
|
||||
"fui:fixed fui:z-50 fui:flex fui:flex-col fui:bg-popover fui:bg-clip-padding fui:text-sm fui:text-popover-foreground fui:shadow-lg fui:transition fui:duration-200 fui:ease-in-out fui:data-ending-style:opacity-0 fui:data-starting-style:opacity-0 fui:data-[side=bottom]:inset-x-0 fui:data-[side=bottom]:bottom-0 fui:data-[side=bottom]:h-auto fui:data-[side=bottom]:border-t fui:data-[side=bottom]:data-ending-style:translate-y-[2.5rem] fui:data-[side=bottom]:data-starting-style:translate-y-[2.5rem] fui:data-[side=left]:inset-y-0 fui:data-[side=left]:left-0 fui:data-[side=left]:h-full fui:data-[side=left]:w-3/4 fui:data-[side=left]:border-r fui:data-[side=left]:data-ending-style:translate-x-[-2.5rem] fui:data-[side=left]:data-starting-style:translate-x-[-2.5rem] fui:data-[side=right]:inset-y-0 fui:data-[side=right]:right-0 fui:data-[side=right]:h-full fui:data-[side=right]:w-3/4 fui:data-[side=right]:border-l fui:data-[side=right]:data-ending-style:translate-x-[2.5rem] fui:data-[side=right]:data-starting-style:translate-x-[2.5rem] fui:data-[side=top]:inset-x-0 fui:data-[side=top]:top-0 fui:data-[side=top]:h-auto fui:data-[side=top]:border-b fui:data-[side=top]:data-ending-style:translate-y-[-2.5rem] fui:data-[side=top]:data-starting-style:translate-y-[-2.5rem] fui:data-[side=left]:sm:max-w-sm fui:data-[side=right]:sm:max-w-sm",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -62,13 +62,13 @@ function SheetContent({
|
||||
render={
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="lib:absolute lib:top-4 lib:right-4"
|
||||
className="fui:absolute fui:top-4 fui:right-4"
|
||||
size="icon-sm"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<XIcon />
|
||||
<span className="lib:sr-only">Close</span>
|
||||
<span className="fui:sr-only">Close</span>
|
||||
</SheetPrimitive.Close>
|
||||
)}
|
||||
</SheetPrimitive.Popup>
|
||||
@@ -80,7 +80,7 @@ function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="sheet-header"
|
||||
className={cn("lib:flex lib:flex-col lib:gap-1.5 lib:p-6", className)}
|
||||
className={cn("fui:flex fui:flex-col fui:gap-1.5 fui:p-6", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -91,7 +91,7 @@ function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||
<div
|
||||
data-slot="sheet-footer"
|
||||
className={cn(
|
||||
"lib:mt-auto lib:flex lib:flex-col lib:gap-2 lib:p-6",
|
||||
"fui:mt-auto fui:flex fui:flex-col fui:gap-2 fui:p-6",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -104,7 +104,7 @@ function SheetTitle({ className, ...props }: SheetPrimitive.Title.Props) {
|
||||
<SheetPrimitive.Title
|
||||
data-slot="sheet-title"
|
||||
className={cn(
|
||||
"lib:font-heading lib:text-base lib:font-medium lib:text-foreground",
|
||||
"fui:font-heading fui:text-base fui:font-medium fui:text-foreground",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -119,7 +119,7 @@ function SheetDescription({
|
||||
return (
|
||||
<SheetPrimitive.Description
|
||||
data-slot="sheet-description"
|
||||
className={cn("lib:text-sm lib:text-muted-foreground", className)}
|
||||
className={cn("fui:text-sm fui:text-muted-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
+48
-48
@@ -133,7 +133,7 @@ function SidebarProvider({
|
||||
} as React.CSSProperties
|
||||
}
|
||||
className={cn(
|
||||
"lib:group/sidebar-wrapper lib:flex lib:min-h-svh lib:w-full lib:has-data-[variant=inset]:bg-sidebar",
|
||||
"fui:group/sidebar-wrapper fui:flex fui:min-h-svh fui:w-full fui:has-data-[variant=inset]:bg-sidebar",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -164,7 +164,7 @@ function Sidebar({
|
||||
<div
|
||||
data-slot="sidebar"
|
||||
className={cn(
|
||||
"lib:flex lib:h-full lib:w-(--sidebar-width) lib:flex-col lib:bg-sidebar lib:text-sidebar-foreground",
|
||||
"fui:flex fui:h-full fui:w-(--sidebar-width) fui:flex-col fui:bg-sidebar fui:text-sidebar-foreground",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -182,7 +182,7 @@ function Sidebar({
|
||||
data-sidebar="sidebar"
|
||||
data-slot="sidebar"
|
||||
data-mobile="true"
|
||||
className="lib:w-(--sidebar-width) lib:bg-sidebar lib:p-0 lib:text-sidebar-foreground lib:[&>button]:hidden"
|
||||
className="fui:w-(--sidebar-width) fui:bg-sidebar fui:p-0 fui:text-sidebar-foreground fui:[&>button]:hidden"
|
||||
style={
|
||||
{
|
||||
"--sidebar-width": SIDEBAR_WIDTH_MOBILE,
|
||||
@@ -190,11 +190,11 @@ function Sidebar({
|
||||
}
|
||||
side={side}
|
||||
>
|
||||
<SheetHeader className="lib:sr-only">
|
||||
<SheetHeader className="fui:sr-only">
|
||||
<SheetTitle>Sidebar</SheetTitle>
|
||||
<SheetDescription>Displays the mobile sidebar.</SheetDescription>
|
||||
</SheetHeader>
|
||||
<div className="lib:flex lib:h-full lib:w-full lib:flex-col">
|
||||
<div className="fui:flex fui:h-full fui:w-full fui:flex-col">
|
||||
{children}
|
||||
</div>
|
||||
</SheetContent>
|
||||
@@ -204,7 +204,7 @@ function Sidebar({
|
||||
|
||||
return (
|
||||
<div
|
||||
className="lib:group lib:peer lib:hidden lib:text-sidebar-foreground lib:md:block"
|
||||
className="fui:group fui:peer fui:hidden fui:text-sidebar-foreground fui:md:block"
|
||||
data-state={state}
|
||||
data-collapsible={state === "collapsed" ? collapsible : ""}
|
||||
data-variant={variant}
|
||||
@@ -215,23 +215,23 @@ function Sidebar({
|
||||
<div
|
||||
data-slot="sidebar-gap"
|
||||
className={cn(
|
||||
"lib:relative lib:w-(--sidebar-width) lib:bg-transparent lib:transition-[width] lib:duration-200 lib:ease-linear",
|
||||
"lib:group-data-[collapsible=offcanvas]:w-0",
|
||||
"lib:group-data-[side=right]:rotate-180",
|
||||
"fui:relative fui:w-(--sidebar-width) fui:bg-transparent fui:transition-[width] fui:duration-200 fui:ease-linear",
|
||||
"fui:group-data-[collapsible=offcanvas]:w-0",
|
||||
"fui:group-data-[side=right]:rotate-180",
|
||||
variant === "floating" || variant === "inset"
|
||||
? "lib:group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]"
|
||||
: "lib:group-data-[collapsible=icon]:w-(--sidebar-width-icon)",
|
||||
? "fui:group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]"
|
||||
: "fui:group-data-[collapsible=icon]:w-(--sidebar-width-icon)",
|
||||
)}
|
||||
/>
|
||||
<div
|
||||
data-slot="sidebar-container"
|
||||
data-side={side}
|
||||
className={cn(
|
||||
"lib:fixed lib:inset-y-0 lib:z-10 lib:hidden lib:h-svh lib:w-(--sidebar-width) lib:transition-[left,right,width] lib:duration-200 lib:ease-linear lib:data-[side=left]:left-0 lib:data-[side=left]:group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)] lib:data-[side=right]:right-0 lib:data-[side=right]:group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)] lib:md:flex",
|
||||
"fui:fixed fui:inset-y-0 fui:z-10 fui:hidden fui:h-svh fui:w-(--sidebar-width) fui:transition-[left,right,width] fui:duration-200 fui:ease-linear fui:data-[side=left]:left-0 fui:data-[side=left]:group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)] fui:data-[side=right]:right-0 fui:data-[side=right]:group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)] fui:md:flex",
|
||||
// Adjust the padding for floating and inset variants.
|
||||
variant === "floating" || variant === "inset"
|
||||
? "lib:p-2 lib:group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]"
|
||||
: "lib:group-data-[collapsible=icon]:w-(--sidebar-width-icon) lib:group-data-[side=left]:border-r lib:group-data-[side=right]:border-l",
|
||||
? "fui:p-2 fui:group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]"
|
||||
: "fui:group-data-[collapsible=icon]:w-(--sidebar-width-icon) fui:group-data-[side=left]:border-r fui:group-data-[side=right]:border-l",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -239,7 +239,7 @@ function Sidebar({
|
||||
<div
|
||||
data-sidebar="sidebar"
|
||||
data-slot="sidebar-inner"
|
||||
className="lib:flex lib:size-full lib:flex-col lib:bg-sidebar lib:group-data-[variant=floating]:rounded-lg lib:group-data-[variant=floating]:shadow-sm lib:group-data-[variant=floating]:ring-1 lib:group-data-[variant=floating]:ring-sidebar-border"
|
||||
className="fui:flex fui:size-full fui:flex-col fui:bg-sidebar fui:group-data-[variant=floating]:rounded-lg fui:group-data-[variant=floating]:shadow-sm fui:group-data-[variant=floating]:ring-1 fui:group-data-[variant=floating]:ring-sidebar-border"
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
@@ -269,7 +269,7 @@ function SidebarTrigger({
|
||||
{...props}
|
||||
>
|
||||
<PanelLeftIcon />
|
||||
<span className="lib:sr-only">Toggle Sidebar</span>
|
||||
<span className="fui:sr-only">Toggle Sidebar</span>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
@@ -286,12 +286,12 @@ function SidebarRail({ className, ...props }: React.ComponentProps<"button">) {
|
||||
onClick={toggleSidebar}
|
||||
title="Toggle Sidebar"
|
||||
className={cn(
|
||||
"lib:absolute lib:inset-y-0 lib:z-20 lib:hidden lib:w-4 lib:transition-all lib:ease-linear lib:group-data-[side=left]:-right-4 lib:group-data-[side=right]:left-0 lib:after:absolute lib:after:inset-y-0 lib:after:start-1/2 lib:after:w-[2px] lib:hover:after:bg-sidebar-border lib:sm:flex lib:ltr:-translate-x-1/2 lib:rtl:-translate-x-1/2",
|
||||
"lib:in-data-[side=left]:cursor-w-resize lib:in-data-[side=right]:cursor-e-resize",
|
||||
"lib:[[data-side=left][data-state=collapsed]_&]:cursor-e-resize lib:[[data-side=right][data-state=collapsed]_&]:cursor-w-resize",
|
||||
"lib:group-data-[collapsible=offcanvas]:translate-x-0 lib:group-data-[collapsible=offcanvas]:after:left-full lib:hover:group-data-[collapsible=offcanvas]:bg-sidebar",
|
||||
"lib:[[data-side=left][data-collapsible=offcanvas]_&]:-right-2",
|
||||
"lib:[[data-side=right][data-collapsible=offcanvas]_&]:-left-2",
|
||||
"fui:absolute fui:inset-y-0 fui:z-20 fui:hidden fui:w-4 fui:transition-all fui:ease-linear fui:group-data-[side=left]:-right-4 fui:group-data-[side=right]:left-0 fui:after:absolute fui:after:inset-y-0 fui:after:start-1/2 fui:after:w-[2px] fui:hover:after:bg-sidebar-border fui:sm:flex fui:ltr:-translate-x-1/2 fui:rtl:-translate-x-1/2",
|
||||
"fui:in-data-[side=left]:cursor-w-resize fui:in-data-[side=right]:cursor-e-resize",
|
||||
"fui:[[data-side=left][data-state=collapsed]_&]:cursor-e-resize fui:[[data-side=right][data-state=collapsed]_&]:cursor-w-resize",
|
||||
"fui:group-data-[collapsible=offcanvas]:translate-x-0 fui:group-data-[collapsible=offcanvas]:after:left-full fui:hover:group-data-[collapsible=offcanvas]:bg-sidebar",
|
||||
"fui:[[data-side=left][data-collapsible=offcanvas]_&]:-right-2",
|
||||
"fui:[[data-side=right][data-collapsible=offcanvas]_&]:-left-2",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -304,7 +304,7 @@ function SidebarInset({ className, ...props }: React.ComponentProps<"main">) {
|
||||
<main
|
||||
data-slot="sidebar-inset"
|
||||
className={cn(
|
||||
"lib:relative lib:flex lib:w-full lib:flex-1 lib:flex-col lib:bg-background lib:md:peer-data-[variant=inset]:m-2 lib:md:peer-data-[variant=inset]:ml-0 lib:md:peer-data-[variant=inset]:rounded-xl lib:md:peer-data-[variant=inset]:shadow-sm lib:md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2",
|
||||
"fui:relative fui:flex fui:w-full fui:flex-1 fui:flex-col fui:bg-background fui:md:peer-data-[variant=inset]:m-2 fui:md:peer-data-[variant=inset]:ml-0 fui:md:peer-data-[variant=inset]:rounded-xl fui:md:peer-data-[variant=inset]:shadow-sm fui:md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -321,7 +321,7 @@ function SidebarInput({
|
||||
data-slot="sidebar-input"
|
||||
data-sidebar="input"
|
||||
className={cn(
|
||||
"lib:h-8 lib:w-full lib:bg-background lib:shadow-none",
|
||||
"fui:h-8 fui:w-full fui:bg-background fui:shadow-none",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -335,7 +335,7 @@ function SidebarHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||
data-slot="sidebar-header"
|
||||
data-sidebar="header"
|
||||
className={cn(
|
||||
"lib:flex lib:flex-col lib:gap-2 lib:p-2 lib:[--radius:var(--radius-xl)]",
|
||||
"fui:flex fui:flex-col fui:gap-2 fui:p-2 fui:[--radius:var(--radius-xl)]",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -348,7 +348,7 @@ function SidebarFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||
<div
|
||||
data-slot="sidebar-footer"
|
||||
data-sidebar="footer"
|
||||
className={cn("lib:flex lib:flex-col lib:gap-2 lib:p-2", className)}
|
||||
className={cn("fui:flex fui:flex-col fui:gap-2 fui:p-2", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -362,7 +362,7 @@ function SidebarSeparator({
|
||||
<Separator
|
||||
data-slot="sidebar-separator"
|
||||
data-sidebar="separator"
|
||||
className={cn("lib:mx-2 lib:w-auto lib:bg-sidebar-border", className)}
|
||||
className={cn("fui:mx-2 fui:w-auto fui:bg-sidebar-border", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -374,7 +374,7 @@ function SidebarContent({ className, ...props }: React.ComponentProps<"div">) {
|
||||
data-slot="sidebar-content"
|
||||
data-sidebar="content"
|
||||
className={cn(
|
||||
"lib:no-scrollbar lib:flex lib:min-h-0 lib:flex-1 lib:flex-col lib:gap-2 lib:overflow-auto lib:[--radius:var(--radius-xl)] lib:group-data-[collapsible=icon]:overflow-hidden",
|
||||
"fui:no-scrollbar fui:flex fui:min-h-0 fui:flex-1 fui:flex-col fui:gap-2 fui:overflow-auto fui:[--radius:var(--radius-xl)] fui:group-data-[collapsible=icon]:overflow-hidden",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -388,7 +388,7 @@ function SidebarGroup({ className, ...props }: React.ComponentProps<"div">) {
|
||||
data-slot="sidebar-group"
|
||||
data-sidebar="group"
|
||||
className={cn(
|
||||
"lib:relative lib:flex lib:w-full lib:min-w-0 lib:flex-col lib:p-2",
|
||||
"fui:relative fui:flex fui:w-full fui:min-w-0 fui:flex-col fui:p-2",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -406,7 +406,7 @@ function SidebarGroupLabel({
|
||||
props: mergeProps<"div">(
|
||||
{
|
||||
className: cn(
|
||||
"flex h-8 shrink-0 items-center rounded-md px-3 text-xs font-medium text-sidebar-foreground/70 ring-sidebar-ring outline-hidden transition-[margin,opacity] duration-200 ease-linear group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0 focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
|
||||
"fui:flex fui:h-8 fui:shrink-0 fui:items-center fui:rounded-md fui:px-3 fui:text-xs fui:font-medium fui:text-sidebar-foreground/70 fui:ring-sidebar-ring fui:outline-hidden fui:transition-[margin,opacity] fui:duration-200 fui:ease-linear fui:group-data-[collapsible=icon]:-mt-8 fui:group-data-[collapsible=icon]:opacity-0 fui:focus-visible:ring-2 fui:[&>svg]:size-4 fui:[&>svg]:shrink-0",
|
||||
className,
|
||||
),
|
||||
},
|
||||
@@ -430,7 +430,7 @@ function SidebarGroupAction({
|
||||
props: mergeProps<"button">(
|
||||
{
|
||||
className: cn(
|
||||
"absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground ring-sidebar-ring outline-hidden transition-transform group-data-[collapsible=icon]:hidden after:absolute after:-inset-2 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 md:after:hidden [&>svg]:size-4 [&>svg]:shrink-0",
|
||||
"fui:absolute fui:top-3.5 fui:right-3 fui:flex fui:aspect-square fui:w-5 fui:items-center fui:justify-center fui:rounded-md fui:p-0 fui:text-sidebar-foreground fui:ring-sidebar-ring fui:outline-hidden fui:transition-transform fui:group-data-[collapsible=icon]:hidden fui:after:absolute fui:after:-inset-2 fui:hover:bg-sidebar-accent fui:hover:text-sidebar-accent-foreground fui:focus-visible:ring-2 fui:md:after:hidden fui:[&>svg]:size-4 fui:[&>svg]:shrink-0",
|
||||
className,
|
||||
),
|
||||
},
|
||||
@@ -452,7 +452,7 @@ function SidebarGroupContent({
|
||||
<div
|
||||
data-slot="sidebar-group-content"
|
||||
data-sidebar="group-content"
|
||||
className={cn("lib:w-full lib:text-sm", className)}
|
||||
className={cn("fui:w-full fui:text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -464,7 +464,7 @@ function SidebarMenu({ className, ...props }: React.ComponentProps<"ul">) {
|
||||
data-slot="sidebar-menu"
|
||||
data-sidebar="menu"
|
||||
className={cn(
|
||||
"lib:flex lib:w-full lib:min-w-0 lib:flex-col lib:gap-1",
|
||||
"fui:flex fui:w-full fui:min-w-0 fui:flex-col fui:gap-1",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -477,26 +477,26 @@ function SidebarMenuItem({ className, ...props }: React.ComponentProps<"li">) {
|
||||
<li
|
||||
data-slot="sidebar-menu-item"
|
||||
data-sidebar="menu-item"
|
||||
className={cn("lib:group/menu-item lib:relative", className)}
|
||||
className={cn("fui:group/menu-item fui:relative", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const sidebarMenuButtonVariants = cva(
|
||||
"lib:peer/menu-button lib:group/menu-button lib:flex lib:w-full lib:items-center lib:gap-2 lib:overflow-hidden lib:rounded-lg lib:px-3 lib:py-2 lib:text-left lib:text-sm lib:ring-sidebar-ring lib:outline-hidden lib:transition-[width,height,padding] lib:group-has-data-[sidebar=menu-action]/menu-item:pr-8 lib:group-data-[collapsible=icon]:size-8! lib:group-data-[collapsible=icon]:p-2! lib:hover:bg-sidebar-accent lib:hover:text-sidebar-accent-foreground lib:focus-visible:ring-2 lib:active:bg-sidebar-accent lib:active:text-sidebar-accent-foreground lib:disabled:pointer-events-none lib:disabled:opacity-50 lib:aria-disabled:pointer-events-none lib:aria-disabled:opacity-50 lib:data-open:hover:bg-sidebar-accent lib:data-open:hover:text-sidebar-accent-foreground lib:data-active:bg-sidebar-accent lib:data-active:font-medium lib:data-active:text-sidebar-accent-foreground lib:[&_svg]:size-4 lib:[&_svg]:shrink-0 lib:[&>span:last-child]:truncate",
|
||||
"fui:peer/menu-button fui:group/menu-button fui:flex fui:w-full fui:items-center fui:gap-2 fui:overflow-hidden fui:rounded-lg fui:px-3 fui:py-2 fui:text-left fui:text-sm fui:ring-sidebar-ring fui:outline-hidden fui:transition-[width,height,padding] fui:group-has-data-[sidebar=menu-action]/menu-item:pr-8 fui:group-data-[collapsible=icon]:size-8! fui:group-data-[collapsible=icon]:p-2! fui:hover:bg-sidebar-accent fui:hover:text-sidebar-accent-foreground fui:focus-visible:ring-2 fui:active:bg-sidebar-accent fui:active:text-sidebar-accent-foreground fui:disabled:pointer-events-none fui:disabled:opacity-50 fui:aria-disabled:pointer-events-none fui:aria-disabled:opacity-50 fui:data-open:hover:bg-sidebar-accent fui:data-open:hover:text-sidebar-accent-foreground fui:data-active:bg-sidebar-accent fui:data-active:font-medium fui:data-active:text-sidebar-accent-foreground fui:[&_svg]:size-4 fui:[&_svg]:shrink-0 fui:[&>span:last-child]:truncate",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"lib:hover:bg-sidebar-accent lib:hover:text-sidebar-accent-foreground",
|
||||
"fui:hover:bg-sidebar-accent fui:hover:text-sidebar-accent-foreground",
|
||||
outline:
|
||||
"lib:bg-background lib:shadow-[0_0_0_1px_var(--sidebar-border)] lib:hover:bg-sidebar-accent lib:hover:text-sidebar-accent-foreground lib:hover:shadow-[0_0_0_1px_var(--sidebar-accent)]",
|
||||
"fui:bg-background fui:shadow-[0_0_0_1px_var(--sidebar-border)] fui:hover:bg-sidebar-accent fui:hover:text-sidebar-accent-foreground fui:hover:shadow-[0_0_0_1px_var(--sidebar-accent)]",
|
||||
},
|
||||
size: {
|
||||
default: "lib:h-9 lib:text-sm",
|
||||
sm: "lib:h-8 lib:text-xs",
|
||||
lg: "lib:h-14 lib:px-3 lib:text-sm lib:group-data-[collapsible=icon]:p-0!",
|
||||
default: "fui:h-9 fui:text-sm",
|
||||
sm: "fui:h-8 fui:text-xs",
|
||||
lg: "fui:h-14 fui:px-3 fui:text-sm fui:group-data-[collapsible=icon]:p-0!",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
@@ -574,7 +574,7 @@ function SidebarMenuAction({
|
||||
props: mergeProps<"button">(
|
||||
{
|
||||
className: cn(
|
||||
"absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground ring-sidebar-ring outline-hidden transition-transform group-data-[collapsible=icon]:hidden peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[size=default]/menu-button:top-2 peer-data-[size=lg]/menu-button:top-2.5 peer-data-[size=sm]/menu-button:top-1 after:absolute after:-inset-2 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 md:after:hidden [&>svg]:size-4 [&>svg]:shrink-0",
|
||||
"fui:absolute fui:top-1.5 fui:right-1 fui:flex fui:aspect-square fui:w-5 fui:items-center fui:justify-center fui:rounded-md fui:p-0 fui:text-sidebar-foreground fui:ring-sidebar-ring fui:outline-hidden fui:transition-transform fui:group-data-[collapsible=icon]:hidden fui:peer-hover/menu-button:text-sidebar-accent-foreground fui:peer-data-[size=default]/menu-button:top-2 fui:peer-data-[size=lg]/menu-button:top-2.5 fui:peer-data-[size=sm]/menu-button:top-1 fui:after:absolute fui:after:-inset-2 fui:hover:bg-sidebar-accent fui:hover:text-sidebar-accent-foreground fui:focus-visible:ring-2 fui:md:after:hidden fui:[&>svg]:size-4 fui:[&>svg]:shrink-0",
|
||||
showOnHover &&
|
||||
"group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 peer-data-active/menu-button:text-sidebar-accent-foreground aria-expanded:opacity-100 md:opacity-0",
|
||||
className,
|
||||
@@ -599,7 +599,7 @@ function SidebarMenuBadge({
|
||||
data-slot="sidebar-menu-badge"
|
||||
data-sidebar="menu-badge"
|
||||
className={cn(
|
||||
"lib:pointer-events-none lib:absolute lib:right-1 lib:flex lib:h-5 lib:min-w-5 lib:items-center lib:justify-center lib:rounded-md lib:px-1 lib:text-xs lib:font-medium lib:text-sidebar-foreground lib:tabular-nums lib:select-none lib:group-data-[collapsible=icon]:hidden lib:peer-hover/menu-button:text-sidebar-accent-foreground lib:peer-data-[size=default]/menu-button:top-1.5 lib:peer-data-[size=lg]/menu-button:top-2.5 lib:peer-data-[size=sm]/menu-button:top-1 lib:peer-data-active/menu-button:text-sidebar-accent-foreground",
|
||||
"fui:pointer-events-none fui:absolute fui:right-1 fui:flex fui:h-5 fui:min-w-5 fui:items-center fui:justify-center fui:rounded-md fui:px-1 fui:text-xs fui:font-medium fui:text-sidebar-foreground fui:tabular-nums fui:select-none fui:group-data-[collapsible=icon]:hidden fui:peer-hover/menu-button:text-sidebar-accent-foreground fui:peer-data-[size=default]/menu-button:top-1.5 fui:peer-data-[size=lg]/menu-button:top-2.5 fui:peer-data-[size=sm]/menu-button:top-1 fui:peer-data-active/menu-button:text-sidebar-accent-foreground",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -624,19 +624,19 @@ function SidebarMenuSkeleton({
|
||||
data-slot="sidebar-menu-skeleton"
|
||||
data-sidebar="menu-skeleton"
|
||||
className={cn(
|
||||
"lib:flex lib:h-8 lib:items-center lib:gap-2 lib:rounded-md lib:px-2",
|
||||
"fui:flex fui:h-8 fui:items-center fui:gap-2 fui:rounded-md fui:px-2",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{showIcon && (
|
||||
<Skeleton
|
||||
className="lib:size-4 lib:rounded-md"
|
||||
className="fui:size-4 fui:rounded-md"
|
||||
data-sidebar="menu-skeleton-icon"
|
||||
/>
|
||||
)}
|
||||
<Skeleton
|
||||
className="lib:h-4 lib:max-w-(--skeleton-width) lib:flex-1"
|
||||
className="fui:h-4 fui:max-w-(--skeleton-width) fui:flex-1"
|
||||
data-sidebar="menu-skeleton-text"
|
||||
style={
|
||||
{
|
||||
@@ -654,7 +654,7 @@ function SidebarMenuSub({ className, ...props }: React.ComponentProps<"ul">) {
|
||||
data-slot="sidebar-menu-sub"
|
||||
data-sidebar="menu-sub"
|
||||
className={cn(
|
||||
"lib:mx-3.5 lib:flex lib:min-w-0 lib:translate-x-px lib:flex-col lib:gap-1 lib:border-l lib:border-sidebar-border lib:px-2.5 lib:py-0.5 lib:group-data-[collapsible=icon]:hidden",
|
||||
"fui:mx-3.5 fui:flex fui:min-w-0 fui:translate-x-px fui:flex-col fui:gap-1 fui:border-l fui:border-sidebar-border fui:px-2.5 fui:py-0.5 fui:group-data-[collapsible=icon]:hidden",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -670,7 +670,7 @@ function SidebarMenuSubItem({
|
||||
<li
|
||||
data-slot="sidebar-menu-sub-item"
|
||||
data-sidebar="menu-sub-item"
|
||||
className={cn("lib:group/menu-sub-item lib:relative", className)}
|
||||
className={cn("fui:group/menu-sub-item fui:relative", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -692,7 +692,7 @@ function SidebarMenuSubButton({
|
||||
props: mergeProps<"a">(
|
||||
{
|
||||
className: cn(
|
||||
"flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground ring-sidebar-ring outline-hidden group-data-[collapsible=icon]:hidden hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[size=md]:text-sm data-[size=sm]:text-xs data-active:bg-sidebar-accent data-active:text-sidebar-accent-foreground [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground",
|
||||
"fui:flex fui:h-7 fui:min-w-0 fui:-translate-x-px fui:items-center fui:gap-2 fui:overflow-hidden fui:rounded-md fui:px-2 fui:text-sidebar-foreground fui:ring-sidebar-ring fui:outline-hidden fui:group-data-[collapsible=icon]:hidden fui:hover:bg-sidebar-accent fui:hover:text-sidebar-accent-foreground fui:focus-visible:ring-2 fui:active:bg-sidebar-accent fui:active:text-sidebar-accent-foreground fui:disabled:pointer-events-none fui:disabled:opacity-50 fui:aria-disabled:pointer-events-none fui:aria-disabled:opacity-50 fui:data-[size=md]:text-sm fui:data-[size=sm]:text-xs fui:data-active:bg-sidebar-accent fui:data-active:text-sidebar-accent-foreground fui:[&>span:last-child]:truncate fui:[&>svg]:size-4 fui:[&>svg]:shrink-0 fui:[&>svg]:text-sidebar-accent-foreground",
|
||||
className,
|
||||
),
|
||||
},
|
||||
|
||||
@@ -4,7 +4,7 @@ function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="skeleton"
|
||||
className={cn("lib:animate-pulse lib:rounded-xl lib:bg-muted", className)}
|
||||
className={cn("fui:animate-pulse fui:rounded-xl fui:bg-muted", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -18,7 +18,7 @@ function Slider({
|
||||
return (
|
||||
<SliderPrimitive.Root
|
||||
className={cn(
|
||||
"lib:data-horizontal:w-full lib:data-vertical:h-full",
|
||||
"fui:data-horizontal:w-full fui:data-vertical:h-full",
|
||||
className,
|
||||
)}
|
||||
data-slot="slider"
|
||||
@@ -29,21 +29,21 @@ function Slider({
|
||||
thumbAlignment="edge"
|
||||
{...props}
|
||||
>
|
||||
<SliderPrimitive.Control className="lib:relative lib:flex lib:w-full lib:touch-none lib:items-center lib:select-none lib:data-disabled:opacity-50 lib:data-vertical:h-full lib:data-vertical:min-h-40 lib:data-vertical:w-auto lib:data-vertical:flex-col">
|
||||
<SliderPrimitive.Control className="fui:relative fui:flex fui:w-full fui:touch-none fui:items-center fui:select-none fui:data-disabled:opacity-50 fui:data-vertical:h-full fui:data-vertical:min-h-40 fui:data-vertical:w-auto fui:data-vertical:flex-col">
|
||||
<SliderPrimitive.Track
|
||||
data-slot="slider-track"
|
||||
className="lib:relative lib:grow lib:overflow-hidden lib:rounded-4xl lib:bg-muted lib:select-none lib:data-horizontal:h-3 lib:data-horizontal:w-full lib:data-vertical:h-full lib:data-vertical:w-3"
|
||||
className="fui:relative fui:grow fui:overflow-hidden fui:rounded-4xl fui:bg-muted fui:select-none fui:data-horizontal:h-3 fui:data-horizontal:w-full fui:data-vertical:h-full fui:data-vertical:w-3"
|
||||
>
|
||||
<SliderPrimitive.Indicator
|
||||
data-slot="slider-range"
|
||||
className="lib:bg-primary lib:select-none lib:data-horizontal:h-full lib:data-vertical:w-full"
|
||||
className="fui:bg-primary fui:select-none fui:data-horizontal:h-full fui:data-vertical:w-full"
|
||||
/>
|
||||
</SliderPrimitive.Track>
|
||||
{Array.from({ length: _values.length }, (_, index) => (
|
||||
<SliderPrimitive.Thumb
|
||||
data-slot="slider-thumb"
|
||||
key={index}
|
||||
className="lib:block lib:size-4 lib:shrink-0 lib:rounded-4xl lib:border lib:border-primary lib:bg-white lib:shadow-sm lib:ring-ring/50 lib:transition-colors lib:select-none lib:hover:ring-4 lib:focus-visible:ring-4 lib:focus-visible:outline-hidden lib:disabled:pointer-events-none lib:disabled:opacity-50"
|
||||
className="fui:block fui:size-4 fui:shrink-0 fui:rounded-4xl fui:border fui:border-primary fui:bg-white fui:shadow-sm fui:ring-ring/50 fui:transition-colors fui:select-none fui:hover:ring-4 fui:focus-visible:ring-4 fui:focus-visible:outline-hidden fui:disabled:pointer-events-none fui:disabled:opacity-50"
|
||||
/>
|
||||
))}
|
||||
</SliderPrimitive.Control>
|
||||
|
||||
@@ -16,13 +16,13 @@ const Toaster = ({ ...props }: ToasterProps) => {
|
||||
return (
|
||||
<Sonner
|
||||
theme={theme as ToasterProps["theme"]}
|
||||
className="lib:toaster lib:group"
|
||||
className="fui:toaster fui:group"
|
||||
icons={{
|
||||
success: <CircleCheckIcon className="lib:size-4" />,
|
||||
info: <InfoIcon className="lib:size-4" />,
|
||||
warning: <TriangleAlertIcon className="lib:size-4" />,
|
||||
error: <OctagonXIcon className="lib:size-4" />,
|
||||
loading: <Loader2Icon className="lib:size-4 lib:animate-spin" />,
|
||||
success: <CircleCheckIcon className="fui:size-4" />,
|
||||
info: <InfoIcon className="fui:size-4" />,
|
||||
warning: <TriangleAlertIcon className="fui:size-4" />,
|
||||
error: <OctagonXIcon className="fui:size-4" />,
|
||||
loading: <Loader2Icon className="fui:size-4 fui:animate-spin" />,
|
||||
}}
|
||||
style={
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@ function Spinner({ className, ...props }: React.ComponentProps<"svg">) {
|
||||
data-slot="spinner"
|
||||
role="status"
|
||||
aria-label="Loading"
|
||||
className={cn("lib:size-4 lib:animate-spin", className)}
|
||||
className={cn("fui:size-4 fui:animate-spin", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -13,14 +13,14 @@ function Switch({
|
||||
data-slot="switch"
|
||||
data-size={size}
|
||||
className={cn(
|
||||
"lib:peer lib:group/switch lib:relative lib:inline-flex lib:shrink-0 lib:items-center lib:rounded-full lib:border lib:border-transparent lib:transition-all lib:outline-none lib:group-has-[:focus-visible]/field-label:border-transparent lib:group-has-[:focus-visible]/field-label:ring-0 lib:after:absolute lib:after:-inset-x-3 lib:after:-inset-y-2 lib:focus-visible:border-ring lib:focus-visible:ring-[3px] lib:focus-visible:ring-ring/50 lib:aria-invalid:border-destructive lib:aria-invalid:ring-[3px] lib:aria-invalid:ring-destructive/20 lib:data-[size=default]:h-[18.4px] lib:data-[size=default]:w-[32px] lib:data-[size=sm]:h-[14px] lib:data-[size=sm]:w-[24px] lib:dark:aria-invalid:border-destructive/50 lib:dark:aria-invalid:ring-destructive/40 lib:data-checked:bg-primary lib:data-unchecked:bg-input lib:dark:data-unchecked:bg-input/80 lib:data-disabled:cursor-not-allowed lib:data-disabled:opacity-50",
|
||||
"fui:peer fui:group/switch fui:relative fui:inline-flex fui:shrink-0 fui:items-center fui:rounded-full fui:border fui:border-transparent fui:transition-all fui:outline-none fui:group-has-[:focus-visible]/field-label:border-transparent fui:group-has-[:focus-visible]/field-label:ring-0 fui:after:absolute fui:after:-inset-x-3 fui:after:-inset-y-2 fui:focus-visible:border-ring fui:focus-visible:ring-[3px] fui:focus-visible:ring-ring/50 fui:aria-invalid:border-destructive fui:aria-invalid:ring-[3px] fui:aria-invalid:ring-destructive/20 fui:data-[size=default]:h-[18.4px] fui:data-[size=default]:w-[32px] fui:data-[size=sm]:h-[14px] fui:data-[size=sm]:w-[24px] fui:dark:aria-invalid:border-destructive/50 fui:dark:aria-invalid:ring-destructive/40 fui:data-checked:bg-primary fui:data-unchecked:bg-input fui:dark:data-unchecked:bg-input/80 fui:data-disabled:cursor-not-allowed fui:data-disabled:opacity-50",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<SwitchPrimitive.Thumb
|
||||
data-slot="switch-thumb"
|
||||
className="lib:pointer-events-none lib:block lib:rounded-full lib:bg-background lib:ring-0 lib:transition-transform lib:group-data-[size=default]/switch:size-4 lib:group-data-[size=sm]/switch:size-3 lib:group-data-[size=default]/switch:data-checked:translate-x-[calc(100%-2px)] lib:group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] lib:dark:data-checked:bg-primary-foreground lib:group-data-[size=default]/switch:data-unchecked:translate-x-0 lib:group-data-[size=sm]/switch:data-unchecked:translate-x-0 lib:dark:data-unchecked:bg-foreground"
|
||||
className="fui:pointer-events-none fui:block fui:rounded-full fui:bg-background fui:ring-0 fui:transition-transform fui:group-data-[size=default]/switch:size-4 fui:group-data-[size=sm]/switch:size-3 fui:group-data-[size=default]/switch:data-checked:translate-x-[calc(100%-2px)] fui:group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] fui:dark:data-checked:bg-primary-foreground fui:group-data-[size=default]/switch:data-unchecked:translate-x-0 fui:group-data-[size=sm]/switch:data-unchecked:translate-x-0 fui:dark:data-unchecked:bg-foreground"
|
||||
/>
|
||||
</SwitchPrimitive.Root>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
import { cn } from "cn";
|
||||
import { CheckIcon, MinusIcon, XIcon } from "lucide-react";
|
||||
import type * as React from "react";
|
||||
import { Badge } from "@/components/badge";
|
||||
|
||||
/**
|
||||
* Cell renderers for `DataTable` and `Table`.
|
||||
*
|
||||
* A listing is scanned far more often than it is read line by line, so the
|
||||
* values that repeat down a column — flags, kinds, states — are better as a
|
||||
* glyph or a tag than as a word. Anything carried by shape or colour alone
|
||||
* also carries text for assistive tech.
|
||||
*/
|
||||
|
||||
type BadgeVariant = React.ComponentProps<typeof Badge>["variant"];
|
||||
|
||||
/**
|
||||
* A yes/no flag.
|
||||
*
|
||||
* A column of "Yes"/"No" reads as a wall of text and gives the eye nothing to
|
||||
* lock onto; a tick against a muted cross does. `undefined` means the row has
|
||||
* no value, which is not the same as a definite "no", so it gets a dash.
|
||||
*
|
||||
* `label` names what the flag means, because a tick on its own tells a screen
|
||||
* reader nothing.
|
||||
*/
|
||||
function BooleanCell({
|
||||
value,
|
||||
label,
|
||||
}: {
|
||||
value: boolean | undefined | null;
|
||||
label: string;
|
||||
}) {
|
||||
if (value === undefined || value === null) {
|
||||
return (
|
||||
<>
|
||||
<MinusIcon
|
||||
className="fui:size-4 fui:text-muted-foreground/60"
|
||||
aria-hidden
|
||||
/>
|
||||
<span className="fui:sr-only">{label}: unknown</span>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{value ? (
|
||||
<CheckIcon className="fui:size-4 fui:text-foreground" aria-hidden />
|
||||
) : (
|
||||
<XIcon
|
||||
className="fui:size-4 fui:text-muted-foreground/60"
|
||||
aria-hidden
|
||||
/>
|
||||
)}
|
||||
<span className="fui:sr-only">
|
||||
{label}: {value ? "yes" : "no"}
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* A single tag.
|
||||
*
|
||||
* `outline` by default: a listing can carry several tag columns, and filled
|
||||
* badges in each turn the table into a colour chart. Spend colour only where
|
||||
* it means something, by passing a `variant`.
|
||||
*/
|
||||
function TagCell({
|
||||
children,
|
||||
variant = "outline",
|
||||
className,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
variant?: BadgeVariant;
|
||||
className?: string;
|
||||
}) {
|
||||
if (children === null || children === undefined || children === "") {
|
||||
return <EmptyCell />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Badge variant={variant} className={cn("fui:font-normal", className)}>
|
||||
{children}
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
|
||||
/** Several tags in one cell, wrapping rather than widening the column. */
|
||||
function TagListCell({
|
||||
values,
|
||||
variant = "outline",
|
||||
empty,
|
||||
}: {
|
||||
values: ReadonlyArray<string>;
|
||||
variant?: BadgeVariant;
|
||||
empty?: React.ReactNode;
|
||||
}) {
|
||||
if (values.length === 0) {
|
||||
return empty !== undefined ? <>{empty}</> : <EmptyCell />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fui:flex fui:flex-wrap fui:gap-1">
|
||||
{values.map((value) => (
|
||||
<TagCell key={value} variant={variant}>
|
||||
{value}
|
||||
</TagCell>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* An identifier: monospaced so digits line up down the column.
|
||||
*
|
||||
* Presentational only. A cell that navigates wraps this in the router's own
|
||||
* link component — taking a route as a string here would mean casting away
|
||||
* whatever type safety that router provides.
|
||||
*/
|
||||
function IdCell({
|
||||
children,
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"span">) {
|
||||
return (
|
||||
<span className={cn("fui:font-mono fui:text-xs", className)} {...props}>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Affordance for a cell whose whole content navigates somewhere, to put on the
|
||||
* router link that wraps it.
|
||||
*/
|
||||
const cellLinkClassName = "fui:text-link fui:hover:underline";
|
||||
|
||||
/** A value the row does not have, rendered so the column keeps its rhythm. */
|
||||
function EmptyCell() {
|
||||
return <span className="fui:text-muted-foreground">—</span>;
|
||||
}
|
||||
|
||||
export {
|
||||
BooleanCell,
|
||||
cellLinkClassName,
|
||||
EmptyCell,
|
||||
IdCell,
|
||||
TagCell,
|
||||
TagListCell,
|
||||
};
|
||||
@@ -7,11 +7,11 @@ function Table({ className, ...props }: React.ComponentProps<"table">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="table-container"
|
||||
className="lib:relative lib:w-full lib:overflow-x-auto"
|
||||
className="fui:relative fui:w-full fui:overflow-x-auto"
|
||||
>
|
||||
<table
|
||||
data-slot="table"
|
||||
className={cn("lib:w-full lib:caption-bottom lib:text-sm", className)}
|
||||
className={cn("fui:w-full fui:caption-bottom fui:text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
@@ -22,7 +22,7 @@ function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
|
||||
return (
|
||||
<thead
|
||||
data-slot="table-header"
|
||||
className={cn("lib:[&_tr]:border-b", className)}
|
||||
className={cn("fui:[&_tr]:border-b", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -32,7 +32,7 @@ function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
|
||||
return (
|
||||
<tbody
|
||||
data-slot="table-body"
|
||||
className={cn("lib:[&_tr:last-child]:border-0", className)}
|
||||
className={cn("fui:[&_tr:last-child]:border-0", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -43,7 +43,7 @@ function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
|
||||
<tfoot
|
||||
data-slot="table-footer"
|
||||
className={cn(
|
||||
"lib:border-t lib:bg-muted/50 lib:font-medium lib:[&>tr]:last:border-b-0",
|
||||
"fui:border-t fui:bg-muted/50 fui:font-medium fui:[&>tr]:last:border-b-0",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -56,7 +56,7 @@ function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
|
||||
<tr
|
||||
data-slot="table-row"
|
||||
className={cn(
|
||||
"lib:border-b lib:transition-colors lib:hover:bg-muted/50 lib:has-aria-expanded:bg-muted/50 lib:data-[state=selected]:bg-muted",
|
||||
"fui:border-b fui:transition-colors fui:hover:bg-muted/50 fui:has-aria-expanded:bg-muted/50 fui:data-[state=selected]:bg-muted",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -69,7 +69,7 @@ function TableHead({ className, ...props }: React.ComponentProps<"th">) {
|
||||
<th
|
||||
data-slot="table-head"
|
||||
className={cn(
|
||||
"lib:h-12 lib:px-3 lib:text-left lib:align-middle lib:font-medium lib:whitespace-nowrap lib:text-foreground lib:[&:has([role=checkbox])]:pr-0",
|
||||
"fui:h-12 fui:px-3 fui:text-left fui:align-middle fui:font-medium fui:whitespace-nowrap fui:text-foreground fui:[&:has([role=checkbox])]:pr-0",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -82,7 +82,7 @@ function TableCell({ className, ...props }: React.ComponentProps<"td">) {
|
||||
<td
|
||||
data-slot="table-cell"
|
||||
className={cn(
|
||||
"lib:p-3 lib:align-middle lib:whitespace-nowrap lib:[&:has([role=checkbox])]:pr-0",
|
||||
"fui:p-3 fui:align-middle fui:whitespace-nowrap fui:[&:has([role=checkbox])]:pr-0",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -98,7 +98,7 @@ function TableCaption({
|
||||
<caption
|
||||
data-slot="table-caption"
|
||||
className={cn(
|
||||
"lib:mt-4 lib:text-sm lib:text-muted-foreground",
|
||||
"fui:mt-4 fui:text-sm fui:text-muted-foreground",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -12,7 +12,7 @@ function Tabs({
|
||||
data-slot="tabs"
|
||||
data-orientation={orientation}
|
||||
className={cn(
|
||||
"lib:group/tabs lib:flex lib:gap-2 lib:data-horizontal:flex-col",
|
||||
"fui:group/tabs fui:flex fui:gap-2 fui:data-horizontal:flex-col",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -21,12 +21,12 @@ function Tabs({
|
||||
}
|
||||
|
||||
const tabsListVariants = cva(
|
||||
"lib:group/tabs-list lib:inline-flex lib:w-fit lib:items-center lib:justify-center lib:rounded-4xl lib:p-[3px] lib:text-muted-foreground lib:group-data-horizontal/tabs:h-9 lib:group-data-vertical/tabs:h-fit lib:group-data-vertical/tabs:flex-col lib:group-data-vertical/tabs:rounded-2xl lib:data-[variant=line]:rounded-none",
|
||||
"fui:group/tabs-list fui:inline-flex fui:w-fit fui:items-center fui:justify-center fui:rounded-4xl fui:p-[3px] fui:text-muted-foreground fui:group-data-horizontal/tabs:h-9 fui:group-data-vertical/tabs:h-fit fui:group-data-vertical/tabs:flex-col fui:group-data-vertical/tabs:rounded-2xl fui:data-[variant=line]:rounded-none",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "lib:bg-muted",
|
||||
line: "lib:gap-1 lib:bg-transparent",
|
||||
default: "fui:bg-muted",
|
||||
line: "fui:gap-1 fui:bg-transparent",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
@@ -55,10 +55,10 @@ function TabsTrigger({ className, ...props }: TabsPrimitive.Tab.Props) {
|
||||
<TabsPrimitive.Tab
|
||||
data-slot="tabs-trigger"
|
||||
className={cn(
|
||||
"lib:relative lib:inline-flex lib:h-[calc(100%-1px)] lib:flex-1 lib:items-center lib:justify-center lib:gap-1.5 lib:rounded-xl lib:border lib:border-transparent lib:px-2 lib:py-1 lib:text-sm lib:font-medium lib:whitespace-nowrap lib:text-foreground/60 lib:transition-all lib:group-data-vertical/tabs:w-full lib:group-data-vertical/tabs:justify-start lib:group-data-vertical/tabs:px-2.5 lib:group-data-vertical/tabs:py-1.5 lib:hover:text-foreground lib:focus-visible:border-ring lib:focus-visible:ring-[3px] lib:focus-visible:ring-ring/50 lib:focus-visible:outline-1 lib:focus-visible:outline-ring lib:disabled:pointer-events-none lib:disabled:opacity-50 lib:has-data-[icon=inline-end]:pr-1.5 lib:has-data-[icon=inline-start]:pl-1.5 lib:aria-disabled:pointer-events-none lib:aria-disabled:opacity-50 lib:dark:text-muted-foreground lib:dark:hover:text-foreground lib:[&_svg]:pointer-events-none lib:[&_svg]:shrink-0 lib:[&_svg:not([class*=size-])]:size-4",
|
||||
"lib:group-data-[variant=line]/tabs-list:bg-transparent lib:group-data-[variant=line]/tabs-list:data-active:bg-transparent lib:dark:group-data-[variant=line]/tabs-list:data-active:border-transparent lib:dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent",
|
||||
"lib:data-active:bg-background lib:data-active:text-foreground lib:dark:data-active:border-input lib:dark:data-active:bg-input/30 lib:dark:data-active:text-foreground",
|
||||
"lib:after:absolute lib:after:bg-foreground lib:after:opacity-0 lib:after:transition-opacity lib:group-data-horizontal/tabs:after:inset-x-0 lib:group-data-horizontal/tabs:after:bottom-[-5px] lib:group-data-horizontal/tabs:after:h-0.5 lib:group-data-vertical/tabs:after:inset-y-0 lib:group-data-vertical/tabs:after:-right-1 lib:group-data-vertical/tabs:after:w-0.5 lib:group-data-[variant=line]/tabs-list:data-active:after:opacity-100",
|
||||
"fui:relative fui:inline-flex fui:h-[calc(100%-1px)] fui:flex-1 fui:items-center fui:justify-center fui:gap-1.5 fui:rounded-xl fui:border fui:border-transparent fui:px-2 fui:py-1 fui:text-sm fui:font-medium fui:whitespace-nowrap fui:text-foreground/60 fui:transition-all fui:group-data-vertical/tabs:w-full fui:group-data-vertical/tabs:justify-start fui:group-data-vertical/tabs:px-2.5 fui:group-data-vertical/tabs:py-1.5 fui:hover:text-foreground fui:focus-visible:border-ring fui:focus-visible:ring-[3px] fui:focus-visible:ring-ring/50 fui:focus-visible:outline-1 fui:focus-visible:outline-ring fui:disabled:pointer-events-none fui:disabled:opacity-50 fui:has-data-[icon=inline-end]:pr-1.5 fui:has-data-[icon=inline-start]:pl-1.5 fui:aria-disabled:pointer-events-none fui:aria-disabled:opacity-50 fui:dark:text-muted-foreground fui:dark:hover:text-foreground fui:[&_svg]:pointer-events-none fui:[&_svg]:shrink-0 fui:[&_svg:not([class*=size-])]:size-4",
|
||||
"fui:group-data-[variant=line]/tabs-list:bg-transparent fui:group-data-[variant=line]/tabs-list:data-active:bg-transparent fui:dark:group-data-[variant=line]/tabs-list:data-active:border-transparent fui:dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent",
|
||||
"fui:data-active:bg-background fui:data-active:text-foreground fui:dark:data-active:border-input fui:dark:data-active:bg-input/30 fui:dark:data-active:text-foreground",
|
||||
"fui:after:absolute fui:after:bg-foreground fui:after:opacity-0 fui:after:transition-opacity fui:group-data-horizontal/tabs:after:inset-x-0 fui:group-data-horizontal/tabs:after:bottom-[-5px] fui:group-data-horizontal/tabs:after:h-0.5 fui:group-data-vertical/tabs:after:inset-y-0 fui:group-data-vertical/tabs:after:-right-1 fui:group-data-vertical/tabs:after:w-0.5 fui:group-data-[variant=line]/tabs-list:data-active:after:opacity-100",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -70,7 +70,7 @@ function TabsContent({ className, ...props }: TabsPrimitive.Panel.Props) {
|
||||
return (
|
||||
<TabsPrimitive.Panel
|
||||
data-slot="tabs-content"
|
||||
className={cn("lib:flex-1 lib:text-sm lib:outline-none", className)}
|
||||
className={cn("fui:flex-1 fui:text-sm fui:outline-none", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -6,7 +6,7 @@ function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
|
||||
<textarea
|
||||
data-slot="textarea"
|
||||
className={cn(
|
||||
"lib:flex lib:field-sizing-content lib:min-h-16 lib:w-full lib:resize-none lib:rounded-xl lib:border lib:border-input lib:bg-input/30 lib:px-3 lib:py-3 lib:text-base lib:transition-colors lib:outline-none lib:placeholder:text-muted-foreground lib:focus-visible:border-ring lib:focus-visible:ring-[3px] lib:focus-visible:ring-ring/50 lib:disabled:cursor-not-allowed lib:disabled:opacity-50 lib:aria-invalid:border-destructive lib:aria-invalid:ring-[3px] lib:aria-invalid:ring-destructive/20 lib:md:text-sm lib:dark:aria-invalid:border-destructive/50 lib:dark:aria-invalid:ring-destructive/40",
|
||||
"fui:flex fui:field-sizing-content fui:min-h-16 fui:w-full fui:resize-none fui:rounded-xl fui:border fui:border-input fui:bg-input/30 fui:px-3 fui:py-3 fui:text-base fui:transition-colors fui:outline-none fui:placeholder:text-muted-foreground fui:focus-visible:border-ring fui:focus-visible:ring-[3px] fui:focus-visible:ring-ring/50 fui:disabled:cursor-not-allowed fui:disabled:opacity-50 fui:aria-invalid:border-destructive fui:aria-invalid:ring-[3px] fui:aria-invalid:ring-destructive/20 fui:md:text-sm fui:dark:aria-invalid:border-destructive/50 fui:dark:aria-invalid:ring-destructive/40",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -40,7 +40,7 @@ function ToggleGroup({
|
||||
data-orientation={orientation}
|
||||
style={{ "--gap": spacing } as React.CSSProperties}
|
||||
className={cn(
|
||||
"lib:group/toggle-group lib:flex lib:w-fit lib:flex-row lib:items-center lib:gap-[--spacing(var(--gap))] lib:data-[spacing=0]:data-[variant=outline]:rounded-4xl lib:data-vertical:flex-col lib:data-vertical:items-stretch",
|
||||
"fui:group/toggle-group fui:flex fui:w-fit fui:flex-row fui:items-center fui:gap-[--spacing(var(--gap))] fui:data-[spacing=0]:data-[variant=outline]:rounded-4xl fui:data-vertical:flex-col fui:data-vertical:items-stretch",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -70,7 +70,7 @@ function ToggleGroupItem({
|
||||
data-size={context.size || size}
|
||||
data-spacing={context.spacing}
|
||||
className={cn(
|
||||
"lib:shrink-0 lib:group-data-[spacing=0]/toggle-group:rounded-none lib:group-data-[spacing=0]/toggle-group:px-3 lib:group-data-[spacing=0]/toggle-group:shadow-none lib:focus:z-10 lib:focus-visible:z-10 lib:group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-2.5 lib:group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-2.5 lib:group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-3xl lib:group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-3xl lib:group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-3xl lib:group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-3xl lib:data-[state=on]:bg-muted lib:group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 lib:group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 lib:group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l lib:group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t",
|
||||
"fui:shrink-0 fui:group-data-[spacing=0]/toggle-group:rounded-none fui:group-data-[spacing=0]/toggle-group:px-3 fui:group-data-[spacing=0]/toggle-group:shadow-none fui:focus:z-10 fui:focus-visible:z-10 fui:group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-2.5 fui:group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-2.5 fui:group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-3xl fui:group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-3xl fui:group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-3xl fui:group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-3xl fui:data-[state=on]:bg-muted fui:group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 fui:group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 fui:group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l fui:group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t",
|
||||
toggleVariants({
|
||||
variant: context.variant || variant,
|
||||
size: context.size || size,
|
||||
|
||||
@@ -5,19 +5,19 @@ import { cva, type VariantProps } from "class-variance-authority";
|
||||
import { cn } from "cn";
|
||||
|
||||
const toggleVariants = cva(
|
||||
"lib:group/toggle lib:inline-flex lib:items-center lib:justify-center lib:gap-1 lib:rounded-4xl lib:text-sm lib:font-medium lib:whitespace-nowrap lib:transition-colors lib:outline-none lib:hover:bg-muted lib:hover:text-foreground lib:focus-visible:border-ring lib:focus-visible:ring-[3px] lib:focus-visible:ring-ring/50 lib:disabled:pointer-events-none lib:disabled:opacity-50 lib:aria-invalid:border-destructive lib:aria-invalid:ring-destructive/20 lib:aria-pressed:bg-muted lib:dark:aria-invalid:ring-destructive/40 lib:[&_svg]:pointer-events-none lib:[&_svg]:shrink-0 lib:[&_svg:not([class*=size-])]:size-4",
|
||||
"fui:group/toggle fui:inline-flex fui:items-center fui:justify-center fui:gap-1 fui:rounded-4xl fui:text-sm fui:font-medium fui:whitespace-nowrap fui:transition-colors fui:outline-none fui:hover:bg-muted fui:hover:text-foreground fui:focus-visible:border-ring fui:focus-visible:ring-[3px] fui:focus-visible:ring-ring/50 fui:disabled:pointer-events-none fui:disabled:opacity-50 fui:aria-invalid:border-destructive fui:aria-invalid:ring-destructive/20 fui:aria-pressed:bg-muted fui:dark:aria-invalid:ring-destructive/40 fui:[&_svg]:pointer-events-none fui:[&_svg]:shrink-0 fui:[&_svg:not([class*=size-])]:size-4",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "lib:bg-transparent",
|
||||
default: "fui:bg-transparent",
|
||||
outline:
|
||||
"lib:border lib:border-input lib:bg-transparent lib:hover:bg-muted",
|
||||
"fui:border fui:border-input fui:bg-transparent fui:hover:bg-muted",
|
||||
},
|
||||
size: {
|
||||
default:
|
||||
"lib:h-9 lib:min-w-9 lib:px-3 lib:has-data-[icon=inline-end]:pr-2.5 lib:has-data-[icon=inline-start]:pl-2.5",
|
||||
sm: "lib:h-8 lib:min-w-8 lib:px-3 lib:has-data-[icon=inline-end]:pr-2 lib:has-data-[icon=inline-start]:pl-2",
|
||||
lg: "lib:h-10 lib:min-w-10 lib:px-4 lib:has-data-[icon=inline-end]:pr-3 lib:has-data-[icon=inline-start]:pl-3",
|
||||
"fui:h-9 fui:min-w-9 fui:px-3 fui:has-data-[icon=inline-end]:pr-2.5 fui:has-data-[icon=inline-start]:pl-2.5",
|
||||
sm: "fui:h-8 fui:min-w-8 fui:px-3 fui:has-data-[icon=inline-end]:pr-2 fui:has-data-[icon=inline-start]:pl-2",
|
||||
lg: "fui:h-10 fui:min-w-10 fui:px-4 fui:has-data-[icon=inline-end]:pr-3 fui:has-data-[icon=inline-start]:pl-3",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
|
||||
@@ -42,18 +42,18 @@ function TooltipContent({
|
||||
alignOffset={alignOffset}
|
||||
side={side}
|
||||
sideOffset={sideOffset}
|
||||
className="lib:isolate lib:z-50"
|
||||
className="fui:isolate fui:z-50"
|
||||
>
|
||||
<TooltipPrimitive.Popup
|
||||
data-slot="tooltip-content"
|
||||
className={cn(
|
||||
"lib:z-50 lib:inline-flex lib:w-fit lib:max-w-xs lib:origin-(--transform-origin) lib:items-center lib:gap-1.5 lib:rounded-2xl lib:bg-foreground lib:px-3 lib:py-1.5 lib:text-xs lib:text-background lib:has-data-[slot=kbd]:pr-1.5 lib:data-[side=bottom]:slide-in-from-top-2 lib:data-[side=inline-end]:slide-in-from-left-2 lib:data-[side=inline-start]:slide-in-from-right-2 lib:data-[side=left]:slide-in-from-right-2 lib:data-[side=right]:slide-in-from-left-2 lib:data-[side=top]:slide-in-from-bottom-2 lib:**:data-[slot=kbd]:relative lib:**:data-[slot=kbd]:isolate lib:**:data-[slot=kbd]:z-50 lib:**:data-[slot=kbd]:rounded-4xl lib:data-[state=delayed-open]:animate-in lib:data-[state=delayed-open]:fade-in-0 lib:data-[state=delayed-open]:zoom-in-95 lib:data-open:animate-in lib:data-open:fade-in-0 lib:data-open:zoom-in-95 lib:data-closed:animate-out lib:data-closed:fade-out-0 lib:data-closed:zoom-out-95",
|
||||
"fui:z-50 fui:inline-flex fui:w-fit fui:max-w-xs fui:origin-(--transform-origin) fui:items-center fui:gap-1.5 fui:rounded-2xl fui:bg-foreground fui:px-3 fui:py-1.5 fui:text-xs fui:text-background fui:has-data-[slot=kbd]:pr-1.5 fui:data-[side=bottom]:slide-in-from-top-2 fui:data-[side=inline-end]:slide-in-from-left-2 fui:data-[side=inline-start]:slide-in-from-right-2 fui:data-[side=left]:slide-in-from-right-2 fui:data-[side=right]:slide-in-from-left-2 fui:data-[side=top]:slide-in-from-bottom-2 fui:**:data-[slot=kbd]:relative fui:**:data-[slot=kbd]:isolate fui:**:data-[slot=kbd]:z-50 fui:**:data-[slot=kbd]:rounded-4xl fui:data-[state=delayed-open]:animate-in fui:data-[state=delayed-open]:fade-in-0 fui:data-[state=delayed-open]:zoom-in-95 fui:data-open:animate-in fui:data-open:fade-in-0 fui:data-open:zoom-in-95 fui:data-closed:animate-out fui:data-closed:fade-out-0 fui:data-closed:zoom-out-95",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<TooltipPrimitive.Arrow className="lib:z-50 lib:size-2.5 lib:translate-y-[calc(-50%-2px)] lib:rotate-45 lib:rounded-[2px] lib:bg-foreground lib:fill-foreground lib:data-[side=bottom]:top-1 lib:data-[side=inline-end]:top-1/2! lib:data-[side=inline-end]:-left-1 lib:data-[side=inline-end]:translate-x-[1.5px] lib:data-[side=inline-end]:-translate-y-1/2 lib:data-[side=inline-start]:top-1/2! lib:data-[side=inline-start]:-right-1 lib:data-[side=inline-start]:translate-x-[-1.5px] lib:data-[side=inline-start]:-translate-y-1/2 lib:data-[side=left]:top-1/2! lib:data-[side=left]:-right-1 lib:data-[side=left]:translate-x-[-1.5px] lib:data-[side=left]:-translate-y-1/2 lib:data-[side=right]:top-1/2! lib:data-[side=right]:-left-1 lib:data-[side=right]:translate-x-[1.5px] lib:data-[side=right]:-translate-y-1/2 lib:data-[side=top]:-bottom-2.5" />
|
||||
<TooltipPrimitive.Arrow className="fui:z-50 fui:size-2.5 fui:translate-y-[calc(-50%-2px)] fui:rotate-45 fui:rounded-[2px] fui:bg-foreground fui:fill-foreground fui:data-[side=bottom]:top-1 fui:data-[side=inline-end]:top-1/2! fui:data-[side=inline-end]:-left-1 fui:data-[side=inline-end]:translate-x-[1.5px] fui:data-[side=inline-end]:-translate-y-1/2 fui:data-[side=inline-start]:top-1/2! fui:data-[side=inline-start]:-right-1 fui:data-[side=inline-start]:translate-x-[-1.5px] fui:data-[side=inline-start]:-translate-y-1/2 fui:data-[side=left]:top-1/2! fui:data-[side=left]:-right-1 fui:data-[side=left]:translate-x-[-1.5px] fui:data-[side=left]:-translate-y-1/2 fui:data-[side=right]:top-1/2! fui:data-[side=right]:-left-1 fui:data-[side=right]:translate-x-[1.5px] fui:data-[side=right]:-translate-y-1/2 fui:data-[side=top]:-bottom-2.5" />
|
||||
</TooltipPrimitive.Popup>
|
||||
</TooltipPrimitive.Positioner>
|
||||
</TooltipPrimitive.Portal>
|
||||
|
||||
@@ -18,7 +18,7 @@ function Prose({
|
||||
render,
|
||||
props: mergeProps<"div">(
|
||||
{
|
||||
className: cn("lib:prose", className),
|
||||
className: cn("fui:prose", className),
|
||||
} as React.ComponentProps<"div">,
|
||||
props,
|
||||
),
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { useCallback, useEffect, useRef } from "react";
|
||||
|
||||
/**
|
||||
* Defer `callback` until `delay` ms have passed without another call.
|
||||
*
|
||||
* Filters that apply as they are touched cost a request per keystroke without
|
||||
* this. Selects and checkboxes change one value at a time and need no delay;
|
||||
* text input does.
|
||||
*
|
||||
* The latest callback is held in a ref, so the returned function is stable and
|
||||
* safe to hand to a memoised child without restarting the timer on every
|
||||
* render.
|
||||
*/
|
||||
export function useDebouncedCallback<Args extends unknown[]>(
|
||||
callback: (...args: Args) => void,
|
||||
delay = 300,
|
||||
): (...args: Args) => void {
|
||||
const latest = useRef(callback);
|
||||
const timer = useRef<ReturnType<typeof setTimeout>>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
latest.current = callback;
|
||||
}, [callback]);
|
||||
|
||||
// A pending call must not fire after the caller has gone.
|
||||
useEffect(() => () => clearTimeout(timer.current), []);
|
||||
|
||||
return useCallback(
|
||||
(...args: Args) => {
|
||||
clearTimeout(timer.current);
|
||||
timer.current = setTimeout(() => latest.current(...args), delay);
|
||||
},
|
||||
[delay],
|
||||
);
|
||||
}
|
||||
+4
-3
@@ -32,6 +32,7 @@
|
||||
--color-popover-foreground: var(--popover-foreground);
|
||||
--color-primary: var(--primary);
|
||||
--color-primary-foreground: var(--primary-foreground);
|
||||
--color-link: var(--link);
|
||||
--color-secondary: var(--secondary);
|
||||
--color-secondary-foreground: var(--secondary-foreground);
|
||||
--color-muted: var(--muted);
|
||||
@@ -64,12 +65,12 @@
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply lib:border-border lib:outline-ring/50;
|
||||
@apply fui:border-border fui:outline-ring/50;
|
||||
}
|
||||
body {
|
||||
@apply lib:bg-background lib:text-foreground;
|
||||
@apply fui:bg-background fui:text-foreground;
|
||||
}
|
||||
html {
|
||||
@apply lib:font-sans;
|
||||
@apply fui:font-sans;
|
||||
}
|
||||
}
|
||||
|
||||
+109
-133
@@ -195,10 +195,6 @@ import {
|
||||
TooltipTrigger,
|
||||
} from "../components/tooltip";
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* data */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
const revenueByMonth = [
|
||||
{ month: "Jan", revenue: 18600, expenses: 11400 },
|
||||
{ month: "Feb", revenue: 30500, expenses: 14200 },
|
||||
@@ -457,10 +453,6 @@ const stats = [
|
||||
},
|
||||
];
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* sidebar */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
function AppSidebar() {
|
||||
return (
|
||||
<Sidebar collapsible="icon" variant="inset">
|
||||
@@ -471,22 +463,22 @@ function AppSidebar() {
|
||||
<DropdownMenuTrigger
|
||||
render={
|
||||
<SidebarMenuButton size="lg" tooltip="Switch workspace">
|
||||
<div className="lib:flex lib:aspect-square lib:size-8 lib:items-center lib:justify-center lib:rounded-lg lib:bg-sidebar-primary lib:text-sidebar-primary-foreground">
|
||||
<Building2 className="lib:size-4" />
|
||||
<div className="fui:flex fui:aspect-square fui:size-8 fui:items-center fui:justify-center fui:rounded-lg fui:bg-sidebar-primary fui:text-sidebar-primary-foreground">
|
||||
<Building2 className="fui:size-4" />
|
||||
</div>
|
||||
<div className="lib:grid lib:flex-1 lib:text-left lib:text-sm lib:leading-tight">
|
||||
<span className="lib:truncate lib:font-medium">
|
||||
<div className="fui:grid fui:flex-1 fui:text-left fui:text-sm fui:leading-tight">
|
||||
<span className="fui:truncate fui:font-medium">
|
||||
Foomo Inc.
|
||||
</span>
|
||||
<span className="lib:truncate lib:text-xs lib:text-sidebar-foreground/70">
|
||||
<span className="fui:truncate fui:text-xs fui:text-sidebar-foreground/70">
|
||||
Enterprise
|
||||
</span>
|
||||
</div>
|
||||
<ChevronsUpDown className="lib:ml-auto lib:size-4" />
|
||||
<ChevronsUpDown className="fui:ml-auto fui:size-4" />
|
||||
</SidebarMenuButton>
|
||||
}
|
||||
/>
|
||||
<DropdownMenuContent align="start" className="lib:w-56">
|
||||
<DropdownMenuContent align="start" className="fui:w-56">
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuLabel>Workspaces</DropdownMenuLabel>
|
||||
<DropdownMenuItem>
|
||||
@@ -530,14 +522,14 @@ function AppSidebar() {
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
|
||||
<Collapsible defaultOpen className="lib:group/collapsible">
|
||||
<Collapsible defaultOpen className="fui:group/collapsible">
|
||||
<SidebarMenuItem>
|
||||
<CollapsibleTrigger
|
||||
render={
|
||||
<SidebarMenuButton tooltip="Projects">
|
||||
<Package />
|
||||
<span>Projects</span>
|
||||
<ChevronRight className="lib:ml-auto lib:size-4 lib:transition-transform lib:duration-200 lib:group-data-open/collapsible:rotate-90" />
|
||||
<ChevronRight className="fui:ml-auto fui:size-4 fui:transition-transform fui:duration-200 fui:group-data-open/collapsible:rotate-90" />
|
||||
</SidebarMenuButton>
|
||||
}
|
||||
/>
|
||||
@@ -620,27 +612,27 @@ function AppSidebar() {
|
||||
<DropdownMenuTrigger
|
||||
render={
|
||||
<SidebarMenuButton size="lg" tooltip="Account">
|
||||
<Avatar className="lib:size-8 lib:rounded-lg">
|
||||
<AvatarFallback className="lib:rounded-lg">
|
||||
<Avatar className="fui:size-8 fui:rounded-lg">
|
||||
<AvatarFallback className="fui:rounded-lg">
|
||||
LB
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="lib:grid lib:flex-1 lib:text-left lib:text-sm lib:leading-tight">
|
||||
<span className="lib:truncate lib:font-medium">
|
||||
<div className="fui:grid fui:flex-1 fui:text-left fui:text-sm fui:leading-tight">
|
||||
<span className="fui:truncate fui:font-medium">
|
||||
Lena Brandt
|
||||
</span>
|
||||
<span className="lib:truncate lib:text-xs lib:text-sidebar-foreground/70">
|
||||
<span className="fui:truncate fui:text-xs fui:text-sidebar-foreground/70">
|
||||
lena@foomo.org
|
||||
</span>
|
||||
</div>
|
||||
<ChevronsUpDown className="lib:ml-auto lib:size-4" />
|
||||
<ChevronsUpDown className="fui:ml-auto fui:size-4" />
|
||||
</SidebarMenuButton>
|
||||
}
|
||||
/>
|
||||
<DropdownMenuContent
|
||||
side="top"
|
||||
align="start"
|
||||
className="lib:w-56"
|
||||
className="fui:w-56"
|
||||
>
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuLabel>My account</DropdownMenuLabel>
|
||||
@@ -678,20 +670,16 @@ function AppSidebar() {
|
||||
);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* header */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
function DashboardHeader({ onOpenCommand }: { onOpenCommand: () => void }) {
|
||||
return (
|
||||
<header className="lib:sticky lib:top-0 lib:z-10 lib:flex lib:h-16 lib:shrink-0 lib:items-center lib:gap-2 lib:border-b lib:bg-background/80 lib:px-4 lib:backdrop-blur">
|
||||
<SidebarTrigger className="lib:-ml-1" />
|
||||
<header className="fui:sticky fui:top-0 fui:z-10 fui:flex fui:h-16 fui:shrink-0 fui:items-center fui:gap-2 fui:border-b fui:bg-background/80 fui:px-4 fui:backdrop-blur">
|
||||
<SidebarTrigger className="fui:-ml-1" />
|
||||
<Separator
|
||||
orientation="vertical"
|
||||
className="lib:mr-2 lib:h-4 lib:self-center!"
|
||||
className="fui:mr-2 fui:h-4 fui:self-center!"
|
||||
/>
|
||||
|
||||
<Breadcrumb className="lib:hidden lib:md:block">
|
||||
<Breadcrumb className="fui:hidden fui:md:block">
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink href="#">Foomo Inc.</BreadcrumbLink>
|
||||
@@ -703,15 +691,15 @@ function DashboardHeader({ onOpenCommand }: { onOpenCommand: () => void }) {
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
|
||||
<div className="lib:ml-auto lib:flex lib:items-center lib:gap-2">
|
||||
<div className="fui:ml-auto fui:flex fui:items-center fui:gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={onOpenCommand}
|
||||
className="lib:hidden lib:w-64 lib:justify-start lib:font-normal lib:text-muted-foreground lib:lg:inline-flex"
|
||||
className="fui:hidden fui:w-64 fui:justify-start fui:font-normal fui:text-muted-foreground fui:lg:inline-flex"
|
||||
>
|
||||
<Search data-icon="inline-start" />
|
||||
<span>Search everything…</span>
|
||||
<KbdGroup className="lib:ml-auto">
|
||||
<KbdGroup className="fui:ml-auto">
|
||||
<Kbd>⌘</Kbd>
|
||||
<Kbd>K</Kbd>
|
||||
</KbdGroup>
|
||||
@@ -724,10 +712,10 @@ function DashboardHeader({ onOpenCommand }: { onOpenCommand: () => void }) {
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={onOpenCommand}
|
||||
className="lib:lg:hidden"
|
||||
className="fui:lg:hidden"
|
||||
>
|
||||
<CommandIcon />
|
||||
<span className="lib:sr-only">Open command palette</span>
|
||||
<span className="fui:sr-only">Open command palette</span>
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
@@ -738,20 +726,20 @@ function DashboardHeader({ onOpenCommand }: { onOpenCommand: () => void }) {
|
||||
|
||||
<Separator
|
||||
orientation="vertical"
|
||||
className="lib:h-4 lib:self-center!"
|
||||
className="fui:h-4 fui:self-center!"
|
||||
/>
|
||||
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
render={
|
||||
<Button variant="ghost" size="icon" className="lib:rounded-full">
|
||||
<Avatar className="lib:size-7">
|
||||
<Button variant="ghost" size="icon" className="fui:rounded-full">
|
||||
<Avatar className="fui:size-7">
|
||||
<AvatarFallback>LB</AvatarFallback>
|
||||
</Avatar>
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<DropdownMenuContent align="end" className="lib:w-56">
|
||||
<DropdownMenuContent align="end" className="fui:w-56">
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuLabel>lena@foomo.org</DropdownMenuLabel>
|
||||
</DropdownMenuGroup>
|
||||
@@ -807,16 +795,16 @@ function NotificationsMenu() {
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
render={
|
||||
<Button variant="ghost" size="icon" className="lib:relative">
|
||||
<Button variant="ghost" size="icon" className="fui:relative">
|
||||
<Bell />
|
||||
<span className="lib:absolute lib:top-1.5 lib:right-1.5 lib:size-2 lib:rounded-full lib:bg-destructive" />
|
||||
<span className="lib:sr-only">Notifications</span>
|
||||
<span className="fui:absolute fui:top-1.5 fui:right-1.5 fui:size-2 fui:rounded-full fui:bg-destructive" />
|
||||
<span className="fui:sr-only">Notifications</span>
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<DropdownMenuContent align="end" className="lib:w-80 lib:p-0">
|
||||
<div className="lib:flex lib:items-center lib:justify-between lib:px-3 lib:py-2">
|
||||
<span className="lib:text-sm lib:font-medium">Notifications</span>
|
||||
<DropdownMenuContent align="end" className="fui:w-80 fui:p-0">
|
||||
<div className="fui:flex fui:items-center fui:justify-between fui:px-3 fui:py-2">
|
||||
<span className="fui:text-sm fui:font-medium">Notifications</span>
|
||||
<Badge variant="secondary">3 new</Badge>
|
||||
</div>
|
||||
<Separator />
|
||||
@@ -831,7 +819,7 @@ function NotificationsMenu() {
|
||||
<ItemDescription>{notification.body}</ItemDescription>
|
||||
</ItemContent>
|
||||
<ItemActions>
|
||||
<span className="lib:text-xs lib:text-muted-foreground">
|
||||
<span className="fui:text-xs fui:text-muted-foreground">
|
||||
{notification.time}
|
||||
</span>
|
||||
</ItemActions>
|
||||
@@ -843,24 +831,20 @@ function NotificationsMenu() {
|
||||
);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* sections */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
function StatCards() {
|
||||
return (
|
||||
<div className="lib:grid lib:gap-4 lib:sm:grid-cols-2 lib:xl:grid-cols-4">
|
||||
<div className="fui:grid fui:gap-4 fui:sm:grid-cols-2 fui:xl:grid-cols-4">
|
||||
{stats.map((stat) => (
|
||||
<Card key={stat.label} size="sm">
|
||||
<CardHeader>
|
||||
<CardDescription>{stat.label}</CardDescription>
|
||||
<CardAction>
|
||||
<stat.icon className="lib:size-4 lib:text-muted-foreground" />
|
||||
<stat.icon className="fui:size-4 fui:text-muted-foreground" />
|
||||
</CardAction>
|
||||
</CardHeader>
|
||||
<CardContent className="lib:flex lib:flex-col lib:gap-3">
|
||||
<div className="lib:flex lib:items-center lib:gap-2">
|
||||
<span className="lib:font-heading lib:text-2xl lib:font-medium lib:tabular-nums">
|
||||
<CardContent className="fui:flex fui:flex-col fui:gap-3">
|
||||
<div className="fui:flex fui:items-center fui:gap-2">
|
||||
<span className="fui:font-heading fui:text-2xl fui:font-medium fui:tabular-nums">
|
||||
{stat.value}
|
||||
</span>
|
||||
<Badge variant="secondary">
|
||||
@@ -872,8 +856,8 @@ function StatCards() {
|
||||
{stat.delta}
|
||||
</Badge>
|
||||
</div>
|
||||
<Progress value={stat.progress} className="lib:h-1.5" />
|
||||
<p className="lib:text-xs lib:text-muted-foreground">{stat.hint}</p>
|
||||
<Progress value={stat.progress} className="fui:h-1.5" />
|
||||
<p className="fui:text-xs fui:text-muted-foreground">{stat.hint}</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
@@ -883,13 +867,13 @@ function StatCards() {
|
||||
|
||||
function RevenueChartCard() {
|
||||
return (
|
||||
<Card className="lib:xl:col-span-4">
|
||||
<Card className="fui:xl:col-span-4">
|
||||
<CardHeader>
|
||||
<CardTitle>Revenue vs. expenses</CardTitle>
|
||||
<CardDescription>Rolling nine months, EUR</CardDescription>
|
||||
<CardAction>
|
||||
<Select defaultValue="9m">
|
||||
<SelectTrigger className="lib:w-36" size="sm">
|
||||
<SelectTrigger className="fui:w-36" size="sm">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -905,7 +889,7 @@ function RevenueChartCard() {
|
||||
<CardContent>
|
||||
<ChartContainer
|
||||
config={revenueChartConfig}
|
||||
className="lib:aspect-auto lib:h-[260px] lib:w-full"
|
||||
className="fui:aspect-auto fui:h-[260px] fui:w-full"
|
||||
>
|
||||
<AreaChart data={revenueByMonth} margin={{ left: 4, right: 4 }}>
|
||||
<defs>
|
||||
@@ -975,20 +959,20 @@ function RevenueChartCard() {
|
||||
|
||||
function ActivityCard() {
|
||||
return (
|
||||
<Card className="lib:xl:col-span-3">
|
||||
<Card className="fui:xl:col-span-3">
|
||||
<CardHeader>
|
||||
<CardTitle>Recent activity</CardTitle>
|
||||
<CardDescription>Last 24 hours across all workspaces</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="lib:px-0">
|
||||
<ScrollArea className="lib:h-[260px]">
|
||||
<ItemGroup className="lib:px-4">
|
||||
<CardContent className="fui:px-0">
|
||||
<ScrollArea className="fui:h-[260px]">
|
||||
<ItemGroup className="fui:px-4">
|
||||
{recentActivity.map((entry, index) => (
|
||||
<React.Fragment key={entry.name}>
|
||||
{index > 0 && <ItemSeparator />}
|
||||
<Item size="sm" className="lib:px-0">
|
||||
<Item size="sm" className="fui:px-0">
|
||||
<ItemMedia>
|
||||
<Avatar className="lib:size-8">
|
||||
<Avatar className="fui:size-8">
|
||||
<AvatarFallback>{entry.initials}</AvatarFallback>
|
||||
</Avatar>
|
||||
</ItemMedia>
|
||||
@@ -996,11 +980,11 @@ function ActivityCard() {
|
||||
<ItemTitle>{entry.name}</ItemTitle>
|
||||
<ItemDescription>{entry.action}</ItemDescription>
|
||||
</ItemContent>
|
||||
<ItemActions className="lib:flex-col lib:items-end lib:gap-0.5">
|
||||
<span className="lib:text-sm lib:font-medium lib:tabular-nums">
|
||||
<ItemActions className="fui:flex-col fui:items-end fui:gap-0.5">
|
||||
<span className="fui:text-sm fui:font-medium fui:tabular-nums">
|
||||
{entry.amount}
|
||||
</span>
|
||||
<span className="lib:text-xs lib:text-muted-foreground">
|
||||
<span className="fui:text-xs fui:text-muted-foreground">
|
||||
{entry.time}
|
||||
</span>
|
||||
</ItemActions>
|
||||
@@ -1011,7 +995,7 @@ function ActivityCard() {
|
||||
</ScrollArea>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<Button variant="outline" size="sm" className="lib:w-full">
|
||||
<Button variant="outline" size="sm" className="fui:w-full">
|
||||
View all activity
|
||||
</Button>
|
||||
</CardFooter>
|
||||
@@ -1027,7 +1011,7 @@ const orderColumns = [
|
||||
header: ({ column: col }) => (
|
||||
<DataTableColumnHeader column={col} title="Invoice" />
|
||||
),
|
||||
cell: (info) => <span className="lib:font-medium">{info.getValue()}</span>,
|
||||
cell: (info) => <span className="fui:font-medium">{info.getValue()}</span>,
|
||||
sortFn: "text",
|
||||
}),
|
||||
column.accessor("customer", {
|
||||
@@ -1035,15 +1019,15 @@ const orderColumns = [
|
||||
<DataTableColumnHeader column={col} title="Customer" />
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<div className="lib:flex lib:items-center lib:gap-2">
|
||||
<Avatar className="lib:size-6">
|
||||
<AvatarFallback className="lib:text-[10px]">
|
||||
<div className="fui:flex fui:items-center fui:gap-2">
|
||||
<Avatar className="fui:size-6">
|
||||
<AvatarFallback className="fui:text-[10px]">
|
||||
{initials(row.original.customer)}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="lib:leading-tight">
|
||||
<div className="fui:leading-tight">
|
||||
<div>{row.original.customer}</div>
|
||||
<div className="lib:text-xs lib:text-muted-foreground">
|
||||
<div className="fui:text-xs fui:text-muted-foreground">
|
||||
{row.original.email}
|
||||
</div>
|
||||
</div>
|
||||
@@ -1065,7 +1049,7 @@ const orderColumns = [
|
||||
cell: (info) => (
|
||||
<Badge
|
||||
variant={statusVariant[info.getValue()]}
|
||||
className="lib:capitalize"
|
||||
className="fui:capitalize"
|
||||
>
|
||||
{info.getValue()}
|
||||
</Badge>
|
||||
@@ -1080,7 +1064,7 @@ const orderColumns = [
|
||||
<DataTableColumnHeader column={col} title="Date" />
|
||||
),
|
||||
cell: (info) => (
|
||||
<span className="lib:whitespace-nowrap lib:text-muted-foreground">
|
||||
<span className="fui:whitespace-nowrap fui:text-muted-foreground">
|
||||
{shortDate.format(info.getValue())}
|
||||
</span>
|
||||
),
|
||||
@@ -1091,7 +1075,7 @@ const orderColumns = [
|
||||
<DataTableColumnHeader column={col} title="Amount" align="end" />
|
||||
),
|
||||
cell: (info) => (
|
||||
<div className="lib:text-right lib:tabular-nums">
|
||||
<div className="fui:text-right fui:tabular-nums">
|
||||
{currency.format(info.getValue())}
|
||||
</div>
|
||||
),
|
||||
@@ -1105,11 +1089,11 @@ const orderColumns = [
|
||||
render={
|
||||
<Button variant="ghost" size="icon-sm">
|
||||
<Ellipsis />
|
||||
<span className="lib:sr-only">Actions for {row.original.id}</span>
|
||||
<span className="fui:sr-only">Actions for {row.original.id}</span>
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<DropdownMenuContent align="end" className="lib:w-44">
|
||||
<DropdownMenuContent align="end" className="fui:w-44">
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem>
|
||||
<Eye />
|
||||
@@ -1147,7 +1131,7 @@ function OrdersTableCard() {
|
||||
: "Sortable, filterable and paginated with TanStack Table"}
|
||||
</CardDescription>
|
||||
<CardAction>
|
||||
<div className="lib:flex lib:items-center lib:gap-2">
|
||||
<div className="fui:flex fui:items-center fui:gap-2">
|
||||
<Button variant="outline" size="sm">
|
||||
<Download data-icon="inline-start" />
|
||||
Export
|
||||
@@ -1220,7 +1204,7 @@ function NewProjectDialog() {
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<DialogContent className="lib:sm:max-w-lg">
|
||||
<DialogContent className="fui:sm:max-w-lg">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Create project</DialogTitle>
|
||||
<DialogDescription>
|
||||
@@ -1287,8 +1271,8 @@ const goals = [
|
||||
|
||||
function AnalyticsTab() {
|
||||
return (
|
||||
<div className="lib:grid lib:gap-4 lib:xl:grid-cols-7">
|
||||
<Card className="lib:xl:col-span-4">
|
||||
<div className="fui:grid fui:gap-4 fui:xl:grid-cols-7">
|
||||
<Card className="fui:xl:col-span-4">
|
||||
<CardHeader>
|
||||
<CardTitle>Traffic by source</CardTitle>
|
||||
<CardDescription>Unique visitors, last 30 days</CardDescription>
|
||||
@@ -1296,7 +1280,7 @@ function AnalyticsTab() {
|
||||
<CardContent>
|
||||
<ChartContainer
|
||||
config={trafficChartConfig}
|
||||
className="lib:aspect-auto lib:h-[240px] lib:w-full"
|
||||
className="fui:aspect-auto fui:h-[240px] fui:w-full"
|
||||
>
|
||||
<BarChart data={trafficBySource} margin={{ left: 4, right: 4 }}>
|
||||
<CartesianGrid vertical={false} />
|
||||
@@ -1318,20 +1302,20 @@ function AnalyticsTab() {
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="lib:xl:col-span-3">
|
||||
<Card className="fui:xl:col-span-3">
|
||||
<CardHeader>
|
||||
<CardTitle>Quarterly goals</CardTitle>
|
||||
<CardDescription>Progress toward Q3 targets</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="lib:flex lib:flex-col lib:gap-5">
|
||||
<CardContent className="fui:flex fui:flex-col fui:gap-5">
|
||||
{goals.map((goal) => (
|
||||
<div
|
||||
key={goal.label}
|
||||
className="lib:flex lib:flex-col lib:gap-2 lib:text-sm"
|
||||
className="fui:flex fui:flex-col fui:gap-2 fui:text-sm"
|
||||
>
|
||||
<div className="lib:flex lib:items-center lib:justify-between">
|
||||
<div className="fui:flex fui:items-center fui:justify-between">
|
||||
<span>{goal.label}</span>
|
||||
<span className="lib:tabular-nums lib:text-muted-foreground">
|
||||
<span className="fui:tabular-nums fui:text-muted-foreground">
|
||||
{goal.value}%
|
||||
</span>
|
||||
</div>
|
||||
@@ -1372,7 +1356,7 @@ function ReportsTab() {
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ItemGroup className="lib:gap-2">
|
||||
<ItemGroup className="fui:gap-2">
|
||||
{reports.map((report) => (
|
||||
<Item key={report.title} variant="outline">
|
||||
<ItemMedia variant="icon">
|
||||
@@ -1404,7 +1388,7 @@ function AuditTab() {
|
||||
<CardDescription>No events recorded in this range.</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Empty className="lib:border">
|
||||
<Empty className="fui:border">
|
||||
<EmptyHeader>
|
||||
<EmptyMedia variant="icon">
|
||||
<Inbox />
|
||||
@@ -1525,46 +1509,46 @@ function InviteDialog() {
|
||||
|
||||
function LoadingSkeleton() {
|
||||
return (
|
||||
<div className="lib:flex lib:flex-col lib:gap-4">
|
||||
<div className="lib:grid lib:gap-4 lib:sm:grid-cols-2 lib:xl:grid-cols-4">
|
||||
<div className="fui:flex fui:flex-col fui:gap-4">
|
||||
<div className="fui:grid fui:gap-4 fui:sm:grid-cols-2 fui:xl:grid-cols-4">
|
||||
{stats.map((stat) => (
|
||||
<Card key={stat.label} size="sm">
|
||||
<CardHeader>
|
||||
<Skeleton className="lib:h-4 lib:w-24" />
|
||||
<Skeleton className="fui:h-4 fui:w-24" />
|
||||
</CardHeader>
|
||||
<CardContent className="lib:flex lib:flex-col lib:gap-3">
|
||||
<Skeleton className="lib:h-7 lib:w-32" />
|
||||
<Skeleton className="lib:h-1.5 lib:w-full" />
|
||||
<Skeleton className="lib:h-3 lib:w-40" />
|
||||
<CardContent className="fui:flex fui:flex-col fui:gap-3">
|
||||
<Skeleton className="fui:h-7 fui:w-32" />
|
||||
<Skeleton className="fui:h-1.5 fui:w-full" />
|
||||
<Skeleton className="fui:h-3 fui:w-40" />
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
<div className="lib:grid lib:gap-4 lib:xl:grid-cols-7">
|
||||
<Card className="lib:xl:col-span-4">
|
||||
<div className="fui:grid fui:gap-4 fui:xl:grid-cols-7">
|
||||
<Card className="fui:xl:col-span-4">
|
||||
<CardHeader>
|
||||
<Skeleton className="lib:h-5 lib:w-48" />
|
||||
<Skeleton className="fui:h-5 fui:w-48" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Skeleton className="lib:h-[260px] lib:w-full" />
|
||||
<Skeleton className="fui:h-[260px] fui:w-full" />
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card className="lib:xl:col-span-3">
|
||||
<Card className="fui:xl:col-span-3">
|
||||
<CardHeader>
|
||||
<Skeleton className="lib:h-5 lib:w-36" />
|
||||
<Skeleton className="fui:h-5 fui:w-36" />
|
||||
</CardHeader>
|
||||
<CardContent className="lib:flex lib:flex-col lib:gap-4">
|
||||
<CardContent className="fui:flex fui:flex-col fui:gap-4">
|
||||
{recentActivity.map((entry) => (
|
||||
<div
|
||||
key={entry.name}
|
||||
className="lib:flex lib:items-center lib:gap-3"
|
||||
className="fui:flex fui:items-center fui:gap-3"
|
||||
>
|
||||
<Skeleton className="lib:size-8 lib:rounded-full" />
|
||||
<div className="lib:flex lib:flex-1 lib:flex-col lib:gap-1.5">
|
||||
<Skeleton className="lib:h-3.5 lib:w-32" />
|
||||
<Skeleton className="lib:h-3 lib:w-48" />
|
||||
<Skeleton className="fui:size-8 fui:rounded-full" />
|
||||
<div className="fui:flex fui:flex-1 fui:flex-col fui:gap-1.5">
|
||||
<Skeleton className="fui:h-3.5 fui:w-32" />
|
||||
<Skeleton className="fui:h-3 fui:w-48" />
|
||||
</div>
|
||||
<Skeleton className="lib:h-3.5 lib:w-16" />
|
||||
<Skeleton className="fui:h-3.5 fui:w-16" />
|
||||
</div>
|
||||
))}
|
||||
</CardContent>
|
||||
@@ -1574,10 +1558,6 @@ function LoadingSkeleton() {
|
||||
);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* page */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
type DashboardProps = {
|
||||
/** Start with the sidebar collapsed to icons. */
|
||||
sidebarOpen?: boolean;
|
||||
@@ -1607,17 +1587,17 @@ function Dashboard({ sidebarOpen = true, loading = false }: DashboardProps) {
|
||||
<SidebarInset>
|
||||
<DashboardHeader onOpenCommand={() => setCommandOpen(true)} />
|
||||
|
||||
<div className="lib:flex lib:flex-1 lib:flex-col lib:gap-4 lib:p-4 lib:md:p-6">
|
||||
<div className="lib:flex lib:flex-wrap lib:items-center lib:justify-between lib:gap-3">
|
||||
<div className="lib:flex lib:flex-col lib:gap-1">
|
||||
<h1 className="lib:font-heading lib:text-xl lib:font-medium">
|
||||
<div className="fui:flex fui:flex-1 fui:flex-col fui:gap-4 fui:p-4 fui:md:p-6">
|
||||
<div className="fui:flex fui:flex-wrap fui:items-center fui:justify-between fui:gap-3">
|
||||
<div className="fui:flex fui:flex-col fui:gap-1">
|
||||
<h1 className="fui:font-heading fui:text-xl fui:font-medium">
|
||||
Good afternoon, Lena
|
||||
</h1>
|
||||
<p className="lib:text-sm lib:text-muted-foreground">
|
||||
<p className="fui:text-sm fui:text-muted-foreground">
|
||||
Here is what happened across Foomo Inc. today.
|
||||
</p>
|
||||
</div>
|
||||
<div className="lib:flex lib:items-center lib:gap-2">
|
||||
<div className="fui:flex fui:items-center fui:gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
@@ -1651,14 +1631,14 @@ function Dashboard({ sidebarOpen = true, loading = false }: DashboardProps) {
|
||||
<StatCards />
|
||||
|
||||
<Tabs defaultValue="overview">
|
||||
<div className="lib:flex lib:flex-wrap lib:items-center lib:justify-between lib:gap-2">
|
||||
<div className="fui:flex fui:flex-wrap fui:items-center fui:justify-between fui:gap-2">
|
||||
<TabsList>
|
||||
<TabsTrigger value="overview">Overview</TabsTrigger>
|
||||
<TabsTrigger value="analytics">Analytics</TabsTrigger>
|
||||
<TabsTrigger value="reports">Reports</TabsTrigger>
|
||||
<TabsTrigger value="audit">Audit</TabsTrigger>
|
||||
</TabsList>
|
||||
<span className="lib:hidden lib:items-center lib:gap-1.5 lib:text-xs lib:text-muted-foreground lib:sm:flex">
|
||||
<span className="fui:hidden fui:items-center fui:gap-1.5 fui:text-xs fui:text-muted-foreground fui:sm:flex">
|
||||
Press
|
||||
<KbdGroup>
|
||||
<Kbd>⌘</Kbd>
|
||||
@@ -1670,9 +1650,9 @@ function Dashboard({ sidebarOpen = true, loading = false }: DashboardProps) {
|
||||
|
||||
<TabsContent
|
||||
value="overview"
|
||||
className="lib:flex lib:flex-col lib:gap-4"
|
||||
className="fui:flex fui:flex-col fui:gap-4"
|
||||
>
|
||||
<div className="lib:grid lib:gap-4 lib:xl:grid-cols-7">
|
||||
<div className="fui:grid fui:gap-4 fui:xl:grid-cols-7">
|
||||
<RevenueChartCard />
|
||||
<ActivityCard />
|
||||
</div>
|
||||
@@ -1703,10 +1683,6 @@ function Dashboard({ sidebarOpen = true, loading = false }: DashboardProps) {
|
||||
);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* stories */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
const meta = {
|
||||
title: "Showcase/Admin Dashboard",
|
||||
component: Dashboard,
|
||||
|
||||
@@ -107,9 +107,9 @@ const columns = column.columns([
|
||||
<DataTableColumnHeader column={column} title="Project" />
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<div className="lib:flex lib:flex-col">
|
||||
<span className="lib:font-medium">{row.original.name}</span>
|
||||
<span className="lib:text-xs lib:text-muted-foreground">
|
||||
<div className="fui:flex fui:flex-col">
|
||||
<span className="fui:font-medium">{row.original.name}</span>
|
||||
<span className="fui:text-xs fui:text-muted-foreground">
|
||||
{row.original.id}
|
||||
</span>
|
||||
</div>
|
||||
@@ -129,7 +129,7 @@ const columns = column.columns([
|
||||
cell: (info) => (
|
||||
<Badge
|
||||
variant={statusVariant[info.getValue()]}
|
||||
className="lib:capitalize"
|
||||
className="fui:capitalize"
|
||||
>
|
||||
{info.getValue()}
|
||||
</Badge>
|
||||
@@ -142,7 +142,7 @@ const columns = column.columns([
|
||||
<DataTableColumnHeader column={column} title="Updated" />
|
||||
),
|
||||
cell: (info) => (
|
||||
<span className="lib:whitespace-nowrap lib:text-muted-foreground">
|
||||
<span className="fui:whitespace-nowrap fui:text-muted-foreground">
|
||||
{dateTime.format(info.getValue())}
|
||||
</span>
|
||||
),
|
||||
@@ -153,7 +153,7 @@ const columns = column.columns([
|
||||
<DataTableColumnHeader column={column} title="Deployments" align="end" />
|
||||
),
|
||||
cell: (info) => (
|
||||
<div className="lib:text-right lib:tabular-nums">{info.getValue()}</div>
|
||||
<div className="fui:text-right fui:tabular-nums">{info.getValue()}</div>
|
||||
),
|
||||
}),
|
||||
]);
|
||||
@@ -171,7 +171,7 @@ const meta = {
|
||||
tags: ["autodocs"],
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<div className="lib:mx-auto lib:w-full lib:max-w-5xl">
|
||||
<div className="fui:mx-auto fui:w-full fui:max-w-5xl">
|
||||
<Story />
|
||||
</div>
|
||||
),
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
prefix: "lib",
|
||||
prefix: "fui",
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user