This commit is contained in:
Arjun Patel
2026-06-01 14:17:58 -07:00
parent 580703fdf6
commit 52ff92083a
165 changed files with 3736 additions and 2907 deletions
@@ -1,12 +1,12 @@
import type { PropsWithChildren } from "react";
import { ErrorBoundary } from "react-error-boundary";
import { useLocation } from "react-router-dom";
import { useQueryErrorResetBoundary } from "@tanstack/react-query";
import { reportError } from "@/lib/errors";
import type { PropsWithChildren } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
import { useLocation } from 'react-router-dom';
import { useQueryErrorResetBoundary } from '@tanstack/react-query';
import { reportError } from '@/lib/errors';
import {
RouteErrorFallback,
TopLevelErrorFallback,
} from "@/components/error-fallback";
} from '@/components/error-fallback';
/** Catches render crashes OUTSIDE the router so bootstrap failures still recover. */
export function TopLevelErrorBoundary({ children }: PropsWithChildren) {
@@ -15,7 +15,7 @@ export function TopLevelErrorBoundary({ children }: PropsWithChildren) {
FallbackComponent={TopLevelErrorFallback}
onError={(error, info) =>
reportError(error, {
boundary: "top",
boundary: 'top',
componentStack: info.componentStack,
})
}
@@ -37,7 +37,7 @@ export function RouteErrorBoundary({ children }: PropsWithChildren) {
FallbackComponent={RouteErrorFallback}
onError={(error, info) =>
reportError(error, {
boundary: "route",
boundary: 'route',
pathname: location.pathname,
componentStack: info.componentStack,
})
@@ -1,4 +1,4 @@
import { useEffect, useRef } from "react";
import { useEffect, useRef } from 'react';
interface AudioLevelBarsProps {
sourceNode: AudioNode;
@@ -83,7 +83,8 @@ export function AudioLevelBars({ sourceNode }: AudioLevelBarsProps) {
smoothedLevel <= threshold
? 0
: Math.min(1, (smoothedLevel - threshold) / (1 - threshold));
const height = MIN_HEIGHT_PX + barLevel * (MAX_HEIGHT_PX - MIN_HEIGHT_PX);
const height =
MIN_HEIGHT_PX + barLevel * (MAX_HEIGHT_PX - MIN_HEIGHT_PX);
el.style.height = `${height}px`;
}
@@ -1,4 +1,4 @@
import { useEffect, useRef, useState } from "react";
import { useEffect, useRef, useState } from 'react';
interface AudioSource {
sourceNode: AudioNode;
@@ -17,7 +17,10 @@ export function useAudioSource(
): AudioSource | null {
const [audioSource, setAudioSource] = useState<AudioSource | null>(null);
const elementSourceCache = useRef<
WeakMap<HTMLAudioElement, { sourceNode: MediaElementAudioSourceNode; ctx: AudioContext }>
WeakMap<
HTMLAudioElement,
{ sourceNode: MediaElementAudioSourceNode; ctx: AudioContext }
>
>(new WeakMap());
useEffect(() => {
@@ -1,6 +1,6 @@
import { useRef } from "react";
import { X } from "lucide-react";
import type { AutoplayPayload } from "@/lib/autoplay-ipc";
import { useRef } from 'react';
import { X } from 'lucide-react';
import type { AutoplayPayload } from '@/lib/autoplay-ipc';
interface AutoplayCardContentProps {
payload: AutoplayPayload;
@@ -17,7 +17,7 @@ export function AutoplayCardContent({
}: AutoplayCardContentProps) {
const mediaRef = useRef<HTMLVideoElement | HTMLAudioElement | null>(null);
const isVideo = payload.mimeType.startsWith("video/");
const isVideo = payload.mimeType.startsWith('video/');
const handleClick = () => {
mediaRef.current?.pause();
@@ -56,7 +56,9 @@ export function AutoplayCardContent({
<div className="flex size-6 shrink-0 items-center justify-center rounded-full bg-white/20 text-[10px] font-medium text-white">
{payload.senderInitials}
</div>
<p className="truncate text-xs text-white/80">{payload.senderName}</p>
<p className="truncate text-xs text-white/80">
{payload.senderName}
</p>
</div>
</>
) : (
@@ -72,7 +74,9 @@ export function AutoplayCardContent({
{payload.senderInitials}
</div>
<div className="min-w-0 flex-1">
<p className="truncate text-sm font-medium text-card-foreground">{payload.senderName}</p>
<p className="truncate text-sm font-medium text-card-foreground">
{payload.senderName}
</p>
<p className="text-xs text-muted-foreground">Playing audio...</p>
</div>
</div>
@@ -1,6 +1,6 @@
import type { Human } from "@/api/types";
import { resolveHumanDisplay } from "@/lib/humans";
import type { ComposingUser } from "@/features/particles/stream-presence-context";
import type { Human } from '@/api/types';
import { resolveHumanDisplay } from '@/lib/humans';
import type { ComposingUser } from '@/features/particles/stream-presence-context';
interface ComposingIndicatorProps {
users: ComposingUser[];
@@ -20,11 +20,11 @@ export function ComposingIndicator({
return (
<div
className="z-100 absolute left-2 top-1/2 z-20 flex -translate-y-1/2 flex-col gap-1.5 animate-in fade-in duration-200"
style={{ writingMode: "vertical-rl" }}
style={{ writingMode: 'vertical-rl' }}
>
{users.map((u) => {
const { displayName } = resolveHumanDisplay(u.humanId, networkHumans);
const modeLabel = u.mode === "typing" ? "typing" : "recording";
const modeLabel = u.mode === 'typing' ? 'typing' : 'recording';
return (
<div
@@ -1,6 +1,6 @@
import { useEffect } from "react";
import { createPortal } from "react-dom";
import { Button } from "@/components/ui/button";
import { useEffect } from 'react';
import { createPortal } from 'react-dom';
import { Button } from '@/components/ui/button';
interface ConfirmDestructiveOverlayProps {
title: string;
@@ -16,21 +16,22 @@ export function ConfirmDestructiveOverlay({
title,
description,
confirmLabel,
pendingLabel = "Working…",
pendingLabel = 'Working…',
isPending,
onConfirm,
onClose,
}: ConfirmDestructiveOverlayProps) {
useEffect(() => {
const handler = (e: KeyboardEvent) => {
if (e.key === "Escape") {
if (e.key === 'Escape') {
e.preventDefault();
e.stopPropagation();
onClose();
}
};
window.addEventListener("keydown", handler, { capture: true });
return () => window.removeEventListener("keydown", handler, { capture: true });
window.addEventListener('keydown', handler, { capture: true });
return () =>
window.removeEventListener('keydown', handler, { capture: true });
}, [onClose]);
return createPortal(
@@ -45,7 +46,7 @@ export function ConfirmDestructiveOverlay({
<span className="text-xs text-white/30">
<kbd className="rounded bg-white/10 px-1.5 py-0.5 font-mono text-xs">
Esc
</kbd>{" "}
</kbd>{' '}
to close
</span>
</div>
@@ -53,7 +54,12 @@ export function ConfirmDestructiveOverlay({
<div className="text-sm text-white/60">{description}</div>
<div className="mt-5 flex items-center justify-end gap-2">
<Button variant="ghost" size="sm" onClick={onClose} disabled={isPending}>
<Button
variant="ghost"
size="sm"
onClick={onClose}
disabled={isPending}
>
Cancel
</Button>
<Button
+7 -7
View File
@@ -1,7 +1,7 @@
import { useEffect, useState } from "react";
import { Check, Copy } from "lucide-react";
import { toast } from "sonner";
import { cn } from "@/lib/utils";
import { useEffect, useState } from 'react';
import { Check, Copy } from 'lucide-react';
import { toast } from 'sonner';
import { cn } from '@/lib/utils';
interface CopyableEmailProps {
email: string;
@@ -22,9 +22,9 @@ export function CopyableEmail({ email, className }: CopyableEmailProps) {
try {
await navigator.clipboard.writeText(email);
setCopied(true);
toast.success("Email copied");
toast.success('Email copied');
} catch {
toast.error("Failed to copy email");
toast.error('Failed to copy email');
}
};
@@ -34,7 +34,7 @@ export function CopyableEmail({ email, className }: CopyableEmailProps) {
onClick={handleCopy}
aria-label={`Copy ${email}`}
className={cn(
"hover:bg-accent inline-flex items-center gap-1.5 rounded-md px-2 py-1 font-mono text-sm transition-colors",
'hover:bg-accent inline-flex items-center gap-1.5 rounded-md px-2 py-1 font-mono text-sm transition-colors',
className,
)}
>
+12 -20
View File
@@ -1,7 +1,7 @@
import type { FallbackProps } from "react-error-boundary";
import { useNavigate } from "react-router-dom";
import { AlertTriangle } from "lucide-react";
import { Button } from "@/components/ui/button";
import type { FallbackProps } from 'react-error-boundary';
import { useNavigate } from 'react-router-dom';
import { AlertTriangle } from 'lucide-react';
import { Button } from '@/components/ui/button';
import {
Card,
CardContent,
@@ -9,9 +9,9 @@ import {
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { toUserMessage } from "@/lib/errors";
import { appEnv } from "@/config/env";
} from '@/components/ui/card';
import { toUserMessage } from '@/lib/errors';
import { appEnv } from '@/config/env';
function ErrorCard({
error,
@@ -32,7 +32,7 @@ function ErrorCard({
</div>
<CardDescription>{toUserMessage(error)}</CardDescription>
</CardHeader>
{appEnv === "dev" && error instanceof Error ? (
{appEnv === 'dev' && error instanceof Error ? (
<CardContent>
<details className="text-muted-foreground text-xs">
<summary className="cursor-pointer select-none">
@@ -63,15 +63,11 @@ export function TopLevelErrorFallback({
resetErrorBoundary,
}: FallbackProps) {
const goHome = () => {
window.location.hash = "#/";
window.location.hash = '#/';
resetErrorBoundary();
};
return (
<ErrorCard
error={error}
onGoHome={goHome}
onRetry={resetErrorBoundary}
/>
<ErrorCard error={error} onGoHome={goHome} onRetry={resetErrorBoundary} />
);
}
@@ -81,14 +77,10 @@ export function RouteErrorFallback({
}: FallbackProps) {
const navigate = useNavigate();
const goHome = () => {
navigate("/");
navigate('/');
resetErrorBoundary();
};
return (
<ErrorCard
error={error}
onGoHome={goHome}
onRetry={resetErrorBoundary}
/>
<ErrorCard error={error} onGoHome={goHome} onRetry={resetErrorBoundary} />
);
}
@@ -1,7 +1,7 @@
import { useCallback } from "react";
import { platform } from "@/lib/platform";
import { useAutoplayPayloadStore } from "@/stores/autoplay-payload-store";
import { AutoplayCardContent } from "@/components/autoplay-card-content";
import { useCallback } from 'react';
import { platform } from '@/lib/platform';
import { useAutoplayPayloadStore } from '@/stores/autoplay-payload-store';
import { AutoplayCardContent } from '@/components/autoplay-card-content';
/**
* Bottom-right floating autoplay card used on the web client. The desktop
@@ -21,10 +21,10 @@ export function InAppAutoplayCard() {
setPendingNav({ networkId: payload.networkId, streamId: payload.streamId });
}, [payload, setPayload, setPendingNav]);
if (platform.kind !== "web") return null;
if (platform.kind !== 'web') return null;
if (!payload) return null;
const isVideo = payload.mimeType.startsWith("video/");
const isVideo = payload.mimeType.startsWith('video/');
return (
<div
@@ -1,6 +1,6 @@
import { useSuspendPlayback } from "@/hooks/use-suspend-playback";
import { useEffect } from "react";
import { createPortal } from "react-dom";
import { useSuspendPlayback } from '@/hooks/use-suspend-playback';
import { useEffect } from 'react';
import { createPortal } from 'react-dom';
export interface KeybindingEntry {
keys: string[];
@@ -23,21 +23,22 @@ export function KeybindingsOverlay({
open,
onClose,
groups,
title = "Keyboard Shortcuts",
title = 'Keyboard Shortcuts',
}: KeybindingsOverlayProps) {
useSuspendPlayback(open, "keybindings");
useSuspendPlayback(open, 'keybindings');
useEffect(() => {
if (!open) return;
const handler = (e: KeyboardEvent) => {
if (e.key === "Escape" || e.key === "?") {
if (e.key === 'Escape' || e.key === '?') {
e.preventDefault();
e.stopPropagation();
onClose();
}
};
window.addEventListener("keydown", handler, { capture: true });
return () => window.removeEventListener("keydown", handler, { capture: true });
window.addEventListener('keydown', handler, { capture: true });
return () =>
window.removeEventListener('keydown', handler, { capture: true });
}, [open, onClose]);
if (!open) return null;
@@ -56,11 +57,11 @@ export function KeybindingsOverlay({
<span className="text-xs text-white/30">
<kbd className="rounded bg-white/10 px-1.5 py-0.5 font-mono text-xs">
Esc
</kbd>{" "}
or{" "}
</kbd>{' '}
or{' '}
<kbd className="rounded bg-white/10 px-1.5 py-0.5 font-mono text-xs">
?
</kbd>{" "}
</kbd>{' '}
to close
</span>
</div>
@@ -1,8 +1,8 @@
import { Copy, ExternalLink, Globe } from "lucide-react";
import type { LinkMetadata } from "@/lib/link-metadata";
import { Skeleton } from "@/components/ui/skeleton";
import { Button } from "@/components/ui/button";
import { platform } from "@/lib/platform";
import { Copy, ExternalLink, Globe } from 'lucide-react';
import type { LinkMetadata } from '@/lib/link-metadata';
import { Skeleton } from '@/components/ui/skeleton';
import { Button } from '@/components/ui/button';
import { platform } from '@/lib/platform';
interface LinkPreviewCardProps {
metadata: LinkMetadata;
@@ -34,7 +34,7 @@ export function LinkPreviewCard({ metadata, compact }: LinkPreviewCardProps) {
alt=""
className="h-32 w-full object-cover"
onError={(e) => {
(e.target as HTMLImageElement).style.display = "none";
(e.target as HTMLImageElement).style.display = 'none';
}}
/>
)}
@@ -47,7 +47,7 @@ export function LinkPreviewCard({ metadata, compact }: LinkPreviewCardProps) {
className="size-4 rounded-sm"
onError={(e) => {
(e.target as HTMLImageElement).replaceWith(
document.createElement("span"),
document.createElement('span'),
);
}}
/>
@@ -1,5 +1,5 @@
import { useEffect, useState } from "react";
import { formatDistanceToNow } from "@/lib/time-utils";
import { useEffect, useState } from 'react';
import { formatDistanceToNow } from '@/lib/time-utils';
const TICK_MS = 30_000;
@@ -1,4 +1,4 @@
import { useState, useEffect } from "react";
import { useState, useEffect } from 'react';
interface ScreenSourcePickerProps {
title?: string;
@@ -9,8 +9,8 @@ interface ScreenSourcePickerProps {
}
export function ScreenSourcePicker({
title = "Select a screen",
confirmLabel = "Select",
title = 'Select a screen',
confirmLabel = 'Select',
getSources,
onSelect,
onCancel,
@@ -30,8 +30,8 @@ export function ScreenSourcePicker({
});
}, [getSources]);
const screens = sources.filter((s) => s.id.startsWith("screen:"));
const windows = sources.filter((s) => s.id.startsWith("window:"));
const screens = sources.filter((s) => s.id.startsWith('screen:'));
const windows = sources.filter((s) => s.id.startsWith('window:'));
return (
<div className="absolute inset-0 z-50 flex items-center justify-center bg-black/90">
@@ -116,8 +116,8 @@ function SourceSection({
onClick={() => onSelect(source.id)}
className={`overflow-hidden rounded-lg border-2 text-left transition-colors ${
selectedId === source.id
? "border-blue-500 bg-zinc-800"
: "border-transparent bg-zinc-800/50 hover:border-zinc-600"
? 'border-blue-500 bg-zinc-800'
: 'border-transparent bg-zinc-800/50 hover:border-zinc-600'
}`}
>
<img
+33 -30
View File
@@ -1,26 +1,26 @@
import * as React from "react"
import { Avatar as AvatarPrimitive } from "radix-ui"
import * as React from 'react';
import { Avatar as AvatarPrimitive } from 'radix-ui';
import { cn } from "@/lib/utils"
import { cn } from '@/lib/utils';
function Avatar({
className,
size = "default",
size = 'default',
...props
}: React.ComponentProps<typeof AvatarPrimitive.Root> & {
size?: "default" | "xs" | "sm" | "lg"
size?: 'default' | 'xs' | 'sm' | 'lg';
}) {
return (
<AvatarPrimitive.Root
data-slot="avatar"
data-size={size}
className={cn(
"size-8 rounded-full after:rounded-full data-[size=lg]:size-10 data-[size=sm]:size-6 data-[size=xs]:size-4 after:border-border group/avatar relative flex shrink-0 select-none after:absolute after:inset-0 after:border after:mix-blend-darken dark:after:mix-blend-lighten",
className
'size-8 rounded-full after:rounded-full data-[size=lg]:size-10 data-[size=sm]:size-6 data-[size=xs]:size-4 after:border-border group/avatar relative flex shrink-0 select-none after:absolute after:inset-0 after:border after:mix-blend-darken dark:after:mix-blend-lighten',
className,
)}
{...props}
/>
)
);
}
function AvatarImage({
@@ -31,12 +31,12 @@ function AvatarImage({
<AvatarPrimitive.Image
data-slot="avatar-image"
className={cn(
"rounded-full aspect-square size-full object-cover",
className
'rounded-full aspect-square size-full object-cover',
className,
)}
{...props}
/>
)
);
}
function AvatarFallback({
@@ -47,55 +47,58 @@ function AvatarFallback({
<AvatarPrimitive.Fallback
data-slot="avatar-fallback"
className={cn(
"bg-muted text-muted-foreground rounded-full flex size-full items-center justify-center text-sm group-data-[size=sm]/avatar:text-xs group-data-[size=xs]/avatar:text-[8px]",
className
'bg-muted text-muted-foreground rounded-full flex size-full items-center justify-center text-sm group-data-[size=sm]/avatar:text-xs group-data-[size=xs]/avatar:text-[8px]',
className,
)}
{...props}
/>
)
);
}
function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) {
function AvatarBadge({ className, ...props }: React.ComponentProps<'span'>) {
return (
<span
data-slot="avatar-badge"
className={cn(
"bg-primary text-primary-foreground ring-background absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-blend-color ring-2 select-none",
"group-data-[size=xs]/avatar:size-1.5 group-data-[size=xs]/avatar:[&>svg]:hidden",
"group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden",
"group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2",
"group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2",
className
'bg-primary text-primary-foreground ring-background absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-blend-color ring-2 select-none',
'group-data-[size=xs]/avatar:size-1.5 group-data-[size=xs]/avatar:[&>svg]:hidden',
'group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden',
'group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2',
'group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2',
className,
)}
{...props}
/>
)
);
}
function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) {
function AvatarGroup({ className, ...props }: React.ComponentProps<'div'>) {
return (
<div
data-slot="avatar-group"
className={cn(
"*:data-[slot=avatar]:ring-background group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2",
className
'*:data-[slot=avatar]:ring-background group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2',
className,
)}
{...props}
/>
)
);
}
function AvatarGroupCount({
className,
...props
}: React.ComponentProps<"div">) {
}: React.ComponentProps<'div'>) {
return (
<div
data-slot="avatar-group-count"
className={cn("bg-muted text-muted-foreground size-8 rounded-full text-sm group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 group-has-data-[size=xs]/avatar-group:size-4 group-has-data-[size=xs]/avatar-group:text-[8px] [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3 ring-background relative flex shrink-0 items-center justify-center ring-2", className)}
className={cn(
'bg-muted text-muted-foreground size-8 rounded-full text-sm group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 group-has-data-[size=xs]/avatar-group:size-4 group-has-data-[size=xs]/avatar-group:text-[8px] [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3 ring-background relative flex shrink-0 items-center justify-center ring-2',
className,
)}
{...props}
/>
)
);
}
export {
@@ -105,4 +108,4 @@ export {
AvatarGroup,
AvatarGroupCount,
AvatarBadge,
}
};
+23 -19
View File
@@ -1,36 +1,40 @@
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { Slot } from "radix-ui"
import * as React from 'react';
import { cva, type VariantProps } from 'class-variance-authority';
import { Slot } from 'radix-ui';
import { cn } from "@/lib/utils"
import { cn } from '@/lib/utils';
const badgeVariants = cva(
"h-5 gap-1 rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium transition-all has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&>svg]:size-3! inline-flex items-center justify-center w-fit whitespace-nowrap shrink-0 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive overflow-hidden group/badge",
'h-5 gap-1 rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium transition-all has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&>svg]:size-3! inline-flex items-center justify-center w-fit whitespace-nowrap shrink-0 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive overflow-hidden group/badge',
{
variants: {
variant: {
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
secondary: "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
destructive: "bg-destructive/10 [a]:hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 text-destructive dark:bg-destructive/20",
outline: "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
ghost: "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
link: "text-primary underline-offset-4 hover:underline",
default: 'bg-primary text-primary-foreground [a]:hover:bg-primary/80',
secondary:
'bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80',
destructive:
'bg-destructive/10 [a]:hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 text-destructive dark:bg-destructive/20',
outline:
'border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground',
ghost:
'hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50',
link: 'text-primary underline-offset-4 hover:underline',
},
},
defaultVariants: {
variant: "default",
variant: 'default',
},
}
)
},
);
function Badge({
className,
variant = "default",
variant = 'default',
asChild = false,
...props
}: React.ComponentProps<"span"> &
}: React.ComponentProps<'span'> &
VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
const Comp = asChild ? Slot.Root : "span"
const Comp = asChild ? Slot.Root : 'span';
return (
<Comp
@@ -39,7 +43,7 @@ function Badge({
className={cn(badgeVariants({ variant }), className)}
{...props}
/>
)
);
}
export { Badge, badgeVariants }
export { Badge, badgeVariants };
+31 -34
View File
@@ -1,10 +1,10 @@
import * as React from "react"
import { Slot } from "radix-ui"
import * as React from 'react';
import { Slot } from 'radix-ui';
import { cn } from "@/lib/utils"
import { ChevronRightIcon, MoreHorizontalIcon } from "lucide-react"
import { cn } from '@/lib/utils';
import { ChevronRightIcon, MoreHorizontalIcon } from 'lucide-react';
function Breadcrumb({ className, ...props }: React.ComponentProps<"nav">) {
function Breadcrumb({ className, ...props }: React.ComponentProps<'nav'>) {
return (
<nav
aria-label="breadcrumb"
@@ -12,103 +12,100 @@ function Breadcrumb({ className, ...props }: React.ComponentProps<"nav">) {
className={cn(className)}
{...props}
/>
)
);
}
function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
function BreadcrumbList({ className, ...props }: React.ComponentProps<'ol'>) {
return (
<ol
data-slot="breadcrumb-list"
className={cn(
"flex flex-wrap items-center gap-1.5 text-sm wrap-break-word text-muted-foreground",
className
'flex flex-wrap items-center gap-1.5 text-sm wrap-break-word text-muted-foreground',
className,
)}
{...props}
/>
)
);
}
function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
function BreadcrumbItem({ className, ...props }: React.ComponentProps<'li'>) {
return (
<li
data-slot="breadcrumb-item"
className={cn("inline-flex items-center gap-1", className)}
className={cn('inline-flex items-center gap-1', className)}
{...props}
/>
)
);
}
function BreadcrumbLink({
asChild,
className,
...props
}: React.ComponentProps<"a"> & {
asChild?: boolean
}: React.ComponentProps<'a'> & {
asChild?: boolean;
}) {
const Comp = asChild ? Slot.Root : "a"
const Comp = asChild ? Slot.Root : 'a';
return (
<Comp
data-slot="breadcrumb-link"
className={cn("transition-colors hover:text-foreground", className)}
className={cn('transition-colors hover:text-foreground', className)}
{...props}
/>
)
);
}
function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
function BreadcrumbPage({ className, ...props }: React.ComponentProps<'span'>) {
return (
<span
data-slot="breadcrumb-page"
role="link"
aria-disabled="true"
aria-current="page"
className={cn("font-normal text-foreground", className)}
className={cn('font-normal text-foreground', className)}
{...props}
/>
)
);
}
function BreadcrumbSeparator({
children,
className,
...props
}: React.ComponentProps<"li">) {
}: React.ComponentProps<'li'>) {
return (
<li
data-slot="breadcrumb-separator"
role="presentation"
aria-hidden="true"
className={cn("[&>svg]:size-3.5", className)}
className={cn('[&>svg]:size-3.5', className)}
{...props}
>
{children ?? (
<ChevronRightIcon />
)}
{children ?? <ChevronRightIcon />}
</li>
)
);
}
function BreadcrumbEllipsis({
className,
...props
}: React.ComponentProps<"span">) {
}: React.ComponentProps<'span'>) {
return (
<span
data-slot="breadcrumb-ellipsis"
role="presentation"
aria-hidden="true"
className={cn(
"flex size-5 items-center justify-center [&>svg]:size-4",
className
'flex size-5 items-center justify-center [&>svg]:size-4',
className,
)}
{...props}
>
<MoreHorizontalIcon
/>
<MoreHorizontalIcon />
<span className="sr-only">More</span>
</span>
)
);
}
export {
@@ -119,4 +116,4 @@ export {
BreadcrumbPage,
BreadcrumbSeparator,
BreadcrumbEllipsis,
}
};
+34 -27
View File
@@ -1,50 +1,57 @@
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { Slot } from "radix-ui"
import * as React from 'react';
import { cva, type VariantProps } from 'class-variance-authority';
import { Slot } from 'radix-ui';
import { cn } from "@/lib/utils"
import { cn } from '@/lib/utils';
const buttonVariants = cva(
"focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-lg border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-3 aria-invalid:ring-3 [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
outline: "border-border bg-background hover:bg-muted hover:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 aria-expanded:bg-muted aria-expanded:text-foreground",
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
ghost: "hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground",
destructive: "bg-destructive/10 hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/20 text-destructive focus-visible:border-destructive/40 dark:hover:bg-destructive/30",
link: "text-primary underline-offset-4 hover:underline",
default: 'bg-primary text-primary-foreground [a]:hover:bg-primary/80',
outline:
'border-border bg-background hover:bg-muted hover:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 aria-expanded:bg-muted aria-expanded:text-foreground',
secondary:
'bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground',
ghost:
'hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground',
destructive:
'bg-destructive/10 hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/20 text-destructive focus-visible:border-destructive/40 dark:hover:bg-destructive/30',
link: 'text-primary underline-offset-4 hover:underline',
},
size: {
default: "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
default:
'h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2',
xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3",
icon: "size-8",
"icon-xs": "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
"icon-sm": "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
"icon-lg": "size-9",
lg: 'h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3',
icon: 'size-8',
'icon-xs':
"size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
'icon-sm':
'size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg',
'icon-lg': 'size-9',
},
},
defaultVariants: {
variant: "default",
size: "default",
variant: 'default',
size: 'default',
},
}
)
},
);
function Button({
className,
variant = "default",
size = "default",
variant = 'default',
size = 'default',
asChild = false,
...props
}: React.ComponentProps<"button"> &
}: React.ComponentProps<'button'> &
VariantProps<typeof buttonVariants> & {
asChild?: boolean
asChild?: boolean;
}) {
const Comp = asChild ? Slot.Root : "button"
const Comp = asChild ? Slot.Root : 'button';
return (
<Comp
@@ -54,7 +61,7 @@ function Button({
className={cn(buttonVariants({ variant, size, className }))}
{...props}
/>
)
);
}
export { Button, buttonVariants }
export { Button, buttonVariants };
+36 -27
View File
@@ -1,86 +1,95 @@
import * as React from "react"
import * as React from 'react';
import { cn } from "@/lib/utils"
import { cn } from '@/lib/utils';
function Card({
className,
size = "default",
size = 'default',
...props
}: React.ComponentProps<"div"> & { size?: "default" | "sm" }) {
}: React.ComponentProps<'div'> & { size?: 'default' | 'sm' }) {
return (
<div
data-slot="card"
data-size={size}
className={cn("ring-foreground/10 bg-card text-card-foreground gap-4 overflow-hidden rounded-xl py-4 text-sm ring-1 has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl group/card flex flex-col", className)}
className={cn(
'ring-foreground/10 bg-card text-card-foreground gap-4 overflow-hidden rounded-xl py-4 text-sm ring-1 has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl group/card flex flex-col',
className,
)}
{...props}
/>
)
);
}
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
function CardHeader({ className, ...props }: React.ComponentProps<'div'>) {
return (
<div
data-slot="card-header"
className={cn(
"gap-1 rounded-t-xl px-4 group-data-[size=sm]/card:px-3 [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3 group/card-header @container/card-header grid auto-rows-min items-start has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto]",
className
'gap-1 rounded-t-xl px-4 group-data-[size=sm]/card:px-3 [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3 group/card-header @container/card-header grid auto-rows-min items-start has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto]',
className,
)}
{...props}
/>
)
);
}
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
function CardTitle({ className, ...props }: React.ComponentProps<'div'>) {
return (
<div
data-slot="card-title"
className={cn("text-base leading-snug font-medium group-data-[size=sm]/card:text-sm", className)}
className={cn(
'text-base leading-snug font-medium group-data-[size=sm]/card:text-sm',
className,
)}
{...props}
/>
)
);
}
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
function CardDescription({ className, ...props }: React.ComponentProps<'div'>) {
return (
<div
data-slot="card-description"
className={cn("text-muted-foreground text-sm", className)}
className={cn('text-muted-foreground text-sm', className)}
{...props}
/>
)
);
}
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
function CardAction({ className, ...props }: React.ComponentProps<'div'>) {
return (
<div
data-slot="card-action"
className={cn(
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
className
'col-start-2 row-span-2 row-start-1 self-start justify-self-end',
className,
)}
{...props}
/>
)
);
}
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
function CardContent({ className, ...props }: React.ComponentProps<'div'>) {
return (
<div
data-slot="card-content"
className={cn("px-4 group-data-[size=sm]/card:px-3", className)}
className={cn('px-4 group-data-[size=sm]/card:px-3', className)}
{...props}
/>
)
);
}
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
function CardFooter({ className, ...props }: React.ComponentProps<'div'>) {
return (
<div
data-slot="card-footer"
className={cn("bg-muted/50 rounded-b-xl border-t p-4 group-data-[size=sm]/card:p-3 flex items-center", className)}
className={cn(
'bg-muted/50 rounded-b-xl border-t p-4 group-data-[size=sm]/card:p-3 flex items-center',
className,
)}
{...props}
/>
)
);
}
export {
@@ -91,4 +100,4 @@ export {
CardAction,
CardDescription,
CardContent,
}
};
+9 -10
View File
@@ -1,8 +1,8 @@
import * as React from "react"
import { Checkbox as CheckboxPrimitive } from "radix-ui"
import * as React from 'react';
import { Checkbox as CheckboxPrimitive } from 'radix-ui';
import { cn } from "@/lib/utils"
import { CheckIcon } from "lucide-react"
import { cn } from '@/lib/utils';
import { CheckIcon } from 'lucide-react';
function Checkbox({
className,
@@ -12,8 +12,8 @@ function Checkbox({
<CheckboxPrimitive.Root
data-slot="checkbox"
className={cn(
"peer relative flex size-4 shrink-0 items-center justify-center rounded-[4px] border border-input transition-colors outline-none group-has-disabled/field:opacity-50 after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary",
className
'peer relative flex size-4 shrink-0 items-center justify-center rounded-[4px] border border-input transition-colors outline-none group-has-disabled/field:opacity-50 after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary',
className,
)}
{...props}
>
@@ -21,11 +21,10 @@ function Checkbox({
data-slot="checkbox-indicator"
className="grid place-content-center text-current transition-none [&>svg]:size-3.5"
>
<CheckIcon
/>
<CheckIcon />
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>
)
);
}
export { Checkbox }
export { Checkbox };
+49 -45
View File
@@ -1,13 +1,13 @@
import * as React from "react"
import { ContextMenu as ContextMenuPrimitive } from "radix-ui"
import * as React from 'react';
import { ContextMenu as ContextMenuPrimitive } from 'radix-ui';
import { cn } from "@/lib/utils"
import { ChevronRightIcon, CheckIcon } from "lucide-react"
import { cn } from '@/lib/utils';
import { ChevronRightIcon, CheckIcon } from 'lucide-react';
function ContextMenu({
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.Root>) {
return <ContextMenuPrimitive.Root data-slot="context-menu" {...props} />
return <ContextMenuPrimitive.Root data-slot="context-menu" {...props} />;
}
function ContextMenuTrigger({
@@ -17,10 +17,10 @@ function ContextMenuTrigger({
return (
<ContextMenuPrimitive.Trigger
data-slot="context-menu-trigger"
className={cn("select-none", className)}
className={cn('select-none', className)}
{...props}
/>
)
);
}
function ContextMenuGroup({
@@ -28,7 +28,7 @@ function ContextMenuGroup({
}: React.ComponentProps<typeof ContextMenuPrimitive.Group>) {
return (
<ContextMenuPrimitive.Group data-slot="context-menu-group" {...props} />
)
);
}
function ContextMenuPortal({
@@ -36,13 +36,13 @@ function ContextMenuPortal({
}: React.ComponentProps<typeof ContextMenuPrimitive.Portal>) {
return (
<ContextMenuPrimitive.Portal data-slot="context-menu-portal" {...props} />
)
);
}
function ContextMenuSub({
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.Sub>) {
return <ContextMenuPrimitive.Sub data-slot="context-menu-sub" {...props} />
return <ContextMenuPrimitive.Sub data-slot="context-menu-sub" {...props} />;
}
function ContextMenuRadioGroup({
@@ -53,34 +53,37 @@ function ContextMenuRadioGroup({
data-slot="context-menu-radio-group"
{...props}
/>
)
);
}
function ContextMenuContent({
className,
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.Content> & {
side?: "top" | "right" | "bottom" | "left"
side?: 'top' | 'right' | 'bottom' | 'left';
}) {
return (
<ContextMenuPrimitive.Portal>
<ContextMenuPrimitive.Content
data-slot="context-menu-content"
className={cn("z-50 max-h-(--radix-context-menu-content-available-height) min-w-36 origin-(--radix-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 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 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )}
className={cn(
'z-50 max-h-(--radix-context-menu-content-available-height) min-w-36 origin-(--radix-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 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 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95',
className,
)}
{...props}
/>
</ContextMenuPrimitive.Portal>
)
);
}
function ContextMenuItem({
className,
inset,
variant = "default",
variant = 'default',
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.Item> & {
inset?: boolean
variant?: "default" | "destructive"
inset?: boolean;
variant?: 'default' | 'destructive';
}) {
return (
<ContextMenuPrimitive.Item
@@ -89,11 +92,11 @@ function ContextMenuItem({
data-variant={variant}
className={cn(
"group/context-menu-item relative flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 focus:*:[svg]:text-accent-foreground data-[variant=destructive]:*:[svg]:text-destructive",
className
className,
)}
{...props}
/>
)
);
}
function ContextMenuSubTrigger({
@@ -102,7 +105,7 @@ function ContextMenuSubTrigger({
children,
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.SubTrigger> & {
inset?: boolean
inset?: boolean;
}) {
return (
<ContextMenuPrimitive.SubTrigger
@@ -110,14 +113,14 @@ function ContextMenuSubTrigger({
data-inset={inset}
className={cn(
"flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-open:bg-accent data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
className,
)}
{...props}
>
{children}
<ChevronRightIcon className="ml-auto" />
</ContextMenuPrimitive.SubTrigger>
)
);
}
function ContextMenuSubContent({
@@ -127,10 +130,13 @@ function ContextMenuSubContent({
return (
<ContextMenuPrimitive.SubContent
data-slot="context-menu-sub-content"
className={cn("z-50 min-w-32 origin-(--radix-context-menu-content-transform-origin) overflow-hidden rounded-lg border bg-popover p-1 text-popover-foreground shadow-lg duration-100 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 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )}
className={cn(
'z-50 min-w-32 origin-(--radix-context-menu-content-transform-origin) overflow-hidden rounded-lg border bg-popover p-1 text-popover-foreground shadow-lg duration-100 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 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95',
className,
)}
{...props}
/>
)
);
}
function ContextMenuCheckboxItem({
@@ -140,7 +146,7 @@ function ContextMenuCheckboxItem({
inset,
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.CheckboxItem> & {
inset?: boolean
inset?: boolean;
}) {
return (
<ContextMenuPrimitive.CheckboxItem
@@ -148,20 +154,19 @@ function ContextMenuCheckboxItem({
data-inset={inset}
className={cn(
"relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
className,
)}
checked={checked}
{...props}
>
<span className="pointer-events-none absolute right-2">
<ContextMenuPrimitive.ItemIndicator>
<CheckIcon
/>
<CheckIcon />
</ContextMenuPrimitive.ItemIndicator>
</span>
{children}
</ContextMenuPrimitive.CheckboxItem>
)
);
}
function ContextMenuRadioItem({
@@ -170,7 +175,7 @@ function ContextMenuRadioItem({
inset,
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.RadioItem> & {
inset?: boolean
inset?: boolean;
}) {
return (
<ContextMenuPrimitive.RadioItem
@@ -178,19 +183,18 @@ function ContextMenuRadioItem({
data-inset={inset}
className={cn(
"relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
className,
)}
{...props}
>
<span className="pointer-events-none absolute right-2">
<ContextMenuPrimitive.ItemIndicator>
<CheckIcon
/>
<CheckIcon />
</ContextMenuPrimitive.ItemIndicator>
</span>
{children}
</ContextMenuPrimitive.RadioItem>
)
);
}
function ContextMenuLabel({
@@ -198,19 +202,19 @@ function ContextMenuLabel({
inset,
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.Label> & {
inset?: boolean
inset?: boolean;
}) {
return (
<ContextMenuPrimitive.Label
data-slot="context-menu-label"
data-inset={inset}
className={cn(
"px-1.5 py-1 text-xs font-medium text-muted-foreground data-inset:pl-7",
className
'px-1.5 py-1 text-xs font-medium text-muted-foreground data-inset:pl-7',
className,
)}
{...props}
/>
)
);
}
function ContextMenuSeparator({
@@ -220,26 +224,26 @@ function ContextMenuSeparator({
return (
<ContextMenuPrimitive.Separator
data-slot="context-menu-separator"
className={cn("-mx-1 my-1 h-px bg-border", className)}
className={cn('-mx-1 my-1 h-px bg-border', className)}
{...props}
/>
)
);
}
function ContextMenuShortcut({
className,
...props
}: React.ComponentProps<"span">) {
}: React.ComponentProps<'span'>) {
return (
<span
data-slot="context-menu-shortcut"
className={cn(
"ml-auto text-xs tracking-widest text-muted-foreground group-focus/context-menu-item:text-accent-foreground",
className
'ml-auto text-xs tracking-widest text-muted-foreground group-focus/context-menu-item:text-accent-foreground',
className,
)}
{...props}
/>
)
);
}
export {
@@ -258,4 +262,4 @@ export {
ContextMenuSubContent,
ContextMenuSubTrigger,
ContextMenuRadioGroup,
}
};
+41 -32
View File
@@ -1,34 +1,34 @@
"use client"
'use client';
import * as React from "react"
import { Dialog as DialogPrimitive } from "radix-ui"
import * as React from 'react';
import { Dialog as DialogPrimitive } from 'radix-ui';
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import { XIcon } from "lucide-react"
import { cn } from '@/lib/utils';
import { Button } from '@/components/ui/button';
import { XIcon } from 'lucide-react';
function Dialog({
...props
}: React.ComponentProps<typeof DialogPrimitive.Root>) {
return <DialogPrimitive.Root data-slot="dialog" {...props} />
return <DialogPrimitive.Root data-slot="dialog" {...props} />;
}
function DialogTrigger({
...props
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
}
function DialogPortal({
...props
}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
}
function DialogClose({
...props
}: React.ComponentProps<typeof DialogPrimitive.Close>) {
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
}
function DialogOverlay({
@@ -38,10 +38,13 @@ function DialogOverlay({
return (
<DialogPrimitive.Overlay
data-slot="dialog-overlay"
className={cn("data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs fixed inset-0 isolate z-50", className)}
className={cn(
'data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs fixed inset-0 isolate z-50',
className,
)}
{...props}
/>
)
);
}
function DialogContent({
@@ -50,7 +53,7 @@ function DialogContent({
showCloseButton = true,
...props
}: React.ComponentProps<typeof DialogPrimitive.Content> & {
showCloseButton?: boolean
showCloseButton?: boolean;
}) {
return (
<DialogPortal>
@@ -58,34 +61,37 @@ function DialogContent({
<DialogPrimitive.Content
data-slot="dialog-content"
className={cn(
"bg-background data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 ring-foreground/10 grid max-w-[calc(100%-2rem)] gap-4 rounded-xl p-4 text-sm ring-1 duration-100 sm:max-w-sm fixed top-1/2 left-1/2 z-50 w-full -translate-x-1/2 -translate-y-1/2 outline-none",
className
'bg-background data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 ring-foreground/10 grid max-w-[calc(100%-2rem)] gap-4 rounded-xl p-4 text-sm ring-1 duration-100 sm:max-w-sm fixed top-1/2 left-1/2 z-50 w-full -translate-x-1/2 -translate-y-1/2 outline-none',
className,
)}
{...props}
>
{children}
{showCloseButton && (
<DialogPrimitive.Close data-slot="dialog-close" asChild>
<Button variant="ghost" className="absolute top-2 right-2" size="icon-sm">
<XIcon
/>
<Button
variant="ghost"
className="absolute top-2 right-2"
size="icon-sm"
>
<XIcon />
<span className="sr-only">Close</span>
</Button>
</DialogPrimitive.Close>
)}
</DialogPrimitive.Content>
</DialogPortal>
)
);
}
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
function DialogHeader({ className, ...props }: React.ComponentProps<'div'>) {
return (
<div
data-slot="dialog-header"
className={cn("gap-2 flex flex-col", className)}
className={cn('gap-2 flex flex-col', className)}
{...props}
/>
)
);
}
function DialogFooter({
@@ -93,15 +99,15 @@ function DialogFooter({
showCloseButton = false,
children,
...props
}: React.ComponentProps<"div"> & {
showCloseButton?: boolean
}: React.ComponentProps<'div'> & {
showCloseButton?: boolean;
}) {
return (
<div
data-slot="dialog-footer"
className={cn(
"bg-muted/50 -mx-4 -mb-4 rounded-b-xl border-t p-4 flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
className
'bg-muted/50 -mx-4 -mb-4 rounded-b-xl border-t p-4 flex flex-col-reverse gap-2 sm:flex-row sm:justify-end',
className,
)}
{...props}
>
@@ -112,7 +118,7 @@ function DialogFooter({
</DialogPrimitive.Close>
)}
</div>
)
);
}
function DialogTitle({
@@ -122,10 +128,10 @@ function DialogTitle({
return (
<DialogPrimitive.Title
data-slot="dialog-title"
className={cn("text-base leading-none font-medium", className)}
className={cn('text-base leading-none font-medium', className)}
{...props}
/>
)
);
}
function DialogDescription({
@@ -135,10 +141,13 @@ function DialogDescription({
return (
<DialogPrimitive.Description
data-slot="dialog-description"
className={cn("text-muted-foreground *:[a]:hover:text-foreground text-sm *:[a]:underline *:[a]:underline-offset-3", className)}
className={cn(
'text-muted-foreground *:[a]:hover:text-foreground text-sm *:[a]:underline *:[a]:underline-offset-3',
className,
)}
{...props}
/>
)
);
}
export {
@@ -152,4 +161,4 @@ export {
DialogPortal,
DialogTitle,
DialogTrigger,
}
};
+52 -42
View File
@@ -1,13 +1,13 @@
import * as React from "react"
import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui"
import * as React from 'react';
import { DropdownMenu as DropdownMenuPrimitive } from 'radix-ui';
import { cn } from "@/lib/utils"
import { CheckIcon, ChevronRightIcon } from "lucide-react"
import { cn } from '@/lib/utils';
import { CheckIcon, ChevronRightIcon } from 'lucide-react';
function DropdownMenu({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />
return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
}
function DropdownMenuPortal({
@@ -15,7 +15,7 @@ function DropdownMenuPortal({
}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
return (
<DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
)
);
}
function DropdownMenuTrigger({
@@ -26,12 +26,12 @@ function DropdownMenuTrigger({
data-slot="dropdown-menu-trigger"
{...props}
/>
)
);
}
function DropdownMenuContent({
className,
align = "start",
align = 'start',
sideOffset = 4,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
@@ -41,11 +41,14 @@ function DropdownMenuContent({
data-slot="dropdown-menu-content"
sideOffset={sideOffset}
align={align}
className={cn("data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-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 ring-foreground/10 bg-popover text-popover-foreground min-w-32 rounded-lg p-1 shadow-md ring-1 duration-100 z-50 max-h-(--radix-dropdown-menu-content-available-height) w-(--radix-dropdown-menu-trigger-width) origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto data-[state=closed]:overflow-hidden", className )}
className={cn(
'data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-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 ring-foreground/10 bg-popover text-popover-foreground min-w-32 rounded-lg p-1 shadow-md ring-1 duration-100 z-50 max-h-(--radix-dropdown-menu-content-available-height) w-(--radix-dropdown-menu-trigger-width) origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto data-[state=closed]:overflow-hidden',
className,
)}
{...props}
/>
</DropdownMenuPrimitive.Portal>
)
);
}
function DropdownMenuGroup({
@@ -53,17 +56,17 @@ function DropdownMenuGroup({
}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
return (
<DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
)
);
}
function DropdownMenuItem({
className,
inset,
variant = "default",
variant = 'default',
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
inset?: boolean
variant?: "default" | "destructive"
inset?: boolean;
variant?: 'default' | 'destructive';
}) {
return (
<DropdownMenuPrimitive.Item
@@ -72,11 +75,11 @@ function DropdownMenuItem({
data-variant={variant}
className={cn(
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:text-destructive not-data-[variant=destructive]:focus:**:text-accent-foreground gap-1.5 rounded-md px-1.5 py-1 text-sm data-inset:pl-7 [&_svg:not([class*='size-'])]:size-4 group/dropdown-menu-item relative flex cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
className
className,
)}
{...props}
/>
)
);
}
function DropdownMenuCheckboxItem({
@@ -86,7 +89,7 @@ function DropdownMenuCheckboxItem({
inset,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem> & {
inset?: boolean
inset?: boolean;
}) {
return (
<DropdownMenuPrimitive.CheckboxItem
@@ -94,7 +97,7 @@ function DropdownMenuCheckboxItem({
data-inset={inset}
className={cn(
"focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm data-inset:pl-7 [&_svg:not([class*='size-'])]:size-4 relative flex cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
className
className,
)}
checked={checked}
{...props}
@@ -104,13 +107,12 @@ function DropdownMenuCheckboxItem({
data-slot="dropdown-menu-checkbox-item-indicator"
>
<DropdownMenuPrimitive.ItemIndicator>
<CheckIcon
/>
<CheckIcon />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.CheckboxItem>
)
);
}
function DropdownMenuRadioGroup({
@@ -121,7 +123,7 @@ function DropdownMenuRadioGroup({
data-slot="dropdown-menu-radio-group"
{...props}
/>
)
);
}
function DropdownMenuRadioItem({
@@ -130,7 +132,7 @@ function DropdownMenuRadioItem({
inset,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem> & {
inset?: boolean
inset?: boolean;
}) {
return (
<DropdownMenuPrimitive.RadioItem
@@ -138,7 +140,7 @@ function DropdownMenuRadioItem({
data-inset={inset}
className={cn(
"focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm data-inset:pl-7 [&_svg:not([class*='size-'])]:size-4 relative flex cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
className
className,
)}
{...props}
>
@@ -147,13 +149,12 @@ function DropdownMenuRadioItem({
data-slot="dropdown-menu-radio-item-indicator"
>
<DropdownMenuPrimitive.ItemIndicator>
<CheckIcon
/>
<CheckIcon />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.RadioItem>
)
);
}
function DropdownMenuLabel({
@@ -161,16 +162,19 @@ function DropdownMenuLabel({
inset,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
inset?: boolean
inset?: boolean;
}) {
return (
<DropdownMenuPrimitive.Label
data-slot="dropdown-menu-label"
data-inset={inset}
className={cn("text-muted-foreground px-1.5 py-1 text-xs font-medium data-inset:pl-7", className)}
className={cn(
'text-muted-foreground px-1.5 py-1 text-xs font-medium data-inset:pl-7',
className,
)}
{...props}
/>
)
);
}
function DropdownMenuSeparator({
@@ -180,29 +184,32 @@ function DropdownMenuSeparator({
return (
<DropdownMenuPrimitive.Separator
data-slot="dropdown-menu-separator"
className={cn("bg-border -mx-1 my-1 h-px", className)}
className={cn('bg-border -mx-1 my-1 h-px', className)}
{...props}
/>
)
);
}
function DropdownMenuShortcut({
className,
...props
}: React.ComponentProps<"span">) {
}: React.ComponentProps<'span'>) {
return (
<span
data-slot="dropdown-menu-shortcut"
className={cn("text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground ml-auto text-xs tracking-widest", className)}
className={cn(
'text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground ml-auto text-xs tracking-widest',
className,
)}
{...props}
/>
)
);
}
function DropdownMenuSub({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />
return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />;
}
function DropdownMenuSubTrigger({
@@ -211,7 +218,7 @@ function DropdownMenuSubTrigger({
children,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
inset?: boolean
inset?: boolean;
}) {
return (
<DropdownMenuPrimitive.SubTrigger
@@ -219,14 +226,14 @@ function DropdownMenuSubTrigger({
data-inset={inset}
className={cn(
"focus:bg-accent focus:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground gap-1.5 rounded-md px-1.5 py-1 text-sm data-inset:pl-7 [&_svg:not([class*='size-'])]:size-4 flex cursor-default items-center outline-hidden select-none [&_svg]:pointer-events-none [&_svg]:shrink-0",
className
className,
)}
{...props}
>
{children}
<ChevronRightIcon className="ml-auto" />
</DropdownMenuPrimitive.SubTrigger>
)
);
}
function DropdownMenuSubContent({
@@ -236,10 +243,13 @@ function DropdownMenuSubContent({
return (
<DropdownMenuPrimitive.SubContent
data-slot="dropdown-menu-sub-content"
className={cn("data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-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 ring-foreground/10 bg-popover text-popover-foreground min-w-[96px] rounded-lg p-1 shadow-lg ring-1 duration-100 z-50 origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden", className )}
className={cn(
'data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-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 ring-foreground/10 bg-popover text-popover-foreground min-w-[96px] rounded-lg p-1 shadow-lg ring-1 duration-100 z-50 origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden',
className,
)}
{...props}
/>
)
);
}
export {
@@ -258,4 +268,4 @@ export {
DropdownMenuSub,
DropdownMenuSubTrigger,
DropdownMenuSubContent,
}
};
+7 -7
View File
@@ -1,19 +1,19 @@
import * as React from "react"
import * as React from 'react';
import { cn } from "@/lib/utils"
import { cn } from '@/lib/utils';
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
function Input({ className, type, ...props }: React.ComponentProps<'input'>) {
return (
<input
type={type}
data-slot="input"
className={cn(
"dark:bg-input/30 border-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 disabled:bg-input/50 dark:disabled:bg-input/80 h-8 rounded-lg border bg-transparent px-2.5 py-1 text-base transition-colors file:h-6 file:text-sm file:font-medium focus-visible:ring-3 aria-invalid:ring-3 md:text-sm file:text-foreground placeholder:text-muted-foreground w-full min-w-0 outline-none file:inline-flex file:border-0 file:bg-transparent disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50",
className
'dark:bg-input/30 border-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 disabled:bg-input/50 dark:disabled:bg-input/80 h-8 rounded-lg border bg-transparent px-2.5 py-1 text-base transition-colors file:h-6 file:text-sm file:font-medium focus-visible:ring-3 aria-invalid:ring-3 md:text-sm file:text-foreground placeholder:text-muted-foreground w-full min-w-0 outline-none file:inline-flex file:border-0 file:bg-transparent disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50',
className,
)}
{...props}
/>
)
);
}
export { Input }
export { Input };
+7 -7
View File
@@ -1,7 +1,7 @@
import * as React from "react"
import { Label as LabelPrimitive } from "radix-ui"
import * as React from 'react';
import { Label as LabelPrimitive } from 'radix-ui';
import { cn } from "@/lib/utils"
import { cn } from '@/lib/utils';
function Label({
className,
@@ -11,12 +11,12 @@ function Label({
<LabelPrimitive.Root
data-slot="label"
className={cn(
"gap-2 text-sm leading-none font-medium group-data-[disabled=true]:opacity-50 peer-disabled:opacity-50 flex items-center select-none group-data-[disabled=true]:pointer-events-none peer-disabled:cursor-not-allowed",
className
'gap-2 text-sm leading-none font-medium group-data-[disabled=true]:opacity-50 peer-disabled:opacity-50 flex items-center select-none group-data-[disabled=true]:pointer-events-none peer-disabled:cursor-not-allowed',
className,
)}
{...props}
/>
)
);
}
export { Label }
export { Label };
+8 -8
View File
@@ -1,9 +1,9 @@
"use client"
'use client';
import * as React from "react"
import { Progress as ProgressPrimitive } from "radix-ui"
import * as React from 'react';
import { Progress as ProgressPrimitive } from 'radix-ui';
import { cn } from "@/lib/utils"
import { cn } from '@/lib/utils';
function Progress({
className,
@@ -14,8 +14,8 @@ function Progress({
<ProgressPrimitive.Root
data-slot="progress"
className={cn(
"bg-muted h-1 rounded-full relative flex w-full items-center overflow-x-hidden",
className
'bg-muted h-1 rounded-full relative flex w-full items-center overflow-x-hidden',
className,
)}
{...props}
>
@@ -25,7 +25,7 @@ function Progress({
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
/>
</ProgressPrimitive.Root>
)
);
}
export { Progress }
export { Progress };
+9 -9
View File
@@ -1,7 +1,7 @@
import * as React from "react"
import { RadioGroup as RadioGroupPrimitive } from "radix-ui"
import * as React from 'react';
import { RadioGroup as RadioGroupPrimitive } from 'radix-ui';
import { cn } from "@/lib/utils"
import { cn } from '@/lib/utils';
function RadioGroup({
className,
@@ -10,10 +10,10 @@ function RadioGroup({
return (
<RadioGroupPrimitive.Root
data-slot="radio-group"
className={cn("grid w-full gap-2", className)}
className={cn('grid w-full gap-2', className)}
{...props}
/>
)
);
}
function RadioGroupItem({
@@ -24,8 +24,8 @@ function RadioGroupItem({
<RadioGroupPrimitive.Item
data-slot="radio-group-item"
className={cn(
"group/radio-group-item peer relative flex aspect-square size-4 shrink-0 rounded-full border border-input outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary",
className
'group/radio-group-item peer relative flex aspect-square size-4 shrink-0 rounded-full border border-input outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary',
className,
)}
{...props}
>
@@ -36,7 +36,7 @@ function RadioGroupItem({
<span className="absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2 rounded-full bg-primary-foreground" />
</RadioGroupPrimitive.Indicator>
</RadioGroupPrimitive.Item>
)
);
}
export { RadioGroup, RadioGroupItem }
export { RadioGroup, RadioGroupItem };
+10 -10
View File
@@ -1,7 +1,7 @@
import * as React from "react"
import { ScrollArea as ScrollAreaPrimitive } from "radix-ui"
import * as React from 'react';
import { ScrollArea as ScrollAreaPrimitive } from 'radix-ui';
import { cn } from "@/lib/utils"
import { cn } from '@/lib/utils';
function ScrollArea({
className,
@@ -11,7 +11,7 @@ function ScrollArea({
return (
<ScrollAreaPrimitive.Root
data-slot="scroll-area"
className={cn("relative", className)}
className={cn('relative', className)}
{...props}
>
<ScrollAreaPrimitive.Viewport
@@ -23,12 +23,12 @@ function ScrollArea({
<ScrollBar />
<ScrollAreaPrimitive.Corner />
</ScrollAreaPrimitive.Root>
)
);
}
function ScrollBar({
className,
orientation = "vertical",
orientation = 'vertical',
...props
}: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>) {
return (
@@ -37,8 +37,8 @@ function ScrollBar({
data-orientation={orientation}
orientation={orientation}
className={cn(
"data-horizontal:h-2.5 data-horizontal:flex-col data-horizontal:border-t data-horizontal:border-t-transparent data-vertical:h-full data-vertical:w-2.5 data-vertical:border-l data-vertical:border-l-transparent flex touch-none p-px transition-colors select-none",
className
'data-horizontal:h-2.5 data-horizontal:flex-col data-horizontal:border-t data-horizontal:border-t-transparent data-vertical:h-full data-vertical:w-2.5 data-vertical:border-l data-vertical:border-l-transparent flex touch-none p-px transition-colors select-none',
className,
)}
{...props}
>
@@ -47,7 +47,7 @@ function ScrollBar({
className="rounded-full bg-border relative flex-1"
/>
</ScrollAreaPrimitive.ScrollAreaScrollbar>
)
);
}
export { ScrollArea, ScrollBar }
export { ScrollArea, ScrollBar };
+44 -35
View File
@@ -1,15 +1,15 @@
"use client"
'use client';
import * as React from "react"
import { Select as SelectPrimitive } from "radix-ui"
import * as React from 'react';
import { Select as SelectPrimitive } from 'radix-ui';
import { cn } from "@/lib/utils"
import { ChevronDownIcon, CheckIcon, ChevronUpIcon } from "lucide-react"
import { cn } from '@/lib/utils';
import { ChevronDownIcon, CheckIcon, ChevronUpIcon } from 'lucide-react';
function Select({
...props
}: React.ComponentProps<typeof SelectPrimitive.Root>) {
return <SelectPrimitive.Root data-slot="select" {...props} />
return <SelectPrimitive.Root data-slot="select" {...props} />;
}
function SelectGroup({
@@ -19,25 +19,25 @@ function SelectGroup({
return (
<SelectPrimitive.Group
data-slot="select-group"
className={cn("scroll-my-1 p-1", className)}
className={cn('scroll-my-1 p-1', className)}
{...props}
/>
)
);
}
function SelectValue({
...props
}: React.ComponentProps<typeof SelectPrimitive.Value>) {
return <SelectPrimitive.Value data-slot="select-value" {...props} />
return <SelectPrimitive.Value data-slot="select-value" {...props} />;
}
function SelectTrigger({
className,
size = "default",
size = 'default',
children,
...props
}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
size?: "sm" | "default"
size?: 'sm' | 'default';
}) {
return (
<SelectPrimitive.Trigger
@@ -45,7 +45,7 @@ function SelectTrigger({
data-size={size}
className={cn(
"border-input data-placeholder:text-muted-foreground dark:bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 gap-1.5 rounded-lg border bg-transparent py-2 pr-2 pl-2.5 text-sm transition-colors select-none focus-visible:ring-3 aria-invalid:ring-3 data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:rounded-[min(var(--radius-md),10px)] *:data-[slot=select-value]:gap-1.5 [&_svg:not([class*='size-'])]:size-4 flex w-fit items-center justify-between whitespace-nowrap outline-none disabled:cursor-not-allowed disabled:opacity-50 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center [&_svg]:pointer-events-none [&_svg]:shrink-0",
className
className,
)}
{...props}
>
@@ -54,22 +54,27 @@ function SelectTrigger({
<ChevronDownIcon className="text-muted-foreground size-4 pointer-events-none" />
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
)
);
}
function SelectContent({
className,
children,
position = "item-aligned",
align = "center",
position = 'item-aligned',
align = 'center',
...props
}: React.ComponentProps<typeof SelectPrimitive.Content>) {
return (
<SelectPrimitive.Portal>
<SelectPrimitive.Content
data-slot="select-content"
data-align-trigger={position === "item-aligned"}
className={cn("bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-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 ring-foreground/10 min-w-36 rounded-lg shadow-md ring-1 duration-100 relative z-50 max-h-(--radix-select-content-available-height) origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto data-[align-trigger=true]:animate-none", 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", className )}
data-align-trigger={position === 'item-aligned'}
className={cn(
'bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-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 ring-foreground/10 min-w-36 rounded-lg shadow-md ring-1 duration-100 relative z-50 max-h-(--radix-select-content-available-height) origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto data-[align-trigger=true]:animate-none',
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',
className,
)}
position={position}
align={align}
{...props}
@@ -78,8 +83,8 @@ function SelectContent({
<SelectPrimitive.Viewport
data-position={position}
className={cn(
"data-[position=popper]:h-(--radix-select-trigger-height) data-[position=popper]:w-full data-[position=popper]:min-w-(--radix-select-trigger-width)",
position === "popper" && ""
'data-[position=popper]:h-(--radix-select-trigger-height) data-[position=popper]:w-full data-[position=popper]:min-w-(--radix-select-trigger-width)',
position === 'popper' && '',
)}
>
{children}
@@ -87,7 +92,7 @@ function SelectContent({
<SelectScrollDownButton />
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
)
);
}
function SelectLabel({
@@ -97,10 +102,10 @@ function SelectLabel({
return (
<SelectPrimitive.Label
data-slot="select-label"
className={cn("text-muted-foreground px-1.5 py-1 text-xs", className)}
className={cn('text-muted-foreground px-1.5 py-1 text-xs', className)}
{...props}
/>
)
);
}
function SelectItem({
@@ -113,7 +118,7 @@ function SelectItem({
data-slot="select-item"
className={cn(
"focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2 relative flex w-full cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
className
className,
)}
{...props}
>
@@ -124,7 +129,7 @@ function SelectItem({
</span>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
</SelectPrimitive.Item>
)
);
}
function SelectSeparator({
@@ -134,10 +139,10 @@ function SelectSeparator({
return (
<SelectPrimitive.Separator
data-slot="select-separator"
className={cn("bg-border -mx-1 my-1 h-px pointer-events-none", className)}
className={cn('bg-border -mx-1 my-1 h-px pointer-events-none', className)}
{...props}
/>
)
);
}
function SelectScrollUpButton({
@@ -147,13 +152,15 @@ function SelectScrollUpButton({
return (
<SelectPrimitive.ScrollUpButton
data-slot="select-scroll-up-button"
className={cn("bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4", className)}
className={cn(
"bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4",
className,
)}
{...props}
>
<ChevronUpIcon
/>
<ChevronUpIcon />
</SelectPrimitive.ScrollUpButton>
)
);
}
function SelectScrollDownButton({
@@ -163,13 +170,15 @@ function SelectScrollDownButton({
return (
<SelectPrimitive.ScrollDownButton
data-slot="select-scroll-down-button"
className={cn("bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4", className)}
className={cn(
"bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4",
className,
)}
{...props}
>
<ChevronDownIcon
/>
<ChevronDownIcon />
</SelectPrimitive.ScrollDownButton>
)
);
}
export {
@@ -183,4 +192,4 @@ export {
SelectSeparator,
SelectTrigger,
SelectValue,
}
};
+8 -8
View File
@@ -1,11 +1,11 @@
import * as React from "react"
import { Separator as SeparatorPrimitive } from "radix-ui"
import * as React from 'react';
import { Separator as SeparatorPrimitive } from 'radix-ui';
import { cn } from "@/lib/utils"
import { cn } from '@/lib/utils';
function Separator({
className,
orientation = "horizontal",
orientation = 'horizontal',
decorative = true,
...props
}: React.ComponentProps<typeof SeparatorPrimitive.Root>) {
@@ -15,12 +15,12 @@ function Separator({
decorative={decorative}
orientation={orientation}
className={cn(
"bg-border shrink-0 data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch",
className
'bg-border shrink-0 data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch',
className,
)}
{...props}
/>
)
);
}
export { Separator }
export { Separator };
+5 -5
View File
@@ -1,13 +1,13 @@
import { cn } from "@/lib/utils"
import { cn } from '@/lib/utils';
function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
function Skeleton({ className, ...props }: React.ComponentProps<'div'>) {
return (
<div
data-slot="skeleton"
className={cn("bg-muted rounded-md animate-pulse", className)}
className={cn('bg-muted rounded-md animate-pulse', className)}
{...props}
/>
)
);
}
export { Skeleton }
export { Skeleton };
+9 -9
View File
@@ -1,7 +1,7 @@
import * as React from "react"
import { Slider as SliderPrimitive } from "radix-ui"
import * as React from 'react';
import { Slider as SliderPrimitive } from 'radix-ui';
import { cn } from "@/lib/utils"
import { cn } from '@/lib/utils';
function Slider({
className,
@@ -18,8 +18,8 @@ function Slider({
: Array.isArray(defaultValue)
? defaultValue
: [min, max],
[value, defaultValue, min, max]
)
[value, defaultValue, min, max],
);
return (
<SliderPrimitive.Root
@@ -29,8 +29,8 @@ function Slider({
min={min}
max={max}
className={cn(
"relative flex w-full touch-none items-center select-none data-disabled:opacity-50 data-vertical:h-full data-vertical:min-h-40 data-vertical:w-auto data-vertical:flex-col",
className
'relative flex w-full touch-none items-center select-none data-disabled:opacity-50 data-vertical:h-full data-vertical:min-h-40 data-vertical:w-auto data-vertical:flex-col',
className,
)}
{...props}
>
@@ -51,7 +51,7 @@ function Slider({
/>
))}
</SliderPrimitive.Root>
)
);
}
export { Slider }
export { Slider };
+21 -25
View File
@@ -1,5 +1,11 @@
import { Toaster as Sonner, type ToasterProps } from "sonner"
import { CircleCheckIcon, InfoIcon, TriangleAlertIcon, OctagonXIcon, Loader2Icon } from "lucide-react"
import { Toaster as Sonner, type ToasterProps } from 'sonner';
import {
CircleCheckIcon,
InfoIcon,
TriangleAlertIcon,
OctagonXIcon,
Loader2Icon,
} from 'lucide-react';
const Toaster = ({ ...props }: ToasterProps) => {
return (
@@ -7,38 +13,28 @@ const Toaster = ({ ...props }: ToasterProps) => {
theme="dark"
className="toaster group"
icons={{
success: (
<CircleCheckIcon className="size-4" />
),
info: (
<InfoIcon className="size-4" />
),
warning: (
<TriangleAlertIcon className="size-4" />
),
error: (
<OctagonXIcon className="size-4" />
),
loading: (
<Loader2Icon className="size-4 animate-spin" />
),
success: <CircleCheckIcon className="size-4" />,
info: <InfoIcon className="size-4" />,
warning: <TriangleAlertIcon className="size-4" />,
error: <OctagonXIcon className="size-4" />,
loading: <Loader2Icon className="size-4 animate-spin" />,
}}
style={
{
"--normal-bg": "var(--popover)",
"--normal-text": "var(--popover-foreground)",
"--normal-border": "var(--border)",
"--border-radius": "var(--radius)",
'--normal-bg': 'var(--popover)',
'--normal-text': 'var(--popover-foreground)',
'--normal-border': 'var(--border)',
'--border-radius': 'var(--radius)',
} as React.CSSProperties
}
toastOptions={{
classNames: {
toast: "cn-toast",
toast: 'cn-toast',
},
}}
{...props}
/>
)
}
);
};
export { Toaster }
export { Toaster };
+9 -9
View File
@@ -1,22 +1,22 @@
import * as React from "react"
import { Switch as SwitchPrimitive } from "radix-ui"
import * as React from 'react';
import { Switch as SwitchPrimitive } from 'radix-ui';
import { cn } from "@/lib/utils"
import { cn } from '@/lib/utils';
function Switch({
className,
size = "default",
size = 'default',
...props
}: React.ComponentProps<typeof SwitchPrimitive.Root> & {
size?: "sm" | "default"
size?: 'sm' | 'default';
}) {
return (
<SwitchPrimitive.Root
data-slot="switch"
data-size={size}
className={cn(
"peer group/switch relative inline-flex shrink-0 items-center rounded-full border border-transparent transition-all outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:bg-primary data-unchecked:bg-input dark:data-unchecked:bg-input/80 data-disabled:cursor-not-allowed data-disabled:opacity-50",
className
'peer group/switch relative inline-flex shrink-0 items-center rounded-full border border-transparent transition-all outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:bg-primary data-unchecked:bg-input dark:data-unchecked:bg-input/80 data-disabled:cursor-not-allowed data-disabled:opacity-50',
className,
)}
{...props}
>
@@ -25,7 +25,7 @@ function Switch({
className="pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 group-data-[size=default]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] dark:data-checked:bg-primary-foreground group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0 dark:data-unchecked:bg-foreground"
/>
</SwitchPrimitive.Root>
)
);
}
export { Switch }
export { Switch };
+25 -25
View File
@@ -1,14 +1,14 @@
"use client"
'use client';
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { Tabs as TabsPrimitive } from "radix-ui"
import * as React from 'react';
import { cva, type VariantProps } from 'class-variance-authority';
import { Tabs as TabsPrimitive } from 'radix-ui';
import { cn } from "@/lib/utils"
import { cn } from '@/lib/utils';
function Tabs({
className,
orientation = "horizontal",
orientation = 'horizontal',
...props
}: React.ComponentProps<typeof TabsPrimitive.Root>) {
return (
@@ -16,32 +16,32 @@ function Tabs({
data-slot="tabs"
data-orientation={orientation}
className={cn(
"group/tabs flex gap-2 data-horizontal:flex-col",
className
'group/tabs flex gap-2 data-horizontal:flex-col',
className,
)}
{...props}
/>
)
);
}
const tabsListVariants = cva(
"group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-horizontal/tabs:h-8 group-data-vertical/tabs:h-fit group-data-vertical/tabs:flex-col data-[variant=line]:rounded-none",
'group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-horizontal/tabs:h-8 group-data-vertical/tabs:h-fit group-data-vertical/tabs:flex-col data-[variant=line]:rounded-none',
{
variants: {
variant: {
default: "bg-muted",
line: "gap-1 bg-transparent",
default: 'bg-muted',
line: 'gap-1 bg-transparent',
},
},
defaultVariants: {
variant: "default",
variant: 'default',
},
}
)
},
);
function TabsList({
className,
variant = "default",
variant = 'default',
...props
}: React.ComponentProps<typeof TabsPrimitive.List> &
VariantProps<typeof tabsListVariants>) {
@@ -52,7 +52,7 @@ function TabsList({
className={cn(tabsListVariants({ variant }), className)}
{...props}
/>
)
);
}
function TabsTrigger({
@@ -64,14 +64,14 @@ function TabsTrigger({
data-slot="tabs-trigger"
className={cn(
"relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-1.5 py-0.5 text-sm font-medium whitespace-nowrap text-foreground/60 transition-all group-data-vertical/tabs:w-full group-data-vertical/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 has-data-[icon=inline-end]:pr-1 has-data-[icon=inline-start]:pl-1 dark:text-muted-foreground dark:hover:text-foreground group-data-[variant=default]/tabs-list:data-active:shadow-sm group-data-[variant=line]/tabs-list:data-active:shadow-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
"group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent",
"data-active:bg-background data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 dark:data-active:text-foreground",
"after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-horizontal/tabs:after:inset-x-0 group-data-horizontal/tabs:after:bottom-[-5px] group-data-horizontal/tabs:after:h-0.5 group-data-vertical/tabs:after:inset-y-0 group-data-vertical/tabs:after:-right-1 group-data-vertical/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100",
className
'group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent',
'data-active:bg-background data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 dark:data-active:text-foreground',
'after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-horizontal/tabs:after:inset-x-0 group-data-horizontal/tabs:after:bottom-[-5px] group-data-horizontal/tabs:after:h-0.5 group-data-vertical/tabs:after:inset-y-0 group-data-vertical/tabs:after:-right-1 group-data-vertical/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100',
className,
)}
{...props}
/>
)
);
}
function TabsContent({
@@ -81,10 +81,10 @@ function TabsContent({
return (
<TabsPrimitive.Content
data-slot="tabs-content"
className={cn("flex-1 text-sm outline-none", className)}
className={cn('flex-1 text-sm outline-none', className)}
{...props}
/>
)
);
}
export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants }
export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants };
+25 -25
View File
@@ -1,34 +1,34 @@
import * as React from "react"
import { type VariantProps } from "class-variance-authority"
import { ToggleGroup as ToggleGroupPrimitive } from "radix-ui"
import * as React from 'react';
import { type VariantProps } from 'class-variance-authority';
import { ToggleGroup as ToggleGroupPrimitive } from 'radix-ui';
import { cn } from "@/lib/utils"
import { toggleVariants } from "@/components/ui/toggle"
import { cn } from '@/lib/utils';
import { toggleVariants } from '@/components/ui/toggle';
const ToggleGroupContext = React.createContext<
VariantProps<typeof toggleVariants> & {
spacing?: number
orientation?: "horizontal" | "vertical"
spacing?: number;
orientation?: 'horizontal' | 'vertical';
}
>({
size: "default",
variant: "default",
size: 'default',
variant: 'default',
spacing: 0,
orientation: "horizontal",
})
orientation: 'horizontal',
});
function ToggleGroup({
className,
variant,
size,
spacing = 0,
orientation = "horizontal",
orientation = 'horizontal',
children,
...props
}: React.ComponentProps<typeof ToggleGroupPrimitive.Root> &
VariantProps<typeof toggleVariants> & {
spacing?: number
orientation?: "horizontal" | "vertical"
spacing?: number;
orientation?: 'horizontal' | 'vertical';
}) {
return (
<ToggleGroupPrimitive.Root
@@ -37,10 +37,10 @@ function ToggleGroup({
data-size={size}
data-spacing={spacing}
data-orientation={orientation}
style={{ "--gap": spacing } as React.CSSProperties}
style={{ '--gap': spacing } as React.CSSProperties}
className={cn(
"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] rounded-lg data-[size=sm]:rounded-[min(var(--radius-md),10px)] data-vertical:flex-col data-vertical:items-stretch",
className
'group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] rounded-lg data-[size=sm]:rounded-[min(var(--radius-md),10px)] data-vertical:flex-col data-vertical:items-stretch',
className,
)}
{...props}
>
@@ -50,18 +50,18 @@ function ToggleGroup({
{children}
</ToggleGroupContext.Provider>
</ToggleGroupPrimitive.Root>
)
);
}
function ToggleGroupItem({
className,
children,
variant = "default",
size = "default",
variant = 'default',
size = 'default',
...props
}: React.ComponentProps<typeof ToggleGroupPrimitive.Item> &
VariantProps<typeof toggleVariants>) {
const context = React.useContext(ToggleGroupContext)
const context = React.useContext(ToggleGroupContext);
return (
<ToggleGroupPrimitive.Item
@@ -70,18 +70,18 @@ function ToggleGroupItem({
data-size={context.size || size}
data-spacing={context.spacing}
className={cn(
"shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 focus:z-10 focus-visible:z-10 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-lg group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-lg group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-lg group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-lg group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t",
'shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 focus:z-10 focus-visible:z-10 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-lg group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-lg group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-lg group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-lg group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t',
toggleVariants({
variant: context.variant || variant,
size: context.size || size,
}),
className
className,
)}
{...props}
>
{children}
</ToggleGroupPrimitive.Item>
)
);
}
export { ToggleGroup, ToggleGroupItem }
export { ToggleGroup, ToggleGroupItem };
+17 -17
View File
@@ -1,34 +1,34 @@
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { Toggle as TogglePrimitive } from "radix-ui"
import * as React from 'react';
import { cva, type VariantProps } from 'class-variance-authority';
import { Toggle as TogglePrimitive } from 'radix-ui';
import { cn } from "@/lib/utils"
import { cn } from '@/lib/utils';
const toggleVariants = cva(
"group/toggle inline-flex items-center justify-center gap-1 rounded-lg text-sm font-medium whitespace-nowrap transition-all outline-none hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-pressed:bg-muted data-[state=on]:bg-muted dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
{
variants: {
variant: {
default: "bg-transparent",
outline: "border border-input bg-transparent hover:bg-muted",
default: 'bg-transparent',
outline: 'border border-input bg-transparent hover:bg-muted',
},
size: {
default: "h-8 min-w-8 px-2",
sm: "h-7 min-w-7 rounded-[min(var(--radius-md),12px)] px-1.5 text-[0.8rem]",
lg: "h-9 min-w-9 px-2.5",
default: 'h-8 min-w-8 px-2',
sm: 'h-7 min-w-7 rounded-[min(var(--radius-md),12px)] px-1.5 text-[0.8rem]',
lg: 'h-9 min-w-9 px-2.5',
},
},
defaultVariants: {
variant: "default",
size: "default",
variant: 'default',
size: 'default',
},
}
)
},
);
function Toggle({
className,
variant = "default",
size = "default",
variant = 'default',
size = 'default',
...props
}: React.ComponentProps<typeof TogglePrimitive.Root> &
VariantProps<typeof toggleVariants>) {
@@ -38,7 +38,7 @@ function Toggle({
className={cn(toggleVariants({ variant, size, className }))}
{...props}
/>
)
);
}
export { Toggle, toggleVariants }
export { Toggle, toggleVariants };
+10 -10
View File
@@ -1,7 +1,7 @@
import * as React from "react"
import { Tooltip as TooltipPrimitive } from "radix-ui"
import * as React from 'react';
import { Tooltip as TooltipPrimitive } from 'radix-ui';
import { cn } from "@/lib/utils"
import { cn } from '@/lib/utils';
function TooltipProvider({
delayDuration = 0,
@@ -13,19 +13,19 @@ function TooltipProvider({
delayDuration={delayDuration}
{...props}
/>
)
);
}
function Tooltip({
...props
}: React.ComponentProps<typeof TooltipPrimitive.Root>) {
return <TooltipPrimitive.Root data-slot="tooltip" {...props} />
return <TooltipPrimitive.Root data-slot="tooltip" {...props} />;
}
function TooltipTrigger({
...props
}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
}
function TooltipContent({
@@ -40,8 +40,8 @@ function TooltipContent({
data-slot="tooltip-content"
sideOffset={sideOffset}
className={cn(
"data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-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 rounded-md px-3 py-1.5 text-xs bg-foreground text-background z-50 w-fit max-w-xs origin-(--radix-tooltip-content-transform-origin)",
className
'data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-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 rounded-md px-3 py-1.5 text-xs bg-foreground text-background z-50 w-fit max-w-xs origin-(--radix-tooltip-content-transform-origin)',
className,
)}
{...props}
>
@@ -49,7 +49,7 @@ function TooltipContent({
<TooltipPrimitive.Arrow className="size-2.5 rotate-45 rounded-[2px] bg-foreground fill-foreground z-50 translate-y-[calc(-50%_-_2px)]" />
</TooltipPrimitive.Content>
</TooltipPrimitive.Portal>
)
);
}
export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger }
export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };
+58 -74
View File
@@ -1,147 +1,131 @@
import { cn } from "@/lib/utils"
import { cn } from '@/lib/utils';
function H1({ className, ...props }: React.ComponentProps<"h1">) {
function H1({ className, ...props }: React.ComponentProps<'h1'>) {
return (
<h1
className={cn(
"scroll-m-20 text-4xl font-extrabold tracking-tight text-balance",
className
'scroll-m-20 text-4xl font-extrabold tracking-tight text-balance',
className,
)}
{...props}
/>
)
);
}
function H2({ className, ...props }: React.ComponentProps<"h2">) {
function H2({ className, ...props }: React.ComponentProps<'h2'>) {
return (
<h2
className={cn(
"scroll-m-20 border-b pb-2 text-3xl font-semibold tracking-tight first:mt-0",
className
'scroll-m-20 border-b pb-2 text-3xl font-semibold tracking-tight first:mt-0',
className,
)}
{...props}
/>
)
);
}
function H3({ className, ...props }: React.ComponentProps<"h3">) {
function H3({ className, ...props }: React.ComponentProps<'h3'>) {
return (
<h3
className={cn(
"scroll-m-20 text-2xl font-semibold tracking-tight",
className
'scroll-m-20 text-2xl font-semibold tracking-tight',
className,
)}
{...props}
/>
)
);
}
function H4({ className, ...props }: React.ComponentProps<"h4">) {
function H4({ className, ...props }: React.ComponentProps<'h4'>) {
return (
<h4
className={cn(
"scroll-m-20 text-xl font-semibold tracking-tight",
className
'scroll-m-20 text-xl font-semibold tracking-tight',
className,
)}
{...props}
/>
)
);
}
function P({ className, ...props }: React.ComponentProps<"p">) {
function P({ className, ...props }: React.ComponentProps<'p'>) {
return (
<p
className={cn(
"leading-7 [&:not(:first-child)]:mt-6",
className
)}
className={cn('leading-7 [&:not(:first-child)]:mt-6', className)}
{...props}
/>
)
);
}
function Blockquote({ className, ...props }: React.ComponentProps<"blockquote">) {
function Blockquote({
className,
...props
}: React.ComponentProps<'blockquote'>) {
return (
<blockquote
className={cn(
"mt-6 border-l-2 pl-6 italic",
className
)}
className={cn('mt-6 border-l-2 pl-6 italic', className)}
{...props}
/>
)
);
}
function List({ className, ...props }: React.ComponentProps<"ul">) {
function List({ className, ...props }: React.ComponentProps<'ul'>) {
return (
<ul
className={cn(
"my-6 ml-6 list-disc [&>li]:mt-2",
className
)}
className={cn('my-6 ml-6 list-disc [&>li]:mt-2', className)}
{...props}
/>
)
);
}
function InlineCode({ className, ...props }: React.ComponentProps<"code">) {
function InlineCode({ className, ...props }: React.ComponentProps<'code'>) {
return (
<code
className={cn(
"bg-muted relative rounded px-[0.3rem] py-[0.2rem] font-mono text-sm font-semibold",
className
'bg-muted relative rounded px-[0.3rem] py-[0.2rem] font-mono text-sm font-semibold',
className,
)}
{...props}
/>
)
);
}
function Lead({ className, ...props }: React.ComponentProps<"p">) {
function Lead({ className, ...props }: React.ComponentProps<'p'>) {
return (
<p
className={cn(
"text-muted-foreground text-xl",
className
)}
{...props}
/>
)
<p className={cn('text-muted-foreground text-xl', className)} {...props} />
);
}
function Large({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
className={cn(
"text-lg font-semibold",
className
)}
{...props}
/>
)
function Large({ className, ...props }: React.ComponentProps<'div'>) {
return <div className={cn('text-lg font-semibold', className)} {...props} />;
}
function Small({ className, ...props }: React.ComponentProps<"small">) {
function Small({ className, ...props }: React.ComponentProps<'small'>) {
return (
<small
className={cn(
"text-sm leading-none font-medium",
className
)}
className={cn('text-sm leading-none font-medium', className)}
{...props}
/>
)
);
}
function Muted({ className, ...props }: React.ComponentProps<"p">) {
function Muted({ className, ...props }: React.ComponentProps<'p'>) {
return (
<p
className={cn(
"text-muted-foreground text-sm",
className
)}
{...props}
/>
)
<p className={cn('text-muted-foreground text-sm', className)} {...props} />
);
}
export { H1, H2, H3, H4, P, Blockquote, List, InlineCode, Lead, Large, Small, Muted }
export {
H1,
H2,
H3,
H4,
P,
Blockquote,
List,
InlineCode,
Lead,
Large,
Small,
Muted,
};
@@ -1,5 +1,5 @@
import { Video, Mic } from "lucide-react";
import { useMediaSettingsStore } from "@/stores/media-settings-store";
import { Video, Mic } from 'lucide-react';
import { useMediaSettingsStore } from '@/stores/media-settings-store';
export function VideoAudioToggle() {
const recordingMode = useMediaSettingsStore((s) => s.recordingMode);
@@ -9,17 +9,19 @@ export function VideoAudioToggle() {
<span
role="button"
onClick={() =>
setRecordingMode(recordingMode === "video" ? "audio" : "video")
setRecordingMode(recordingMode === 'video' ? 'audio' : 'video')
}
title={
recordingMode === "video" ? "Switch to audio-only (V)" : "Switch to video (V)"
recordingMode === 'video'
? 'Switch to audio-only (V)'
: 'Switch to video (V)'
}
className="cursor-pointer transition-colors hover:text-white/80"
>
<kbd className="rounded bg-white/10 px-1.5 py-0.5 font-mono text-xs">
V
</kbd>{" "}
{recordingMode === "video" ? (
</kbd>{' '}
{recordingMode === 'video' ? (
<>
<Video className="inline size-3" /> video
</>
+8 -14
View File
@@ -1,9 +1,9 @@
import { useEffect, useState } from "react";
import { Copy, Minus, Square, X } from "lucide-react";
import { useEffect, useState } from 'react';
import { Copy, Minus, Square, X } from 'lucide-react';
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import { platform } from "@/lib/platform";
import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';
import { platform } from '@/lib/platform';
export function WindowControls() {
const [isMaximized, setIsMaximized] = useState(false);
@@ -12,7 +12,7 @@ export function WindowControls() {
return platform.window.onMaximizeChange(setIsMaximized);
}, []);
if (platform.kind !== "electron") return null;
if (platform.kind !== 'electron') return null;
const minimize = (
<Button
@@ -33,7 +33,7 @@ export function WindowControls() {
variant="ghost"
size="icon-sm"
onClick={() => platform.window.maximize()}
aria-label={isMaximized ? "Restore" : "Maximize"}
aria-label={isMaximized ? 'Restore' : 'Maximize'}
className="dark:hover:bg-white/10 rounded text-white/50"
>
{isMaximized ? <Copy /> : <Square />}
@@ -56,12 +56,6 @@ export function WindowControls() {
const buttons = [close, maximize, minimize];
return (
<div
className={cn(
"no-drag flex items-center gap-0.5"
)}
>
{buttons}
</div>
<div className={cn('no-drag flex items-center gap-0.5')}>{buttons}</div>
);
}