mirror of
https://github.com/lingble/twenty.git
synced 2025-10-30 20:27:55 +00:00
Unselect record table records on table body click (#8306)
We have previously fixed the unselection of table records on click outside. However, the ref was mispositioned as it selected the full height table. In the case of low record numbers, we also want the unselection to happen on table body click
This commit is contained in:
@@ -3,14 +3,20 @@ import { isNonEmptyString, isNull } from '@sniptt/guards';
|
|||||||
|
|
||||||
import { RecordTableComponentInstance } from '@/object-record/record-table/components/RecordTableComponentInstance';
|
import { RecordTableComponentInstance } from '@/object-record/record-table/components/RecordTableComponentInstance';
|
||||||
import { RecordTableContextProvider } from '@/object-record/record-table/components/RecordTableContextProvider';
|
import { RecordTableContextProvider } from '@/object-record/record-table/components/RecordTableContextProvider';
|
||||||
|
import { RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-table/constants/RecordTableClickOutsideListenerId';
|
||||||
import { RecordTableEmptyState } from '@/object-record/record-table/empty-state/components/RecordTableEmptyState';
|
import { RecordTableEmptyState } from '@/object-record/record-table/empty-state/components/RecordTableEmptyState';
|
||||||
|
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||||
import { RecordTableBody } from '@/object-record/record-table/record-table-body/components/RecordTableBody';
|
import { RecordTableBody } from '@/object-record/record-table/record-table-body/components/RecordTableBody';
|
||||||
import { RecordTableBodyEffect } from '@/object-record/record-table/record-table-body/components/RecordTableBodyEffect';
|
import { RecordTableBodyEffect } from '@/object-record/record-table/record-table-body/components/RecordTableBodyEffect';
|
||||||
|
import { RecordTableBodyUnselectEffect } from '@/object-record/record-table/record-table-body/components/RecordTableBodyUnselectEffect';
|
||||||
import { RecordTableHeader } from '@/object-record/record-table/record-table-header/components/RecordTableHeader';
|
import { RecordTableHeader } from '@/object-record/record-table/record-table-header/components/RecordTableHeader';
|
||||||
import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState';
|
import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState';
|
||||||
import { recordTablePendingRecordIdComponentState } from '@/object-record/record-table/states/recordTablePendingRecordIdComponentState';
|
import { recordTablePendingRecordIdComponentState } from '@/object-record/record-table/states/recordTablePendingRecordIdComponentState';
|
||||||
import { tableRowIdsComponentState } from '@/object-record/record-table/states/tableRowIdsComponentState';
|
import { tableRowIdsComponentState } from '@/object-record/record-table/states/tableRowIdsComponentState';
|
||||||
|
import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect';
|
||||||
|
import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useClickOutsideListener';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
|
import { useRef } from 'react';
|
||||||
|
|
||||||
const StyledTable = styled.table`
|
const StyledTable = styled.table`
|
||||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||||
@@ -32,11 +38,17 @@ export const RecordTable = ({
|
|||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
onColumnsChange,
|
onColumnsChange,
|
||||||
}: RecordTableProps) => {
|
}: RecordTableProps) => {
|
||||||
|
const tableBodyRef = useRef<HTMLTableElement>(null);
|
||||||
|
|
||||||
const isRecordTableInitialLoading = useRecoilComponentValueV2(
|
const isRecordTableInitialLoading = useRecoilComponentValueV2(
|
||||||
isRecordTableInitialLoadingComponentState,
|
isRecordTableInitialLoadingComponentState,
|
||||||
recordTableId,
|
recordTableId,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { toggleClickOutsideListener } = useClickOutsideListener(
|
||||||
|
RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID,
|
||||||
|
);
|
||||||
|
|
||||||
const tableRowIds = useRecoilComponentValueV2(
|
const tableRowIds = useRecoilComponentValueV2(
|
||||||
tableRowIdsComponentState,
|
tableRowIdsComponentState,
|
||||||
recordTableId,
|
recordTableId,
|
||||||
@@ -47,6 +59,10 @@ export const RecordTable = ({
|
|||||||
recordTableId,
|
recordTableId,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { resetTableRowSelection, setRowSelected } = useRecordTable({
|
||||||
|
recordTableId,
|
||||||
|
});
|
||||||
|
|
||||||
const recordTableIsEmpty =
|
const recordTableIsEmpty =
|
||||||
!isRecordTableInitialLoading &&
|
!isRecordTableInitialLoading &&
|
||||||
tableRowIds.length === 0 &&
|
tableRowIds.length === 0 &&
|
||||||
@@ -67,15 +83,32 @@ export const RecordTable = ({
|
|||||||
viewBarId={viewBarId}
|
viewBarId={viewBarId}
|
||||||
>
|
>
|
||||||
<RecordTableBodyEffect />
|
<RecordTableBodyEffect />
|
||||||
|
<RecordTableBodyUnselectEffect
|
||||||
|
tableBodyRef={tableBodyRef}
|
||||||
|
recordTableId={recordTableId}
|
||||||
|
/>
|
||||||
{recordTableIsEmpty ? (
|
{recordTableIsEmpty ? (
|
||||||
<RecordTableEmptyState />
|
<RecordTableEmptyState />
|
||||||
) : (
|
) : (
|
||||||
<StyledTable className="entity-table-cell">
|
<>
|
||||||
<RecordTableHeader
|
<StyledTable className="entity-table-cell" ref={tableBodyRef}>
|
||||||
objectMetadataNameSingular={objectNameSingular}
|
<RecordTableHeader
|
||||||
|
objectMetadataNameSingular={objectNameSingular}
|
||||||
|
/>
|
||||||
|
<RecordTableBody />
|
||||||
|
</StyledTable>
|
||||||
|
<DragSelect
|
||||||
|
dragSelectable={tableBodyRef}
|
||||||
|
onDragSelectionStart={() => {
|
||||||
|
resetTableRowSelection();
|
||||||
|
toggleClickOutsideListener(false);
|
||||||
|
}}
|
||||||
|
onDragSelectionChange={setRowSelected}
|
||||||
|
onDragSelectionEnd={() => {
|
||||||
|
toggleClickOutsideListener(true);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<RecordTableBody />
|
</>
|
||||||
</StyledTable>
|
|
||||||
)}
|
)}
|
||||||
</RecordTableContextProvider>
|
</RecordTableContextProvider>
|
||||||
</RecordTableComponentInstance>
|
</RecordTableComponentInstance>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { useRef } from 'react';
|
|
||||||
import { useRecoilCallback } from 'recoil';
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
||||||
@@ -7,15 +6,11 @@ import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'
|
|||||||
import { RecordTable } from '@/object-record/record-table/components/RecordTable';
|
import { RecordTable } from '@/object-record/record-table/components/RecordTable';
|
||||||
import { EntityDeleteContext } from '@/object-record/record-table/contexts/EntityDeleteHookContext';
|
import { EntityDeleteContext } from '@/object-record/record-table/contexts/EntityDeleteHookContext';
|
||||||
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
||||||
import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect';
|
|
||||||
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
|
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
|
||||||
import { useSaveCurrentViewFields } from '@/views/hooks/useSaveCurrentViewFields';
|
import { useSaveCurrentViewFields } from '@/views/hooks/useSaveCurrentViewFields';
|
||||||
import { mapColumnDefinitionsToViewFields } from '@/views/utils/mapColumnDefinitionToViewField';
|
import { mapColumnDefinitionsToViewFields } from '@/views/utils/mapColumnDefinitionToViewField';
|
||||||
|
|
||||||
import { RecordUpdateContext } from '../contexts/EntityUpdateMutationHookContext';
|
import { RecordUpdateContext } from '../contexts/EntityUpdateMutationHookContext';
|
||||||
import { useRecordTable } from '../hooks/useRecordTable';
|
|
||||||
|
|
||||||
import { RecordTableInternalEffect } from './RecordTableInternalEffect';
|
|
||||||
|
|
||||||
const StyledTableWithHeader = styled.div`
|
const StyledTableWithHeader = styled.div`
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@@ -45,12 +40,6 @@ export const RecordTableWithWrappers = ({
|
|||||||
recordTableId,
|
recordTableId,
|
||||||
viewBarId,
|
viewBarId,
|
||||||
}: RecordTableWithWrappersProps) => {
|
}: RecordTableWithWrappersProps) => {
|
||||||
const tableBodyRef = useRef<HTMLDivElement>(null);
|
|
||||||
|
|
||||||
const { resetTableRowSelection, setRowSelected } = useRecordTable({
|
|
||||||
recordTableId,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { saveViewFields } = useSaveCurrentViewFields(viewBarId);
|
const { saveViewFields } = useSaveCurrentViewFields(viewBarId);
|
||||||
|
|
||||||
const { deleteOneRecord } = useDeleteOneRecord({ objectNameSingular });
|
const { deleteOneRecord } = useDeleteOneRecord({ objectNameSingular });
|
||||||
@@ -72,25 +61,14 @@ export const RecordTableWithWrappers = ({
|
|||||||
<RecordUpdateContext.Provider value={updateRecordMutation}>
|
<RecordUpdateContext.Provider value={updateRecordMutation}>
|
||||||
<StyledTableWithHeader>
|
<StyledTableWithHeader>
|
||||||
<StyledTableContainer>
|
<StyledTableContainer>
|
||||||
<StyledTableInternalContainer ref={tableBodyRef}>
|
<StyledTableInternalContainer>
|
||||||
<RecordTable
|
<RecordTable
|
||||||
viewBarId={viewBarId}
|
viewBarId={viewBarId}
|
||||||
recordTableId={recordTableId}
|
recordTableId={recordTableId}
|
||||||
objectNameSingular={objectNameSingular}
|
objectNameSingular={objectNameSingular}
|
||||||
onColumnsChange={handleColumnsChange}
|
onColumnsChange={handleColumnsChange}
|
||||||
/>
|
/>
|
||||||
<DragSelect
|
|
||||||
dragSelectable={tableBodyRef}
|
|
||||||
onDragSelectionStart={() => {
|
|
||||||
resetTableRowSelection();
|
|
||||||
}}
|
|
||||||
onDragSelectionChange={setRowSelected}
|
|
||||||
/>
|
|
||||||
</StyledTableInternalContainer>
|
</StyledTableInternalContainer>
|
||||||
<RecordTableInternalEffect
|
|
||||||
tableBodyRef={tableBodyRef}
|
|
||||||
recordTableId={recordTableId}
|
|
||||||
/>
|
|
||||||
</StyledTableContainer>
|
</StyledTableContainer>
|
||||||
</StyledTableWithHeader>
|
</StyledTableWithHeader>
|
||||||
</RecordUpdateContext.Provider>
|
</RecordUpdateContext.Provider>
|
||||||
|
|||||||
@@ -7,16 +7,16 @@ import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkey
|
|||||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||||
import { useListenClickOutsideV2 } from '@/ui/utilities/pointer-event/hooks/useListenClickOutsideV2';
|
import { useListenClickOutsideV2 } from '@/ui/utilities/pointer-event/hooks/useListenClickOutsideV2';
|
||||||
|
|
||||||
type RecordTableInternalEffectProps = {
|
type RecordTableBodyUnselectEffectProps = {
|
||||||
recordTableId: string;
|
|
||||||
tableBodyRef: React.RefObject<HTMLDivElement>;
|
tableBodyRef: React.RefObject<HTMLDivElement>;
|
||||||
|
recordTableId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const RecordTableInternalEffect = ({
|
export const RecordTableBodyUnselectEffect = ({
|
||||||
recordTableId,
|
|
||||||
tableBodyRef,
|
tableBodyRef,
|
||||||
}: RecordTableInternalEffectProps) => {
|
recordTableId,
|
||||||
const leaveTableFocus = useLeaveTableFocus(recordTableId);
|
}: RecordTableBodyUnselectEffectProps) => {
|
||||||
|
const leaveTableFocus = useLeaveTableFocus();
|
||||||
|
|
||||||
const { resetTableRowSelection, useMapKeyboardToSoftFocus } = useRecordTable({
|
const { resetTableRowSelection, useMapKeyboardToSoftFocus } = useRecordTable({
|
||||||
recordTableId,
|
recordTableId,
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import { RefObject } from 'react';
|
|
||||||
import {
|
import {
|
||||||
boxesIntersect,
|
boxesIntersect,
|
||||||
useSelectionContainer,
|
useSelectionContainer,
|
||||||
} from '@air/react-drag-to-select';
|
} from '@air/react-drag-to-select';
|
||||||
import { useTheme } from '@emotion/react';
|
import { useTheme } from '@emotion/react';
|
||||||
|
import { RefObject } from 'react';
|
||||||
import { RGBA } from 'twenty-ui';
|
import { RGBA } from 'twenty-ui';
|
||||||
|
|
||||||
import { useDragSelect } from '../hooks/useDragSelect';
|
import { useDragSelect } from '../hooks/useDragSelect';
|
||||||
@@ -11,13 +11,15 @@ import { useDragSelect } from '../hooks/useDragSelect';
|
|||||||
type DragSelectProps = {
|
type DragSelectProps = {
|
||||||
dragSelectable: RefObject<HTMLElement>;
|
dragSelectable: RefObject<HTMLElement>;
|
||||||
onDragSelectionChange: (id: string, selected: boolean) => void;
|
onDragSelectionChange: (id: string, selected: boolean) => void;
|
||||||
onDragSelectionStart?: () => void;
|
onDragSelectionStart?: (event: MouseEvent) => void;
|
||||||
|
onDragSelectionEnd?: (event: MouseEvent) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DragSelect = ({
|
export const DragSelect = ({
|
||||||
dragSelectable,
|
dragSelectable,
|
||||||
onDragSelectionChange,
|
onDragSelectionChange,
|
||||||
onDragSelectionStart,
|
onDragSelectionStart,
|
||||||
|
onDragSelectionEnd,
|
||||||
}: DragSelectProps) => {
|
}: DragSelectProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { isDragSelectionStartEnabled } = useDragSelect();
|
const { isDragSelectionStartEnabled } = useDragSelect();
|
||||||
@@ -37,6 +39,7 @@ export const DragSelect = ({
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
onSelectionStart: onDragSelectionStart,
|
onSelectionStart: onDragSelectionStart,
|
||||||
|
onSelectionEnd: onDragSelectionEnd,
|
||||||
onSelectionChange: (box) => {
|
onSelectionChange: (box) => {
|
||||||
const scrollAwareBox = {
|
const scrollAwareBox = {
|
||||||
...box,
|
...box,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { clickOutsideListenerCallbacksComponentState } from '@/ui/utilities/pointer-event/states/clickOutsideListenerCallbacksComponentState';
|
import { clickOutsideListenerCallbacksComponentState } from '@/ui/utilities/pointer-event/states/clickOutsideListenerCallbacksComponentState';
|
||||||
import { clickOutsideListenerIsActivatedComponentState } from '@/ui/utilities/pointer-event/states/clickOutsideListenerIsActivatedComponentState';
|
import { clickOutsideListenerIsActivatedComponentState } from '@/ui/utilities/pointer-event/states/clickOutsideListenerIsActivatedComponentState';
|
||||||
import { clickOutsideListenerIsMouseDownInsideComponentState } from '@/ui/utilities/pointer-event/states/clickOutsideListenerIsMouseDownInsideComponentState';
|
import { clickOutsideListenerIsMouseDownInsideComponentState } from '@/ui/utilities/pointer-event/states/clickOutsideListenerIsMouseDownInsideComponentState';
|
||||||
import { lockedListenerIdState } from '@/ui/utilities/pointer-event/states/lockedListenerIdState';
|
import { clickOutsideListenerMouseDownHappenedComponentState } from '@/ui/utilities/pointer-event/states/clickOutsideListenerMouseDownHappenedComponentState';
|
||||||
import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId';
|
import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId';
|
||||||
import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState';
|
import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState';
|
||||||
|
|
||||||
@@ -22,6 +22,9 @@ export const useClickOustideListenerStates = (componentId: string) => {
|
|||||||
clickOutsideListenerIsActivatedComponentState,
|
clickOutsideListenerIsActivatedComponentState,
|
||||||
scopeId,
|
scopeId,
|
||||||
),
|
),
|
||||||
lockedListenerIdState,
|
getClickOutsideListenerMouseDownHappenedState: extractComponentState(
|
||||||
|
clickOutsideListenerMouseDownHappenedComponentState,
|
||||||
|
scopeId,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,17 +7,14 @@ import {
|
|||||||
useListenClickOutsideV2,
|
useListenClickOutsideV2,
|
||||||
} from '@/ui/utilities/pointer-event/hooks/useListenClickOutsideV2';
|
} from '@/ui/utilities/pointer-event/hooks/useListenClickOutsideV2';
|
||||||
import { ClickOutsideListenerCallback } from '@/ui/utilities/pointer-event/types/ClickOutsideListenerCallback';
|
import { ClickOutsideListenerCallback } from '@/ui/utilities/pointer-event/types/ClickOutsideListenerCallback';
|
||||||
import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId';
|
|
||||||
import { toSpliced } from '~/utils/array/toSpliced';
|
import { toSpliced } from '~/utils/array/toSpliced';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
export const useClickOutsideListener = (componentId: string) => {
|
export const useClickOutsideListener = (componentId: string) => {
|
||||||
// TODO: improve typing
|
|
||||||
const scopeId = getScopeIdFromComponentId(componentId) ?? '';
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
getClickOutsideListenerIsActivatedState,
|
getClickOutsideListenerIsActivatedState,
|
||||||
getClickOutsideListenerCallbacksState,
|
getClickOutsideListenerCallbacksState,
|
||||||
|
getClickOutsideListenerMouseDownHappenedState,
|
||||||
} = useClickOustideListenerStates(componentId);
|
} = useClickOustideListenerStates(componentId);
|
||||||
|
|
||||||
const useListenClickOutside = <T extends Element>({
|
const useListenClickOutside = <T extends Element>({
|
||||||
@@ -53,8 +50,15 @@ export const useClickOutsideListener = (componentId: string) => {
|
|||||||
({ set }) =>
|
({ set }) =>
|
||||||
(activated: boolean) => {
|
(activated: boolean) => {
|
||||||
set(getClickOutsideListenerIsActivatedState, activated);
|
set(getClickOutsideListenerIsActivatedState, activated);
|
||||||
|
|
||||||
|
if (!activated) {
|
||||||
|
set(getClickOutsideListenerMouseDownHappenedState, false);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[getClickOutsideListenerIsActivatedState],
|
[
|
||||||
|
getClickOutsideListenerIsActivatedState,
|
||||||
|
getClickOutsideListenerMouseDownHappenedState,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
const registerOnClickOutsideCallback = useRecoilCallback(
|
const registerOnClickOutsideCallback = useRecoilCallback(
|
||||||
@@ -148,7 +152,6 @@ export const useClickOutsideListener = (componentId: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
scopeId,
|
|
||||||
useListenClickOutside,
|
useListenClickOutside,
|
||||||
toggleClickOutsideListener,
|
toggleClickOutsideListener,
|
||||||
useRegisterClickOutsideListenerCallback,
|
useRegisterClickOutsideListenerCallback,
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ export const useListenClickOutsideV2 = <T extends Element>({
|
|||||||
const {
|
const {
|
||||||
getClickOutsideListenerIsMouseDownInsideState,
|
getClickOutsideListenerIsMouseDownInsideState,
|
||||||
getClickOutsideListenerIsActivatedState,
|
getClickOutsideListenerIsActivatedState,
|
||||||
|
getClickOutsideListenerMouseDownHappenedState,
|
||||||
} = useClickOustideListenerStates(listenerId);
|
} = useClickOustideListenerStates(listenerId);
|
||||||
|
|
||||||
const handleMouseDown = useRecoilCallback(
|
const handleMouseDown = useRecoilCallback(
|
||||||
@@ -37,6 +38,8 @@ export const useListenClickOutsideV2 = <T extends Element>({
|
|||||||
.getLoadable(getClickOutsideListenerIsActivatedState)
|
.getLoadable(getClickOutsideListenerIsActivatedState)
|
||||||
.getValue();
|
.getValue();
|
||||||
|
|
||||||
|
set(getClickOutsideListenerMouseDownHappenedState, true);
|
||||||
|
|
||||||
const isListening = clickOutsideListenerIsActivated && enabled;
|
const isListening = clickOutsideListenerIsActivated && enabled;
|
||||||
|
|
||||||
if (!isListening) {
|
if (!isListening) {
|
||||||
@@ -92,21 +95,32 @@ export const useListenClickOutsideV2 = <T extends Element>({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
|
getClickOutsideListenerIsActivatedState,
|
||||||
|
enabled,
|
||||||
mode,
|
mode,
|
||||||
refs,
|
refs,
|
||||||
getClickOutsideListenerIsMouseDownInsideState,
|
getClickOutsideListenerIsMouseDownInsideState,
|
||||||
enabled,
|
getClickOutsideListenerMouseDownHappenedState,
|
||||||
getClickOutsideListenerIsActivatedState,
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleClickOutside = useRecoilCallback(
|
const handleClickOutside = useRecoilCallback(
|
||||||
({ snapshot }) =>
|
({ snapshot }) =>
|
||||||
(event: MouseEvent | TouchEvent) => {
|
(event: MouseEvent | TouchEvent) => {
|
||||||
|
const clickOutsideListenerIsActivated = snapshot
|
||||||
|
.getLoadable(getClickOutsideListenerIsActivatedState)
|
||||||
|
.getValue();
|
||||||
|
|
||||||
|
const isListening = clickOutsideListenerIsActivated && enabled;
|
||||||
|
|
||||||
const isMouseDownInside = snapshot
|
const isMouseDownInside = snapshot
|
||||||
.getLoadable(getClickOutsideListenerIsMouseDownInsideState)
|
.getLoadable(getClickOutsideListenerIsMouseDownInsideState)
|
||||||
.getValue();
|
.getValue();
|
||||||
|
|
||||||
|
const hasMouseDownHappened = snapshot
|
||||||
|
.getLoadable(getClickOutsideListenerMouseDownHappenedState)
|
||||||
|
.getValue();
|
||||||
|
|
||||||
if (mode === ClickOutsideMode.compareHTMLRef) {
|
if (mode === ClickOutsideMode.compareHTMLRef) {
|
||||||
const clickedElement = event.target as HTMLElement;
|
const clickedElement = event.target as HTMLElement;
|
||||||
let isClickedOnExcluded = false;
|
let isClickedOnExcluded = false;
|
||||||
@@ -132,6 +146,8 @@ export const useListenClickOutsideV2 = <T extends Element>({
|
|||||||
.some((ref) => ref.current?.contains(event.target as Node));
|
.some((ref) => ref.current?.contains(event.target as Node));
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
isListening &&
|
||||||
|
hasMouseDownHappened &&
|
||||||
!clickedOnAtLeastOneRef &&
|
!clickedOnAtLeastOneRef &&
|
||||||
!isMouseDownInside &&
|
!isMouseDownInside &&
|
||||||
!isClickedOnExcluded
|
!isClickedOnExcluded
|
||||||
@@ -171,13 +187,21 @@ export const useListenClickOutsideV2 = <T extends Element>({
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!clickedOnAtLeastOneRef && !isMouseDownInside) {
|
if (
|
||||||
|
!clickedOnAtLeastOneRef &&
|
||||||
|
!isMouseDownInside &&
|
||||||
|
isListening &&
|
||||||
|
hasMouseDownHappened
|
||||||
|
) {
|
||||||
callback(event);
|
callback(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
|
getClickOutsideListenerIsActivatedState,
|
||||||
|
enabled,
|
||||||
getClickOutsideListenerIsMouseDownInsideState,
|
getClickOutsideListenerIsMouseDownInsideState,
|
||||||
|
getClickOutsideListenerMouseDownHappenedState,
|
||||||
mode,
|
mode,
|
||||||
refs,
|
refs,
|
||||||
excludeClassNames,
|
excludeClassNames,
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||||
|
|
||||||
|
export const clickOutsideListenerMouseDownHappenedComponentState =
|
||||||
|
createComponentState<boolean>({
|
||||||
|
key: 'clickOutsideListenerMouseDownHappenedComponentState',
|
||||||
|
defaultValue: false,
|
||||||
|
});
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import { createState } from 'twenty-ui';
|
|
||||||
|
|
||||||
export const lockedListenerIdState = createState<string | null>({
|
|
||||||
key: 'lockedListenerIdState',
|
|
||||||
defaultValue: null,
|
|
||||||
});
|
|
||||||
Reference in New Issue
Block a user