|
| 1 | +<template> |
| 2 | + <img ref="image" v-bind="restAttrs" :src="src" :alt="alt" :class="rootClasses" @click.stop="zoom.open()" /> |
| 3 | +</template> |
| 4 | + |
| 5 | +<style> |
| 6 | + /* medium-zoom appends the overlay and the zoomed image to body: they have to cover the page |
| 7 | + and the docked agent chat (z-40); the zoomed image comes later, so it is above the overlay */ |
| 8 | + .medium-zoom-overlay, |
| 9 | + .medium-zoom-image--opened { |
| 10 | + @apply z-50; |
| 11 | + } |
| 12 | +</style> |
| 13 | + |
| 14 | +<script setup lang="ts"> |
| 15 | +import { computed, onBeforeUnmount, onMounted, useAttrs, useTemplateRef } from 'vue'; |
| 16 | +import mediumZoom, { type Zoom } from 'medium-zoom'; |
| 17 | +import { type ClassNameValue } from 'tailwind-merge'; |
| 18 | +import { mergeClasses } from './classMerge'; |
| 19 | +
|
| 20 | +defineOptions({ inheritAttrs: false }); |
| 21 | +
|
| 22 | +withDefaults(defineProps<{ |
| 23 | + src: string, |
| 24 | + alt?: string, |
| 25 | +}>(), { |
| 26 | + alt: '', |
| 27 | +}); |
| 28 | +
|
| 29 | +// a photo lightbox is dark in both themes, so the overlay is a black tint and not a theme token; |
| 30 | +// medium-zoom sets it inline, a class could not override it |
| 31 | +const OVERLAY_BACKGROUND = 'rgb(0 0 0 / 0.8)'; |
| 32 | +const ZOOM_MARGIN = 24; |
| 33 | +
|
| 34 | +const attrs = useAttrs(); |
| 35 | +const restAttrs = computed(() => { |
| 36 | + const { class: _class, ...rest } = attrs; |
| 37 | + return rest; |
| 38 | +}); |
| 39 | +
|
| 40 | +const rootClasses = computed(() => mergeClasses( |
| 41 | + 'afcl-zoomable-image cursor-zoom-in', |
| 42 | + attrs.class as ClassNameValue, |
| 43 | +)); |
| 44 | +
|
| 45 | +const image = useTemplateRef<HTMLImageElement>('image'); |
| 46 | +// medium-zoom opens an image from a click listener on document, which a parent stopping the click |
| 47 | +// (a table row which opens the record) would never let through. The image opens itself and keeps |
| 48 | +// the click from its parents; closing stays with medium-zoom (overlay, zoomed image, Esc, scroll). |
| 49 | +let zoom: Zoom; |
| 50 | +
|
| 51 | +onMounted(() => { |
| 52 | + zoom = mediumZoom(image.value!, { margin: ZOOM_MARGIN, background: OVERLAY_BACKGROUND }); |
| 53 | +}); |
| 54 | +
|
| 55 | +onBeforeUnmount(() => zoom.detach()); |
| 56 | +</script> |
0 commit comments