From f05e3012e390ba23486f4cf916a884055ff5dfef Mon Sep 17 00:00:00 2001 From: Lim Chee Aun Date: Sun, 4 Aug 2024 13:32:30 +0800 Subject: [PATCH] Preliminary step for RTL --- src/app.css | 321 +++++++++++++------------- src/app.jsx | 2 + src/components/ICONS.jsx | 117 +++++++--- src/components/account-block.css | 2 + src/components/account-block.jsx | 2 +- src/components/account-info.css | 32 ++- src/components/account-info.jsx | 11 +- src/components/compose.css | 28 +-- src/components/compose.jsx | 10 +- src/components/drafts.css | 2 +- src/components/generic-accounts.css | 6 +- src/components/icon.jsx | 10 +- src/components/links-bar.css | 13 +- src/components/media-modal.jsx | 20 +- src/components/media-post.css | 2 +- src/components/menu2.jsx | 14 +- src/components/modal.css | 19 +- src/components/name-text.jsx | 6 +- src/components/nav-menu.css | 12 +- src/components/poll.jsx | 3 - src/components/report-modal.css | 4 +- src/components/search-form.jsx | 1 + src/components/shortcuts-settings.css | 18 +- src/components/shortcuts-settings.jsx | 5 + src/components/shortcuts.css | 10 +- src/components/status.css | 172 ++++++++------ src/components/status.jsx | 20 +- src/components/timeline.jsx | 11 +- src/components/translation-block.css | 7 +- src/index.css | 16 ++ src/pages/accounts.css | 4 +- src/pages/catchup.css | 44 ++-- src/pages/filters.jsx | 1 + src/pages/hashtag.jsx | 5 +- src/pages/login.css | 5 +- src/pages/login.jsx | 1 + src/pages/notifications.css | 15 +- src/pages/search.css | 15 +- src/pages/settings.css | 10 +- src/pages/status.css | 4 +- src/pages/status.jsx | 2 + src/pages/trending.jsx | 2 +- src/pages/welcome.css | 7 +- src/utils/is-rtl.js | 26 +++ 44 files changed, 654 insertions(+), 383 deletions(-) create mode 100644 src/utils/is-rtl.js diff --git a/src/app.css b/src/app.css index e675c4d1..52245283 100644 --- a/src/app.css +++ b/src/app.css @@ -162,7 +162,7 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) { white-space: nowrap; } .deck > header .header-grid > .header-side:last-of-type { - text-align: right; + text-align: end; grid-column: 3; } .deck > header .header-grid :is(button, .button).plain { @@ -181,8 +181,8 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) { grid-template-columns: 1fr max-content; } .deck > header .header-grid-2 h1 { - text-align: left; - padding-left: 8px; + text-align: start; + padding-inline-start: 8px; } .deck > header .header-grid h1:has(.ancestors-indicator) { display: flex; @@ -217,6 +217,19 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) { opacity: 0.25; } } +@keyframes indeterminate-bar-rtl { + 0% { + transform: translateX(50%); + opacity: 0.25; + } + 50% { + opacity: 1; + } + 100% { + transform: translateX(-50%); + opacity: 0.25; + } +} .deck > header.loading:after { pointer-events: none; content: ''; @@ -232,6 +245,9 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) { transparent ); animation: indeterminate-bar 1s ease-in-out infinite alternate; + &:dir(rtl) { + animation-name: indeterminate-bar-rtl; + } } @media (min-width: 40em) { .deck > header.loading:after { @@ -268,6 +284,9 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) { width: 95vw; max-width: calc(320px * 3.3); transform: translateX(calc(-50% + var(--main-width) / 2)); + &:dir(rtl) { + transform: translateX(calc(50% - var(--main-width) / 2)); + } } } @@ -346,6 +365,7 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) { margin: 0; padding: 0; border-bottom: var(--hairline-width) solid var(--divider-color); + --line-dir: var(--to-forward); } .timeline.flat > li { border-bottom: none; @@ -362,10 +382,14 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) { --avatar-size: 50px; --avatar-margin-start: 16px; --avatar-margin-end: 12px; + --line-curve: 45deg; + :dir(rtl) & { + --line-curve: -45deg; + } } .timeline.contextual > li { background-image: linear-gradient( - to right, + var(--line-dir), transparent, transparent var(--line-start), var(--comment-line-color) var(--line-start), @@ -394,7 +418,7 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) { .timeline.contextual > li.descendant:not(.thread) > :is(.status-link, .status-focus) { - padding-left: 40px; + padding-inline-start: 40px; } .timeline.contextual .replies[data-scroll-left]:not([data-scroll-left='0']) { background-color: var(--bg-color); @@ -408,7 +432,7 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) { } .timeline.contextual .replies[data-comments-level='4']:has(.replies) { overflow-x: auto; - mask-image: linear-gradient(to left, transparent, black 32px); + mask-image: linear-gradient(var(--to-backward), transparent, black 32px); } .timeline.contextual .replies[data-comments-level='4']:has(.replies) @@ -426,145 +450,61 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) { > :is(.status-link, .status-focus) + .replies .replies-summary { - margin-left: calc( + margin-inline-start: calc( var(--avatar-size) + var(--avatar-margin-start) + var(--avatar-margin-end) + (var(--line-margin-end) * (var(--comments-level) - 1)) ); } -/* .timeline.contextual - > li.descendant.thread - > .status-link - + .replies - .replies - > .replies-summary { - margin-left: calc( - var(--avatar-size) + var(--avatar-margin-start) + var(--avatar-margin-end) + - var(--line-margin-end) - ); -} -.timeline.contextual - > li.descendant.thread - > .status-link - + .replies - .replies - .replies - > .replies-summary { - margin-left: calc( - var(--avatar-size) + var(--avatar-margin-start) + var(--avatar-margin-end) + - (var(--line-margin-end) * 2) - ); -} */ .timeline.contextual > li.descendant.thread > :is(.status-link, .status-focus) + .replies :is(.status-link, .status-focus) { - padding-left: calc( + padding-inline-start: calc( var(--avatar-size) + var(--avatar-margin-start) + var(--avatar-margin-end) + (var(--line-margin-end) * (var(--comments-level) - 1)) ); } -/* .timeline.contextual - > li.descendant.thread - > .status-link - + .replies - .replies - .status-link { - padding-left: calc( - var(--avatar-size) + var(--avatar-margin-start) + var(--avatar-margin-end) + - var(--line-margin-end) - ); -} -.timeline.contextual - > li.descendant.thread - > .status-link - + .replies - .replies - .replies - .status-link { - padding-left: calc( - var(--avatar-size) + var(--avatar-margin-start) + var(--avatar-margin-end) + - (var(--line-margin-end) * 2) - ); -} */ .timeline.contextual > li.descendant:not(.thread) > :is(.status-link, .status-focus) + .replies .replies-summary { - margin-left: calc( + margin-inline-start: calc( var(--thread-start) + var(--line-margin-end) * var(--comments-level) ); } -/* .timeline.contextual - > li.descendant:not(.thread) - > .status-link - + .replies - .replies - > .replies-summary { - margin-left: calc( - var(--thread-start) + var(--line-margin-end) + var(--line-margin-end) - ); -} -.timeline.contextual - > li.descendant:not(.thread) - > .status-link - + .replies - .replies - .replies - > .replies-summary { - margin-left: calc( - var(--thread-start) + var(--line-margin-end) + (var(--line-margin-end) * 2) - ); -} */ .timeline.contextual > li.descendant:not(.thread) > :is(.status-link, .status-focus) + .replies :is(.status-link, .status-focus) { - padding-left: calc( + padding-inline-start: calc( var(--thread-start) + var(--line-margin-end) * var(--comments-level) ); } -/* .timeline.contextual - > li.descendant:not(.thread) - > .status-link - + .replies - .replies - .status-link { - padding-left: calc(var(--thread-start) + (var(--line-margin-end) * 2)); -} -.timeline.contextual - > li.descendant:not(.thread) - > .status-link - + .replies - .replies - .replies - .status-link { - padding-left: calc(var(--thread-start) + (var(--line-margin-end) * 3)); -} */ .timeline.contextual > li.descendant:not(.thread):before { content: ''; position: absolute; top: 10px; - left: var(--line-start); + inset-inline-start: var(--line-start); width: var(--line-diameter); height: var(--line-diameter); border-radius: var(--line-radius); border-style: solid; border-width: var(--line-width); border-color: transparent transparent var(--comment-line-color) transparent; - transform: rotate(45deg); + transform: rotate(var(--line-curve)); } .timeline.contextual > li .replies-link { color: var(--text-insignificant-color); - margin-left: 16px; + margin-inline-start: 16px; margin-top: -12px; padding-bottom: 12px; font-size: 90%; } .timeline.contextual > li.ancestor .replies-link { - margin-left: calc( + margin-inline-start: calc( var(--avatar-size) + var(--avatar-margin-start) + var(--avatar-margin-end) ); } @@ -572,7 +512,7 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) { > li.thread > :is(.status-link, .status-focus) .replies-link { - margin-left: calc( + margin-inline-start: calc( var(--avatar-size) + var(--avatar-margin-start) + var(--avatar-margin-end) ); } @@ -603,7 +543,7 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) { list-style: none; gap: 8px; align-items: center; - margin-right: calc(44px + 8px); + margin-inline-end: calc(44px + 8px); b { font-weight: 500; @@ -618,7 +558,9 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) { transition: transform 0.3s ease; &:not(:first-child) { - margin: 0 0 0 -4px; + transform: rotate(0deg); + margin: 0; + margin-inline-start: -4px; } } } @@ -637,7 +579,7 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) { .replies-parent-link { position: absolute; - right: 4px; + inset-inline-end: 4px; height: 100%; z-index: 2; font-size: 16px; @@ -648,8 +590,11 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) { align-items: center; padding: var(--summary-padding) calc(var(--summary-padding) * 2); transform: translateX(100%); - margin: calc(-1 * var(--summary-padding)) calc(-1 * var(--summary-padding)) - calc(-1 * var(--summary-padding)) 0; + &:dir(rtl) { + transform: translateX(-100%); + } + margin: calc(-1 * var(--summary-padding)) 0; + margin-inline-end: calc(-1 * var(--summary-padding)); border-radius: 8px; background-color: var(--link-bg-color); @@ -681,7 +626,7 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) { color: var(--text-color); background-color: var(--comment-line-color); background-image: linear-gradient( - to top right, + to top var(--forward), var(--comment-line-color), var(--bg-faded-color) ); @@ -697,7 +642,7 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) { } } .timeline.contextual > li .replies[open] > .replies-summary { - border-bottom-left-radius: 0; + border-end-start-radius: 0; .avatars { opacity: 0.5; @@ -727,7 +672,7 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) { ); --line-end: calc(var(--line-start) + var(--line-width)); background-image: linear-gradient( - to right, + var(--line-dir), transparent, transparent var(--line-start), var(--comment-line-color) var(--line-start), @@ -768,14 +713,14 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) { content: ''; position: absolute; top: 10px; - left: var(--line-start); + inset-inline-start: var(--line-start); width: var(--line-diameter); height: var(--line-diameter); border-radius: var(--line-radius); border-style: solid; border-width: var(--line-width); border-color: transparent transparent var(--comment-line-color) transparent; - transform: rotate(45deg); + transform: rotate(var(--line-curve)); } /* .timeline.contextual > li .replies .replies li:before { --line-start: calc( @@ -814,8 +759,11 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) { > ul > li:only-child { > .replies { > ul > li:only-child { - margin-left: calc(-1 * var(--line-margin-end)); - background-position: calc(16px) 0; + margin-inline-start: calc(-1 * var(--line-margin-end)); + background-position: 16px 0; + &:dir(rtl) { + background-position: -16px 0; + } background-size: 100% calc(20px + 8px); &:before { @@ -856,7 +804,7 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) { --line-width: 3px; --line-end: calc(var(--line-start) + var(--line-width)); background-image: linear-gradient( - to right, + var(--line-dir), transparent, transparent var(--line-start), var(--comment-line-color) var(--line-start), @@ -868,8 +816,8 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) { } .timeline:not(.flat) > li.timeline-item-container-start { margin-bottom: 0; - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; + border-end-start-radius: 0; + border-end-end-radius: 0; border-bottom: 0; background-position: 0 calc(16px + var(--avatar-size)); } @@ -882,8 +830,8 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) { } .timeline:not(.flat) > li.timeline-item-container-end { margin-top: 0; - border-top-left-radius: 0; - border-top-right-radius: 0; + border-start-start-radius: 0; + border-start-end-radius: 0; border-top: 0; background-size: 100% 16px; @@ -909,8 +857,10 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) { } .timeline .show-more { - padding-left: calc(var(--line-end) + var(--line-margin-end)) !important; - text-align: left; + padding-inline-start: calc( + var(--line-end) + var(--line-margin-end) + ) !important; + text-align: start; background-color: transparent !important; backdrop-filter: none !important; position: relative; @@ -918,7 +868,7 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) { padding-block: 16px !important; .avatars-bunch > .avatar:not(:first-child) { - margin-left: -4px; + margin-inline-start: -4px; } } .timeline .show-more:hover { @@ -930,14 +880,14 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) { content: ''; position: absolute; top: 10px; - left: var(--line-start); + inset-inline-start: var(--line-start); width: var(--line-diameter); height: var(--line-diameter); border-radius: var(--line-radius); border-style: solid; border-width: var(--line-width); border-color: transparent transparent var(--comment-line-color) transparent; - transform: rotate(45deg); + transform: rotate(var(--line-curve)); } .status-loading { @@ -988,7 +938,7 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) { .status-carousel { --carousel-faded-color: var(--bg-faded-color); background: linear-gradient( - to bottom right, + to bottom var(--forward), var(--carousel-faded-color), transparent ); @@ -1058,12 +1008,12 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) { display: none; } .status-carousel .status-carousel-beacon { - margin-right: calc(-1 * var(--carousel-gap)); + margin-inline-end: calc(-1 * var(--carousel-gap)); pointer-events: none; opacity: 0; ~ .status-carousel-beacon { - margin-left: calc(-1 * var(--carousel-gap)); + margin-inline-start: calc(-1 * var(--carousel-gap)); } } /* @@ -1107,7 +1057,7 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) { .status-carousel.boosts-carousel > ul > li:before { content: counter(index); position: absolute; - left: 0; + inset-inline-start: 0; font-size: 10px; color: var(--text-insignificant-color); padding: 6px; @@ -1147,11 +1097,11 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) { box-shadow: 0 1px var(--bg-color); &:has(.status-badge:not(:empty)) { - border-top-right-radius: 8px; + border-start-end-radius: 8px; } - .status-carousel.boosts-carousel & { - border-top-left-radius: 8px; + .status-carousel.boosts-carousel &:not(.timeline-item-carousel-group &) { + border-start-start-radius: 8px; } } .status-carousel-link::focus { @@ -1189,14 +1139,29 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) { transform: translate3d(0, 0, 0); } } +@keyframes slide-in-rtl { + 0% { + transform: translate3d(-100%, 0, 0); + } + 100% { + transform: translate3d(0, 0, 0); + } +} .deck-backdrop .deck { width: var(--main-width); max-width: 100vw; background-color: var(--bg-color); box-shadow: -1px 0 var(--bg-color); + &:dir(rtl) { + box-shadow: 1px 0 var(--bg-color); + } } .deck-backdrop .deck.slide-in:not(.deck-view-full) { animation: slide-in 0.5s var(--timing-function); + + &:dir(rtl) { + animation-name: slide-in-rtl; + } } .deck-backdrop .deck .status { max-width: var(--main-width); @@ -1240,7 +1205,7 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) { content: ''; display: inline-block; position: absolute; - right: 10px; + inset-inline-end: 10px; width: 4px; height: 4px; border-radius: 50%; @@ -1519,7 +1484,7 @@ body:has(.media-modal-container + .status-deck) .media-post-link { .media-modal-container + .status-deck { /* display: none; */ position: absolute; - right: 0; + inset-inline-end: 0; z-index: -1; pointer-events: none; user-select: none; @@ -1547,8 +1512,8 @@ body:has(.media-modal-container + .status-deck) .media-post-link { ) #modal-container > div { - left: 0; - right: 350px; + inset-inline-start: 0; + inset-inline-end: 350px; width: auto; } /* ✨ New */ @@ -1579,8 +1544,8 @@ body:has(.media-modal-container + .status-deck) .media-post-link { position: fixed; bottom: 16px; bottom: max(16px, env(safe-area-inset-bottom)); - right: 16px; - right: max(16px, env(safe-area-inset-right)); + inset-inline-end: 16px; + inset-inline-end: max(16px, env(safe-area-inset-right)); padding: 16px; background-color: var(--button-bg-blur-color); /* backdrop-filter: blur(16px); */ @@ -1629,7 +1594,7 @@ body:has(.media-modal-container + .status-deck) .media-post-link { display: block; position: absolute; top: 0; - right: 0; + inset-inline-end: 0; width: 14px; height: 14px; border-radius: 50%; @@ -1696,6 +1661,10 @@ body:has(.media-modal-container + .status-deck) .media-post-link { border-radius: 0; padding: 0; right: env(safe-area-inset-right); + &:dir(rtl) { + right: auto; + left: env(safe-area-inset-left); + } width: 44px; height: 44px; display: inline-flex; @@ -1737,6 +1706,11 @@ body:has(.media-modal-container + .status-deck) .media-post-link { } .sheet .sheet-close:not(.outer) + header { padding-right: max(44px, env(safe-area-inset-right)); + + &:dir(rtl) { + padding-right: max(16px, env(safe-area-inset-right)); + padding-left: max(44px, env(safe-area-inset-left)); + } } .sheet header :is(h1, h2, h3) { margin: 0; @@ -1774,6 +1748,10 @@ body:has(.media-modal-container + .status-deck) .media-post-link { width: 100%; height: 100%; } + + :dir(rtl) &.rtl-flip { + transform: scaleX(-1); + } } /* TAG */ @@ -1839,7 +1817,7 @@ body > .szh-menu-container { border: 1px solid var(--outline-color); border-radius: 8px; box-shadow: 0 3px 16px -3px var(--drop-shadow-color); - text-align: left; + text-align: start; /* animation: appear-smooth 0.15s ease-in-out; */ width: 16em; max-width: 90vw; @@ -1975,7 +1953,7 @@ body > .szh-menu-container { } .szh-menu .menu-horizontal > .szh-menu__item:not(:only-child):first-child, .szh-menu .menu-horizontal > *:not(:only-child):first-child .szh-menu__item { - padding-right: 4px !important; + padding-inline-end: 4px !important; } .szh-menu .menu-horizontal @@ -1984,12 +1962,12 @@ body > .szh-menu-container { .menu-horizontal > *:not(:only-child):not(:first-child):not(:last-child) .szh-menu__item { - padding-left: 8px !important; - padding-right: 4px !important; + padding-inline-start: 8px !important; + padding-inline-end: 4px !important; } .szh-menu .menu-horizontal > .szh-menu__item:not(:only-child):last-child, .szh-menu .menu-horizontal > *:not(:only-child):last-child .szh-menu__item { - padding-left: 8px !important; + padding-inline-start: 8px !important; } .szh-menu .szh-menu__item .menu-shortcut { opacity: 0.5; @@ -2053,7 +2031,11 @@ body > .szh-menu-container { text-align: center; opacity: 0.5; text-overflow: clip; - mask-image: linear-gradient(to left, transparent, black 16px); + mask-image: linear-gradient( + var(--to-backward), + transparent, + black 16px + ); } } } @@ -2069,10 +2051,10 @@ body > .szh-menu-container { } > [class^='szh-menu']:first-child { - border-top-left-radius: 8px; + border-start-start-radius: 8px; } > [class^='szh-menu']:last-child { - border-top-right-radius: 8px; + border-start-end-radius: 8px; } } } @@ -2153,6 +2135,9 @@ body > .szh-menu-container { background-image: var(--middle-circle), conic-gradient(var(--color) var(--fill), var(--outline-color) 0); transform: scale(0.7); + &:dir(rtl) { + transform: scale(-0.7, 0.7); + } transition: transform 0.2s ease-in-out; &::-webkit-meter-inner-element, @@ -2362,12 +2347,12 @@ ul.link-list li a { } } ul.link-list li:first-child a { - border-top-left-radius: var(--radius); - border-top-right-radius: var(--radius); + border-start-start-radius: var(--radius); + border-start-end-radius: var(--radius); } ul.link-list li:last-child a { - border-bottom-left-radius: var(--radius); - border-bottom-right-radius: var(--radius); + border-end-start-radius: var(--radius); + border-end-end-radius: var(--radius); } ul.link-list li a:is(:hover, :focus) { color: var(--text-color); @@ -2404,8 +2389,8 @@ ul.link-list li a .icon { } .nav-menu-button.with-avatar .icon { position: absolute; - bottom: 4px; - right: 8px; + inset-block-end: 4px; + inset-inline-end: 8px; background-color: var(--bg-color); border-radius: 2px; } @@ -2433,13 +2418,17 @@ ul.link-list li a .icon { } */ #columns > * { overscroll-behavior: auto; - scroll-snap-align: left; + scroll-snap-align: start; scroll-snap-stop: always; overscroll-behavior: auto; flex-basis: min(100vw, 360px); flex-shrink: 0; box-shadow: -1px 0 var(--bg-color), -2px 0 var(--drop-shadow-color), -3px 0 var(--bg-color); + &:dir(rtl) { + box-shadow: 1px 0 var(--bg-color), 2px 0 var(--drop-shadow-color), + 3px 0 var(--bg-color); + } } #columns:has(> :nth-child(3)) > *:nth-child(even), #columns:has(> :nth-child(3)) @@ -2572,7 +2561,7 @@ ul.link-list li a .icon { gap: 8px; overflow-x: auto; mask-image: linear-gradient( - to right, + var(--to-forward), transparent, black 16px, black calc(100% - 16px), @@ -2586,6 +2575,9 @@ ul.link-list li a .icon { width: 95vw; max-width: calc(320px * 3.3); transform: translateX(calc(-50% + var(--main-width) / 2)); + &:dir(rtl) { + transform: translateX(calc(50% - var(--main-width) / 2)); + } } } @@ -2704,7 +2696,8 @@ ul.link-list li a .icon { min-width: 16px; min-height: 16px; padding: 4px; - margin: -4px -8px -4px 0; + margin: -4px 0; + margin-inline-end: -8px; background-color: var(--bg-faded-color); border-radius: 999px; } @@ -2734,11 +2727,14 @@ ul.link-list li a .icon { .deck-container:has(~ .deck-backdrop .deck) { transition: transform 0.4s ease-out; transform: translate3d(-5vw, 0, 0); + &:dir(rtl) { + transform: translate3d(5vw, 0, 0); + } } .deck-backdrop .deck { /* width: 50%; min-width: var(--main-width); */ - border-left: 1px solid var(--divider-color); + border-inline-start: 1px solid var(--divider-color); } .timeline-deck { border: 0; @@ -2799,16 +2795,19 @@ ul.link-list li a .icon { > li:not(.timeline-item-container-end, .timeline-item-container-middle):has( .status-badge:not(:empty) ) { - border-top-right-radius: 8px; + border-start-end-radius: 8px; } .timeline:not(.flat) > li:has(.status-link.is-active) { transition: var(--back-transition); transform: translate3d(-2.5vw, 0, 0); + &:dir(rtl) { + transform: translate3d(2.5vw, 0, 0); + } } .timeline:not(.flat) > li.timeline-item-container:has(.status-link.is-active) { - border-top-left-radius: var(--item-radius); - border-bottom-left-radius: var(--item-radius); + border-start-start-radius: var(--item-radius); + border-end-start-radius: var(--item-radius); } .timeline:not(.flat) > li:not(:has(.status-carousel)):has(+ li .status-link.is-active), @@ -2817,19 +2816,22 @@ ul.link-list li a .icon { + li { transition: var(--back-transition); transform: translate3d(-1.25vw, 0, 0); + &:dir(rtl) { + transform: translate3d(1.25vw, 0, 0); + } } .timeline:not(.flat) > li.timeline-item-container:not(:has(.status-carousel)):has( + li .status-link.is-active ) { - border-top-left-radius: var(--item-radius); + border-start-start-radius: var(--item-radius); } .timeline:not(.flat) > li.timeline-item-container:not(:has(.status-carousel)):has( .status-link.is-active ) + li.timeline-item-container { - border-bottom-left-radius: var(--item-radius); + border-end-start-radius: var(--item-radius); } .box { padding: 32px; @@ -2841,5 +2843,8 @@ ul.link-list li a .icon { width: 95vw; max-width: calc(320px * 3.3); transform: translateX(calc(-50% + var(--main-width) / 2)); + &:dir(rtl) { + transform: translateX(calc(50% - var(--main-width) / 2)); + } } } diff --git a/src/app.jsx b/src/app.jsx index d2d044c2..fd3ef5ed 100644 --- a/src/app.jsx +++ b/src/app.jsx @@ -132,6 +132,8 @@ setTimeout(() => { setTimeout(() => { if (Array.isArray(ICONS[icon])) { ICONS[icon][0]?.(); + } else if (typeof ICONS[icon] === 'object') { + ICONS[icon].module?.(); } else { ICONS[icon]?.(); } diff --git a/src/components/ICONS.jsx b/src/components/ICONS.jsx index 0fa7880f..cac8190b 100644 --- a/src/components/ICONS.jsx +++ b/src/components/ICONS.jsx @@ -6,8 +6,14 @@ export const ICONS = { 'x-circle': () => import('@iconify-icons/mingcute/close-circle-line'), transfer: () => import('@iconify-icons/mingcute/transfer-4-line'), rocket: () => import('@iconify-icons/mingcute/rocket-line'), - 'arrow-left': () => import('@iconify-icons/mingcute/arrow-left-line'), - 'arrow-right': () => import('@iconify-icons/mingcute/arrow-right-line'), + 'arrow-left': { + module: () => import('@iconify-icons/mingcute/arrow-left-line'), + rtl: true, + }, + 'arrow-right': { + module: () => import('@iconify-icons/mingcute/arrow-right-line'), + rtl: true, + }, 'arrow-up': () => import('@iconify-icons/mingcute/arrow-up-line'), 'arrow-down': () => import('@iconify-icons/mingcute/arrow-down-line'), earth: () => import('@iconify-icons/mingcute/earth-line'), @@ -16,8 +22,14 @@ export const ICONS = { 'eye-close': () => import('@iconify-icons/mingcute/eye-close-line'), 'eye-open': () => import('@iconify-icons/mingcute/eye-2-line'), message: () => import('@iconify-icons/mingcute/mail-line'), - comment: () => import('@iconify-icons/mingcute/chat-3-line'), - comment2: () => import('@iconify-icons/mingcute/comment-2-line'), + comment: { + module: () => import('@iconify-icons/mingcute/chat-3-line'), + rtl: true, + }, + comment2: { + module: () => import('@iconify-icons/mingcute/comment-2-line'), + rtl: true, + }, home: () => import('@iconify-icons/mingcute/home-3-line'), notification: () => import('@iconify-icons/mingcute/notification-line'), follow: () => import('@iconify-icons/mingcute/user-follow-line'), @@ -31,23 +43,46 @@ export const ICONS = { gear: () => import('@iconify-icons/mingcute/settings-3-line'), more: () => import('@iconify-icons/mingcute/more-3-line'), more2: () => import('@iconify-icons/mingcute/more-1-fill'), - external: () => import('@iconify-icons/mingcute/external-link-line'), - popout: () => import('@iconify-icons/mingcute/external-link-line'), - popin: [() => import('@iconify-icons/mingcute/external-link-line'), '180deg'], + external: { + module: () => import('@iconify-icons/mingcute/external-link-line'), + rtl: true, + }, + popout: { + module: () => import('@iconify-icons/mingcute/external-link-line'), + rtl: true, + }, + popin: { + module: () => import('@iconify-icons/mingcute/external-link-line'), + rotate: '180deg', + rtl: true, + }, plus: () => import('@iconify-icons/mingcute/add-circle-line'), - 'chevron-left': () => import('@iconify-icons/mingcute/left-line'), - 'chevron-right': () => import('@iconify-icons/mingcute/right-line'), + 'chevron-left': { + module: () => import('@iconify-icons/mingcute/left-line'), + rtl: true, + }, + 'chevron-right': { + module: () => import('@iconify-icons/mingcute/right-line'), + rtl: true, + }, 'chevron-down': () => import('@iconify-icons/mingcute/down-line'), - reply: [ - () => import('@iconify-icons/mingcute/share-forward-line'), - '180deg', - 'horizontal', - ], + reply: { + module: () => import('@iconify-icons/mingcute/share-forward-line'), + rotate: '180deg', + flip: 'horizontal', + rtl: true, + }, thread: () => import('@iconify-icons/mingcute/route-line'), - group: () => import('@iconify-icons/mingcute/group-line'), + group: { + module: () => import('@iconify-icons/mingcute/group-line'), + rtl: true, + }, bot: () => import('@iconify-icons/mingcute/android-2-line'), menu: () => import('@iconify-icons/mingcute/rows-4-line'), - list: () => import('@iconify-icons/mingcute/list-check-line'), + list: { + module: () => import('@iconify-icons/mingcute/list-check-line'), + rtl: true, + }, search: () => import('@iconify-icons/mingcute/search-2-line'), hashtag: () => import('@iconify-icons/mingcute/hashtag-line'), info: () => import('@iconify-icons/mingcute/information-line'), @@ -62,12 +97,21 @@ export const ICONS = { share: () => import('@iconify-icons/mingcute/share-2-line'), sparkles: () => import('@iconify-icons/mingcute/sparkles-line'), sparkles2: () => import('@iconify-icons/mingcute/sparkles-2-line'), - exit: () => import('@iconify-icons/mingcute/exit-line'), + exit: { + module: () => import('@iconify-icons/mingcute/exit-line'), + rtl: true, + }, translate: () => import('@iconify-icons/mingcute/translate-line'), play: () => import('@iconify-icons/mingcute/play-fill'), trash: () => import('@iconify-icons/mingcute/delete-2-line'), - mute: () => import('@iconify-icons/mingcute/volume-mute-line'), - unmute: () => import('@iconify-icons/mingcute/volume-line'), + mute: { + module: () => import('@iconify-icons/mingcute/volume-mute-line'), + rtl: true, + }, + unmute: { + module: () => import('@iconify-icons/mingcute/volume-line'), + rtl: true, + }, block: () => import('@iconify-icons/mingcute/forbid-circle-line'), unblock: [ () => import('@iconify-icons/mingcute/forbid-circle-line'), @@ -81,30 +125,51 @@ export const ICONS = { filters: () => import('@iconify-icons/mingcute/filter-line'), chart: () => import('@iconify-icons/mingcute/chart-line-line'), react: () => import('@iconify-icons/mingcute/react-line'), - layout4: () => import('@iconify-icons/mingcute/layout-4-line'), + layout4: { + module: () => import('@iconify-icons/mingcute/layout-4-line'), + rtl: true, + }, layout5: () => import('@iconify-icons/mingcute/layout-5-line'), - announce: () => import('@iconify-icons/mingcute/announcement-line'), + announce: { + module: () => import('@iconify-icons/mingcute/announcement-line'), + rtl: true, + }, alert: () => import('@iconify-icons/mingcute/alert-line'), round: () => import('@iconify-icons/mingcute/round-fill'), 'arrow-up-circle': () => import('@iconify-icons/mingcute/arrow-up-circle-line'), 'arrow-down-circle': () => import('@iconify-icons/mingcute/arrow-down-circle-line'), - clipboard: () => import('@iconify-icons/mingcute/clipboard-line'), + clipboard: { + module: () => import('@iconify-icons/mingcute/clipboard-line'), + rtl: true, + }, 'account-edit': () => import('@iconify-icons/mingcute/user-edit-line'), 'account-warning': () => import('@iconify-icons/mingcute/user-warning-line'), keyboard: () => import('@iconify-icons/mingcute/keyboard-line'), cloud: () => import('@iconify-icons/mingcute/cloud-line'), - month: () => import('@iconify-icons/mingcute/calendar-month-line'), + month: { + module: () => import('@iconify-icons/mingcute/calendar-month-line'), + rtl: true, + }, media: () => import('@iconify-icons/mingcute/photo-album-line'), speak: () => import('@iconify-icons/mingcute/radar-line'), building: () => import('@iconify-icons/mingcute/building-5-line'), - history2: () => import('@iconify-icons/mingcute/history-2-line'), + history2: { + module: () => import('@iconify-icons/mingcute/history-2-line'), + rtl: true, + }, document: () => import('@iconify-icons/mingcute/document-line'), - 'arrows-right': () => import('@iconify-icons/mingcute/arrows-right-line'), + 'arrows-right': { + module: () => import('@iconify-icons/mingcute/arrows-right-line'), + rtl: true, + }, code: () => import('@iconify-icons/mingcute/code-line'), copy: () => import('@iconify-icons/mingcute/copy-2-line'), - quote: () => import('@iconify-icons/mingcute/quote-left-line'), + quote: { + module: () => import('@iconify-icons/mingcute/quote-left-line'), + rtl: true, + }, settings: () => import('@iconify-icons/mingcute/settings-6-line'), 'heart-break': () => import('@iconify-icons/mingcute/heart-crack-line'), 'user-x': () => import('@iconify-icons/mingcute/user-x-line'), diff --git a/src/components/account-block.css b/src/components/account-block.css index 14309fb9..890e4968 100644 --- a/src/components/account-block.css +++ b/src/components/account-block.css @@ -29,6 +29,8 @@ line-clamp: 1; text-overflow: ellipsis; overflow: hidden; + unicode-bidi: isolate; + direction: initial; } a { diff --git a/src/components/account-block.jsx b/src/components/account-block.jsx index 326f415c..47b4e711 100644 --- a/src/components/account-block.jsx +++ b/src/components/account-block.jsx @@ -120,7 +120,7 @@ function AccountBlock({ )} )}{' '} - + ${displayNameWithEmoji || username} -
@${encodeHTML(acct)} +
@${encodeHTML( + acct, + )}
`; diff --git a/src/components/drafts.css b/src/components/drafts.css index 2b093b8c..198dd46c 100644 --- a/src/components/drafts.css +++ b/src/components/drafts.css @@ -27,7 +27,7 @@ button.draft-item { background-color: var(--bg-color); color: var(--text-color); border: 1px solid var(--link-faded-color); - text-align: left; + text-align: start; padding: 0; } button.draft-item:is(:hover, :focus) { diff --git a/src/components/generic-accounts.css b/src/components/generic-accounts.css index 7ef1443b..d328ae40 100644 --- a/src/components/generic-accounts.css +++ b/src/components/generic-accounts.css @@ -62,13 +62,13 @@ border-top: var(--hairline-width) solid var(--divider-color); position: absolute; bottom: calc(-1 * var(--list-gap) / 2); - left: 40px; - right: 0; + inset-inline-start: 40px; + inset-inline-end: 0; } &:has(.reactions-block):before { /* avatar + reactions + gap */ - left: calc(40px + 16px + 8px); + inset-inline-start: calc(40px + 16px + 8px); } } diff --git a/src/components/icon.jsx b/src/components/icon.jsx index 16fb7c22..b7633437 100644 --- a/src/components/icon.jsx +++ b/src/components/icon.jsx @@ -53,9 +53,14 @@ function Icon({ return null; } - let rotate, flip; + let rotate, + flip, + rtl = false; if (Array.isArray(iconBlock)) { [iconBlock, rotate, flip] = iconBlock; + } else if (typeof iconBlock === 'object') { + ({ rotate, flip, rtl } = iconBlock); + iconBlock = iconBlock.module; } const [iconData, setIconData] = useState(ICONDATA[icon]); @@ -72,13 +77,14 @@ function Icon({ return ( {iconData && ( // { let handleScroll = () => { const { clientWidth, scrollLeft } = carouselRef.current; - const index = Math.round(scrollLeft / clientWidth); + const index = Math.round(Math.abs(scrollLeft) / clientWidth); setCurrentIndex(index); }; if (carouselRef.current) { @@ -178,7 +179,7 @@ function MediaModal({ ? { backgroundAttachment: 'local', backgroundImage: `linear-gradient( - to right, ${mediaAccentGradient})`, + to ${isRTL() ? 'left' : 'right'}, ${mediaAccentGradient})`, } : {} } @@ -257,7 +258,8 @@ function MediaModal({ e.preventDefault(); e.stopPropagation(); carouselRef.current.scrollTo({ - left: carouselRef.current.clientWidth * i, + left: + carouselRef.current.clientWidth * i * (isRTL() ? -1 : 1), behavior: 'smooth', }); carouselRef.current.focus(); @@ -368,7 +370,10 @@ function MediaModal({ e.stopPropagation(); carouselRef.current.focus(); carouselRef.current.scrollTo({ - left: carouselRef.current.clientWidth * (currentIndex - 1), + left: + carouselRef.current.clientWidth * + (currentIndex - 1) * + (isRTL() ? -1 : 1), behavior: 'smooth', }); }} @@ -384,7 +389,10 @@ function MediaModal({ e.stopPropagation(); carouselRef.current.focus(); carouselRef.current.scrollTo({ - left: carouselRef.current.clientWidth * (currentIndex + 1), + left: + carouselRef.current.clientWidth * + (currentIndex + 1) * + (isRTL() ? -1 : 1), behavior: 'smooth', }); }} diff --git a/src/components/media-post.css b/src/components/media-post.css index 2a52a11a..306e5a27 100644 --- a/src/components/media-post.css +++ b/src/components/media-post.css @@ -23,7 +23,7 @@ pointer-events: none; position: absolute; top: 0; - left: 0; + inset-inline-start: 0; z-index: 1; background-color: var(--bg-blur-color); margin: 8px; diff --git a/src/components/menu2.jsx b/src/components/menu2.jsx index 3d83a72d..fe79318c 100644 --- a/src/components/menu2.jsx +++ b/src/components/menu2.jsx @@ -1,21 +1,33 @@ import { Menu } from '@szhsin/react-menu'; import { useRef } from 'preact/hooks'; +import isRTL from '../utils/is-rtl'; import safeBoundingBoxPadding from '../utils/safe-bounding-box-padding'; import useWindowSize from '../utils/useWindowSize'; // It's like Menu but with sensible defaults, bug fixes and improvements. function Menu2(props) { - const { containerProps, instanceRef: _instanceRef } = props; + const { containerProps, instanceRef: _instanceRef, align } = props; const size = useWindowSize(); const instanceRef = _instanceRef?.current ? _instanceRef : useRef(); + // Values: start, end, center + // Note: don't mess with 'center' + const rtlAlign = isRTL() + ? align === 'end' + ? 'start' + : align === 'start' + ? 'end' + : align + : align; + return ( { diff --git a/src/components/modal.css b/src/components/modal.css index 62d6c8b6..80b9ccad 100644 --- a/src/components/modal.css +++ b/src/components/modal.css @@ -1,7 +1,7 @@ #modal-container > div { position: fixed; top: 0; - right: 0; + inset-inline-end: 0; height: 100%; width: 100%; z-index: 1000; @@ -26,21 +26,30 @@ user-select: none; overflow: hidden; transform: scale(0); - --right: max( + --end: max( var(--compose-button-dimension-margin), env(safe-area-inset-right) ); + :dir(rtl) & { + --end: max( + var(--compose-button-dimension-margin), + env(safe-area-inset-left) + ); + } --bottom: max( var(--compose-button-dimension-margin), env(safe-area-inset-bottom) ); - --origin-right: calc( - 100% - var(--compose-button-dimension-half) - var(--right) + --origin-end: calc( + 100% - var(--compose-button-dimension-half) - var(--end) ); + :dir(rtl) & { + --origin-end: calc(var(--compose-button-dimension-half) + var(--end)); + } --origin-bottom: calc( 100% - var(--compose-button-dimension-half) - var(--bottom) ); - transform-origin: var(--origin-right) var(--origin-bottom); + transform-origin: var(--origin-end) var(--origin-bottom); } .sheet { diff --git a/src/components/name-text.jsx b/src/components/name-text.jsx index ad329e93..8906ee79 100644 --- a/src/components/name-text.jsx +++ b/src/components/name-text.jsx @@ -88,13 +88,13 @@ function NameText({ )} {displayName && !short ? ( <> - + {!showAcct && !hideUsername && ( <> {' '} - @{username} + @{username} )} @@ -106,7 +106,7 @@ function NameText({ {showAcct && ( <>
- + {acct2 ? '' : '@'} {acct1} {!!acct2 && {acct2}} diff --git a/src/components/nav-menu.css b/src/components/nav-menu.css index 24bc1349..70efdd46 100644 --- a/src/components/nav-menu.css +++ b/src/components/nav-menu.css @@ -35,11 +35,15 @@ } .nav-menu section:last-child { background-image: linear-gradient( - to right, + var(--to-forward), var(--divider-color) 1px, transparent 1px ), - linear-gradient(to bottom left, var(--bg-blur-color), transparent), + linear-gradient( + to bottom var(--backward), + var(--bg-blur-color), + transparent + ), url(../assets/phanpy-bg.svg); background-repeat: no-repeat; /* background-size: auto, auto, 200%; */ @@ -49,8 +53,8 @@ position: sticky; top: 0; animation: phanpying 0.2s ease-in-out both; - border-top-right-radius: inherit; - border-bottom-right-radius: inherit; + border-start-end-radius: inherit; + border-end-end-radius: inherit; margin-bottom: 0; display: flex; flex-direction: column; diff --git a/src/components/poll.jsx b/src/components/poll.jsx index 1095a5dc..b8a893d2 100644 --- a/src/components/poll.jsx +++ b/src/components/poll.jsx @@ -187,9 +187,6 @@ export default function Poll({ type="button" class="plain small" disabled={uiState === 'loading'} - style={{ - marginLeft: -8, - }} onClick={(e) => { e.preventDefault(); setUIState('loading'); diff --git a/src/components/report-modal.css b/src/components/report-modal.css index 831c5d22..b99db159 100644 --- a/src/components/report-modal.css +++ b/src/components/report-modal.css @@ -92,7 +92,7 @@ pointer-events: none; user-select: none; position: absolute; - right: 32px; + inset-inline-end: 32px; margin-top: -48px; animation: rubber-stamp 0.3s ease-in both; position: absolute; @@ -148,7 +148,7 @@ } .report-rules { - margin-left: 1.75em; + margin-inline-start: 1.75em; } } diff --git a/src/components/search-form.jsx b/src/components/search-form.jsx index 2df8d0ae..94f9ae08 100644 --- a/src/components/search-form.jsx +++ b/src/components/search-form.jsx @@ -273,6 +273,7 @@ const SearchForm = forwardRef((props, ref) => { class={`search-popover-item ${i === 0 ? 'focus' : ''}`} // hidden={hidden} onClick={(e) => { + console.log('onClick', e); props?.onSubmit?.(e); }} > diff --git a/src/components/shortcuts-settings.css b/src/components/shortcuts-settings.css index 86cc6f70..1ef5bac9 100644 --- a/src/components/shortcuts-settings.css +++ b/src/components/shortcuts-settings.css @@ -18,8 +18,8 @@ counter-increment: index; display: inline-block; width: 1.2em; - text-align: right; - margin-right: 8px; + text-align: end; + margin-inline-end: 8px; color: var(--text-insignificant-color); font-size: 90%; flex-shrink: 0; @@ -55,12 +55,12 @@ justify-content: center; } #shortcuts-settings-container .shortcuts-view-mode label:first-child { - border-top-left-radius: 16px; - border-bottom-left-radius: 16px; + border-start-start-radius: 16px; + border-end-start-radius: 16px; } #shortcuts-settings-container .shortcuts-view-mode label:last-child { - border-top-right-radius: 16px; - border-bottom-right-radius: 16px; + border-start-end-radius: 16px; + border-end-end-radius: 16px; } #shortcuts-settings-container .shortcuts-view-mode label img { max-height: 64px; @@ -114,7 +114,7 @@ } #shortcut-settings-form label > span:first-child { flex-basis: 5em; - text-align: right; + text-align: end; } #shortcut-settings-form :is(input[type='text'], select) { flex-grow: 1; @@ -185,8 +185,8 @@ counter-increment: index; display: inline-block; width: 1.2em; - text-align: right; - margin-right: 8px; + text-align: end; + margin-inline-end: 8px; color: var(--text-insignificant-color); font-size: 90%; flex-shrink: 0; diff --git a/src/components/shortcuts-settings.jsx b/src/components/shortcuts-settings.jsx index de31c334..2f294b35 100644 --- a/src/components/shortcuts-settings.jsx +++ b/src/components/shortcuts-settings.jsx @@ -612,6 +612,7 @@ function ShortcutForm({ }} defaultValue={editMode ? shortcut.type : undefined} name="type" + dir="auto" > {TYPES.map((type) => ( @@ -632,6 +633,7 @@ function ShortcutForm({ required={!notRequired} disabled={disabled || uiState === 'loading'} defaultValue={editMode ? shortcut.id : undefined} + dir="auto" > {lists.map((list) => ( @@ -663,6 +665,7 @@ function ShortcutForm({ autocapitalize="off" spellCheck={false} pattern={pattern} + dir="auto" /> {currentType === 'hashtag' && followedHashtags.length > 0 && ( @@ -780,6 +783,7 @@ function ImportExport({ shortcuts, onClose }) { onInput={(e) => { setImportShortcutStr(e.target.value); }} + dir="auto" /> {states.settings.shortcutSettingsCloudImportExport && (