{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "work-experience",
  "type": "registry:component",
  "title": "Work Experience",
  "author": "ncdai <dai@chanhdai.com>",
  "description": "Displays a list of work experiences with role details and durations.",
  "dependencies": [
    "react-markdown",
    "lucide-react"
  ],
  "devDependencies": [
    "@tailwindcss/typography",
    "tw-animate-css"
  ],
  "registryDependencies": [
    "@ncdai/utils",
    "collapsible",
    "separator"
  ],
  "files": [
    {
      "path": "src/registry/work-experience/work-experience.tsx",
      "content": "import {\n  BriefcaseBusinessIcon,\n  ChevronsDownUpIcon,\n  ChevronsUpDownIcon,\n  CodeXmlIcon,\n  DraftingCompassIcon,\n  GraduationCapIcon,\n} from \"lucide-react\";\nimport Image from \"next/image\";\nimport React from \"react\";\nimport ReactMarkdown from \"react-markdown\";\n\nimport {\n  Collapsible,\n  CollapsibleContent,\n  CollapsibleTrigger,\n} from \"@/components/ui/collapsible\";\nimport { Separator } from \"@/components/ui/separator\";\nimport { cn } from \"@/lib/utils\";\n\nconst iconMap = {\n  code: CodeXmlIcon,\n  design: DraftingCompassIcon,\n  business: BriefcaseBusinessIcon,\n  education: GraduationCapIcon,\n} as const;\n\n/**\n * Represents the valid keys of the `iconMap` object, used to specify the type of icon\n * associated with an experience position.\n */\nexport type ExperiencePositionIconType = keyof typeof iconMap;\n\nexport type ExperiencePositionItemType = {\n  /** Unique identifier for the position */\n  id: string;\n  /** The job title or position name */\n  title: string;\n  /** The period during which the position was held (e.g., \"Jan 2020 - Dec 2021\") */\n  employmentPeriod: string;\n  /** The type of employment (e.g., \"Full-time\", \"Part-time\", \"Contract\") */\n  employmentType?: string;\n  /** A brief description of the position or responsibilities */\n  description?: string;\n  /** An icon representing the position */\n  icon?: ExperiencePositionIconType;\n  /** A list of skills associated with the position */\n  skills?: string[];\n  /** Indicates if the position details are expanded in the UI */\n  isExpanded?: boolean;\n};\n\nexport type ExperienceItemType = {\n  /** Unique identifier for the experience item */\n  id: string;\n  /** Name of the company where the experience was gained */\n  companyName: string;\n  /** URL or path to the company's logo image */\n  companyLogo?: string;\n  /** List of positions held at the company */\n  positions: ExperiencePositionItemType[];\n  /** Indicates if this is the user's current employer */\n  isCurrentEmployer?: boolean;\n};\n\nexport function WorkExperience({\n  className,\n  experiences,\n}: {\n  className?: string;\n  experiences: ExperienceItemType[];\n}) {\n  return (\n    <div className={cn(\"bg-background px-4\", className)}>\n      {experiences.map((experience) => (\n        <ExperienceItem key={experience.id} experience={experience} />\n      ))}\n    </div>\n  );\n}\n\nexport function ExperienceItem({\n  experience,\n}: {\n  experience: ExperienceItemType;\n}) {\n  return (\n    <div className=\"space-y-4 py-4\">\n      <div className=\"not-prose flex items-center gap-3\">\n        <div\n          className=\"flex size-6 shrink-0 items-center justify-center\"\n          aria-hidden\n        >\n          {experience.companyLogo ? (\n            <Image\n              src={experience.companyLogo}\n              alt={experience.companyName}\n              width={24}\n              height={24}\n              quality={100}\n              className=\"rounded-full\"\n              unoptimized\n            />\n          ) : (\n            <span className=\"flex size-2 rounded-full bg-zinc-300 dark:bg-zinc-600\" />\n          )}\n        </div>\n\n        <h3 className=\"text-lg leading-snug font-medium text-foreground\">\n          {experience.companyName}\n        </h3>\n\n        {experience.isCurrentEmployer && (\n          <span className=\"relative flex items-center justify-center\">\n            <span className=\"absolute inline-flex size-3 animate-ping rounded-full bg-info opacity-50\" />\n            <span className=\"relative inline-flex size-2 rounded-full bg-info\" />\n            <span className=\"sr-only\">Current Employer</span>\n          </span>\n        )}\n      </div>\n\n      <div className=\"relative space-y-4 before:absolute before:left-3 before:h-full before:w-px before:bg-border\">\n        {experience.positions.map((position) => (\n          <ExperiencePositionItem key={position.id} position={position} />\n        ))}\n      </div>\n    </div>\n  );\n}\n\nexport function ExperiencePositionItem({\n  position,\n}: {\n  position: ExperiencePositionItemType;\n}) {\n  const ExperienceIcon = iconMap[position.icon || \"business\"];\n\n  return (\n    <Collapsible defaultOpen={position.isExpanded} asChild>\n      <div className=\"relative last:before:absolute last:before:h-full last:before:w-4 last:before:bg-background\">\n        <CollapsibleTrigger\n          className={cn(\n            \"group/experience not-prose block w-full text-left select-none\",\n            \"relative before:absolute before:-top-1 before:-right-1 before:-bottom-1.5 before:left-7 before:rounded-lg hover:before:bg-muted/50\"\n          )}\n        >\n          <div className=\"relative z-1 mb-1 flex items-center gap-3\">\n            <div\n              className=\"flex size-6 shrink-0 items-center justify-center rounded-lg bg-muted text-muted-foreground\"\n              aria-hidden\n            >\n              <ExperienceIcon className=\"size-4\" />\n            </div>\n\n            <h4 className=\"flex-1 text-base font-medium text-balance text-foreground\">\n              {position.title}\n            </h4>\n\n            <div\n              className=\"shrink-0 text-muted-foreground [&_svg]:size-4\"\n              aria-hidden\n            >\n              <ChevronsDownUpIcon className=\"hidden group-data-[state=open]/experience:block\" />\n              <ChevronsUpDownIcon className=\"hidden group-data-[state=closed]/experience:block\" />\n            </div>\n          </div>\n\n          <div className=\"relative z-1 flex items-center gap-2 pl-9 text-sm text-muted-foreground\">\n            {position.employmentType && (\n              <>\n                <dl>\n                  <dt className=\"sr-only\">Employment Type</dt>\n                  <dd>{position.employmentType}</dd>\n                </dl>\n\n                <Separator\n                  className=\"data-[orientation=vertical]:h-4\"\n                  orientation=\"vertical\"\n                />\n              </>\n            )}\n\n            <dl>\n              <dt className=\"sr-only\">Employment Period</dt>\n              <dd>{position.employmentPeriod}</dd>\n            </dl>\n          </div>\n        </CollapsibleTrigger>\n\n        <CollapsibleContent className=\"overflow-hidden duration-300 data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down\">\n          {position.description && (\n            <Prose className=\"pt-2 pl-9\">\n              <ReactMarkdown>{position.description}</ReactMarkdown>\n            </Prose>\n          )}\n\n          {Array.isArray(position.skills) && position.skills.length > 0 && (\n            <ul className=\"not-prose flex flex-wrap gap-1.5 pt-2 pl-9\">\n              {position.skills.map((skill, index) => (\n                <li key={index} className=\"flex\">\n                  <Skill>{skill}</Skill>\n                </li>\n              ))}\n            </ul>\n          )}\n        </CollapsibleContent>\n      </div>\n    </Collapsible>\n  );\n}\n\nfunction Prose({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      className={cn(\n        \"prose prose-sm max-w-none font-mono text-foreground prose-zinc dark:prose-invert\",\n        \"prose-a:font-medium prose-a:wrap-break-word prose-a:text-foreground prose-a:underline prose-a:underline-offset-4\",\n        \"prose-code:rounded-md prose-code:border prose-code:bg-muted/50 prose-code:px-[0.3rem] prose-code:py-[0.2rem] prose-code:text-sm prose-code:font-normal prose-code:before:content-none prose-code:after:content-none\",\n        className\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction Skill({ className, ...props }: React.ComponentProps<\"span\">) {\n  return (\n    <span\n      className={cn(\n        \"inline-flex items-center rounded-lg border bg-muted/50 px-1.5 py-0.5 font-mono text-xs text-muted-foreground\",\n        className\n      )}\n      {...props}\n    />\n  );\n}\n",
      "type": "registry:component"
    }
  ],
  "cssVars": {
    "light": {
      "background": "oklch(1 0 0)",
      "muted": "oklch(0.967 0.001 286.375)",
      "muted-foreground": "oklch(0.552 0.016 285.938)",
      "border": "oklch(0.92 0.004 286.32)"
    },
    "dark": {
      "background": "oklch(0.141 0.005 285.823)",
      "muted": "oklch(0.274 0.006 286.033)",
      "muted-foreground": "oklch(0.705 0.015 286.067)",
      "border": "oklch(0.274 0.006 286.033)"
    }
  },
  "css": {
    "@import \"tw-animate-css\"": {},
    "@plugin @tailwindcss/typography": {}
  },
  "docs": "https://chanhdai.com/components/work-experience-component"
}