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

@@ -7,7 +7,6 @@ import { isEmptyObject } from 'widget/helpers/utils';
import { getRegexp } from 'shared/helpers/Validators';
import { useMessageFormatter } from 'shared/composables/useMessageFormatter';
import routerMixin from 'widget/mixins/routerMixin';
import { useDarkMode } from 'widget/composables/useDarkMode';
import configMixin from 'widget/mixins/configMixin';
import { FormKit, createInput } from '@formkit/vue';
import PhoneInput from 'widget/components/Form/PhoneInput.vue';
@@ -31,9 +30,8 @@ export default {
props: ['hasErrorInPhoneInput'],
});
const { formatMessage } = useMessageFormatter();
const { getThemeClass } = useDarkMode();
return { formatMessage, phoneInput, getThemeClass };
return { formatMessage, phoneInput };
},
data() {
return {
@@ -66,7 +64,10 @@ export default {
return !isEmptyObject(this.activeCampaign);
},
shouldShowHeaderMessage() {
return this.hasActiveCampaign || this.preChatFormEnabled;
return (
this.hasActiveCampaign ||
(this.preChatFormEnabled && !!this.headerMessage)
);
},
headerMessage() {
if (this.preChatFormEnabled) {
@@ -138,35 +139,12 @@ export default {
});
return contactAttributes;
},
inputStyles() {
return `mt-1 border rounded w-full py-2 px-3 text-slate-700 outline-none`;
},
isInputDarkOrLightMode() {
return `${this.getThemeClass(
'bg-white',
'dark:bg-slate-600'
)} ${this.getThemeClass('text-slate-700', 'dark:text-slate-50')}`;
},
inputBorderColor() {
return `${this.getThemeClass(
'border-black-200',
'dark:border-black-500'
)}`;
},
},
methods: {
labelClass(context) {
const { hasErrors } = context;
if (!hasErrors) {
return `text-xs font-medium ${this.getThemeClass(
'text-black-800',
'dark:text-slate-50'
)}`;
}
return `text-xs font-medium ${this.getThemeClass(
'text-red-400',
'dark:text-red-400'
)}`;
labelClass(input) {
const { state } = input.context;
const hasErrors = state.invalid;
return !hasErrors ? 'text-n-slate-12' : 'text-n-ruby-10';
},
inputClass(input) {
const { state, family: classification, type } = input.context;
@@ -178,9 +156,9 @@ export default {
this.hasErrorInPhoneInput = hasErrors;
}
if (!hasErrors) {
return `${this.inputStyles} hover:border-black-300 focus:border-black-300 ${this.isInputDarkOrLightMode} ${this.inputBorderColor}`;
return `mt-1 rounded w-full py-2 px-3`;
}
return `${this.inputStyles} border-red-200 hover:border-red-300 focus:border-red-300 ${this.isInputDarkOrLightMode}`;
return `mt-1 rounded w-full py-2 px-3 error`;
},
isContactFieldRequired(field) {
return this.preChatFields.find(option => option.name === field).required;
@@ -286,8 +264,7 @@ export default {
<div
v-if="shouldShowHeaderMessage"
v-dompurify-html="formatMessage(headerMessage, false)"
class="mb-4 text-sm leading-5 pre-chat-header-message"
:class="getThemeClass('text-black-800', 'dark:text-slate-50')"
class="mb-4 text-base leading-5 pre-chat-header-message text-n-slate-12"
/>
<!-- Why do the v-bind shenanigan? Because Formkit API is really bad.
If we just pass the options as is even with null or undefined or false,
@@ -307,7 +284,7 @@ export default {
}
: undefined
"
:label-class="context => labelClass(context)"
:label-class="context => `text-sm font-medium ${labelClass(context)}`"
:input-class="context => inputClass(context)"
:validation-messages="{
startsWithPlus: $t(
@@ -326,7 +303,7 @@ export default {
v-if="!hasActiveCampaign"
name="message"
type="textarea"
:label-class="context => labelClass(context)"
:label-class="context => `text-sm font-medium ${labelClass(context)}`"
:input-class="context => inputClass(context)"
:label="$t('PRE_CHAT_FORM.FIELDS.MESSAGE.LABEL')"
:placeholder="$t('PRE_CHAT_FORM.FIELDS.MESSAGE.PLACEHOLDER')"
@@ -337,7 +314,7 @@ export default {
/>
<CustomButton
class="mt-2 mb-5 font-medium"
class="mt-3 mb-5 font-medium flex items-center justify-center gap-2"
block
:bg-color="widgetColor"
:text-color="textColor"
@@ -352,10 +329,22 @@ export default {
<style lang="scss">
.formkit-outer {
@apply mt-2;
.formkit-inner {
input.error,
textarea.error,
select.error {
@apply outline-n-ruby-8 dark:outline-n-ruby-8 hover:outline-n-ruby-9 dark:hover:outline-n-ruby-9 focus:outline-n-ruby-9 dark:focus:outline-n-ruby-9;
}
input[type='checkbox'] {
@apply size-4 outline-none;
}
}
}
[data-invalid] .formkit-message {
@apply text-red-500 block text-xs font-normal mb-1 w-full;
@apply text-n-ruby-10 block text-xs font-normal my-0.5 w-full;
}
.formkit-outer[data-type='checkbox'] .formkit-wrapper {