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:
@ -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,6 +73,7 @@ export const CommandMenuActionMenuDropdown = () => {
|
||||
setSelectedItemId(selectableItemIdArray[0]);
|
||||
}}
|
||||
dropdownComponents={
|
||||
<DropdownContent>
|
||||
<DropdownMenuItemsContainer>
|
||||
<SelectableList
|
||||
selectableListInstanceId={actionMenuId}
|
||||
@ -85,6 +87,7 @@ export const CommandMenuActionMenuDropdown = () => {
|
||||
))}
|
||||
</SelectableList>
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -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,6 +83,7 @@ export const RecordIndexActionMenuDropdown = () => {
|
||||
y: actionMenuDropdownPosition.y ?? 0,
|
||||
}}
|
||||
dropdownComponents={
|
||||
<DropdownContent>
|
||||
<StyledDropdownMenuContainer
|
||||
data-click-outside-id={ACTION_MENU_DROPDOWN_CLICK_OUTSIDE_ID}
|
||||
>
|
||||
@ -115,6 +117,7 @@ export const RecordIndexActionMenuDropdown = () => {
|
||||
</SelectableList>
|
||||
</DropdownMenuItemsContainer>
|
||||
</StyledDropdownMenuContainer>
|
||||
</DropdownContent>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -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}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -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
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -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 }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -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,8 +50,8 @@ export const AttachmentDropdown = ({
|
||||
clickableComponent={
|
||||
<LightIconButton Icon={IconDotsVertical} accent="tertiary" />
|
||||
}
|
||||
dropdownWidth={160}
|
||||
dropdownComponents={
|
||||
<DropdownContent widthInPixels={GenericDropdownContentWidth.Narrow}>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
text="Download"
|
||||
@ -68,6 +70,7 @@ export const AttachmentDropdown = ({
|
||||
onClick={handleDelete}
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
}
|
||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
||||
/>
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,6 +54,7 @@ export const CommandMenuContextChipGroups = ({
|
||||
/>
|
||||
}
|
||||
dropdownComponents={
|
||||
<DropdownContent>
|
||||
<DropdownMenuItemsContainer>
|
||||
{firstChips.map((chip, index) => (
|
||||
<MenuItem
|
||||
@ -60,12 +62,15 @@ export const CommandMenuContextChipGroups = ({
|
||||
LeftComponent={chip.Icons}
|
||||
text={chip.text}
|
||||
onClick={() => {
|
||||
closeDropdown(COMMAND_MENU_CONTEXT_CHIP_GROUPS_DROPDOWN_ID);
|
||||
closeDropdown(
|
||||
COMMAND_MENU_CONTEXT_CHIP_GROUPS_DROPDOWN_ID,
|
||||
);
|
||||
chip.onClick?.();
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
}
|
||||
dropdownHotkeyScope={{
|
||||
scope: AppHotkeyScope.CommandMenu,
|
||||
|
||||
@ -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,6 +42,7 @@ export const FavoriteFolderNavigationDrawerItemDropdown = ({
|
||||
}
|
||||
dropdownPlacement="bottom-start"
|
||||
dropdownComponents={
|
||||
<DropdownContent widthInPixels={GenericDropdownContentWidth.Narrow}>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
LeftIcon={IconPencil}
|
||||
@ -54,6 +57,7 @@ export const FavoriteFolderNavigationDrawerItemDropdown = ({
|
||||
text="Delete"
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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,6 +152,7 @@ export const AdvancedFilterAddFilterRuleSelect = ({
|
||||
<LightButton Icon={IconPlus} title="Add filter rule" />
|
||||
}
|
||||
dropdownComponents={
|
||||
<DropdownContent>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
LeftIcon={IconPlus}
|
||||
@ -165,6 +167,7 @@ export const AdvancedFilterAddFilterRuleSelect = ({
|
||||
/>
|
||||
)}
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
}
|
||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
||||
dropdownOffset={{ y: 8, x: 0 }}
|
||||
|
||||
@ -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 />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<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 />
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
))}
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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,6 +54,7 @@ export const AdvancedFilterRecordFilterGroupOptionsDropdown = ({
|
||||
/>
|
||||
}
|
||||
dropdownComponents={
|
||||
<DropdownContent>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
text="Remove rule group"
|
||||
@ -61,6 +63,7 @@ export const AdvancedFilterRecordFilterGroupOptionsDropdown = ({
|
||||
accent="danger"
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
}
|
||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
||||
dropdownOffset={{ y: 2, x: 0 }}
|
||||
|
||||
@ -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,7 +96,8 @@ export const AdvancedFilterRecordFilterOperandSelect = ({
|
||||
/>
|
||||
}
|
||||
dropdownComponents={
|
||||
<DropdownMenuItemsContainer width="auto">
|
||||
<DropdownContent widthInPixels={GenericDropdownContentWidth.Narrow}>
|
||||
<DropdownMenuItemsContainer>
|
||||
<SelectableList
|
||||
hotkeyScope={dropdownId}
|
||||
selectableItemIdArray={operandsForFilterType.map(
|
||||
@ -121,11 +124,11 @@ export const AdvancedFilterRecordFilterOperandSelect = ({
|
||||
))}
|
||||
</SelectableList>
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
}
|
||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
||||
dropdownOffset={DEFAULT_ADVANCED_FILTER_DROPDOWN_OFFSET}
|
||||
dropdownPlacement="bottom-start"
|
||||
dropdownWidth={200}
|
||||
/>
|
||||
</StyledContainer>
|
||||
);
|
||||
|
||||
@ -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,6 +74,7 @@ export const AdvancedFilterRecordFilterOptionsDropdown = ({
|
||||
/>
|
||||
}
|
||||
dropdownComponents={
|
||||
<DropdownContent>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
text="Remove rule"
|
||||
@ -81,6 +83,7 @@ export const AdvancedFilterRecordFilterOptionsDropdown = ({
|
||||
accent="danger"
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
}
|
||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
||||
dropdownOffset={DEFAULT_ADVANCED_FILTER_DROPDOWN_OFFSET}
|
||||
|
||||
@ -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,6 +38,7 @@ export const AdvancedFilterRootRecordFilterGroup = () => {
|
||||
|
||||
return (
|
||||
<ScrollWrapper componentInstanceId={`scroll-wrapper-dropdown-menu-${id}`}>
|
||||
<DropdownContent widthInPixels={ADVANCED_FILTER_DROPDOWN_CONTENT_WIDTH}>
|
||||
<StyledContainer>
|
||||
{childRecordFiltersAndRecordFilterGroups.map(
|
||||
(recordFilterGroupChild, recordFilterGroupChildIndex) =>
|
||||
@ -62,6 +64,7 @@ export const AdvancedFilterRootRecordFilterGroup = () => {
|
||||
recordFilterGroup={rootRecordFilterGroup}
|
||||
/>
|
||||
</StyledContainer>
|
||||
</DropdownContent>
|
||||
</ScrollWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -133,7 +133,6 @@ export const AdvancedFilterValueInput = ({
|
||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
||||
dropdownOffset={dropdownContentOffset}
|
||||
dropdownPlacement="bottom-start"
|
||||
dropdownWidth={280}
|
||||
onClose={handleFilterValueDropdownClose}
|
||||
onOpen={handleFilterValueDropdownOpen}
|
||||
/>
|
||||
|
||||
@ -0,0 +1 @@
|
||||
export const ADVANCED_FILTER_DROPDOWN_CONTENT_WIDTH = 650;
|
||||
@ -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,6 +52,7 @@ export const ObjectFilterDropdownBooleanSelect = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<DropdownContent>
|
||||
<SelectableList
|
||||
selectableListInstanceId="boolean-select"
|
||||
selectableItemIdArray={options.map((option) => option.toString())}
|
||||
@ -73,5 +75,6 @@ export const ObjectFilterDropdownBooleanSelect = () => {
|
||||
))}
|
||||
</DropdownMenuItemsContainer>
|
||||
</SelectableList>
|
||||
</DropdownContent>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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,12 +63,26 @@ export const ObjectFilterDropdownFilterInput = ({
|
||||
fieldMetadataItemUsedInDropdown.type,
|
||||
);
|
||||
|
||||
const isNotASubFieldFilter = !isDefined(subFieldNameUsedInDropdown);
|
||||
const isDateFilter = DATE_FILTER_TYPES.includes(filterType);
|
||||
const isOnlyOperand = !isConfigurable;
|
||||
|
||||
if (isOnlyOperand) {
|
||||
return (
|
||||
<>
|
||||
{isConfigurable && selectedOperandInDropdown && (
|
||||
<>
|
||||
<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 />
|
||||
)}
|
||||
@ -82,51 +90,16 @@ export const ObjectFilterDropdownFilterInput = ({
|
||||
<ObjectFilterDropdownNumberInput />
|
||||
)}
|
||||
{filterType === 'RATING' && <ObjectFilterDropdownRatingInput />}
|
||||
{DATE_FILTER_TYPES.includes(filterType) && (
|
||||
<ObjectFilterDropdownDateInput />
|
||||
)}
|
||||
{filterType === 'RELATION' && (
|
||||
<>
|
||||
<ObjectFilterDropdownSearchInput />
|
||||
<DropdownMenuSeparator />
|
||||
<ObjectFilterDropdownRecordSelect
|
||||
recordFilterId={recordFilterId}
|
||||
/>
|
||||
<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 />
|
||||
))}
|
||||
{filterType === 'ACTOR' && <ObjectFilterDropdownTextInput />}
|
||||
{filterType === 'ADDRESS' && <ObjectFilterDropdownTextInput />}
|
||||
{filterType === 'CURRENCY' && <ObjectFilterDropdownNumberInput />}
|
||||
{['SELECT', 'MULTI_SELECT'].includes(filterType) && (
|
||||
<>
|
||||
<ObjectFilterDropdownSearchInput />
|
||||
@ -135,8 +108,7 @@ export const ObjectFilterDropdownFilterInput = ({
|
||||
</>
|
||||
)}
|
||||
{filterType === 'BOOLEAN' && <ObjectFilterDropdownBooleanSelect />}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
</DropdownContent>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@ -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,6 +33,9 @@ export const ObjectFilterDropdownOperandDropdown = ({
|
||||
const dropdownId = `${filterDropdownId}-operand-dropdown`;
|
||||
|
||||
return (
|
||||
<ClickOutsideListenerContext.Provider
|
||||
value={{ excludedClickOutsideId: OPERAND_DROPDOWN_CLICK_OUTSIDE_ID }}
|
||||
>
|
||||
<Dropdown
|
||||
dropdownId={dropdownId}
|
||||
clickableComponent={
|
||||
@ -45,5 +52,6 @@ export const ObjectFilterDropdownOperandDropdown = ({
|
||||
}}
|
||||
dropdownOffset={{ x: parseInt(theme.spacing(2), 10) }}
|
||||
/>
|
||||
</ClickOutsideListenerContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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,6 +46,7 @@ export const ObjectFilterDropdownOperandSelect = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<DropdownContent>
|
||||
<StyledDropdownMenuItemsContainer>
|
||||
{operandsForFilterType.map((filterOperand, index) => (
|
||||
<MenuItem
|
||||
@ -57,5 +59,6 @@ export const ObjectFilterDropdownOperandSelect = () => {
|
||||
/>
|
||||
))}
|
||||
</StyledDropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1 @@
|
||||
export const DATE_PICKER_DROPDOWN_CONTENT_WIDTH = 280;
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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,8 +69,7 @@ export const ObjectOptionsDropdownHiddenRecordGroupsContent = () => {
|
||||
}, [hiddenRecordGroupIds, currentContentId, onContentChange]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropdownMenuItemsContainer>
|
||||
<DropdownContent>
|
||||
<DropdownMenuHeader
|
||||
StartComponent={
|
||||
<DropdownMenuHeaderLeftComponent
|
||||
@ -80,8 +80,6 @@ export const ObjectOptionsDropdownHiddenRecordGroupsContent = () => {
|
||||
>
|
||||
Hidden {recordGroupFieldMetadata?.label}
|
||||
</DropdownMenuHeader>
|
||||
</DropdownMenuItemsContainer>
|
||||
|
||||
<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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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>
|
||||
);
|
||||
|
||||
@ -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,6 +233,7 @@ export const ObjectSortDropdownButton = ({
|
||||
</StyledHeaderDropdownButton>
|
||||
}
|
||||
dropdownComponents={
|
||||
<DropdownContent>
|
||||
<SelectableList
|
||||
selectableListInstanceId={OBJECT_SORT_DROPDOWN_ID}
|
||||
hotkeyScope={hotkeyScope.scope}
|
||||
@ -269,7 +271,6 @@ export const ObjectSortDropdownButton = ({
|
||||
? t`Ascending`
|
||||
: t`Descending`}
|
||||
</DropdownMenuHeader>
|
||||
<DropdownMenuSeparator />
|
||||
<StyledInput
|
||||
autoFocus
|
||||
value={objectSortDropdownSearchInput}
|
||||
@ -297,7 +298,8 @@ export const ObjectSortDropdownButton = ({
|
||||
),
|
||||
)}
|
||||
{shouldShowSeparator && <DropdownMenuSeparator />}
|
||||
{hiddenFieldMetadataItems.map((hiddenFieldMetadataItem, index) => (
|
||||
{hiddenFieldMetadataItems.map(
|
||||
(hiddenFieldMetadataItem, index) => (
|
||||
<SelectableListItem
|
||||
key={hiddenFieldMetadataItem.id}
|
||||
itemId={hiddenFieldMetadataItem.id}
|
||||
@ -311,9 +313,11 @@ export const ObjectSortDropdownButton = ({
|
||||
text={hiddenFieldMetadataItem.label}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
))}
|
||||
),
|
||||
)}
|
||||
</DropdownMenuItemsContainer>
|
||||
</SelectableList>
|
||||
</DropdownContent>
|
||||
}
|
||||
onClose={handleDropdownButtonClose}
|
||||
/>
|
||||
|
||||
@ -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>
|
||||
);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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,6 +74,7 @@ export const MultiItemFieldMenuItem = <T,>({
|
||||
RightIcon={!isHovered && showPrimaryIcon ? IconBookmark : null}
|
||||
dropdownId={dropdownId}
|
||||
dropdownContent={
|
||||
<DropdownContent>
|
||||
<DropdownMenuItemsContainer>
|
||||
{showSetAsPrimaryButton && (
|
||||
<MenuItem
|
||||
@ -93,6 +95,7 @@ export const MultiItemFieldMenuItem = <T,>({
|
||||
onClick={handleDeleteClick}
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -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 }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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,6 +247,7 @@ export const RecordDetailRelationRecordsListItem = ({
|
||||
/>
|
||||
}
|
||||
dropdownComponents={
|
||||
<DropdownContent>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
LeftIcon={IconUnlink}
|
||||
@ -261,6 +263,7 @@ export const RecordDetailRelationRecordsListItem = ({
|
||||
/>
|
||||
)}
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
}
|
||||
dropdownHotkeyScope={{ scope: dropdownScopeId }}
|
||||
/>
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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,6 +108,7 @@ export const RecordTableColumnHeadDropdownMenu = ({
|
||||
const canHide = column.isLabelIdentifier !== true;
|
||||
|
||||
return (
|
||||
<DropdownContent>
|
||||
<StyledDropdownMenuItemsContainer>
|
||||
{isFilterable && (
|
||||
<MenuItem
|
||||
@ -145,5 +147,6 @@ export const RecordTableColumnHeadDropdownMenu = ({
|
||||
/>
|
||||
)}
|
||||
</StyledDropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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,6 +81,7 @@ export const MultipleSelectDropdown = ({
|
||||
const selectableItemIds = itemsInDropdown.map((item) => item.id);
|
||||
|
||||
return (
|
||||
<DropdownContent>
|
||||
<SelectableList
|
||||
selectableListInstanceId={selectableListId}
|
||||
selectableItemIdArray={selectableItemIds}
|
||||
@ -122,5 +124,6 @@ export const MultipleSelectDropdown = ({
|
||||
{loadingItems && <DropdownMenuSkeletonItem />}
|
||||
</DropdownMenuItemsContainer>
|
||||
</SelectableList>
|
||||
</DropdownContent>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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,8 +55,8 @@ export const SettingsAccountsRowDropdownMenu = ({
|
||||
clickableComponent={
|
||||
<LightIconButton Icon={IconDotsVertical} accent="tertiary" />
|
||||
}
|
||||
dropdownWidth={160}
|
||||
dropdownComponents={
|
||||
<DropdownContent>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
LeftIcon={IconMail}
|
||||
@ -93,6 +94,7 @@ export const SettingsAccountsRowDropdownMenu = ({
|
||||
}}
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
}
|
||||
/>
|
||||
<ConfirmationModal
|
||||
|
||||
@ -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,6 +116,7 @@ export const ConfigVariableDatabaseInput = ({
|
||||
/>
|
||||
}
|
||||
dropdownComponents={
|
||||
<DropdownContent>
|
||||
<DropdownMenuItemsContainer>
|
||||
{selectOptions.map((option) => (
|
||||
<MenuItemMultiSelect
|
||||
@ -128,6 +130,7 @@ export const ConfigVariableDatabaseInput = ({
|
||||
/>
|
||||
))}
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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 }}
|
||||
/>
|
||||
|
||||
@ -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,6 +120,7 @@ export const SettingsDataModelFieldSelectFormOptionRow = ({
|
||||
dropdownHotkeyScope={{ scope: SELECT_COLOR_DROPDOWN_ID }}
|
||||
clickableComponent={<StyledColorSample colorName={option.color} />}
|
||||
dropdownComponents={
|
||||
<DropdownContent>
|
||||
<DropdownMenuItemsContainer>
|
||||
{MAIN_COLOR_NAMES.map((colorName) => (
|
||||
<MenuItemSelectColor
|
||||
@ -133,6 +134,7 @@ export const SettingsDataModelFieldSelectFormOptionRow = ({
|
||||
/>
|
||||
))}
|
||||
</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>
|
||||
|
||||
@ -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,8 +57,8 @@ export const SettingsObjectFieldActiveActionDropdown = ({
|
||||
accent="tertiary"
|
||||
/>
|
||||
}
|
||||
dropdownWidth={160}
|
||||
dropdownComponents={
|
||||
<DropdownContent widthInPixels={GenericDropdownContentWidth.Narrow}>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
text={isCustomField ? 'Edit' : 'View'}
|
||||
@ -78,6 +80,7 @@ export const SettingsObjectFieldActiveActionDropdown = ({
|
||||
/>
|
||||
)}
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
}
|
||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
||||
/>
|
||||
|
||||
@ -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,8 +62,8 @@ export const SettingsObjectFieldInactiveActionDropdown = ({
|
||||
accent="tertiary"
|
||||
/>
|
||||
}
|
||||
dropdownWidth={160}
|
||||
dropdownComponents={
|
||||
<DropdownContent widthInPixels={GenericDropdownContentWidth.Narrow}>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
text={isCustomField ? t`Edit` : t`View`}
|
||||
@ -82,6 +84,7 @@ export const SettingsObjectFieldInactiveActionDropdown = ({
|
||||
/>
|
||||
)}
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
}
|
||||
dropdownHotkeyScope={{
|
||||
scope: dropdownId,
|
||||
|
||||
@ -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,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -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,8 +44,8 @@ export const SettingsObjectInactiveMenuDropDown = ({
|
||||
accent="tertiary"
|
||||
/>
|
||||
}
|
||||
dropdownWidth={160}
|
||||
dropdownComponents={
|
||||
<DropdownContent widthInPixels={GenericDropdownContentWidth.Narrow}>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
text="Activate"
|
||||
@ -59,6 +61,7 @@ export const SettingsObjectInactiveMenuDropDown = ({
|
||||
/>
|
||||
)}
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
}
|
||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
||||
/>
|
||||
|
||||
@ -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,6 +59,7 @@ export const SettingsIntegrationDatabaseConnectionSummaryCard = ({
|
||||
<LightIconButton Icon={IconDotsVertical} accent="tertiary" />
|
||||
}
|
||||
dropdownComponents={
|
||||
<DropdownContent>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
LeftIcon={IconTrash}
|
||||
@ -68,6 +70,7 @@ export const SettingsIntegrationDatabaseConnectionSummaryCard = ({
|
||||
<MenuItem LeftIcon={IconPencil} text="Edit" />
|
||||
</UndecoratedLink>
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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,8 +73,8 @@ export const SettingsSecuritySSORowDropdownMenu = ({
|
||||
clickableComponent={
|
||||
<LightIconButton Icon={IconDotsVertical} accent="tertiary" />
|
||||
}
|
||||
dropdownWidth={160}
|
||||
dropdownComponents={
|
||||
<DropdownContent>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
accent="default"
|
||||
@ -94,6 +95,7 @@ export const SettingsSecuritySSORowDropdownMenu = ({
|
||||
}}
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -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,8 +62,8 @@ export const SettingsSecurityApprovedAccessDomainRowDropdownMenu = ({
|
||||
clickableComponent={
|
||||
<LightIconButton Icon={IconDotsVertical} accent="tertiary" />
|
||||
}
|
||||
dropdownWidth={160}
|
||||
dropdownComponents={
|
||||
<DropdownContent>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
accent="danger"
|
||||
@ -74,6 +75,7 @@ export const SettingsSecurityApprovedAccessDomainRowDropdownMenu = ({
|
||||
}}
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -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,6 +110,7 @@ export const SettingsServerlessFunctionTabEnvironmentVariableTableRow = ({
|
||||
/>
|
||||
}
|
||||
dropdownComponents={
|
||||
<DropdownContent>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
text={'Edit'}
|
||||
@ -127,6 +129,7 @@ export const SettingsServerlessFunctionTabEnvironmentVariableTableRow = ({
|
||||
}}
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
}
|
||||
dropdownHotkeyScope={{ scope: dropDownId }}
|
||||
/>
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,8 +28,8 @@ export const SupportDropdown = () => {
|
||||
dropdownPlacement="top-start"
|
||||
dropdownOffset={{ x: 0, y: -28 }}
|
||||
clickableComponent={<SupportButton />}
|
||||
dropdownWidth={160}
|
||||
dropdownComponents={
|
||||
<DropdownContent widthInPixels={GenericDropdownContentWidth.Narrow}>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
text="Talk to us"
|
||||
@ -40,6 +42,7 @@ export const SupportDropdown = () => {
|
||||
onClick={handleUserGuide}
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
}
|
||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
||||
/>
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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={
|
||||
<DropdownContent widthInPixels={ICON_PICKER_DROPDOWN_CONTENT_WIDTH}>
|
||||
<SelectableList
|
||||
selectableListInstanceId="icon-list"
|
||||
selectableItemIdMatrix={iconKeys2d}
|
||||
hotkeyScope={IconPickerHotkeyScope.IconPicker}
|
||||
>
|
||||
<DropdownMenu width={176}>
|
||||
<DropdownMenuSearchInput
|
||||
placeholder={t`Search icon`}
|
||||
autoFocus
|
||||
@ -220,8 +220,8 @@ export const IconPicker = ({
|
||||
</StyledMenuIconItemsContainer>
|
||||
</DropdownMenuItemsContainer>
|
||||
</div>
|
||||
</DropdownMenu>
|
||||
</SelectableList>
|
||||
</DropdownContent>
|
||||
}
|
||||
onClickOutside={onClickOutside}
|
||||
onClose={() => {
|
||||
|
||||
@ -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 }}
|
||||
/>
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -0,0 +1 @@
|
||||
export const ICON_PICKER_DROPDOWN_CONTENT_WIDTH = 176;
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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>,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
export const DropdownContent = forwardRef(
|
||||
(
|
||||
{
|
||||
children,
|
||||
widthInPixels = GenericDropdownContentWidth.Medium,
|
||||
selectDisabled = false,
|
||||
}: React.PropsWithChildren<{
|
||||
widthInPixels?: number;
|
||||
selectDisabled?: boolean;
|
||||
}>,
|
||||
ref: Ref<HTMLDivElement>,
|
||||
) => {
|
||||
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}
|
||||
<StyledInternalBaseDropdownContent
|
||||
data-select-disable={selectDisabled}
|
||||
widthInPixels={widthInPixels}
|
||||
ref={ref}
|
||||
>
|
||||
<OverlayContainer>
|
||||
<DropdownMenu
|
||||
id={dropdownId}
|
||||
width={dropdownWidth}
|
||||
data-select-disable
|
||||
>
|
||||
{dropdownComponents}
|
||||
</DropdownMenu>
|
||||
</OverlayContainer>
|
||||
</StyledDropdownContentContainer>
|
||||
</FloatingPortal>
|
||||
</>
|
||||
{children}
|
||||
</StyledInternalBaseDropdownContent>
|
||||
);
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
@ -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;
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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,8 +19,11 @@ const StyledDropdownMenuItemsExternalContainer = styled.div<{
|
||||
padding: var(--padding);
|
||||
|
||||
${({ width }) =>
|
||||
isDefined(width) &&
|
||||
css`
|
||||
isDefined(width) && width === '100%'
|
||||
? css`
|
||||
width: 100%;
|
||||
`
|
||||
: css`
|
||||
width: ${width}px;
|
||||
`}
|
||||
`;
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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 },
|
||||
|
||||
@ -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={
|
||||
<DropdownContent>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem LeftIcon={IconPlus} text={`Create Workspace`} />
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
}
|
||||
/>
|
||||
),
|
||||
|
||||
@ -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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,6 @@
|
||||
export enum GenericDropdownContentWidth {
|
||||
Narrow = 160,
|
||||
Medium = 200,
|
||||
Large = 240,
|
||||
ExtraLarge = 320,
|
||||
}
|
||||
@ -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>
|
||||
|
||||
@ -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']};
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
@ -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,6 +117,7 @@ export const MultiWorkspaceDropdownDefaultComponents = () => {
|
||||
dropdownId={'multi-workspace-dropdown-context-menu'}
|
||||
dropdownHotkeyScope={{ scope: SelectHotkeyScope.Select }}
|
||||
dropdownComponents={
|
||||
<DropdownContent>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
LeftIcon={IconPlus}
|
||||
@ -123,13 +125,13 @@ export const MultiWorkspaceDropdownDefaultComponents = () => {
|
||||
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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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
Reference in New Issue
Block a user