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:
padreug 2025-09-28 12:57:57 +02:00
parent ca0ac2b9ad
commit 3742937aea
2 changed files with 1 additions and 241 deletions

View file

@ -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>

View file

@ -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
}
}