{
  "name": "card-lightbox",
  "dependencies": [
    "motion-v"
  ],
  "registryDependencies": [],
  "files": [
    {
      "name": "CardLightbox.vue",
      "content": "<script setup lang=\"ts\">\nimport { onMounted, onUnmounted, ref } from 'vue';\nimport { AnimatePresence, motion } from 'motion-v';\nimport type { Item } from './types';\nimport { XIcon } from 'lucide-vue-next';\n\ntype Props = {\n  items: Item[];\n};\n\nconst props = defineProps<Props>();\n\nconst openId = ref<string | null>(null);\n\nonMounted(() => {\n  document.addEventListener('keydown', handleEscKeydown);\n});\n\nonUnmounted(() => {\n  document.removeEventListener('keydown', handleEscKeydown);\n});\n\nfunction open(id: string) {\n  openId.value = id;\n}\n\nfunction close() {\n  openId.value = null;\n}\n\nfunction handleEscKeydown(event: KeyboardEvent) {\n  if (event.key === 'Escape') {\n    close();\n  }\n}\n</script>\n\n<template>\n  <div class=\"mx-auto w-full max-w-[990px] flex flex-col @container/card-lightbox\">\n    <ul class=\"grid grid-cols-1 gap-2.5 p-0 m-0 list-none @lg/card-lightbox:grid-cols-10 @lg/card-lightbox:gap-5\">\n      <motion.button\n        v-for=\"card in props.items\"\n        :key=\"card.id\"\n        :data-open=\"openId === card.id\"\n        :class=\"[\n          'relative p-0 overflow-hidden focus-visible:rounded-[20px] focus-visible:shadow-[0_0_0_2px_hsl(var(--background)),_0_0_0_4px_hsl(var(--ring))] focus-visible:outline-none',\n          'h-[280px] w-full col-span-1',\n          '@lg/card-lightbox:h-[420px]',\n          card.theme === 'dark' ? 'group theme-dark' : '',\n          (props.items.findIndex(item => item.id === card.id) + 1) % 4 === 1 || (props.items.findIndex(item => item.id === card.id) + 1) % 4 === 0\n            ? '@lg/card-lightbox:col-span-6'\n            : '@lg/card-lightbox:col-span-4',\n        ]\"\n        @click=\"() => open(card.id)\"\n      >\n        <motion.div\n          class=\"relative pointer-events-auto select-none rounded-[20px] bg-card overflow-hidden w-full h-full m-auto animate-fade-in\"\n          :layout-id=\"`card-container-${card.id}`\"\n        >\n          <motion.div\n            class=\"relative overflow-hidden h-full w-full\"\n            :layout-id=\"`card-image-container-${card.id}`\"\n          >\n            <motion.img\n              class=\"absolute inset-0 z-10 w-full h-full object-cover pointer-events-none\"\n              :src=\"card.image\"\n              alt=\"\"\n            />\n            <div\n              :class=\"[\n                'absolute inset-0 z-10 pointer-events-none',\n                card.theme === 'dark'\n                  ? 'bg-linear-[160deg,rgba(255,255,255,0.95)_-8%,rgba(255,255,255,0)_30%]'\n                  : 'bg-linear-[160deg,rgba(0,0,0,0.95)_-8%,rgba(0,0,0,0)_30%]'\n              ]\"\n            />\n          </motion.div>\n          <motion.div\n            class=\"absolute top-0 left-0 z-20 max-w-[300px] p-5 text-left\"\n            :layout-id=\"`title-container-${card.id}`\"\n            layout=\"position\"\n          >\n            <span\n              :class=\"[\n                'px-2 py-1 rounded-[12px] text-[12px] uppercase backdrop-blur-[8px]',\n                'text-white bg-white/10 group-[.theme-dark]:text-black/90 group-[.theme-dark]:bg-black/10'\n              ]\"\n            >\n              {{ card.category }}\n            </span>\n            <h2\n              :class=\"[\n                'my-2 text-[20px] font-semibold',\n                'text-white group-[.theme-dark]:text-black/90'\n              ]\"\n            >\n              {{ card.title }}\n            </h2>\n          </motion.div>\n        </motion.div>\n      </motion.button>\n    </ul>\n    <AnimatePresence>\n      <motion.div\n        v-if=\"openId\"\n        :initial=\"{ opacity: 0 }\"\n        :animate=\"{ opacity: 1 }\"\n        :exit=\"{ opacity: 0 }\"\n        :transition=\"{ duration: 0.2 }\"\n        style=\"pointer-events: auto\"\n        class=\"fixed inset-0 z-50 backdrop-blur-[12px] bg-black/20 [will-change:opacity]\"\n        @click=\"close\"\n      />\n    </AnimatePresence>\n    <AnimatePresence>\n      <div\n        v-if=\"openId\"\n        :class=\"[\n          'fixed inset-0 z-[51] w-full h-full pointer-events-none flex flex-col justify-center items-center p-0 lg:p-[40px_0]',\n          props.items.find(item => item.id === openId)?.theme === 'dark' ? 'group theme-dark' : '',\n        ]\"\n      >\n        <motion.div\n          class=\"relative pointer-events-auto select-none bg-card overflow-hidden m-auto overflow-y-auto w-screen max-w-full h-screen rounded-none lg:w-auto lg:max-w-[700px] lg:h-[70dvh] lg:rounded-[20px]\"\n          :layout-id=\"`card-container-${openId}`\"\n        >\n          <motion.div\n            class=\"relative overflow-hidden w-full h-[280px] lg:h-[420px]\"\n            :layout-id=\"`card-image-container-${openId}`\"\n          >\n            <motion.img\n              class=\"absolute inset-0 z-10 w-full h-full object-cover pointer-events-none\"\n              :src=\"props.items.find(item => item.id === openId)?.image\"\n              alt=\"\"\n              :layout-id=\"`card-image-${openId}`\"\n              :initial=\"{ opacity: 0, filter: 'blur(12px)' }\"\n              :animate=\"{ opacity: 1, filter: 'blur(0px)' }\"\n              :exit=\"{ opacity: 0, filter: 'blur(12px)' }\"\n              :transition=\"{ duration: 0.5 }\"\n            />\n            <div\n              :class=\"[\n                'absolute inset-0 z-10 pointer-events-none',\n                props.items.find(item => item.id === openId)?.theme === 'dark'\n                  ? 'bg-linear-[160deg,rgba(255,255,255,0.95)_-8%,rgba(255,255,255,0)_30%]'\n                  : 'bg-linear-[160deg,rgba(0,0,0,0.95)_-8%,rgba(0,0,0,0)_30%]'\n              ]\"\n            />\n            <motion.div\n              class=\"absolute top-0 left-0 z-20 p-5\"\n              :layout-id=\"`title-container-${openId}`\"\n              layout=\"position\"\n            >\n              <span\n                :class=\"[\n                  'px-2 py-1 rounded-[12px] text-[12px] uppercase backdrop-blur-[8px]',\n                  'text-white bg-white/10 group-[.theme-dark]:text-black/90 group-[.theme-dark]:bg-black/10'\n                ]\"\n              >\n                {{ props.items.find(item => item.id === openId)?.category }}\n              </span>\n              <h2\n                :class=\"[\n                  'my-2 text-[20px] font-semibold',\n                  'text-white group-[.theme-dark]:text-black/90'\n                ]\"\n              >\n                {{ props.items.find(item => item.id === openId)?.title }}\n              </h2>\n            </motion.div>\n            <motion.div\n              class=\"absolute top-0 right-0 z-20 p-5 cursor-pointer\"\n              @click=\"close\"\n            >\n              <XIcon class=\"stroke-white group-[.theme-dark]:stroke-black/90\" />\n            </motion.div>\n          </motion.div>\n          <motion.div\n            class=\"p-[35px] max-w-[700px] w-screen text-primary\"\n          >\n            <div v-html=\"props.items.find(item => item.id === openId)?.content\" />\n          </motion.div>\n        </motion.div>\n      </div>\n    </AnimatePresence>\n  </div>\n</template>\n"
    },
    {
      "name": "index.ts",
      "content": "export { default as CardLightbox } from './CardLightbox.vue';\nexport * from './types';\n"
    },
    {
      "name": "types.ts",
      "content": "import type { Component } from 'vue';\n\nexport interface Item {\n  id: string;\n  category: string;\n  title: string;\n  content: string | Component;\n  theme: 'light' | 'dark';\n  image: string;\n}\n"
    }
  ],
  "type": "components:ui"
}