From 8d479ab238a1121b39c65ccc825885fa5da5163b Mon Sep 17 00:00:00 2001 From: Jed Fox Date: Wed, 25 Nov 2020 18:21:33 -0500 Subject: [PATCH] RTL updates (#2416) * Update a bunch of icons to be mirrored in RTL * Fix RTL layout issues in in zen mode and collaboration * Small change to the shortcuts dialog to make isRTL unnecessary * Tweaks to alignment in RTL --- src/components/Actions.tsx | 22 +++++++++++++++--- src/components/LayerUI.scss | 12 ++++++++-- src/components/RoomDialog.scss | 11 ++++++--- src/components/ShortcutsDialog.tsx | 4 +--- src/components/icons.tsx | 36 +++++++++++++++++------------- src/css/styles.scss | 6 +++-- 6 files changed, 63 insertions(+), 28 deletions(-) diff --git a/src/components/Actions.tsx b/src/components/Actions.tsx index 995301aa..b7144e15 100644 --- a/src/components/Actions.tsx +++ b/src/components/Actions.tsx @@ -35,6 +35,8 @@ export const SelectedShapeActions = ({ const isEditing = Boolean(appState.editingElement); const isMobile = useIsMobile(); + const isRTL = document.documentElement.getAttribute("dir") === "rtl"; + return (
{renderAction("changeStrokeColor")} @@ -88,9 +90,23 @@ export const SelectedShapeActions = ({
{t("labels.align")}
- {renderAction("alignLeft")} - {renderAction("alignHorizontallyCentered")} - {renderAction("alignRight")} + { + // swap this order for RTL so the button positions always match their action + // (i.e. the leftmost button aligns left) + } + {isRTL ? ( + <> + {renderAction("alignRight")} + {renderAction("alignHorizontallyCentered")} + {renderAction("alignLeft")} + + ) : ( + <> + {renderAction("alignLeft")} + {renderAction("alignHorizontallyCentered")} + {renderAction("alignRight")} + + )} {targetElements.length > 2 && renderAction("distributeHorizontally")}
diff --git a/src/components/LayerUI.scss b/src/components/LayerUI.scss index c40ca6e0..a9a80931 100644 --- a/src/components/LayerUI.scss +++ b/src/components/LayerUI.scss @@ -142,16 +142,24 @@ transform: translate(-999px, 0); } - &.App-menu_bottom--transition-left { + :root[dir="ltr"] &.App-menu_bottom--transition-left { transform: translate(-92px, 0); } + :root[dir="rtl"] &.App-menu_bottom--transition-left { + transform: translate(92px, 0); + } } .disable-zen-mode { height: 30px; position: absolute; bottom: 10px; - right: 15px; + [dir="ltr"] & { + right: 15px; + } + [dir="rtl"] & { + left: 15px; + } font-size: 10px; padding: 10px; font-weight: 500; diff --git a/src/components/RoomDialog.scss b/src/components/RoomDialog.scss index 9e54152d..5142ece2 100644 --- a/src/components/RoomDialog.scss +++ b/src/components/RoomDialog.scss @@ -12,8 +12,13 @@ .RoomDialog-modalButton-collaborators { min-width: 1em; position: absolute; + :root[dir="ltr"] & { + right: -5px; + } + :root[dir="rtl"] & { + left: -5px; + } bottom: -5px; - right: -5px; padding: 3px; border-radius: 50%; background-color: $oc-green-6; @@ -31,7 +36,7 @@ color: var(--text-color-primary); min-width: 0; flex: 1 1 auto; - margin-left: 1em; + margin-inline-start: 1em; display: inline-block; cursor: pointer; border: none; @@ -61,7 +66,7 @@ appearance: none; min-width: 0; flex: 1 1 auto; - margin-left: 1em; + margin-inline-start: 1em; height: 2.5rem; font-size: 1em; line-height: 1.5; diff --git a/src/components/ShortcutsDialog.tsx b/src/components/ShortcutsDialog.tsx index d85adee1..c9b2e1ca 100644 --- a/src/components/ShortcutsDialog.tsx +++ b/src/components/ShortcutsDialog.tsx @@ -37,7 +37,6 @@ const Shortcut = (props: { shortcuts: string[]; isOr: boolean; }) => { - const isRTL = document.documentElement.getAttribute("dir") === "rtl"; return (
diff --git a/src/components/icons.tsx b/src/components/icons.tsx index 0ae2929f..e6d23198 100644 --- a/src/components/icons.tsx +++ b/src/components/icons.tsx @@ -3,6 +3,9 @@ // Icons are under the license https://fontawesome.com/license // +// Note: when adding new icons, review https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/RTL_Guidelines +// to determine whether or not the icons should be mirrored in right-to-left languages. + import React from "react"; import oc from "open-color"; @@ -89,7 +92,6 @@ export const zoomOut = createIcon( export const done = createIcon( "M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z", - { mirror: true }, ); export const menu = createIcon( @@ -135,7 +137,7 @@ export const BringForwardIcon = React.memo( strokeWidth="2" /> , - { width: 24 }, + { width: 24, mirror: true }, ), ); @@ -156,7 +158,7 @@ export const SendBackwardIcon = React.memo( strokeWidth="2" /> , - { width: 24 }, + { width: 24, mirror: true }, ), ); @@ -177,7 +179,7 @@ export const BringToFrontIcon = React.memo( strokeWidth="2" /> , - { width: 24 }, + { width: 24, mirror: true }, ), ); @@ -200,12 +202,15 @@ export const SendToBackIcon = React.memo( strokeWidth="2" /> , - { width: 24 }, + { width: 24, mirror: true }, ), ); // // Align action icons created from scratch to match those of z-index actions +// Note: vertical align icons are flipped so the larger item is always the +// first one the user sees. Horizontal align icons should not be flipped since +// that would make them lie about their function. // export const AlignTopIcon = React.memo( ({ appearance }: { appearance: "light" | "dark" }) => @@ -225,7 +230,7 @@ export const AlignTopIcon = React.memo( strokeWidth="2" /> , - { width: 24 }, + { width: 24, mirror: true }, ), ); @@ -247,7 +252,7 @@ export const AlignBottomIcon = React.memo( strokeWidth="2" /> , - { width: 24 }, + { width: 24, mirror: true }, ), ); @@ -366,7 +371,7 @@ export const CenterVerticallyIcon = React.memo( strokeLinecap="round" /> , - { width: 24 }, + { width: 24, mirror: true }, ), ); @@ -398,6 +403,7 @@ export const users = createIcon( { width: 640, height: 512, mirror: true }, ); +// not mirrored because it's inspired by a playback control, which is always RTL export const start = createIcon( "M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm115.7 272l-176 101c-15.8 8.8-35.7-2.5-35.7-21V152c0-18.4 19.8-29.8 35.7-21l176 107c16.4 9.2 16.4 32.9 0 42z", ); @@ -480,7 +486,7 @@ export const GroupIcon = React.memo( strokeWidth="6" /> , - { width: 182, height: 182 }, + { width: 182, height: 182, mirror: true }, ), ); @@ -555,7 +561,7 @@ export const UngroupIcon = React.memo( strokeWidth="6" /> , - { width: 182, height: 182 }, + { width: 182, height: 182, mirror: true }, ), ); @@ -672,7 +678,7 @@ export const SloppinessArchitectIcon = React.memo( strokeWidth={2} fill="none" />, - { width: 40, height: 20 }, + { width: 40, height: 20, mirror: true }, ), ); @@ -685,7 +691,7 @@ export const SloppinessArtistIcon = React.memo( strokeWidth={2} fill="none" />, - { width: 40, height: 20 }, + { width: 40, height: 20, mirror: true }, ), ); @@ -706,7 +712,7 @@ export const SloppinessCartoonistIcon = React.memo( fill="none" /> , - { width: 40, height: 20 }, + { width: 40, height: 20, mirror: true }, ), ); @@ -719,7 +725,7 @@ export const EdgeSharpIcon = React.memo( strokeWidth={2} fill="none" />, - { width: 40, height: 20 }, + { width: 40, height: 20, mirror: true }, ), ); @@ -732,6 +738,6 @@ export const EdgeRoundIcon = React.memo( strokeWidth={2} fill="none" />, - { width: 40, height: 20 }, + { width: 40, height: 20, mirror: true }, ), ); diff --git a/src/css/styles.scss b/src/css/styles.scss index 7d483882..0b929053 100644 --- a/src/css/styles.scss +++ b/src/css/styles.scss @@ -104,7 +104,8 @@ } .ToolIcon { - margin: 0 8px 0 0; + margin: 0; + margin-inline-end: 8px; &:focus { outline: transparent; @@ -392,7 +393,8 @@ } .zIndexButton { - margin: 0 8px 0 0; + margin: 0; + margin-inline-end: 8px; padding: 5px; display: inline-flex; align-items: center;