Skip to content

Commit

Permalink
add renderInput prop and use proper herf for phones
Browse files Browse the repository at this point in the history
  • Loading branch information
gitstart-twenty committed Sep 6, 2024
1 parent dfb4b88 commit 3bad3f2
Show file tree
Hide file tree
Showing 9 changed files with 142 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useEmailsField } from '@/object-record/record-field/meta-types/hooks/us
import { EmailsFieldMenuItem } from '@/object-record/record-field/meta-types/input/components/EmailsFieldMenuItem';
import { useMemo } from 'react';
import { isDefined } from 'twenty-ui';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { MultiItemFieldInput } from './MultiItemFieldInput';

type EmailsFieldInputProps = {
Expand Down Expand Up @@ -34,6 +35,7 @@ export const EmailsFieldInput = ({ onCancel }: EmailsFieldInputProps) => {
onPersist={handlePersistEmails}
onCancel={onCancel}
placeholder="Email"
fieldMetadataType={FieldMetadataType.Emails}
renderItem={({
value: email,
index,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useLinksField } from '@/object-record/record-field/meta-types/hooks/use
import { LinksFieldMenuItem } from '@/object-record/record-field/meta-types/input/components/LinksFieldMenuItem';
import { useMemo } from 'react';
import { isDefined } from 'twenty-ui';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { absoluteUrlSchema } from '~/utils/validation-schemas/absoluteUrlSchema';
import { MultiItemFieldInput } from './MultiItemFieldInput';

Expand Down Expand Up @@ -47,6 +48,7 @@ export const LinksFieldInput = ({ onCancel }: LinksFieldInputProps) => {
onPersist={handlePersistLinks}
onCancel={onCancel}
placeholder="URL"
fieldMetadataType={FieldMetadataType.Links}
validateInput={(input) => absoluteUrlSchema.safeParse(input).success}
formatInput={(input) => ({ url: input, label: '' })}
renderItem={({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@ import React, { useRef, useState } from 'react';
import { Key } from 'ts-key-enum';
import { IconCheck, IconPlus } from 'twenty-ui';

import { PhoneRecord } from '@/object-record/record-field/types/FieldMetadata';
import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
import { DropdownMenuInput } from '@/ui/layout/dropdown/components/DropdownMenuInput';
import {
DropdownMenuInput,
DropdownMenuInputProps,
} 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';
import { moveArrayItem } from '~/utils/array/moveArrayItem';
import { toSpliced } from '~/utils/array/toSpliced';

Expand All @@ -35,7 +40,8 @@ type MultiItemFieldInputProps<T> = {
handleDelete: () => void;
}) => React.ReactNode;
hotkeyScope: string;
isPhoneInput?: boolean;
fieldMetadataType: FieldMetadataType;
renderInput?: DropdownMenuInputProps['renderInput'];
};

export const MultiItemFieldInput = <T,>({
Expand All @@ -47,7 +53,8 @@ export const MultiItemFieldInput = <T,>({
formatInput,
renderItem,
hotkeyScope,
isPhoneInput = false,
fieldMetadataType,
renderInput,
}: MultiItemFieldInputProps<T>) => {
const containerRef = useRef<HTMLDivElement>(null);
const handleDropdownClose = () => {
Expand All @@ -72,17 +79,25 @@ export const MultiItemFieldInput = <T,>({
};

const handleEditButtonClick = (index: number) => {
const item = items[index] as { label: string; url: string };
setItemToEditIndex(index);
if (isPhoneInput) {
const phoneToEdit = items[index] as {
countryCode: string;
number: string;
};
setInputValue(phoneToEdit.countryCode + phoneToEdit.number);
} else {
setInputValue(item.url || '');
let item;
switch (fieldMetadataType) {
case FieldMetadataType.Links:
item = items[index] as { label: string; url: string };
setInputValue(item.url || '');
break;
case FieldMetadataType.Phones:
item = items[index] as PhoneRecord;
setInputValue(item.countryCode + item.number);
break;
case FieldMetadataType.Emails:
item = items[index] as string;
setInputValue(item);
break;
default:
throw new Error(`Unsupported field type: ${fieldMetadataType}`);
}

setItemToEditIndex(index);
setIsInputDisplayed(true);
};

Expand Down Expand Up @@ -142,9 +157,17 @@ export const MultiItemFieldInput = <T,>({
placeholder={placeholder}
value={inputValue}
hotkeyScope={hotkeyScope}
isPhoneInput={isPhoneInput}
renderInput={
renderInput
? (props) =>
renderInput({
...props,
onChange: (newValue) =>
setInputValue(newValue as unknown as string),
})
: undefined
}
onChange={(event) => setInputValue(event.target.value)}
onPhoneChange={(value) => setInputValue(value as string)}
onEnter={handleSubmitInput}
rightComponent={
<LightIconButton
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,47 @@
import { usePhonesField } from '@/object-record/record-field/meta-types/hooks/usePhonesField';
import { PhonesFieldMenuItem } from '@/object-record/record-field/meta-types/input/components/PhonesFieldMenuItem';
import { parsePhoneNumber } from 'libphonenumber-js';
import styled from '@emotion/styled';
import { E164Number, parsePhoneNumber } from 'libphonenumber-js';
import { useMemo } from 'react';
import ReactPhoneNumberInput from 'react-phone-number-input';
import 'react-phone-number-input/style.css';
import { isDefined } from 'twenty-ui';
import { isDefined, TEXT_INPUT_STYLE } from 'twenty-ui';

import { MultiItemFieldInput } from './MultiItemFieldInput';

import { PhoneCountryPickerDropdownButton } from '@/ui/input/components/internal/phone/components/PhoneCountryPickerDropdownButton';
import { FieldMetadataType } from '~/generated-metadata/graphql';

const StyledCustomPhoneInput = styled(ReactPhoneNumberInput)`
font-family: ${({ theme }) => theme.font.family};
height: 32px;
${TEXT_INPUT_STYLE}
padding: 0;
.PhoneInputInput {
background: none;
border: none;
color: ${({ theme }) => theme.font.color.primary};
&::placeholder,
&::-webkit-input-placeholder {
color: ${({ theme }) => theme.font.color.light};
font-family: ${({ theme }) => theme.font.family};
font-weight: ${({ theme }) => theme.font.weight.medium};
}
:focus {
outline: none;
}
}
& svg {
border-radius: ${({ theme }) => theme.border.radius.xs};
height: 12px;
}
width: calc(100% - ${({ theme }) => theme.spacing(8)});
`;

type PhonesFieldInputProps = {
onCancel?: () => void;
};
Expand Down Expand Up @@ -49,7 +84,7 @@ export const PhonesFieldInput = ({ onCancel }: PhonesFieldInputProps) => {
onPersist={handlePersistPhones}
onCancel={onCancel}
placeholder="Phone"
isPhoneInput
fieldMetadataType={FieldMetadataType.Phones}
formatInput={(input) => {
const phone = parsePhoneNumber(input);
if (phone !== undefined) {
Expand Down Expand Up @@ -80,6 +115,19 @@ export const PhonesFieldInput = ({ onCancel }: PhonesFieldInputProps) => {
onDelete={handleDelete}
/>
)}
renderInput={({ value, onChange, autoFocus, placeholder }) => {
return (
<StyledCustomPhoneInput
autoFocus={autoFocus}
placeholder={placeholder}
value={value as E164Number}
onChange={onChange as unknown as (newValue: E164Number) => void}
international={true}
withCountryCallingCode={true}
countrySelectComponent={PhoneCountryPickerDropdownButton}
/>
);
}}
hotkeyScope={hotkeyScope}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
FieldSelectValue,
FieldTextValue,
FieldUUidValue,
PhoneRecord,
} from '@/object-record/record-field/types/FieldMetadata';

export type FieldTextDraftValue = string;
Expand All @@ -30,7 +31,7 @@ export type FieldPhoneDraftValue = string;
export type FieldPhonesDraftValue = {
primaryPhoneNumber: string;
primaryPhoneCountryCode: string;
additionalPhones?: { number: string; countryCode: string }[] | null;
additionalPhones?: PhoneRecord[] | null;
};
export type FieldEmailDraftValue = string;
export type FieldEmailsDraftValue = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,10 @@ export type FieldActorValue = {
name: string;
};

export type PhoneRecord = { number: string; countryCode: string };

export type FieldPhonesValue = {
primaryPhoneNumber: string;
primaryPhoneCountryCode: string;
additionalPhones?: { number: string; countryCode: string }[] | null;
additionalPhones?: PhoneRecord[] | null;
};
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,10 @@ export const isRecordMatchingFilter = ({
case FieldMetadataType.Phones: {
const phonesFilter = filterValue as PhonesFilter;

const keys = ['primaryPhoneNumber', 'primaryPhoneCountryCode'] as const;
const keys: (keyof PhonesFilter)[] = [
'primaryPhoneNumber',
'primaryPhoneCountryCode',
];

return keys.some((key) => {
const value = phonesFilter[key];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import styled from '@emotion/styled';
import { useMemo } from 'react';
import { formatPhoneNumberIntl } from 'react-phone-number-input';
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 = {
Expand Down Expand Up @@ -57,23 +57,31 @@ export const PhonesDisplay = ({ value, isFocused }: PhonesDisplayProps) => {

return isFocused ? (
<ExpandableList isChipCountDisplayed>
{phones.map(({ number, countryCode }, index) => (
<RoundedLink
key={index}
href={'#'}
label={formatPhoneNumberIntl(`${countryCode}${number}`)}
/>
))}
{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) => (
<RoundedLink
key={index}
href={'#'}
label={formatPhoneNumberIntl(`${countryCode}${number}`)}
/>
))}
{phones.map(({ number, countryCode }, index) => {
const parsedPhone = parsePhoneNumber(countryCode + number);
const URI = parsedPhone.getURI();
return (
<RoundedLink
key={index}
href={URI}
label={parsedPhone.formatInternational()}
/>
);
})}
</StyledContainer>
);
};
Loading

0 comments on commit 3bad3f2

Please sign in to comment.