Files
twenty/packages/twenty-front/src/modules/layout-customization/hooks/useEnterLayoutCustomizationMode.ts
T
Raphaël Bosi 77315b7f6e Sync record selection with the edit mode (#19276)
## PR description

The previous approach used a detached flag to represent selection mode,
which could was not synced sync with actual UI selection state. This
change ties the edit mode's selection/fallback behavior directly to
whether records are truly selected in the record index.

- Removes `commandMenuItemEditSelectionModeState` (a standalone 'none' |
'selection' atom) and replaces it with
`mainContextStoreHasSelectedRecordsSelector`, which derives selection
state from the real record selection in the context store.
- Adds `useSelectFirstRecordForEditMode` to programmatically select the
first record (table row or kanban card) when toggling to selection mode,
and useResetRecordIndexSelection to clear selection when toggling off or
exiting edit mode.
- Ensures record selection is properly cleaned up when exiting layout
customization mode or closing the side panel.

## Video QA

### Table


https://github.com/user-attachments/assets/1d845809-8369-4872-b3a9-a0281b8e464b

### Board


https://github.com/user-attachments/assets/325c2d58-4ca0-408a-ab4a-66b97d9d70de
2026-04-02 16:35:14 +00:00

64 lines
2.7 KiB
TypeScript

import { t } from '@lingui/core/macro';
import { useStore } from 'jotai';
import { useCallback } from 'react';
import { SidePanelPages } from 'twenty-shared/types';
import { IconPencil } from 'twenty-ui/display';
import { commandMenuItemsDraftState } from '@/command-menu-item/server-items/edit/states/commandMenuItemsDraftState';
import { commandMenuItemsSelector } from '@/command-menu-item/server-items/common/states/commandMenuItemsSelector';
import { activeCustomizationPageLayoutIdsState } from '@/layout-customization/states/activeCustomizationPageLayoutIdsState';
import { isLayoutCustomizationModeEnabledState } from '@/layout-customization/states/isLayoutCustomizationModeEnabledState';
import { navigationMenuItemsDraftState } from '@/navigation-menu-item/common/states/navigationMenuItemsDraftState';
import { navigationMenuItemsSelector } from '@/navigation-menu-item/common/states/navigationMenuItemsSelector';
import { filterWorkspaceNavigationMenuItems } from '@/navigation-menu-item/common/utils/filterWorkspaceNavigationMenuItems';
import { useNavigateSidePanel } from '@/side-panel/hooks/useNavigateSidePanel';
import { isSidePanelOpenedState } from '@/side-panel/states/isSidePanelOpenedState';
import { sidePanelPageState } from '@/side-panel/states/sidePanelPageState';
export const useEnterLayoutCustomizationMode = () => {
const store = useStore();
const { navigateSidePanel } = useNavigateSidePanel();
const enterLayoutCustomizationMode = useCallback(() => {
const isLayoutCustomizationModeAlreadyEnabled = store.get(
isLayoutCustomizationModeEnabledState.atom,
);
if (isLayoutCustomizationModeAlreadyEnabled) {
return;
}
const prefetchNavigationMenuItems = store.get(
navigationMenuItemsSelector.atom,
);
const workspaceNavigationMenuItems = filterWorkspaceNavigationMenuItems(
prefetchNavigationMenuItems,
);
store.set(navigationMenuItemsDraftState.atom, workspaceNavigationMenuItems);
const persistedCommandMenuItems = store.get(commandMenuItemsSelector.atom);
store.set(commandMenuItemsDraftState.atom, persistedCommandMenuItems);
store.set(activeCustomizationPageLayoutIdsState.atom, []);
store.set(isLayoutCustomizationModeEnabledState.atom, true);
const isSidePanelOpened = store.get(isSidePanelOpenedState.atom);
const currentSidePanelPage = store.get(sidePanelPageState.atom);
if (
isSidePanelOpened &&
currentSidePanelPage === SidePanelPages.CommandMenuDisplay
) {
navigateSidePanel({
page: SidePanelPages.CommandMenuEdit,
pageTitle: t`Edit actions`,
pageIcon: IconPencil,
resetNavigationStack: true,
});
}
}, [navigateSidePanel, store]);
return { enterLayoutCustomizationMode };
};