mirror of
https://github.com/we-promise/sure.git
synced 2026-07-12 21:05:20 +00:00
* feat(layout): make the accounts and assistant sidebars resizable
Add draggable dividers on the left (accounts) and right (assistant)
sidebars. Width is driven by a `--{side}-sidebar-width` CSS variable on
the layout root and persisted per-device in localStorage; it composes
with the existing show/hide toggle (collapse still wins via `w-0`,
reopening restores the last width).
Clamping keeps each sidebar within its bounds (min 240px, max 480/560px)
and guarantees the center column never drops below 400px, so the
dashboard stays legible no matter how wide a sidebar is dragged.
- sidebar_resize_controller.js: pointer drag, keyboard nudge
(arrows / Home), double-click reset, localStorage persistence
- utils/sidebar_resize.js: pure, unit-tested clamp helper
- layouts/shared/_sidebar_resize_handle.html.erb: accessible
separator handle (role=separator, aria-label, focusable)
- test/javascript/utils/sidebar_resize_test.mjs: clamp logic tests
* fix(sidebar-resize): free a collapsed sidebar's space and guard localStorage read
- #clamp now uses the opposite sidebar's rendered width (0 when collapsed)
instead of its stored CSS variable, so closing one sidebar lets the other
use the freed space (addresses Codex review).
- #applyStoredWidth wraps the localStorage read in try/catch and falls back
to the default, so connect() can't bail in storage-blocked browsers
(addresses CodeRabbit review).
43 lines
1.8 KiB
JavaScript
43 lines
1.8 KiB
JavaScript
// Pure geometry helpers for the resizable app sidebars.
|
|
//
|
|
// Kept free of any DOM access so the clamping logic can be unit-tested in
|
|
// isolation. Must be kept in sync with
|
|
// test/javascript/utils/sidebar_resize_test.mjs.
|
|
|
|
// Width of the always-visible icon navbar to the left of the sidebars.
|
|
export const SIDEBAR_NAVBAR_WIDTH = 84;
|
|
// Smallest a sidebar may shrink to while still being usable.
|
|
export const SIDEBAR_MIN_WIDTH = 240;
|
|
// Minimum breathing room the center content must always keep so the dashboard
|
|
// stays legible no matter how wide the sidebars are dragged.
|
|
export const SIDEBAR_MIN_MAIN_WIDTH = 400;
|
|
|
|
export const SIDEBAR_DEFAULTS = { left: 320, right: 400 };
|
|
// Hard upper bounds per side, regardless of how much room is available.
|
|
export const SIDEBAR_ABS_MAX = { left: 480, right: 560 };
|
|
|
|
// Clamp a requested sidebar width to a value that respects the per-side bounds
|
|
// AND guarantees the center column keeps at least SIDEBAR_MIN_MAIN_WIDTH.
|
|
//
|
|
// rawWidth - desired width in px (e.g. derived from the pointer position)
|
|
// viewportWidth - window.innerWidth
|
|
// otherWidth - current width of the opposite sidebar (0 if collapsed/hidden)
|
|
export function clampSidebarWidth(
|
|
rawWidth,
|
|
{
|
|
viewportWidth,
|
|
otherWidth = 0,
|
|
navbarWidth = SIDEBAR_NAVBAR_WIDTH,
|
|
min = SIDEBAR_MIN_WIDTH,
|
|
minMain = SIDEBAR_MIN_MAIN_WIDTH,
|
|
absMax = Number.POSITIVE_INFINITY,
|
|
} = {},
|
|
) {
|
|
const available = viewportWidth - navbarWidth - otherWidth - minMain;
|
|
// Never let the computed max fall below `min`; if the viewport is genuinely
|
|
// too small we pin to `min` and accept the squeeze rather than going invalid.
|
|
const max = Math.max(min, Math.min(absMax, available));
|
|
const clamped = Math.min(Math.max(rawWidth, min), max);
|
|
return Math.round(clamped);
|
|
}
|