chore: RTL configuration (#6521)
* chore: RTL configuration * Adds scss file
This commit is contained in:
@@ -1,5 +1,10 @@
|
||||
<template>
|
||||
<div v-if="!authUIFlags.isFetching" id="app" class="app-wrapper app-root">
|
||||
<div
|
||||
v-if="!authUIFlags.isFetching"
|
||||
id="app"
|
||||
class="app-wrapper app-root"
|
||||
:class="{ 'app-rtl--wrapper': isRTLView }"
|
||||
>
|
||||
<update-banner :latest-chatwoot-version="latestChatwootVersion" />
|
||||
<transition name="fade" mode="out-in">
|
||||
<router-view />
|
||||
@@ -22,6 +27,7 @@ import NetworkNotification from './components/NetworkNotification';
|
||||
import UpdateBanner from './components/app/UpdateBanner.vue';
|
||||
import vueActionCable from './helper/actionCable';
|
||||
import WootSnackbarBox from './components/SnackbarContainer';
|
||||
import rtlMixin from 'shared/mixins/rtlMixin';
|
||||
import {
|
||||
registerSubscription,
|
||||
verifyServiceWorkerExistence,
|
||||
@@ -38,6 +44,8 @@ export default {
|
||||
WootSnackbarBox,
|
||||
},
|
||||
|
||||
mixins: [rtlMixin],
|
||||
|
||||
data() {
|
||||
return {
|
||||
showAddAccountModal: false,
|
||||
|
||||
436
app/javascript/dashboard/assets/scss/_rtl.scss
Normal file
436
app/javascript/dashboard/assets/scss/_rtl.scss
Normal file
@@ -0,0 +1,436 @@
|
||||
.app-rtl--wrapper {
|
||||
direction: rtl;
|
||||
|
||||
// Primary sidebar
|
||||
.primary--sidebar {
|
||||
border-left: 1px solid var(--s-50);
|
||||
border-right: 0;
|
||||
|
||||
.options-menu.dropdown-pane {
|
||||
right: var(--space-smaller);
|
||||
|
||||
.auto-offline--toggle {
|
||||
padding: var(--space-smaller) var(--space-one) var(--space-smaller)
|
||||
var(--space-smaller);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Secondary sidebar
|
||||
.secondary-sidebar {
|
||||
.secondary-menu {
|
||||
border-left: 1px solid var(--s-50);
|
||||
border-right: 0;
|
||||
|
||||
.nested.vertical.menu {
|
||||
align-items: flex-start;
|
||||
|
||||
li {
|
||||
direction: initial;
|
||||
}
|
||||
}
|
||||
|
||||
.secondary-menu--icon {
|
||||
margin-left: var(--space-smaller);
|
||||
margin-right: unset;
|
||||
}
|
||||
|
||||
.account-context--group .account-context--switch-group {
|
||||
--overlay-shadow: linear-gradient(
|
||||
to left,
|
||||
rgba(255, 255, 255, 0) 0%,
|
||||
rgba(255, 255, 255, 1) 50%
|
||||
);
|
||||
background-image: var(--overlay-shadow);
|
||||
}
|
||||
|
||||
// Help center sidebar
|
||||
.sidebar-header--wrap .header-title--wrap {
|
||||
margin-left: unset;
|
||||
margin-right: var(--space-small);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Woot button
|
||||
.button {
|
||||
.icon--emoji + .button__content {
|
||||
padding-left: 0;
|
||||
padding-right: var(--space-small);
|
||||
}
|
||||
|
||||
.icon--font + .button__content {
|
||||
padding-left: 0;
|
||||
padding-right: var(--space-small);
|
||||
}
|
||||
|
||||
.icon + .button__content {
|
||||
padding-left: 0;
|
||||
padding-right: var(--space-small);
|
||||
}
|
||||
}
|
||||
|
||||
// Settings header
|
||||
.settings-header {
|
||||
.header--icon {
|
||||
margin-left: 0;
|
||||
margin-right: $space-small;
|
||||
}
|
||||
}
|
||||
|
||||
.settings.back-button {
|
||||
direction: initial;
|
||||
margin-left: var(--space-normal);
|
||||
margin-right: var(--space-smaller);
|
||||
}
|
||||
|
||||
// Settings header action button
|
||||
.button--fixed-top {
|
||||
left: $space-small;
|
||||
position: fixed;
|
||||
right: unset;
|
||||
top: $space-small;
|
||||
}
|
||||
|
||||
// Woot Tabs
|
||||
.tabs-title {
|
||||
&:first-child {
|
||||
margin-left: $space-slab;
|
||||
margin-right: unset;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-left: unset;
|
||||
margin-right: $space-slab;
|
||||
}
|
||||
}
|
||||
|
||||
// woot tables
|
||||
table,
|
||||
thead,
|
||||
th {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
// Table footer
|
||||
.footer {
|
||||
.page-meta {
|
||||
direction: initial;
|
||||
}
|
||||
}
|
||||
|
||||
// Wizard box
|
||||
.wizard-box {
|
||||
direction: initial;
|
||||
}
|
||||
|
||||
// Conversation details
|
||||
.conversation-details-wrap {
|
||||
.conv-header {
|
||||
.user {
|
||||
margin-left: var(--space-normal);
|
||||
margin-right: unset;
|
||||
|
||||
.user--profile__meta {
|
||||
margin-left: unset;
|
||||
margin-right: $space-slab;
|
||||
}
|
||||
}
|
||||
|
||||
.actions--container .resolve-actions {
|
||||
margin-left: unset;
|
||||
margin-right: var(--space-small);
|
||||
}
|
||||
}
|
||||
|
||||
.conversation-panel {
|
||||
// Message text
|
||||
.text-content {
|
||||
p {
|
||||
unicode-bidi: plaintext;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: unset;
|
||||
padding-right: var(--space-two);
|
||||
}
|
||||
|
||||
li {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
// Message items and actions
|
||||
li {
|
||||
&.right {
|
||||
.sender--info {
|
||||
padding: var(--space-small) var(--space-smaller)
|
||||
var(--space-smaller) 0;
|
||||
}
|
||||
|
||||
.context-menu-wrap {
|
||||
margin-left: 0;
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Conversation footer
|
||||
.conversation-footer {
|
||||
.preview-item {
|
||||
direction: initial;
|
||||
}
|
||||
}
|
||||
|
||||
// Custom attributes section in conversation sidebar
|
||||
.conversation-sidebar-wrap .checkbox-wrap {
|
||||
.checkbox {
|
||||
margin-left: var(--space-small);
|
||||
}
|
||||
}
|
||||
|
||||
// Conversation sidebar toggle button
|
||||
.sidebar-toggle--button {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
// Conversation sidebar close button
|
||||
.close-button--rtl {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
// Resolve actions button
|
||||
.resolve-actions {
|
||||
.button-group .button:first-child {
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: var(--border-radius-normal);
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: var(--border-radius-normal);
|
||||
}
|
||||
|
||||
.button-group .button:last-child {
|
||||
border-bottom-left-radius: var(--border-radius-normal);
|
||||
border-bottom-right-radius: 0;
|
||||
border-top-left-radius: var(--border-radius-normal);
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Conversation list
|
||||
.conversations-list-wrap {
|
||||
border-right: 0;
|
||||
|
||||
.conversation {
|
||||
.conversation--meta {
|
||||
left: $space-normal;
|
||||
right: unset;
|
||||
|
||||
.unread {
|
||||
margin-left: unset;
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.show-more--button {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
.search-header--wrap .search--input {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
// Secondary sidebar toggle button
|
||||
.toggle-sidebar {
|
||||
margin-left: 0;
|
||||
margin-right: var(--space-minus-small);
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
// Bulk actions
|
||||
.bulk-action__container {
|
||||
.triangle {
|
||||
left: var(--triangle-position);
|
||||
right: unset;
|
||||
}
|
||||
|
||||
.bulk-action__agents {
|
||||
left: var(--space-small);
|
||||
right: unset;
|
||||
}
|
||||
|
||||
.labels-container {
|
||||
left: var(--space-small);
|
||||
right: unset;
|
||||
|
||||
.label-checkbox {
|
||||
margin: 0 0 0 var(--space-one);
|
||||
}
|
||||
}
|
||||
|
||||
.actions-container {
|
||||
left: var(--space-small);
|
||||
right: unset;
|
||||
}
|
||||
|
||||
.bulk-action__teams {
|
||||
left: var(--space-small);
|
||||
right: unset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Contact notes
|
||||
.card.note-wrap {
|
||||
.time-stamp {
|
||||
unicode-bidi: plaintext;
|
||||
}
|
||||
}
|
||||
|
||||
// Labels
|
||||
.label {
|
||||
direction: initial;
|
||||
}
|
||||
|
||||
// Notification panel
|
||||
.notification-wrap {
|
||||
left: 0;
|
||||
right: var(--space-jumbo);
|
||||
|
||||
.action-button {
|
||||
margin-left: var(--space-small);
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.notification-content--wrap {
|
||||
margin-left: 0;
|
||||
margin-right: var(--space-slab);
|
||||
}
|
||||
}
|
||||
|
||||
// Help center
|
||||
.article-container .row--article-block {
|
||||
td:last-child {
|
||||
direction: initial;
|
||||
}
|
||||
}
|
||||
|
||||
.header-left-wrap .page-title {
|
||||
margin-right: var(--space-slab) !important;
|
||||
}
|
||||
|
||||
.portal-container .container {
|
||||
margin-left: unset !important;
|
||||
margin-right: var(--space-small);
|
||||
|
||||
.configuration-items--wrap {
|
||||
margin-left: var(--space-mega);
|
||||
margin-right: unset !important;
|
||||
}
|
||||
|
||||
thead th {
|
||||
padding-left: var(--space-one);
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
tbody td {
|
||||
padding-left: var(--space-one);
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.portal-popover__container .portal {
|
||||
.actions-container {
|
||||
margin-left: unset;
|
||||
margin-right: var(--space-one);
|
||||
}
|
||||
}
|
||||
|
||||
.edit-article--container {
|
||||
.header-right--wrap {
|
||||
.button-group .button:first-child {
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: var(--border-radius-normal);
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: var(--border-radius-normal);
|
||||
}
|
||||
|
||||
.button-group .button:last-child {
|
||||
border-bottom-left-radius: var(--border-radius-normal);
|
||||
border-bottom-right-radius: 0;
|
||||
border-top-left-radius: var(--border-radius-normal);
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.header-left--wrap {
|
||||
.back-button {
|
||||
direction: initial;
|
||||
}
|
||||
}
|
||||
|
||||
.article--buttons {
|
||||
.dropdown-pane {
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-button {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
.article-settings--container {
|
||||
border-left: 0;
|
||||
border-right: 1px solid var(--color-border-light);
|
||||
flex-direction: row-reverse;
|
||||
margin-left: 0;
|
||||
margin-right: var(--space-normal);
|
||||
padding-left: 0;
|
||||
padding-right: var(--space-normal);
|
||||
}
|
||||
|
||||
.category-list--container .header-left--wrap {
|
||||
direction: initial;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
// Widget builder
|
||||
.widget-builder-container .widget-preview {
|
||||
direction: initial;
|
||||
}
|
||||
|
||||
// Other changes
|
||||
|
||||
.account-selector--wrap {
|
||||
direction: initial;
|
||||
}
|
||||
|
||||
.inbox--name {
|
||||
direction: initial;
|
||||
}
|
||||
|
||||
.colorpicker--chrome {
|
||||
direction: initial;
|
||||
}
|
||||
|
||||
.mention--box {
|
||||
direction: initial;
|
||||
}
|
||||
|
||||
.contact--details .contact--bio {
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
.merge-contacts .child-contact-wrap {
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
.contact--form .input-group {
|
||||
direction: initial;
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,7 @@
|
||||
@import 'layout';
|
||||
@import 'animations';
|
||||
@import 'foundation-custom';
|
||||
@import 'rtl';
|
||||
|
||||
@import 'widgets/buttons';
|
||||
@import 'widgets/conv-header';
|
||||
|
||||
@@ -739,4 +739,9 @@ export const getLanguageName = (languageCode = '') => {
|
||||
return languageObj.name || '';
|
||||
};
|
||||
|
||||
export const getLanguageDirection = (languageCode = '') => {
|
||||
const rtlLanguageIds = ['ar', 'as', 'fa', 'he', 'ku', 'ur'];
|
||||
return rtlLanguageIds.includes(languageCode);
|
||||
};
|
||||
|
||||
export default languages;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getLanguageName } from '../languages';
|
||||
import { getLanguageName, getLanguageDirection } from '../languages';
|
||||
|
||||
describe('#getLanguageName', () => {
|
||||
it('Returns correct language name', () => {
|
||||
@@ -8,3 +8,10 @@ describe('#getLanguageName', () => {
|
||||
expect(getLanguageName('')).toEqual('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getLanguageDirection', () => {
|
||||
it('Returns correct language direction', () => {
|
||||
expect(getLanguageDirection('es')).toEqual(false);
|
||||
expect(getLanguageDirection('ar')).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -125,9 +125,11 @@ import configMixin from 'shared/mixins/configMixin';
|
||||
import accountMixin from '../../../../mixins/account';
|
||||
import { FEATURE_FLAGS } from '../../../../featureFlags';
|
||||
const semver = require('semver');
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
import { getLanguageDirection } from 'dashboard/components/widgets/conversation/advancedFilterItems/languages';
|
||||
|
||||
export default {
|
||||
mixins: [accountMixin, alertMixin, configMixin],
|
||||
mixins: [accountMixin, alertMixin, configMixin, uiSettingsMixin],
|
||||
data() {
|
||||
return {
|
||||
id: '',
|
||||
@@ -249,11 +251,20 @@ export default {
|
||||
auto_resolve_duration: this.autoResolveDuration,
|
||||
});
|
||||
this.$root.$i18n.locale = this.locale;
|
||||
this.getAccount(this.id).locale = this.locale;
|
||||
this.updateDirectionView(this.locale);
|
||||
this.showAlert(this.$t('GENERAL_SETTINGS.UPDATE.SUCCESS'));
|
||||
} catch (error) {
|
||||
this.showAlert(this.$t('GENERAL_SETTINGS.UPDATE.ERROR'));
|
||||
}
|
||||
},
|
||||
|
||||
updateDirectionView(locale) {
|
||||
const isRTLSupported = getLanguageDirection(locale);
|
||||
this.updateUISettings({
|
||||
rtl_view: isRTLSupported,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user