UNPKG

sigma-ui

Version:
22 lines 7.79 kB
{ "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" }