Detail Panel
ilinxa/detail-panelFreeBlock
Selection-aware detail container with read and edit modes, lifecycle states, sticky header and footer actions, and a slot-based body.
Install it
npx shadcn@latest add https://ui.ilinxa.com/r/detail-panel.jsonSource
1"use client";23import {4 useCallback,5 useEffect,6 useImperativeHandle,7 useMemo,8 useRef,9} from "react";10import type {11 DetailPanelContextValue,12 DetailPanelHandle,13 DetailPanelProps,14} from "./types";15import { DEFAULT_DETAIL_PANEL_LABELS } from "./types";16import { DetailPanelContext } from "./parts/detail-panel-context";17import { DetailPanelHeader } from "./parts/detail-panel-header";18import { DetailPanelBody } from "./parts/detail-panel-body";19import { DetailPanelActions } from "./parts/detail-panel-actions";20import { DetailPanelEmptyState } from "./parts/detail-panel-empty-state";21import { DetailPanelSkeleton } from "./parts/detail-panel-skeleton";22import { DetailPanelError } from "./parts/detail-panel-error";23import { SelectionAnnouncer } from "./parts/selection-announcer";24import { useDetailPanelMode } from "./hooks/use-detail-panel-mode";25import { useFocusRestore } from "./hooks/use-focus-restore";26import { keyForSelection } from "./lib/selection-key";27import { cn } from "@/lib/utils";2829const FOCUSABLE =30 'input, textarea, select, button, [tabindex]:not([tabindex="-1"]), [contenteditable="true"]';3132function DetailPanelImpl({33 selection,34 mode: controlledMode,35 onModeChange,36 canEdit = true,37 loading = false,38 error = null,39 emptyState,40 children,41 ariaLabel,42 labels,43 className,44 ref,45}: DetailPanelProps) {46 const resolvedAriaLabel =47 ariaLabel ?? labels?.region ?? DEFAULT_DETAIL_PANEL_LABELS.region;48 const rootRef = useRef<HTMLDivElement>(null);49 const bodyRef = useRef<HTMLDivElement>(null);5051 const selectionKey = useMemo(() => keyForSelection(selection), [selection]);5253 const { mode, setMode } = useDetailPanelMode({54 controlledMode,55 onModeChange,56 selectionKey,57 });5859 const { captureTrigger } = useFocusRestore({ mode, rootRef, bodyRef });6061 const wrappedSetMode = useCallback(62 (next: typeof mode) => {63 captureTrigger();64 setMode(next);65 },66 [captureTrigger, setMode],67 );6869 const hasError = !!error;70 const hasSelection = selection !== null;7172 const lastSelectionKeyRef = useRef(selectionKey);73 useEffect(() => {74 if (lastSelectionKeyRef.current === selectionKey) return;75 lastSelectionKeyRef.current = selectionKey;76 rootRef.current?.focus({ preventScroll: true });77 }, [selectionKey]);7879 const focusBody = useCallback(() => {80 const body = bodyRef.current;81 if (!body) return;82 const focusable = body.querySelector<HTMLElement>(FOCUSABLE);83// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from ilinxa
All 184 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Account Switcheraccount-switcher | ilinxa | Blocks | Free | 1 dep · 10 files | |
| Account Switcher (deprecated alias)account-switcher-01 | ilinxa | Blocks | Free | no deps | |
| Account Switcher — fixturesaccount-switcher-fixtures | ilinxa | Blocks | Free | no deps | |
| App Sidebarapp-sidebar | ilinxa | Blocks | Free | 1 dep · 37 files | |
| App Sidebar — fixturesapp-sidebar-fixtures | ilinxa | Blocks | Free | no deps | |
| Rich Text Editor (deprecated alias)article-body-01 | ilinxa | Blocks | Free | no deps | |
| Article Metaarticle-meta | ilinxa | Blocks | Free | 1 dep · 4 files | |
| Article Meta (deprecated alias)article-meta-01 | ilinxa | Blocks | Free | no deps |
