feat(search): hide searchbar instantly via window.history state when task modal is open
All checks were successful
Deploy Flowstate to VPS / deploy (push) Successful in 53s

This commit is contained in:
Chneemann 2026-08-26 09:11:26 +02:00
parent 6f42285405
commit 093944e8c4
No known key found for this signature in database
2 changed files with 49 additions and 8 deletions

View file

@ -36,7 +36,7 @@ interface TaskModalProps {
/**
* Renders a full task detail modal with status indicators, priority details, description, assignees, and creator-only action buttons.
* Integrates keydown listeners to dismiss the modal on pressing the Escape key.
* Supports ESC key navigation and updates URL query parameters dynamically.
*
* @param {TaskModalProps} props - The component props.
* @returns {JSX.Element | null} The rendered modal component or null when no task is selected.
@ -46,13 +46,13 @@ export default function TaskModal({ task, onClose, onDelete }: TaskModalProps) {
useEffect(() => {
/**
* Keyboard event handler closing the modal dialog when pressing the Escape key.
* Attaches a global keydown event listener to close the modal when the Escape key is pressed.
*
* @param {KeyboardEvent} e - The global window keydown event.
* @param {KeyboardEvent} e - The keyboard event object.
*/
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === "Escape") {
onClose();
handleClose();
}
};
window.addEventListener("keydown", handleKeyDown);
@ -61,6 +61,43 @@ export default function TaskModal({ task, onClose, onDelete }: TaskModalProps) {
};
}, [onClose]);
useEffect(() => {
/**
* Updates the URL search parameters to include `modal=task-detail` when a task is selected,
* maintaining modal state in the browser history without triggering a full page re-render.
*/
if (task) {
const params = new URLSearchParams(window.location.search);
if (params.get("modal") !== "task-detail") {
params.set("modal", "task-detail");
window.history.replaceState(
{},
"",
`${window.location.pathname}?${params.toString()}`,
);
}
}
}, [task]);
/**
* Removes modal query parameters from the browser location history without causing a Next.js soft navigation, then triggers onClose.
*/
const handleClose = () => {
const params = new URLSearchParams(window.location.search);
params.delete("modal");
const newQuery = params.toString();
window.history.replaceState(
{},
"",
newQuery
? `${window.location.pathname}?${newQuery}`
: window.location.pathname,
);
onClose();
};
if (!task) return null;
const priorityConfig = task.priority && PRIORITY_CONFIG[task.priority];
@ -72,7 +109,7 @@ export default function TaskModal({ task, onClose, onDelete }: TaskModalProps) {
return (
<div
className="fixed inset-0 z-49 flex items-center justify-center bg-black/80 backdrop-blur-md p-4 animate-in fade-in duration-300"
onClick={onClose}
onClick={handleClose}
>
<div
className="bg-card border border-border rounded-3xl max-w-xl w-full max-h-[80vh] md:max-h-[90vh] shadow-2xl relative flex flex-col animate-in zoom-in-95 duration-200 overflow-hidden"
@ -114,7 +151,7 @@ export default function TaskModal({ task, onClose, onDelete }: TaskModalProps) {
</div>
<button
onClick={onClose}
onClick={handleClose}
className="text-foreground-muted hover:text-foreground p-2 rounded-xl bg-background/50 hover:bg-background border border-border transition-all cursor-pointer shrink-0"
aria-label="Close modal"
>
@ -238,7 +275,7 @@ export default function TaskModal({ task, onClose, onDelete }: TaskModalProps) {
<button
onClick={() => {
onDelete?.(task.id);
onClose();
handleClose();
}}
className="inline-flex items-center space-x-2 px-4 py-2.5 text-sm font-medium bg-destructive-bg text-destructive border border-destructive-border hover:bg-destructive/20 rounded-xl transition-all cursor-pointer"
>

View file

@ -39,6 +39,7 @@ export default function SearchBar({
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
const isModalOpen = searchParams.get("modal") !== null;
// Check whether the current route supports the search
const isSearchableRoute = SEARCHABLE_ROUTES.some(
@ -58,6 +59,9 @@ export default function SearchBar({
useEffect(() => {
if (!isSearchableRoute) return;
/**
* Updates the URL search parameters based on the current local input value.
*/
const timer = setTimeout(() => {
if (localValue !== searchQuery) {
const params = new URLSearchParams(searchParams.toString());
@ -83,7 +87,7 @@ export default function SearchBar({
isSearchableRoute,
]);
if (!isSearchableRoute) {
if (!isSearchableRoute || isModalOpen) {
return null;
}