Unselect table rows using esc key or click outside (#1420)

* unselect table rows by esc or clickoutside of tablebody

* exclude action-bar

* exclude context-menu

* added enums, handled touch listener
This commit is contained in:
Matthew
2023-09-05 03:56:07 -04:00
committed by GitHub
parent 732b5a5ddf
commit 7bced2b49b
5 changed files with 59 additions and 14 deletions

View File

@ -41,7 +41,7 @@ export function ActionBar({ selectedIds }: OwnProps) {
return null; return null;
} }
return ( return (
<StyledContainerActionBar ref={wrapperRef}> <StyledContainerActionBar className="action-bar" ref={wrapperRef}>
{actionBarEntries} {actionBarEntries}
</StyledContainerActionBar> </StyledContainerActionBar>
); );

View File

@ -87,7 +87,8 @@ export function EntityBoard({
); );
useListenClickOutsideByClassName({ useListenClickOutsideByClassName({
className: 'entity-board-card', classNames: ['entity-board-card'],
excludeClassNames: ['action-bar', 'context-menu'],
callback: unselectAllActiveCards, callback: unselectAllActiveCards,
}); });

View File

@ -59,7 +59,11 @@ export function ContextMenu({ selectedIds }: OwnProps) {
return null; return null;
} }
return ( return (
<StyledContainerContextMenu ref={wrapperRef} position={position}> <StyledContainerContextMenu
className="context-menu"
ref={wrapperRef}
position={position}
>
<StyledDropdownMenu> <StyledDropdownMenu>
<StyledDropdownMenuItemsContainer> <StyledDropdownMenuItemsContainer>
{contextMenuEntries} {contextMenuEntries}

View File

@ -3,7 +3,11 @@ import styled from '@emotion/styled';
import { SortType } from '@/ui/filter-n-sort/types/interface'; import { SortType } from '@/ui/filter-n-sort/types/interface';
import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect'; 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 { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
import { EntityUpdateMutationContext } from '../contexts/EntityUpdateMutationHookContext'; import { EntityUpdateMutationContext } from '../contexts/EntityUpdateMutationHookContext';
@ -13,6 +17,7 @@ import { useResetTableRowSelection } from '../hooks/useResetTableRowSelection';
import { useSetRowSelectedState } from '../hooks/useSetRowSelectedState'; import { useSetRowSelectedState } from '../hooks/useSetRowSelectedState';
import type { TableView } from '../states/tableViewsState'; import type { TableView } from '../states/tableViewsState';
import { TableHeader } from '../table-header/components/TableHeader'; import { TableHeader } from '../table-header/components/TableHeader';
import { TableHotkeyScope } from '../types/TableHotkeyScope';
import { EntityTableBody } from './EntityTableBody'; import { EntityTableBody } from './EntityTableBody';
import { EntityTableHeader } from './EntityTableHeader'; import { EntityTableHeader } from './EntityTableHeader';
@ -113,6 +118,22 @@ export function EntityTable<SortField>({
}, },
}); });
useScopedHotkeys(
'escape',
() => {
resetTableRowSelection();
},
TableHotkeyScope.Table,
);
useListenClickOutsideByClassName({
classNames: ['entity-table-cell'],
excludeClassNames: ['action-bar', 'context-menu'],
callback: () => {
resetTableRowSelection();
},
});
return ( return (
<EntityUpdateMutationContext.Provider value={updateEntityMutation}> <EntityUpdateMutationContext.Provider value={updateEntityMutation}>
<StyledTableWithHeader> <StyledTableWithHeader>
@ -126,7 +147,7 @@ export function EntityTable<SortField>({
/> />
<ScrollWrapper> <ScrollWrapper>
<div> <div>
<StyledTable> <StyledTable className="entity-table-cell">
<EntityTableHeader /> <EntityTableHeader />
<EntityTableBody /> <EntityTableBody />
</StyledTable> </StyledTable>

View File

@ -76,38 +76,57 @@ export function useListenClickOutside<T extends Element>({
}; };
}, [refs, callback, mode]); }, [refs, callback, mode]);
} }
export const useListenClickOutsideByClassName = ({ export const useListenClickOutsideByClassName = ({
className, classNames,
excludeClassNames,
callback, callback,
}: { }: {
className: string; classNames: string[];
excludeClassNames?: string[];
callback: () => void; callback: () => void;
}) => { }) => {
useEffect(() => { useEffect(() => {
const handleClickOutside = (event: MouseEvent) => { const handleClickOutside = (event: MouseEvent | TouchEvent) => {
if (!(event.target instanceof Node)) return;
const clickedElement = event.target as HTMLElement; const clickedElement = event.target as HTMLElement;
let isClickedInside = false; let isClickedInside = false;
let isClickedOnExcluded = false;
let currentElement: HTMLElement | null = clickedElement; let currentElement: HTMLElement | null = clickedElement;
// Check if the clicked element or any of its parent elements have the specified class
while (currentElement) { while (currentElement) {
if (currentElement.classList.contains(className)) { const currentClassList = currentElement.classList;
isClickedInside = true;
isClickedInside = classNames.some((className) =>
currentClassList.contains(className),
);
isClickedOnExcluded =
excludeClassNames?.some((className) =>
currentClassList.contains(className),
) ?? false;
if (isClickedInside || isClickedOnExcluded) {
break; break;
} }
currentElement = currentElement.parentElement; currentElement = currentElement.parentElement;
} }
if (!isClickedInside) { if (!isClickedInside && !isClickedOnExcluded) {
callback(); callback();
} }
}; };
document.addEventListener('mousedown', handleClickOutside); document.addEventListener('mousedown', handleClickOutside);
document.addEventListener('touchend', handleClickOutside, {
capture: true,
});
return () => { return () => {
document.removeEventListener('mousedown', handleClickOutside); document.removeEventListener('mousedown', handleClickOutside);
document.removeEventListener('touchend', handleClickOutside, {
capture: true,
});
}; };
}, [callback, className]); }, [callback, classNames, excludeClassNames]);
}; };