Support for multiple values in the Phone field (#6882)
### Description - This is the first PR on Phones field; - We are introducing new field type(Phones) - We are Forbidding creation of Phone field - We Added support for filtering and sorting on Phones field - We are using the same display mode as used on the Links field type (chips), check the Domain field of the Company object - We are also using the same logic of the link when editing the field **How to Test** 1. Checkout to TWNTY-6260 branch 2. Reset database using "npx nx database:reset twenty-server" command 3. Add custom field of type Phones in settings/data-model **Loom Video:**\ <https://www.loom.com/share/3c981260be254dcf851256d020a20ab0?sid=58507361-3a3b-452c-9de8-b5b1abda70ac> ### Refs #6260 Co-authored-by: gitstart-twenty <gitstart-twenty@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
91187dcf82
commit
846953b0f4
@ -24,7 +24,7 @@ export const PhoneDisplay = ({ value }: PhoneDisplayProps) => {
|
||||
}
|
||||
|
||||
const URI = parsedPhoneNumber.getURI();
|
||||
const formattedNational = parsedPhoneNumber?.formatNational();
|
||||
const formatedPhoneNumber = parsedPhoneNumber.formatInternational();
|
||||
|
||||
return (
|
||||
<ContactLink
|
||||
@ -33,7 +33,7 @@ export const PhoneDisplay = ({ value }: PhoneDisplayProps) => {
|
||||
event.stopPropagation();
|
||||
}}
|
||||
>
|
||||
{formattedNational || value}
|
||||
{formatedPhoneNumber || value}
|
||||
</ContactLink>
|
||||
);
|
||||
};
|
||||
|
||||
@ -0,0 +1,87 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { useMemo } from 'react';
|
||||
import { THEME_COMMON } from 'twenty-ui';
|
||||
|
||||
import { FieldPhonesValue } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { ExpandableList } from '@/ui/layout/expandable-list/components/ExpandableList';
|
||||
import { RoundedLink } from '@/ui/navigation/link/components/RoundedLink';
|
||||
|
||||
import { parsePhoneNumber } from 'libphonenumber-js';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
type PhonesDisplayProps = {
|
||||
value?: FieldPhonesValue;
|
||||
isFocused?: boolean;
|
||||
};
|
||||
|
||||
const themeSpacing = THEME_COMMON.spacingMultiplicator;
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: ${themeSpacing * 1}px;
|
||||
justify-content: flex-start;
|
||||
|
||||
max-width: 100%;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export const PhonesDisplay = ({ value, isFocused }: PhonesDisplayProps) => {
|
||||
const phones = useMemo(
|
||||
() =>
|
||||
[
|
||||
value?.primaryPhoneNumber
|
||||
? {
|
||||
number: value.primaryPhoneNumber,
|
||||
countryCode: value.primaryPhoneCountryCode,
|
||||
}
|
||||
: null,
|
||||
...(value?.additionalPhones ?? []),
|
||||
]
|
||||
.filter(isDefined)
|
||||
.map(({ number, countryCode }) => {
|
||||
return {
|
||||
number,
|
||||
countryCode,
|
||||
};
|
||||
}),
|
||||
[
|
||||
value?.primaryPhoneNumber,
|
||||
value?.primaryPhoneCountryCode,
|
||||
value?.additionalPhones,
|
||||
],
|
||||
);
|
||||
|
||||
return isFocused ? (
|
||||
<ExpandableList isChipCountDisplayed>
|
||||
{phones.map(({ number, countryCode }, index) => {
|
||||
const parsedPhone = parsePhoneNumber(countryCode + number);
|
||||
const URI = parsedPhone.getURI();
|
||||
return (
|
||||
<RoundedLink
|
||||
key={index}
|
||||
href={URI}
|
||||
label={parsedPhone.formatInternational()}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</ExpandableList>
|
||||
) : (
|
||||
<StyledContainer>
|
||||
{phones.map(({ number, countryCode }, index) => {
|
||||
const parsedPhone = parsePhoneNumber(countryCode + number);
|
||||
const URI = parsedPhone.getURI();
|
||||
return (
|
||||
<RoundedLink
|
||||
key={index}
|
||||
href={URI}
|
||||
label={parsedPhone.formatInternational()}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
@ -1,6 +1,7 @@
|
||||
import { forwardRef, InputHTMLAttributes, ReactNode, useRef } from 'react';
|
||||
import { css } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { forwardRef, InputHTMLAttributes, ReactNode, useRef } from 'react';
|
||||
import 'react-phone-number-input/style.css';
|
||||
import { RGBA, TEXT_INPUT_STYLE } from 'twenty-ui';
|
||||
|
||||
import { useRegisterInputEvents } from '@/object-record/record-field/meta-types/input/hooks/useRegisterInputEvents';
|
||||
@ -43,7 +44,9 @@ const StyledRightContainer = styled.div`
|
||||
transform: translateY(-50%);
|
||||
`;
|
||||
|
||||
type DropdownMenuInputProps = InputHTMLAttributes<HTMLInputElement> & {
|
||||
type HTMLInputProps = InputHTMLAttributes<HTMLInputElement>;
|
||||
|
||||
export type DropdownMenuInputProps = HTMLInputProps & {
|
||||
hotkeyScope?: string;
|
||||
onClickOutside?: () => void;
|
||||
onEnter?: () => void;
|
||||
@ -51,6 +54,12 @@ type DropdownMenuInputProps = InputHTMLAttributes<HTMLInputElement> & {
|
||||
onShiftTab?: () => void;
|
||||
onTab?: () => void;
|
||||
rightComponent?: ReactNode;
|
||||
renderInput?: (props: {
|
||||
value: HTMLInputProps['value'];
|
||||
onChange: HTMLInputProps['onChange'];
|
||||
autoFocus: HTMLInputProps['autoFocus'];
|
||||
placeholder: HTMLInputProps['placeholder'];
|
||||
}) => React.ReactNode;
|
||||
};
|
||||
|
||||
export const DropdownMenuInput = forwardRef<
|
||||
@ -71,6 +80,7 @@ export const DropdownMenuInput = forwardRef<
|
||||
onShiftTab,
|
||||
onTab,
|
||||
rightComponent,
|
||||
renderInput,
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
@ -90,14 +100,23 @@ export const DropdownMenuInput = forwardRef<
|
||||
|
||||
return (
|
||||
<StyledInputContainer className={className}>
|
||||
<StyledInput
|
||||
autoFocus={autoFocus}
|
||||
value={value}
|
||||
placeholder={placeholder}
|
||||
onChange={onChange}
|
||||
ref={combinedRef}
|
||||
withRightComponent={!!rightComponent}
|
||||
/>
|
||||
{renderInput ? (
|
||||
renderInput({
|
||||
value,
|
||||
onChange,
|
||||
autoFocus,
|
||||
placeholder,
|
||||
})
|
||||
) : (
|
||||
<StyledInput
|
||||
autoFocus={autoFocus}
|
||||
value={value}
|
||||
placeholder={placeholder}
|
||||
onChange={onChange}
|
||||
ref={combinedRef}
|
||||
withRightComponent={!!rightComponent}
|
||||
/>
|
||||
)}
|
||||
{!!rightComponent && (
|
||||
<StyledRightContainer>{rightComponent}</StyledRightContainer>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user