feat: Add RTL Support to Widget (#11022)

This PR adds RTL support to the web widget for improved right-to-left language compatibility, updates colors, and cleans up code.

Fixes https://linear.app/chatwoot/issue/CW-4089/rtl-issues-on-widget

https://github.com/chatwoot/chatwoot/issues/9791

Other PR: https://github.com/chatwoot/chatwoot/pull/11016
This commit is contained in:
Sivin Varghese
2025-03-21 22:09:03 +05:30
committed by GitHub
parent e4ea078e52
commit 3a693947b5
76 changed files with 966 additions and 2406 deletions

View File

@@ -1,68 +1,22 @@
<script>
import TeamAvailability from 'widget/components/TeamAvailability.vue';
import ArticleHero from 'widget/components/ArticleHero.vue';
import ArticleCardSkeletonLoader from 'widget/components/ArticleCardSkeletonLoader.vue';
import { mapGetters } from 'vuex';
import { useDarkMode } from 'widget/composables/useDarkMode';
import routerMixin from 'widget/mixins/routerMixin';
import configMixin from 'widget/mixins/configMixin';
import ArticleContainer from '../components/pageComponents/Home/Article/ArticleContainer.vue';
export default {
name: 'Home',
components: {
ArticleHero,
ArticleContainer,
TeamAvailability,
ArticleCardSkeletonLoader,
},
mixins: [configMixin, routerMixin],
setup() {
const { prefersDarkMode } = useDarkMode();
return { prefersDarkMode };
},
computed: {
...mapGetters({
availableAgents: 'agent/availableAgents',
conversationSize: 'conversation/getConversationSize',
unreadMessageCount: 'conversation/getUnreadMessageCount',
popularArticles: 'article/popularArticles',
articleUiFlags: 'article/uiFlags',
}),
widgetLocale() {
return this.$i18n.locale || 'en';
},
portal() {
return window.chatwootWebChannel.portal;
},
showArticles() {
return (
this.portal &&
!this.articleUiFlags.isFetching &&
this.popularArticles.length
);
},
defaultLocale() {
const widgetLocale = this.widgetLocale;
const { allowed_locales: allowedLocales, default_locale: defaultLocale } =
this.portal.config;
// IMPORTANT: Variation strict locale matching, Follow iso_639_1_code
// If the exact match of a locale is available in the list of portal locales, return it
// Else return the default locale. Eg: `es` will not work if `es_ES` is available in the list
if (allowedLocales.includes(widgetLocale)) {
return widgetLocale;
}
return defaultLocale;
},
},
mounted() {
if (this.portal && this.popularArticles.length === 0) {
const locale = this.defaultLocale;
this.$store.dispatch('article/fetch', {
slug: this.portal.slug,
locale,
});
}
},
methods: {
startConversation() {
@@ -71,59 +25,19 @@ export default {
}
return this.replaceRoute('messages');
},
openArticleInArticleViewer(link) {
const params = new URLSearchParams({
show_plain_layout: 'true',
theme: this.prefersDarkMode ? 'dark' : 'light',
});
const linkToOpen = `${link}?${params.toString()}`;
this.$router.push({
name: 'article-viewer',
query: { link: linkToOpen },
});
},
viewAllArticles() {
const locale = this.defaultLocale;
const {
portal: { slug },
} = window.chatwootWebChannel;
this.openArticleInArticleViewer(`/hc/${slug}/${locale}`);
},
},
};
</script>
<template>
<div
class="z-50 flex flex-col flex-1 w-full rounded-md"
:class="{ 'pb-2': showArticles, 'justify-end': !showArticles }"
>
<div class="w-full px-4 pt-4">
<TeamAvailability
:available-agents="availableAgents"
:has-conversation="!!conversationSize"
:unread-count="unreadMessageCount"
@start-conversation="startConversation"
/>
</div>
<div v-if="showArticles" class="w-full px-4 py-2">
<div class="w-full p-4 bg-white rounded-md shadow-sm dark:bg-slate-700">
<ArticleHero
v-if="
!articleUiFlags.isFetching &&
!articleUiFlags.isError &&
popularArticles.length
"
:articles="popularArticles"
@view="openArticleInArticleViewer"
@view-all="viewAllArticles"
/>
</div>
</div>
<div v-if="articleUiFlags.isFetching" class="w-full px-4 py-2">
<div class="w-full p-4 bg-white rounded-md shadow-sm dark:bg-slate-700">
<ArticleCardSkeletonLoader />
</div>
</div>
<div class="z-50 flex flex-col justify-end flex-1 w-full p-4 gap-4">
<TeamAvailability
:available-agents="availableAgents"
:has-conversation="!!conversationSize"
:unread-count="unreadMessageCount"
@start-conversation="startConversation"
/>
<ArticleContainer />
</div>
</template>