Plugin studio: right-to-left in Arabic
Some checks failed
build-gate / build (push) Has been cancelled

rega's :dir(rtl) mirror for VerticalTabsLayout (5e7f73d0) compiled into
the studio, but nothing set the direction, so the studio stayed
left-to-right in Arabic. The studio now sets dir from the language
list's own dir flag, as doxa-maps-spa does.

The Explorer tree's own CSS assumed left-to-right: indentation, indent
guides, the active-row bar, the part count and the slim-rail activity
icons used left/right properties. They now use logical properties
(padding-inline, inset-inline-start, border-inline-start,
margin-inline-start), so they mirror, and a closed twisty points toward
the start of the line.

Checked at 1280x900: in Arabic the rail sits on the right, the active
bar on its right edge, the guides on the right, and doxa-simple-map
still renders its map; in English the geometry is unchanged (rail 0..256,
stage from 268).
This commit is contained in:
doxa-maps-88655c70 2026-09-25 05:13:33 +00:00
parent 0d846c3a1e
commit 7eaa4012c1
2 changed files with 20 additions and 13 deletions

View file

@ -52,6 +52,7 @@
panes with their direct-element mount + heavy-row teardown machinery. -->
<VerticalTabsLayout
:title="heading || tr('title')"
:dir="uiDir"
:nav-label="tr('navLabel')"
:labels="{ toggleList: tr('toggleList'), openViews: tr('openViews'), expandList: tr('expandList'), collapseList: tr('collapseList'), collapse: tr('collapse') }"
:header="true"
@ -547,7 +548,11 @@ export default defineComponent({
const nb = new Set(this.rows.map(r => r.bundle)).size
return this.tr('countLabel', { bundles: nb, elements: this.rows.length })
},
activeLang() { return getLanguage(this.locale) || { code: this.locale, flag: '🌐', nativeName: this.locale } }
activeLang() { return getLanguage(this.locale) || { code: this.locale, flag: '🌐', nativeName: this.locale } },
// Right-to-left for a right-to-left language (languages.js marks Arabic dir:'rtl'), which is what
// switches on VerticalTabsLayout's :dir(rtl) mirror — rega's 5e7f73d0, which doxa-maps-spa turns on
// the same way. Without this the mirror compiled into the studio but never applied.
uiDir() { return (this.activeLang && this.activeLang.dir) === 'rtl' ? 'rtl' : 'ltr' }
},
mounted() {
this.loadManifest()

View file

@ -106,11 +106,11 @@ const ST_CSS = '' +
'.st-tool:hover { background:#f3f3f1; color:#3b463d; } .st-tool:focus-visible { outline:2px solid #92b195; outline-offset:-2px; }' +
'.st-tool svg { width:16px; height:16px; }' +
'.st-tree { display:flex; flex-direction:column; padding:0 0 8px; outline:0; }' +
'.st-row { position:relative; display:flex; align-items:center; gap:6px; height:28px; box-sizing:border-box; padding:0 10px 0 calc(8px + var(--st-depth) * 14px); font-size:13px; color:#3b463d; cursor:pointer; white-space:nowrap; user-select:none; outline:0; }' +
'.st-row { position:relative; display:flex; align-items:center; gap:6px; height:28px; box-sizing:border-box; padding-block:0; padding-inline:calc(8px + var(--st-depth) * 14px) 10px; font-size:13px; color:#3b463d; cursor:pointer; white-space:nowrap; user-select:none; outline:0; }' +
'.st-row:hover { background:#f3f3f1; }' +
'.st-row:focus-visible { box-shadow:inset 0 0 0 1px #92b195; }' +
'.st-row.active { background:rgba(146,177,149,.24); }' +
'.st-row.active::after { content:""; position:absolute; left:0; top:4px; bottom:4px; width:3px; border-radius:0 3px 3px 0; background:#92b195; }' +
'.st-row.active::after { content:""; position:absolute; inset-inline-start:0; top:4px; bottom:4px; width:3px; border-start-end-radius:3px; border-end-end-radius:3px; background:#92b195; }' +
'.st-row.alias.active { background:rgba(146,177,149,.12); }' +
'.st-sec { position:sticky; top:46px; z-index:2; height:30px; margin-top:6px; background:#ffffff; font-size:11px; font-weight:600; letter-spacing:.06em; text-transform:uppercase; color:#6d766e; border-top:1px solid #eceee9; }' +
'.st-sec:first-child { margin-top:0; border-top:0; }' +
@ -120,6 +120,7 @@ const ST_CSS = '' +
'.st-twisty, .st-twisty-sp { flex:0 0 16px; width:16px; height:16px; display:inline-flex; align-items:center; justify-content:center; color:#6d766e; }' +
'.st-twisty svg { width:14px; height:14px; transition:transform .12s ease; }' +
'.st-row[aria-expanded="true"] > .st-twisty svg { transform:rotate(90deg); }' +
'.st:dir(rtl) .st-twisty svg { transform:scaleX(-1); } .st:dir(rtl) .st-row[aria-expanded="true"] > .st-twisty svg { transform:rotate(90deg); }' +
'.st-ic { flex:0 0 16px; display:inline-flex; color:#6d766e; } .st-ic svg { width:16px; height:16px; }' +
'.st-row.active .st-ic { color:#5f8a63; }' +
'.st-label { flex:0 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; }' +
@ -127,12 +128,12 @@ const ST_CSS = '' +
// ellipsised to make room for the tag (VS Code truncates the description first, too).
'.st-desc { flex:1 1 0; min-width:0; overflow:hidden; text-overflow:ellipsis; font:11px/1 ui-monospace,Menlo,monospace; color:#8a928b; }' +
'.st-part { flex:0 0 auto; font:600 9px/1 system-ui,sans-serif; text-transform:uppercase; letter-spacing:.3px; padding:2px 4px; border:1px solid #92b195; border-radius:4px; color:#5f8a63; }' +
'.st-count { flex:0 0 auto; margin-left:auto; min-width:18px; text-align:right; font-size:11px; font-weight:500; letter-spacing:0; color:#6d766e; }' +
'.st-guide { position:absolute; top:0; bottom:0; left:calc(15px + var(--st-g) * 14px); border-left:1px solid #dfe2db; pointer-events:none; }' +
'.st-row:hover > .st-guide { border-left-color:#c4c8c0; }' +
'.st-count { flex:0 0 auto; margin-inline-start:auto; min-width:18px; text-align:right; font-size:11px; font-weight:500; letter-spacing:0; color:#6d766e; }' +
'.st-guide { position:absolute; top:0; bottom:0; inset-inline-start:calc(15px + var(--st-g) * 14px); border-inline-start:1px solid #dfe2db; pointer-events:none; }' +
'.st-row:hover > .st-guide { border-inline-start-color:#c4c8c0; }' +
'.st-empty { margin:12px; font-size:13px; color:#6d766e; }' +
'.st-activity { display:flex; flex-direction:column; gap:4px; }' +
'.st-act { position:relative; display:flex; align-items:center; justify-content:center; width:48px; height:44px; margin-left:172px; padding:0; border:0; border-radius:10px; background:none; color:#6d766e; cursor:pointer; }' +
'.st-act { position:relative; display:flex; align-items:center; justify-content:center; width:48px; height:44px; margin-inline-start:172px; padding:0; border:0; border-radius:10px; background:none; color:#6d766e; cursor:pointer; }' +
'.st-act:hover, .st-act.active { background:#f3f3f1; color:#3b463d; }' +
'.st-act:focus-visible { outline:2px solid #92b195; outline-offset:-2px; }' +
'.st-act-ic { display:inline-flex; } .st-act-ic svg { width:20px; height:20px; }' +
@ -352,11 +353,11 @@ export default defineComponent({
.st-tool:hover { background:#f3f3f1; color:#3b463d; } .st-tool:focus-visible { outline:2px solid #92b195; outline-offset:-2px; }
.st-tool svg { width:16px; height:16px; }
.st-tree { display:flex; flex-direction:column; padding:0 0 8px; outline:0; }
.st-row { position:relative; display:flex; align-items:center; gap:6px; height:28px; box-sizing:border-box; padding:0 10px 0 calc(8px + var(--st-depth) * 14px); font-size:13px; color:#3b463d; cursor:pointer; white-space:nowrap; user-select:none; outline:0; }
.st-row { position:relative; display:flex; align-items:center; gap:6px; height:28px; box-sizing:border-box; padding-block:0; padding-inline:calc(8px + var(--st-depth) * 14px) 10px; font-size:13px; color:#3b463d; cursor:pointer; white-space:nowrap; user-select:none; outline:0; }
.st-row:hover { background:#f3f3f1; }
.st-row:focus-visible { box-shadow:inset 0 0 0 1px #92b195; }
.st-row.active { background:rgba(146,177,149,.24); }
.st-row.active::after { content:""; position:absolute; left:0; top:4px; bottom:4px; width:3px; border-radius:0 3px 3px 0; background:#92b195; }
.st-row.active::after { content:""; position:absolute; inset-inline-start:0; top:4px; bottom:4px; width:3px; border-start-end-radius:3px; border-end-end-radius:3px; background:#92b195; }
.st-row.alias.active { background:rgba(146,177,149,.12); }
.st-sec { position:sticky; top:46px; z-index:2; height:30px; margin-top:6px; background:#ffffff; font-size:11px; font-weight:600; letter-spacing:.06em; text-transform:uppercase; color:#6d766e; border-top:1px solid #eceee9; }
.st-sec:first-child { margin-top:0; border-top:0; }
@ -366,17 +367,18 @@ export default defineComponent({
.st-twisty, .st-twisty-sp { flex:0 0 16px; width:16px; height:16px; display:inline-flex; align-items:center; justify-content:center; color:#6d766e; }
.st-twisty svg { width:14px; height:14px; transition:transform .12s ease; }
.st-row[aria-expanded="true"] > .st-twisty svg { transform:rotate(90deg); }
.st:dir(rtl) .st-twisty svg { transform:scaleX(-1); } .st:dir(rtl) .st-row[aria-expanded="true"] > .st-twisty svg { transform:rotate(90deg); }
.st-ic { flex:0 0 16px; display:inline-flex; color:#6d766e; } .st-ic svg { width:16px; height:16px; }
.st-row.active .st-ic { color:#5f8a63; }
.st-label { flex:0 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; }
.st-desc { flex:1 1 0; min-width:0; overflow:hidden; text-overflow:ellipsis; font:11px/1 ui-monospace,Menlo,monospace; color:#8a928b; }
.st-part { flex:0 0 auto; font:600 9px/1 system-ui,sans-serif; text-transform:uppercase; letter-spacing:.3px; padding:2px 4px; border:1px solid #92b195; border-radius:4px; color:#5f8a63; }
.st-count { flex:0 0 auto; margin-left:auto; min-width:18px; text-align:right; font-size:11px; font-weight:500; letter-spacing:0; color:#6d766e; }
.st-guide { position:absolute; top:0; bottom:0; left:calc(15px + var(--st-g) * 14px); border-left:1px solid #dfe2db; pointer-events:none; }
.st-row:hover > .st-guide { border-left-color:#c4c8c0; }
.st-count { flex:0 0 auto; margin-inline-start:auto; min-width:18px; text-align:right; font-size:11px; font-weight:500; letter-spacing:0; color:#6d766e; }
.st-guide { position:absolute; top:0; bottom:0; inset-inline-start:calc(15px + var(--st-g) * 14px); border-inline-start:1px solid #dfe2db; pointer-events:none; }
.st-row:hover > .st-guide { border-inline-start-color:#c4c8c0; }
.st-empty { margin:12px; font-size:13px; color:#6d766e; }
.st-activity { display:flex; flex-direction:column; gap:4px; }
.st-act { position:relative; display:flex; align-items:center; justify-content:center; width:48px; height:44px; margin-left:172px; padding:0; border:0; border-radius:10px; background:none; color:#6d766e; cursor:pointer; }
.st-act { position:relative; display:flex; align-items:center; justify-content:center; width:48px; height:44px; margin-inline-start:172px; padding:0; border:0; border-radius:10px; background:none; color:#6d766e; cursor:pointer; }
.st-act:hover, .st-act.active { background:#f3f3f1; color:#3b463d; }
.st-act:focus-visible { outline:2px solid #92b195; outline-offset:-2px; }
.st-act-ic { display:inline-flex; } .st-act-ic svg { width:20px; height:20px; }