feat(rtl): basic rtl layout for rtl languages
This commit is contained in:
parent
5e65f223bc
commit
654e397638
|
|
@ -74,7 +74,7 @@
|
|||
>
|
||||
<span class="username">{{ authStore.userDisplayName }}</span>
|
||||
<span
|
||||
class="ml-1 dropdown-icon icon is-small"
|
||||
class="ms-1 dropdown-icon icon is-small"
|
||||
:style="{
|
||||
transform: open ? 'rotate(180deg)' : 'rotate(0)',
|
||||
}"
|
||||
|
|
@ -167,7 +167,7 @@ $user-dropdown-width-mobile: 5rem;
|
|||
background: var(--site-background);
|
||||
|
||||
@media screen and (min-width: $tablet) {
|
||||
padding-left: 2rem;
|
||||
padding-inline-start: 2rem;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
|
|
@ -193,17 +193,17 @@ $user-dropdown-width-mobile: 5rem;
|
|||
align-self: stretch;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: .5rem;
|
||||
margin-inline-end: .5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-button {
|
||||
margin-right: auto;
|
||||
margin-inline-end: auto;
|
||||
align-self: stretch;
|
||||
flex: 0 0 auto;
|
||||
|
||||
@media screen and (max-width: $tablet) {
|
||||
margin-left: 1rem;
|
||||
margin-inline-start: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -252,7 +252,7 @@ $user-dropdown-width-mobile: 5rem;
|
|||
}
|
||||
|
||||
.navbar-end {
|
||||
margin-left: auto;
|
||||
margin-inline-start: auto;
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
|
|
@ -263,18 +263,18 @@ $user-dropdown-width-mobile: 5rem;
|
|||
}
|
||||
|
||||
.username-dropdown-trigger {
|
||||
padding-left: .75rem;
|
||||
padding-inline-start: .75rem;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-size: .85rem;
|
||||
font-weight: 700;
|
||||
|
||||
@media screen and (max-width: $tablet) {
|
||||
padding-right: .5rem;
|
||||
padding-inline-end: .5rem;
|
||||
}
|
||||
|
||||
@media screen and (min-width: $tablet) {
|
||||
padding-right: .75rem;
|
||||
padding-inline-end: .75rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -294,6 +294,6 @@ $user-dropdown-width-mobile: 5rem;
|
|||
border-radius: 100%;
|
||||
vertical-align: middle;
|
||||
height: 40px;
|
||||
margin-right: .5rem;
|
||||
margin-inline-end: .5rem;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -135,6 +135,11 @@ projectStore.loadAllProjects()
|
|||
top: 0.5rem;
|
||||
right: 0.5rem;
|
||||
z-index: 31;
|
||||
|
||||
[dir="rtl"] & {
|
||||
right: auto;
|
||||
left: 0.5rem;
|
||||
}
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
display: flex;
|
||||
|
|
@ -170,9 +175,14 @@ projectStore.loadAllProjects()
|
|||
padding: 1.5rem 0.5rem 0;
|
||||
// TODO refactor: DRY `transition-timing-function` with `./Navigation.vue`.
|
||||
transition: margin-left $transition-duration;
|
||||
|
||||
[dir="rtl"] & {
|
||||
transition: margin-right $transition-duration;
|
||||
}
|
||||
|
||||
@media screen and (max-width: $tablet) {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
min-height: calc(100vh - 4rem);
|
||||
}
|
||||
|
||||
|
|
@ -184,6 +194,13 @@ projectStore.loadAllProjects()
|
|||
@media screen and (min-width: $tablet) {
|
||||
margin-left: $navbar-width;
|
||||
}
|
||||
|
||||
[dir="rtl"] & {
|
||||
@media screen and (min-width: $tablet) {
|
||||
margin-left: 0;
|
||||
margin-right: $navbar-width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Used to make sure the spinner is always in the middle while loading
|
||||
|
|
@ -222,6 +239,11 @@ projectStore.loadAllProjects()
|
|||
bottom: calc(1rem - 4px);
|
||||
right: 1rem;
|
||||
z-index: 4500; // The modal has a z-index of 4000
|
||||
|
||||
[dir="rtl"] & {
|
||||
right: auto;
|
||||
left: 1rem;
|
||||
}
|
||||
|
||||
color: var(--grey-500);
|
||||
transition: color $transition;
|
||||
|
|
|
|||
|
|
@ -139,8 +139,8 @@ const savedFilterProjects = computed(() => projectStore.savedFilterProjects)
|
|||
.logo {
|
||||
display: block;
|
||||
|
||||
padding-left: 1rem;
|
||||
margin-right: 1rem;
|
||||
padding-inline-start: 1rem;
|
||||
margin-inline-end: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
@media screen and (min-width: $tablet) {
|
||||
|
|
@ -163,6 +163,12 @@ const savedFilterProjects = computed(() => projectStore.savedFilterProjects)
|
|||
width: $navbar-width;
|
||||
overflow-y: auto;
|
||||
|
||||
[dir="rtl"] & {
|
||||
left: auto;
|
||||
right: 0;
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
@media screen and (max-width: $tablet) {
|
||||
top: 0;
|
||||
width: 70vw;
|
||||
|
|
@ -183,7 +189,7 @@ const savedFilterProjects = computed(() => projectStore.savedFilterProjects)
|
|||
|
||||
.list-menu-link,
|
||||
li > a {
|
||||
padding-left: 2rem;
|
||||
padding-inline-start: 2rem;
|
||||
display: inline-block;
|
||||
|
||||
.icon {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
<ColorBubble
|
||||
v-if="!showProjectSeparately && projectColor !== '' && currentProject?.id !== task.projectId"
|
||||
:color="projectColor"
|
||||
class="mr-1"
|
||||
class="me-1"
|
||||
/>
|
||||
|
||||
<div
|
||||
|
|
@ -30,8 +30,8 @@
|
|||
v-if="showProject && typeof project !== 'undefined'"
|
||||
v-tooltip="$t('task.detail.belongsToProject', {project: project.title})"
|
||||
:to="{ name: 'project.index', params: { projectId: task.projectId } }"
|
||||
class="task-project mr-1"
|
||||
:class="{'mr-2': task.hexColor !== ''}"
|
||||
class="task-project me-1"
|
||||
:class="{'me-2': task.hexColor !== ''}"
|
||||
@click.stop
|
||||
>
|
||||
{{ project.title }}
|
||||
|
|
@ -40,13 +40,13 @@
|
|||
<ColorBubble
|
||||
v-if="task.hexColor !== ''"
|
||||
:color="getHexColor(task.hexColor)"
|
||||
class="mr-1"
|
||||
class="me-1"
|
||||
/>
|
||||
|
||||
<PriorityLabel
|
||||
:priority="task.priority"
|
||||
:done="task.done"
|
||||
class="pr-2"
|
||||
class="pe-2"
|
||||
/>
|
||||
|
||||
<RouterLink
|
||||
|
|
@ -61,7 +61,7 @@
|
|||
|
||||
<Labels
|
||||
v-if="task.labels.length > 0"
|
||||
class="labels ml-2 mr-1"
|
||||
class="labels ms-2 me-1"
|
||||
:labels="task.labels"
|
||||
/>
|
||||
|
||||
|
|
@ -69,7 +69,7 @@
|
|||
v-if="task.assignees.length > 0"
|
||||
:assignees="task.assignees"
|
||||
:avatar-size="25"
|
||||
class="ml-1"
|
||||
class="ms-1"
|
||||
:inline="true"
|
||||
/>
|
||||
|
||||
|
|
@ -134,7 +134,7 @@
|
|||
<ColorBubble
|
||||
v-if="showProjectSeparately && projectColor !== '' && currentProject?.id !== task.projectId"
|
||||
:color="projectColor"
|
||||
class="mr-1"
|
||||
class="me-1"
|
||||
/>
|
||||
|
||||
<RouterLink
|
||||
|
|
|
|||
|
|
@ -49,6 +49,12 @@ export const DEFAULT_LANGUAGE: SupportedLocale= 'en'
|
|||
|
||||
export type ISOLanguage = string
|
||||
|
||||
const RTL_LANGUAGES = ['ar-SA', 'he-IL'] as const
|
||||
|
||||
export function isRTLLanguage(locale: SupportedLocale): boolean {
|
||||
return RTL_LANGUAGES.includes(locale as typeof RTL_LANGUAGES[number])
|
||||
}
|
||||
|
||||
// we load all messages async
|
||||
export const i18n = createI18n({
|
||||
fallbackLocale: DEFAULT_LANGUAGE,
|
||||
|
|
@ -102,6 +108,7 @@ export async function setLanguage(lang: SupportedLocale): Promise<SupportedLocal
|
|||
|
||||
i18n.global.locale.value = lang
|
||||
document.documentElement.lang = lang
|
||||
document.documentElement.dir = isRTLLanguage(lang) ? 'rtl' : 'ltr'
|
||||
return lang
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// very hard to untangle
|
||||
// they have many overwrites at different positions
|
||||
.tasks {
|
||||
text-align: left;
|
||||
text-align: start;
|
||||
|
||||
|
||||
@media screen and (max-width: $tablet) {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
}
|
||||
|
||||
td.actions {
|
||||
text-align: right;
|
||||
text-align: end;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -26,5 +26,5 @@
|
|||
|
||||
.content blockquote {
|
||||
background-color: var(--grey-200);
|
||||
border-left: .25rem solid var(--grey-300);
|
||||
border-inline-start: .25rem solid var(--grey-300);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
.field.has-addons .button {
|
||||
height: 2.5rem;
|
||||
margin-left: 0 !important;
|
||||
margin-inline-start: 0 !important;
|
||||
}
|
||||
|
||||
.field.has-addons .select select {
|
||||
margin-right: 0;
|
||||
margin-inline-end: 0;
|
||||
}
|
||||
|
||||
.input,
|
||||
|
|
@ -67,7 +67,7 @@
|
|||
|
||||
// Buttons icons
|
||||
.button .icon.is-small {
|
||||
margin-right: 0.05rem !important;
|
||||
margin-inline-end: 0.05rem !important;
|
||||
}
|
||||
|
||||
// FIXME: used for
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
@include loader;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
margin-left: calc(50% - 1rem);
|
||||
margin-inline-start: calc(50% - 1rem);
|
||||
margin-top: 1rem;
|
||||
border-width: 0.25rem;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@
|
|||
.icon {
|
||||
height: 1rem;
|
||||
vertical-align: middle;
|
||||
padding-right: 0.5rem;
|
||||
padding-inline-end: 0.5rem;
|
||||
}
|
||||
|
||||
&.router-link-exact-active .icon:not(.handle) {
|
||||
|
|
@ -134,7 +134,7 @@
|
|||
}
|
||||
|
||||
menu {
|
||||
border-left: 0;
|
||||
border-inline-start: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
<ImportHint v-if="tasksLoaded" />
|
||||
<div
|
||||
v-if="projectHistory.length > 0"
|
||||
class="is-max-width-desktop has-text-left mt-4"
|
||||
class="is-max-width-desktop has-text-start mt-4"
|
||||
>
|
||||
<h3>{{ $t('home.lastViewed') }}</h3>
|
||||
<ProjectCardGrid
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@ function toggleGroupPermissionsFromChild(group: string, checked: boolean) {
|
|||
<th>{{ $t('user.settings.apiTokens.attributes.permissions') }}</th>
|
||||
<th>{{ $t('user.settings.apiTokens.attributes.expiresAt') }}</th>
|
||||
<th>{{ $t('misc.created') }}</th>
|
||||
<th class="has-text-right">
|
||||
<th class="has-text-end">
|
||||
{{ $t('misc.actions') }}
|
||||
</th>
|
||||
</tr>
|
||||
|
|
@ -216,7 +216,7 @@ function toggleGroupPermissionsFromChild(group: string, checked: boolean) {
|
|||
</p>
|
||||
</td>
|
||||
<td>{{ formatDisplayDate(tk.created) }}</td>
|
||||
<td class="has-text-right">
|
||||
<td class="has-text-end">
|
||||
<XButton
|
||||
variant="secondary"
|
||||
@click="() => {tokenToDelete = tk; showDeleteModal = true}"
|
||||
|
|
@ -290,7 +290,7 @@ function toggleGroupPermissionsFromChild(group: string, checked: boolean) {
|
|||
<flat-pickr
|
||||
v-if="newTokenExpiry === 'custom'"
|
||||
v-model="newTokenExpiryCustom"
|
||||
class="ml-2"
|
||||
class="ms-2"
|
||||
:config="flatPickerConfig"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -310,7 +310,7 @@ function toggleGroupPermissionsFromChild(group: string, checked: boolean) {
|
|||
>
|
||||
<FancyCheckbox
|
||||
v-model="newTokenPermissionsGroup[group]"
|
||||
class="mr-2 is-capitalized has-text-weight-bold"
|
||||
class="me-2 is-capitalized has-text-weight-bold"
|
||||
@update:modelValue="checked => selectPermissionGroup(group, checked)"
|
||||
>
|
||||
{{ formatPermissionTitle(group) }}
|
||||
|
|
@ -323,7 +323,7 @@ function toggleGroupPermissionsFromChild(group: string, checked: boolean) {
|
|||
>
|
||||
<FancyCheckbox
|
||||
v-model="newTokenPermissions[group][route]"
|
||||
class="ml-4 mr-2 is-capitalized"
|
||||
class="ms-4 me-2 is-capitalized"
|
||||
@update:modelValue="checked => toggleGroupPermissionsFromChild(group, checked)"
|
||||
>
|
||||
{{ formatPermissionTitle(route) }}
|
||||
|
|
|
|||
Loading…
Reference in New Issue