Persist table cell values on cell close (#655)

* Persist table cell values on cell close

* Apply to all cells
This commit is contained in:
Charles Bochet
2023-07-13 21:20:08 -07:00
committed by GitHub
parent ca1723f2e6
commit 551c3b5e60
20 changed files with 260 additions and 133 deletions

View File

@ -1,3 +1,5 @@
import { useEffect, useState } from 'react';
import { CellCommentChip } from '@/comments/components/table/CellCommentChip'; import { CellCommentChip } from '@/comments/components/table/CellCommentChip';
import { useOpenTimelineRightDrawer } from '@/comments/hooks/useOpenTimelineRightDrawer'; import { useOpenTimelineRightDrawer } from '@/comments/hooks/useOpenTimelineRightDrawer';
import { EditableCellChip } from '@/ui/components/editable-cell/types/EditableChip'; import { EditableCellChip } from '@/ui/components/editable-cell/types/EditableChip';
@ -21,6 +23,8 @@ export function CompanyEditableNameChipCell({ company }: OwnProps) {
const openCommentRightDrawer = useOpenTimelineRightDrawer(); const openCommentRightDrawer = useOpenTimelineRightDrawer();
const [updateCompany] = useUpdateCompanyMutation(); const [updateCompany] = useUpdateCompanyMutation();
const [internalValue, setInternalValue] = useState(company.name ?? '');
function handleCommentClick(event: React.MouseEvent<HTMLDivElement>) { function handleCommentClick(event: React.MouseEvent<HTMLDivElement>) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
@ -33,20 +37,17 @@ export function CompanyEditableNameChipCell({ company }: OwnProps) {
]); ]);
} }
useEffect(() => {
setInternalValue(company.name ?? '');
}, [company.name]);
return ( return (
<EditableCellChip <EditableCellChip
value={company.name ?? ''} value={internalValue}
placeholder="Name" placeholder="Name"
picture={getLogoUrlFromDomainName(company.domainName)} picture={getLogoUrlFromDomainName(company.domainName)}
id={company.id} id={company.id}
changeHandler={(value: string) => { changeHandler={setInternalValue}
updateCompany({
variables: {
id: company.id,
name: value,
},
});
}}
ChipComponent={CompanyChip} ChipComponent={CompanyChip}
rightEndContents={[ rightEndContents={[
<CellCommentChip <CellCommentChip
@ -54,6 +55,15 @@ export function CompanyEditableNameChipCell({ company }: OwnProps) {
onClick={handleCommentClick} onClick={handleCommentClick}
/>, />,
]} ]}
onSubmit={() =>
updateCompany({
variables: {
id: company.id,
name: internalValue,
},
})
}
onCancel={() => setInternalValue(company.name ?? '')}
/> />
); );
} }

View File

@ -1,3 +1,4 @@
import { useEffect, useState } from 'react';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { companyAddressFamilyState } from '@/companies/states/companyAddressFamilyState'; import { companyAddressFamilyState } from '@/companies/states/companyAddressFamilyState';
@ -14,19 +15,24 @@ export function EditableCompanyAddressCell() {
companyAddressFamilyState(currentRowEntityId ?? ''), companyAddressFamilyState(currentRowEntityId ?? ''),
); );
const [internalValue, setInternalValue] = useState(address ?? '');
useEffect(() => {
setInternalValue(address ?? '');
}, [address]);
return ( return (
<EditableCellText <EditableCellText
value={address ?? ''} value={internalValue}
onChange={async (newAddress: string) => { onChange={setInternalValue}
if (!currentRowEntityId) return; onSubmit={() =>
updateCompany({
await updateCompany({
variables: { variables: {
id: currentRowEntityId, id: currentRowEntityId,
address: newAddress, address: internalValue,
}, },
}); })
}} }
onCancel={() => setInternalValue(address ?? '')}
/> />
); );
} }

View File

@ -1,3 +1,4 @@
import { useEffect, useState } from 'react';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { companyDomainNameFamilyState } from '@/companies/states/companyDomainNameFamilyState'; import { companyDomainNameFamilyState } from '@/companies/states/companyDomainNameFamilyState';
@ -13,20 +14,24 @@ export function EditableCompanyDomainNameCell() {
const name = useRecoilValue( const name = useRecoilValue(
companyDomainNameFamilyState(currentRowEntityId ?? ''), companyDomainNameFamilyState(currentRowEntityId ?? ''),
); );
const [internalValue, setInternalValue] = useState(name ?? '');
useEffect(() => {
setInternalValue(name ?? '');
}, [name]);
return ( return (
<EditableCellText <EditableCellText
value={name ?? ''} value={internalValue}
onChange={async (domainName: string) => { onChange={setInternalValue}
if (!currentRowEntityId) return; onSubmit={() =>
updateCompany({
await updateCompany({
variables: { variables: {
id: currentRowEntityId, id: currentRowEntityId,
domainName: domainName, domainName: internalValue,
}, },
}); })
}} }
onCancel={() => setInternalValue(name ?? '')}
/> />
); );
} }

View File

@ -1,3 +1,4 @@
import { useEffect, useState } from 'react';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { companyEmployeesFamilyState } from '@/companies/states/companyEmployeesFamilyState'; import { companyEmployeesFamilyState } from '@/companies/states/companyEmployeesFamilyState';
@ -14,20 +15,26 @@ export function EditableCompanyEmployeesCell() {
companyEmployeesFamilyState(currentRowEntityId ?? ''), companyEmployeesFamilyState(currentRowEntityId ?? ''),
); );
const [internalValue, setInternalValue] = useState(employees ?? '');
useEffect(() => {
setInternalValue(employees ?? '');
}, [employees]);
return ( return (
// TODO: Create an EditableCellNumber component // TODO: Create an EditableCellNumber component
<EditableCellText <EditableCellText
value={employees ?? ''} value={internalValue}
onChange={async (newEmployees: string) => { onChange={setInternalValue}
if (!currentRowEntityId) return; onSubmit={() =>
updateCompany({
await updateCompany({
variables: { variables: {
id: currentRowEntityId, id: currentRowEntityId,
employees: parseInt(newEmployees), employees: parseInt(internalValue),
}, },
}); })
}} }
onCancel={() => setInternalValue(employees ?? '')}
/> />
); );
} }

View File

@ -15,6 +15,8 @@ type OwnProps = {
| null | null
| undefined; | undefined;
onChange: (firstName: string, lastName: string) => void; onChange: (firstName: string, lastName: string) => void;
onSubmit?: () => void;
onCancel?: () => void;
}; };
const NoEditModeContainer = styled.div` const NoEditModeContainer = styled.div`
@ -28,7 +30,12 @@ const RightContainer = styled.div`
margin-left: ${(props) => props.theme.spacing(1)}; margin-left: ${(props) => props.theme.spacing(1)};
`; `;
export function EditablePeopleFullName({ person, onChange }: OwnProps) { export function EditablePeopleFullName({
person,
onChange,
onSubmit,
onCancel,
}: OwnProps) {
const openCommentRightDrawer = useOpenTimelineRightDrawer(); const openCommentRightDrawer = useOpenTimelineRightDrawer();
function handleDoubleTextChange( function handleDoubleTextChange(
@ -61,6 +68,8 @@ export function EditablePeopleFullName({ person, onChange }: OwnProps) {
firstValuePlaceholder="First name" firstValuePlaceholder="First name"
secondValuePlaceholder="Last name" secondValuePlaceholder="Last name"
onChange={handleDoubleTextChange} onChange={handleDoubleTextChange}
onSubmit={onSubmit}
onCancel={onCancel}
nonEditModeContent={ nonEditModeContent={
<NoEditModeContainer> <NoEditModeContainer>
<PersonChip <PersonChip

View File

@ -72,7 +72,7 @@ export function PeopleCompanyCreateCell({ people }: OwnProps) {
secondValuePlaceholder="Name" secondValuePlaceholder="Name"
onChange={handleDoubleTextChange} onChange={handleDoubleTextChange}
onSubmit={() => handleCompanyCreate(companyName, companyDomainName)} onSubmit={() => handleCompanyCreate(companyName, companyDomainName)}
onExit={() => setIsCreating(false)} onCancel={() => setIsCreating(false)}
/> />
); );
} }

View File

@ -1,3 +1,4 @@
import { useEffect, useState } from 'react';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { peopleCityFamilyState } from '@/people/states/peopleCityFamilyState'; import { peopleCityFamilyState } from '@/people/states/peopleCityFamilyState';
@ -12,19 +13,25 @@ export function EditablePeopleCityCell() {
const city = useRecoilValue(peopleCityFamilyState(currentRowEntityId ?? '')); const city = useRecoilValue(peopleCityFamilyState(currentRowEntityId ?? ''));
const [internalValue, setInternalValue] = useState(city ?? '');
useEffect(() => {
setInternalValue(city ?? '');
}, [city]);
return ( return (
<EditableCellPhone <EditableCellPhone
value={city ?? ''} value={internalValue}
onChange={async (newCity: string) => { onChange={setInternalValue}
if (!currentRowEntityId) return; onSubmit={() =>
updatePerson({
await updatePerson({
variables: { variables: {
id: currentRowEntityId, id: currentRowEntityId,
city: newCity, city: internalValue,
}, },
}); })
}} }
onCancel={() => setInternalValue(city ?? '')}
/> />
); );
} }

View File

@ -1,3 +1,4 @@
import { useEffect, useState } from 'react';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { peopleEmailFamilyState } from '@/people/states/peopleEmailFamilyState'; import { peopleEmailFamilyState } from '@/people/states/peopleEmailFamilyState';
@ -14,19 +15,25 @@ export function EditablePeopleEmailCell() {
peopleEmailFamilyState(currentRowEntityId ?? ''), peopleEmailFamilyState(currentRowEntityId ?? ''),
); );
const [internalValue, setInternalValue] = useState(email ?? '');
useEffect(() => {
setInternalValue(email ?? '');
}, [email]);
return ( return (
<EditableCellText <EditableCellText
value={email ?? ''} value={internalValue}
onChange={async (newEmail: string) => { onChange={setInternalValue}
if (!currentRowEntityId) return; onSubmit={() =>
updatePerson({
await updatePerson({
variables: { variables: {
id: currentRowEntityId, id: currentRowEntityId,
email: newEmail, email: internalValue,
}, },
}); })
}} }
onCancel={() => setInternalValue(email ?? '')}
/> />
); );
} }

View File

@ -1,3 +1,4 @@
import { useEffect, useState } from 'react';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { EditablePeopleFullName } from '@/people/components/EditablePeopleFullName'; import { EditablePeopleFullName } from '@/people/components/EditablePeopleFullName';
@ -14,24 +15,38 @@ export function EditablePeopleFullNameCell() {
peopleNameCellFamilyState(currentRowEntityId ?? ''), peopleNameCellFamilyState(currentRowEntityId ?? ''),
); );
const [internalFirstName, setInternalFirstName] = useState(firstName ?? '');
const [internalLastName, setInternalLastName] = useState(lastName ?? '');
useEffect(() => {
setInternalFirstName(firstName ?? '');
setInternalLastName(lastName ?? '');
}, [firstName, lastName]);
return ( return (
<EditablePeopleFullName <EditablePeopleFullName
person={{ person={{
id: currentRowEntityId ?? undefined, id: currentRowEntityId ?? undefined,
_commentThreadCount: commentCount ?? undefined, _commentThreadCount: commentCount ?? undefined,
firstName: firstName ?? undefined, firstName: internalFirstName,
lastName: lastName ?? undefined, lastName: internalLastName,
}} }}
onChange={async (firstName: string, lastName: string) => { onChange={(firstName, lastName) => {
if (!currentRowEntityId) return; setInternalFirstName(firstName);
setInternalLastName(lastName);
await updatePerson({ }}
onSubmit={() =>
updatePerson({
variables: { variables: {
id: currentRowEntityId, id: currentRowEntityId,
firstName, firstName: internalFirstName,
lastName, lastName: internalLastName,
}, },
}); })
}
onCancel={() => {
setInternalFirstName(firstName ?? '');
setInternalLastName(lastName ?? '');
}} }}
/> />
); );

View File

@ -1,3 +1,4 @@
import { useEffect, useState } from 'react';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { peoplePhoneFamilyState } from '@/people/states/peoplePhoneFamilyState'; import { peoplePhoneFamilyState } from '@/people/states/peoplePhoneFamilyState';
@ -13,19 +14,26 @@ export function EditablePeoplePhoneCell() {
const phone = useRecoilValue( const phone = useRecoilValue(
peoplePhoneFamilyState(currentRowEntityId ?? ''), peoplePhoneFamilyState(currentRowEntityId ?? ''),
); );
const [internalValue, setInternalValue] = useState(phone ?? '');
useEffect(() => {
setInternalValue(phone ?? '');
}, [phone]);
return ( return (
<EditableCellPhone <EditableCellPhone
value={phone ?? ''} value={internalValue}
onChange={async (newPhone: string) => { onChange={setInternalValue}
if (!currentRowEntityId) return; onSubmit={() =>
updatePerson({
await updatePerson({
variables: { variables: {
id: currentRowEntityId, id: currentRowEntityId,
phone: newPhone, phone: internalValue,
}, },
}); })
}} }
onCancel={() => setInternalValue(phone ?? '')}
/> />
); );
} }

View File

@ -26,6 +26,8 @@ type OwnProps = {
editModeHorizontalAlign?: 'left' | 'right'; editModeHorizontalAlign?: 'left' | 'right';
editModeVerticalPosition?: 'over' | 'below'; editModeVerticalPosition?: 'over' | 'below';
editHotkeysScope?: HotkeysScope; editHotkeysScope?: HotkeysScope;
onSubmit?: () => void;
onCancel?: () => void;
}; };
export function EditableCell({ export function EditableCell({
@ -34,6 +36,8 @@ export function EditableCell({
editModeContent, editModeContent,
nonEditModeContent, nonEditModeContent,
editHotkeysScope, editHotkeysScope,
onSubmit,
onCancel,
}: OwnProps) { }: OwnProps) {
const { isCurrentCellInEditMode } = useCurrentCellEditMode(); const { isCurrentCellInEditMode } = useCurrentCellEditMode();
@ -45,6 +49,8 @@ export function EditableCell({
<EditableCellEditMode <EditableCellEditMode
editModeHorizontalAlign={editModeHorizontalAlign} editModeHorizontalAlign={editModeHorizontalAlign}
editModeVerticalPosition={editModeVerticalPosition} editModeVerticalPosition={editModeVerticalPosition}
onSubmit={onSubmit}
onCancel={onCancel}
> >
{editModeContent} {editModeContent}
</EditableCellEditMode> </EditableCellEditMode>

View File

@ -1,13 +1,9 @@
import { ReactElement, useRef } from 'react'; import { ReactElement, useRef } from 'react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { useListenClickOutsideArrayOfRef } from '@/ui/hooks/useListenClickOutsideArrayOfRef';
import { useMoveSoftFocus } from '@/ui/tables/hooks/useMoveSoftFocus';
import { overlayBackground } from '@/ui/themes/effects'; import { overlayBackground } from '@/ui/themes/effects';
import { useEditableCell } from './hooks/useEditableCell'; import { useRegisterCloseCellHandlers } from './hooks/useRegisterCloseCellHandlers';
export const EditableCellEditModeContainer = styled.div<OwnProps>` export const EditableCellEditModeContainer = styled.div<OwnProps>`
align-items: center; align-items: center;
@ -33,60 +29,20 @@ type OwnProps = {
editModeHorizontalAlign?: 'left' | 'right'; editModeHorizontalAlign?: 'left' | 'right';
editModeVerticalPosition?: 'over' | 'below'; editModeVerticalPosition?: 'over' | 'below';
onOutsideClick?: () => void; onOutsideClick?: () => void;
onCancel?: () => void;
onSubmit?: () => void;
}; };
export function EditableCellEditMode({ export function EditableCellEditMode({
editModeHorizontalAlign, editModeHorizontalAlign,
editModeVerticalPosition, editModeVerticalPosition,
children, children,
onCancel,
onSubmit,
}: OwnProps) { }: OwnProps) {
const wrapperRef = useRef(null); const wrapperRef = useRef(null);
const { closeEditableCell } = useEditableCell(); useRegisterCloseCellHandlers(wrapperRef, onSubmit, onCancel);
const { moveRight, moveLeft, moveDown } = useMoveSoftFocus();
useListenClickOutsideArrayOfRef([wrapperRef], () => {
closeEditableCell();
});
useScopedHotkeys(
'enter',
() => {
closeEditableCell();
moveDown();
},
InternalHotkeysScope.CellEditMode,
[closeEditableCell],
);
useScopedHotkeys(
'esc',
() => {
closeEditableCell();
},
InternalHotkeysScope.CellEditMode,
[closeEditableCell],
);
useScopedHotkeys(
'tab',
() => {
closeEditableCell();
moveRight();
},
InternalHotkeysScope.CellEditMode,
[closeEditableCell, moveRight],
);
useScopedHotkeys(
'shift+tab',
() => {
closeEditableCell();
moveLeft();
},
InternalHotkeysScope.CellEditMode,
[closeEditableCell, moveRight],
);
return ( return (
<EditableCellEditModeContainer <EditableCellEditModeContainer

View File

@ -4,7 +4,6 @@ import { useSetHotkeysScope } from '@/hotkeys/hooks/useSetHotkeysScope';
import { HotkeysScope } from '@/hotkeys/types/internal/HotkeysScope'; import { HotkeysScope } from '@/hotkeys/types/internal/HotkeysScope';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope'; import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { useCloseCurrentCellInEditMode } from '@/ui/tables/hooks/useClearCellInEditMode'; import { useCloseCurrentCellInEditMode } from '@/ui/tables/hooks/useClearCellInEditMode';
import { isSoftFocusActiveState } from '@/ui/tables/states/isSoftFocusActiveState';
import { isSomeInputInEditModeState } from '@/ui/tables/states/isSomeInputInEditModeState'; import { isSomeInputInEditModeState } from '@/ui/tables/states/isSomeInputInEditModeState';
import { useCurrentCellEditMode } from './useCurrentCellEditMode'; import { useCurrentCellEditMode } from './useCurrentCellEditMode';
@ -30,7 +29,6 @@ export function useEditableCell() {
if (!isSomeInputInEditMode) { if (!isSomeInputInEditMode) {
set(isSomeInputInEditModeState, true); set(isSomeInputInEditModeState, true);
set(isSoftFocusActiveState, false);
setCurrentCellInEditMode(); setCurrentCellInEditMode();

View File

@ -0,0 +1,66 @@
import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { useListenClickOutsideArrayOfRef } from '@/ui/hooks/useListenClickOutsideArrayOfRef';
import { useMoveSoftFocus } from '@/ui/tables/hooks/useMoveSoftFocus';
import { useCurrentCellEditMode } from './useCurrentCellEditMode';
import { useEditableCell } from './useEditableCell';
export function useRegisterCloseCellHandlers(
wrapperRef: React.RefObject<HTMLDivElement>,
onSubmit?: () => void,
onCancel?: () => void,
) {
const { closeEditableCell } = useEditableCell();
const { isCurrentCellInEditMode } = useCurrentCellEditMode();
useListenClickOutsideArrayOfRef([wrapperRef], () => {
if (isCurrentCellInEditMode) {
onSubmit?.();
closeEditableCell();
}
});
const { moveRight, moveLeft, moveDown } = useMoveSoftFocus();
useScopedHotkeys(
'enter',
() => {
onSubmit?.();
closeEditableCell();
moveDown();
},
InternalHotkeysScope.CellEditMode,
[closeEditableCell, onSubmit, moveDown],
);
useScopedHotkeys(
'esc',
() => {
closeEditableCell();
onCancel?.();
},
InternalHotkeysScope.CellEditMode,
[closeEditableCell, onCancel],
);
useScopedHotkeys(
'tab',
() => {
onSubmit?.();
closeEditableCell();
moveRight();
},
InternalHotkeysScope.CellEditMode,
[closeEditableCell, onSubmit, moveRight],
);
useScopedHotkeys(
'shift+tab',
() => {
onSubmit?.();
closeEditableCell();
moveLeft();
},
InternalHotkeysScope.CellEditMode,
[closeEditableCell, onSubmit, moveRight],
);
}

View File

@ -14,6 +14,8 @@ type OwnProps = {
secondValuePlaceholder: string; secondValuePlaceholder: string;
nonEditModeContent: ReactElement; nonEditModeContent: ReactElement;
onChange: (firstValue: string, secondValue: string) => void; onChange: (firstValue: string, secondValue: string) => void;
onSubmit?: () => void;
onCancel?: () => void;
loading?: boolean; loading?: boolean;
}; };
@ -23,6 +25,8 @@ export function EditableCellDoubleText({
firstValuePlaceholder, firstValuePlaceholder,
secondValuePlaceholder, secondValuePlaceholder,
onChange, onChange,
onSubmit,
onCancel,
nonEditModeContent, nonEditModeContent,
loading, loading,
}: OwnProps) { }: OwnProps) {
@ -50,6 +54,8 @@ export function EditableCellDoubleText({
firstValuePlaceholder={firstValuePlaceholder} firstValuePlaceholder={firstValuePlaceholder}
secondValuePlaceholder={secondValuePlaceholder} secondValuePlaceholder={secondValuePlaceholder}
onChange={handleOnChange} onChange={handleOnChange}
onSubmit={onSubmit}
onCancel={onCancel}
/> />
} }
nonEditModeContent={loading ? <CellSkeleton /> : nonEditModeContent} nonEditModeContent={loading ? <CellSkeleton /> : nonEditModeContent}

View File

@ -15,8 +15,8 @@ type OwnProps = {
firstValuePlaceholder: string; firstValuePlaceholder: string;
secondValuePlaceholder: string; secondValuePlaceholder: string;
onChange: (firstValue: string, secondValue: string) => void; onChange: (firstValue: string, secondValue: string) => void;
onSubmit?: (firstValue: string, secondValue: string) => void; onSubmit?: () => void;
onExit?: () => void; onCancel?: () => void;
}; };
const StyledContainer = styled.div` const StyledContainer = styled.div`
@ -37,7 +37,7 @@ export function EditableCellDoubleTextEditMode({
secondValuePlaceholder, secondValuePlaceholder,
onChange, onChange,
onSubmit, onSubmit,
onExit, onCancel,
}: OwnProps) { }: OwnProps) {
const [focusPosition, setFocusPosition] = useState<'left' | 'right'>('left'); const [focusPosition, setFocusPosition] = useState<'left' | 'right'>('left');
@ -57,9 +57,7 @@ export function EditableCellDoubleTextEditMode({
() => { () => {
closeCell(); closeCell();
moveDown(); moveDown();
if (onSubmit) { onSubmit?.();
onSubmit(firstValue, secondValue);
}
}, },
InternalHotkeysScope.CellDoubleTextInput, InternalHotkeysScope.CellDoubleTextInput,
[closeCell], [closeCell],
@ -68,9 +66,7 @@ export function EditableCellDoubleTextEditMode({
useScopedHotkeys( useScopedHotkeys(
Key.Escape, Key.Escape,
() => { () => {
if (onExit) { onCancel?.();
onExit();
}
closeCell(); closeCell();
}, },
InternalHotkeysScope.CellDoubleTextInput, InternalHotkeysScope.CellDoubleTextInput,
@ -84,9 +80,7 @@ export function EditableCellDoubleTextEditMode({
setFocusPosition('right'); setFocusPosition('right');
secondValueInputRef.current?.focus(); secondValueInputRef.current?.focus();
} else { } else {
if (onExit) { onSubmit?.();
onExit();
}
closeCell(); closeCell();
moveRight(); moveRight();
} }
@ -102,6 +96,7 @@ export function EditableCellDoubleTextEditMode({
setFocusPosition('left'); setFocusPosition('left');
firstValueInputRef.current?.focus(); firstValueInputRef.current?.focus();
} else { } else {
onSubmit?.();
closeCell(); closeCell();
moveLeft(); moveLeft();
} }

View File

@ -9,9 +9,17 @@ type OwnProps = {
placeholder?: string; placeholder?: string;
value: string; value: string;
onChange: (updated: string) => void; onChange: (updated: string) => void;
onSubmit?: () => void;
onCancel?: () => void;
}; };
export function EditableCellPhone({ value, placeholder, onChange }: OwnProps) { export function EditableCellPhone({
value,
placeholder,
onChange,
onSubmit,
onCancel,
}: OwnProps) {
const inputRef = useRef<HTMLInputElement>(null); const inputRef = useRef<HTMLInputElement>(null);
const [inputValue, setInputValue] = useState(value); const [inputValue, setInputValue] = useState(value);
@ -34,6 +42,8 @@ export function EditableCellPhone({ value, placeholder, onChange }: OwnProps) {
/> />
} }
nonEditModeContent={<InplaceInputPhoneDisplayMode value={inputValue} />} nonEditModeContent={<InplaceInputPhoneDisplayMode value={inputValue} />}
onSubmit={onSubmit}
onCancel={onCancel}
/> />
); );
} }

View File

@ -12,6 +12,8 @@ type OwnProps = {
onChange: (newValue: string) => void; onChange: (newValue: string) => void;
editModeHorizontalAlign?: 'left' | 'right'; editModeHorizontalAlign?: 'left' | 'right';
loading?: boolean; loading?: boolean;
onSubmit?: () => void;
onCancel?: () => void;
}; };
export function EditableCellText({ export function EditableCellText({
@ -20,6 +22,8 @@ export function EditableCellText({
onChange, onChange,
editModeHorizontalAlign, editModeHorizontalAlign,
loading, loading,
onCancel,
onSubmit,
}: OwnProps) { }: OwnProps) {
const [internalValue, setInternalValue] = useState(value); const [internalValue, setInternalValue] = useState(value);
@ -41,6 +45,8 @@ export function EditableCellText({
}} }}
/> />
} }
onSubmit={onSubmit}
onCancel={onCancel}
nonEditModeContent={ nonEditModeContent={
loading ? ( loading ? (
<CellSkeleton /> <CellSkeleton />

View File

@ -28,6 +28,8 @@ export type EditableChipProps = {
commentThreadCount?: number; commentThreadCount?: number;
onCommentClick?: (event: React.MouseEvent<HTMLDivElement>) => void; onCommentClick?: (event: React.MouseEvent<HTMLDivElement>) => void;
rightEndContents?: ReactNode[]; rightEndContents?: ReactNode[];
onSubmit?: () => void;
onCancel?: () => void;
}; };
// TODO: refactor // TODO: refactor
@ -58,6 +60,8 @@ export function EditableCellChip({
editModeHorizontalAlign, editModeHorizontalAlign,
ChipComponent, ChipComponent,
rightEndContents, rightEndContents,
onSubmit,
onCancel,
}: EditableChipProps) { }: EditableChipProps) {
const inputRef = useRef<HTMLInputElement>(null); const inputRef = useRef<HTMLInputElement>(null);
const [inputValue, setInputValue] = useState(value); const [inputValue, setInputValue] = useState(value);
@ -87,6 +91,8 @@ export function EditableCellChip({
}} }}
/> />
} }
onSubmit={onSubmit}
onCancel={onCancel}
nonEditModeContent={ nonEditModeContent={
<NoEditModeContainer> <NoEditModeContainer>
<ChipComponent id={id} name={inputValue} picture={picture} /> <ChipComponent id={id} name={inputValue} picture={picture} />

View File

@ -31,6 +31,10 @@ export function useLeaveTableFocus() {
.getLoadable(currentHotkeysScopeState) .getLoadable(currentHotkeysScopeState)
.valueOrThrow(); .valueOrThrow();
if (isSomeInputInEditMode) {
return;
}
if (!isSoftFocusActive && !isSomeInputInEditMode) { if (!isSoftFocusActive && !isSomeInputInEditMode) {
return; return;
} }