fix(frontend): harden activity split responsiveness

This commit is contained in:
User
2026-07-15 15:52:22 +02:00
parent f0ef2654df
commit 1f540fcb78
6 changed files with 160 additions and 18 deletions
+38 -7
View File
@@ -71,11 +71,17 @@ export function resizeWidthFromKey(
return null;
}
type DragState = { pointerId: number; startX: number; startWidth: number };
type DragState = {
pointerId: number;
startX: number;
startWidth: number;
target: HTMLDivElement;
};
export function useActivityPanelResize(): {
containerRef: RefObject<HTMLDivElement>;
panelWidth: number;
desktopSplit: boolean;
resizing: boolean;
separatorProps: {
role: "separator";
@@ -103,6 +109,7 @@ export function useActivityPanelResize(): {
const dragRef = useRef<DragState | null>(null);
const [panelWidth, setPanelWidth] = useState(initialWidthRef.current);
const [bounds, setBounds] = useState(boundsRef.current);
const [desktopSplit, setDesktopSplit] = useState(false);
const [resizing, setResizing] = useState(false);
const applyWidth = useCallback((rawWidth: number, persist: boolean) => {
@@ -116,20 +123,42 @@ export function useActivityPanelResize(): {
useLayoutEffect(() => {
const container = containerRef.current;
if (!container) return;
const syncBounds = () => {
if (container.clientWidth < ACTIVITY_PANEL_MIN_WIDTH + ACTIVITY_CENTRAL_MIN_WIDTH) return;
const cancelDrag = () => {
const drag = dragRef.current;
if (drag?.target.hasPointerCapture?.(drag.pointerId)) {
drag.target.releasePointerCapture?.(drag.pointerId);
}
dragRef.current = null;
setResizing(false);
};
const syncLayout = () => {
const viewportIsDesktop = window.matchMedia?.("(min-width: 1024px)").matches
?? window.innerWidth >= 1024;
const hasUsableContainer = container.clientWidth >= ACTIVITY_PANEL_MIN_WIDTH + ACTIVITY_CENTRAL_MIN_WIDTH;
const nextDesktopSplit = viewportIsDesktop && hasUsableContainer;
setDesktopSplit(nextDesktopSplit);
if (!nextDesktopSplit) {
cancelDrag();
const savedWidth = loadActivityPanelWidth(storageRef.current);
widthRef.current = savedWidth;
setPanelWidth(savedWidth);
return;
}
const nextBounds = getActivityPanelBounds(container.clientWidth);
boundsRef.current = nextBounds;
setBounds(nextBounds);
applyWidth(widthRef.current, true);
};
syncBounds();
const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(syncBounds);
syncLayout();
const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(syncLayout);
observer?.observe(container);
window.addEventListener("resize", syncBounds);
const media = window.matchMedia?.("(min-width: 1024px)");
media?.addEventListener?.("change", syncLayout);
window.addEventListener("resize", syncLayout);
return () => {
observer?.disconnect();
window.removeEventListener("resize", syncBounds);
media?.removeEventListener?.("change", syncLayout);
window.removeEventListener("resize", syncLayout);
};
}, [applyWidth]);
@@ -138,6 +167,7 @@ export function useActivityPanelResize(): {
pointerId: event.pointerId,
startX: event.clientX,
startWidth: widthRef.current,
target: event.currentTarget,
};
event.currentTarget.setPointerCapture?.(event.pointerId);
setResizing(true);
@@ -174,6 +204,7 @@ export function useActivityPanelResize(): {
return {
containerRef,
panelWidth,
desktopSplit,
resizing,
separatorProps: {
role: "separator",