fix: Fix the issue with context menu for right click on images and videos (#11114)
This pull request includes changes to various components adding a `skip-context-menu` class to ensure the system context menu opens instead of the Chatwoot message one --------- Co-authored-by: Pranav <pranav@chatwoot.com> Co-authored-by: Pranav <pranavrajs@gmail.com>
This commit is contained in:
@@ -13,6 +13,9 @@ const attachment = computed(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<BaseBubble class="bg-transparent" data-bubble-name="audio">
|
<BaseBubble class="bg-transparent" data-bubble-name="audio">
|
||||||
<AudioChip :attachment="attachment" class="p-2 text-n-slate-12" />
|
<AudioChip
|
||||||
|
:attachment="attachment"
|
||||||
|
class="p-2 text-n-slate-12 skip-context-menu"
|
||||||
|
/>
|
||||||
</BaseBubble>
|
</BaseBubble>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ const downloadAttachment = async () => {
|
|||||||
</div>
|
</div>
|
||||||
<div v-else class="relative group rounded-lg overflow-hidden">
|
<div v-else class="relative group rounded-lg overflow-hidden">
|
||||||
<img
|
<img
|
||||||
|
class="skip-context-menu"
|
||||||
:src="attachment.dataUrl"
|
:src="attachment.dataUrl"
|
||||||
:width="attachment.width"
|
:width="attachment.width"
|
||||||
:height="attachment.height"
|
:height="attachment.height"
|
||||||
@@ -63,8 +64,9 @@ const downloadAttachment = async () => {
|
|||||||
@error="handleError"
|
@error="handleError"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="inset-0 p-2 absolute bg-gradient-to-tl from-n-slate-12/30 dark:from-n-slate-1/50 via-transparent to-transparent hidden group-hover:flex items-end justify-end gap-1.5"
|
class="inset-0 p-2 pointer-events-none absolute bg-gradient-to-tl from-n-slate-12/30 dark:from-n-slate-1/50 via-transparent to-transparent hidden group-hover:flex"
|
||||||
>
|
/>
|
||||||
|
<div class="absolute right-2 bottom-2 hidden group-hover:flex gap-2">
|
||||||
<Button xs solid slate icon="i-lucide-expand" class="opacity-60" />
|
<Button xs solid slate icon="i-lucide-expand" class="opacity-60" />
|
||||||
<Button
|
<Button
|
||||||
xs
|
xs
|
||||||
|
|||||||
@@ -41,13 +41,13 @@ const onVideoLoadError = () => {
|
|||||||
<div v-if="content" v-dompurify-html="formattedContent" class="mb-2" />
|
<div v-if="content" v-dompurify-html="formattedContent" class="mb-2" />
|
||||||
<img
|
<img
|
||||||
v-if="!hasImgStoryError"
|
v-if="!hasImgStoryError"
|
||||||
class="rounded-lg max-w-80"
|
class="rounded-lg max-w-80 skip-context-menu"
|
||||||
:src="attachment.dataUrl"
|
:src="attachment.dataUrl"
|
||||||
@error="onImageLoadError"
|
@error="onImageLoadError"
|
||||||
/>
|
/>
|
||||||
<video
|
<video
|
||||||
v-else-if="!hasVideoStoryError"
|
v-else-if="!hasVideoStoryError"
|
||||||
class="rounded-lg max-w-80"
|
class="rounded-lg max-w-80 skip-context-menu"
|
||||||
controls
|
controls
|
||||||
:src="attachment.dataUrl"
|
:src="attachment.dataUrl"
|
||||||
@error="onVideoLoadError"
|
@error="onVideoLoadError"
|
||||||
|
|||||||
@@ -35,13 +35,13 @@ const isReel = computed(() => {
|
|||||||
<div class="relative group rounded-lg overflow-hidden">
|
<div class="relative group rounded-lg overflow-hidden">
|
||||||
<div
|
<div
|
||||||
v-if="isReel"
|
v-if="isReel"
|
||||||
class="absolute p-2 flex items-start justify-end right-0"
|
class="absolute p-2 flex items-start justify-end right-0 pointer-events-none"
|
||||||
>
|
>
|
||||||
<Icon icon="i-lucide-instagram" class="text-white shadow-lg" />
|
<Icon icon="i-lucide-instagram" class="text-white shadow-lg" />
|
||||||
</div>
|
</div>
|
||||||
<video
|
<video
|
||||||
controls
|
controls
|
||||||
class="rounded-lg"
|
class="rounded-lg skip-context-menu"
|
||||||
:src="attachment.dataUrl"
|
:src="attachment.dataUrl"
|
||||||
:class="{
|
:class="{
|
||||||
'max-w-48': isReel,
|
'max-w-48': isReel,
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ const handleError = () => {
|
|||||||
</div>
|
</div>
|
||||||
<img
|
<img
|
||||||
v-else
|
v-else
|
||||||
class="object-cover w-full h-full"
|
class="object-cover w-full h-full skip-context-menu"
|
||||||
:src="attachment.dataUrl"
|
:src="attachment.dataUrl"
|
||||||
@error="handleError"
|
@error="handleError"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -166,188 +166,190 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<woot-modal
|
<Teleport to="body">
|
||||||
v-model:show="show"
|
<woot-modal
|
||||||
full-width
|
v-model:show="show"
|
||||||
:show-close-button="false"
|
full-width
|
||||||
:on-close="onClose"
|
:show-close-button="false"
|
||||||
>
|
:on-close="onClose"
|
||||||
<div
|
|
||||||
class="bg-n-background flex flex-col h-[inherit] w-[inherit] overflow-hidden select-none"
|
|
||||||
@click="onClose"
|
|
||||||
>
|
>
|
||||||
<header
|
<div
|
||||||
class="z-10 flex items-center justify-between w-full h-16 px-6 py-2 bg-n-background border-b border-n-weak"
|
class="bg-n-background flex flex-col h-[inherit] w-[inherit] overflow-hidden select-none"
|
||||||
@click.stop
|
@click="onClose"
|
||||||
>
|
>
|
||||||
<div
|
<header
|
||||||
v-if="senderDetails"
|
class="z-10 flex items-center justify-between w-full h-16 px-6 py-2 bg-n-background border-b border-n-weak"
|
||||||
class="flex items-center min-w-[15rem] shrink-0"
|
@click.stop
|
||||||
>
|
>
|
||||||
<Thumbnail
|
|
||||||
v-if="senderDetails.avatar"
|
|
||||||
:username="senderDetails.name"
|
|
||||||
:src="senderDetails.avatar"
|
|
||||||
class="flex-shrink-0"
|
|
||||||
/>
|
|
||||||
<div class="flex flex-col ml-2 rtl:ml-0 rtl:mr-2 overflow-hidden">
|
|
||||||
<h3 class="text-base leading-5 m-0 font-medium">
|
|
||||||
<span
|
|
||||||
class="overflow-hidden text-n-slate-12 whitespace-nowrap text-ellipsis"
|
|
||||||
>
|
|
||||||
{{ senderDetails.name }}
|
|
||||||
</span>
|
|
||||||
</h3>
|
|
||||||
<span
|
|
||||||
class="text-xs text-n-slate-11 whitespace-nowrap text-ellipsis"
|
|
||||||
>
|
|
||||||
{{ readableTime }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="flex-1 mx-2 px-2 truncate text-sm font-medium text-center text-n-slate-12"
|
|
||||||
>
|
|
||||||
<span v-dompurify-html="fileNameFromDataUrl" class="truncate" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex items-center gap-2 ml-2 shrink-0">
|
|
||||||
<NextButton
|
|
||||||
v-if="isImage"
|
|
||||||
icon="i-lucide-zoom-in"
|
|
||||||
slate
|
|
||||||
ghost
|
|
||||||
@click="onZoom(0.1)"
|
|
||||||
/>
|
|
||||||
<NextButton
|
|
||||||
v-if="isImage"
|
|
||||||
icon="i-lucide-zoom-out"
|
|
||||||
slate
|
|
||||||
ghost
|
|
||||||
@click="onZoom(-0.1)"
|
|
||||||
/>
|
|
||||||
<NextButton
|
|
||||||
v-if="isImage"
|
|
||||||
icon="i-lucide-rotate-ccw"
|
|
||||||
slate
|
|
||||||
ghost
|
|
||||||
@click="onRotate('counter-clockwise')"
|
|
||||||
/>
|
|
||||||
<NextButton
|
|
||||||
v-if="isImage"
|
|
||||||
icon="i-lucide-rotate-cw"
|
|
||||||
slate
|
|
||||||
ghost
|
|
||||||
@click="onRotate('clockwise')"
|
|
||||||
/>
|
|
||||||
<NextButton
|
|
||||||
icon="i-lucide-download"
|
|
||||||
slate
|
|
||||||
ghost
|
|
||||||
:is-loading="isDownloading"
|
|
||||||
:disabled="isDownloading"
|
|
||||||
@click="onClickDownload"
|
|
||||||
/>
|
|
||||||
<NextButton icon="i-lucide-x" slate ghost @click="onClose" />
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main class="flex items-stretch flex-1 h-full overflow-hidden">
|
|
||||||
<div class="flex items-center justify-center w-16 shrink-0">
|
|
||||||
<NextButton
|
|
||||||
v-if="hasMoreThanOneAttachment"
|
|
||||||
icon="i-lucide-chevron-left"
|
|
||||||
class="z-10"
|
|
||||||
blue
|
|
||||||
faded
|
|
||||||
lg
|
|
||||||
:disabled="activeImageIndex === 0"
|
|
||||||
@click.stop="
|
|
||||||
onClickChangeAttachment(
|
|
||||||
allAttachments[activeImageIndex - 1],
|
|
||||||
activeImageIndex - 1
|
|
||||||
)
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex-1 flex items-center justify-center overflow-hidden">
|
|
||||||
<div
|
<div
|
||||||
v-if="isImage"
|
v-if="senderDetails"
|
||||||
:style="imageWrapperStyle"
|
class="flex items-center min-w-[15rem] shrink-0"
|
||||||
class="flex items-center justify-center origin-center"
|
|
||||||
:class="{
|
|
||||||
// Adjust dimensions when rotated 90/270 degrees to maintain visibility
|
|
||||||
// and prevent image from overflowing container in different aspect ratios
|
|
||||||
'w-[calc(100dvh-8rem)] h-[calc(100dvw-7rem)]':
|
|
||||||
activeImageRotation % 180 !== 0,
|
|
||||||
'size-full': activeImageRotation % 180 === 0,
|
|
||||||
}"
|
|
||||||
>
|
>
|
||||||
<img
|
<Thumbnail
|
||||||
ref="imageRef"
|
v-if="senderDetails.avatar"
|
||||||
:key="activeAttachment.message_id"
|
:username="senderDetails.name"
|
||||||
:src="activeAttachment.data_url"
|
:src="senderDetails.avatar"
|
||||||
:style="imageStyle"
|
class="flex-shrink-0"
|
||||||
class="max-h-full max-w-full object-contain duration-100 ease-in-out transform select-none"
|
/>
|
||||||
@click.stop
|
<div class="flex flex-col ml-2 rtl:ml-0 rtl:mr-2 overflow-hidden">
|
||||||
@dblclick.stop="onDoubleClickZoomImage"
|
<h3 class="text-base leading-5 m-0 font-medium">
|
||||||
@wheel.prevent.stop="onWheelImageZoom"
|
<span
|
||||||
@mousemove="onMouseMove"
|
class="overflow-hidden text-n-slate-12 whitespace-nowrap text-ellipsis"
|
||||||
@mouseleave="onMouseLeave"
|
>
|
||||||
|
{{ senderDetails.name }}
|
||||||
|
</span>
|
||||||
|
</h3>
|
||||||
|
<span
|
||||||
|
class="text-xs text-n-slate-11 whitespace-nowrap text-ellipsis"
|
||||||
|
>
|
||||||
|
{{ readableTime }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="flex-1 mx-2 px-2 truncate text-sm font-medium text-center text-n-slate-12"
|
||||||
|
>
|
||||||
|
<span v-dompurify-html="fileNameFromDataUrl" class="truncate" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center gap-2 ml-2 shrink-0">
|
||||||
|
<NextButton
|
||||||
|
v-if="isImage"
|
||||||
|
icon="i-lucide-zoom-in"
|
||||||
|
slate
|
||||||
|
ghost
|
||||||
|
@click="onZoom(0.1)"
|
||||||
|
/>
|
||||||
|
<NextButton
|
||||||
|
v-if="isImage"
|
||||||
|
icon="i-lucide-zoom-out"
|
||||||
|
slate
|
||||||
|
ghost
|
||||||
|
@click="onZoom(-0.1)"
|
||||||
|
/>
|
||||||
|
<NextButton
|
||||||
|
v-if="isImage"
|
||||||
|
icon="i-lucide-rotate-ccw"
|
||||||
|
slate
|
||||||
|
ghost
|
||||||
|
@click="onRotate('counter-clockwise')"
|
||||||
|
/>
|
||||||
|
<NextButton
|
||||||
|
v-if="isImage"
|
||||||
|
icon="i-lucide-rotate-cw"
|
||||||
|
slate
|
||||||
|
ghost
|
||||||
|
@click="onRotate('clockwise')"
|
||||||
|
/>
|
||||||
|
<NextButton
|
||||||
|
icon="i-lucide-download"
|
||||||
|
slate
|
||||||
|
ghost
|
||||||
|
:is-loading="isDownloading"
|
||||||
|
:disabled="isDownloading"
|
||||||
|
@click="onClickDownload"
|
||||||
|
/>
|
||||||
|
<NextButton icon="i-lucide-x" slate ghost @click="onClose" />
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="flex items-stretch flex-1 h-full overflow-hidden">
|
||||||
|
<div class="flex items-center justify-center w-16 shrink-0">
|
||||||
|
<NextButton
|
||||||
|
v-if="hasMoreThanOneAttachment"
|
||||||
|
icon="i-lucide-chevron-left"
|
||||||
|
class="z-10"
|
||||||
|
blue
|
||||||
|
faded
|
||||||
|
lg
|
||||||
|
:disabled="activeImageIndex === 0"
|
||||||
|
@click.stop="
|
||||||
|
onClickChangeAttachment(
|
||||||
|
allAttachments[activeImageIndex - 1],
|
||||||
|
activeImageIndex - 1
|
||||||
|
)
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<video
|
<div class="flex-1 flex items-center justify-center overflow-hidden">
|
||||||
v-if="isVideo"
|
<div
|
||||||
:key="activeAttachment.message_id"
|
v-if="isImage"
|
||||||
:src="activeAttachment.data_url"
|
:style="imageWrapperStyle"
|
||||||
controls
|
class="flex items-center justify-center origin-center"
|
||||||
playsInline
|
:class="{
|
||||||
class="max-h-full max-w-full object-contain"
|
// Adjust dimensions when rotated 90/270 degrees to maintain visibility
|
||||||
@click.stop
|
// and prevent image from overflowing container in different aspect ratios
|
||||||
/>
|
'w-[calc(100dvh-8rem)] h-[calc(100dvw-7rem)]':
|
||||||
|
activeImageRotation % 180 !== 0,
|
||||||
|
'size-full': activeImageRotation % 180 === 0,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
ref="imageRef"
|
||||||
|
:key="activeAttachment.message_id"
|
||||||
|
:src="activeAttachment.data_url"
|
||||||
|
:style="imageStyle"
|
||||||
|
class="max-h-full max-w-full object-contain duration-100 ease-in-out transform select-none"
|
||||||
|
@click.stop
|
||||||
|
@dblclick.stop="onDoubleClickZoomImage"
|
||||||
|
@wheel.prevent.stop="onWheelImageZoom"
|
||||||
|
@mousemove="onMouseMove"
|
||||||
|
@mouseleave="onMouseLeave"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<audio
|
<video
|
||||||
v-if="isAudio"
|
v-if="isVideo"
|
||||||
:key="activeAttachment.message_id"
|
:key="activeAttachment.message_id"
|
||||||
controls
|
:src="activeAttachment.data_url"
|
||||||
class="w-full max-w-md"
|
controls
|
||||||
@click.stop
|
playsInline
|
||||||
>
|
class="max-h-full max-w-full object-contain"
|
||||||
<source :src="`${activeAttachment.data_url}?t=${Date.now()}`" />
|
@click.stop
|
||||||
</audio>
|
/>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex items-center justify-center w-16 shrink-0">
|
<audio
|
||||||
<NextButton
|
v-if="isAudio"
|
||||||
v-if="hasMoreThanOneAttachment"
|
:key="activeAttachment.message_id"
|
||||||
icon="i-lucide-chevron-right"
|
controls
|
||||||
class="z-10"
|
class="w-full max-w-md"
|
||||||
blue
|
@click.stop
|
||||||
faded
|
>
|
||||||
lg
|
<source :src="`${activeAttachment.data_url}?t=${Date.now()}`" />
|
||||||
:disabled="activeImageIndex === allAttachments.length - 1"
|
</audio>
|
||||||
@click.stop="
|
</div>
|
||||||
onClickChangeAttachment(
|
|
||||||
allAttachments[activeImageIndex + 1],
|
|
||||||
activeImageIndex + 1
|
|
||||||
)
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<footer
|
<div class="flex items-center justify-center w-16 shrink-0">
|
||||||
class="z-10 flex items-center justify-center h-12 border-t border-n-weak"
|
<NextButton
|
||||||
>
|
v-if="hasMoreThanOneAttachment"
|
||||||
<div
|
icon="i-lucide-chevron-right"
|
||||||
class="rounded-md flex items-center justify-center px-3 py-1 bg-n-slate-3 text-n-slate-12 text-sm font-medium"
|
class="z-10"
|
||||||
|
blue
|
||||||
|
faded
|
||||||
|
lg
|
||||||
|
:disabled="activeImageIndex === allAttachments.length - 1"
|
||||||
|
@click.stop="
|
||||||
|
onClickChangeAttachment(
|
||||||
|
allAttachments[activeImageIndex + 1],
|
||||||
|
activeImageIndex + 1
|
||||||
|
)
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer
|
||||||
|
class="z-10 flex items-center justify-center h-12 border-t border-n-weak"
|
||||||
>
|
>
|
||||||
{{ `${activeImageIndex + 1} / ${allAttachments.length}` }}
|
<div
|
||||||
</div>
|
class="rounded-md flex items-center justify-center px-3 py-1 bg-n-slate-3 text-n-slate-12 text-sm font-medium"
|
||||||
</footer>
|
>
|
||||||
</div>
|
{{ `${activeImageIndex + 1} / ${allAttachments.length}` }}
|
||||||
</woot-modal>
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</woot-modal>
|
||||||
|
</Teleport>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user