From 52078365662893cd1b4d3a408148fa24185ee296 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Bosi?= <71827178+bosiraphael@users.noreply.github.com> Date: Fri, 25 Apr 2025 16:50:48 +0200 Subject: [PATCH] Table hover and click outside fixes (#11737) # Table hover and click outside fixes This PR improves table interaction behavior by refining cell hover states and click-outside handling in the record table component. ## Changes ### Click Outside Handling - Removed conditional rendering of `RecordTableBodyFocusClickOutsideEffect` ### Hover State Management Implemented hover state cleanup in multiple components: - Added `recordTableHoverPosition` state reset in `useLeaveTableFocus` hook - Integrated mouse leave handler in `RecordTableBodyDroppable` to clear hover position ### Fixes double focus bug - Reset the focus and the hover when the table data changes ## Videos ### Before https://github.com/user-attachments/assets/f815b65c-c545-4841-a0d9-04c58771e69f ### After https://github.com/user-attachments/assets/046cc7df-18b8-46ca-b2cc-bdfa3125311b --- .../RecordTableBodyEffectsWrapper.tsx | 4 +--- .../hooks/internal/useLeaveTableFocus.ts | 9 +++++++++ .../hooks/internal/useSetRecordTableData.ts | 18 +++++++++++++++++- .../components/RecordTableBodyDroppable.tsx | 7 +++++++ 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableBodyEffectsWrapper.tsx b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableBodyEffectsWrapper.tsx index 6b616ea47..ce3715550 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableBodyEffectsWrapper.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableBodyEffectsWrapper.tsx @@ -33,9 +33,7 @@ export const RecordTableBodyEffectsWrapper = ({ )} {isAtLeastOneRecordSelected && } {isRecordTableFocusActive && } - {isRecordTableFocusActive && ( - - )} + ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useLeaveTableFocus.ts b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useLeaveTableFocus.ts index 61f627e0b..8f3ad47a8 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useLeaveTableFocus.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useLeaveTableFocus.ts @@ -1,7 +1,9 @@ import { useResetTableRowSelection } from '@/object-record/record-table/hooks/internal/useResetTableRowSelection'; import { useSetIsRecordTableFocusActive } from '@/object-record/record-table/record-table-cell/hooks/useSetIsRecordTableFocusActive'; import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; +import { recordTableHoverPositionComponentState } from '@/object-record/record-table/states/recordTableHoverPositionComponentState'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; export const useLeaveTableFocus = (recordTableId?: string) => { const recordTableIdFromContext = useAvailableComponentInstanceIdOrThrow( @@ -17,9 +19,16 @@ export const useLeaveTableFocus = (recordTableId?: string) => { recordTableIdFromContext, ); + const setRecordTableHoverPosition = useSetRecoilComponentStateV2( + recordTableHoverPositionComponentState, + recordTableIdFromContext, + ); + return () => { resetTableRowSelection(); setIsFocusActiveForCurrentPosition(false); + + setRecordTableHoverPosition(null); }; }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSetRecordTableData.ts b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSetRecordTableData.ts index 2e472f4f4..db22c3357 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSetRecordTableData.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSetRecordTableData.ts @@ -3,13 +3,16 @@ import { useRecoilCallback } from 'recoil'; import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState'; import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; +import { useSetIsRecordTableFocusActive } from '@/object-record/record-table/record-table-cell/hooks/useSetIsRecordTableFocusActive'; import { hasUserSelectedAllRowsComponentState } from '@/object-record/record-table/record-table-row/states/hasUserSelectedAllRowsFamilyState'; import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState'; +import { recordTableHoverPositionComponentState } from '@/object-record/record-table/states/recordTableHoverPositionComponentState'; import { ObjectRecord } from '@/object-record/types/ObjectRecord'; import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; -import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { isDefined } from 'twenty-shared/utils'; +import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; type useSetRecordTableDataProps = { recordTableId?: string; @@ -44,6 +47,14 @@ export const useSetRecordTableData = ({ recordTableId, ); + const { setIsFocusActiveForCurrentPosition } = + useSetIsRecordTableFocusActive(recordTableId); + + const setRecordTableHoverPosition = useSetRecoilComponentStateV2( + recordTableHoverPositionComponentState, + recordTableId, + ); + return useRecoilCallback( ({ set, snapshot }) => ({ @@ -84,6 +95,9 @@ export const useSetRecordTableData = ({ const recordIds = records.map((record) => record.id); if (!isDeeplyEqual(currentRowIds, recordIds)) { + setIsFocusActiveForCurrentPosition(false); + setRecordTableHoverPosition(null); + if (hasUserSelectedAllRows) { for (const rowId of recordIds) { set(isRowSelectedFamilyState(rowId), true); @@ -106,6 +120,8 @@ export const useSetRecordTableData = ({ recordIndexRecordIdsByGroupFamilyState, recordIndexAllRecordIdsSelector, hasUserSelectedAllRowsState, + setIsFocusActiveForCurrentPosition, + setRecordTableHoverPosition, onEntityCountChange, isRowSelectedFamilyState, ], diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyDroppable.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyDroppable.tsx index 8e731aaf5..a113a4c1f 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyDroppable.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyDroppable.tsx @@ -1,4 +1,6 @@ import { RecordTableBody } from '@/object-record/record-table/record-table-body/components/RecordTableBody'; +import { recordTableHoverPositionComponentState } from '@/object-record/record-table/states/recordTableHoverPositionComponentState'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { Droppable } from '@hello-pangea/dnd'; import { ReactNode, useState } from 'react'; import { v4 } from 'uuid'; @@ -17,6 +19,10 @@ export const RecordTableBodyDroppable = ({ const [v4Persistable] = useState(v4()); const recordTableBodyId = `record-table-body${recordGroupId ? '-' + recordGroupId : ''}`; + const setRecordTableHoverPosition = useSetRecoilComponentStateV2( + recordTableHoverPositionComponentState, + ); + return ( setRecordTableHoverPosition(null)} > {children} {provided.placeholder}