From 2b77f598b29fcc184bc18c03d549f30fa31e209b Mon Sep 17 00:00:00 2001 From: Lucas Bordeau Date: Wed, 9 Apr 2025 18:34:31 +0200 Subject: [PATCH] Fixed (#11482) This PR fixes many small bugs around the recent hotkey scope refactor. - Removed unused ActionBar files - Created components CommandMenuOpenContainer and KeyboardShortcutMenuOpenContent to avoid mounting listeners when not needed - Added DEFAULT_CELL_SCOPE where missing in some field inputs - Called setHotkeyScopeAndMemorizePreviousScope instead of setHotkeyScope in new useOpenFieldInputEditMode hook - Broke down RecordTableBodyUnselectEffect into multiple simpler effect components that are mounted only when needed to avoid listening for keyboard and clickoutside event - Re-implemented recently deleted table cell soft focus component logic into RecordTableCellDisplayMode - Created component selector isAtLeastOneTableRowSelectedSelector - Drill down hotkey scope when opening a dropdown - Improved debug logs --- .../getActionBarIdFromActionMenuId.test.ts | 9 -- .../utils/getActionBarIdFromActionMenuId.ts | 3 - .../components/CommandMenuContainer.tsx | 81 ++---------------- .../components/CommandMenuOpenContainer.tsx | 85 +++++++++++++++++++ .../components/KeyboardShortcutMenu.tsx | 36 +------- .../KeyboardShortcutMenuOpenContent.tsx | 43 ++++++++++ .../record-board/components/RecordBoard.tsx | 3 - .../hooks/useOpenFieldInputEditMode.ts | 8 +- .../input/components/FullNameFieldInput.tsx | 5 +- .../input/components/SelectFieldInput.tsx | 2 +- .../components/RecordInlineCell.tsx | 6 +- .../RecordTableBodyEffectsWrapper.tsx | 43 +++++++--- .../components/RecordTableWithWrappers.tsx | 15 ++-- .../RecordTableBodyEscapeHotkeyEffect.tsx | 24 ++++++ ...dTableBodySoftFocusClickOutsideEffect.tsx} | 25 +----- ...RecordTableBodySoftFocusKeyboardEffect.tsx | 14 +++ .../components/RecordTableCellDisplayMode.tsx | 47 ++++++++-- .../components/RecordTableCellEditButton.tsx | 36 ++++++++ .../isAtLeastOneTableRowSelectedSelector.ts | 30 +++++++ .../components/RecordTitleCellFieldInput.tsx | 7 +- .../layout/dropdown/components/Dropdown.tsx | 2 +- .../ui/layout/dropdown/hooks/useDropdown.ts | 4 +- .../hotkey/hooks/usePreviousHotkeyScope.ts | 9 +- .../hotkey/hooks/useSetHotkeyScope.ts | 4 +- .../hooks/useListenClickOutside.ts | 15 ++++ 25 files changed, 362 insertions(+), 194 deletions(-) delete mode 100644 packages/twenty-front/src/modules/action-menu/utils/__tests__/getActionBarIdFromActionMenuId.test.ts delete mode 100644 packages/twenty-front/src/modules/action-menu/utils/getActionBarIdFromActionMenuId.ts create mode 100644 packages/twenty-front/src/modules/command-menu/components/CommandMenuOpenContainer.tsx create mode 100644 packages/twenty-front/src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx create mode 100644 packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyEscapeHotkeyEffect.tsx rename packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/{RecordTableBodyUnselectEffect.tsx => RecordTableBodySoftFocusClickOutsideEffect.tsx} (58%) create mode 100644 packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodySoftFocusKeyboardEffect.tsx create mode 100644 packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellEditButton.tsx create mode 100644 packages/twenty-front/src/modules/object-record/record-table/record-table-row/states/isAtLeastOneTableRowSelectedSelector.ts diff --git a/packages/twenty-front/src/modules/action-menu/utils/__tests__/getActionBarIdFromActionMenuId.test.ts b/packages/twenty-front/src/modules/action-menu/utils/__tests__/getActionBarIdFromActionMenuId.test.ts deleted file mode 100644 index bf9990909..000000000 --- a/packages/twenty-front/src/modules/action-menu/utils/__tests__/getActionBarIdFromActionMenuId.test.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { getActionBarIdFromActionMenuId } from '@/action-menu/utils/getActionBarIdFromActionMenuId'; - -describe('getActionBarIdFromActionMenuId', () => { - it('should return the correct action bar id', () => { - expect(getActionBarIdFromActionMenuId('action-menu-id')).toBe( - 'action-bar-action-menu-id', - ); - }); -}); diff --git a/packages/twenty-front/src/modules/action-menu/utils/getActionBarIdFromActionMenuId.ts b/packages/twenty-front/src/modules/action-menu/utils/getActionBarIdFromActionMenuId.ts deleted file mode 100644 index 2005c4830..000000000 --- a/packages/twenty-front/src/modules/action-menu/utils/getActionBarIdFromActionMenuId.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const getActionBarIdFromActionMenuId = (actionMenuId: string) => { - return `action-bar-${actionMenuId}`; -}; diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuContainer.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuContainer.tsx index b5c272535..7e14c34ed 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuContainer.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuContainer.tsx @@ -1,12 +1,8 @@ import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext'; -import { COMMAND_MENU_ANIMATION_VARIANTS } from '@/command-menu/constants/CommandMenuAnimationVariants'; +import { CommandMenuOpenContainer } from '@/command-menu/components/CommandMenuOpenContainer'; import { COMMAND_MENU_COMPONENT_INSTANCE_ID } from '@/command-menu/constants/CommandMenuComponentInstanceId'; -import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; import { useCommandMenuCloseAnimationCompleteCleanup } from '@/command-menu/hooks/useCommandMenuCloseAnimationCompleteCleanup'; -import { useCommandMenuHotKeys } from '@/command-menu/hooks/useCommandMenuHotKeys'; import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState'; -import { CommandMenuAnimationVariant } from '@/command-menu/types/CommandMenuAnimationVariant'; -import { CommandMenuHotkeyScope } from '@/command-menu/types/CommandMenuHotkeyScope'; import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState'; import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState'; import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext'; @@ -15,78 +11,20 @@ import { RecordFilterGroupsComponentInstanceContext } from '@/object-record/reco import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext'; import { RecordSortsComponentInstanceContext } from '@/object-record/record-sort/states/context/RecordSortsComponentInstanceContext'; import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId'; -import { RootStackingContextZIndices } from '@/ui/layout/constants/RootStackingContextZIndices'; -import { currentHotkeyScopeState } from '@/ui/utilities/hotkey/states/internal/currentHotkeyScopeState'; -import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; -import { useTheme } from '@emotion/react'; -import styled from '@emotion/styled'; -import { AnimatePresence, motion } from 'framer-motion'; -import { useRef } from 'react'; -import { useRecoilCallback, useRecoilValue } from 'recoil'; -import { useIsMobile } from 'twenty-ui/utilities'; - -const StyledCommandMenu = styled(motion.div)` - background: ${({ theme }) => theme.background.primary}; - border-left: 1px solid ${({ theme }) => theme.border.color.medium}; - box-shadow: ${({ theme }) => theme.boxShadow.strong}; - font-family: ${({ theme }) => theme.font.family}; - height: 100%; - overflow: hidden; - padding: 0; - position: fixed; - right: 0%; - top: 0%; - z-index: ${RootStackingContextZIndices.CommandMenu}; - display: flex; - flex-direction: column; -`; +import { AnimatePresence } from 'framer-motion'; +import { useRecoilValue } from 'recoil'; export const CommandMenuContainer = ({ children, }: { children: React.ReactNode; }) => { - const { closeCommandMenu } = useCommandMenu(); - const { commandMenuCloseAnimationCompleteCleanup } = useCommandMenuCloseAnimationCompleteCleanup(); const isCommandMenuOpened = useRecoilValue(isCommandMenuOpenedState); - const commandMenuRef = useRef(null); - - useCommandMenuHotKeys(); - - const handleClickOutside = useRecoilCallback( - ({ snapshot }) => - () => { - const hotkeyScope = snapshot - .getLoadable(currentHotkeyScopeState) - .getValue(); - - if (hotkeyScope?.scope === CommandMenuHotkeyScope.CommandMenuFocused) { - closeCommandMenu(); - } - }, - [closeCommandMenu], - ); - - useListenClickOutside({ - refs: [commandMenuRef], - callback: handleClickOutside, - listenerId: 'COMMAND_MENU_LISTENER_ID', - excludeClassNames: ['page-header-command-menu-button'], - }); - - const isMobile = useIsMobile(); - - const targetVariantForAnimation: CommandMenuAnimationVariant = isMobile - ? 'fullScreen' - : 'normal'; - - const theme = useTheme(); - const objectMetadataItemId = useRecoilComponentValueV2( contextStoreCurrentObjectMetadataItemIdComponentState, COMMAND_MENU_COMPONENT_INSTANCE_ID, @@ -129,18 +67,9 @@ export const CommandMenuContainer = ({ onExitComplete={commandMenuCloseAnimationCompleteCleanup} > {isCommandMenuOpened && ( - + {children} - + )} diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuOpenContainer.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuOpenContainer.tsx new file mode 100644 index 000000000..c19beb3e2 --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuOpenContainer.tsx @@ -0,0 +1,85 @@ +import { COMMAND_MENU_ANIMATION_VARIANTS } from '@/command-menu/constants/CommandMenuAnimationVariants'; +import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; +import { useCommandMenuHotKeys } from '@/command-menu/hooks/useCommandMenuHotKeys'; +import { CommandMenuAnimationVariant } from '@/command-menu/types/CommandMenuAnimationVariant'; +import { CommandMenuHotkeyScope } from '@/command-menu/types/CommandMenuHotkeyScope'; +import { RootStackingContextZIndices } from '@/ui/layout/constants/RootStackingContextZIndices'; +import { currentHotkeyScopeState } from '@/ui/utilities/hotkey/states/internal/currentHotkeyScopeState'; +import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; +import { useTheme } from '@emotion/react'; + +import styled from '@emotion/styled'; +import { motion } from 'framer-motion'; +import { useRef } from 'react'; +import { useRecoilCallback } from 'recoil'; +import { useIsMobile } from 'twenty-ui/utilities'; + +const StyledCommandMenu = styled(motion.div)` + background: ${({ theme }) => theme.background.primary}; + border-left: 1px solid ${({ theme }) => theme.border.color.medium}; + box-shadow: ${({ theme }) => theme.boxShadow.strong}; + font-family: ${({ theme }) => theme.font.family}; + height: 100%; + overflow: hidden; + padding: 0; + position: fixed; + right: 0%; + top: 0%; + z-index: ${RootStackingContextZIndices.CommandMenu}; + display: flex; + flex-direction: column; +`; + +export const CommandMenuOpenContainer = ({ + children, +}: React.PropsWithChildren) => { + const isMobile = useIsMobile(); + + const targetVariantForAnimation: CommandMenuAnimationVariant = isMobile + ? 'fullScreen' + : 'normal'; + + const theme = useTheme(); + + const { closeCommandMenu } = useCommandMenu(); + + const commandMenuRef = useRef(null); + + useCommandMenuHotKeys(); + + const handleClickOutside = useRecoilCallback( + ({ snapshot }) => + () => { + const hotkeyScope = snapshot + .getLoadable(currentHotkeyScopeState) + .getValue(); + + if (hotkeyScope?.scope === CommandMenuHotkeyScope.CommandMenuFocused) { + closeCommandMenu(); + } + }, + [closeCommandMenu], + ); + + useListenClickOutside({ + refs: [commandMenuRef], + callback: handleClickOutside, + listenerId: 'COMMAND_MENU_LISTENER_ID', + excludeClassNames: ['page-header-command-menu-button'], + }); + + return ( + + {children} + + ); +}; diff --git a/packages/twenty-front/src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenu.tsx b/packages/twenty-front/src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenu.tsx index 4296bcfb0..3c9c043e1 100644 --- a/packages/twenty-front/src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenu.tsx +++ b/packages/twenty-front/src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenu.tsx @@ -1,22 +1,16 @@ import { useRecoilValue } from 'recoil'; -import { Key } from 'ts-key-enum'; import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; -import { KEYBOARD_SHORTCUTS_GENERAL } from '@/keyboard-shortcut-menu/constants/KeyboardShortcutsGeneral'; -import { KEYBOARD_SHORTCUTS_TABLE } from '@/keyboard-shortcut-menu/constants/KeyboardShortcutsTable'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope'; import { useKeyboardShortcutMenu } from '../hooks/useKeyboardShortcutMenu'; import { isKeyboardShortcutMenuOpenedState } from '../states/isKeyboardShortcutMenuOpenedState'; -import { KeyboardMenuDialog } from './KeyboardShortcutMenuDialog'; -import { KeyboardMenuGroup } from './KeyboardShortcutMenuGroup'; -import { KeyboardMenuItem } from './KeyboardShortcutMenuItem'; +import { KeyboardShortcutMenuOpenContent } from '@/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent'; export const KeyboardShortcutMenu = () => { - const { toggleKeyboardShortcutMenu, closeKeyboardShortcutMenu } = - useKeyboardShortcutMenu(); + const { toggleKeyboardShortcutMenu } = useKeyboardShortcutMenu(); const isKeyboardShortcutMenuOpened = useRecoilValue( isKeyboardShortcutMenuOpenedState, ); @@ -32,31 +26,7 @@ export const KeyboardShortcutMenu = () => { [toggleKeyboardShortcutMenu], ); - useScopedHotkeys( - [Key.Escape], - () => { - closeKeyboardShortcutMenu(); - }, - AppHotkeyScope.KeyboardShortcutMenuOpen, - [closeKeyboardShortcutMenu], - ); - return ( - <> - {isKeyboardShortcutMenuOpened && ( - - - {KEYBOARD_SHORTCUTS_TABLE.map((TableShortcut, index) => ( - - ))} - - - {KEYBOARD_SHORTCUTS_GENERAL.map((GeneralShortcut) => ( - - ))} - - - )} - + <>{isKeyboardShortcutMenuOpened && } ); }; diff --git a/packages/twenty-front/src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx b/packages/twenty-front/src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx new file mode 100644 index 000000000..ae68bdc4e --- /dev/null +++ b/packages/twenty-front/src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx @@ -0,0 +1,43 @@ +import { Key } from 'ts-key-enum'; + +import { KEYBOARD_SHORTCUTS_GENERAL } from '@/keyboard-shortcut-menu/constants/KeyboardShortcutsGeneral'; +import { KEYBOARD_SHORTCUTS_TABLE } from '@/keyboard-shortcut-menu/constants/KeyboardShortcutsTable'; +import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; +import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope'; + +import { useKeyboardShortcutMenu } from '../hooks/useKeyboardShortcutMenu'; + +import { KeyboardMenuDialog } from './KeyboardShortcutMenuDialog'; +import { KeyboardMenuGroup } from './KeyboardShortcutMenuGroup'; +import { KeyboardMenuItem } from './KeyboardShortcutMenuItem'; + +export const KeyboardShortcutMenuOpenContent = () => { + const { toggleKeyboardShortcutMenu, closeKeyboardShortcutMenu } = + useKeyboardShortcutMenu(); + + useScopedHotkeys( + [Key.Escape], + () => { + closeKeyboardShortcutMenu(); + }, + AppHotkeyScope.KeyboardShortcutMenuOpen, + [closeKeyboardShortcutMenu], + ); + + return ( + <> + + + {KEYBOARD_SHORTCUTS_TABLE.map((TableShortcut, index) => ( + + ))} + + + {KEYBOARD_SHORTCUTS_GENERAL.map((GeneralShortcut) => ( + + ))} + + + + ); +}; diff --git a/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoard.tsx b/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoard.tsx index 822684dfb..aac8012ac 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoard.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoard.tsx @@ -2,7 +2,6 @@ import styled from '@emotion/styled'; import { DragDropContext, OnDragEndResponder } from '@hello-pangea/dnd'; // Atlassian dnd does not support StrictMode from RN 18, so we use a fork @hello-pangea/dnd https://github.com/atlassian/react-beautiful-dnd/issues/2350 import { useContext, useRef } from 'react'; import { useRecoilCallback, useSetRecoilState } from 'recoil'; -import { Key } from 'ts-key-enum'; import { getActionMenuIdFromRecordIndexId } from '@/action-menu/utils/getActionMenuIdFromRecordIndexId'; import { RecordBoardHeader } from '@/object-record/record-board/components/RecordBoardHeader'; @@ -136,8 +135,6 @@ export const RecordBoard = () => { useScopedHotkeys('ctrl+a,meta+a', selectAll, TableHotkeyScope.Table); - useScopedHotkeys(Key.Escape, resetRecordSelection, TableHotkeyScope.Table); - const setIsRemoveSortingModalOpen = useSetRecoilState( isRemoveSortingModalOpenState, ); diff --git a/packages/twenty-front/src/modules/object-record/record-field/hooks/useOpenFieldInputEditMode.ts b/packages/twenty-front/src/modules/object-record/record-field/hooks/useOpenFieldInputEditMode.ts index 50695d003..0aae2c4b8 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/hooks/useOpenFieldInputEditMode.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/hooks/useOpenFieldInputEditMode.ts @@ -18,7 +18,7 @@ import { isFieldRelationToOneObject } from '@/object-record/record-field/types/g import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector'; import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2'; -import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope'; +import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope'; import { useRecoilCallback } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; @@ -30,7 +30,7 @@ export const useOpenFieldInputEditMode = () => { const { openActivityTargetCellEditMode } = useOpenActivityTargetCellEditMode(); - const setHotkeyScope = useSetHotkeyScope(); + const { setHotkeyScopeAndMemorizePreviousScope } = usePreviousHotkeyScope(); const openFieldInput = useRecoilCallback( ({ snapshot }) => @@ -102,7 +102,7 @@ export const useOpenFieldInputEditMode = () => { } } - setHotkeyScope( + setHotkeyScopeAndMemorizePreviousScope( DEFAULT_CELL_SCOPE.scope, DEFAULT_CELL_SCOPE.customScopes, ); @@ -111,7 +111,7 @@ export const useOpenFieldInputEditMode = () => { openActivityTargetCellEditMode, openRelationFromManyFieldInput, openRelationToOneFieldInput, - setHotkeyScope, + setHotkeyScopeAndMemorizePreviousScope, ], ); diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/FullNameFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/FullNameFieldInput.tsx index a60baa1cb..bca67a3fe 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/FullNameFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/FullNameFieldInput.tsx @@ -9,6 +9,7 @@ import { FieldInputClickOutsideEvent, FieldInputEvent, } from '@/object-record/record-field/types/FieldInputEvent'; +import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2'; type FullNameFieldInputProps = { onClickOutside?: FieldInputClickOutsideEvent; @@ -25,7 +26,7 @@ export const FullNameFieldInput = ({ onTab, onShiftTab, }: FullNameFieldInputProps) => { - const { draftValue, setDraftValue, persistFullNameField, fieldDefinition } = + const { draftValue, setDraftValue, persistFullNameField } = useFullNameField(); const convertToFullName = (newDoubleText: FieldDoubleText) => { @@ -93,7 +94,7 @@ export const FullNameFieldInput = ({ onShiftTab={handleShiftTab} onTab={handleTab} onPaste={handlePaste} - hotkeyScope={`full-name-field-input-${fieldDefinition.metadata.fieldName}`} + hotkeyScope={DEFAULT_CELL_SCOPE.scope} onChange={handleChange} /> ); diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/SelectFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/SelectFieldInput.tsx index 69d551394..596e31a55 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/SelectFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/SelectFieldInput.tsx @@ -50,7 +50,7 @@ export const SelectFieldInput = ({ onCancel?.(); resetSelectedItem(); }, - `select-field-input-${fieldDefinition.metadata.fieldName}`, + DEFAULT_CELL_SCOPE.scope, [onCancel, resetSelectedItem], ); diff --git a/packages/twenty-front/src/modules/object-record/record-inline-cell/components/RecordInlineCell.tsx b/packages/twenty-front/src/modules/object-record/record-inline-cell/components/RecordInlineCell.tsx index 37c17f810..8172584b4 100644 --- a/packages/twenty-front/src/modules/object-record/record-inline-cell/components/RecordInlineCell.tsx +++ b/packages/twenty-front/src/modules/object-record/record-inline-cell/components/RecordInlineCell.tsx @@ -14,7 +14,7 @@ import { useIsFieldInputOnly } from '@/object-record/record-field/hooks/useIsFie import { useOpenFieldInputEditMode } from '@/object-record/record-field/hooks/useOpenFieldInputEditMode'; import { useInlineCell } from '@/object-record/record-inline-cell/hooks/useInlineCell'; -import { InlineCellHotkeyScope } from '@/object-record/record-inline-cell/types/InlineCellHotkeyScope'; +import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2'; import { currentHotkeyScopeState } from '@/ui/utilities/hotkey/states/internal/currentHotkeyScopeState'; import { useRecoilCallback } from 'recoil'; import { useIcons } from 'twenty-ui/display'; @@ -80,9 +80,11 @@ export const RecordInlineCell = ({ loading }: RecordInlineCellProps) => { const hotkeyScope = snapshot .getLoadable(currentHotkeyScopeState) .getValue(); - if (hotkeyScope.scope !== InlineCellHotkeyScope.InlineCell) { + + if (hotkeyScope.scope !== DEFAULT_CELL_SCOPE.scope) { return; } + event.stopImmediatePropagation(); persistField(); diff --git a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableBodyEffectsWrapper.tsx b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableBodyEffectsWrapper.tsx index 3d2954312..45fe5551a 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableBodyEffectsWrapper.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableBodyEffectsWrapper.tsx @@ -1,6 +1,11 @@ -import { RecordTableBodyUnselectEffect } from '@/object-record/record-table/record-table-body/components/RecordTableBodyUnselectEffect'; +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 { RecordTableBodySoftFocusKeyboardEffect } from '@/object-record/record-table/record-table-body/components/RecordTableBodySoftFocusKeyboardEffect'; 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 { isAtLeastOneTableRowSelectedSelector } from '@/object-record/record-table/record-table-row/states/isAtLeastOneTableRowSelectedSelector'; +import { isSoftFocusActiveComponentState } from '@/object-record/record-table/states/isSoftFocusActiveComponentState'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; export interface RecordTableBodyEffectsWrapperProps { hasRecordGroups: boolean; @@ -10,13 +15,29 @@ export interface RecordTableBodyEffectsWrapperProps { export const RecordTableBodyEffectsWrapper = ({ hasRecordGroups, tableBodyRef, -}: RecordTableBodyEffectsWrapperProps) => ( - <> - {hasRecordGroups ? ( - - ) : ( - - )} - - -); +}: RecordTableBodyEffectsWrapperProps) => { + const isAtLeastOneRecordSelected = useRecoilComponentValueV2( + isAtLeastOneTableRowSelectedSelector, + ); + + const isSoftFocusActiveState = useRecoilComponentValueV2( + isSoftFocusActiveComponentState, + ); + + return ( + <> + {hasRecordGroups ? ( + + ) : ( + + )} + {isAtLeastOneRecordSelected && } + {isSoftFocusActiveState && } + {isSoftFocusActiveState && ( + + )} + + ); +}; diff --git a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableWithWrappers.tsx b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableWithWrappers.tsx index b599a4104..45cd693a6 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableWithWrappers.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableWithWrappers.tsx @@ -10,14 +10,12 @@ import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper'; import { useSaveCurrentViewFields } from '@/views/hooks/useSaveCurrentViewFields'; import { mapColumnDefinitionsToViewFields } from '@/views/utils/mapColumnDefinitionToViewField'; -import { RecordUpdateContext } from '../contexts/EntityUpdateMutationHookContext'; -import { useRecordTable } from '../hooks/useRecordTable'; - import { RecordTableComponentInstance } from '@/object-record/record-table/components/RecordTableComponentInstance'; import { RecordTableContextProvider } from '@/object-record/record-table/components/RecordTableContextProvider'; import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; -import { Key } from 'ts-key-enum'; +import { RecordUpdateContext } from '../contexts/EntityUpdateMutationHookContext'; +import { useRecordTable } from '../hooks/useRecordTable'; const StyledTableContainer = styled.div` display: flex; @@ -39,10 +37,9 @@ export const RecordTableWithWrappers = ({ recordTableId, viewBarId, }: RecordTableWithWrappersProps) => { - const { resetTableRowSelection, selectAllRows, setHasUserSelectedAllRows } = - useRecordTable({ - recordTableId, - }); + const { selectAllRows, setHasUserSelectedAllRows } = useRecordTable({ + recordTableId, + }); const handleSelectAllRows = () => { setHasUserSelectedAllRows(true); @@ -55,8 +52,6 @@ export const RecordTableWithWrappers = ({ TableHotkeyScope.Table, ); - useScopedHotkeys(Key.Escape, resetTableRowSelection, TableHotkeyScope.Table); - const { saveViewFields } = useSaveCurrentViewFields(); const { deleteOneRecord } = useDeleteOneRecord({ objectNameSingular }); diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyEscapeHotkeyEffect.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyEscapeHotkeyEffect.tsx new file mode 100644 index 000000000..54272d089 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyEscapeHotkeyEffect.tsx @@ -0,0 +1,24 @@ +import { Key } from 'ts-key-enum'; + +import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext'; +import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable'; +import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope'; +import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; + +export const RecordTableBodyEscapeHotkeyEffect = () => { + const { recordTableId } = useRecordTableContextOrThrow(); + + const { resetTableRowSelection } = useRecordTable({ + recordTableId, + }); + + useScopedHotkeys( + [Key.Escape], + () => { + resetTableRowSelection(); + }, + TableHotkeyScope.Table, + ); + + return <>; +}; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyUnselectEffect.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodySoftFocusClickOutsideEffect.tsx similarity index 58% rename from packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyUnselectEffect.tsx rename to packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodySoftFocusClickOutsideEffect.tsx index 4381106a8..65925cfb0 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyUnselectEffect.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodySoftFocusClickOutsideEffect.tsx @@ -1,38 +1,19 @@ -import { Key } from 'ts-key-enum'; - import { RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-table/constants/RecordTableClickOutsideListenerId'; import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext'; import { useLeaveTableFocus } from '@/object-record/record-table/hooks/internal/useLeaveTableFocus'; -import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable'; -import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope'; -import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; -type RecordTableBodyUnselectEffectProps = { +type RecordTableBodySoftFocusClickOutsideEffectProps = { tableBodyRef: React.RefObject; }; -export const RecordTableBodyUnselectEffect = ({ +export const RecordTableBodySoftFocusClickOutsideEffect = ({ tableBodyRef, -}: RecordTableBodyUnselectEffectProps) => { +}: RecordTableBodySoftFocusClickOutsideEffectProps) => { const { recordTableId } = useRecordTableContextOrThrow(); const leaveTableFocus = useLeaveTableFocus(recordTableId); - const { resetTableRowSelection, useMapKeyboardToSoftFocus } = useRecordTable({ - recordTableId, - }); - - useMapKeyboardToSoftFocus(); - - useScopedHotkeys( - [Key.Escape], - () => { - resetTableRowSelection(); - }, - TableHotkeyScope.Table, - ); - useListenClickOutside({ excludeClassNames: [ 'bottom-bar', diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodySoftFocusKeyboardEffect.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodySoftFocusKeyboardEffect.tsx new file mode 100644 index 000000000..66265bc0e --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodySoftFocusKeyboardEffect.tsx @@ -0,0 +1,14 @@ +import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext'; +import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable'; + +export const RecordTableBodySoftFocusKeyboardEffect = () => { + const { recordTableId } = useRecordTableContextOrThrow(); + + const { useMapKeyboardToSoftFocus } = useRecordTable({ + recordTableId, + }); + + useMapKeyboardToSoftFocus(); + + return <>; +}; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellDisplayMode.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellDisplayMode.tsx index 6814c021e..06ec2e1e4 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellDisplayMode.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellDisplayMode.tsx @@ -1,26 +1,55 @@ import { FieldContext } from '@/object-record/record-field/contexts/FieldContext'; +import { useIsFieldInputOnly } from '@/object-record/record-field/hooks/useIsFieldInputOnly'; 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 { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; import { useContext } from 'react'; import { RecordTableCellDisplayContainer } from './RecordTableCellDisplayContainer'; export const RecordTableCellDisplayMode = ({ children, - softFocus, -}: React.PropsWithChildren<{ softFocus?: boolean }>) => { - const { recordId } = useContext(FieldContext); +}: React.PropsWithChildren) => { + const { recordId, isReadOnly } = useContext(FieldContext); + + const { columnIndex, hasSoftFocus } = useContext(RecordTableCellContext); + + const isMobile = useIsMobile(); const { onActionMenuDropdownOpened } = useRecordTableBodyContextOrThrow(); + const { openTableCell } = useOpenRecordTableCellFromCell(); + + const isFieldInputOnly = useIsFieldInputOnly(); + const isFirstColumn = columnIndex === 0; + + const showButton = + hasSoftFocus && + !isFieldInputOnly && + !isReadOnly && + !(isMobile && isFirstColumn); + const handleActionMenuDropdown = (event: React.MouseEvent) => { onActionMenuDropdownOpened(event, recordId); }; + const handleClick = () => { + if (!isFieldInputOnly && !isReadOnly) { + openTableCell(); + } + }; + return ( - - {children} - + <> + + {children} + + {showButton && } + ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellEditButton.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellEditButton.tsx new file mode 100644 index 000000000..886dabca1 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellEditButton.tsx @@ -0,0 +1,36 @@ +import { useGetButtonIcon } from '@/object-record/record-field/hooks/useGetButtonIcon'; +import { useIsFieldInputOnly } from '@/object-record/record-field/hooks/useIsFieldInputOnly'; +import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext'; +import { RecordTableCellButton } from '@/object-record/record-table/record-table-cell/components/RecordTableCellButton'; +import { useOpenRecordTableCellFromCell } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellFromCell'; +import { useContext } from 'react'; +import { isDefined } from 'twenty-shared/utils'; +import { IconArrowUpRight, IconPencil } from 'twenty-ui/display'; + +export const RecordTableCellEditButton = () => { + const { columnIndex } = useContext(RecordTableCellContext); + + const { openTableCell } = useOpenRecordTableCellFromCell(); + + const isFieldInputOnly = useIsFieldInputOnly(); + const isFirstColumn = columnIndex === 0; + const customButtonIcon = useGetButtonIcon(); + + const buttonIcon = isFirstColumn + ? IconArrowUpRight + : isDefined(customButtonIcon) + ? customButtonIcon + : IconPencil; + + const handleButtonClick = () => { + if (!isFieldInputOnly && isFirstColumn) { + openTableCell(undefined, false, true); + } else { + openTableCell(); + } + }; + + return ( + + ); +}; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-row/states/isAtLeastOneTableRowSelectedSelector.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-row/states/isAtLeastOneTableRowSelectedSelector.ts new file mode 100644 index 000000000..8ed9616ee --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-row/states/isAtLeastOneTableRowSelectedSelector.ts @@ -0,0 +1,30 @@ +import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector'; +import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState'; +import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; +import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2'; + +export const isAtLeastOneTableRowSelectedSelector = + createComponentSelectorV2({ + key: 'isAtLeastOneTableRowSelectedSelector', + componentInstanceContext: RecordTableComponentInstanceContext, + get: + ({ instanceId }) => + ({ get }) => { + const allRecordIds = get( + recordIndexAllRecordIdsComponentSelector.selectorFamily({ + instanceId, + }), + ); + + const isAnyRecordSelected = allRecordIds.some((recordId) => + get( + isRowSelectedComponentFamilyState.atomFamily({ + instanceId, + familyKey: recordId, + }), + ), + ); + + return isAnyRecordSelected; + }, + }); diff --git a/packages/twenty-front/src/modules/object-record/record-title-cell/components/RecordTitleCellFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-title-cell/components/RecordTitleCellFieldInput.tsx index 9acf65d93..cb2885c51 100644 --- a/packages/twenty-front/src/modules/object-record/record-title-cell/components/RecordTitleCellFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-title-cell/components/RecordTitleCellFieldInput.tsx @@ -4,6 +4,7 @@ import { FieldContext } from '@/object-record/record-field/contexts/FieldContext import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent'; import { isFieldFullName } from '@/object-record/record-field/types/guards/isFieldFullName'; import { isFieldText } from '@/object-record/record-field/types/guards/isFieldText'; +import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2'; import { RecordTitleCellTextFieldInput } from '@/object-record/record-title-cell/components/RecordTitleCellTextFieldInput'; import { RecordTitleFullNameFieldInput } from '@/object-record/record-title-cell/components/RecordTitleFullNameFieldInput'; @@ -27,7 +28,7 @@ export const RecordTitleCellFieldInput = ({ onTab, onClickOutside, }: RecordTitleCellFieldInputProps) => { - const { fieldDefinition, recordId } = useContext(FieldContext); + const { fieldDefinition } = useContext(FieldContext); if (!isFieldText(fieldDefinition) && !isFieldFullName(fieldDefinition)) { throw new Error('Field definition is not a text or full name field'); @@ -43,7 +44,7 @@ export const RecordTitleCellFieldInput = ({ onTab={onTab} onShiftTab={onShiftTab} sizeVariant={sizeVariant} - hotkeyScope={`record-title-cell-text-field-input-${recordId}`} + hotkeyScope={DEFAULT_CELL_SCOPE.scope} /> ) : isFieldFullName(fieldDefinition) ? ( ) : null} diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/components/Dropdown.tsx b/packages/twenty-front/src/modules/ui/layout/dropdown/components/Dropdown.tsx index 4e08747b5..dae210843 100644 --- a/packages/twenty-front/src/modules/ui/layout/dropdown/components/Dropdown.tsx +++ b/packages/twenty-front/src/modules/ui/layout/dropdown/components/Dropdown.tsx @@ -140,7 +140,7 @@ export const Dropdown = ({ dropdownHotkeyScope, ); - toggleDropdown(); + toggleDropdown(dropdownHotkeyScope); onClickOutside?.(); }, [dropdownId, dropdownHotkeyScope, onClickOutside, toggleDropdown], diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useDropdown.ts b/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useDropdown.ts index 1f1cc83d3..a8cf900ba 100644 --- a/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useDropdown.ts +++ b/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useDropdown.ts @@ -83,11 +83,11 @@ export const useDropdown = (dropdownId?: string) => { ], ); - const toggleDropdown = () => { + const toggleDropdown = (dropdownHotkeyScopeFromProps?: HotkeyScope) => { if (isDropdownOpen) { closeDropdown(); } else { - openDropdown(); + openDropdown(dropdownHotkeyScopeFromProps); } }; diff --git a/packages/twenty-front/src/modules/ui/utilities/hotkey/hooks/usePreviousHotkeyScope.ts b/packages/twenty-front/src/modules/ui/utilities/hotkey/hooks/usePreviousHotkeyScope.ts index 1f31fbf43..d98e9ab12 100644 --- a/packages/twenty-front/src/modules/ui/utilities/hotkey/hooks/usePreviousHotkeyScope.ts +++ b/packages/twenty-front/src/modules/ui/utilities/hotkey/hooks/usePreviousHotkeyScope.ts @@ -20,11 +20,18 @@ export const usePreviousHotkeyScope = (memoizeKey = 'global') => { .getValue(); if (!previousHotkeyScope) { + if (DEBUG_HOTKEY_SCOPE) { + logDebug(`DEBUG: no previous hotkey scope ${memoizeKey}`); + } + return; } if (DEBUG_HOTKEY_SCOPE) { - logDebug('DEBUG: goBackToPreviousHotkeyScope', previousHotkeyScope); + logDebug( + `DEBUG: goBackToPreviousHotkeyScope ${previousHotkeyScope.scope}`, + previousHotkeyScope, + ); } setHotkeyScope( diff --git a/packages/twenty-front/src/modules/ui/utilities/hotkey/hooks/useSetHotkeyScope.ts b/packages/twenty-front/src/modules/ui/utilities/hotkey/hooks/useSetHotkeyScope.ts index 543909293..adcf1cb28 100644 --- a/packages/twenty-front/src/modules/ui/utilities/hotkey/hooks/useSetHotkeyScope.ts +++ b/packages/twenty-front/src/modules/ui/utilities/hotkey/hooks/useSetHotkeyScope.ts @@ -1,9 +1,9 @@ import { useRecoilCallback } from 'recoil'; import { DEBUG_HOTKEY_SCOPE } from '@/ui/utilities/hotkey/hooks/useScopedHotkeyCallback'; -import { logDebug } from '~/utils/logDebug'; import { isDefined } from 'twenty-shared/utils'; +import { logDebug } from '~/utils/logDebug'; import { DEFAULT_HOTKEYS_SCOPE_CUSTOM_SCOPES } from '../constants/DefaultHotkeysScopeCustomScopes'; import { currentHotkeyScopeState } from '../states/internal/currentHotkeyScopeState'; import { internalHotkeysEnabledScopesState } from '../states/internal/internalHotkeysEnabledScopesState'; @@ -84,7 +84,7 @@ export const useSetHotkeyScope = () => scopesToSet.push(newHotkeyScope.scope); if (DEBUG_HOTKEY_SCOPE) { - logDebug('DEBUG: set new hotkey scope', { + logDebug(`DEBUG: set new hotkey scope : ${newHotkeyScope.scope}`, { scopesToSet, newHotkeyScope, }); diff --git a/packages/twenty-front/src/modules/ui/utilities/pointer-event/hooks/useListenClickOutside.ts b/packages/twenty-front/src/modules/ui/utilities/pointer-event/hooks/useListenClickOutside.ts index 882b82743..76be7d45a 100644 --- a/packages/twenty-front/src/modules/ui/utilities/pointer-event/hooks/useListenClickOutside.ts +++ b/packages/twenty-front/src/modules/ui/utilities/pointer-event/hooks/useListenClickOutside.ts @@ -162,6 +162,21 @@ export const useListenClickOutside = ({ !isMouseDownInside && !isClickedOnExcluded; + if (CLICK_OUTSIDE_DEBUG_MODE) { + // eslint-disable-next-line no-console + console.log('click outside compare ref', { + listenerId, + shouldTrigger, + clickedOnAtLeastOneRef, + isMouseDownInside, + isListening, + hasMouseDownHappened, + isClickedOnExcluded, + enabled, + event, + }); + } + if (shouldTrigger) { callback(event); }