Files
chatwoot/app/javascript/shared/components/ChatForm.vue
Sivin Varghese 3a693947b5 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
2025-03-21 09:39:03 -07:00

188 lines
4.3 KiB
Vue

<script>
import { mapGetters } from 'vuex';
import { getContrastingTextColor } from '@chatwoot/utils';
export default {
props: {
buttonLabel: {
type: String,
default: '',
},
items: {
type: Array,
default: () => [],
},
submittedValues: {
type: Array,
default: () => [],
},
},
emits: ['submit'],
data() {
return {
formValues: {},
hasSubmitted: false,
};
},
computed: {
...mapGetters({
widgetColor: 'appConfig/getWidgetColor',
}),
textColor() {
return getContrastingTextColor(this.widgetColor);
},
isFormValid() {
return this.items.reduce((acc, { name }) => {
return !!this.formValues[name] && acc;
}, true);
},
},
mounted() {
if (this.submittedValues.length) {
this.updateFormValues();
} else {
this.setFormDefaults();
}
},
methods: {
onSubmitClick() {
this.hasSubmitted = true;
},
onSubmit() {
if (!this.isFormValid) {
return;
}
this.$emit('submit', this.formValues);
},
buildFormObject(formObjectArray) {
return formObjectArray.reduce((acc, obj) => {
return {
...acc,
[obj.name]: obj.value || obj.default,
};
}, {});
},
updateFormValues() {
this.formValues = this.buildFormObject(this.submittedValues);
},
setFormDefaults() {
this.formValues = this.buildFormObject(this.items);
},
},
};
</script>
<template>
<div
class="form chat-bubble agent w-full p-4 bg-n-background dark:bg-n-solid-3"
>
<form @submit.prevent="onSubmit">
<div
v-for="item in items"
:key="item.key"
class="pb-2 w-full"
:class="{
'has-submitted': hasSubmitted,
}"
>
<label class="text-n-slate-12">
{{ item.label }}
</label>
<input
v-if="item.type === 'email'"
v-model="formValues[item.name]"
:type="item.type"
:pattern="item.regex"
:title="item.title"
:required="item.required && 'required'"
:name="item.name"
:placeholder="item.placeholder"
:disabled="!!submittedValues.length"
/>
<input
v-else-if="item.type === 'text'"
v-model="formValues[item.name]"
:required="item.required && 'required'"
:pattern="item.pattern"
:title="item.title"
:type="item.type"
:name="item.name"
:placeholder="item.placeholder"
:disabled="!!submittedValues.length"
/>
<textarea
v-else-if="item.type === 'text_area'"
v-model="formValues[item.name]"
:required="item.required && 'required'"
:title="item.title"
:name="item.name"
:placeholder="item.placeholder"
:disabled="!!submittedValues.length"
/>
<select
v-else-if="item.type === 'select'"
v-model="formValues[item.name]"
:required="item.required && 'required'"
>
<option
v-for="option in item.options"
:key="option.key"
:value="option.value"
>
{{ option.label }}
</option>
</select>
<span class="error-message">
{{ item.pattern_error || $t('CHAT_FORM.INVALID.FIELD') }}
</span>
</div>
<button
v-if="!submittedValues.length"
class="button block"
type="submit"
:style="{
background: widgetColor,
borderColor: widgetColor,
color: textColor,
}"
@click="onSubmitClick"
>
{{ buttonLabel || $t('COMPONENTS.FORM_BUBBLE.SUBMIT') }}
</button>
</form>
</div>
</template>
<style scoped lang="scss">
.form {
label {
@apply block font-medium py-1 px-0 capitalize;
}
.button {
@apply text-sm rounded-lg;
}
.error-message {
@apply text-n-ruby-9 mt-1 hidden;
}
input,
textarea,
select {
@apply dark:bg-n-alpha-black1;
}
.has-submitted {
input:invalid,
textarea:invalid {
@apply outline-n-ruby-8 dark:outline-n-ruby-8 hover:outline-n-ruby-9 dark:hover:outline-n-ruby-9;
}
input:invalid + .error-message,
textarea:invalid + .error-message {
display: block;
}
}
}
</style>