The UI is adapted to the RTL layout

This commit is contained in:
ErickSkrauch
2021-03-26 04:19:04 +01:00
parent 370725dd7e
commit 4525089725
41 changed files with 254 additions and 170 deletions

View File

@@ -119,6 +119,7 @@ interface State {
formsHeights: Record<PanelId, number>;
}
// TODO: completely broken for RTL languages
class PanelTransition extends React.PureComponent<Props, State> {
state: State = {
contextHeight: 0,

View File

@@ -16,7 +16,6 @@
display: block;
position: absolute;
left: 0;
bottom: 0;
height: 3px;
width: 40px;

View File

@@ -3,7 +3,7 @@
.accountSwitcher {
background: $black;
text-align: left;
text-align: start;
}
$border: 1px solid lighter($black);
@@ -37,12 +37,12 @@ $border: 1px solid lighter($black);
.accountAvatar {
font-size: 35px;
margin-right: 15px;
margin-inline-end: 15px;
}
.accountInfo {
flex-grow: 1;
margin-right: 15px;
margin-inline-end: 15px;
min-width: 0; // Fix for text-overflow. See https://stackoverflow.com/a/40612184
}
@@ -76,17 +76,25 @@ $border: 1px solid lighter($black);
composes: arrowRight from '~app/components/ui/icons.scss';
position: relative;
left: 0;
inset-inline-start: 0;
font-size: 24px;
color: #4e4e4e;
line-height: 35px;
transition: color 0.25s, inset-inline-start 0.5s;
// TODO: right now transition property doesn't support the bidirectional value.
// See https://github.com/gasolin/postcss-bidirection/issues/25.
// noinspection CssOverwrittenProperties Graceful degradation
transition: color 0.25s, left 0.5s;
html[dir='rtl'] & {
transition: color 0.25s, right 0.5s;
}
.item:hover & {
color: #aaa;
left: 5px;
inset-inline-start: 5px;
}
}

View File

@@ -16,7 +16,7 @@
position: relative;
bottom: 1px;
padding-left: 3px;
padding-inline-start: 3px;
color: #666666;
font-size: 10px;

View File

@@ -4,18 +4,18 @@
.authInfo {
// Отступы сверху и снизу разные т.к. мы ужимаем высоту линии строки с логином на 2 пикселя и из-за этого теряем отступ снизу
padding: 5px 20px 7px;
text-align: left;
text-align: start;
}
.authInfoAvatar {
$size: 30px;
float: left;
float: start;
height: $size;
width: $size;
font-size: $size;
line-height: 1;
margin-right: 10px;
margin-inline-end: 10px;
margin-top: 2px;
color: #aaa;
@@ -38,7 +38,7 @@
.permissionsContainer {
padding: 15px 12px;
text-align: left;
text-align: start;
}
.permissionsTitle {
@@ -57,7 +57,7 @@
font-size: 14px;
line-height: 1.4;
padding-bottom: 4px;
padding-left: 17px;
padding-inline-start: 17px;
position: relative;
&:last-of-type {
@@ -71,7 +71,7 @@
line-height: 9px;
position: absolute;
top: 6px;
left: -4px;
inset-inline-start: -4px;
}
}
}

View File

@@ -37,7 +37,7 @@
width: 50%;
&:first-of-type {
margin-right: $popupPadding;
margin-inline-end: $popupPadding;
}
}

View File

@@ -160,7 +160,7 @@
composes: arrowRight from '~app/components/ui/icons.scss';
position: relative;
left: 0;
inset-inline-start: 0;
font-size: 28px;
color: #ebe8e1;
@@ -173,7 +173,11 @@
.appExpanded & {
color: #777;
transform: rotate(360deg) !important; // Prevent it from hover rotating
transform: rotate(360deg)!important; // Prevent it from hover rotating
html[dir='rtl'] & {
transform: rotate(0)!important;
}
}
}
@@ -195,7 +199,7 @@ $appDetailsContainerRightLeftPadding: 30px;
.editAppLink {
position: absolute;
top: 4px;
right: 0;
inset-inline-end: 0;
font-size: 12px;
color: #9a9a9a;
@@ -221,17 +225,19 @@ $appDetailsContainerRightLeftPadding: 30px;
}
.appActionButton {
margin: 0 10px 10px 0;
margin-inline-end: 10px;
margin-bottom: 10px;
&:last-of-type {
margin-right: 0;
margin-inline-end: 0;
}
}
.appActionContainer {
position: absolute;
width: calc(100% - #{$appDetailsContainerRightLeftPadding * 2});
top: 100%;
left: 0;
inset-inline-start: 0;
padding: 0 $appDetailsContainerRightLeftPadding;
background: #f5f5f5;
}
@@ -244,7 +250,7 @@ $appDetailsContainerRightLeftPadding: 30px;
.continueActionButtonWrapper {
display: inline-block;
margin-left: 10px;
margin-inline-start: 10px;
}
.continueActionLink {

View File

@@ -24,5 +24,5 @@
position: relative;
bottom: 1px;
font-size: 11px;
margin-right: 3px;
margin-inline-end: 3px;
}

View File

@@ -26,7 +26,7 @@
position: relative;
top: 1px;
display: inline-block;
margin-right: 4px;
margin-inline-end: 4px;
height: $height;
width: $height * 4 / 3;
box-shadow: 0 0 1px rgba(#000, 0.2);

View File

@@ -33,7 +33,7 @@
position: absolute;
top: 14px;
right: 12px;
inset-inline-end: 12px;
font-size: 22px;
color: #edebe5;
pointer-events: none; // Иконка чисто декоративная, так что клик должен проходить сквозь неё
@@ -94,7 +94,7 @@ $languageListBorderStyle: 1px solid $languageListBorderColor;
.languageIco {
display: inline-block;
margin-right: 7px;
margin-inline-end: 7px;
width: 40px;
height: 30px;
box-shadow: 0 0 1px rgba(#000, 0.2);
@@ -129,7 +129,7 @@ $languageListBorderStyle: 1px solid $languageListBorderColor;
box-sizing: border-box;
width: 22px;
height: 22px;
right: -32px;
inset-inline-end: -32px;
font-size: 10px;
line-height: 18px;
@@ -146,13 +146,13 @@ $languageListBorderStyle: 1px solid $languageListBorderColor;
}
.languageItem:hover & {
right: 0;
inset-inline-end: 0;
}
.activeLanguageItem & {
border-color: $green;
background: $green;
right: 0;
inset-inline-end: 0;
&:before {
opacity: 1;
@@ -209,7 +209,7 @@ $languageListBorderStyle: 1px solid $languageListBorderColor;
color: lighter($blue);
font-size: 22px;
margin-right: 10px;
margin-inline-end: 10px;
}
.improveTranslatesContent {

View File

@@ -19,12 +19,12 @@
li {
position: relative;
padding-left: 11px;
padding-inline-start: 11px;
&:before {
content: '';
position: absolute;
left: 0;
inset-inline-start: 0;
top: 2px;
}

View File

@@ -75,7 +75,7 @@ export default function OsInstruction({ os }: { os: OS }) {
<ul className={styles.appList}>
{linksByOs[os].featured.map((item) => (
<li key={item.label}>
<a href={item.link} target="_blank">
<a href={item.link} target="_blank" dir="ltr">
{item.label}
</a>
</li>

View File

@@ -22,7 +22,7 @@
.otherApps {
position: absolute;
right: 0;
inset-inline-end: 0;
bottom: 5px;
font-size: 10px;
@@ -96,7 +96,7 @@
.androidActive & {
transform: translateX(0);
left: 0;
inset-inline-start: 0;
}
.appleActive &,
@@ -111,21 +111,33 @@
$translateX: -51%;
transform: translateX($translateX) scale(1);
left: 49%;
inset-inline-start: 49%;
html[dir='rtl'] & {
transform: translateX(-$translateX) scale(1);
}
&:hover {
transform: translateX($translateX) scale(1.1);
html[dir='rtl'] & {
transform: translateX(-$translateX) scale(1.1);
}
}
.appleActive & {
transform: translateX(0);
left: 0;
transform: translateX(0)!important; // override dir='rtl'
inset-inline-start: 0;
}
.androidActive &,
.windowsActive & {
transform: translateX($translateX) scale(0);
opacity: 0;
html[dir='rtl'] & {
transform: translateX(-$translateX) scale(0);
}
}
}
@@ -134,21 +146,33 @@
$translateX: -100%;
transform: translateX($translateX) scale(1);
left: 100%;
inset-inline-start: 100%;
html[dir='rtl'] & {
transform: translateX(-$translateX) scale(1);
}
&:hover {
transform: translateX($translateX) scale(1.1);
html[dir='rtl'] & {
transform: translateX(-$translateX) scale(1.1);
}
}
.windowsActive & {
transform: translateX(0);
left: 0;
transform: translateX(0)!important; // override dir='rtl'
inset-inline-start: 0;
}
.appleActive &,
.androidActive & {
transform: translateX($translateX) scale(0);
opacity: 0;
html[dir='rtl'] & {
transform: translateX(-$translateX) scale(0);
}
}
}
@@ -166,8 +190,8 @@
position: relative;
z-index: 1;
margin: 15px;
margin-left: 30%;
padding-left: 15px;
margin-inline-start: 30%;
padding-inline-start: 15px;
padding-bottom: 15px;
min-height: $boxHeight;
}
@@ -212,7 +236,7 @@
.osName {
font-family: $font-family-title;
font-size: 16px;
margin-left: 10px;
margin-inline-start: 10px;
}
@mixin commonNonActiveTile() {

View File

@@ -16,8 +16,8 @@ $maxQrCodeSize: 242px;
}
.or {
position: absolute;
left: 0;
position: absolute; // Use absolute positioning to put an element between margin
inset-inline-start: 0; // TODO: doesn't have any effect. Probably can be removed
width: 100%;
margin-top: -18px;

View File

@@ -13,7 +13,8 @@ $formColumnWidth: 416px;
}
.descriptionColumn {
padding: 12px 20px 0 0;
padding-top: 12px;
padding-inline-end: 20px;
box-sizing: border-box;
}
@@ -97,7 +98,8 @@ $formColumnWidth: 416px;
}
.paramMessage {
padding: 10px 40px 0 0;
padding-top: 10px;
padding-inline-end: 40px;
color: $red;
font-size: 11px;
@@ -168,7 +170,7 @@ $formColumnWidth: 416px;
}
.paramEditIcon {
margin-left: 5px;
margin-inline-start: 5px;
vertical-align: top;
}

View File

@@ -9,7 +9,7 @@
.backButton {
position: absolute;
left: -60px;
inset-inline-start: -60px;
top: 15px;
width: 25px;
height: 25px;
@@ -60,7 +60,6 @@
display: block;
position: absolute;
left: 0;
bottom: 0;
height: 3px;
width: 86px;
@@ -110,7 +109,7 @@
.backButton {
top: 29px;
left: 27px;
inset-inline-start: 27px;
padding: 0;
margin: 0;
width: auto;

View File

@@ -29,7 +29,8 @@
}
.iconWrapper {
margin: 4px 12px 0 0;
margin-top: 4px;
margin-inline-end: 12px;
}
.dbIcon {
@@ -68,14 +69,15 @@
font-family: $font-family-title;
font-size: 14px;
color: #9a9a9a;
margin: 0 0 5px 1px; // Add 1px from the left to make icon look visually aligned with texts
margin-bottom: 5px;
margin-inline-start: 1px; // Add 1px from the left to make icon look visually aligned with texts
}
.githubIcon {
composes: github from '~app/components/ui/icons.scss';
font-size: 12px;
margin-right: 2px;
margin-inline-end: 2px;
position: relative;
bottom: 1px;
}

View File

@@ -1,20 +0,0 @@
.horizontalGroup {
display: flex;
}
.item {
// TODO: in some cases we do not need overflow hidden
// probably, it is better to create a separate class for children, that will
// enable overflow hidden and ellipsis
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
$borderConfig: 1px solid rgba(#fff, 0.15);
border-left: $borderConfig;
&:last-child {
border-right: $borderConfig;
}
}

View File

@@ -1,29 +1,29 @@
import React from 'react';
import React, { ReactNode } from 'react';
import { MessageDescriptor } from 'react-intl';
import i18n from 'app/services/i18n';
function isMessageDescriptor(value: any): value is MessageDescriptor {
return typeof value === 'object' && value.id;
}
export default class FormComponent<P, S = {}> extends React.Component<P, S> {
/**
* Formats message resolving intl translations
*
* @param {string|object} message - message string, or intl message descriptor with an `id` field
*
* @deprecated
* @returns {string}
*/
formatMessage(message: string | MessageDescriptor): string {
if (!message) {
throw new Error('A message is required');
formatMessage<T extends ReactNode | MessageDescriptor>(message: T): T extends MessageDescriptor ? string : T {
if (isMessageDescriptor(message)) {
// @ts-ignore
return i18n.getIntl().formatMessage(message);
}
if (typeof message === 'string') {
return message;
}
if (!message.id) {
throw new Error(`Invalid message format: ${JSON.stringify(message)}`);
}
return i18n.getIntl().formatMessage(message);
// @ts-ignore
return message;
}
/**

View File

@@ -1,4 +1,4 @@
import React from 'react';
import React, { ReactNode } from 'react';
import { MessageDescriptor } from 'react-intl';
import clsx from 'clsx';
import { uniqueId, omit } from 'app/functions';
@@ -17,7 +17,7 @@ export default class Input extends FormInputComponent<
color: Color;
center: boolean;
disabled: boolean;
label?: string | MessageDescriptor;
label?: ReactNode | MessageDescriptor;
placeholder?: string | MessageDescriptor;
icon?: string;
copy?: boolean;

View File

@@ -25,8 +25,9 @@ $dropdownPadding: 15px;
display: inline-block;
box-sizing: border-box;
height: 50px;
padding-inline-start: $dropdownPadding;
// 28px - ширина иконки при заданном размере шрифта
padding: 0 ($dropdownPadding * 2 + 28px) 0 $dropdownPadding;
padding-inline-end: $dropdownPadding * 2 + 28px;
position: relative;
font-family: $font-family-title;
@@ -48,6 +49,7 @@ $dropdownPadding: 15px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
text-align: start;
}
.opened {
@@ -57,18 +59,26 @@ $dropdownPadding: 15px;
composes: selecter from '~app/components/ui/icons.scss';
position: absolute;
right: $dropdownPadding;
inset-inline-end: $dropdownPadding;
top: 16px;
font-size: 17px;
transition: right 0.3s cubic-bezier(0.23, 1, 0.32, 1); // easeOutQuint
transition: inset-inline-end 0.3s cubic-bezier(0.23, 1, 0.32, 1); // easeOutQuint
// TODO: right now transition property doesn't support the bidirectional value.
// See https://github.com/gasolin/postcss-bidirection/issues/25.
// noinspection CssOverwrittenProperties Graceful degradation
transition: right 0.3s cubic-bezier(0.23, 1, 0.32, 1);
html[dir='rtl'] & {
transition: left 0.3s cubic-bezier(0.23, 1, 0.32, 1);
}
.dropdown:hover & {
right: $dropdownPadding - 5px;
inset-inline-end: $dropdownPadding - 5px;
}
.dropdown:active &,
.dropdown.opened & {
right: $dropdownPadding + 5px;
inset-inline-end: $dropdownPadding + 5px;
}
}
@@ -84,7 +94,7 @@ $dropdownPadding: 15px;
.menu {
position: absolute;
top: 60px;
left: 0;
inset-inline-start: 0;
z-index: 10;
width: 120%;

View File

@@ -36,7 +36,7 @@
composes: formRow;
.textField {
padding-left: 60px;
padding-inline-start: 60px;
}
}
@@ -80,7 +80,7 @@
.textFieldIcon {
box-sizing: border-box;
position: absolute;
left: 0;
inset-inline-start: 0;
top: 0;
height: 50px;
width: 50px;
@@ -95,7 +95,7 @@
.copyIcon {
position: absolute;
right: 5px;
inset-inline-end: 5px;
top: 10px;
padding: 5px;
@@ -175,6 +175,7 @@
font-family: $font-family-title;
color: #666;
font-size: 18px;
text-align: start;
}
.fieldError {
@@ -248,7 +249,7 @@
.markableContainer {
display: inline-block;
position: relative;
padding-left: 27px;
padding-inline-start: 27px;
font-family: $font-family-title;
font-size: 16px;
@@ -260,7 +261,7 @@
.markPosition {
position: absolute;
box-sizing: border-box;
left: 0;
inset-inline-start: 0;
top: 0;
margin: 0;

View File

@@ -15,10 +15,30 @@
@extend .arrow;
transform: rotate(270deg);
html[dir='rtl'] & {
transform: rotate(90deg);
}
}
.arrowLeft {
@extend .arrow;
transform: rotate(90deg);
html[dir='rtl'] & {
transform: rotate(270deg);
}
}
html[dir='rtl'] {
.brush,
.exit,
.key,
.pencil,
.search {
&:before {
transform: scaleX(-1);
}
}
}

View File

@@ -1,4 +1,6 @@
.loader-overlay {
direction: ltr;
background: rgba(255, 255, 255, 0.3);
position: fixed;
top: 0;

View File

@@ -77,6 +77,7 @@ class SlideMotion extends React.PureComponent<Props, State> {
<div
className={styles.container}
style={{
// TODO: inverse for RTL language
WebkitTransform: `translateX(-${interpolatingStyle.transform}%)`,
transform: `translateX(-${interpolatingStyle.transform}%)`,
}}

View File

@@ -25,12 +25,12 @@ $headerHeight: 60px;
.headerControl {
composes: black from '~app/components/ui/buttons.scss';
float: left;
float: start;
overflow: hidden;
height: $headerHeight - 1px;
width: 49px;
padding: 0;
border-right: 1px solid lighter($black);
border-inline-end: 1px solid lighter($black);
line-height: 1;
text-align: center;
@@ -122,7 +122,7 @@ $bodyTopBottomPadding: 15px;
composes: close from '~app/components/ui/icons.scss';
position: absolute;
right: 5px;
inset-inline-end: 5px;
top: 5px;
font-size: 10px;

View File

@@ -59,7 +59,7 @@ $popupMargin: 20px; // Outer popup margins
.popup {
white-space: normal;
text-align: left;
text-align: start;
background: #fff;
box-shadow: 0 0 10px rgba(#000, 0.2);
@@ -95,7 +95,7 @@ $popupMargin: 20px; // Outer popup margins
composes: close from '~app/components/ui/icons.scss';
position: absolute;
right: 0;
inset-inline-end: 0;
top: 0;
padding: 15px;
cursor: pointer;
@@ -122,6 +122,10 @@ $popupMargin: 20px; // Outer popup margins
opacity: 0;
transform: translate(100%);
transition: 0s;
html[dir='rtl'] & {
transform: translate(-100%);
}
}
}

View File

@@ -47,7 +47,7 @@
position: absolute;
top: 0.16em;
left: -0.145em;
inset-inline-start: -0.145em;
font-size: 0.7em;
color: rgba($red, 0.75);
}

View File

@@ -8,7 +8,7 @@
.step {
position: relative;
text-align: right;
text-align: end;
width: 100%;
height: 4px;
@@ -24,8 +24,8 @@
position: absolute;
height: 4px;
left: 0;
right: 100%;
inset-inline-start: 0;
inset-inline-end: 100%;
top: 50%;
margin-top: -2px;
@@ -53,7 +53,7 @@
.activeStep {
&:before {
right: 0;
inset-inline-end: 0;
transition-delay: unset;
}

View File

@@ -47,7 +47,7 @@ const AccountSwitcher: ComponentType<Props> = ({
<div className={clsx(styles.accountSwitcher)} data-testid="account-switcher">
<div className={styles.item} data-testid="active-account">
<PseudoAvatar className={styles.activeAccountIcon} />
<div className={styles.activeAccountInfo}>
<div>
<div className={styles.activeAccountUsername}>{activeAccount.username}</div>
<div className={clsx(styles.accountEmail, styles.activeAccountEmail)}>{activeAccount.email}</div>
<div className={styles.links}>
@@ -81,16 +81,16 @@ const AccountSwitcher: ComponentType<Props> = ({
>
<PseudoAvatar index={index + 1} deleted={account.isDeleted} className={styles.accountIcon} />
<div className={styles.accountInfo}>
<div className={styles.accountUsername}>{account.username}</div>
<div className={styles.accountEmail}>{account.email}</div>
</div>
<div
className={styles.logoutIcon}
data-testid="logout-account"
onClick={onAccountRemoveCallback(account)}
/>
<div className={styles.accountInfo}>
<div className={styles.accountUsername}>{account.username}</div>
<div className={styles.accountEmail}>{account.email}</div>
</div>
</div>
))}
<Link to="/login" onClick={onLoginClick}>

View File

@@ -8,8 +8,6 @@ $bodyLeftRightPadding: 20px;
$lightBorderColor: #eee;
.accountSwitcher {
text-align: left;
background: #fff;
color: #444;
min-width: 205px;
@@ -20,9 +18,6 @@ $lightBorderColor: #eee;
border-bottom: 7px solid darker($green);
}
.accountInfo {
}
.accountUsername,
.accountEmail {
overflow: hidden;
@@ -30,6 +25,7 @@ $lightBorderColor: #eee;
}
.item {
display: flex;
padding: 15px;
border-bottom: 1px solid $lightBorderColor;
}
@@ -50,8 +46,7 @@ $lightBorderColor: #eee;
.accountIcon {
font-size: 27px;
width: 20px;
text-align: center;
float: left;
margin-inline-end: 9px;
}
.activeAccountIcon {
@@ -60,10 +55,6 @@ $lightBorderColor: #eee;
font-size: 40px;
}
.activeAccountInfo {
margin-left: 29px;
}
.activeAccountUsername {
font-family: $font-family-title;
font-size: 20px;
@@ -88,8 +79,7 @@ $lightBorderColor: #eee;
}
.accountInfo {
margin-left: 29px;
margin-right: 25px;
flex-grow: 1;
}
.accountUsername {
@@ -117,15 +107,16 @@ $lightBorderColor: #eee;
color: $green;
position: relative;
bottom: 1px;
margin-right: 3px;
margin-inline-end: 3px;
}
.logoutIcon {
composes: exit from '~app/components/ui/icons.scss';
align-self: center;
margin-inline-start: 9px;
color: #cdcdcd;
float: right;
line-height: 27px;
transition: 0.25s;
&:hover {

View File

@@ -27,6 +27,10 @@
.expandIcon {
transform: rotate(-180deg);
html[dir='rtl'] & {
transform: rotate(180deg); // Fix spin direction
}
}
}
@@ -36,13 +40,13 @@
position: relative;
bottom: 2px;
padding-right: 5px;
padding-inline-end: 5px;
}
.expandIcon {
composes: caret from '~app/components/ui/icons.scss';
margin-left: 4px;
margin-inline-start: 4px;
font-size: 6px;
color: #ccc;
transition: 0.2s;
@@ -54,7 +58,7 @@
.accountSwitcherContainer {
position: absolute;
top: 100%;
right: -2px;
inset-inline-end: -2px;
cursor: auto;
display: none;