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
This commit is contained in:
-5
@@ -1,5 +1,4 @@
|
||||
import { AnimatedIconCrossfade } from 'twenty-ui/utilities';
|
||||
import { commandMenuItemEditSelectionModeState } from '@/command-menu-item/server-items/edit/states/commandMenuItemEditSelectionModeState';
|
||||
import { isLayoutCustomizationModeEnabledState } from '@/layout-customization/states/isLayoutCustomizationModeEnabledState';
|
||||
import { useNavigateSidePanel } from '@/side-panel/hooks/useNavigateSidePanel';
|
||||
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
|
||||
@@ -7,14 +6,12 @@ import { isSidePanelOpenedState } from '@/side-panel/states/isSidePanelOpenedSta
|
||||
import { sidePanelPageState } from '@/side-panel/states/sidePanelPageState';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useStore } from 'jotai';
|
||||
import { SidePanelPages } from 'twenty-shared/types';
|
||||
import { IconPencil, IconX } from 'twenty-ui/display';
|
||||
import { AnimatedButton } from 'twenty-ui/input';
|
||||
|
||||
export const CommandMenuItemEditButton = () => {
|
||||
const { t } = useLingui();
|
||||
const store = useStore();
|
||||
const { navigateSidePanel } = useNavigateSidePanel();
|
||||
const { closeSidePanelMenu } = useSidePanelMenu();
|
||||
|
||||
@@ -38,8 +35,6 @@ export const CommandMenuItemEditButton = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
store.set(commandMenuItemEditSelectionModeState.atom, 'selection');
|
||||
|
||||
navigateSidePanel({
|
||||
page: SidePanelPages.CommandMenuEdit,
|
||||
pageTitle: t`Edit actions`,
|
||||
|
||||
+18
-11
@@ -1,12 +1,14 @@
|
||||
import { COMMAND_MENU_DROPDOWN_CLICK_OUTSIDE_ID } from '@/command-menu-item/constants/CommandMenuDropdownClickOutsideId';
|
||||
import { commandMenuItemEditSelectionModeState } from '@/command-menu-item/server-items/edit/states/commandMenuItemEditSelectionModeState';
|
||||
import { useSelectFirstRecordForEditMode } from '@/command-menu-item/server-items/edit/hooks/useSelectFirstRecordForEditMode';
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { mainContextStoreHasSelectedRecordsSelector } from '@/context-store/states/selectors/mainContextStoreHasSelectedRecordsSelector';
|
||||
import { useResetRecordIndexSelection } from '@/object-record/record-index/hooks/useResetRecordIndexSelection';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
||||
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
import { styled } from '@linaria/react';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import {
|
||||
@@ -55,19 +57,24 @@ export const CommandMenuItemEditRecordSelectionDropdown = ({
|
||||
const { t } = useLingui();
|
||||
const { closeDropdown } = useCloseDropdown();
|
||||
|
||||
const commandMenuItemEditSelectionMode = useAtomStateValue(
|
||||
commandMenuItemEditSelectionModeState,
|
||||
);
|
||||
const setCommandMenuItemEditSelectionMode = useSetAtomState(
|
||||
commandMenuItemEditSelectionModeState,
|
||||
const mainContextStoreHasSelectedRecords = useAtomStateValue(
|
||||
mainContextStoreHasSelectedRecordsSelector,
|
||||
);
|
||||
|
||||
const isNoneSelected = isRecordPage
|
||||
? false
|
||||
: commandMenuItemEditSelectionMode === 'none';
|
||||
const { selectFirstRecordForEditMode } = useSelectFirstRecordForEditMode();
|
||||
const { resetRecordIndexSelection } = useResetRecordIndexSelection(
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
);
|
||||
|
||||
const isNoneSelected = !mainContextStoreHasSelectedRecords;
|
||||
|
||||
const handleSelectMode = (mode: 'none' | 'selection') => {
|
||||
setCommandMenuItemEditSelectionMode(mode);
|
||||
if (mode === 'selection' && isNoneSelected) {
|
||||
selectFirstRecordForEditMode();
|
||||
} else if (mode === 'none') {
|
||||
resetRecordIndexSelection();
|
||||
}
|
||||
|
||||
closeDropdown(DROPDOWN_ID);
|
||||
};
|
||||
|
||||
|
||||
+9
-12
@@ -1,14 +1,14 @@
|
||||
import { CommandMenuButton } from '@/command-menu/components/CommandMenuButton';
|
||||
import { useCommandMenuContextApi } from '@/command-menu-item/server-items/common/hooks/useCommandMenuContextApi';
|
||||
import { doesCommandMenuItemMatchObjectMetadataId } from '@/command-menu-item/server-items/common/utils/doesCommandMenuItemMatchObjectMetadataId';
|
||||
import { PinnedCommandMenuItemsInlineMeasurements } from '@/command-menu-item/server-items/display/components/PinnedCommandMenuItemsInlineMeasurements';
|
||||
import { PINNED_COMMAND_MENU_ITEMS_GAP } from '@/command-menu-item/server-items/display/constants/PinnedCommandMenuItemsGap';
|
||||
import { usePinnedCommandMenuItemsInlineLayout } from '@/command-menu-item/server-items/display/hooks/usePinnedCommandMenuItemsInlineLayout';
|
||||
import { commandMenuItemEditSelectionModeState } from '@/command-menu-item/server-items/edit/states/commandMenuItemEditSelectionModeState';
|
||||
import { mainContextStoreHasSelectedRecordsSelector } from '@/context-store/states/selectors/mainContextStoreHasSelectedRecordsSelector';
|
||||
import { commandMenuItemsDraftState } from '@/command-menu-item/server-items/edit/states/commandMenuItemsDraftState';
|
||||
import { CommandMenuItemScope } from '@/command-menu-item/types/CommandMenuItemScope';
|
||||
import { type CommandMenuItemConfig } from '@/command-menu-item/types/CommandMenuItemConfig';
|
||||
import { CommandMenuItemScope } from '@/command-menu-item/types/CommandMenuItemScope';
|
||||
import { CommandMenuItemType } from '@/command-menu-item/types/CommandMenuItemType';
|
||||
import { CommandMenuButton } from '@/command-menu/components/CommandMenuButton';
|
||||
import { NodeDimension } from '@/ui/utilities/dimensions/components/NodeDimension';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { COMMAND_MENU_DEFAULT_ICON } from '@/workflow/workflow-trigger/constants/CommandMenuDefaultIcon';
|
||||
@@ -18,10 +18,7 @@ import { useContext, useMemo } from 'react';
|
||||
import { interpolateCommandMenuItemLabel } from 'twenty-shared/utils';
|
||||
import { useIcons } from 'twenty-ui/display';
|
||||
import { ThemeContext } from 'twenty-ui/theme-constants';
|
||||
import {
|
||||
CommandMenuItemAvailabilityType,
|
||||
type CommandMenuItemFieldsFragment,
|
||||
} from '~/generated-metadata/graphql';
|
||||
import { CommandMenuItemAvailabilityType } from '~/generated-metadata/graphql';
|
||||
|
||||
const StyledActionContainer = styled(motion.div)`
|
||||
align-items: center;
|
||||
@@ -59,19 +56,20 @@ export const PinnedCommandMenuItemButtonsEditMode = () => {
|
||||
|
||||
const commandMenuItemsDraft =
|
||||
useAtomStateValue(commandMenuItemsDraftState) ?? [];
|
||||
const commandMenuItemEditSelectionMode = useAtomStateValue(
|
||||
commandMenuItemEditSelectionModeState,
|
||||
|
||||
const mainContextStoreHasSelectedRecords = useAtomStateValue(
|
||||
mainContextStoreHasSelectedRecordsSelector,
|
||||
);
|
||||
|
||||
const allowedAvailabilityTypes = useMemo(
|
||||
() =>
|
||||
new Set<CommandMenuItemAvailabilityType>([
|
||||
CommandMenuItemAvailabilityType.GLOBAL,
|
||||
commandMenuItemEditSelectionMode === 'selection'
|
||||
mainContextStoreHasSelectedRecords
|
||||
? CommandMenuItemAvailabilityType.RECORD_SELECTION
|
||||
: CommandMenuItemAvailabilityType.FALLBACK,
|
||||
]),
|
||||
[commandMenuItemEditSelectionMode],
|
||||
[mainContextStoreHasSelectedRecords],
|
||||
);
|
||||
|
||||
const interpolateLabel = (rawLabel: string | null | undefined) =>
|
||||
@@ -114,7 +112,6 @@ export const PinnedCommandMenuItemButtonsEditMode = () => {
|
||||
}),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[
|
||||
commandMenuItemsDraft,
|
||||
currentObjectMetadataItemId,
|
||||
allowedAvailabilityTypes,
|
||||
commandMenuContextApi,
|
||||
|
||||
+5
-12
@@ -1,16 +1,13 @@
|
||||
import { useCommandMenuContextApi } from '@/command-menu-item/server-items/common/hooks/useCommandMenuContextApi';
|
||||
import { commandMenuItemsSelector } from '@/command-menu-item/server-items/common/states/commandMenuItemsSelector';
|
||||
import { doesCommandMenuItemMatchObjectMetadataId } from '@/command-menu-item/server-items/common/utils/doesCommandMenuItemMatchObjectMetadataId';
|
||||
import { commandMenuItemsDraftState } from '@/command-menu-item/server-items/edit/states/commandMenuItemsDraftState';
|
||||
import { CommandMenuItemEditRecordSelectionDropdown } from '@/command-menu-item/server-items/edit/components/CommandMenuItemEditRecordSelectionDropdown';
|
||||
import { CommandMenuItemOptionsDropdown } from '@/command-menu-item/server-items/edit/components/CommandMenuItemOptionsDropdown';
|
||||
import { useReorderCommandMenuItemsInDraft } from '@/command-menu-item/server-items/edit/hooks/useReorderCommandMenuItemsInDraft';
|
||||
import { useResetCommandMenuItemsDraft } from '@/command-menu-item/server-items/edit/hooks/useResetCommandMenuItemsDraft';
|
||||
import {
|
||||
type CommandMenuItemEditSelectionMode,
|
||||
commandMenuItemEditSelectionModeState,
|
||||
} from '@/command-menu-item/server-items/edit/states/commandMenuItemEditSelectionModeState';
|
||||
import { useUpdateCommandMenuItemInDraft } from '@/command-menu-item/server-items/edit/hooks/useUpdateCommandMenuItemInDraft';
|
||||
import { commandMenuItemsDraftState } from '@/command-menu-item/server-items/edit/states/commandMenuItemsDraftState';
|
||||
import { mainContextStoreHasSelectedRecordsSelector } from '@/context-store/states/selectors/mainContextStoreHasSelectedRecordsSelector';
|
||||
import { COMMAND_MENU_CLICK_OUTSIDE_ID } from '@/command-menu/constants/CommandMenuClickOutsideId';
|
||||
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
|
||||
import { SidePanelList } from '@/side-panel/components/SidePanelList';
|
||||
@@ -88,14 +85,10 @@ export const SidePanelCommandMenuItemEditPage = () => {
|
||||
commandMenuContextApi.pageType ===
|
||||
CommandMenuContextApiPageType.RECORD_PAGE;
|
||||
|
||||
const commandMenuItemEditSelectionMode = useAtomStateValue(
|
||||
commandMenuItemEditSelectionModeState,
|
||||
const mainContextStoreHasSelectedRecords = useAtomStateValue(
|
||||
mainContextStoreHasSelectedRecordsSelector,
|
||||
);
|
||||
|
||||
const effectiveSelectionMode: CommandMenuItemEditSelectionMode = isRecordPage
|
||||
? 'selection'
|
||||
: commandMenuItemEditSelectionMode;
|
||||
|
||||
const sidePanelSearch = useAtomStateValue(sidePanelSearchState);
|
||||
|
||||
const commandMenuItems = useAtomStateValue(commandMenuItemsSelector);
|
||||
@@ -110,7 +103,7 @@ export const SidePanelCommandMenuItemEditPage = () => {
|
||||
|
||||
const allowedAvailabilityTypes = new Set<CommandMenuItemAvailabilityType>([
|
||||
CommandMenuItemAvailabilityType.GLOBAL,
|
||||
effectiveSelectionMode === 'selection'
|
||||
mainContextStoreHasSelectedRecords
|
||||
? CommandMenuItemAvailabilityType.RECORD_SELECTION
|
||||
: CommandMenuItemAvailabilityType.FALLBACK,
|
||||
]);
|
||||
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { getRecordIndexId } from '@/command-menu-item/server-items/edit/utils/getRecordIndexId';
|
||||
import { isRecordBoardCardSelectedComponentFamilyState } from '@/object-record/record-board/states/isRecordBoardCardSelectedComponentFamilyState';
|
||||
import { useResetRecordIndexSelection } from '@/object-record/record-index/hooks/useResetRecordIndexSelection';
|
||||
import { recordIndexViewTypeState } from '@/object-record/record-index/states/recordIndexViewTypeState';
|
||||
import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector';
|
||||
import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState';
|
||||
import { ViewType } from '@/views/types/ViewType';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useSelectFirstRecordForEditMode = () => {
|
||||
const store = useStore();
|
||||
const { resetRecordIndexSelection } = useResetRecordIndexSelection(
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
);
|
||||
|
||||
const selectFirstRecordForEditMode = useCallback(() => {
|
||||
const recordIndexId = getRecordIndexId(store);
|
||||
|
||||
if (!isDefined(recordIndexId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
resetRecordIndexSelection();
|
||||
|
||||
const allRecordIds = store.get(
|
||||
recordIndexAllRecordIdsComponentSelector.selectorFamily({
|
||||
instanceId: recordIndexId,
|
||||
}),
|
||||
);
|
||||
|
||||
const firstRecordId = allRecordIds[0];
|
||||
|
||||
if (!isDefined(firstRecordId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const viewType = store.get(recordIndexViewTypeState.atom);
|
||||
|
||||
switch (viewType) {
|
||||
case ViewType.TABLE: {
|
||||
store.set(
|
||||
isRowSelectedComponentFamilyState.atomFamily({
|
||||
instanceId: recordIndexId,
|
||||
familyKey: firstRecordId,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
break;
|
||||
}
|
||||
case ViewType.KANBAN: {
|
||||
store.set(
|
||||
isRecordBoardCardSelectedComponentFamilyState.atomFamily({
|
||||
instanceId: recordIndexId,
|
||||
familyKey: firstRecordId,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}, [store, resetRecordIndexSelection]);
|
||||
|
||||
return { selectFirstRecordForEditMode };
|
||||
};
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState';
|
||||
|
||||
export type CommandMenuItemEditSelectionMode = 'none' | 'selection';
|
||||
|
||||
export const commandMenuItemEditSelectionModeState =
|
||||
createAtomState<CommandMenuItemEditSelectionMode>({
|
||||
key: 'commandMenuItemEditSelectionModeState',
|
||||
defaultValue: 'selection',
|
||||
});
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
|
||||
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
|
||||
import { objectMetadataItemsSelector } from '@/object-metadata/states/objectMetadataItemsSelector';
|
||||
import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId';
|
||||
import type { useStore } from 'jotai';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const getRecordIndexId = (
|
||||
store: ReturnType<typeof useStore>,
|
||||
): string | null => {
|
||||
const objectMetadataItemId = store.get(
|
||||
contextStoreCurrentObjectMetadataItemIdComponentState.atomFamily({
|
||||
instanceId: MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
}),
|
||||
);
|
||||
|
||||
const viewId = store.get(
|
||||
contextStoreCurrentViewIdComponentState.atomFamily({
|
||||
instanceId: MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
}),
|
||||
);
|
||||
|
||||
if (!isDefined(objectMetadataItemId) || !isDefined(viewId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const objectMetadataItems = store.get(objectMetadataItemsSelector.atom);
|
||||
|
||||
const objectMetadataItem = objectMetadataItems.find(
|
||||
(item) => item.id === objectMetadataItemId,
|
||||
);
|
||||
|
||||
if (!isDefined(objectMetadataItem)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return getRecordIndexIdFromObjectNamePluralAndViewId(
|
||||
objectMetadataItem.namePlural,
|
||||
viewId,
|
||||
);
|
||||
};
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
|
||||
import { createAtomSelector } from '@/ui/utilities/state/jotai/utils/createAtomSelector';
|
||||
|
||||
export const mainContextStoreHasSelectedRecordsSelector =
|
||||
createAtomSelector<boolean>({
|
||||
key: 'mainContextStoreHasSelectedRecordsSelector',
|
||||
get: ({ get }) => {
|
||||
const numberOfSelectedRecords = get(
|
||||
contextStoreNumberOfSelectedRecordsComponentState,
|
||||
{ instanceId: MAIN_CONTEXT_STORE_INSTANCE_ID },
|
||||
);
|
||||
|
||||
return numberOfSelectedRecords > 0;
|
||||
},
|
||||
});
|
||||
-3
@@ -4,7 +4,6 @@ import { useCallback } from 'react';
|
||||
import { SidePanelPages } from 'twenty-shared/types';
|
||||
import { IconPencil } from 'twenty-ui/display';
|
||||
|
||||
import { commandMenuItemEditSelectionModeState } from '@/command-menu-item/server-items/edit/states/commandMenuItemEditSelectionModeState';
|
||||
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';
|
||||
@@ -51,8 +50,6 @@ export const useEnterLayoutCustomizationMode = () => {
|
||||
isSidePanelOpened &&
|
||||
currentSidePanelPage === SidePanelPages.CommandMenuDisplay
|
||||
) {
|
||||
store.set(commandMenuItemEditSelectionModeState.atom, 'selection');
|
||||
|
||||
navigateSidePanel({
|
||||
page: SidePanelPages.CommandMenuEdit,
|
||||
pageTitle: t`Edit actions`,
|
||||
|
||||
+7
@@ -1,5 +1,7 @@
|
||||
import { commandMenuItemsDraftState } from '@/command-menu-item/server-items/edit/states/commandMenuItemsDraftState';
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { isLayoutCustomizationModeEnabledState } from '@/layout-customization/states/isLayoutCustomizationModeEnabledState';
|
||||
import { useResetRecordIndexSelection } from '@/object-record/record-index/hooks/useResetRecordIndexSelection';
|
||||
import { navigationMenuItemsDraftState } from '@/navigation-menu-item/common/states/navigationMenuItemsDraftState';
|
||||
import { selectedNavigationMenuItemIdInEditModeState } from '@/navigation-menu-item/common/states/selectedNavigationMenuItemIdInEditModeState';
|
||||
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
|
||||
@@ -11,6 +13,9 @@ export const useExitLayoutCustomizationMode = () => {
|
||||
const store = useStore();
|
||||
|
||||
const { closeSidePanelMenu } = useSidePanelMenu();
|
||||
const { resetRecordIndexSelection } = useResetRecordIndexSelection(
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
);
|
||||
|
||||
const setNavigationMenuItemsDraft = useSetAtomState(
|
||||
navigationMenuItemsDraftState,
|
||||
@@ -23,12 +28,14 @@ export const useExitLayoutCustomizationMode = () => {
|
||||
);
|
||||
|
||||
const exitLayoutCustomizationMode = useCallback(() => {
|
||||
resetRecordIndexSelection();
|
||||
setNavigationMenuItemsDraft(null);
|
||||
setSelectedNavigationMenuItemIdInEditMode(null);
|
||||
store.set(commandMenuItemsDraftState.atom, null);
|
||||
setIsLayoutCustomizationModeEnabled(false);
|
||||
closeSidePanelMenu();
|
||||
}, [
|
||||
resetRecordIndexSelection,
|
||||
setNavigationMenuItemsDraft,
|
||||
setSelectedNavigationMenuItemIdInEditMode,
|
||||
setIsLayoutCustomizationModeEnabled,
|
||||
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
import { useContextStoreObjectMetadataItem } from '@/context-store/hooks/useContextStoreObjectMetadataItem';
|
||||
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
|
||||
import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState';
|
||||
import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType';
|
||||
import { useResetRecordBoardSelection } from '@/object-record/record-board/hooks/useResetRecordBoardSelection';
|
||||
import { useResetTableRowSelection } from '@/object-record/record-table/hooks/internal/useResetTableRowSelection';
|
||||
import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId';
|
||||
import { useAtomComponentStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateCallbackState';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
const PLACEHOLDER_RECORD_INDEX_ID = 'placeholder-record-index-id';
|
||||
|
||||
export const useResetRecordIndexSelection = (
|
||||
contextStoreInstanceId?: string,
|
||||
) => {
|
||||
const store = useStore();
|
||||
|
||||
const { objectMetadataItem } = useContextStoreObjectMetadataItem(
|
||||
contextStoreInstanceId,
|
||||
);
|
||||
|
||||
const contextStoreCurrentViewId = useAtomComponentStateValue(
|
||||
contextStoreCurrentViewIdComponentState,
|
||||
contextStoreInstanceId,
|
||||
);
|
||||
|
||||
const contextStoreCurrentViewTypeAtom = useAtomComponentStateCallbackState(
|
||||
contextStoreCurrentViewTypeComponentState,
|
||||
contextStoreInstanceId,
|
||||
);
|
||||
|
||||
const objectNamePlural = objectMetadataItem?.namePlural;
|
||||
|
||||
const hasValidContext = isDefined(objectNamePlural);
|
||||
|
||||
const recordIndexId = hasValidContext
|
||||
? getRecordIndexIdFromObjectNamePluralAndViewId(
|
||||
objectNamePlural,
|
||||
contextStoreCurrentViewId ?? '',
|
||||
)
|
||||
: PLACEHOLDER_RECORD_INDEX_ID;
|
||||
|
||||
const { resetTableRowSelection } = useResetTableRowSelection(recordIndexId);
|
||||
|
||||
const { resetRecordBoardSelection } =
|
||||
useResetRecordBoardSelection(recordIndexId);
|
||||
|
||||
const resetRecordIndexSelection = useCallback(() => {
|
||||
if (!hasValidContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
const contextStoreCurrentViewType = store.get(
|
||||
contextStoreCurrentViewTypeAtom,
|
||||
);
|
||||
|
||||
switch (contextStoreCurrentViewType) {
|
||||
case ContextStoreViewType.Table:
|
||||
resetTableRowSelection();
|
||||
break;
|
||||
case ContextStoreViewType.Kanban:
|
||||
resetRecordBoardSelection();
|
||||
break;
|
||||
}
|
||||
}, [
|
||||
hasValidContext,
|
||||
store,
|
||||
contextStoreCurrentViewTypeAtom,
|
||||
resetTableRowSelection,
|
||||
resetRecordBoardSelection,
|
||||
]);
|
||||
|
||||
return { resetRecordIndexSelection };
|
||||
};
|
||||
@@ -1,4 +1,6 @@
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { isLayoutCustomizationModeEnabledState } from '@/layout-customization/states/isLayoutCustomizationModeEnabledState';
|
||||
import { useResetRecordIndexSelection } from '@/object-record/record-index/hooks/useResetRecordIndexSelection';
|
||||
import { selectedNavigationMenuItemIdInEditModeState } from '@/navigation-menu-item/common/states/selectedNavigationMenuItemIdInEditModeState';
|
||||
import { SIDE_PANEL_FOCUS_ID } from '@/side-panel/constants/SidePanelFocusId';
|
||||
import { useNavigateSidePanel } from '@/side-panel/hooks/useNavigateSidePanel';
|
||||
@@ -21,6 +23,9 @@ export const useSidePanelMenu = () => {
|
||||
const store = useStore();
|
||||
const { navigateSidePanel } = useNavigateSidePanel();
|
||||
const { closeAnyOpenDropdown } = useCloseAnyOpenDropdown();
|
||||
const { resetRecordIndexSelection } = useResetRecordIndexSelection(
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
);
|
||||
|
||||
const { removeFocusItemFromFocusStackById } =
|
||||
useRemoveFocusItemFromFocusStackById();
|
||||
@@ -29,6 +34,14 @@ export const useSidePanelMenu = () => {
|
||||
const isSidePanelOpened = store.get(isSidePanelOpenedState.atom);
|
||||
|
||||
if (isSidePanelOpened) {
|
||||
const isLayoutCustomizationModeEnabled = store.get(
|
||||
isLayoutCustomizationModeEnabledState.atom,
|
||||
);
|
||||
|
||||
if (isLayoutCustomizationModeEnabled) {
|
||||
resetRecordIndexSelection();
|
||||
}
|
||||
|
||||
store.set(sidePanelNavigationStackState.atom, []);
|
||||
store.set(isSidePanelOpenedState.atom, false);
|
||||
store.set(isSidePanelClosingState.atom, true);
|
||||
@@ -37,7 +50,12 @@ export const useSidePanelMenu = () => {
|
||||
focusId: SIDE_PANEL_FOCUS_ID,
|
||||
});
|
||||
}
|
||||
}, [closeAnyOpenDropdown, removeFocusItemFromFocusStackById, store]);
|
||||
}, [
|
||||
closeAnyOpenDropdown,
|
||||
removeFocusItemFromFocusStackById,
|
||||
store,
|
||||
resetRecordIndexSelection,
|
||||
]);
|
||||
|
||||
const openSidePanelMenu = useCallback(() => {
|
||||
emitSidePanelOpenEvent();
|
||||
@@ -45,6 +63,7 @@ export const useSidePanelMenu = () => {
|
||||
const isLayoutCustomizationModeEnabled = store.get(
|
||||
isLayoutCustomizationModeEnabledState.atom,
|
||||
);
|
||||
|
||||
const selectedNavigationItemId = store.get(
|
||||
selectedNavigationMenuItemIdInEditModeState.atom,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user