Interactive item list
retab-ui/interactive-item-listFreeComponent
A virtualized listbox interaction primitive for preview, selection, keyboard navigation, and visible-item reporting.
Install it
npx shadcn@latest add https://ui.retab.com/r/interactive-item-list.jsonSource
1"use client";23import * as React from "react";45import { cn } from "@/lib/utils";6import { ScrollArea } from "@/components/ui/scroll-area";7import { useMeasuredRowVirtualization } from "./measured-row-virtualization";89export type InteractiveItemListItem = {10 id: string;11 disabled?: boolean;12};1314export type InteractiveItemListRenderState = {15 isActive: boolean;16 isDisabled: boolean;17 isSelected: boolean;18};1920const DEFAULT_ESTIMATE_SIZE = 72;21const ROW_PADDING = 12;2223function requestFrame(callback: () => void) {24 if (typeof window !== "undefined" && window.requestAnimationFrame) {25 window.requestAnimationFrame(callback);26 return;27 }28 setTimeout(callback, 0);29}3031export function InteractiveItemList<Item extends InteractiveItemListItem>({32 activeItemId,33 "aria-label": ariaLabel,34 className,35 emptyLabel = "No items.",36 estimateSize = DEFAULT_ESTIMATE_SIZE,37 items,38 onActivateItem,39 onClearPreview,40 onClearSelection,41 onPreviewItem,42 onVisibleItemChange,43 renderItem,44 selectedItemId,45}: {46 activeItemId?: string | null;47 "aria-label": string;48 className?: string;49 emptyLabel?: string;50 estimateSize?: number;51 items: readonly Item[];52 onActivateItem?: (item: Item) => void;53 onClearPreview?: () => void;54 onClearSelection?: () => void;55 onPreviewItem?: (item: Item) => void;56 onVisibleItemChange?: (item: Item) => void;57 renderItem: (58 item: Item,59 state: InteractiveItemListRenderState,60 ) => React.ReactNode;61 selectedItemId?: string | null;62}) {63 const viewportRef = React.useRef<HTMLDivElement | null>(null);64 const optionRefs = React.useRef(new Map<string, HTMLButtonElement>());65 const visibleItemIdRef = React.useRef<string | null>(null);66 const getItemKey = React.useCallback(67 (index: number) => items[index]?.id ?? index,68 [items],69 );70 const { measureRow, scrollToIndex, totalSize, virtualRows } =71 useMeasuredRowVirtualization({72 count: items.length,73 estimateSize,74 getItemKey,75 initialViewportHeight: 600,76 overscan: 8,77 paddingEnd: ROW_PADDING,78 paddingStart: ROW_PADDING,79 scrollRef: viewportRef,80 });8182 const firstEnabledIndex = React.useCallback(83 () => items.findIndex((item) => !item.disabled),84 [items],85 );8687 const lastEnabledIndex = React.useCallback(() => {88 for (let index = items.length - 1; index >= 0; index -= 1) {89 if (!items[index]?.disabled) return index;90 }91 return -1;92 }, [items]);9394// … truncated
What it pulls in
Other registry items
Files it writes
More from retab-ui
All 130 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Alertalert | retab-ui | Components | Free | 1 dep | |
| Alert Dialogalert-dialog | retab-ui | Components | Free | 1 dep | |
| Aspect Ratioaspect-ratio | retab-ui | Components | Free | no deps | |
| Attachment Sidebarattachment-sidebar | retab-ui | Components | Free | 1 dep | |
| Autocompleteautocomplete | retab-ui | Components | Free | 2 deps | |
| Calendarcalendar | retab-ui | Components | Free | 2 deps | |
| Code Viewercode-viewer | retab-ui | Components | Free | 2 deps · 32 files | |
| Commandcommand | retab-ui | Components | Free | 2 deps |
