mirror of
https://github.com/lingble/chatwoot.git
synced 2025-10-30 02:32:29 +00:00
fix: Fix bubble design in RTL (#10683)
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
<script setup>
|
||||
import { computed, ref, toRefs } from 'vue';
|
||||
import { onMounted, computed, ref, toRefs } from 'vue';
|
||||
import { useTimeoutFn } from '@vueuse/core';
|
||||
import { provideMessageContext } from './provider.js';
|
||||
import { useTrack } from 'dashboard/composables';
|
||||
import { emitter } from 'shared/helpers/mitt';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { LocalStorage } from 'shared/helpers/localStorage';
|
||||
import { ACCOUNT_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
|
||||
import { LOCAL_STORAGE_KEYS } from 'dashboard/constants/localStorage';
|
||||
@@ -115,7 +117,7 @@ const props = defineProps({
|
||||
createdAt: { type: Number, required: true }, // eslint-disable-line vue/no-unused-properties
|
||||
currentUserId: { type: Number, required: true },
|
||||
groupWithNext: { type: Boolean, default: false },
|
||||
inboxId: { type: Number, required: true }, // eslint-disable-line vue/no-unused-properties
|
||||
inboxId: { type: Number, default: null }, // eslint-disable-line vue/no-unused-properties
|
||||
inboxSupportsReplyTo: { type: Object, default: () => ({}) },
|
||||
inReplyTo: { type: Object, default: null }, // eslint-disable-line vue/no-unused-properties
|
||||
isEmailInbox: { type: Boolean, default: false },
|
||||
@@ -127,8 +129,10 @@ const props = defineProps({
|
||||
});
|
||||
|
||||
const contextMenuPosition = ref({});
|
||||
const showBackgroundHighlight = ref(false);
|
||||
const showContextMenu = ref(false);
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
|
||||
/**
|
||||
* Computes the message variant based on props
|
||||
@@ -329,6 +333,22 @@ const contextMenuEnabledOptions = computed(() => {
|
||||
};
|
||||
});
|
||||
|
||||
const shouldRenderMessage = computed(() => {
|
||||
const hasAttachments = !!(props.attachments && props.attachments.length > 0);
|
||||
const isEmailContentType = props.contentType === CONTENT_TYPES.INCOMING_EMAIL;
|
||||
const isUnsupported = props.contentAttributes?.isUnsupported;
|
||||
const isAnIntegrationMessage =
|
||||
props.contentType === CONTENT_TYPES.INTEGRATIONS;
|
||||
|
||||
return (
|
||||
hasAttachments ||
|
||||
props.content ||
|
||||
isEmailContentType ||
|
||||
isUnsupported ||
|
||||
isAnIntegrationMessage
|
||||
);
|
||||
});
|
||||
|
||||
function openContextMenu(e) {
|
||||
const shouldSkipContextMenu =
|
||||
e.target?.classList.contains('skip-context-menu') ||
|
||||
@@ -382,6 +402,20 @@ const avatarInfo = computed(() => {
|
||||
};
|
||||
});
|
||||
|
||||
const setupHighlightTimer = () => {
|
||||
if (Number(route.query.messageId) !== Number(props.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
showBackgroundHighlight.value = true;
|
||||
const HIGHLIGHT_TIMER = 1000;
|
||||
useTimeoutFn(() => {
|
||||
showBackgroundHighlight.value = false;
|
||||
}, HIGHLIGHT_TIMER);
|
||||
};
|
||||
|
||||
onMounted(setupHighlightTimer);
|
||||
|
||||
provideMessageContext({
|
||||
...toRefs(props),
|
||||
isPrivate: computed(() => props.private),
|
||||
@@ -392,14 +426,19 @@ provideMessageContext({
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- eslint-disable-next-line vue/no-root-v-if -->
|
||||
<template>
|
||||
<div
|
||||
v-if="shouldRenderMessage"
|
||||
:id="`message${props.id}`"
|
||||
class="flex w-full message-bubble-container"
|
||||
:data-message-id="props.id"
|
||||
:class="[
|
||||
flexOrientationClass,
|
||||
shouldGroupWithNext ? 'group-with-next mb-2' : 'mb-4',
|
||||
{
|
||||
'bg-n-alpha-1': showBackgroundHighlight,
|
||||
},
|
||||
]"
|
||||
>
|
||||
<div v-if="variant === MESSAGE_VARIANTS.ACTIVITY">
|
||||
@@ -428,7 +467,7 @@ provideMessageContext({
|
||||
<div
|
||||
class="[grid-area:bubble] flex"
|
||||
:class="{
|
||||
'pl-9 justify-end': orientation === ORIENTATION.RIGHT,
|
||||
'ltr:pl-9 rtl:pl-0 justify-end': orientation === ORIENTATION.RIGHT,
|
||||
'min-w-0': variant === MESSAGE_VARIANTS.EMAIL,
|
||||
}"
|
||||
@contextmenu="openContextMenu($event)"
|
||||
@@ -461,11 +500,11 @@ provideMessageContext({
|
||||
<style lang="scss">
|
||||
.group-with-next + .message-bubble-container {
|
||||
.left-bubble {
|
||||
@apply rounded-tl-sm;
|
||||
@apply ltr:rounded-tl-sm rtl:rounded-tr-sm;
|
||||
}
|
||||
|
||||
.right-bubble {
|
||||
@apply rounded-tr-sm;
|
||||
@apply ltr:rounded-tr-sm rtl:rounded-tl-sm;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -28,8 +28,8 @@ const { t } = useI18n();
|
||||
<div
|
||||
class="absolute bg-n-alpha-3 px-4 py-3 border rounded-xl border-n-strong text-n-slate-12 bottom-6 w-52 text-xs backdrop-blur-[100px] shadow-[0px_0px_24px_0px_rgba(0,0,0,0.12)] opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all"
|
||||
:class="{
|
||||
'left-0': orientation === ORIENTATION.LEFT,
|
||||
'right-0': orientation === ORIENTATION.RIGHT,
|
||||
'ltr:left-0 rtl:right-0': orientation === ORIENTATION.LEFT,
|
||||
'ltr:right-0 rtl:left-0': orientation === ORIENTATION.RIGHT,
|
||||
}"
|
||||
>
|
||||
{{ error }}
|
||||
|
||||
@@ -13,13 +13,10 @@ const readableTime = computed(() =>
|
||||
|
||||
<template>
|
||||
<BaseBubble
|
||||
class="px-2 py-0.5 !rounded-full flex items-center gap-2"
|
||||
v-tooltip.top="readableTime"
|
||||
class="px-2 py-0.5 !rounded-full flex min-w-0 items-center gap-2"
|
||||
data-bubble-name="activity"
|
||||
>
|
||||
<span v-dompurify-html="content" :title="content" class="truncate" />
|
||||
<div v-if="readableTime" class="w-px h-3 rounded-full bg-n-slate-7" />
|
||||
<time class="text-n-slate-10 truncate flex-shrink" :title="readableTime">
|
||||
{{ readableTime }}
|
||||
</time>
|
||||
<span v-dompurify-html="content" :title="content" />
|
||||
</BaseBubble>
|
||||
</template>
|
||||
|
||||
@@ -29,8 +29,10 @@ const varaintBaseMap = {
|
||||
};
|
||||
|
||||
const orientationMap = {
|
||||
[ORIENTATION.LEFT]: 'left-bubble rounded-xl rounded-bl-sm',
|
||||
[ORIENTATION.RIGHT]: 'right-bubble rounded-xl rounded-br-sm',
|
||||
[ORIENTATION.LEFT]:
|
||||
'left-bubble rounded-xl ltr:rounded-bl-sm rtl:rounded-br-sm',
|
||||
[ORIENTATION.RIGHT]:
|
||||
'right-bubble rounded-xl ltr:rounded-br-sm rtl:rounded-bl-sm',
|
||||
[ORIENTATION.CENTER]: 'rounded-md',
|
||||
};
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ const showMeta = computed(() => {
|
||||
<template>
|
||||
<section
|
||||
v-show="showMeta"
|
||||
class="space-y-1 pr-9 border-b border-n-strong text-sm"
|
||||
class="space-y-1 rtl:pl-9 ltr:pr-9 border-b border-n-strong text-sm break-words"
|
||||
:class="hasError ? 'text-n-ruby-11' : 'text-n-slate-11'"
|
||||
>
|
||||
<template v-if="showMeta">
|
||||
|
||||
@@ -109,7 +109,10 @@ const textToShow = computed(() => {
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
<section v-if="attachments.length" class="px-4 pb-4 space-y-2">
|
||||
<section
|
||||
v-if="Array.isArray(attachments) && attachments.length"
|
||||
class="px-4 pb-4 space-y-2"
|
||||
>
|
||||
<AttachmentChips :attachments="attachments" class="gap-1" />
|
||||
</section>
|
||||
</BaseBubble>
|
||||
|
||||
@@ -14,7 +14,7 @@ const isTemplate = computed(() => {
|
||||
});
|
||||
|
||||
const isEmpty = computed(() => {
|
||||
return !content.value && !attachments.value.length;
|
||||
return !content.value && !attachments.value?.length;
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -109,15 +109,31 @@ const tailwindConfig = {
|
||||
'ul li': {
|
||||
margin: '0 0 0.5em 1em',
|
||||
listStyleType: 'disc',
|
||||
'[dir="rtl"] &': {
|
||||
margin: '0 1em 0.5em 0',
|
||||
},
|
||||
},
|
||||
'ol li': {
|
||||
margin: '0 0 0.5em 1em',
|
||||
listStyleType: 'decimal',
|
||||
'[dir="rtl"] &': {
|
||||
margin: '0 1em 0.5em 0',
|
||||
},
|
||||
},
|
||||
blockquote: {
|
||||
color: 'rgb(var(--slate-11))',
|
||||
borderLeft: `4px solid rgb(var(--black-alpha-1))`,
|
||||
paddingLeft: '1em',
|
||||
'[dir="rtl"] &': {
|
||||
borderLeft: 'none',
|
||||
paddingLeft: '0',
|
||||
borderRight: `4px solid rgb(var(--black-alpha-1))`,
|
||||
paddingRight: '1em',
|
||||
},
|
||||
'[dir="ltr"] &': {
|
||||
borderRight: 'none',
|
||||
paddingRight: '0',
|
||||
},
|
||||
},
|
||||
code: {
|
||||
backgroundColor: 'rgb(var(--alpha-3))',
|
||||
@@ -146,7 +162,7 @@ const tailwindConfig = {
|
||||
padding: '0.75em',
|
||||
color: 'rgb(var(--slate-12))',
|
||||
border: `none`,
|
||||
textAlign: 'left',
|
||||
textAlign: 'start',
|
||||
fontWeight: '600',
|
||||
},
|
||||
tr: {
|
||||
|
||||
Reference in New Issue
Block a user