Files
wekan/client/components/settings/attachments.css
T
Lauri Ojansivu 2536861b79 - Full right-to-left (RTL) UI for every page when an RTL language is selected
(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
2026-06-14 08:59:58 +03:00

243 lines
5.3 KiB
CSS

.move-attachment-buttons {
display: flex;
gap: 10px;
}
.attachments-content .move-compact-section,
.setting-content .content-body .main-body .form-group .compact-block {
margin: 16px 0;
padding: 12px;
border: 1px solid #d0d7de;
border-radius: 6px;
background: #fff8e1;
max-width: 650px;
box-sizing: border-box;
}
.attachments-content .move-compact-section p,
.setting-content .content-body .main-body .form-group .compact-block p {
max-width: 100%;
white-space: normal;
word-wrap: break-word;
}
.attachments-content .move-compact-section h4,
.setting-content .content-body .main-body h4 {
margin: 0 0 6px;
font-size: 1em;
font-weight: 600;
}
.attachments-content .compact-result-list,
.setting-content .content-body .main-body .compact-result-list {
margin: 6px 0 0;
padding-inline-start: 20px;
font-size: 0.85em;
font-family: monospace;
}
.attachments-content .move-progress-section {
margin: 12px 0 16px;
padding: 12px;
border: 1px solid #d0d7de;
border-radius: 6px;
background: #f6f8fa;
}
.attachments-content .move-progress-header {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 10px;
}
.attachments-content .move-progress-info {
display: flex;
flex-direction: column;
gap: 2px;
}
.attachments-content .move-progress-counter {
font-weight: 600;
font-size: 0.9em;
}
.attachments-content .move-progress-filename {
font-size: 0.85em;
color: #57606a;
word-break: break-all;
}
.attachments-content .move-progress-bar-track {
background: #dde1e4;
border-radius: 4px;
height: 8px;
overflow: hidden;
margin: 6px 0;
}
.attachments-content .move-progress-bar-fill {
background: #1976d2;
height: 100%;
border-radius: 4px;
transition: width 0.4s ease;
min-width: 2px;
}
.attachments-content .move-progress-bar-indeterminate {
height: 100%;
border-radius: 4px;
background: linear-gradient(90deg, transparent 0%, #1976d2 40%, #42a5f5 60%, transparent 100%);
background-size: 200% 100%;
animation: move-progress-slide 1.4s ease-in-out infinite;
}
@keyframes move-progress-slide {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
.attachments-content .move-progress-controls {
display: flex;
gap: 8px;
margin-top: 10px;
}
.attachments-content .move-storage-form {
display: flex;
align-items: flex-end;
gap: 12px;
flex-wrap: wrap;
margin-bottom: 16px;
}
.attachments-content .move-storage-selects {
display: flex;
align-items: flex-end;
gap: 12px;
flex-wrap: wrap;
}
.attachments-content .move-storage-form .form-group {
margin: 0;
}
.attachments-content .move-storage-form .form-group label {
display: block;
margin-bottom: 4px;
font-weight: 600;
}
.attachments-content .move-attachment-buttons button,
.attachments-content .board-attachments td button {
background-color: #1976d2;
border: 1px solid #1565c0;
color: #fff;
}
.attachments-content .move-attachment-buttons button:hover,
.attachments-content .board-attachments td button:hover {
background-color: #1565c0;
color: #fff;
}
.attachments-content hr {
height: 0px;
border: 1px solid #000;
}
.setting-content .content-body .main-body .storage-rw-row {
display: flex;
align-items: center;
gap: 24px;
margin-bottom: 12px;
padding: 6px 0;
}
.setting-content .content-body .main-body .storage-rw-item {
display: flex;
align-items: center;
gap: 6px;
}
.setting-content .content-body .main-body .storage-rw-row input[type="checkbox"] {
position: static;
inset-inline-start: auto;
visibility: visible;
display: inline-block;
width: 16px;
height: 16px;
margin: 0;
flex-shrink: 0;
cursor: pointer;
}
.setting-content .content-body .main-body .storage-rw-row input[type="checkbox"]:disabled {
cursor: default;
opacity: 0.6;
}
.setting-content .content-body .main-body .storage-rw-row label {
margin: 0;
font-weight: 600;
cursor: pointer;
}
.setting-content .content-body .main-body .storage-rw-row input[type="checkbox"]:disabled + label {
cursor: default;
opacity: 0.6;
}
.setting-content .content-body .main-body .gridfs-stats-table {
border-collapse: collapse;
margin-top: 12px;
width: auto !important;
min-width: 0 !important;
}
.setting-content .content-body .main-body .gridfs-stats-table th,
.setting-content .content-body .main-body .gridfs-stats-table td {
border: 1px solid #d0d7de;
padding: 6px 8px;
text-align: start;
white-space: nowrap;
min-width: 0 !important;
}
/* Cloud storage (S3 / Azure / GCS): example value shown below each input. */
.attachments-content .cloud-input-example {
display: block;
color: #888;
font-size: 0.82em;
font-family: monospace;
word-break: break-all;
line-height: 1.35;
}
/* Cloud storage: where-to-find-this-value path shown below the example. */
.attachments-content .cloud-input-path {
display: block;
color: #6a737d;
font-size: 0.82em;
line-height: 1.4;
margin-top: 2px;
}
/* Cloud storage: translated label shown under the (English) label. */
.attachments-content .cloud-input-label-tr {
display: block;
color: #57606a;
font-size: 0.9em;
margin-bottom: 4px;
}
/* Cloud storage: description text (English literal and translated). */
.attachments-content .cloud-input-desc {
display: block;
color: #6a737d;
font-size: 0.82em;
line-height: 1.4;
margin-top: 2px;
}