Use shadcn Alert for the "no devices connected" hints
Replaces the ad-hoc bp-glass Card used for the "No devices connected yet." placeholder in ButtplugConsole and ReplayPlayer with the shadcn Alert component (newly installed). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -22,6 +22,8 @@ import { DeviceCard } from "./DeviceCard";
|
||||
import { RecordControls } from "./RecordControls";
|
||||
import { NameSessionDialog } from "./NameSessionDialog";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||
import { Info } from "lucide-react";
|
||||
|
||||
interface ActiveSession {
|
||||
id: number;
|
||||
@@ -240,12 +242,13 @@ export function ButtplugConsole() {
|
||||
{storeError && <p className="text-sm text-destructive">{storeError}</p>}
|
||||
|
||||
{Object.keys(devices).length === 0 ? (
|
||||
<Card className="bp-glass">
|
||||
<CardContent className="text-sm text-muted-foreground">
|
||||
<Alert className="bp-glass">
|
||||
<Info />
|
||||
<AlertDescription>
|
||||
No devices connected yet. Scan to discover nearby toys, then select one from your browser's
|
||||
pairing prompt.
|
||||
</CardContent>
|
||||
</Card>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
) : (
|
||||
<div className="grid gap-4 sm:grid-cols-2">
|
||||
{Object.values(devices).map((device) => (
|
||||
|
||||
@@ -11,7 +11,8 @@ import { disconnectAll, getButtplugClientHandle, getDevice, startScanning, stopS
|
||||
import { SessionPlayer, type SessionEventRow } from "@/lib/buttplug/player";
|
||||
import { useButtplugStore } from "@/lib/buttplug/store";
|
||||
import type { ActuatorInfo, ReplayDeviceSlot } from "@/lib/buttplug/types";
|
||||
import { Pause, Play } from "lucide-react";
|
||||
import { Info, Pause, Play } from "lucide-react";
|
||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||
|
||||
interface ReplayDataResponse {
|
||||
session: {
|
||||
@@ -163,12 +164,13 @@ export function ReplayPlayer({ sessionId }: { sessionId: number }) {
|
||||
</div>
|
||||
|
||||
{connectedDevices.length === 0 && (
|
||||
<Card className="bp-glass">
|
||||
<CardContent className="text-sm text-muted-foreground">
|
||||
<Alert className="bp-glass">
|
||||
<Info />
|
||||
<AlertDescription>
|
||||
No devices connected yet. Scan to discover nearby toys, then match them to this session's
|
||||
original device slots.
|
||||
</CardContent>
|
||||
</Card>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
import * as React from "react"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const alertVariants = cva(
|
||||
"group/alert relative grid w-full gap-0.5 rounded-lg border px-2.5 py-2 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-card text-card-foreground",
|
||||
destructive:
|
||||
"bg-card text-destructive *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-current",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
function Alert({
|
||||
className,
|
||||
variant,
|
||||
...props
|
||||
}: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="alert"
|
||||
role="alert"
|
||||
className={cn(alertVariants({ variant }), className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="alert-title"
|
||||
className={cn(
|
||||
"font-medium group-has-[>svg]/alert:col-start-2 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AlertDescription({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="alert-description"
|
||||
className={cn(
|
||||
"text-sm text-balance text-muted-foreground md:text-pretty [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AlertAction({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="alert-action"
|
||||
className={cn("absolute top-2 right-2", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Alert, AlertTitle, AlertDescription, AlertAction }
|
||||
Reference in New Issue
Block a user