{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "multi-select-lasso",
  "type": "registry:component",
  "title": "Multi Select Lasso",
  "description": "Selection rectangle overlay for canvas multi-select gestures.",
  "dependencies": [
    "@vllnt/ui@^0.2.1"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/multi-select-lasso/multi-select-lasso.tsx",
      "content": "\"use client\";\n\nimport {\n  type ComponentPropsWithoutRef,\n  forwardRef,\n  type ReactNode,\n} from \"react\";\n\nimport { cn } from \"@vllnt/ui\";\n\n/**\n * Lasso geometry — pixel coordinates on the underlying canvas.\n *\n * @public\n */\nexport type LassoRect = {\n  /** Height in pixels (always positive after normalization). */\n  height: number;\n  /** Width in pixels (always positive after normalization). */\n  width: number;\n  /** Left edge in pixels. */\n  x: number;\n  /** Top edge in pixels. */\n  y: number;\n};\n\n/**\n * Localizable strings.\n *\n * @public\n */\nexport type MultiSelectLassoLabels = {\n  /** Plural noun for the count badge. Defaults to `\"items\"`. */\n  plural?: string;\n  /** Aria-label for the lasso layer. Defaults to `\"Multi-select lasso\"`. */\n  region?: string;\n  /** Singular noun for the count badge. Defaults to `\"item\"`. */\n  singular?: string;\n};\n\nconst DEFAULT_LABELS = {\n  plural: \"items\",\n  region: \"Multi-select lasso\",\n  singular: \"item\",\n} as const satisfies Required<MultiSelectLassoLabels>;\n\n/**\n * Props for {@link MultiSelectLasso}.\n *\n * @public\n */\nexport type MultiSelectLassoProps = {\n  /** Optional count badge — rendered near the bottom-right corner. */\n  count?: number;\n  /** Optional override slot rendered inside the lasso (e.g. a label). */\n  hint?: ReactNode;\n  /** Localizable strings. */\n  labels?: MultiSelectLassoLabels;\n  /** Drag rectangle in pixels. When `null`, nothing renders. */\n  rect: LassoRect | null;\n} & ComponentPropsWithoutRef<\"div\">;\n\nconst normalizeRect = (rect: LassoRect): LassoRect => {\n  const x = rect.width < 0 ? rect.x + rect.width : rect.x;\n  const y = rect.height < 0 ? rect.y + rect.height : rect.y;\n  const width = Math.abs(rect.width);\n  const height = Math.abs(rect.height);\n  return { height, width, x, y };\n};\n\n/**\n * Selection rectangle for canvas multi-select. Render as a sibling of\n * the canvas with `position: absolute` parent so the `rect` coordinates\n * land in the same pixel space. Pure presentation; the host owns the\n * pointer-down/move/up lifecycle and produces the rect (or `null` to\n * hide the lasso).\n *\n * @example\n * ```tsx\n * <div className=\"relative h-screen w-screen\" onPointerDown={…}>\n *   <Canvas />\n *   <MultiSelectLasso rect={lassoRect} count={hoveredIds.length} />\n * </div>\n * ```\n *\n * @public\n */\nexport const MultiSelectLasso = forwardRef<\n  HTMLDivElement,\n  MultiSelectLassoProps\n>((props, ref) => {\n  const { className, count, hint, labels, rect, ...rest } = props;\n  if (!rect) {\n    return null;\n  }\n  const resolvedLabels = { ...DEFAULT_LABELS, ...labels };\n  const normalized = normalizeRect(rect);\n  if (normalized.width === 0 || normalized.height === 0) {\n    return null;\n  }\n  const noun = count === 1 ? resolvedLabels.singular : resolvedLabels.plural;\n  return (\n    <div\n      aria-hidden=\"true\"\n      aria-label={resolvedLabels.region}\n      className={cn(\n        \"pointer-events-none absolute z-30 rounded-md border-2 border-blue-500/70 bg-blue-500/10\",\n        className,\n      )}\n      data-multi-select-lasso\n      ref={ref}\n      style={{\n        height: normalized.height,\n        left: normalized.x,\n        top: normalized.y,\n        width: normalized.width,\n      }}\n      {...rest}\n    >\n      {hint ? (\n        <span\n          className=\"absolute -top-7 left-0 rounded-md bg-foreground/90 px-2 py-0.5 text-[10px] font-medium uppercase tracking-wide text-background\"\n          data-multi-select-hint\n        >\n          {hint}\n        </span>\n      ) : null}\n      {typeof count === \"number\" ? (\n        <span\n          className=\"absolute -bottom-7 right-0 rounded-full bg-foreground px-2 py-0.5 text-[10px] font-semibold text-background\"\n          data-multi-select-count\n        >\n          {count} {noun}\n        </span>\n      ) : null}\n    </div>\n  );\n});\nMultiSelectLasso.displayName = \"MultiSelectLasso\";\n",
      "type": "registry:component"
    }
  ],
  "version": "0.2.1",
  "stability": "stable"
}
