feat(FN-4936): complete Step 1 — add resizable pane state and handlers

Fusion-Task-Id: FN-4936
Fusion-Task-Lineage: 6c860bad-2120-4a81-a399-02c1d45aa63e
This commit is contained in:
Fusion (runfusion.ai)
2026-05-18 01:00:08 -07:00
committed by gsxdsm
parent 2242d5b2ff
commit 2f8a634464

View File

@@ -1,5 +1,5 @@
import "./GitHubImportModal.css";
import { useState, useEffect, useCallback, useRef } from "react";
import { useState, useEffect, useCallback, useRef, type KeyboardEvent, type PointerEvent } from "react";
import type { Task } from "@fusion/core";
import { getErrorMessage } from "@fusion/core";
import {
@@ -27,9 +27,17 @@ interface GitHubImportModalProps {
// Mobile breakpoint in pixels
const MOBILE_BREAKPOINT = 640;
const GITHUB_IMPORT_LIST_PANE_MIN_WIDTH = 240;
const GITHUB_IMPORT_LIST_PANE_MAX_WIDTH = 640;
const GITHUB_IMPORT_LIST_PANE_DEFAULT_WIDTH = 360;
const GITHUB_IMPORT_LIST_PANE_STORAGE_KEY = "fusion:github-import-list-pane-width";
type TabType = "issues" | "pulls";
function clampListPaneWidth(width: number) {
return Math.max(GITHUB_IMPORT_LIST_PANE_MIN_WIDTH, Math.min(GITHUB_IMPORT_LIST_PANE_MAX_WIDTH, width));
}
export function GitHubImportModal({ isOpen, onClose, onImport, tasks, projectId }: GitHubImportModalProps) {
useMobileScrollLock(isOpen);
const [owner, setOwner] = useState("");
@@ -63,6 +71,25 @@ export function GitHubImportModal({ isOpen, onClose, onImport, tasks, projectId
// Mobile view state
const [isMobile, setIsMobile] = useState(false);
const [mobileView, setMobileView] = useState<"list" | "preview">("list");
const [listPaneWidth, setListPaneWidth] = useState(() => {
if (typeof window === "undefined") {
return GITHUB_IMPORT_LIST_PANE_DEFAULT_WIDTH;
}
try {
const stored = window.localStorage.getItem(GITHUB_IMPORT_LIST_PANE_STORAGE_KEY);
if (!stored) {
return GITHUB_IMPORT_LIST_PANE_DEFAULT_WIDTH;
}
const parsed = Number.parseInt(stored, 10);
if (!Number.isFinite(parsed)) {
return GITHUB_IMPORT_LIST_PANE_DEFAULT_WIDTH;
}
return clampListPaneWidth(parsed);
} catch {
return GITHUB_IMPORT_LIST_PANE_DEFAULT_WIDTH;
}
});
// Track which owner/repo we've already auto-loaded to prevent duplicate loads
const autoLoadedRef = useRef<{ owner: string; repo: string; labels: string; tab: TabType } | null>(null);
@@ -257,6 +284,75 @@ export function GitHubImportModal({ isOpen, onClose, onImport, tasks, projectId
return () => window.removeEventListener("resize", checkMobile);
}, [isOpen]);
useEffect(() => {
if (typeof window === "undefined") {
return;
}
try {
window.localStorage.setItem(GITHUB_IMPORT_LIST_PANE_STORAGE_KEY, String(listPaneWidth));
} catch {
// Ignore storage write failures.
}
}, [listPaneWidth]);
const handleListPaneResizeStart = useCallback((event: PointerEvent<HTMLDivElement>) => {
if (isMobile) {
return;
}
const startX = event.clientX;
const startWidth = listPaneWidth;
const target = event.currentTarget;
target.setPointerCapture(event.pointerId);
const handlePointerMove = (moveEvent: PointerEvent) => {
const deltaX = moveEvent.clientX - startX;
setListPaneWidth(clampListPaneWidth(startWidth + deltaX));
};
const handlePointerUp = () => {
document.removeEventListener("pointermove", handlePointerMove);
document.removeEventListener("pointerup", handlePointerUp);
if (target.hasPointerCapture(event.pointerId)) {
target.releasePointerCapture(event.pointerId);
}
};
document.addEventListener("pointermove", handlePointerMove);
document.addEventListener("pointerup", handlePointerUp);
}, [isMobile, listPaneWidth]);
const handleListPaneResizeKeyDown = useCallback((event: KeyboardEvent<HTMLDivElement>) => {
if (isMobile) {
return;
}
const step = event.shiftKey ? 50 : 10;
if (event.key === "ArrowLeft") {
event.preventDefault();
setListPaneWidth((current) => clampListPaneWidth(current - step));
return;
}
if (event.key === "ArrowRight") {
event.preventDefault();
setListPaneWidth((current) => clampListPaneWidth(current + step));
return;
}
if (event.key === "Home") {
event.preventDefault();
setListPaneWidth(GITHUB_IMPORT_LIST_PANE_MIN_WIDTH);
return;
}
if (event.key === "End") {
event.preventDefault();
setListPaneWidth(GITHUB_IMPORT_LIST_PANE_MAX_WIDTH);
}
}, [isMobile]);
// Handle issue selection - switch to preview view on mobile
const handleIssueSelect = useCallback((issueNumber: number) => {
setSelectedIssueNumber(issueNumber);
@@ -509,6 +605,7 @@ export function GitHubImportModal({ isOpen, onClose, onImport, tasks, projectId
{/* Left pane: Issue/PR list */}
<section
className={`github-import-list-pane ${isMobile ? 'mobile' : ''} ${mobileView === 'list' ? 'active' : ''}`}
style={!isMobile ? { flex: `0 0 ${listPaneWidth}px` } : undefined}
data-testid="github-import-list-pane"
aria-labelledby="github-import-results-heading"
>
@@ -646,6 +743,22 @@ export function GitHubImportModal({ isOpen, onClose, onImport, tasks, projectId
</div>
</section>
{!isMobile && (
<div
className="github-import-workspace__resize-handle"
role="separator"
aria-orientation="vertical"
aria-label="Resize issues list"
aria-valuemin={GITHUB_IMPORT_LIST_PANE_MIN_WIDTH}
aria-valuemax={GITHUB_IMPORT_LIST_PANE_MAX_WIDTH}
aria-valuenow={listPaneWidth}
tabIndex={0}
onPointerDown={handleListPaneResizeStart}
onKeyDown={handleListPaneResizeKeyDown}
data-testid="github-import-resize-handle"
/>
)}
{/* Right pane: Preview */}
<section
className={`github-import-preview-pane ${isMobile ? 'mobile' : ''} ${mobileView === 'preview' ? 'active' : ''}`}