|
1 | 1 | import Templates, { Index } from "./components/Templates.mjs" |
| 2 | +import ScreenshotsGallery from "./components/ScreenshotsGallery.mjs" |
2 | 3 |
|
3 | 4 | const ReactTemplate = { |
4 | 5 | components: { Templates }, |
@@ -61,161 +62,6 @@ const ReactTemplate = { |
61 | 62 | } |
62 | 63 | } |
63 | 64 |
|
64 | | -const ScreenshotsGallery = { |
65 | | - props: { |
66 | | - images: Object |
67 | | - }, |
68 | | - template:` |
69 | | - <div class="not-prose my-16"> |
70 | | - <!-- Gallery Grid --> |
71 | | - <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8"> |
72 | | - <div v-for="(imageUrl, title) in images" :key="title" |
73 | | - @click="openLightbox(imageUrl, title)" |
74 | | - class="group relative overflow-hidden rounded-xl shadow-lg hover:shadow-2xl transition-all duration-300 transform hover:scale-[1.02] cursor-pointer bg-white dark:bg-gray-800"> |
75 | | - |
76 | | - <!-- Image Container with aspect ratio for 2048x2158 --> |
77 | | - <div class="relative aspect-[2048/2158] overflow-hidden"> |
78 | | - <!-- Gradient overlay on hover --> |
79 | | - <div class="absolute inset-0 bg-gradient-to-t from-black/70 via-black/20 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300 z-10"></div> |
80 | | - |
81 | | - <!-- Image --> |
82 | | - <img :src="imageUrl" |
83 | | - :alt="title" |
84 | | - class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110" |
85 | | - loading="lazy"> |
86 | | - |
87 | | - <!-- Title overlay --> |
88 | | - <div class="absolute bottom-0 left-0 right-0 p-6 z-20 transform translate-y-full group-hover:translate-y-0 transition-transform duration-300"> |
89 | | - <h3 class="text-white text-lg font-semibold capitalize">{{ title }}</h3> |
90 | | - <p class="text-gray-200 text-sm mt-1">Click to view full size</p> |
91 | | - </div> |
92 | | - |
93 | | - <!-- Zoom icon --> |
94 | | - <div class="absolute top-4 right-4 z-20 opacity-0 group-hover:opacity-100 transition-opacity duration-300"> |
95 | | - <div class="bg-white/90 dark:bg-gray-900/90 rounded-full p-2 shadow-lg"> |
96 | | - <svg class="w-5 h-5 text-gray-900 dark:text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
97 | | - <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v6m3-3H7"></path> |
98 | | - </svg> |
99 | | - </div> |
100 | | - </div> |
101 | | - </div> |
102 | | - </div> |
103 | | - </div> |
104 | | - |
105 | | - <!-- Lightbox Modal --> |
106 | | - <div v-if="lightboxOpen" |
107 | | - @click="closeLightbox" |
108 | | - class="fixed inset-0 z-50 flex items-center justify-center bg-black/95 backdrop-blur-sm p-4"> |
109 | | - |
110 | | - <!-- Close button --> |
111 | | - <button @click="closeLightbox" |
112 | | - class="absolute top-4 right-4 z-50 text-white hover:text-gray-300 transition-colors p-2 hover:bg-white/10 rounded-full"> |
113 | | - <svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
114 | | - <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path> |
115 | | - </svg> |
116 | | - </button> |
117 | | - |
118 | | - <!-- Image title --> |
119 | | - <div class="absolute top-4 left-4 z-50 bg-black/50 backdrop-blur-sm px-4 py-2 rounded-lg"> |
120 | | - <h3 class="text-white text-xl font-semibold capitalize">{{ currentTitle }}</h3> |
121 | | - </div> |
122 | | - |
123 | | - <!-- Image container --> |
124 | | - <div @click.stop class="relative max-w-6xl max-h-[90vh] flex items-center justify-center"> |
125 | | - <img :src="currentImage" |
126 | | - :alt="currentTitle" |
127 | | - class="max-w-full max-h-[90vh] object-contain rounded-lg shadow-2xl"> |
128 | | - </div> |
129 | | - |
130 | | - <!-- Navigation arrows (if multiple images) --> |
131 | | - <button v-if="imageKeys.length > 1" |
132 | | - @click.stop="previousImage" |
133 | | - class="absolute left-4 top-1/2 -translate-y-1/2 text-white hover:text-gray-300 transition-colors p-3 hover:bg-white/10 rounded-full"> |
134 | | - <svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
135 | | - <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path> |
136 | | - </svg> |
137 | | - </button> |
138 | | - |
139 | | - <button v-if="imageKeys.length > 1" |
140 | | - @click.stop="nextImage" |
141 | | - class="absolute right-4 top-1/2 -translate-y-1/2 text-white hover:text-gray-300 transition-colors p-3 hover:bg-white/10 rounded-full"> |
142 | | - <svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
143 | | - <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path> |
144 | | - </svg> |
145 | | - </button> |
146 | | - |
147 | | - <!-- Image counter --> |
148 | | - <div v-if="imageKeys.length > 1" |
149 | | - class="absolute bottom-4 left-1/2 -translate-x-1/2 bg-black/50 backdrop-blur-sm px-4 py-2 rounded-lg"> |
150 | | - <p class="text-white text-sm">{{ currentIndex + 1 }} / {{ imageKeys.length }}</p> |
151 | | - </div> |
152 | | - </div> |
153 | | - </div> |
154 | | - `, |
155 | | - data() { |
156 | | - return { |
157 | | - lightboxOpen: false, |
158 | | - currentImage: '', |
159 | | - currentTitle: '', |
160 | | - currentIndex: 0 |
161 | | - } |
162 | | - }, |
163 | | - computed: { |
164 | | - imageKeys() { |
165 | | - return Object.keys(this.images) |
166 | | - } |
167 | | - }, |
168 | | - methods: { |
169 | | - openLightbox(imageUrl, title) { |
170 | | - this.currentImage = imageUrl |
171 | | - this.currentTitle = title |
172 | | - this.currentIndex = this.imageKeys.indexOf(title) |
173 | | - this.lightboxOpen = true |
174 | | - document.body.style.overflow = 'hidden' |
175 | | - document.addEventListener('keydown', this.handleKeydown) |
176 | | - }, |
177 | | - closeLightbox() { |
178 | | - this.lightboxOpen = false |
179 | | - document.body.style.overflow = '' |
180 | | - document.removeEventListener('keydown', this.handleKeydown) |
181 | | - }, |
182 | | - nextImage() { |
183 | | - this.currentIndex = (this.currentIndex + 1) % this.imageKeys.length |
184 | | - const key = this.imageKeys[this.currentIndex] |
185 | | - this.currentImage = this.images[key] |
186 | | - this.currentTitle = key |
187 | | - }, |
188 | | - previousImage() { |
189 | | - this.currentIndex = (this.currentIndex - 1 + this.imageKeys.length) % this.imageKeys.length |
190 | | - const key = this.imageKeys[this.currentIndex] |
191 | | - this.currentImage = this.images[key] |
192 | | - this.currentTitle = key |
193 | | - }, |
194 | | - handleKeydown(e) { |
195 | | - if (!this.lightboxOpen) return |
196 | | - |
197 | | - switch(e.key) { |
198 | | - case 'Escape': |
199 | | - this.closeLightbox() |
200 | | - break |
201 | | - case 'ArrowRight': |
202 | | - if (this.imageKeys.length > 1) { |
203 | | - this.nextImage() |
204 | | - } |
205 | | - break |
206 | | - case 'ArrowLeft': |
207 | | - if (this.imageKeys.length > 1) { |
208 | | - this.previousImage() |
209 | | - } |
210 | | - break |
211 | | - } |
212 | | - } |
213 | | - }, |
214 | | - beforeUnmount() { |
215 | | - document.removeEventListener('keydown', this.handleKeydown) |
216 | | - } |
217 | | -} |
218 | | - |
219 | 65 | export default { |
220 | 66 | install(app) { |
221 | 67 | }, |
|
0 commit comments