# Description Closes [#696](https://github.com/twentyhq/core-team-issues/issues/696) - `useAction` hooks have been removed for all actions - Every action can now declare a react component - Some standard action components have been introduced: `Action`, `ActionLink` and `ActionModal` - The `ActionDisplay` component uses the new `displayType` prop of the `ActionMenuContext` to render the right component for the action according to its container: `ActionButton`, `ActionDropdownItem` or `ActionListItem` - The `ActionDisplayer` wraps the action component inside a context which gives it all the information about the action -`actionMenuEntriesComponenState` has been removed and now all actions are computed directly using `useRegisteredAction` - This computation is done inside `ActionMenuContextProvider` and the actions are passed inside a context - `actionMenuType` gives information about the container of the action, so the action can know wether or not to close this container upon execution
153 lines
6.4 KiB
TypeScript
153 lines
6.4 KiB
TypeScript
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
|
import { COMMAND_MENU_ANIMATION_VARIANTS } from '@/command-menu/constants/CommandMenuAnimationVariants';
|
|
import { COMMAND_MENU_COMPONENT_INSTANCE_ID } from '@/command-menu/constants/CommandMenuComponentInstanceId';
|
|
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
|
import { useCommandMenuCloseAnimationCompleteCleanup } from '@/command-menu/hooks/useCommandMenuCloseAnimationCompleteCleanup';
|
|
import { useCommandMenuHotKeys } from '@/command-menu/hooks/useCommandMenuHotKeys';
|
|
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
|
|
import { CommandMenuAnimationVariant } from '@/command-menu/types/CommandMenuAnimationVariant';
|
|
import { CommandMenuHotkeyScope } from '@/command-menu/types/CommandMenuHotkeyScope';
|
|
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
|
|
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
|
|
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
|
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
|
import { RecordFilterGroupsComponentInstanceContext } from '@/object-record/record-filter-group/states/context/RecordFilterGroupsComponentInstanceContext';
|
|
import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext';
|
|
import { RecordSortsComponentInstanceContext } from '@/object-record/record-sort/states/context/RecordSortsComponentInstanceContext';
|
|
import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId';
|
|
import { RootStackingContextZIndices } from '@/ui/layout/constants/RootStackingContextZIndices';
|
|
import { currentHotkeyScopeState } from '@/ui/utilities/hotkey/states/internal/currentHotkeyScopeState';
|
|
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
|
import { useTheme } from '@emotion/react';
|
|
import styled from '@emotion/styled';
|
|
import { AnimatePresence, motion } from 'framer-motion';
|
|
import { useRef } from 'react';
|
|
import { useRecoilCallback, useRecoilValue } from 'recoil';
|
|
import { useIsMobile } from 'twenty-ui/utilities';
|
|
|
|
const StyledCommandMenu = styled(motion.div)`
|
|
background: ${({ theme }) => theme.background.primary};
|
|
border-left: 1px solid ${({ theme }) => theme.border.color.medium};
|
|
box-shadow: ${({ theme }) => theme.boxShadow.strong};
|
|
font-family: ${({ theme }) => theme.font.family};
|
|
height: 100%;
|
|
overflow: hidden;
|
|
padding: 0;
|
|
position: fixed;
|
|
right: 0%;
|
|
top: 0%;
|
|
z-index: ${RootStackingContextZIndices.CommandMenu};
|
|
display: flex;
|
|
flex-direction: column;
|
|
`;
|
|
|
|
export const CommandMenuContainer = ({
|
|
children,
|
|
}: {
|
|
children: React.ReactNode;
|
|
}) => {
|
|
const { closeCommandMenu } = useCommandMenu();
|
|
|
|
const { commandMenuCloseAnimationCompleteCleanup } =
|
|
useCommandMenuCloseAnimationCompleteCleanup();
|
|
|
|
const isCommandMenuOpened = useRecoilValue(isCommandMenuOpenedState);
|
|
|
|
const commandMenuRef = useRef<HTMLDivElement>(null);
|
|
|
|
useCommandMenuHotKeys();
|
|
|
|
const handleClickOutside = useRecoilCallback(
|
|
({ snapshot }) =>
|
|
() => {
|
|
const hotkeyScope = snapshot
|
|
.getLoadable(currentHotkeyScopeState)
|
|
.getValue();
|
|
|
|
if (hotkeyScope?.scope === CommandMenuHotkeyScope.CommandMenuFocused) {
|
|
closeCommandMenu();
|
|
}
|
|
},
|
|
[closeCommandMenu],
|
|
);
|
|
|
|
useListenClickOutside({
|
|
refs: [commandMenuRef],
|
|
callback: handleClickOutside,
|
|
listenerId: 'COMMAND_MENU_LISTENER_ID',
|
|
excludeClassNames: ['page-header-command-menu-button'],
|
|
});
|
|
|
|
const isMobile = useIsMobile();
|
|
|
|
const targetVariantForAnimation: CommandMenuAnimationVariant = isMobile
|
|
? 'fullScreen'
|
|
: 'normal';
|
|
|
|
const theme = useTheme();
|
|
|
|
const objectMetadataItemId = useRecoilComponentValueV2(
|
|
contextStoreCurrentObjectMetadataItemIdComponentState,
|
|
COMMAND_MENU_COMPONENT_INSTANCE_ID,
|
|
);
|
|
|
|
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
|
|
|
const objectMetadataItem = objectMetadataItems.find(
|
|
(objectMetadataItem) => objectMetadataItem.id === objectMetadataItemId,
|
|
);
|
|
|
|
const currentViewId = useRecoilComponentValueV2(
|
|
contextStoreCurrentViewIdComponentState,
|
|
COMMAND_MENU_COMPONENT_INSTANCE_ID,
|
|
);
|
|
|
|
const recordIndexId = getRecordIndexIdFromObjectNamePluralAndViewId(
|
|
objectMetadataItem?.namePlural ?? '',
|
|
currentViewId ?? '',
|
|
);
|
|
|
|
return (
|
|
<RecordFilterGroupsComponentInstanceContext.Provider
|
|
value={{ instanceId: recordIndexId }}
|
|
>
|
|
<RecordFiltersComponentInstanceContext.Provider
|
|
value={{ instanceId: recordIndexId }}
|
|
>
|
|
<RecordSortsComponentInstanceContext.Provider
|
|
value={{ instanceId: recordIndexId }}
|
|
>
|
|
<ContextStoreComponentInstanceContext.Provider
|
|
value={{ instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID }}
|
|
>
|
|
<ActionMenuComponentInstanceContext.Provider
|
|
value={{ instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID }}
|
|
>
|
|
<AnimatePresence
|
|
mode="wait"
|
|
onExitComplete={commandMenuCloseAnimationCompleteCleanup}
|
|
>
|
|
{isCommandMenuOpened && (
|
|
<StyledCommandMenu
|
|
data-testid="command-menu"
|
|
ref={commandMenuRef}
|
|
className="command-menu"
|
|
animate={targetVariantForAnimation}
|
|
initial="closed"
|
|
exit="closed"
|
|
variants={COMMAND_MENU_ANIMATION_VARIANTS}
|
|
transition={{ duration: theme.animation.duration.normal }}
|
|
>
|
|
{children}
|
|
</StyledCommandMenu>
|
|
)}
|
|
</AnimatePresence>
|
|
</ActionMenuComponentInstanceContext.Provider>
|
|
</ContextStoreComponentInstanceContext.Provider>
|
|
</RecordSortsComponentInstanceContext.Provider>
|
|
</RecordFiltersComponentInstanceContext.Provider>
|
|
</RecordFilterGroupsComponentInstanceContext.Provider>
|
|
);
|
|
};
|