Switching from Arabic to Chinese left the page right-to-left
Every element that sets a reading direction did it one way: if the locale was RTL it wrote dir="rtl", and there was no else. So a reader who moved from an RTL locale to an LTR one kept the right-to-left layout for the rest of the session -- Chinese laid out backwards, which reads as a broken page rather than a stale setting. The reason the clear was missing is sound and is kept: a host page that declares its own dir owns that decision, and blindly removing the attribute would take that with it. So each element now remembers what it found before it first wrote, restores that value verbatim when the locale stops being RTL, and removes the attribute only when there was none to begin with. It clears its own, never the host's. FOUR elements, not the three the report named: big-picture.vue, research-map-plus-grid.vue and people-groups-grid.vue all carry it, and so does dashboard-plus-grid.vue -- the Big Picture composite, same shape, found by checking the neighbours of the named lines rather than only the lines themselves. The comment above each one said "Only ever SET, never cleared". Left alone it would now describe the opposite of the code, so it says what the code does. Verified two-sided against all four shipped files, driving the real extracted function: it clears what it set, preserves a host's own dir, and still sets rtl. Restoring the one-way body to one file fails exactly the first two and leaves the third passing, with the other three files unaffected.
This commit is contained in:
parent
b8c7e9a391
commit
77415cf361
4 changed files with 64 additions and 8 deletions
|
|
@ -105,17 +105,32 @@ const heading = computed(() => config.value.title
|
|||
//
|
||||
// Set on the HOST rather than on .bp-root, deliberately: `dir` takes effect through the inherited
|
||||
// CSS `direction`, and inheritance crosses shadow boundaries — so one attribute covers this
|
||||
// element's own chrome AND both nested elements beneath it. Only ever SET, never cleared: a host
|
||||
// element's own chrome AND both nested elements beneath it. Set and CLEARED, but only ever ours: a host
|
||||
// page that already declares its own dir owns that decision.
|
||||
const textDir = computed(() => {
|
||||
const l = hostLang.value || getRuntimeLocale() || detectLocale()
|
||||
return RTL_LOCALES.has(l) ? 'rtl' : 'ltr'
|
||||
})
|
||||
const _self = getCurrentInstance()
|
||||
// ★ WE CLEAR ONLY THE DIRECTION WE SET. The line below used to be a one-way
|
||||
// `if (rtl) setAttribute('dir','rtl')` with no else, so a reader who switched from an RTL locale to
|
||||
// an LTR one (ar -> zh) kept a right-to-left layout for the rest of the session: Chinese laid out
|
||||
// backwards, which reads as a broken page rather than a stale setting.
|
||||
// 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
|
||||
function reflectDir() {
|
||||
try {
|
||||
const host = _self?.proxy?.$el?.getRootNode?.()?.host
|
||||
if (host && textDir.value === 'rtl') host.setAttribute('dir', 'rtl')
|
||||
if (!host || !host.setAttribute) return
|
||||
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
|
||||
}
|
||||
} catch (_) { /* no host (SSR/tests) — nothing to reflect */ }
|
||||
}
|
||||
onMounted(reflectDir)
|
||||
|
|
|
|||
|
|
@ -300,7 +300,7 @@ watchEffect(() => { if (hostLang.value) setRuntimeLocale(hostLang.value) })
|
|||
// Set on the custom-element HOST rather than on the layout inside it, deliberately: `dir` takes
|
||||
// effect through the inherited CSS `direction`, and inheritance crosses shadow boundaries — so one
|
||||
// attribute on the host covers this composite's own chrome AND every nested element beneath it,
|
||||
// which is the half the audit found missing. Only ever SET, never cleared: a host page that already
|
||||
// which is the half the audit found missing. Set and CLEARED, but only ever ours: a host page that already
|
||||
// declares its own dir owns that decision.
|
||||
const textDir = computed(() => {
|
||||
const l = hostLang.value || getRuntimeLocale() || detectLocale()
|
||||
|
|
@ -316,10 +316,21 @@ const textDir = computed(() => {
|
|||
// it is". Measured in this tree before, under Vue 3.5.42, when research-map.vue's emitFilterState
|
||||
// died the same way; the fix is its _selfInstance, and this is the same fix.
|
||||
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
|
||||
function reflectDir() {
|
||||
try {
|
||||
const host = _selfInstance?.proxy?.$el?.getRootNode?.()?.host
|
||||
if (host && textDir.value === 'rtl') host.setAttribute('dir', 'rtl')
|
||||
if (!host || !host.setAttribute) return
|
||||
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
|
||||
}
|
||||
} catch (_) { /* no host (SSR/tests) — nothing to reflect */ }
|
||||
}
|
||||
onMounted(reflectDir)
|
||||
|
|
|
|||
|
|
@ -278,16 +278,31 @@ const languageCode = computed(() =>
|
|||
// page it owns and useless for an element dropped into someone else's, which is this grid's whole
|
||||
// job. Set on the HOST, like both composites: `dir` takes effect through the inherited CSS
|
||||
// `direction`, and inheritance crosses shadow boundaries, so one attribute turns the element and
|
||||
// everything it renders. Only ever SET, never cleared — a host page that declares its own dir owns
|
||||
// everything it renders. Set and CLEARED, but only ever ours — a host page that declares its own dir owns
|
||||
// that decision.
|
||||
const textDir = computed(() => {
|
||||
const l = languageCode.value || getRuntimeLocale() || detectLocale()
|
||||
return RTL_LOCALES.has(String(l).split('-')[0].toLowerCase()) ? 'rtl' : 'ltr'
|
||||
})
|
||||
// ★ WE CLEAR ONLY THE DIRECTION WE SET. The line below used to be a one-way
|
||||
// `if (rtl) setAttribute('dir','rtl')` with no else, so a reader who switched from an RTL locale to
|
||||
// an LTR one (ar -> zh) kept a right-to-left layout for the rest of the session: Chinese laid out
|
||||
// backwards, which reads as a broken page rather than a stale setting.
|
||||
// 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
|
||||
function reflectDir() {
|
||||
try {
|
||||
const host = hostEl()
|
||||
if (host && host.setAttribute && textDir.value === 'rtl') host.setAttribute('dir', 'rtl')
|
||||
if (!host || !host.setAttribute) return
|
||||
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
|
||||
}
|
||||
} catch (_) { /* no host (SSR/tests) — nothing to reflect */ }
|
||||
}
|
||||
watch(textDir, reflectDir)
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ const hostList = computed(() => (typeof hostConfig.value?.list === 'string' ? ho
|
|||
// Set on the custom-element HOST rather than on the layout inside it, deliberately: `dir` takes
|
||||
// effect through the inherited CSS `direction`, and inheritance crosses shadow boundaries — so one
|
||||
// attribute on the host covers this composite's own chrome AND every nested element beneath it,
|
||||
// which is the half the audit found missing. Only ever SET, never cleared: a host page that already
|
||||
// which is the half the audit found missing. Set and CLEARED, but only ever ours: a host page that already
|
||||
// declares its own dir owns that decision.
|
||||
const textDir = computed(() => {
|
||||
const l = hostLang.value || getRuntimeLocale() || detectLocale()
|
||||
|
|
@ -168,10 +168,25 @@ const textDir = computed(() => {
|
|||
// it is". Measured in this tree before, under Vue 3.5.42, when research-map.vue's emitFilterState
|
||||
// died the same way; the fix is its _selfInstance, and this is the same fix.
|
||||
const _selfInstance = getCurrentInstance()
|
||||
// ★ WE CLEAR ONLY THE DIRECTION WE SET. The line below used to be a one-way
|
||||
// `if (rtl) setAttribute('dir','rtl')` with no else, so a reader who switched from an RTL locale to
|
||||
// an LTR one (ar -> zh) kept a right-to-left layout for the rest of the session: Chinese laid out
|
||||
// backwards, which reads as a broken page rather than a stale setting.
|
||||
// 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
|
||||
function reflectDir() {
|
||||
try {
|
||||
const host = _selfInstance?.proxy?.$el?.getRootNode?.()?.host
|
||||
if (host && textDir.value === 'rtl') host.setAttribute('dir', 'rtl')
|
||||
if (!host || !host.setAttribute) return
|
||||
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
|
||||
}
|
||||
} catch (_) { /* no host (SSR/tests) — nothing to reflect */ }
|
||||
}
|
||||
onMounted(reflectDir)
|
||||
|
|
|
|||
Loading…
Reference in a new issue