322 compact command chips dropdown (#10456)

Closes https://github.com/twentyhq/core-team-issues/issues/322



https://github.com/user-attachments/assets/d4806f04-e217-40f5-9707-93334bbd49ea

---------

Co-authored-by: Devessier <baptiste@devessier.fr>
This commit is contained in:
Raphaël Bosi
2025-02-25 16:42:38 +01:00
committed by GitHub
parent a1c7e3279c
commit 9997cf5a4e
9 changed files with 139 additions and 65 deletions

View File

@ -1,4 +1,9 @@
import { COMMAND_MENU_CONTEXT_CHIP_GROUPS_DROPDOWN_ID } from '@/command-menu/constants/CommandMenuContextChipGroupsDropdownId';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
import { isDefined } from 'twenty-shared';
import { MenuItem } from 'twenty-ui';
import {
CommandMenuContextChip,
CommandMenuContextChipProps,
@ -34,9 +39,30 @@ export const CommandMenuContextChipGroups = ({
return (
<>
{firstChips.length > 0 && (
<CommandMenuContextChip
Icons={firstChips.map((chip) => chip.Icons?.[0])}
/>
<Dropdown
clickableComponent={
<CommandMenuContextChip
Icons={firstChips.map((chip) => chip.Icons?.[0])}
onClick={() => {}}
/>
}
dropdownComponents={
<DropdownMenuItemsContainer>
{firstChips.map((chip) => (
<MenuItem
LeftComponent={chip.Icons}
text={chip.text}
onClick={chip.onClick}
/>
))}
</DropdownMenuItemsContainer>
}
dropdownHotkeyScope={{
scope: AppHotkeyScope.CommandMenu,
}}
dropdownId={COMMAND_MENU_CONTEXT_CHIP_GROUPS_DROPDOWN_ID}
dropdownPlacement="bottom-start"
></Dropdown>
)}
{isDefined(lastChip) && (

View File

@ -1,5 +1,6 @@
import { CommandMenuContextChipGroups } from '@/command-menu/components/CommandMenuContextChipGroups';
import { CommandMenuContextRecordChipAvatars } from '@/command-menu/components/CommandMenuContextRecordChipAvatars';
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
import { getSelectedRecordsContextText } from '@/command-menu/utils/getRecordContextText';
import { useFindManyRecordsSelectedInContextStore } from '@/context-store/hooks/useFindManyRecordsSelectedInContextStore';
import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById';
@ -22,6 +23,8 @@ export const CommandMenuContextChipGroupsWithRecordSelection = ({
limit: 3,
});
const { openRootCommandMenu } = useCommandMenu();
if (loading) {
return null;
}
@ -43,6 +46,7 @@ export const CommandMenuContextChipGroupsWithRecordSelection = ({
totalCount,
),
Icons: Avatars,
onClick: contextChips.length > 0 ? openRootCommandMenu : undefined,
}
: undefined;

View File

@ -15,6 +15,7 @@ import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useLingui } from '@lingui/react/macro';
import { AnimatePresence, motion } from 'framer-motion';
import { useMemo, useRef } from 'react';
import { useLocation } from 'react-router-dom';
import { useRecoilState, useRecoilValue } from 'recoil';
@ -99,7 +100,11 @@ export const CommandMenuTopBar = () => {
const isMobile = useIsMobile();
const { closeCommandMenu, goBackFromCommandMenu } = useCommandMenu();
const {
closeCommandMenu,
goBackFromCommandMenu,
navigateCommandMenuHistory,
} = useCommandMenu();
const contextStoreCurrentObjectMetadataItem = useRecoilComponentValueV2(
contextStoreCurrentObjectMetadataItemComponentState,
@ -118,35 +123,56 @@ export const CommandMenuTopBar = () => {
);
const contextChips = useMemo(() => {
return commandMenuNavigationStack
.filter((page) => page.page !== CommandMenuPages.Root)
.map((page) => {
return {
Icons: [<page.pageIcon size={theme.icon.size.sm} />],
text: page.pageTitle,
};
});
}, [commandMenuNavigationStack, theme.icon.size.sm]);
const filteredCommandMenuNavigationStack =
commandMenuNavigationStack.filter(
(page) => page.page !== CommandMenuPages.Root,
);
return filteredCommandMenuNavigationStack.map((page, index) => ({
Icons: [<page.pageIcon size={theme.icon.size.sm} />],
text: page.pageTitle,
onClick:
index === filteredCommandMenuNavigationStack.length - 1
? undefined
: () => {
navigateCommandMenuHistory(index);
},
}));
}, [
commandMenuNavigationStack,
navigateCommandMenuHistory,
theme.icon.size.sm,
]);
const location = useLocation();
const isButtonVisible =
!location.pathname.startsWith('/objects/') &&
!location.pathname.startsWith('/object/');
const backButtonAnimationDuration =
contextChips.length > 0 ? theme.animation.duration.instant : 0;
return (
<StyledInputContainer>
<StyledContentContainer>
{isCommandMenuV2Enabled && (
<>
{commandMenuPage !== CommandMenuPages.Root && (
<CommandMenuContextChip
Icons={[<IconChevronLeft size={theme.icon.size.sm} />]}
onClick={() => {
goBackFromCommandMenu();
}}
testId="command-menu-go-back-button"
/>
)}
<AnimatePresence>
{commandMenuPage !== CommandMenuPages.Root && (
<motion.div
exit={{ opacity: 0, width: 0 }}
transition={{
duration: backButtonAnimationDuration,
}}
>
<CommandMenuContextChip
Icons={[<IconChevronLeft size={theme.icon.size.sm} />]}
onClick={goBackFromCommandMenu}
testId="command-menu-go-back-button"
/>
</motion.div>
)}
</AnimatePresence>
{isDefined(contextStoreCurrentObjectMetadataItem) &&
commandMenuPage !== CommandMenuPages.SearchRecords ? (
<CommandMenuContextChipGroupsWithRecordSelection