hitbox

PreviousNext
Docs
diceuicomponent

Preview

Loading preview…
components/hitbox.tsx
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
import type * as React from "react";

import { cn } from "@/lib/utils";

type Size = "default" | "sm" | "lg";
type DynamicSize = Size | (string & {});

const sizes: DynamicSize[] = ["default", "sm", "lg"];

const hitboxVariants = cva(
  "relative [--size-default:12px] [--size-lg:16px] [--size-sm:8px] after:absolute after:content-['']",
  {
    variants: {
      size: {
        default: "[--size:var(--size-default)]",
        sm: "[--size:var(--size-sm)]",
        lg: "[--size:var(--size-lg)]",
        dynamic: "[--size:var(--size)]",
      },
      position: {
        all: "after:[inset:calc(-1*var(--size))]",
        top: "after:[height:var(--size)] after:[left:0] after:[right:0] after:[top:calc(-1*var(--size))]",
        bottom:
          "after:[bottom:calc(-1*var(--size))] after:[height:var(--size)] after:[left:0] after:[right:0]",
        left: "after:[bottom:0] after:[left:calc(-1*var(--size))] after:[top:0] after:[width:var(--size)]",
        right:
          "after:[bottom:0] after:[right:calc(-1*var(--size))] after:[top:0] after:[width:var(--size)]",
        vertical:
          "after:[bottom:calc(-1*var(--size))] after:[left:0] after:[right:0] after:[top:calc(-1*var(--size))]",
        horizontal:
          "after:[bottom:0] after:[left:calc(-1*var(--size))] after:[right:calc(-1*var(--size))] after:[top:0]",
      },
      radius: {
        none: "",
        sm: "after:rounded-sm",
        md: "after:rounded-md",
        lg: "after:rounded-lg",
        full: "after:rounded-full",
      },
      debug: {
        true: "after:border after:border-red-500 after:border-dashed after:bg-red-500/20",
        false: "",
      },
    },
    defaultVariants: {
      size: "default",
      position: "all",
      radius: "none",
      debug: false,
    },
  },
);

interface HitboxProps
  extends React.ComponentProps<typeof Slot>,
    Omit<VariantProps<typeof hitboxVariants>, "size"> {
  size?: DynamicSize;
}

function Hitbox(props: HitboxProps) {
  const {
    className,
    style,
    size,
    position,
    radius,
    debug = false,
    ...hitboxProps
  } = props;

  const isDynamicSize = size && !sizes.includes(size);

  return (
    <Slot
      {...hitboxProps}
      className={cn(
        hitboxVariants({
          size: isDynamicSize ? "dynamic" : (size as Size),
          position,
          radius,
          debug,
        }),
        className,
      )}
      style={{
        ...(isDynamicSize && { "--size": size }),
        ...style,
      }}
    />
  );
}

export { Hitbox };

Installation

npx shadcn@latest add @diceui/hitbox

Usage

import { Hitbox } from "@/components/hitbox"
<Hitbox />