From cbc0d06a2f7b0dc5909851020d11dfcb2e8ea6ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Bosi?= <71827178+bosiraphael@users.noreply.github.com> Date: Thu, 19 Jun 2025 14:53:18 +0200 Subject: [PATCH] Replace hotkey scopes by focus stack (Part 1 - Dropdowns and Side Panel) (#12673) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR is the first part of a refactoring aiming to deprecate the hotkey scopes api in favor of the new focus stack api which is more robust. The refactored components in this PR are the dropdowns and the side panel/command menu. - Replaced `useScopedHotkeys` by `useHotkeysOnFocusedElement` for all dropdown components, selectable lists and the command menu - Introduced `focusId` for all dropdowns and created a common hotkey scope `DropdownHotkeyScope` for backward compatibility - Replaced `setHotkeyScopeAndMemorizePreviousScope` occurrences with `usePushFocusItemToFocusStack` and `goBackToPreviousHotkeyScope` with `removeFocusItemFromFocusStack` Note: Test that the shorcuts and arrow key navigation still work properly when interacting with dropdowns and the command menu. Bugs that I have spotted during the QA but which are already present on main: - Icon picker select with arrow keys doesn’t work inside dropdowns - Some dropdowns are not selectable with arrow keys (no selectable list) - Dropdowns in dropdowns don’t reset the hotkey scope correctly when closing - The table click outside is not triggered after closing a table cell and clicking outside of the table --- .../components/CmdEnterActionButton.tsx | 16 ++-- .../CommandMenuActionMenuDropdown.tsx | 58 +++++++------ .../RecordIndexActionMenuDropdown.tsx | 8 +- .../RecordShowRightDrawerOpenRecordButton.tsx | 16 ++-- .../types/ActionMenuDropdownHotKeyScope.ts | 3 - ...ommandMenuActionMenuDropdownHotkeyScope.ts | 3 - .../files/components/AttachmentDropdown.tsx | 1 - .../components/ActivityTargetsInlineCell.tsx | 1 + .../useOpenActivityTargetCellEditMode.ts | 21 +++-- .../CommandMenuContextChipGroups.tsx | 4 - .../components/CommandMenuList.tsx | 4 +- .../constants/SidePanelFocusId.ts | 1 + .../command-menu/hooks/useCommandMenu.ts | 13 ++- .../hooks/useNavigateCommandMenu.ts | 25 ++++-- ...riteFolderNavigationDrawerItemDropdown.tsx | 4 - .../components/FavoriteFolderPicker.tsx | 34 +++++--- .../AdvancedFilterAddFilterRuleSelect.tsx | 1 - .../AdvancedFilterDropdownFilterInput.tsx | 11 ++- ...dvancedFilterFieldSelectDropdownButton.tsx | 1 - .../AdvancedFilterFieldSelectMenu.tsx | 4 +- ...FilterRecordFilterGroupOptionsDropdown.tsx | 1 - ...FilterRecordFilterOperandSelectContent.tsx | 5 +- ...ancedFilterRecordFilterOptionsDropdown.tsx | 1 - .../AdvancedFilterSubFieldSelectMenu.tsx | 4 +- .../components/AdvancedFilterValueInput.tsx | 6 +- ...SelectFieldUsedInAdvancedFilterDropdown.ts | 20 +++-- .../ObjectFilterDropdownBooleanSelect.tsx | 5 +- .../ObjectFilterDropdownFilterInput.tsx | 9 +- .../ObjectFilterDropdownOperandDropdown.tsx | 4 - .../ObjectFilterDropdownOptionSelect.tsx | 26 +++--- .../ObjectFilterDropdownRecordSelect.tsx | 5 +- .../ObjectFilterDropdownSourceSelect.tsx | 9 +- .../types/FiltersHotkeyScope.ts | 5 -- .../components/ObjectOptionsDropdown.tsx | 2 - .../ObjectOptionsDropdownLayoutContent.tsx | 5 +- ...jectOptionsDropdownLayoutOpenInContent.tsx | 5 +- .../ObjectOptionsDropdownMenuContent.tsx | 16 +--- .../ObjectOptionsDropdownMenuViewName.tsx | 17 ++-- ...tOptionsDropdownRecordGroupSortContent.tsx | 5 +- ...jectOptionsDropdownRecordGroupsContent.tsx | 12 ++- .../components/ObjectSortDropdownButton.tsx | 30 +++---- .../components/RecordBoardCard.tsx | 4 +- ...cordBoardColumnHeaderAggregateDropdown.tsx | 4 - .../components/FormMultiSelectFieldInput.tsx | 4 +- .../components/FormSingleRecordPicker.tsx | 4 +- .../hooks/useOpenFieldInputEditMode.ts | 33 ++++++-- .../meta-types/hooks/useMultiSelectField.ts | 1 + .../meta-types/hooks/useSelectField.ts | 1 + .../components/MultiSelectFieldInput.tsx | 10 ++- .../components/RelationFromManyFieldInput.tsx | 7 +- .../components/RelationToOneFieldInput.tsx | 7 +- .../input/components/SelectFieldInput.tsx | 9 +- .../RelationFromManyFieldInput.stories.tsx | 10 ++- .../RelationToOneFieldInput.stories.tsx | 31 +++++-- .../useOpenRelationFromManyFieldInput.tsx | 27 ++++-- .../hooks/useOpenRelationToOneFieldInput.tsx | 26 ++++-- .../input/types/RelationPickerHotkeyScope.ts | 3 - ...getRelationFromManyFieldInputInstanceId.ts | 9 ++ .../getRelationToOneFieldInputInstanceId.ts | 9 ++ .../utils/getFieldInputInstanceId.ts | 6 ++ .../components/MultipleRecordPicker.tsx | 29 ++++--- .../MultipleRecordPickerItemsDisplay.tsx | 4 +- .../MultipleRecordPickerMenuItems.tsx | 7 +- .../types/MultipleRecordPickerHotkeyScope.ts | 3 - .../components/SingleRecordPicker.tsx | 2 + .../SingleRecordPickerMenuItems.tsx | 24 +++--- .../SingleRecordPickerMenuItemsWithSearch.tsx | 5 ++ .../types/SingleRecordPickerHotkeyScope.ts | 3 - .../RecordDetailRelationRecordsListItem.tsx | 1 - .../RecordDetailRelationSectionDropdown.tsx | 3 +- .../useCloseRecordTableCellInGroup.ts | 8 ++ .../useCloseRecordTableCellNoGroup.ts | 8 ++ .../hooks/useTriggerActionMenuDropdown.ts | 5 +- ...TableColumnAggregateFooterWithDropdown.tsx | 1 - .../RecordTableColumnHeadWithDropdown.tsx | 1 - .../RecordTableHeaderLastColumn.tsx | 6 -- .../components/MultipleSelectDropdown.tsx | 23 ++--- .../SettingsAccountsRowDropdownMenu.tsx | 1 - .../ConfigVariableDatabaseInput.tsx | 2 - .../ConfigVariableFilterDropdown.tsx | 1 - ...ngsDataModelNewFieldBreadcrumbDropDown.tsx | 1 - ...tingsDataModelFieldSelectFormOptionRow.tsx | 2 - ...ettingsObjectFieldActiveActionDropdown.tsx | 1 - ...tingsObjectFieldDisabledActionDropdown.tsx | 3 - .../SettingsObjectInactiveMenuDropDown.tsx | 1 - ...tegrationDatabaseConnectionSummaryCard.tsx | 1 - .../components/SettingsRoleAssignment.tsx | 1 - .../SettingsSecuritySSORowDropdownMenu.tsx | 1 - ...ityApprovedAccessDomainRowDropdownMenu.tsx | 1 - ...FunctionTabEnvironmentVariableTableRow.tsx | 1 - .../components/MatchColumnToFieldSelect.tsx | 3 - .../SubMatchingSelectRowRightDropdown.tsx | 1 - .../support/components/SupportDropdown.tsx | 1 - .../input/components/MultiSelectInput.tsx | 52 +++++++----- .../ui/field/input/components/SelectInput.tsx | 10 ++- .../ui/input/components/IconPicker.tsx | 8 +- .../modules/ui/input/components/Select.tsx | 6 +- .../ui/input/components/SelectInput.tsx | 83 +++++++++++-------- .../CurrencyPickerDropdownButton.tsx | 1 - .../PhoneCountryPickerDropdownButton.tsx | 3 - .../ui/input/types/SelectHotkeyScope.ts | 3 - .../layout/dropdown/components/Dropdown.tsx | 32 +++---- .../components/DropdownMenuInnerSelect.tsx | 10 +-- .../__stories__/Dropdown.stories.tsx | 3 - .../DropdownMenuHeader.stories.tsx | 2 - .../internal/DropdownInternalContainer.tsx | 37 +++++---- .../dropdown/constants/DropdownHotkeyScope.ts | 3 + .../constants/DropdownMenuHotkeyScope.ts | 3 - .../useInternalHotkeyScopeManagement.test.tsx | 47 ----------- .../hooks/internal/useDropdownStates.ts | 5 -- .../dropdown/hooks/useCloseAnyOpenDropdown.ts | 18 ++-- .../ui/layout/dropdown/hooks/useDropdown.ts | 81 +++++++++--------- .../ui/layout/dropdown/hooks/useDropdownV2.ts | 71 +++++++++------- .../hooks/useInternalHotkeyScopeManagement.ts | 30 ------- .../states/dropdownHotkeyComponentState.ts | 9 -- .../ui/layout/modal/hooks/useModal.tsx | 14 ++-- .../components/SelectableList.tsx | 15 +++- .../SelectableListItemHotkeyEffect.tsx | 6 +- .../internal/useSelectableListHotKeys.ts | 41 ++++++--- ...SelectableListListenToEnterHotkeyOnItem.ts | 23 +++-- .../states/contexts/SelectableListContext.tsx | 1 + .../tab-list/components/TabListDropdown.tsx | 1 - .../components/MenuItemWithOptionDropdown.tsx | 2 - .../MultiWorkspaceDropdownButton.tsx | 4 - ...ultiWorkspaceDropdownDefaultComponents.tsx | 8 +- .../focus/constants/DebugFocusStack.ts | 3 + .../usePushFocusItemToFocusStack.test.tsx | 8 +- ...useRemoveFocusItemFromFocusStack.test.tsx} | 21 ++--- .../__tests__/useResetFocusStack.test.tsx | 8 +- .../useResetFocusStackToFocusItem.test.tsx | 10 +-- .../hooks/usePushFocusItemToFocusStack.ts | 56 +++++++++---- .../hooks/useRemoveFocusIdFromFocusStack.ts | 22 ----- .../hooks/useRemoveFocusItemFromFocusStack.ts | 34 ++++++++ .../focus/hooks/useResetFocusStack.ts | 10 ++- .../hooks/useResetFocusStackToFocusItem.ts | 12 ++- .../focus/types/FocusComponentType.ts | 3 + .../hotkey/components/HotkeyEffect.tsx | 16 ++-- .../useHotkeysOnFocusedElementCallback.ts | 4 +- .../AdvancedFilterDropdownButton.tsx | 1 - .../EditableFilterDropdownButton.tsx | 4 - .../components/UpdateViewButtonGroup.tsx | 5 +- .../src/modules/views/components/ViewBar.tsx | 22 +---- .../views/components/ViewBarDetails.tsx | 7 +- .../views/components/ViewBarFilterButton.tsx | 17 +--- .../components/ViewBarFilterDropdown.tsx | 15 ++-- ...wBarFilterDropdownAdvancedFilterButton.tsx | 4 +- .../ViewBarFilterDropdownFieldSelectMenu.tsx | 6 +- ...temFromViewBarFilterDropdown.test.test.tsx | 28 +++++-- ...ldMetadataItemFromViewBarFilterDropdown.ts | 28 +++++-- .../modules/views/types/ViewsHotkeyScope.ts | 4 - .../ViewPickerContentCreateMode.tsx | 31 +++---- .../components/ViewPickerContentEditMode.tsx | 21 +++-- .../components/ViewPickerDropdown.tsx | 2 - .../WorkflowEditTriggerDatabaseEventForm.tsx | 4 +- .../components/WorkflowVariablesDropdown.tsx | 3 - 155 files changed, 977 insertions(+), 845 deletions(-) delete mode 100644 packages/twenty-front/src/modules/action-menu/types/ActionMenuDropdownHotKeyScope.ts delete mode 100644 packages/twenty-front/src/modules/action-menu/types/CommandMenuActionMenuDropdownHotkeyScope.ts create mode 100644 packages/twenty-front/src/modules/command-menu/constants/SidePanelFocusId.ts delete mode 100644 packages/twenty-front/src/modules/object-record/object-filter-dropdown/types/FiltersHotkeyScope.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-field/meta-types/input/types/RelationPickerHotkeyScope.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-field/meta-types/input/utils/getRelationFromManyFieldInputInstanceId.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-field/meta-types/input/utils/getRelationToOneFieldInputInstanceId.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-field/utils/getFieldInputInstanceId.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-picker/multiple-record-picker/types/MultipleRecordPickerHotkeyScope.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope.ts delete mode 100644 packages/twenty-front/src/modules/ui/input/types/SelectHotkeyScope.ts create mode 100644 packages/twenty-front/src/modules/ui/layout/dropdown/constants/DropdownHotkeyScope.ts delete mode 100644 packages/twenty-front/src/modules/ui/layout/dropdown/constants/DropdownMenuHotkeyScope.ts delete mode 100644 packages/twenty-front/src/modules/ui/layout/dropdown/hooks/__tests__/useInternalHotkeyScopeManagement.test.tsx delete mode 100644 packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useInternalHotkeyScopeManagement.ts delete mode 100644 packages/twenty-front/src/modules/ui/layout/dropdown/states/dropdownHotkeyComponentState.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/focus/constants/DebugFocusStack.ts rename packages/twenty-front/src/modules/ui/utilities/focus/hooks/__tests__/{useRemoveFocusIdFromFocusStack.test.tsx => useRemoveFocusItemFromFocusStack.test.tsx} (81%) delete mode 100644 packages/twenty-front/src/modules/ui/utilities/focus/hooks/useRemoveFocusIdFromFocusStack.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStack.ts delete mode 100644 packages/twenty-front/src/modules/views/types/ViewsHotkeyScope.ts diff --git a/packages/twenty-front/src/modules/action-menu/components/CmdEnterActionButton.tsx b/packages/twenty-front/src/modules/action-menu/components/CmdEnterActionButton.tsx index 0d3227416..e7076b5dd 100644 --- a/packages/twenty-front/src/modules/action-menu/components/CmdEnterActionButton.tsx +++ b/packages/twenty-front/src/modules/action-menu/components/CmdEnterActionButton.tsx @@ -1,4 +1,5 @@ -import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; +import { SIDE_PANEL_FOCUS_ID } from '@/command-menu/constants/SidePanelFocusId'; +import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement'; import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope'; import { Key } from 'ts-key-enum'; import { Button } from 'twenty-ui/input'; @@ -13,12 +14,13 @@ export const CmdEnterActionButton = ({ onClick: () => void; disabled?: boolean; }) => { - useScopedHotkeys( - [`${Key.Control}+${Key.Enter}`, `${Key.Meta}+${Key.Enter}`], - () => onClick(), - AppHotkeyScope.CommandMenuOpen, - [onClick], - ); + useHotkeysOnFocusedElement({ + keys: [`${Key.Control}+${Key.Enter}`, `${Key.Meta}+${Key.Enter}`], + callback: () => onClick(), + focusId: SIDE_PANEL_FOCUS_ID, + scope: AppHotkeyScope.CommandMenuOpen, + dependencies: [onClick], + }); return (