From 3a693947b56b5173208d3cf59591652363d0c943 Mon Sep 17 00:00:00 2001
From: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Date: Fri, 21 Mar 2025 22:09:03 +0530
Subject: [PATCH] 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
---
.../assets/scss/_helper-classes.scss | 1 -
.../dashboard/assets/scss/_mixins.scss | 145 --------
.../widgets/conversation/Message.vue | 6 +-
app/javascript/portal/application.scss | 5 -
.../shared/assets/fonts/widget_fonts.scss | 40 +++
app/javascript/shared/components/Branding.vue | 30 +-
app/javascript/shared/components/Button.vue | 2 +-
.../shared/components/CardButton.vue | 14 +-
app/javascript/shared/components/ChatCard.vue | 59 +---
app/javascript/shared/components/ChatForm.vue | 104 +-----
.../shared/components/ChatOption.vue | 23 +-
.../shared/components/ChatOptions.vue | 80 +----
.../components/CustomerSatisfaction.vue | 86 +----
.../shared/components/DateSeparator.vue | 39 +--
.../shared/components/IframeLoader.vue | 30 ++
app/javascript/shared/components/Spinner.vue | 69 +---
.../__snapshots__/DateSeparator.spec.js.snap | 12 +-
app/javascript/survey/assets/scss/woot.scss | 6 +-
app/javascript/survey/views/Response.vue | 4 +-
app/javascript/widget/App.vue | 14 +-
.../widget/assets/scss/_buttons.scss | 71 ----
app/javascript/widget/assets/scss/_forms.scss | 83 -----
.../widget/assets/scss/_mixins.scss | 78 -----
.../widget/assets/scss/_utilities.scss | 3 -
.../widget/assets/scss/_variables.scss | 87 -----
.../assets/scss/views/_conversation.scss | 118 +++----
app/javascript/widget/assets/scss/woot.scss | 331 +++++++++++++++++-
.../widget/components/AgentMessage.vue | 33 +-
.../widget/components/AgentMessageBubble.vue | 8 +-
.../widget/components/AgentTypingBubble.vue | 33 +-
.../components/ArticleCardSkeletonLoader.vue | 15 -
.../widget/components/ArticleCategoryCard.vue | 54 ---
.../widget/components/ArticleHero.vue | 27 --
.../widget/components/ArticleList.vue | 36 --
.../widget/components/ArticleListItem.vue | 41 ---
.../widget/components/ArticleSearch.vue | 52 ---
.../widget/components/AvailableAgents.vue | 27 --
app/javascript/widget/components/Banner.vue | 12 +-
.../widget/components/ChatAttachment.vue | 2 +-
.../widget/components/ChatFooter.vue | 20 --
.../widget/components/ChatHeader.vue | 32 +-
.../widget/components/ChatHeaderExpanded.vue | 59 ++--
.../widget/components/ChatInputWrap.vue | 81 +----
.../widget/components/ChatMessage.vue | 53 ---
.../widget/components/ChatSendButton.vue | 2 +-
.../widget/components/ContinueChatButton.vue | 62 ----
.../widget/components/ConversationWrap.vue | 5 +-
.../widget/components/FileBubble.vue | 49 +--
.../widget/components/Form/PhoneInput.vue | 107 ++----
.../widget/components/GroupedAvatars.vue | 32 +-
.../widget/components/HeaderActions.vue | 36 +-
.../widget/components/ImageBubble.vue | 15 +-
.../widget/components/PreChat/Form.vue | 67 ++--
.../widget/components/SearchArticle.vue | 52 ---
.../widget/components/TeamAvailability.vue | 33 +-
.../widget/components/UnreadMessage.vue | 27 +-
.../widget/components/UnreadMessageList.vue | 37 +-
.../widget/components/UserAvatar.vue | 49 ---
.../widget/components/UserMessage.vue | 4 +-
.../widget/components/UserMessageBubble.vue | 18 +-
.../components/dropdown/DropdownMenu.vue | 84 -----
.../components/dropdown/DropdownMenuItem.vue | 71 ----
.../components/layouts/ViewWithHeader.vue | 32 +-
.../Home/Article/ArticleBlock.vue | 48 +++
.../Home/Article/ArticleContainer.vue | 86 +++++
.../Home/Article/ArticleListItem.vue | 32 ++
.../Home/Article/SkeletonLoader.vue | 15 +
.../widget/components/template/Article.vue | 59 +---
.../widget/components/template/EmailInput.vue | 41 +--
.../components/template/IntegrationCard.vue | 13 +-
.../composables/specs/useDarkMode.spec.js | 24 +-
.../widget/composables/useDarkMode.js | 9 -
app/javascript/widget/router.js | 18 +-
app/javascript/widget/views/ArticleViewer.vue | 10 +-
app/javascript/widget/views/Home.vue | 108 +-----
app/javascript/widget/views/Messages.vue | 2 +-
76 files changed, 966 insertions(+), 2406 deletions(-)
delete mode 100755 app/javascript/widget/assets/scss/_buttons.scss
delete mode 100755 app/javascript/widget/assets/scss/_forms.scss
delete mode 100755 app/javascript/widget/assets/scss/_mixins.scss
delete mode 100644 app/javascript/widget/assets/scss/_utilities.scss
delete mode 100755 app/javascript/widget/assets/scss/_variables.scss
delete mode 100644 app/javascript/widget/components/ArticleCardSkeletonLoader.vue
delete mode 100644 app/javascript/widget/components/ArticleCategoryCard.vue
delete mode 100644 app/javascript/widget/components/ArticleHero.vue
delete mode 100644 app/javascript/widget/components/ArticleList.vue
delete mode 100644 app/javascript/widget/components/ArticleListItem.vue
delete mode 100644 app/javascript/widget/components/ArticleSearch.vue
delete mode 100644 app/javascript/widget/components/AvailableAgents.vue
delete mode 100644 app/javascript/widget/components/ContinueChatButton.vue
delete mode 100644 app/javascript/widget/components/SearchArticle.vue
delete mode 100755 app/javascript/widget/components/UserAvatar.vue
delete mode 100644 app/javascript/widget/components/dropdown/DropdownMenu.vue
delete mode 100644 app/javascript/widget/components/dropdown/DropdownMenuItem.vue
create mode 100644 app/javascript/widget/components/pageComponents/Home/Article/ArticleBlock.vue
create mode 100644 app/javascript/widget/components/pageComponents/Home/Article/ArticleContainer.vue
create mode 100644 app/javascript/widget/components/pageComponents/Home/Article/ArticleListItem.vue
create mode 100644 app/javascript/widget/components/pageComponents/Home/Article/SkeletonLoader.vue
diff --git a/app/javascript/dashboard/assets/scss/_helper-classes.scss b/app/javascript/dashboard/assets/scss/_helper-classes.scss
index 48ee1918b..229606254 100644
--- a/app/javascript/dashboard/assets/scss/_helper-classes.scss
+++ b/app/javascript/dashboard/assets/scss/_helper-classes.scss
@@ -4,7 +4,6 @@
@apply inline-block h-6 py-0 px-6 relative align-middle w-6;
&.message {
- @include normal-shadow;
@apply bg-white dark:bg-slate-800 rounded-full left-0 my-3 mx-auto p-4 top-0;
&::before {
diff --git a/app/javascript/dashboard/assets/scss/_mixins.scss b/app/javascript/dashboard/assets/scss/_mixins.scss
index 9ca87c859..fc742d24f 100644
--- a/app/javascript/dashboard/assets/scss/_mixins.scss
+++ b/app/javascript/dashboard/assets/scss/_mixins.scss
@@ -1,79 +1,7 @@
@import 'dashboard/assets/scss/variables';
-@import 'widget/assets/scss/mixins';
$spinner-before-border-color: rgba(255, 255, 255, 0.7);
-//borders
-@mixin border-nil() {
- border-color: transparent;
- border: 0;
-}
-
-@mixin thin-border($color) {
- border: 1px solid $color;
-}
-
-@mixin custom-border-bottom($size, $color) {
- border-bottom: $size solid $color;
-}
-
-@mixin custom-border-top($size, $color) {
- border-top: $size solid $color;
-}
-
-@mixin border-normal() {
- @apply border border-slate-50 dark:border-slate-700;
-}
-
-@mixin border-normal-left() {
- @apply border-l border-slate-50 dark:border-slate-700;
-}
-
-@mixin border-normal-top() {
- @apply border-t border-slate-50 dark:border-slate-700;
-}
-
-@mixin border-normal-right() {
- @apply border-r border-slate-50 dark:border-slate-700;
-}
-
-@mixin border-normal-bottom() {
- @apply border-b border-slate-50 dark:border-slate-700;
-}
-
-@mixin border-light() {
- @apply border border-slate-25 dark:border-slate-700;
-}
-
-@mixin border-light-left() {
- @apply border-l border-slate-25 dark:border-slate-700;
-}
-
-@mixin border-light-top() {
- @apply border-t border-slate-25 dark:border-slate-700;
-}
-
-@mixin border-light-right() {
- @apply border-r border-slate-25 dark:border-slate-700;
-}
-
-@mixin border-light-bottom() {
- @apply border-b border-slate-25 dark:border-slate-700;
-}
-
-// background
-@mixin background-gray() {
- background: $color-background;
-}
-
-@mixin background-light() {
- @apply bg-slate-50 dark:bg-slate-800;
-}
-
-@mixin background-white() {
- @apply bg-white dark:bg-slate-900;
-}
-
// input form
@mixin ghost-input() {
box-shadow: none;
@@ -87,65 +15,6 @@ $spinner-before-border-color: rgba(255, 255, 255, 0.7);
}
}
-// flex-layout
-@mixin space-between() {
- display: flex;
- justify-content: space-between;
-}
-
-@mixin space-between-column() {
- @include space-between;
- flex-direction: column;
-}
-
-@mixin space-between-row() {
- @include space-between;
- flex-direction: row;
-}
-
-@mixin flex-shrink() {
- flex: 0 0 auto;
- max-width: 100%;
-}
-
-@mixin flex-weight($value) {
- // Grab flex-grow for older browsers.
- $flex-grow: nth($value, 1);
-
- // 2009
- @include prefixer(box-flex, $flex-grow, webkit moz spec);
-
- // 2011 (IE 10), 2012
- @include prefixer(flex, $value, webkit moz ms spec);
-}
-
-// full height
-@mixin full-height() {
- height: 100%;
-}
-
-@mixin round-corner() {
- border-radius: 1000px;
-}
-
-@mixin scroll-on-hover() {
- overflow: hidden;
-
- &:hover {
- overflow-y: auto;
- }
-}
-
-
-@mixin horizontal-scroll() {
- overflow-y: auto;
-}
-
-@mixin elegant-card() {
- @include normal-shadow;
- border-radius: $space-small;
-}
-
@mixin color-spinner() {
@keyframes spinner {
to {
@@ -230,17 +99,3 @@ $spinner-before-border-color: rgba(255, 255, 255, 0.7);
border-left: $size solid transparent;
}
}
-
-@mixin text-ellipsis {
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-@mixin three-column-grid($column-one-width: 16rem,
- $column-three-width: 16rem) {
- width: 100%;
- height: 100%;
- display: grid;
- grid-template-columns: minmax($column-one-width, 6fr) 10fr minmax($column-three-width, 6fr);
-}
diff --git a/app/javascript/dashboard/components/widgets/conversation/Message.vue b/app/javascript/dashboard/components/widgets/conversation/Message.vue
index 6a98fe42f..253eaeaa7 100644
--- a/app/javascript/dashboard/components/widgets/conversation/Message.vue
+++ b/app/javascript/dashboard/components/widgets/conversation/Message.vue
@@ -663,10 +663,10 @@ export default {
}
&.is-failed {
- @apply bg-red-200 dark:bg-red-200;
+ @apply bg-n-ruby-4 dark:bg-n-ruby-4 text-n-slate-12;
.message-text--metadata .time {
- @apply text-red-50 dark:text-red-50;
+ @apply text-n-ruby-12 dark:text-n-ruby-12;
}
}
}
@@ -727,7 +727,7 @@ li.right {
}
.wrap.is-failed {
- @apply flex items-end ml-auto;
+ @apply flex items-end ltr:ml-auto rtl:mr-auto;
}
}
diff --git a/app/javascript/portal/application.scss b/app/javascript/portal/application.scss
index 01246578e..bdb7bd1d4 100644
--- a/app/javascript/portal/application.scss
+++ b/app/javascript/portal/application.scss
@@ -3,10 +3,6 @@
@import 'tailwindcss/utilities';
@import 'widget/assets/scss/reset';
-@import 'widget/assets/scss/variables';
-@import 'widget/assets/scss/buttons';
-@import 'widget/assets/scss/mixins';
-@import 'widget/assets/scss/forms';
@import 'shared/assets/fonts/InterDisplay/inter-display';
html,
@@ -18,7 +14,6 @@ body {
letter-spacing: 0.2px;
}
-
// Taking these utils from tailwind 3.x.x, need to remove once we upgrade
.scroll-mt-24 {
scroll-margin-top: 6rem;
diff --git a/app/javascript/shared/assets/fonts/widget_fonts.scss b/app/javascript/shared/assets/fonts/widget_fonts.scss
index a8145900e..f7fe95c17 100644
--- a/app/javascript/shared/assets/fonts/widget_fonts.scss
+++ b/app/javascript/shared/assets/fonts/widget_fonts.scss
@@ -1,3 +1,19 @@
+@font-face {
+ font-family: 'Inter';
+ font-style: normal;
+ font-weight: 100;
+ font-display: swap;
+ src: url('shared/assets/fonts/Inter/Inter-Thin.woff2') format('woff2');
+}
+
+@font-face {
+ font-family: 'Inter';
+ font-style: normal;
+ font-weight: 300;
+ font-display: swap;
+ src: url('shared/assets/fonts/Inter/Inter-Light.woff2') format('woff2');
+}
+
@font-face {
font-family: 'Inter';
font-style: normal;
@@ -6,6 +22,14 @@
src: url('shared/assets/fonts/Inter/Inter-Regular.woff2') format('woff2');
}
+@font-face {
+ font-family: 'Inter';
+ font-style: italic;
+ font-weight: 400;
+ font-display: swap;
+ src: url('shared/assets/fonts/Inter/Inter-Italic.woff2') format('woff2');
+}
+
@font-face {
font-family: 'Inter';
font-style: normal;
@@ -13,3 +37,19 @@
font-display: swap;
src: url('shared/assets/fonts/Inter/Inter-Medium.woff2') format('woff2');
}
+
+@font-face {
+ font-family: 'Inter';
+ font-style: normal;
+ font-weight: 600;
+ font-display: swap;
+ src: url('shared/assets/fonts/Inter/Inter-SemiBold.woff2') format('woff2');
+}
+
+@font-face {
+ font-family: 'Inter';
+ font-style: normal;
+ font-weight: 700;
+ font-display: swap;
+ src: url('shared/assets/fonts/Inter/Inter-Bold.woff2') format('woff2');
+}
diff --git a/app/javascript/shared/components/Branding.vue b/app/javascript/shared/components/Branding.vue
index 3d3e5ff57..cc1ae385d 100644
--- a/app/javascript/shared/components/Branding.vue
+++ b/app/javascript/shared/components/Branding.vue
@@ -53,10 +53,10 @@ export default {
:href="brandRedirectURL"
rel="noreferrer noopener nofollow"
target="_blank"
- class="branding--link justify-center items-center leading-3"
+ class="branding--link text-n-slate-11 hover:text-n-slate-12 cursor-pointer text-xs inline-flex grayscale-[1] hover:grayscale-0 hover:opacity-100 opacity-90 no-underline justify-center items-center leading-3"
>
@@ -67,29 +67,3 @@ export default {