{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "schedule-list",
  "title": "Schedule List",
  "author": "ilinxa",
  "description": "Time-anchored agenda list — time or range, title, optional description and icons, per-row links, framed or bare.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "src/registry/components/data/schedule-list/schedule-list.tsx",
      "content": "\"use client\";\n\nimport { memo, useId, useMemo } from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport {\n  DEFAULT_SCHEDULE_LIST_LABELS,\n  type ScheduleListLabels,\n  type ScheduleListProps,\n} from \"./types\";\nimport { ScheduleRow } from \"./parts/schedule-row\";\n\nfunction ScheduleListInner({\n  items,\n  heading,\n  headingAs = \"h2\",\n  framed = true,\n  linkComponent = \"a\",\n  labels: labelsProp,\n  renderItem,\n  renderTime,\n  emptyState,\n  className,\n  headingClassName,\n  itemClassName,\n}: ScheduleListProps) {\n  const headingId = useId();\n  const HeadingTag = headingAs;\n\n  const labels = useMemo<Required<ScheduleListLabels>>(\n    () => ({ ...DEFAULT_SCHEDULE_LIST_LABELS, ...labelsProp }),\n    [labelsProp],\n  );\n\n  const isEmpty = items.length === 0;\n\n  return (\n    <section\n      aria-labelledby={heading ? headingId : undefined}\n      className={cn(\"space-y-6\", className)}\n    >\n      {heading && (\n        <HeadingTag\n          id={headingId}\n          className={cn(\n            \"text-2xl font-bold text-foreground\",\n            headingClassName,\n          )}\n        >\n          {heading}\n        </HeadingTag>\n      )}\n\n      {isEmpty ? (\n        emptyState ?? (\n          <p\n            role=\"status\"\n            className=\"text-sm text-muted-foreground py-4\"\n          >\n            {labels.emptyText}\n          </p>\n        )\n      ) : (\n        <ol\n          role=\"list\"\n          className={cn(framed ? \"space-y-4\" : \"space-y-2\")}\n        >\n          {items.map((item) =>\n            renderItem ? (\n              <li key={item.id}>{renderItem(item)}</li>\n            ) : (\n              <ScheduleRow\n                key={item.id}\n                item={item}\n                framed={framed}\n                timeSeparator={labels.timeRangeSeparator}\n                renderTime={renderTime}\n                linkComponent={linkComponent}\n                itemClassName={itemClassName}\n              />\n            ),\n          )}\n        </ol>\n      )}\n    </section>\n  );\n}\n\nconst ScheduleList = memo(ScheduleListInner);\nScheduleList.displayName = \"ScheduleList\";\n\nexport { ScheduleList };\nexport default ScheduleList;\n",
      "type": "registry:component",
      "target": "components/schedule-list/schedule-list.tsx"
    },
    {
      "path": "src/registry/components/data/schedule-list/index.ts",
      "content": "export { ScheduleList, default } from \"./schedule-list\";\n\nexport type {\n  ScheduleListItem,\n  ScheduleListLabels,\n  ScheduleListProps,\n} from \"./types\";\n\nexport { DEFAULT_SCHEDULE_LIST_LABELS } from \"./types\";\n\n",
      "type": "registry:component",
      "target": "components/schedule-list/index.ts"
    },
    {
      "path": "src/registry/components/data/schedule-list/types.ts",
      "content": "import type { ComponentType, ElementType, ReactNode } from \"react\";\n\nexport interface ScheduleListItem {\n  /** Stable identifier. Used for React keys + accessible-name composition. */\n  id: string;\n  /** Time string (\"09:00\", \"14:30\", \"9pm\"). Required — schedules ARE time-anchored. */\n  time: string;\n  /** Optional end time for a time range. Renders as `${time}{separator}${endTime}` (e.g. \"09:00 - 10:30\"). */\n  endTime?: string;\n  /** Item title (bold). Required. */\n  title: string;\n  /** Item description (muted, smaller). Optional. */\n  description?: string;\n  /** Optional Lucide-style icon shown left of the time column (e.g. Coffee, Users for break / networking). */\n  icon?: ComponentType<{\n    className?: string;\n    \"aria-hidden\"?: boolean | \"true\" | \"false\";\n  }>;\n  /** Optional URL — when supplied + `linkComponent` provided, item becomes clickable. */\n  href?: string;\n}\n\nexport interface ScheduleListLabels {\n  /** Default: \" - \" (with surrounding spaces) — separator between time + endTime. */\n  timeRangeSeparator?: string;\n  /** Default: \"No items scheduled.\" Used when `items` is empty AND `emptyState` not provided. */\n  emptyText?: string;\n}\n\nexport interface ScheduleListProps {\n  /** Schedule items in display order (chronological). */\n  items: ScheduleListItem[];\n\n  /** Optional section heading text. Omit to render without heading (consumer wraps externally). */\n  heading?: string;\n  /** Heading semantic level. Default: 'h2'. */\n  headingAs?: \"h2\" | \"h3\" | \"h4\";\n\n  /** Render per-row card chrome (`bg-card rounded-xl border ...`). Default: true. */\n  framed?: boolean;\n\n  /** Element used for clickable items. Default: 'a'. Pass NextLink / RemixLink / etc. */\n  linkComponent?: ElementType;\n\n  /** Localized labels. Defaults are English. */\n  labels?: ScheduleListLabels;\n\n  /** Custom per-item renderer — receives the item, returns the row content (replaces the default time + title + description layout). */\n  renderItem?: (item: ScheduleListItem) => ReactNode;\n\n  /** Custom time-string renderer — returns ReactNode (e.g. \"09:00\" → \"9:00 AM\"). Default: time + optional endTime joined by separator. */\n  renderTime?: (item: ScheduleListItem) => ReactNode;\n\n  /** Empty-state slot. Wins over `labels.emptyText` when provided. */\n  emptyState?: ReactNode;\n\n  /** Override classes for the root <section>. */\n  className?: string;\n  /** Override classes for the heading. */\n  headingClassName?: string;\n  /** Override classes per row. */\n  itemClassName?: string;\n}\n\n/** Default English labels — exported for consumer composition. */\nexport const DEFAULT_SCHEDULE_LIST_LABELS: Required<ScheduleListLabels> = {\n  timeRangeSeparator: \" - \",\n  emptyText: \"No items scheduled.\",\n};\n",
      "type": "registry:component",
      "target": "components/schedule-list/types.ts"
    },
    {
      "path": "src/registry/components/data/schedule-list/parts/schedule-row.tsx",
      "content": "\"use client\";\n\nimport type { ElementType, ReactNode } from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport type { ScheduleListItem } from \"../types\";\n\ninterface ScheduleRowProps {\n  item: ScheduleListItem;\n  framed: boolean;\n  timeSeparator: string;\n  renderTime?: (item: ScheduleListItem) => ReactNode;\n  linkComponent: ElementType;\n  itemClassName?: string;\n}\n\nexport function ScheduleRow({\n  item,\n  framed,\n  timeSeparator,\n  renderTime,\n  linkComponent: LinkComponent,\n  itemClassName,\n}: ScheduleRowProps) {\n  const IconComponent = item.icon;\n  const isLinked = item.href != null && item.href.length > 0;\n\n  const timeContent = renderTime ? (\n    renderTime(item)\n  ) : (\n    <span className=\"text-lg font-bold text-primary\">\n      {item.time}\n      {item.endTime ? `${timeSeparator}${item.endTime}` : null}\n    </span>\n  );\n\n  return (\n    <li\n      className={cn(\n        \"relative group flex items-start gap-4\",\n        framed &&\n          \"p-4 bg-card rounded-xl border border-border/50 motion-safe:hover:border-border transition-colors\",\n        itemClassName,\n      )}\n    >\n      {IconComponent && (\n        <div className=\"shrink-0 mt-1\">\n          <IconComponent\n            aria-hidden=\"true\"\n            className=\"w-5 h-5 text-primary\"\n          />\n        </div>\n      )}\n\n      <div className=\"w-auto min-w-20 shrink-0\">{timeContent}</div>\n\n      <div className=\"min-w-0 flex-1\">\n        <h4 className=\"font-semibold text-foreground motion-safe:group-hover:text-primary transition-colors\">\n          {item.title}\n        </h4>\n        {item.description && (\n          <p className=\"text-sm text-muted-foreground mt-0.5\">\n            {item.description}\n          </p>\n        )}\n      </div>\n\n      {isLinked && (\n        <LinkComponent\n          href={item.href}\n          aria-label={item.title}\n          className={cn(\n            \"absolute inset-0 z-0 outline-none\",\n            framed\n              ? \"rounded-xl focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n              : \"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset\",\n          )}\n        />\n      )}\n    </li>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/schedule-list/parts/schedule-row.tsx"
    }
  ],
  "categories": [
    "data",
    "events"
  ],
  "type": "registry:block"
}