From dd0ea2366ffcdd64afb27f6d5575819dbb1ec7d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Bosi?= <71827178+bosiraphael@users.noreply.github.com> Date: Thu, 17 Apr 2025 18:40:24 +0200 Subject: [PATCH] 839 Table focus refactoring (#11629) # Table Focus Refactoring This pull request implements the table focus refactoring requested in [#839](https://github.com/twentyhq/core-team-issues/issues/839), dissociating hover and focus behaviors in the table component to improve keyboard navigation. ## Technical Implementation - Created separate component states to handle focus and hover independently. - Updated all relevant hooks and functions to use the new focus mechanism. - Removed deprecated states and hooks. - Introduced dedicated portal components to improve the table performance (the table cells are much simpler and the more complex logic is handled via the portals) ## Key Behavior Changes - Performance improvements - Focus is now handled exclusively with keyboard navigation - Clicking on a cell or inline-cell now sets the focus to that cell - Hover state is managed separately from focus, improving user experience and accessibility - The table scrolls when the focused cell changes ## Video https://github.com/user-attachments/assets/9966beac-3b0f-4433-a87a-299506d83353 --- .../record-table/components/RecordTable.tsx | 9 + .../RecordTableBodyEffectsWrapper.tsx | 18 +- ...cordTableFocusModeHotkeysSetterEffect.tsx} | 33 +--- ...dTableNoRecordGroupBodyContextProvider.tsx | 13 +- ...ordTableRecordGroupBodyContextProvider.tsx | 13 +- ...ecordTableScrollToFocusedElementEffect.tsx | 49 ++++++ .../perf/RecordTableCell.perf.stories.tsx | 5 +- .../constants/FocusClickOutsideListenerId.ts | 2 + .../SoftFocusClickOutsideListenerId.ts | 2 - .../contexts/RecordTableBodyContext.ts | 2 +- .../contexts/RecordTableCellContext.ts | 3 - .../useCloseCurrentTableCellInEditMode.ts | 28 +--- .../hooks/internal/useDisableSoftFocus.ts | 42 ----- .../internal/useGetIsSomeCellInEditMode.ts | 35 ---- .../internal/useHandleContainerMouseEnter.ts | 51 ++---- .../hooks/internal/useLeaveTableFocus.ts | 40 +---- .../internal/useMoveEditModeToCellPosition.ts | 41 ----- .../useSetRecordTableFocusPosition.ts | 28 ++++ .../hooks/internal/useSetSoftFocusPosition.ts | 48 ------ .../record-table/hooks/useRecordTable.ts | 44 ++--- .../hooks/useRecordTableMoveFocus.ts | 78 ++++----- ...ecordTableBodyFocusClickOutsideEffect.tsx} | 6 +- ...=> RecordTableBodyFocusKeyboardEffect.tsx} | 6 +- .../RecordTableNoRecordGroupBody.tsx | 2 + .../RecordTableRecordGroupsBody.tsx | 2 + .../components/RecordTableCell.tsx | 6 +- .../RecordTableCellBaseContainer.tsx | 44 ++--- .../components/RecordTableCellContainer.tsx | 22 +-- .../RecordTableCellDisplayContainer.tsx | 6 +- .../components/RecordTableCellDisplayMode.tsx | 36 ++-- .../components/RecordTableCellEditButton.tsx | 4 +- .../components/RecordTableCellEditMode.tsx | 54 ++++-- .../RecordTableCellEditModePortal.tsx | 44 +++++ .../RecordTableCellHoveredPortal.tsx | 96 +++++++++++ .../RecordTableCellPortalWrapper.tsx | 66 ++++++++ .../components/RecordTableCellPortals.tsx | 22 +++ .../components/RecordTableCellWrapper.tsx | 16 -- .../record-table-cell/hooks/__mocks__/cell.ts | 3 - .../__tests__/useCurrentCellPosition.test.tsx | 37 ----- .../useCurrentTableCellEditMode.test.tsx | 50 ------ .../useIsSoftFocusOnCurrentTableCell.test.tsx | 42 ----- .../useMoveHoverToCurrentCell.test.tsx | 80 +++++++++ ...MoveSoftFocusToCurrentCellOnHover.test.tsx | 147 ----------------- .../useSelectedTableCellEditMode.test.tsx | 63 ------- .../useSetIsRecordTableFocusActive.test.tsx | 155 ++++++++++++++++++ .../useCloseRecordTableCellInGroup.test.tsx | 18 +- .../useCloseRecordTableCellNoGroup.test.tsx | 19 +-- .../useCloseRecordTableCellInGroup.ts | 6 +- .../useCloseRecordTableCellNoGroup.ts | 6 +- .../hooks/useCurrentCellPosition.ts | 9 - .../hooks/useCurrentTableCellEditMode.ts | 27 --- .../hooks/useIsSoftFocusOnCurrentTableCell.ts | 14 -- .../hooks/useMoveHoverToCurrentCell.ts | 55 +++++++ .../useMoveSoftFocusToCurrentCellOnHover.ts | 68 -------- .../hooks/useOpenRecordTableCellFromCell.ts | 9 +- .../hooks/useOpenRecordTableCellV2.ts | 18 +- .../hooks/useSelectedTableCellEditMode.ts | 21 --- .../hooks/useSetIsRecordTableFocusActive.ts | 51 ++++++ .../hooks/useSetSoftFocus.ts | 39 ----- .../useSetSoftFocusOnCurrentTableCell.ts | 13 -- ...isRecordTableFocusActiveComponentState.ts} | 11 +- .../states/isSoftFocusActiveComponentState.ts | 8 - ...oftFocusOnTableCellComponentFamilyState.ts | 10 -- .../states/isSoftFocusUsingMouseState.ts | 5 - ...TableCellInEditModeComponentFamilyState.ts | 10 -- ...ableCellEditModePositionComponentState.ts} | 11 +- ...recordTableFocusPositionComponentState.ts} | 4 +- .../recordTableHoverPositionComponentState.ts | 10 ++ .../isSomeCellInEditModeComponentSelector.ts | 20 +++ .../tableRecordGroupIdsComponentState.ts | 11 -- .../record-table/types/TableHotkeyScope.ts | 2 +- .../decorators/RecordTableDecorator.tsx | 2 +- 72 files changed, 920 insertions(+), 1150 deletions(-) rename packages/twenty-front/src/modules/object-record/record-table/{record-table-cell/components/RecordTableCellSoftFocusModeHotkeysSetterEffect.tsx => components/RecordTableFocusModeHotkeysSetterEffect.tsx} (66%) create mode 100644 packages/twenty-front/src/modules/object-record/record-table/components/RecordTableScrollToFocusedElementEffect.tsx create mode 100644 packages/twenty-front/src/modules/object-record/record-table/constants/FocusClickOutsideListenerId.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-table/constants/SoftFocusClickOutsideListenerId.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useDisableSoftFocus.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useGetIsSomeCellInEditMode.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useMoveEditModeToCellPosition.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSetRecordTableFocusPosition.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSetSoftFocusPosition.ts rename packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/{RecordTableBodySoftFocusClickOutsideEffect.tsx => RecordTableBodyFocusClickOutsideEffect.tsx} (84%) rename packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/{RecordTableBodySoftFocusKeyboardEffect.tsx => RecordTableBodyFocusKeyboardEffect.tsx} (66%) create mode 100644 packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellEditModePortal.tsx create mode 100644 packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellHoveredPortal.tsx create mode 100644 packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellPortalWrapper.tsx create mode 100644 packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellPortals.tsx delete mode 100644 packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useCurrentCellPosition.test.tsx delete mode 100644 packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useCurrentTableCellEditMode.test.tsx delete mode 100644 packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useIsSoftFocusOnCurrentTableCell.test.tsx create mode 100644 packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useMoveHoverToCurrentCell.test.tsx delete mode 100644 packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useMoveSoftFocusToCurrentCellOnHover.test.tsx delete mode 100644 packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useSelectedTableCellEditMode.test.tsx create mode 100644 packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useSetIsRecordTableFocusActive.test.tsx delete mode 100644 packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useCurrentCellPosition.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useCurrentTableCellEditMode.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useIsSoftFocusOnCurrentTableCell.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useMoveHoverToCurrentCell.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useMoveSoftFocusToCurrentCellOnHover.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useSelectedTableCellEditMode.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useSetIsRecordTableFocusActive.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useSetSoftFocus.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useSetSoftFocusOnCurrentTableCell.ts rename packages/twenty-front/src/modules/object-record/record-table/states/{tableCellWidthsComponentState.ts => isRecordTableFocusActiveComponentState.ts} (50%) delete mode 100644 packages/twenty-front/src/modules/object-record/record-table/states/isSoftFocusActiveComponentState.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-table/states/isSoftFocusOnTableCellComponentFamilyState.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-table/states/isSoftFocusUsingMouseState.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-table/states/isTableCellInEditModeComponentFamilyState.ts rename packages/twenty-front/src/modules/object-record/record-table/states/{currentTableCellInEditModePositionComponentState.ts => recordTableCellEditModePositionComponentState.ts} (62%) rename packages/twenty-front/src/modules/object-record/record-table/states/{softFocusPositionComponentState.ts => recordTableFocusPositionComponentState.ts} (82%) create mode 100644 packages/twenty-front/src/modules/object-record/record-table/states/recordTableHoverPositionComponentState.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-table/states/selectors/isSomeCellInEditModeComponentSelector.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-table/states/tableRecordGroupIdsComponentState.ts diff --git a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTable.tsx b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTable.tsx index 8f5627ac6..621e5b068 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTable.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTable.tsx @@ -6,9 +6,11 @@ import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record import { RecordTableBodyEffectsWrapper } from '@/object-record/record-table/components/RecordTableBodyEffectsWrapper'; import { RecordTableContent } from '@/object-record/record-table/components/RecordTableContent'; import { RecordTableEmpty } from '@/object-record/record-table/components/RecordTableEmpty'; +import { RecordTableScrollToFocusedElementEffect } from '@/object-record/record-table/components/RecordTableScrollToFocusedElementEffect'; import { RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-table/constants/RecordTableClickOutsideListenerId'; import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext'; import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable'; +import { isRecordTableFocusActiveComponentState } from '@/object-record/record-table/states/isRecordTableFocusActiveComponentState'; import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState'; import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useClickOutsideListener'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; @@ -44,6 +46,11 @@ export const RecordTable = () => { const recordTableIsEmpty = !isRecordTableInitialLoading && allRecordIds.length === 0; + const isRecordTableFocusActive = useRecoilComponentValueV2( + isRecordTableFocusActiveComponentState, + recordTableId, + ); + if (!isNonEmptyString(objectNameSingular)) { return <>; } @@ -64,6 +71,8 @@ export const RecordTable = () => { tableBodyRef={tableBodyRef} /> + {isRecordTableFocusActive && } + {recordTableIsEmpty && !hasRecordGroups ? ( )} {isAtLeastOneRecordSelected && } - {isSoftFocusActiveState && } - {isSoftFocusActiveState && ( - + {isRecordTableFocusActive && } + {isRecordTableFocusActive && ( + )} ); diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellSoftFocusModeHotkeysSetterEffect.tsx b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableFocusModeHotkeysSetterEffect.tsx similarity index 66% rename from packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellSoftFocusModeHotkeysSetterEffect.tsx rename to packages/twenty-front/src/modules/object-record/record-table/components/RecordTableFocusModeHotkeysSetterEffect.tsx index 7503a8e1c..2e09b9841 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellSoftFocusModeHotkeysSetterEffect.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableFocusModeHotkeysSetterEffect.tsx @@ -1,5 +1,4 @@ -import { useContext, useEffect, useRef } from 'react'; -import { useRecoilValue } from 'recoil'; +import { useContext } from 'react'; import { Key } from 'ts-key-enum'; import { useClearField } from '@/object-record/record-field/hooks/useClearField'; @@ -7,17 +6,13 @@ import { useIsFieldClearable } from '@/object-record/record-field/hooks/useIsFie import { useIsFieldInputOnly } from '@/object-record/record-field/hooks/useIsFieldInputOnly'; import { useToggleEditOnlyInput } from '@/object-record/record-field/hooks/useToggleEditOnlyInput'; import { useOpenRecordTableCellFromCell } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellFromCell'; -import { isSoftFocusUsingMouseState } from '@/object-record/record-table/states/isSoftFocusUsingMouseState'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { isNonTextWritingKey } from '@/ui/utilities/hotkey/utils/isNonTextWritingKey'; import { FieldContext } from '@/object-record/record-field/contexts/FieldContext'; -import { currentHotkeyScopeState } from '@/ui/utilities/hotkey/states/internal/currentHotkeyScopeState'; -import { TableHotkeyScope } from '../../types/TableHotkeyScope'; - -export const RecordTableCellSoftFocusModeHotkeysSetterEffect = () => { - const currentHotkeyScope = useRecoilValue(currentHotkeyScopeState); +import { TableHotkeyScope } from '../types/TableHotkeyScope'; +export const RecordTableFocusModeHotkeysSetterEffect = () => { const { openTableCell } = useOpenRecordTableCellFromCell(); const { isReadOnly } = useContext(FieldContext); @@ -26,21 +21,9 @@ export const RecordTableCellSoftFocusModeHotkeysSetterEffect = () => { const isFieldClearable = useIsFieldClearable(); const toggleEditOnlyInput = useToggleEditOnlyInput(); - const scrollRef = useRef(null); - const isSoftFocusUsingMouse = useRecoilValue(isSoftFocusUsingMouseState); const clearField = useClearField(); - useEffect(() => { - if (currentHotkeyScope.scope !== TableHotkeyScope.TableSoftFocus) { - return; - } - - if (!isSoftFocusUsingMouse) { - scrollRef.current?.scrollIntoView({ block: 'nearest' }); - } - }, [currentHotkeyScope.scope, isSoftFocusUsingMouse]); - useScopedHotkeys( [Key.Backspace, Key.Delete], () => { @@ -48,7 +31,7 @@ export const RecordTableCellSoftFocusModeHotkeysSetterEffect = () => { clearField(); } }, - TableHotkeyScope.TableSoftFocus, + TableHotkeyScope.TableFocus, [clearField, isFieldClearable, isFieldInputOnly], ); @@ -65,8 +48,8 @@ export const RecordTableCellSoftFocusModeHotkeysSetterEffect = () => { toggleEditOnlyInput(); } }, - TableHotkeyScope.TableSoftFocus, - [openTableCell], + TableHotkeyScope.TableFocus, + [openTableCell, isFieldInputOnly, toggleEditOnlyInput, isReadOnly], ); useScopedHotkeys( @@ -93,8 +76,8 @@ export const RecordTableCellSoftFocusModeHotkeysSetterEffect = () => { openTableCell(keyboardEvent.key); } }, - TableHotkeyScope.TableSoftFocus, - [openTableCell], + TableHotkeyScope.TableFocus, + [openTableCell, isFieldInputOnly, toggleEditOnlyInput, isReadOnly], { preventDefault: false, }, diff --git a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableNoRecordGroupBodyContextProvider.tsx b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableNoRecordGroupBodyContextProvider.tsx index 7d57d626e..4278883a8 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableNoRecordGroupBodyContextProvider.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableNoRecordGroupBodyContextProvider.tsx @@ -3,7 +3,7 @@ import { useRecordTableContextOrThrow } from '@/object-record/record-table/conte import { useHandleContainerMouseEnter } from '@/object-record/record-table/hooks/internal/useHandleContainerMouseEnter'; import { useRecordTableMoveFocus } from '@/object-record/record-table/hooks/useRecordTableMoveFocus'; import { useCloseRecordTableCellNoGroup } from '@/object-record/record-table/record-table-cell/hooks/internal/useCloseRecordTableCellNoGroup'; -import { useMoveSoftFocusToCurrentCellOnHover } from '@/object-record/record-table/record-table-cell/hooks/useMoveSoftFocusToCurrentCellOnHover'; +import { useMoveHoverToCurrentCell } from '@/object-record/record-table/record-table-cell/hooks/useMoveHoverToCurrentCell'; import { OpenTableCellArgs, useOpenRecordTableCellV2, @@ -40,13 +40,10 @@ export const RecordTableNoRecordGroupBodyContextProvider = ({ closeTableCellNoGroup(); }; - const { moveSoftFocusToCurrentCell } = - useMoveSoftFocusToCurrentCellOnHover(recordTableId); + const { moveHoverToCurrentCell } = useMoveHoverToCurrentCell(recordTableId); - const handleMoveSoftFocusToCurrentCell = ( - cellPosition: TableCellPosition, - ) => { - moveSoftFocusToCurrentCell(cellPosition); + const handleMoveHoverToCurrentCell = (cellPosition: TableCellPosition) => { + moveHoverToCurrentCell(cellPosition); }; const { triggerActionMenuDropdown } = useTriggerActionMenuDropdown({ @@ -70,7 +67,7 @@ export const RecordTableNoRecordGroupBodyContextProvider = ({ onOpenTableCell: handleOpenTableCell, onMoveFocus: handleMoveFocus, onCloseTableCell: handleCloseTableCell, - onMoveSoftFocusToCurrentCell: handleMoveSoftFocusToCurrentCell, + onMoveHoverToCurrentCell: handleMoveHoverToCurrentCell, onActionMenuDropdownOpened: handleActionMenuDropdown, onCellMouseEnter: handleContainerMouseEnter, }} diff --git a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableRecordGroupBodyContextProvider.tsx b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableRecordGroupBodyContextProvider.tsx index f65702193..8151628f5 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableRecordGroupBodyContextProvider.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableRecordGroupBodyContextProvider.tsx @@ -3,7 +3,7 @@ import { useRecordTableContextOrThrow } from '@/object-record/record-table/conte import { useHandleContainerMouseEnter } from '@/object-record/record-table/hooks/internal/useHandleContainerMouseEnter'; import { useRecordTableMoveFocus } from '@/object-record/record-table/hooks/useRecordTableMoveFocus'; import { useCloseRecordTableCellInGroup } from '@/object-record/record-table/record-table-cell/hooks/internal/useCloseRecordTableCellInGroup'; -import { useMoveSoftFocusToCurrentCellOnHover } from '@/object-record/record-table/record-table-cell/hooks/useMoveSoftFocusToCurrentCellOnHover'; +import { useMoveHoverToCurrentCell } from '@/object-record/record-table/record-table-cell/hooks/useMoveHoverToCurrentCell'; import { OpenTableCellArgs, useOpenRecordTableCellV2, @@ -41,13 +41,10 @@ export const RecordTableRecordGroupBodyContextProvider = ({ closeTableCellInGroup(); }; - const { moveSoftFocusToCurrentCell } = - useMoveSoftFocusToCurrentCellOnHover(recordTableId); + const { moveHoverToCurrentCell } = useMoveHoverToCurrentCell(recordTableId); - const handleMoveSoftFocusToCurrentCell = ( - cellPosition: TableCellPosition, - ) => { - moveSoftFocusToCurrentCell(cellPosition); + const handleMoveHoverToCurrentCell = (cellPosition: TableCellPosition) => { + moveHoverToCurrentCell(cellPosition); }; const { triggerActionMenuDropdown } = useTriggerActionMenuDropdown({ @@ -71,7 +68,7 @@ export const RecordTableRecordGroupBodyContextProvider = ({ onOpenTableCell: handleOpenTableCell, onMoveFocus: handleMoveFocus, onCloseTableCell: handlecloseTableCellInGroup, - onMoveSoftFocusToCurrentCell: handleMoveSoftFocusToCurrentCell, + onMoveHoverToCurrentCell: handleMoveHoverToCurrentCell, onActionMenuDropdownOpened: handleActionMenuDropdown, onCellMouseEnter: handleContainerMouseEnter, }} diff --git a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableScrollToFocusedElementEffect.tsx b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableScrollToFocusedElementEffect.tsx new file mode 100644 index 000000000..b018f500f --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableScrollToFocusedElementEffect.tsx @@ -0,0 +1,49 @@ +import { recordTableFocusPositionComponentState } from '@/object-record/record-table/states/recordTableFocusPositionComponentState'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { useEffect } from 'react'; +import { isDefined } from 'twenty-shared/utils'; + +export const RecordTableScrollToFocusedElementEffect = () => { + const focusPosition = useRecoilComponentValueV2( + recordTableFocusPositionComponentState, + ); + + useEffect(() => { + if (!focusPosition) { + return; + } + + const focusElement = document.getElementById( + `record-table-cell-${focusPosition.column}-${focusPosition.row}`, + ); + + if (!focusElement) { + return; + } + + const isSecondColumn = focusPosition.column === 1; + + if (isSecondColumn) { + const checkBoxColumnCell = document.getElementById( + `record-table-cell-0-0`, + ); + const firstColumnCell = document.getElementById(`record-table-cell-1-0`); + + if (isDefined(checkBoxColumnCell) && isDefined(firstColumnCell)) { + const checkBoxColumnWidth = checkBoxColumnCell.offsetWidth; + const firstColumnWidth = firstColumnCell.offsetWidth; + focusElement.style.scrollMarginLeft = `${checkBoxColumnWidth + firstColumnWidth}px`; + } + } + + focusElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + + return () => { + if (isDefined(focusElement)) { + focusElement.style.scrollMarginLeft = ''; + } + }; + }, [focusPosition]); + + return null; +}; diff --git a/packages/twenty-front/src/modules/object-record/record-table/components/__stories__/perf/RecordTableCell.perf.stories.tsx b/packages/twenty-front/src/modules/object-record/record-table/components/__stories__/perf/RecordTableCell.perf.stories.tsx index 63e4fcaec..5fd5335a7 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/components/__stories__/perf/RecordTableCell.perf.stories.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/components/__stories__/perf/RecordTableCell.perf.stories.tsx @@ -93,7 +93,7 @@ const meta: Meta = { onOpenTableCell: () => {}, onMoveFocus: () => {}, onCloseTableCell: () => {}, - onMoveSoftFocusToCurrentCell: () => {}, + onMoveHoverToCurrentCell: () => {}, onActionMenuDropdownOpened: () => {}, onCellMouseEnter: () => {}, }} @@ -122,10 +122,7 @@ const meta: Meta = { void; onMoveFocus: (direction: MoveFocusDirection) => void; onCloseTableCell: () => void; - onMoveSoftFocusToCurrentCell: (cellPosition: TableCellPosition) => void; + onMoveHoverToCurrentCell: (cellPosition: TableCellPosition) => void; onActionMenuDropdownOpened: ( event: React.MouseEvent, recordId: string, diff --git a/packages/twenty-front/src/modules/object-record/record-table/contexts/RecordTableCellContext.ts b/packages/twenty-front/src/modules/object-record/record-table/contexts/RecordTableCellContext.ts index 527d99019..7fc397071 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/contexts/RecordTableCellContext.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/contexts/RecordTableCellContext.ts @@ -6,9 +6,6 @@ import { TableCellPosition } from '@/object-record/record-table/types/TableCellP export type RecordTableCellContextValue = { columnDefinition: ColumnDefinition; - columnIndex: number; - isInEditMode: boolean; - hasSoftFocus: boolean; cellPosition: TableCellPosition; }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useCloseCurrentTableCellInEditMode.ts b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useCloseCurrentTableCellInEditMode.ts index 33f97d7a1..f4c1806a3 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useCloseCurrentTableCellInEditMode.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useCloseCurrentTableCellInEditMode.ts @@ -1,45 +1,27 @@ import { useRecoilCallback } from 'recoil'; -import { currentTableCellInEditModePositionComponentState } from '@/object-record/record-table/states/currentTableCellInEditModePositionComponentState'; -import { isTableCellInEditModeComponentFamilyState } from '@/object-record/record-table/states/isTableCellInEditModeComponentFamilyState'; +import { recordTableCellEditModePositionComponentState } from '@/object-record/record-table/states/recordTableCellEditModePositionComponentState'; import { useGoBackToPreviousDropdownFocusId } from '@/ui/layout/dropdown/hooks/useGoBackToPreviousDropdownFocusId'; -import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; export const useCloseCurrentTableCellInEditMode = (recordTableId?: string) => { const currentTableCellInEditModePositionState = useRecoilComponentCallbackStateV2( - currentTableCellInEditModePositionComponentState, + recordTableCellEditModePositionComponentState, recordTableId, ); - const isTableCellInEditModeFamilyState = useRecoilComponentCallbackStateV2( - isTableCellInEditModeComponentFamilyState, - recordTableId, - ); const { goBackToPreviousDropdownFocusId } = useGoBackToPreviousDropdownFocusId(); return useRecoilCallback( - ({ set, snapshot }) => { + ({ set }) => { return async () => { - const currentTableCellInEditModePosition = getSnapshotValue( - snapshot, - currentTableCellInEditModePositionState, - ); - - set( - isTableCellInEditModeFamilyState(currentTableCellInEditModePosition), - false, - ); + set(currentTableCellInEditModePositionState, null); goBackToPreviousDropdownFocusId(); }; }, - [ - currentTableCellInEditModePositionState, - isTableCellInEditModeFamilyState, - goBackToPreviousDropdownFocusId, - ], + [currentTableCellInEditModePositionState, goBackToPreviousDropdownFocusId], ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useDisableSoftFocus.ts b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useDisableSoftFocus.ts deleted file mode 100644 index 90a5819f4..000000000 --- a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useDisableSoftFocus.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { useRecoilCallback } from 'recoil'; - -import { isSoftFocusActiveComponentState } from '@/object-record/record-table/states/isSoftFocusActiveComponentState'; -import { isSoftFocusOnTableCellComponentFamilyState } from '@/object-record/record-table/states/isSoftFocusOnTableCellComponentFamilyState'; -import { softFocusPositionComponentState } from '@/object-record/record-table/states/softFocusPositionComponentState'; -import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; -import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; - -export const useDisableSoftFocus = (recordTableId?: string) => { - const softFocusPositionState = useRecoilComponentCallbackStateV2( - softFocusPositionComponentState, - recordTableId, - ); - const isSoftFocusActiveState = useRecoilComponentCallbackStateV2( - isSoftFocusActiveComponentState, - recordTableId, - ); - const isSoftFocusOnTableCellFamilyState = useRecoilComponentCallbackStateV2( - isSoftFocusOnTableCellComponentFamilyState, - recordTableId, - ); - - return useRecoilCallback( - ({ set, snapshot }) => { - return () => { - const currentPosition = getSnapshotValue( - snapshot, - softFocusPositionState, - ); - - set(isSoftFocusActiveState, false); - - set(isSoftFocusOnTableCellFamilyState(currentPosition), false); - }; - }, - [ - isSoftFocusActiveState, - softFocusPositionState, - isSoftFocusOnTableCellFamilyState, - ], - ); -}; diff --git a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useGetIsSomeCellInEditMode.ts b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useGetIsSomeCellInEditMode.ts deleted file mode 100644 index 96e1dfdbd..000000000 --- a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useGetIsSomeCellInEditMode.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { useRecoilCallback } from 'recoil'; - -import { currentTableCellInEditModePositionComponentState } from '@/object-record/record-table/states/currentTableCellInEditModePositionComponentState'; -import { isTableCellInEditModeComponentFamilyState } from '@/object-record/record-table/states/isTableCellInEditModeComponentFamilyState'; -import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; -import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; - -export const useGetIsSomeCellInEditModeState = (recordTableId?: string) => { - const currentTableCellInEditModePositionState = - useRecoilComponentCallbackStateV2( - currentTableCellInEditModePositionComponentState, - recordTableId, - ); - const isTableCellInEditModeFamilyState = useRecoilComponentCallbackStateV2( - isTableCellInEditModeComponentFamilyState, - recordTableId, - ); - - return useRecoilCallback( - ({ snapshot }) => - () => { - const currentTableCellInEditModePosition = getSnapshotValue( - snapshot, - currentTableCellInEditModePositionState, - ); - - const isSomeCellInEditModeState = isTableCellInEditModeFamilyState( - currentTableCellInEditModePosition, - ); - - return isSomeCellInEditModeState; - }, - [currentTableCellInEditModePositionState, isTableCellInEditModeFamilyState], - ); -}; diff --git a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useHandleContainerMouseEnter.ts b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useHandleContainerMouseEnter.ts index e2f0d8e91..7fb081552 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useHandleContainerMouseEnter.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useHandleContainerMouseEnter.ts @@ -1,10 +1,7 @@ import { useRecoilCallback } from 'recoil'; -import { useMoveSoftFocusToCurrentCellOnHover } from '@/object-record/record-table/record-table-cell/hooks/useMoveSoftFocusToCurrentCellOnHover'; -import { currentTableCellInEditModePositionComponentState } from '@/object-record/record-table/states/currentTableCellInEditModePositionComponentState'; -import { isSoftFocusOnTableCellComponentFamilyState } from '@/object-record/record-table/states/isSoftFocusOnTableCellComponentFamilyState'; -import { isSoftFocusUsingMouseState } from '@/object-record/record-table/states/isSoftFocusUsingMouseState'; -import { isTableCellInEditModeComponentFamilyState } from '@/object-record/record-table/states/isTableCellInEditModeComponentFamilyState'; +import { useMoveHoverToCurrentCell } from '@/object-record/record-table/record-table-cell/hooks/useMoveHoverToCurrentCell'; +import { isSomeCellInEditModeComponentSelector } from '@/object-record/record-table/states/selectors/isSomeCellInEditModeComponentSelector'; import { TableCellPosition } from '@/object-record/record-table/types/TableCellPosition'; import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; @@ -18,54 +15,26 @@ export const useHandleContainerMouseEnter = ({ }: { recordTableId: string; }) => { - const { moveSoftFocusToCurrentCell } = - useMoveSoftFocusToCurrentCellOnHover(recordTableId); + const { moveHoverToCurrentCell } = useMoveHoverToCurrentCell(recordTableId); - const currentTableCellInEditModePositionState = - useRecoilComponentCallbackStateV2( - currentTableCellInEditModePositionComponentState, - recordTableId, - ); - - const isSoftFocusOnTableCellFamilyState = useRecoilComponentCallbackStateV2( - isSoftFocusOnTableCellComponentFamilyState, - recordTableId, - ); - - const isTableCellInEditModeFamilyState = useRecoilComponentCallbackStateV2( - isTableCellInEditModeComponentFamilyState, + const isSomeCellInEditModeSelector = useRecoilComponentCallbackStateV2( + isSomeCellInEditModeComponentSelector, recordTableId, ); const handleContainerMouseEnter = useRecoilCallback( - ({ snapshot, set }) => + ({ snapshot }) => ({ cellPosition }: HandleContainerMouseEnterArgs) => { - const isSoftFocusOnTableCell = getSnapshotValue( - snapshot, - isSoftFocusOnTableCellFamilyState(cellPosition), - ); - - const currentTableCellInEditModePosition = getSnapshotValue( - snapshot, - currentTableCellInEditModePositionState, - ); - const isSomeCellInEditMode = getSnapshotValue( snapshot, - isTableCellInEditModeFamilyState(currentTableCellInEditModePosition), + isSomeCellInEditModeSelector, ); - if (!isSomeCellInEditMode && !isSoftFocusOnTableCell) { - moveSoftFocusToCurrentCell(cellPosition); - set(isSoftFocusUsingMouseState, true); + if (!isSomeCellInEditMode) { + moveHoverToCurrentCell(cellPosition); } }, - [ - isSoftFocusOnTableCellFamilyState, - currentTableCellInEditModePositionState, - isTableCellInEditModeFamilyState, - moveSoftFocusToCurrentCell, - ], + [isSomeCellInEditModeSelector, moveHoverToCurrentCell], ); return { 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 dad738671..61f627e0b 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,13 +1,7 @@ -import { useRecoilCallback } from 'recoil'; - -import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; - 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 { isSoftFocusActiveComponentState } from '@/object-record/record-table/states/isSoftFocusActiveComponentState'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; -import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; -import { useDisableSoftFocus } from './useDisableSoftFocus'; export const useLeaveTableFocus = (recordTableId?: string) => { const recordTableIdFromContext = useAvailableComponentInstanceIdOrThrow( @@ -15,33 +9,17 @@ export const useLeaveTableFocus = (recordTableId?: string) => { recordTableId, ); - const disableSoftFocus = useDisableSoftFocus(recordTableIdFromContext); - - const isSoftFocusActiveState = useRecoilComponentCallbackStateV2( - isSoftFocusActiveComponentState, - recordTableIdFromContext, - ); - const resetTableRowSelection = useResetTableRowSelection( recordTableIdFromContext, ); - return useRecoilCallback( - ({ snapshot }) => - () => { - const isSoftFocusActive = getSnapshotValue( - snapshot, - isSoftFocusActiveState, - ); - - resetTableRowSelection(); - - if (!isSoftFocusActive) { - return; - } - - disableSoftFocus(); - }, - [disableSoftFocus, isSoftFocusActiveState, resetTableRowSelection], + const { setIsFocusActiveForCurrentPosition } = useSetIsRecordTableFocusActive( + recordTableIdFromContext, ); + + return () => { + resetTableRowSelection(); + + setIsFocusActiveForCurrentPosition(false); + }; }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useMoveEditModeToCellPosition.ts b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useMoveEditModeToCellPosition.ts deleted file mode 100644 index cdfd84235..000000000 --- a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useMoveEditModeToCellPosition.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { useRecoilCallback } from 'recoil'; - -import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; - -import { currentTableCellInEditModePositionComponentState } from '@/object-record/record-table/states/currentTableCellInEditModePositionComponentState'; -import { isTableCellInEditModeComponentFamilyState } from '@/object-record/record-table/states/isTableCellInEditModeComponentFamilyState'; -import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; -import { TableCellPosition } from '../../types/TableCellPosition'; - -export const useMoveEditModeToTableCellPosition = (recordTableId?: string) => { - const isTableCellInEditModeFamilyState = useRecoilComponentCallbackStateV2( - isTableCellInEditModeComponentFamilyState, - recordTableId, - ); - const currentTableCellInEditModePositionState = - useRecoilComponentCallbackStateV2( - currentTableCellInEditModePositionComponentState, - recordTableId, - ); - - return useRecoilCallback( - ({ set, snapshot }) => { - return (newPosition: TableCellPosition) => { - const currentTableCellInEditModePosition = getSnapshotValue( - snapshot, - currentTableCellInEditModePositionState, - ); - - set( - isTableCellInEditModeFamilyState(currentTableCellInEditModePosition), - false, - ); - - set(currentTableCellInEditModePositionState, newPosition); - - set(isTableCellInEditModeFamilyState(newPosition), true); - }; - }, - [currentTableCellInEditModePositionState, isTableCellInEditModeFamilyState], - ); -}; diff --git a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSetRecordTableFocusPosition.ts b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSetRecordTableFocusPosition.ts new file mode 100644 index 000000000..520920620 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSetRecordTableFocusPosition.ts @@ -0,0 +1,28 @@ +import { useRecoilCallback } from 'recoil'; + +import { recordTableFocusPositionComponentState } from '@/object-record/record-table/states/recordTableFocusPositionComponentState'; +import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; +import { useSetIsRecordTableFocusActive } from '../../record-table-cell/hooks/useSetIsRecordTableFocusActive'; +import { TableCellPosition } from '../../types/TableCellPosition'; + +export const useSetRecordTableFocusPosition = (recordTableId?: string) => { + const focusPositionState = useRecoilComponentCallbackStateV2( + recordTableFocusPositionComponentState, + recordTableId, + ); + + const { setIsFocusActive, setIsFocusActiveForCurrentPosition } = + useSetIsRecordTableFocusActive(recordTableId); + + return useRecoilCallback( + ({ set }) => { + return (newPosition: TableCellPosition) => { + set(focusPositionState, newPosition); + + setIsFocusActiveForCurrentPosition(false); + setIsFocusActive(true, newPosition); + }; + }, + [focusPositionState, setIsFocusActive, setIsFocusActiveForCurrentPosition], + ); +}; diff --git a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSetSoftFocusPosition.ts b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSetSoftFocusPosition.ts deleted file mode 100644 index 5a2756216..000000000 --- a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSetSoftFocusPosition.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { useRecoilCallback } from 'recoil'; - -import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; - -import { isSoftFocusActiveComponentState } from '@/object-record/record-table/states/isSoftFocusActiveComponentState'; -import { isSoftFocusOnTableCellComponentFamilyState } from '@/object-record/record-table/states/isSoftFocusOnTableCellComponentFamilyState'; -import { softFocusPositionComponentState } from '@/object-record/record-table/states/softFocusPositionComponentState'; -import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; -import { TableCellPosition } from '../../types/TableCellPosition'; - -export const useSetSoftFocusPosition = (recordTableId?: string) => { - const softFocusPositionState = useRecoilComponentCallbackStateV2( - softFocusPositionComponentState, - recordTableId, - ); - const isSoftFocusActiveState = useRecoilComponentCallbackStateV2( - isSoftFocusActiveComponentState, - recordTableId, - ); - const isSoftFocusOnTableCellFamilyState = useRecoilComponentCallbackStateV2( - isSoftFocusOnTableCellComponentFamilyState, - recordTableId, - ); - - return useRecoilCallback( - ({ set, snapshot }) => { - return (newPosition: TableCellPosition) => { - const currentPosition = getSnapshotValue( - snapshot, - softFocusPositionState, - ); - - set(isSoftFocusActiveState, true); - - set(isSoftFocusOnTableCellFamilyState(currentPosition), false); - - set(softFocusPositionState, newPosition); - - set(isSoftFocusOnTableCellFamilyState(newPosition), true); - }; - }, - [ - softFocusPositionState, - isSoftFocusActiveState, - isSoftFocusOnTableCellFamilyState, - ], - ); -}; diff --git a/packages/twenty-front/src/modules/object-record/record-table/hooks/useRecordTable.ts b/packages/twenty-front/src/modules/object-record/record-table/hooks/useRecordTable.ts index 90b96d1b9..941b47c0a 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/hooks/useRecordTable.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/hooks/useRecordTable.ts @@ -1,11 +1,9 @@ -import { useRecoilCallback, useSetRecoilState } from 'recoil'; +import { useRecoilCallback } from 'recoil'; import { Key } from 'ts-key-enum'; import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'; -import { useGetIsSomeCellInEditModeState } from '@/object-record/record-table/hooks/internal/useGetIsSomeCellInEditMode'; import { useSetHasUserSelectedAllRows } from '@/object-record/record-table/hooks/internal/useSetAllRowSelectedState'; import { useRecordTableMoveFocus } from '@/object-record/record-table/hooks/useRecordTableMoveFocus'; -import { isSoftFocusUsingMouseState } from '@/object-record/record-table/states/isSoftFocusUsingMouseState'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope'; import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; @@ -15,6 +13,7 @@ import { useUpsertRecordFromState } from '../../hooks/useUpsertRecordFromState'; import { ColumnDefinition } from '../types/ColumnDefinition'; import { TableHotkeyScope } from '../types/TableHotkeyScope'; +import { useSetIsRecordTableFocusActive } from '@/object-record/record-table/record-table-cell/hooks/useSetIsRecordTableFocusActive'; import { availableTableColumnsComponentState } from '@/object-record/record-table/states/availableTableColumnsComponentState'; import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState'; @@ -26,13 +25,12 @@ import { tableLastRowVisibleComponentState } from '@/object-record/record-table/ import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; -import { useDisableSoftFocus } from './internal/useDisableSoftFocus'; import { useLeaveTableFocus } from './internal/useLeaveTableFocus'; import { useResetTableRowSelection } from './internal/useResetTableRowSelection'; import { useSelectAllRows } from './internal/useSelectAllRows'; import { useSetRecordTableData } from './internal/useSetRecordTableData'; +import { useSetRecordTableFocusPosition } from './internal/useSetRecordTableFocusPosition'; import { useSetRowSelectedState } from './internal/useSetRowSelectedState'; -import { useSetSoftFocusPosition } from './internal/useSetSoftFocusPosition'; type useRecordTableProps = { recordTableId?: string; @@ -145,25 +143,23 @@ export const useRecordTable = (props?: useRecordTableProps) => { const upsertRecordTableItem = useUpsertRecordFromState; - const setSoftFocusPosition = useSetSoftFocusPosition(recordTableId); + const setFocusPosition = useSetRecordTableFocusPosition(recordTableId); + + const { setIsFocusActiveForCurrentPosition } = + useSetIsRecordTableFocusActive(recordTableId); const { moveDown, moveLeft, moveRight, moveUp } = useRecordTableMoveFocus(recordTableId); - const useMapKeyboardToSoftFocus = () => { - const disableSoftFocus = useDisableSoftFocus(recordTableId); + const useMapKeyboardToFocus = () => { const setHotkeyScope = useSetHotkeyScope(); - const setIsSoftFocusUsingMouseState = useSetRecoilState( - isSoftFocusUsingMouseState, - ); - useScopedHotkeys( [Key.ArrowUp, `${Key.Shift}+${Key.Enter}`], () => { moveUp(); }, - TableHotkeyScope.TableSoftFocus, + TableHotkeyScope.TableFocus, [moveUp], ); @@ -172,7 +168,7 @@ export const useRecordTable = (props?: useRecordTableProps) => { () => { moveDown(); }, - TableHotkeyScope.TableSoftFocus, + TableHotkeyScope.TableFocus, [moveDown], ); @@ -180,9 +176,8 @@ export const useRecordTable = (props?: useRecordTableProps) => { [Key.ArrowLeft, `${Key.Shift}+${Key.Tab}`], () => { moveLeft(); - setIsSoftFocusUsingMouseState(false); }, - TableHotkeyScope.TableSoftFocus, + TableHotkeyScope.TableFocus, [moveLeft], ); @@ -190,9 +185,8 @@ export const useRecordTable = (props?: useRecordTableProps) => { [Key.ArrowRight, Key.Tab], () => { moveRight(); - setIsSoftFocusUsingMouseState(false); }, - TableHotkeyScope.TableSoftFocus, + TableHotkeyScope.TableFocus, [moveRight], ); @@ -203,18 +197,15 @@ export const useRecordTable = (props?: useRecordTableProps) => { goto: true, keyboardShortcutMenu: true, }); - disableSoftFocus(); + setIsFocusActiveForCurrentPosition(false); }, - TableHotkeyScope.TableSoftFocus, - [disableSoftFocus], + TableHotkeyScope.TableFocus, + [setIsFocusActiveForCurrentPosition], ); }; const { selectAllRows } = useSelectAllRows(recordTableId); - const isSomeCellInEditModeState = - useGetIsSomeCellInEditModeState(recordTableId); - return { onColumnsChange, setAvailableTableColumns, @@ -228,13 +219,12 @@ export const useRecordTable = (props?: useRecordTableProps) => { moveLeft, moveRight, moveUp, - useMapKeyboardToSoftFocus, + useMapKeyboardToFocus, selectAllRows, setOnColumnsChange, setIsRecordTableInitialLoading, setRecordTableLastRowVisible, - setSoftFocusPosition, - isSomeCellInEditModeState, + setFocusPosition, setHasUserSelectedAllRows, setOnToggleColumnFilter, setOnToggleColumnSort, diff --git a/packages/twenty-front/src/modules/object-record/record-table/hooks/useRecordTableMoveFocus.ts b/packages/twenty-front/src/modules/object-record/record-table/hooks/useRecordTableMoveFocus.ts index 8e97bebe2..09cada125 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/hooks/useRecordTableMoveFocus.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/hooks/useRecordTableMoveFocus.ts @@ -4,16 +4,16 @@ import { MoveFocusDirection } from '@/object-record/record-table/types/MoveFocus import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector'; +import { useSetRecordTableFocusPosition } from '@/object-record/record-table/hooks/internal/useSetRecordTableFocusPosition'; +import { recordTableFocusPositionComponentState } from '@/object-record/record-table/states/recordTableFocusPositionComponentState'; import { numberOfTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/numberOfTableColumnsComponentSelector'; -import { softFocusPositionComponentState } from '@/object-record/record-table/states/softFocusPositionComponentState'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; -import { useSetSoftFocusPosition } from './internal/useSetSoftFocusPosition'; export const useRecordTableMoveFocus = (recordTableId?: string) => { - const setSoftFocusPosition = useSetSoftFocusPosition(recordTableId); + const setFocusPosition = useSetRecordTableFocusPosition(recordTableId); - const softFocusPositionState = useRecoilComponentCallbackStateV2( - softFocusPositionComponentState, + const focusPositionState = useRecoilComponentCallbackStateV2( + recordTableFocusPositionComponentState, recordTableId, ); @@ -25,23 +25,20 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => { const moveUp = useRecoilCallback( ({ snapshot }) => () => { - const softFocusPosition = getSnapshotValue( - snapshot, - softFocusPositionState, - ); + const focusPosition = getSnapshotValue(snapshot, focusPositionState); - let newRowIndex = softFocusPosition.row - 1; + let newRowIndex = focusPosition.row - 1; if (newRowIndex < 0) { newRowIndex = 0; } - setSoftFocusPosition({ - ...softFocusPosition, + setFocusPosition({ + ...focusPosition, row: newRowIndex, }); }, - [softFocusPositionState, setSoftFocusPosition], + [focusPositionState, setFocusPosition], ); const moveDown = useRecoilCallback( @@ -51,27 +48,20 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => { snapshot, recordIndexAllRecordIdsSelector, ); - const softFocusPosition = getSnapshotValue( - snapshot, - softFocusPositionState, - ); + const focusPosition = getSnapshotValue(snapshot, focusPositionState); - let newRowIndex = softFocusPosition.row + 1; + let newRowIndex = focusPosition.row + 1; if (newRowIndex >= allRecordIds.length) { newRowIndex = allRecordIds.length - 1; } - setSoftFocusPosition({ - ...softFocusPosition, + setFocusPosition({ + ...focusPosition, row: newRowIndex, }); }, - [ - recordIndexAllRecordIdsSelector, - setSoftFocusPosition, - softFocusPositionState, - ], + [recordIndexAllRecordIdsSelector, setFocusPosition, focusPositionState], ); const numberOfTableColumnsSelector = useRecoilComponentCallbackStateV2( @@ -86,18 +76,15 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => { snapshot, recordIndexAllRecordIdsSelector, ); - const softFocusPosition = getSnapshotValue( - snapshot, - softFocusPositionState, - ); + const focusPosition = getSnapshotValue(snapshot, focusPositionState); const numberOfTableColumns = getSnapshotValue( snapshot, numberOfTableColumnsSelector, ); - const currentColumnIndex = softFocusPosition.column; - const currentRowIndex = softFocusPosition.row; + const currentColumnIndex = focusPosition.column; + const currentRowIndex = focusPosition.row; const isLastRowAndLastColumn = currentColumnIndex === numberOfTableColumns - 1 && @@ -114,12 +101,12 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => { } if (isNotLastColumn) { - setSoftFocusPosition({ + setFocusPosition({ row: currentRowIndex, column: currentColumnIndex + 1, }); } else if (isLastColumnButNotLastRow) { - setSoftFocusPosition({ + setFocusPosition({ row: currentRowIndex + 1, column: 0, }); @@ -127,27 +114,24 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => { }, [ recordIndexAllRecordIdsSelector, - softFocusPositionState, + focusPositionState, numberOfTableColumnsSelector, - setSoftFocusPosition, + setFocusPosition, ], ); const moveLeft = useRecoilCallback( ({ snapshot }) => () => { - const softFocusPosition = getSnapshotValue( - snapshot, - softFocusPositionState, - ); + const focusPosition = getSnapshotValue(snapshot, focusPositionState); const numberOfTableColumns = getSnapshotValue( snapshot, numberOfTableColumnsSelector, ); - const currentColumnIndex = softFocusPosition.column; - const currentRowIndex = softFocusPosition.row; + const currentColumnIndex = focusPosition.column; + const currentRowIndex = focusPosition.row; const isFirstRowAndFirstColumn = currentColumnIndex === 0 && currentRowIndex === 0; @@ -162,22 +146,18 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => { } if (isNotFirstColumn) { - setSoftFocusPosition({ + setFocusPosition({ row: currentRowIndex, column: currentColumnIndex - 1, }); } else if (isFirstColumnButNotFirstRow) { - setSoftFocusPosition({ + setFocusPosition({ row: currentRowIndex - 1, column: numberOfTableColumns - 1, }); } }, - [ - numberOfTableColumnsSelector, - softFocusPositionState, - setSoftFocusPosition, - ], + [numberOfTableColumnsSelector, focusPositionState, setFocusPosition], ); const moveFocus = (direction: MoveFocusDirection) => { @@ -202,7 +182,7 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => { moveLeft, moveRight, moveUp, - setSoftFocusPosition, + setFocusPosition, moveFocus, }; }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodySoftFocusClickOutsideEffect.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyFocusClickOutsideEffect.tsx similarity index 84% rename from packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodySoftFocusClickOutsideEffect.tsx rename to packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyFocusClickOutsideEffect.tsx index 65925cfb0..a7fd3b2a9 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodySoftFocusClickOutsideEffect.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyFocusClickOutsideEffect.tsx @@ -3,13 +3,13 @@ import { useRecordTableContextOrThrow } from '@/object-record/record-table/conte import { useLeaveTableFocus } from '@/object-record/record-table/hooks/internal/useLeaveTableFocus'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; -type RecordTableBodySoftFocusClickOutsideEffectProps = { +type RecordTableBodyFocusClickOutsideEffectProps = { tableBodyRef: React.RefObject; }; -export const RecordTableBodySoftFocusClickOutsideEffect = ({ +export const RecordTableBodyFocusClickOutsideEffect = ({ tableBodyRef, -}: RecordTableBodySoftFocusClickOutsideEffectProps) => { +}: RecordTableBodyFocusClickOutsideEffectProps) => { const { recordTableId } = useRecordTableContextOrThrow(); const leaveTableFocus = useLeaveTableFocus(recordTableId); diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodySoftFocusKeyboardEffect.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyFocusKeyboardEffect.tsx similarity index 66% rename from packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodySoftFocusKeyboardEffect.tsx rename to packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyFocusKeyboardEffect.tsx index 66265bc0e..98e7b90ab 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodySoftFocusKeyboardEffect.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyFocusKeyboardEffect.tsx @@ -1,14 +1,14 @@ import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext'; import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable'; -export const RecordTableBodySoftFocusKeyboardEffect = () => { +export const RecordTableBodyFocusKeyboardEffect = () => { const { recordTableId } = useRecordTableContextOrThrow(); - const { useMapKeyboardToSoftFocus } = useRecordTable({ + const { useMapKeyboardToFocus } = useRecordTable({ recordTableId, }); - useMapKeyboardToSoftFocus(); + useMapKeyboardToFocus(); return <>; }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableNoRecordGroupBody.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableNoRecordGroupBody.tsx index 02539907e..ef9bf5ff2 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableNoRecordGroupBody.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableNoRecordGroupBody.tsx @@ -4,6 +4,7 @@ import { RecordTableNoRecordGroupRows } from '@/object-record/record-table/compo import { RecordTableBodyDragDropContextProvider } from '@/object-record/record-table/record-table-body/components/RecordTableBodyDragDropContextProvider'; import { RecordTableBodyDroppable } from '@/object-record/record-table/record-table-body/components/RecordTableBodyDroppable'; import { RecordTableBodyLoading } from '@/object-record/record-table/record-table-body/components/RecordTableBodyLoading'; +import { RecordTableCellPortals } from '@/object-record/record-table/record-table-cell/components/RecordTableCellPortals'; import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; @@ -25,6 +26,7 @@ export const RecordTableNoRecordGroupBody = () => { + diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableRecordGroupsBody.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableRecordGroupsBody.tsx index 087019240..e6720e66b 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableRecordGroupsBody.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableRecordGroupsBody.tsx @@ -6,6 +6,7 @@ import { RecordTableRecordGroupRows } from '@/object-record/record-table/compone import { RecordTableBodyDroppable } from '@/object-record/record-table/record-table-body/components/RecordTableBodyDroppable'; import { RecordTableBodyLoading } from '@/object-record/record-table/record-table-body/components/RecordTableBodyLoading'; import { RecordTableBodyRecordGroupDragDropContextProvider } from '@/object-record/record-table/record-table-body/components/RecordTableBodyRecordGroupDragDropContextProvider'; +import { RecordTableCellPortals } from '@/object-record/record-table/record-table-cell/components/RecordTableCellPortals'; import { RecordTableRecordGroupSection } from '@/object-record/record-table/record-table-section/components/RecordTableRecordGroupSection'; import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState'; import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2'; @@ -42,6 +43,7 @@ export const RecordTableRecordGroupsBody = () => { + diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCell.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCell.tsx index 4d6fcdd74..32b2687c5 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCell.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCell.tsx @@ -1,15 +1,11 @@ import { FieldDisplay } from '@/object-record/record-field/components/FieldDisplay'; import { FieldFocusContextProvider } from '@/object-record/record-field/contexts/FieldFocusContextProvider'; import { RecordTableCellContainer } from '@/object-record/record-table/record-table-cell/components/RecordTableCellContainer'; -import { RecordTableCellFieldInput } from '@/object-record/record-table/record-table-cell/components/RecordTableCellFieldInput'; export const RecordTableCell = () => { return ( - } - nonEditModeContent={} - /> + } /> ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellBaseContainer.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellBaseContainer.tsx index 9fddb0436..7e9593e32 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellBaseContainer.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellBaseContainer.tsx @@ -9,11 +9,11 @@ import { useOpenRecordTableCellFromCell } from '@/object-record/record-table/rec import { BORDER_COMMON, ThemeContext } from 'twenty-ui/theme'; const StyledBaseContainer = styled.div<{ - hasSoftFocus: boolean; fontColorExtraLight: string; fontColorMedium: string; backgroundColorTransparentSecondary: string; isReadOnly: boolean; + borderColorBlue: string; }>` align-items: center; box-sizing: border-box; @@ -23,23 +23,10 @@ const StyledBaseContainer = styled.div<{ position: relative; user-select: none; - background: ${({ hasSoftFocus, backgroundColorTransparentSecondary }) => - hasSoftFocus ? backgroundColorTransparentSecondary : 'none'}; - - border-radius: ${({ hasSoftFocus, isReadOnly }) => - hasSoftFocus && !isReadOnly ? BORDER_COMMON.radius.sm : 'none'}; - - outline: ${({ - hasSoftFocus, - fontColorExtraLight, - fontColorMedium, - isReadOnly, - }) => - hasSoftFocus - ? isReadOnly - ? `1px solid ${fontColorMedium}` - : `1px solid ${fontColorExtraLight}` - : 'none'}; + &.focus-active { + border-radius: ${BORDER_COMMON.radius.sm}; + outline: 1px solid ${({ borderColorBlue }) => borderColorBlue}; + } `; export const RecordTableCellBaseContainer = ({ @@ -52,18 +39,16 @@ export const RecordTableCellBaseContainer = ({ const { openTableCell } = useOpenRecordTableCellFromCell(); const { theme } = useContext(ThemeContext); - const { hasSoftFocus, cellPosition } = useContext(RecordTableCellContext); + const { cellPosition } = useContext(RecordTableCellContext); - const { onMoveSoftFocusToCurrentCell, onCellMouseEnter } = + const { onMoveHoverToCurrentCell, onCellMouseEnter } = useRecordTableBodyContextOrThrow(); const handleContainerMouseMove = () => { setIsFocused(true); - if (!hasSoftFocus) { - onCellMouseEnter({ - cellPosition, - }); - } + onCellMouseEnter({ + cellPosition, + }); }; const handleContainerMouseLeave = () => { @@ -71,10 +56,8 @@ export const RecordTableCellBaseContainer = ({ }; const handleContainerClick = () => { - if (!hasSoftFocus) { - onMoveSoftFocusToCurrentCell(cellPosition); - openTableCell(); - } + onMoveHoverToCurrentCell(cellPosition); + openTableCell(); }; return ( @@ -87,8 +70,9 @@ export const RecordTableCellBaseContainer = ({ } fontColorExtraLight={theme.font.color.extraLight} fontColorMedium={theme.border.color.medium} - hasSoftFocus={hasSoftFocus} + borderColorBlue={theme.adaptiveColors.blue4} isReadOnly={isReadOnly ?? false} + id={`record-table-cell-${cellPosition.column}-${cellPosition.row}`} > {children} diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellContainer.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellContainer.tsx index 9db07ceaf..480a67d77 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellContainer.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellContainer.tsx @@ -1,14 +1,10 @@ -import { ReactElement, useContext } from 'react'; +import { ReactElement } from 'react'; -import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext'; import { RecordTableCellBaseContainer } from '@/object-record/record-table/record-table-cell/components/RecordTableCellBaseContainer'; -import { RecordTableCellSoftFocusModeHotkeysSetterEffect } from '@/object-record/record-table/record-table-cell/components/RecordTableCellSoftFocusModeHotkeysSetterEffect'; import { RecordTableCellDisplayMode } from './RecordTableCellDisplayMode'; -import { RecordTableCellEditMode } from './RecordTableCellEditMode'; export type RecordTableCellContainerProps = { - editModeContent: ReactElement; nonEditModeContent: ReactElement; transparent?: boolean; maxContentWidth?: number; @@ -17,23 +13,13 @@ export type RecordTableCellContainerProps = { }; export const RecordTableCellContainer = ({ - editModeContent, nonEditModeContent, }: RecordTableCellContainerProps) => { - const { hasSoftFocus, isInEditMode } = useContext(RecordTableCellContext); - return ( - {isInEditMode ? ( - {editModeContent} - ) : ( - - {nonEditModeContent} - - )} - {hasSoftFocus ? ( - - ) : null} + + {nonEditModeContent} + ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellDisplayContainer.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellDisplayContainer.tsx index 4f0f2c342..c11df4434 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellDisplayContainer.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellDisplayContainer.tsx @@ -26,7 +26,7 @@ const StyledEmptyPlaceholderField = withTheme(styled.div<{ theme: Theme }>` `); export type EditableCellDisplayContainerProps = { - softFocus?: boolean; + focus?: boolean; onClick?: () => void; scrollRef?: Ref; isHovered?: boolean; @@ -36,7 +36,7 @@ export type EditableCellDisplayContainerProps = { export const RecordTableCellDisplayContainer = ({ children, - softFocus, + focus, onClick, scrollRef, onContextMenu, @@ -44,7 +44,7 @@ export const RecordTableCellDisplayContainer = ({ }: React.PropsWithChildren) => ( { +}: { + children: ReactNode; +}) => { const { recordId, isReadOnly } = useContext(FieldContext); - const { columnIndex, hasSoftFocus } = useContext(RecordTableCellContext); - - const isMobile = useIsMobile(); - const { onActionMenuDropdownOpened } = useRecordTableBodyContextOrThrow(); const { openTableCell } = useOpenRecordTableCellFromCell(); const isFieldInputOnly = useIsFieldInputOnly(); - const isFirstColumn = columnIndex === 0; - - const showButton = - hasSoftFocus && - !isFieldInputOnly && - !isReadOnly && - !(isMobile && isFirstColumn); const handleActionMenuDropdown = (event: React.MouseEvent) => { onActionMenuDropdownOpened(event, recordId); @@ -41,15 +29,11 @@ export const RecordTableCellDisplayMode = ({ }; return ( - <> - - {children} - - {showButton && } - + + {children} + ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellEditButton.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellEditButton.tsx index 886dabca1..aef9c027c 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellEditButton.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellEditButton.tsx @@ -8,12 +8,12 @@ import { isDefined } from 'twenty-shared/utils'; import { IconArrowUpRight, IconPencil } from 'twenty-ui/display'; export const RecordTableCellEditButton = () => { - const { columnIndex } = useContext(RecordTableCellContext); + const { cellPosition } = useContext(RecordTableCellContext); const { openTableCell } = useOpenRecordTableCellFromCell(); const isFieldInputOnly = useIsFieldInputOnly(); - const isFirstColumn = columnIndex === 0; + const isFirstColumn = cellPosition.column === 0; const customButtonIcon = useGetButtonIcon(); const buttonIcon = isFirstColumn diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellEditMode.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellEditMode.tsx index 0ad8d1c67..13b18d27b 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellEditMode.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellEditMode.tsx @@ -1,7 +1,10 @@ +import { useIsFieldInputOnly } from '@/object-record/record-field/hooks/useIsFieldInputOnly'; import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext'; import { recordFieldInputIsFieldInErrorComponentState } from '@/object-record/record-field/states/recordFieldInputIsFieldInErrorComponentState'; import { recordFieldInputLayoutDirectionComponentState } from '@/object-record/record-field/states/recordFieldInputLayoutDirectionComponentState'; import { recordFieldInputLayoutDirectionLoadingComponentState } from '@/object-record/record-field/states/recordFieldInputLayoutDirectionLoadingComponentState'; +import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext'; +import { useSetRecordTableFocusPosition } from '@/object-record/record-table/hooks/internal/useSetRecordTableFocusPosition'; import { OverlayContainer } from '@/ui/layout/overlay/components/OverlayContainer'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; @@ -14,9 +17,11 @@ import { offset, useFloating, } from '@floating-ui/react'; -import { ReactElement } from 'react'; +import { ReactElement, useContext } from 'react'; -const StyledEditableCellEditModeContainer = styled.div` +const StyledEditableCellEditModeContainer = styled.div<{ + isFieldInputOnly: boolean; +}>` align-items: center; display: flex; height: 100%; @@ -25,11 +30,17 @@ const StyledEditableCellEditModeContainer = styled.div - - {children} - + {isFieldInputOnly ? ( + { + setFocusPosition(cellPosition); + }} + > + {children} + + ) : ( + + {children} + + )} ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellEditModePortal.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellEditModePortal.tsx new file mode 100644 index 000000000..49384d00d --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellEditModePortal.tsx @@ -0,0 +1,44 @@ +import { RecordTableCellPortalWrapper } from '@/object-record/record-table/record-table-cell/components/RecordTableCellPortalWrapper'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; + +import { RecordTableFocusModeHotkeysSetterEffect } from '@/object-record/record-table/components/RecordTableFocusModeHotkeysSetterEffect'; +import { RecordTableCellEditMode } from '@/object-record/record-table/record-table-cell/components/RecordTableCellEditMode'; +import { RecordTableCellFieldInput } from '@/object-record/record-table/record-table-cell/components/RecordTableCellFieldInput'; +import { recordTableCellEditModePositionComponentState } from '@/object-record/record-table/states/recordTableCellEditModePositionComponentState'; +import { recordTableFocusPositionComponentState } from '@/object-record/record-table/states/recordTableFocusPositionComponentState'; +import styled from '@emotion/styled'; + +const StyledRecordTableCellHoveredPortal = styled.div` + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; +`; + +export const RecordTableCellEditModePortal = () => { + const focusedCellPosition = useRecoilComponentValueV2( + recordTableFocusPositionComponentState, + ); + + const currentTableCellInEditModePosition = useRecoilComponentValueV2( + recordTableCellEditModePositionComponentState, + ); + + if (!focusedCellPosition) { + return null; + } + + return ( + + {currentTableCellInEditModePosition && ( + + + + + + )} + + + ); +}; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellHoveredPortal.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellHoveredPortal.tsx new file mode 100644 index 000000000..50b5273fb --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellHoveredPortal.tsx @@ -0,0 +1,96 @@ +import { RecordTableCellPortalWrapper } from '@/object-record/record-table/record-table-cell/components/RecordTableCellPortalWrapper'; +import { recordTableHoverPositionComponentState } from '@/object-record/record-table/states/recordTableHoverPositionComponentState'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import styled from '@emotion/styled'; + +import { FieldDisplay } from '@/object-record/record-field/components/FieldDisplay'; +import { FieldContext } from '@/object-record/record-field/contexts/FieldContext'; +import { useIsFieldInputOnly } from '@/object-record/record-field/hooks/useIsFieldInputOnly'; +import { RecordTableCellDisplayMode } from '@/object-record/record-table/record-table-cell/components/RecordTableCellDisplayMode'; +import { RecordTableCellEditButton } from '@/object-record/record-table/record-table-cell/components/RecordTableCellEditButton'; +import { RecordTableCellEditMode } from '@/object-record/record-table/record-table-cell/components/RecordTableCellEditMode'; +import { RecordTableCellFieldInput } from '@/object-record/record-table/record-table-cell/components/RecordTableCellFieldInput'; +import { useContext } from 'react'; +import { BORDER_COMMON } from 'twenty-ui/theme'; +import { useIsMobile } from 'twenty-ui/utilities'; + +const StyledRecordTableCellHoveredPortalContent = styled.div<{ + isReadOnly: boolean; +}>` + align-items: center; + background: ${({ theme }) => theme.background.transparent.secondary}; + background-color: ${({ theme }) => theme.background.primary}; + border-radius: ${({ isReadOnly }) => + !isReadOnly ? BORDER_COMMON.radius.sm : 'none'}; + box-sizing: border-box; + cursor: ${({ isReadOnly }) => (isReadOnly ? 'default' : 'pointer')}; + display: flex; + + height: 32px; + + outline: ${({ theme, isReadOnly }) => + isReadOnly + ? `1px solid ${theme.border.color.medium}` + : `1px solid ${theme.font.color.extraLight}`}; + + position: relative; + user-select: none; +`; + +const RecordTableCellHoveredPortalContent = () => { + const hoverPosition = useRecoilComponentValueV2( + recordTableHoverPositionComponentState, + ); + + const isMobile = useIsMobile(); + + const isFirstColumn = hoverPosition?.column === 0; + + const { isReadOnly } = useContext(FieldContext); + + const isFieldInputOnly = useIsFieldInputOnly(); + + const showButton = + !isFieldInputOnly && !isReadOnly && !(isMobile && isFirstColumn); + + return ( + + {isFieldInputOnly ? ( + + + + ) : ( + + + + )} + {showButton && } + + ); +}; + +const StyledRecordTableCellHoveredPortal = styled.div` + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; +`; + +export const RecordTableCellHoveredPortal = () => { + const hoverPosition = useRecoilComponentValueV2( + recordTableHoverPositionComponentState, + ); + + if (!hoverPosition) { + return null; + } + + return ( + + + + + + ); +}; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellPortalWrapper.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellPortalWrapper.tsx new file mode 100644 index 000000000..9c307b9e4 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellPortalWrapper.tsx @@ -0,0 +1,66 @@ +import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow'; +import { getBasePathToShowPage } from '@/object-metadata/utils/getBasePathToShowPage'; +import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector'; +import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext'; +import { RecordTableRowContextProvider } from '@/object-record/record-table/contexts/RecordTableRowContext'; +import { RecordTableCellFieldContextWrapper } from '@/object-record/record-table/record-table-cell/components/RecordTableCellFieldContextWrapper'; +import { visibleTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/visibleTableColumnsComponentSelector'; +import { TableCellPosition } from '@/object-record/record-table/types/TableCellPosition'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import ReactDOM from 'react-dom'; + +export const RecordTableCellPortalWrapper = ({ + position, + children, +}: { + position: TableCellPosition; + children: React.ReactNode; +}) => { + const anchorElement = document.body.querySelector( + `#record-table-cell-${position.column}-${position.row}`, + ) as HTMLElement; + + const allRecordIds = useRecoilComponentValueV2( + recordIndexAllRecordIdsComponentSelector, + ); + + const { objectMetadataItem } = useContextStoreObjectMetadataItemOrThrow(); + + const visibleTableColumns = useRecoilComponentValueV2( + visibleTableColumnsComponentSelector, + ); + + if (!anchorElement) { + return null; + } + + const recordId = allRecordIds[position.row]; + + return ReactDOM.createPortal( + + + + {children} + + + , + anchorElement, + ); +}; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellPortals.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellPortals.tsx new file mode 100644 index 000000000..f48c05abb --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellPortals.tsx @@ -0,0 +1,22 @@ +import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext'; +import { RecordTableCellEditModePortal } from '@/object-record/record-table/record-table-cell/components/RecordTableCellEditModePortal'; +import { RecordTableCellHoveredPortal } from '@/object-record/record-table/record-table-cell/components/RecordTableCellHoveredPortal'; +import { isRecordTableFocusActiveComponentState } from '@/object-record/record-table/states/isRecordTableFocusActiveComponentState'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; + +export const RecordTableCellPortals = () => { + const { recordTableId } = useRecordTableContextOrThrow(); + + const isRecordTableFocusActive = useRecoilComponentValueV2( + isRecordTableFocusActiveComponentState, + recordTableId, + ); + + return ( + <> + + + {isRecordTableFocusActive && } + + ); +}; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellWrapper.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellWrapper.tsx index 81f2ea988..f7259963f 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellWrapper.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellWrapper.tsx @@ -2,11 +2,8 @@ import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata' import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext'; import { useRecordTableRowContextOrThrow } from '@/object-record/record-table/contexts/RecordTableRowContext'; import { RecordTableCellFieldContextWrapper } from '@/object-record/record-table/record-table-cell/components/RecordTableCellFieldContextWrapper'; -import { isSoftFocusOnTableCellComponentFamilyState } from '@/object-record/record-table/states/isSoftFocusOnTableCellComponentFamilyState'; -import { isTableCellInEditModeComponentFamilyState } from '@/object-record/record-table/states/isTableCellInEditModeComponentFamilyState'; import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition'; import { TableCellPosition } from '@/object-record/record-table/types/TableCellPosition'; -import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2'; import { useMemo } from 'react'; export const RecordTableCellWrapper = ({ @@ -28,23 +25,10 @@ export const RecordTableCellWrapper = ({ [columnIndex, rowIndex], ); - const isInEditMode = useRecoilComponentFamilyValueV2( - isTableCellInEditModeComponentFamilyState, - currentTableCellPosition, - ); - - const hasSoftFocus = useRecoilComponentFamilyValueV2( - isSoftFocusOnTableCellComponentFamilyState, - currentTableCellPosition, - ); - return ( { - it('should return the current table cell position', () => { - const wrapper = ({ children }: { children: React.ReactNode }) => ( - - - - {children} - - - - ); - - const { result } = renderHook(() => useCurrentTableCellPosition(), { - wrapper, - }); - - expect(result.current).toEqual({ - column: 3, - row: 2, - }); - }); -}); diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useCurrentTableCellEditMode.test.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useCurrentTableCellEditMode.test.tsx deleted file mode 100644 index 47f024ace..000000000 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useCurrentTableCellEditMode.test.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { act, renderHook, waitFor } from '@testing-library/react'; -import { RecoilRoot } from 'recoil'; - -import { RecordTableComponentInstance } from '@/object-record/record-table/components/RecordTableComponentInstance'; - -import { useCurrentTableCellEditMode } from '../useCurrentTableCellEditMode'; - -const onColumnsChange = jest.fn(); - -const recordTableId = 'scopeId'; - -const Wrapper = ({ children }: { children: React.ReactNode }) => ( - - - {children} - - -); - -describe('useCurrentTableCellEditMode.', () => { - it('should return initial values', () => { - const { result } = renderHook(() => useCurrentTableCellEditMode(), { - wrapper: Wrapper, - }); - - expect(result.current.isCurrentTableCellInEditMode).toBe(false); - expect(result.current.setCurrentTableCellInEditMode).toBeInstanceOf( - Function, - ); - }); - - it('should call setCurrentTableCellInEditMode', async () => { - const { result } = renderHook(() => useCurrentTableCellEditMode(), { - wrapper: Wrapper, - }); - - expect(result.current.isCurrentTableCellInEditMode).toBe(false); - - act(() => { - result.current.setCurrentTableCellInEditMode(); - }); - - await waitFor(() => { - expect(result.current.isCurrentTableCellInEditMode).toBe(true); - }); - }); -}); diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useIsSoftFocusOnCurrentTableCell.test.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useIsSoftFocusOnCurrentTableCell.test.tsx deleted file mode 100644 index c89570918..000000000 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useIsSoftFocusOnCurrentTableCell.test.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { renderHook } from '@testing-library/react'; -import { RecoilRoot } from 'recoil'; - -import { RecordTableComponentInstance } from '@/object-record/record-table/components/RecordTableComponentInstance'; -import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext'; -import { RecordTableRowContextProvider } from '@/object-record/record-table/contexts/RecordTableRowContext'; -import { RecordTableRowDraggableContextProvider } from '@/object-record/record-table/contexts/RecordTableRowDraggableContext'; -import { - recordTableCellContextValue, - recordTableRowContextValue, - recordTableRowDraggableContextValue, -} from '@/object-record/record-table/record-table-cell/hooks/__mocks__/cell'; -import { useIsSoftFocusOnCurrentTableCell } from '@/object-record/record-table/record-table-cell/hooks/useIsSoftFocusOnCurrentTableCell'; - -const Wrapper = ({ children }: { children: React.ReactNode }) => ( - - - - - - {children} - - - - - -); - -describe('useIsSoftFocusOnCurrentTableCell', () => { - it('should work as expected', () => { - const { result } = renderHook(() => useIsSoftFocusOnCurrentTableCell(), { - wrapper: Wrapper, - }); - - expect(result.current).toBe(false); - }); -}); diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useMoveHoverToCurrentCell.test.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useMoveHoverToCurrentCell.test.tsx new file mode 100644 index 000000000..d804584c3 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useMoveHoverToCurrentCell.test.tsx @@ -0,0 +1,80 @@ +import { renderHook } from '@testing-library/react'; +import { act } from 'react'; +import { RecoilRoot, useRecoilValue } from 'recoil'; + +import { RecordTableComponentInstance } from '@/object-record/record-table/components/RecordTableComponentInstance'; +import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext'; +import { RecordTableRowContextProvider } from '@/object-record/record-table/contexts/RecordTableRowContext'; +import { RecordTableRowDraggableContextProvider } from '@/object-record/record-table/contexts/RecordTableRowDraggableContext'; +import { + recordTableCellContextValue, + recordTableRowContextValue, + recordTableRowDraggableContextValue, +} from '@/object-record/record-table/record-table-cell/hooks/__mocks__/cell'; +import { useMoveHoverToCurrentCell } from '@/object-record/record-table/record-table-cell/hooks/useMoveHoverToCurrentCell'; +import { recordTableHoverPositionComponentState } from '@/object-record/record-table/states/recordTableHoverPositionComponentState'; +import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope'; +import { currentHotkeyScopeState } from '@/ui/utilities/hotkey/states/internal/currentHotkeyScopeState'; + +const Wrapper = ({ children }: { children: React.ReactNode }) => ( + { + set(currentHotkeyScopeState, { + scope: TableHotkeyScope.TableFocus, + customScopes: {}, + }); + }} + > + + + + + {children} + + + + + +); + +describe('useMoveHoverToCurrentCell', () => { + it('should work as expected', () => { + const { result } = renderHook( + () => { + const recordTableHoverPosition = useRecoilValue( + recordTableHoverPositionComponentState.atomFamily({ + instanceId: 'test-record-table-instance-id', + }), + ); + const { moveHoverToCurrentCell } = useMoveHoverToCurrentCell( + 'test-record-table-instance-id', + ); + + return { + moveHoverToCurrentCell, + recordTableHoverPosition, + }; + }, + { + wrapper: Wrapper, + }, + ); + + act(() => { + result.current.moveHoverToCurrentCell({ + column: 3, + row: 2, + }); + }); + + expect(result.current.recordTableHoverPosition).toEqual({ + column: 3, + row: 2, + }); + }); +}); diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useMoveSoftFocusToCurrentCellOnHover.test.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useMoveSoftFocusToCurrentCellOnHover.test.tsx deleted file mode 100644 index 3b5a5d94b..000000000 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useMoveSoftFocusToCurrentCellOnHover.test.tsx +++ /dev/null @@ -1,147 +0,0 @@ -import { renderHook } from '@testing-library/react'; -import { act } from 'react'; -import { CallbackInterface, RecoilRoot } from 'recoil'; - -import { RecordTableComponentInstance } from '@/object-record/record-table/components/RecordTableComponentInstance'; -import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext'; -import { RecordTableRowContextProvider } from '@/object-record/record-table/contexts/RecordTableRowContext'; -import { RecordTableRowDraggableContextProvider } from '@/object-record/record-table/contexts/RecordTableRowDraggableContext'; -import { - recordTableCellContextValue, - recordTableRowContextValue, - recordTableRowDraggableContextValue, -} from '@/object-record/record-table/record-table-cell/hooks/__mocks__/cell'; -import { useMoveSoftFocusToCurrentCellOnHover } from '@/object-record/record-table/record-table-cell/hooks/useMoveSoftFocusToCurrentCellOnHover'; -import { TableCellPosition } from '@/object-record/record-table/types/TableCellPosition'; -import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope'; - -const mockSoftFocusPositionState = { - key: 'softFocusPositionComponentState__{"instanceId":"scopeId"}', -}; -const mockSoftFocusActiveState = { - key: 'isSoftFocusActiveComponentState__{"instanceId":"scopeId"}', -}; -const mockIsSoftFocusOnTableCellFamilyStateCurrentPosition = { - key: 'isSoftFocusOnTableCellComponentFamilyState__{"familyKey":{"column":1,"row":0},"instanceId":"scopeId"}', -}; -const mockIsSoftFocusOnTableCellFamilyStateNewPosition = { - key: 'isSoftFocusOnTableCellComponentFamilyState__{"familyKey":{"column":3,"row":2},"instanceId":"scopeId"}', -}; -const mockCurrentTableCellInEditModePositionState = { - key: 'currentTableCellInEditModePositionComponentState__{"instanceId":"scopeId"}', -}; -const mockIsTableCellInEditModeFamilyState = { - key: 'isTableCellInEditModeComponentFamilyState__{"familyKey":{"column":1,"row":0},"instanceId":"scopeId"}', -}; -const mockCurrentHotKeyScopeState = { - key: 'currentHotkeyScopeState', -}; - -const mockCallbackInterface = { - set: jest.fn(), - snapshot: { - getLoadable: (recoilValue: { key: string }) => ({ - getValue: () => { - if (recoilValue.key === mockSoftFocusPositionState.key) - return { column: 1, row: 0 }; - if (recoilValue.key === mockCurrentTableCellInEditModePositionState.key) - return { column: 1, row: 0 }; - else if (recoilValue.key === mockIsTableCellInEditModeFamilyState.key) - return false; - else if (recoilValue.key === mockCurrentHotKeyScopeState.key) - return { scope: TableHotkeyScope.Table }; - }, - }), - }, -} as unknown as CallbackInterface; - -const setSoftFocusPosition = jest.fn(); -const setOnColumnsChange = jest.fn(); -const setHotkeyScope = jest.fn(); - -jest.mock('recoil', () => ({ - ...jest.requireActual('recoil'), - useRecoilCallback: ( - callback: ( - _interface: CallbackInterface, - ) => (newPosition: TableCellPosition) => void, - ) => callback(mockCallbackInterface), -})); -jest.mock('@/object-record/record-table/hooks/useRecordTable', () => ({ - useRecordTable: () => ({ - setSoftFocusPosition, - setOnColumnsChange, - }), -})); -jest.mock('@/ui/utilities/hotkey/hooks/useSetHotkeyScope', () => ({ - useSetHotkeyScope: () => setHotkeyScope, -})); - -const Wrapper = ({ children }: { children: React.ReactNode }) => ( - - - - - - {children} - - - - - -); - -describe('useMoveSoftFocusToCurrentCellOnHover', () => { - it('should work as expected', () => { - const { result } = renderHook( - () => useMoveSoftFocusToCurrentCellOnHover('scopeId'), - { wrapper: Wrapper }, - ); - - act(() => { - result.current.moveSoftFocusToCurrentCell({ - column: 3, - row: 2, - }); - }); - - expect(mockCallbackInterface.set).toHaveBeenNthCalledWith( - 1, - mockSoftFocusActiveState, - true, - ); - - expect(mockCallbackInterface.set).toHaveBeenNthCalledWith( - 2, - mockIsSoftFocusOnTableCellFamilyStateCurrentPosition, - false, - ); - - expect(mockCallbackInterface.set).toHaveBeenNthCalledWith( - 3, - mockSoftFocusPositionState, - { column: 3, row: 2 }, - ); - - expect(mockCallbackInterface.set).toHaveBeenNthCalledWith( - 4, - mockIsSoftFocusOnTableCellFamilyStateNewPosition, - true, - ); - - expect(mockCallbackInterface.set).toHaveBeenNthCalledWith( - 5, - mockSoftFocusActiveState, - true, - ); - - expect(setHotkeyScope).toHaveBeenCalledWith( - TableHotkeyScope.TableSoftFocus, - ); - }); -}); diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useSelectedTableCellEditMode.test.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useSelectedTableCellEditMode.test.tsx deleted file mode 100644 index c0eb841e8..000000000 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useSelectedTableCellEditMode.test.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { act, renderHook } from '@testing-library/react'; -import { CallbackInterface, RecoilRoot } from 'recoil'; - -import { TableCellPosition } from '@/object-record/record-table/types/TableCellPosition'; - -import { useSelectedTableCellEditMode } from '../useSelectedTableCellEditMode'; - -const scopeId = 'yourScopeId'; - -const mockCallbackInterface = { - set: jest.fn(), - snapshot: { - getLoadable: () => ({ - getValue: () => ({ row: 0, column: 0 }), - }), - }, -} as unknown as CallbackInterface; - -jest.mock('recoil', () => ({ - ...jest.requireActual('recoil'), - useRecoilCallback: ( - callback: ( - _interface: CallbackInterface, - ) => (newPosition: TableCellPosition) => void, - ) => callback(mockCallbackInterface), -})); - -describe('useSelectedTableCellEditMode', () => { - it('should have property setSelectedTableCellEditMode', async () => { - const tableCellPosition: TableCellPosition = { - row: 1, - column: 5, - }; - - const { result } = renderHook( - () => useSelectedTableCellEditMode({ scopeId }), - { - wrapper: RecoilRoot, - }, - ); - - act(() => { - result.current.setSelectedTableCellEditMode( - tableCellPosition.row, - tableCellPosition.column, - ); - }); - - expect(mockCallbackInterface.set).toHaveBeenCalledWith( - { - key: 'isTableCellInEditModeComponentFamilyState__{"familyKey":{"column":0,"row":0},"instanceId":"yourScopeId"}', - }, - false, - ); - - expect(mockCallbackInterface.set).toHaveBeenCalledWith( - { - key: 'isTableCellInEditModeComponentFamilyState__{"familyKey":{"column":5,"row":1},"instanceId":"yourScopeId"}', - }, - true, - ); - }); -}); diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useSetIsRecordTableFocusActive.test.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useSetIsRecordTableFocusActive.test.tsx new file mode 100644 index 000000000..4f7769ddb --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useSetIsRecordTableFocusActive.test.tsx @@ -0,0 +1,155 @@ +import { renderHook } from '@testing-library/react'; +import React, { act } from 'react'; +import { RecoilRoot, useRecoilValue } from 'recoil'; + +import { RecordTableComponentInstance } from '@/object-record/record-table/components/RecordTableComponentInstance'; +import { useSetIsRecordTableFocusActive } from '@/object-record/record-table/record-table-cell/hooks/useSetIsRecordTableFocusActive'; +import { isRecordTableFocusActiveComponentState } from '@/object-record/record-table/states/isRecordTableFocusActiveComponentState'; +import { recordTableFocusPositionComponentState } from '@/object-record/record-table/states/recordTableFocusPositionComponentState'; +import { TableCellPosition } from '@/object-record/record-table/types/TableCellPosition'; + +const mockClassList = { + add: jest.fn(), + remove: jest.fn(), +}; + +const mockGetElementById = jest.spyOn(document, 'getElementById'); + +const Wrapper = ({ children }: { children: React.ReactNode }) => ( + { + set( + isRecordTableFocusActiveComponentState.atomFamily({ + instanceId: 'test-table-id', + }), + false, + ); + set( + recordTableFocusPositionComponentState.atomFamily({ + instanceId: 'test-table-id', + }), + { + column: 1, + row: 0, + }, + ); + }} + > + + {children} + + +); + +const renderHooks = () => { + const { result } = renderHook( + () => { + const { setIsFocusActive, setIsFocusActiveForCurrentPosition } = + useSetIsRecordTableFocusActive('test-table-id'); + const isRecordTableFocusActive = useRecoilValue( + isRecordTableFocusActiveComponentState.atomFamily({ + instanceId: 'test-table-id', + }), + ); + const focusPosition = useRecoilValue( + recordTableFocusPositionComponentState.atomFamily({ + instanceId: 'test-table-id', + }), + ); + return { + setIsFocusActive, + setIsFocusActiveForCurrentPosition, + isRecordTableFocusActive, + focusPosition, + }; + }, + { wrapper: Wrapper }, + ); + + return { result }; +}; + +describe('useSetIsRecordTableFocusActive', () => { + beforeEach(() => { + jest.clearAllMocks(); + + mockGetElementById.mockReturnValue({ + classList: mockClassList, + } as unknown as HTMLElement); + }); + + it('should set focus active state and add class to cell element when focus is activated', () => { + const { result } = renderHooks(); + + const cellPosition: TableCellPosition = { column: 1, row: 0 }; + + act(() => { + result.current.setIsFocusActive(true, cellPosition); + }); + + expect(mockGetElementById).toHaveBeenCalledWith('record-table-cell-1-0'); + + expect(mockClassList.add).toHaveBeenCalledWith('focus-active'); + + expect(result.current.isRecordTableFocusActive).toBe(true); + + expect(result.current.focusPosition).toEqual(cellPosition); + }); + + it('should remove focus-active class when focus is deactivated and update isRecordTableFocusActiveComponentState', () => { + const { result } = renderHooks(); + + const cellPosition: TableCellPosition = { column: 1, row: 0 }; + + act(() => { + result.current.setIsFocusActive(false, cellPosition); + }); + + expect(mockGetElementById).toHaveBeenCalledWith('record-table-cell-1-0'); + + expect(mockClassList.remove).toHaveBeenCalledWith('focus-active'); + + expect(result.current.isRecordTableFocusActive).toBe(false); + + expect(result.current.focusPosition).toEqual(cellPosition); + }); + + it('should set focus for current position', () => { + const { result } = renderHooks(); + + act(() => { + result.current.setIsFocusActiveForCurrentPosition(true); + }); + + expect(mockGetElementById).toHaveBeenCalledWith('record-table-cell-1-0'); + + expect(mockClassList.add).toHaveBeenCalledWith('focus-active'); + + expect(result.current.isRecordTableFocusActive).toBe(true); + + expect(result.current.focusPosition).toEqual({ column: 1, row: 0 }); + }); + + it('should handle case when the cell element is not found', () => { + mockGetElementById.mockReturnValue(null); + + const { result } = renderHooks(); + + const cellPosition: TableCellPosition = { column: 1, row: 0 }; + + act(() => { + result.current.setIsFocusActive(true, cellPosition); + }); + + expect(mockGetElementById).toHaveBeenCalledWith('record-table-cell-1-0'); + + expect(mockClassList.add).not.toHaveBeenCalled(); + + expect(result.current.isRecordTableFocusActive).toBe(true); + + expect(result.current.focusPosition).toEqual(cellPosition); + }); +}); diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/internal/__tests__/useCloseRecordTableCellInGroup.test.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/internal/__tests__/useCloseRecordTableCellInGroup.test.tsx index 20a6a7ef5..c6b6edd18 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/internal/__tests__/useCloseRecordTableCellInGroup.test.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/internal/__tests__/useCloseRecordTableCellInGroup.test.tsx @@ -16,10 +16,8 @@ import { recordTableRowDraggableContextValue, } from '@/object-record/record-table/record-table-cell/hooks/__mocks__/cell'; import { useCloseRecordTableCellInGroup } from '@/object-record/record-table/record-table-cell/hooks/internal/useCloseRecordTableCellInGroup'; -import { currentTableCellInEditModePositionComponentState } from '@/object-record/record-table/states/currentTableCellInEditModePositionComponentState'; -import { isTableCellInEditModeComponentFamilyState } from '@/object-record/record-table/states/isTableCellInEditModeComponentFamilyState'; +import { recordTableCellEditModePositionComponentState } from '@/object-record/record-table/states/recordTableCellEditModePositionComponentState'; import { useDragSelect } from '@/ui/utilities/drag-select/hooks/useDragSelect'; -import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems'; @@ -60,7 +58,7 @@ const Wrapper = ({ children }: { children: React.ReactNode }) => ( value={recordTableRowDraggableContextValue} > {children} @@ -77,16 +75,12 @@ describe('useCloseRecordTableCellInGroup', () => { const { result } = renderHook( () => { const currentTableCellInEditModePosition = useRecoilComponentValueV2( - currentTableCellInEditModePositionComponentState, - ); - const isTableCellInEditMode = useRecoilComponentFamilyValueV2( - isTableCellInEditModeComponentFamilyState, - currentTableCellInEditModePosition, + recordTableCellEditModePositionComponentState, ); return { ...useCloseRecordTableCellInGroup(), ...useDragSelect(), - isTableCellInEditMode, + currentTableCellInEditModePosition, }; }, { @@ -99,7 +93,7 @@ describe('useCloseRecordTableCellInGroup', () => { }); expect(result.current.isDragSelectionStartEnabled()).toBe(true); - expect(result.current.isTableCellInEditMode).toBe(false); - expect(setHotkeyScope).toHaveBeenCalledWith('table-soft-focus'); + expect(result.current.currentTableCellInEditModePosition).toBe(null); + expect(setHotkeyScope).toHaveBeenCalledWith('table-focus'); }); }); diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/internal/__tests__/useCloseRecordTableCellNoGroup.test.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/internal/__tests__/useCloseRecordTableCellNoGroup.test.tsx index 88561640e..9045267ec 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/internal/__tests__/useCloseRecordTableCellNoGroup.test.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/internal/__tests__/useCloseRecordTableCellNoGroup.test.tsx @@ -16,10 +16,8 @@ import { recordTableRowDraggableContextValue, } from '@/object-record/record-table/record-table-cell/hooks/__mocks__/cell'; import { useCloseRecordTableCellNoGroup } from '@/object-record/record-table/record-table-cell/hooks/internal/useCloseRecordTableCellNoGroup'; -import { currentTableCellInEditModePositionComponentState } from '@/object-record/record-table/states/currentTableCellInEditModePositionComponentState'; -import { isTableCellInEditModeComponentFamilyState } from '@/object-record/record-table/states/isTableCellInEditModeComponentFamilyState'; +import { recordTableCellEditModePositionComponentState } from '@/object-record/record-table/states/recordTableCellEditModePositionComponentState'; import { useDragSelect } from '@/ui/utilities/drag-select/hooks/useDragSelect'; -import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems'; @@ -60,7 +58,7 @@ const Wrapper = ({ children }: { children: React.ReactNode }) => ( value={recordTableRowDraggableContextValue} > {children} @@ -77,16 +75,13 @@ describe('useCloseRecordTableCellNoGroup', () => { const { result } = renderHook( () => { const currentTableCellInEditModePosition = useRecoilComponentValueV2( - currentTableCellInEditModePositionComponentState, - ); - const isTableCellInEditMode = useRecoilComponentFamilyValueV2( - isTableCellInEditModeComponentFamilyState, - currentTableCellInEditModePosition, + recordTableCellEditModePositionComponentState, ); + return { ...useCloseRecordTableCellNoGroup(), ...useDragSelect(), - isTableCellInEditMode, + currentTableCellInEditModePosition, }; }, { @@ -99,7 +94,7 @@ describe('useCloseRecordTableCellNoGroup', () => { }); expect(result.current.isDragSelectionStartEnabled()).toBe(true); - expect(result.current.isTableCellInEditMode).toBe(false); - expect(setHotkeyScope).toHaveBeenCalledWith('table-soft-focus'); + expect(result.current.currentTableCellInEditModePosition).toBe(null); + expect(setHotkeyScope).toHaveBeenCalledWith('table-focus'); }); }); diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/internal/useCloseRecordTableCellInGroup.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/internal/useCloseRecordTableCellInGroup.ts index 9a985d8af..2070def65 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/internal/useCloseRecordTableCellInGroup.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/internal/useCloseRecordTableCellInGroup.ts @@ -1,6 +1,6 @@ import { useRecoilCallback } from 'recoil'; -import { SOFT_FOCUS_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-table/constants/SoftFocusClickOutsideListenerId'; +import { FOCUS_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-table/constants/FocusClickOutsideListenerId'; import { useDragSelect } from '@/ui/utilities/drag-select/hooks/useDragSelect'; import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope'; import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useClickOutsideListener'; @@ -16,7 +16,7 @@ export const useCloseRecordTableCellInGroup = () => { const { setDragSelectionStartEnabled } = useDragSelect(); const { toggleClickOutsideListener } = useClickOutsideListener( - SOFT_FOCUS_CLICK_OUTSIDE_LISTENER_ID, + FOCUS_CLICK_OUTSIDE_LISTENER_ID, ); const closeCurrentTableCellInEditMode = @@ -27,7 +27,7 @@ export const useCloseRecordTableCellInGroup = () => { toggleClickOutsideListener(true); setDragSelectionStartEnabled(true); closeCurrentTableCellInEditMode(); - setHotkeyScope(TableHotkeyScope.TableSoftFocus); + setHotkeyScope(TableHotkeyScope.TableFocus); }, [ closeCurrentTableCellInEditMode, diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/internal/useCloseRecordTableCellNoGroup.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/internal/useCloseRecordTableCellNoGroup.ts index ba3dc25dc..97b69d490 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/internal/useCloseRecordTableCellNoGroup.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/internal/useCloseRecordTableCellNoGroup.ts @@ -1,4 +1,4 @@ -import { SOFT_FOCUS_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-table/constants/SoftFocusClickOutsideListenerId'; +import { FOCUS_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-table/constants/FocusClickOutsideListenerId'; import { useDragSelect } from '@/ui/utilities/drag-select/hooks/useDragSelect'; import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope'; import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useClickOutsideListener'; @@ -16,7 +16,7 @@ export const useCloseRecordTableCellNoGroup = () => { const { setDragSelectionStartEnabled } = useDragSelect(); const { toggleClickOutsideListener } = useClickOutsideListener( - SOFT_FOCUS_CLICK_OUTSIDE_LISTENER_ID, + FOCUS_CLICK_OUTSIDE_LISTENER_ID, ); const closeCurrentTableCellInEditMode = @@ -26,7 +26,7 @@ export const useCloseRecordTableCellNoGroup = () => { toggleClickOutsideListener(true); setDragSelectionStartEnabled(true); closeCurrentTableCellInEditMode(); - setHotkeyScope(TableHotkeyScope.TableSoftFocus); + setHotkeyScope(TableHotkeyScope.TableFocus); }, [ closeCurrentTableCellInEditMode, setDragSelectionStartEnabled, diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useCurrentCellPosition.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useCurrentCellPosition.ts deleted file mode 100644 index 383a81de4..000000000 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useCurrentCellPosition.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { useContext } from 'react'; - -import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext'; - -export const useCurrentTableCellPosition = () => { - const { cellPosition } = useContext(RecordTableCellContext); - - return cellPosition; -}; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useCurrentTableCellEditMode.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useCurrentTableCellEditMode.ts deleted file mode 100644 index fb4f77498..000000000 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useCurrentTableCellEditMode.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { useCallback } from 'react'; - -import { useMoveEditModeToTableCellPosition } from '../../hooks/internal/useMoveEditModeToCellPosition'; - -import { isTableCellInEditModeComponentFamilyState } from '@/object-record/record-table/states/isTableCellInEditModeComponentFamilyState'; -import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2'; -import { useCurrentTableCellPosition } from './useCurrentCellPosition'; - -export const useCurrentTableCellEditMode = () => { - const moveEditModeToTableCellPosition = useMoveEditModeToTableCellPosition(); - - const currentTableCellPosition = useCurrentTableCellPosition(); - - const isCurrentTableCellInEditMode = useRecoilComponentFamilyValueV2( - isTableCellInEditModeComponentFamilyState, - currentTableCellPosition, - ); - - const setCurrentTableCellInEditMode = useCallback(() => { - moveEditModeToTableCellPosition(currentTableCellPosition); - }, [currentTableCellPosition, moveEditModeToTableCellPosition]); - - return { - isCurrentTableCellInEditMode, - setCurrentTableCellInEditMode, - }; -}; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useIsSoftFocusOnCurrentTableCell.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useIsSoftFocusOnCurrentTableCell.ts deleted file mode 100644 index 67605e093..000000000 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useIsSoftFocusOnCurrentTableCell.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { isSoftFocusOnTableCellComponentFamilyState } from '@/object-record/record-table/states/isSoftFocusOnTableCellComponentFamilyState'; -import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2'; -import { useCurrentTableCellPosition } from './useCurrentCellPosition'; - -export const useIsSoftFocusOnCurrentTableCell = () => { - const currentTableCellPosition = useCurrentTableCellPosition(); - - const isSoftFocusOnTableCell = useRecoilComponentFamilyValueV2( - isSoftFocusOnTableCellComponentFamilyState, - currentTableCellPosition, - ); - - return isSoftFocusOnTableCell; -}; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useMoveHoverToCurrentCell.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useMoveHoverToCurrentCell.ts new file mode 100644 index 000000000..efb848e8d --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useMoveHoverToCurrentCell.ts @@ -0,0 +1,55 @@ +import { useRecoilCallback } from 'recoil'; + +import { TableCellPosition } from '@/object-record/record-table/types/TableCellPosition'; +import { currentHotkeyScopeState } from '@/ui/utilities/hotkey/states/internal/currentHotkeyScopeState'; +import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; + +import { recordTableHoverPositionComponentState } from '@/object-record/record-table/states/recordTableHoverPositionComponentState'; +import { isSomeCellInEditModeComponentSelector } from '@/object-record/record-table/states/selectors/isSomeCellInEditModeComponentSelector'; +import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope'; +import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; +import { TableHotkeyScope } from '../../types/TableHotkeyScope'; + +export const useMoveHoverToCurrentCell = (recordTableId: string) => { + const setHoverPosition = useSetRecoilComponentStateV2( + recordTableHoverPositionComponentState, + recordTableId, + ); + + const isSomeCellInEditModeSelector = useRecoilComponentCallbackStateV2( + isSomeCellInEditModeComponentSelector, + recordTableId, + ); + + const moveHoverToCurrentCell = useRecoilCallback( + ({ snapshot }) => + (cellPosition: TableCellPosition) => { + const isSomeCellInEditMode = getSnapshotValue( + snapshot, + isSomeCellInEditModeSelector, + ); + + const currentHotkeyScope = getSnapshotValue( + snapshot, + currentHotkeyScopeState, + ); + + if ( + currentHotkeyScope.scope !== TableHotkeyScope.TableFocus && + currentHotkeyScope.scope !== TableHotkeyScope.CellEditMode && + currentHotkeyScope.scope !== TableHotkeyScope.Table && + currentHotkeyScope.scope !== AppHotkeyScope.CommandMenuOpen + ) { + return; + } + + if (!isSomeCellInEditMode) { + setHoverPosition(cellPosition); + } + }, + [isSomeCellInEditModeSelector, setHoverPosition], + ); + + return { moveHoverToCurrentCell }; +}; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useMoveSoftFocusToCurrentCellOnHover.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useMoveSoftFocusToCurrentCellOnHover.ts deleted file mode 100644 index 60c4f1f13..000000000 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useMoveSoftFocusToCurrentCellOnHover.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { useRecoilCallback } from 'recoil'; - -import { useSetSoftFocus } from '@/object-record/record-table/record-table-cell/hooks/useSetSoftFocus'; -import { TableCellPosition } from '@/object-record/record-table/types/TableCellPosition'; -import { currentHotkeyScopeState } from '@/ui/utilities/hotkey/states/internal/currentHotkeyScopeState'; -import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; - -import { currentTableCellInEditModePositionComponentState } from '@/object-record/record-table/states/currentTableCellInEditModePositionComponentState'; -import { isTableCellInEditModeComponentFamilyState } from '@/object-record/record-table/states/isTableCellInEditModeComponentFamilyState'; -import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope'; -import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; -import { TableHotkeyScope } from '../../types/TableHotkeyScope'; - -export const useMoveSoftFocusToCurrentCellOnHover = (recordTableId: string) => { - const setSoftFocus = useSetSoftFocus(recordTableId); - - const currentTableCellInEditModePositionState = - useRecoilComponentCallbackStateV2( - currentTableCellInEditModePositionComponentState, - recordTableId, - ); - const isTableCellInEditModeFamilyState = useRecoilComponentCallbackStateV2( - isTableCellInEditModeComponentFamilyState, - recordTableId, - ); - - const moveSoftFocusToCurrentCell = useRecoilCallback( - ({ snapshot }) => - (cellPosition: TableCellPosition) => { - const currentTableCellInEditModePosition = getSnapshotValue( - snapshot, - currentTableCellInEditModePositionState, - ); - - const isSomeCellInEditMode = snapshot - .getLoadable( - isTableCellInEditModeFamilyState( - currentTableCellInEditModePosition, - ), - ) - .getValue(); - - const currentHotkeyScope = snapshot - .getLoadable(currentHotkeyScopeState) - .getValue(); - - if ( - currentHotkeyScope.scope !== TableHotkeyScope.TableSoftFocus && - currentHotkeyScope.scope !== TableHotkeyScope.CellEditMode && - currentHotkeyScope.scope !== TableHotkeyScope.Table && - currentHotkeyScope.scope !== AppHotkeyScope.CommandMenuOpen - ) { - return; - } - - if (!isSomeCellInEditMode) { - setSoftFocus(cellPosition); - } - }, - [ - currentTableCellInEditModePositionState, - isTableCellInEditModeFamilyState, - setSoftFocus, - ], - ); - - return { moveSoftFocusToCurrentCell }; -}; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellFromCell.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellFromCell.ts index c000414fa..f90bf23de 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellFromCell.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellFromCell.ts @@ -4,11 +4,12 @@ import { FieldContext } from '@/object-record/record-field/contexts/FieldContext import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition'; import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'; import { useRecordTableRowContextOrThrow } from '@/object-record/record-table/contexts/RecordTableRowContext'; -import { useCurrentTableCellPosition } from '@/object-record/record-table/record-table-cell/hooks/useCurrentCellPosition'; import { TableCellPosition } from '@/object-record/record-table/types/TableCellPosition'; import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; import { useRecordTableBodyContextOrThrow } from '@/object-record/record-table/contexts/RecordTableBodyContext'; +import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext'; +import { useSetRecordTableFocusPosition } from '@/object-record/record-table/hooks/internal/useSetRecordTableFocusPosition'; import { TableHotkeyScope } from '../../types/TableHotkeyScope'; export const DEFAULT_CELL_SCOPE: HotkeyScope = { @@ -33,7 +34,9 @@ export const useOpenRecordTableCellFromCell = () => { const { onOpenTableCell } = useRecordTableBodyContextOrThrow(); - const cellPosition = useCurrentTableCellPosition(); + const { cellPosition } = useContext(RecordTableCellContext); + + const setFocusPosition = useSetRecordTableFocusPosition(); const openTableCell = ( initialValue?: string, @@ -51,6 +54,8 @@ export const useOpenRecordTableCellFromCell = () => { isActionButtonClick, isNavigating, }); + + setFocusPosition(cellPosition); }; return { diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2.ts index 651d976fa..b09ca7623 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2.ts @@ -6,9 +6,8 @@ import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata' import { isFieldValueEmpty } from '@/object-record/record-field/utils/isFieldValueEmpty'; import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState'; import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector'; -import { SOFT_FOCUS_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-table/constants/SoftFocusClickOutsideListenerId'; +import { FOCUS_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-table/constants/FocusClickOutsideListenerId'; import { useLeaveTableFocus } from '@/object-record/record-table/hooks/internal/useLeaveTableFocus'; -import { useMoveEditModeToTableCellPosition } from '@/object-record/record-table/hooks/internal/useMoveEditModeToCellPosition'; import { TableCellPosition } from '@/object-record/record-table/types/TableCellPosition'; import { useDragSelect } from '@/ui/utilities/drag-select/hooks/useDragSelect'; import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; @@ -21,14 +20,15 @@ import { useRecordIndexContextOrThrow } from '@/object-record/record-index/conte import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState'; import { viewableRecordNameSingularState } from '@/object-record/record-right-drawer/states/viewableRecordNameSingularState'; import { RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-table/constants/RecordTableClickOutsideListenerId'; +import { recordTableCellEditModePositionComponentState } from '@/object-record/record-table/states/recordTableCellEditModePositionComponentState'; import { getDropdownFocusIdForRecordField } from '@/object-record/utils/getDropdownFocusIdForRecordField'; import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId'; import { useSetActiveDropdownFocusIdAndMemorizePrevious } from '@/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious'; import { useClickOustideListenerStates } from '@/ui/utilities/pointer-event/hooks/useClickOustideListenerStates'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType'; import { useNavigate } from 'react-router-dom'; import { TableHotkeyScope } from '../../types/TableHotkeyScope'; - export const DEFAULT_CELL_SCOPE: HotkeyScope = { scope: TableHotkeyScope.CellEditMode, }; @@ -50,14 +50,16 @@ export const useOpenRecordTableCellV2 = (tableScopeId: string) => { useClickOustideListenerStates(RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID); const { indexIdentifierUrl } = useRecordIndexContextOrThrow(); - const moveEditModeToTableCellPosition = - useMoveEditModeToTableCellPosition(tableScopeId); + const setCurrentTableCellInEditModePosition = useSetRecoilComponentStateV2( + recordTableCellEditModePositionComponentState, + tableScopeId, + ); const { setDragSelectionStartEnabled } = useDragSelect(); const leaveTableFocus = useLeaveTableFocus(tableScopeId); const { toggleClickOutsideListener } = useClickOutsideListener( - SOFT_FOCUS_CLICK_OUTSIDE_LISTENER_ID, + FOCUS_CLICK_OUTSIDE_LISTENER_ID, ); const initDraftValue = useInitDraftValueV2(); @@ -148,7 +150,7 @@ export const useOpenRecordTableCellV2 = (tableScopeId: string) => { recordId, }); - moveEditModeToTableCellPosition(cellPosition); + setCurrentTableCellInEditModePosition(cellPosition); initDraftValue({ value: initialValue, @@ -175,7 +177,7 @@ export const useOpenRecordTableCellV2 = (tableScopeId: string) => { getClickOutsideListenerIsActivatedState, setDragSelectionStartEnabled, openFieldInput, - moveEditModeToTableCellPosition, + setCurrentTableCellInEditModePosition, initDraftValue, toggleClickOutsideListener, setActiveDropdownFocusIdAndMemorizePrevious, diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useSelectedTableCellEditMode.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useSelectedTableCellEditMode.ts deleted file mode 100644 index d768c0617..000000000 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useSelectedTableCellEditMode.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { useCallback } from 'react'; - -import { useMoveEditModeToTableCellPosition } from '../../hooks/internal/useMoveEditModeToCellPosition'; - -export const useSelectedTableCellEditMode = ({ - scopeId, -}: { - scopeId: string; -}) => { - const moveEditModeToTableCellPosition = - useMoveEditModeToTableCellPosition(scopeId); - - const setSelectedTableCellEditMode = useCallback( - (row: number, column: number) => { - moveEditModeToTableCellPosition({ column, row }); - }, - [moveEditModeToTableCellPosition], - ); - - return { setSelectedTableCellEditMode }; -}; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useSetIsRecordTableFocusActive.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useSetIsRecordTableFocusActive.ts new file mode 100644 index 000000000..6461e3f1d --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useSetIsRecordTableFocusActive.ts @@ -0,0 +1,51 @@ +import { isRecordTableFocusActiveComponentState } from '@/object-record/record-table/states/isRecordTableFocusActiveComponentState'; +import { recordTableFocusPositionComponentState } from '@/object-record/record-table/states/recordTableFocusPositionComponentState'; +import { TableCellPosition } from '@/object-record/record-table/types/TableCellPosition'; +import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; +import { useRecoilCallback } from 'recoil'; + +export const useSetIsRecordTableFocusActive = (recordTableId?: string) => { + const isRecordTableFocusActiveState = useRecoilComponentCallbackStateV2( + isRecordTableFocusActiveComponentState, + recordTableId, + ); + + const focusPositionState = useRecoilComponentCallbackStateV2( + recordTableFocusPositionComponentState, + recordTableId, + ); + + const setIsFocusActive = useRecoilCallback( + ({ set }) => + (isRecordTableFocusActive: boolean, cellPosition: TableCellPosition) => { + const cellId = `record-table-cell-${cellPosition.column}-${cellPosition.row}`; + + const cellElement = document.getElementById(cellId); + + if (isRecordTableFocusActive) { + cellElement?.classList.add('focus-active'); + } + + if (!isRecordTableFocusActive) { + cellElement?.classList.remove('focus-active'); + } + + set(isRecordTableFocusActiveState, isRecordTableFocusActive); + }, + [isRecordTableFocusActiveState], + ); + + const setIsFocusActiveForCurrentPosition = useRecoilCallback( + ({ snapshot }) => + (isRecordTableFocusActive: boolean) => { + const currentPosition = snapshot + .getLoadable(focusPositionState) + .getValue(); + + setIsFocusActive(isRecordTableFocusActive, currentPosition); + }, + [setIsFocusActive, focusPositionState], + ); + + return { setIsFocusActive, setIsFocusActiveForCurrentPosition }; +}; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useSetSoftFocus.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useSetSoftFocus.ts deleted file mode 100644 index 62a9dd076..000000000 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useSetSoftFocus.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { useRecoilCallback } from 'recoil'; - -import { useSetSoftFocusPosition } from '@/object-record/record-table/hooks/internal/useSetSoftFocusPosition'; -import { TableCellPosition } from '@/object-record/record-table/types/TableCellPosition'; -import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope'; - -import { isSoftFocusActiveComponentState } from '@/object-record/record-table/states/isSoftFocusActiveComponentState'; -import { currentHotkeyScopeState } from '@/ui/utilities/hotkey/states/internal/currentHotkeyScopeState'; -import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope'; -import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; -import { TableHotkeyScope } from '../../types/TableHotkeyScope'; - -export const useSetSoftFocus = (recordTableId?: string) => { - const setSoftFocusPosition = useSetSoftFocusPosition(recordTableId); - - const isSoftFocusActiveState = useRecoilComponentCallbackStateV2( - isSoftFocusActiveComponentState, - recordTableId, - ); - - const setHotkeyScope = useSetHotkeyScope(); - - return useRecoilCallback( - ({ snapshot, set }) => - (newPosition: TableCellPosition) => { - setSoftFocusPosition(newPosition); - - set(isSoftFocusActiveState, true); - - if ( - snapshot.getLoadable(currentHotkeyScopeState).getValue().scope !== - AppHotkeyScope.CommandMenuOpen - ) { - setHotkeyScope(TableHotkeyScope.TableSoftFocus); - } - }, - [setSoftFocusPosition, isSoftFocusActiveState, setHotkeyScope], - ); -}; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useSetSoftFocusOnCurrentTableCell.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useSetSoftFocusOnCurrentTableCell.ts deleted file mode 100644 index 3bee41b33..000000000 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useSetSoftFocusOnCurrentTableCell.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { useSetSoftFocus } from '@/object-record/record-table/record-table-cell/hooks/useSetSoftFocus'; - -import { useCurrentTableCellPosition } from './useCurrentCellPosition'; - -export const useSetSoftFocusOnCurrentTableCell = () => { - const setSoftFocus = useSetSoftFocus(); - - const currentTableCellPosition = useCurrentTableCellPosition(); - - return () => { - setSoftFocus(currentTableCellPosition); - }; -}; diff --git a/packages/twenty-front/src/modules/object-record/record-table/states/tableCellWidthsComponentState.ts b/packages/twenty-front/src/modules/object-record/record-table/states/isRecordTableFocusActiveComponentState.ts similarity index 50% rename from packages/twenty-front/src/modules/object-record/record-table/states/tableCellWidthsComponentState.ts rename to packages/twenty-front/src/modules/object-record/record-table/states/isRecordTableFocusActiveComponentState.ts index 54ab3dacf..36466471d 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/states/tableCellWidthsComponentState.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/states/isRecordTableFocusActiveComponentState.ts @@ -1,8 +1,9 @@ import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; -export const tableCellWidthsComponentState = createComponentStateV2({ - key: 'tableCellWidthsComponentState', - defaultValue: [], - componentInstanceContext: RecordTableComponentInstanceContext, -}); +export const isRecordTableFocusActiveComponentState = + createComponentStateV2({ + key: 'isRecordTableFocusActiveComponentState', + defaultValue: false, + componentInstanceContext: RecordTableComponentInstanceContext, + }); diff --git a/packages/twenty-front/src/modules/object-record/record-table/states/isSoftFocusActiveComponentState.ts b/packages/twenty-front/src/modules/object-record/record-table/states/isSoftFocusActiveComponentState.ts deleted file mode 100644 index a077573b2..000000000 --- a/packages/twenty-front/src/modules/object-record/record-table/states/isSoftFocusActiveComponentState.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; -import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; - -export const isSoftFocusActiveComponentState = createComponentStateV2({ - key: 'isSoftFocusActiveComponentState', - defaultValue: false, - componentInstanceContext: RecordTableComponentInstanceContext, -}); diff --git a/packages/twenty-front/src/modules/object-record/record-table/states/isSoftFocusOnTableCellComponentFamilyState.ts b/packages/twenty-front/src/modules/object-record/record-table/states/isSoftFocusOnTableCellComponentFamilyState.ts deleted file mode 100644 index 57ee967a2..000000000 --- a/packages/twenty-front/src/modules/object-record/record-table/states/isSoftFocusOnTableCellComponentFamilyState.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; -import { createComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentFamilyStateV2'; -import { TableCellPosition } from '../types/TableCellPosition'; - -export const isSoftFocusOnTableCellComponentFamilyState = - createComponentFamilyStateV2({ - key: 'isSoftFocusOnTableCellComponentFamilyState', - defaultValue: false, - componentInstanceContext: RecordTableComponentInstanceContext, - }); diff --git a/packages/twenty-front/src/modules/object-record/record-table/states/isSoftFocusUsingMouseState.ts b/packages/twenty-front/src/modules/object-record/record-table/states/isSoftFocusUsingMouseState.ts deleted file mode 100644 index d1ad40261..000000000 --- a/packages/twenty-front/src/modules/object-record/record-table/states/isSoftFocusUsingMouseState.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { createState } from 'twenty-ui/utilities'; -export const isSoftFocusUsingMouseState = createState({ - key: 'isSoftFocusUsingMouseState', - defaultValue: false, -}); diff --git a/packages/twenty-front/src/modules/object-record/record-table/states/isTableCellInEditModeComponentFamilyState.ts b/packages/twenty-front/src/modules/object-record/record-table/states/isTableCellInEditModeComponentFamilyState.ts deleted file mode 100644 index ec71ea571..000000000 --- a/packages/twenty-front/src/modules/object-record/record-table/states/isTableCellInEditModeComponentFamilyState.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; -import { createComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentFamilyStateV2'; -import { TableCellPosition } from '../types/TableCellPosition'; - -export const isTableCellInEditModeComponentFamilyState = - createComponentFamilyStateV2({ - key: 'isTableCellInEditModeComponentFamilyState', - defaultValue: false, - componentInstanceContext: RecordTableComponentInstanceContext, - }); diff --git a/packages/twenty-front/src/modules/object-record/record-table/states/currentTableCellInEditModePositionComponentState.ts b/packages/twenty-front/src/modules/object-record/record-table/states/recordTableCellEditModePositionComponentState.ts similarity index 62% rename from packages/twenty-front/src/modules/object-record/record-table/states/currentTableCellInEditModePositionComponentState.ts rename to packages/twenty-front/src/modules/object-record/record-table/states/recordTableCellEditModePositionComponentState.ts index 77adc75b2..7cccbf1f7 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/states/currentTableCellInEditModePositionComponentState.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/states/recordTableCellEditModePositionComponentState.ts @@ -2,12 +2,9 @@ import { RecordTableComponentInstanceContext } from '@/object-record/record-tabl import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; import { TableCellPosition } from '../types/TableCellPosition'; -export const currentTableCellInEditModePositionComponentState = - createComponentStateV2({ - key: 'currentTableCellInEditModePositionComponentState', - defaultValue: { - row: 0, - column: 1, - }, +export const recordTableCellEditModePositionComponentState = + createComponentStateV2({ + key: 'recordTableCellEditModePositionComponentState', + defaultValue: null, componentInstanceContext: RecordTableComponentInstanceContext, }); diff --git a/packages/twenty-front/src/modules/object-record/record-table/states/softFocusPositionComponentState.ts b/packages/twenty-front/src/modules/object-record/record-table/states/recordTableFocusPositionComponentState.ts similarity index 82% rename from packages/twenty-front/src/modules/object-record/record-table/states/softFocusPositionComponentState.ts rename to packages/twenty-front/src/modules/object-record/record-table/states/recordTableFocusPositionComponentState.ts index c59f2cd1f..c479676fe 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/states/softFocusPositionComponentState.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/states/recordTableFocusPositionComponentState.ts @@ -2,9 +2,9 @@ import { RecordTableComponentInstanceContext } from '@/object-record/record-tabl import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; import { TableCellPosition } from '../types/TableCellPosition'; -export const softFocusPositionComponentState = +export const recordTableFocusPositionComponentState = createComponentStateV2({ - key: 'softFocusPositionComponentState', + key: 'recordTableFocusPositionComponentState', defaultValue: { row: 0, column: 1, diff --git a/packages/twenty-front/src/modules/object-record/record-table/states/recordTableHoverPositionComponentState.ts b/packages/twenty-front/src/modules/object-record/record-table/states/recordTableHoverPositionComponentState.ts new file mode 100644 index 000000000..f4ab8da5c --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-table/states/recordTableHoverPositionComponentState.ts @@ -0,0 +1,10 @@ +import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; +import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; +import { TableCellPosition } from '../types/TableCellPosition'; + +export const recordTableHoverPositionComponentState = + createComponentStateV2({ + key: 'recordTableHoverPositionComponentState', + defaultValue: null, + componentInstanceContext: RecordTableComponentInstanceContext, + }); diff --git a/packages/twenty-front/src/modules/object-record/record-table/states/selectors/isSomeCellInEditModeComponentSelector.ts b/packages/twenty-front/src/modules/object-record/record-table/states/selectors/isSomeCellInEditModeComponentSelector.ts new file mode 100644 index 000000000..17ef7c8cb --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-table/states/selectors/isSomeCellInEditModeComponentSelector.ts @@ -0,0 +1,20 @@ +import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; +import { recordTableCellEditModePositionComponentState } from '@/object-record/record-table/states/recordTableCellEditModePositionComponentState'; +import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2'; +import { isDefined } from 'twenty-shared/utils'; + +export const isSomeCellInEditModeComponentSelector = createComponentSelectorV2({ + key: 'isSomeCellInEditModeComponentSelector', + componentInstanceContext: RecordTableComponentInstanceContext, + get: + ({ instanceId }) => + ({ get }) => { + const currentTableCellInEditModePosition = get( + recordTableCellEditModePositionComponentState.atomFamily({ + instanceId, + }), + ); + + return isDefined(currentTableCellInEditModePosition); + }, +}); diff --git a/packages/twenty-front/src/modules/object-record/record-table/states/tableRecordGroupIdsComponentState.ts b/packages/twenty-front/src/modules/object-record/record-table/states/tableRecordGroupIdsComponentState.ts deleted file mode 100644 index fd6f666a9..000000000 --- a/packages/twenty-front/src/modules/object-record/record-table/states/tableRecordGroupIdsComponentState.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition'; -import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; -import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; - -export const tableRecordGroupIdsComponentState = createComponentStateV2< - RecordGroupDefinition['id'][] ->({ - key: 'tableRecordGroupIdsComponentState', - defaultValue: [], - componentInstanceContext: RecordTableComponentInstanceContext, -}); diff --git a/packages/twenty-front/src/modules/object-record/record-table/types/TableHotkeyScope.ts b/packages/twenty-front/src/modules/object-record/record-table/types/TableHotkeyScope.ts index 37c831819..b1bae4957 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/types/TableHotkeyScope.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/types/TableHotkeyScope.ts @@ -2,6 +2,6 @@ export enum TableHotkeyScope { CellDoubleTextInput = 'cell-double-text-input', CellEditMode = 'cell-edit-mode', CellDateEditMode = 'cell-date-edit-mode', - TableSoftFocus = 'table-soft-focus', + TableFocus = 'table-focus', Table = 'table', } diff --git a/packages/twenty-front/src/testing/decorators/RecordTableDecorator.tsx b/packages/twenty-front/src/testing/decorators/RecordTableDecorator.tsx index 93315ff85..0fa6d1ff8 100644 --- a/packages/twenty-front/src/testing/decorators/RecordTableDecorator.tsx +++ b/packages/twenty-front/src/testing/decorators/RecordTableDecorator.tsx @@ -85,7 +85,7 @@ const InternalTableContextProviders = ({ onOpenTableCell: () => {}, onActionMenuDropdownOpened: () => {}, onMoveFocus: () => {}, - onMoveSoftFocusToCurrentCell: () => {}, + onMoveHoverToCurrentCell: () => {}, }} > {children}