mirror of
https://github.com/wekan/wekan.git
synced 2026-11-08 11:25:19 -05:00
(Arabic and its variants, Persian/Farsi, Hebrew, Uyghur, Uzbek-Arabic and Yiddish — the languages flagged `rtl: true` in `imports/i18n/languages.js`). Previously only some pages flipped. The root `<html>` element now gets a reactive `dir="rtl"`/`dir="ltr"` attribute that follows the chosen language, so the whole UI mirrors at once. To make this work on every page rather than a handful, all directional component CSS was converted to CSS logical properties (`margin-left`→`margin-inline-start`, `left:`→`inset-inline-start:`, `text-align: left`→`text-align: start`, `float: left`→`float: inline-start`, and the mirror-image right/end variants) across 48 stylesheets, which flip automatically with `dir`. Horizontal-centering rules using `left: 50%` + `transform: translate(-50%, …)` were intentionally kept physical, since those already center correctly in both directions. The calendar view also renders RTL. Thanks to xet7 and Claude. - Tests for the RTL UI. A fast, server-less `tests/rtl.test.js` (run with `node tests/rtl.test.js`) checks that exactly the expected languages are flagged `rtl: true`, that the direction helper maps each language to the right `dir` value, that the root `<html>` and the client keep `dir` in sync, and — as a regression guard — that component CSS keeps using logical properties (no physical `margin-left`/`float: left`/bare `left:` offsets sneak back in). A Playwright browser spec `tests/playwright/specs/18-rtl-layout.e2e.js` drives the app in both English (LTR) and Arabic (RTL) and asserts, on the boards list, board view, card details, my-cards / due-cards / global-search, the admin settings page and the login page, that the direction is correct, the translated text is visible, and leading-edge content (the boards menu, the first board list) sits on the correct side. Thanks to xet7 and Claude. - Fixed the "Meteor unit tests" CI job hanging until its 45-minute timeout. The workflow set `TEST_WATCH: '0'`, but meteortesting:mocha computes `testWatch: TEST_WATCH || …` and the string `'0'` is truthy in JavaScript, so it turned on watch mode and the process never exited. Removing the env var lets `meteor test --once` finish and exit with the correct status. Thanks to xet7 and Claude ! Fixes #6136, fixes #3376, fixes #884
124 lines
3.6 KiB
CSS
124 lines
3.6 KiB
CSS
.sk-spinner-cube {
|
|
margin: 100px auto;
|
|
width: 40px;
|
|
height: 40px;
|
|
position: relative;
|
|
}
|
|
.sk-cube1,
|
|
.sk-cube2 {
|
|
background-color: #333;
|
|
width: 15px;
|
|
height: 15px;
|
|
position: absolute;
|
|
top: 0;
|
|
inset-inline-start: 0;
|
|
-webkit-animation: sk-cubemove 1.8s infinite ease-in-out;
|
|
animation: sk-cubemove 1.8s infinite ease-in-out;
|
|
}
|
|
.sk-cube2 {
|
|
-webkit-animation-delay: -0.9s;
|
|
animation-delay: -0.9s;
|
|
}
|
|
@-webkit-keyframes sk-cubemove {
|
|
25% {
|
|
-webkit-transform: translateX(35px) rotate(-90deg) scale(0.5);
|
|
}
|
|
50% {
|
|
-webkit-transform: translateX(35px) translateY(35px) rotate(-180deg);
|
|
}
|
|
75% {
|
|
-webkit-transform: translateX(0px) translateY(35px) rotate(-270deg) scale(0.5);
|
|
}
|
|
100% {
|
|
-webkit-transform: rotate(-360deg);
|
|
}
|
|
}
|
|
@-moz-keyframes sk-cubemove {
|
|
25% {
|
|
transform: translateX(35px) rotate(-90deg) scale(0.5);
|
|
-webkit-transform: translateX(35px) rotate(-90deg) scale(0.5);
|
|
}
|
|
50% {
|
|
transform: translateX(35px) translateY(35px) rotate(-179deg);
|
|
-webkit-transform: translateX(35px) translateY(35px) rotate(-179deg);
|
|
}
|
|
50.1% {
|
|
transform: translateX(35px) translateY(35px) rotate(-180deg);
|
|
-webkit-transform: translateX(35px) translateY(35px) rotate(-180deg);
|
|
}
|
|
75% {
|
|
transform: translateX(0px) translateY(35px) rotate(-270deg) scale(0.5);
|
|
-webkit-transform: translateX(0px) translateY(35px) rotate(-270deg) scale(0.5);
|
|
}
|
|
100% {
|
|
transform: rotate(-360deg);
|
|
-webkit-transform: rotate(-360deg);
|
|
}
|
|
}
|
|
@-webkit-keyframes sk-cubemove {
|
|
25% {
|
|
transform: translateX(35px) rotate(-90deg) scale(0.5);
|
|
-webkit-transform: translateX(35px) rotate(-90deg) scale(0.5);
|
|
}
|
|
50% {
|
|
transform: translateX(35px) translateY(35px) rotate(-179deg);
|
|
-webkit-transform: translateX(35px) translateY(35px) rotate(-179deg);
|
|
}
|
|
50.1% {
|
|
transform: translateX(35px) translateY(35px) rotate(-180deg);
|
|
-webkit-transform: translateX(35px) translateY(35px) rotate(-180deg);
|
|
}
|
|
75% {
|
|
transform: translateX(0px) translateY(35px) rotate(-270deg) scale(0.5);
|
|
-webkit-transform: translateX(0px) translateY(35px) rotate(-270deg) scale(0.5);
|
|
}
|
|
100% {
|
|
transform: rotate(-360deg);
|
|
-webkit-transform: rotate(-360deg);
|
|
}
|
|
}
|
|
@-o-keyframes sk-cubemove {
|
|
25% {
|
|
transform: translateX(35px) rotate(-90deg) scale(0.5);
|
|
-webkit-transform: translateX(35px) rotate(-90deg) scale(0.5);
|
|
}
|
|
50% {
|
|
transform: translateX(35px) translateY(35px) rotate(-179deg);
|
|
-webkit-transform: translateX(35px) translateY(35px) rotate(-179deg);
|
|
}
|
|
50.1% {
|
|
transform: translateX(35px) translateY(35px) rotate(-180deg);
|
|
-webkit-transform: translateX(35px) translateY(35px) rotate(-180deg);
|
|
}
|
|
75% {
|
|
transform: translateX(0px) translateY(35px) rotate(-270deg) scale(0.5);
|
|
-webkit-transform: translateX(0px) translateY(35px) rotate(-270deg) scale(0.5);
|
|
}
|
|
100% {
|
|
transform: rotate(-360deg);
|
|
-webkit-transform: rotate(-360deg);
|
|
}
|
|
}
|
|
@keyframes sk-cubemove {
|
|
25% {
|
|
transform: translateX(35px) rotate(-90deg) scale(0.5);
|
|
-webkit-transform: translateX(35px) rotate(-90deg) scale(0.5);
|
|
}
|
|
50% {
|
|
transform: translateX(35px) translateY(35px) rotate(-179deg);
|
|
-webkit-transform: translateX(35px) translateY(35px) rotate(-179deg);
|
|
}
|
|
50.1% {
|
|
transform: translateX(35px) translateY(35px) rotate(-180deg);
|
|
-webkit-transform: translateX(35px) translateY(35px) rotate(-180deg);
|
|
}
|
|
75% {
|
|
transform: translateX(0px) translateY(35px) rotate(-270deg) scale(0.5);
|
|
-webkit-transform: translateX(0px) translateY(35px) rotate(-270deg) scale(0.5);
|
|
}
|
|
100% {
|
|
transform: rotate(-360deg);
|
|
-webkit-transform: rotate(-360deg);
|
|
}
|
|
}
|