{"$schema":"https://ui.shadcn.com/schema/registry-item.json","author":"Eduardo Calvo <educlopez93@gmail.com>","css":{},"dependencies":["motion","usehooks-ts"],"description":"A JobListingComponent component for SmoothUI.","devDependencies":[],"files":[{"content":"\"use client\";\n\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\";\nimport type { SVGProps } from \"react\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { useOnClickOutside } from \"usehooks-ts\";\n\nexport interface Job {\n  company: string;\n  job_description: string;\n  job_time: string;\n  location: string;\n  logo: React.ReactNode;\n  remote: string;\n  salary: string;\n  title: string;\n}\n\nexport interface JobListingComponentProps {\n  className?: string;\n  jobs: Job[];\n  onJobClick?: (job: Job) => void;\n}\n\nexport const Resend = (props: SVGProps<SVGSVGElement>) => (\n  <svg\n    fill=\"none\"\n    height=\"1em\"\n    viewBox=\"0 0 600 600\"\n    width=\"1em\"\n    xmlns=\"http://www.w3.org/2000/svg\"\n    {...props}\n  >\n    <title>Resend logo</title>\n    <path\n      d=\"M186 447.471V154H318.062C336.788 154 353.697 158.053 368.79 166.158C384.163 174.263 396.181 185.443 404.845 199.698C413.51 213.672 417.842 229.604 417.842 247.491C417.842 265.938 413.51 282.568 404.845 297.381C396.181 311.915 384.302 323.375 369.209 331.759C354.117 340.144 337.067 344.337 318.062 344.337H253.917V447.471H186ZM348.667 447.471L274.041 314.99L346.99 304.509L430 447.471H348.667ZM253.917 289.835H311.773C319.04 289.835 325.329 288.298 330.639 285.223C336.229 281.869 340.421 277.258 343.216 271.388C346.291 265.519 347.828 258.811 347.828 251.265C347.828 243.718 346.151 237.15 342.797 231.56C339.443 225.691 334.552 221.219 328.124 218.144C321.975 215.07 314.428 213.533 305.484 213.533H253.917V289.835Z\"\n      fill=\"currentColor\"\n    />\n  </svg>\n);\n\nexport const Turso = (props: SVGProps<SVGSVGElement>) => (\n  <svg\n    fill=\"none\"\n    height=\"1em\"\n    viewBox=\"0 0 201 170\"\n    width=\"1em\"\n    xmlns=\"http://www.w3.org/2000/svg\"\n    {...props}\n  >\n    <title>Turso logo</title>\n    <path\n      d=\"m100.055 170c-2.1901 0-18.2001-12.8-21.3001-16.45-2.44 3.73-6.44 7.96-6.44 7.96-11.05-5.57-25.17-20.06-27.83-25.13-2.62-5-12.13-62.58-12.39-79.3-.34-9.41 5.85-28.49 67.9601-28.49 62.11 0 68.29 19.08 67.96 28.49-.25 16.72-9.76 74.3-12.39 79.3-2.66 5.07-16.78 19.56-27.83 25.13 0 0-4-4.23-6.44-7.96-3.1 3.65-19.11 16.45-21.3 16.45z\"\n      fill=\"#1ebca1\"\n    />\n    <path\n      d=\"m100.055 132.92c-20.7301 0-33.9601-10.95-33.9601-10.95l1.91-26.67-21.75-1.94-3.91-31.55h115.4301l-3.91 31.55-21.75 1.94 1.91 26.67s-13.23 10.95-33.96 10.95z\"\n      fill=\"#183134\"\n    />\n    <path\n      d=\"m121.535 75.79 78.52-27.18c-4.67-27.94-29.16-48.61-29.16-48.61v30.78l-14.54 3.75-9.11-10.97-7.8 15.34-39.38 10.16-39.3801-10.16-7.8-15.34-9.11 10.97-14.54-3.75v-30.78s-24.50997 20.67-29.1799684 48.61l78.5199684 27.18-2.8 37.39c6.7 1.7 13.75 3.39 24.2801 3.39 10.53 0 17.57-1.69 24.27-3.39l-2.8-37.39z\"\n      fill=\"#4ff8d2\"\n    />\n  </svg>\n);\n\nexport const Supabase = (props: SVGProps<SVGSVGElement>) => (\n  <svg\n    fill=\"none\"\n    height=\"1em\"\n    viewBox=\"0 0 109 113\"\n    width=\"1em\"\n    xmlns=\"http://www.w3.org/2000/svg\"\n    {...props}\n  >\n    <title>Supabase logo</title>\n    <path\n      d=\"M63.7076 110.284C60.8481 113.885 55.0502 111.912 54.9813 107.314L53.9738 40.0627L99.1935 40.0627C107.384 40.0627 111.952 49.5228 106.859 55.9374L63.7076 110.284Z\"\n      fill=\"url(#paint0_linear)\"\n    />\n    <path\n      d=\"M63.7076 110.284C60.8481 113.885 55.0502 111.912 54.9813 107.314L53.9738 40.0627L99.1935 40.0627C107.384 40.0627 111.952 49.5228 106.859 55.9374L63.7076 110.284Z\"\n      fill=\"url(#paint1_linear)\"\n      fillOpacity={0.2}\n    />\n    <path\n      d=\"M45.317 2.07103C48.1765 -1.53037 53.9745 0.442937 54.0434 5.041L54.4849 72.2922H9.83113C1.64038 72.2922 -2.92775 62.8321 2.1655 56.4175L45.317 2.07103Z\"\n      fill=\"#3ECF8E\"\n    />\n    <defs>\n      <linearGradient\n        gradientUnits=\"userSpaceOnUse\"\n        id=\"paint0_linear\"\n        x1={53.9738}\n        x2={94.1635}\n        y1={54.974}\n        y2={71.8295}\n      >\n        <stop stopColor=\"#249361\" />\n        <stop offset={1} stopColor=\"#3ECF8E\" />\n      </linearGradient>\n      <linearGradient\n        gradientUnits=\"userSpaceOnUse\"\n        id=\"paint1_linear\"\n        x1={36.1558}\n        x2={54.4844}\n        y1={30.578}\n        y2={65.0806}\n      >\n        <stop />\n        <stop offset={1} stopOpacity={0} />\n      </linearGradient>\n    </defs>\n  </svg>\n);\n\nexport default function JobListingComponent({\n  jobs,\n  className,\n  onJobClick,\n}: JobListingComponentProps) {\n  const [activeItem, setActiveItem] = useState<Job | null>(null);\n  const ref = useRef<HTMLDivElement>(null) as React.RefObject<HTMLDivElement>;\n  const shouldReduceMotion = useReducedMotion();\n  useOnClickOutside(ref, () => setActiveItem(null));\n\n  useEffect(() => {\n    function onKeyDown(event: { key: string }) {\n      if (event.key === \"Escape\") {\n        setActiveItem(null);\n      }\n    }\n\n    window.addEventListener(\"keydown\", onKeyDown);\n    return () => window.removeEventListener(\"keydown\", onKeyDown);\n  }, []);\n\n  return (\n    <>\n      <AnimatePresence>\n        {activeItem ? (\n          <motion.div\n            animate={shouldReduceMotion ? { opacity: 1 } : { opacity: 1 }}\n            className=\"pointer-events-none absolute inset-0 z-10 bg-smooth-1000/10 bg-blend-luminosity backdrop-blur-xl\"\n            exit={\n              shouldReduceMotion\n                ? { opacity: 0, transition: { duration: 0 } }\n                : { opacity: 0 }\n            }\n            initial={shouldReduceMotion ? { opacity: 1 } : { opacity: 0 }}\n            transition={\n              shouldReduceMotion\n                ? { duration: 0 }\n                : { duration: 0.2, ease: [0.215, 0.61, 0.355, 1] }\n            }\n          />\n        ) : null}\n      </AnimatePresence>\n      <AnimatePresence>\n        {activeItem ? (\n          <div className=\"group absolute inset-0 z-10 grid place-items-center\">\n            <motion.div\n              className=\"flex h-fit w-[90%] max-w-2xl cursor-pointer select-none flex-col items-start gap-4 overflow-hidden border bg-background p-4 shadow-xs\"\n              layoutId={\n                shouldReduceMotion\n                  ? undefined\n                  : `workItem-${activeItem.company}`\n              }\n              ref={ref}\n              style={{\n                borderRadius: 12,\n                willChange: shouldReduceMotion ? \"auto\" : \"transform\",\n              }}\n              transition={\n                shouldReduceMotion\n                  ? { duration: 0 }\n                  : {\n                      bounce: 0.1,\n                      duration: 0.25,\n                      layout: {\n                        duration: 0.25,\n                        ease: [0.645, 0.045, 0.355, 1],\n                      },\n                      type: \"spring\" as const,\n                    }\n              }\n            >\n              <div className=\"relative flex w-full items-center gap-4\">\n                <motion.div\n                  layoutId={\n                    shouldReduceMotion\n                      ? undefined\n                      : `workItemLogo-${activeItem.company}`\n                  }\n                  style={{\n                    flexShrink: 0,\n                    willChange: shouldReduceMotion ? \"auto\" : \"transform\",\n                  }}\n                >\n                  {activeItem.logo}\n                </motion.div>\n                <div className=\"flex min-w-0 grow items-center justify-between\">\n                  <div className=\"flex min-w-0 flex-col gap-0.5\">\n                    <div className=\"flex w-full flex-row justify-between gap-0.5\">\n                      <div className=\"font-medium text-foreground text-sm\">\n                        {activeItem.company}\n                      </div>\n                    </div>\n                    <p className=\"text-primary-foreground text-sm\">\n                      {activeItem.title} / {activeItem.salary}\n                    </p>\n                    <div className=\"flex min-w-0 flex-row flex-wrap gap-2 text-primary-foreground text-xs\">\n                      {activeItem.remote === \"Yes\" &&\n                        ` ${activeItem.location} `}\n                      {activeItem.remote === \"No\" && ` ${activeItem.location} `}\n                      {activeItem.remote === \"Hybrid\" &&\n                        ` ${activeItem.remote} / ${activeItem.location} `}\n                      | {activeItem.job_time}\n                    </div>\n                  </div>\n                </div>\n              </div>\n              <motion.p\n                animate={shouldReduceMotion ? { opacity: 1 } : { opacity: 1 }}\n                className=\"text-primary-foreground text-sm\"\n                exit={\n                  shouldReduceMotion\n                    ? { opacity: 0, transition: { duration: 0 } }\n                    : { opacity: 0 }\n                }\n                initial={shouldReduceMotion ? { opacity: 1 } : { opacity: 0 }}\n                transition={\n                  shouldReduceMotion\n                    ? { duration: 0 }\n                    : {\n                        delay: 0.05,\n                        duration: 0.2,\n                        ease: [0.215, 0.61, 0.355, 1],\n                      }\n                }\n              >\n                {activeItem.job_description}\n              </motion.p>\n            </motion.div>\n          </div>\n        ) : null}\n      </AnimatePresence>\n      <div className={`relative flex items-start p-6 ${className || \"\"}`}>\n        <div className=\"relative flex w-full flex-col items-center gap-4 px-2\">\n          {jobs.map((role) => (\n            <motion.div\n              className=\"group relative flex w-full cursor-pointer select-none flex-row items-center gap-4 overflow-hidden border bg-background p-2 shadow-xs md:p-4\"\n              key={role.company}\n              layoutId={\n                shouldReduceMotion ? undefined : `workItem-${role.company}`\n              }\n              onClick={() => {\n                setActiveItem(role);\n                if (onJobClick) {\n                  onJobClick(role);\n                }\n              }}\n              style={{\n                borderRadius: 8,\n                willChange: shouldReduceMotion ? \"auto\" : \"transform\",\n              }}\n              transition={\n                shouldReduceMotion\n                  ? { duration: 0 }\n                  : {\n                      bounce: 0.1,\n                      duration: 0.25,\n                      layout: {\n                        duration: 0.25,\n                        ease: [0.645, 0.045, 0.355, 1],\n                      },\n                      type: \"spring\" as const,\n                    }\n              }\n              whileTap={shouldReduceMotion ? undefined : { scale: 0.97 }}\n            >\n              <motion.div\n                layoutId={\n                  shouldReduceMotion\n                    ? undefined\n                    : `workItemLogo-${role.company}`\n                }\n                style={{\n                  flexShrink: 0,\n                  willChange: shouldReduceMotion ? \"auto\" : \"transform\",\n                }}\n              >\n                {role.logo}\n              </motion.div>\n              <div className=\"flex w-full flex-col items-start justify-between gap-0.5\">\n                <div className=\"font-medium text-foreground\">\n                  {role.company}\n                </div>\n                <div className=\"text-primary-foreground text-xs\">\n                  {role.title} / {role.salary}\n                </div>\n\n                <div className=\"flex min-w-0 flex-row flex-wrap gap-2 text-primary-foreground text-xs\">\n                  {role.remote === \"Yes\" && ` ${role.location} `}\n                  {role.remote === \"No\" && ` ${role.location} `}\n                  {role.remote === \"Hybrid\" &&\n                    ` ${role.remote} / ${role.location} `}\n                  | {role.job_time}\n                </div>\n              </div>\n              <div\n                style={{\n                  opacity: 0,\n                  pointerEvents: \"none\",\n                  position: \"absolute\",\n                  top: \"100%\",\n                }}\n              >\n                {role.job_description}\n              </div>\n            </motion.div>\n          ))}\n        </div>\n      </div>\n    </>\n  );\n}\n","path":"index.tsx","target":"components/smoothui/job-listing-component/index.tsx","type":"registry:ui"}],"name":"job-listing-component","registryDependencies":["https://smoothui.dev/r/tokens.json"],"title":"Job Listing Component","type":"registry:ui"}