chore: upgrade to tailwind v4 and add a generic filter for dynamic filter handling
All checks were successful
Build and Push Docker Images / Build and Push Docker Images (push) Successful in 7m51s

This commit is contained in:
2025-06-27 14:27:44 +05:00
parent 9e0d2d277b
commit 11ac852762
36 changed files with 1552 additions and 1376 deletions

View File

@ -3,7 +3,7 @@ import React from 'react'
export default function Agreements() { export default function Agreements() {
return ( return (
<div> <div>
<div className="flex justify-between items-center border-[1px] rounded-md border-dashed font-bold title-bg py-4 px-2 mb-4"> <div className="flex justify-between items-center border rounded-md border-dashed font-bold title-bg py-4 px-2 mb-4">
<h3 className="text-sarLinkOrange text-2xl"> <h3 className="text-sarLinkOrange text-2xl">
Agreements Agreements
</h3> </h3>

View File

@ -21,7 +21,7 @@ export default async function Devices({
if (session?.user?.is_admin) return redirect("/user-devices"); if (session?.user?.is_admin) return redirect("/user-devices");
return ( return (
<div> <div>
<div className="flex justify-between items-center border-[1px] rounded-md border-dashed font-bold title-bg py-4 px-2 mb-4"> <div className="flex justify-between items-center border rounded-md border-dashed font-bold title-bg py-4 px-2 mb-4">
<h3 className="text-sarLinkOrange text-2xl">My Devices</h3> <h3 className="text-sarLinkOrange text-2xl">My Devices</h3>
<AddDeviceDialogForm user_id={session?.user?.id} /> <AddDeviceDialogForm user_id={session?.user?.id} />
</div> </div>

View File

@ -15,7 +15,7 @@ export default async function ParentalControl({
const query = (await searchParams)?.query || ""; const query = (await searchParams)?.query || "";
return ( return (
<div> <div>
<div className="flex justify-between items-center border-[1px] rounded-md border-dashed font-bold title-bg py-4 px-2 mb-4"> <div className="flex justify-between items-center border rounded-md border-dashed font-bold title-bg py-4 px-2 mb-4">
<h3 className="text-sarLinkOrange text-2xl">Parental Control</h3> <h3 className="text-sarLinkOrange text-2xl">Parental Control</h3>
</div> </div>

View File

@ -25,13 +25,13 @@ export default async function PaymentPage({
return ( return (
<div> <div>
<div className="flex justify-between items-center border-[1px] rounded-md border-dashed font-bold title-bg py-4 px-4 mb-4 mx-2"> <div className="flex justify-between items-center border rounded-md border-dashed font-bold title-bg py-4 px-4 mb-4 mx-2">
<h3 className="text-sarLinkOrange text-2xl">Payment</h3> <h3 className="text-sarLinkOrange text-2xl">Payment</h3>
<div className="flex flex-col gap-4 items-end w-full"> <div className="flex flex-col gap-4 items-end w-full">
<Button <Button
disabled disabled
className={cn( className={cn(
"rounded-md !opacity-100 uppercase font-semibold", "rounded-md opacity-100! uppercase font-semibold",
payment?.paid payment?.paid
? "text-green-500 bg-green-500/20" ? "text-green-500 bg-green-500/20"
: "text-yellow-500 bg-yellow-900", : "text-yellow-500 bg-yellow-900",

View File

@ -17,15 +17,14 @@ export default async function Payments({
}) { }) {
const query = (await searchParams)?.query || ""; const query = (await searchParams)?.query || "";
const session = await getServerSession(authOptions); const session = await getServerSession(authOptions);
if (!session?.user?.is_admin) { if (session?.user?.is_admin) {
return redirect("/user-payments"); return redirect("/user-payments");
} }
return ( return (
<div> <div>
<div className="flex justify-between items-center border-[1px] rounded-md border-dashed font-bold title-bg py-4 px-2 mb-4"> <div className="flex justify-between items-center border rounded-md border-dashed font-bold title-bg py-4 px-2 mb-4">
<h3 className="text-sarLinkOrange text-2xl">My Payments</h3> <h3 className="text-sarLinkOrange text-2xl">My Payments</h3>
</div> </div>
<div <div
id="user-filters" id="user-filters"
className=" pb-4 gap-4 flex sm:flex-row flex-col items-start justify-start" className=" pb-4 gap-4 flex sm:flex-row flex-col items-start justify-start"

View File

@ -18,7 +18,7 @@ export default async function UserDevices({
const query = (await searchParams)?.query || ""; const query = (await searchParams)?.query || "";
return ( return (
<div> <div>
<div className="flex justify-between items-center border-[1px] rounded-md border-dashed font-bold title-bg py-4 px-2 mb-4"> <div className="flex justify-between items-center border rounded-md border-dashed font-bold title-bg py-4 px-2 mb-4">
<h3 className="text-sarLinkOrange text-2xl"> <h3 className="text-sarLinkOrange text-2xl">
User Devices User Devices
</h3> </h3>

View File

@ -1,4 +1,6 @@
import { authOptions } from "@/app/auth";
import { UsersPaymentsTable } from "@/components/admin/user-payments-table"; import { UsersPaymentsTable } from "@/components/admin/user-payments-table";
import { getServerSession } from "next-auth";
import React, { Suspense } from "react"; import React, { Suspense } from "react";
export default async function UserPayments({ export default async function UserPayments({
@ -12,12 +14,13 @@ export default async function UserPayments({
}>; }>;
}) { }) {
const query = (await searchParams)?.query || ""; const query = (await searchParams)?.query || "";
const session = await getServerSession(authOptions);
return ( return (
<div> <div>
<div className="flex justify-between items-center border-[1px] rounded-md border-dashed font-bold title-bg py-4 px-2 mb-4"> <div className="flex justify-between items-center border rounded-md border-dashed font-bold title-bg py-4 px-2 mb-4">
<h3 className="text-sarLinkOrange text-2xl">User Payments</h3> <h3 className="text-sarLinkOrange text-2xl">User Payments</h3>
</div> </div>
<pre>{JSON.stringify(session, null, 2)}</pre>
<Suspense key={query} fallback={"loading...."}> <Suspense key={query} fallback={"loading...."}>
<UsersPaymentsTable searchParams={searchParams} /> <UsersPaymentsTable searchParams={searchParams} />
</Suspense> </Suspense>

View File

@ -35,7 +35,7 @@ export default async function AdminUsers({
}) { }) {
return ( return (
<div> <div>
<div className="flex justify-between items-center border-[1px] rounded-md border-dashed font-bold title-bg py-4 px-2 mb-4"> <div className="flex justify-between items-center border rounded-md border-dashed font-bold title-bg py-4 px-2 mb-4">
<h3 className="text-sarLinkOrange text-2xl">Users</h3> <h3 className="text-sarLinkOrange text-2xl">Users</h3>
</div> </div>

View File

@ -3,7 +3,7 @@ import React from 'react'
export default function UserWallet() { export default function UserWallet() {
return ( return (
<div> <div>
<div className="flex justify-between items-center border-[1px] rounded-md border-dashed font-bold title-bg py-4 px-2 mb-4"> <div className="flex justify-between items-center border rounded-md border-dashed font-bold title-bg py-4 px-2 mb-4">
<h3 className="text-sarLinkOrange text-2xl"> <h3 className="text-sarLinkOrange text-2xl">
My Wallet My Wallet
</h3> </h3>

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,7 @@
"use client"; "use client";
import { ListFilter, Loader2, X } from "lucide-react";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useEffect, useMemo, useState, useTransition } from "react";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox"; import { Checkbox } from "@/components/ui/checkbox";
@ -13,11 +16,8 @@ import {
DrawerTrigger, DrawerTrigger,
} from "@/components/ui/drawer"; } from "@/components/ui/drawer";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; // Assuming shadcn Label import { Label } from "@/components/ui/label";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { ListFilter, Loader2, X } from "lucide-react";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useEffect, useMemo, useState, useTransition } from "react";
import { RadioGroup, RadioGroupItem } from "./ui/radio-group"; import { RadioGroup, RadioGroupItem } from "./ui/radio-group";
interface CheckboxGroupProps { interface CheckboxGroupProps {
@ -48,7 +48,10 @@ const CheckboxGroup: React.FC<CheckboxGroupProps> = ({
<Label className="font-semibold text-sm">{label}</Label> <Label className="font-semibold text-sm">{label}</Label>
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
{options.map((option) => ( {options.map((option) => (
<div key={`${name}-${option.value}`} className="flex items-center space-x-2"> <div
key={`${name}-${option.value}`}
className="flex items-center space-x-2"
>
<Checkbox <Checkbox
id={`${name}-${option.value}`} id={`${name}-${option.value}`}
checked={selectedValues.includes(option.value)} checked={selectedValues.includes(option.value)}
@ -64,7 +67,6 @@ const CheckboxGroup: React.FC<CheckboxGroupProps> = ({
); );
}; };
type FilterOption = { value: string; label: string }; type FilterOption = { value: string; label: string };
type FilterInputConfig<TKey extends string> = type FilterInputConfig<TKey extends string> =
@ -90,8 +92,10 @@ type FilterInputConfig<TKey extends string> =
}; };
// Utility type to extract the config for a specific key from the array // Utility type to extract the config for a specific key from the array
type GetConfigForKey<TKey, TConfigArray extends FilterInputConfig<string>[]> = type GetConfigForKey<
TConfigArray extends ReadonlyArray<infer U> TKey,
TConfigArray extends FilterInputConfig<string>[],
> = TConfigArray extends ReadonlyArray<infer U>
? U extends FilterInputConfig<string> & { name: TKey } ? U extends FilterInputConfig<string> & { name: TKey }
? U ? U
: never : never
@ -102,7 +106,9 @@ type FilterValues<
TFilterKeys extends string, TFilterKeys extends string,
TInputs extends FilterInputConfig<string>[], TInputs extends FilterInputConfig<string>[],
> = { > = {
[K in TFilterKeys]: GetConfigForKey<K, TInputs> extends { type: "checkbox-group" } [K in TFilterKeys]: GetConfigForKey<K, TInputs> extends {
type: "checkbox-group";
}
? string[] ? string[]
: GetConfigForKey<K, TInputs> extends { type: "radio-group" } : GetConfigForKey<K, TInputs> extends { type: "radio-group" }
? string ? string
@ -124,7 +130,6 @@ interface DynamicFilterProps<
description?: string; description?: string;
} }
// --- Main DynamicFilter Component ---
export default function DynamicFilter< export default function DynamicFilter<
TFilterKeys extends string, TFilterKeys extends string,
TInputs extends FilterInputConfig<TFilterKeys>[], TInputs extends FilterInputConfig<TFilterKeys>[],
@ -153,17 +158,24 @@ export default function DynamicFilter<
const urlValue = searchParams.get(input.name); const urlValue = searchParams.get(input.name);
if (input.type === "checkbox-group") { if (input.type === "checkbox-group") {
const deserialize = input.deserialize || defaultDeserialize; const deserialize = input.deserialize || defaultDeserialize;
(initialState as FilterValues<TFilterKeys, TInputs>)[input.name] = deserialize(urlValue || "") as FilterValues<TFilterKeys, TInputs>[typeof input.name]; (initialState as FilterValues<TFilterKeys, TInputs>)[input.name] =
deserialize(urlValue || "") as FilterValues<
TFilterKeys,
TInputs
>[typeof input.name];
} else { } else {
(initialState as FilterValues<TFilterKeys, TInputs>)[input.name] = (urlValue || "") as FilterValues<TFilterKeys, TInputs>[typeof input.name]; (initialState as FilterValues<TFilterKeys, TInputs>)[input.name] =
(urlValue || "") as FilterValues<
TFilterKeys,
TInputs
>[typeof input.name];
} }
} }
return initialState as FilterValues<TFilterKeys, TInputs>; return initialState as FilterValues<TFilterKeys, TInputs>;
}, [inputs, searchParams]); // Re-initialize if inputs config or URL searchParams change }, [inputs, searchParams]); // Re-initialize if inputs config or URL searchParams change
const [inputValues, setInputValues] = useState< const [inputValues, setInputValues] =
FilterValues<TFilterKeys, TInputs> useState<FilterValues<TFilterKeys, TInputs>>(initialInputState);
>(initialInputState);
// Update local state if URL searchParams change while drawer is closed // Update local state if URL searchParams change while drawer is closed
// This is important if filters are applied externally or on page load // This is important if filters are applied externally or on page load
@ -173,7 +185,6 @@ export default function DynamicFilter<
} }
}, [isOpen, initialInputState]); }, [isOpen, initialInputState]);
// Handler for text/number input changes // Handler for text/number input changes
const handleTextOrNumberInputChange = const handleTextOrNumberInputChange =
(name: TFilterKeys) => (e: React.ChangeEvent<HTMLInputElement>) => { (name: TFilterKeys) => (e: React.ChangeEvent<HTMLInputElement>) => {
@ -217,31 +228,34 @@ export default function DynamicFilter<
} }
} }
newParams.set("page", "1"); // Always reset page on filter apply newParams.set("page", "1");
startTransition(() => { startTransition(() => {
replace(`${pathname}?${newParams.toString()}`); replace(`${pathname}?${newParams.toString()}`);
setIsOpen(false); // Close the drawer after applying filters setIsOpen(false);
}); });
}; };
// Handles clearing all filters // Handles clearing all filters
const handleClearFilters = () => { const handleClearFilters = () => {
// Reset local input values // Reset local input values
const clearedInputState: Partial<FilterValues<TFilterKeys, TInputs>> = {}; const clearedInputState: Partial<FilterValues<TFilterKeys, TInputs>> = {};
for (const input of inputs) { for (const input of inputs) {
if (input.type === "checkbox-group") { if (input.type === "checkbox-group") {
(clearedInputState as FilterValues<TFilterKeys, TInputs>)[input.name as TFilterKeys] = [] as any; (clearedInputState as FilterValues<TFilterKeys, TInputs>)[
input.name as TFilterKeys
] = [] as any;
} else { } else {
(clearedInputState as FilterValues<TFilterKeys, TInputs>)[input.name as TFilterKeys] = "" as any; (clearedInputState as FilterValues<TFilterKeys, TInputs>)[
input.name as TFilterKeys
] = "" as any;
} }
} }
setInputValues(clearedInputState as FilterValues<TFilterKeys, TInputs>); setInputValues(clearedInputState as FilterValues<TFilterKeys, TInputs>);
startTransition(() => { startTransition(() => {
replace(pathname); // Navigate to base path replace(pathname);
setIsOpen(false); // Close the drawer setIsOpen(false);
}); });
}; };
@ -289,13 +303,12 @@ export default function DynamicFilter<
) => { ) => {
if (config.type === "checkbox-group") { if (config.type === "checkbox-group") {
const labels = (value as string[]) const labels = (value as string[])
.map( .map((v) => config.options.find((opt) => opt.value === v)?.label || v)
(v) => config.options.find((opt) => opt.value === v)?.label || v,
)
.join(", "); .join(", ");
return ( return (
<p> <p>
{config.label.replace(/%20/g, " ")}: <span className="text-muted-foreground">{labels}</span> {config.label.replace(/%20/g, " ")}:{" "}
<span className="text-muted-foreground">{labels}</span>
</p> </p>
); );
} }
@ -373,8 +386,13 @@ export default function DynamicFilter<
); );
case "radio-group": case "radio-group":
return ( return (
<div key={input.name} className="flex flex-col gap-2 p-2 border rounded-md"> <div
<Label className="font-semibold text-sm">{input.label}</Label> key={input.name}
className="flex flex-col gap-2 p-2 border rounded-md"
>
<Label className="font-semibold text-sm">
{input.label}
</Label>
<RadioGroup <RadioGroup
value={inputValues[input.name] as string} value={inputValues[input.name] as string}
onValueChange={(value) => onValueChange={(value) =>
@ -385,9 +403,17 @@ export default function DynamicFilter<
} }
> >
{input.options.map((option) => ( {input.options.map((option) => (
<div key={`${input.name}-${option.value}`} className="flex items-center space-x-2"> <div
<RadioGroupItem value={option.value} id={`${input.name}-${option.value}`} /> key={`${input.name}-${option.value}`}
<Label htmlFor={`${input.name}-${option.value}`}>{option.label}</Label> className="flex items-center space-x-2"
>
<RadioGroupItem
value={option.value}
id={`${input.name}-${option.value}`}
/>
<Label htmlFor={`${input.name}-${option.value}`}>
{option.label}
</Label>
</div> </div>
))} ))}
</RadioGroup> </RadioGroup>
@ -424,10 +450,9 @@ export default function DynamicFilter<
<Badge <Badge
aria-disabled={disabled} aria-disabled={disabled}
variant={"outline"} variant={"outline"}
className={cn( className={cn("flex p-2 gap-2 items-center justify-between", {
"flex p-2 gap-2 items-center justify-between", "opacity-50 pointer-events-none": disabled,
{ "opacity-50 pointer-events-none": disabled }, })}
)}
key={filter.key} key={filter.key}
> >
<span className="text-md"> <span className="text-md">

View File

@ -10,7 +10,7 @@ export default function InputReadOnly({ label, value, labelClassName, className,
checkTrue?: boolean checkTrue?: boolean
}) { }) {
return ( return (
<div className={cn("relative flex items-center justify-between rounded-lg border border-input bg-background shadow-sm shadow-black/5 transition-shadow focus-within:border-ring focus-within:outline-none focus-within:ring-[3px] focus-within:ring-ring/20 has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-80 [&:has(input:is(:disabled))_*]:pointer-events-none", className)}> <div className={cn("relative flex items-center justify-between rounded-lg border border-input bg-background shadow-sm shadow-black/5 transition-shadow focus-within:border-ring focus-within:outline-none focus-within:ring-[3px] focus-within:ring-ring/20 has-disabled:cursor-not-allowed has-disabled:opacity-80 [&:has(input:is(:disabled))_*]:pointer-events-none", className)}>
<div> <div>
<label htmlFor="input-33" className={cn("block px-3 pt-2 text-xs font-medium", labelClassName)}> <label htmlFor="input-33" className={cn("block px-3 pt-2 text-xs font-medium", labelClassName)}>

View File

@ -29,14 +29,14 @@ export default function NumberInput({
<NumberField isDisabled={isDisabled} className={cn(className)} value={value} minValue={0} onChange={onChange}> <NumberField isDisabled={isDisabled} className={cn(className)} value={value} minValue={0} onChange={onChange}>
<div className="space-y-2"> <div className="space-y-2">
<Label className="text-sm font-medium text-foreground">{label}</Label> <Label className="text-sm font-medium text-foreground">{label}</Label>
<Group className="relative inline-flex h-9 w-full items-center overflow-hidden whitespace-nowrap rounded-lg border border-input text-sm shadow-sm shadow-black/5 transition-shadow data-[focus-within]:border-ring data-[disabled]:opacity-50 data-[focus-within]:outline-none data-[focus-within]:ring-[3px] data-[focus-within]:ring-ring/20"> <Group className="relative inline-flex h-9 w-full items-center overflow-hidden whitespace-nowrap rounded-lg border border-input text-sm shadow-sm shadow-black/5 transition-shadow data-[focus-within]:border-ring data-disabled:opacity-50 data-focus-within:outline-none data-focus-within:ring-[3px] data-[focus-within]:ring-ring/20">
<Button <Button
slot="decrement" slot="decrement"
className="-ms-px flex aspect-square h-[inherit] items-center justify-center rounded-s-lg border border-input bg-background text-sm text-muted-foreground/80 transition-shadow hover:bg-accent hover:text-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50" className="-ms-px flex aspect-square h-[inherit] items-center justify-center rounded-s-lg border border-input bg-background text-sm text-muted-foreground/80 transition-shadow hover:bg-accent hover:text-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50"
> >
<Minus size={16} strokeWidth={2} aria-hidden="true" /> <Minus size={16} strokeWidth={2} aria-hidden="true" />
</Button> </Button>
<Input className="w-full grow bg-background px-3 py-2 text-center tabular-nums text-foreground focus:outline-none" /> <Input className="w-full grow bg-background px-3 py-2 text-center text-base tabular-nums text-foreground focus:outline-none" />
<Button <Button
slot="increment" slot="increment"
className="-me-px flex aspect-square h-[inherit] items-center justify-center rounded-e-lg border border-input bg-background text-sm text-muted-foreground/80 transition-shadow hover:bg-accent hover:text-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50" className="-me-px flex aspect-square h-[inherit] items-center justify-center rounded-e-lg border border-input bg-background text-sm text-muted-foreground/80 transition-shadow hover:bg-accent hover:text-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50"

View File

@ -35,7 +35,7 @@ export default function PriceCalculator() {
return ( return (
<div className="border p-2 rounded-xl"> <div className="border p-2 rounded-xl">
<div className="flex justify-between items-center border-[1px] rounded-md border-dashed font-bold title-bg py-4 px-2 mb-4"> <div className="flex justify-between items-center border rounded-md border-dashed font-bold title-bg py-4 px-2 mb-4">
<h3 className="text-sarLinkOrange text-2xl"> <h3 className="text-sarLinkOrange text-2xl">
Price Calculator Price Calculator
</h3> </h3>
@ -69,7 +69,7 @@ export default function PriceCalculator() {
</div> </div>
<div className="mt-4"> <div className="mt-4">
<div className="title-bg relative rounded-lg border border-input shadow-sm shadow-black/5 transition-shadow focus-within:border-ring focus-within:outline-none focus-within:ring-[3px] focus-within:ring-ring/20 has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-50 [&:has(input:is(:disabled))_*]:pointer-events-none"> <div className="title-bg relative rounded-lg border border-input shadow-sm shadow-black/5 transition-shadow focus-within:border-ring focus-within:outline-none focus-within:ring-[3px] focus-within:ring-ring/20 has-disabled:cursor-not-allowed has-disabled:opacity-50 [&:has(input:is(:disabled))_*]:pointer-events-none">
<label <label
htmlFor="" htmlFor=""
className="block px-3 pt-2 text-md font-medium text-foreground" className="block px-3 pt-2 text-md font-medium text-foreground"

View File

@ -2,56 +2,65 @@
import * as React from "react" import * as React from "react"
import { Accordion as AccordionPrimitive } from "radix-ui" import { Accordion as AccordionPrimitive } from "radix-ui"
import { ChevronDown } from "lucide-react" import { ChevronDownIcon } from "lucide-react"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
const Accordion = AccordionPrimitive.Root function Accordion({
...props
}: React.ComponentProps<typeof AccordionPrimitive.Root>) {
return <AccordionPrimitive.Root data-slot="accordion" {...props} />
}
const AccordionItem = React.forwardRef< function AccordionItem({
React.ElementRef<typeof AccordionPrimitive.Item>, className,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item> ...props
>(({ className, ...props }, ref) => ( }: React.ComponentProps<typeof AccordionPrimitive.Item>) {
return (
<AccordionPrimitive.Item <AccordionPrimitive.Item
ref={ref} data-slot="accordion-item"
className={cn("border-b", className)} className={cn("border-b last:border-b-0", className)}
{...props} {...props}
/> />
)) )
AccordionItem.displayName = "AccordionItem" }
const AccordionTrigger = React.forwardRef< function AccordionTrigger({
React.ElementRef<typeof AccordionPrimitive.Trigger>, className,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger> children,
>(({ className, children, ...props }, ref) => ( ...props
}: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {
return (
<AccordionPrimitive.Header className="flex"> <AccordionPrimitive.Header className="flex">
<AccordionPrimitive.Trigger <AccordionPrimitive.Trigger
ref={ref} data-slot="accordion-trigger"
className={cn( className={cn(
"flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all hover:underline text-left [&[data-state=open]>svg]:rotate-180", "focus-visible:border-ring focus-visible:ring-ring/50 flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180",
className className
)} )}
{...props} {...props}
> >
{children} {children}
<ChevronDown className="h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200" /> <ChevronDownIcon className="text-muted-foreground pointer-events-none size-4 shrink-0 translate-y-0.5 transition-transform duration-200" />
</AccordionPrimitive.Trigger> </AccordionPrimitive.Trigger>
</AccordionPrimitive.Header> </AccordionPrimitive.Header>
)) )
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName }
const AccordionContent = React.forwardRef< function AccordionContent({
React.ElementRef<typeof AccordionPrimitive.Content>, className,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content> children,
>(({ className, children, ...props }, ref) => ( ...props
}: React.ComponentProps<typeof AccordionPrimitive.Content>) {
return (
<AccordionPrimitive.Content <AccordionPrimitive.Content
ref={ref} data-slot="accordion-content"
className="overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down" className="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm"
{...props} {...props}
> >
<div className={cn("pb-4 pt-0", className)}>{children}</div> <div className={cn("pt-0 pb-4", className)}>{children}</div>
</AccordionPrimitive.Content> </AccordionPrimitive.Content>
)) )
AccordionContent.displayName = AccordionPrimitive.Content.displayName }
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }

View File

@ -29,7 +29,7 @@ function Calendar({
<DayPicker <DayPicker
showOutsideDays={showOutsideDays} showOutsideDays={showOutsideDays}
className={cn( className={cn(
"bg-background group/calendar p-3 [--cell-size:2rem] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent", "bg-background group/calendar p-3 [--cell-size:2rem] in-data-[slot=card-content]:bg-transparent in-data-[slot=popover-content]:bg-transparent",
String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`, String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`, String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
className className
@ -53,20 +53,20 @@ function Calendar({
), ),
button_previous: cn( button_previous: cn(
buttonVariants({ variant: buttonVariant }), buttonVariants({ variant: buttonVariant }),
"h-[--cell-size] w-[--cell-size] select-none p-0 aria-disabled:opacity-50", "h-(--cell-size) w-(--cell-size) select-none p-0 aria-disabled:opacity-50",
defaultClassNames.button_previous defaultClassNames.button_previous
), ),
button_next: cn( button_next: cn(
buttonVariants({ variant: buttonVariant }), buttonVariants({ variant: buttonVariant }),
"h-[--cell-size] w-[--cell-size] select-none p-0 aria-disabled:opacity-50", "h-(--cell-size) w-(--cell-size) select-none p-0 aria-disabled:opacity-50",
defaultClassNames.button_next defaultClassNames.button_next
), ),
month_caption: cn( month_caption: cn(
"flex h-[--cell-size] w-full items-center justify-center px-[--cell-size]", "flex h-(--cell-size) w-full items-center justify-center px-(--cell-size)",
defaultClassNames.month_caption defaultClassNames.month_caption
), ),
dropdowns: cn( dropdowns: cn(
"flex h-[--cell-size] w-full items-center justify-center gap-1.5 text-sm font-medium", "flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-sm font-medium",
defaultClassNames.dropdowns defaultClassNames.dropdowns
), ),
dropdown_root: cn( dropdown_root: cn(
@ -89,7 +89,7 @@ function Calendar({
), ),
week: cn("mt-2 flex w-full", defaultClassNames.week), week: cn("mt-2 flex w-full", defaultClassNames.week),
week_number_header: cn( week_number_header: cn(
"w-[--cell-size] select-none", "w-(--cell-size) select-none",
defaultClassNames.week_number_header defaultClassNames.week_number_header
), ),
week_number: cn( week_number: cn(
@ -156,7 +156,7 @@ function Calendar({
WeekNumber: ({ children, ...props }) => { WeekNumber: ({ children, ...props }) => {
return ( return (
<td {...props}> <td {...props}>
<div className="flex size-[--cell-size] items-center justify-center text-center"> <div className="flex size-(--cell-size) items-center justify-center text-center">
{children} {children}
</div> </div>
</td> </td>
@ -198,7 +198,7 @@ function CalendarDayButton({
data-range-end={modifiers.range_end} data-range-end={modifiers.range_end}
data-range-middle={modifiers.range_middle} data-range-middle={modifiers.range_middle}
className={cn( className={cn(
"data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 flex aspect-square h-auto w-full min-w-[--cell-size] flex-col gap-1 font-normal leading-none data-[range-end=true]:rounded-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px] [&>span]:text-xs [&>span]:opacity-70", "data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 flex aspect-square h-auto w-full min-w-(--cell-size) flex-col gap-1 font-normal leading-none data-[range-end=true]:rounded-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px] [&>span]:text-xs [&>span]:opacity-70",
defaultClassNames.day, defaultClassNames.day,
className className
)} )}

View File

@ -1,33 +1,58 @@
"use client" "use client"
import * as React from "react" import * as React from "react"
import { type DialogProps } from "radix-ui"
import { Command as CommandPrimitive } from "cmdk" import { Command as CommandPrimitive } from "cmdk"
import { Search } from "lucide-react" import { SearchIcon } from "lucide-react"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import { Dialog, DialogContent } from "@/components/ui/dialog" import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog"
const Command = React.forwardRef< function Command({
React.ElementRef<typeof CommandPrimitive>, className,
React.ComponentPropsWithoutRef<typeof CommandPrimitive> ...props
>(({ className, ...props }, ref) => ( }: React.ComponentProps<typeof CommandPrimitive>) {
return (
<CommandPrimitive <CommandPrimitive
ref={ref} data-slot="command"
className={cn( className={cn(
"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground", "bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md",
className className
)} )}
{...props} {...props}
/> />
)) )
Command.displayName = CommandPrimitive.displayName }
const CommandDialog = ({ children, ...props }: DialogProps) => { function CommandDialog({
title = "Command Palette",
description = "Search for a command to run...",
children,
className,
showCloseButton = true,
...props
}: React.ComponentProps<typeof Dialog> & {
title?: string
description?: string
className?: string
showCloseButton?: boolean
}) {
return ( return (
<Dialog {...props}> <Dialog {...props}>
<DialogContent className="overflow-hidden p-0"> <DialogHeader className="sr-only">
<Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5"> <DialogTitle>{title}</DialogTitle>
<DialogDescription>{description}</DialogDescription>
</DialogHeader>
<DialogContent
className={cn("overflow-hidden p-0", className)}
showCloseButton={showCloseButton}
>
<Command className="[&_[cmdk-group-heading]]:text-muted-foreground **:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
{children} {children}
</Command> </Command>
</DialogContent> </DialogContent>
@ -35,110 +60,116 @@ const CommandDialog = ({ children, ...props }: DialogProps) => {
) )
} }
const CommandInput = React.forwardRef< function CommandInput({
React.ElementRef<typeof CommandPrimitive.Input>, className,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input> ...props
>(({ className, ...props }, ref) => ( }: React.ComponentProps<typeof CommandPrimitive.Input>) {
<div className="flex items-center border-b px-3" cmdk-input-wrapper=""> return (
<Search className="mr-2 h-4 w-4 shrink-0 opacity-50" /> <div
data-slot="command-input-wrapper"
className="flex h-9 items-center gap-2 border-b px-3"
>
<SearchIcon className="size-4 shrink-0 opacity-50" />
<CommandPrimitive.Input <CommandPrimitive.Input
ref={ref} data-slot="command-input"
className={cn( className={cn(
"flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50", "placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50",
className className
)} )}
{...props} {...props}
/> />
</div> </div>
)) )
}
CommandInput.displayName = CommandPrimitive.Input.displayName function CommandList({
const CommandList = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.List>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>
>(({ className, ...props }, ref) => (
<CommandPrimitive.List
ref={ref}
className={cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className)}
{...props}
/>
))
CommandList.displayName = CommandPrimitive.List.displayName
const CommandEmpty = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Empty>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>
>((props, ref) => (
<CommandPrimitive.Empty
ref={ref}
className="py-6 text-center text-sm"
{...props}
/>
))
CommandEmpty.displayName = CommandPrimitive.Empty.displayName
const CommandGroup = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Group>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>
>(({ className, ...props }, ref) => (
<CommandPrimitive.Group
ref={ref}
className={cn(
"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
className
)}
{...props}
/>
))
CommandGroup.displayName = CommandPrimitive.Group.displayName
const CommandSeparator = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>
>(({ className, ...props }, ref) => (
<CommandPrimitive.Separator
ref={ref}
className={cn("-mx-1 h-px bg-border", className)}
{...props}
/>
))
CommandSeparator.displayName = CommandPrimitive.Separator.displayName
const CommandItem = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>
>(({ className, ...props }, ref) => (
<CommandPrimitive.Item
ref={ref}
className={cn(
"relative flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
className
)}
{...props}
/>
))
CommandItem.displayName = CommandPrimitive.Item.displayName
const CommandShortcut = ({
className, className,
...props ...props
}: React.HTMLAttributes<HTMLSpanElement>) => { }: React.ComponentProps<typeof CommandPrimitive.List>) {
return ( return (
<span <CommandPrimitive.List
data-slot="command-list"
className={cn( className={cn(
"ml-auto text-xs tracking-widest text-muted-foreground", "max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto",
className
)}
{...props}
/>
)
}
function CommandEmpty({
...props
}: React.ComponentProps<typeof CommandPrimitive.Empty>) {
return (
<CommandPrimitive.Empty
data-slot="command-empty"
className="py-6 text-center text-sm"
{...props}
/>
)
}
function CommandGroup({
className,
...props
}: React.ComponentProps<typeof CommandPrimitive.Group>) {
return (
<CommandPrimitive.Group
data-slot="command-group"
className={cn(
"text-foreground [&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium",
className
)}
{...props}
/>
)
}
function CommandSeparator({
className,
...props
}: React.ComponentProps<typeof CommandPrimitive.Separator>) {
return (
<CommandPrimitive.Separator
data-slot="command-separator"
className={cn("bg-border -mx-1 h-px", className)}
{...props}
/>
)
}
function CommandItem({
className,
...props
}: React.ComponentProps<typeof CommandPrimitive.Item>) {
return (
<CommandPrimitive.Item
data-slot="command-item"
className={cn(
"data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
/>
)
}
function CommandShortcut({
className,
...props
}: React.ComponentProps<"span">) {
return (
<span
data-slot="command-shortcut"
className={cn(
"text-muted-foreground ml-auto text-xs tracking-widest",
className className
)} )}
{...props} {...props}
/> />
) )
} }
CommandShortcut.displayName = "CommandShortcut"
export { export {
Command, Command,

View File

@ -46,7 +46,7 @@ const ContextMenuSubContent = React.forwardRef<
<ContextMenuPrimitive.SubContent <ContextMenuPrimitive.SubContent
ref={ref} ref={ref}
className={cn( className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-context-menu-content-transform-origin]", "z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--radix-context-menu-content-transform-origin)",
className className
)} )}
{...props} {...props}
@ -62,7 +62,7 @@ const ContextMenuContent = React.forwardRef<
<ContextMenuPrimitive.Content <ContextMenuPrimitive.Content
ref={ref} ref={ref}
className={cn( className={cn(
"z-50 max-h-[--radix-context-menu-content-available-height] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-context-menu-content-transform-origin]", "z-50 max-h-(--radix-context-menu-content-available-height) min-w-32 overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--radix-context-menu-content-transform-origin)",
className className
)} )}
{...props} {...props}
@ -80,7 +80,7 @@ const ContextMenuItem = React.forwardRef<
<ContextMenuPrimitive.Item <ContextMenuPrimitive.Item
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50",
inset && "pl-8", inset && "pl-8",
className className
)} )}
@ -96,7 +96,7 @@ const ContextMenuCheckboxItem = React.forwardRef<
<ContextMenuPrimitive.CheckboxItem <ContextMenuPrimitive.CheckboxItem
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50",
className className
)} )}
checked={checked} checked={checked}
@ -120,7 +120,7 @@ const ContextMenuRadioItem = React.forwardRef<
<ContextMenuPrimitive.RadioItem <ContextMenuPrimitive.RadioItem
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50",
className className
)} )}
{...props} {...props}

View File

@ -2,121 +2,142 @@
import * as React from "react" import * as React from "react"
import { Dialog as DialogPrimitive } from "radix-ui" import { Dialog as DialogPrimitive } from "radix-ui"
import { X } from "lucide-react" import { XIcon } from "lucide-react"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
const Dialog = DialogPrimitive.Root function Dialog({
...props
}: React.ComponentProps<typeof DialogPrimitive.Root>) {
return <DialogPrimitive.Root data-slot="dialog" {...props} />
}
const DialogTrigger = DialogPrimitive.Trigger function DialogTrigger({
...props
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
}
const DialogPortal = DialogPrimitive.Portal function DialogPortal({
...props
}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
}
const DialogClose = DialogPrimitive.Close function DialogClose({
...props
}: React.ComponentProps<typeof DialogPrimitive.Close>) {
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
}
const DialogOverlay = React.forwardRef< function DialogOverlay({
React.ElementRef<typeof DialogPrimitive.Overlay>, className,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay> ...props
>(({ className, ...props }, ref) => ( }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
return (
<DialogPrimitive.Overlay <DialogPrimitive.Overlay
ref={ref} data-slot="dialog-overlay"
className={cn( className={cn(
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
className className
)} )}
{...props} {...props}
/> />
)) )
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName }
const DialogContent = React.forwardRef< function DialogContent({
React.ElementRef<typeof DialogPrimitive.Content>, className,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> children,
>(({ className, children, ...props }, ref) => ( showCloseButton = true,
<DialogPortal> ...props
}: React.ComponentProps<typeof DialogPrimitive.Content> & {
showCloseButton?: boolean
}) {
return (
<DialogPortal data-slot="dialog-portal">
<DialogOverlay /> <DialogOverlay />
<DialogPrimitive.Content <DialogPrimitive.Content
ref={ref} data-slot="dialog-content"
className={cn( className={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 [data-[state=closed]:slide-out-to-top-[48%]] data-[state=open]:slide-in-from-left-1/2 [data-[state=open]:slide-in-from-top-[48%]] sm:rounded-lg", "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
className className
)} )}
{...props} {...props}
> >
{children} {children}
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground"> {showCloseButton && (
<X className="h-4 w-4" /> <DialogPrimitive.Close
data-slot="dialog-close"
className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
>
<XIcon />
<span className="sr-only">Close</span> <span className="sr-only">Close</span>
</DialogPrimitive.Close> </DialogPrimitive.Close>
)}
</DialogPrimitive.Content> </DialogPrimitive.Content>
</DialogPortal> </DialogPortal>
)) )
DialogContent.displayName = DialogPrimitive.Content.displayName }
const DialogHeader = ({ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
className, return (
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div <div
data-slot="dialog-header"
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
{...props}
/>
)
}
function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="dialog-footer"
className={cn( className={cn(
"flex flex-col space-y-1.5 text-center sm:text-left", "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
className className
)} )}
{...props} {...props}
/> />
) )
DialogHeader.displayName = "DialogHeader" }
const DialogFooter = ({ function DialogTitle({
className, className,
...props ...props
}: React.HTMLAttributes<HTMLDivElement>) => ( }: React.ComponentProps<typeof DialogPrimitive.Title>) {
<div return (
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className
)}
{...props}
/>
)
DialogFooter.displayName = "DialogFooter"
const DialogTitle = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Title <DialogPrimitive.Title
ref={ref} data-slot="dialog-title"
className={cn( className={cn("text-lg leading-none font-semibold", className)}
"text-lg font-semibold leading-none tracking-tight",
className
)}
{...props} {...props}
/> />
)) )
DialogTitle.displayName = DialogPrimitive.Title.displayName }
const DialogDescription = React.forwardRef< function DialogDescription({
React.ElementRef<typeof DialogPrimitive.Description>, className,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description> ...props
>(({ className, ...props }, ref) => ( }: React.ComponentProps<typeof DialogPrimitive.Description>) {
return (
<DialogPrimitive.Description <DialogPrimitive.Description
ref={ref} data-slot="dialog-description"
className={cn("text-sm text-muted-foreground", className)} className={cn("text-muted-foreground text-sm", className)}
{...props} {...props}
/> />
)) )
DialogDescription.displayName = DialogPrimitive.Description.displayName }
export { export {
Dialog, Dialog,
DialogPortal,
DialogOverlay,
DialogTrigger,
DialogClose, DialogClose,
DialogContent, DialogContent,
DialogHeader,
DialogFooter,
DialogTitle,
DialogDescription, DialogDescription,
DialogFooter,
DialogHeader,
DialogOverlay,
DialogPortal,
DialogTitle,
DialogTrigger,
} }

View File

@ -47,7 +47,7 @@ const DropdownMenuSubContent = React.forwardRef<
<DropdownMenuPrimitive.SubContent <DropdownMenuPrimitive.SubContent
ref={ref} ref={ref}
className={cn( className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-dropdown-menu-content-transform-origin]", "z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--radix-dropdown-menu-content-transform-origin)",
className className
)} )}
{...props} {...props}
@ -65,8 +65,8 @@ const DropdownMenuContent = React.forwardRef<
ref={ref} ref={ref}
sideOffset={sideOffset} sideOffset={sideOffset}
className={cn( className={cn(
"z-50 max-h-[var(--radix-dropdown-menu-content-available-height)] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md", "z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-32 overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-dropdown-menu-content-transform-origin]", "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--radix-dropdown-menu-content-transform-origin)",
className className
)} )}
{...props} {...props}
@ -84,7 +84,7 @@ const DropdownMenuItem = React.forwardRef<
<DropdownMenuPrimitive.Item <DropdownMenuPrimitive.Item
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0", "relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0",
inset && "pl-8", inset && "pl-8",
className className
)} )}
@ -100,7 +100,7 @@ const DropdownMenuCheckboxItem = React.forwardRef<
<DropdownMenuPrimitive.CheckboxItem <DropdownMenuPrimitive.CheckboxItem
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50",
className className
)} )}
checked={checked} checked={checked}
@ -124,7 +124,7 @@ const DropdownMenuRadioItem = React.forwardRef<
<DropdownMenuPrimitive.RadioItem <DropdownMenuPrimitive.RadioItem
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50",
className className
)} )}
{...props} {...props}

View File

@ -18,7 +18,7 @@ const HoverCardContent = React.forwardRef<
align={align} align={align}
sideOffset={sideOffset} sideOffset={sideOffset}
className={cn( className={cn(
"z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-hover-card-content-transform-origin]", "z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--radix-hover-card-content-transform-origin)",
className className
)} )}
{...props} {...props}

View File

@ -13,7 +13,7 @@ const InputOTP = React.forwardRef<
<OTPInput <OTPInput
ref={ref} ref={ref}
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)}

View File

@ -94,7 +94,7 @@ const MenubarSubContent = React.forwardRef<
<MenubarPrimitive.SubContent <MenubarPrimitive.SubContent
ref={ref} ref={ref}
className={cn( className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-menubar-content-transform-origin]", "z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--radix-menubar-content-transform-origin)",
className className
)} )}
{...props} {...props}
@ -117,7 +117,7 @@ const MenubarContent = React.forwardRef<
alignOffset={alignOffset} alignOffset={alignOffset}
sideOffset={sideOffset} sideOffset={sideOffset}
className={cn( className={cn(
"z-50 min-w-[12rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-menubar-content-transform-origin]", "z-50 min-w-48 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--radix-menubar-content-transform-origin)",
className className
)} )}
{...props} {...props}
@ -136,7 +136,7 @@ const MenubarItem = React.forwardRef<
<MenubarPrimitive.Item <MenubarPrimitive.Item
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50",
inset && "pl-8", inset && "pl-8",
className className
)} )}
@ -152,7 +152,7 @@ const MenubarCheckboxItem = React.forwardRef<
<MenubarPrimitive.CheckboxItem <MenubarPrimitive.CheckboxItem
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50",
className className
)} )}
checked={checked} checked={checked}
@ -175,7 +175,7 @@ const MenubarRadioItem = React.forwardRef<
<MenubarPrimitive.RadioItem <MenubarPrimitive.RadioItem
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50",
className className
)} )}
{...props} {...props}

View File

@ -55,7 +55,7 @@ const NavigationMenuTrigger = React.forwardRef<
> >
{children}{" "} {children}{" "}
<ChevronDown <ChevronDown
className="relative top-[1px] ml-1 h-3 w-3 transition duration-300 group-data-[state=open]:rotate-180" className="relative top-px ml-1 h-3 w-3 transition duration-300 group-data-[state=open]:rotate-180"
aria-hidden="true" aria-hidden="true"
/> />
</NavigationMenuPrimitive.Trigger> </NavigationMenuPrimitive.Trigger>
@ -86,7 +86,7 @@ const NavigationMenuViewport = React.forwardRef<
<div className={cn("absolute left-0 top-full flex justify-center")}> <div className={cn("absolute left-0 top-full flex justify-center")}>
<NavigationMenuPrimitive.Viewport <NavigationMenuPrimitive.Viewport
className={cn( className={cn(
"origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-[var(--radix-navigation-menu-viewport-width)]", "origin-top-center relative mt-1.5 h-(--radix-navigation-menu-viewport-height) w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-(--radix-navigation-menu-viewport-width)",
className className
)} )}
ref={ref} ref={ref}
@ -104,7 +104,7 @@ const NavigationMenuIndicator = React.forwardRef<
<NavigationMenuPrimitive.Indicator <NavigationMenuPrimitive.Indicator
ref={ref} ref={ref}
className={cn( className={cn(
"top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in", "top-full z-1 flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in",
className className
)} )}
{...props} {...props}

View File

@ -21,7 +21,7 @@ const PopoverContent = React.forwardRef<
align={align} align={align}
sideOffset={sideOffset} sideOffset={sideOffset}
className={cn( className={cn(
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-popover-content-transform-origin]", "z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--radix-popover-content-transform-origin)",
className className
)} )}
{...props} {...props}

View File

@ -33,9 +33,9 @@ const ScrollBar = React.forwardRef<
className={cn( className={cn(
"flex touch-none select-none transition-colors", "flex touch-none select-none transition-colors",
orientation === "vertical" && orientation === "vertical" &&
"h-full w-2.5 border-l border-l-transparent p-[1px]", "h-full w-2.5 border-l border-l-transparent p-px",
orientation === "horizontal" && orientation === "horizontal" &&
"h-2.5 flex-col border-t border-t-transparent p-[1px]", "h-2.5 flex-col border-t border-t-transparent p-px",
className className
)} )}
{...props} {...props}

View File

@ -75,7 +75,7 @@ const SelectContent = React.forwardRef<
<SelectPrimitive.Content <SelectPrimitive.Content
ref={ref} ref={ref}
className={cn( className={cn(
"relative z-50 max-h-[--radix-select-content-available-height] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-select-content-transform-origin]", "relative z-50 max-h-(--radix-select-content-available-height) min-w-32 overflow-y-auto overflow-x-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--radix-select-content-transform-origin)",
position === "popper" && position === "popper" &&
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
className className
@ -88,7 +88,7 @@ const SelectContent = React.forwardRef<
className={cn( className={cn(
"p-1", "p-1",
position === "popper" && position === "popper" &&
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]" "h-(--radix-select-trigger-height) w-full min-w-(--radix-select-trigger-width)"
)} )}
> >
{children} {children}
@ -118,7 +118,7 @@ const SelectItem = React.forwardRef<
<SelectPrimitive.Item <SelectPrimitive.Item
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50",
className className
)} )}
{...props} {...props}

View File

@ -19,7 +19,7 @@ const Separator = React.forwardRef<
orientation={orientation} orientation={orientation}
className={cn( className={cn(
"shrink-0 bg-border", "shrink-0 bg-border",
orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]", orientation === "horizontal" ? "h-px w-full" : "h-full w-px",
className className
)} )}
{...props} {...props}

View File

@ -187,7 +187,7 @@ const Sidebar = React.forwardRef<
return ( return (
<div <div
className={cn( className={cn(
"flex h-full w-[--sidebar-width] flex-col bg-sidebar text-sidebar-foreground", "flex h-full w-(--sidebar-width) flex-col bg-sidebar text-sidebar-foreground",
className className
)} )}
ref={ref} ref={ref}
@ -204,7 +204,7 @@ const Sidebar = React.forwardRef<
<SheetContent <SheetContent
data-sidebar="sidebar" data-sidebar="sidebar"
data-mobile="true" data-mobile="true"
className="w-[--sidebar-width] bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden" className="w-(--sidebar-width) bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden"
style={ style={
{ {
"--sidebar-width": SIDEBAR_WIDTH_MOBILE, "--sidebar-width": SIDEBAR_WIDTH_MOBILE,
@ -234,24 +234,24 @@ const Sidebar = React.forwardRef<
{/* This is what handles the sidebar gap on desktop */} {/* This is what handles the sidebar gap on desktop */}
<div <div
className={cn( className={cn(
"relative w-[--sidebar-width] bg-transparent transition-[width] duration-200 ease-linear", "relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear",
"group-data-[collapsible=offcanvas]:w-0", "group-data-[collapsible=offcanvas]:w-0",
"group-data-[side=right]:rotate-180", "group-data-[side=right]:rotate-180",
variant === "floating" || variant === "inset" variant === "floating" || variant === "inset"
? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]" ? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]"
: "group-data-[collapsible=icon]:w-[--sidebar-width-icon]" : "group-data-[collapsible=icon]:w-(--sidebar-width-icon)"
)} )}
/> />
<div <div
className={cn( className={cn(
"fixed inset-y-0 z-10 hidden h-svh w-[--sidebar-width] transition-[left,right,width] duration-200 ease-linear md:flex", "fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex",
side === "left" side === "left"
? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]" ? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]"
: "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]", : "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]",
// Adjust the padding for floating and inset variants. // Adjust the padding for floating and inset variants.
variant === "floating" || variant === "inset" variant === "floating" || variant === "inset"
? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)]" ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]"
: "group-data-[collapsible=icon]:w-[--sidebar-width-icon] group-data-[side=left]:border-r group-data-[side=right]:border-l", : "group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l",
className className
)} )}
{...props} {...props}
@ -311,7 +311,7 @@ const SidebarRail = React.forwardRef<
title="Toggle Sidebar" title="Toggle Sidebar"
className={cn( className={cn(
"absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] hover:after:bg-sidebar-border group-data-[side=left]:-right-4 group-data-[side=right]:left-0 sm:flex", "absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] hover:after:bg-sidebar-border group-data-[side=left]:-right-4 group-data-[side=right]:left-0 sm:flex",
"[[data-side=left]_&]:cursor-w-resize [[data-side=right]_&]:cursor-e-resize", "in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize",
"[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize", "[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize",
"group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full group-data-[collapsible=offcanvas]:hover:bg-sidebar", "group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full group-data-[collapsible=offcanvas]:hover:bg-sidebar",
"[[data-side=left][data-collapsible=offcanvas]_&]:-right-2", "[[data-side=left][data-collapsible=offcanvas]_&]:-right-2",
@ -522,7 +522,7 @@ const SidebarMenuItem = React.forwardRef<
SidebarMenuItem.displayName = "SidebarMenuItem" SidebarMenuItem.displayName = "SidebarMenuItem"
const sidebarMenuButtonVariants = cva( const sidebarMenuButtonVariants = cva(
"peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] 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 group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0", "peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] 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 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
{ {
variants: { variants: {
variant: { variant: {
@ -533,7 +533,7 @@ const sidebarMenuButtonVariants = cva(
size: { size: {
default: "h-8 text-sm", default: "h-8 text-sm",
sm: "h-7 text-xs", sm: "h-7 text-xs",
lg: "h-12 text-sm group-data-[collapsible=icon]:!p-0", lg: "h-12 text-sm group-data-[collapsible=icon]:p-0!",
}, },
}, },
defaultVariants: { defaultVariants: {
@ -679,7 +679,7 @@ const SidebarMenuSkeleton = React.forwardRef<
/> />
)} )}
<Skeleton <Skeleton
className="h-4 max-w-[--skeleton-width] flex-1" className="h-4 max-w-(--skeleton-width) flex-1"
data-sidebar="menu-skeleton-text" data-sidebar="menu-skeleton-text"
style={ style={
{ {

View File

@ -29,7 +29,7 @@ export function TextShimmer({
return ( return (
<MotionComponent <MotionComponent
className={cn( className={cn(
'relative inline-block bg-[length:250%_100%,auto] bg-clip-text', 'relative inline-block bg-size-[250%_100%,auto] bg-clip-text',
'text-transparent [--base-color:#a1a1aa] [--base-gradient-color:#000]', 'text-transparent [--base-color:#a1a1aa] [--base-gradient-color:#000]',
'[--bg:linear-gradient(90deg,#0000_calc(50%-var(--spread)),var(--base-gradient-color),#0000_calc(50%+var(--spread)))] [background-repeat:no-repeat,padding-box]', '[--bg:linear-gradient(90deg,#0000_calc(50%-var(--spread)),var(--base-gradient-color),#0000_calc(50%+var(--spread)))] [background-repeat:no-repeat,padding-box]',
'dark:[--base-color:#71717a] dark:[--base-gradient-color:#ffffff] dark:[--bg:linear-gradient(90deg,#0000_calc(50%-var(--spread)),var(--base-gradient-color),#0000_calc(50%+var(--spread)))]', 'dark:[--base-color:#71717a] dark:[--base-gradient-color:#ffffff] dark:[--bg:linear-gradient(90deg,#0000_calc(50%-var(--spread)),var(--base-gradient-color),#0000_calc(50%+var(--spread)))]',

View File

@ -20,7 +20,7 @@ const TooltipContent = React.forwardRef<
ref={ref} ref={ref}
sideOffset={sideOffset} sideOffset={sideOffset}
className={cn( className={cn(
"z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-tooltip-content-transform-origin]", "z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--radix-tooltip-content-transform-origin)",
className className
)} )}
{...props} {...props}

1749
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -12,6 +12,8 @@
"@faker-js/faker": "^9.3.0", "@faker-js/faker": "^9.3.0",
"@hookform/resolvers": "^5.1.1", "@hookform/resolvers": "^5.1.1",
"@pyncz/tailwind-mask-image": "^2.0.0", "@pyncz/tailwind-mask-image": "^2.0.0",
"@radix-ui/react-dialog": "^1.1.14",
"@tailwindcss/postcss": "^4.1.11",
"@tanstack/react-query": "^5.61.4", "@tanstack/react-query": "^5.61.4",
"axios": "^1.8.4", "axios": "^1.8.4",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
@ -49,12 +51,15 @@
"@types/node": "^22.10.2", "@types/node": "^22.10.2",
"@types/react": "^19.1.0", "@types/react": "^19.1.0",
"@types/react-dom": "^19.1.2", "@types/react-dom": "^19.1.2",
"eslint": "^9.17.0", "@typescript-eslint/eslint-plugin": "^8.35.0",
"@typescript-eslint/parser": "^8.35.0",
"eslint": "^9.29.0",
"eslint-config-next": "15.1.2", "eslint-config-next": "15.1.2",
"postcss": "^8.4.49", "postcss": "^8.5.6",
"tailwindcss": "^3.4.17", "tailwindcss": "^4.1.11",
"tailwindcss-motion": "^1.1.0", "tailwindcss-motion": "^1.1.0",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"tw-animate-css": "^1.3.4",
"typescript": "^5.8.3" "typescript": "^5.8.3"
} }
} }

View File

@ -1,7 +1,7 @@
/** @type {import('postcss-load-config').Config} */ /** @type {import('postcss-load-config').Config} */
const config = { const config = {
plugins: { plugins: {
tailwindcss: {}, "@tailwindcss/postcss": {},
}, },
}; };

View File

@ -1,109 +0,0 @@
import type { Config } from "tailwindcss";
import tailwindcssAnimate from "tailwindcss-animate";
import tailwindcssMotion from "tailwindcss-motion";
export default {
darkMode: ["class"],
content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
colors: {
sarLinkOrange: '#f49b5b',
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
card: {
DEFAULT: 'hsl(var(--card))',
foreground: 'hsl(var(--card-foreground))'
},
popover: {
DEFAULT: 'hsl(var(--popover))',
foreground: 'hsl(var(--popover-foreground))'
},
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))'
},
secondary: {
DEFAULT: 'hsl(var(--secondary))',
foreground: 'hsl(var(--secondary-foreground))'
},
muted: {
DEFAULT: 'hsl(var(--muted))',
foreground: 'hsl(var(--muted-foreground))'
},
accent: {
DEFAULT: 'hsl(var(--accent))',
foreground: 'hsl(var(--accent-foreground))'
},
destructive: {
DEFAULT: 'hsl(var(--destructive))',
foreground: 'hsl(var(--destructive-foreground))'
},
border: 'hsl(var(--border))',
input: 'hsl(var(--input))',
ring: 'hsl(var(--ring))',
chart: {
'1': 'hsl(var(--chart-1))',
'2': 'hsl(var(--chart-2))',
'3': 'hsl(var(--chart-3))',
'4': 'hsl(var(--chart-4))',
'5': 'hsl(var(--chart-5))'
},
sidebar: {
DEFAULT: 'hsl(var(--sidebar-background))',
foreground: 'hsl(var(--sidebar-foreground))',
primary: 'hsl(var(--sidebar-primary))',
'primary-foreground': 'hsl(var(--sidebar-primary-foreground))',
accent: 'hsl(var(--sidebar-accent))',
'accent-foreground': 'hsl(var(--sidebar-accent-foreground))',
border: 'hsl(var(--sidebar-border))',
ring: 'hsl(var(--sidebar-ring))'
}
},
borderRadius: {
lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)',
sm: 'calc(var(--radius) - 4px)'
},
fontFamily: {
sans: [
'var(--font-barlow)'
],
mono: [
'var(--font-mono)'
]
},
keyframes: {
'accordion-down': {
from: {
height: '0'
},
to: {
height: 'var(--radix-accordion-content-height)'
}
},
'accordion-up': {
from: {
height: 'var(--radix-accordion-content-height)'
},
to: {
height: '0'
}
}
},
animation: {
'accordion-down': 'accordion-down 0.2s ease-out',
'accordion-up': 'accordion-up 0.2s ease-out'
}
}
},
plugins: [
tailwindcssAnimate,
tailwindcssMotion,
require("@pyncz/tailwind-mask-image"),
require("tailwindcss-animate")
],
} satisfies Config;

View File

@ -1,7 +1,11 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ES2017", "target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"], "lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,
"strict": true, "strict": true,
@ -19,9 +23,18 @@
} }
], ],
"paths": { "paths": {
"@/*": ["./*"] "@/*": [
"./*"
]
} }
}, },
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "include": [
"exclude": ["node_modules"] "next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
],
"exclude": [
"node_modules"
]
} }