Phone drawer: starts closed from the first paint; the language sits at its foot
- On a phone the drawer started OPEN (navOpen = initialNavOpen) and mounted() closed it, so every load drew it on screen and slid it away (measured: up to ~1.6 s, both shells, app and web mode). data() now reads the same (max-width: 760px) query, so the first render is already the phone's closed state; mounted() still tracks breakpoint crossings. - In web mode the language at the drawer's foot sat right under the last row: margin-top:auto needs the nav's flex column (.has-rail-end), which only the desktop rail-end set. The phone drawer's foot sets it too. - check:shell-header asserts the flex column in web mode (14 planted defects).
This commit is contained in:
parent
f4066c36fc
commit
326eccb465
2 changed files with 14 additions and 3 deletions
|
|
@ -75,6 +75,8 @@ async function audit({ vtl = VTL, menu = MENU, langs = LANGS } = {}) {
|
|||
if (/class="vtl-pbar"/.test(html) !== wantBar) fail(`the phone bar ${wantBar ? 'is missing' : 'renders'} at ${tag}`)
|
||||
if (/probe-phone-end/.test(html) !== phoneNoHeader) fail(`#phone-end ${phoneNoHeader ? 'is not filled' : 'renders'} at ${tag}`)
|
||||
if (/vtl-drawer-end/.test(html) !== wantFab) fail(`the language at the drawer's foot ${wantFab ? 'is missing' : 'renders'} at ${tag}`)
|
||||
// The foot is margin-top:auto in a FLEX column (.has-rail-end); in a block nav it sits under the last row.
|
||||
if (wantFab && !/<nav class="[^"]*\bhas-rail-end\b[^"]*"/.test(html)) fail(`the drawer is not a flex column, so its language sits under the last row, not at the foot, at ${tag}`)
|
||||
if (/class="vtl-rail-end"/.test(html) !== wantRail || /probe-rail-end/.test(html) !== wantRail) fail(`#rail-end ${wantRail ? 'is missing' : 'renders'} at ${tag}`)
|
||||
if (wantBar && !/class="vtl-phone-menu"[^>]*aria-expanded=/.test(html)) fail(`the bar's ☰ has no aria-expanded at ${tag}`)
|
||||
if (/class="vtl-fab"/.test(html) !== wantFab) fail(`the corner ☰ ${wantFab ? 'is missing' : 'renders'} at ${tag}`)
|
||||
|
|
@ -143,6 +145,7 @@ const POISONS = [
|
|||
{ name: 'the bar also shows with a header', files: () => ({ vtl: plant(VTL, 'bar-header', (s) => s.replace("phoneBarShown() { return this.narrow && !this.header && this.mode === 'app' }", "phoneBarShown() { return this.narrow && this.mode === 'app' }")) }) },
|
||||
{ name: 'the rail-end also shows on a phone', files: () => ({ vtl: plant(VTL, 'rail-phone', (s) => s.replace('railEndShown() { return !this.narrow && !this.header }', 'railEndShown() { return !this.header }')) }) },
|
||||
{ name: 'web mode stacks the phone bar under the host header', files: () => ({ vtl: plant(VTL, 'webbar', (s) => s.replace("phoneBarShown() { return this.narrow && !this.header && this.mode === 'app' }", 'phoneBarShown() { return this.narrow && !this.header }')) }) },
|
||||
{ name: 'web mode leaves the drawer a block (the language under the last row)', files: () => ({ vtl: plant(VTL, 'webfoot', (s) => s.replace(" || (drawerEndShown && !!$slots['phone-end'])", '')) }) },
|
||||
{ name: 'web mode drops the language', files: () => ({ vtl: plant(VTL, 'weblang', (s) => s.replace("drawerEndShown() { return this.narrow && !this.header && this.mode === 'web' },", 'drawerEndShown() { return false },')) }) },
|
||||
{ name: 'the corner circle in app mode (under the bar)', files: () => ({ vtl: plant(VTL, 'fab', (s) => s.replace('<div v-if="phoneBarShown" class="vtl-pbar">', '<button v-if="phoneBarShown" class="vtl-fab">☰</button><div v-if="phoneBarShown" class="vtl-pbar">')) }) },
|
||||
{ name: 'the event is not cancelable', files: () => { const r = plant(REQ, 'nocancel', (s) => s.replace('bubbles: true, composed: true, cancelable: true', 'bubbles: true, composed: true')); return { menu: plant(MENU, 'm-nocancel', (s) => s + ' ', [['../utils/languageRequest.js', '../utils/' + r.split('/').pop()]]) } } },
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@
|
|||
an off-canvas drawer parked at translateX(-100%) and slid in by the `nav-open` class. Because it
|
||||
is never display:none, a mobile-closed drawer can no longer strand the rail after a resize back
|
||||
to desktop (the old cross-breakpoint bug). `aria-hidden` mirrors real visibility for a11y. -->
|
||||
<nav class="vtl-nav" :class="{ 'has-rail-end': railEndShown && !!$slots['rail-end'] }" :aria-label="navLabel" :aria-hidden="String(!railShown)"
|
||||
<nav class="vtl-nav" :class="{ 'has-rail-end': (railEndShown && !!$slots['rail-end']) || (drawerEndShown && !!$slots['phone-end']) }" :aria-label="navLabel" :aria-hidden="String(!railShown)"
|
||||
:role="$slots.rail ? null : 'tablist'" :aria-orientation="$slots.rail ? null : 'vertical'">
|
||||
<!-- THE HOST'S WAY HOME (optional). A host page that embeds this layout full-page (DX Life's
|
||||
/deep-research) has no header of its own there, so it hands in its homepage URL and label
|
||||
|
|
@ -678,12 +678,20 @@ export default defineComponent({
|
|||
// Nested-element hosting (lazy): activatedKeys marks which element-rows have EVER been the active
|
||||
// tab (so their pane is rendered + element mounted); hostState/hostError track each pane's load.
|
||||
// A row not in activatedKeys is never rendered — the lazy contract (no visit → no cost).
|
||||
// ★ A PHONE STARTS CLOSED FROM THE FIRST RENDER. navOpen used to start as initialNavOpen (true) and only
|
||||
// mounted() closed it, so on a phone the first paint drew the drawer OPEN (.nav-open) and its transform
|
||||
// transition then slid it away (measured: on screen until ~0.75-1.6 s after load, both shells, app and
|
||||
// web). Reading the same media query here makes the first render already the phone's closed state, so
|
||||
// nothing animates; mounted() keeps tracking crossings as before.
|
||||
const narrowNow = (() => {
|
||||
try { return typeof window !== 'undefined' && !!window.matchMedia && window.matchMedia('(max-width: 760px)').matches } catch (_) { return false }
|
||||
})()
|
||||
return {
|
||||
navOpen: this.initialNavOpen, railCollapsed: this.initialRailCollapsed, _tabEls: {}, folderOverride: {},
|
||||
navOpen: this.initialNavOpen && !narrowNow, railCollapsed: narrowNow ? false : this.initialRailCollapsed, _tabEls: {}, folderOverride: {},
|
||||
// `narrow` mirrors the (max-width:760px) media query as REACTIVE state, kept current by a resize
|
||||
// listener. It lets us reconcile the two boolean state machines (navOpen = mobile drawer,
|
||||
// railCollapsed = desktop slim rail) at the breakpoint and drive rail visibility correctly.
|
||||
narrow: false, _mql: null, _onMedia: null,
|
||||
narrow: narrowNow, _mql: null, _onMedia: null,
|
||||
activatedKeys: {}, hostState: {}, hostError: {}, _hostSlots: {}, _hostMounted: {},
|
||||
hostEls: {},
|
||||
// The resolved shellMode ('app' | 'web'), set at mount (it reads the host element and the platform).
|
||||
|
|
|
|||
Loading…
Reference in a new issue