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
All checks were successful
Deploy Flowstate to VPS / deploy (push) Successful in 53s
This commit is contained in:
parent
6f42285405
commit
093944e8c4
2 changed files with 49 additions and 8 deletions
|
|
@ -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"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue