Report pane resize handle size to assistive tech (APG window splitter)

This commit is contained in:
2026-06-05 12:33:19 +03:00
parent 7b8115f623
commit 8ef749c2d0
4 changed files with 144 additions and 24 deletions
+35 -6
View File
@@ -31,11 +31,19 @@ export const PANE_DEFAULT = { library: 280, preview: 360 } as const;
/** Combined width (px) the resize handles occupy between the panes. */
export const HANDLES_TOTAL = 12;
/**
* The widest a side pane can be while the editor still meets its minimum.
* `containerWidth` is the full panes-row width; `otherWidth` is the opposite
* side pane's current width. Never reports below the pane's own minimum.
*/
export function maxSideWidth(side: PaneSide, containerWidth: number, otherWidth: number): number {
return Math.max(PANE_MIN[side], containerWidth - otherWidth - HANDLES_TOTAL - PANE_MIN.editor);
}
/**
* Clamp a desired side-pane width so it stays at least its own minimum and
* leaves the editor at least its minimum. Pure — the single place the resize
* constraint lives. `containerWidth` is the full panes-row width; `otherWidth`
* is the opposite side pane's current width.
* constraint lives.
*/
export function clampSideWidth(
side: PaneSide,
@@ -43,11 +51,32 @@ export function clampSideWidth(
containerWidth: number,
otherWidth: number,
): number {
return Math.max(
PANE_MIN[side],
Math.min(desired, maxSideWidth(side, containerWidth, otherWidth)),
);
}
/**
* Normalize a side pane's width to the 0100 position a window splitter reports
* via `aria-valuenow` (WAI-ARIA APG → Window Splitter): 0 = pane at its minimum
* size, 100 = pane at its maximum. Returns null when the container width isn't
* known yet (pre-layout) or the range has collapsed, so the caller omits the
* attribute rather than emitting NaN. The 0100 scale is APG's "typical" choice
* and announces as a percentage — more meaningful than a moving pixel count.
*/
export function sideWidthValue(
side: PaneSide,
width: number,
containerWidth: number,
otherWidth: number,
): number | null {
if (containerWidth <= 0) return null;
const min = PANE_MIN[side];
// The widest this pane can be while the editor still meets its minimum.
const max = containerWidth - otherWidth - HANDLES_TOTAL - PANE_MIN.editor;
// If the container is too narrow for everyone, the min wins (never below it).
return Math.max(min, Math.min(desired, Math.max(min, max)));
const max = maxSideWidth(side, containerWidth, otherWidth);
if (max <= min) return null;
const pct = ((width - min) / (max - min)) * 100;
return Math.round(Math.min(100, Math.max(0, pct)));
}
export interface PanesState {