Status Dot
A 10px status dot for queued, building, error, ready, and canceled.
Preview
Loading...
import { StatusDot } from "@/components/ui/StatusDot";
export function StatusDotDemo() {
return (
<div className="flex flex-col gap-2">
<div className="flex items-center gap-2">
<StatusDot state="queued" />
<StatusDot state="building" />
<StatusDot state="error" />
<StatusDot state="ready" />
<StatusDot state="canceled" />
</div>
<div className="flex flex-wrap items-center gap-2">
<StatusDot state="queued" label />
<StatusDot state="building" label />
<StatusDot state="error" label />
<StatusDot state="ready" label />
<StatusDot state="canceled" label />
</div>
</div>
)
}Installation
Make sure that namespace is set in your component.json file. Namespace docs: Learn more about namespaces
pnpm dlx shadcn@latest add @aura/status-dotManual
Install the following dependencies:
pnpm install class-variance-authorityCopy and paste the class names utility into your utils/class-names.ts file.
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
Copy and paste the StatusDot component into your components/ui/StatusDot.tsx file.
/**
* @description A 10px status dot for queued, building, error, ready, and canceled.
*/
import * as React from "react";
import { cva } from "class-variance-authority";
import { cn } from "@/utils/class-names";
const STATES = {
queued: { label: "Queued", message: "Queued." },
building: { label: "Building", message: "In progress." },
error: { label: "Error", message: "Failed." },
ready: { label: "Ready", message: "Ready." },
canceled: { label: "Canceled", message: "Canceled." },
} as const;
type StatusDotState = keyof typeof STATES;
const dot = cva("inline-block size-[10px] shrink-0 rounded-full", {
variants: {
state: {
queued: "bg-status-dot-queued",
building: "bg-status-dot-building",
error: "bg-status-dot-error",
ready: "bg-status-dot-ready",
canceled: "bg-status-dot-canceled",
},
ring: { true: "ring-2 ring-status-dot-ring", false: "" },
},
defaultVariants: { state: "queued", ring: false },
});
interface StatusDotProps extends React.ComponentProps<"span"> {
state?: StatusDotState;
/** `true` uses the state name. A string replaces it. */
label?: boolean | string;
/** Light ring for overlaying the dot on an avatar. */
ring?: boolean;
}
function StatusDot({
state = "queued",
label,
ring,
className,
...props
}: StatusDotProps) {
const s = STATES[state];
return (
<span
data-slot="status-dot"
data-state={state}
role="status"
aria-label={s.label}
title={s.message}
className={cn("inline-flex items-center gap-[8px]", className)}
{...props}
>
<span
data-slot="status-dot-indicator"
aria-hidden
className={dot({ state, ring })}
/>
{label ? (
<span className="text-sm font-medium text-gray-12">
{label === true ? s.label : label}
</span>
) : null}
</span>
);
}
export { StatusDot, type StatusDotState };
Usage
WithRing
export const WithRing = () => (
<div className="flex items-center gap-2 rounded-md bg-gray-12 p-2">
<StatusDot state="ready" ring />
<StatusDot state="building" ring />
<StatusDot state="error" ring />
<StatusDot state="queued" ring />
<StatusDot state="canceled" ring />
</div>
)CustomLabel
export const CustomLabel = () => (
<StatusDot state="building" label="Deploying" />
)