From 1fd562c28ec333fd7c6222c0a0935c4aa064354e Mon Sep 17 00:00:00 2001 From: Doxa Maps Date: Tue, 29 Sep 2026 20:15:16 +0000 Subject: [PATCH] Read the page's own direction at mount, not at our first write The clear-only-ours fix recorded the host's dir lazily, inside the rtl branch, the first time it wrote. That is correct only while nothing else has written a direction first. If anything set dir="rtl" on the host before that moment, the element recorded rtl as "the page's own declaration" and then faithfully restored it on every switch away from Arabic -- so the direction never cleared, which is the defect this whole change exists to remove. Measured on the Big Picture leaf: ar left rtl, and zh put rtl straight back, while its three siblings with the same code passed. The difference was not the code; it was that something writes rtl on that host before the element does. Reading the attribute on the FIRST call instead, before any write of ours, is the only moment it is certainly the page's and not ours. A page that genuinely declares dir still owns that decision and still gets it back. Five cases driven against the real function in all four files: no dir at boot, a page declaring ltr, a page declaring rtl, an element mounting while already Arabic, and -- the case that reproduces the failure -- a FOREIGN writer setting rtl after mount and before ours. Restoring the lazy capture to one file fails that fifth case alone and leaves the other four passing. --- plugin-designs/big-picture/big-picture.vue | 18 +++++++++++++----- .../dashboard-plus-grid.vue | 18 +++++++++++++----- .../people-groups-grid/people-groups-grid.vue | 18 +++++++++++++----- .../research-map-plus-grid.vue | 18 +++++++++++++----- 4 files changed, 52 insertions(+), 20 deletions(-) diff --git a/plugin-designs/big-picture/big-picture.vue b/plugin-designs/big-picture/big-picture.vue index f02100847..001487bd5 100644 --- a/plugin-designs/big-picture/big-picture.vue +++ b/plugin-designs/big-picture/big-picture.vue @@ -119,17 +119,25 @@ const _self = getCurrentInstance() // The original reason for never clearing is still honoured, and is why this remembers rather than // just removing: a host page that declares its OWN dir owns that decision, so the value found before // we first wrote is restored verbatim, and removed only when there was none. -let _dirPrev = null +// ★ CAPTURED AT MOUNT, NOT AT FIRST WRITE. The capture used to sit inside the rtl branch, so it +// recorded whatever `dir` happened to be on the host the first time WE wrote — and if anything else +// had already set rtl by then, that value was recorded as "the page's own" and faithfully restored +// forever after. MEASURED on the Big Picture leaf: ar left rtl, and zh put rtl straight back. +// Reading it on the FIRST call instead, before any write of ours, is the only moment the attribute +// is certainly the page's and not ours. +let _hostDir = null +let _weSetDir = false function reflectDir() { try { const host = _self?.proxy?.$el?.getRootNode?.()?.host if (!host || !host.setAttribute) return + if (_hostDir === null) _hostDir = host.getAttribute('dir') || '' if (textDir.value === 'rtl') { - if (_dirPrev === null) _dirPrev = host.getAttribute('dir') || '' host.setAttribute('dir', 'rtl') - } else if (_dirPrev !== null) { - if (_dirPrev) host.setAttribute('dir', _dirPrev); else host.removeAttribute('dir') - _dirPrev = null + _weSetDir = true + } else if (_weSetDir) { + if (_hostDir) host.setAttribute('dir', _hostDir); else host.removeAttribute('dir') + _weSetDir = false } } catch (_) { /* no host (SSR/tests) — nothing to reflect */ } } diff --git a/plugin-designs/dashboard-plus-grid/dashboard-plus-grid.vue b/plugin-designs/dashboard-plus-grid/dashboard-plus-grid.vue index 8e24e81a1..5ac1a0d18 100644 --- a/plugin-designs/dashboard-plus-grid/dashboard-plus-grid.vue +++ b/plugin-designs/dashboard-plus-grid/dashboard-plus-grid.vue @@ -319,17 +319,25 @@ const _selfInstance = getCurrentInstance() // ★ WE CLEAR ONLY THE DIRECTION WE SET. This was a one-way `if (rtl) setAttribute` with no else, // so ar -> zh kept a right-to-left layout for the rest of the session. The host page's own dir is // still respected: the value found before we first wrote is restored verbatim. -let _dirPrev = null +// ★ CAPTURED AT MOUNT, NOT AT FIRST WRITE. The capture used to sit inside the rtl branch, so it +// recorded whatever `dir` happened to be on the host the first time WE wrote — and if anything else +// had already set rtl by then, that value was recorded as "the page's own" and faithfully restored +// forever after. MEASURED on the Big Picture leaf: ar left rtl, and zh put rtl straight back. +// Reading it on the FIRST call instead, before any write of ours, is the only moment the attribute +// is certainly the page's and not ours. +let _hostDir = null +let _weSetDir = false function reflectDir() { try { const host = _selfInstance?.proxy?.$el?.getRootNode?.()?.host if (!host || !host.setAttribute) return + if (_hostDir === null) _hostDir = host.getAttribute('dir') || '' if (textDir.value === 'rtl') { - if (_dirPrev === null) _dirPrev = host.getAttribute('dir') || '' host.setAttribute('dir', 'rtl') - } else if (_dirPrev !== null) { - if (_dirPrev) host.setAttribute('dir', _dirPrev); else host.removeAttribute('dir') - _dirPrev = null + _weSetDir = true + } else if (_weSetDir) { + if (_hostDir) host.setAttribute('dir', _hostDir); else host.removeAttribute('dir') + _weSetDir = false } } catch (_) { /* no host (SSR/tests) — nothing to reflect */ } } diff --git a/plugin-designs/people-groups-grid/people-groups-grid.vue b/plugin-designs/people-groups-grid/people-groups-grid.vue index 96e623668..23d9d498d 100644 --- a/plugin-designs/people-groups-grid/people-groups-grid.vue +++ b/plugin-designs/people-groups-grid/people-groups-grid.vue @@ -291,17 +291,25 @@ const textDir = computed(() => { // The original reason for never clearing is still honoured, and is why this remembers rather than // just removing: a host page that declares its OWN dir owns that decision, so the value found before // we first wrote is restored verbatim, and removed only when there was none. -let _dirPrev = null +// ★ CAPTURED AT MOUNT, NOT AT FIRST WRITE. The capture used to sit inside the rtl branch, so it +// recorded whatever `dir` happened to be on the host the first time WE wrote — and if anything else +// had already set rtl by then, that value was recorded as "the page's own" and faithfully restored +// forever after. MEASURED on the Big Picture leaf: ar left rtl, and zh put rtl straight back. +// Reading it on the FIRST call instead, before any write of ours, is the only moment the attribute +// is certainly the page's and not ours. +let _hostDir = null +let _weSetDir = false function reflectDir() { try { const host = hostEl() if (!host || !host.setAttribute) return + if (_hostDir === null) _hostDir = host.getAttribute('dir') || '' if (textDir.value === 'rtl') { - if (_dirPrev === null) _dirPrev = host.getAttribute('dir') || '' host.setAttribute('dir', 'rtl') - } else if (_dirPrev !== null) { - if (_dirPrev) host.setAttribute('dir', _dirPrev); else host.removeAttribute('dir') - _dirPrev = null + _weSetDir = true + } else if (_weSetDir) { + if (_hostDir) host.setAttribute('dir', _hostDir); else host.removeAttribute('dir') + _weSetDir = false } } catch (_) { /* no host (SSR/tests) — nothing to reflect */ } } diff --git a/plugin-designs/research-map-plus-grid/research-map-plus-grid.vue b/plugin-designs/research-map-plus-grid/research-map-plus-grid.vue index e1d3a45cb..0b8956fce 100644 --- a/plugin-designs/research-map-plus-grid/research-map-plus-grid.vue +++ b/plugin-designs/research-map-plus-grid/research-map-plus-grid.vue @@ -175,17 +175,25 @@ const _selfInstance = getCurrentInstance() // The original reason for never clearing is still honoured, and is why this remembers rather than // just removing: a host page that declares its OWN dir owns that decision, so the value found before // we first wrote is restored verbatim, and removed only when there was none. -let _dirPrev = null +// ★ CAPTURED AT MOUNT, NOT AT FIRST WRITE. The capture used to sit inside the rtl branch, so it +// recorded whatever `dir` happened to be on the host the first time WE wrote — and if anything else +// had already set rtl by then, that value was recorded as "the page's own" and faithfully restored +// forever after. MEASURED on the Big Picture leaf: ar left rtl, and zh put rtl straight back. +// Reading it on the FIRST call instead, before any write of ours, is the only moment the attribute +// is certainly the page's and not ours. +let _hostDir = null +let _weSetDir = false function reflectDir() { try { const host = _selfInstance?.proxy?.$el?.getRootNode?.()?.host if (!host || !host.setAttribute) return + if (_hostDir === null) _hostDir = host.getAttribute('dir') || '' if (textDir.value === 'rtl') { - if (_dirPrev === null) _dirPrev = host.getAttribute('dir') || '' host.setAttribute('dir', 'rtl') - } else if (_dirPrev !== null) { - if (_dirPrev) host.setAttribute('dir', _dirPrev); else host.removeAttribute('dir') - _dirPrev = null + _weSetDir = true + } else if (_weSetDir) { + if (_hostDir) host.setAttribute('dir', _hostDir); else host.removeAttribute('dir') + _weSetDir = false } } catch (_) { /* no host (SSR/tests) — nothing to reflect */ } }