diff --git a/packages/twenty-front/src/modules/action-menu/actions/components/ActionModal.tsx b/packages/twenty-front/src/modules/action-menu/actions/components/ActionModal.tsx
index 14787efc4..c21113dea 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/components/ActionModal.tsx
+++ b/packages/twenty-front/src/modules/action-menu/actions/components/ActionModal.tsx
@@ -33,9 +33,8 @@ export const ActionModal = ({
const { closeActionMenu } = useCloseActionMenu();
const handleConfirmClick = () => {
- closeActionMenu();
onConfirmClick();
- setIsOpen(false);
+ closeActionMenu();
};
const actionConfig = useContext(ActionConfigContext);
diff --git a/packages/twenty-front/src/modules/app/effect-components/PageChangeEffect.tsx b/packages/twenty-front/src/modules/app/effect-components/PageChangeEffect.tsx
index d99712590..0749116df 100644
--- a/packages/twenty-front/src/modules/app/effect-components/PageChangeEffect.tsx
+++ b/packages/twenty-front/src/modules/app/effect-components/PageChangeEffect.tsx
@@ -17,11 +17,16 @@ import { isCaptchaScriptLoadedState } from '@/captcha/states/isCaptchaScriptLoad
import { isCaptchaRequiredForPath } from '@/captcha/utils/isCaptchaRequiredForPath';
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
+import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState';
+import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType';
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
+import { useActiveRecordBoardCard } from '@/object-record/record-board/hooks/useActiveRecordBoardCard';
+import { useFocusedRecordBoardCard } from '@/object-record/record-board/hooks/useFocusedRecordBoardCard';
+import { useRecordBoardSelection } from '@/object-record/record-board/hooks/useRecordBoardSelection';
+import { RecordIndexHotkeyScope } from '@/object-record/record-index/types/RecordIndexHotkeyScope';
import { useResetTableRowSelection } from '@/object-record/record-table/hooks/internal/useResetTableRowSelection';
import { useActiveRecordTableRow } from '@/object-record/record-table/hooks/useActiveRecordTableRow';
import { useFocusedRecordTableRow } from '@/object-record/record-table/hooks/useFocusedRecordTableRow';
-import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope';
import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId';
import { AppBasePath } from '@/types/AppBasePath';
import { AppPath } from '@/types/AppPath';
@@ -64,6 +69,11 @@ export const PageChangeEffect = () => {
MAIN_CONTEXT_STORE_INSTANCE_ID,
);
+ const contextStoreCurrentViewType = useRecoilComponentValueV2(
+ contextStoreCurrentViewTypeComponentState,
+ MAIN_CONTEXT_STORE_INSTANCE_ID,
+ );
+
const recordIndexId = getRecordIndexIdFromObjectNamePluralAndViewId(
objectNamePlural,
contextStoreCurrentViewId || '',
@@ -73,6 +83,10 @@ export const PageChangeEffect = () => {
const { unfocusRecordTableRow } = useFocusedRecordTableRow(recordIndexId);
const { deactivateRecordTableRow } = useActiveRecordTableRow(recordIndexId);
+ const { resetRecordSelection } = useRecordBoardSelection(recordIndexId);
+ const { deactivateBoardCard } = useActiveRecordBoardCard(recordIndexId);
+ const { unfocusBoardCard } = useFocusedRecordBoardCard(recordIndexId);
+
const { executeTasksOnAnyLocationChange } =
useExecuteTasksOnAnyLocationChange();
@@ -100,9 +114,16 @@ export const PageChangeEffect = () => {
);
if (isLeavingRecordIndexPage) {
- resetTableSelections();
- unfocusRecordTableRow();
- deactivateRecordTableRow();
+ if (contextStoreCurrentViewType === ContextStoreViewType.Table) {
+ resetTableSelections();
+ unfocusRecordTableRow();
+ deactivateRecordTableRow();
+ }
+ if (contextStoreCurrentViewType === ContextStoreViewType.Kanban) {
+ resetRecordSelection();
+ deactivateBoardCard();
+ unfocusBoardCard();
+ }
}
}, [
isMatchingLocation,
@@ -110,12 +131,16 @@ export const PageChangeEffect = () => {
resetTableSelections,
unfocusRecordTableRow,
deactivateRecordTableRow,
+ contextStoreCurrentViewType,
+ resetRecordSelection,
+ deactivateBoardCard,
+ unfocusBoardCard,
]);
useEffect(() => {
switch (true) {
case isMatchingLocation(AppPath.RecordIndexPage): {
- setHotkeyScope(TableHotkeyScope.Table, {
+ setHotkeyScope(RecordIndexHotkeyScope.RecordIndex, {
goto: true,
keyboardShortcutMenu: true,
});
diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useNavigateCommandMenu.ts b/packages/twenty-front/src/modules/command-menu/hooks/useNavigateCommandMenu.ts
index 36bb69231..c92fb987d 100644
--- a/packages/twenty-front/src/modules/command-menu/hooks/useNavigateCommandMenu.ts
+++ b/packages/twenty-front/src/modules/command-menu/hooks/useNavigateCommandMenu.ts
@@ -51,6 +51,10 @@ export const useNavigateCommandMenu = () => {
commandMenuCloseAnimationCompleteCleanup();
}
+ if (isCommandMenuOpened) {
+ return;
+ }
+
setHotkeyScopeAndMemorizePreviousScope(
CommandMenuHotkeyScope.CommandMenuFocused,
{
@@ -58,10 +62,6 @@ export const useNavigateCommandMenu = () => {
},
);
- if (isCommandMenuOpened) {
- return;
- }
-
copyContextStoreStates({
instanceIdToCopyFrom: MAIN_CONTEXT_STORE_INSTANCE_ID,
instanceIdToCopyTo: COMMAND_MENU_COMPONENT_INSTANCE_ID,
diff --git a/packages/twenty-front/src/modules/keyboard-shortcut-menu/hooks/__tests__/useKeyboardShortcutMenu.test.tsx b/packages/twenty-front/src/modules/keyboard-shortcut-menu/hooks/__tests__/useKeyboardShortcutMenu.test.tsx
index 49d98de15..d38b3b577 100644
--- a/packages/twenty-front/src/modules/keyboard-shortcut-menu/hooks/__tests__/useKeyboardShortcutMenu.test.tsx
+++ b/packages/twenty-front/src/modules/keyboard-shortcut-menu/hooks/__tests__/useKeyboardShortcutMenu.test.tsx
@@ -1,5 +1,6 @@
import { expect } from '@storybook/test';
-import { act, renderHook } from '@testing-library/react';
+import { renderHook } from '@testing-library/react';
+import { act } from 'react';
import { RecoilRoot, useRecoilValue } from 'recoil';
import { isKeyboardShortcutMenuOpenedState } from '@/keyboard-shortcut-menu/states/isKeyboardShortcutMenuOpenedState';
diff --git a/packages/twenty-front/src/modules/keyboard-shortcut-menu/hooks/useKeyboardShortcutMenu.ts b/packages/twenty-front/src/modules/keyboard-shortcut-menu/hooks/useKeyboardShortcutMenu.ts
index 347e40db0..0d568a473 100644
--- a/packages/twenty-front/src/modules/keyboard-shortcut-menu/hooks/useKeyboardShortcutMenu.ts
+++ b/packages/twenty-front/src/modules/keyboard-shortcut-menu/hooks/useKeyboardShortcutMenu.ts
@@ -1,4 +1,4 @@
-import { useRecoilState, useRecoilValue } from 'recoil';
+import { useRecoilCallback } from 'recoil';
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
@@ -6,38 +6,52 @@ import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
import { isKeyboardShortcutMenuOpenedState } from '../states/isKeyboardShortcutMenuOpenedState';
export const useKeyboardShortcutMenu = () => {
- const [, setIsKeyboardShortcutMenuOpened] = useRecoilState(
- isKeyboardShortcutMenuOpenedState,
- );
- const isKeyboardShortcutMenuOpened = useRecoilValue(
- isKeyboardShortcutMenuOpenedState,
- );
const {
setHotkeyScopeAndMemorizePreviousScope,
goBackToPreviousHotkeyScope,
} = usePreviousHotkeyScope();
- const toggleKeyboardShortcutMenu = () => {
- if (isKeyboardShortcutMenuOpened === false) {
- setIsKeyboardShortcutMenuOpened(true);
- setHotkeyScopeAndMemorizePreviousScope(
- AppHotkeyScope.KeyboardShortcutMenu,
- );
- } else {
- setIsKeyboardShortcutMenuOpened(false);
- goBackToPreviousHotkeyScope();
- }
- };
+ const openKeyboardShortcutMenu = useRecoilCallback(
+ ({ set }) =>
+ () => {
+ set(isKeyboardShortcutMenuOpenedState, true);
+ setHotkeyScopeAndMemorizePreviousScope(
+ AppHotkeyScope.KeyboardShortcutMenu,
+ );
+ },
+ [setHotkeyScopeAndMemorizePreviousScope],
+ );
- const openKeyboardShortcutMenu = () => {
- setIsKeyboardShortcutMenuOpened(true);
- setHotkeyScopeAndMemorizePreviousScope(AppHotkeyScope.KeyboardShortcutMenu);
- };
+ const closeKeyboardShortcutMenu = useRecoilCallback(
+ ({ set, snapshot }) =>
+ () => {
+ const isKeyboardShortcutMenuOpened = snapshot
+ .getLoadable(isKeyboardShortcutMenuOpenedState)
+ .getValue();
- const closeKeyboardShortcutMenu = () => {
- setIsKeyboardShortcutMenuOpened(false);
- goBackToPreviousHotkeyScope();
- };
+ if (isKeyboardShortcutMenuOpened) {
+ set(isKeyboardShortcutMenuOpenedState, false);
+ goBackToPreviousHotkeyScope();
+ }
+ },
+ [goBackToPreviousHotkeyScope],
+ );
+
+ const toggleKeyboardShortcutMenu = useRecoilCallback(
+ ({ snapshot }) =>
+ () => {
+ const isKeyboardShortcutMenuOpened = snapshot
+ .getLoadable(isKeyboardShortcutMenuOpenedState)
+ .getValue();
+
+ if (isKeyboardShortcutMenuOpened === false) {
+ openKeyboardShortcutMenu();
+ } else {
+ closeKeyboardShortcutMenu();
+ }
+ },
+ [closeKeyboardShortcutMenu, openKeyboardShortcutMenu],
+ );
return {
toggleKeyboardShortcutMenu,
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 c87033b9b..04b57f57f 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
@@ -5,10 +5,14 @@ import { useRecoilCallback, useSetRecoilState } from 'recoil';
import { getActionMenuIdFromRecordIndexId } from '@/action-menu/utils/getActionMenuIdFromRecordIndexId';
import { RecordBoardHeader } from '@/object-record/record-board/components/RecordBoardHeader';
+import { RecordBoardScrollToFocusedCardEffect } from '@/object-record/record-board/components/RecordBoardScrollToFocusedCardEffect';
import { RecordBoardStickyHeaderEffect } from '@/object-record/record-board/components/RecordBoardStickyHeaderEffect';
import { RECORD_BOARD_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-board/constants/RecordBoardClickOutsideListenerId';
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
+import { useActiveRecordBoardCard } from '@/object-record/record-board/hooks/useActiveRecordBoardCard';
+import { useFocusedRecordBoardCard } from '@/object-record/record-board/hooks/useFocusedRecordBoardCard';
import { useRecordBoardSelection } from '@/object-record/record-board/hooks/useRecordBoardSelection';
+import { RecordBoardDeactivateBoardCardEffect } from '@/object-record/record-board/record-board-card/components/RecordBoardDeactivateBoardCardEffect';
import { RecordBoardColumn } from '@/object-record/record-board/record-board-column/components/RecordBoardColumn';
import { RecordBoardScope } from '@/object-record/record-board/scopes/RecordBoardScope';
import { RecordBoardComponentInstanceContext } from '@/object-record/record-board/states/contexts/RecordBoardComponentInstanceContext';
@@ -16,14 +20,11 @@ import { getDraggedRecordPosition } from '@/object-record/record-board/utils/get
import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState';
import { visibleRecordGroupIdsComponentFamilySelector } from '@/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentFamilySelector';
import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState';
-import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector';
import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { isRemoveSortingModalOpenState } from '@/object-record/record-table/states/isRemoveSortingModalOpenState';
-import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope';
import { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2';
import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect';
-import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useClickOutsideListener';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
@@ -71,6 +72,9 @@ export const RecordBoard = () => {
const { closeDropdown } = useDropdownV2();
+ const { deactivateBoardCard } = useActiveRecordBoardCard(recordBoardId);
+ const { unfocusBoardCard } = useFocusedRecordBoardCard(recordBoardId);
+
const handleDragSelectionStart = () => {
closeDropdown(actionMenuId);
@@ -91,10 +95,6 @@ export const RecordBoard = () => {
recordIndexRecordIdsByGroupComponentFamilyState,
);
- const recordIndexAllRecordIdsState = useRecoilComponentCallbackStateV2(
- recordIndexAllRecordIdsComponentSelector,
- );
-
const { resetRecordSelection, setRecordAsSelected } =
useRecordBoardSelection(recordBoardId);
@@ -115,26 +115,11 @@ export const RecordBoard = () => {
refs: [],
callback: () => {
resetRecordSelection();
+ deactivateBoardCard();
+ unfocusBoardCard();
},
});
- const selectAll = useRecoilCallback(
- ({ snapshot }) =>
- () => {
- const allRecordIds = getSnapshotValue(
- snapshot,
- recordIndexAllRecordIdsState,
- );
-
- for (const recordId of allRecordIds) {
- setRecordAsSelected(recordId, true);
- }
- },
- [recordIndexAllRecordIdsState, setRecordAsSelected],
- );
-
- useScopedHotkeys('ctrl+a,meta+a', selectAll, TableHotkeyScope.Table);
-
const setIsRemoveSortingModalOpen = useSetRecoilState(
isRemoveSortingModalOpenState,
);
@@ -228,16 +213,19 @@ export const RecordBoard = () => {
componentInstanceId={`scroll-wrapper-record-board-${recordBoardId}`}
>
+
+
- {visibleRecordGroupIds.map((recordGroupId) => (
+ {visibleRecordGroupIds.map((recordGroupId, index) => (
))}
diff --git a/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoardBodyEscapeHotkeyEffect.tsx b/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoardBodyEscapeHotkeyEffect.tsx
new file mode 100644
index 000000000..717aaf11f
--- /dev/null
+++ b/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoardBodyEscapeHotkeyEffect.tsx
@@ -0,0 +1,52 @@
+import { useContext } from 'react';
+import { Key } from 'ts-key-enum';
+
+import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
+import { useFocusedRecordBoardCard } from '@/object-record/record-board/hooks/useFocusedRecordBoardCard';
+import { useRecordBoardSelection } from '@/object-record/record-board/hooks/useRecordBoardSelection';
+import { recordBoardSelectedRecordIdsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardSelectedRecordIdsComponentSelector';
+import { BoardHotkeyScope } from '@/object-record/record-board/types/BoardHotkeyScope';
+import { RecordIndexHotkeyScope } from '@/object-record/record-index/types/RecordIndexHotkeyScope';
+import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
+import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
+
+export const RecordBoardBodyEscapeHotkeyEffect = () => {
+ const { recordBoardId } = useContext(RecordBoardContext);
+
+ const { resetRecordSelection } = useRecordBoardSelection(recordBoardId);
+
+ const { unfocusBoardCard } = useFocusedRecordBoardCard(recordBoardId);
+
+ const selectedRecordIds = useRecoilComponentValueV2(
+ recordBoardSelectedRecordIdsComponentSelector,
+ recordBoardId,
+ );
+
+ const isAtLeastOneRecordSelected = selectedRecordIds.length > 0;
+
+ useScopedHotkeys(
+ [Key.Escape],
+ () => {
+ unfocusBoardCard();
+ if (isAtLeastOneRecordSelected) {
+ resetRecordSelection();
+ }
+ },
+ RecordIndexHotkeyScope.RecordIndex,
+ [isAtLeastOneRecordSelected, resetRecordSelection, unfocusBoardCard],
+ );
+
+ useScopedHotkeys(
+ [Key.Escape],
+ () => {
+ unfocusBoardCard();
+ if (isAtLeastOneRecordSelected) {
+ resetRecordSelection();
+ }
+ },
+ BoardHotkeyScope.BoardFocus,
+ [isAtLeastOneRecordSelected, resetRecordSelection, unfocusBoardCard],
+ );
+
+ return null;
+};
diff --git a/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoardHeader.tsx b/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoardHeader.tsx
index 3ca0b6e68..187606a64 100644
--- a/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoardHeader.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoardHeader.tsx
@@ -31,9 +31,10 @@ export const RecordBoardHeader = () => {
return (