diff --git a/scripts/package-boundaries.json b/scripts/package-boundaries.json index 6c312deef..c8783bde9 100644 --- a/scripts/package-boundaries.json +++ b/scripts/package-boundaries.json @@ -60,6 +60,7 @@ "src/ui/scenePresentation.js", "src/i18n/index.js", "src/i18n/locale.js", + "src/i18n/locales/ar.json", "src/i18n/locales/de.json", "src/i18n/locales/es.json", "src/i18n/locales/fr.json", diff --git a/src/annotations/drawTool.test.mjs b/src/annotations/drawTool.test.mjs index d955d2d6f..b4e25abf8 100644 --- a/src/annotations/drawTool.test.mjs +++ b/src/annotations/drawTool.test.mjs @@ -112,10 +112,11 @@ test('the DISPLAY rail styles live in their current owner, not the shim', () => // these rules lived in the root style.css. That file is an import shim now, // and putting rules back into it would silently undo the split. const shim = read('style.css'); - assert.ok( - shim.split('\n').length < 40, - 'style.css is an import shim, not a stylesheet', - ); + const rules = shim + .replace(/\/\*[\s\S]*?\*\//g, '') + .split('\n') + .filter((line) => line.trim() && !/^@import '[^']+';$/.test(line.trim())); + assert.deepEqual(rules, [], 'style.css is an import shim, not a stylesheet'); assert.doesNotMatch( shim, /draw-hint|pp-text-input/, diff --git a/src/ui/rtlLayout.test.mjs b/src/ui/rtlLayout.test.mjs index ea9056a6f..a28ddcaa5 100644 --- a/src/ui/rtlLayout.test.mjs +++ b/src/ui/rtlLayout.test.mjs @@ -111,3 +111,38 @@ test('the Space Missions counter reads as a number pair in right-to-left', () => assert.match(counters[1], /\[data-mission-index\]/); assert.match(counters[2], /direction: ltr;/); }); + +test('select chevrons and legend ranges follow the field and the ramp in right-to-left', () => { + const field = sheet('form-field.css'); + assert.match( + ruleBody(field, 'select.gev-field'), + /padding-inline-end: 22px;/, + ); + assert.match( + ruleBody(field, 'select.gev-field--compact'), + /padding-inline-end: 18px;/, + ); + // Selects that set their own padding keep the chevron's room at the end. + for (const [name, selector, inline] of [ + ['radio.css', '#radio-filter', '8px 24px'], + ['radio.css', '.sdr-gain-row select', '7px 22px'], + ['voice-card.css', '#gev-voice-timeout-select', '0.45rem 22px'], + ]) { + const body = ruleBody(sheet(name), selector); + assert.match(body, new RegExp(`padding-inline: ${inline};`)); + assert.doesNotMatch(body, /(?:^|\s)padding(?:-right)?:/); + } + const rtl = sheet('rtl.css'); + assert.match( + ruleBody(rtl, "html[dir='rtl'] select.gev-field"), + /background-position: left 8px center;/, + ); + assert.match( + ruleBody(rtl, "html[dir='rtl'] select.gev-field--compact"), + /background-position: left 6px center;/, + ); + assert.match( + ruleBody(rtl, "html[dir='rtl'] .rail-card-scale"), + /direction: ltr;/, + ); +}); diff --git a/src/ui/styles/form-field.css b/src/ui/styles/form-field.css index 81462ba21..11e7d5a2d 100644 --- a/src/ui/styles/form-field.css +++ b/src/ui/styles/form-field.css @@ -19,7 +19,7 @@ select.gev-field { -webkit-appearance: none; appearance: none; - padding-right: 22px; + padding-inline-end: 22px; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='rgba(255,255,255,0.62)' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 8px center; @@ -45,7 +45,7 @@ select.gev-field { } select.gev-field--compact { - padding-right: 18px; + padding-inline-end: 18px; background-position: right 6px center; } diff --git a/src/ui/styles/radio.css b/src/ui/styles/radio.css index 4d6df2230..e491c7409 100644 --- a/src/ui/styles/radio.css +++ b/src/ui/styles/radio.css @@ -49,7 +49,8 @@ #radio-filter { /* Radio owns its compact density and arrow space; shared fields paint the palette and disabled state, including this intentional theme exception. */ - padding: 7px 24px 7px 8px; + padding-block: 7px; + padding-inline: 8px 24px; border-radius: 5px; font: 9px/1.2 var(--font-mono); --gev-field-border-color: rgba(65, 220, 231, 0.28); @@ -1128,7 +1129,8 @@ html:not([lang='en']) .data-row-list-text { .sdr-gain-row select { flex: 1; min-width: 0; - padding: 5px 22px 5px 7px; + padding-block: 5px; + padding-inline: 7px 22px; } .sdr-adsb-stats { display: grid; diff --git a/src/ui/styles/rtl.css b/src/ui/styles/rtl.css index e5e3c629d..10b5c13bd 100644 --- a/src/ui/styles/rtl.css +++ b/src/ui/styles/rtl.css @@ -205,3 +205,18 @@ html[dir='rtl'] html[dir='rtl'] #radio-tuner-value { unicode-bidi: plaintext; } + +/* The select chevron sits at the end of the field, where the text stops + (form-field.css pads that side through padding-inline-end). */ +html[dir='rtl'] select.gev-field { + background-position: left 8px center; +} +html[dir='rtl'] select.gev-field--compact { + background-position: left 6px center; +} + +/* A legend's range is printed under its colour ramp, which always runs low + to high from left to right; the numbers keep that order. */ +html[dir='rtl'] .rail-card-scale { + direction: ltr; +} diff --git a/src/ui/styles/voice-card.css b/src/ui/styles/voice-card.css index dfda6d9c7..637a4e88a 100644 --- a/src/ui/styles/voice-card.css +++ b/src/ui/styles/voice-card.css @@ -553,7 +553,8 @@ body.recording-mode .gev-pointer-reticle { #gev-voice-timeout-select { flex: 1 1 auto; min-width: 0; - padding: 0 22px 0 0.45rem; + padding-block: 0; + padding-inline: 0.45rem 22px; } #gev-voice-timeout-input { flex: 0 0 4.5rem;