"use client"; import { forwardRef, useCallback } from "react"; import { useSelectionContext } from "@/selection/context"; import { SELECTABLE_ITEM_ATTRIBUTE } from "@/selection/attributes"; import type { SelectableItemProps } from "@/selection/types"; import { cn } from "@/utils/ui"; function setForwardedRef(ref: React.ForwardedRef, value: T | null) { if (typeof ref === "function") { ref(value); return; } if (ref) { ref.current = value; } } export const SelectableItem = forwardRef( function SelectableItem( { id, children, className, onClick, onKeyDown, onMouseDown, onContextMenu, tabIndex, ...rest }: SelectableItemProps, forwardedRef, ) { const { highlightedId, isBoxSelecting, isSelected, handleItemClick, handleItemMouseDown, registerItem, } = useSelectionContext(); const isItemSelected = isSelected(id); const isItemHighlighted = highlightedId === id; const stateClassName = cn( "relative", isBoxSelecting && "pointer-events-none", isItemSelected && "ring-1 ring-primary rounded-sm bg-primary/10", isItemHighlighted && (isItemSelected ? "rounded-sm shadow-[0_0_0_1px_hsl(var(--primary))]" : "ring-1 ring-primary/60 rounded-sm bg-primary/5"), ); const handleRef = useCallback( (element: HTMLDivElement | null) => { registerItem(id, element); setForwardedRef(forwardedRef, element); }, [forwardedRef, id, registerItem], ); return (
{ onClick?.(event); if (event.defaultPrevented) { return; } handleItemClick({ event, id }); }} onMouseDown={(event) => { onMouseDown?.(event); if (event.defaultPrevented) { return; } handleItemMouseDown({ event, id }); }} onKeyDown={(event) => { onKeyDown?.(event); if (event.defaultPrevented || event.target !== event.currentTarget) { return; } if (event.key !== "Enter" && event.key !== " ") { return; } event.preventDefault(); handleItemClick({ event, id }); }} onContextMenu={onContextMenu} {...rest} > {children}
); }, );