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
This commit is contained in:
Raphaël Bosi
2025-04-17 18:40:24 +02:00
committed by GitHub
parent 18a89b5152
commit dd0ea2366f
72 changed files with 920 additions and 1150 deletions

View File

@ -6,9 +6,11 @@ import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record
import { RecordTableBodyEffectsWrapper } from '@/object-record/record-table/components/RecordTableBodyEffectsWrapper'; import { RecordTableBodyEffectsWrapper } from '@/object-record/record-table/components/RecordTableBodyEffectsWrapper';
import { RecordTableContent } from '@/object-record/record-table/components/RecordTableContent'; import { RecordTableContent } from '@/object-record/record-table/components/RecordTableContent';
import { RecordTableEmpty } from '@/object-record/record-table/components/RecordTableEmpty'; 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 { RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-table/constants/RecordTableClickOutsideListenerId';
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext'; import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable'; 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 { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState';
import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useClickOutsideListener'; import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useClickOutsideListener';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
@ -44,6 +46,11 @@ export const RecordTable = () => {
const recordTableIsEmpty = const recordTableIsEmpty =
!isRecordTableInitialLoading && allRecordIds.length === 0; !isRecordTableInitialLoading && allRecordIds.length === 0;
const isRecordTableFocusActive = useRecoilComponentValueV2(
isRecordTableFocusActiveComponentState,
recordTableId,
);
if (!isNonEmptyString(objectNameSingular)) { if (!isNonEmptyString(objectNameSingular)) {
return <></>; return <></>;
} }
@ -64,6 +71,8 @@ export const RecordTable = () => {
tableBodyRef={tableBodyRef} tableBodyRef={tableBodyRef}
/> />
{isRecordTableFocusActive && <RecordTableScrollToFocusedElementEffect />}
{recordTableIsEmpty && !hasRecordGroups ? ( {recordTableIsEmpty && !hasRecordGroups ? (
<RecordTableEmpty <RecordTableEmpty
tableBodyRef={tableBodyRef} tableBodyRef={tableBodyRef}

View File

@ -1,10 +1,10 @@
import { RecordTableBodyEscapeHotkeyEffect } from '@/object-record/record-table/record-table-body/components/RecordTableBodyEscapeHotkeyEffect'; import { RecordTableBodyEscapeHotkeyEffect } from '@/object-record/record-table/record-table-body/components/RecordTableBodyEscapeHotkeyEffect';
import { RecordTableBodySoftFocusClickOutsideEffect } from '@/object-record/record-table/record-table-body/components/RecordTableBodySoftFocusClickOutsideEffect'; import { RecordTableBodyFocusClickOutsideEffect } from '@/object-record/record-table/record-table-body/components/RecordTableBodyFocusClickOutsideEffect';
import { RecordTableBodySoftFocusKeyboardEffect } from '@/object-record/record-table/record-table-body/components/RecordTableBodySoftFocusKeyboardEffect'; import { RecordTableBodyFocusKeyboardEffect } from '@/object-record/record-table/record-table-body/components/RecordTableBodyFocusKeyboardEffect';
import { RecordTableNoRecordGroupBodyEffect } from '@/object-record/record-table/record-table-body/components/RecordTableNoRecordGroupBodyEffect'; import { RecordTableNoRecordGroupBodyEffect } from '@/object-record/record-table/record-table-body/components/RecordTableNoRecordGroupBodyEffect';
import { RecordTableRecordGroupBodyEffects } from '@/object-record/record-table/record-table-body/components/RecordTableRecordGroupBodyEffects'; import { RecordTableRecordGroupBodyEffects } from '@/object-record/record-table/record-table-body/components/RecordTableRecordGroupBodyEffects';
import { isAtLeastOneTableRowSelectedSelector } from '@/object-record/record-table/record-table-row/states/isAtLeastOneTableRowSelectedSelector'; import { isAtLeastOneTableRowSelectedSelector } from '@/object-record/record-table/record-table-row/states/isAtLeastOneTableRowSelectedSelector';
import { isSoftFocusActiveComponentState } from '@/object-record/record-table/states/isSoftFocusActiveComponentState'; import { isRecordTableFocusActiveComponentState } from '@/object-record/record-table/states/isRecordTableFocusActiveComponentState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
export interface RecordTableBodyEffectsWrapperProps { export interface RecordTableBodyEffectsWrapperProps {
@ -20,8 +20,8 @@ export const RecordTableBodyEffectsWrapper = ({
isAtLeastOneTableRowSelectedSelector, isAtLeastOneTableRowSelectedSelector,
); );
const isSoftFocusActiveState = useRecoilComponentValueV2( const isRecordTableFocusActive = useRecoilComponentValueV2(
isSoftFocusActiveComponentState, isRecordTableFocusActiveComponentState,
); );
return ( return (
@ -32,11 +32,9 @@ export const RecordTableBodyEffectsWrapper = ({
<RecordTableNoRecordGroupBodyEffect /> <RecordTableNoRecordGroupBodyEffect />
)} )}
{isAtLeastOneRecordSelected && <RecordTableBodyEscapeHotkeyEffect />} {isAtLeastOneRecordSelected && <RecordTableBodyEscapeHotkeyEffect />}
{isSoftFocusActiveState && <RecordTableBodySoftFocusKeyboardEffect />} {isRecordTableFocusActive && <RecordTableBodyFocusKeyboardEffect />}
{isSoftFocusActiveState && ( {isRecordTableFocusActive && (
<RecordTableBodySoftFocusClickOutsideEffect <RecordTableBodyFocusClickOutsideEffect tableBodyRef={tableBodyRef} />
tableBodyRef={tableBodyRef}
/>
)} )}
</> </>
); );

View File

@ -1,5 +1,4 @@
import { useContext, useEffect, useRef } from 'react'; import { useContext } from 'react';
import { useRecoilValue } from 'recoil';
import { Key } from 'ts-key-enum'; import { Key } from 'ts-key-enum';
import { useClearField } from '@/object-record/record-field/hooks/useClearField'; 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 { useIsFieldInputOnly } from '@/object-record/record-field/hooks/useIsFieldInputOnly';
import { useToggleEditOnlyInput } from '@/object-record/record-field/hooks/useToggleEditOnlyInput'; import { useToggleEditOnlyInput } from '@/object-record/record-field/hooks/useToggleEditOnlyInput';
import { useOpenRecordTableCellFromCell } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellFromCell'; 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 { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { isNonTextWritingKey } from '@/ui/utilities/hotkey/utils/isNonTextWritingKey'; import { isNonTextWritingKey } from '@/ui/utilities/hotkey/utils/isNonTextWritingKey';
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext'; import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
import { currentHotkeyScopeState } from '@/ui/utilities/hotkey/states/internal/currentHotkeyScopeState'; import { TableHotkeyScope } from '../types/TableHotkeyScope';
import { TableHotkeyScope } from '../../types/TableHotkeyScope';
export const RecordTableCellSoftFocusModeHotkeysSetterEffect = () => {
const currentHotkeyScope = useRecoilValue(currentHotkeyScopeState);
export const RecordTableFocusModeHotkeysSetterEffect = () => {
const { openTableCell } = useOpenRecordTableCellFromCell(); const { openTableCell } = useOpenRecordTableCellFromCell();
const { isReadOnly } = useContext(FieldContext); const { isReadOnly } = useContext(FieldContext);
@ -26,21 +21,9 @@ export const RecordTableCellSoftFocusModeHotkeysSetterEffect = () => {
const isFieldClearable = useIsFieldClearable(); const isFieldClearable = useIsFieldClearable();
const toggleEditOnlyInput = useToggleEditOnlyInput(); const toggleEditOnlyInput = useToggleEditOnlyInput();
const scrollRef = useRef<HTMLDivElement>(null);
const isSoftFocusUsingMouse = useRecoilValue(isSoftFocusUsingMouseState);
const clearField = useClearField(); const clearField = useClearField();
useEffect(() => {
if (currentHotkeyScope.scope !== TableHotkeyScope.TableSoftFocus) {
return;
}
if (!isSoftFocusUsingMouse) {
scrollRef.current?.scrollIntoView({ block: 'nearest' });
}
}, [currentHotkeyScope.scope, isSoftFocusUsingMouse]);
useScopedHotkeys( useScopedHotkeys(
[Key.Backspace, Key.Delete], [Key.Backspace, Key.Delete],
() => { () => {
@ -48,7 +31,7 @@ export const RecordTableCellSoftFocusModeHotkeysSetterEffect = () => {
clearField(); clearField();
} }
}, },
TableHotkeyScope.TableSoftFocus, TableHotkeyScope.TableFocus,
[clearField, isFieldClearable, isFieldInputOnly], [clearField, isFieldClearable, isFieldInputOnly],
); );
@ -65,8 +48,8 @@ export const RecordTableCellSoftFocusModeHotkeysSetterEffect = () => {
toggleEditOnlyInput(); toggleEditOnlyInput();
} }
}, },
TableHotkeyScope.TableSoftFocus, TableHotkeyScope.TableFocus,
[openTableCell], [openTableCell, isFieldInputOnly, toggleEditOnlyInput, isReadOnly],
); );
useScopedHotkeys( useScopedHotkeys(
@ -93,8 +76,8 @@ export const RecordTableCellSoftFocusModeHotkeysSetterEffect = () => {
openTableCell(keyboardEvent.key); openTableCell(keyboardEvent.key);
} }
}, },
TableHotkeyScope.TableSoftFocus, TableHotkeyScope.TableFocus,
[openTableCell], [openTableCell, isFieldInputOnly, toggleEditOnlyInput, isReadOnly],
{ {
preventDefault: false, preventDefault: false,
}, },

View File

@ -3,7 +3,7 @@ import { useRecordTableContextOrThrow } from '@/object-record/record-table/conte
import { useHandleContainerMouseEnter } from '@/object-record/record-table/hooks/internal/useHandleContainerMouseEnter'; import { useHandleContainerMouseEnter } from '@/object-record/record-table/hooks/internal/useHandleContainerMouseEnter';
import { useRecordTableMoveFocus } from '@/object-record/record-table/hooks/useRecordTableMoveFocus'; import { useRecordTableMoveFocus } from '@/object-record/record-table/hooks/useRecordTableMoveFocus';
import { useCloseRecordTableCellNoGroup } from '@/object-record/record-table/record-table-cell/hooks/internal/useCloseRecordTableCellNoGroup'; 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 { import {
OpenTableCellArgs, OpenTableCellArgs,
useOpenRecordTableCellV2, useOpenRecordTableCellV2,
@ -40,13 +40,10 @@ export const RecordTableNoRecordGroupBodyContextProvider = ({
closeTableCellNoGroup(); closeTableCellNoGroup();
}; };
const { moveSoftFocusToCurrentCell } = const { moveHoverToCurrentCell } = useMoveHoverToCurrentCell(recordTableId);
useMoveSoftFocusToCurrentCellOnHover(recordTableId);
const handleMoveSoftFocusToCurrentCell = ( const handleMoveHoverToCurrentCell = (cellPosition: TableCellPosition) => {
cellPosition: TableCellPosition, moveHoverToCurrentCell(cellPosition);
) => {
moveSoftFocusToCurrentCell(cellPosition);
}; };
const { triggerActionMenuDropdown } = useTriggerActionMenuDropdown({ const { triggerActionMenuDropdown } = useTriggerActionMenuDropdown({
@ -70,7 +67,7 @@ export const RecordTableNoRecordGroupBodyContextProvider = ({
onOpenTableCell: handleOpenTableCell, onOpenTableCell: handleOpenTableCell,
onMoveFocus: handleMoveFocus, onMoveFocus: handleMoveFocus,
onCloseTableCell: handleCloseTableCell, onCloseTableCell: handleCloseTableCell,
onMoveSoftFocusToCurrentCell: handleMoveSoftFocusToCurrentCell, onMoveHoverToCurrentCell: handleMoveHoverToCurrentCell,
onActionMenuDropdownOpened: handleActionMenuDropdown, onActionMenuDropdownOpened: handleActionMenuDropdown,
onCellMouseEnter: handleContainerMouseEnter, onCellMouseEnter: handleContainerMouseEnter,
}} }}

View File

@ -3,7 +3,7 @@ import { useRecordTableContextOrThrow } from '@/object-record/record-table/conte
import { useHandleContainerMouseEnter } from '@/object-record/record-table/hooks/internal/useHandleContainerMouseEnter'; import { useHandleContainerMouseEnter } from '@/object-record/record-table/hooks/internal/useHandleContainerMouseEnter';
import { useRecordTableMoveFocus } from '@/object-record/record-table/hooks/useRecordTableMoveFocus'; import { useRecordTableMoveFocus } from '@/object-record/record-table/hooks/useRecordTableMoveFocus';
import { useCloseRecordTableCellInGroup } from '@/object-record/record-table/record-table-cell/hooks/internal/useCloseRecordTableCellInGroup'; 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 { import {
OpenTableCellArgs, OpenTableCellArgs,
useOpenRecordTableCellV2, useOpenRecordTableCellV2,
@ -41,13 +41,10 @@ export const RecordTableRecordGroupBodyContextProvider = ({
closeTableCellInGroup(); closeTableCellInGroup();
}; };
const { moveSoftFocusToCurrentCell } = const { moveHoverToCurrentCell } = useMoveHoverToCurrentCell(recordTableId);
useMoveSoftFocusToCurrentCellOnHover(recordTableId);
const handleMoveSoftFocusToCurrentCell = ( const handleMoveHoverToCurrentCell = (cellPosition: TableCellPosition) => {
cellPosition: TableCellPosition, moveHoverToCurrentCell(cellPosition);
) => {
moveSoftFocusToCurrentCell(cellPosition);
}; };
const { triggerActionMenuDropdown } = useTriggerActionMenuDropdown({ const { triggerActionMenuDropdown } = useTriggerActionMenuDropdown({
@ -71,7 +68,7 @@ export const RecordTableRecordGroupBodyContextProvider = ({
onOpenTableCell: handleOpenTableCell, onOpenTableCell: handleOpenTableCell,
onMoveFocus: handleMoveFocus, onMoveFocus: handleMoveFocus,
onCloseTableCell: handlecloseTableCellInGroup, onCloseTableCell: handlecloseTableCellInGroup,
onMoveSoftFocusToCurrentCell: handleMoveSoftFocusToCurrentCell, onMoveHoverToCurrentCell: handleMoveHoverToCurrentCell,
onActionMenuDropdownOpened: handleActionMenuDropdown, onActionMenuDropdownOpened: handleActionMenuDropdown,
onCellMouseEnter: handleContainerMouseEnter, onCellMouseEnter: handleContainerMouseEnter,
}} }}

View File

@ -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;
};

View File

@ -93,7 +93,7 @@ const meta: Meta = {
onOpenTableCell: () => {}, onOpenTableCell: () => {},
onMoveFocus: () => {}, onMoveFocus: () => {},
onCloseTableCell: () => {}, onCloseTableCell: () => {},
onMoveSoftFocusToCurrentCell: () => {}, onMoveHoverToCurrentCell: () => {},
onActionMenuDropdownOpened: () => {}, onActionMenuDropdownOpened: () => {},
onCellMouseEnter: () => {}, onCellMouseEnter: () => {},
}} }}
@ -122,10 +122,7 @@ const meta: Meta = {
<RecordTableCellContext.Provider <RecordTableCellContext.Provider
value={{ value={{
columnDefinition: mockPerformance.fieldDefinition, columnDefinition: mockPerformance.fieldDefinition,
columnIndex: 0,
cellPosition: { row: 0, column: 0 }, cellPosition: { row: 0, column: 0 },
hasSoftFocus: false,
isInEditMode: false,
}} }}
> >
<FieldContext.Provider <FieldContext.Provider

View File

@ -0,0 +1,2 @@
export const FOCUS_CLICK_OUTSIDE_LISTENER_ID =
'focus-click-outside-listener-id';

View File

@ -1,2 +0,0 @@
export const SOFT_FOCUS_CLICK_OUTSIDE_LISTENER_ID =
'soft-focus-click-outside-listener-id';

View File

@ -11,7 +11,7 @@ export type RecordTableBodyContextProps = {
onOpenTableCell: (args: OpenTableCellArgs) => void; onOpenTableCell: (args: OpenTableCellArgs) => void;
onMoveFocus: (direction: MoveFocusDirection) => void; onMoveFocus: (direction: MoveFocusDirection) => void;
onCloseTableCell: () => void; onCloseTableCell: () => void;
onMoveSoftFocusToCurrentCell: (cellPosition: TableCellPosition) => void; onMoveHoverToCurrentCell: (cellPosition: TableCellPosition) => void;
onActionMenuDropdownOpened: ( onActionMenuDropdownOpened: (
event: React.MouseEvent, event: React.MouseEvent,
recordId: string, recordId: string,

View File

@ -6,9 +6,6 @@ import { TableCellPosition } from '@/object-record/record-table/types/TableCellP
export type RecordTableCellContextValue = { export type RecordTableCellContextValue = {
columnDefinition: ColumnDefinition<FieldMetadata>; columnDefinition: ColumnDefinition<FieldMetadata>;
columnIndex: number;
isInEditMode: boolean;
hasSoftFocus: boolean;
cellPosition: TableCellPosition; cellPosition: TableCellPosition;
}; };

View File

@ -1,45 +1,27 @@
import { useRecoilCallback } from 'recoil'; import { useRecoilCallback } from 'recoil';
import { currentTableCellInEditModePositionComponentState } from '@/object-record/record-table/states/currentTableCellInEditModePositionComponentState'; import { recordTableCellEditModePositionComponentState } from '@/object-record/record-table/states/recordTableCellEditModePositionComponentState';
import { isTableCellInEditModeComponentFamilyState } from '@/object-record/record-table/states/isTableCellInEditModeComponentFamilyState';
import { useGoBackToPreviousDropdownFocusId } from '@/ui/layout/dropdown/hooks/useGoBackToPreviousDropdownFocusId'; 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'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
export const useCloseCurrentTableCellInEditMode = (recordTableId?: string) => { export const useCloseCurrentTableCellInEditMode = (recordTableId?: string) => {
const currentTableCellInEditModePositionState = const currentTableCellInEditModePositionState =
useRecoilComponentCallbackStateV2( useRecoilComponentCallbackStateV2(
currentTableCellInEditModePositionComponentState, recordTableCellEditModePositionComponentState,
recordTableId, recordTableId,
); );
const isTableCellInEditModeFamilyState = useRecoilComponentCallbackStateV2(
isTableCellInEditModeComponentFamilyState,
recordTableId,
);
const { goBackToPreviousDropdownFocusId } = const { goBackToPreviousDropdownFocusId } =
useGoBackToPreviousDropdownFocusId(); useGoBackToPreviousDropdownFocusId();
return useRecoilCallback( return useRecoilCallback(
({ set, snapshot }) => { ({ set }) => {
return async () => { return async () => {
const currentTableCellInEditModePosition = getSnapshotValue( set(currentTableCellInEditModePositionState, null);
snapshot,
currentTableCellInEditModePositionState,
);
set(
isTableCellInEditModeFamilyState(currentTableCellInEditModePosition),
false,
);
goBackToPreviousDropdownFocusId(); goBackToPreviousDropdownFocusId();
}; };
}, },
[ [currentTableCellInEditModePositionState, goBackToPreviousDropdownFocusId],
currentTableCellInEditModePositionState,
isTableCellInEditModeFamilyState,
goBackToPreviousDropdownFocusId,
],
); );
}; };

View File

@ -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,
],
);
};

View File

@ -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],
);
};

View File

@ -1,10 +1,7 @@
import { useRecoilCallback } from 'recoil'; import { useRecoilCallback } from 'recoil';
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 { currentTableCellInEditModePositionComponentState } from '@/object-record/record-table/states/currentTableCellInEditModePositionComponentState'; import { isSomeCellInEditModeComponentSelector } from '@/object-record/record-table/states/selectors/isSomeCellInEditModeComponentSelector';
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 { TableCellPosition } from '@/object-record/record-table/types/TableCellPosition'; import { TableCellPosition } from '@/object-record/record-table/types/TableCellPosition';
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
@ -18,54 +15,26 @@ export const useHandleContainerMouseEnter = ({
}: { }: {
recordTableId: string; recordTableId: string;
}) => { }) => {
const { moveSoftFocusToCurrentCell } = const { moveHoverToCurrentCell } = useMoveHoverToCurrentCell(recordTableId);
useMoveSoftFocusToCurrentCellOnHover(recordTableId);
const currentTableCellInEditModePositionState = const isSomeCellInEditModeSelector = useRecoilComponentCallbackStateV2(
useRecoilComponentCallbackStateV2( isSomeCellInEditModeComponentSelector,
currentTableCellInEditModePositionComponentState,
recordTableId,
);
const isSoftFocusOnTableCellFamilyState = useRecoilComponentCallbackStateV2(
isSoftFocusOnTableCellComponentFamilyState,
recordTableId,
);
const isTableCellInEditModeFamilyState = useRecoilComponentCallbackStateV2(
isTableCellInEditModeComponentFamilyState,
recordTableId, recordTableId,
); );
const handleContainerMouseEnter = useRecoilCallback( const handleContainerMouseEnter = useRecoilCallback(
({ snapshot, set }) => ({ snapshot }) =>
({ cellPosition }: HandleContainerMouseEnterArgs) => { ({ cellPosition }: HandleContainerMouseEnterArgs) => {
const isSoftFocusOnTableCell = getSnapshotValue(
snapshot,
isSoftFocusOnTableCellFamilyState(cellPosition),
);
const currentTableCellInEditModePosition = getSnapshotValue(
snapshot,
currentTableCellInEditModePositionState,
);
const isSomeCellInEditMode = getSnapshotValue( const isSomeCellInEditMode = getSnapshotValue(
snapshot, snapshot,
isTableCellInEditModeFamilyState(currentTableCellInEditModePosition), isSomeCellInEditModeSelector,
); );
if (!isSomeCellInEditMode && !isSoftFocusOnTableCell) { if (!isSomeCellInEditMode) {
moveSoftFocusToCurrentCell(cellPosition); moveHoverToCurrentCell(cellPosition);
set(isSoftFocusUsingMouseState, true);
} }
}, },
[ [isSomeCellInEditModeSelector, moveHoverToCurrentCell],
isSoftFocusOnTableCellFamilyState,
currentTableCellInEditModePositionState,
isTableCellInEditModeFamilyState,
moveSoftFocusToCurrentCell,
],
); );
return { return {

View File

@ -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 { 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 { 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 { 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) => { export const useLeaveTableFocus = (recordTableId?: string) => {
const recordTableIdFromContext = useAvailableComponentInstanceIdOrThrow( const recordTableIdFromContext = useAvailableComponentInstanceIdOrThrow(
@ -15,33 +9,17 @@ export const useLeaveTableFocus = (recordTableId?: string) => {
recordTableId, recordTableId,
); );
const disableSoftFocus = useDisableSoftFocus(recordTableIdFromContext);
const isSoftFocusActiveState = useRecoilComponentCallbackStateV2(
isSoftFocusActiveComponentState,
recordTableIdFromContext,
);
const resetTableRowSelection = useResetTableRowSelection( const resetTableRowSelection = useResetTableRowSelection(
recordTableIdFromContext, recordTableIdFromContext,
); );
return useRecoilCallback( const { setIsFocusActiveForCurrentPosition } = useSetIsRecordTableFocusActive(
({ snapshot }) => recordTableIdFromContext,
() => {
const isSoftFocusActive = getSnapshotValue(
snapshot,
isSoftFocusActiveState,
);
resetTableRowSelection();
if (!isSoftFocusActive) {
return;
}
disableSoftFocus();
},
[disableSoftFocus, isSoftFocusActiveState, resetTableRowSelection],
); );
return () => {
resetTableRowSelection();
setIsFocusActiveForCurrentPosition(false);
};
}; };

View File

@ -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],
);
};

View File

@ -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],
);
};

View File

@ -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,
],
);
};

View File

@ -1,11 +1,9 @@
import { useRecoilCallback, useSetRecoilState } from 'recoil'; import { useRecoilCallback } from 'recoil';
import { Key } from 'ts-key-enum'; import { Key } from 'ts-key-enum';
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'; 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 { useSetHasUserSelectedAllRows } from '@/object-record/record-table/hooks/internal/useSetAllRowSelectedState';
import { useRecordTableMoveFocus } from '@/object-record/record-table/hooks/useRecordTableMoveFocus'; 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 { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope'; import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
@ -15,6 +13,7 @@ import { useUpsertRecordFromState } from '../../hooks/useUpsertRecordFromState';
import { ColumnDefinition } from '../types/ColumnDefinition'; import { ColumnDefinition } from '../types/ColumnDefinition';
import { TableHotkeyScope } from '../types/TableHotkeyScope'; 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 { availableTableColumnsComponentState } from '@/object-record/record-table/states/availableTableColumnsComponentState';
import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext';
import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState'; 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 { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { useDisableSoftFocus } from './internal/useDisableSoftFocus';
import { useLeaveTableFocus } from './internal/useLeaveTableFocus'; import { useLeaveTableFocus } from './internal/useLeaveTableFocus';
import { useResetTableRowSelection } from './internal/useResetTableRowSelection'; import { useResetTableRowSelection } from './internal/useResetTableRowSelection';
import { useSelectAllRows } from './internal/useSelectAllRows'; import { useSelectAllRows } from './internal/useSelectAllRows';
import { useSetRecordTableData } from './internal/useSetRecordTableData'; import { useSetRecordTableData } from './internal/useSetRecordTableData';
import { useSetRecordTableFocusPosition } from './internal/useSetRecordTableFocusPosition';
import { useSetRowSelectedState } from './internal/useSetRowSelectedState'; import { useSetRowSelectedState } from './internal/useSetRowSelectedState';
import { useSetSoftFocusPosition } from './internal/useSetSoftFocusPosition';
type useRecordTableProps = { type useRecordTableProps = {
recordTableId?: string; recordTableId?: string;
@ -145,25 +143,23 @@ export const useRecordTable = (props?: useRecordTableProps) => {
const upsertRecordTableItem = useUpsertRecordFromState; const upsertRecordTableItem = useUpsertRecordFromState;
const setSoftFocusPosition = useSetSoftFocusPosition(recordTableId); const setFocusPosition = useSetRecordTableFocusPosition(recordTableId);
const { setIsFocusActiveForCurrentPosition } =
useSetIsRecordTableFocusActive(recordTableId);
const { moveDown, moveLeft, moveRight, moveUp } = const { moveDown, moveLeft, moveRight, moveUp } =
useRecordTableMoveFocus(recordTableId); useRecordTableMoveFocus(recordTableId);
const useMapKeyboardToSoftFocus = () => { const useMapKeyboardToFocus = () => {
const disableSoftFocus = useDisableSoftFocus(recordTableId);
const setHotkeyScope = useSetHotkeyScope(); const setHotkeyScope = useSetHotkeyScope();
const setIsSoftFocusUsingMouseState = useSetRecoilState(
isSoftFocusUsingMouseState,
);
useScopedHotkeys( useScopedHotkeys(
[Key.ArrowUp, `${Key.Shift}+${Key.Enter}`], [Key.ArrowUp, `${Key.Shift}+${Key.Enter}`],
() => { () => {
moveUp(); moveUp();
}, },
TableHotkeyScope.TableSoftFocus, TableHotkeyScope.TableFocus,
[moveUp], [moveUp],
); );
@ -172,7 +168,7 @@ export const useRecordTable = (props?: useRecordTableProps) => {
() => { () => {
moveDown(); moveDown();
}, },
TableHotkeyScope.TableSoftFocus, TableHotkeyScope.TableFocus,
[moveDown], [moveDown],
); );
@ -180,9 +176,8 @@ export const useRecordTable = (props?: useRecordTableProps) => {
[Key.ArrowLeft, `${Key.Shift}+${Key.Tab}`], [Key.ArrowLeft, `${Key.Shift}+${Key.Tab}`],
() => { () => {
moveLeft(); moveLeft();
setIsSoftFocusUsingMouseState(false);
}, },
TableHotkeyScope.TableSoftFocus, TableHotkeyScope.TableFocus,
[moveLeft], [moveLeft],
); );
@ -190,9 +185,8 @@ export const useRecordTable = (props?: useRecordTableProps) => {
[Key.ArrowRight, Key.Tab], [Key.ArrowRight, Key.Tab],
() => { () => {
moveRight(); moveRight();
setIsSoftFocusUsingMouseState(false);
}, },
TableHotkeyScope.TableSoftFocus, TableHotkeyScope.TableFocus,
[moveRight], [moveRight],
); );
@ -203,18 +197,15 @@ export const useRecordTable = (props?: useRecordTableProps) => {
goto: true, goto: true,
keyboardShortcutMenu: true, keyboardShortcutMenu: true,
}); });
disableSoftFocus(); setIsFocusActiveForCurrentPosition(false);
}, },
TableHotkeyScope.TableSoftFocus, TableHotkeyScope.TableFocus,
[disableSoftFocus], [setIsFocusActiveForCurrentPosition],
); );
}; };
const { selectAllRows } = useSelectAllRows(recordTableId); const { selectAllRows } = useSelectAllRows(recordTableId);
const isSomeCellInEditModeState =
useGetIsSomeCellInEditModeState(recordTableId);
return { return {
onColumnsChange, onColumnsChange,
setAvailableTableColumns, setAvailableTableColumns,
@ -228,13 +219,12 @@ export const useRecordTable = (props?: useRecordTableProps) => {
moveLeft, moveLeft,
moveRight, moveRight,
moveUp, moveUp,
useMapKeyboardToSoftFocus, useMapKeyboardToFocus,
selectAllRows, selectAllRows,
setOnColumnsChange, setOnColumnsChange,
setIsRecordTableInitialLoading, setIsRecordTableInitialLoading,
setRecordTableLastRowVisible, setRecordTableLastRowVisible,
setSoftFocusPosition, setFocusPosition,
isSomeCellInEditModeState,
setHasUserSelectedAllRows, setHasUserSelectedAllRows,
setOnToggleColumnFilter, setOnToggleColumnFilter,
setOnToggleColumnSort, setOnToggleColumnSort,

View File

@ -4,16 +4,16 @@ import { MoveFocusDirection } from '@/object-record/record-table/types/MoveFocus
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector'; 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 { 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 { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
import { useSetSoftFocusPosition } from './internal/useSetSoftFocusPosition';
export const useRecordTableMoveFocus = (recordTableId?: string) => { export const useRecordTableMoveFocus = (recordTableId?: string) => {
const setSoftFocusPosition = useSetSoftFocusPosition(recordTableId); const setFocusPosition = useSetRecordTableFocusPosition(recordTableId);
const softFocusPositionState = useRecoilComponentCallbackStateV2( const focusPositionState = useRecoilComponentCallbackStateV2(
softFocusPositionComponentState, recordTableFocusPositionComponentState,
recordTableId, recordTableId,
); );
@ -25,23 +25,20 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => {
const moveUp = useRecoilCallback( const moveUp = useRecoilCallback(
({ snapshot }) => ({ snapshot }) =>
() => { () => {
const softFocusPosition = getSnapshotValue( const focusPosition = getSnapshotValue(snapshot, focusPositionState);
snapshot,
softFocusPositionState,
);
let newRowIndex = softFocusPosition.row - 1; let newRowIndex = focusPosition.row - 1;
if (newRowIndex < 0) { if (newRowIndex < 0) {
newRowIndex = 0; newRowIndex = 0;
} }
setSoftFocusPosition({ setFocusPosition({
...softFocusPosition, ...focusPosition,
row: newRowIndex, row: newRowIndex,
}); });
}, },
[softFocusPositionState, setSoftFocusPosition], [focusPositionState, setFocusPosition],
); );
const moveDown = useRecoilCallback( const moveDown = useRecoilCallback(
@ -51,27 +48,20 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => {
snapshot, snapshot,
recordIndexAllRecordIdsSelector, recordIndexAllRecordIdsSelector,
); );
const softFocusPosition = getSnapshotValue( const focusPosition = getSnapshotValue(snapshot, focusPositionState);
snapshot,
softFocusPositionState,
);
let newRowIndex = softFocusPosition.row + 1; let newRowIndex = focusPosition.row + 1;
if (newRowIndex >= allRecordIds.length) { if (newRowIndex >= allRecordIds.length) {
newRowIndex = allRecordIds.length - 1; newRowIndex = allRecordIds.length - 1;
} }
setSoftFocusPosition({ setFocusPosition({
...softFocusPosition, ...focusPosition,
row: newRowIndex, row: newRowIndex,
}); });
}, },
[ [recordIndexAllRecordIdsSelector, setFocusPosition, focusPositionState],
recordIndexAllRecordIdsSelector,
setSoftFocusPosition,
softFocusPositionState,
],
); );
const numberOfTableColumnsSelector = useRecoilComponentCallbackStateV2( const numberOfTableColumnsSelector = useRecoilComponentCallbackStateV2(
@ -86,18 +76,15 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => {
snapshot, snapshot,
recordIndexAllRecordIdsSelector, recordIndexAllRecordIdsSelector,
); );
const softFocusPosition = getSnapshotValue( const focusPosition = getSnapshotValue(snapshot, focusPositionState);
snapshot,
softFocusPositionState,
);
const numberOfTableColumns = getSnapshotValue( const numberOfTableColumns = getSnapshotValue(
snapshot, snapshot,
numberOfTableColumnsSelector, numberOfTableColumnsSelector,
); );
const currentColumnIndex = softFocusPosition.column; const currentColumnIndex = focusPosition.column;
const currentRowIndex = softFocusPosition.row; const currentRowIndex = focusPosition.row;
const isLastRowAndLastColumn = const isLastRowAndLastColumn =
currentColumnIndex === numberOfTableColumns - 1 && currentColumnIndex === numberOfTableColumns - 1 &&
@ -114,12 +101,12 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => {
} }
if (isNotLastColumn) { if (isNotLastColumn) {
setSoftFocusPosition({ setFocusPosition({
row: currentRowIndex, row: currentRowIndex,
column: currentColumnIndex + 1, column: currentColumnIndex + 1,
}); });
} else if (isLastColumnButNotLastRow) { } else if (isLastColumnButNotLastRow) {
setSoftFocusPosition({ setFocusPosition({
row: currentRowIndex + 1, row: currentRowIndex + 1,
column: 0, column: 0,
}); });
@ -127,27 +114,24 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => {
}, },
[ [
recordIndexAllRecordIdsSelector, recordIndexAllRecordIdsSelector,
softFocusPositionState, focusPositionState,
numberOfTableColumnsSelector, numberOfTableColumnsSelector,
setSoftFocusPosition, setFocusPosition,
], ],
); );
const moveLeft = useRecoilCallback( const moveLeft = useRecoilCallback(
({ snapshot }) => ({ snapshot }) =>
() => { () => {
const softFocusPosition = getSnapshotValue( const focusPosition = getSnapshotValue(snapshot, focusPositionState);
snapshot,
softFocusPositionState,
);
const numberOfTableColumns = getSnapshotValue( const numberOfTableColumns = getSnapshotValue(
snapshot, snapshot,
numberOfTableColumnsSelector, numberOfTableColumnsSelector,
); );
const currentColumnIndex = softFocusPosition.column; const currentColumnIndex = focusPosition.column;
const currentRowIndex = softFocusPosition.row; const currentRowIndex = focusPosition.row;
const isFirstRowAndFirstColumn = const isFirstRowAndFirstColumn =
currentColumnIndex === 0 && currentRowIndex === 0; currentColumnIndex === 0 && currentRowIndex === 0;
@ -162,22 +146,18 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => {
} }
if (isNotFirstColumn) { if (isNotFirstColumn) {
setSoftFocusPosition({ setFocusPosition({
row: currentRowIndex, row: currentRowIndex,
column: currentColumnIndex - 1, column: currentColumnIndex - 1,
}); });
} else if (isFirstColumnButNotFirstRow) { } else if (isFirstColumnButNotFirstRow) {
setSoftFocusPosition({ setFocusPosition({
row: currentRowIndex - 1, row: currentRowIndex - 1,
column: numberOfTableColumns - 1, column: numberOfTableColumns - 1,
}); });
} }
}, },
[ [numberOfTableColumnsSelector, focusPositionState, setFocusPosition],
numberOfTableColumnsSelector,
softFocusPositionState,
setSoftFocusPosition,
],
); );
const moveFocus = (direction: MoveFocusDirection) => { const moveFocus = (direction: MoveFocusDirection) => {
@ -202,7 +182,7 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => {
moveLeft, moveLeft,
moveRight, moveRight,
moveUp, moveUp,
setSoftFocusPosition, setFocusPosition,
moveFocus, moveFocus,
}; };
}; };

View File

@ -3,13 +3,13 @@ import { useRecordTableContextOrThrow } from '@/object-record/record-table/conte
import { useLeaveTableFocus } from '@/object-record/record-table/hooks/internal/useLeaveTableFocus'; import { useLeaveTableFocus } from '@/object-record/record-table/hooks/internal/useLeaveTableFocus';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
type RecordTableBodySoftFocusClickOutsideEffectProps = { type RecordTableBodyFocusClickOutsideEffectProps = {
tableBodyRef: React.RefObject<HTMLDivElement>; tableBodyRef: React.RefObject<HTMLDivElement>;
}; };
export const RecordTableBodySoftFocusClickOutsideEffect = ({ export const RecordTableBodyFocusClickOutsideEffect = ({
tableBodyRef, tableBodyRef,
}: RecordTableBodySoftFocusClickOutsideEffectProps) => { }: RecordTableBodyFocusClickOutsideEffectProps) => {
const { recordTableId } = useRecordTableContextOrThrow(); const { recordTableId } = useRecordTableContextOrThrow();
const leaveTableFocus = useLeaveTableFocus(recordTableId); const leaveTableFocus = useLeaveTableFocus(recordTableId);

View File

@ -1,14 +1,14 @@
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext'; import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable'; import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
export const RecordTableBodySoftFocusKeyboardEffect = () => { export const RecordTableBodyFocusKeyboardEffect = () => {
const { recordTableId } = useRecordTableContextOrThrow(); const { recordTableId } = useRecordTableContextOrThrow();
const { useMapKeyboardToSoftFocus } = useRecordTable({ const { useMapKeyboardToFocus } = useRecordTable({
recordTableId, recordTableId,
}); });
useMapKeyboardToSoftFocus(); useMapKeyboardToFocus();
return <></>; return <></>;
}; };

View File

@ -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 { RecordTableBodyDragDropContextProvider } from '@/object-record/record-table/record-table-body/components/RecordTableBodyDragDropContextProvider';
import { RecordTableBodyDroppable } from '@/object-record/record-table/record-table-body/components/RecordTableBodyDroppable'; 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 { 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 { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
@ -25,6 +26,7 @@ export const RecordTableNoRecordGroupBody = () => {
<RecordTableBodyDragDropContextProvider> <RecordTableBodyDragDropContextProvider>
<RecordTableBodyDroppable> <RecordTableBodyDroppable>
<RecordTableNoRecordGroupRows /> <RecordTableNoRecordGroupRows />
<RecordTableCellPortals />
</RecordTableBodyDroppable> </RecordTableBodyDroppable>
</RecordTableBodyDragDropContextProvider> </RecordTableBodyDragDropContextProvider>
</RecordTableNoRecordGroupBodyContextProvider> </RecordTableNoRecordGroupBodyContextProvider>

View File

@ -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 { RecordTableBodyDroppable } from '@/object-record/record-table/record-table-body/components/RecordTableBodyDroppable';
import { RecordTableBodyLoading } from '@/object-record/record-table/record-table-body/components/RecordTableBodyLoading'; 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 { 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 { RecordTableRecordGroupSection } from '@/object-record/record-table/record-table-section/components/RecordTableRecordGroupSection';
import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState'; import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState';
import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2'; import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2';
@ -42,6 +43,7 @@ export const RecordTableRecordGroupsBody = () => {
<RecordTableBodyDroppable recordGroupId={recordGroupId}> <RecordTableBodyDroppable recordGroupId={recordGroupId}>
<RecordTableRecordGroupSection /> <RecordTableRecordGroupSection />
<RecordTableRecordGroupRows /> <RecordTableRecordGroupRows />
<RecordTableCellPortals />
</RecordTableBodyDroppable> </RecordTableBodyDroppable>
</RecordGroupContext.Provider> </RecordGroupContext.Provider>
</RecordTableRecordGroupBodyContextProvider> </RecordTableRecordGroupBodyContextProvider>

View File

@ -1,15 +1,11 @@
import { FieldDisplay } from '@/object-record/record-field/components/FieldDisplay'; import { FieldDisplay } from '@/object-record/record-field/components/FieldDisplay';
import { FieldFocusContextProvider } from '@/object-record/record-field/contexts/FieldFocusContextProvider'; import { FieldFocusContextProvider } from '@/object-record/record-field/contexts/FieldFocusContextProvider';
import { RecordTableCellContainer } from '@/object-record/record-table/record-table-cell/components/RecordTableCellContainer'; 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 = () => { export const RecordTableCell = () => {
return ( return (
<FieldFocusContextProvider> <FieldFocusContextProvider>
<RecordTableCellContainer <RecordTableCellContainer nonEditModeContent={<FieldDisplay />} />
editModeContent={<RecordTableCellFieldInput />}
nonEditModeContent={<FieldDisplay />}
/>
</FieldFocusContextProvider> </FieldFocusContextProvider>
); );
}; };

View File

@ -9,11 +9,11 @@ import { useOpenRecordTableCellFromCell } from '@/object-record/record-table/rec
import { BORDER_COMMON, ThemeContext } from 'twenty-ui/theme'; import { BORDER_COMMON, ThemeContext } from 'twenty-ui/theme';
const StyledBaseContainer = styled.div<{ const StyledBaseContainer = styled.div<{
hasSoftFocus: boolean;
fontColorExtraLight: string; fontColorExtraLight: string;
fontColorMedium: string; fontColorMedium: string;
backgroundColorTransparentSecondary: string; backgroundColorTransparentSecondary: string;
isReadOnly: boolean; isReadOnly: boolean;
borderColorBlue: string;
}>` }>`
align-items: center; align-items: center;
box-sizing: border-box; box-sizing: border-box;
@ -23,23 +23,10 @@ const StyledBaseContainer = styled.div<{
position: relative; position: relative;
user-select: none; user-select: none;
background: ${({ hasSoftFocus, backgroundColorTransparentSecondary }) => &.focus-active {
hasSoftFocus ? backgroundColorTransparentSecondary : 'none'}; border-radius: ${BORDER_COMMON.radius.sm};
outline: 1px solid ${({ borderColorBlue }) => borderColorBlue};
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'};
`; `;
export const RecordTableCellBaseContainer = ({ export const RecordTableCellBaseContainer = ({
@ -52,18 +39,16 @@ export const RecordTableCellBaseContainer = ({
const { openTableCell } = useOpenRecordTableCellFromCell(); const { openTableCell } = useOpenRecordTableCellFromCell();
const { theme } = useContext(ThemeContext); const { theme } = useContext(ThemeContext);
const { hasSoftFocus, cellPosition } = useContext(RecordTableCellContext); const { cellPosition } = useContext(RecordTableCellContext);
const { onMoveSoftFocusToCurrentCell, onCellMouseEnter } = const { onMoveHoverToCurrentCell, onCellMouseEnter } =
useRecordTableBodyContextOrThrow(); useRecordTableBodyContextOrThrow();
const handleContainerMouseMove = () => { const handleContainerMouseMove = () => {
setIsFocused(true); setIsFocused(true);
if (!hasSoftFocus) { onCellMouseEnter({
onCellMouseEnter({ cellPosition,
cellPosition, });
});
}
}; };
const handleContainerMouseLeave = () => { const handleContainerMouseLeave = () => {
@ -71,10 +56,8 @@ export const RecordTableCellBaseContainer = ({
}; };
const handleContainerClick = () => { const handleContainerClick = () => {
if (!hasSoftFocus) { onMoveHoverToCurrentCell(cellPosition);
onMoveSoftFocusToCurrentCell(cellPosition); openTableCell();
openTableCell();
}
}; };
return ( return (
@ -87,8 +70,9 @@ export const RecordTableCellBaseContainer = ({
} }
fontColorExtraLight={theme.font.color.extraLight} fontColorExtraLight={theme.font.color.extraLight}
fontColorMedium={theme.border.color.medium} fontColorMedium={theme.border.color.medium}
hasSoftFocus={hasSoftFocus} borderColorBlue={theme.adaptiveColors.blue4}
isReadOnly={isReadOnly ?? false} isReadOnly={isReadOnly ?? false}
id={`record-table-cell-${cellPosition.column}-${cellPosition.row}`}
> >
{children} {children}
</StyledBaseContainer> </StyledBaseContainer>

View File

@ -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 { 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 { RecordTableCellDisplayMode } from './RecordTableCellDisplayMode';
import { RecordTableCellEditMode } from './RecordTableCellEditMode';
export type RecordTableCellContainerProps = { export type RecordTableCellContainerProps = {
editModeContent: ReactElement;
nonEditModeContent: ReactElement; nonEditModeContent: ReactElement;
transparent?: boolean; transparent?: boolean;
maxContentWidth?: number; maxContentWidth?: number;
@ -17,23 +13,13 @@ export type RecordTableCellContainerProps = {
}; };
export const RecordTableCellContainer = ({ export const RecordTableCellContainer = ({
editModeContent,
nonEditModeContent, nonEditModeContent,
}: RecordTableCellContainerProps) => { }: RecordTableCellContainerProps) => {
const { hasSoftFocus, isInEditMode } = useContext(RecordTableCellContext);
return ( return (
<RecordTableCellBaseContainer> <RecordTableCellBaseContainer>
{isInEditMode ? ( <RecordTableCellDisplayMode>
<RecordTableCellEditMode>{editModeContent}</RecordTableCellEditMode> {nonEditModeContent}
) : ( </RecordTableCellDisplayMode>
<RecordTableCellDisplayMode>
{nonEditModeContent}
</RecordTableCellDisplayMode>
)}
{hasSoftFocus ? (
<RecordTableCellSoftFocusModeHotkeysSetterEffect />
) : null}
</RecordTableCellBaseContainer> </RecordTableCellBaseContainer>
); );
}; };

View File

@ -26,7 +26,7 @@ const StyledEmptyPlaceholderField = withTheme(styled.div<{ theme: Theme }>`
`); `);
export type EditableCellDisplayContainerProps = { export type EditableCellDisplayContainerProps = {
softFocus?: boolean; focus?: boolean;
onClick?: () => void; onClick?: () => void;
scrollRef?: Ref<HTMLDivElement>; scrollRef?: Ref<HTMLDivElement>;
isHovered?: boolean; isHovered?: boolean;
@ -36,7 +36,7 @@ export type EditableCellDisplayContainerProps = {
export const RecordTableCellDisplayContainer = ({ export const RecordTableCellDisplayContainer = ({
children, children,
softFocus, focus,
onClick, onClick,
scrollRef, scrollRef,
onContextMenu, onContextMenu,
@ -44,7 +44,7 @@ export const RecordTableCellDisplayContainer = ({
}: React.PropsWithChildren<EditableCellDisplayContainerProps>) => ( }: React.PropsWithChildren<EditableCellDisplayContainerProps>) => (
<StyledOuterContainer <StyledOuterContainer
data-testid={ data-testid={
softFocus ? 'editable-cell-soft-focus-mode' : 'editable-cell-display-mode' focus ? 'editable-cell-focus-mode' : 'editable-cell-display-mode'
} }
onClick={onClick} onClick={onClick}
ref={scrollRef} ref={scrollRef}

View File

@ -1,34 +1,22 @@
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext'; import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
import { useIsFieldInputOnly } from '@/object-record/record-field/hooks/useIsFieldInputOnly'; import { useIsFieldInputOnly } from '@/object-record/record-field/hooks/useIsFieldInputOnly';
import { useRecordTableBodyContextOrThrow } from '@/object-record/record-table/contexts/RecordTableBodyContext'; import { useRecordTableBodyContextOrThrow } from '@/object-record/record-table/contexts/RecordTableBodyContext';
import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext';
import { RecordTableCellEditButton } from '@/object-record/record-table/record-table-cell/components/RecordTableCellEditButton';
import { useOpenRecordTableCellFromCell } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellFromCell'; import { useOpenRecordTableCellFromCell } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellFromCell';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; import { ReactNode, useContext } from 'react';
import { useContext } from 'react';
import { RecordTableCellDisplayContainer } from './RecordTableCellDisplayContainer'; import { RecordTableCellDisplayContainer } from './RecordTableCellDisplayContainer';
export const RecordTableCellDisplayMode = ({ export const RecordTableCellDisplayMode = ({
children, children,
}: React.PropsWithChildren) => { }: {
children: ReactNode;
}) => {
const { recordId, isReadOnly } = useContext(FieldContext); const { recordId, isReadOnly } = useContext(FieldContext);
const { columnIndex, hasSoftFocus } = useContext(RecordTableCellContext);
const isMobile = useIsMobile();
const { onActionMenuDropdownOpened } = useRecordTableBodyContextOrThrow(); const { onActionMenuDropdownOpened } = useRecordTableBodyContextOrThrow();
const { openTableCell } = useOpenRecordTableCellFromCell(); const { openTableCell } = useOpenRecordTableCellFromCell();
const isFieldInputOnly = useIsFieldInputOnly(); const isFieldInputOnly = useIsFieldInputOnly();
const isFirstColumn = columnIndex === 0;
const showButton =
hasSoftFocus &&
!isFieldInputOnly &&
!isReadOnly &&
!(isMobile && isFirstColumn);
const handleActionMenuDropdown = (event: React.MouseEvent) => { const handleActionMenuDropdown = (event: React.MouseEvent) => {
onActionMenuDropdownOpened(event, recordId); onActionMenuDropdownOpened(event, recordId);
@ -41,15 +29,11 @@ export const RecordTableCellDisplayMode = ({
}; };
return ( return (
<> <RecordTableCellDisplayContainer
<RecordTableCellDisplayContainer onContextMenu={handleActionMenuDropdown}
softFocus={hasSoftFocus} onClick={handleClick}
onContextMenu={handleActionMenuDropdown} >
onClick={handleClick} {children}
> </RecordTableCellDisplayContainer>
{children}
</RecordTableCellDisplayContainer>
{showButton && <RecordTableCellEditButton />}
</>
); );
}; };

View File

@ -8,12 +8,12 @@ import { isDefined } from 'twenty-shared/utils';
import { IconArrowUpRight, IconPencil } from 'twenty-ui/display'; import { IconArrowUpRight, IconPencil } from 'twenty-ui/display';
export const RecordTableCellEditButton = () => { export const RecordTableCellEditButton = () => {
const { columnIndex } = useContext(RecordTableCellContext); const { cellPosition } = useContext(RecordTableCellContext);
const { openTableCell } = useOpenRecordTableCellFromCell(); const { openTableCell } = useOpenRecordTableCellFromCell();
const isFieldInputOnly = useIsFieldInputOnly(); const isFieldInputOnly = useIsFieldInputOnly();
const isFirstColumn = columnIndex === 0; const isFirstColumn = cellPosition.column === 0;
const customButtonIcon = useGetButtonIcon(); const customButtonIcon = useGetButtonIcon();
const buttonIcon = isFirstColumn const buttonIcon = isFirstColumn

View File

@ -1,7 +1,10 @@
import { useIsFieldInputOnly } from '@/object-record/record-field/hooks/useIsFieldInputOnly';
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext'; import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
import { recordFieldInputIsFieldInErrorComponentState } from '@/object-record/record-field/states/recordFieldInputIsFieldInErrorComponentState'; import { recordFieldInputIsFieldInErrorComponentState } from '@/object-record/record-field/states/recordFieldInputIsFieldInErrorComponentState';
import { recordFieldInputLayoutDirectionComponentState } from '@/object-record/record-field/states/recordFieldInputLayoutDirectionComponentState'; import { recordFieldInputLayoutDirectionComponentState } from '@/object-record/record-field/states/recordFieldInputLayoutDirectionComponentState';
import { recordFieldInputLayoutDirectionLoadingComponentState } from '@/object-record/record-field/states/recordFieldInputLayoutDirectionLoadingComponentState'; 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 { OverlayContainer } from '@/ui/layout/overlay/components/OverlayContainer';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
@ -14,9 +17,11 @@ import {
offset, offset,
useFloating, useFloating,
} from '@floating-ui/react'; } from '@floating-ui/react';
import { ReactElement } from 'react'; import { ReactElement, useContext } from 'react';
const StyledEditableCellEditModeContainer = styled.div<RecordTableCellEditModeProps>` const StyledEditableCellEditModeContainer = styled.div<{
isFieldInputOnly: boolean;
}>`
align-items: center; align-items: center;
display: flex; display: flex;
height: 100%; height: 100%;
@ -25,11 +30,17 @@ const StyledEditableCellEditModeContainer = styled.div<RecordTableCellEditModePr
z-index: 6; z-index: 6;
`; `;
const StyledInputModeOnlyContainer = styled.div`
align-items: center;
display: flex;
height: 100%;
overflow: hidden;
padding-left: 8px;
width: 100%;
`;
export type RecordTableCellEditModeProps = { export type RecordTableCellEditModeProps = {
children: ReactElement; children: ReactElement;
transparent?: boolean;
maxContentWidth?: number;
initialValue?: string;
}; };
export const RecordTableCellEditMode = ({ export const RecordTableCellEditMode = ({
@ -77,19 +88,36 @@ export const RecordTableCellEditMode = ({
whileElementsMounted: autoUpdate, whileElementsMounted: autoUpdate,
}); });
const isFieldInputOnly = useIsFieldInputOnly();
const { cellPosition } = useContext(RecordTableCellContext);
const setFocusPosition = useSetRecordTableFocusPosition();
return ( return (
<StyledEditableCellEditModeContainer <StyledEditableCellEditModeContainer
ref={refs.setReference} ref={refs.setReference}
data-testid="editable-cell-edit-mode-container" data-testid="editable-cell-edit-mode-container"
isFieldInputOnly={isFieldInputOnly}
> >
<OverlayContainer {isFieldInputOnly ? (
ref={refs.setFloating} <StyledInputModeOnlyContainer
style={floatingStyles} onClick={() => {
borderRadius="sm" setFocusPosition(cellPosition);
hasDangerBorder={isFieldInError} }}
> >
{children} {children}
</OverlayContainer> </StyledInputModeOnlyContainer>
) : (
<OverlayContainer
ref={refs.setFloating}
style={floatingStyles}
borderRadius="sm"
hasDangerBorder={isFieldInError}
>
{children}
</OverlayContainer>
)}
</StyledEditableCellEditModeContainer> </StyledEditableCellEditModeContainer>
); );
}; };

View File

@ -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 (
<RecordTableCellPortalWrapper position={focusedCellPosition}>
{currentTableCellInEditModePosition && (
<StyledRecordTableCellHoveredPortal>
<RecordTableCellEditMode>
<RecordTableCellFieldInput />
</RecordTableCellEditMode>
</StyledRecordTableCellHoveredPortal>
)}
<RecordTableFocusModeHotkeysSetterEffect />
</RecordTableCellPortalWrapper>
);
};

View File

@ -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 (
<StyledRecordTableCellHoveredPortalContent isReadOnly={isReadOnly}>
{isFieldInputOnly ? (
<RecordTableCellEditMode>
<RecordTableCellFieldInput />
</RecordTableCellEditMode>
) : (
<RecordTableCellDisplayMode>
<FieldDisplay />
</RecordTableCellDisplayMode>
)}
{showButton && <RecordTableCellEditButton />}
</StyledRecordTableCellHoveredPortalContent>
);
};
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 (
<RecordTableCellPortalWrapper position={hoverPosition}>
<StyledRecordTableCellHoveredPortal>
<RecordTableCellHoveredPortalContent />
</StyledRecordTableCellHoveredPortal>
</RecordTableCellPortalWrapper>
);
};

View File

@ -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(
<RecordTableRowContextProvider
value={{
recordId,
rowIndex: position.row,
isSelected: false,
inView: true,
pathToShowPage:
getBasePathToShowPage({
objectNameSingular: objectMetadataItem.nameSingular,
}) + recordId,
objectNameSingular: objectMetadataItem.nameSingular,
}}
>
<RecordTableCellContext.Provider
value={{
columnDefinition: visibleTableColumns[position.column],
cellPosition: position,
}}
>
<RecordTableCellFieldContextWrapper>
{children}
</RecordTableCellFieldContextWrapper>
</RecordTableCellContext.Provider>
</RecordTableRowContextProvider>,
anchorElement,
);
};

View File

@ -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 (
<>
<RecordTableCellHoveredPortal />
{isRecordTableFocusActive && <RecordTableCellEditModePortal />}
</>
);
};

View File

@ -2,11 +2,8 @@ import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'
import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext'; import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext';
import { useRecordTableRowContextOrThrow } from '@/object-record/record-table/contexts/RecordTableRowContext'; import { useRecordTableRowContextOrThrow } from '@/object-record/record-table/contexts/RecordTableRowContext';
import { RecordTableCellFieldContextWrapper } from '@/object-record/record-table/record-table-cell/components/RecordTableCellFieldContextWrapper'; 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 { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
import { TableCellPosition } from '@/object-record/record-table/types/TableCellPosition'; import { TableCellPosition } from '@/object-record/record-table/types/TableCellPosition';
import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2';
import { useMemo } from 'react'; import { useMemo } from 'react';
export const RecordTableCellWrapper = ({ export const RecordTableCellWrapper = ({
@ -28,23 +25,10 @@ export const RecordTableCellWrapper = ({
[columnIndex, rowIndex], [columnIndex, rowIndex],
); );
const isInEditMode = useRecoilComponentFamilyValueV2(
isTableCellInEditModeComponentFamilyState,
currentTableCellPosition,
);
const hasSoftFocus = useRecoilComponentFamilyValueV2(
isSoftFocusOnTableCellComponentFamilyState,
currentTableCellPosition,
);
return ( return (
<RecordTableCellContext.Provider <RecordTableCellContext.Provider
value={{ value={{
columnDefinition: column, columnDefinition: column,
columnIndex,
isInEditMode,
hasSoftFocus,
cellPosition: currentTableCellPosition, cellPosition: currentTableCellPosition,
}} }}
key={column.fieldMetadataId} key={column.fieldMetadataId}

View File

@ -18,7 +18,6 @@ export const recordTableRowDraggableContextValue: RecordTableRowDraggableContext
}; };
export const recordTableCellContextValue: RecordTableCellContextValue = { export const recordTableCellContextValue: RecordTableCellContextValue = {
columnIndex: 3,
columnDefinition: { columnDefinition: {
size: 1, size: 1,
position: 1, position: 1,
@ -35,6 +34,4 @@ export const recordTableCellContextValue: RecordTableCellContextValue = {
row: 2, row: 2,
column: 3, column: 3,
}, },
hasSoftFocus: false,
isInEditMode: false,
}; };

View File

@ -1,37 +0,0 @@
import { renderHook } from '@testing-library/react';
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 { useCurrentTableCellPosition } from '../useCurrentCellPosition';
describe('useCurrentTableCellPosition', () => {
it('should return the current table cell position', () => {
const wrapper = ({ children }: { children: React.ReactNode }) => (
<RecordTableRowContextProvider value={recordTableRowContextValue}>
<RecordTableRowDraggableContextProvider
value={recordTableRowDraggableContextValue}
>
<RecordTableCellContext.Provider value={recordTableCellContextValue}>
{children}
</RecordTableCellContext.Provider>
</RecordTableRowDraggableContextProvider>
</RecordTableRowContextProvider>
);
const { result } = renderHook(() => useCurrentTableCellPosition(), {
wrapper,
});
expect(result.current).toEqual({
column: 3,
row: 2,
});
});
});

View File

@ -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 }) => (
<RecoilRoot>
<RecordTableComponentInstance
recordTableId={recordTableId}
onColumnsChange={onColumnsChange}
>
{children}
</RecordTableComponentInstance>
</RecoilRoot>
);
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);
});
});
});

View File

@ -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 }) => (
<RecoilRoot>
<RecordTableComponentInstance
recordTableId="scopeId"
onColumnsChange={jest.fn()}
>
<RecordTableRowContextProvider value={recordTableRowContextValue}>
<RecordTableRowDraggableContextProvider
value={recordTableRowDraggableContextValue}
>
<RecordTableCellContext.Provider value={recordTableCellContextValue}>
{children}
</RecordTableCellContext.Provider>
</RecordTableRowDraggableContextProvider>
</RecordTableRowContextProvider>
</RecordTableComponentInstance>
</RecoilRoot>
);
describe('useIsSoftFocusOnCurrentTableCell', () => {
it('should work as expected', () => {
const { result } = renderHook(() => useIsSoftFocusOnCurrentTableCell(), {
wrapper: Wrapper,
});
expect(result.current).toBe(false);
});
});

View File

@ -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 }) => (
<RecoilRoot
initializeState={({ set }) => {
set(currentHotkeyScopeState, {
scope: TableHotkeyScope.TableFocus,
customScopes: {},
});
}}
>
<RecordTableComponentInstance
recordTableId="test-record-table-instance-id"
onColumnsChange={jest.fn()}
>
<RecordTableRowContextProvider value={recordTableRowContextValue}>
<RecordTableRowDraggableContextProvider
value={recordTableRowDraggableContextValue}
>
<RecordTableCellContext.Provider value={recordTableCellContextValue}>
{children}
</RecordTableCellContext.Provider>
</RecordTableRowDraggableContextProvider>
</RecordTableRowContextProvider>
</RecordTableComponentInstance>
</RecoilRoot>
);
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,
});
});
});

View File

@ -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 }) => (
<RecoilRoot>
<RecordTableComponentInstance
recordTableId="scopeId"
onColumnsChange={jest.fn()}
>
<RecordTableRowContextProvider value={recordTableRowContextValue}>
<RecordTableRowDraggableContextProvider
value={recordTableRowDraggableContextValue}
>
<RecordTableCellContext.Provider value={recordTableCellContextValue}>
{children}
</RecordTableCellContext.Provider>
</RecordTableRowDraggableContextProvider>
</RecordTableRowContextProvider>
</RecordTableComponentInstance>
</RecoilRoot>
);
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,
);
});
});

View File

@ -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,
);
});
});

View File

@ -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 }) => (
<RecoilRoot
initializeState={({ set }) => {
set(
isRecordTableFocusActiveComponentState.atomFamily({
instanceId: 'test-table-id',
}),
false,
);
set(
recordTableFocusPositionComponentState.atomFamily({
instanceId: 'test-table-id',
}),
{
column: 1,
row: 0,
},
);
}}
>
<RecordTableComponentInstance
recordTableId="test-table-id"
onColumnsChange={jest.fn()}
>
{children}
</RecordTableComponentInstance>
</RecoilRoot>
);
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);
});
});

View File

@ -16,10 +16,8 @@ import {
recordTableRowDraggableContextValue, recordTableRowDraggableContextValue,
} from '@/object-record/record-table/record-table-cell/hooks/__mocks__/cell'; } 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 { useCloseRecordTableCellInGroup } from '@/object-record/record-table/record-table-cell/hooks/internal/useCloseRecordTableCellInGroup';
import { currentTableCellInEditModePositionComponentState } from '@/object-record/record-table/states/currentTableCellInEditModePositionComponentState'; import { recordTableCellEditModePositionComponentState } from '@/object-record/record-table/states/recordTableCellEditModePositionComponentState';
import { isTableCellInEditModeComponentFamilyState } from '@/object-record/record-table/states/isTableCellInEditModeComponentFamilyState';
import { useDragSelect } from '@/ui/utilities/drag-select/hooks/useDragSelect'; 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 { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems'; import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
@ -60,7 +58,7 @@ const Wrapper = ({ children }: { children: React.ReactNode }) => (
value={recordTableRowDraggableContextValue} value={recordTableRowDraggableContextValue}
> >
<RecordTableCellContext.Provider <RecordTableCellContext.Provider
value={{ ...recordTableCellContextValue, columnIndex: 0 }} value={{ ...recordTableCellContextValue }}
> >
{children} {children}
</RecordTableCellContext.Provider> </RecordTableCellContext.Provider>
@ -77,16 +75,12 @@ describe('useCloseRecordTableCellInGroup', () => {
const { result } = renderHook( const { result } = renderHook(
() => { () => {
const currentTableCellInEditModePosition = useRecoilComponentValueV2( const currentTableCellInEditModePosition = useRecoilComponentValueV2(
currentTableCellInEditModePositionComponentState, recordTableCellEditModePositionComponentState,
);
const isTableCellInEditMode = useRecoilComponentFamilyValueV2(
isTableCellInEditModeComponentFamilyState,
currentTableCellInEditModePosition,
); );
return { return {
...useCloseRecordTableCellInGroup(), ...useCloseRecordTableCellInGroup(),
...useDragSelect(), ...useDragSelect(),
isTableCellInEditMode, currentTableCellInEditModePosition,
}; };
}, },
{ {
@ -99,7 +93,7 @@ describe('useCloseRecordTableCellInGroup', () => {
}); });
expect(result.current.isDragSelectionStartEnabled()).toBe(true); expect(result.current.isDragSelectionStartEnabled()).toBe(true);
expect(result.current.isTableCellInEditMode).toBe(false); expect(result.current.currentTableCellInEditModePosition).toBe(null);
expect(setHotkeyScope).toHaveBeenCalledWith('table-soft-focus'); expect(setHotkeyScope).toHaveBeenCalledWith('table-focus');
}); });
}); });

View File

@ -16,10 +16,8 @@ import {
recordTableRowDraggableContextValue, recordTableRowDraggableContextValue,
} from '@/object-record/record-table/record-table-cell/hooks/__mocks__/cell'; } 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 { useCloseRecordTableCellNoGroup } from '@/object-record/record-table/record-table-cell/hooks/internal/useCloseRecordTableCellNoGroup';
import { currentTableCellInEditModePositionComponentState } from '@/object-record/record-table/states/currentTableCellInEditModePositionComponentState'; import { recordTableCellEditModePositionComponentState } from '@/object-record/record-table/states/recordTableCellEditModePositionComponentState';
import { isTableCellInEditModeComponentFamilyState } from '@/object-record/record-table/states/isTableCellInEditModeComponentFamilyState';
import { useDragSelect } from '@/ui/utilities/drag-select/hooks/useDragSelect'; 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 { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems'; import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
@ -60,7 +58,7 @@ const Wrapper = ({ children }: { children: React.ReactNode }) => (
value={recordTableRowDraggableContextValue} value={recordTableRowDraggableContextValue}
> >
<RecordTableCellContext.Provider <RecordTableCellContext.Provider
value={{ ...recordTableCellContextValue, columnIndex: 0 }} value={{ ...recordTableCellContextValue }}
> >
{children} {children}
</RecordTableCellContext.Provider> </RecordTableCellContext.Provider>
@ -77,16 +75,13 @@ describe('useCloseRecordTableCellNoGroup', () => {
const { result } = renderHook( const { result } = renderHook(
() => { () => {
const currentTableCellInEditModePosition = useRecoilComponentValueV2( const currentTableCellInEditModePosition = useRecoilComponentValueV2(
currentTableCellInEditModePositionComponentState, recordTableCellEditModePositionComponentState,
);
const isTableCellInEditMode = useRecoilComponentFamilyValueV2(
isTableCellInEditModeComponentFamilyState,
currentTableCellInEditModePosition,
); );
return { return {
...useCloseRecordTableCellNoGroup(), ...useCloseRecordTableCellNoGroup(),
...useDragSelect(), ...useDragSelect(),
isTableCellInEditMode, currentTableCellInEditModePosition,
}; };
}, },
{ {
@ -99,7 +94,7 @@ describe('useCloseRecordTableCellNoGroup', () => {
}); });
expect(result.current.isDragSelectionStartEnabled()).toBe(true); expect(result.current.isDragSelectionStartEnabled()).toBe(true);
expect(result.current.isTableCellInEditMode).toBe(false); expect(result.current.currentTableCellInEditModePosition).toBe(null);
expect(setHotkeyScope).toHaveBeenCalledWith('table-soft-focus'); expect(setHotkeyScope).toHaveBeenCalledWith('table-focus');
}); });
}); });

View File

@ -1,6 +1,6 @@
import { useRecoilCallback } from 'recoil'; 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 { useDragSelect } from '@/ui/utilities/drag-select/hooks/useDragSelect';
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope'; import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useClickOutsideListener'; import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useClickOutsideListener';
@ -16,7 +16,7 @@ export const useCloseRecordTableCellInGroup = () => {
const { setDragSelectionStartEnabled } = useDragSelect(); const { setDragSelectionStartEnabled } = useDragSelect();
const { toggleClickOutsideListener } = useClickOutsideListener( const { toggleClickOutsideListener } = useClickOutsideListener(
SOFT_FOCUS_CLICK_OUTSIDE_LISTENER_ID, FOCUS_CLICK_OUTSIDE_LISTENER_ID,
); );
const closeCurrentTableCellInEditMode = const closeCurrentTableCellInEditMode =
@ -27,7 +27,7 @@ export const useCloseRecordTableCellInGroup = () => {
toggleClickOutsideListener(true); toggleClickOutsideListener(true);
setDragSelectionStartEnabled(true); setDragSelectionStartEnabled(true);
closeCurrentTableCellInEditMode(); closeCurrentTableCellInEditMode();
setHotkeyScope(TableHotkeyScope.TableSoftFocus); setHotkeyScope(TableHotkeyScope.TableFocus);
}, },
[ [
closeCurrentTableCellInEditMode, closeCurrentTableCellInEditMode,

View File

@ -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 { useDragSelect } from '@/ui/utilities/drag-select/hooks/useDragSelect';
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope'; import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useClickOutsideListener'; import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useClickOutsideListener';
@ -16,7 +16,7 @@ export const useCloseRecordTableCellNoGroup = () => {
const { setDragSelectionStartEnabled } = useDragSelect(); const { setDragSelectionStartEnabled } = useDragSelect();
const { toggleClickOutsideListener } = useClickOutsideListener( const { toggleClickOutsideListener } = useClickOutsideListener(
SOFT_FOCUS_CLICK_OUTSIDE_LISTENER_ID, FOCUS_CLICK_OUTSIDE_LISTENER_ID,
); );
const closeCurrentTableCellInEditMode = const closeCurrentTableCellInEditMode =
@ -26,7 +26,7 @@ export const useCloseRecordTableCellNoGroup = () => {
toggleClickOutsideListener(true); toggleClickOutsideListener(true);
setDragSelectionStartEnabled(true); setDragSelectionStartEnabled(true);
closeCurrentTableCellInEditMode(); closeCurrentTableCellInEditMode();
setHotkeyScope(TableHotkeyScope.TableSoftFocus); setHotkeyScope(TableHotkeyScope.TableFocus);
}, [ }, [
closeCurrentTableCellInEditMode, closeCurrentTableCellInEditMode,
setDragSelectionStartEnabled, setDragSelectionStartEnabled,

View File

@ -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;
};

View File

@ -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,
};
};

View File

@ -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;
};

View File

@ -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 };
};

View File

@ -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 };
};

View File

@ -4,11 +4,12 @@ import { FieldContext } from '@/object-record/record-field/contexts/FieldContext
import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition'; import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition';
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'; import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
import { useRecordTableRowContextOrThrow } from '@/object-record/record-table/contexts/RecordTableRowContext'; 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 { TableCellPosition } from '@/object-record/record-table/types/TableCellPosition';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { useRecordTableBodyContextOrThrow } from '@/object-record/record-table/contexts/RecordTableBodyContext'; 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'; import { TableHotkeyScope } from '../../types/TableHotkeyScope';
export const DEFAULT_CELL_SCOPE: HotkeyScope = { export const DEFAULT_CELL_SCOPE: HotkeyScope = {
@ -33,7 +34,9 @@ export const useOpenRecordTableCellFromCell = () => {
const { onOpenTableCell } = useRecordTableBodyContextOrThrow(); const { onOpenTableCell } = useRecordTableBodyContextOrThrow();
const cellPosition = useCurrentTableCellPosition(); const { cellPosition } = useContext(RecordTableCellContext);
const setFocusPosition = useSetRecordTableFocusPosition();
const openTableCell = ( const openTableCell = (
initialValue?: string, initialValue?: string,
@ -51,6 +54,8 @@ export const useOpenRecordTableCellFromCell = () => {
isActionButtonClick, isActionButtonClick,
isNavigating, isNavigating,
}); });
setFocusPosition(cellPosition);
}; };
return { return {

View File

@ -6,9 +6,8 @@ import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'
import { isFieldValueEmpty } from '@/object-record/record-field/utils/isFieldValueEmpty'; import { isFieldValueEmpty } from '@/object-record/record-field/utils/isFieldValueEmpty';
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState'; import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector'; 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 { 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 { TableCellPosition } from '@/object-record/record-table/types/TableCellPosition';
import { useDragSelect } from '@/ui/utilities/drag-select/hooks/useDragSelect'; import { useDragSelect } from '@/ui/utilities/drag-select/hooks/useDragSelect';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; 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 { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState';
import { viewableRecordNameSingularState } from '@/object-record/record-right-drawer/states/viewableRecordNameSingularState'; 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 { 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 { getDropdownFocusIdForRecordField } from '@/object-record/utils/getDropdownFocusIdForRecordField';
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId'; import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
import { useSetActiveDropdownFocusIdAndMemorizePrevious } from '@/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious'; import { useSetActiveDropdownFocusIdAndMemorizePrevious } from '@/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious';
import { useClickOustideListenerStates } from '@/ui/utilities/pointer-event/hooks/useClickOustideListenerStates'; 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 { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { TableHotkeyScope } from '../../types/TableHotkeyScope'; import { TableHotkeyScope } from '../../types/TableHotkeyScope';
export const DEFAULT_CELL_SCOPE: HotkeyScope = { export const DEFAULT_CELL_SCOPE: HotkeyScope = {
scope: TableHotkeyScope.CellEditMode, scope: TableHotkeyScope.CellEditMode,
}; };
@ -50,14 +50,16 @@ export const useOpenRecordTableCellV2 = (tableScopeId: string) => {
useClickOustideListenerStates(RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID); useClickOustideListenerStates(RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID);
const { indexIdentifierUrl } = useRecordIndexContextOrThrow(); const { indexIdentifierUrl } = useRecordIndexContextOrThrow();
const moveEditModeToTableCellPosition = const setCurrentTableCellInEditModePosition = useSetRecoilComponentStateV2(
useMoveEditModeToTableCellPosition(tableScopeId); recordTableCellEditModePositionComponentState,
tableScopeId,
);
const { setDragSelectionStartEnabled } = useDragSelect(); const { setDragSelectionStartEnabled } = useDragSelect();
const leaveTableFocus = useLeaveTableFocus(tableScopeId); const leaveTableFocus = useLeaveTableFocus(tableScopeId);
const { toggleClickOutsideListener } = useClickOutsideListener( const { toggleClickOutsideListener } = useClickOutsideListener(
SOFT_FOCUS_CLICK_OUTSIDE_LISTENER_ID, FOCUS_CLICK_OUTSIDE_LISTENER_ID,
); );
const initDraftValue = useInitDraftValueV2(); const initDraftValue = useInitDraftValueV2();
@ -148,7 +150,7 @@ export const useOpenRecordTableCellV2 = (tableScopeId: string) => {
recordId, recordId,
}); });
moveEditModeToTableCellPosition(cellPosition); setCurrentTableCellInEditModePosition(cellPosition);
initDraftValue({ initDraftValue({
value: initialValue, value: initialValue,
@ -175,7 +177,7 @@ export const useOpenRecordTableCellV2 = (tableScopeId: string) => {
getClickOutsideListenerIsActivatedState, getClickOutsideListenerIsActivatedState,
setDragSelectionStartEnabled, setDragSelectionStartEnabled,
openFieldInput, openFieldInput,
moveEditModeToTableCellPosition, setCurrentTableCellInEditModePosition,
initDraftValue, initDraftValue,
toggleClickOutsideListener, toggleClickOutsideListener,
setActiveDropdownFocusIdAndMemorizePrevious, setActiveDropdownFocusIdAndMemorizePrevious,

View File

@ -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 };
};

View File

@ -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 };
};

View File

@ -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],
);
};

View File

@ -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);
};
};

View File

@ -1,8 +1,9 @@
import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext';
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
export const tableCellWidthsComponentState = createComponentStateV2<number[]>({ export const isRecordTableFocusActiveComponentState =
key: 'tableCellWidthsComponentState', createComponentStateV2<boolean>({
defaultValue: [], key: 'isRecordTableFocusActiveComponentState',
componentInstanceContext: RecordTableComponentInstanceContext, defaultValue: false,
}); componentInstanceContext: RecordTableComponentInstanceContext,
});

View File

@ -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<boolean>({
key: 'isSoftFocusActiveComponentState',
defaultValue: false,
componentInstanceContext: RecordTableComponentInstanceContext,
});

View File

@ -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<boolean, TableCellPosition>({
key: 'isSoftFocusOnTableCellComponentFamilyState',
defaultValue: false,
componentInstanceContext: RecordTableComponentInstanceContext,
});

View File

@ -1,5 +0,0 @@
import { createState } from 'twenty-ui/utilities';
export const isSoftFocusUsingMouseState = createState<boolean>({
key: 'isSoftFocusUsingMouseState',
defaultValue: false,
});

View File

@ -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<boolean, TableCellPosition>({
key: 'isTableCellInEditModeComponentFamilyState',
defaultValue: false,
componentInstanceContext: RecordTableComponentInstanceContext,
});

View File

@ -2,12 +2,9 @@ import { RecordTableComponentInstanceContext } from '@/object-record/record-tabl
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
import { TableCellPosition } from '../types/TableCellPosition'; import { TableCellPosition } from '../types/TableCellPosition';
export const currentTableCellInEditModePositionComponentState = export const recordTableCellEditModePositionComponentState =
createComponentStateV2<TableCellPosition>({ createComponentStateV2<TableCellPosition | null>({
key: 'currentTableCellInEditModePositionComponentState', key: 'recordTableCellEditModePositionComponentState',
defaultValue: { defaultValue: null,
row: 0,
column: 1,
},
componentInstanceContext: RecordTableComponentInstanceContext, componentInstanceContext: RecordTableComponentInstanceContext,
}); });

View File

@ -2,9 +2,9 @@ import { RecordTableComponentInstanceContext } from '@/object-record/record-tabl
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
import { TableCellPosition } from '../types/TableCellPosition'; import { TableCellPosition } from '../types/TableCellPosition';
export const softFocusPositionComponentState = export const recordTableFocusPositionComponentState =
createComponentStateV2<TableCellPosition>({ createComponentStateV2<TableCellPosition>({
key: 'softFocusPositionComponentState', key: 'recordTableFocusPositionComponentState',
defaultValue: { defaultValue: {
row: 0, row: 0,
column: 1, column: 1,

View File

@ -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<TableCellPosition | null>({
key: 'recordTableHoverPositionComponentState',
defaultValue: null,
componentInstanceContext: RecordTableComponentInstanceContext,
});

View File

@ -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);
},
});

View File

@ -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,
});

View File

@ -2,6 +2,6 @@ export enum TableHotkeyScope {
CellDoubleTextInput = 'cell-double-text-input', CellDoubleTextInput = 'cell-double-text-input',
CellEditMode = 'cell-edit-mode', CellEditMode = 'cell-edit-mode',
CellDateEditMode = 'cell-date-edit-mode', CellDateEditMode = 'cell-date-edit-mode',
TableSoftFocus = 'table-soft-focus', TableFocus = 'table-focus',
Table = 'table', Table = 'table',
} }

View File

@ -85,7 +85,7 @@ const InternalTableContextProviders = ({
onOpenTableCell: () => {}, onOpenTableCell: () => {},
onActionMenuDropdownOpened: () => {}, onActionMenuDropdownOpened: () => {},
onMoveFocus: () => {}, onMoveFocus: () => {},
onMoveSoftFocusToCurrentCell: () => {}, onMoveHoverToCurrentCell: () => {},
}} }}
> >
{children} {children}