feat(KB-657): add expand/collapse toggle to quick add components
- Add toggle button (ChevronDown/ChevronUp) to QuickEntryBox (list view) and InlineCreateCard (board view) - Replace auto-expand behavior with manual toggle control - Add CSS styles for collapsed/expanded states and toggle button positioning - Update tests to verify toggle behavior - Provide cleaner default UI while keeping advanced options discoverable
This commit is contained in:
@@ -3,7 +3,7 @@ import type { ToastType } from "../hooks/useToast";
|
||||
import type { Task, TaskCreateInput } from "@fusion/core";
|
||||
import type { ModelInfo, RefinementType } from "../api";
|
||||
import { fetchModels, refineText, getRefineErrorMessage } from "../api";
|
||||
import { Link, Brain, Lightbulb, ListTree, Sparkles, Save, ChevronUp, ChevronDown } from "lucide-react";
|
||||
import { Link, Brain, Lightbulb, ListTree, Sparkles, Save, ChevronDown, ChevronUp } from "lucide-react";
|
||||
import { CustomModelDropdown } from "./CustomModelDropdown";
|
||||
import { ModelSelectionModal } from "./ModelSelectionModal";
|
||||
|
||||
@@ -70,7 +70,6 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
|
||||
return true;
|
||||
});
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
const blurTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const justResetRef = useRef(false);
|
||||
|
||||
// Rich creation state (mirrors InlineCreateCard)
|
||||
@@ -151,19 +150,10 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
|
||||
}
|
||||
}, [description]);
|
||||
|
||||
// Persist disclosure state to localStorage whenever it changes
|
||||
useEffect(() => {
|
||||
if (typeof window !== "undefined") {
|
||||
localStorage.setItem(DISCLOSURE_STORAGE_KEY, isDisclosureExpanded.toString());
|
||||
}
|
||||
}, [isDisclosureExpanded]);
|
||||
|
||||
// Cleanup on unmount
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (blurTimeoutRef.current) {
|
||||
clearTimeout(blurTimeoutRef.current);
|
||||
}
|
||||
// No blur timeout to clean up
|
||||
};
|
||||
}, []);
|
||||
|
||||
@@ -227,8 +217,7 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
|
||||
setIsModelModalOpen(false);
|
||||
setIsRefineMenuOpen(false);
|
||||
setIsRefining(false);
|
||||
setIsExpanded(false); // Collapse textarea height on reset
|
||||
// Note: isDisclosureExpanded is NOT reset - user preference persists
|
||||
setIsExpanded(false); // Collapse on reset
|
||||
justResetRef.current = true;
|
||||
if (textareaRef.current) {
|
||||
textareaRef.current.style.height = "auto";
|
||||
@@ -314,9 +303,8 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
|
||||
localStorage.removeItem(STORAGE_KEY);
|
||||
}
|
||||
}
|
||||
// Collapse textarea and disclosure on escape
|
||||
// Collapse on escape
|
||||
setIsExpanded(false);
|
||||
setIsDisclosureExpanded(false);
|
||||
textareaRef.current?.blur();
|
||||
}
|
||||
},
|
||||
@@ -327,7 +315,6 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
|
||||
showDeps,
|
||||
isModelModalOpen,
|
||||
isRefineMenuOpen,
|
||||
setIsDisclosureExpanded,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -337,31 +324,16 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
|
||||
justResetRef.current = false;
|
||||
return;
|
||||
}
|
||||
// Only auto-expand if autoExpand prop is true (defaults to true for backward compatibility)
|
||||
if (autoExpand) {
|
||||
setIsExpanded(true);
|
||||
}
|
||||
}, [autoExpand]);
|
||||
// No auto-expand on focus — manual toggle only
|
||||
}, []);
|
||||
|
||||
const handleBlur = useCallback(() => {
|
||||
// Clear any existing timeout
|
||||
if (blurTimeoutRef.current) {
|
||||
clearTimeout(blurTimeoutRef.current);
|
||||
// No auto-collapse on blur — state persists until manually toggled or task is submitted/cancelled
|
||||
// Only clear the justResetRef flag if needed
|
||||
if (justResetRef.current) {
|
||||
justResetRef.current = false;
|
||||
}
|
||||
|
||||
// Collapse after a short delay to allow click events on dropdowns
|
||||
// Collapse regardless of content - only check if dropdowns are open
|
||||
blurTimeoutRef.current = setTimeout(() => {
|
||||
if (!showDeps && !isModelModalOpen && !isRefineMenuOpen) {
|
||||
setIsExpanded(false);
|
||||
// Reset height when collapsing
|
||||
if (textareaRef.current) {
|
||||
textareaRef.current.style.height = "auto";
|
||||
}
|
||||
}
|
||||
blurTimeoutRef.current = null;
|
||||
}, 200);
|
||||
}, [showDeps, isModelModalOpen, isRefineMenuOpen]);
|
||||
}, []);
|
||||
|
||||
const toggleDep = useCallback((id: string) => {
|
||||
setDependencies((prev) =>
|
||||
@@ -466,16 +438,15 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
|
||||
}
|
||||
}, [availableModels]);
|
||||
|
||||
// Show expanded controls based on disclosure state (user preference), not textarea focus
|
||||
const showExpandedControls = isDisclosureExpanded;
|
||||
// Show expanded controls only when manually expanded (isExpanded)
|
||||
const showExpandedControls = isExpanded;
|
||||
|
||||
const toggleExpanded = useCallback(() => {
|
||||
setIsDisclosureExpanded((prev) => !prev);
|
||||
setIsExpanded((prev) => !prev);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={`quick-entry-box ${isDisclosureExpanded ? "quick-entry-box--expanded" : "quick-entry-box--collapsed"}`} data-testid="quick-entry-box">
|
||||
<div className={`quick-entry-box ${isExpanded ? "quick-entry-box--expanded" : "quick-entry-box--collapsed"}`} data-testid="quick-entry-box">
|
||||
<div className="quick-entry-main-row">
|
||||
<textarea
|
||||
ref={textareaRef}
|
||||
@@ -495,16 +466,16 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
|
||||
type="button"
|
||||
className="btn btn-sm quick-entry-toggle"
|
||||
onClick={toggleExpanded}
|
||||
aria-expanded={isDisclosureExpanded}
|
||||
aria-expanded={isExpanded}
|
||||
aria-controls="quick-entry-controls"
|
||||
data-testid="quick-entry-toggle"
|
||||
title={isDisclosureExpanded ? "Collapse" : "Expand"}
|
||||
title={isExpanded ? "Collapse" : "Expand"}
|
||||
>
|
||||
{isDisclosureExpanded ? <ChevronUp size={14} /> : <ChevronDown size={14} />}
|
||||
{isExpanded ? <ChevronUp size={14} /> : <ChevronDown size={14} />}
|
||||
</button>
|
||||
</div>
|
||||
{showExpandedControls && (
|
||||
<div className="quick-entry-controls">
|
||||
<div id="quick-entry-controls" className="quick-entry-controls">
|
||||
<div className="quick-entry-controls-left">
|
||||
<div className="dep-trigger-wrap">
|
||||
<button
|
||||
|
||||
Reference in New Issue
Block a user