Skip to content

Commit 535128e

Browse files
levochkaaclaude
andcommitted
fix(web): panels that share a width follow resizes from other tabs
useResizableWidth read storage only on mount or when its key changed, so with Shared panel width on, a resize in one tab or window left the others at the old width until reload, and their next drag overwrote it. The hook now reads the width through useLocalStorage, which follows changes to the key in this tab and in others. Co-authored-by: Claude Opus 5.5 <[email protected]>
1 parent ba53f8a commit 535128e

2 files changed

Lines changed: 41 additions & 24 deletions

File tree

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { describe, expect, it } from "vite-plus/test";
2+
3+
import { act, render } from "../../test/render";
4+
import { useResizableWidth } from "./useResizableWidth";
5+
6+
const STORAGE_KEY = "t3code:test:resizable-width";
7+
8+
function Probe() {
9+
const { width } = useResizableWidth({
10+
storageKey: STORAGE_KEY,
11+
defaultWidth: 400,
12+
minWidth: 300,
13+
maxWidth: 800,
14+
edge: "left",
15+
});
16+
return <output>{width}</output>;
17+
}
18+
19+
describe("useResizableWidth", () => {
20+
it("follows a width another tab stores under the same key", async () => {
21+
using _storage = { [Symbol.dispose]: () => localStorage.removeItem(STORAGE_KEY) };
22+
await using screen = await render(<Probe />, {
23+
// A bare hook probe; nothing here navigates.
24+
router: false,
25+
});
26+
await expect.element(screen.getByText("400")).toBeInTheDocument();
27+
28+
// Another tab's write reaches this one only as a storage event.
29+
await act(() => {
30+
localStorage.setItem(STORAGE_KEY, "640");
31+
window.dispatchEvent(new StorageEvent("storage", { key: STORAGE_KEY }));
32+
});
33+
await expect.element(screen.getByText("640")).toBeInTheDocument();
34+
});
35+
});

‎apps/web/src/hooks/useResizableWidth.ts‎

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as Schema from "effect/Schema";
2-
import { type PointerEvent as ReactPointerEvent, useLayoutEffect, useState } from "react";
2+
import { type PointerEvent as ReactPointerEvent, useLayoutEffect } from "react";
33

4-
import { getLocalStorageItem, setLocalStorageItem } from "./useLocalStorage";
4+
import { useLocalStorage, writeLocalStorageValue } from "./useLocalStorage";
55
import { useResizeDrag } from "./useResizeDrag";
66

77
const WidthSchema = Schema.Finite;
@@ -38,22 +38,9 @@ export function useResizableWidth(options: UseResizableWidthOptions): {
3838
return Math.max(minWidth, Math.min(maxWidth, value));
3939
};
4040

41-
const readWidth = () => {
42-
if (typeof window === "undefined") return defaultWidth;
43-
try {
44-
const stored = getLocalStorageItem(storageKey, WidthSchema);
45-
return clamp(stored ?? defaultWidth);
46-
} catch (error) {
47-
console.error("Could not read persisted panel width.", error);
48-
return defaultWidth;
49-
}
50-
};
51-
const [widthState, setWidthState] = useState(() => ({ storageKey, width: readWidth() }));
52-
if (widthState.storageKey !== storageKey) {
53-
setWidthState({ storageKey, width: readWidth() });
54-
}
55-
56-
const clampedWidth = clamp(widthState.width);
41+
// Panels that share a key follow each other's drags, in this tab and in others.
42+
const [storedWidth] = useLocalStorage(storageKey, defaultWidth, WidthSchema);
43+
const clampedWidth = clamp(storedWidth);
5744

5845
const drag = useResizeDrag<HTMLElement>((event) => {
5946
const host = event.currentTarget.parentElement;
@@ -72,12 +59,7 @@ export function useResizableWidth(options: UseResizableWidthOptions): {
7259
return nextWidth;
7360
},
7461
finish(finalWidth) {
75-
setWidthState({ storageKey: startStorageKey, width: finalWidth });
76-
try {
77-
setLocalStorageItem(startStorageKey, finalWidth, WidthSchema);
78-
} catch (error) {
79-
console.error("Could not persist panel width.", error);
80-
}
62+
writeLocalStorageValue(startStorageKey, finalWidth, defaultWidth, WidthSchema);
8163
},
8264
cleanup(committed) {
8365
host?.style.removeProperty("transition-property");

0 commit comments

Comments
 (0)