Migrate to twenty-ui - navigation/menu-item (#8213)

This PR was created by [GitStart](https://gitstart.com/) to address the
requirements from this ticket:
[TWNTY-7536](https://clients.gitstart.com/twenty/5449/tickets/TWNTY-7536).

 --- 

### Description

Migrate all menu items components to twenty ui and update imports.

```typescript
MenuItem
MenuItemAvata
MenuItemCommand
MenuItemCommandHotKeys
MenuItemDraggable
MenuItemMultiSelect
MenuItemMultiSelectAvatar
MenuItemMultiSelectTag
MenuItemNavigate
MenuItemSelect
MenuItemSelectAvatar
MenuItemSelectColor
MenuItemSelectTag
MenuItemSuggestion
MenuItemToggle
```

\
Also migrate all other dependent components and utilities like
`Checkbox` & `Toggle`\
\
Fixes twentyhq/private-issues#82

---------

Co-authored-by: gitstart-twenty <gitstart-twenty@users.noreply.github.com>
Co-authored-by: gitstart-twenty <140154534+gitstart-twenty@users.noreply.github.com>
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
gitstart-app[bot]
2024-11-07 16:51:39 +00:00
committed by GitHub
parent f9a136ab6d
commit 6264d509bd
106 changed files with 326 additions and 256 deletions

View File

@ -10,10 +10,10 @@ import { ActionMenuDropdownHotkeyScope } from '@/action-menu/types/ActionMenuDro
import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState';
import { MenuItem } from 'twenty-ui';
type StyledContainerProps = {
position: PositionType;

View File

@ -1,8 +1,7 @@
import { ActionMenuEntry } from '@/action-menu/types/ActionMenuEntry';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { MOBILE_VIEWPORT } from 'twenty-ui';
import { ActionMenuEntry } from '@/action-menu/types/ActionMenuEntry';
import { MenuItemAccent } from '@/ui/navigation/menu-item/types/MenuItemAccent';
import { MOBILE_VIEWPORT, MenuItemAccent } from 'twenty-ui';
type RecordShowActionMenuBarEntryProps = {
entry: ActionMenuEntry;

View File

@ -8,13 +8,13 @@ import { ActionMenuComponentInstanceContext } from '@/action-menu/states/context
import { ActionMenuEntry } from '@/action-menu/types/ActionMenuEntry';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { MenuItemAccent } from '@/ui/navigation/menu-item/types/MenuItemAccent';
import { userEvent, waitFor, within } from '@storybook/test';
import {
ComponentDecorator,
IconFileExport,
IconHeart,
IconTrash,
MenuItemAccent,
} from 'twenty-ui';
const deleteMock = jest.fn();

View File

@ -1,7 +1,5 @@
import { MouseEvent, ReactNode } from 'react';
import { IconComponent } from 'twenty-ui';
import { MenuItemAccent } from '@/ui/navigation/menu-item/types/MenuItemAccent';
import { IconComponent, MenuItemAccent } from 'twenty-ui';
export type ActionMenuEntry = {
type: 'standard' | 'workflow-run';

View File

@ -1,9 +1,8 @@
import { MessageThreadSubscriber } from '@/activities/emails/types/MessageThreadSubscriber';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
import { MenuItemAvatar } from '@/ui/navigation/menu-item/components/MenuItemAvatar';
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
import { IconPlus } from 'twenty-ui';
import { IconPlus, MenuItemAvatar } from 'twenty-ui';
export const MessageThreadSubscriberDropdownAddSubscriberMenuItem = ({
workspaceMember,

View File

@ -1,5 +1,5 @@
import { offset } from '@floating-ui/react';
import { IconMinus, IconPlus } from 'twenty-ui';
import { IconMinus, IconPlus, MenuItem, MenuItemAvatar } from 'twenty-ui';
import { MessageThreadSubscriberDropdownAddSubscriber } from '@/activities/emails/components/MessageThreadSubscriberDropdownAddSubscriber';
import { MessageThreadSubscribersChip } from '@/activities/emails/components/MessageThreadSubscribersChip';
@ -10,8 +10,6 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop
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 { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { MenuItemAvatar } from '@/ui/navigation/menu-item/components/MenuItemAvatar';
import { useState } from 'react';
export const MESSAGE_THREAD_SUBSCRIBER_DROPDOWN_ID =

View File

@ -4,13 +4,13 @@ import {
IconPencil,
IconTrash,
LightIconButton,
MenuItem,
} from 'twenty-ui';
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 { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
type AttachmentDropdownProps = {
onDownload: () => void;

View File

@ -1,9 +1,8 @@
import { isNonEmptyString } from '@sniptt/guards';
import { useRecoilValue } from 'recoil';
import { IconArrowUpRight, IconComponent } from 'twenty-ui';
import { IconArrowUpRight, IconComponent, MenuItemCommand } from 'twenty-ui';
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
import { MenuItemCommand } from '@/ui/navigation/menu-item/components/MenuItemCommand';
import { useCommandMenu } from '../hooks/useCommandMenu';

View File

@ -4,7 +4,6 @@ import { getOperandsForFilterDefinition } from '@/object-record/object-filter-dr
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 { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { ADVANCED_FILTER_DROPDOWN_ID } from '@/views/constants/AdvancedFilterDropdownId';
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
@ -13,7 +12,13 @@ import { availableFilterDefinitionsComponentState } from '@/views/states/availab
import { ViewFilterGroup } from '@/views/types/ViewFilterGroup';
import { ViewFilterGroupLogicalOperator } from '@/views/types/ViewFilterGroupLogicalOperator';
import { useCallback } from 'react';
import { IconLibraryPlus, IconPlus, isDefined, LightButton } from 'twenty-ui';
import {
IconLibraryPlus,
IconPlus,
isDefined,
LightButton,
MenuItem,
} from 'twenty-ui';
import { v4 } from 'uuid';
type AdvancedFilterAddFilterRuleSelectProps = {

View File

@ -4,10 +4,9 @@ import { useCurrentViewViewFilterGroup } from '@/object-record/advanced-filter/h
import { useDeleteCombinedViewFilterGroup } from '@/object-record/advanced-filter/hooks/useDeleteCombinedViewFilterGroup';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { ADVANCED_FILTER_DROPDOWN_ID } from '@/views/constants/AdvancedFilterDropdownId';
import { useDeleteCombinedViewFilters } from '@/views/hooks/useDeleteCombinedViewFilters';
import { isDefined } from 'twenty-ui';
import { isDefined, MenuItem } from 'twenty-ui';
type AdvancedFilterRuleOptionsDropdownProps =
| {

View File

@ -6,12 +6,11 @@ import { SelectControl } from '@/ui/input/components/SelectControl';
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 { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { ADVANCED_FILTER_DROPDOWN_ID } from '@/views/constants/AdvancedFilterDropdownId';
import { useUpsertCombinedViewFilters } from '@/views/hooks/useUpsertCombinedViewFilters';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import styled from '@emotion/styled';
import { isDefined } from 'twenty-ui';
import { isDefined, MenuItem } from 'twenty-ui';
const StyledContainer = styled.div`
flex: 1;

View File

@ -3,8 +3,6 @@ import { useUpsertCombinedViewFilterGroup } from '@/object-record/advanced-filte
import { OBJECT_FILTER_DROPDOWN_ID } from '@/object-record/object-filter-dropdown/constants/ObjectFilterDropdownId';
import { getOperandsForFilterDefinition } from '@/object-record/object-filter-dropdown/utils/getOperandsForFilterType';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { MenuItemLeftContent } from '@/ui/navigation/menu-item/internals/components/MenuItemLeftContent';
import { StyledMenuItemBase } from '@/ui/navigation/menu-item/internals/components/StyledMenuItemBase';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { ADVANCED_FILTER_DROPDOWN_ID } from '@/views/constants/AdvancedFilterDropdownId';
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
@ -12,7 +10,12 @@ import { useUpsertCombinedViewFilters } from '@/views/hooks/useUpsertCombinedVie
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
import { ViewFilterGroupLogicalOperator } from '@/views/types/ViewFilterGroupLogicalOperator';
import styled from '@emotion/styled';
import { IconFilter, Pill } from 'twenty-ui';
import {
IconFilter,
MenuItemLeftContent,
Pill,
StyledMenuItemBase,
} from 'twenty-ui';
import { v4 } from 'uuid';
export const StyledContainer = styled.div`

View File

@ -12,11 +12,16 @@ import { getOperandsForFilterDefinition } from '@/object-record/object-filter-dr
import { SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS } from '@/settings/data-model/constants/SettingsCompositeFieldTypeConfigs';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
import { useState } from 'react';
import { useRecoilValue } from 'recoil';
import { IconApps, IconChevronLeft, isDefined, useIcons } from 'twenty-ui';
import {
IconApps,
IconChevronLeft,
isDefined,
MenuItem,
useIcons,
} from 'twenty-ui';
export const ObjectFilterDropdownFilterSelectCompositeFieldSubMenu = () => {
const [searchText] = useState('');

View File

@ -13,11 +13,10 @@ import { getOperandsForFilterDefinition } from '@/object-record/object-filter-dr
import { isCompositeField } from '@/object-record/object-filter-dropdown/utils/isCompositeField';
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
import { MenuItemSelect } from '@/ui/navigation/menu-item/components/MenuItemSelect';
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
import { useRecoilValue } from 'recoil';
import { useIcons } from 'twenty-ui';
import { MenuItemSelect, useIcons } from 'twenty-ui';
export type ObjectFilterDropdownFilterSelectMenuItemProps = {
filterDefinition: FilterDefinition;

View File

@ -3,8 +3,8 @@ import { v4 } from 'uuid';
import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { MenuItem } from 'twenty-ui';
import { isDefined } from '~/utils/isDefined';
import { getInitialFilterValue } from '@/object-record/object-filter-dropdown/utils/getInitialFilterValue';

View File

@ -13,10 +13,10 @@ import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
import { useSelectableListStates } from '@/ui/layout/selectable-list/hooks/internal/useSelectableListStates';
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { MenuItemMultiSelect } from '@/ui/navigation/menu-item/components/MenuItemMultiSelect';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
import { MenuItem, MenuItemMultiSelect } from 'twenty-ui';
import { isDefined } from '~/utils/isDefined';
export const EMPTY_FILTER_VALUE = '';

View File

@ -1,9 +1,8 @@
import { IconFilterOff } from 'twenty-ui';
import { IconFilterOff, MenuItem } from 'twenty-ui';
import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
export const ObjectFilterDropdownRecordRemoveFilterMenuItem = () => {
const { emptyFilterButKeepDefinition } = useFilterDropdown();

View File

@ -1,6 +1,6 @@
import styled from '@emotion/styled';
import { useRecoilValue } from 'recoil';
import { IconChevronDown, useIcons } from 'twenty-ui';
import { IconChevronDown, MenuItem, useIcons } from 'twenty-ui';
import { OBJECT_SORT_DROPDOWN_ID } from '@/object-record/object-sort-dropdown/constants/ObjectSortDropdownId';
import { useObjectSortDropdown } from '@/object-record/object-sort-dropdown/hooks/useObjectSortDropdown';
@ -14,7 +14,6 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';

View File

@ -4,8 +4,8 @@ import { useCallback, useRef } from 'react';
import { useRecordGroupActions } from '@/object-record/record-group/hooks/useRecordGroupActions';
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { MenuItem } from 'twenty-ui';
const StyledMenuContainer = styled.div`
position: absolute;

View File

@ -1,7 +1,7 @@
import styled from '@emotion/styled';
import React, { useRef, useState } from 'react';
import { Key } from 'ts-key-enum';
import { IconCheck, IconPlus, LightIconButton } from 'twenty-ui';
import { IconCheck, IconPlus, LightIconButton, MenuItem } from 'twenty-ui';
import { PhoneRecord } from '@/object-record/record-field/types/FieldMetadata';
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
@ -11,7 +11,6 @@ import {
} from '@/ui/layout/dropdown/components/DropdownMenuInput';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { FieldMetadataType } from '~/generated-metadata/graphql';

View File

@ -1,6 +1,5 @@
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { MenuItemWithOptionDropdown } from '@/ui/navigation/menu-item/components/MenuItemWithOptionDropdown';
import { useState } from 'react';
import {
@ -8,6 +7,7 @@ import {
IconBookmarkPlus,
IconPencil,
IconTrash,
MenuItem,
} from 'twenty-ui';
type MultiItemFieldMenuItemProps<T> = {

View File

@ -12,9 +12,9 @@ import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownM
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
import { useSelectableListStates } from '@/ui/layout/selectable-list/hooks/internal/useSelectableListStates';
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
import { MenuItemMultiSelectTag } from '@/ui/navigation/menu-item/components/MenuItemMultiSelectTag';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { MenuItemMultiSelectTag } from 'twenty-ui';
import { isDefined } from '~/utils/isDefined';
import { turnIntoEmptyStringIfWhitespacesOnly } from '~/utils/string/turnIntoEmptyStringIfWhitespacesOnly';

View File

@ -1,8 +1,7 @@
import { RecordGroupDefinitionType } from '@/object-record/record-group/types/RecordGroupDefinition';
import { useRecordIndexPageKanbanAddMenuItem } from '@/object-record/record-index/hooks/useRecordIndexPageKanbanAddMenuItem';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import styled from '@emotion/styled';
import { Tag } from 'twenty-ui';
import { MenuItem, Tag } from 'twenty-ui';
const StyledMenuItem = styled(MenuItem)`
width: calc(100% - 2 * var(--horizontal-padding));

View File

@ -9,6 +9,9 @@ import {
IconRotate2,
IconSettings,
IconTag,
MenuItem,
MenuItemNavigate,
MenuItemToggle,
UndecoratedLink,
useIcons,
} from 'twenty-ui';
@ -36,9 +39,6 @@ import { DropdownMenuHeader } 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 { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { MenuItemNavigate } from '@/ui/navigation/menu-item/components/MenuItemNavigate';
import { MenuItemToggle } from '@/ui/navigation/menu-item/components/MenuItemToggle';
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';

View File

@ -10,6 +10,7 @@ import {
IconTrash,
IconUnlink,
LightIconButton,
MenuItem,
} from 'twenty-ui';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
@ -38,7 +39,6 @@ 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 { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { RelationDefinitionType } from '~/generated-metadata/graphql';
const StyledListItem = styled(RecordDetailRecordsListItem)<{

View File

@ -4,13 +4,13 @@ import {
IconEyeOff,
IconFilter,
IconSortDescending,
MenuItem,
} from 'twenty-ui';
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
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 { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { onToggleColumnFilterComponentState } from '@/object-record/record-table/states/onToggleColumnFilterComponentState';
import { onToggleColumnSortComponentState } from '@/object-record/record-table/states/onToggleColumnSortComponentState';

View File

@ -1,7 +1,7 @@
import { useCallback, useContext } from 'react';
import { useLocation } from 'react-router-dom';
import { useSetRecoilState } from 'recoil';
import { IconSettings, UndecoratedLink, useIcons } from 'twenty-ui';
import { IconSettings, MenuItem, UndecoratedLink, useIcons } from 'twenty-ui';
import { getObjectSlug } from '@/object-metadata/utils/getObjectSlug';
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
@ -12,7 +12,6 @@ import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefin
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 { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';

View File

@ -1,13 +1,12 @@
import styled from '@emotion/styled';
import { useRecoilValue } from 'recoil';
import { Avatar } from 'twenty-ui';
import { Avatar, MenuItemMultiSelectAvatar } from 'twenty-ui';
import { useObjectRecordMultiSelectScopedStates } from '@/activities/hooks/useObjectRecordMultiSelectScopedStates';
import { MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID } from '@/object-record/relation-picker/constants/MultiObjectRecordSelectSelectableListId';
import { RelationPickerScopeInternalContext } from '@/object-record/relation-picker/scopes/scope-internal-context/RelationPickerScopeInternalContext';
import { SelectableItem } from '@/ui/layout/selectable-list/components/SelectableItem';
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
import { MenuItemMultiSelectAvatar } from '@/ui/navigation/menu-item/components/MenuItemMultiSelectAvatar';
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
import { isDefined } from '~/utils/isDefined';

View File

@ -1,11 +1,10 @@
import styled from '@emotion/styled';
import { useRecoilValue } from 'recoil';
import { Avatar } from 'twenty-ui';
import { Avatar, MenuItemSelectAvatar } from 'twenty-ui';
import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect';
import { SelectableItem } from '@/ui/layout/selectable-list/components/SelectableItem';
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
import { MenuItemSelectAvatar } from '@/ui/navigation/menu-item/components/MenuItemSelectAvatar';
type SelectableMenuItemSelectProps = {
entity: EntityForSelect;

View File

@ -2,7 +2,7 @@ import { isNonEmptyString } from '@sniptt/guards';
import { Fragment, useRef } from 'react';
import { useRecoilValue } from 'recoil';
import { Key } from 'ts-key-enum';
import { IconComponent, IconPlus } from 'twenty-ui';
import { IconComponent, IconPlus, MenuItem, MenuItemSelect } from 'twenty-ui';
import { SelectableMenuItemSelect } from '@/object-record/relation-picker/components/SelectableMenuItemSelect';
import { SINGLE_ENTITY_SELECT_BASE_LIST } from '@/object-record/relation-picker/constants/SingleEntitySelectBaseList';
@ -12,8 +12,6 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { MenuItemSelect } from '@/ui/navigation/menu-item/components/MenuItemSelect';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { isDefined } from '~/utils/isDefined';

View File

@ -2,7 +2,7 @@ import styled from '@emotion/styled';
import { useEffect, useState } from 'react';
import { useRecoilValue } from 'recoil';
import { Key } from 'ts-key-enum';
import { AvatarChip } from 'twenty-ui';
import { AvatarChip, MenuItem, MenuItemMultiSelectAvatar } from 'twenty-ui';
import { SelectableItem } from '@/object-record/select/types/SelectableItem';
import { DropdownMenuSkeletonItem } from '@/ui/input/relation-picker/components/skeletons/DropdownMenuSkeletonItem';
@ -11,8 +11,6 @@ import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
import { useSelectableListStates } from '@/ui/layout/selectable-list/hooks/internal/useSelectableListStates';
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { MenuItemMultiSelectAvatar } from '@/ui/navigation/menu-item/components/MenuItemMultiSelectAvatar';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
const StyledAvatarChip = styled(AvatarChip)`

View File

@ -5,7 +5,7 @@ import { SettingsAccountsEventVisibilitySettingsCard } from '@/settings/accounts
import { SettingsOptionCardContent } from '@/settings/components/SettingsOptionCardContent';
import styled from '@emotion/styled';
import { Section } from '@react-email/components';
import { H2Title, Toggle, Card } from 'twenty-ui';
import { Card, H2Title, Toggle } from 'twenty-ui';
import { CalendarChannelVisibility } from '~/generated-metadata/graphql';
const StyledDetailsContainer = styled.div`

View File

@ -6,6 +6,7 @@ import {
IconRefresh,
IconTrash,
LightIconButton,
MenuItem,
} from 'twenty-ui';
import { ConnectedAccount } from '@/accounts/types/ConnectedAccount';
@ -16,7 +17,6 @@ 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 { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
type SettingsAccountsRowDropdownMenuProps = {
account: ConnectedAccount;

View File

@ -0,0 +1,60 @@
import styled from '@emotion/styled';
import { Card, CardContent, Toggle } from 'twenty-ui';
type Parameter = {
value: boolean;
title: string;
description: string;
onToggle: (value: boolean) => void;
};
type SettingsAccountsToggleSettingCardProps = {
parameters: Parameter[];
};
const StyledCardContent = styled(CardContent)`
align-items: center;
display: flex;
gap: ${({ theme }) => theme.spacing(4)};
cursor: pointer;
&:hover {
background: ${({ theme }) => theme.background.transparent.lighter};
}
`;
const StyledTitle = styled.div`
color: ${({ theme }) => theme.font.color.primary};
font-weight: ${({ theme }) => theme.font.weight.medium};
margin-bottom: ${({ theme }) => theme.spacing(2)};
`;
const StyledDescription = styled.div`
color: ${({ theme }) => theme.font.color.tertiary};
font-size: ${({ theme }) => theme.font.size.sm};
`;
const StyledToggle = styled(Toggle)`
margin-left: auto;
`;
export const SettingsAccountsToggleSettingCard = ({
parameters,
}: SettingsAccountsToggleSettingCardProps) => (
<Card rounded>
{parameters.map((parameter, index) => (
<StyledCardContent
key={index}
divider={index < parameters.length - 1}
onClick={() => parameter.onToggle(!parameter.value)}
>
<div>
<StyledTitle>{parameter.title}</StyledTitle>
<StyledDescription>{parameter.description}</StyledDescription>
</div>
<StyledToggle value={parameter.value} onChange={parameter.onToggle} />
</StyledCardContent>
))}
</Card>
);

View File

@ -3,7 +3,6 @@ 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 { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import {
@ -12,7 +11,7 @@ import {
useParams,
useSearchParams,
} from 'react-router-dom';
import { Button, IconChevronDown, isDefined } from 'twenty-ui';
import { Button, IconChevronDown, isDefined, MenuItem } from 'twenty-ui';
const StyledContainer = styled.div`
align-items: center;

View File

@ -10,6 +10,8 @@ import {
IconX,
LightIconButton,
MAIN_COLOR_NAMES,
MenuItem,
MenuItemSelectColor,
} from 'twenty-ui';
import { v4 } from 'uuid';
@ -22,8 +24,6 @@ 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 { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { MenuItemSelectColor } from '@/ui/navigation/menu-item/components/MenuItemSelectColor';
import { isAdvancedModeEnabledState } from '@/ui/navigation/navigation-drawer/states/isAdvancedModeEnabledState';
import { AnimatePresence, motion } from 'framer-motion';
import { useRecoilValue } from 'recoil';

View File

@ -5,13 +5,13 @@ import {
IconPencil,
IconTextSize,
LightIconButton,
MenuItem,
} from 'twenty-ui';
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 { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
type SettingsObjectFieldActiveActionDropdownProps = {
isCustomField?: boolean;

View File

@ -5,13 +5,13 @@ import {
IconPencil,
IconTrash,
LightIconButton,
MenuItem,
} from 'twenty-ui';
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 { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { FieldMetadataType } from '~/generated-metadata/graphql';
type SettingsObjectFieldInactiveActionDropdownProps = {

View File

@ -5,6 +5,7 @@ import {
IconDotsVertical,
IconPencil,
LightIconButton,
MenuItem,
useIcons,
} from 'twenty-ui';
@ -18,7 +19,6 @@ 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 { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
type SettingsObjectSummaryCardProps = {
objectMetadataItem: ObjectMetadataItem;

View File

@ -3,13 +3,13 @@ import {
IconDotsVertical,
IconTrash,
LightIconButton,
MenuItem,
} from 'twenty-ui';
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 { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
type SettingsObjectInactiveMenuDropDownProps = {
isCustomObject: boolean;

View File

@ -3,13 +3,13 @@ import { SettingsIntegrationDatabaseConnectionSyncStatus } from '@/settings/inte
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 { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import styled from '@emotion/styled';
import {
IconDotsVertical,
IconPencil,
IconTrash,
LightIconButton,
MenuItem,
UndecoratedLink,
} from 'twenty-ui';

View File

@ -4,7 +4,7 @@ import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/Snac
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import styled from '@emotion/styled';
import { useRecoilState } from 'recoil';
import { IconLink, Toggle, Card } from 'twenty-ui';
import { Card, IconLink, Toggle } from 'twenty-ui';
import { useUpdateWorkspaceMutation } from '~/generated/graphql';
const StyledToggle = styled(Toggle)`

View File

@ -5,6 +5,7 @@ import {
IconDotsVertical,
IconTrash,
LightIconButton,
MenuItem,
} from 'twenty-ui';
import { useDeleteSSOIdentityProvider } from '@/settings/security/hooks/useDeleteSSOIdentityProvider';
@ -16,7 +17,6 @@ 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 { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { UnwrapRecoilValue } from 'recoil';
import { SsoIdentityProviderStatus } from '~/generated/graphql';
import { isDefined } from '~/utils/isDefined';

View File

@ -6,7 +6,6 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { TableCell } from '@/ui/layout/table/components/TableCell';
import { TableRow } from '@/ui/layout/table/components/TableRow';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import styled from '@emotion/styled';
import { useState } from 'react';
import {
@ -16,6 +15,7 @@ import {
IconTrash,
IconX,
LightIconButton,
MenuItem,
OverflowingTextWithTooltip,
} from 'twenty-ui';

View File

@ -1,5 +1,3 @@
import React, { useCallback, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import {
@ -9,7 +7,9 @@ import {
size,
useFloating,
} from '@floating-ui/react';
import { AppTooltip } from 'twenty-ui';
import React, { useCallback, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { AppTooltip, MenuItem, MenuItemSelect } from 'twenty-ui';
import { ReadonlyDeep } from 'type-fest';
import { useDebouncedCallback } from 'use-debounce';
@ -18,8 +18,6 @@ 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 { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { MenuItemSelect } from '@/ui/navigation/menu-item/components/MenuItemSelect';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { useUpdateEffect } from '~/hooks/useUpdateEffect';

View File

@ -3,8 +3,7 @@ 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 { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { IconHelpCircle, IconMessage } from 'twenty-ui';
import { IconHelpCircle, IconMessage, MenuItem } from 'twenty-ui';
export const SupportDropdown = () => {
const dropdownId = `support-field-active-action-dropdown`;

View File

@ -1,13 +1,12 @@
import styled from '@emotion/styled';
import { MouseEvent, useMemo, useRef, useState } from 'react';
import { IconComponent } from 'twenty-ui';
import { IconComponent, MenuItem } from 'twenty-ui';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
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 { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { SelectControl } from '@/ui/input/components/SelectControl';
import { isDefined } from '~/utils/isDefined';

View File

@ -6,7 +6,6 @@ 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 { MenuItemSelectTag } from '@/ui/navigation/menu-item/components/MenuItemSelectTag';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { useTheme } from '@emotion/react';
@ -20,7 +19,7 @@ import {
} from '@floating-ui/react';
import { useEffect, useMemo, useRef, useState } from 'react';
import { Key } from 'ts-key-enum';
import { TagColor, isDefined } from 'twenty-ui';
import { MenuItemSelectTag, TagColor, isDefined } from 'twenty-ui';
const StyledRelationPickerContainer = styled.div`
left: -1px;

View File

@ -4,9 +4,8 @@ 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 { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { MenuItemSelectAvatar } from '@/ui/navigation/menu-item/components/MenuItemSelectAvatar';
import { MenuItem, MenuItemSelectAvatar } from 'twenty-ui';
import { Currency } from './CurrencyPickerDropdownButton';
export const CurrencyPickerDropdownSelect = ({

View File

@ -2,12 +2,15 @@ import styled from '@emotion/styled';
import { DateTime } from 'luxon';
import ReactDatePicker from 'react-datepicker';
import { Key } from 'ts-key-enum';
import { IconCalendarX, OVERLAY_BACKGROUND } from 'twenty-ui';
import {
IconCalendarX,
MenuItemLeftContent,
OVERLAY_BACKGROUND,
StyledHoverableMenuItemBase,
} from 'twenty-ui';
import { DateTimeInput } from '@/ui/input/components/internal/date/components/DateTimeInput';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { MenuItemLeftContent } from '@/ui/navigation/menu-item/internals/components/MenuItemLeftContent';
import { StyledHoverableMenuItemBase } from '@/ui/navigation/menu-item/internals/components/StyledMenuItemBase';
import { isDefined } from '~/utils/isDefined';
import { AbsoluteDatePickerHeader } from '@/ui/input/components/internal/date/components/AbsoluteDatePickerHeader';

View File

@ -1,15 +1,14 @@
import { useMemo, useState } from 'react';
import styled from '@emotion/styled';
import { useMemo, useState } from 'react';
import { Country } from '@/ui/input/components/internal/types/Country';
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 { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { MenuItemSelectAvatar } from '@/ui/navigation/menu-item/components/MenuItemSelectAvatar';
import 'react-phone-number-input/style.css';
import { MenuItem, MenuItemSelectAvatar } from 'twenty-ui';
const StyledIconContainer = styled.div`
align-items: center;

View File

@ -1,10 +1,9 @@
import { SuggestionMenuProps } from '@blocknote/react';
import styled from '@emotion/styled';
import { IconComponent } from 'twenty-ui';
import { IconComponent, MenuItemSuggestion } from 'twenty-ui';
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { MenuItemSuggestion } from '@/ui/navigation/menu-item/components/MenuItemSuggestion';
export type SuggestionItem = {
title: string;

View File

@ -1,7 +1,6 @@
import { css } from '@emotion/react';
import styled from '@emotion/styled';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { MenuItem } from 'twenty-ui';
const StyledCreateNewButton = styled(MenuItem)<{ hovered?: boolean }>`
${({ hovered, theme }) =>

View File

@ -1,9 +1,8 @@
import { DragDropContext, Droppable } from '@hello-pangea/dnd';
import { Meta, StoryObj } from '@storybook/react';
import { ComponentDecorator, IconBell } from 'twenty-ui';
import { ComponentDecorator, IconBell, MenuItemDraggable } from 'twenty-ui';
import { DraggableItem } from '@/ui/layout/draggable-list/components/DraggableItem';
import { MenuItemDraggable } from '@/ui/navigation/menu-item/components/MenuItemDraggable';
const meta: Meta<typeof DraggableItem> = {
title: 'UI/Layout/DraggableList/DraggableItem',

View File

@ -1,10 +1,8 @@
import { action } from '@storybook/addon-actions';
import { Meta, StoryObj } from '@storybook/react';
import { ComponentDecorator, IconBell } from 'twenty-ui';
import { DraggableItem } from '@/ui/layout/draggable-list/components/DraggableItem';
import { DraggableList } from '@/ui/layout/draggable-list/components/DraggableList';
import { MenuItemDraggable } from '@/ui/navigation/menu-item/components/MenuItemDraggable';
import { action } from '@storybook/addon-actions';
import { Meta, StoryObj } from '@storybook/react';
import { ComponentDecorator, IconBell, MenuItemDraggable } from 'twenty-ui';
const meta: Meta<typeof DraggableList> = {
title: 'UI/Layout/DraggableList/DraggableList',

View File

@ -3,12 +3,16 @@ import { Decorator, Meta, StoryObj } from '@storybook/react';
import { expect, userEvent, waitFor, within } from '@storybook/test';
import { PlayFunction } from '@storybook/types';
import { useState } from 'react';
import { Avatar, Button, ComponentDecorator } from 'twenty-ui';
import {
Avatar,
Button,
ComponentDecorator,
MenuItem,
MenuItemMultiSelectAvatar,
MenuItemSelectAvatar,
} from 'twenty-ui';
import { DropdownMenuSkeletonItem } from '@/ui/input/relation-picker/components/skeletons/DropdownMenuSkeletonItem';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { MenuItemMultiSelectAvatar } from '@/ui/navigation/menu-item/components/MenuItemMultiSelectAvatar';
import { MenuItemSelectAvatar } from '@/ui/navigation/menu-item/components/MenuItemSelectAvatar';
import { Dropdown } from '../Dropdown';
import { DropdownMenuHeader } from '../DropdownMenuHeader';

View File

@ -1,5 +1,11 @@
import styled from '@emotion/styled';
import { IconButton, IconCheckbox, IconNotes, IconPlus } from 'twenty-ui';
import {
IconButton,
IconCheckbox,
IconNotes,
IconPlus,
MenuItem,
} from 'twenty-ui';
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
@ -7,7 +13,6 @@ 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 { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { Dropdown } from '../../dropdown/components/Dropdown';

View File

@ -6,13 +6,13 @@ import {
IconDotsVertical,
IconRestore,
IconTrash,
MenuItem,
} from 'twenty-ui';
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
import { useDestroyOneRecord } from '@/object-record/hooks/useDestroyOneRecord';

View File

@ -1,89 +0,0 @@
import { useTheme } from '@emotion/react';
import { FunctionComponent, MouseEvent, ReactElement, ReactNode } from 'react';
import {
IconChevronRight,
IconComponent,
LightIconButtonGroup,
LightIconButtonProps,
} from 'twenty-ui';
import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent';
import {
StyledHoverableMenuItemBase,
StyledMenuItemLeftContent,
} from '../internals/components/StyledMenuItemBase';
import { MenuItemAccent } from '../types/MenuItemAccent';
export type MenuItemIconButton = {
Wrapper?: FunctionComponent<{ iconButton: ReactElement }>;
Icon: IconComponent;
accent?: LightIconButtonProps['accent'];
onClick?: (event: MouseEvent<any>) => void;
};
export type MenuItemProps = {
accent?: MenuItemAccent;
className?: string;
iconButtons?: MenuItemIconButton[];
isIconDisplayedOnHoverOnly?: boolean;
isTooltipOpen?: boolean;
LeftIcon?: IconComponent | null;
onClick?: (event: MouseEvent<HTMLDivElement>) => void;
onMouseEnter?: (event: MouseEvent<HTMLDivElement>) => void;
onMouseLeave?: (event: MouseEvent<HTMLDivElement>) => void;
testId?: string;
text: ReactNode;
hasSubMenu?: boolean;
};
export const MenuItem = ({
accent = 'default',
className,
iconButtons,
isIconDisplayedOnHoverOnly = true,
LeftIcon,
onClick,
onMouseEnter,
onMouseLeave,
testId,
text,
hasSubMenu = false,
}: MenuItemProps) => {
const theme = useTheme();
const showIconButtons = Array.isArray(iconButtons) && iconButtons.length > 0;
const handleMenuItemClick = (event: MouseEvent<HTMLDivElement>) => {
if (!onClick) return;
event.preventDefault();
event.stopPropagation();
onClick?.(event);
};
return (
<StyledHoverableMenuItemBase
data-testid={testId ?? undefined}
onClick={handleMenuItemClick}
className={className}
accent={accent}
isIconDisplayedOnHoverOnly={isIconDisplayedOnHoverOnly}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
>
<StyledMenuItemLeftContent>
<MenuItemLeftContent LeftIcon={LeftIcon ?? undefined} text={text} />
</StyledMenuItemLeftContent>
<div className="hoverable-buttons">
{showIconButtons && (
<LightIconButtonGroup iconButtons={iconButtons} size="small" />
)}
</div>
{hasSubMenu && (
<IconChevronRight
size={theme.icon.size.sm}
color={theme.font.color.tertiary}
/>
)}
</StyledHoverableMenuItemBase>
);
};

View File

@ -1,105 +0,0 @@
import { useTheme } from '@emotion/react';
import { FunctionComponent, MouseEvent, ReactElement } from 'react';
import {
Avatar,
AvatarProps,
IconChevronRight,
IconComponent,
LightIconButtonGroup,
LightIconButtonProps,
OverflowingTextWithTooltip,
isDefined,
} from 'twenty-ui';
import {
StyledHoverableMenuItemBase,
StyledMenuItemLeftContent,
} from '../internals/components/StyledMenuItemBase';
import { MenuItemAccent } from '../types/MenuItemAccent';
export type MenuItemIconButton = {
Wrapper?: FunctionComponent<{ iconButton: ReactElement }>;
Icon: IconComponent;
accent?: LightIconButtonProps['accent'];
onClick?: (event: MouseEvent<any>) => void;
};
export type MenuItemAvatarProps = {
accent?: MenuItemAccent;
className?: string;
iconButtons?: MenuItemIconButton[];
isIconDisplayedOnHoverOnly?: boolean;
isTooltipOpen?: boolean;
avatar?: Pick<
AvatarProps,
'avatarUrl' | 'placeholderColorSeed' | 'placeholder' | 'size' | 'type'
> | null;
onClick?: (event: MouseEvent<HTMLDivElement>) => void;
onMouseEnter?: (event: MouseEvent<HTMLDivElement>) => void;
onMouseLeave?: (event: MouseEvent<HTMLDivElement>) => void;
testId?: string;
text: string;
hasSubMenu?: boolean;
};
// TODO: merge with MenuItem
export const MenuItemAvatar = ({
accent = 'default',
className,
iconButtons,
isIconDisplayedOnHoverOnly = true,
onClick,
onMouseEnter,
onMouseLeave,
testId,
avatar,
hasSubMenu = false,
text,
}: MenuItemAvatarProps) => {
const theme = useTheme();
const showIconButtons = Array.isArray(iconButtons) && iconButtons.length > 0;
const handleMenuItemClick = (event: MouseEvent<HTMLDivElement>) => {
if (!onClick) return;
event.preventDefault();
event.stopPropagation();
onClick?.(event);
};
return (
<StyledHoverableMenuItemBase
data-testid={testId ?? undefined}
onClick={handleMenuItemClick}
className={className}
accent={accent}
isIconDisplayedOnHoverOnly={isIconDisplayedOnHoverOnly}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
>
<StyledMenuItemLeftContent>
{isDefined(avatar) && (
<Avatar
placeholder={avatar.placeholder}
avatarUrl={avatar.avatarUrl}
placeholderColorSeed={avatar.placeholderColorSeed}
size={avatar.size}
type={avatar.type}
/>
)}
<OverflowingTextWithTooltip text={text ?? ''} />
</StyledMenuItemLeftContent>
<div className="hoverable-buttons">
{showIconButtons && (
<LightIconButtonGroup iconButtons={iconButtons} size="small" />
)}
</div>
{hasSubMenu && (
<IconChevronRight
size={theme.icon.size.sm}
color={theme.font.color.tertiary}
/>
)}
</StyledHoverableMenuItemBase>
);
};

View File

@ -1,114 +0,0 @@
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { IconComponent } from 'twenty-ui';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import {
StyledMenuItemLabel,
StyledMenuItemLeftContent,
} from '../internals/components/StyledMenuItemBase';
import { MenuItemCommandHotKeys } from './MenuItemCommandHotKeys';
const StyledMenuItemLabelText = styled(StyledMenuItemLabel)`
color: ${({ theme }) => theme.font.color.primary};
`;
const StyledBigIconContainer = styled.div`
align-items: center;
background: ${({ theme }) => theme.background.transparent.light};
border-radius: ${({ theme }) => theme.border.radius.sm};
display: flex;
flex-direction: row;
padding: ${({ theme }) => theme.spacing(1)};
`;
const StyledMenuItemCommandContainer = styled.div<{ isSelected?: boolean }>`
--horizontal-padding: ${({ theme }) => theme.spacing(1)};
--vertical-padding: ${({ theme }) => theme.spacing(2)};
align-items: center;
background: ${({ isSelected, theme }) =>
isSelected
? theme.background.transparent.light
: theme.background.secondary};
border-radius: ${({ theme }) => theme.border.radius.sm};
color: ${({ theme }) => theme.font.color.secondary};
cursor: pointer;
display: flex;
flex-direction: row;
font-size: ${({ theme }) => theme.font.size.sm};
gap: ${({ theme }) => theme.spacing(2)};
justify-content: space-between;
padding: var(--vertical-padding) var(--horizontal-padding);
position: relative;
transition: all 150ms ease;
transition-property: none;
user-select: none;
width: calc(100% - 2 * var(--horizontal-padding));
&:hover {
background: ${({ theme }) => theme.background.transparent.light};
}
&[data-selected='true'] {
background: ${({ theme }) => theme.background.tertiary};
}
&[data-disabled='true'] {
color: ${({ theme }) => theme.font.color.light};
cursor: not-allowed;
}
svg {
height: 16px;
width: 16px;
}
`;
export type MenuItemCommandProps = {
LeftIcon?: IconComponent;
text: string;
firstHotKey?: string;
secondHotKey?: string;
className?: string;
isSelected?: boolean;
onClick?: () => void;
};
export const MenuItemCommand = ({
LeftIcon,
text,
firstHotKey,
secondHotKey,
className,
isSelected,
onClick,
}: MenuItemCommandProps) => {
const theme = useTheme();
const isMobile = useIsMobile();
return (
<StyledMenuItemCommandContainer
onClick={onClick}
className={className}
isSelected={isSelected}
>
<StyledMenuItemLeftContent>
{LeftIcon && (
<StyledBigIconContainer>
<LeftIcon size={theme.icon.size.sm} />
</StyledBigIconContainer>
)}
<StyledMenuItemLabelText hasLeftIcon={!!LeftIcon}>
{text}
</StyledMenuItemLabelText>
</StyledMenuItemLeftContent>
{!isMobile && (
<MenuItemCommandHotKeys
firstHotKey={firstHotKey}
secondHotKey={secondHotKey}
/>
)}
</StyledMenuItemCommandContainer>
);
};

View File

@ -1,62 +0,0 @@
import styled from '@emotion/styled';
const StyledCommandTextContainer = styled.div`
align-items: center;
display: flex;
flex-direction: row;
gap: ${({ theme }) => theme.spacing(1)};
justify-content: center;
`;
const StyledCommandText = styled.div`
color: ${({ theme }) => theme.font.color.light};
padding-bottom: ${({ theme }) => theme.spacing(1)};
padding-left: ${({ theme }) => theme.spacing(2)};
padding-right: ${({ theme }) => theme.spacing(2)};
padding-top: ${({ theme }) => theme.spacing(1)};
white-space: nowrap;
`;
const StyledCommandKey = styled.div`
align-items: center;
background-color: ${({ theme }) => theme.background.secondary};
border: 1px solid ${({ theme }) => theme.border.color.strong};
border-radius: ${({ theme }) => theme.border.radius.sm};
box-shadow: ${({ theme }) => theme.boxShadow.underline};
display: flex;
flex-direction: column;
height: ${({ theme }) => theme.spacing(5)};
height: 18px;
justify-content: center;
text-align: center;
width: ${({ theme }) => theme.spacing(4)};
`;
export type MenuItemCommandHotKeysProps = {
firstHotKey?: string;
joinLabel?: string;
secondHotKey?: string;
};
export const MenuItemCommandHotKeys = ({
firstHotKey,
secondHotKey,
joinLabel = 'then',
}: MenuItemCommandHotKeysProps) => {
return (
<StyledCommandText>
{firstHotKey && (
<StyledCommandTextContainer>
<StyledCommandKey>{firstHotKey}</StyledCommandKey>
{secondHotKey && (
<>
{joinLabel}
<StyledCommandKey>{secondHotKey}</StyledCommandKey>
</>
)}
</StyledCommandTextContainer>
)}
</StyledCommandText>
);
};

View File

@ -1,65 +0,0 @@
import { IconComponent, LightIconButtonGroup } from 'twenty-ui';
import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent';
import { StyledHoverableMenuItemBase } from '../internals/components/StyledMenuItemBase';
import { MenuItemAccent } from '../types/MenuItemAccent';
import { MenuItemIconButton } from './MenuItem';
import { ReactNode } from 'react';
export type MenuItemDraggableProps = {
LeftIcon?: IconComponent | undefined;
accent?: MenuItemAccent;
iconButtons?: MenuItemIconButton[];
isTooltipOpen?: boolean;
onClick?: () => void;
text: ReactNode;
className?: string;
isIconDisplayedOnHoverOnly?: boolean;
showGrip?: boolean;
isDragDisabled?: boolean;
isHoverDisabled?: boolean;
};
export const MenuItemDraggable = ({
LeftIcon,
accent = 'default',
iconButtons,
onClick,
text,
isDragDisabled = false,
className,
isIconDisplayedOnHoverOnly = true,
showGrip = false,
}: MenuItemDraggableProps) => {
const showIconButtons = Array.isArray(iconButtons) && iconButtons.length > 0;
const cursorType = showGrip
? isDragDisabled
? 'not-allowed'
: 'drag'
: 'default';
return (
<StyledHoverableMenuItemBase
onClick={onClick}
accent={accent}
className={className}
isIconDisplayedOnHoverOnly={isIconDisplayedOnHoverOnly}
cursor={cursorType}
>
<MenuItemLeftContent
LeftIcon={LeftIcon}
text={text}
isDisabled={isDragDisabled}
showGrip={showGrip}
/>
{showIconButtons && (
<LightIconButtonGroup
className="hoverable-buttons"
iconButtons={iconButtons}
/>
)}
</StyledHoverableMenuItemBase>
);
};

View File

@ -1,54 +0,0 @@
import styled from '@emotion/styled';
import { Checkbox, IconComponent, Tag, ThemeColor } from 'twenty-ui';
import { MenuItemLeftContent } from '@/ui/navigation/menu-item/internals/components/MenuItemLeftContent';
import { StyledMenuItemBase } from '../internals/components/StyledMenuItemBase';
const StyledLeftContentWithCheckboxContainer = styled.div`
align-items: center;
display: flex;
flex-direction: row;
gap: ${({ theme }) => theme.spacing(2)};
`;
type MenuItemMultiSelectProps = {
color?: ThemeColor;
LeftIcon?: IconComponent;
selected: boolean;
isKeySelected?: boolean;
text: string;
className: string;
onSelectChange?: (selected: boolean) => void;
};
export const MenuItemMultiSelect = ({
color,
LeftIcon,
text,
selected,
isKeySelected,
className,
onSelectChange,
}: MenuItemMultiSelectProps) => {
const handleOnClick = () => {
onSelectChange?.(!selected);
};
return (
<StyledMenuItemBase
isKeySelected={isKeySelected}
className={className}
onClick={handleOnClick}
>
<StyledLeftContentWithCheckboxContainer>
<Checkbox checked={selected} />
{color ? (
<Tag color={color} text={text} Icon={LeftIcon} />
) : (
<MenuItemLeftContent LeftIcon={LeftIcon} text={text} />
)}
</StyledLeftContentWithCheckboxContainer>
</StyledMenuItemBase>
);
};

View File

@ -1,57 +0,0 @@
import styled from '@emotion/styled';
import { ReactNode } from 'react';
import { Checkbox, OverflowingTextWithTooltip } from 'twenty-ui';
import {
StyledMenuItemBase,
StyledMenuItemLabel,
StyledMenuItemLeftContent,
} from '../internals/components/StyledMenuItemBase';
const StyledLeftContentWithCheckboxContainer = styled.div`
align-items: center;
display: flex;
flex-direction: row;
gap: ${({ theme }) => theme.spacing(2)};
width: 100%;
`;
type MenuItemMultiSelectAvatarProps = {
avatar?: ReactNode;
selected: boolean;
isKeySelected?: boolean;
text?: string;
className?: string;
onSelectChange?: (selected: boolean) => void;
};
export const MenuItemMultiSelectAvatar = ({
avatar,
text,
selected,
className,
isKeySelected,
onSelectChange,
}: MenuItemMultiSelectAvatarProps) => {
const handleOnClick = () => {
onSelectChange?.(!selected);
};
return (
<StyledMenuItemBase
className={className}
onClick={handleOnClick}
isKeySelected={isKeySelected}
>
<StyledLeftContentWithCheckboxContainer>
<Checkbox checked={selected} />
<StyledMenuItemLeftContent>
{avatar}
<StyledMenuItemLabel hasLeftIcon={!!avatar}>
<OverflowingTextWithTooltip text={text} />
</StyledMenuItemLabel>
</StyledMenuItemLeftContent>
</StyledLeftContentWithCheckboxContainer>
</StyledMenuItemBase>
);
};

View File

@ -1,47 +0,0 @@
import {
Checkbox,
CheckboxShape,
CheckboxSize,
Tag,
ThemeColor,
} from 'twenty-ui';
import {
StyledMenuItemBase,
StyledMenuItemLeftContent,
} from '../internals/components/StyledMenuItemBase';
type MenuItemMultiSelectTagProps = {
selected: boolean;
className?: string;
isKeySelected?: boolean;
onClick?: () => void;
color: ThemeColor;
text: string;
};
export const MenuItemMultiSelectTag = ({
color,
selected,
className,
onClick,
isKeySelected,
text,
}: MenuItemMultiSelectTagProps) => {
return (
<StyledMenuItemBase
isKeySelected={isKeySelected}
onClick={onClick}
className={className}
>
<Checkbox
size={CheckboxSize.Small}
shape={CheckboxShape.Squared}
checked={selected}
/>
<StyledMenuItemLeftContent>
<Tag color={color} text={text} />
</StyledMenuItemLeftContent>
</StyledMenuItemBase>
);
};

View File

@ -1,36 +0,0 @@
import { useTheme } from '@emotion/react';
import { IconChevronRight, IconComponent } from 'twenty-ui';
import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent';
import {
StyledMenuItemBase,
StyledMenuItemLeftContent,
} from '../internals/components/StyledMenuItemBase';
export type MenuItemNavigateProps = {
LeftIcon?: IconComponent;
text: string;
onClick?: () => void;
className?: string;
};
export const MenuItemNavigate = ({
LeftIcon,
text,
className,
onClick,
}: MenuItemNavigateProps) => {
const theme = useTheme();
return (
<StyledMenuItemBase onClick={onClick} className={className}>
<StyledMenuItemLeftContent>
<MenuItemLeftContent LeftIcon={LeftIcon} text={text} />
</StyledMenuItemLeftContent>
<IconChevronRight
size={theme.icon.size.sm}
color={theme.font.color.tertiary}
/>
</StyledMenuItemBase>
);
};

View File

@ -1,81 +0,0 @@
import { css, useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { IconCheck, IconChevronRight, IconComponent } from 'twenty-ui';
import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent';
import { StyledMenuItemBase } from '../internals/components/StyledMenuItemBase';
export const StyledMenuItemSelect = styled(StyledMenuItemBase)<{
selected: boolean;
disabled?: boolean;
hovered?: boolean;
}>`
${({ theme, selected, disabled, hovered }) => {
if (selected) {
return css`
background: ${theme.background.transparent.light};
&:hover {
background: ${theme.background.transparent.medium};
}
`;
} else if (disabled === true) {
return css`
background: inherit;
&:hover {
background: inherit;
}
color: ${theme.font.color.tertiary};
cursor: default;
`;
} else if (hovered === true) {
return css`
background: ${theme.background.transparent.light};
`;
}
}}
`;
type MenuItemSelectProps = {
LeftIcon: IconComponent | null | undefined;
selected: boolean;
text: string;
className?: string;
onClick?: () => void;
disabled?: boolean;
hovered?: boolean;
hasSubMenu?: boolean;
};
export const MenuItemSelect = ({
LeftIcon,
text,
selected,
className,
onClick,
disabled,
hovered,
hasSubMenu = false,
}: MenuItemSelectProps) => {
const theme = useTheme();
return (
<StyledMenuItemSelect
onClick={onClick}
className={className}
selected={selected}
disabled={disabled}
hovered={hovered}
>
<MenuItemLeftContent LeftIcon={LeftIcon} text={text} />
{selected && <IconCheck size={theme.icon.size.md} />}
{hasSubMenu && (
<IconChevronRight
size={theme.icon.size.sm}
color={theme.font.color.tertiary}
/>
)}
</StyledMenuItemSelect>
);
};

View File

@ -1,53 +0,0 @@
import { ReactNode } from 'react';
import { useTheme } from '@emotion/react';
import { IconCheck, OverflowingTextWithTooltip } from 'twenty-ui';
import {
StyledMenuItemLabel,
StyledMenuItemLeftContent,
} from '../internals/components/StyledMenuItemBase';
import { StyledMenuItemSelect } from './MenuItemSelect';
type MenuItemSelectAvatarProps = {
avatar?: ReactNode;
selected: boolean;
text: string;
className?: string;
onClick?: () => void;
disabled?: boolean;
hovered?: boolean;
testId?: string;
};
export const MenuItemSelectAvatar = ({
avatar,
text,
selected,
className,
onClick,
disabled,
hovered,
testId,
}: MenuItemSelectAvatarProps) => {
const theme = useTheme();
return (
<StyledMenuItemSelect
onClick={onClick}
className={className}
selected={selected}
disabled={disabled}
hovered={hovered}
data-testid={testId}
>
<StyledMenuItemLeftContent>
{avatar}
<StyledMenuItemLabel hasLeftIcon={!!avatar}>
<OverflowingTextWithTooltip text={text} />
</StyledMenuItemLabel>
</StyledMenuItemLeftContent>
{selected && <IconCheck size={theme.icon.size.md} />}
</StyledMenuItemSelect>
);
};

View File

@ -1,67 +0,0 @@
import { useTheme } from '@emotion/react';
import {
ColorSample,
ColorSampleVariant,
IconCheck,
ThemeColor,
} from 'twenty-ui';
import {
StyledMenuItemLabel,
StyledMenuItemLeftContent,
} from '../internals/components/StyledMenuItemBase';
import { StyledMenuItemSelect } from './MenuItemSelect';
type MenuItemSelectColorProps = {
selected: boolean;
className?: string;
onClick?: () => void;
disabled?: boolean;
hovered?: boolean;
color: ThemeColor;
variant?: ColorSampleVariant;
};
export const colorLabels: Record<ThemeColor, string> = {
green: 'Green',
turquoise: 'Turquoise',
sky: 'Sky',
blue: 'Blue',
purple: 'Purple',
pink: 'Pink',
red: 'Red',
orange: 'Orange',
yellow: 'Yellow',
gray: 'Gray',
};
export const MenuItemSelectColor = ({
color,
selected,
className,
onClick,
disabled,
hovered,
variant = 'default',
}: MenuItemSelectColorProps) => {
const theme = useTheme();
return (
<StyledMenuItemSelect
onClick={onClick}
className={className}
selected={selected}
disabled={disabled}
hovered={hovered}
>
<StyledMenuItemLeftContent>
<ColorSample colorName={color} variant={variant} />
<StyledMenuItemLabel hasLeftIcon={true}>
{colorLabels[color]}
</StyledMenuItemLabel>
</StyledMenuItemLeftContent>
{selected && <IconCheck size={theme.icon.size.md} />}
</StyledMenuItemSelect>
);
};

View File

@ -1,42 +0,0 @@
import { useTheme } from '@emotion/react';
import { IconCheck, Tag, ThemeColor } from 'twenty-ui';
import { StyledMenuItemLeftContent } from '../internals/components/StyledMenuItemBase';
import { StyledMenuItemSelect } from './MenuItemSelect';
type MenuItemSelectTagProps = {
selected: boolean;
isKeySelected?: boolean;
className?: string;
onClick?: () => void;
color: ThemeColor | 'transparent';
text: string;
variant?: 'solid' | 'outline';
};
export const MenuItemSelectTag = ({
color,
selected,
isKeySelected,
className,
onClick,
text,
variant = 'solid',
}: MenuItemSelectTagProps) => {
const theme = useTheme();
return (
<StyledMenuItemSelect
onClick={onClick}
className={className}
selected={selected}
isKeySelected={isKeySelected}
>
<StyledMenuItemLeftContent>
<Tag variant={variant} color={color} text={text} />
</StyledMenuItemLeftContent>
{selected && <IconCheck size={theme.icon.size.sm} />}
</StyledMenuItemSelect>
);
};

View File

@ -1,77 +0,0 @@
import styled from '@emotion/styled';
import { MouseEvent } from 'react';
import { HOVER_BACKGROUND, IconComponent } from 'twenty-ui';
import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent';
import { StyledMenuItemLeftContent } from '../internals/components/StyledMenuItemBase';
export type MenuItemSuggestionProps = {
LeftIcon?: IconComponent | null;
text: string;
selected?: boolean;
className?: string;
onClick?: (event: MouseEvent<HTMLLIElement>) => void;
};
const StyledSuggestionMenuItem = styled.li<{
selected?: boolean;
}>`
--horizontal-padding: ${({ theme }) => theme.spacing(1)};
--vertical-padding: ${({ theme }) => theme.spacing(2)};
align-items: center;
border-radius: ${({ theme }) => theme.border.radius.sm};
cursor: pointer;
display: flex;
flex-direction: row;
font-size: ${({ theme }) => theme.font.size.sm};
gap: ${({ theme }) => theme.spacing(2)};
height: calc(32px - 2 * var(--vertical-padding));
justify-content: space-between;
padding: var(--vertical-padding) var(--horizontal-padding);
background: ${({ selected, theme }) =>
selected ? theme.background.transparent.medium : ''};
${HOVER_BACKGROUND};
position: relative;
user-select: none;
width: calc(100% - 2 * var(--horizontal-padding));
`;
export const MenuItemSuggestion = ({
LeftIcon,
text,
className,
selected,
onClick,
}: MenuItemSuggestionProps) => {
const handleMenuItemClick = (event: MouseEvent<HTMLLIElement>) => {
if (!onClick) return;
event.preventDefault();
event.stopPropagation();
onClick?.(event);
};
return (
<StyledSuggestionMenuItem
onClick={handleMenuItemClick}
className={className}
selected={selected}
>
<StyledMenuItemLeftContent>
<MenuItemLeftContent LeftIcon={LeftIcon ?? undefined} text={text} />
</StyledMenuItemLeftContent>
</StyledSuggestionMenuItem>
);
};

View File

@ -1,38 +0,0 @@
import { IconComponent, Toggle, ToggleSize } from 'twenty-ui';
import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent';
import {
StyledMenuItemBase,
StyledMenuItemRightContent,
} from '../internals/components/StyledMenuItemBase';
type MenuItemToggleProps = {
LeftIcon?: IconComponent;
toggled: boolean;
text: string;
className?: string;
onToggleChange?: (toggled: boolean) => void;
toggleSize?: ToggleSize;
};
export const MenuItemToggle = ({
LeftIcon,
text,
toggled,
className,
onToggleChange,
toggleSize,
}: MenuItemToggleProps) => {
return (
<StyledMenuItemBase className={className}>
<MenuItemLeftContent LeftIcon={LeftIcon} text={text} />
<StyledMenuItemRightContent>
<Toggle
value={toggled}
onChange={onToggleChange}
toggleSize={toggleSize}
/>
</StyledMenuItemRightContent>
</StyledMenuItemBase>
);
};

View File

@ -1,3 +1,5 @@
import { SelectHotkeyScope } from '@/ui/input/types/SelectHotkeyScope';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { useTheme } from '@emotion/react';
import { FunctionComponent, MouseEvent, ReactElement, ReactNode } from 'react';
import {
@ -6,16 +8,11 @@ import {
IconDotsVertical,
LightIconButton,
LightIconButtonProps,
} from 'twenty-ui';
import { SelectHotkeyScope } from '@/ui/input/types/SelectHotkeyScope';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent';
import {
MenuItemAccent,
MenuItemLeftContent,
StyledHoverableMenuItemBase,
StyledMenuItemLeftContent,
} from '../internals/components/StyledMenuItemBase';
import { MenuItemAccent } from '../types/MenuItemAccent';
} from 'twenty-ui';
export type MenuItemIconButton = {
Wrapper?: FunctionComponent<{ iconButton: ReactElement }>;

View File

@ -1,112 +0,0 @@
import { action } from '@storybook/addon-actions';
import { Meta, StoryObj } from '@storybook/react';
import {
CatalogDecorator,
CatalogStory,
ComponentDecorator,
IconBell,
} from 'twenty-ui';
import { MenuItemAccent } from '../../types/MenuItemAccent';
import { MenuItem } from '../MenuItem';
const meta: Meta<typeof MenuItem> = {
title: 'UI/Navigation/MenuItem/MenuItem',
component: MenuItem,
};
export default meta;
type Story = StoryObj<typeof MenuItem>;
export const Default: Story = {
args: {
text: 'Menu item text',
LeftIcon: IconBell,
accent: 'default',
iconButtons: [
{ Icon: IconBell, onClick: action('Clicked') },
{ Icon: IconBell, onClick: action('Clicked') },
],
},
decorators: [ComponentDecorator],
};
export const Catalog: CatalogStory<Story, typeof MenuItem> = {
args: { ...Default.args },
argTypes: {
accent: { control: false },
className: { control: false },
iconButtons: { control: false },
},
parameters: {
pseudo: { hover: ['.hover'], active: ['.pressed'], focus: ['.focus'] },
catalog: {
dimensions: [
{
name: 'withIcon',
values: [true, false],
props: (withIcon: boolean) => ({
LeftIcon: withIcon ? IconBell : undefined,
}),
labels: (withIcon: boolean) =>
withIcon ? 'With left icon' : 'Without left icon',
},
{
name: 'accents',
values: ['default', 'danger'] satisfies MenuItemAccent[],
props: (accent: MenuItemAccent) => ({ accent }),
},
{
name: 'states',
values: ['default', 'hover'],
props: (state: string) => {
switch (state) {
case 'default':
return {};
case 'hover':
return { className: state };
default:
return {};
}
},
},
{
name: 'iconButtons',
values: ['no icon button', 'two icon buttons'],
props: (choice: string) => {
switch (choice) {
case 'no icon button': {
return {
iconButtons: [],
};
}
case 'two icon buttons': {
return {
iconButtons: [
{
Icon: IconBell,
onClick: action('Clicked on first icon button'),
},
{
Icon: IconBell,
onClick: action('Clicked on second icon button'),
},
],
};
}
default:
return {};
}
},
},
],
options: {
elementContainer: {
width: 200,
},
},
},
},
decorators: [CatalogDecorator],
};

View File

@ -1,96 +0,0 @@
import { Meta, StoryObj } from '@storybook/react';
import {
CatalogDecorator,
CatalogStory,
ComponentDecorator,
IconBell,
} from 'twenty-ui';
import { MenuItemCommand } from '../MenuItemCommand';
const meta: Meta<typeof MenuItemCommand> = {
title: 'UI/Navigation/MenuItem/MenuItemCommand',
component: MenuItemCommand,
};
export default meta;
type Story = StoryObj<typeof MenuItemCommand>;
export const Default: Story = {
args: {
text: 'First option',
firstHotKey: '⌘',
secondHotKey: '1',
},
render: (props) => (
<MenuItemCommand
LeftIcon={props.LeftIcon}
text={props.text}
firstHotKey={props.firstHotKey}
secondHotKey={props.secondHotKey}
className={props.className}
onClick={props.onClick}
isSelected={false}
></MenuItemCommand>
),
decorators: [ComponentDecorator],
};
export const Catalog: CatalogStory<Story, typeof MenuItemCommand> = {
args: {
text: 'Menu item',
firstHotKey: '⌘',
secondHotKey: '1',
},
argTypes: {
className: { control: false },
},
parameters: {
pseudo: { hover: ['.hover'] },
catalog: {
dimensions: [
{
name: 'withIcon',
values: [true, false],
props: (withIcon: boolean) => ({
LeftIcon: withIcon ? IconBell : undefined,
}),
labels: (withIcon: boolean) =>
withIcon ? 'With left icon' : 'Without left icon',
},
{
name: 'states',
values: ['default', 'hover'],
props: (state: string) => {
switch (state) {
case 'default':
return {};
case 'hover':
return { className: state };
default:
return {};
}
},
},
],
options: {
elementContainer: {
width: 200,
},
},
},
},
render: (props) => (
<MenuItemCommand
LeftIcon={props.LeftIcon}
text={props.text}
firstHotKey={props.firstHotKey}
secondHotKey={props.secondHotKey}
className={props.className}
onClick={props.onClick}
isSelected={false}
></MenuItemCommand>
),
decorators: [CatalogDecorator],
};

View File

@ -1,114 +0,0 @@
import { action } from '@storybook/addon-actions';
import { Meta, StoryObj } from '@storybook/react';
import {
CatalogDecorator,
CatalogDimension,
CatalogOptions,
ComponentDecorator,
IconBell,
IconMinus,
} from 'twenty-ui';
import { MenuItemAccent } from '../../types/MenuItemAccent';
import { MenuItemDraggable } from '../MenuItemDraggable';
const meta: Meta<typeof MenuItemDraggable> = {
title: 'ui/Navigation/MenuItem/MenuItemDraggable',
component: MenuItemDraggable,
};
export default meta;
type Story = StoryObj<typeof MenuItemDraggable>;
export const Default: Story = {
args: {
LeftIcon: IconBell,
accent: 'default',
iconButtons: [{ Icon: IconMinus, onClick: action('Clicked') }],
onClick: action('Clicked'),
text: 'Menu item draggable',
isDragDisabled: false,
},
decorators: [ComponentDecorator],
};
export const Catalog: Story = {
args: { ...Default.args },
argTypes: {
accent: { control: false },
iconButtons: { control: false },
},
parameters: {
pseudo: { hover: ['.hover'] },
catalog: {
dimensions: [
{
name: 'isDragDisabled',
values: [true, false],
props: (isDragDisabled: boolean) => ({
isDragDisabled: isDragDisabled,
}),
labels: (isDragDisabled: boolean) =>
isDragDisabled ? 'Without drag icon' : 'With drag icon',
},
{
name: 'accents',
values: ['default', 'danger', 'placeholder'] as MenuItemAccent[],
props: (accent: MenuItemAccent) => ({ accent }),
},
{
name: 'states',
values: ['default', 'hover'],
props: (state: string) => {
switch (state) {
case 'default':
return {};
case 'hover':
return { className: state };
default:
return {};
}
},
},
{
name: 'iconButtons',
values: ['no icon button', 'minus icon buttons'],
props: (choice: string) => {
switch (choice) {
case 'no icon button': {
return {
iconButtons: [],
};
}
case 'minus icon buttons': {
return {
iconButtons: [
{
Icon: IconMinus,
onClick: action('Clicked on minus icon button'),
},
],
};
}
}
},
},
] as CatalogDimension[],
options: {
elementContainer: {
width: 200,
},
} as CatalogOptions,
},
},
decorators: [CatalogDecorator],
};
export const Grip: Story = {
args: { ...Default.args, showGrip: true, isDragDisabled: false },
};
export const HoverDisabled: Story = {
args: { ...Default.args, isHoverDisabled: true },
};

View File

@ -1,77 +0,0 @@
import { Meta, StoryObj } from '@storybook/react';
import {
CatalogDecorator,
CatalogDimension,
CatalogOptions,
CatalogStory,
ComponentDecorator,
IconBell,
} from 'twenty-ui';
import { MenuItemMultiSelect } from '../MenuItemMultiSelect';
const meta: Meta<typeof MenuItemMultiSelect> = {
title: 'UI/Navigation/MenuItem/MenuItemMultiSelect',
component: MenuItemMultiSelect,
};
export default meta;
type Story = StoryObj<typeof MenuItemMultiSelect>;
export const Default: Story = {
args: {
text: 'First option',
},
decorators: [ComponentDecorator],
};
export const Catalog: CatalogStory<Story, typeof MenuItemMultiSelect> = {
args: { LeftIcon: IconBell, text: 'Menu item' },
argTypes: {
className: { control: false },
},
parameters: {
pseudo: { hover: ['.hover'], active: ['.pressed'], focus: ['.focus'] },
catalog: {
dimensions: [
{
name: 'withIcon',
values: [true, false],
props: (withIcon: boolean) => ({
LeftIcon: withIcon ? IconBell : undefined,
}),
labels: (withIcon: boolean) =>
withIcon ? 'With left icon' : 'Without left icon',
},
{
name: 'selected',
values: [true, false],
props: (selected: boolean) => ({ selected }),
labels: (selected: boolean) =>
selected ? 'Selected' : 'Not selected',
},
{
name: 'states',
values: ['default', 'hover'],
props: (state: string) => {
switch (state) {
case 'default':
return {};
case 'hover':
return { className: state };
default:
return {};
}
},
},
] as CatalogDimension[],
options: {
elementContainer: {
width: 200,
},
} as CatalogOptions,
},
},
decorators: [CatalogDecorator],
};

View File

@ -1,84 +0,0 @@
import { Meta, StoryObj } from '@storybook/react';
import {
Avatar,
CatalogDecorator,
CatalogDimension,
CatalogOptions,
CatalogStory,
ComponentDecorator,
} from 'twenty-ui';
import { avatarUrl } from '~/testing/mock-data/users';
import { MenuItemMultiSelectAvatar } from '../MenuItemMultiSelectAvatar';
const meta: Meta<typeof MenuItemMultiSelectAvatar> = {
title: 'UI/Navigation/MenuItem/MenuItemMultiSelectAvatar',
component: MenuItemMultiSelectAvatar,
};
export default meta;
type Story = StoryObj<typeof MenuItemMultiSelectAvatar>;
export const Default: Story = {
args: {
text: 'First option',
avatar: <Avatar avatarUrl={avatarUrl} placeholder="L" />,
},
decorators: [ComponentDecorator],
};
export const Catalog: CatalogStory<Story, typeof MenuItemMultiSelectAvatar> = {
args: { text: 'Menu item' },
argTypes: {
className: { control: false },
},
parameters: {
pseudo: { hover: ['.hover'], active: ['.pressed'], focus: ['.focus'] },
catalog: {
dimensions: [
{
name: 'withAvatar',
values: [true, false],
props: (withAvatar: boolean) => ({
avatar: withAvatar ? (
<Avatar avatarUrl={avatarUrl} placeholder="L" />
) : (
<Avatar avatarUrl={''} placeholder="L" />
),
}),
labels: (withAvatar: boolean) =>
withAvatar ? 'With avatar' : 'Without avatar',
},
{
name: 'selected',
values: [true, false],
props: (selected: boolean) => ({ selected }),
labels: (selected: boolean) =>
selected ? 'Selected' : 'Not selected',
},
{
name: 'states',
values: ['default', 'hover'],
props: (state: string) => {
switch (state) {
case 'default':
return {};
case 'hover':
return { className: state };
default:
return {};
}
},
},
] as CatalogDimension[],
options: {
elementContainer: {
width: 200,
},
} as CatalogOptions,
},
},
decorators: [CatalogDecorator],
};

View File

@ -1,70 +0,0 @@
import { Meta, StoryObj } from '@storybook/react';
import {
CatalogDecorator,
CatalogDimension,
CatalogOptions,
CatalogStory,
ComponentDecorator,
IconBell,
} from 'twenty-ui';
import { MenuItemNavigate } from '../MenuItemNavigate';
const meta: Meta<typeof MenuItemNavigate> = {
title: 'UI/Navigation/MenuItem/MenuItemNavigate',
component: MenuItemNavigate,
};
export default meta;
type Story = StoryObj<typeof MenuItemNavigate>;
export const Default: Story = {
args: {
text: 'First option',
},
decorators: [ComponentDecorator],
};
export const Catalog: CatalogStory<Story, typeof MenuItemNavigate> = {
args: { LeftIcon: IconBell, text: 'Menu item' },
argTypes: {
className: { control: false },
},
parameters: {
pseudo: { hover: ['.hover'], active: ['.pressed'], focus: ['.focus'] },
catalog: {
dimensions: [
{
name: 'withIcon',
values: [true, false],
props: (withIcon: boolean) => ({
LeftIcon: withIcon ? IconBell : undefined,
}),
labels: (withIcon: boolean) =>
withIcon ? 'With left icon' : 'Without left icon',
},
{
name: 'states',
values: ['default', 'hover'],
props: (state: string) => {
switch (state) {
case 'default':
return {};
case 'hover':
return { className: state };
default:
return {};
}
},
},
] as CatalogDimension[],
options: {
elementContainer: {
width: 200,
},
} as CatalogOptions,
},
},
decorators: [CatalogDecorator],
};

View File

@ -1,78 +0,0 @@
import { Meta, StoryObj } from '@storybook/react';
import {
CatalogDecorator,
CatalogDimension,
CatalogOptions,
CatalogStory,
ComponentDecorator,
IconBell,
} from 'twenty-ui';
import { MenuItemSelect } from '../MenuItemSelect';
const meta: Meta<typeof MenuItemSelect> = {
title: 'UI/Navigation/MenuItem/MenuItemSelect',
component: MenuItemSelect,
};
export default meta;
type Story = StoryObj<typeof MenuItemSelect>;
export const Default: Story = {
args: {
text: 'First option',
LeftIcon: IconBell,
},
argTypes: {
className: { control: false },
},
decorators: [ComponentDecorator],
};
export const Catalog: CatalogStory<Story, typeof MenuItemSelect> = {
args: { LeftIcon: IconBell, text: 'Menu item' },
argTypes: {
className: { control: false },
},
parameters: {
pseudo: { hover: ['.hover'], active: ['.pressed'], focus: ['.focus'] },
catalog: {
dimensions: [
{
name: 'withIcon',
values: [true, false],
props: (withIcon: boolean) => ({
LeftIcon: withIcon ? IconBell : undefined,
}),
labels: (withIcon: boolean) =>
withIcon ? 'With left icon' : 'Without left icon',
},
{
name: 'states',
values: ['default', 'hover', 'selected', 'hover+selected'],
props: (state: string) => {
switch (state) {
case 'default':
return {};
case 'hover':
return { className: 'hover' };
case 'selected':
return { selected: true };
case 'hover+selected':
return { className: 'hover', selected: true };
default:
return {};
}
},
},
] as CatalogDimension[],
options: {
elementContainer: {
width: 200,
},
} as CatalogOptions,
},
},
decorators: [CatalogDecorator],
};

View File

@ -1,93 +0,0 @@
import { Meta, StoryObj } from '@storybook/react';
import {
Avatar,
CatalogDecorator,
CatalogDimension,
CatalogOptions,
CatalogStory,
ComponentDecorator,
} from 'twenty-ui';
import { avatarUrl } from '~/testing/mock-data/users';
import { MenuItemSelectAvatar } from '../MenuItemSelectAvatar';
const meta: Meta<typeof MenuItemSelectAvatar> = {
title: 'UI/Navigation/MenuItem/MenuItemSelectAvatar',
component: MenuItemSelectAvatar,
};
export default meta;
type Story = StoryObj<typeof MenuItemSelectAvatar>;
export const Default: Story = {
args: {
text: 'First option',
avatar: <Avatar avatarUrl={avatarUrl} placeholder="L" />,
},
argTypes: {
className: { control: false },
},
decorators: [ComponentDecorator],
};
export const Catalog: CatalogStory<Story, typeof MenuItemSelectAvatar> = {
args: { text: 'Menu item' },
argTypes: {
className: { control: false },
},
parameters: {
pseudo: { hover: ['.hover'], active: ['.pressed'], focus: ['.focus'] },
catalog: {
dimensions: [
{
name: 'withAvatar',
values: [true, false],
props: (withAvatar: boolean) => ({
avatar: withAvatar ? (
<Avatar avatarUrl={avatarUrl} placeholder="L" />
) : (
<Avatar avatarUrl={''} placeholder="L" />
),
}),
labels: (withAvatar: boolean) =>
withAvatar ? 'With avatar' : 'Without avatar',
},
{
name: 'states',
values: [
'default',
'hover',
'disabled',
'selected',
'hover+selected',
],
props: (state: string) => {
switch (state) {
case 'default':
return {};
case 'hover':
return { className: 'hover' };
case 'disabled':
return { disabled: true };
case 'selected':
return { selected: true };
case 'hover+selected':
return { className: 'hover', selected: true };
default:
return {};
}
},
},
] as CatalogDimension[],
options: {
elementContainer: {
width: 200,
},
} as CatalogOptions,
},
},
decorators: [CatalogDecorator],
};

View File

@ -1,84 +0,0 @@
import { Meta, StoryObj } from '@storybook/react';
import {
CatalogDecorator,
CatalogDimension,
CatalogOptions,
CatalogStory,
ColorSampleVariant,
ComponentDecorator,
MAIN_COLOR_NAMES,
ThemeColor,
} from 'twenty-ui';
import { MenuItemSelectColor } from '../MenuItemSelectColor';
const meta: Meta<typeof MenuItemSelectColor> = {
title: 'UI/Navigation/MenuItem/MenuItemSelectColor',
component: MenuItemSelectColor,
};
export default meta;
type Story = StoryObj<typeof MenuItemSelectColor>;
export const Default: Story = {
args: { color: 'green' },
argTypes: { className: { control: false } },
decorators: [ComponentDecorator],
};
export const Catalog: CatalogStory<Story, typeof MenuItemSelectColor> = {
argTypes: { className: { control: false } },
parameters: {
pseudo: { hover: ['.hover'], active: ['.pressed'], focus: ['.focus'] },
catalog: {
dimensions: [
{
name: 'color',
values: MAIN_COLOR_NAMES,
props: (color: ThemeColor) => ({ color }),
labels: (color: ThemeColor) => color,
},
{
name: 'states',
values: [
'default',
'hover',
'disabled',
'selected',
'hover+selected',
],
props: (state: string) => {
switch (state) {
case 'default':
return {};
case 'hover':
return { className: 'hover' };
case 'disabled':
return { disabled: true };
case 'selected':
return { selected: true };
case 'hover+selected':
return { className: 'hover', selected: true };
default:
return {};
}
},
},
{
name: 'variant',
values: ['default', 'pipeline'],
props: (variant: ColorSampleVariant) => ({ variant }),
labels: (variant: ColorSampleVariant) => variant,
},
] as CatalogDimension[],
options: {
elementContainer: {
width: 200,
},
} as CatalogOptions,
},
},
decorators: [CatalogDecorator],
};

View File

@ -1,94 +0,0 @@
import { Meta, StoryObj } from '@storybook/react';
import {
CatalogDecorator,
CatalogDimension,
CatalogOptions,
CatalogStory,
ComponentDecorator,
ThemeColor,
} from 'twenty-ui';
import { MenuItemSelectTag } from '../MenuItemSelectTag';
const meta: Meta<typeof MenuItemSelectTag> = {
title: 'UI/Navigation/MenuItem/MenuItemSelectTag',
component: MenuItemSelectTag,
};
export default meta;
type Story = StoryObj<typeof MenuItemSelectTag>;
export const Default: Story = {
args: {
selected: false,
onClick: undefined,
text: 'Item A',
},
argTypes: {
selected: {
control: 'boolean',
defaultValue: false,
},
onClick: {
control: false,
},
},
decorators: [ComponentDecorator],
};
export const Catalog: CatalogStory<Story, typeof MenuItemSelectTag> = {
args: {
text: 'Item A',
},
parameters: {
pseudo: {
hover: ['.hover'],
active: ['.pressed'],
focus: ['.focus'],
},
catalog: {
dimensions: [
{
name: 'color',
values: [
'green',
'turquoise',
'sky',
'blue',
'purple',
'pink',
'red',
'orange',
'yellow',
'gray',
],
props: (color: ThemeColor) => ({ color }),
},
{
name: 'states',
values: ['default', 'hover', 'selected', 'hover+selected'],
props: (state: string) => {
switch (state) {
case 'default':
return {};
case 'hover':
return { className: 'hover' };
case 'selected':
return { selected: true };
case 'hover+selected':
return { className: 'hover', selected: true };
default:
return {};
}
},
labels: (state: string) => `State: ${state}`,
},
] as CatalogDimension[],
options: {
elementContainer: { width: 200 },
} as CatalogOptions,
},
},
decorators: [CatalogDecorator],
};

View File

@ -1,76 +0,0 @@
import { Meta, StoryObj } from '@storybook/react';
import {
CatalogDecorator,
CatalogDimension,
CatalogOptions,
CatalogStory,
ComponentDecorator,
IconBell,
} from 'twenty-ui';
import { MenuItemToggle } from '../MenuItemToggle';
const meta: Meta<typeof MenuItemToggle> = {
title: 'UI/Navigation/MenuItem/MenuItemToggle',
component: MenuItemToggle,
};
export default meta;
type Story = StoryObj<typeof MenuItemToggle>;
export const Default: Story = {
args: {
text: 'First option',
},
decorators: [ComponentDecorator],
};
export const Catalog: CatalogStory<Story, typeof MenuItemToggle> = {
args: { LeftIcon: IconBell, text: 'Menu item' },
argTypes: {
className: { control: false },
},
parameters: {
pseudo: { hover: ['.hover'], active: ['.pressed'], focus: ['.focus'] },
catalog: {
dimensions: [
{
name: 'withIcon',
values: [true, false],
props: (withIcon: boolean) => ({
LeftIcon: withIcon ? IconBell : undefined,
}),
labels: (withIcon: boolean) =>
withIcon ? 'With left icon' : 'Without left icon',
},
{
name: 'toggled',
values: [true, false],
props: (toggled: boolean) => ({ toggled }),
labels: (toggled: boolean) => (toggled ? 'Toggled' : 'Not toggled'),
},
{
name: 'states',
values: ['default', 'hover'],
props: (state: string) => {
switch (state) {
case 'default':
return {};
case 'hover':
return { className: state };
default:
return {};
}
},
},
] satisfies CatalogDimension[],
options: {
elementContainer: {
width: 200,
},
} satisfies CatalogOptions,
},
},
decorators: [CatalogDecorator],
};

View File

@ -1,69 +0,0 @@
import { useTheme } from '@emotion/react';
import { isString } from '@sniptt/guards';
import { ReactNode } from 'react';
import {
IconComponent,
IconGripVertical,
OverflowingTextWithTooltip,
} from 'twenty-ui';
import {
StyledDraggableItem,
StyledMenuItemLabel,
StyledMenuItemLeftContent,
} from './StyledMenuItemBase';
type MenuItemLeftContentProps = {
className?: string;
LeftIcon: IconComponent | null | undefined;
showGrip?: boolean;
isDisabled?: boolean;
text: ReactNode;
};
export const MenuItemLeftContent = ({
className,
LeftIcon,
text,
showGrip = false,
isDisabled = false,
}: MenuItemLeftContentProps) => {
const theme = useTheme();
return (
<StyledMenuItemLeftContent className={className}>
{showGrip &&
(isDisabled ? (
<StyledDraggableItem>
<IconGripVertical
size={theme.icon.size.md}
stroke={theme.icon.stroke.sm}
color={
isDisabled
? theme.font.color.extraLight
: theme.font.color.light
}
/>
</StyledDraggableItem>
) : (
<StyledDraggableItem>
<IconGripVertical
size={theme.icon.size.md}
stroke={theme.icon.stroke.sm}
color={
isDisabled
? theme.font.color.extraLight
: theme.font.color.light
}
/>
</StyledDraggableItem>
))}
{LeftIcon && (
<LeftIcon size={theme.icon.size.md} stroke={theme.icon.stroke.sm} />
)}
<StyledMenuItemLabel hasLeftIcon={!!LeftIcon}>
{isString(text) ? <OverflowingTextWithTooltip text={text} /> : text}
</StyledMenuItemLabel>
</StyledMenuItemLeftContent>
);
};

View File

@ -1,149 +0,0 @@
import { css } from '@emotion/react';
import styled from '@emotion/styled';
import { HOVER_BACKGROUND } from 'twenty-ui';
import { MenuItemAccent } from '../../types/MenuItemAccent';
export type MenuItemBaseProps = {
accent?: MenuItemAccent;
isKeySelected?: boolean;
isHoverBackgroundDisabled?: boolean;
hovered?: boolean;
};
export const StyledMenuItemBase = styled.div<MenuItemBaseProps>`
--horizontal-padding: ${({ theme }) => theme.spacing(1)};
--vertical-padding: ${({ theme }) => theme.spacing(2)};
align-items: center;
border-radius: ${({ theme }) => theme.border.radius.sm};
cursor: pointer;
display: flex;
flex-direction: row;
font-size: ${({ theme }) => theme.font.size.sm};
gap: ${({ theme }) => theme.spacing(2)};
height: calc(32px - 2 * var(--vertical-padding));
justify-content: space-between;
padding: var(--vertical-padding) var(--horizontal-padding);
${({ theme, isKeySelected }) =>
isKeySelected ? `background: ${theme.background.transparent.light};` : ''}
${({ isHoverBackgroundDisabled }) =>
isHoverBackgroundDisabled ?? HOVER_BACKGROUND};
${({ theme, accent }) => {
switch (accent) {
case 'danger': {
return css`
color: ${theme.font.color.danger};
&:hover {
background: ${theme.background.transparent.danger};
}
`;
}
case 'placeholder': {
return css`
color: ${theme.font.color.tertiary};
`;
}
case 'default':
default: {
return css`
color: ${theme.font.color.secondary};
`;
}
}
}}
position: relative;
user-select: none;
width: calc(100% - 2 * var(--horizontal-padding));
`;
export const StyledMenuItemLabel = styled.div<{ hasLeftIcon: boolean }>`
font-size: ${({ theme }) => theme.font.size.md};
font-weight: ${({ theme }) => theme.font.weight.regular};
overflow: hidden;
padding-left: ${({ theme, hasLeftIcon }) =>
hasLeftIcon ? '' : theme.spacing(1)};
white-space: nowrap;
`;
export const StyledNoIconFiller = styled.div`
width: ${({ theme }) => theme.spacing(1)};
`;
export const StyledMenuItemLeftContent = styled.div`
align-items: center;
display: flex;
flex-direction: row;
gap: ${({ theme }) => theme.spacing(2)};
min-width: 0;
width: 100%;
& > svg {
flex-shrink: 0;
}
`;
export const StyledMenuItemRightContent = styled.div`
align-items: center;
display: flex;
flex-direction: row;
`;
export const StyledDraggableItem = styled.div`
cursor: grab;
align-items: center;
display: flex;
`;
export const StyledHoverableMenuItemBase = styled(StyledMenuItemBase)<{
isIconDisplayedOnHoverOnly?: boolean;
cursor?: 'drag' | 'default' | 'not-allowed';
}>`
${({ isIconDisplayedOnHoverOnly, theme }) =>
isIconDisplayedOnHoverOnly &&
css`
& .hoverable-buttons {
opacity: 0;
position: fixed;
right: ${theme.spacing(2)};
}
&:hover {
& .hoverable-buttons {
opacity: 1;
position: static;
}
}
`};
& .hoverable-buttons {
transition: opacity ${({ theme }) => theme.animation.duration.instant}s ease;
}
cursor: ${({ cursor }) => {
switch (cursor) {
case 'drag':
return 'grab';
case 'not-allowed':
return 'not-allowed';
default:
return 'pointer';
}
}};
`;

View File

@ -1 +0,0 @@
export type MenuItemAccent = 'default' | 'danger' | 'placeholder';

View File

@ -3,7 +3,6 @@ import { Workspaces } from '@/auth/states/workspaces';
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 { MenuItemSelectAvatar } from '@/ui/navigation/menu-item/components/MenuItemSelectAvatar';
import { NavigationDrawerAnimatedCollapseWrapper } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerAnimatedCollapseWrapper';
import { DEFAULT_WORKSPACE_LOGO } from '@/ui/navigation/navigation-drawer/constants/DefaultWorkspaceLogo';
import { MULTI_WORKSPACE_DROPDOWN_ID } from '@/ui/navigation/navigation-drawer/constants/MulitWorkspaceDropdownId';
@ -14,7 +13,7 @@ import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useState } from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';
import { IconChevronDown } from 'twenty-ui';
import { IconChevronDown, MenuItemSelectAvatar } from 'twenty-ui';
import { getImageAbsoluteURI } from '~/utils/image/getImageAbsoluteURI';
const StyledLogo = styled.div<{ logo: string }>`

View File

@ -1,10 +1,15 @@
import styled from '@emotion/styled';
import { Button, ButtonGroup, IconChevronDown, IconPlus } from 'twenty-ui';
import {
Button,
ButtonGroup,
IconChevronDown,
IconPlus,
MenuItem,
} from 'twenty-ui';
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 { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';

View File

@ -10,6 +10,7 @@ import {
IconEye,
IconEyeOff,
IconInfoCircle,
MenuItemDraggable,
useIcons,
} from 'twenty-ui';
@ -19,7 +20,6 @@ import { DraggableItem } from '@/ui/layout/draggable-list/components/DraggableIt
import { DraggableList } from '@/ui/layout/draggable-list/components/DraggableList';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { StyledDropdownMenuSubheader } from '@/ui/layout/dropdown/components/StyledDropdownMenuSubheader';
import { MenuItemDraggable } from '@/ui/navigation/menu-item/components/MenuItemDraggable';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { groupArrayItemsBy } from '~/utils/array/groupArrayItemsBy';
import { isDefined } from '~/utils/isDefined';

View File

@ -4,7 +4,7 @@ import {
ResponderProvided,
} from '@hello-pangea/dnd';
import { useRef } from 'react';
import { IconEye, IconEyeOff, Tag } from 'twenty-ui';
import { IconEye, IconEyeOff, MenuItemDraggable, Tag } from 'twenty-ui';
import {
RecordGroupDefinition,
@ -14,7 +14,6 @@ import { DraggableItem } from '@/ui/layout/draggable-list/components/DraggableIt
import { DraggableList } from '@/ui/layout/draggable-list/components/DraggableList';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { StyledDropdownMenuSubheader } from '@/ui/layout/dropdown/components/StyledDropdownMenuSubheader';
import { MenuItemDraggable } from '@/ui/navigation/menu-item/components/MenuItemDraggable';
import { isDefined } from '~/utils/isDefined';
type ViewGroupsVisibilityDropdownSectionProps = {

View File

@ -6,6 +6,8 @@ import {
IconPencil,
IconPlus,
LightIconButtonAccent,
MenuItem,
MenuItemDraggable,
useIcons,
} from 'twenty-ui';
@ -14,8 +16,6 @@ import { DraggableList } from '@/ui/layout/draggable-list/components/DraggableLi
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 { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { MenuItemDraggable } from '@/ui/navigation/menu-item/components/MenuItemDraggable';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { useChangeView } from '@/views/hooks/useChangeView';
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';

View File

@ -1,8 +1,8 @@
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { ACTIONS } from '@/workflow/constants/Actions';
import { useCreateStep } from '@/workflow/hooks/useCreateStep';
import { WorkflowWithCurrentVersion } from '@/workflow/types/Workflow';
import styled from '@emotion/styled';
import { MenuItem } from 'twenty-ui';
const StyledActionListContainer = styled.div`
display: flex;

View File

@ -1,7 +1,6 @@
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
import { RightDrawerPages } from '@/ui/layout/right-drawer/types/RightDrawerPages';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { TRIGGER_STEP_ID } from '@/workflow/constants/TriggerStepId';
import { TRIGGER_TYPES } from '@/workflow/constants/TriggerTypes';
import { useUpdateWorkflowVersionTrigger } from '@/workflow/hooks/useUpdateWorkflowVersionTrigger';
@ -10,6 +9,7 @@ import { WorkflowWithCurrentVersion } from '@/workflow/types/Workflow';
import { getTriggerDefaultDefinition } from '@/workflow/utils/getTriggerDefaultDefinition';
import styled from '@emotion/styled';
import { useSetRecoilState } from 'recoil';
import { MenuItem } from 'twenty-ui';
const StyledActionListContainer = styled.div`
display: flex;

View File

@ -1,6 +1,5 @@
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { MenuItemSelect } from '@/ui/navigation/menu-item/components/MenuItemSelect';
import { StepOutputSchema } from '@/workflow/search-variables/types/StepOutputSchema';
import { MenuItem, MenuItemSelect } from 'twenty-ui';
type SearchVariablesDropdownStepItemProps = {
steps: StepOutputSchema[];

View File

@ -1,9 +1,8 @@
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader';
import { MenuItemSelect } from '@/ui/navigation/menu-item/components/MenuItemSelect';
import { StepOutputSchema } from '@/workflow/search-variables/types/StepOutputSchema';
import { isObject } from '@sniptt/guards';
import { useState } from 'react';
import { IconChevronLeft } from 'twenty-ui';
import { IconChevronLeft, MenuItemSelect } from 'twenty-ui';
type SearchVariablesDropdownStepSubItemProps = {
step: StepOutputSchema;