Selection Presence

Dashed-border overlay marking what another user has selected on the canvas. Install Selection Presence from the VLLNT UI registry with the shadcn CLI.

Report a bug

Dashed-border overlay marking what another user has selected on the canvas. Part of the utility family in VLLNT UI, it ships as a machine-readable registry entry — copy the source directly into your app with the shadcn CLI and own it, no runtime dependency on a component library.

Preview

Switch between light and dark to inspect the embedded Storybook preview.

Installation

Add Selection Presence to your project with the shadcn CLI. The source lands in your codebase, ready to adapt:

pnpm dlx shadcn@latest add https://ui.vllnt.ai/r/selection-presence.json

Source

"use client"; import type { ComponentPropsWithoutRef, ReactNode } from "react"; import { cn } from "../../lib/utils"; /** * Localizable strings. * * @public */ export type SelectionPresenceLabels = { /** Aria-label override. Defaults to `"Selection presence"`. */ region?: string; }; const DEFAULT_LABELS = { region: "Selection presence", } as const satisfies Required<SelectionPresenceLabels>; /** * Props for {@link SelectionPresence}. * * @public */ export type SelectionPresenceProps = { /** Tailwind / CSS color used for the border + chip. Defaults to `var(--foreground)`. */ color?: string; /** Selection rectangle height in pixels. */ height: number; /** Localizable strings. */ labels?: SelectionPresenceLabels; /** Optional name shown in the corner chip (e.g. owner of the selection). */ name?: ReactNode; /** Selection rectangle width in pixels. */ width: number; /** Selection rectangle X (top-left) in canvas pixels. */ x: number; /** Selection rectangle Y (top-left) in canvas pixels. */ y: number; } & ComponentPropsWithoutRef<"div">; /** * Overlay marking what another user has selected on the canvas. The * dashed border + soft fill stay calm so they communicate * "someone-else's-selection" without blocking primary content. Pure * presentation; the host computes the rect from the remote user's * viewport and supplies an accent color per user. * * The wrapper is `pointer-events: none` so host gestures pass through. * * @example * ```tsx * <div className="relative h-screen w-screen"> * <Canvas /> * <SelectionPresence * x={120} y={80} width={240} height={120} * color="#5b8def" * name="Bea" * /> * </div> * ``` * * @public */ export const SelectionPresence = ({ ref, ...props }: SelectionPresenceProps & { ref?: React.Ref<HTMLDivElement> }) => { const { className, color, height, labels, name, width, x, y, ...rest } = props; const resolvedLabels = { ...DEFAULT_LABELS, ...labels }; const accent = color ?? "var(--foreground)"; const ariaLabel = typeof name === "string" ? `${resolvedLabels.region}: ${name}` : resolvedLabels.region; return ( <div aria-label={ariaLabel} className={cn( "pointer-events-none absolute z-20 rounded-md border-2 border-dashed", className, )} data-selection-presence ref={ref} role="img" style={{ backgroundColor: `color-mix(in srgb, ${accent} 10%, transparent)`, borderColor: accent, height, left: x, top: y, width, }} {...rest} > {name === null || name === undefined ? null : ( <span className="absolute -top-5 left-0 inline-flex items-center rounded-md px-1.5 py-0.5 text-[10px] font-medium text-white shadow-sm" data-selection-presence-chip style={{ backgroundColor: accent }} > {name} </span> )} </div> ); }; SelectionPresence.displayName = "SelectionPresence";

Stories

Explore every variant and state in the interactive Storybook:

Preview

Switch between light and dark to inspect the embedded Storybook preview.

Dependencies

  • @vllnt/ui@^0.3.0