copy to clipboard on MultiItemFieldMenuItem (#13292)

# ISSUE

- closes #13089
This commit is contained in:
Nabhag Motivaras
2025-07-23 15:13:16 +05:30
committed by GitHub
parent ac8dab279a
commit 2730b3ea3d
4 changed files with 37 additions and 0 deletions

View File

@ -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<string[]>(
() =>
@ -56,6 +58,10 @@ export const EmailsFieldInput = ({
setIsFieldInError(hasError && values.length === 0);
};
const handleCopy = (email: string) => {
copyToClipboard(email);
};
return (
<MultiItemFieldInput
items={emails}
@ -80,10 +86,12 @@ export const EmailsFieldInput = ({
dropdownId={`emails-${index}`}
showPrimaryIcon={getShowPrimaryIcon(index)}
showSetAsPrimaryButton={getShowSetAsPrimaryButton(index)}
showCopyButton={true}
email={email}
onEdit={handleEdit}
onSetAsPrimary={handleSetPrimary}
onDelete={handleDelete}
onCopy={handleCopy}
/>
)}
onError={handleError}

View File

@ -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 (
<MultiItemFieldMenuItem
@ -30,6 +34,8 @@ export const EmailsFieldMenuItem = ({
DisplayComponent={EmailDisplay}
showPrimaryIcon={showPrimaryIcon}
showSetAsPrimaryButton={showSetAsPrimaryButton}
showCopyButton={showCopyButton}
onCopy={onCopy}
/>
);
};

View File

@ -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<T> = {
onEdit?: () => void;
onSetAsPrimary?: () => void;
onDelete?: () => void;
onCopy?: (value: T) => void;
DisplayComponent: React.ComponentType<{ value: T }>;
showPrimaryIcon: boolean;
showSetAsPrimaryButton: boolean;
showCopyButton?: boolean;
};
export const MultiItemFieldMenuItem = <T,>({
@ -33,6 +36,8 @@ export const MultiItemFieldMenuItem = <T,>({
DisplayComponent,
showPrimaryIcon,
showSetAsPrimaryButton,
showCopyButton,
onCopy,
}: MultiItemFieldMenuItemProps<T>) => {
const [isHovered, setIsHovered] = useState(false);
const { closeDropdown } = useCloseDropdown();
@ -71,6 +76,14 @@ export const MultiItemFieldMenuItem = <T,>({
onEdit?.();
};
const handleCopyClick = (event: React.MouseEvent<HTMLDivElement>) => {
event.stopPropagation();
event.preventDefault();
closeDropdown(dropdownId);
onCopy?.(value);
};
return (
<MenuItemWithOptionDropdown
onMouseEnter={handleMouseEnter}
@ -100,6 +113,13 @@ export const MultiItemFieldMenuItem = <T,>({
text="Delete"
onClick={handleDeleteClick}
/>
{showCopyButton && (
<MenuItem
LeftIcon={IconCopy}
text="Copy"
onClick={handleCopyClick}
/>
)}
</DropdownMenuItemsContainer>
</DropdownContent>
}

View File

@ -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<typeof EmailInputWithContext> = {
title: 'UI/Input/EmailsFieldInput',
component: EmailInputWithContext,
decorators: [SnackBarDecorator, I18nFrontDecorator],
};
export default meta;