diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/EmailsFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/EmailsFieldInput.tsx index b7704cdaa..96c829660 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/EmailsFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/EmailsFieldInput.tsx @@ -6,6 +6,7 @@ import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-sta import { useCallback, useMemo } from 'react'; import { isDefined } from 'twenty-shared/utils'; import { FieldMetadataType } from '~/generated-metadata/graphql'; +import { useCopyToClipboard } from '~/hooks/useCopyToClipboard'; import { MultiItemFieldInput } from './MultiItemFieldInput'; type EmailsFieldInputProps = { @@ -18,6 +19,7 @@ export const EmailsFieldInput = ({ onClickOutside, }: EmailsFieldInputProps) => { const { persistEmailsField, fieldValue } = useEmailsField(); + const { copyToClipboard } = useCopyToClipboard(); const emails = useMemo( () => @@ -56,6 +58,10 @@ export const EmailsFieldInput = ({ setIsFieldInError(hasError && values.length === 0); }; + const handleCopy = (email: string) => { + copyToClipboard(email); + }; + return ( )} onError={handleError} diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/EmailsFieldMenuItem.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/EmailsFieldMenuItem.tsx index 2ca2bfe29..a0305b4a8 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/EmailsFieldMenuItem.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/EmailsFieldMenuItem.tsx @@ -6,9 +6,11 @@ type EmailsFieldMenuItemProps = { onEdit?: () => void; onSetAsPrimary?: () => void; onDelete?: () => void; + onCopy?: (email: string) => void; email: string; showPrimaryIcon: boolean; showSetAsPrimaryButton: boolean; + showCopyButton: boolean; }; export const EmailsFieldMenuItem = ({ @@ -19,6 +21,8 @@ export const EmailsFieldMenuItem = ({ email, showPrimaryIcon, showSetAsPrimaryButton, + showCopyButton, + onCopy, }: EmailsFieldMenuItemProps) => { return ( ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/MultiItemFieldMenuItem.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/MultiItemFieldMenuItem.tsx index 3e9954dcd..7e6ecf84c 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/MultiItemFieldMenuItem.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/MultiItemFieldMenuItem.tsx @@ -8,6 +8,7 @@ import React, { useState } from 'react'; import { IconBookmark, IconBookmarkPlus, + IconCopy, IconPencil, IconTrash, } from 'twenty-ui/display'; @@ -19,9 +20,11 @@ type MultiItemFieldMenuItemProps = { onEdit?: () => void; onSetAsPrimary?: () => void; onDelete?: () => void; + onCopy?: (value: T) => void; DisplayComponent: React.ComponentType<{ value: T }>; showPrimaryIcon: boolean; showSetAsPrimaryButton: boolean; + showCopyButton?: boolean; }; export const MultiItemFieldMenuItem = ({ @@ -33,6 +36,8 @@ export const MultiItemFieldMenuItem = ({ DisplayComponent, showPrimaryIcon, showSetAsPrimaryButton, + showCopyButton, + onCopy, }: MultiItemFieldMenuItemProps) => { const [isHovered, setIsHovered] = useState(false); const { closeDropdown } = useCloseDropdown(); @@ -71,6 +76,14 @@ export const MultiItemFieldMenuItem = ({ onEdit?.(); }; + const handleCopyClick = (event: React.MouseEvent) => { + event.stopPropagation(); + event.preventDefault(); + + closeDropdown(dropdownId); + onCopy?.(value); + }; + return ( ({ text="Delete" onClick={handleDeleteClick} /> + {showCopyButton && ( + + )} } diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/EmailsFieldInput.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/EmailsFieldInput.stories.tsx index db6c7f4aa..8e9e78830 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/EmailsFieldInput.stories.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/EmailsFieldInput.stories.tsx @@ -13,6 +13,8 @@ import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFi import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack'; import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType'; import { FieldMetadataType } from '~/generated-metadata/graphql'; +import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator'; +import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator'; import { EmailsFieldInput } from '../EmailsFieldInput'; const updateRecord = fn(); @@ -113,6 +115,7 @@ const EmailInputWithContext = ({ const meta: Meta = { title: 'UI/Input/EmailsFieldInput', component: EmailInputWithContext, + decorators: [SnackBarDecorator, I18nFrontDecorator], }; export default meta;