feat: edit link in Links field (#5447)

Closes #5376
This commit is contained in:
Thaïs
2024-05-22 10:42:08 +02:00
committed by GitHub
parent 47a6146dd0
commit e2b48e2c4e
2 changed files with 58 additions and 11 deletions

View File

@ -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"
/> />

View File

@ -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}