diff --git a/front/src/modules/ui/action-bar/components/ActionBar.tsx b/front/src/modules/ui/action-bar/components/ActionBar.tsx index d8f040488..88df4e738 100644 --- a/front/src/modules/ui/action-bar/components/ActionBar.tsx +++ b/front/src/modules/ui/action-bar/components/ActionBar.tsx @@ -41,7 +41,7 @@ export function ActionBar({ selectedIds }: OwnProps) { return null; } return ( - + {actionBarEntries} ); diff --git a/front/src/modules/ui/board/components/EntityBoard.tsx b/front/src/modules/ui/board/components/EntityBoard.tsx index 43f1bd5ee..4d81cb002 100644 --- a/front/src/modules/ui/board/components/EntityBoard.tsx +++ b/front/src/modules/ui/board/components/EntityBoard.tsx @@ -87,7 +87,8 @@ export function EntityBoard({ ); useListenClickOutsideByClassName({ - className: 'entity-board-card', + classNames: ['entity-board-card'], + excludeClassNames: ['action-bar', 'context-menu'], callback: unselectAllActiveCards, }); diff --git a/front/src/modules/ui/context-menu/components/ContextMenu.tsx b/front/src/modules/ui/context-menu/components/ContextMenu.tsx index ba439558b..e55b1dd1c 100644 --- a/front/src/modules/ui/context-menu/components/ContextMenu.tsx +++ b/front/src/modules/ui/context-menu/components/ContextMenu.tsx @@ -59,7 +59,11 @@ export function ContextMenu({ selectedIds }: OwnProps) { return null; } return ( - + {contextMenuEntries} diff --git a/front/src/modules/ui/table/components/EntityTable.tsx b/front/src/modules/ui/table/components/EntityTable.tsx index 96eb212c7..a5a71621b 100644 --- a/front/src/modules/ui/table/components/EntityTable.tsx +++ b/front/src/modules/ui/table/components/EntityTable.tsx @@ -3,7 +3,11 @@ import styled from '@emotion/styled'; import { SortType } from '@/ui/filter-n-sort/types/interface'; import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect'; -import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; +import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; +import { + useListenClickOutside, + useListenClickOutsideByClassName, +} from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper'; import { EntityUpdateMutationContext } from '../contexts/EntityUpdateMutationHookContext'; @@ -13,6 +17,7 @@ import { useResetTableRowSelection } from '../hooks/useResetTableRowSelection'; import { useSetRowSelectedState } from '../hooks/useSetRowSelectedState'; import type { TableView } from '../states/tableViewsState'; import { TableHeader } from '../table-header/components/TableHeader'; +import { TableHotkeyScope } from '../types/TableHotkeyScope'; import { EntityTableBody } from './EntityTableBody'; import { EntityTableHeader } from './EntityTableHeader'; @@ -113,6 +118,22 @@ export function EntityTable({ }, }); + useScopedHotkeys( + 'escape', + () => { + resetTableRowSelection(); + }, + TableHotkeyScope.Table, + ); + + useListenClickOutsideByClassName({ + classNames: ['entity-table-cell'], + excludeClassNames: ['action-bar', 'context-menu'], + callback: () => { + resetTableRowSelection(); + }, + }); + return ( @@ -126,7 +147,7 @@ export function EntityTable({ />
- + diff --git a/front/src/modules/ui/utilities/pointer-event/hooks/useListenClickOutside.ts b/front/src/modules/ui/utilities/pointer-event/hooks/useListenClickOutside.ts index d3c3b7928..f53bbf365 100644 --- a/front/src/modules/ui/utilities/pointer-event/hooks/useListenClickOutside.ts +++ b/front/src/modules/ui/utilities/pointer-event/hooks/useListenClickOutside.ts @@ -76,38 +76,57 @@ export function useListenClickOutside({ }; }, [refs, callback, mode]); } - export const useListenClickOutsideByClassName = ({ - className, + classNames, + excludeClassNames, callback, }: { - className: string; + classNames: string[]; + excludeClassNames?: string[]; callback: () => void; }) => { useEffect(() => { - const handleClickOutside = (event: MouseEvent) => { + const handleClickOutside = (event: MouseEvent | TouchEvent) => { + if (!(event.target instanceof Node)) return; + const clickedElement = event.target as HTMLElement; let isClickedInside = false; + let isClickedOnExcluded = false; let currentElement: HTMLElement | null = clickedElement; - // Check if the clicked element or any of its parent elements have the specified class while (currentElement) { - if (currentElement.classList.contains(className)) { - isClickedInside = true; + const currentClassList = currentElement.classList; + + isClickedInside = classNames.some((className) => + currentClassList.contains(className), + ); + isClickedOnExcluded = + excludeClassNames?.some((className) => + currentClassList.contains(className), + ) ?? false; + + if (isClickedInside || isClickedOnExcluded) { break; } + currentElement = currentElement.parentElement; } - if (!isClickedInside) { + if (!isClickedInside && !isClickedOnExcluded) { callback(); } }; document.addEventListener('mousedown', handleClickOutside); + document.addEventListener('touchend', handleClickOutside, { + capture: true, + }); return () => { document.removeEventListener('mousedown', handleClickOutside); + document.removeEventListener('touchend', handleClickOutside, { + capture: true, + }); }; - }, [callback, className]); + }, [callback, classNames, excludeClassNames]); };