Merge branch 'lucide'

This commit is contained in:
Marko Trebizan 2025-08-21 10:36:20 +02:00
commit ce95ec2a3c
No known key found for this signature in database
45 changed files with 1365 additions and 1356 deletions

View File

@ -15,7 +15,17 @@
"linter": { "linter": {
"enabled": true, "enabled": true,
"rules": { "rules": {
"recommended": true "recommended": true,
"correctness": {
"noUnusedImports": "error"
},
"suspicious": {
"noExplicitAny": "warn",
"noArrayIndexKey": "warn"
},
"a11y": {
"noSvgWithoutTitle": "warn"
}
} }
}, },
"javascript": { "javascript": {

View File

@ -13,6 +13,11 @@
"import": "./dist/*.js", "import": "./dist/*.js",
"require": "./dist/*.cjs" "require": "./dist/*.cjs"
}, },
"./icons": {
"types": "./dist/icons.d.ts",
"import": "./dist/icons.js",
"require": "./dist/icons.cjs"
},
"./ui.css": "./dist/ui.css" "./ui.css": "./dist/ui.css"
}, },
"scripts": { "scripts": {
@ -24,90 +29,90 @@
"build-storybook": "storybook build" "build-storybook": "storybook build"
}, },
"dependencies": { "dependencies": {
"@hookform/resolvers": "^5.2.1", "@hookform/resolvers": "5.2.1",
"@radix-ui/react-accordion": "^1.2.11", "@radix-ui/react-accordion": "1.2.11",
"@radix-ui/react-alert-dialog": "^1.1.14", "@radix-ui/react-alert-dialog": "1.1.14",
"@radix-ui/react-aspect-ratio": "^1.1.7", "@radix-ui/react-aspect-ratio": "1.1.7",
"@radix-ui/react-avatar": "^1.1.10", "@radix-ui/react-avatar": "1.1.10",
"@radix-ui/react-checkbox": "^1.3.2", "@radix-ui/react-checkbox": "1.3.2",
"@radix-ui/react-collapsible": "^1.1.11", "@radix-ui/react-collapsible": "1.1.11",
"@radix-ui/react-context-menu": "^2.2.15", "@radix-ui/react-context-menu": "2.2.15",
"@radix-ui/react-dialog": "^1.1.14", "@radix-ui/react-dialog": "1.1.14",
"@radix-ui/react-dropdown-menu": "^2.1.15", "@radix-ui/react-dropdown-menu": "2.1.15",
"@radix-ui/react-hover-card": "^1.1.14", "@radix-ui/react-hover-card": "1.1.14",
"@radix-ui/react-label": "^2.1.7", "@radix-ui/react-label": "2.1.7",
"@radix-ui/react-menubar": "^1.1.15", "@radix-ui/react-menubar": "1.1.15",
"@radix-ui/react-navigation-menu": "^1.2.13", "@radix-ui/react-navigation-menu": "1.2.13",
"@radix-ui/react-popover": "^1.1.14", "@radix-ui/react-popover": "1.1.14",
"@radix-ui/react-progress": "^1.1.7", "@radix-ui/react-progress": "1.1.7",
"@radix-ui/react-radio-group": "^1.3.7", "@radix-ui/react-radio-group": "1.3.7",
"@radix-ui/react-scroll-area": "^1.2.9", "@radix-ui/react-scroll-area": "1.2.9",
"@radix-ui/react-select": "^2.2.5", "@radix-ui/react-select": "2.2.5",
"@radix-ui/react-separator": "^1.1.7", "@radix-ui/react-separator": "1.1.7",
"@radix-ui/react-slider": "^1.3.5", "@radix-ui/react-slider": "1.3.5",
"@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-slot": "1.2.3",
"@radix-ui/react-switch": "^1.2.5", "@radix-ui/react-switch": "1.2.5",
"@radix-ui/react-tabs": "^1.1.12", "@radix-ui/react-tabs": "1.1.12",
"@radix-ui/react-toggle": "^1.1.9", "@radix-ui/react-toggle": "1.1.9",
"@radix-ui/react-toggle-group": "^1.1.10", "@radix-ui/react-toggle-group": "1.1.10",
"@radix-ui/react-tooltip": "^1.2.7", "@radix-ui/react-tooltip": "1.2.7",
"class-variance-authority": "^0.7.1", "class-variance-authority": "0.7.1",
"clsx": "^2.1.1", "clsx": "2.1.1",
"cmdk": "^1.1.1", "cmdk": "1.1.1",
"date-fns": "^4.1.0", "date-fns": "4.1.0",
"embla-carousel-react": "^8.6.0", "embla-carousel-react": "8.6.0",
"input-otp": "^1.4.2", "input-otp": "1.4.2",
"lucide-react": "^0.534.0", "lucide-react": "0.534.0",
"next-themes": "^0.4.6", "next-themes": "0.4.6",
"react-day-picker": "^9.8.1", "react-day-picker": "9.8.1",
"react-hook-form": "^7.61.1", "react-hook-form": "7.61.1",
"react-resizable-panels": "^3.0.4", "react-resizable-panels": "3.0.4",
"recharts": "2.15.4", "recharts": "2.15.4",
"sonner": "^2.0.6", "sonner": "2.0.6",
"vaul": "^1.1.2", "vaul": "1.1.2",
"zod": "^4.0.14" "zod": "4.0.14"
}, },
"peerDependencies": { "peerDependencies": {
"tailwind-merge": "^3.3.1", "tailwind-merge": "3.3.1",
"tailwindcss": "^4.1.11", "tailwindcss": "4.1.11",
"react": "^19.0.0", "react": "19.1.0",
"react-dom": "^19.0.0" "react-dom": "19.1.0"
}, },
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "2.1.2", "@biomejs/biome": "2.1.2",
"@chromatic-com/storybook": "^4.0.1", "@chromatic-com/storybook": "4.0.1",
"@eslint/js": "^9.30.1", "@eslint/js": "9.30.1",
"@storybook/addon-a11y": "^9.0.18", "@storybook/addon-a11y": "9.0.18",
"@storybook/addon-docs": "^9.0.18", "@storybook/addon-docs": "9.0.18",
"@storybook/addon-onboarding": "^9.0.18", "@storybook/addon-onboarding": "9.0.18",
"@storybook/addon-vitest": "^9.0.18", "@storybook/addon-vitest": "9.0.18",
"@storybook/builder-vite": "^9.0.18", "@storybook/builder-vite": "9.0.18",
"@storybook/react-vite": "^9.0.18", "@storybook/react-vite": "9.0.18",
"@tailwindcss/typography": "^0.5.16", "@tailwindcss/typography": "0.5.16",
"@tailwindcss/vite": "^4.1.11", "@tailwindcss/vite": "4.1.11",
"@types/bun": "latest", "@types/bun": "latest",
"@types/glob": "^9.0.0", "@types/glob": "9.0.0",
"@types/node": "^24.1.0", "@types/node": "24.1.0",
"@types/react": "^19.1.8", "@types/react": "19.1.8",
"@types/react-dom": "^19.1.6", "@types/react-dom": "19.1.6",
"@vitejs/plugin-react": "^4.6.0", "@vitejs/plugin-react": "4.6.0",
"@vitest/browser": "^3.2.4", "@vitest/browser": "3.2.4",
"@vitest/coverage-v8": "^3.2.4", "@vitest/coverage-v8": "3.2.4",
"eslint": "^9.30.1", "eslint": "9.30.1",
"eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-hooks": "5.2.0",
"eslint-plugin-react-refresh": "^0.4.20", "eslint-plugin-react-refresh": "0.4.20",
"eslint-plugin-storybook": "^9.0.18", "eslint-plugin-storybook": "9.0.18",
"glob": "^11.0.3", "glob": "11.0.3",
"globals": "^16.3.0", "globals": "16.3.0",
"playwright": "^1.54.1", "playwright": "1.54.1",
"storybook": "^9.0.18", "storybook": "9.0.18",
"tw-animate-css": "^1.3.6", "tw-animate-css": "1.3.6",
"typescript": "~5.8.3", "typescript": "~5.8.3",
"typescript-eslint": "^8.35.1", "typescript-eslint": "8.35.1",
"vite": "^7.0.4", "vite": "7.0.4",
"vitest": "^3.2.4" "vitest": "3.2.4"
} }
} }

View File

@ -1,4 +1,4 @@
import * as React from "react" import type * as React from "react"
import * as AccordionPrimitive from "@radix-ui/react-accordion" import * as AccordionPrimitive from "@radix-ui/react-accordion"
import { ChevronDownIcon } from "lucide-react" import { ChevronDownIcon } from "lucide-react"

View File

@ -1,6 +1,6 @@
"use client" "use client"
import * as React from "react" import type * as React from "react"
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"

View File

@ -1,4 +1,4 @@
import * as React from "react" import type * as React from "react"
import { cva, type VariantProps } from "class-variance-authority" import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"

View File

@ -1,6 +1,6 @@
"use client" "use client"
import * as React from "react" import type * as React from "react"
import * as AvatarPrimitive from "@radix-ui/react-avatar" import * as AvatarPrimitive from "@radix-ui/react-avatar"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"

View File

@ -1,4 +1,4 @@
import * as React from "react" import type * as React from "react"
import { Slot } from "@radix-ui/react-slot" import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority" import { cva, type VariantProps } from "class-variance-authority"

View File

@ -1,4 +1,4 @@
import * as React from "react" import type * as React from "react"
import { Slot } from "@radix-ui/react-slot" import { Slot } from "@radix-ui/react-slot"
import { ChevronRight, MoreHorizontal } from "lucide-react" import { ChevronRight, MoreHorizontal } from "lucide-react"
@ -53,7 +53,6 @@ function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
return ( return (
<span <span
data-slot="breadcrumb-page" data-slot="breadcrumb-page"
role="link"
aria-disabled="true" aria-disabled="true"
aria-current="page" aria-current="page"
className={cn("text-foreground font-normal", className)} className={cn("text-foreground font-normal", className)}

View File

@ -4,7 +4,7 @@ import {
ChevronLeftIcon, ChevronLeftIcon,
ChevronRightIcon, ChevronRightIcon,
} from "lucide-react" } from "lucide-react"
import { DayButton, DayPicker, getDefaultClassNames } from "react-day-picker" import { type DayButton, DayPicker, getDefaultClassNames } from "react-day-picker"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import { Button, buttonVariants } from "@/components/button" import { Button, buttonVariants } from "@/components/button"

View File

@ -1,4 +1,4 @@
import * as React from "react" import type * as React from "react"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"

View File

@ -1,241 +1,238 @@
"use client" "use client";
import * as React from "react" import { Button } from "@/components/button";
import { cn } from "@/lib/utils";
import useEmblaCarousel, { import useEmblaCarousel, {
type UseEmblaCarouselType, type UseEmblaCarouselType,
} from "embla-carousel-react" } from "embla-carousel-react";
import { ArrowLeft, ArrowRight } from "lucide-react" import { ArrowLeft, ArrowRight } from "lucide-react";
import * as React from "react";
import { cn } from "@/lib/utils" type CarouselApi = UseEmblaCarouselType[1];
import { Button } from "@/components/button" type UseCarouselParameters = Parameters<typeof useEmblaCarousel>;
type CarouselOptions = UseCarouselParameters[0];
type CarouselApi = UseEmblaCarouselType[1] type CarouselPlugin = UseCarouselParameters[1];
type UseCarouselParameters = Parameters<typeof useEmblaCarousel>
type CarouselOptions = UseCarouselParameters[0]
type CarouselPlugin = UseCarouselParameters[1]
type CarouselProps = { type CarouselProps = {
opts?: CarouselOptions opts?: CarouselOptions;
plugins?: CarouselPlugin plugins?: CarouselPlugin;
orientation?: "horizontal" | "vertical" orientation?: "horizontal" | "vertical";
setApi?: (api: CarouselApi) => void setApi?: (api: CarouselApi) => void;
} };
type CarouselContextProps = { type CarouselContextProps = {
carouselRef: ReturnType<typeof useEmblaCarousel>[0] carouselRef: ReturnType<typeof useEmblaCarousel>[0];
api: ReturnType<typeof useEmblaCarousel>[1] api: ReturnType<typeof useEmblaCarousel>[1];
scrollPrev: () => void scrollPrev: () => void;
scrollNext: () => void scrollNext: () => void;
canScrollPrev: boolean canScrollPrev: boolean;
canScrollNext: boolean canScrollNext: boolean;
} & CarouselProps } & CarouselProps;
const CarouselContext = React.createContext<CarouselContextProps | null>(null) const CarouselContext = React.createContext<CarouselContextProps | null>(null);
function useCarousel() { function useCarousel() {
const context = React.useContext(CarouselContext) const context = React.useContext(CarouselContext);
if (!context) { if (!context) {
throw new Error("useCarousel must be used within a <Carousel />") throw new Error("useCarousel must be used within a <Carousel />");
} }
return context return context;
} }
function Carousel({ function Carousel({
orientation = "horizontal", orientation = "horizontal",
opts, opts,
setApi, setApi,
plugins, plugins,
className, className,
children, children,
...props ...props
}: React.ComponentProps<"div"> & CarouselProps) { }: React.ComponentProps<"div"> & CarouselProps) {
const [carouselRef, api] = useEmblaCarousel( const [carouselRef, api] = useEmblaCarousel(
{ {
...opts, ...opts,
axis: orientation === "horizontal" ? "x" : "y", axis: orientation === "horizontal" ? "x" : "y",
}, },
plugins plugins,
) );
const [canScrollPrev, setCanScrollPrev] = React.useState(false) const [canScrollPrev, setCanScrollPrev] = React.useState(false);
const [canScrollNext, setCanScrollNext] = React.useState(false) const [canScrollNext, setCanScrollNext] = React.useState(false);
const onSelect = React.useCallback((api: CarouselApi) => { const onSelect = React.useCallback((api: CarouselApi) => {
if (!api) return if (!api) return;
setCanScrollPrev(api.canScrollPrev()) setCanScrollPrev(api.canScrollPrev());
setCanScrollNext(api.canScrollNext()) setCanScrollNext(api.canScrollNext());
}, []) }, []);
const scrollPrev = React.useCallback(() => { const scrollPrev = React.useCallback(() => {
api?.scrollPrev() api?.scrollPrev();
}, [api]) }, [api]);
const scrollNext = React.useCallback(() => { const scrollNext = React.useCallback(() => {
api?.scrollNext() api?.scrollNext();
}, [api]) }, [api]);
const handleKeyDown = React.useCallback( const handleKeyDown = React.useCallback(
(event: React.KeyboardEvent<HTMLDivElement>) => { (event: React.KeyboardEvent<HTMLDivElement>) => {
if (event.key === "ArrowLeft") { if (event.key === "ArrowLeft") {
event.preventDefault() event.preventDefault();
scrollPrev() scrollPrev();
} else if (event.key === "ArrowRight") { } else if (event.key === "ArrowRight") {
event.preventDefault() event.preventDefault();
scrollNext() scrollNext();
} }
}, },
[scrollPrev, scrollNext] [scrollPrev, scrollNext],
) );
React.useEffect(() => { React.useEffect(() => {
if (!api || !setApi) return if (!api || !setApi) return;
setApi(api) setApi(api);
}, [api, setApi]) }, [api, setApi]);
React.useEffect(() => { React.useEffect(() => {
if (!api) return if (!api) return;
onSelect(api) onSelect(api);
api.on("reInit", onSelect) api.on("reInit", onSelect);
api.on("select", onSelect) api.on("select", onSelect);
return () => { return () => {
api?.off("select", onSelect) api?.off("select", onSelect);
} };
}, [api, onSelect]) }, [api, onSelect]);
return ( return (
<CarouselContext.Provider <CarouselContext.Provider
value={{ value={{
carouselRef, carouselRef,
api: api, api: api,
opts, opts,
orientation: orientation:
orientation || (opts?.axis === "y" ? "vertical" : "horizontal"), orientation || (opts?.axis === "y" ? "vertical" : "horizontal"),
scrollPrev, scrollPrev,
scrollNext, scrollNext,
canScrollPrev, canScrollPrev,
canScrollNext, canScrollNext,
}} }}
> >
<div <section
onKeyDownCapture={handleKeyDown} onKeyDownCapture={handleKeyDown}
className={cn("relative", className)} className={cn("relative", className)}
role="region" data-slot="carousel"
aria-roledescription="carousel" {...props}
data-slot="carousel" >
{...props} {children}
> </section>
{children} </CarouselContext.Provider>
</div> );
</CarouselContext.Provider>
)
} }
function CarouselContent({ className, ...props }: React.ComponentProps<"div">) { function CarouselContent({ className, ...props }: React.ComponentProps<"div">) {
const { carouselRef, orientation } = useCarousel() const { carouselRef, orientation } = useCarousel();
return ( return (
<div <div
ref={carouselRef} ref={carouselRef}
className="overflow-hidden" className="overflow-hidden"
data-slot="carousel-content" data-slot="carousel-content"
> >
<div <div
className={cn( className={cn(
"flex", "flex",
orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col", orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col",
className className,
)} )}
{...props} {...props}
/> />
</div> </div>
) );
} }
function CarouselItem({ className, ...props }: React.ComponentProps<"div">) { function CarouselItem({ className, ...props }: React.ComponentProps<"div">) {
const { orientation } = useCarousel() const { orientation } = useCarousel();
return ( return (
<div <div
role="group" role="group"
aria-roledescription="slide" aria-roledescription="slide"
data-slot="carousel-item" data-slot="carousel-item"
className={cn( className={cn(
"min-w-0 shrink-0 grow-0 basis-full", "min-w-0 shrink-0 grow-0 basis-full",
orientation === "horizontal" ? "pl-4" : "pt-4", orientation === "horizontal" ? "pl-4" : "pt-4",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
function CarouselPrevious({ function CarouselPrevious({
className, className,
variant = "outline", variant = "outline",
size = "icon", size = "icon",
...props ...props
}: React.ComponentProps<typeof Button>) { }: React.ComponentProps<typeof Button>) {
const { orientation, scrollPrev, canScrollPrev } = useCarousel() const { orientation, scrollPrev, canScrollPrev } = useCarousel();
return ( return (
<Button <Button
data-slot="carousel-previous" data-slot="carousel-previous"
variant={variant} variant={variant}
size={size} size={size}
className={cn( className={cn(
"absolute size-8 rounded-full", "absolute size-8 rounded-full",
orientation === "horizontal" orientation === "horizontal"
? "top-1/2 -left-12 -translate-y-1/2" ? "top-1/2 -left-12 -translate-y-1/2"
: "-top-12 left-1/2 -translate-x-1/2 rotate-90", : "-top-12 left-1/2 -translate-x-1/2 rotate-90",
className className,
)} )}
disabled={!canScrollPrev} disabled={!canScrollPrev}
onClick={scrollPrev} onClick={scrollPrev}
{...props} {...props}
> >
<ArrowLeft /> <ArrowLeft />
<span className="sr-only">Previous slide</span> <span className="sr-only">Previous slide</span>
</Button> </Button>
) );
} }
function CarouselNext({ function CarouselNext({
className, className,
variant = "outline", variant = "outline",
size = "icon", size = "icon",
...props ...props
}: React.ComponentProps<typeof Button>) { }: React.ComponentProps<typeof Button>) {
const { orientation, scrollNext, canScrollNext } = useCarousel() const { orientation, scrollNext, canScrollNext } = useCarousel();
return ( return (
<Button <Button
data-slot="carousel-next" data-slot="carousel-next"
variant={variant} variant={variant}
size={size} size={size}
className={cn( className={cn(
"absolute size-8 rounded-full", "absolute size-8 rounded-full",
orientation === "horizontal" orientation === "horizontal"
? "top-1/2 -right-12 -translate-y-1/2" ? "top-1/2 -right-12 -translate-y-1/2"
: "-bottom-12 left-1/2 -translate-x-1/2 rotate-90", : "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",
className className,
)} )}
disabled={!canScrollNext} disabled={!canScrollNext}
onClick={scrollNext} onClick={scrollNext}
{...props} {...props}
> >
<ArrowRight /> <ArrowRight />
<span className="sr-only">Next slide</span> <span className="sr-only">Next slide</span>
</Button> </Button>
) );
} }
export { export {
type CarouselApi, Carousel,
Carousel, CarouselContent,
CarouselContent, CarouselItem,
CarouselItem, CarouselNext,
CarouselPrevious, CarouselPrevious,
CarouselNext, type CarouselApi,
} };

View File

@ -1,351 +1,352 @@
import * as React from "react" import * as React from "react";
import * as RechartsPrimitive from "recharts" import * as RechartsPrimitive from "recharts";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
// Format: { THEME_NAME: CSS_SELECTOR } // Format: { THEME_NAME: CSS_SELECTOR }
const THEMES = { light: "", dark: ".dark" } as const const THEMES = { light: "", dark: ".dark" } as const;
export type ChartConfig = { export type ChartConfig = {
[k in string]: { [k in string]: {
label?: React.ReactNode label?: React.ReactNode;
icon?: React.ComponentType icon?: React.ComponentType;
} & ( } & (
| { color?: string; theme?: never } | { color?: string; theme?: never }
| { color?: never; theme: Record<keyof typeof THEMES, string> } | { color?: never; theme: Record<keyof typeof THEMES, string> }
) );
} };
type ChartContextProps = { type ChartContextProps = {
config: ChartConfig config: ChartConfig;
} };
const ChartContext = React.createContext<ChartContextProps | null>(null) const ChartContext = React.createContext<ChartContextProps | null>(null);
function useChart() { function useChart() {
const context = React.useContext(ChartContext) const context = React.useContext(ChartContext);
if (!context) { if (!context) {
throw new Error("useChart must be used within a <ChartContainer />") throw new Error("useChart must be used within a <ChartContainer />");
} }
return context return context;
} }
function ChartContainer({ function ChartContainer({
id, id,
className, className,
children, children,
config, config,
...props ...props
}: React.ComponentProps<"div"> & { }: React.ComponentProps<"div"> & {
config: ChartConfig config: ChartConfig;
children: React.ComponentProps< children: React.ComponentProps<
typeof RechartsPrimitive.ResponsiveContainer typeof RechartsPrimitive.ResponsiveContainer
>["children"] >["children"];
}) { }) {
const uniqueId = React.useId() const uniqueId = React.useId();
const chartId = `chart-${id || uniqueId.replace(/:/g, "")}` const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`;
return ( return (
<ChartContext.Provider value={{ config }}> <ChartContext.Provider value={{ config }}>
<div <div
data-slot="chart" data-slot="chart"
data-chart={chartId} data-chart={chartId}
className={cn( className={cn(
"[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border flex aspect-video justify-center text-xs [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden", "[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border flex aspect-video justify-center text-xs [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden",
className className,
)} )}
{...props} {...props}
> >
<ChartStyle id={chartId} config={config} /> <ChartStyle id={chartId} config={config} />
<RechartsPrimitive.ResponsiveContainer> <RechartsPrimitive.ResponsiveContainer>
{children} {children}
</RechartsPrimitive.ResponsiveContainer> </RechartsPrimitive.ResponsiveContainer>
</div> </div>
</ChartContext.Provider> </ChartContext.Provider>
) );
} }
const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => { const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
const colorConfig = Object.entries(config).filter( const colorConfig = Object.entries(config).filter(
([, config]) => config.theme || config.color ([, config]) => config.theme || config.color,
) );
if (!colorConfig.length) { if (!colorConfig.length) {
return null return null;
} }
return ( return (
<style <style
dangerouslySetInnerHTML={{ // biome-ignore lint/security/noDangerouslySetInnerHtml: ok
__html: Object.entries(THEMES) dangerouslySetInnerHTML={{
.map( __html: Object.entries(THEMES)
([theme, prefix]) => ` .map(
([theme, prefix]) => `
${prefix} [data-chart=${id}] { ${prefix} [data-chart=${id}] {
${colorConfig ${colorConfig
.map(([key, itemConfig]) => { .map(([key, itemConfig]) => {
const color = const color =
itemConfig.theme?.[theme as keyof typeof itemConfig.theme] || itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ||
itemConfig.color itemConfig.color;
return color ? ` --color-${key}: ${color};` : null return color ? ` --color-${key}: ${color};` : null;
}) })
.join("\n")} .join("\n")}
}
`
)
.join("\n"),
}}
/>
)
} }
`,
)
.join("\n"),
}}
/>
);
};
const ChartTooltip = RechartsPrimitive.Tooltip const ChartTooltip = RechartsPrimitive.Tooltip;
function ChartTooltipContent({ function ChartTooltipContent({
active, active,
payload, payload,
className, className,
indicator = "dot", indicator = "dot",
hideLabel = false, hideLabel = false,
hideIndicator = false, hideIndicator = false,
label, label,
labelFormatter, labelFormatter,
labelClassName, labelClassName,
formatter, formatter,
color, color,
nameKey, nameKey,
labelKey, labelKey,
}: React.ComponentProps<typeof RechartsPrimitive.Tooltip> & }: React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
React.ComponentProps<"div"> & { React.ComponentProps<"div"> & {
hideLabel?: boolean hideLabel?: boolean;
hideIndicator?: boolean hideIndicator?: boolean;
indicator?: "line" | "dot" | "dashed" indicator?: "line" | "dot" | "dashed";
nameKey?: string nameKey?: string;
labelKey?: string labelKey?: string;
}) { }) {
const { config } = useChart() const { config } = useChart();
const tooltipLabel = React.useMemo(() => { const tooltipLabel = React.useMemo(() => {
if (hideLabel || !payload?.length) { if (hideLabel || !payload?.length) {
return null return null;
} }
const [item] = payload const [item] = payload;
const key = `${labelKey || item?.dataKey || item?.name || "value"}` const key = `${labelKey || item?.dataKey || item?.name || "value"}`;
const itemConfig = getPayloadConfigFromPayload(config, item, key) const itemConfig = getPayloadConfigFromPayload(config, item, key);
const value = const value =
!labelKey && typeof label === "string" !labelKey && typeof label === "string"
? config[label as keyof typeof config]?.label || label ? config[label as keyof typeof config]?.label || label
: itemConfig?.label : itemConfig?.label;
if (labelFormatter) { if (labelFormatter) {
return ( return (
<div className={cn("font-medium", labelClassName)}> <div className={cn("font-medium", labelClassName)}>
{labelFormatter(value, payload)} {labelFormatter(value, payload)}
</div> </div>
) );
} }
if (!value) { if (!value) {
return null return null;
} }
return <div className={cn("font-medium", labelClassName)}>{value}</div> return <div className={cn("font-medium", labelClassName)}>{value}</div>;
}, [ }, [
label, label,
labelFormatter, labelFormatter,
payload, payload,
hideLabel, hideLabel,
labelClassName, labelClassName,
config, config,
labelKey, labelKey,
]) ]);
if (!active || !payload?.length) { if (!active || !payload?.length) {
return null return null;
} }
const nestLabel = payload.length === 1 && indicator !== "dot" const nestLabel = payload.length === 1 && indicator !== "dot";
return ( return (
<div <div
className={cn( className={cn(
"border-border/50 bg-background grid min-w-[8rem] items-start gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs shadow-xl", "border-border/50 bg-background grid min-w-[8rem] items-start gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs shadow-xl",
className className,
)} )}
> >
{!nestLabel ? tooltipLabel : null} {!nestLabel ? tooltipLabel : null}
<div className="grid gap-1.5"> <div className="grid gap-1.5">
{payload.map((item, index) => { {payload.map((item, index) => {
const key = `${nameKey || item.name || item.dataKey || "value"}` const key = `${nameKey || item.name || item.dataKey || "value"}`;
const itemConfig = getPayloadConfigFromPayload(config, item, key) const itemConfig = getPayloadConfigFromPayload(config, item, key);
const indicatorColor = color || item.payload.fill || item.color const indicatorColor = color || item.payload.fill || item.color;
return ( return (
<div <div
key={item.dataKey} key={item.dataKey}
className={cn( className={cn(
"[&>svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5", "[&>svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5",
indicator === "dot" && "items-center" indicator === "dot" && "items-center",
)} )}
> >
{formatter && item?.value !== undefined && item.name ? ( {formatter && item?.value !== undefined && item.name ? (
formatter(item.value, item.name, item, index, item.payload) formatter(item.value, item.name, item, index, item.payload)
) : ( ) : (
<> <>
{itemConfig?.icon ? ( {itemConfig?.icon ? (
<itemConfig.icon /> <itemConfig.icon />
) : ( ) : (
!hideIndicator && ( !hideIndicator && (
<div <div
className={cn( className={cn(
"shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)", "shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)",
{ {
"h-2.5 w-2.5": indicator === "dot", "h-2.5 w-2.5": indicator === "dot",
"w-1": indicator === "line", "w-1": indicator === "line",
"w-0 border-[1.5px] border-dashed bg-transparent": "w-0 border-[1.5px] border-dashed bg-transparent":
indicator === "dashed", indicator === "dashed",
"my-0.5": nestLabel && indicator === "dashed", "my-0.5": nestLabel && indicator === "dashed",
} },
)} )}
style={ style={
{ {
"--color-bg": indicatorColor, "--color-bg": indicatorColor,
"--color-border": indicatorColor, "--color-border": indicatorColor,
} as React.CSSProperties } as React.CSSProperties
} }
/> />
) )
)} )}
<div <div
className={cn( className={cn(
"flex flex-1 justify-between leading-none", "flex flex-1 justify-between leading-none",
nestLabel ? "items-end" : "items-center" nestLabel ? "items-end" : "items-center",
)} )}
> >
<div className="grid gap-1.5"> <div className="grid gap-1.5">
{nestLabel ? tooltipLabel : null} {nestLabel ? tooltipLabel : null}
<span className="text-muted-foreground"> <span className="text-muted-foreground">
{itemConfig?.label || item.name} {itemConfig?.label || item.name}
</span> </span>
</div> </div>
{item.value && ( {item.value && (
<span className="text-foreground font-mono font-medium tabular-nums"> <span className="text-foreground font-mono font-medium tabular-nums">
{item.value.toLocaleString()} {item.value.toLocaleString()}
</span> </span>
)} )}
</div> </div>
</> </>
)} )}
</div> </div>
) );
})} })}
</div> </div>
</div> </div>
) );
} }
const ChartLegend = RechartsPrimitive.Legend const ChartLegend = RechartsPrimitive.Legend;
function ChartLegendContent({ function ChartLegendContent({
className, className,
hideIcon = false, hideIcon = false,
payload, payload,
verticalAlign = "bottom", verticalAlign = "bottom",
nameKey, nameKey,
}: React.ComponentProps<"div"> & }: React.ComponentProps<"div"> &
Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & { Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
hideIcon?: boolean hideIcon?: boolean;
nameKey?: string nameKey?: string;
}) { }) {
const { config } = useChart() const { config } = useChart();
if (!payload?.length) { if (!payload?.length) {
return null return null;
} }
return ( return (
<div <div
className={cn( className={cn(
"flex items-center justify-center gap-4", "flex items-center justify-center gap-4",
verticalAlign === "top" ? "pb-3" : "pt-3", verticalAlign === "top" ? "pb-3" : "pt-3",
className className,
)} )}
> >
{payload.map((item) => { {payload.map((item) => {
const key = `${nameKey || item.dataKey || "value"}` const key = `${nameKey || item.dataKey || "value"}`;
const itemConfig = getPayloadConfigFromPayload(config, item, key) const itemConfig = getPayloadConfigFromPayload(config, item, key);
return ( return (
<div <div
key={item.value} key={item.value}
className={cn( className={cn(
"[&>svg]:text-muted-foreground flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3" "[&>svg]:text-muted-foreground flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3",
)} )}
> >
{itemConfig?.icon && !hideIcon ? ( {itemConfig?.icon && !hideIcon ? (
<itemConfig.icon /> <itemConfig.icon />
) : ( ) : (
<div <div
className="h-2 w-2 shrink-0 rounded-[2px]" className="h-2 w-2 shrink-0 rounded-[2px]"
style={{ style={{
backgroundColor: item.color, backgroundColor: item.color,
}} }}
/> />
)} )}
{itemConfig?.label} {itemConfig?.label}
</div> </div>
) );
})} })}
</div> </div>
) );
} }
// Helper to extract item config from a payload. // Helper to extract item config from a payload.
function getPayloadConfigFromPayload( function getPayloadConfigFromPayload(
config: ChartConfig, config: ChartConfig,
payload: unknown, payload: unknown,
key: string key: string,
) { ) {
if (typeof payload !== "object" || payload === null) { if (typeof payload !== "object" || payload === null) {
return undefined return undefined;
} }
const payloadPayload = const payloadPayload =
"payload" in payload && "payload" in payload &&
typeof payload.payload === "object" && typeof payload.payload === "object" &&
payload.payload !== null payload.payload !== null
? payload.payload ? payload.payload
: undefined : undefined;
let configLabelKey: string = key let configLabelKey: string = key;
if ( if (
key in payload && key in payload &&
typeof payload[key as keyof typeof payload] === "string" typeof payload[key as keyof typeof payload] === "string"
) { ) {
configLabelKey = payload[key as keyof typeof payload] as string configLabelKey = payload[key as keyof typeof payload] as string;
} else if ( } else if (
payloadPayload && payloadPayload &&
key in payloadPayload && key in payloadPayload &&
typeof payloadPayload[key as keyof typeof payloadPayload] === "string" typeof payloadPayload[key as keyof typeof payloadPayload] === "string"
) { ) {
configLabelKey = payloadPayload[ configLabelKey = payloadPayload[
key as keyof typeof payloadPayload key as keyof typeof payloadPayload
] as string ] as string;
} }
return configLabelKey in config return configLabelKey in config
? config[configLabelKey] ? config[configLabelKey]
: config[key as keyof typeof config] : config[key as keyof typeof config];
} }
export { export {
ChartContainer, ChartContainer,
ChartTooltip, ChartLegend,
ChartTooltipContent, ChartLegendContent,
ChartLegend, ChartStyle,
ChartLegendContent, ChartTooltip,
ChartStyle, ChartTooltipContent,
} };

View File

@ -1,6 +1,6 @@
"use client" "use client"
import * as React from "react" import type * as React from "react"
import * as CheckboxPrimitive from "@radix-ui/react-checkbox" import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
import { CheckIcon } from "lucide-react" import { CheckIcon } from "lucide-react"

View File

@ -1,6 +1,6 @@
"use client" "use client"
import * as React from "react" import type * as React from "react"
import { Command as CommandPrimitive } from "cmdk" import { Command as CommandPrimitive } from "cmdk"
import { SearchIcon } from "lucide-react" import { SearchIcon } from "lucide-react"

View File

@ -1,6 +1,6 @@
"use client" "use client"
import * as React from "react" import type * as React from "react"
import * as ContextMenuPrimitive from "@radix-ui/react-context-menu" import * as ContextMenuPrimitive from "@radix-ui/react-context-menu"
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react" import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"

View File

@ -1,4 +1,4 @@
import * as React from "react" import type * as React from "react"
import * as DialogPrimitive from "@radix-ui/react-dialog" import * as DialogPrimitive from "@radix-ui/react-dialog"
import { XIcon } from "lucide-react" import { XIcon } from "lucide-react"

View File

@ -1,4 +1,4 @@
import * as React from "react" import type * as React from "react"
import { Drawer as DrawerPrimitive } from "vaul" import { Drawer as DrawerPrimitive } from "vaul"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"

View File

@ -1,6 +1,6 @@
"use client" "use client"
import * as React from "react" import type * as React from "react"
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu" import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react" import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"

View File

@ -1,5 +1,5 @@
import * as React from "react" import * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label" import type * as LabelPrimitive from "@radix-ui/react-label"
import { Slot } from "@radix-ui/react-slot" import { Slot } from "@radix-ui/react-slot"
import { import {
Controller, Controller,

View File

@ -1,4 +1,4 @@
import * as React from "react" import type * as React from "react"
import * as HoverCardPrimitive from "@radix-ui/react-hover-card" import * as HoverCardPrimitive from "@radix-ui/react-hover-card"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"

View File

@ -1,77 +1,77 @@
"use client" "use client";
import * as React from "react" import { OTPInput, OTPInputContext } from "input-otp";
import { OTPInput, OTPInputContext } from "input-otp" import { MinusIcon } from "lucide-react";
import { MinusIcon } from "lucide-react" import * as React from "react";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
function InputOTP({ function InputOTP({
className, className,
containerClassName, containerClassName,
...props ...props
}: React.ComponentProps<typeof OTPInput> & { }: React.ComponentProps<typeof OTPInput> & {
containerClassName?: string containerClassName?: string;
}) { }) {
return ( return (
<OTPInput <OTPInput
data-slot="input-otp" data-slot="input-otp"
containerClassName={cn( containerClassName={cn(
"flex items-center gap-2 has-disabled:opacity-50", "flex items-center gap-2 has-disabled:opacity-50",
containerClassName containerClassName,
)} )}
className={cn("disabled:cursor-not-allowed", className)} className={cn("disabled:cursor-not-allowed", className)}
{...props} {...props}
/> />
) );
} }
function InputOTPGroup({ className, ...props }: React.ComponentProps<"div">) { function InputOTPGroup({ className, ...props }: React.ComponentProps<"div">) {
return ( return (
<div <div
data-slot="input-otp-group" data-slot="input-otp-group"
className={cn("flex items-center", className)} className={cn("flex items-center", className)}
{...props} {...props}
/> />
) );
} }
function InputOTPSlot({ function InputOTPSlot({
index, index,
className, className,
...props ...props
}: React.ComponentProps<"div"> & { }: React.ComponentProps<"div"> & {
index: number index: number;
}) { }) {
const inputOTPContext = React.useContext(OTPInputContext) const inputOTPContext = React.useContext(OTPInputContext);
const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {} const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {};
return ( return (
<div <div
data-slot="input-otp-slot" data-slot="input-otp-slot"
data-active={isActive} data-active={isActive}
className={cn( className={cn(
"data-[active=true]:border-ring data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:ring-destructive/20 dark:data-[active=true]:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[active=true]:aria-invalid:border-destructive dark:bg-input/30 border-input relative flex h-9 w-9 items-center justify-center border-y border-r text-sm shadow-xs transition-all outline-none first:rounded-l-md first:border-l last:rounded-r-md data-[active=true]:z-10 data-[active=true]:ring-[3px]", "data-[active=true]:border-ring data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:ring-destructive/20 dark:data-[active=true]:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[active=true]:aria-invalid:border-destructive dark:bg-input/30 border-input relative flex h-9 w-9 items-center justify-center border-y border-r text-sm shadow-xs transition-all outline-none first:rounded-l-md first:border-l last:rounded-r-md data-[active=true]:z-10 data-[active=true]:ring-[3px]",
className className,
)} )}
{...props} {...props}
> >
{char} {char}
{hasFakeCaret && ( {hasFakeCaret && (
<div className="pointer-events-none absolute inset-0 flex items-center justify-center"> <div className="pointer-events-none absolute inset-0 flex items-center justify-center">
<div className="animate-caret-blink bg-foreground h-4 w-px duration-1000" /> <div className="animate-caret-blink bg-foreground h-4 w-px duration-1000" />
</div> </div>
)} )}
</div> </div>
) );
} }
function InputOTPSeparator({ ...props }: React.ComponentProps<"div">) { function InputOTPSeparator({ ...props }: React.ComponentProps<"div">) {
return ( return (
<div data-slot="input-otp-separator" role="separator" {...props}> <div data-slot="input-otp-separator" {...props}>
<MinusIcon /> <MinusIcon />
</div> </div>
) );
} }
export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator } export { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot };

View File

@ -1,4 +1,4 @@
import * as React from "react" import type * as React from "react"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"

View File

@ -1,6 +1,6 @@
"use client" "use client"
import * as React from "react" import type * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label" import * as LabelPrimitive from "@radix-ui/react-label"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"

View File

@ -1,4 +1,4 @@
import * as React from "react" import type * as React from "react"
import * as MenubarPrimitive from "@radix-ui/react-menubar" import * as MenubarPrimitive from "@radix-ui/react-menubar"
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react" import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"

View File

@ -1,4 +1,4 @@
import * as React from "react" import type * as React from "react"
import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu" import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu"
import { cva } from "class-variance-authority" import { cva } from "class-variance-authority"
import { ChevronDownIcon } from "lucide-react" import { ChevronDownIcon } from "lucide-react"

View File

@ -1,127 +1,125 @@
import * as React from "react" import { type Button, buttonVariants } from "@/components/button";
import { cn } from "@/lib/utils";
import { import {
ChevronLeftIcon, ChevronLeftIcon,
ChevronRightIcon, ChevronRightIcon,
MoreHorizontalIcon, MoreHorizontalIcon,
} from "lucide-react" } from "lucide-react";
import type * as React from "react";
import { cn } from "@/lib/utils"
import { Button, buttonVariants } from "@/components/button"
function Pagination({ className, ...props }: React.ComponentProps<"nav">) { function Pagination({ className, ...props }: React.ComponentProps<"nav">) {
return ( return (
<nav <nav
role="navigation" aria-label="pagination"
aria-label="pagination" data-slot="pagination"
data-slot="pagination" className={cn("mx-auto flex w-full justify-center", className)}
className={cn("mx-auto flex w-full justify-center", className)} {...props}
{...props} />
/> );
)
} }
function PaginationContent({ function PaginationContent({
className, className,
...props ...props
}: React.ComponentProps<"ul">) { }: React.ComponentProps<"ul">) {
return ( return (
<ul <ul
data-slot="pagination-content" data-slot="pagination-content"
className={cn("flex flex-row items-center gap-1", className)} className={cn("flex flex-row items-center gap-1", className)}
{...props} {...props}
/> />
) );
} }
function PaginationItem({ ...props }: React.ComponentProps<"li">) { function PaginationItem({ ...props }: React.ComponentProps<"li">) {
return <li data-slot="pagination-item" {...props} /> return <li data-slot="pagination-item" {...props} />;
} }
type PaginationLinkProps = { type PaginationLinkProps = {
isActive?: boolean isActive?: boolean;
} & Pick<React.ComponentProps<typeof Button>, "size"> & } & Pick<React.ComponentProps<typeof Button>, "size"> &
React.ComponentProps<"a"> React.ComponentProps<"a">;
function PaginationLink({ function PaginationLink({
className, className,
isActive, isActive,
size = "icon", size = "icon",
...props ...props
}: PaginationLinkProps) { }: PaginationLinkProps) {
return ( return (
<a <a
aria-current={isActive ? "page" : undefined} aria-current={isActive ? "page" : undefined}
data-slot="pagination-link" data-slot="pagination-link"
data-active={isActive} data-active={isActive}
className={cn( className={cn(
buttonVariants({ buttonVariants({
variant: isActive ? "outline" : "ghost", variant: isActive ? "outline" : "ghost",
size, size,
}), }),
className className,
)} )}
{...props} {...props}
/> />
) );
} }
function PaginationPrevious({ function PaginationPrevious({
className, className,
...props ...props
}: React.ComponentProps<typeof PaginationLink>) { }: React.ComponentProps<typeof PaginationLink>) {
return ( return (
<PaginationLink <PaginationLink
aria-label="Go to previous page" aria-label="Go to previous page"
size="default" size="default"
className={cn("gap-1 px-2.5 sm:pl-2.5", className)} className={cn("gap-1 px-2.5 sm:pl-2.5", className)}
{...props} {...props}
> >
<ChevronLeftIcon /> <ChevronLeftIcon />
<span className="hidden sm:block">Previous</span> <span className="hidden sm:block">Previous</span>
</PaginationLink> </PaginationLink>
) );
} }
function PaginationNext({ function PaginationNext({
className, className,
...props ...props
}: React.ComponentProps<typeof PaginationLink>) { }: React.ComponentProps<typeof PaginationLink>) {
return ( return (
<PaginationLink <PaginationLink
aria-label="Go to next page" aria-label="Go to next page"
size="default" size="default"
className={cn("gap-1 px-2.5 sm:pr-2.5", className)} className={cn("gap-1 px-2.5 sm:pr-2.5", className)}
{...props} {...props}
> >
<span className="hidden sm:block">Next</span> <span className="hidden sm:block">Next</span>
<ChevronRightIcon /> <ChevronRightIcon />
</PaginationLink> </PaginationLink>
) );
} }
function PaginationEllipsis({ function PaginationEllipsis({
className, className,
...props ...props
}: React.ComponentProps<"span">) { }: React.ComponentProps<"span">) {
return ( return (
<span <span
aria-hidden aria-hidden
data-slot="pagination-ellipsis" data-slot="pagination-ellipsis"
className={cn("flex size-9 items-center justify-center", className)} className={cn("flex size-9 items-center justify-center", className)}
{...props} {...props}
> >
<MoreHorizontalIcon className="size-4" /> <MoreHorizontalIcon className="size-4" />
<span className="sr-only">More pages</span> <span className="sr-only">More pages</span>
</span> </span>
) );
} }
export { export {
Pagination, Pagination,
PaginationContent, PaginationContent,
PaginationLink, PaginationEllipsis,
PaginationItem, PaginationItem,
PaginationPrevious, PaginationLink,
PaginationNext, PaginationNext,
PaginationEllipsis, PaginationPrevious,
} };

View File

@ -1,6 +1,6 @@
"use client" "use client"
import * as React from "react" import type * as React from "react"
import * as PopoverPrimitive from "@radix-ui/react-popover" import * as PopoverPrimitive from "@radix-ui/react-popover"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"

View File

@ -1,4 +1,4 @@
import * as React from "react" import type * as React from "react"
import * as ProgressPrimitive from "@radix-ui/react-progress" import * as ProgressPrimitive from "@radix-ui/react-progress"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"

View File

@ -1,6 +1,6 @@
"use client" "use client"
import * as React from "react" import type * as React from "react"
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group" import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
import { CircleIcon } from "lucide-react" import { CircleIcon } from "lucide-react"

View File

@ -1,4 +1,4 @@
import * as React from "react" import type * as React from "react"
import { GripVerticalIcon } from "lucide-react" import { GripVerticalIcon } from "lucide-react"
import * as ResizablePrimitive from "react-resizable-panels" import * as ResizablePrimitive from "react-resizable-panels"

View File

@ -1,6 +1,6 @@
"use client" "use client"
import * as React from "react" import type * as React from "react"
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area" import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"

View File

@ -1,4 +1,4 @@
import * as React from "react" import type * as React from "react"
import * as SelectPrimitive from "@radix-ui/react-select" import * as SelectPrimitive from "@radix-ui/react-select"
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react" import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"

View File

@ -1,6 +1,6 @@
"use client" "use client"
import * as React from "react" import type * as React from "react"
import * as SeparatorPrimitive from "@radix-ui/react-separator" import * as SeparatorPrimitive from "@radix-ui/react-separator"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"

View File

@ -1,4 +1,4 @@
import * as React from "react" import type * as React from "react"
import * as SheetPrimitive from "@radix-ui/react-dialog" import * as SheetPrimitive from "@radix-ui/react-dialog"
import { XIcon } from "lucide-react" import { XIcon } from "lucide-react"

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
"use client" "use client"
import * as React from "react" import type * as React from "react"
import * as SwitchPrimitive from "@radix-ui/react-switch" import * as SwitchPrimitive from "@radix-ui/react-switch"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"

View File

@ -1,4 +1,4 @@
import * as React from "react" import type * as React from "react"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"

View File

@ -1,6 +1,6 @@
"use client" "use client"
import * as React from "react" import type * as React from "react"
import * as TabsPrimitive from "@radix-ui/react-tabs" import * as TabsPrimitive from "@radix-ui/react-tabs"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"

View File

@ -1,4 +1,4 @@
import * as React from "react" import type * as React from "react"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"

View File

@ -2,7 +2,7 @@
import * as React from "react" import * as React from "react"
import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group" import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group"
import { type VariantProps } from "class-variance-authority" import type { VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import { toggleVariants } from "@/components/toggle" import { toggleVariants } from "@/components/toggle"

View File

@ -1,4 +1,4 @@
import * as React from "react" import type * as React from "react"
import * as TogglePrimitive from "@radix-ui/react-toggle" import * as TogglePrimitive from "@radix-ui/react-toggle"
import { cva, type VariantProps } from "class-variance-authority" import { cva, type VariantProps } from "class-variance-authority"

View File

@ -1,4 +1,4 @@
import * as React from "react" import type * as React from "react"
import * as TooltipPrimitive from "@radix-ui/react-tooltip" import * as TooltipPrimitive from "@radix-ui/react-tooltip"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"

2
src/icons.ts Normal file
View File

@ -0,0 +1,2 @@
// Re-export all lucide-react icons
export * from "lucide-react";

View File

@ -1,6 +1,6 @@
import { clsx, type ClassValue } from "clsx" import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge" import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) { export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs)) return twMerge(clsx(inputs));
} }

View File

@ -1,5 +1,3 @@
// Import the main CSS file to ensure it's bundled // Import the main CSS file to ensure it's bundled
import "./index.css"; import "./index.css";
// This file exists solely to bundle the CSS
export {};