@ -1,7 +1,7 @@
|
|||||||
import { useMemo, useRef, useState } from 'react';
|
import { useMemo, useRef, useState } from 'react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { Key } from 'ts-key-enum';
|
import { Key } from 'ts-key-enum';
|
||||||
import { IconPlus } from 'twenty-ui';
|
import { IconCheck, IconPlus } from 'twenty-ui';
|
||||||
|
|
||||||
import { useLinksField } from '@/object-record/record-field/meta-types/hooks/useLinksField';
|
import { useLinksField } from '@/object-record/record-field/meta-types/hooks/useLinksField';
|
||||||
import { LinksFieldMenuItem } from '@/object-record/record-field/meta-types/input/components/LinksFieldMenuItem';
|
import { LinksFieldMenuItem } from '@/object-record/record-field/meta-types/input/components/LinksFieldMenuItem';
|
||||||
@ -17,6 +17,7 @@ import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useLis
|
|||||||
import { moveArrayItem } from '~/utils/array/moveArrayItem';
|
import { moveArrayItem } from '~/utils/array/moveArrayItem';
|
||||||
import { toSpliced } from '~/utils/array/toSpliced';
|
import { toSpliced } from '~/utils/array/toSpliced';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
import { absoluteUrlSchema } from '~/utils/validation-schemas/absoluteUrlSchema';
|
||||||
|
|
||||||
const StyledDropdownMenu = styled(DropdownMenu)`
|
const StyledDropdownMenu = styled(DropdownMenu)`
|
||||||
left: -1px;
|
left: -1px;
|
||||||
@ -68,14 +69,44 @@ export const LinksFieldInput = ({
|
|||||||
|
|
||||||
const [isInputDisplayed, setIsInputDisplayed] = useState(false);
|
const [isInputDisplayed, setIsInputDisplayed] = useState(false);
|
||||||
const [inputValue, setInputValue] = useState('');
|
const [inputValue, setInputValue] = useState('');
|
||||||
|
const [linkToEditIndex, setLinkToEditIndex] = useState(-1);
|
||||||
|
const isAddingNewLink = linkToEditIndex === -1;
|
||||||
|
|
||||||
const handleAddLink = () => {
|
const handleAddButtonClick = () => {
|
||||||
if (!inputValue) return;
|
setLinkToEditIndex(-1);
|
||||||
|
setIsInputDisplayed(true);
|
||||||
|
};
|
||||||
|
|
||||||
setIsInputDisplayed(false);
|
const handleEditButtonClick = (index: number) => {
|
||||||
setInputValue('');
|
setLinkToEditIndex(index);
|
||||||
|
setInputValue(links[index].url);
|
||||||
|
setIsInputDisplayed(true);
|
||||||
|
};
|
||||||
|
|
||||||
const nextLinks = [...links, { label: '', url: inputValue }];
|
const urlInputValidation = inputValue
|
||||||
|
? absoluteUrlSchema.safeParse(inputValue)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const handleSubmitInput = () => {
|
||||||
|
if (!urlInputValidation?.success) return;
|
||||||
|
|
||||||
|
const validatedInputValue = urlInputValidation.data;
|
||||||
|
|
||||||
|
// Don't persist if value hasn't changed.
|
||||||
|
if (
|
||||||
|
!isAddingNewLink &&
|
||||||
|
validatedInputValue === links[linkToEditIndex].url
|
||||||
|
) {
|
||||||
|
setIsInputDisplayed(false);
|
||||||
|
setInputValue('');
|
||||||
|
onCancel?.();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const linkValue = { label: '', url: validatedInputValue };
|
||||||
|
const nextLinks = isAddingNewLink
|
||||||
|
? [...links, linkValue]
|
||||||
|
: toSpliced(links, linkToEditIndex, 1, linkValue);
|
||||||
const [nextPrimaryLink, ...nextSecondaryLinks] = nextLinks;
|
const [nextPrimaryLink, ...nextSecondaryLinks] = nextLinks;
|
||||||
|
|
||||||
onSubmit?.(() =>
|
onSubmit?.(() =>
|
||||||
@ -85,6 +116,9 @@ export const LinksFieldInput = ({
|
|||||||
secondaryLinks: nextSecondaryLinks,
|
secondaryLinks: nextSecondaryLinks,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
setIsInputDisplayed(false);
|
||||||
|
setInputValue('');
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSetPrimaryLink = (index: number) => {
|
const handleSetPrimaryLink = (index: number) => {
|
||||||
@ -124,6 +158,7 @@ export const LinksFieldInput = ({
|
|||||||
dropdownId={`${hotkeyScope}-links-${index}`}
|
dropdownId={`${hotkeyScope}-links-${index}`}
|
||||||
isPrimary={index === 0}
|
isPrimary={index === 0}
|
||||||
label={label}
|
label={label}
|
||||||
|
onEdit={() => handleEditButtonClick(index)}
|
||||||
onSetAsPrimary={() => handleSetPrimaryLink(index)}
|
onSetAsPrimary={() => handleSetPrimaryLink(index)}
|
||||||
onDelete={() => handleDeleteLink(index)}
|
onDelete={() => handleDeleteLink(index)}
|
||||||
url={url}
|
url={url}
|
||||||
@ -140,15 +175,19 @@ export const LinksFieldInput = ({
|
|||||||
value={inputValue}
|
value={inputValue}
|
||||||
hotkeyScope={hotkeyScope}
|
hotkeyScope={hotkeyScope}
|
||||||
onChange={(event) => setInputValue(event.target.value)}
|
onChange={(event) => setInputValue(event.target.value)}
|
||||||
onEnter={handleAddLink}
|
onEnter={handleSubmitInput}
|
||||||
rightComponent={
|
rightComponent={
|
||||||
<LightIconButton Icon={IconPlus} onClick={handleAddLink} />
|
<LightIconButton
|
||||||
|
Icon={isAddingNewLink ? IconPlus : IconCheck}
|
||||||
|
disabled={!urlInputValidation?.success}
|
||||||
|
onClick={handleSubmitInput}
|
||||||
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<DropdownMenuItemsContainer>
|
<DropdownMenuItemsContainer>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onClick={() => setIsInputDisplayed(true)}
|
onClick={handleAddButtonClick}
|
||||||
LeftIcon={IconPlus}
|
LeftIcon={IconPlus}
|
||||||
text="Add link"
|
text="Add link"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import {
|
|||||||
IconBookmarkPlus,
|
IconBookmarkPlus,
|
||||||
IconComponent,
|
IconComponent,
|
||||||
IconDotsVertical,
|
IconDotsVertical,
|
||||||
|
IconPencil,
|
||||||
IconTrash,
|
IconTrash,
|
||||||
} from 'twenty-ui';
|
} from 'twenty-ui';
|
||||||
|
|
||||||
@ -18,8 +19,9 @@ type LinksFieldMenuItemProps = {
|
|||||||
dropdownId: string;
|
dropdownId: string;
|
||||||
isPrimary?: boolean;
|
isPrimary?: boolean;
|
||||||
label: string;
|
label: string;
|
||||||
onSetAsPrimary: () => void;
|
onEdit?: () => void;
|
||||||
onDelete: () => void;
|
onSetAsPrimary?: () => void;
|
||||||
|
onDelete?: () => void;
|
||||||
url: string;
|
url: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -33,6 +35,7 @@ export const LinksFieldMenuItem = ({
|
|||||||
dropdownId,
|
dropdownId,
|
||||||
isPrimary,
|
isPrimary,
|
||||||
label,
|
label,
|
||||||
|
onEdit,
|
||||||
onSetAsPrimary,
|
onSetAsPrimary,
|
||||||
onDelete,
|
onDelete,
|
||||||
url,
|
url,
|
||||||
@ -71,6 +74,11 @@ export const LinksFieldMenuItem = ({
|
|||||||
text="Set as Primary"
|
text="Set as Primary"
|
||||||
onClick={onSetAsPrimary}
|
onClick={onSetAsPrimary}
|
||||||
/>
|
/>
|
||||||
|
<MenuItem
|
||||||
|
LeftIcon={IconPencil}
|
||||||
|
text="Edit"
|
||||||
|
onClick={onEdit}
|
||||||
|
/>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
accent="danger"
|
accent="danger"
|
||||||
LeftIcon={IconTrash}
|
LeftIcon={IconTrash}
|
||||||
|
|||||||
Reference in New Issue
Block a user