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:
@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -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,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -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}
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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]);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user