Clicking a phone number should copy its value (#9069)
https://github.com/user-attachments/assets/7ce595fa-be90-4ec7-81e5-075dafee6422 I have added the functionality of copying the phone number to clipboard according to the issue #8905 . If anything needed to change just comment in my PR --------- Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
@ -1,11 +1,50 @@
|
||||
import { useFieldFocus } from '@/object-record/record-field/hooks/useFieldFocus';
|
||||
import { usePhonesFieldDisplay } from '@/object-record/record-field/meta-types/hooks/usePhonesFieldDisplay';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { PhonesDisplay } from '@/ui/field/display/components/PhonesDisplay';
|
||||
import React from 'react';
|
||||
import { useIcons } from 'twenty-ui';
|
||||
|
||||
export const PhonesFieldDisplay = () => {
|
||||
const { fieldValue } = usePhonesFieldDisplay();
|
||||
|
||||
const { isFocused } = useFieldFocus();
|
||||
|
||||
return <PhonesDisplay value={fieldValue} isFocused={isFocused} />;
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
|
||||
const { getIcon } = useIcons();
|
||||
|
||||
const IconCircleCheck = getIcon('IconCircleCheck');
|
||||
const IconExclamationCircle = getIcon('IconExclamationCircle');
|
||||
|
||||
const handleClick = async (
|
||||
phoneNumber: string,
|
||||
event: React.MouseEvent<HTMLElement>,
|
||||
) => {
|
||||
event.preventDefault();
|
||||
|
||||
try {
|
||||
await navigator.clipboard.writeText(phoneNumber);
|
||||
enqueueSnackBar('Phone number copied to clipboard', {
|
||||
variant: SnackBarVariant.Success,
|
||||
icon: <IconCircleCheck size={16} color="green" />,
|
||||
duration: 2000,
|
||||
});
|
||||
} catch (err) {
|
||||
enqueueSnackBar('Error copying to clipboard', {
|
||||
variant: SnackBarVariant.Error,
|
||||
icon: <IconExclamationCircle size={16} color="red" />,
|
||||
duration: 2000,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<PhonesDisplay
|
||||
value={fieldValue}
|
||||
isFocused={isFocused}
|
||||
onPhoneNumberClick={handleClick}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -4,6 +4,7 @@ import { ComponentDecorator } from 'twenty-ui';
|
||||
import { PhonesFieldDisplay } from '@/object-record/record-field/meta-types/display/components/PhonesFieldDisplay';
|
||||
import { getFieldDecorator } from '~/testing/decorators/getFieldDecorator';
|
||||
import { MemoryRouterDecorator } from '~/testing/decorators/MemoryRouterDecorator';
|
||||
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
|
||||
import { getProfilingStory } from '~/testing/profiling/utils/getProfilingStory';
|
||||
|
||||
const meta: Meta = {
|
||||
@ -12,6 +13,7 @@ const meta: Meta = {
|
||||
MemoryRouterDecorator,
|
||||
getFieldDecorator('person', 'phones'),
|
||||
ComponentDecorator,
|
||||
SnackBarDecorator,
|
||||
],
|
||||
component: PhonesFieldDisplay,
|
||||
args: {},
|
||||
|
||||
@ -74,7 +74,6 @@ const StyledHeader = styled.div`
|
||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
height: ${({ theme }) => theme.spacing(6)};
|
||||
margin-bottom: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
const StyledActions = styled.div`
|
||||
@ -83,15 +82,6 @@ const StyledActions = styled.div`
|
||||
margin-left: auto;
|
||||
`;
|
||||
|
||||
const StyledDescription = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
font-size: ${({ theme }) => theme.font.size.sm};
|
||||
padding-left: ${({ theme }) => theme.spacing(6)};
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
width: 200px;
|
||||
`;
|
||||
|
||||
const defaultTitleByVariant: Record<SnackBarVariant, string> = {
|
||||
[SnackBarVariant.Default]: 'Alert',
|
||||
[SnackBarVariant.Error]: 'Error',
|
||||
@ -183,7 +173,7 @@ export const SnackBar = ({
|
||||
/>
|
||||
<StyledHeader>
|
||||
{icon}
|
||||
{title}
|
||||
{message}
|
||||
<StyledActions>
|
||||
{!!onCancel && <LightButton title="Cancel" onClick={onCancel} />}
|
||||
|
||||
@ -192,7 +182,6 @@ export const SnackBar = ({
|
||||
)}
|
||||
</StyledActions>
|
||||
</StyledHeader>
|
||||
{message && <StyledDescription>{message}</StyledDescription>}
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { useMemo } from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { RoundedLink, THEME_COMMON } from 'twenty-ui';
|
||||
|
||||
import { FieldPhonesValue } from '@/object-record/record-field/types/FieldMetadata';
|
||||
@ -13,6 +13,10 @@ import { logError } from '~/utils/logError';
|
||||
type PhonesDisplayProps = {
|
||||
value?: FieldPhonesValue;
|
||||
isFocused?: boolean;
|
||||
onPhoneNumberClick?: (
|
||||
phoneNumber: string,
|
||||
event: React.MouseEvent<HTMLElement>,
|
||||
) => void;
|
||||
};
|
||||
|
||||
const themeSpacing = THEME_COMMON.spacingMultiplicator;
|
||||
@ -30,7 +34,11 @@ const StyledContainer = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export const PhonesDisplay = ({ value, isFocused }: PhonesDisplayProps) => {
|
||||
export const PhonesDisplay = ({
|
||||
value,
|
||||
isFocused,
|
||||
onPhoneNumberClick,
|
||||
}: PhonesDisplayProps) => {
|
||||
const phones = useMemo(
|
||||
() =>
|
||||
[
|
||||
@ -67,6 +75,13 @@ export const PhonesDisplay = ({ value, isFocused }: PhonesDisplayProps) => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleClick = (
|
||||
number: string,
|
||||
event: React.MouseEvent<HTMLElement>,
|
||||
) => {
|
||||
onPhoneNumberClick?.(number, event);
|
||||
};
|
||||
|
||||
return isFocused ? (
|
||||
<ExpandableList isChipCountDisplayed>
|
||||
{phones.map(({ number, callingCode }, index) => {
|
||||
@ -80,6 +95,7 @@ export const PhonesDisplay = ({ value, isFocused }: PhonesDisplayProps) => {
|
||||
label={
|
||||
parsedPhone ? parsedPhone.formatInternational() : invalidPhone
|
||||
}
|
||||
onClick={(event) => handleClick(callingCode + number, event)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
@ -97,6 +113,7 @@ export const PhonesDisplay = ({ value, isFocused }: PhonesDisplayProps) => {
|
||||
label={
|
||||
parsedPhone ? parsedPhone.formatInternational() : invalidPhone
|
||||
}
|
||||
onClick={(event) => handleClick(callingCode + number, event)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
Reference in New Issue
Block a user