diff --git a/frontend/src/css/_shell.css b/frontend/src/css/_shell.css index 388a3363..8f38e807 100644 --- a/frontend/src/css/_shell.css +++ b/frontend/src/css/_shell.css @@ -16,6 +16,10 @@ transition: .2s ease transform; } +body.rtl .shell { + direction: ltr; +} + .shell__result { display: flex; padding: 0.5em; diff --git a/frontend/src/css/base.css b/frontend/src/css/base.css index 05d799c9..58ce9aec 100644 --- a/frontend/src/css/base.css +++ b/frontend/src/css/base.css @@ -5,6 +5,10 @@ body { color: #333333; } +body.rtl { + direction: rtl; +} + * { box-sizing: border-box; } @@ -58,6 +62,11 @@ nav { left: 0; } +body.rtl nav { + left: unset; + right: 0; +} + nav .action { width: 100%; display: block; @@ -69,6 +78,11 @@ nav .action { text-overflow: ellipsis; } +body.rtl .action { + direction: rtl; + text-align: right; +} + nav > div { border-top: 1px solid rgba(0, 0, 0, 0.05); } @@ -101,6 +115,10 @@ main { border-radius: 0.125em; } +body.rtl .breadcrumbs a { + transform: translateX(-16em); +} + .breadcrumbs a:hover { background-color: rgba(0, 0, 0, 0.05); } diff --git a/frontend/src/css/dashboard.css b/frontend/src/css/dashboard.css index 558d8de7..d30fb543 100644 --- a/frontend/src/css/dashboard.css +++ b/frontend/src/css/dashboard.css @@ -8,6 +8,10 @@ flex-wrap: wrap; } +body.rtl .dashboard .row { + margin-right: 16em; +} + .dashboard .row .column { display: flex; padding: 0 .5em; @@ -60,6 +64,10 @@ p code { border-bottom: 2px solid rgba(0, 0, 0, 0.05); } +body.rtl #nav .wrapper { + margin-right: 16em; +} + .dashboard #nav ul { list-style: none; display: flex; @@ -138,6 +146,13 @@ table tr>*:first-child { padding-left: 1em; } +body.rtl table tr>* { + padding-left: unset; + padding-right: 1em; + text-align: right; + direction: ltr; +} + table tr>*:last-child { padding-right: 1em; } @@ -181,6 +196,11 @@ table tr>*:last-child { margin-right: auto; } +body.rtl .card .card-title>*:first-child { + margin-right: 0; + text-align: right; +} + .card>div { padding: 1em 1em; } @@ -461,4 +481,10 @@ table tr>*:last-child { .card .card-action.full .action .title { font-size: 1.5em; font-weight: 500; +} + +/*** RTL - Fix disk usage information (in english) ***/ +body.rtl .credits { + text-align: right; + direction: ltr; } \ No newline at end of file diff --git a/frontend/src/css/header.css b/frontend/src/css/header.css index 396f926b..3dc8bd51 100644 --- a/frontend/src/css/header.css +++ b/frontend/src/css/header.css @@ -135,10 +135,25 @@ header .menu-button { z-index: 1; } +body.rtl #search #result { + direction: ltr; +} + #search #result>div>*:first-child { margin-top: 0; } +body.rtl #search #result { + direction: rtl; + text-align: right; +} + +/*** RTL - Keep search result LTR because it has paths (in english) ***/ +body.rtl #search #result ul>* { + direction: ltr; + text-align: left; +} + #search.active #result { padding: .5em; height: calc(100% - 4em); @@ -224,6 +239,10 @@ header .menu-button { padding: .5em; } +body.rtl #search .boxes h3 { + text-align: right; +} + #search .boxes>div { display: flex; flex-wrap: wrap; diff --git a/frontend/src/css/listing.css b/frontend/src/css/listing.css index 17583bc4..528311fe 100644 --- a/frontend/src/css/listing.css +++ b/frontend/src/css/listing.css @@ -2,6 +2,10 @@ --item-selected: white; } +body.rtl #listing { + margin-right: 16em; +} + #listing h2 { margin: 0 0 0 0.5em; font-size: .9em; diff --git a/frontend/src/css/mobile.css b/frontend/src/css/mobile.css index 6417080f..ef4b173c 100644 --- a/frontend/src/css/mobile.css +++ b/frontend/src/css/mobile.css @@ -40,6 +40,13 @@ transform-origin: top right; z-index: 99999; } + + body.rtl #dropdown { + right: unset; + left: 1em; + transform-origin: top left; + } + #dropdown > div { display: block; } @@ -95,9 +102,20 @@ transition: .1s ease left; left: -17em; } + + body.rtl nav { + left: unset; + right: -17em; + } nav.active { left: 0; } + + body.rtl nav.active { + left: unset; + right: 0; + } + header .search-button, header .menu-button { display: inherit; @@ -108,6 +126,23 @@ #listing { margin-bottom: 5em; } + + body.rtl #listing { + margin-right: unset; + } + + body.rtl .breadcrumbs { + transform: translateX(16em); + } + + body.rtl #nav .wrapper { + margin-right: unset; + } + + body.rtl .dashboard .row { + margin-right: unset; + } + main { margin: 0 1em; width: calc(100% - 2em); diff --git a/frontend/src/css/styles.css b/frontend/src/css/styles.css index 8b3b608c..2ba99532 100644 --- a/frontend/src/css/styles.css +++ b/frontend/src/css/styles.css @@ -315,6 +315,11 @@ main .spinner .bounce2 { padding: 0 1em; } +/*** RTL - flip and position arrow of path ***/ +body.rtl .breadcrumbs .chevron { + transform: scaleX(-1) translateX(16em); +} + #editor-container .breadcrumbs span { font-size: .75rem; } @@ -404,3 +409,22 @@ main .spinner .bounce2 { } @import './mobile.css'; + +/* * * * * * * * * * * * * * * * + * RTL overrides * + * * * * * * * * * * * * * * * */ + +body.rtl .card-content textarea { + direction: ltr; + text-align: left; +} + +body.rtl .card-content .small + input { + direction: ltr; + text-align: left; +} + +body.rtl .card.floating .card-content .file-list { + direction: ltr; + text-align: left; +} diff --git a/frontend/src/i18n/index.js b/frontend/src/i18n/index.js index b0def2c3..84d08a7d 100644 --- a/frontend/src/i18n/index.js +++ b/frontend/src/i18n/index.js @@ -100,6 +100,8 @@ const removeEmpty = (obj) => {} ); +export const rtlLanguages = ["he", "ar"]; + const i18n = new VueI18n({ locale: detectLocale(), fallbackLocale: "en", diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js index 6af06f33..c2a8b957 100644 --- a/frontend/src/router/index.js +++ b/frontend/src/router/index.js @@ -13,7 +13,7 @@ import Shares from "@/views/settings/Shares"; import Errors from "@/views/Errors"; import store from "@/store"; import { baseURL, name } from "@/utils/constants"; -import i18n from "@/i18n"; +import i18n, { rtlLanguages } from "@/i18n"; Vue.use(Router); @@ -158,6 +158,18 @@ router.beforeEach((to, from, next) => { const title = i18n.t(titles[to.name]); document.title = title + " - " + name; + /*** RTL related settings per route ****/ + const rtlSet = document.querySelector("body").classList.contains("rtl"); + const shouldSetRtl = rtlLanguages.includes(i18n.locale); + switch (true) { + case shouldSetRtl && !rtlSet: + document.querySelector("body").classList.add("rtl"); + break; + case !shouldSetRtl && rtlSet: + document.querySelector("body").classList.remove("rtl"); + break; + } + if (to.matched.some((record) => record.meta.requiresAuth)) { if (!store.getters.isLogged) { next({ diff --git a/frontend/src/views/settings/Profile.vue b/frontend/src/views/settings/Profile.vue index 0b8af637..4002e08c 100644 --- a/frontend/src/views/settings/Profile.vue +++ b/frontend/src/views/settings/Profile.vue @@ -75,6 +75,7 @@ import { mapState, mapMutations } from "vuex"; import { users as api } from "@/api"; import Languages from "@/components/settings/Languages"; +import i18n, { rtlLanguages } from "@/i18n"; export default { name: "settings", @@ -143,6 +144,9 @@ export default { singleClick: this.singleClick, dateFormat: this.dateFormat, }; + const shouldReload = + rtlLanguages.includes(data.locale) !== + rtlLanguages.includes(i18n.locale); await api.update(data, [ "locale", "hideDotfiles", @@ -150,6 +154,9 @@ export default { "dateFormat", ]); this.updateUser(data); + if (shouldReload) { + location.reload(); + } this.$showSuccess(this.$t("settings.settingsUpdated")); } catch (e) { this.$showError(e);