fix(gui): keep sidebar controls when collapsed
This commit is contained in:
@@ -27,13 +27,8 @@
|
||||
border-left: 1px solid var(--dsw-alias-border-l2);
|
||||
}
|
||||
|
||||
/* Collapsed columns keep children mounted; the border must not paint a 1px seam.
|
||||
Flags live on the frame — DetailsColumn renders inside the provider body and
|
||||
does not know its own width. */
|
||||
.frame[data-sidebar-collapsed] .sidebarCol {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
/* The details subtree stays mounted at zero width, so its border must not paint
|
||||
a 1px seam. The collapsed sidebar instead retains a bordered compact rail. */
|
||||
.frame[data-details-collapsed] .detailsCol {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
@@ -137,12 +137,14 @@ export function AppFrame(props: AppFrameProps) {
|
||||
ref={frameRef}
|
||||
className={css.frame}
|
||||
style={{ gridTemplateColumns: `${cols.sidebar}px minmax(0, 1fr) ${cols.details}px` }}
|
||||
data-sidebar-collapsed={cols.sidebar === 0 || undefined}
|
||||
data-sidebar-collapsed={!sidebar.open || undefined}
|
||||
data-details-collapsed={cols.details === 0 || undefined}
|
||||
>
|
||||
<div className={css.sidebarCol}>{props.sidebar}</div>
|
||||
{props.children}
|
||||
{cols.sidebar > 0 && <DragHandle left={cols.sidebar} onStart={onSidebarStart} onDrag={onSidebarDrag} />}
|
||||
{sidebar.open && cols.sidebar > 0
|
||||
? <DragHandle left={cols.sidebar} onStart={onSidebarStart} onDrag={onSidebarDrag} />
|
||||
: null}
|
||||
{cols.details > 0 && <DragHandle left={viewport - cols.details} onStart={onDetailsStart} onDrag={onDetailsDrag} />}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/**
|
||||
* Pure concession-chain column solver for the three-column AppFrame.
|
||||
* Chain order is fixed by contract: keep center >= CENTER_MIN by shrinking
|
||||
* details first, then sidebar, then auto-closing details (derived zero width —
|
||||
* persisted open/width preferences are never rewritten, so widening the window
|
||||
* restores them). Center absorbs any remaining deficit as the last resort.
|
||||
* details first, then sidebar, then auto-closing details. A closed sidebar
|
||||
* keeps its compact rail; persisted open/width preferences are never rewritten,
|
||||
* so widening the window restores them. Center absorbs any remaining deficit.
|
||||
*/
|
||||
|
||||
/** Panel viewing state consumed by the solver (mirrors LayoutService PanelState). */
|
||||
@@ -21,6 +21,8 @@ export const SIDEBAR_MIN = 240
|
||||
export const SIDEBAR_MAX = 420
|
||||
/** Sidebar width before any user drag. */
|
||||
export const SIDEBAR_DEFAULT = 300
|
||||
/** Closed-sidebar rail: one 28px control between 16px horizontal paddings. */
|
||||
export const SIDEBAR_COLLAPSED = 60
|
||||
/** Details drag clamp floor. */
|
||||
export const DETAILS_MIN = 300
|
||||
/** Details drag clamp ceiling. */
|
||||
@@ -47,13 +49,11 @@ export function clampWidth(px: number, min: number, max: number): number {
|
||||
* @param viewport - available frame width in px.
|
||||
* @param sidebar - sidebar preference (open flag + persisted width).
|
||||
* @param details - details preference (open flag + persisted width).
|
||||
* @returns resolved widths; details 0 means visually closed (never unmounted).
|
||||
* @returns resolved widths; details 0 means visually closed, while a closed sidebar keeps its compact rail.
|
||||
*/
|
||||
export function computeColumns(viewport: number, sidebar: PanelInput, details: PanelInput): Columns {
|
||||
const want = (p: PanelInput, min: number, max: number): number =>
|
||||
p.open ? clampWidth(p.width, min, max) : 0
|
||||
const s0 = want(sidebar, SIDEBAR_MIN, SIDEBAR_MAX)
|
||||
const d0 = want(details, DETAILS_MIN, DETAILS_MAX)
|
||||
const s0 = sidebar.open ? clampWidth(sidebar.width, SIDEBAR_MIN, SIDEBAR_MAX) : SIDEBAR_COLLAPSED
|
||||
const d0 = details.open ? clampWidth(details.width, DETAILS_MIN, DETAILS_MAX) : 0
|
||||
|
||||
// Step 1: everything fits at preferred widths.
|
||||
if (s0 + d0 + CENTER_MIN <= viewport) return { sidebar: s0, center: viewport - s0 - d0, details: d0 }
|
||||
@@ -63,14 +63,14 @@ export function computeColumns(viewport: number, sidebar: PanelInput, details: P
|
||||
if (s0 + d1 + CENTER_MIN <= viewport) return { sidebar: s0, center: CENTER_MIN, details: d1 }
|
||||
|
||||
// Step 3: shrink sidebar toward its minimum.
|
||||
const s1 = s0 === 0 ? 0 : Math.max(SIDEBAR_MIN, viewport - d1 - CENTER_MIN)
|
||||
const s1 = sidebar.open ? Math.max(SIDEBAR_MIN, viewport - d1 - CENTER_MIN) : SIDEBAR_COLLAPSED
|
||||
if (s1 + d1 + CENTER_MIN <= viewport) return { sidebar: s1, center: CENTER_MIN, details: d1 }
|
||||
|
||||
// Step 4: auto-close details (derived — preferences untouched). With the
|
||||
// details pressure gone the sidebar concession is re-solved from preference.
|
||||
if (d1 > 0) {
|
||||
if (s0 + CENTER_MIN <= viewport) return { sidebar: s0, center: viewport - s0, details: 0 }
|
||||
const s2 = s0 === 0 ? 0 : Math.max(SIDEBAR_MIN, viewport - CENTER_MIN)
|
||||
const s2 = sidebar.open ? Math.max(SIDEBAR_MIN, viewport - CENTER_MIN) : SIDEBAR_COLLAPSED
|
||||
return { sidebar: s2, center: Math.max(0, viewport - s2), details: 0 }
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,8 @@ import { LayoutService } from './service.ts'
|
||||
export { AppFrame, CenterColumn, DetailsColumn, type AppFrameProps } from './AppFrame.tsx'
|
||||
export {
|
||||
clampWidth, computeColumns,
|
||||
CENTER_MIN, DETAILS_DEFAULT, DETAILS_MAX, DETAILS_MIN, SIDEBAR_DEFAULT, SIDEBAR_MAX, SIDEBAR_MIN,
|
||||
CENTER_MIN, DETAILS_DEFAULT, DETAILS_MAX, DETAILS_MIN,
|
||||
SIDEBAR_COLLAPSED, SIDEBAR_DEFAULT, SIDEBAR_MAX, SIDEBAR_MIN,
|
||||
type Columns, type PanelInput,
|
||||
} from './columns.ts'
|
||||
export { LayoutService, type NavState, type PanelState, type ViewId } from './service.ts'
|
||||
|
||||
Reference in New Issue
Block a user