Refactored dropdown content and fixed all dropdown width bugs (#12334)

This PR refactors all the dropdown content wrapping mechanism across the
entire app.

It refactors the internals of the `Dropdown` component and introduces a
new generic `DropdownContent` component that is a generic wrapper used
for each dropdown.

## Why this PR ?

Because we’ve been experiencing continuous regressions for months on the
dropdown content width, with weird scrolling behaviors in some and not
in others, and every time a solution was found for a particular set of
dropdowns, it broke another set of dropdowns, which wasn’t noticed
because doing the QA of all dropdowns of the app is very difficult for
fixing an apparently small bug.

## Don’t we already have a `DropdownMenu` component ?

Indeed, this new `DropdownContent` is almost like `DropdownMenu` and
took inspiration from it but `DropdownContent` acts as a generic content
container that sets the width of the whole dropdown, whether we have a
menu or not.

## Why don’t we put it directly in Dropdown internals ?

Because the Dropdown component is using a complex logic with floating-ui
middleware to compute its position and size, and for this logic to work
correctly, it cannot be responsible for the “wanted” width of its
content, because the children components, which the dropdown is not
aware of, can request different widths after the dropdown has been
mounted.

A good example with multiple use cases inside the same dropdown can be
found in `AdvancedFilterDropdownFilterInput`

Thus, it is the responsibility of the content of the dropdown to
determine the width it wants to have.

## What is the difference with DropdownMenuItemsContainer ?

We can have multiple `DropdownMenuItemsContainer` in a dropdown,
alongside other components like `DropdownMenuSeparator` or
`DropdownMenuHeader`, and each of those components behaves differently
regarding to its width, paddings, etc. Therefore it is logical that the
`DropdownMenuItemsContainer` cannot be responsible for the whole
dropdown content width, and trying to do so has been the cause of many
regressions for months.

Now `DropdownMenuItemsContainer` is taking a width of `auto` by default,
which is the best to adapt to a parent which has a defined width.

## How do I set the width of my dropdown now ?

By passing a pixel width to the props `widthInPixels` of
`DropdownContent`, which only accepts numbers to avoid any confusion
with `auto` , `100%` or `160px` and other specific width variables.

The `dropdownWidth` props has been removed from `<Dropdown>` to avoid
any confusion.

Also the `DropdownMenuItemsContainer` is now using `auto` as its default
width to fill the available space inside `DropdownContent` .

It is highly recommended to use the enum `GenericDropdownContentWidt` to
define your width.

## Where to use this new `DropdownContent` component ?

There are two main use cases.

If the dropdown content is defined directly inline in the Dropdown
props, then it is recommended to use it here too.

On the other hand if the dropdown content is abstracted in another
component, it’s recommended to use this new component alongside the
others components like `DropdownMenuItemsContainer`.

A good rule of thumb is to place `DropdownContent` where
`DropdownMenuItemsContainer`, `DropdownMenuSearchInput`, etc. are
placed.

## What if I have a custom width ?

Just define a constant like `ICON_PICKER_DROPDOWN_CONTENT_WIDTH` and use
it with the props `widthInPixels` .

Otherwise there’s a `GenericDropdownContentWidth` enum. The default
value being `GenericDropdownContentWidth.Medium` (or 200px), which most
dropdowns use.

## QA 


Component | Comment
-- | --
AttachmentDropdown | Fixed overflowing (thanks to DropdownContent)
RecordIndexActionMenuDropdown |  
CommandMenuActionMenuDropdown |  
SupportDropdown | Fixed overflowing (thanks to DropdownContent)
MessageThreadSubscribersDropdownButton | Removed because unused
FavoriteFolderNavigationDrawerItemDropdown | Set width at Narrow
FavoriteFolderPicker |  
ViewPickerOptionDropdown |  
PageFavoriteFolderDropdown | Removed because unused
AdvancedFilterAddFilterRuleSelect |  
AdvancedFilterAddFilterRuleSelect |  
AdvancedFilterFieldSelectMenu |  
AdvancedFilterRecordFilterGroupOptionsDropdown |  
AdvancedFilterRecordFilterOperanceSelect | Set width at Narrow
AdvancedFilterLogicalOperatorDropdown | Set width at Narrow
AdvancedFilterRecordFilterOptionsDropdown |  
AdvancedFilterRootRecordFilterGroup | Fixed broken horizontal scrolling
behavior
AdvancedFilterSubFieldSelectMenu |  
AdvancedFilterDropdownFilterInput |  
ObjectFilterDropdownBooleanSelect |  
ObjectFilterDropdownCountrySelect | Fixed broken menu items container
ObjectFilterDropdownCurrencySelect | Set width to Large
ObjectFilterDropdownFilterInput |  
ObjectFilterDropdownOperandDropdown | Fixed width that was not fixed
ObjectFilterDropdownFilterInput | Fixed width that wasn’t the same for
EditableFilterChip
ObjectFilterDropdownOperandSelect | Refactored
ObjectOptionsDropdownRecordGroupFieldsContent | Added missing separator
ObjectOptionDropdownFieldsContent |  
ObjectOptionsDropdownHiddenFieldsContent |  
ObjectOptionsDropdownLayoutContent |  
ObjectOptionsDropdownLayoutOpenInContent |  
ObjectOptionsDropdownMenuContent |  
ObjectOptionsDropdownRecordGroupFieldsContent |  
ObjectOptionsDropdownRecordGroupsContent |  
ObjectOptionsDropdownRecordGroupSortContent |  
ObjectOptionsDropdownHiddenRecordGroupsContent | Removed unnecessary
DropdownMenuItemsContainer
RecordBoardColumnHeaderAggregateDropdown | Fixed overflowing (thanks to
DropdownContent)
RecordBoardColumnHeaderAggregateDropdownFieldsContent | Fixed
overflowing (thanks to DropdownContent)
RecordBoardColumnHeaderAggregateDropdownMenuContent | Fixed overflowing
(thanks to DropdownContent)
RecordBoardColumnHeaderAggregateDropdownOptionsContent | Fixed
overflowing (thanks to DropdownContent)
MultiItemFieldInput | Fixed overflowing (thanks to DropdownContent)
MultiItemFieldMenuItem |  
MultipleRecordPicker | Fixed overflowing (thanks to DropdownContent)
SingleRecordPicker |  
RecordTableColumnAggregateDropdownSubmenuContent |  
RecordTableColumnAggregateFooterMenuContent |  
RecordTableColumnHeadDropdownMenu | Fixed overflowing (thanks to
DropdownContent)
RecordTableHeaderPlusButtonContent |  
MultipleSelectDropdown | Broken width fixed
ObjectSortDropdownButton |  
RecordDetailRelationRecordsListItem |  
ConfigVariableDatabaseInput |  
ConfigVariableOptionsDropdownContent |  
SettingsObjectFieldActiveActionDropdown | Fixed overflowing (thanks to
DropdownContent)
SettingsObjectFieldDisabledActionDropdown | Set width at Narrow
SettingsObjectSummaryCard | Removed because unused
SettingsDataModelFieldSelectFormOptionRow |  
SettingsDataModelNewFieldBreadcrumbDropdown |  
SettingsObjectInactiveMenuDropDown |  
SettingsRoleAssignementWorkspaceMemberPickerDropdown |  
SettingsRolePermissionObjectLevelObjectPickerDropdownContent |  
SettingsSecurityApprovedAccessDomainRowDropdownMenu | Couldn’t test
SettingsSecuritySSORowDropdownMenu | Couldn’t test
SettingsAccountsRowDropdownMenu | Fixed overflowing (thanks to
DropdownContent)
SettingsIntegrationDatabaseConnectionSummaryCard | Couldn’t test
SettingsServerlessFunctionTablEnvironmentVariableTableRow | Deactivated
scope
MatchColumnSelectFieldSelectDropdownContent | Removed now unnecessary
width on DropdownMenuItemsContainer
MatchColumnSelectSubFieldSelectDropdownContent |  
SubMatchingSelectInput |  
CurrencyPickerDropdownSelect |  
IconPicker | Fixed overflowing (thanks to DropdownContent)
PhoneCountryPickerDropdownSelect |  
Select | Refactored to drilldown wanted width of content, in this case
it’s intended
ExpandedListDropdown |  
ShowPageAddButton | Removed because unused
MultiWorkspaceDropdownDefaultComponent |  
MultiWorkspaceDropdownThemesComponent |  
MultiWorkspaceDropdownWorkspacesListComponent |  
AdvancedFilterDropdownButton |  
EditableFilterChip |  
EditableFilterDropdownButton |  
UpdateViewButtonGroup |  
ViewBarFilterDropdown |  
ViewBarFilterDropdownFieldSelectMenu |  
ViewPickerContentCreateMode |  
ViewPickerContentEditMode |  
ViewPickerListContent |  
WorkflowEditTriggerDatabaseEventForm |  
WorkflowVariablesDropdownFieldItems |  
WorkflowVariablesDropdownObjectItems |  
WorkflowVariablesDropdownWorkflowStepItems |  
CommandMenuContextChipGroups |  
RecordBoardColumnDropdownMenu |  
MultiSelectInput |  
SelectInput |  
CustomSlashMenu |  
DropdownMenu | Removed and replaced by DropdownContent
OverlayContainer and around |  


<!-- notionvc: 1e23bdb8-2dda-4f8d-a64d-ecc829a768a2 -->

## Miscellaneous 

Side notes : 

- The `Select` component is now wrapping the `DropdownContent` because
it computes a dynamic width.
- The advanced filter dropdown has been fixed, it was broken when
resizing the window horizontally, we couldn’t scroll. This specific edge
case was taken into account when refactoring the whole dropdown content
system
- As discussed with Nitin, data-select-disable will probably be removed
entirely, so I let it as is, because right now it is not used by the
refactored d&d selection.
- Duplicate separators under DropdownMenuHeader have been removed.

Fixes : https://github.com/twentyhq/twenty/issues/12327
Fixes : https://github.com/twentyhq/core-team-issues/issues/951
This commit is contained in:
Lucas Bordeau
2025-05-27 19:44:13 +02:00
committed by GitHub
parent 5ce462b17e
commit 01b40e173b
116 changed files with 1243 additions and 1755 deletions

View File

@ -5,6 +5,7 @@ import { ActionMenuComponentInstanceContext } from '@/action-menu/states/context
import { CommandMenuActionMenuDropdownHotkeyScope } from '@/action-menu/types/CommandMenuActionMenuDropdownHotkeyScope';
import { getRightDrawerActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getRightDrawerActionMenuDropdownIdFromActionMenuId';
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 { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2';
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
@ -72,19 +73,21 @@ export const CommandMenuActionMenuDropdown = () => {
setSelectedItemId(selectableItemIdArray[0]);
}}
dropdownComponents={
<DropdownMenuItemsContainer>
<SelectableList
selectableListInstanceId={actionMenuId}
hotkeyScope={
CommandMenuActionMenuDropdownHotkeyScope.CommandMenuActionMenuDropdown
}
selectableItemIdArray={selectableItemIdArray}
>
{recordSelectionActions.map((action) => (
<ActionComponent action={action} key={action.key} />
))}
</SelectableList>
</DropdownMenuItemsContainer>
<DropdownContent>
<DropdownMenuItemsContainer>
<SelectableList
selectableListInstanceId={actionMenuId}
hotkeyScope={
CommandMenuActionMenuDropdownHotkeyScope.CommandMenuActionMenuDropdown
}
selectableItemIdArray={selectableItemIdArray}
>
{recordSelectionActions.map((action) => (
<ActionComponent action={action} key={action.key} />
))}
</SelectableList>
</DropdownMenuItemsContainer>
</DropdownContent>
}
/>
);

View File

@ -9,6 +9,7 @@ import { ActionMenuDropdownHotkeyScope } from '@/action-menu/types/ActionMenuDro
import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId';
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
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 { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2';
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
@ -82,39 +83,41 @@ export const RecordIndexActionMenuDropdown = () => {
y: actionMenuDropdownPosition.y ?? 0,
}}
dropdownComponents={
<StyledDropdownMenuContainer
data-click-outside-id={ACTION_MENU_DROPDOWN_CLICK_OUTSIDE_ID}
>
<DropdownMenuItemsContainer>
<SelectableList
hotkeyScope={ActionMenuDropdownHotkeyScope.ActionMenuDropdown}
selectableItemIdArray={selectedItemIdArray}
selectableListInstanceId={dropdownId}
>
{recordIndexActions.map((action) => (
<ActionComponent action={action} key={action.key} />
))}
<SelectableListItem
itemId="more-actions"
key="more-actions"
onEnter={() => {
closeDropdown(dropdownId);
openCommandMenu();
}}
<DropdownContent>
<StyledDropdownMenuContainer
data-click-outside-id={ACTION_MENU_DROPDOWN_CLICK_OUTSIDE_ID}
>
<DropdownMenuItemsContainer>
<SelectableList
hotkeyScope={ActionMenuDropdownHotkeyScope.ActionMenuDropdown}
selectableItemIdArray={selectedItemIdArray}
selectableListInstanceId={dropdownId}
>
<MenuItem
LeftIcon={IconLayoutSidebarRightExpand}
onClick={() => {
{recordIndexActions.map((action) => (
<ActionComponent action={action} key={action.key} />
))}
<SelectableListItem
itemId="more-actions"
key="more-actions"
onEnter={() => {
closeDropdown(dropdownId);
openCommandMenu();
}}
focused={selectedItemId === 'more-actions'}
text="More actions"
/>
</SelectableListItem>
</SelectableList>
</DropdownMenuItemsContainer>
</StyledDropdownMenuContainer>
>
<MenuItem
LeftIcon={IconLayoutSidebarRightExpand}
onClick={() => {
closeDropdown(dropdownId);
openCommandMenu();
}}
focused={selectedItemId === 'more-actions'}
text="More actions"
/>
</SelectableListItem>
</SelectableList>
</DropdownMenuItemsContainer>
</StyledDropdownMenuContainer>
</DropdownContent>
}
/>
);

View File

@ -1,16 +0,0 @@
import { MessageThreadSubscribersDropdownButton } from '@/activities/emails/components/MessageThreadSubscribersDropdownButton';
import { MessageThread } from '@/activities/emails/types/MessageThread';
export const EmailThreadMembersChip = ({
messageThread,
}: {
messageThread: MessageThread;
}) => {
const subscribers = messageThread.subscribers ?? [];
return (
<MessageThreadSubscribersDropdownButton
messageThreadSubscribers={subscribers}
/>
);
};

View File

@ -1,75 +0,0 @@
import { MessageThreadSubscriber } from '@/activities/emails/types/MessageThreadSubscriber';
import { isNonEmptyString } from '@sniptt/guards';
import { useContext } from 'react';
import { Chip, ChipVariant } from 'twenty-ui/components';
import { Avatar, AvatarGroup, IconChevronDown } from 'twenty-ui/display';
import { ThemeContext } from 'twenty-ui/theme';
const MAX_NUMBER_OF_AVATARS = 3;
export const MessageThreadSubscribersChip = ({
messageThreadSubscribers,
}: {
messageThreadSubscribers: MessageThreadSubscriber[];
}) => {
const { theme } = useContext(ThemeContext);
const numberOfMessageThreadSubscribers = messageThreadSubscribers.length;
const isOnlyOneSubscriber = numberOfMessageThreadSubscribers === 1;
const isPrivateThread = isOnlyOneSubscriber;
const privateLabel = 'Private';
const susbcriberAvatarUrls = messageThreadSubscribers
.map((member) => member.workspaceMember.avatarUrl)
.filter(isNonEmptyString);
const firstAvatarUrl = susbcriberAvatarUrls[0];
const firstAvatarColorSeed = messageThreadSubscribers?.[0].workspaceMember.id;
const firstAvatarPlaceholder =
messageThreadSubscribers?.[0].workspaceMember.name.firstName;
const subscriberNames = messageThreadSubscribers.map(
(member) => member.workspaceMember?.name.firstName,
);
const moreAvatarsLabel =
numberOfMessageThreadSubscribers > MAX_NUMBER_OF_AVATARS
? `+${numberOfMessageThreadSubscribers - MAX_NUMBER_OF_AVATARS}`
: null;
const label = isPrivateThread ? privateLabel : (moreAvatarsLabel ?? '');
return (
<Chip
label={label}
variant={ChipVariant.Highlighted}
leftComponent={
isOnlyOneSubscriber ? (
<Avatar
avatarUrl={firstAvatarUrl}
placeholderColorSeed={firstAvatarColorSeed}
placeholder={firstAvatarPlaceholder}
size="md"
type={'rounded'}
/>
) : (
<AvatarGroup
avatars={subscriberNames.map((name, index) => (
<Avatar
key={name}
avatarUrl={susbcriberAvatarUrls[index] ?? ''}
placeholder={name}
type="rounded"
/>
))}
/>
)
}
rightComponent={() => <IconChevronDown size={theme.icon.size.sm} />}
clickable
/>
);
};

View File

@ -1,107 +0,0 @@
import { offset } from '@floating-ui/react';
import { MessageThreadSubscriberDropdownAddSubscriber } from '@/activities/emails/components/MessageThreadSubscriberDropdownAddSubscriber';
import { MessageThreadSubscribersChip } from '@/activities/emails/components/MessageThreadSubscribersChip';
import { MessageThreadSubscriber } from '@/activities/emails/types/MessageThreadSubscriber';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { useListenRightDrawerClose } from '@/ui/layout/right-drawer/hooks/useListenRightDrawerClose';
import { useState } from 'react';
import { IconMinus, IconPlus } from 'twenty-ui/display';
import { MenuItem, MenuItemAvatar } from 'twenty-ui/navigation';
export const MESSAGE_THREAD_SUBSCRIBER_DROPDOWN_ID =
'message-thread-subscriber';
export const MessageThreadSubscribersDropdownButton = ({
messageThreadSubscribers,
}: {
messageThreadSubscribers: MessageThreadSubscriber[];
}) => {
const [isAddingSubscriber, setIsAddingSubscriber] = useState(false);
const { closeDropdown } = useDropdown(MESSAGE_THREAD_SUBSCRIBER_DROPDOWN_ID);
const mockSubscribers = [
...messageThreadSubscribers,
...messageThreadSubscribers,
...messageThreadSubscribers,
...messageThreadSubscribers,
];
// TODO: implement
const handleAddSubscriberClick = () => {
setIsAddingSubscriber(true);
};
// TODO: implement
const handleRemoveSubscriber = (_subscriber: MessageThreadSubscriber) => {
closeDropdown();
};
useListenRightDrawerClose(() => {
closeDropdown();
});
return (
<Dropdown
dropdownId={MESSAGE_THREAD_SUBSCRIBER_DROPDOWN_ID}
clickableComponent={
<MessageThreadSubscribersChip
messageThreadSubscribers={mockSubscribers}
/>
}
dropdownComponents={
<DropdownMenu width="160px" z-index={offset(1)}>
{isAddingSubscriber ? (
<MessageThreadSubscriberDropdownAddSubscriber
existingSubscribers={messageThreadSubscribers}
/>
) : (
<DropdownMenuItemsContainer>
{messageThreadSubscribers?.map((subscriber) => (
<MenuItemAvatar
key={subscriber.workspaceMember.id}
testId="menu-item"
onClick={() => {
handleRemoveSubscriber(subscriber);
}}
text={
subscriber.workspaceMember.name.firstName +
' ' +
subscriber.workspaceMember.name.lastName
}
avatar={{
placeholder: subscriber.workspaceMember.name.firstName,
avatarUrl: subscriber.workspaceMember.avatarUrl,
placeholderColorSeed: subscriber.workspaceMember.id,
size: 'md',
type: 'rounded',
}}
iconButtons={[
{
Icon: IconMinus,
onClick: () => {
handleRemoveSubscriber(subscriber);
},
},
]}
/>
))}
<DropdownMenuSeparator />
<MenuItem
LeftIcon={IconPlus}
onClick={handleAddSubscriberClick}
text="Add subscriber"
/>
</DropdownMenuItemsContainer>
)}
</DropdownMenu>
}
dropdownHotkeyScope={{ scope: MESSAGE_THREAD_SUBSCRIBER_DROPDOWN_ID }}
/>
);
};

View File

@ -1,5 +1,7 @@
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 { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import {
IconDotsVertical,
@ -48,26 +50,27 @@ export const AttachmentDropdown = ({
clickableComponent={
<LightIconButton Icon={IconDotsVertical} accent="tertiary" />
}
dropdownWidth={160}
dropdownComponents={
<DropdownMenuItemsContainer>
<MenuItem
text="Download"
LeftIcon={IconDownload}
onClick={handleDownload}
/>
<MenuItem
text="Rename"
LeftIcon={IconPencil}
onClick={handleRename}
/>
<MenuItem
text="Delete"
accent="danger"
LeftIcon={IconTrash}
onClick={handleDelete}
/>
</DropdownMenuItemsContainer>
<DropdownContent widthInPixels={GenericDropdownContentWidth.Narrow}>
<DropdownMenuItemsContainer>
<MenuItem
text="Download"
LeftIcon={IconDownload}
onClick={handleDownload}
/>
<MenuItem
text="Rename"
LeftIcon={IconPencil}
onClick={handleRename}
/>
<MenuItem
text="Delete"
accent="danger"
LeftIcon={IconTrash}
onClick={handleDelete}
/>
</DropdownMenuItemsContainer>
</DropdownContent>
}
dropdownHotkeyScope={{ scope: dropdownId }}
/>

View File

@ -4,11 +4,11 @@ import { SubTitle } from '@/auth/components/SubTitle';
import { Title } from '@/auth/components/Title';
import { useHandleResendEmailVerificationToken } from '@/auth/sign-in-up/hooks/useHandleResendEmailVerificationToken';
import { useTheme } from '@emotion/react';
import { AnimatedEaseIn } from 'twenty-ui/utilities';
import { IconMail } from 'twenty-ui/display';
import { Loader } from 'twenty-ui/feedback';
import { MainButton } from 'twenty-ui/input';
import { RGBA } from 'twenty-ui/theme';
import { AnimatedEaseIn } from 'twenty-ui/utilities';
const StyledMailContainer = styled.div`
align-items: center;

View File

@ -1,14 +1,15 @@
import { COMMAND_MENU_CONTEXT_CHIP_GROUPS_DROPDOWN_ID } from '@/command-menu/constants/CommandMenuContextChipGroupsDropdownId';
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 { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2';
import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
import { isDefined } from 'twenty-shared/utils';
import { MenuItem } from 'twenty-ui/navigation';
import {
CommandMenuContextChip,
CommandMenuContextChipProps,
} from './CommandMenuContextChip';
import { MenuItem } from 'twenty-ui/navigation';
export const CommandMenuContextChipGroups = ({
contextChips,
@ -53,19 +54,23 @@ export const CommandMenuContextChipGroups = ({
/>
}
dropdownComponents={
<DropdownMenuItemsContainer>
{firstChips.map((chip, index) => (
<MenuItem
key={index}
LeftComponent={chip.Icons}
text={chip.text}
onClick={() => {
closeDropdown(COMMAND_MENU_CONTEXT_CHIP_GROUPS_DROPDOWN_ID);
chip.onClick?.();
}}
/>
))}
</DropdownMenuItemsContainer>
<DropdownContent>
<DropdownMenuItemsContainer>
{firstChips.map((chip, index) => (
<MenuItem
key={index}
LeftComponent={chip.Icons}
text={chip.text}
onClick={() => {
closeDropdown(
COMMAND_MENU_CONTEXT_CHIP_GROUPS_DROPDOWN_ID,
);
chip.onClick?.();
}}
/>
))}
</DropdownMenuItemsContainer>
</DropdownContent>
}
dropdownHotkeyScope={{
scope: AppHotkeyScope.CommandMenu,

View File

@ -1,6 +1,8 @@
import { FavoriteFolderHotkeyScope } from '@/favorites/constants/FavoriteFolderRightIconDropdownHotkeyScope';
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 { IconDotsVertical, IconPencil, IconTrash } from 'twenty-ui/display';
import { LightIconButton } from 'twenty-ui/input';
import { MenuItem } from 'twenty-ui/navigation';
@ -40,20 +42,22 @@ export const FavoriteFolderNavigationDrawerItemDropdown = ({
}
dropdownPlacement="bottom-start"
dropdownComponents={
<DropdownMenuItemsContainer>
<MenuItem
LeftIcon={IconPencil}
onClick={handleRename}
accent="default"
text="Rename"
/>
<MenuItem
LeftIcon={IconTrash}
onClick={handleDelete}
accent="danger"
text="Delete"
/>
</DropdownMenuItemsContainer>
<DropdownContent widthInPixels={GenericDropdownContentWidth.Narrow}>
<DropdownMenuItemsContainer>
<MenuItem
LeftIcon={IconPencil}
onClick={handleRename}
accent="default"
text="Rename"
/>
<MenuItem
LeftIcon={IconTrash}
onClick={handleDelete}
accent="danger"
text="Delete"
/>
</DropdownMenuItemsContainer>
</DropdownContent>
}
/>
);

View File

@ -1,50 +0,0 @@
import { PageFavoriteButton } from '@/favorites/components/PageFavoriteButton';
import { FavoriteFolderPicker } from '@/favorites/favorite-folder-picker/components/FavoriteFolderPicker';
import { FavoriteFolderPickerEffect } from '@/favorites/favorite-folder-picker/components/FavoriteFolderPickerEffect';
import { FavoriteFolderPickerComponentInstanceContext } from '@/favorites/favorite-folder-picker/scopes/FavoriteFolderPickerScope';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
type PageFavoriteFoldersDropdownProps = {
dropdownId: string;
isFavorite: boolean;
record?: ObjectRecord;
objectNameSingular: string;
};
export const PageFavoriteFoldersDropdown = ({
dropdownId,
isFavorite,
record,
objectNameSingular,
}: PageFavoriteFoldersDropdownProps) => {
const { closeDropdown } = useDropdown(dropdownId);
return (
<FavoriteFolderPickerComponentInstanceContext
favoriteFoldersScopeId={dropdownId}
>
<DropdownScope dropdownScopeId={dropdownId}>
<Dropdown
dropdownId={dropdownId}
dropdownPlacement="bottom-start"
clickableComponent={<PageFavoriteButton isFavorite={isFavorite} />}
dropdownComponents={
<>
<FavoriteFolderPickerEffect record={record} />
<FavoriteFolderPicker
onSubmit={closeDropdown}
record={record}
objectNameSingular={objectNameSingular}
dropdownId={dropdownId}
/>
</>
}
dropdownHotkeyScope={{ scope: dropdownId }}
/>
</DropdownScope>
</FavoriteFolderPickerComponentInstanceContext>
);
};

View File

@ -6,7 +6,7 @@ import { FavoriteFolderPickerInstanceContext } from '@/favorites/favorite-folder
import { favoriteFolderSearchFilterComponentState } from '@/favorites/favorite-folder-picker/states/favoriteFoldersSearchFilterComponentState';
import { isFavoriteFolderCreatingState } from '@/favorites/states/isFavoriteFolderCreatingState';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
@ -90,7 +90,7 @@ export const FavoriteFolderPicker = ({
);
return (
<DropdownMenu data-select-disable>
<DropdownContent>
<FavoriteFolderPickerSearchInput />
<DropdownMenuSeparator />
<DropdownMenuItemsContainer hasMaxHeight>
@ -101,6 +101,6 @@ export const FavoriteFolderPicker = ({
</DropdownMenuItemsContainer>
<DropdownMenuSeparator />
<FavoriteFolderPickerFooter dropdownId={dropdownId} />
</DropdownMenu>
</DropdownContent>
);
};

View File

@ -11,6 +11,7 @@ import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
import { getDefaultSubFieldNameForCompositeFilterableFieldType } from '@/object-record/record-filter/utils/getDefaultSubFieldNameForCompositeFilterableFieldType';
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
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 { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly';
@ -151,20 +152,22 @@ export const AdvancedFilterAddFilterRuleSelect = ({
<LightButton Icon={IconPlus} title="Add filter rule" />
}
dropdownComponents={
<DropdownMenuItemsContainer>
<MenuItem
LeftIcon={IconPlus}
text="Add rule"
onClick={handleAddFilter}
/>
{isFilterRuleGroupOptionVisible && (
<DropdownContent>
<DropdownMenuItemsContainer>
<MenuItem
LeftIcon={IconLibraryPlus}
text="Add rule group"
onClick={handleAddFilterGroup}
LeftIcon={IconPlus}
text="Add rule"
onClick={handleAddFilter}
/>
)}
</DropdownMenuItemsContainer>
{isFilterRuleGroupOptionVisible && (
<MenuItem
LeftIcon={IconLibraryPlus}
text="Add rule group"
onClick={handleAddFilterGroup}
/>
)}
</DropdownMenuItemsContainer>
</DropdownContent>
}
dropdownHotkeyScope={{ scope: dropdownId }}
dropdownOffset={{ y: 8, x: 0 }}

View File

@ -16,6 +16,7 @@ import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object
import { isExpectedSubFieldName } from '@/object-record/object-filter-dropdown/utils/isExpectedSubFieldName';
import { isFilterOnActorSourceSubField } from '@/object-record/object-filter-dropdown/utils/isFilterOnActorSourceSubField';
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { FieldMetadataType } from 'twenty-shared/types';
@ -52,28 +53,24 @@ export const AdvancedFilterDropdownFilterInput = ({
<ObjectFilterDropdownDateInput />
)}
{filterType === 'RELATION' && (
<>
<DropdownContent>
<ObjectFilterDropdownSearchInput />
<DropdownMenuSeparator />
<ObjectFilterDropdownRecordSelect recordFilterId={recordFilter.id} />
</>
</DropdownContent>
)}
{filterType === 'ACTOR' &&
(isActorSourceCompositeFilter ? (
<>
<ObjectFilterDropdownSourceSelect />
</>
<ObjectFilterDropdownSourceSelect />
) : (
<>
<ObjectFilterDropdownTextInput />
</>
<ObjectFilterDropdownTextInput />
))}
{['SELECT', 'MULTI_SELECT'].includes(filterType) && (
<>
<DropdownContent>
<ObjectFilterDropdownSearchInput />
<DropdownMenuSeparator />
<ObjectFilterDropdownOptionSelect />
</>
</DropdownContent>
)}
{filterType === 'BOOLEAN' && <ObjectFilterDropdownBooleanSelect />}
{filterType === 'CURRENCY' &&
@ -82,9 +79,7 @@ export const AdvancedFilterDropdownFilterInput = ({
'currencyCode',
recordFilter.subFieldName,
) ? (
<>
<ObjectFilterDropdownCurrencySelect />
</>
<ObjectFilterDropdownCurrencySelect />
) : (
<></>
))}

View File

@ -21,6 +21,7 @@ import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/o
import { objectFilterDropdownSubMenuFieldTypeComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSubMenuFieldTypeComponentState';
import { isCompositeFieldType } from '@/object-record/object-filter-dropdown/utils/isCompositeFieldType';
import { useFilterableFieldMetadataItemsInRecordIndexContext } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItemsInRecordIndexContext';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
@ -132,7 +133,7 @@ export const AdvancedFilterFieldSelectMenu = ({
];
return (
<>
<DropdownContent>
<AdvancedFilterFieldSelectSearchInput />
<SelectableList
hotkeyScope={advancedFilterFieldSelectDropdownId}
@ -175,6 +176,6 @@ export const AdvancedFilterFieldSelectMenu = ({
)}
</DropdownMenuItemsContainer>
</SelectableList>
</>
</DropdownContent>
);
};

View File

@ -4,6 +4,7 @@ import { useUpsertRecordFilterGroup } from '@/object-record/record-filter-group/
import { RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup';
import { RecordFilterGroupLogicalOperator } from '@/object-record/record-filter-group/types/RecordFilterGroupLogicalOperator';
import { Select } from '@/ui/input/components/Select';
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
type AdvancedFilterLogicalOperatorDropdownProps = {
recordFilterGroup: RecordFilterGroup;
@ -27,6 +28,7 @@ export const AdvancedFilterLogicalOperatorDropdown = ({
return (
<Select
fullWidth
dropdownWidth={GenericDropdownContentWidth.Narrow}
dropdownId={`advanced-filter-logical-operator-${recordFilterGroup.id}`}
value={recordFilterGroup.logicalOperator}
onChange={handleChange}

View File

@ -4,10 +4,11 @@ import { useRemoveRootRecordFilterGroupIfEmpty } from '@/object-record/record-fi
import { useRemoveRecordFilter } from '@/object-record/record-filter/hooks/useRemoveRecordFilter';
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 { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { IconButton } from 'twenty-ui/input';
import { IconDotsVertical, IconTrash } from 'twenty-ui/display';
import { IconButton } from 'twenty-ui/input';
import { MenuItem } from 'twenty-ui/navigation';
type AdvancedFilterRecordFilterGroupOptionsDropdownProps = {
@ -53,14 +54,16 @@ export const AdvancedFilterRecordFilterGroupOptionsDropdown = ({
/>
}
dropdownComponents={
<DropdownMenuItemsContainer>
<MenuItem
text="Remove rule group"
onClick={handleRemove}
LeftIcon={IconTrash}
accent="danger"
/>
</DropdownMenuItemsContainer>
<DropdownContent>
<DropdownMenuItemsContainer>
<MenuItem
text="Remove rule group"
onClick={handleRemove}
LeftIcon={IconTrash}
accent="danger"
/>
</DropdownMenuItemsContainer>
</DropdownContent>
}
dropdownHotkeyScope={{ scope: dropdownId }}
dropdownOffset={{ y: 2, x: 0 }}

View File

@ -6,7 +6,9 @@ import { currentRecordFiltersComponentState } from '@/object-record/record-filte
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
import { SelectControl } from '@/ui/input/components/SelectControl';
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 { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
@ -94,38 +96,39 @@ export const AdvancedFilterRecordFilterOperandSelect = ({
/>
}
dropdownComponents={
<DropdownMenuItemsContainer width="auto">
<SelectableList
hotkeyScope={dropdownId}
selectableItemIdArray={operandsForFilterType.map(
(operand) => operand,
)}
selectableListInstanceId={dropdownId}
>
{operandsForFilterType.map((filterOperand, index) => (
<SelectableListItem
itemId={filterOperand}
key={`select-filter-operand-${index}`}
onEnter={() => {
handleOperandChange(filterOperand);
}}
>
<MenuItem
focused={selectedItemId === filterOperand}
onClick={() => {
<DropdownContent widthInPixels={GenericDropdownContentWidth.Narrow}>
<DropdownMenuItemsContainer>
<SelectableList
hotkeyScope={dropdownId}
selectableItemIdArray={operandsForFilterType.map(
(operand) => operand,
)}
selectableListInstanceId={dropdownId}
>
{operandsForFilterType.map((filterOperand, index) => (
<SelectableListItem
itemId={filterOperand}
key={`select-filter-operand-${index}`}
onEnter={() => {
handleOperandChange(filterOperand);
}}
text={getOperandLabel(filterOperand)}
/>
</SelectableListItem>
))}
</SelectableList>
</DropdownMenuItemsContainer>
>
<MenuItem
focused={selectedItemId === filterOperand}
onClick={() => {
handleOperandChange(filterOperand);
}}
text={getOperandLabel(filterOperand)}
/>
</SelectableListItem>
))}
</SelectableList>
</DropdownMenuItemsContainer>
</DropdownContent>
}
dropdownHotkeyScope={{ scope: dropdownId }}
dropdownOffset={DEFAULT_ADVANCED_FILTER_DROPDOWN_OFFSET}
dropdownPlacement="bottom-start"
dropdownWidth={200}
/>
</StyledContainer>
);

View File

@ -7,12 +7,13 @@ import { currentRecordFiltersComponentState } from '@/object-record/record-filte
import { DEFAULT_ADVANCED_FILTER_DROPDOWN_OFFSET } from '@/object-record/advanced-filter/constants/DefaultAdvancedFilterDropdownOffset';
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 { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { isDefined } from 'twenty-shared/utils';
import { IconButton } from 'twenty-ui/input';
import { IconDotsVertical, IconTrash } from 'twenty-ui/display';
import { IconButton } from 'twenty-ui/input';
import { MenuItem } from 'twenty-ui/navigation';
type AdvancedFilterRecordFilterOptionsDropdownProps = {
@ -73,14 +74,16 @@ export const AdvancedFilterRecordFilterOptionsDropdown = ({
/>
}
dropdownComponents={
<DropdownMenuItemsContainer>
<MenuItem
text="Remove rule"
onClick={handleRemove}
LeftIcon={IconTrash}
accent="danger"
/>
</DropdownMenuItemsContainer>
<DropdownContent>
<DropdownMenuItemsContainer>
<MenuItem
text="Remove rule"
onClick={handleRemove}
LeftIcon={IconTrash}
accent="danger"
/>
</DropdownMenuItemsContainer>
</DropdownContent>
}
dropdownHotkeyScope={{ scope: dropdownId }}
dropdownOffset={DEFAULT_ADVANCED_FILTER_DROPDOWN_OFFSET}

View File

@ -2,9 +2,11 @@ import { AdvancedFilterAddFilterRuleSelect } from '@/object-record/advanced-filt
import { AdvancedFilterRecordFilterGroupRow } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterGroupRow';
import { AdvancedFilterRecordFilterRow } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterRow';
import { ADVANCED_FILTER_DROPDOWN_CONTENT_WIDTH } from '@/object-record/advanced-filter/constants/AdvancedFilterDropdownContentWidth';
import { useChildRecordFiltersAndRecordFilterGroups } from '@/object-record/advanced-filter/hooks/useChildRecordFiltersAndRecordFilterGroups';
import { rootLevelRecordFilterGroupComponentSelector } from '@/object-record/advanced-filter/states/rootLevelRecordFilterGroupComponentSelector';
import { isRecordFilterGroupChildARecordFilterGroup } from '@/object-record/advanced-filter/utils/isRecordFilterGroupChildARecordFilterGroup';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import styled from '@emotion/styled';
@ -18,7 +20,6 @@ const StyledContainer = styled.div<{ isGrayBackground?: boolean }>`
flex-direction: column;
gap: ${({ theme }) => theme.spacing(2)};
padding: ${({ theme }) => theme.spacing(2)};
min-width: 650px;
`;
export const AdvancedFilterRootRecordFilterGroup = () => {
@ -37,31 +38,33 @@ export const AdvancedFilterRootRecordFilterGroup = () => {
return (
<ScrollWrapper componentInstanceId={`scroll-wrapper-dropdown-menu-${id}`}>
<StyledContainer>
{childRecordFiltersAndRecordFilterGroups.map(
(recordFilterGroupChild, recordFilterGroupChildIndex) =>
isRecordFilterGroupChildARecordFilterGroup(
recordFilterGroupChild,
) ? (
<AdvancedFilterRecordFilterGroupRow
key={recordFilterGroupChild.id}
parentRecordFilterGroup={rootRecordFilterGroup}
recordFilterGroup={recordFilterGroupChild}
recordFilterGroupIndex={recordFilterGroupChildIndex}
/>
) : (
<AdvancedFilterRecordFilterRow
key={recordFilterGroupChild.id}
recordFilterGroup={rootRecordFilterGroup}
recordFilter={recordFilterGroupChild}
recordFilterIndex={recordFilterGroupChildIndex}
/>
),
)}
<AdvancedFilterAddFilterRuleSelect
recordFilterGroup={rootRecordFilterGroup}
/>
</StyledContainer>
<DropdownContent widthInPixels={ADVANCED_FILTER_DROPDOWN_CONTENT_WIDTH}>
<StyledContainer>
{childRecordFiltersAndRecordFilterGroups.map(
(recordFilterGroupChild, recordFilterGroupChildIndex) =>
isRecordFilterGroupChildARecordFilterGroup(
recordFilterGroupChild,
) ? (
<AdvancedFilterRecordFilterGroupRow
key={recordFilterGroupChild.id}
parentRecordFilterGroup={rootRecordFilterGroup}
recordFilterGroup={recordFilterGroupChild}
recordFilterGroupIndex={recordFilterGroupChildIndex}
/>
) : (
<AdvancedFilterRecordFilterRow
key={recordFilterGroupChild.id}
recordFilterGroup={rootRecordFilterGroup}
recordFilter={recordFilterGroupChild}
recordFilterIndex={recordFilterGroupChildIndex}
/>
),
)}
<AdvancedFilterAddFilterRuleSelect
recordFilterGroup={rootRecordFilterGroup}
/>
</StyledContainer>
</DropdownContent>
</ScrollWrapper>
);
};

View File

@ -16,6 +16,7 @@ import { areCompositeTypeSubFieldsFilterable } from '@/object-record/record-filt
import { isCompositeTypeFilterableByAnySubField } from '@/object-record/record-filter/utils/isCompositeTypeFilterableByAnySubField';
import { SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS } from '@/settings/data-model/constants/SettingsCompositeFieldTypeConfigs';
import { CompositeFieldSubFieldName } from '@/settings/data-model/types/CompositeFieldSubFieldName';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
@ -110,7 +111,7 @@ export const AdvancedFilterSubFieldSelectMenu = ({
];
return (
<>
<DropdownContent>
<DropdownMenuHeader
StartComponent={
<DropdownMenuHeaderLeftComponent
@ -184,6 +185,6 @@ export const AdvancedFilterSubFieldSelectMenu = ({
))}
</SelectableList>
</DropdownMenuItemsContainer>
</>
</DropdownContent>
);
};

View File

@ -133,7 +133,6 @@ export const AdvancedFilterValueInput = ({
dropdownHotkeyScope={{ scope: dropdownId }}
dropdownOffset={dropdownContentOffset}
dropdownPlacement="bottom-start"
dropdownWidth={280}
onClose={handleFilterValueDropdownClose}
onOpen={handleFilterValueDropdownOpen}
/>

View File

@ -0,0 +1 @@
export const ADVANCED_FILTER_DROPDOWN_CONTENT_WIDTH = 650;

View File

@ -5,6 +5,7 @@ import { useApplyObjectFilterDropdownFilterValue } from '@/object-record/object-
import { useObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useObjectFilterDropdownFilterValue';
import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope';
import { BooleanDisplay } from '@/ui/field/display/components/BooleanDisplay';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
@ -51,27 +52,29 @@ export const ObjectFilterDropdownBooleanSelect = () => {
};
return (
<SelectableList
selectableListInstanceId="boolean-select"
selectableItemIdArray={options.map((option) => option.toString())}
hotkeyScope={SingleRecordPickerHotkeyScope.SingleRecordPicker}
>
<DropdownMenuItemsContainer hasMaxHeight width="auto">
{options.map((option) => (
<StyledBooleanSelectContainer
key={String(option)}
onClick={() => handleOptionSelect(option)}
selected={objectFilterDropdownFilterValue === option.toString()}
>
<BooleanDisplay value={option} />
{objectFilterDropdownFilterValue === option.toString() && (
<StyledIconCheckContainer>
<IconCheck color={theme.grayScale.gray50} size={16} />
</StyledIconCheckContainer>
)}
</StyledBooleanSelectContainer>
))}
</DropdownMenuItemsContainer>
</SelectableList>
<DropdownContent>
<SelectableList
selectableListInstanceId="boolean-select"
selectableItemIdArray={options.map((option) => option.toString())}
hotkeyScope={SingleRecordPickerHotkeyScope.SingleRecordPicker}
>
<DropdownMenuItemsContainer hasMaxHeight width="auto">
{options.map((option) => (
<StyledBooleanSelectContainer
key={String(option)}
onClick={() => handleOptionSelect(option)}
selected={objectFilterDropdownFilterValue === option.toString()}
>
<BooleanDisplay value={option} />
{objectFilterDropdownFilterValue === option.toString() && (
<StyledIconCheckContainer>
<IconCheck color={theme.grayScale.gray50} size={16} />
</StyledIconCheckContainer>
)}
</StyledBooleanSelectContainer>
))}
</DropdownMenuItemsContainer>
</SelectableList>
</DropdownContent>
);
};

View File

@ -5,6 +5,7 @@ import { getCountryFlagMenuItemAvatar } from '@/object-record/object-filter-drop
import { turnCountryIntoSelectableItem } from '@/object-record/object-filter-dropdown/utils/turnCountryIntoSelectableItem';
import { SelectableItem } from '@/object-record/select/types/SelectableItem';
import { useCountries } from '@/ui/input/components/internal/hooks/useCountries';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
@ -18,13 +19,7 @@ import { MenuItem, MenuItemMultiSelectAvatar } from 'twenty-ui/navigation';
export const EMPTY_FILTER_VALUE = '[]';
export const MAX_ITEMS_TO_DISPLAY = 5;
type ObjectFilterDropdownCountrySelectProps = {
dropdownWidth?: number;
};
export const ObjectFilterDropdownCountrySelect = ({
dropdownWidth,
}: ObjectFilterDropdownCountrySelectProps) => {
export const ObjectFilterDropdownCountrySelect = () => {
const [searchText, setSearchText] = useState('');
const objectFilterDropdownCurrentRecordFilter = useRecoilComponentValueV2(
@ -101,7 +96,7 @@ export const ObjectFilterDropdownCountrySelect = ({
const { t } = useLingui();
return (
<>
<DropdownContent>
<DropdownMenuSearchInput
autoFocus
type="text"
@ -112,7 +107,7 @@ export const ObjectFilterDropdownCountrySelect = ({
}}
/>
<DropdownMenuSeparator />
<DropdownMenuItemsContainer hasMaxHeight width={dropdownWidth ?? 200}>
<DropdownMenuItemsContainer hasMaxHeight>
{filteredSelectedItems?.map((item) => {
return (
<MenuItemMultiSelectAvatar
@ -141,6 +136,6 @@ export const ObjectFilterDropdownCountrySelect = ({
})}
{showNoResult && <MenuItem text={t`No results`} />}
</DropdownMenuItemsContainer>
</>
</DropdownContent>
);
};

View File

@ -5,9 +5,11 @@ import { turnCurrencyIntoSelectableItem } from '@/object-record/object-filter-dr
import { StyledMultipleSelectDropdownAvatarChip } from '@/object-record/select/components/StyledMultipleSelectDropdownAvatarChip';
import { SelectableItem } from '@/object-record/select/types/SelectableItem';
import { CURRENCIES } from '@/settings/data-model/constants/Currencies';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useLingui } from '@lingui/react/macro';
import { isNonEmptyString } from '@sniptt/guards';
@ -95,7 +97,7 @@ export const ObjectFilterDropdownCurrencySelect = () => {
searchText !== '';
return (
<>
<DropdownContent widthInPixels={GenericDropdownContentWidth.Large}>
<DropdownMenuSearchInput
autoFocus
type="text"
@ -153,6 +155,6 @@ export const ObjectFilterDropdownCurrencySelect = () => {
})}
{showNoResult && <MenuItem text={t`No results`} />}
</DropdownMenuItemsContainer>
</>
</DropdownContent>
);
};

View File

@ -9,17 +9,16 @@ import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { ObjectFilterDropdownBooleanSelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownBooleanSelect';
import { ObjectFilterDropdownCurrencySelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownCurrencySelect';
import { ObjectFilterDropdownOperandDropdown } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownOperandDropdown';
import { ObjectFilterDropdownTextInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownTextInput';
import { DATE_FILTER_TYPES } from '@/object-record/object-filter-dropdown/constants/DateFilterTypes';
import { DATE_PICKER_DROPDOWN_CONTENT_WIDTH } from '@/object-record/object-filter-dropdown/constants/DatePickerDropdownContentWidth';
import { NUMBER_FILTER_TYPES } from '@/object-record/object-filter-dropdown/constants/NumberFilterTypes';
import { TEXT_FILTER_TYPES } from '@/object-record/object-filter-dropdown/constants/TextFilterTypes';
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState';
import { isExpectedSubFieldName } from '@/object-record/object-filter-dropdown/utils/isExpectedSubFieldName';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { FieldMetadataType } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
type ObjectFilterDropdownFilterInputProps = {
@ -36,11 +35,6 @@ export const ObjectFilterDropdownFilterInput = ({
filterDropdownId,
);
const subFieldNameUsedInDropdown = useRecoilComponentValueV2(
subFieldNameUsedInDropdownComponentState,
filterDropdownId,
);
const selectedOperandInDropdown = useRecoilComponentValueV2(
selectedOperandInDropdownComponentState,
filterDropdownId,
@ -69,74 +63,52 @@ export const ObjectFilterDropdownFilterInput = ({
fieldMetadataItemUsedInDropdown.type,
);
const isNotASubFieldFilter = !isDefined(subFieldNameUsedInDropdown);
const isDateFilter = DATE_FILTER_TYPES.includes(filterType);
const isOnlyOperand = !isConfigurable;
return (
<>
{isConfigurable && selectedOperandInDropdown && (
<>
{TEXT_FILTER_TYPES.includes(filterType) && (
<ObjectFilterDropdownTextInput />
)}
{NUMBER_FILTER_TYPES.includes(filterType) && (
<ObjectFilterDropdownNumberInput />
)}
{filterType === 'RATING' && <ObjectFilterDropdownRatingInput />}
{DATE_FILTER_TYPES.includes(filterType) && (
<ObjectFilterDropdownDateInput />
)}
{filterType === 'RELATION' && (
<>
<ObjectFilterDropdownSearchInput />
<DropdownMenuSeparator />
<ObjectFilterDropdownRecordSelect
recordFilterId={recordFilterId}
/>
</>
)}
{filterType === 'ACTOR' && (
<>
<ObjectFilterDropdownTextInput />
</>
)}
{filterType === 'ADDRESS' &&
(isNotASubFieldFilter ? (
<>
<ObjectFilterDropdownTextInput />
</>
) : (
<></>
))}
{filterType === 'CURRENCY' &&
(isExpectedSubFieldName(
FieldMetadataType.CURRENCY,
'currencyCode',
subFieldNameUsedInDropdown,
) ? (
<>
<ObjectFilterDropdownCurrencySelect />
</>
) : isExpectedSubFieldName(
FieldMetadataType.CURRENCY,
'amountMicros',
subFieldNameUsedInDropdown,
) ? (
<>
<ObjectFilterDropdownNumberInput />
</>
) : (
<ObjectFilterDropdownNumberInput />
))}
{['SELECT', 'MULTI_SELECT'].includes(filterType) && (
<>
<ObjectFilterDropdownSearchInput />
<DropdownMenuSeparator />
<ObjectFilterDropdownOptionSelect />
</>
)}
{filterType === 'BOOLEAN' && <ObjectFilterDropdownBooleanSelect />}
</>
)}
</>
);
if (isOnlyOperand) {
return (
<DropdownContent>
<ObjectFilterDropdownOperandDropdown />
</DropdownContent>
);
} else if (isDateFilter) {
return (
<DropdownContent widthInPixels={DATE_PICKER_DROPDOWN_CONTENT_WIDTH}>
<ObjectFilterDropdownOperandDropdown />
<ObjectFilterDropdownDateInput />
</DropdownContent>
);
} else {
return (
<DropdownContent>
<ObjectFilterDropdownOperandDropdown />
{TEXT_FILTER_TYPES.includes(filterType) && (
<ObjectFilterDropdownTextInput />
)}
{NUMBER_FILTER_TYPES.includes(filterType) && (
<ObjectFilterDropdownNumberInput />
)}
{filterType === 'RATING' && <ObjectFilterDropdownRatingInput />}
{filterType === 'RELATION' && (
<>
<ObjectFilterDropdownSearchInput />
<DropdownMenuSeparator />
<ObjectFilterDropdownRecordSelect recordFilterId={recordFilterId} />
</>
)}
{filterType === 'ACTOR' && <ObjectFilterDropdownTextInput />}
{filterType === 'ADDRESS' && <ObjectFilterDropdownTextInput />}
{filterType === 'CURRENCY' && <ObjectFilterDropdownNumberInput />}
{['SELECT', 'MULTI_SELECT'].includes(filterType) && (
<>
<ObjectFilterDropdownSearchInput />
<DropdownMenuSeparator />
<ObjectFilterDropdownOptionSelect />
</>
)}
{filterType === 'BOOLEAN' && <ObjectFilterDropdownBooleanSelect />}
</DropdownContent>
);
}
};

View File

@ -5,16 +5,20 @@ import { selectedOperandInDropdownComponentState } from '@/object-record/object-
import { FiltersHotkeyScope } from '@/object-record/object-filter-dropdown/types/FiltersHotkeyScope';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { ClickOutsideListenerContext } from '@/ui/utilities/pointer-event/contexts/ClickOutsideListenerContext';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { getOperandLabel } from '../utils/getOperandLabel';
import { IconChevronDown } from 'twenty-ui/display';
import { getOperandLabel } from '../utils/getOperandLabel';
const StyledDropdownMenuHeader = styled(DropdownMenuHeader)`
cursor: pointer;
`;
export const OPERAND_DROPDOWN_CLICK_OUTSIDE_ID =
'object-filter-dropdown-operand-dropdown-click-outside-id';
export const ObjectFilterDropdownOperandDropdown = ({
filterDropdownId,
}: {
@ -29,21 +33,25 @@ export const ObjectFilterDropdownOperandDropdown = ({
const dropdownId = `${filterDropdownId}-operand-dropdown`;
return (
<Dropdown
dropdownId={dropdownId}
clickableComponent={
<StyledDropdownMenuHeader
key={'selected-filter-operand'}
EndComponent={<IconChevronDown />}
>
{getOperandLabel(selectedOperandInDropdown)}
</StyledDropdownMenuHeader>
}
dropdownComponents={<ObjectFilterDropdownOperandSelect />}
dropdownHotkeyScope={{
scope: FiltersHotkeyScope.ObjectFilterDropdownOperandDropdown,
}}
dropdownOffset={{ x: parseInt(theme.spacing(2), 10) }}
/>
<ClickOutsideListenerContext.Provider
value={{ excludedClickOutsideId: OPERAND_DROPDOWN_CLICK_OUTSIDE_ID }}
>
<Dropdown
dropdownId={dropdownId}
clickableComponent={
<StyledDropdownMenuHeader
key={'selected-filter-operand'}
EndComponent={<IconChevronDown />}
>
{getOperandLabel(selectedOperandInDropdown)}
</StyledDropdownMenuHeader>
}
dropdownComponents={<ObjectFilterDropdownOperandSelect />}
dropdownHotkeyScope={{
scope: FiltersHotkeyScope.ObjectFilterDropdownOperandDropdown,
}}
dropdownOffset={{ x: parseInt(theme.spacing(2), 10) }}
/>
</ClickOutsideListenerContext.Provider>
);
};

View File

@ -3,6 +3,7 @@ import { useApplyObjectFilterDropdownOperand } from '@/object-record/object-filt
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState';
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
@ -45,17 +46,19 @@ export const ObjectFilterDropdownOperandSelect = () => {
};
return (
<StyledDropdownMenuItemsContainer>
{operandsForFilterType.map((filterOperand, index) => (
<MenuItem
key={`select-filter-operand-${index}`}
onClick={() => {
handleOperandChange(filterOperand);
closeDropdown();
}}
text={getOperandLabel(filterOperand)}
/>
))}
</StyledDropdownMenuItemsContainer>
<DropdownContent>
<StyledDropdownMenuItemsContainer>
{operandsForFilterType.map((filterOperand, index) => (
<MenuItem
key={`select-filter-operand-${index}`}
onClick={() => {
handleOperandChange(filterOperand);
closeDropdown();
}}
text={getOperandLabel(filterOperand)}
/>
))}
</StyledDropdownMenuItemsContainer>
</DropdownContent>
);
};

View File

@ -1,19 +0,0 @@
import { ObjectFilterDropdownFilterInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterInput';
import { ObjectFilterDropdownOperandDropdown } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownOperandDropdown';
type ObjectFilterOperandSelectAndInputProps = {
filterDropdownId?: string;
};
export const ObjectFilterOperandSelectAndInput = ({
filterDropdownId,
}: ObjectFilterOperandSelectAndInputProps) => {
return (
<>
<ObjectFilterDropdownOperandDropdown
filterDropdownId={filterDropdownId}
/>
<ObjectFilterDropdownFilterInput filterDropdownId={filterDropdownId} />
</>
);
};

View File

@ -0,0 +1 @@
export const DATE_PICKER_DROPDOWN_CONTENT_WIDTH = 280;

View File

@ -1,13 +1,14 @@
import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard';
import { useObjectOptionsForTable } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForTable';
import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { ViewFieldsVisibilityDropdownSection } from '@/views/components/ViewFieldsVisibilityDropdownSection';
import { ViewType } from '@/views/types/ViewType';
import { useLingui } from '@lingui/react/macro';
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
import { IconChevronLeft, IconEyeOff } from 'twenty-ui/display';
import { MenuItemNavigate } from 'twenty-ui/navigation';
@ -51,7 +52,7 @@ export const ObjectOptionsDropdownFieldsContent = () => {
: handleColumnVisibilityChange;
return (
<>
<DropdownContent>
<DropdownMenuHeader
StartComponent={
<DropdownMenuHeaderLeftComponent
@ -79,6 +80,6 @@ export const ObjectOptionsDropdownFieldsContent = () => {
text={t`Hidden Fields`}
/>
</DropdownMenuItemsContainer>
</>
</DropdownContent>
);
};

View File

@ -7,17 +7,18 @@ import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdow
import { useObjectOptionsForTable } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForTable';
import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown';
import { SettingsPath } from '@/types/SettingsPath';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
import { ViewFieldsVisibilityDropdownSection } from '@/views/components/ViewFieldsVisibilityDropdownSection';
import { ViewType } from '@/views/types/ViewType';
import { useLingui } from '@lingui/react/macro';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
import { IconChevronLeft, IconSettings } from 'twenty-ui/display';
import { MenuItem, UndecoratedLink } from 'twenty-ui/navigation';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
export const ObjectOptionsDropdownHiddenFieldsContent = () => {
const { t } = useLingui();
@ -61,7 +62,7 @@ export const ObjectOptionsDropdownHiddenFieldsContent = () => {
);
return (
<>
<DropdownContent>
<DropdownMenuHeader
StartComponent={
<DropdownMenuHeaderLeftComponent
@ -94,6 +95,6 @@ export const ObjectOptionsDropdownHiddenFieldsContent = () => {
<MenuItem LeftIcon={IconSettings} text={t`Edit Fields`} />
</DropdownMenuItemsContainer>
</UndecoratedLink>
</>
</DropdownContent>
);
};

View File

@ -8,7 +8,9 @@ import { useRecordGroupVisibility } from '@/object-record/record-group/hooks/use
import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
import { hiddenRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/hiddenRecordGroupIdsComponentSelector';
import { SettingsPath } from '@/types/SettingsPath';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
@ -16,10 +18,9 @@ import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/
import { useLingui } from '@lingui/react/macro';
import { useLocation } from 'react-router-dom';
import { useSetRecoilState } from 'recoil';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
import { IconChevronLeft, IconSettings } from 'twenty-ui/display';
import { MenuItem, UndecoratedLink } from 'twenty-ui/navigation';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
export const ObjectOptionsDropdownHiddenRecordGroupsContent = () => {
const { t } = useLingui();
@ -68,20 +69,17 @@ export const ObjectOptionsDropdownHiddenRecordGroupsContent = () => {
}, [hiddenRecordGroupIds, currentContentId, onContentChange]);
return (
<>
<DropdownMenuItemsContainer>
<DropdownMenuHeader
StartComponent={
<DropdownMenuHeaderLeftComponent
onClick={() => onContentChange('recordGroups')}
Icon={IconChevronLeft}
/>
}
>
Hidden {recordGroupFieldMetadata?.label}
</DropdownMenuHeader>
</DropdownMenuItemsContainer>
<DropdownContent>
<DropdownMenuHeader
StartComponent={
<DropdownMenuHeaderLeftComponent
onClick={() => onContentChange('recordGroups')}
Icon={IconChevronLeft}
/>
}
>
Hidden {recordGroupFieldMetadata?.label}
</DropdownMenuHeader>
<RecordGroupsVisibilityDropdownSection
title={`Hidden ${recordGroupFieldMetadata?.label}`}
recordGroupIds={hiddenRecordGroupIds}
@ -102,6 +100,6 @@ export const ObjectOptionsDropdownHiddenRecordGroupsContent = () => {
<MenuItem LeftIcon={IconSettings} text={t`Edit field values`} />
</DropdownMenuItemsContainer>
</UndecoratedLink>
</>
</DropdownContent>
);
};

View File

@ -5,6 +5,7 @@ import { useSetViewTypeFromLayoutOptionsMenu } from '@/object-record/object-opti
import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState';
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
@ -92,7 +93,7 @@ export const ObjectOptionsDropdownLayoutContent = () => {
);
return (
<>
<DropdownContent>
<DropdownMenuHeader
StartComponent={
<DropdownMenuHeaderLeftComponent
@ -234,6 +235,6 @@ export const ObjectOptionsDropdownLayoutContent = () => {
</SelectableList>
</DropdownMenuItemsContainer>
)}
</>
</DropdownContent>
);
};

View File

@ -3,6 +3,7 @@ import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hook
import { useUpdateObjectViewOptions } from '@/object-record/object-options-dropdown/hooks/useUpdateObjectViewOptions';
import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState';
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
@ -38,7 +39,7 @@ export const ObjectOptionsDropdownLayoutOpenInContent = () => {
];
return (
<>
<DropdownContent>
<DropdownMenuHeader
StartComponent={
<DropdownMenuHeaderLeftComponent
@ -105,6 +106,6 @@ export const ObjectOptionsDropdownLayoutOpenInContent = () => {
</SelectableListItem>
</SelectableList>
</DropdownMenuItemsContainer>
</>
</DropdownContent>
);
};

View File

@ -8,6 +8,7 @@ import { recordGroupFieldMetadataComponentState } from '@/object-record/record-g
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
@ -93,7 +94,7 @@ export const ObjectOptionsDropdownMenuContent = () => {
);
return (
<>
<DropdownContent>
{currentView && (
<ObjectOptionsDropdownMenuViewName currentView={currentView} />
)}
@ -121,7 +122,6 @@ export const ObjectOptionsDropdownMenuContent = () => {
</SelectableListItem>
</DropdownMenuItemsContainer>
<DropdownMenuSeparator />
<DropdownMenuItemsContainer scrollable={false}>
<SelectableListItem
itemId="Fields"
@ -136,7 +136,6 @@ export const ObjectOptionsDropdownMenuContent = () => {
hasSubMenu
/>
</SelectableListItem>
<div id="group-by-menu-item">
<SelectableListItem
itemId="Group"
@ -175,7 +174,6 @@ export const ObjectOptionsDropdownMenuContent = () => {
/>
)}
<DropdownMenuSeparator />
<SelectableListItem
itemId="Copy link to view"
onEnter={() => {
@ -228,6 +226,6 @@ export const ObjectOptionsDropdownMenuContent = () => {
)}
</DropdownMenuItemsContainer>
</SelectableList>
</>
</DropdownContent>
);
};

View File

@ -9,6 +9,7 @@ import { recordGroupFieldMetadataComponentState } from '@/object-record/record-g
import { hiddenRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/hiddenRecordGroupIdsComponentSelector';
import { useHandleRecordGroupField } from '@/object-record/record-index/hooks/useHandleRecordGroupField';
import { SettingsPath } from '@/types/SettingsPath';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
@ -103,7 +104,7 @@ export const ObjectOptionsDropdownRecordGroupFieldsContent = () => {
}, [hiddenRecordGroupIds, currentContentId, onContentChange]);
return (
<>
<DropdownContent>
<DropdownMenuHeader
StartComponent={
<DropdownMenuHeaderLeftComponent
@ -124,6 +125,7 @@ export const ObjectOptionsDropdownRecordGroupFieldsContent = () => {
placeholder={t`Search fields`}
onChange={(event) => setRecordGroupFieldSearchInput(event.target.value)}
/>
<DropdownMenuSeparator />
<DropdownMenuItemsContainer>
{viewType === ViewType.Table && (
<MenuItemSelect
@ -154,6 +156,6 @@ export const ObjectOptionsDropdownRecordGroupFieldsContent = () => {
<MenuItem LeftIcon={IconSettings} text={t`Create select field`} />
</UndecoratedLink>
</DropdownMenuItemsContainer>
</>
</DropdownContent>
);
};

View File

@ -6,6 +6,7 @@ import { hiddenRecordGroupIdsComponentSelector } from '@/object-record/record-gr
import { RecordGroupSort } from '@/object-record/record-group/types/RecordGroupSort';
import { recordIndexRecordGroupSortComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupSortComponentState';
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
@ -58,7 +59,7 @@ export const ObjectOptionsDropdownRecordGroupSortContent = () => {
];
return (
<>
<DropdownContent>
<DropdownMenuHeader
StartComponent={
<DropdownMenuHeaderLeftComponent
@ -123,6 +124,6 @@ export const ObjectOptionsDropdownRecordGroupSortContent = () => {
</SelectableListItem>
</SelectableList>
</DropdownMenuItemsContainer>
</>
</DropdownContent>
);
};

View File

@ -10,6 +10,7 @@ import { visibleRecordGroupIdsComponentFamilySelector } from '@/object-record/re
import { recordIndexRecordGroupHideComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordGroupHideComponentFamilyState';
import { recordIndexRecordGroupSortComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupSortComponentState';
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
@ -94,7 +95,7 @@ export const ObjectOptionsDropdownRecordGroupsContent = () => {
];
return (
<>
<DropdownContent>
<DropdownMenuHeader
StartComponent={
<DropdownMenuHeaderLeftComponent
@ -192,6 +193,6 @@ export const ObjectOptionsDropdownRecordGroupsContent = () => {
</DropdownMenuItemsContainer>
</>
)}
</>
</DropdownContent>
);
};

View File

@ -10,19 +10,19 @@ import { RecordFiltersComponentInstanceContext } from '@/object-record/record-fi
import { RecordIndexContextProvider } from '@/object-record/record-index/contexts/RecordIndexContext';
import { RecordSortsComponentInstanceContext } from '@/object-record/record-sort/states/context/RecordSortsComponentInstanceContext';
import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext';
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
import { ViewType } from '@/views/types/ViewType';
import { useEffect } from 'react';
import { MemoryRouter } from 'react-router-dom';
import { useSetRecoilState } from 'recoil';
import { ComponentDecorator } from 'twenty-ui/testing';
import { ContextStoreDecorator } from '~/testing/decorators/ContextStoreDecorator';
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
import { IconsProviderDecorator } from '~/testing/decorators/IconsProviderDecorator';
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
import { ComponentDecorator } from 'twenty-ui/testing';
const instanceId = 'entity-options-scope';
@ -111,9 +111,9 @@ const createStory = (contentId: ObjectOptionsContentId | null): Story => ({
dropdownId: OBJECT_OPTIONS_DROPDOWN_ID,
}}
>
<DropdownMenu>
<DropdownContent>
<Story />
</DropdownMenu>
</DropdownContent>
</ObjectOptionsDropdownContext.Provider>
</RecordIndexContextProvider>
);

View File

@ -19,6 +19,7 @@ import {
import { hiddenTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/hiddenTableColumnsComponentSelector';
import { visibleTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/visibleTableColumnsComponentSelector';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
@ -232,88 +233,91 @@ export const ObjectSortDropdownButton = ({
</StyledHeaderDropdownButton>
}
dropdownComponents={
<SelectableList
selectableListInstanceId={OBJECT_SORT_DROPDOWN_ID}
hotkeyScope={hotkeyScope.scope}
selectableItemIdArray={selectableItemIdArray}
>
{isRecordSortDirectionMenuUnfolded && (
<StyledSelectedSortDirectionContainer>
<DropdownMenuItemsContainer>
{RECORD_SORT_DIRECTIONS.map((sortDirection, index) => (
<MenuItem
key={index}
focused={selectedItemId === sortDirection}
onClick={() => handleSortDirectionClick(sortDirection)}
text={
sortDirection === 'asc' ? t`Ascending` : t`Descending`
}
/>
))}
</DropdownMenuItemsContainer>
</StyledSelectedSortDirectionContainer>
)}
<DropdownMenuHeader
onClick={() =>
setIsRecordSortDirectionMenuUnfolded(
!isRecordSortDirectionMenuUnfolded,
)
}
EndComponent={
<StyledDropdownMenuHeaderEndComponent>
<IconChevronDown size={theme.icon.size.md} />
</StyledDropdownMenuHeaderEndComponent>
}
<DropdownContent>
<SelectableList
selectableListInstanceId={OBJECT_SORT_DROPDOWN_ID}
hotkeyScope={hotkeyScope.scope}
selectableItemIdArray={selectableItemIdArray}
>
{selectedRecordSortDirection === 'asc'
? t`Ascending`
: t`Descending`}
</DropdownMenuHeader>
<DropdownMenuSeparator />
<StyledInput
autoFocus
value={objectSortDropdownSearchInput}
placeholder={t`Search fields`}
onChange={(event) =>
setObjectSortDropdownSearchInput(event.target.value)
}
/>
<DropdownMenuItemsContainer scrollable={false}>
{visibleFieldMetadataItems.map(
(visibleFieldMetadataItem, index) => (
<SelectableListItem
key={visibleFieldMetadataItem.id}
itemId={visibleFieldMetadataItem.id}
onEnter={() => handleAddSort(visibleFieldMetadataItem)}
>
<MenuItem
focused={selectedItemId === visibleFieldMetadataItem.id}
testId={`visible-select-sort-${index}`}
onClick={() => handleAddSort(visibleFieldMetadataItem)}
LeftIcon={getIcon(visibleFieldMetadataItem.icon)}
text={visibleFieldMetadataItem.label}
/>
</SelectableListItem>
),
{isRecordSortDirectionMenuUnfolded && (
<StyledSelectedSortDirectionContainer>
<DropdownMenuItemsContainer>
{RECORD_SORT_DIRECTIONS.map((sortDirection, index) => (
<MenuItem
key={index}
focused={selectedItemId === sortDirection}
onClick={() => handleSortDirectionClick(sortDirection)}
text={
sortDirection === 'asc' ? t`Ascending` : t`Descending`
}
/>
))}
</DropdownMenuItemsContainer>
</StyledSelectedSortDirectionContainer>
)}
{shouldShowSeparator && <DropdownMenuSeparator />}
{hiddenFieldMetadataItems.map((hiddenFieldMetadataItem, index) => (
<SelectableListItem
key={hiddenFieldMetadataItem.id}
itemId={hiddenFieldMetadataItem.id}
onEnter={() => handleAddSort(hiddenFieldMetadataItem)}
>
<MenuItem
focused={selectedItemId === hiddenFieldMetadataItem.id}
testId={`hidden-select-sort-${index}`}
onClick={() => handleAddSort(hiddenFieldMetadataItem)}
LeftIcon={getIcon(hiddenFieldMetadataItem.icon)}
text={hiddenFieldMetadataItem.label}
/>
</SelectableListItem>
))}
</DropdownMenuItemsContainer>
</SelectableList>
<DropdownMenuHeader
onClick={() =>
setIsRecordSortDirectionMenuUnfolded(
!isRecordSortDirectionMenuUnfolded,
)
}
EndComponent={
<StyledDropdownMenuHeaderEndComponent>
<IconChevronDown size={theme.icon.size.md} />
</StyledDropdownMenuHeaderEndComponent>
}
>
{selectedRecordSortDirection === 'asc'
? t`Ascending`
: t`Descending`}
</DropdownMenuHeader>
<StyledInput
autoFocus
value={objectSortDropdownSearchInput}
placeholder={t`Search fields`}
onChange={(event) =>
setObjectSortDropdownSearchInput(event.target.value)
}
/>
<DropdownMenuItemsContainer scrollable={false}>
{visibleFieldMetadataItems.map(
(visibleFieldMetadataItem, index) => (
<SelectableListItem
key={visibleFieldMetadataItem.id}
itemId={visibleFieldMetadataItem.id}
onEnter={() => handleAddSort(visibleFieldMetadataItem)}
>
<MenuItem
focused={selectedItemId === visibleFieldMetadataItem.id}
testId={`visible-select-sort-${index}`}
onClick={() => handleAddSort(visibleFieldMetadataItem)}
LeftIcon={getIcon(visibleFieldMetadataItem.icon)}
text={visibleFieldMetadataItem.label}
/>
</SelectableListItem>
),
)}
{shouldShowSeparator && <DropdownMenuSeparator />}
{hiddenFieldMetadataItems.map(
(hiddenFieldMetadataItem, index) => (
<SelectableListItem
key={hiddenFieldMetadataItem.id}
itemId={hiddenFieldMetadataItem.id}
onEnter={() => handleAddSort(hiddenFieldMetadataItem)}
>
<MenuItem
focused={selectedItemId === hiddenFieldMetadataItem.id}
testId={`hidden-select-sort-${index}`}
onClick={() => handleAddSort(hiddenFieldMetadataItem)}
LeftIcon={getIcon(hiddenFieldMetadataItem.icon)}
text={hiddenFieldMetadataItem.label}
/>
</SelectableListItem>
),
)}
</DropdownMenuItemsContainer>
</SelectableList>
</DropdownContent>
}
onClose={handleDropdownButtonClose}
/>

View File

@ -2,7 +2,7 @@ import styled from '@emotion/styled';
import { useCallback, useRef } from 'react';
import { useRecordGroupActions } from '@/object-record/record-group/hooks/useRecordGroupActions';
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { OverlayContainer } from '@/ui/layout/overlay/components/OverlayContainer';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
@ -22,7 +22,6 @@ type RecordBoardColumnDropdownMenuProps = {
stageId: string;
};
// TODO: unify and use Dropdown component
export const RecordBoardColumnDropdownMenu = ({
onClose,
}: RecordBoardColumnDropdownMenuProps) => {
@ -45,7 +44,7 @@ export const RecordBoardColumnDropdownMenu = ({
return (
<StyledMenuContainer ref={boardColumnMenuRef}>
<OverlayContainer>
<DropdownMenu data-select-disable>
<DropdownContent selectDisabled>
<DropdownMenuItemsContainer>
{recordGroupActions.map((action) => (
<MenuItem
@ -59,7 +58,7 @@ export const RecordBoardColumnDropdownMenu = ({
/>
))}
</DropdownMenuItemsContainer>
</DropdownMenu>
</DropdownContent>
</OverlayContainer>
</StyledMenuContainer>
);

View File

@ -1,5 +1,4 @@
import { DROPDOWN_OFFSET_Y } from '@/dropdown/constants/DropdownOffsetY';
import { DROPDOWN_WIDTH } from '@/dropdown/constants/DropdownWidth';
import { useCurrentContentId } from '@/dropdown/hooks/useCurrentContentId';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { RecordBoardColumnHeaderAggregateDropdownComponentInstanceContext } from '@/object-record/record-board/contexts/RecordBoardColumnHeaderAggregateDropdownComponentInstanceContext';
@ -46,7 +45,6 @@ export const RecordBoardColumnHeaderAggregateDropdown = ({
dropdownHotkeyScope={{
scope: RecordBoardColumnHotkeyScope.ColumnHeader,
}}
dropdownWidth={DROPDOWN_WIDTH}
dropdownOffset={{ y: DROPDOWN_OFFSET_Y }}
clickableComponent={
<RecordBoardColumnHeaderAggregateDropdownButton

View File

@ -4,12 +4,13 @@ import { aggregateOperationComponentState } from '@/object-record/record-board/r
import { availableFieldIdsForAggregateOperationComponentState } from '@/object-record/record-board/record-board-column/states/availableFieldIdsForAggregateOperationComponentState';
import { getAggregateOperationLabel } from '@/object-record/record-board/record-board-column/utils/getAggregateOperationLabel';
import { recordIndexKanbanAggregateOperationState } from '@/object-record/record-index/states/recordIndexKanbanAggregateOperationState';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useUpdateViewAggregate } from '@/views/hooks/useUpdateViewAggregate';
import { useRecoilValue } from 'recoil';
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
import { isDefined } from 'twenty-shared/utils';
import {
Icon123,
@ -48,7 +49,7 @@ export const RecordBoardColumnHeaderAggregateDropdownFieldsContent = () => {
if (!isDefined(aggregateOperation)) return <></>;
return (
<>
<DropdownContent>
<DropdownMenuHeader
StartComponent={
<DropdownMenuHeaderLeftComponent
@ -94,6 +95,6 @@ export const RecordBoardColumnHeaderAggregateDropdownFieldsContent = () => {
);
})}
</DropdownMenuItemsContainer>
</>
</DropdownContent>
);
};

View File

@ -7,6 +7,7 @@ import {
} from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownContext';
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useLingui } from '@lingui/react/macro';
@ -29,7 +30,7 @@ export const RecordBoardColumnHeaderAggregateDropdownMenuContent = () => {
);
return (
<>
<DropdownContent>
<DropdownMenuItemsContainer>
<MenuItem
onClick={() => {
@ -60,6 +61,6 @@ export const RecordBoardColumnHeaderAggregateDropdownMenuContent = () => {
hasSubMenu
/>
</DropdownMenuItemsContainer>
</>
</DropdownContent>
);
};

View File

@ -12,7 +12,9 @@ import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/Agg
import { ExtendedAggregateOperations } from '@/object-record/record-table/types/ExtendedAggregateOperations';
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
import { AvailableFieldsForAggregateOperation } from '@/object-record/types/AvailableFieldsForAggregateOperation';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
@ -20,7 +22,6 @@ import { useUpdateViewAggregate } from '@/views/hooks/useUpdateViewAggregate';
import isEmpty from 'lodash.isempty';
import { useRecoilValue } from 'recoil';
import { Key } from 'ts-key-enum';
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
import { IconCheck, IconChevronLeft } from 'twenty-ui/display';
export const RecordBoardColumnHeaderAggregateDropdownOptionsContent = ({
@ -58,7 +59,7 @@ export const RecordBoardColumnHeaderAggregateDropdownOptionsContent = ({
);
return (
<>
<DropdownContent>
<DropdownMenuHeader
StartComponent={
<DropdownMenuHeaderLeftComponent
@ -121,6 +122,6 @@ export const RecordBoardColumnHeaderAggregateDropdownOptionsContent = ({
),
)}
</DropdownMenuItemsContainer>
</>
</DropdownContent>
);
};

View File

@ -7,7 +7,7 @@ import {
} from '@/object-record/record-field/meta-types/input/components/MultiItemBaseInput';
import { FieldInputClickOutsideEvent } from '@/object-record/record-field/types/FieldInputEvent';
import { PhoneRecord } from '@/object-record/record-field/types/FieldMetadata';
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
@ -181,7 +181,7 @@ export const MultiItemFieldInput = <T,>({
};
return (
<DropdownMenu ref={containerRef} width={200}>
<DropdownContent ref={containerRef}>
{!!items.length && (
<>
<DropdownMenuItemsContainer>
@ -232,6 +232,6 @@ export const MultiItemFieldInput = <T,>({
/>
</DropdownMenuItemsContainer>
)}
</DropdownMenu>
</DropdownContent>
);
};

View File

@ -1,3 +1,4 @@
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { MenuItemWithOptionDropdown } from '@/ui/navigation/menu-item/components/MenuItemWithOptionDropdown';
@ -73,26 +74,28 @@ export const MultiItemFieldMenuItem = <T,>({
RightIcon={!isHovered && showPrimaryIcon ? IconBookmark : null}
dropdownId={dropdownId}
dropdownContent={
<DropdownMenuItemsContainer>
{showSetAsPrimaryButton && (
<DropdownContent>
<DropdownMenuItemsContainer>
{showSetAsPrimaryButton && (
<MenuItem
LeftIcon={IconBookmarkPlus}
text="Set as Primary"
onClick={handleSetAsPrimaryClick}
/>
)}
<MenuItem
LeftIcon={IconBookmarkPlus}
text="Set as Primary"
onClick={handleSetAsPrimaryClick}
LeftIcon={IconPencil}
text="Edit"
onClick={handleEditClick}
/>
)}
<MenuItem
LeftIcon={IconPencil}
text="Edit"
onClick={handleEditClick}
/>
<MenuItem
accent="danger"
LeftIcon={IconTrash}
text="Delete"
onClick={handleDeleteClick}
/>
</DropdownMenuItemsContainer>
<MenuItem
accent="danger"
LeftIcon={IconTrash}
text="Delete"
onClick={handleDeleteClick}
/>
</DropdownMenuItemsContainer>
</DropdownContent>
}
/>
);

View File

@ -1,99 +0,0 @@
import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
import { availableRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/availableRecordGroupIdsComponentSelector';
import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition';
import { RecordIndexPageKanbanAddMenuItem } from '@/object-record/record-index/components/RecordIndexPageKanbanAddMenuItem';
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
import { useCreateNewIndexRecord } from '@/object-record/record-table/hooks/useCreateNewIndexRecord';
import { isRecordGroupTableSectionToggledComponentState } from '@/object-record/record-table/record-table-section/states/isRecordGroupTableSectionToggledComponentState';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { useSetActiveDropdownFocusIdAndMemorizePrevious } from '@/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious';
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useRecoilCallback } from 'recoil';
type RecordIndexAddRecordInGroupDropdownProps = {
dropdownId: string;
clickableComponent: React.ReactNode;
};
export const RecordIndexAddRecordInGroupDropdown = ({
dropdownId,
clickableComponent,
}: RecordIndexAddRecordInGroupDropdownProps) => {
const { objectMetadataItem } = useRecordIndexContextOrThrow();
const { setActiveDropdownFocusIdAndMemorizePrevious } =
useSetActiveDropdownFocusIdAndMemorizePrevious();
const recordGroupIds = useRecoilComponentValueV2(
availableRecordGroupIdsComponentSelector,
);
const recordGroupFieldMetadata = useRecoilComponentValueV2(
recordGroupFieldMetadataComponentState,
);
const isRecordGroupTableSectionToggledState =
useRecoilComponentCallbackStateV2(
isRecordGroupTableSectionToggledComponentState,
);
const selectFieldMetadataItem = objectMetadataItem.fields.find(
(field) => field.id === recordGroupFieldMetadata?.id,
);
const { closeDropdown } = useDropdown(dropdownId);
const { createNewIndexRecord } = useCreateNewIndexRecord({
objectMetadataItem,
});
const handleCreateNewTableRecordInGroup = useRecoilCallback(
({ set }) =>
(recordGroup: RecordGroupDefinition) => {
set(isRecordGroupTableSectionToggledState(recordGroup.id), true);
setActiveDropdownFocusIdAndMemorizePrevious(null);
if (!selectFieldMetadataItem) {
return;
}
createNewIndexRecord({
[selectFieldMetadataItem.name]: recordGroup.value,
});
closeDropdown();
},
[
isRecordGroupTableSectionToggledState,
setActiveDropdownFocusIdAndMemorizePrevious,
selectFieldMetadataItem,
createNewIndexRecord,
closeDropdown,
],
);
if (!selectFieldMetadataItem) {
return null;
}
return (
<Dropdown
dropdownWidth="200px"
dropdownPlacement="bottom-start"
clickableComponent={clickableComponent}
dropdownId={dropdownId}
dropdownComponents={
<DropdownMenuItemsContainer>
{recordGroupIds.map((recordGroupId) => (
<RecordIndexPageKanbanAddMenuItem
key={recordGroupId}
columnId={recordGroupId}
onItemClick={handleCreateNewTableRecordInGroup}
/>
))}
</DropdownMenuItemsContainer>
}
dropdownHotkeyScope={{ scope: dropdownId }}
/>
);
};

View File

@ -9,7 +9,7 @@ import { RecordPickerLayoutDirection } from '@/object-record/record-picker/types
import { RecordPickerPickableMorphItem } from '@/object-record/record-picker/types/RecordPickerPickableMorphItem';
import { useHasObjectReadOnlyPermission } from '@/settings/roles/hooks/useHasObjectReadOnlyPermission';
import { CreateNewButton } from '@/ui/input/relation-picker/components/CreateNewButton';
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
@ -110,7 +110,7 @@ export const MultipleRecordPicker = ({
containerRef={containerRef}
onClickOutside={onClickOutside}
/>
<DropdownMenu ref={containerRef} data-select-disable width={200}>
<DropdownContent ref={containerRef}>
{layoutDirection === 'search-bar-on-bottom' && (
<>
{createNewButtonSection}
@ -124,7 +124,7 @@ export const MultipleRecordPicker = ({
{createNewButtonSection}
</>
)}
</DropdownMenu>
</DropdownContent>
</MultipleRecordPickerComponentInstanceContext.Provider>
);
};

View File

@ -5,7 +5,7 @@ import {
SingleRecordPickerMenuItemsWithSearchProps,
} from '@/object-record/record-picker/single-record-picker/components/SingleRecordPickerMenuItemsWithSearch';
import { SingleRecordPickerComponentInstanceContext } from '@/object-record/record-picker/single-record-picker/states/contexts/SingleRecordPickerComponentInstanceContext';
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { isDefined } from 'twenty-shared/utils';
@ -49,7 +49,7 @@ export const SingleRecordPicker = ({
<SingleRecordPickerComponentInstanceContext.Provider
value={{ instanceId: componentInstanceId }}
>
<DropdownMenu ref={containerRef} data-select-disable>
<DropdownContent ref={containerRef}>
<SingleRecordPickerMenuItemsWithSearch
{...{
EmptyIcon,
@ -62,7 +62,7 @@ export const SingleRecordPicker = ({
layoutDirection,
}}
/>
</DropdownMenu>
</DropdownContent>
</SingleRecordPickerComponentInstanceContext.Provider>
);
};

View File

@ -29,6 +29,7 @@ import { getForeignKeyNameFromRelationFieldName } from '@/object-record/utils/ge
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
import { isFieldCellSupported } from '@/object-record/utils/isFieldCellSupported';
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 { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
@ -246,21 +247,23 @@ export const RecordDetailRelationRecordsListItem = ({
/>
}
dropdownComponents={
<DropdownMenuItemsContainer>
<MenuItem
LeftIcon={IconUnlink}
text="Detach"
onClick={handleDetach}
/>
{!isAccountOwnerRelation && (
<DropdownContent>
<DropdownMenuItemsContainer>
<MenuItem
LeftIcon={IconTrash}
text="Delete"
accent="danger"
onClick={handleDelete}
LeftIcon={IconUnlink}
text="Detach"
onClick={handleDetach}
/>
)}
</DropdownMenuItemsContainer>
{!isAccountOwnerRelation && (
<MenuItem
LeftIcon={IconTrash}
text="Delete"
accent="danger"
onClick={handleDelete}
/>
)}
</DropdownMenuItemsContainer>
</DropdownContent>
}
dropdownHotkeyScope={{ scope: dropdownScopeId }}
/>

View File

@ -2,13 +2,14 @@ import { RecordTableColumnAggregateFooterAggregateOperationMenuItems } from '@/o
import { RecordTableColumnAggregateFooterDropdownContext } from '@/object-record/record-table/record-table-footer/components/RecordTableColumnAggregateFooterDropdownContext';
import { ExtendedAggregateOperations } from '@/object-record/record-table/types/ExtendedAggregateOperations';
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useContext } from 'react';
import { Key } from 'ts-key-enum';
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
import { IconChevronLeft } from 'twenty-ui/display';
export const RecordTableColumnAggregateFooterDropdownSubmenuContent = ({
@ -32,7 +33,7 @@ export const RecordTableColumnAggregateFooterDropdownSubmenuContent = ({
TableOptionsHotkeyScope.Dropdown,
);
return (
<>
<DropdownContent>
<DropdownMenuHeader
StartComponent={
<DropdownMenuHeaderLeftComponent
@ -48,6 +49,6 @@ export const RecordTableColumnAggregateFooterDropdownSubmenuContent = ({
aggregateOperations={aggregateOperations}
/>
</DropdownMenuItemsContainer>
</>
</DropdownContent>
);
};

View File

@ -5,16 +5,17 @@ import { NON_STANDARD_AGGREGATE_OPERATION_OPTIONS } from '@/object-record/record
import { useViewFieldAggregateOperation } from '@/object-record/record-table/record-table-footer/hooks/useViewFieldAggregateOperation';
import { getAvailableAggregateOperationsForFieldMetadataType } from '@/object-record/record-table/record-table-footer/utils/getAvailableAggregateOperationsForFieldMetadataType';
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { t } from '@lingui/core/macro';
import { useContext, useMemo } from 'react';
import { Key } from 'ts-key-enum';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { isDefined, isFieldMetadataDateKind } from 'twenty-shared/utils';
import { IconCheck } from 'twenty-ui/display';
import { MenuItem } from 'twenty-ui/navigation';
import { FieldMetadataType } from '~/generated-metadata/graphql';
export const RecordTableColumnAggregateFooterMenuContent = () => {
const {
@ -64,7 +65,7 @@ export const RecordTableColumnAggregateFooterMenuContent = () => {
} = useViewFieldAggregateOperation();
return (
<>
<DropdownContent>
<DropdownMenuItemsContainer>
<MenuItem
onClick={() => {
@ -116,6 +117,6 @@ export const RecordTableColumnAggregateFooterMenuContent = () => {
aria-selected={!isDefined(currentViewFieldAggregateOperation)}
/>
</DropdownMenuItemsContainer>
</>
</DropdownContent>
);
};

View File

@ -6,6 +6,7 @@ import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { useOpenRecordFilterChipFromTableHeader } from '@/object-record/record-table/record-table-header/hooks/useOpenRecordFilterChipFromTableHeader';
import { onToggleColumnSortComponentState } from '@/object-record/record-table/states/onToggleColumnSortComponentState';
import { visibleTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/visibleTableColumnsComponentSelector';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { useToggleScrollWrapper } from '@/ui/utilities/scroll/hooks/useToggleScrollWrapper';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import styled from '@emotion/styled';
@ -107,43 +108,45 @@ export const RecordTableColumnHeadDropdownMenu = ({
const canHide = column.isLabelIdentifier !== true;
return (
<StyledDropdownMenuItemsContainer>
{isFilterable && (
<MenuItem
LeftIcon={IconFilter}
onClick={handleFilterClick}
text={t`Filter`}
/>
)}
{isSortable && (
<MenuItem
LeftIcon={IconSortDescending}
onClick={handleSortClick}
text={t`Sort`}
/>
)}
{showSeparator && <DropdownMenuSeparator />}
{canMoveLeft && (
<MenuItem
LeftIcon={IconArrowLeft}
onClick={handleColumnMoveLeft}
text={t`Move left`}
/>
)}
{canMoveRight && (
<MenuItem
LeftIcon={IconArrowRight}
onClick={handleColumnMoveRight}
text={t`Move right`}
/>
)}
{canHide && (
<MenuItem
LeftIcon={IconEyeOff}
onClick={handleColumnVisibility}
text={t`Hide`}
/>
)}
</StyledDropdownMenuItemsContainer>
<DropdownContent>
<StyledDropdownMenuItemsContainer>
{isFilterable && (
<MenuItem
LeftIcon={IconFilter}
onClick={handleFilterClick}
text={t`Filter`}
/>
)}
{isSortable && (
<MenuItem
LeftIcon={IconSortDescending}
onClick={handleSortClick}
text={t`Sort`}
/>
)}
{showSeparator && <DropdownMenuSeparator />}
{canMoveLeft && (
<MenuItem
LeftIcon={IconArrowLeft}
onClick={handleColumnMoveLeft}
text={t`Move left`}
/>
)}
{canMoveRight && (
<MenuItem
LeftIcon={IconArrowRight}
onClick={handleColumnMoveRight}
text={t`Move right`}
/>
)}
{canHide && (
<MenuItem
LeftIcon={IconEyeOff}
onClick={handleColumnVisibility}
text={t`Hide`}
/>
)}
</StyledDropdownMenuItemsContainer>
</DropdownContent>
);
};

View File

@ -8,15 +8,16 @@ import { useTableColumns } from '@/object-record/record-table/hooks/useTableColu
import { hiddenTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/hiddenTableColumnsComponentSelector';
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
import { SettingsPath } from '@/types/SettingsPath';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
import { useLingui } from '@lingui/react/macro';
import { IconSettings, useIcons } from 'twenty-ui/display';
import { MenuItem, UndecoratedLink } from 'twenty-ui/navigation';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
export const RecordTableHeaderPlusButtonContent = () => {
const { t } = useLingui();
@ -45,7 +46,7 @@ export const RecordTableHeaderPlusButtonContent = () => {
);
return (
<>
<DropdownContent>
<DropdownMenuItemsContainer>
{hiddenTableColumns.map((column) => (
<MenuItem
@ -70,6 +71,6 @@ export const RecordTableHeaderPlusButtonContent = () => {
<MenuItem LeftIcon={IconSettings} text={t`Customize fields`} />
</UndecoratedLink>
</DropdownMenuItemsContainer>
</>
</DropdownContent>
);
};

View File

@ -3,6 +3,7 @@ import { Key } from 'ts-key-enum';
import { StyledMultipleSelectDropdownAvatarChip } from '@/object-record/select/components/StyledMultipleSelectDropdownAvatarChip';
import { SelectableItem } from '@/object-record/select/types/SelectableItem';
import { DropdownMenuSkeletonItem } from '@/ui/input/relation-picker/components/skeletons/DropdownMenuSkeletonItem';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
@ -80,47 +81,49 @@ export const MultipleSelectDropdown = ({
const selectableItemIds = itemsInDropdown.map((item) => item.id);
return (
<SelectableList
selectableListInstanceId={selectableListId}
selectableItemIdArray={selectableItemIds}
hotkeyScope={hotkeyScope}
>
<DropdownMenuItemsContainer hasMaxHeight width="auto">
{itemsInDropdown?.map((item) => {
return (
<SelectableListItem
itemId={item.id}
onEnter={() => {
resetSelectedItem();
handleItemSelectChange(item, !item.isSelected);
}}
>
<MenuItemMultiSelectAvatar
key={item.id}
selected={item.isSelected}
isKeySelected={item.id === selectedItemId}
onSelectChange={(newCheckedValue) => {
<DropdownContent>
<SelectableList
selectableListInstanceId={selectableListId}
selectableItemIdArray={selectableItemIds}
hotkeyScope={hotkeyScope}
>
<DropdownMenuItemsContainer hasMaxHeight width="auto">
{itemsInDropdown?.map((item) => {
return (
<SelectableListItem
itemId={item.id}
onEnter={() => {
resetSelectedItem();
handleItemSelectChange(item, newCheckedValue);
handleItemSelectChange(item, !item.isSelected);
}}
avatar={
<StyledMultipleSelectDropdownAvatarChip
className="avatar-icon-container"
name={item.name}
avatarUrl={item.avatarUrl}
LeftIcon={item.AvatarIcon}
avatarType={item.avatarType}
isIconInverted={item.isIconInverted}
placeholderColorSeed={item.id}
/>
}
/>
</SelectableListItem>
);
})}
{showNoResult && <MenuItem text="No results" />}
{loadingItems && <DropdownMenuSkeletonItem />}
</DropdownMenuItemsContainer>
</SelectableList>
>
<MenuItemMultiSelectAvatar
key={item.id}
selected={item.isSelected}
isKeySelected={item.id === selectedItemId}
onSelectChange={(newCheckedValue) => {
resetSelectedItem();
handleItemSelectChange(item, newCheckedValue);
}}
avatar={
<StyledMultipleSelectDropdownAvatarChip
className="avatar-icon-container"
name={item.name}
avatarUrl={item.avatarUrl}
LeftIcon={item.AvatarIcon}
avatarType={item.avatarType}
isIconInverted={item.isIconInverted}
placeholderColorSeed={item.id}
/>
}
/>
</SelectableListItem>
);
})}
{showNoResult && <MenuItem text="No results" />}
{loadingItems && <DropdownMenuSkeletonItem />}
</DropdownMenuItemsContainer>
</SelectableList>
</DropdownContent>
);
};

View File

@ -4,6 +4,7 @@ import { useDestroyOneRecord } from '@/object-record/hooks/useDestroyOneRecord';
import { useTriggerApisOAuth } from '@/settings/accounts/hooks/useTriggerApiOAuth';
import { SettingsPath } from '@/types/SettingsPath';
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 { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
@ -54,45 +55,46 @@ export const SettingsAccountsRowDropdownMenu = ({
clickableComponent={
<LightIconButton Icon={IconDotsVertical} accent="tertiary" />
}
dropdownWidth={160}
dropdownComponents={
<DropdownMenuItemsContainer>
<MenuItem
LeftIcon={IconMail}
text={t`Emails settings`}
onClick={() => {
navigate(SettingsPath.AccountsEmails);
closeDropdown();
}}
/>
<MenuItem
LeftIcon={IconCalendarEvent}
text={t`Calendar settings`}
onClick={() => {
navigate(SettingsPath.AccountsCalendars);
closeDropdown();
}}
/>
{account.authFailedAt && (
<DropdownContent>
<DropdownMenuItemsContainer>
<MenuItem
LeftIcon={IconRefresh}
text={t`Reconnect`}
LeftIcon={IconMail}
text={t`Emails settings`}
onClick={() => {
triggerApisOAuth(account.provider);
navigate(SettingsPath.AccountsEmails);
closeDropdown();
}}
/>
)}
<MenuItem
accent="danger"
LeftIcon={IconTrash}
text={t`Remove account`}
onClick={() => {
closeDropdown();
openModal(DELETE_ACCOUNT_MODAL_ID);
}}
/>
</DropdownMenuItemsContainer>
<MenuItem
LeftIcon={IconCalendarEvent}
text={t`Calendar settings`}
onClick={() => {
navigate(SettingsPath.AccountsCalendars);
closeDropdown();
}}
/>
{account.authFailedAt && (
<MenuItem
LeftIcon={IconRefresh}
text={t`Reconnect`}
onClick={() => {
triggerApisOAuth(account.provider);
closeDropdown();
}}
/>
)}
<MenuItem
accent="danger"
LeftIcon={IconTrash}
text={t`Remove account`}
onClick={() => {
closeDropdown();
openModal(DELETE_ACCOUNT_MODAL_ID);
}}
/>
</DropdownMenuItemsContainer>
</DropdownContent>
}
/>
<ConfirmationModal

View File

@ -4,6 +4,7 @@ import { TextArea } from '@/ui/input/components/TextArea';
import { TextInputV2 } from '@/ui/input/components/TextInputV2';
import { SelectHotkeyScope } from '@/ui/input/types/SelectHotkeyScope';
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 { ConfigVariableValue } from 'twenty-shared/types';
import { MenuItemMultiSelect } from 'twenty-ui/navigation';
@ -115,19 +116,21 @@ export const ConfigVariableDatabaseInput = ({
/>
}
dropdownComponents={
<DropdownMenuItemsContainer>
{selectOptions.map((option) => (
<MenuItemMultiSelect
key={option.value}
text={option.label}
selected={isValueSelected(option.value)}
className="config-variable-array-menu-item-multi-select"
onSelectChange={() =>
handleMultiSelectChange(option.value)
}
/>
))}
</DropdownMenuItemsContainer>
<DropdownContent>
<DropdownMenuItemsContainer>
{selectOptions.map((option) => (
<MenuItemMultiSelect
key={option.value}
text={option.label}
selected={isValueSelected(option.value)}
className="config-variable-array-menu-item-multi-select"
onSelectChange={() =>
handleMultiSelectChange(option.value)
}
/>
))}
</DropdownMenuItemsContainer>
</DropdownContent>
}
/>
) : (

View File

@ -3,6 +3,7 @@ import { ConfigVariableSourceOptions } from '@/settings/admin-panel/config-varia
import { ConfigVariableFilterCategory } from '@/settings/admin-panel/config-variables/types/ConfigVariableFilterCategory';
import { ConfigVariableGroupFilter } from '@/settings/admin-panel/config-variables/types/ConfigVariableGroupFilter';
import { ConfigVariableSourceFilter } from '@/settings/admin-panel/config-variables/types/ConfigVariableSourceFilter';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
@ -48,7 +49,7 @@ export const ConfigVariableOptionsDropdownContent = ({
if (!selectedCategory) {
return (
<>
<DropdownContent>
<DropdownMenuItemsContainer>
<MenuItemSelectTag
text={t`Source`}
@ -85,12 +86,12 @@ export const ConfigVariableOptionsDropdownContent = ({
onClick={() => onShowHiddenChange(!showHiddenGroupVariables)}
/>
</DropdownMenuItemsContainer>
</>
</DropdownContent>
);
}
return (
<>
<DropdownContent>
<DropdownMenuHeader
StartComponent={
<DropdownMenuHeaderLeftComponent
@ -136,6 +137,6 @@ export const ConfigVariableOptionsDropdownContent = ({
</>
)}
</DropdownMenuItemsContainer>
</>
</DropdownContent>
);
};

View File

@ -1,18 +1,18 @@
import { SettingsFieldType } from '@/settings/data-model/types/SettingsFieldType';
import { SettingsPath } from '@/types/SettingsPath';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useLocation, useParams, useSearchParams } from 'react-router-dom';
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
import { t } from '@lingui/core/macro';
import { useLocation, useParams, useSearchParams } from 'react-router-dom';
import { isDefined } from 'twenty-shared/utils';
import { Button } from 'twenty-ui/input';
import { IconChevronDown } from 'twenty-ui/display';
import { Button } from 'twenty-ui/input';
import { MenuItem } from 'twenty-ui/navigation';
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
const StyledContainer = styled.div`
align-items: center;
@ -110,7 +110,7 @@ export const SettingsDataModelNewFieldBreadcrumbDropDown = () => {
</StyledButtonContainer>
}
dropdownComponents={
<DropdownMenu>
<DropdownContent>
<DropdownMenuItemsContainer>
<StyledMenuItemWrapper>
<StyledMenuItem
@ -128,7 +128,7 @@ export const SettingsDataModelNewFieldBreadcrumbDropDown = () => {
/>
</StyledMenuItemWrapper>
</DropdownMenuItemsContainer>
</DropdownMenu>
</DropdownContent>
}
dropdownHotkeyScope={{ scope: dropdownId }}
/>

View File

@ -3,13 +3,12 @@ import { AdvancedSettingsWrapper } from '@/settings/components/AdvancedSettingsW
import { OPTION_VALUE_MAXIMUM_LENGTH } from '@/settings/data-model/constants/OptionValueMaximumLength';
import { TextInput } from '@/ui/input/components/TextInput';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { t } from '@lingui/core/macro';
import { computeOptionValueFromLabel } from '~/pages/settings/data-model/utils/compute-option-value-from-label.utils';
import {
ColorSample,
IconCheck,
@ -19,8 +18,9 @@ import {
IconX,
} from 'twenty-ui/display';
import { LightIconButton } from 'twenty-ui/input';
import { MAIN_COLOR_NAMES } from 'twenty-ui/theme';
import { MenuItem, MenuItemSelectColor } from 'twenty-ui/navigation';
import { MAIN_COLOR_NAMES } from 'twenty-ui/theme';
import { computeOptionValueFromLabel } from '~/pages/settings/data-model/utils/compute-option-value-from-label.utils';
type SettingsDataModelFieldSelectFormOptionRowProps = {
className?: string;
@ -120,19 +120,21 @@ export const SettingsDataModelFieldSelectFormOptionRow = ({
dropdownHotkeyScope={{ scope: SELECT_COLOR_DROPDOWN_ID }}
clickableComponent={<StyledColorSample colorName={option.color} />}
dropdownComponents={
<DropdownMenuItemsContainer>
{MAIN_COLOR_NAMES.map((colorName) => (
<MenuItemSelectColor
key={colorName}
onClick={() => {
onChange({ ...option, color: colorName });
closeColorDropdown();
}}
color={colorName}
selected={colorName === option.color}
/>
))}
</DropdownMenuItemsContainer>
<DropdownContent>
<DropdownMenuItemsContainer>
{MAIN_COLOR_NAMES.map((colorName) => (
<MenuItemSelectColor
key={colorName}
onClick={() => {
onChange({ ...option, color: colorName });
closeColorDropdown();
}}
color={colorName}
selected={colorName === option.color}
/>
))}
</DropdownMenuItemsContainer>
</DropdownContent>
}
/>
<StyledOptionInput
@ -163,7 +165,7 @@ export const SettingsDataModelFieldSelectFormOptionRow = ({
<StyledLightIconButton accent="tertiary" Icon={IconDotsVertical} />
}
dropdownComponents={
<DropdownMenu>
<DropdownContent>
<DropdownMenuItemsContainer>
{isDefault ? (
<MenuItem
@ -196,7 +198,7 @@ export const SettingsDataModelFieldSelectFormOptionRow = ({
/>
)}
</DropdownMenuItemsContainer>
</DropdownMenu>
</DropdownContent>
}
/>
</StyledRow>

View File

@ -1,5 +1,7 @@
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 { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import {
IconArchive,
@ -55,29 +57,30 @@ export const SettingsObjectFieldActiveActionDropdown = ({
accent="tertiary"
/>
}
dropdownWidth={160}
dropdownComponents={
<DropdownMenuItemsContainer>
<MenuItem
text={isCustomField ? 'Edit' : 'View'}
LeftIcon={isCustomField ? IconPencil : IconEye}
onClick={handleEdit}
/>
{!!onSetAsLabelIdentifier && (
<DropdownContent widthInPixels={GenericDropdownContentWidth.Narrow}>
<DropdownMenuItemsContainer>
<MenuItem
text="Set as record text"
LeftIcon={IconTextSize}
onClick={handleSetAsLabelIdentifier}
text={isCustomField ? 'Edit' : 'View'}
LeftIcon={isCustomField ? IconPencil : IconEye}
onClick={handleEdit}
/>
)}
{!!onDeactivate && (
<MenuItem
text="Deactivate"
LeftIcon={IconArchive}
onClick={handleDeactivate}
/>
)}
</DropdownMenuItemsContainer>
{!!onSetAsLabelIdentifier && (
<MenuItem
text="Set as record text"
LeftIcon={IconTextSize}
onClick={handleSetAsLabelIdentifier}
/>
)}
{!!onDeactivate && (
<MenuItem
text="Deactivate"
LeftIcon={IconArchive}
onClick={handleDeactivate}
/>
)}
</DropdownMenuItemsContainer>
</DropdownContent>
}
dropdownHotkeyScope={{ scope: dropdownId }}
/>

View File

@ -1,5 +1,7 @@
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 { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { t } from '@lingui/core/macro';
import {
@ -60,28 +62,29 @@ export const SettingsObjectFieldInactiveActionDropdown = ({
accent="tertiary"
/>
}
dropdownWidth={160}
dropdownComponents={
<DropdownMenuItemsContainer>
<MenuItem
text={isCustomField ? t`Edit` : t`View`}
LeftIcon={isCustomField ? IconPencil : IconEye}
onClick={handleEdit}
/>
<MenuItem
text={t`Activate`}
LeftIcon={IconArchiveOff}
onClick={handleActivate}
/>
{isDeletable && (
<DropdownContent widthInPixels={GenericDropdownContentWidth.Narrow}>
<DropdownMenuItemsContainer>
<MenuItem
text={t`Delete`}
accent="danger"
LeftIcon={IconTrash}
onClick={handleDelete}
text={isCustomField ? t`Edit` : t`View`}
LeftIcon={isCustomField ? IconPencil : IconEye}
onClick={handleEdit}
/>
)}
</DropdownMenuItemsContainer>
<MenuItem
text={t`Activate`}
LeftIcon={IconArchiveOff}
onClick={handleActivate}
/>
{isDeletable && (
<MenuItem
text={t`Delete`}
accent="danger"
LeftIcon={IconTrash}
onClick={handleDelete}
/>
)}
</DropdownMenuItemsContainer>
</DropdownContent>
}
dropdownHotkeyScope={{
scope: dropdownId,

View File

@ -1,103 +0,0 @@
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { SettingsSummaryCard } from '@/settings/components/SettingsSummaryCard';
import { SettingsDataModelObjectTypeTag } from '@/settings/data-model/objects/components/SettingsDataModelObjectTypeTag';
import { getObjectTypeLabel } from '@/settings/data-model/utils/getObjectTypeLabel';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import {
IconArchive,
IconDotsVertical,
IconPencil,
useIcons,
} from 'twenty-ui/display';
import { LightIconButton } from 'twenty-ui/input';
import { MenuItem } from 'twenty-ui/navigation';
type SettingsObjectSummaryCardProps = {
objectMetadataItem: ObjectMetadataItem;
iconKey?: string;
name: string;
onDeactivate: () => void;
onEdit: () => void;
};
const StyledObjectTypeTag = styled(SettingsDataModelObjectTypeTag)`
box-sizing: border-box;
height: ${({ theme }) => theme.spacing(6)};
`;
const dropdownId = 'settings-object-edit-about-menu-dropdown';
export const SettingsObjectSummaryCard = ({
objectMetadataItem,
iconKey = '',
name,
onDeactivate,
onEdit,
}: SettingsObjectSummaryCardProps) => {
const theme = useTheme();
const { getIcon } = useIcons();
const Icon = getIcon(iconKey);
const { closeDropdown } = useDropdown(dropdownId);
const handleEdit = () => {
onEdit();
closeDropdown();
};
const handleDeactivate = () => {
onDeactivate();
closeDropdown();
};
const objectTypeLabel = getObjectTypeLabel(objectMetadataItem);
return (
<SettingsSummaryCard
title={
<>
{!!Icon && <Icon size={theme.icon.size.md} />}
{name}
</>
}
rightComponent={
<>
<StyledObjectTypeTag objectTypeLabel={objectTypeLabel} />
<Dropdown
dropdownId={dropdownId}
clickableComponent={
<LightIconButton
aria-label="Object Options"
Icon={IconDotsVertical}
accent="tertiary"
/>
}
dropdownWidth={160}
dropdownComponents={
<DropdownMenuItemsContainer>
<MenuItem
text="Edit"
LeftIcon={IconPencil}
onClick={handleEdit}
/>
<MenuItem
text="Deactivate"
LeftIcon={IconArchive}
onClick={handleDeactivate}
/>
</DropdownMenuItemsContainer>
}
dropdownHotkeyScope={{
scope: dropdownId,
}}
/>
</>
}
/>
);
};

View File

@ -1,5 +1,7 @@
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 { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { IconArchiveOff, IconDotsVertical, IconTrash } from 'twenty-ui/display';
import { LightIconButton } from 'twenty-ui/input';
@ -42,23 +44,24 @@ export const SettingsObjectInactiveMenuDropDown = ({
accent="tertiary"
/>
}
dropdownWidth={160}
dropdownComponents={
<DropdownMenuItemsContainer>
<MenuItem
text="Activate"
LeftIcon={IconArchiveOff}
onClick={handleActivate}
/>
{isCustomObject && (
<DropdownContent widthInPixels={GenericDropdownContentWidth.Narrow}>
<DropdownMenuItemsContainer>
<MenuItem
text="Delete"
LeftIcon={IconTrash}
accent="danger"
onClick={handleDelete}
text="Activate"
LeftIcon={IconArchiveOff}
onClick={handleActivate}
/>
)}
</DropdownMenuItemsContainer>
{isCustomObject && (
<MenuItem
text="Delete"
LeftIcon={IconTrash}
accent="danger"
onClick={handleDelete}
/>
)}
</DropdownMenuItemsContainer>
</DropdownContent>
}
dropdownHotkeyScope={{ scope: dropdownId }}
/>

View File

@ -1,6 +1,7 @@
import { SettingsSummaryCard } from '@/settings/components/SettingsSummaryCard';
import { SettingsIntegrationDatabaseConnectionSyncStatus } from '@/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionSyncStatus';
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 styled from '@emotion/styled';
import { IconDotsVertical, IconPencil, IconTrash } from 'twenty-ui/display';
@ -58,16 +59,18 @@ export const SettingsIntegrationDatabaseConnectionSummaryCard = ({
<LightIconButton Icon={IconDotsVertical} accent="tertiary" />
}
dropdownComponents={
<DropdownMenuItemsContainer>
<MenuItem
LeftIcon={IconTrash}
text="Remove"
onClick={onRemove}
/>
<UndecoratedLink to="./edit">
<MenuItem LeftIcon={IconPencil} text="Edit" />
</UndecoratedLink>
</DropdownMenuItemsContainer>
<DropdownContent>
<DropdownMenuItemsContainer>
<MenuItem
LeftIcon={IconTrash}
text="Remove"
onClick={onRemove}
/>
<UndecoratedLink to="./edit">
<MenuItem LeftIcon={IconPencil} text="Edit" />
</UndecoratedLink>
</DropdownMenuItemsContainer>
</DropdownContent>
}
/>
</>

View File

@ -1,7 +1,7 @@
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useObjectRecordSearchRecords } from '@/object-record/hooks/useObjectRecordSearchRecords';
import { SettingsRoleAssignmentWorkspaceMemberPickerDropdownContent } from '@/settings/roles/role-assignment/components/SettingsRoleAssignmentWorkspaceMemberPickerDropdownContent';
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
@ -38,7 +38,7 @@ export const SettingsRoleAssignmentWorkspaceMemberPickerDropdown = ({
const { t } = useLingui();
return (
<DropdownMenu>
<DropdownContent>
<DropdownMenuSearchInput
value={searchFilter}
onChange={handleSearchFilterChange}
@ -53,6 +53,6 @@ export const SettingsRoleAssignmentWorkspaceMemberPickerDropdown = ({
onSelect={onSelect}
/>
</DropdownMenuItemsContainer>
</DropdownMenu>
</DropdownContent>
);
};

View File

@ -1,5 +1,5 @@
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
@ -37,7 +37,7 @@ export const SettingsRolePermissionsObjectLevelObjectPickerDropdownContent = ({
);
return (
<DropdownMenu>
<DropdownContent>
<DropdownMenuSearchInput
value={searchFilter}
onChange={handleSearchFilterChange}
@ -54,6 +54,6 @@ export const SettingsRolePermissionsObjectLevelObjectPickerDropdownContent = ({
/>
))}
</DropdownMenuItemsContainer>
</DropdownMenu>
</DropdownContent>
);
};

View File

@ -4,6 +4,7 @@ import { SSOIdentitiesProvidersState } from '@/settings/security/states/SSOIdent
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
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 { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { useLingui } from '@lingui/react/macro';
@ -72,28 +73,29 @@ export const SettingsSecuritySSORowDropdownMenu = ({
clickableComponent={
<LightIconButton Icon={IconDotsVertical} accent="tertiary" />
}
dropdownWidth={160}
dropdownComponents={
<DropdownMenuItemsContainer>
<MenuItem
accent="default"
LeftIcon={IconArchive}
text={SSOIdp.status === 'Active' ? t`Deactivate` : t`Activate`}
onClick={() => {
toggleSSOIdentityProviderStatus(SSOIdp.id);
closeDropdown();
}}
/>
<MenuItem
accent="danger"
LeftIcon={IconTrash}
text={t`Delete`}
onClick={() => {
handleDeleteSSOIdentityProvider(SSOIdp.id);
closeDropdown();
}}
/>
</DropdownMenuItemsContainer>
<DropdownContent>
<DropdownMenuItemsContainer>
<MenuItem
accent="default"
LeftIcon={IconArchive}
text={SSOIdp.status === 'Active' ? t`Deactivate` : t`Activate`}
onClick={() => {
toggleSSOIdentityProviderStatus(SSOIdp.id);
closeDropdown();
}}
/>
<MenuItem
accent="danger"
LeftIcon={IconTrash}
text={t`Delete`}
onClick={() => {
handleDeleteSSOIdentityProvider(SSOIdp.id);
closeDropdown();
}}
/>
</DropdownMenuItemsContainer>
</DropdownContent>
}
/>
);

View File

@ -2,6 +2,7 @@ import { approvedAccessDomainsState } from '@/settings/security/states/ApprovedA
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
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 { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { UnwrapRecoilValue, useSetRecoilState } from 'recoil';
@ -61,19 +62,20 @@ export const SettingsSecurityApprovedAccessDomainRowDropdownMenu = ({
clickableComponent={
<LightIconButton Icon={IconDotsVertical} accent="tertiary" />
}
dropdownWidth={160}
dropdownComponents={
<DropdownMenuItemsContainer>
<MenuItem
accent="danger"
LeftIcon={IconTrash}
text="Delete"
onClick={() => {
handleDeleteApprovedAccessDomain();
closeDropdown();
}}
/>
</DropdownMenuItemsContainer>
<DropdownContent>
<DropdownMenuItemsContainer>
<MenuItem
accent="danger"
LeftIcon={IconTrash}
text="Delete"
onClick={() => {
handleDeleteApprovedAccessDomain();
closeDropdown();
}}
/>
</DropdownMenuItemsContainer>
</DropdownContent>
}
/>
);

View File

@ -1,6 +1,7 @@
import { EnvironmentVariable } from '@/settings/serverless-functions/components/tabs/SettingsServerlessFunctionTabEnvironmentVariablesSection';
import { TextInputV2 } from '@/ui/input/components/TextInputV2';
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 { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { TableCell } from '@/ui/layout/table/components/TableCell';
@ -109,24 +110,26 @@ export const SettingsServerlessFunctionTabEnvironmentVariableTableRow = ({
/>
}
dropdownComponents={
<DropdownMenuItemsContainer>
<MenuItem
text={'Edit'}
LeftIcon={IconPencil}
onClick={() => {
setEditMode(true);
closeDropdown();
}}
/>
<MenuItem
text={'Delete'}
LeftIcon={IconTrash}
onClick={() => {
onDelete();
closeDropdown();
}}
/>
</DropdownMenuItemsContainer>
<DropdownContent>
<DropdownMenuItemsContainer>
<MenuItem
text={'Edit'}
LeftIcon={IconPencil}
onClick={() => {
setEditMode(true);
closeDropdown();
}}
/>
<MenuItem
text={'Delete'}
LeftIcon={IconTrash}
onClick={() => {
onDelete();
closeDropdown();
}}
/>
</DropdownMenuItemsContainer>
</DropdownContent>
}
dropdownHotkeyScope={{ scope: dropDownId }}
/>

View File

@ -2,6 +2,7 @@ import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { isCompositeFieldType } from '@/object-record/object-filter-dropdown/utils/isCompositeFieldType';
import { DO_NOT_IMPORT_OPTION_KEY } from '@/spreadsheet-import/constants/DoNotImportOptionKey';
import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpreadsheetImportInternal';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
@ -59,7 +60,7 @@ export const MatchColumnSelectFieldSelectDropdownContent = ({
const { t } = useLingui();
return (
<>
<DropdownContent>
<DropdownMenuHeader
StartComponent={
<DropdownMenuHeaderLeftComponent
@ -76,7 +77,7 @@ export const MatchColumnSelectFieldSelectDropdownContent = ({
autoFocus
/>
<DropdownMenuSeparator />
<DropdownMenuItemsContainer hasMaxHeight width={200}>
<DropdownMenuItemsContainer hasMaxHeight>
<MenuItemSelect
selected={selectedValue?.value === DO_NOT_IMPORT_OPTION_KEY}
onClick={onDoNotImportSelect}
@ -98,6 +99,6 @@ export const MatchColumnSelectFieldSelectDropdownContent = ({
/>
))}
</DropdownMenuItemsContainer>
</>
</DropdownContent>
);
};

View File

@ -2,6 +2,7 @@ import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { isCompositeFieldType } from '@/object-record/object-filter-dropdown/utils/isCompositeFieldType';
import { getSubFieldOptionKey } from '@/object-record/spreadsheet-import/utils/getSubFieldOptionKey';
import { SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS } from '@/settings/data-model/constants/SettingsCompositeFieldTypeConfigs';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
@ -68,7 +69,7 @@ export const MatchColumnSelectSubFieldSelectDropdownContent = ({
.filter((subFieldName) => subFieldName.includes(searchFilter));
return (
<>
<DropdownContent>
<DropdownMenuHeader
StartComponent={
<DropdownMenuHeaderLeftComponent
@ -85,7 +86,7 @@ export const MatchColumnSelectSubFieldSelectDropdownContent = ({
autoFocus
/>
<DropdownMenuSeparator />
<DropdownMenuItemsContainer hasMaxHeight width={200}>
<DropdownMenuItemsContainer hasMaxHeight>
{subFieldNamesThatExistInOptions.map((subFieldName) => (
<MenuItem
key={subFieldName}
@ -104,6 +105,6 @@ export const MatchColumnSelectSubFieldSelectDropdownContent = ({
/>
))}
</DropdownMenuItemsContainer>
</>
</DropdownContent>
);
};

View File

@ -1,4 +1,4 @@
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
@ -48,7 +48,7 @@ export const SubMatchingSelectInput = ({
};
return (
<DropdownMenu ref={containerRef} data-select-disable>
<DropdownContent ref={containerRef}>
<DropdownMenuSearchInput
value={searchFilter}
onChange={(e) => setSearchFilter(e.target.value)}
@ -67,6 +67,6 @@ export const SubMatchingSelectInput = ({
/>
))}
</DropdownMenuItemsContainer>
</DropdownMenu>
</DropdownContent>
);
};

View File

@ -4,8 +4,8 @@ import { SupportButtonSkeletonLoader } from '@/support/components/SupportButtonS
import { useSupportChat } from '@/support/hooks/useSupportChat';
import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
import { useRecoilValue } from 'recoil';
import { Button, LightIconButton } from 'twenty-ui/input';
import { IconHelpCircle } from 'twenty-ui/display';
import { Button, LightIconButton } from 'twenty-ui/input';
const StyledButtonContainer = styled.div`
display: flex;

View File

@ -1,6 +1,8 @@
import { SupportButton } from '@/support/components/SupportButton';
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 { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { IconHelpCircle, IconMessage } from 'twenty-ui/display';
import { MenuItem } from 'twenty-ui/navigation';
@ -26,20 +28,21 @@ export const SupportDropdown = () => {
dropdownPlacement="top-start"
dropdownOffset={{ x: 0, y: -28 }}
clickableComponent={<SupportButton />}
dropdownWidth={160}
dropdownComponents={
<DropdownMenuItemsContainer>
<MenuItem
text="Talk to us"
LeftIcon={IconMessage}
onClick={handleTalkToUs}
/>
<MenuItem
text="Documentation"
LeftIcon={IconHelpCircle}
onClick={handleUserGuide}
/>
</DropdownMenuItemsContainer>
<DropdownContent widthInPixels={GenericDropdownContentWidth.Narrow}>
<DropdownMenuItemsContainer>
<MenuItem
text="Talk to us"
LeftIcon={IconMessage}
onClick={handleTalkToUs}
/>
<MenuItem
text="Documentation"
LeftIcon={IconHelpCircle}
onClick={handleUserGuide}
/>
</DropdownMenuItemsContainer>
</DropdownContent>
}
dropdownHotkeyScope={{ scope: dropdownId }}
/>

View File

@ -2,12 +2,12 @@ import { useRef, useState } from 'react';
import { Key } from 'ts-key-enum';
import { FieldMultiSelectValue } from '@/object-record/record-field/types/FieldMetadata';
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
@ -102,7 +102,7 @@ export const MultiSelectInput = ({
selectableItemIdArray={optionIds}
hotkeyScope={hotkeyScope}
>
<DropdownMenu data-select-disable ref={containerRef}>
<DropdownContent ref={containerRef} selectDisabled>
<DropdownMenuSearchInput
value={searchFilter}
onChange={(event) =>
@ -130,7 +130,7 @@ export const MultiSelectInput = ({
);
})}
</DropdownMenuItemsContainer>
</DropdownMenu>
</DropdownContent>
</SelectableList>
);
};

View File

@ -2,7 +2,6 @@ import styled from '@emotion/styled';
import { useMemo, useState } from 'react';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
@ -11,6 +10,8 @@ import { SelectableList } from '@/ui/layout/selectable-list/components/Selectabl
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { arrayToChunks } from '~/utils/array/arrayToChunks';
import { ICON_PICKER_DROPDOWN_CONTENT_WIDTH } from '@/ui/input/components/constants/IconPickerDropdownContentWidth';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { useSelectableListListenToEnterHotkeyOnItem } from '@/ui/layout/selectable-list/hooks/useSelectableListListenToEnterHotkeyOnItem';
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
@ -179,14 +180,13 @@ export const IconPicker = ({
size={size}
/>
}
dropdownWidth={176}
dropdownComponents={
<SelectableList
selectableListInstanceId="icon-list"
selectableItemIdMatrix={iconKeys2d}
hotkeyScope={IconPickerHotkeyScope.IconPicker}
>
<DropdownMenu width={176}>
<DropdownContent widthInPixels={ICON_PICKER_DROPDOWN_CONTENT_WIDTH}>
<SelectableList
selectableListInstanceId="icon-list"
selectableItemIdMatrix={iconKeys2d}
hotkeyScope={IconPickerHotkeyScope.IconPicker}
>
<DropdownMenuSearchInput
placeholder={t`Search icon`}
autoFocus
@ -220,8 +220,8 @@ export const IconPicker = ({
</StyledMenuIconItemsContainer>
</DropdownMenuItemsContainer>
</div>
</DropdownMenu>
</SelectableList>
</SelectableList>
</DropdownContent>
}
onClickOutside={onClickOutside}
onClose={() => {

View File

@ -8,6 +8,8 @@ import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownM
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { SelectControl } from '@/ui/input/components/SelectControl';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
import { DropdownOffset } from '@/ui/layout/dropdown/types/DropdownOffset';
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
@ -67,7 +69,7 @@ export const Select = <Value extends SelectValue>({
disabled: disabledFromProps,
selectSizeVariant,
dropdownId,
dropdownWidth = 176,
dropdownWidth = GenericDropdownContentWidth.Medium,
dropdownWidthAuto = false,
emptyOption,
fullWidth,
@ -139,7 +141,6 @@ export const Select = <Value extends SelectValue>({
) : (
<Dropdown
dropdownId={dropdownId}
dropdownWidth={dropDownMenuWidth}
dropdownPlacement="bottom-start"
dropdownOffset={dropdownOffset}
clickableComponent={
@ -151,7 +152,7 @@ export const Select = <Value extends SelectValue>({
/>
}
dropdownComponents={
<>
<DropdownContent widthInPixels={dropDownMenuWidth}>
{!!withSearchInput && (
<DropdownMenuSearchInput
autoFocus
@ -163,7 +164,7 @@ export const Select = <Value extends SelectValue>({
<DropdownMenuSeparator />
)}
{!!filteredOptions.length && (
<DropdownMenuItemsContainer hasMaxHeight width={'auto'}>
<DropdownMenuItemsContainer hasMaxHeight>
<SelectableList
hotkeyScope={SelectHotkeyScope.Select}
selectableListInstanceId={dropdownId}
@ -208,7 +209,7 @@ export const Select = <Value extends SelectValue>({
/>
</DropdownMenuItemsContainer>
)}
</>
</DropdownContent>
}
dropdownHotkeyScope={{ scope: SelectHotkeyScope.Select }}
/>

View File

@ -1,4 +1,4 @@
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
@ -96,7 +96,7 @@ export const SelectInput = ({
);
return (
<DropdownMenu ref={containerRef} data-select-disable>
<DropdownContent ref={containerRef} selectDisabled>
<DropdownMenuSearchInput
value={searchFilter}
onChange={(e) => setSearchFilter(e.target.value)}
@ -129,6 +129,6 @@ export const SelectInput = ({
);
})}
</DropdownMenuItemsContainer>
</DropdownMenu>
</DropdownContent>
);
};

View File

@ -0,0 +1 @@
export const ICON_PICKER_DROPDOWN_CONTENT_WIDTH = 176;

View File

@ -1,12 +1,12 @@
import { useMemo, useState } from 'react';
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { CURRENCIES } from '@/settings/data-model/constants/Currencies';
import { Currency } from '@/ui/input/components/internal/types/Currency';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { MenuItem, MenuItemSelectAvatar } from 'twenty-ui/navigation';
export const CurrencyPickerDropdownSelect = ({
@ -31,7 +31,7 @@ export const CurrencyPickerDropdownSelect = ({
);
return (
<DropdownMenu>
<DropdownContent>
<DropdownMenuSearchInput
value={searchFilter}
onChange={(event) => setSearchFilter(event.target.value)}
@ -65,6 +65,6 @@ export const CurrencyPickerDropdownSelect = ({
</>
)}
</DropdownMenuItemsContainer>
</DropdownMenu>
</DropdownContent>
);
};

View File

@ -6,6 +6,7 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import 'react-phone-number-input/style.css';
import { MenuItem, MenuItemSelectAvatar } from 'twenty-ui/navigation';
@ -46,7 +47,7 @@ export const PhoneCountryPickerDropdownSelect = ({
);
return (
<>
<DropdownContent>
<DropdownMenuSearchInput
value={searchFilter}
onChange={(event) => setSearchFilter(event.currentTarget.value)}
@ -90,6 +91,6 @@ export const PhoneCountryPickerDropdownSelect = ({
</>
)}
</DropdownMenuItemsContainer>
</>
</DropdownContent>
);
};

View File

@ -1,7 +1,7 @@
import { SuggestionMenuProps } from '@blocknote/react';
import styled from '@emotion/styled';
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { OverlayContainer } from '@/ui/layout/overlay/components/OverlayContainer';
import { autoUpdate, useFloating } from '@floating-ui/react';
@ -46,7 +46,7 @@ export const CustomSlashMenu = (props: CustomSlashMenuProps) => {
>
<OverlayContainer ref={refs.setFloating} style={floatingStyles}>
<StyledInnerContainer>
<DropdownMenu style={{ zIndex: 2001 }}>
<DropdownContent>
<DropdownMenuItemsContainer>
{props.items.map((item, index) => (
<MenuItemSuggestion
@ -58,7 +58,7 @@ export const CustomSlashMenu = (props: CustomSlashMenuProps) => {
/>
))}
</DropdownMenuItemsContainer>
</DropdownMenu>
</DropdownContent>
</StyledInnerContainer>
</OverlayContainer>
</motion.div>,

View File

@ -1,5 +1,5 @@
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownOnToggleEffect } from '@/ui/layout/dropdown/components/DropdownOnToggleEffect';
import { DropdownInternalContainer } from '@/ui/layout/dropdown/components/internal/DropdownInternalContainer';
import { DROPDOWN_RESIZE_MIN_HEIGHT } from '@/ui/layout/dropdown/constants/DropdownResizeMinHeight';
import { DROPDOWN_RESIZE_MIN_WIDTH } from '@/ui/layout/dropdown/constants/DropdownResizeMinWidth';
import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponeInstanceContext';
@ -52,18 +52,17 @@ export type DropdownProps = {
dropdownHotkeyScope: HotkeyScope;
dropdownId: string;
dropdownPlacement?: Placement;
dropdownWidth?: Width;
dropdownOffset?: DropdownOffset;
dropdownStrategy?: 'fixed' | 'absolute';
onClickOutside?: () => void;
onClose?: () => void;
onOpen?: () => void;
excludedClickOutsideIds?: string[];
};
export const Dropdown = ({
clickableComponent,
dropdownComponents,
dropdownWidth,
hotkey,
dropdownId,
dropdownHotkeyScope,
@ -74,6 +73,7 @@ export const Dropdown = ({
onClose,
onOpen,
clickableComponentWidth = 'auto',
excludedClickOutsideIds,
}: DropdownProps) => {
const { isDropdownOpen, toggleDropdown } = useDropdown(dropdownId);
@ -182,9 +182,8 @@ export const Dropdown = ({
<StyledDropdownFallbackAnchor ref={refs.setReference} />
)}
{isDropdownOpen && (
<DropdownContent
<DropdownInternalContainer
floatingStyles={floatingStyles}
dropdownWidth={dropdownWidth}
dropdownComponents={dropdownComponents}
dropdownId={dropdownId}
dropdownPlacement={placement}
@ -193,6 +192,7 @@ export const Dropdown = ({
hotkey={hotkey}
onClickOutside={onClickOutside}
onHotkeyTriggered={toggleDropdown}
excludedClickOutsideIds={excludedClickOutsideIds}
/>
)}
<DropdownOnToggleEffect

View File

@ -1,149 +1,37 @@
import { RootStackingContextZIndices } from '@/ui/layout/constants/RootStackingContextZIndices';
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { useInternalHotkeyScopeManagement } from '@/ui/layout/dropdown/hooks/useInternalHotkeyScopeManagement';
import { activeDropdownFocusIdState } from '@/ui/layout/dropdown/states/activeDropdownFocusIdState';
import { dropdownMaxHeightComponentState } from '@/ui/layout/dropdown/states/internal/dropdownMaxHeightComponentState';
import { dropdownMaxWidthComponentState } from '@/ui/layout/dropdown/states/internal/dropdownMaxWidthComponentState';
import { OverlayContainer } from '@/ui/layout/overlay/components/OverlayContainer';
import { HotkeyEffect } from '@/ui/utilities/hotkey/components/HotkeyEffect';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { ClickOutsideListenerContext } from '@/ui/utilities/pointer-event/contexts/ClickOutsideListenerContext';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
import styled from '@emotion/styled';
import {
FloatingPortal,
Placement,
UseFloatingReturn,
} from '@floating-ui/react';
import { useContext, useEffect } from 'react';
import { Keys } from 'react-hotkeys-hook';
import { useRecoilValue } from 'recoil';
import { Key } from 'ts-key-enum';
import { Ref, forwardRef } from 'react';
export const StyledDropdownContentContainer = styled.div`
const StyledInternalBaseDropdownContent = styled.div<{
widthInPixels: number;
}>`
display: flex;
z-index: ${RootStackingContextZIndices.DropdownPortal};
flex-direction: column;
height: 100%;
width: ${({ widthInPixels }) => widthInPixels}px;
`;
export type DropdownContentProps = {
dropdownId: string;
dropdownPlacement: Placement;
floatingUiRefs: UseFloatingReturn['refs'];
onClickOutside?: () => void;
hotkeyScope: HotkeyScope;
floatingStyles: UseFloatingReturn['floatingStyles'];
hotkey?: {
key: Keys;
scope: string;
};
onHotkeyTriggered?: () => void;
dropdownWidth?: `${string}px` | `${number}%` | 'auto' | number;
dropdownComponents: React.ReactNode;
parentDropdownId?: string;
};
export const DropdownContent = ({
dropdownId,
dropdownPlacement,
floatingUiRefs,
onClickOutside,
hotkeyScope,
floatingStyles,
hotkey,
onHotkeyTriggered,
dropdownWidth,
dropdownComponents,
}: DropdownContentProps) => {
const { isDropdownOpen, closeDropdown, setDropdownPlacement } =
useDropdown(dropdownId);
const activeDropdownFocusId = useRecoilValue(activeDropdownFocusIdState);
const dropdownMaxHeight = useRecoilComponentValueV2(
dropdownMaxHeightComponentState,
dropdownId,
);
const dropdownMaxWidth = useRecoilComponentValueV2(
dropdownMaxWidthComponentState,
dropdownId,
);
useEffect(() => {
setDropdownPlacement(dropdownPlacement);
}, [dropdownPlacement, setDropdownPlacement]);
useListenClickOutside({
refs: [floatingUiRefs.floating, floatingUiRefs.domReference],
listenerId: dropdownId,
callback: (event) => {
if (activeDropdownFocusId !== dropdownId) return;
if (isDropdownOpen) {
event.stopImmediatePropagation();
event.preventDefault();
closeDropdown();
}
onClickOutside?.();
},
});
useInternalHotkeyScopeManagement({
dropdownScopeId: dropdownId,
dropdownHotkeyScopeFromParent: hotkeyScope,
});
useScopedHotkeys(
[Key.Escape],
() => {
if (activeDropdownFocusId !== dropdownId) return;
if (isDropdownOpen) {
closeDropdown();
}
},
hotkeyScope?.scope,
[closeDropdown, isDropdownOpen],
);
const dropdownMenuStyles = {
...floatingStyles,
maxHeight: dropdownMaxHeight,
maxWidth: dropdownMaxWidth,
};
const { excludedClickOutsideId } = useContext(ClickOutsideListenerContext);
return (
<>
{hotkey && onHotkeyTriggered && (
<HotkeyEffect hotkey={hotkey} onHotkeyTriggered={onHotkeyTriggered} />
)}
<FloatingPortal>
<StyledDropdownContentContainer
ref={floatingUiRefs.setFloating}
style={dropdownMenuStyles}
role="listbox"
id={`${dropdownId}-options`}
data-click-outside-id={excludedClickOutsideId}
>
<OverlayContainer>
<DropdownMenu
id={dropdownId}
width={dropdownWidth}
data-select-disable
>
{dropdownComponents}
</DropdownMenu>
</OverlayContainer>
</StyledDropdownContentContainer>
</FloatingPortal>
</>
);
};
export const DropdownContent = forwardRef(
(
{
children,
widthInPixels = GenericDropdownContentWidth.Medium,
selectDisabled = false,
}: React.PropsWithChildren<{
widthInPixels?: number;
selectDisabled?: boolean;
}>,
ref: Ref<HTMLDivElement>,
) => {
return (
<StyledInternalBaseDropdownContent
data-select-disable={selectDisabled}
widthInPixels={widthInPixels}
ref={ref}
>
{children}
</StyledInternalBaseDropdownContent>
);
},
);

View File

@ -1,19 +0,0 @@
import styled from '@emotion/styled';
import { isDefined } from 'twenty-shared/utils';
const StyledDropdownMenu = styled.div<{
width?: `${string}px` | `${number}%` | 'auto' | number;
}>`
display: flex;
flex-direction: column;
height: 100%;
width: ${({ width }) =>
isDefined(width)
? typeof width === 'number'
? `${width}px`
: width
: 'auto'};
`;
export const DropdownMenu = StyledDropdownMenu;

View File

@ -12,6 +12,9 @@ const StyledHeader = styled.li`
border-top-left-radius: ${({ theme }) => theme.border.radius.sm};
border-top-right-radius: ${({ theme }) => theme.border.radius.sm};
padding: ${({ theme }) => theme.spacing(1)};
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
height: ${({ theme }) => theme.spacing(6)};
user-select: none;

View File

@ -6,7 +6,7 @@ import { isDefined } from 'twenty-shared/utils';
const StyledDropdownMenuItemsExternalContainer = styled.div<{
hasMaxHeight?: boolean;
width: number | 'auto';
width: number | 'auto' | '100%';
}>`
--padding: ${({ theme }) => theme.spacing(1)};
@ -19,10 +19,13 @@ const StyledDropdownMenuItemsExternalContainer = styled.div<{
padding: var(--padding);
${({ width }) =>
isDefined(width) &&
css`
width: ${width}px;
`}
isDefined(width) && width === '100%'
? css`
width: 100%;
`
: css`
width: ${width}px;
`}
`;
const StyledDropdownMenuItemsInternalContainer = styled.div`
@ -31,6 +34,7 @@ const StyledDropdownMenuItemsInternalContainer = styled.div`
flex-direction: column;
gap: 2px;
height: 100%;
width: 100%;
`;
@ -39,20 +43,18 @@ const StyledScrollWrapper = styled(ScrollWrapper)`
width: 100%;
`;
// TODO: refactor this, the dropdown should handle the max height behavior + scroll with the size middleware
// We should instead create a DropdownMenuItemsContainerScrollable or take for granted that it is the default behavior
export const DropdownMenuItemsContainer = ({
children,
hasMaxHeight,
className,
width = 200,
scrollable = true,
width = 'auto',
}: {
children: React.ReactNode;
hasMaxHeight?: boolean;
className?: string;
scrollable?: boolean;
width?: number | 'auto';
width?: number | 'auto' | '100%';
}) => {
const id = useId();

View File

@ -32,7 +32,6 @@ const meta: Meta<typeof Dropdown> = {
dropdownHotkeyScope: { scope: 'testDropdownMenu' },
dropdownOffset: { x: 0, y: 8 },
dropdownId: 'test-dropdown-id',
dropdownWidth: '200px',
},
argTypes: {
clickableComponent: { control: false },

View File

@ -2,10 +2,10 @@ import { Meta, StoryObj } from '@storybook/react';
import { SelectHotkeyScope } from '@/ui/input/types/SelectHotkeyScope';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { AVATAR_URL_MOCK, ComponentDecorator } from 'twenty-ui/testing';
import {
Avatar,
IconChevronLeft,
@ -13,6 +13,7 @@ import {
IconPlus,
} from 'twenty-ui/display';
import { MenuItem } from 'twenty-ui/navigation';
import { AVATAR_URL_MOCK, ComponentDecorator } from 'twenty-ui/testing';
const meta: Meta<typeof DropdownMenuHeader> = {
title: 'UI/Layout/Dropdown/DropdownMenuHeader',
@ -64,9 +65,11 @@ export const ContextDropdownAndAvatar: Story = {
dropdownId={'story-dropdown-id-context-menu'}
dropdownHotkeyScope={{ scope: SelectHotkeyScope.Select }}
dropdownComponents={
<DropdownMenuItemsContainer>
<MenuItem LeftIcon={IconPlus} text={`Create Workspace`} />
</DropdownMenuItemsContainer>
<DropdownContent>
<DropdownMenuItemsContainer>
<MenuItem LeftIcon={IconPlus} text={`Create Workspace`} />
</DropdownMenuItemsContainer>
</DropdownContent>
}
/>
),

View File

@ -0,0 +1,153 @@
import { RootStackingContextZIndices } from '@/ui/layout/constants/RootStackingContextZIndices';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { useInternalHotkeyScopeManagement } from '@/ui/layout/dropdown/hooks/useInternalHotkeyScopeManagement';
import { activeDropdownFocusIdState } from '@/ui/layout/dropdown/states/activeDropdownFocusIdState';
import { dropdownMaxHeightComponentState } from '@/ui/layout/dropdown/states/internal/dropdownMaxHeightComponentState';
import { dropdownMaxWidthComponentState } from '@/ui/layout/dropdown/states/internal/dropdownMaxWidthComponentState';
import { OverlayContainer } from '@/ui/layout/overlay/components/OverlayContainer';
import { HotkeyEffect } from '@/ui/utilities/hotkey/components/HotkeyEffect';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { ClickOutsideListenerContext } from '@/ui/utilities/pointer-event/contexts/ClickOutsideListenerContext';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import styled from '@emotion/styled';
import {
FloatingPortal,
Placement,
UseFloatingReturn,
} from '@floating-ui/react';
import { useContext, useEffect } from 'react';
import { Keys } from 'react-hotkeys-hook';
import { useRecoilValue } from 'recoil';
import { Key } from 'ts-key-enum';
export const StyledDropdownContentContainer = styled.div`
display: flex;
z-index: ${RootStackingContextZIndices.DropdownPortal};
`;
const StyledDropdownInsideContainer = styled.div`
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
`;
export type DropdownInternalContainerProps = {
dropdownId: string;
dropdownPlacement: Placement;
floatingUiRefs: UseFloatingReturn['refs'];
onClickOutside?: () => void;
hotkeyScope: HotkeyScope;
floatingStyles: UseFloatingReturn['floatingStyles'];
hotkey?: {
key: Keys;
scope: string;
};
onHotkeyTriggered?: () => void;
dropdownComponents: React.ReactNode;
parentDropdownId?: string;
excludedClickOutsideIds?: string[];
};
export const DropdownInternalContainer = ({
dropdownId,
dropdownPlacement,
floatingUiRefs,
onClickOutside,
hotkeyScope,
floatingStyles,
hotkey,
onHotkeyTriggered,
dropdownComponents,
excludedClickOutsideIds,
}: DropdownInternalContainerProps) => {
const { isDropdownOpen, closeDropdown, setDropdownPlacement } =
useDropdown(dropdownId);
const activeDropdownFocusId = useRecoilValue(activeDropdownFocusIdState);
const dropdownMaxHeight = useRecoilComponentValueV2(
dropdownMaxHeightComponentState,
dropdownId,
);
const dropdownMaxWidth = useRecoilComponentValueV2(
dropdownMaxWidthComponentState,
dropdownId,
);
useEffect(() => {
setDropdownPlacement(dropdownPlacement);
}, [dropdownPlacement, setDropdownPlacement]);
useListenClickOutside({
refs: [floatingUiRefs.floating, floatingUiRefs.domReference],
listenerId: dropdownId,
callback: (event) => {
if (activeDropdownFocusId !== dropdownId) return;
if (isDropdownOpen) {
event.stopImmediatePropagation();
event.preventDefault();
closeDropdown();
}
onClickOutside?.();
},
excludedClickOutsideIds,
});
useInternalHotkeyScopeManagement({
dropdownScopeId: dropdownId,
dropdownHotkeyScopeFromParent: hotkeyScope,
});
useScopedHotkeys(
[Key.Escape],
() => {
if (activeDropdownFocusId !== dropdownId) return;
if (isDropdownOpen) {
closeDropdown();
}
},
hotkeyScope?.scope,
[closeDropdown, isDropdownOpen],
);
const dropdownMenuStyles = {
...floatingStyles,
maxHeight: dropdownMaxHeight,
maxWidth: dropdownMaxWidth,
};
const { excludedClickOutsideId } = useContext(ClickOutsideListenerContext);
return (
<>
{hotkey && onHotkeyTriggered && (
<HotkeyEffect hotkey={hotkey} onHotkeyTriggered={onHotkeyTriggered} />
)}
<FloatingPortal>
<StyledDropdownContentContainer
ref={floatingUiRefs.setFloating}
style={dropdownMenuStyles}
role="listbox"
id={`${dropdownId}-options`}
data-click-outside-id={excludedClickOutsideId}
>
<OverlayContainer>
<StyledDropdownInsideContainer id={dropdownId} data-select-disable>
{dropdownComponents}
</StyledDropdownInsideContainer>
</OverlayContainer>
</StyledDropdownContentContainer>
</FloatingPortal>
</>
);
};

View File

@ -0,0 +1,6 @@
export enum GenericDropdownContentWidth {
Narrow = 160,
Medium = 200,
Large = 240,
ExtraLarge = 320,
}

View File

@ -1,12 +1,11 @@
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { StyledDropdownContentContainer } from '@/ui/layout/dropdown/components/internal/DropdownInternalContainer';
import { OverlayContainer } from '@/ui/layout/overlay/components/OverlayContainer';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import styled from '@emotion/styled';
import { FloatingPortal, offset, shift, useFloating } from '@floating-ui/react';
import { ReactNode } from 'react';
import { StyledDropdownContentContainer } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
import { OverlayContainer } from '@/ui/layout/overlay/components/OverlayContainer';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
type ExpandedListDropdownProps = {
anchorElement?: HTMLElement;
children: ReactNode;
@ -40,6 +39,10 @@ export const ExpandedListDropdown = ({
listenerId: 'expandable-list',
});
const dropdownContentWidth = anchorElement
? Math.max(220, anchorElement.getBoundingClientRect().width)
: undefined;
return (
<FloatingPortal>
<StyledDropdownContentContainer
@ -47,17 +50,11 @@ export const ExpandedListDropdown = ({
style={floatingStyles}
>
<OverlayContainer>
<DropdownMenu
width={
anchorElement
? Math.max(220, anchorElement.getBoundingClientRect().width)
: undefined
}
>
<DropdownContent widthInPixels={dropdownContentWidth}>
<StyledExpandedListContainer>
{children}
</StyledExpandedListContainer>
</DropdownMenu>
</DropdownContent>
</OverlayContainer>
</StyledDropdownContentContainer>
</FloatingPortal>

View File

@ -9,7 +9,6 @@ export const OverlayContainer = styled.div<{
display: flex;
backdrop-filter: ${({ theme }) => theme.blur.medium};
width: fit-content;
border-radius: ${({ theme, borderRadius }) =>
theme.border.radius[borderRadius ?? 'md']};

View File

@ -1,101 +0,0 @@
import styled from '@emotion/styled';
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { SHOW_PAGE_ADD_BUTTON_DROPDOWN_ID } from '@/ui/layout/show-page/constants/ShowPageAddButtonDropdownId';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { isWorkflowSubObjectMetadata } from '@/object-metadata/utils/isWorkflowSubObjectMetadata';
import { useHasObjectReadOnlyPermission } from '@/settings/roles/hooks/useHasObjectReadOnlyPermission';
import { Dropdown } from '../../dropdown/components/Dropdown';
import { Button } from 'twenty-ui/input';
import { IconCheckbox, IconNotes, IconPlus } from 'twenty-ui/display';
import { MenuItem } from 'twenty-ui/navigation';
const StyledContainer = styled.div`
z-index: 1;
`;
export const ShowPageAddButton = ({
activityTargetObject,
}: {
activityTargetObject: ActivityTargetableObject;
}) => {
const { closeDropdown } = useDropdown(SHOW_PAGE_ADD_BUTTON_DROPDOWN_ID);
const openNote = useOpenCreateActivityDrawer({
activityObjectNameSingular: CoreObjectNameSingular.Note,
});
const openTask = useOpenCreateActivityDrawer({
activityObjectNameSingular: CoreObjectNameSingular.Task,
});
const hasObjectReadOnlyPermission = useHasObjectReadOnlyPermission();
const handleSelect = (objectNameSingular: CoreObjectNameSingular) => {
if (objectNameSingular === CoreObjectNameSingular.Note) {
openNote({
targetableObjects: [activityTargetObject],
});
} else if (objectNameSingular === CoreObjectNameSingular.Task) {
openTask({
targetableObjects: [activityTargetObject],
});
}
closeDropdown();
};
if (
activityTargetObject.targetObjectNameSingular ===
CoreObjectNameSingular.Task ||
activityTargetObject.targetObjectNameSingular ===
CoreObjectNameSingular.Note ||
isWorkflowSubObjectMetadata(activityTargetObject.targetObjectNameSingular)
) {
return;
}
if (hasObjectReadOnlyPermission) {
return null;
}
return (
<StyledContainer>
<Dropdown
dropdownId={SHOW_PAGE_ADD_BUTTON_DROPDOWN_ID}
clickableComponent={
<Button
Icon={IconPlus}
dataTestId="add-button"
size="small"
variant="secondary"
accent="default"
title="New note/task"
ariaLabel="New note/task"
/>
}
dropdownComponents={
<DropdownMenuItemsContainer>
<MenuItem
onClick={() => handleSelect(CoreObjectNameSingular.Note)}
accent="default"
LeftIcon={IconNotes}
text="Note"
/>
<MenuItem
onClick={() => handleSelect(CoreObjectNameSingular.Task)}
accent="default"
LeftIcon={IconCheckbox}
text="Task"
/>
</DropdownMenuItemsContainer>
}
dropdownHotkeyScope={{ scope: PageHotkeyScope.ShowPage }}
/>
</StyledContainer>
);
};

View File

@ -11,6 +11,7 @@ import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/Snac
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { SelectHotkeyScope } from '@/ui/input/types/SelectHotkeyScope';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
@ -92,7 +93,7 @@ export const MultiWorkspaceDropdownDefaultComponents = () => {
};
return (
<>
<DropdownContent>
<DropdownMenuHeader
StartComponent={
<DropdownMenuHeaderLeftComponent
@ -116,20 +117,21 @@ export const MultiWorkspaceDropdownDefaultComponents = () => {
dropdownId={'multi-workspace-dropdown-context-menu'}
dropdownHotkeyScope={{ scope: SelectHotkeyScope.Select }}
dropdownComponents={
<DropdownMenuItemsContainer>
<MenuItem
LeftIcon={IconPlus}
text={t`Create Workspace`}
onClick={createWorkspace}
/>
</DropdownMenuItemsContainer>
<DropdownContent>
<DropdownMenuItemsContainer>
<MenuItem
LeftIcon={IconPlus}
text={t`Create Workspace`}
onClick={createWorkspace}
/>
</DropdownMenuItemsContainer>
</DropdownContent>
}
/>
}
>
{currentWorkspace?.displayName}
</DropdownMenuHeader>
<DropdownMenuSeparator />
{workspaces.length > 1 && (
<>
<StyledDropdownMenuItemsContainer>
@ -193,6 +195,6 @@ export const MultiWorkspaceDropdownDefaultComponents = () => {
</UndecoratedLink>
<MenuItem LeftIcon={IconLogout} text={t`Log out`} onClick={signOut} />
</DropdownMenuItemsContainer>
</>
</DropdownContent>
);
};

View File

@ -1,3 +1,4 @@
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
@ -7,7 +8,6 @@ import { useLingui } from '@lingui/react/macro';
import { useSetRecoilState } from 'recoil';
import { IconCheck, IconChevronLeft } from 'twenty-ui/display';
import { MenuItem } from 'twenty-ui/navigation';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
export const MultiWorkspaceDropdownThemesComponents = () => {
const { t } = useLingui();
@ -19,7 +19,7 @@ export const MultiWorkspaceDropdownThemesComponents = () => {
);
return (
<>
<DropdownContent>
<DropdownMenuHeader
StartComponent={
<DropdownMenuHeaderLeftComponent
@ -30,7 +30,6 @@ export const MultiWorkspaceDropdownThemesComponents = () => {
>
{t`Theme`}
</DropdownMenuHeader>
<DropdownMenuSeparator />
<DropdownMenuItemsContainer>
{colorSchemeList.map((theme) => (
<MenuItem
@ -43,6 +42,6 @@ export const MultiWorkspaceDropdownThemesComponents = () => {
/>
))}
</DropdownMenuItemsContainer>
</>
</DropdownContent>
);
};

View File

@ -2,6 +2,7 @@ import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { Workspaces, workspacesState } from '@/auth/states/workspaces';
import { useBuildWorkspaceUrl } from '@/domain-manager/hooks/useBuildWorkspaceUrl';
import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirectToWorkspaceDomain';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
@ -32,7 +33,7 @@ export const MultiWorkspaceDropdownWorkspacesListComponents = () => {
const [searchValue, setSearchValue] = useState('');
return (
<>
<DropdownContent>
<DropdownMenuHeader
StartComponent={
<DropdownMenuHeaderLeftComponent
@ -43,7 +44,6 @@ export const MultiWorkspaceDropdownWorkspacesListComponents = () => {
>
{t`Other workspaces`}
</DropdownMenuHeader>
<DropdownMenuSeparator />
<DropdownMenuSearchInput
placeholder={t`Search`}
autoFocus
@ -83,6 +83,6 @@ export const MultiWorkspaceDropdownWorkspacesListComponents = () => {
</UndecoratedLink>
))}
</DropdownMenuItemsContainer>
</>
</DropdownContent>
);
};

Some files were not shown because too many files have changed in this diff Show More