refactor: simplify sidebar logics
This commit is contained in:
@@ -91,33 +91,21 @@ export function AppFrame({
|
||||
renderSlot,
|
||||
}: AppFrameProps) {
|
||||
const panels = useStore(s => s)
|
||||
const sessionsPhase = useSessions(s => s.phase)
|
||||
const detailsSession = useSessions((s) => {
|
||||
const current = s.current
|
||||
if (current === undefined) return undefined
|
||||
const session = s.byId[current]
|
||||
return session !== undefined && !session.blank ? current : undefined
|
||||
return current !== undefined && s.byId[current]?.blank === false ? current : undefined
|
||||
})
|
||||
const frameRef = useRef<HTMLDivElement | null>(null)
|
||||
const [viewport, setViewport] = useState(() => window.innerWidth)
|
||||
|
||||
// The first ready active Session is baseline restoration, so its persisted
|
||||
// panel may remain open. New Session has no inspectable selection, and any
|
||||
// later details owner change closes the root-scoped column before paint.
|
||||
const detailsBaselineReady = useRef(false)
|
||||
const previousDetailsSession = useRef(detailsSession)
|
||||
const lastSession = useRef(detailsSession)
|
||||
useLayoutEffect(() => {
|
||||
if (sessionsPhase !== 'ready') return
|
||||
if (!detailsBaselineReady.current) {
|
||||
detailsBaselineReady.current = true
|
||||
previousDetailsSession.current = detailsSession
|
||||
if (detailsSession === undefined) actions.closeDetails()
|
||||
return
|
||||
if (detailsSession === undefined) return
|
||||
if (lastSession.current !== undefined && lastSession.current !== detailsSession) {
|
||||
actions.closeDetails()
|
||||
}
|
||||
if (previousDetailsSession.current === detailsSession) return
|
||||
previousDetailsSession.current = detailsSession
|
||||
actions.closeDetails()
|
||||
}, [actions, detailsSession, sessionsPhase])
|
||||
lastSession.current = detailsSession
|
||||
}, [actions, detailsSession])
|
||||
|
||||
// Track the frame's own box (not the window): rAF-throttled ResizeObserver.
|
||||
useEffect(() => {
|
||||
@@ -139,12 +127,12 @@ export function AppFrame({
|
||||
}
|
||||
}, [])
|
||||
|
||||
const cols = computeColumns(viewport, panels.sidebar, panels.details)
|
||||
const cols = computeColumns(viewport, panels.sidebar, detailsSession === undefined ? 0 : panels.details)
|
||||
const colsRef = useRef(cols)
|
||||
colsRef.current = cols
|
||||
|
||||
// The drag base is the rendered width captured at drag start (grabbing a
|
||||
// concession-clamped panel must not jump back to the persisted preference);
|
||||
// concession-clamped panel must not jump back to the stored preference);
|
||||
// it stays frozen for the whole gesture so dx deltas do not compound.
|
||||
const sidebarBase = useRef(0)
|
||||
const detailsBase = useRef(0)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Pure concession-chain column solver for the three-column AppFrame.
|
||||
* Chain order is fixed by contract: keep center >= CENTER_MIN by shrinking
|
||||
* details, then auto-closing it (derived zero width — persisted width
|
||||
* details, then auto-closing it (derived zero width — preferred width
|
||||
* preferences are never rewritten, so widening the window restores them).
|
||||
* The sidebar never concedes: its rendered width is always the drag
|
||||
* preference (or the collapsed rail), and center absorbs any remaining
|
||||
@@ -45,8 +45,8 @@ export function clampWidth(px: number, min: number, max: number): number {
|
||||
/**
|
||||
* Solve the three column widths for one viewport frame. Pure: no hysteresis —
|
||||
* the output is a function of (viewport, preferences) only, so recovery on
|
||||
* re-widening is automatic. Preferences re-clamp here because they cross a
|
||||
* durable boundary (localStorage rehydration may carry stale ranges).
|
||||
* re-widening is automatic. Preferences re-clamp here because they cross the
|
||||
* store boundary and callers may still supply stale ranges.
|
||||
* @param viewport - available frame width in px.
|
||||
* @param sidebar - sidebar width preference in px (0 = closed).
|
||||
* @param details - details width preference in px (0 = closed).
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* The root entry's layout store: panel geometry as plain widths in px
|
||||
* (0 = closed), persisted across reloads. Module level exports the factory
|
||||
* only — a module-level handle would pin the store's identity in the module
|
||||
* The root entry's transient layout store: panel geometry as plain widths in
|
||||
* px (0 = closed). Module level exports the factory only — a module-level
|
||||
* handle would pin the store's identity in the module
|
||||
* cache (a de-facto singleton surviving plugin reloads). register() receives
|
||||
* the factory (exclusive use: the framework instantiates per entry), AppFrame
|
||||
* derives its PropsStore share from the return type, and the service face
|
||||
@@ -29,17 +29,16 @@ type LayoutActions = {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the layout panel store handle. The persisted preference IS the
|
||||
* width, so closing a panel forgets its drag width — reopening restores the
|
||||
* contract default. Actions are the complete write set: drag writes clamp
|
||||
* Create the layout panel store handle. The preference IS the width, so
|
||||
* closing a panel forgets its drag width — reopening restores the contract
|
||||
* default. Actions are the complete write set: drag writes clamp
|
||||
* into the panel's contract range and never cross the open/closed line;
|
||||
* open/close transitions write 0 / the default explicitly.
|
||||
* @returns the store handle (spec + type + identity + factory in one).
|
||||
*/
|
||||
export function createLayoutStore(): EngineStoreHandle<LayoutState, LayoutActions> {
|
||||
const handle = defineStore({
|
||||
init: (): LayoutState => ({ sidebar: SIDEBAR_DEFAULT, details: 0 }),
|
||||
persist: 'dsh.layout.panels',
|
||||
init: (): LayoutState => ({ sidebar: SIDEBAR_DEFAULT, details: DETAILS_DEFAULT }),
|
||||
actions: {
|
||||
setSidebar: (d, px: number) => { d.sidebar = clampWidth(px, SIDEBAR_MIN, SIDEBAR_MAX) },
|
||||
setDetails: (d, px: number) => { d.details = clampWidth(px, DETAILS_MIN, DETAILS_MAX) },
|
||||
|
||||
@@ -15,8 +15,8 @@ export const name = 'client-ui-layout-invariant'
|
||||
export const inject = ['invariants']
|
||||
|
||||
/**
|
||||
* No runtime invariant: shell viewing-state stores (zustand+persist) behind
|
||||
* ctx.layout — it emits no cordis events; clamp/prune/concession-chain
|
||||
* No runtime invariant: the shell viewing-state store behind ctx.layout emits
|
||||
* no cordis events; clamp/prune/concession-chain
|
||||
* sequencing is asserted directly by this package's columns and service specs.
|
||||
*/
|
||||
const install: InvariantInstaller = () => {}
|
||||
|
||||
Reference in New Issue
Block a user