7339 implement contextual actions inside the commandmenu (#8000)

Closes #7339


https://github.com/user-attachments/assets/b623caa4-c1b3-448e-8880-4a8301802ba8
This commit is contained in:
Raphaël Bosi
2024-10-29 15:10:45 +01:00
committed by GitHub
parent 8bb07c4a4f
commit fe2c8bb43b
30 changed files with 399 additions and 237 deletions

View File

@ -71,7 +71,7 @@ export const RecordBoard = () => {
useListenClickOutsideByClassName({
classNames: ['record-board-card'],
excludeClassNames: ['bottom-bar', 'action-menu-dropdown'],
excludeClassNames: ['bottom-bar', 'action-menu-dropdown', 'command-menu'],
callback: resetRecordSelection,
});

View File

@ -206,6 +206,7 @@ export const RecordIndexContainer = () => {
viewBarId={recordIndexId}
/>
</SpreadsheetImportProvider>
{recordIndexViewType === ViewType.Table && (
<>
<RecordIndexTableContainer
@ -232,7 +233,7 @@ export const RecordIndexContainer = () => {
/>
</StyledContainerWithPadding>
)}
<RecordIndexActionMenu actionMenuId={recordIndexId} />
<RecordIndexActionMenu />
</RecordFieldValueSelectorContextProvider>
</StyledContainer>
);

View File

@ -1,5 +1,7 @@
import { useRecoilValue } from 'recoil';
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
import { isNewViewableRecordLoadingState } from '@/object-record/record-right-drawer/states/isNewViewableRecordLoading';
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
import { viewableRecordNameSingularState } from '@/object-record/record-right-drawer/states/viewableRecordNameSingularState';
@ -38,19 +40,29 @@ export const RightDrawerRecord = () => {
);
return (
<StyledRightDrawerRecord>
<RecordFieldValueSelectorContextProvider>
{!isNewViewableRecordLoading && (
<RecordValueSetterEffect recordId={objectRecordId} />
)}
<RecordShowContainer
objectNameSingular={objectNameSingular}
objectRecordId={objectRecordId}
loading={false}
isInRightDrawer={true}
isNewRightDrawerItemLoading={isNewViewableRecordLoading}
/>
</RecordFieldValueSelectorContextProvider>
</StyledRightDrawerRecord>
<ContextStoreComponentInstanceContext.Provider
value={{
instanceId: `record-show-${objectRecordId}`,
}}
>
<ActionMenuComponentInstanceContext.Provider
value={{ instanceId: `record-show-${objectRecordId}` }}
>
<StyledRightDrawerRecord>
<RecordFieldValueSelectorContextProvider>
{!isNewViewableRecordLoading && (
<RecordValueSetterEffect recordId={objectRecordId} />
)}
<RecordShowContainer
objectNameSingular={objectNameSingular}
objectRecordId={objectRecordId}
loading={false}
isInRightDrawer={true}
isNewRightDrawerItemLoading={isNewViewableRecordLoading}
/>
</RecordFieldValueSelectorContextProvider>
</StyledRightDrawerRecord>
</ActionMenuComponentInstanceContext.Provider>
</ContextStoreComponentInstanceContext.Provider>
);
};

View File

@ -1,10 +1,11 @@
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { ShowPageContainer } from '@/ui/layout/page/components/ShowPageContainer';
import { SetMainContextStoreComponentInstanceIdEffect } from '@/context-store/components/SetMainContextStoreComponentInstanceIdEffect';
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
import { MainContextStoreComponentInstanceIdSetterEffect } from '@/context-store/components/MainContextStoreComponentInstanceIdSetterEffect';
import { InformationBannerDeletedRecord } from '@/information-banner/components/deleted-record/InformationBannerDeletedRecord';
import { RecordShowContainerContextStoreEffect } from '@/object-record/record-show/components/RecordShowContainerContextStoreEffect';
import { RecordShowContainerContextStoreObjectMetadataIdEffect } from '@/object-record/record-show/components/RecordShowContainerContextStoreObjectMetadataIdEffect';
import { RecordShowContainerContextStoreTargetedRecordsEffect } from '@/object-record/record-show/components/RecordShowContainerContextStoreTargetedRecordsEffect';
import { useRecordShowContainerData } from '@/object-record/record-show/hooks/useRecordShowContainerData';
import { useRecordShowContainerTabs } from '@/object-record/record-show/hooks/useRecordShowContainerTabs';
import { ShowPageSubContainer } from '@/ui/layout/show-page/components/ShowPageSubContainer';
@ -41,16 +42,15 @@ export const RecordShowContainer = ({
);
return (
<ContextStoreComponentInstanceContext.Provider
value={{
instanceId: 'record-show',
}}
>
<RecordShowContainerContextStoreEffect
<>
<RecordShowContainerContextStoreObjectMetadataIdEffect
recordId={objectRecordId}
objectNameSingular={objectNameSingular}
/>
{!isInRightDrawer && <SetMainContextStoreComponentInstanceIdEffect />}
<RecordShowContainerContextStoreTargetedRecordsEffect
recordId={objectRecordId}
/>
{!isInRightDrawer && <MainContextStoreComponentInstanceIdSetterEffect />}
{recordFromStore && recordFromStore.deletedAt && (
<InformationBannerDeletedRecord
recordId={objectRecordId}
@ -69,6 +69,6 @@ export const RecordShowContainer = ({
isNewRightDrawerItemLoading={isNewRightDrawerItemLoading}
/>
</ShowPageContainer>
</ContextStoreComponentInstanceContext.Provider>
</>
);
};

View File

@ -0,0 +1,30 @@
import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { useEffect } from 'react';
export const RecordShowContainerContextStoreObjectMetadataIdEffect = ({
recordId,
objectNameSingular,
}: {
recordId: string;
objectNameSingular: string;
}) => {
const setContextStoreCurrentObjectMetadataId = useSetRecoilComponentStateV2(
contextStoreCurrentObjectMetadataIdComponentState,
);
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular: objectNameSingular,
});
useEffect(() => {
setContextStoreCurrentObjectMetadataId(objectMetadataItem?.id);
return () => {
setContextStoreCurrentObjectMetadataId(null);
};
}, [recordId, setContextStoreCurrentObjectMetadataId, objectMetadataItem.id]);
return null;
};

View File

@ -1,29 +1,17 @@
import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { useEffect } from 'react';
export const RecordShowContainerContextStoreEffect = ({
export const RecordShowContainerContextStoreTargetedRecordsEffect = ({
recordId,
objectNameSingular,
}: {
recordId: string;
objectNameSingular: string;
}) => {
const setContextStoreTargetedRecordsRule = useSetRecoilComponentStateV2(
contextStoreTargetedRecordsRuleComponentState,
);
const setContextStoreCurrentObjectMetadataId = useSetRecoilComponentStateV2(
contextStoreCurrentObjectMetadataIdComponentState,
);
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular: objectNameSingular,
});
const setContextStoreNumberOfSelectedRecords = useSetRecoilComponentStateV2(
contextStoreNumberOfSelectedRecordsComponentState,
);
@ -33,7 +21,6 @@ export const RecordShowContainerContextStoreEffect = ({
mode: 'selection',
selectedRecordIds: [recordId],
});
setContextStoreCurrentObjectMetadataId(objectMetadataItem?.id);
setContextStoreNumberOfSelectedRecords(1);
return () => {
@ -41,14 +28,11 @@ export const RecordShowContainerContextStoreEffect = ({
mode: 'selection',
selectedRecordIds: [],
});
setContextStoreCurrentObjectMetadataId(null);
setContextStoreNumberOfSelectedRecords(0);
};
}, [
recordId,
setContextStoreTargetedRecordsRule,
setContextStoreCurrentObjectMetadataId,
objectMetadataItem?.id,
setContextStoreNumberOfSelectedRecords,
]);

View File

@ -27,7 +27,7 @@ export const RecordTableInternalEffect = ({
useListenClickOutsideByClassName({
classNames: ['entity-table-cell'],
excludeClassNames: ['bottom-bar', 'action-menu-dropdown'],
excludeClassNames: ['bottom-bar', 'action-menu-dropdown', 'command-menu'],
callback: () => {
leaveTableFocus();
},

View File

@ -1,11 +1,13 @@
import { useRecoilCallback } from 'recoil';
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
import { recordIndexActionMenuDropdownPositionComponentState } from '@/action-menu/states/recordIndexActionMenuDropdownPositionComponentState';
import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState';
import { isBottomBarOpenedComponentState } from '@/ui/layout/bottom-bar/states/isBottomBarOpenedComponentState';
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId';
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { extractComponentFamilyState } from '@/ui/utilities/state/component-state/utils/extractComponentFamilyState';
import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState';
@ -14,6 +16,10 @@ export const useTriggerActionMenuDropdown = ({
}: {
recordTableId: string;
}) => {
const actionMenuInstanceId = useAvailableComponentInstanceIdOrThrow(
ActionMenuComponentInstanceContext,
);
const triggerActionMenuDropdown = useRecoilCallback(
({ set, snapshot }) =>
(event: React.MouseEvent, recordId: string) => {
@ -24,7 +30,7 @@ export const useTriggerActionMenuDropdown = ({
set(
extractComponentState(
recordIndexActionMenuDropdownPositionComponentState,
`action-menu-dropdown-${recordTableId}`,
`action-menu-dropdown-${actionMenuInstanceId}`,
),
{
x: event.clientX,
@ -48,19 +54,19 @@ export const useTriggerActionMenuDropdown = ({
const isActionMenuDropdownOpenState = extractComponentState(
isDropdownOpenComponentState,
`action-menu-dropdown-${recordTableId}`,
`action-menu-dropdown-${actionMenuInstanceId}`,
);
const isActionBarOpenState = isBottomBarOpenedComponentState.atomFamily(
{
instanceId: `action-bar-${recordTableId}`,
instanceId: `action-bar-${actionMenuInstanceId}`,
},
);
set(isActionBarOpenState, false);
set(isActionMenuDropdownOpenState, true);
},
[recordTableId],
[actionMenuInstanceId, recordTableId],
);
return { triggerActionMenuDropdown };