From 01b40e173bae4c95100a901aa75b2f8ff446de34 Mon Sep 17 00:00:00 2001 From: Lucas Bordeau Date: Tue, 27 May 2025 19:44:13 +0200 Subject: [PATCH] Refactored dropdown content and fixed all dropdown width bugs (#12334) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 `` 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 |   ## 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 --- .../CommandMenuActionMenuDropdown.tsx | 29 +-- .../RecordIndexActionMenuDropdown.tsx | 61 +++--- .../components/EmailThreadMembersChip.tsx | 16 -- .../MessageThreadSubscribersChip.tsx | 75 -------- ...MessageThreadSubscribersDropdownButton.tsx | 107 ----------- .../files/components/AttachmentDropdown.tsx | 41 ++-- .../components/EmailVerificationSent.tsx | 2 +- .../CommandMenuContextChipGroups.tsx | 33 ++-- ...riteFolderNavigationDrawerItemDropdown.tsx | 32 ++-- .../components/PageFavoriteFolderDropdown.tsx | 50 ----- .../components/FavoriteFolderPicker.tsx | 6 +- .../AdvancedFilterAddFilterRuleSelect.tsx | 27 +-- .../AdvancedFilterDropdownFilterInput.tsx | 21 +-- .../AdvancedFilterFieldSelectMenu.tsx | 5 +- .../AdvancedFilterLogicalOperatorDropdown.tsx | 2 + ...FilterRecordFilterGroupOptionsDropdown.tsx | 21 ++- ...dvancedFilterRecordFilterOperandSelect.tsx | 55 +++--- ...ancedFilterRecordFilterOptionsDropdown.tsx | 21 ++- .../AdvancedFilterRootRecordFilterGroup.tsx | 55 +++--- .../AdvancedFilterSubFieldSelectMenu.tsx | 5 +- .../components/AdvancedFilterValueInput.tsx | 1 - .../AdvancedFilterDropdownContentWidth.ts | 1 + .../ObjectFilterDropdownBooleanSelect.tsx | 47 ++--- .../ObjectFilterDropdownCountrySelect.tsx | 15 +- .../ObjectFilterDropdownCurrencySelect.tsx | 6 +- .../ObjectFilterDropdownFilterInput.tsx | 128 +++++-------- .../ObjectFilterDropdownOperandDropdown.tsx | 42 +++-- .../ObjectFilterDropdownOperandSelect.tsx | 27 +-- .../ObjectFilterOperandSelectAndInput.tsx | 19 -- .../DatePickerDropdownContentWidth.ts | 1 + .../ObjectOptionsDropdownFieldsContent.tsx | 7 +- ...jectOptionsDropdownHiddenFieldsContent.tsx | 9 +- ...tionsDropdownHiddenRecordGroupsContent.tsx | 32 ++-- .../ObjectOptionsDropdownLayoutContent.tsx | 5 +- ...jectOptionsDropdownLayoutOpenInContent.tsx | 5 +- .../ObjectOptionsDropdownMenuContent.tsx | 8 +- ...ptionsDropdownRecordGroupFieldsContent.tsx | 6 +- ...tOptionsDropdownRecordGroupSortContent.tsx | 5 +- ...jectOptionsDropdownRecordGroupsContent.tsx | 5 +- .../ObjectOptionsDropdownContent.stories.tsx | 8 +- .../components/ObjectSortDropdownButton.tsx | 164 ++++++++-------- .../RecordBoardColumnDropdownMenu.tsx | 7 +- ...cordBoardColumnHeaderAggregateDropdown.tsx | 2 - ...mnHeaderAggregateDropdownFieldsContent.tsx | 7 +- ...lumnHeaderAggregateDropdownMenuContent.tsx | 5 +- ...nHeaderAggregateDropdownOptionsContent.tsx | 7 +- .../input/components/MultiItemFieldInput.tsx | 6 +- .../components/MultiItemFieldMenuItem.tsx | 39 ++-- .../RecordIndexAddRecordInGroupDropdown.tsx | 99 ---------- .../components/MultipleRecordPicker.tsx | 6 +- .../components/SingleRecordPicker.tsx | 6 +- .../RecordDetailRelationRecordsListItem.tsx | 29 +-- ...eColumnAggregateDropdownSubmenuContent.tsx | 7 +- ...dTableColumnAggregateFooterMenuContent.tsx | 7 +- .../RecordTableColumnHeadDropdownMenu.tsx | 79 ++++---- .../RecordTableHeaderPlusButtonContent.tsx | 7 +- .../components/MultipleSelectDropdown.tsx | 83 +++++---- .../SettingsAccountsRowDropdownMenu.tsx | 68 +++---- .../ConfigVariableDatabaseInput.tsx | 29 +-- .../ConfigVariableOptionsDropdownContent.tsx | 9 +- ...ngsDataModelNewFieldBreadcrumbDropDown.tsx | 12 +- ...tingsDataModelFieldSelectFormOptionRow.tsx | 38 ++-- ...ettingsObjectFieldActiveActionDropdown.tsx | 43 +++-- ...tingsObjectFieldDisabledActionDropdown.tsx | 41 ++-- .../components/SettingsObjectSummaryCard.tsx | 103 ---------- .../SettingsObjectInactiveMenuDropDown.tsx | 31 +-- ...tegrationDatabaseConnectionSummaryCard.tsx | 23 ++- ...ssignmentWorkspaceMemberPickerDropdown.tsx | 6 +- ...ObjectLevelObjectPickerDropdownContent.tsx | 6 +- .../SettingsSecuritySSORowDropdownMenu.tsx | 44 ++--- ...ityApprovedAccessDomainRowDropdownMenu.tsx | 26 +-- ...FunctionTabEnvironmentVariableTableRow.tsx | 39 ++-- ...ColumnSelectFieldSelectDropdownContent.tsx | 7 +- ...umnSelectSubFieldSelectDropdownContent.tsx | 7 +- .../components/SubMatchingSelectInput.tsx | 6 +- .../support/components/SupportButton.tsx | 2 +- .../support/components/SupportDropdown.tsx | 29 +-- .../input/components/MultiSelectInput.tsx | 6 +- .../ui/input/components/IconPicker.tsx | 20 +- .../modules/ui/input/components/Select.tsx | 11 +- .../ui/input/components/SelectInput.tsx | 6 +- .../IconPickerDropdownContentWidth.ts | 1 + .../CurrencyPickerDropdownSelect.tsx | 6 +- .../PhoneCountryPickerDropdownSelect.tsx | 5 +- .../editor/components/CustomSlashMenu.tsx | 6 +- .../layout/dropdown/components/Dropdown.tsx | 10 +- .../dropdown/components/DropdownContent.tsx | 176 ++++-------------- .../dropdown/components/DropdownMenu.tsx | 19 -- .../DropdownMenuHeader/DropdownMenuHeader.tsx | 3 + .../components/DropdownMenuItemsContainer.tsx | 20 +- .../__stories__/Dropdown.stories.tsx | 1 - .../DropdownMenuHeader.stories.tsx | 11 +- .../internal/DropdownInternalContainer.tsx | 153 +++++++++++++++ .../constants/GenericDropdownContentWidth.ts | 6 + .../components/ExpandedListDropdown.tsx | 23 +-- .../overlay/components/OverlayContainer.tsx | 1 - .../components/ShowPageAddButton.tsx | 101 ---------- ...ultiWorkspaceDropdownDefaultComponents.tsx | 22 ++- ...MultiWorkspaceDropdownThemesComponents.tsx | 7 +- ...kspaceDropdownWorkspacesListComponents.tsx | 6 +- .../AdvancedFilterDropdownButton.tsx | 1 - .../views/components/EditableFilterChip.tsx | 3 +- .../EditableFilterDropdownButton.tsx | 7 +- .../components/UpdateViewButtonGroup.tsx | 19 +- .../components/ViewBarFilterDropdown.tsx | 2 + .../ViewBarFilterDropdownContent.tsx | 10 +- .../ViewBarFilterDropdownFieldSelectMenu.tsx | 9 +- .../ViewPickerContentCreateMode.tsx | 10 +- .../components/ViewPickerContentEditMode.tsx | 6 +- .../components/ViewPickerListContent.tsx | 5 +- .../components/ViewPickerOptionDropdown.tsx | 55 +++--- .../WorkflowEditTriggerDatabaseEventForm.tsx | 9 +- .../WorkflowVariablesDropdownFieldItems.tsx | 5 +- .../WorkflowVariablesDropdownObjectItems.tsx | 5 +- ...flowVariablesDropdownWorkflowStepItems.tsx | 5 +- .../RecordShowPageBaseHeader.tsx | 57 ------ 116 files changed, 1243 insertions(+), 1755 deletions(-) delete mode 100644 packages/twenty-front/src/modules/activities/emails/components/EmailThreadMembersChip.tsx delete mode 100644 packages/twenty-front/src/modules/activities/emails/components/MessageThreadSubscribersChip.tsx delete mode 100644 packages/twenty-front/src/modules/activities/emails/components/MessageThreadSubscribersDropdownButton.tsx delete mode 100644 packages/twenty-front/src/modules/favorites/components/PageFavoriteFolderDropdown.tsx create mode 100644 packages/twenty-front/src/modules/object-record/advanced-filter/constants/AdvancedFilterDropdownContentWidth.ts delete mode 100644 packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterOperandSelectAndInput.tsx create mode 100644 packages/twenty-front/src/modules/object-record/object-filter-dropdown/constants/DatePickerDropdownContentWidth.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexAddRecordInGroupDropdown.tsx delete mode 100644 packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectSummaryCard.tsx create mode 100644 packages/twenty-front/src/modules/ui/input/components/constants/IconPickerDropdownContentWidth.ts delete mode 100644 packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenu.tsx create mode 100644 packages/twenty-front/src/modules/ui/layout/dropdown/components/internal/DropdownInternalContainer.tsx create mode 100644 packages/twenty-front/src/modules/ui/layout/dropdown/constants/GenericDropdownContentWidth.ts delete mode 100644 packages/twenty-front/src/modules/ui/layout/show-page/components/ShowPageAddButton.tsx delete mode 100644 packages/twenty-front/src/pages/object-record/RecordShowPageBaseHeader.tsx diff --git a/packages/twenty-front/src/modules/action-menu/components/CommandMenuActionMenuDropdown.tsx b/packages/twenty-front/src/modules/action-menu/components/CommandMenuActionMenuDropdown.tsx index 3592a1d2f..683a109b7 100644 --- a/packages/twenty-front/src/modules/action-menu/components/CommandMenuActionMenuDropdown.tsx +++ b/packages/twenty-front/src/modules/action-menu/components/CommandMenuActionMenuDropdown.tsx @@ -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={ - - - {recordSelectionActions.map((action) => ( - - ))} - - + + + + {recordSelectionActions.map((action) => ( + + ))} + + + } /> ); diff --git a/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuDropdown.tsx b/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuDropdown.tsx index e2aa47b19..669835403 100644 --- a/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuDropdown.tsx +++ b/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuDropdown.tsx @@ -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={ - - - - {recordIndexActions.map((action) => ( - - ))} - { - closeDropdown(dropdownId); - openCommandMenu(); - }} + + + + - { + {recordIndexActions.map((action) => ( + + ))} + { closeDropdown(dropdownId); openCommandMenu(); }} - focused={selectedItemId === 'more-actions'} - text="More actions" - /> - - - - + > + { + closeDropdown(dropdownId); + openCommandMenu(); + }} + focused={selectedItemId === 'more-actions'} + text="More actions" + /> + + + + + } /> ); diff --git a/packages/twenty-front/src/modules/activities/emails/components/EmailThreadMembersChip.tsx b/packages/twenty-front/src/modules/activities/emails/components/EmailThreadMembersChip.tsx deleted file mode 100644 index 192b1ed3a..000000000 --- a/packages/twenty-front/src/modules/activities/emails/components/EmailThreadMembersChip.tsx +++ /dev/null @@ -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 ( - - ); -}; diff --git a/packages/twenty-front/src/modules/activities/emails/components/MessageThreadSubscribersChip.tsx b/packages/twenty-front/src/modules/activities/emails/components/MessageThreadSubscribersChip.tsx deleted file mode 100644 index e78e29159..000000000 --- a/packages/twenty-front/src/modules/activities/emails/components/MessageThreadSubscribersChip.tsx +++ /dev/null @@ -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 ( - - ) : ( - ( - - ))} - /> - ) - } - rightComponent={() => } - clickable - /> - ); -}; diff --git a/packages/twenty-front/src/modules/activities/emails/components/MessageThreadSubscribersDropdownButton.tsx b/packages/twenty-front/src/modules/activities/emails/components/MessageThreadSubscribersDropdownButton.tsx deleted file mode 100644 index 1c35a91a4..000000000 --- a/packages/twenty-front/src/modules/activities/emails/components/MessageThreadSubscribersDropdownButton.tsx +++ /dev/null @@ -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 ( - - } - dropdownComponents={ - - {isAddingSubscriber ? ( - - ) : ( - - {messageThreadSubscribers?.map((subscriber) => ( - { - 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); - }, - }, - ]} - /> - ))} - - - - )} - - } - dropdownHotkeyScope={{ scope: MESSAGE_THREAD_SUBSCRIBER_DROPDOWN_ID }} - /> - ); -}; diff --git a/packages/twenty-front/src/modules/activities/files/components/AttachmentDropdown.tsx b/packages/twenty-front/src/modules/activities/files/components/AttachmentDropdown.tsx index 4a3bddc56..0d29c1879 100644 --- a/packages/twenty-front/src/modules/activities/files/components/AttachmentDropdown.tsx +++ b/packages/twenty-front/src/modules/activities/files/components/AttachmentDropdown.tsx @@ -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={ } - dropdownWidth={160} dropdownComponents={ - - - - - + + + + + + + } dropdownHotkeyScope={{ scope: dropdownId }} /> diff --git a/packages/twenty-front/src/modules/auth/sign-in-up/components/EmailVerificationSent.tsx b/packages/twenty-front/src/modules/auth/sign-in-up/components/EmailVerificationSent.tsx index 0b85a8026..c82076fa7 100644 --- a/packages/twenty-front/src/modules/auth/sign-in-up/components/EmailVerificationSent.tsx +++ b/packages/twenty-front/src/modules/auth/sign-in-up/components/EmailVerificationSent.tsx @@ -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; diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuContextChipGroups.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuContextChipGroups.tsx index 928f4c3bf..de32e94c4 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuContextChipGroups.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuContextChipGroups.tsx @@ -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={ - - {firstChips.map((chip, index) => ( - { - closeDropdown(COMMAND_MENU_CONTEXT_CHIP_GROUPS_DROPDOWN_ID); - chip.onClick?.(); - }} - /> - ))} - + + + {firstChips.map((chip, index) => ( + { + closeDropdown( + COMMAND_MENU_CONTEXT_CHIP_GROUPS_DROPDOWN_ID, + ); + chip.onClick?.(); + }} + /> + ))} + + } dropdownHotkeyScope={{ scope: AppHotkeyScope.CommandMenu, diff --git a/packages/twenty-front/src/modules/favorites/components/FavoriteFolderNavigationDrawerItemDropdown.tsx b/packages/twenty-front/src/modules/favorites/components/FavoriteFolderNavigationDrawerItemDropdown.tsx index 10d479c4a..9ed862212 100644 --- a/packages/twenty-front/src/modules/favorites/components/FavoriteFolderNavigationDrawerItemDropdown.tsx +++ b/packages/twenty-front/src/modules/favorites/components/FavoriteFolderNavigationDrawerItemDropdown.tsx @@ -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={ - - - - + + + + + + } /> ); diff --git a/packages/twenty-front/src/modules/favorites/components/PageFavoriteFolderDropdown.tsx b/packages/twenty-front/src/modules/favorites/components/PageFavoriteFolderDropdown.tsx deleted file mode 100644 index d147ccfdf..000000000 --- a/packages/twenty-front/src/modules/favorites/components/PageFavoriteFolderDropdown.tsx +++ /dev/null @@ -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 ( - - - } - dropdownComponents={ - <> - - - - } - dropdownHotkeyScope={{ scope: dropdownId }} - /> - - - ); -}; diff --git a/packages/twenty-front/src/modules/favorites/favorite-folder-picker/components/FavoriteFolderPicker.tsx b/packages/twenty-front/src/modules/favorites/favorite-folder-picker/components/FavoriteFolderPicker.tsx index d64b04cbf..b633275d9 100644 --- a/packages/twenty-front/src/modules/favorites/favorite-folder-picker/components/FavoriteFolderPicker.tsx +++ b/packages/twenty-front/src/modules/favorites/favorite-folder-picker/components/FavoriteFolderPicker.tsx @@ -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 ( - + @@ -101,6 +101,6 @@ export const FavoriteFolderPicker = ({ - + ); }; diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterAddFilterRuleSelect.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterAddFilterRuleSelect.tsx index b1e75599e..64a0c04f6 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterAddFilterRuleSelect.tsx +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterAddFilterRuleSelect.tsx @@ -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 = ({ } dropdownComponents={ - - - {isFilterRuleGroupOptionVisible && ( + + - )} - + {isFilterRuleGroupOptionVisible && ( + + )} + + } dropdownHotkeyScope={{ scope: dropdownId }} dropdownOffset={{ y: 8, x: 0 }} diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterDropdownFilterInput.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterDropdownFilterInput.tsx index f3b6dc5e8..e9bbd0031 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterDropdownFilterInput.tsx +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterDropdownFilterInput.tsx @@ -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 = ({ )} {filterType === 'RELATION' && ( - <> + - + )} {filterType === 'ACTOR' && (isActorSourceCompositeFilter ? ( - <> - - + ) : ( - <> - - + ))} {['SELECT', 'MULTI_SELECT'].includes(filterType) && ( - <> + - + )} {filterType === 'BOOLEAN' && } {filterType === 'CURRENCY' && @@ -82,9 +79,7 @@ export const AdvancedFilterDropdownFilterInput = ({ 'currencyCode', recordFilter.subFieldName, ) ? ( - <> - - + ) : ( <> ))} diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterFieldSelectMenu.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterFieldSelectMenu.tsx index d949beb88..e8f5d2f91 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterFieldSelectMenu.tsx +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterFieldSelectMenu.tsx @@ -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 ( - <> + - + ); }; diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterLogicalOperatorDropdown.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterLogicalOperatorDropdown.tsx index 54112cc12..68b643ebb 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterLogicalOperatorDropdown.tsx +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterLogicalOperatorDropdown.tsx @@ -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 (