diff --git a/app/gallery/page.tsx b/app/gallery/page.tsx new file mode 100644 index 0000000..86cb83f --- /dev/null +++ b/app/gallery/page.tsx @@ -0,0 +1,12 @@ +import { GalleryContent } from "@/components/gallery-content"; +import { GALLERY_ITEMS } from "@/data/gallery"; +import type { Metadata } from "next"; + +export const metadata: Metadata = { + title: "Galeri", + description: "Poyraz Avsever'in tasarımları, projeleri ve görsel içeriklerinden oluşan galeri portföyü.", +}; + +export default function GalleryPage() { + return ; +} diff --git a/apps/data-panel/renderer/configs.js b/apps/data-panel/renderer/configs.js index 1fc465a..1849e15 100644 --- a/apps/data-panel/renderer/configs.js +++ b/apps/data-panel/renderer/configs.js @@ -481,4 +481,49 @@ export const COLLECTION_CONFIGS = { meta: [], }), }, + gallery: { + label: "Gallery", + itemLabel: "Gallery Item", + fileName: "gallery.ts", + exportName: "GALLERY_ITEMS", + fields: [ + { + key: "id", + label: "ID", + required: true, + placeholder: "gallery-1", + }, + { + key: "title", + label: "Title", + required: true, + placeholder: "Project Mockup", + }, + { + key: "category", + label: "Category", + required: true, + placeholder: "UI Design", + }, + { + key: "image", + label: "Image URL", + required: true, + type: "url", + placeholder: "/gallery/mockup.png", + }, + { + key: "description", + label: "Description", + type: "textarea", + full: true, + placeholder: "Detail about visual...", + }, + ], + card: (item) => ({ + title: item.title || item.id || "Untitled", + meta: [item.category], + footer: item.description, + }), + }, }; diff --git a/apps/data-panel/renderer/index.html b/apps/data-panel/renderer/index.html index c042022..a25b431 100644 --- a/apps/data-panel/renderer/index.html +++ b/apps/data-panel/renderer/index.html @@ -103,6 +103,13 @@ > YouTube + diff --git a/components/gallery-content.tsx b/components/gallery-content.tsx new file mode 100644 index 0000000..d561bae --- /dev/null +++ b/components/gallery-content.tsx @@ -0,0 +1,205 @@ +"use client"; + +import Image from "next/image"; +import { useCallback, useEffect, useMemo, useState } from "react"; +import { Icon } from "@iconify/react"; +import { Badge, Typography } from "poyraz-ui/atoms"; +import { StaggerContainer, StaggerItem } from "@/components/motion-wrapper"; +import type { GalleryItem } from "@/data/gallery"; + +type GalleryContentProps = { + items: GalleryItem[]; +}; + +export function GalleryContent({ items }: GalleryContentProps) { + const [activeCategory, setActiveCategory] = useState("All"); + const [lightboxIndex, setLightboxIndex] = useState(null); + + // Keyboard navigation for lightbox + useEffect(() => { + if (lightboxIndex === null) return; + + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === "Escape") { + setLightboxIndex(null); + } else if (e.key === "ArrowLeft") { + setLightboxIndex((prev) => (prev !== null && prev > 0 ? prev - 1 : prev)); + } else if (e.key === "ArrowRight") { + setLightboxIndex((prev) => + prev !== null && prev < items.length - 1 ? prev + 1 : prev, + ); + } + }; + + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); + }, [lightboxIndex, items.length]); + + const categories = useMemo(() => { + const cats = new Set(items.map((item) => item.category)); + return ["All", ...Array.from(cats)].sort(); + }, [items]); + + const filteredItems = useMemo(() => { + if (activeCategory === "All") return items; + return items.filter((item) => item.category === activeCategory); + }, [items, activeCategory]); + + const openLightbox = useCallback( + (item: GalleryItem) => { + const index = items.findIndex((i) => i.id === item.id); + if (index !== -1) { + setLightboxIndex(index); + } + }, + [items], + ); + + const closeLightbox = useCallback(() => { + setLightboxIndex(null); + }, []); + + const lightboxItem = lightboxIndex !== null ? items[lightboxIndex] : null; + + return ( + <> +
+ + {categories.length > 1 && ( +
+ {categories.map((category) => ( + + ))} +
+ )} + + {filteredItems.length === 0 ? ( +
+ Henüz bu kategoride görsel bulunmuyor. +
+ ) : ( + + {filteredItems.map((item) => ( + +
openLightbox(item)} + > + {item.title} +
+
+ + {item.title} + + + {item.category} + +
+
+ + ))} + + )} +
+ + {/* Lightbox / Modal */} + {lightboxItem && ( +
+ + + {lightboxIndex! > 0 && ( + + )} + + {lightboxIndex! < items.length - 1 && ( + + )} + +
e.stopPropagation()} + > +
+ {lightboxItem.title} +
+ +
+
+
+ + {lightboxItem.title} + + + {lightboxItem.category} + +
+
+ {lightboxIndex! + 1} / {items.length} +
+
+ {lightboxItem.description && ( + + {lightboxItem.description} + + )} +
+
+
+ )} + + ); +} diff --git a/data/gallery.ts b/data/gallery.ts new file mode 100644 index 0000000..7e55244 --- /dev/null +++ b/data/gallery.ts @@ -0,0 +1,9 @@ +export type GalleryItem = { + id: string; + title: string; + description: string; + image: string; + category: string; +}; + +export const GALLERY_ITEMS: GalleryItem[] = []; diff --git a/lib/links.ts b/lib/links.ts index 0e84789..6aec57c 100644 --- a/lib/links.ts +++ b/lib/links.ts @@ -3,6 +3,7 @@ export const NAV_LINKS = [ { id: "blog", label: "Blog", href: "/blog" }, { id: "content", label: "İçerikler", href: "/content" }, { id: "projects", label: "Projeler", href: "/projects" }, + { id: "gallery", label: "Galeri", href: "/gallery" }, { id: "contact", label: "İletişim", href: "/contact" }, ] as const;