Fit the layout between phone and desktop widths
From about 920px down to the phone layout (700px), the sidebar kept its full width and the topbar's credit balance, Learning Lab link and language switch ran past the right edge. Between 701 and 1024px the sidebar now starts as the icon rail (the toggle opens it for the visit), and up to 1100px the topbar and the page head's buttons wrap onto another line, without the engine label. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useLayoutEffect, useRef, useState } from 'react'
|
||||
import { useEffect, useLayoutEffect, useRef, useState, useSyncExternalStore } from 'react'
|
||||
import { useClerk, useOrganization, useOrganizationList, useUser } from '@clerk/react'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { NavLink, Outlet, useLocation } from 'react-router-dom'
|
||||
@@ -130,6 +130,16 @@ function OrgSwitcher() {
|
||||
}
|
||||
|
||||
// Remembered per browser; storage can throw in private windows, so it falls back to expanded.
|
||||
// Between the phone layout and a full desktop, the sidebar would leave too little room for the
|
||||
// page and the topbar, so it starts as the icon rail there; the toggle still opens it for this visit.
|
||||
const NARROW = '(min-width: 701px) and (max-width: 1024px)'
|
||||
function useNarrow() {
|
||||
return useSyncExternalStore(
|
||||
notify => { const m = matchMedia(NARROW); m.addEventListener('change', notify); return () => m.removeEventListener('change', notify) },
|
||||
() => matchMedia(NARROW).matches,
|
||||
)
|
||||
}
|
||||
|
||||
const COLLAPSED_KEY = 'sidebar-collapsed'
|
||||
function readCollapsed() {
|
||||
try { return localStorage.getItem(COLLAPSED_KEY) === '1' } catch { return false }
|
||||
@@ -149,11 +159,17 @@ export function AppLayout() {
|
||||
// In braces: Chrome's scrollTo now returns a Promise, which React would keep as the effect's
|
||||
// cleanup and call on the next route change ("is not a function"), unmounting the whole app.
|
||||
useLayoutEffect(() => { window.scrollTo(0, 0) }, [pathname])
|
||||
const [collapsed, setCollapsed] = useState(readCollapsed)
|
||||
const toggleCollapsed = () => setCollapsed(c => {
|
||||
try { localStorage.setItem(COLLAPSED_KEY, c ? '0' : '1') } catch { /* keep it for this visit only */ }
|
||||
return !c
|
||||
})
|
||||
const [saved, setSaved] = useState(readCollapsed)
|
||||
const narrow = useNarrow()
|
||||
const [openNarrow, setOpenNarrow] = useState(false)
|
||||
const collapsed = narrow ? !openNarrow : saved
|
||||
const toggleCollapsed = () => {
|
||||
if (narrow) return setOpenNarrow(o => !o)
|
||||
setSaved(c => {
|
||||
try { localStorage.setItem(COLLAPSED_KEY, c ? '0' : '1') } catch { /* keep it for this visit only */ }
|
||||
return !c
|
||||
})
|
||||
}
|
||||
const name = user?.fullName || user?.primaryEmailAddress?.emailAddress || '-'
|
||||
|
||||
return (
|
||||
|
||||
@@ -428,3 +428,13 @@ dialog::backdrop{background:rgba(16,24,40,.45);backdrop-filter:blur(4px)}
|
||||
}
|
||||
@media(max-width:700px){.sidebar-toggle,.sidebar-help-icon{display:none}}
|
||||
@media(prefers-reduced-motion:reduce){.sidebar,.workspace-main{transition:none}}
|
||||
|
||||
/* Tablet widths: the topbar wraps instead of pushing its pills past the edge, and the page head's
|
||||
buttons go under the title when they no longer fit beside it */
|
||||
@media(min-width:701px) and (max-width:1100px){
|
||||
.topbar{height:auto;min-height:64px;padding-top:10px;padding-bottom:10px;flex-wrap:wrap;row-gap:8px}
|
||||
.topbar-right{flex-wrap:wrap;justify-content:flex-end;gap:8px 10px}
|
||||
.demo-indicator{display:none}
|
||||
.page-head{flex-wrap:wrap}
|
||||
.page-head .head-actions{flex-wrap:wrap}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user