{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "segment-interaction",
  "title": "Segment Interaction",
  "description": "React-free hover, focus, selection, and current-page helpers for Segment[] surfaces.",
  "registryDependencies": [
    "@retab/segments"
  ],
  "files": [
    {
      "path": "registry/new-york-v4/lib/segment-interaction.ts",
      "content": "import { type Segment } from \"@/lib/segments\";\n\nexport interface SegmentInteraction {\n  previewSegmentId: string | null;\n  previewSegment: (segmentId: string) => void;\n  clearPreview: () => void;\n}\n\nexport interface SegmentInteractionState {\n  currentPage: number | null;\n  currentSegmentId: string | null;\n  currentSegmentIds: readonly string[];\n  previewSegmentId: string | null;\n  highlightedSegmentId: string | null;\n  highlightedSegmentIds: readonly string[];\n}\n\nexport interface SegmentViewState {\n  isPreviewed: boolean;\n  isCurrent: boolean;\n  isHighlighted: boolean;\n  isDimmed: boolean;\n}\n\nexport interface SegmentSurfaceProps {\n  state: SegmentViewState;\n  eventHandlers: {\n    onPointerEnter: () => void;\n    onPointerLeave: () => void;\n    onClick: () => void;\n  };\n  dataProps: {\n    \"data-previewed\": boolean;\n    \"data-current\": boolean;\n    \"data-highlighted\": boolean;\n  };\n}\n\nexport function resolvePreviewedSegmentId(\n  interaction?: Partial<Pick<SegmentInteraction, \"previewSegmentId\">> | null,\n): string | null {\n  return interaction?.previewSegmentId ?? null;\n}\n\nexport function getSegmentInteractionState({\n  segments,\n  currentPage,\n  interaction,\n}: {\n  segments: Segment[];\n  currentPage?: number | null;\n  interaction?: SegmentInteraction | null;\n}): SegmentInteractionState {\n  const segmentIds = new Set(segments.map((segment) => segment.id));\n  const previewSegmentId = knownSegmentId(\n    resolvePreviewedSegmentId(interaction),\n    segmentIds,\n  );\n  const resolvedCurrentPage = normalizeCurrentPage(currentPage);\n  const currentSegmentIds =\n    resolvedCurrentPage == null\n      ? []\n      : resolveCurrentSegmentIds(segments, resolvedCurrentPage);\n  const highlightedSegmentIds =\n    previewSegmentId != null ? [previewSegmentId] : currentSegmentIds;\n\n  return {\n    currentPage: resolvedCurrentPage,\n    currentSegmentId: currentSegmentIds[0] ?? null,\n    currentSegmentIds,\n    previewSegmentId,\n    highlightedSegmentId: highlightedSegmentIds[0] ?? null,\n    highlightedSegmentIds,\n  };\n}\n\nexport function scopeSegmentInteraction(\n  interaction: SegmentInteraction | null | undefined,\n  segmentIds: Iterable<string>,\n): SegmentInteraction | null | undefined {\n  if (!interaction) return interaction;\n\n  const knownIds = new Set(segmentIds);\n  const previewSegmentId = knownSegmentId(\n    interaction.previewSegmentId,\n    knownIds,\n  );\n\n  if (previewSegmentId === interaction.previewSegmentId) {\n    return interaction;\n  }\n\n  return {\n    ...interaction,\n    previewSegmentId,\n  };\n}\n\nfunction knownSegmentId(\n  id: string | null,\n  knownIds: ReadonlySet<string>,\n): string | null {\n  return id != null && knownIds.has(id) ? id : null;\n}\n\nexport function isSegmentCurrentPage(\n  segment: Segment,\n  currentPage?: number | null,\n): boolean {\n  return (\n    currentPage != null &&\n    Number.isInteger(currentPage) &&\n    currentPage > 0 &&\n    Array.isArray(segment.pages) &&\n    segment.pages.includes(currentPage)\n  );\n}\n\nfunction normalizeCurrentPage(currentPage?: number | null): number | null {\n  return currentPage != null && Number.isInteger(currentPage) && currentPage > 0\n    ? currentPage\n    : null;\n}\n\nfunction resolveCurrentSegmentIds(\n  segments: Segment[],\n  currentPage: number,\n): string[] {\n  return segments\n    .filter((segment) => isSegmentCurrentPage(segment, currentPage))\n    .map((segment) => segment.id);\n}\n\nexport function getSegmentViewState({\n  segment,\n  interactionState,\n  isCurrent,\n}: {\n  segment: Segment;\n  interactionState: SegmentInteractionState;\n  isCurrent?: boolean;\n}): SegmentViewState {\n  const current =\n    isCurrent ??\n    (interactionState.currentSegmentIds.includes(segment.id) &&\n      isSegmentCurrentPage(segment, interactionState.currentPage));\n  const isPreviewed = interactionState.previewSegmentId === segment.id;\n  const isHighlighted =\n    interactionState.highlightedSegmentIds.includes(segment.id) &&\n    (interactionState.previewSegmentId != null ? isPreviewed : current);\n\n  return {\n    isPreviewed,\n    isCurrent: current,\n    isHighlighted,\n    isDimmed: interactionState.previewSegmentId != null && !isPreviewed,\n  };\n}\n\nexport function getSegmentSurfaceProps({\n  segment,\n  interaction,\n  interactionState,\n  isCurrent,\n  onSelect,\n}: {\n  segment: Segment;\n  interaction?: SegmentInteraction | null;\n  interactionState: SegmentInteractionState;\n  isCurrent?: boolean;\n  onSelect?: (segment: Segment) => void;\n}): SegmentSurfaceProps {\n  const state = getSegmentViewState({\n    segment,\n    interactionState,\n    isCurrent,\n  });\n\n  return {\n    state,\n    eventHandlers: {\n      onPointerEnter: () => interaction?.previewSegment(segment.id),\n      onPointerLeave: () => interaction?.clearPreview(),\n      onClick: () => {\n        interaction?.clearPreview();\n        onSelect?.(segment);\n      },\n    },\n    dataProps: {\n      \"data-previewed\": state.isPreviewed,\n      \"data-current\": state.isCurrent,\n      \"data-highlighted\": state.isHighlighted,\n    },\n  };\n}\n",
      "type": "registry:lib",
      "target": "@lib/segment-interaction.ts"
    }
  ],
  "type": "registry:lib"
}