refactor: remove ImageDisplay component and update base module exports
- Deleted the ImageDisplay component to streamline image handling. - Updated the base module to export only the ImageUpload component, simplifying the component structure. These changes enhance the clarity and maintainability of the image handling components in the application.
This commit is contained in:
parent
ca0ac2b9ad
commit
3742937aea
2 changed files with 1 additions and 241 deletions
|
|
@ -1,238 +0,0 @@
|
|||
<template>
|
||||
<div class="image-display">
|
||||
<!-- Primary image display -->
|
||||
<div v-if="primaryImage" class="primary-image relative">
|
||||
<img
|
||||
:src="getImageUrl(primaryImage, props.sizing?.primaryImageOptions)"
|
||||
:alt="alt || 'Image'"
|
||||
:class="imageClass"
|
||||
@click="showLightbox && openLightbox(primaryImage)"
|
||||
:style="{ cursor: showLightbox ? 'pointer' : 'default' }"
|
||||
/>
|
||||
<Badge
|
||||
v-if="showBadge && images.length > 1"
|
||||
class="absolute top-2 right-2"
|
||||
variant="secondary"
|
||||
>
|
||||
1 of {{ images.length }}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<!-- Thumbnail gallery -->
|
||||
<div
|
||||
v-if="showThumbnails && images.length > 1"
|
||||
class="thumbnail-list flex gap-2 mt-3 overflow-x-auto"
|
||||
>
|
||||
<div
|
||||
v-for="(image, index) in images"
|
||||
:key="image.alias"
|
||||
@click="selectImage(image)"
|
||||
class="thumbnail-item flex-shrink-0 cursor-pointer rounded-md overflow-hidden border-2 transition-colors"
|
||||
:class="{
|
||||
'border-primary': image.alias === primaryImage?.alias,
|
||||
'border-transparent hover:border-muted-foreground': image.alias !== primaryImage?.alias
|
||||
}"
|
||||
>
|
||||
<img
|
||||
:src="imageService.getThumbnailUrl(image.alias, thumbnailSize)"
|
||||
:alt="`Thumbnail ${index + 1}`"
|
||||
class="w-16 h-16 object-cover"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lightbox modal -->
|
||||
<Dialog v-if="!isEmbedded" v-model:open="lightboxOpen">
|
||||
<DialogContent class="max-w-4xl p-0">
|
||||
<div class="relative">
|
||||
<img
|
||||
v-if="lightboxImage"
|
||||
:src="getImageUrl(lightboxImage)"
|
||||
:alt="alt || 'Full size image'"
|
||||
class="w-full h-auto"
|
||||
/>
|
||||
<Button
|
||||
@click="lightboxOpen = false"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
class="absolute top-2 right-2 bg-background/80 backdrop-blur hover:bg-background/90"
|
||||
>
|
||||
<X class="h-4 w-4" />
|
||||
</Button>
|
||||
|
||||
<!-- Navigation buttons if multiple images -->
|
||||
<template v-if="images.length > 1">
|
||||
<Button
|
||||
@click="previousImage"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
class="absolute left-2 top-1/2 -translate-y-1/2 bg-background/80 backdrop-blur hover:bg-background/90"
|
||||
>
|
||||
<ChevronLeft class="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
@click="nextImage"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
class="absolute right-2 top-1/2 -translate-y-1/2 bg-background/80 backdrop-blur hover:bg-background/90"
|
||||
>
|
||||
<ChevronRight class="h-4 w-4" />
|
||||
</Button>
|
||||
</template>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
<!-- Embedded lightbox (when used inside another dialog) -->
|
||||
<div
|
||||
v-if="isEmbedded && lightboxOpen"
|
||||
class="fixed inset-0 z-[60] bg-background/80 backdrop-blur-sm flex items-center justify-center p-4"
|
||||
@click.self="lightboxOpen = false"
|
||||
>
|
||||
<div class="relative max-w-[90vw] max-h-[90vh] bg-background rounded-lg p-0 shadow-lg">
|
||||
<div class="relative">
|
||||
<img
|
||||
v-if="lightboxImage"
|
||||
:src="getImageUrl(lightboxImage)"
|
||||
:alt="alt || 'Full size image'"
|
||||
class="max-w-full max-h-[90vh] object-contain rounded-lg"
|
||||
/>
|
||||
<Button
|
||||
@click="lightboxOpen = false"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
class="absolute top-2 right-2 bg-background/80 backdrop-blur hover:bg-background/90"
|
||||
>
|
||||
<X class="h-4 w-4" />
|
||||
</Button>
|
||||
|
||||
<!-- Navigation buttons if multiple images -->
|
||||
<template v-if="images.length > 1">
|
||||
<Button
|
||||
@click="previousImage"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
class="absolute left-2 top-1/2 -translate-y-1/2 bg-background/80 backdrop-blur hover:bg-background/90"
|
||||
>
|
||||
<ChevronLeft class="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
@click="nextImage"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
class="absolute right-2 top-1/2 -translate-y-1/2 bg-background/80 backdrop-blur hover:bg-background/90"
|
||||
>
|
||||
<ChevronRight class="h-4 w-4" />
|
||||
</Button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, defineProps } from 'vue'
|
||||
import { X, ChevronLeft, ChevronRight } from 'lucide-vue-next'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
} from '@/components/ui/dialog'
|
||||
import { injectService, SERVICE_TOKENS } from '@/core/di-container'
|
||||
import type { ImageUploadService, ImageUrlOptions } from '../services/ImageUploadService'
|
||||
|
||||
export interface DisplayImage {
|
||||
alias: string
|
||||
isPrimary?: boolean
|
||||
}
|
||||
|
||||
interface ImageDisplayOptions {
|
||||
showThumbnails?: boolean
|
||||
showLightbox?: boolean
|
||||
showBadge?: boolean
|
||||
isEmbedded?: boolean
|
||||
}
|
||||
|
||||
interface ImageSizingOptions {
|
||||
thumbnailSize?: number
|
||||
primaryImageOptions?: ImageUrlOptions
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
images: DisplayImage[]
|
||||
alt?: string
|
||||
imageClass?: string
|
||||
options?: ImageDisplayOptions
|
||||
sizing?: ImageSizingOptions
|
||||
}>()
|
||||
|
||||
// Inject the image upload service
|
||||
const imageService = injectService<ImageUploadService>(SERVICE_TOKENS.IMAGE_UPLOAD_SERVICE)
|
||||
|
||||
// Component state
|
||||
const selectedImageAlias = ref<string | null>(null)
|
||||
const lightboxOpen = ref(false)
|
||||
const lightboxImage = ref<DisplayImage | null>(null)
|
||||
|
||||
// Computed properties
|
||||
const primaryImage = computed(() => {
|
||||
if (selectedImageAlias.value) {
|
||||
return props.images.find(img => img.alias === selectedImageAlias.value)
|
||||
}
|
||||
return props.images.find(img => img.isPrimary) || props.images[0]
|
||||
})
|
||||
|
||||
const thumbnailSize = computed(() => props.sizing?.thumbnailSize || 100)
|
||||
const isEmbedded = computed(() => props.options?.isEmbedded || false)
|
||||
const showThumbnails = computed(() => props.options?.showThumbnails || false)
|
||||
const showLightbox = computed(() => props.options?.showLightbox || false)
|
||||
const showBadge = computed(() => props.options?.showBadge || false)
|
||||
|
||||
// Methods
|
||||
const getImageUrl = (image: DisplayImage, options?: ImageUrlOptions) => {
|
||||
// Check if the alias is already a full URL
|
||||
if (image.alias.startsWith('http://') || image.alias.startsWith('https://')) {
|
||||
// Already a full URL, return as-is
|
||||
return image.alias
|
||||
}
|
||||
|
||||
// Otherwise, use the image service to generate the URL
|
||||
const defaultOptions = options || props.sizing?.primaryImageOptions || { resize: 800 }
|
||||
return imageService.getImageUrl(image.alias, defaultOptions)
|
||||
}
|
||||
|
||||
const selectImage = (image: DisplayImage) => {
|
||||
selectedImageAlias.value = image.alias
|
||||
}
|
||||
|
||||
const openLightbox = (image: DisplayImage) => {
|
||||
lightboxImage.value = image
|
||||
lightboxOpen.value = true
|
||||
}
|
||||
|
||||
const getCurrentImageIndex = () => {
|
||||
if (!lightboxImage.value) return 0
|
||||
return props.images.findIndex(img => img.alias === lightboxImage.value?.alias)
|
||||
}
|
||||
|
||||
const previousImage = () => {
|
||||
const currentIndex = getCurrentImageIndex()
|
||||
const newIndex = currentIndex > 0 ? currentIndex - 1 : props.images.length - 1
|
||||
lightboxImage.value = props.images[newIndex]
|
||||
}
|
||||
|
||||
const nextImage = () => {
|
||||
const currentIndex = getCurrentImageIndex()
|
||||
const newIndex = currentIndex < props.images.length - 1 ? currentIndex + 1 : 0
|
||||
lightboxImage.value = props.images[newIndex]
|
||||
}
|
||||
|
||||
// Expose methods for parent components
|
||||
defineExpose({
|
||||
selectImage,
|
||||
openLightbox,
|
||||
getCurrentImage: () => primaryImage.value
|
||||
})
|
||||
</script>
|
||||
|
|
@ -20,7 +20,6 @@ import { ImageUploadService } from './services/ImageUploadService'
|
|||
|
||||
// Import components
|
||||
import ImageUpload from './components/ImageUpload.vue'
|
||||
import ImageDisplay from './components/ImageDisplay.vue'
|
||||
|
||||
// Create service instances
|
||||
const invoiceService = new InvoiceService()
|
||||
|
|
@ -143,8 +142,7 @@ export const baseModule: ModulePlugin = {
|
|||
|
||||
// Export components for use by other modules
|
||||
components: {
|
||||
ImageUpload,
|
||||
ImageDisplay
|
||||
ImageUpload
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue