Lucas/t 352 i dont want another input cell to open when i click outside (#163)

* Added logic to handle global edit mode

* Added recoil global edit mode state into generic editable components

* Fix lint

* Added tests
This commit is contained in:
Lucas Bordeau
2023-05-31 16:33:11 +02:00
committed by GitHub
parent c61beb1066
commit 723ea462e8
10 changed files with 294 additions and 107 deletions
@@ -1,9 +1,9 @@
import { ReactElement, useRef } from 'react';
import { useOutsideAlerter } from '../../hooks/useOutsideAlerter';
import { useHotkeys } from 'react-hotkeys-hook';
import { ReactElement } from 'react';
import { CellBaseContainer } from './CellBaseContainer';
import { CellEditModeContainer } from './CellEditModeContainer';
import { CellNormalModeContainer } from './CellNormalModeContainer';
import { useRecoilState } from 'recoil';
import { isSomeInputInEditModeState } from '../../modules/ui/tables/states/isSomeInputInEditModeState';
import { EditableCellEditMode } from './EditableCellEditMode';
type OwnProps = {
editModeContent: ReactElement;
@@ -25,58 +25,27 @@ export function EditableCell({
onOutsideClick,
onInsideClick,
}: OwnProps) {
const wrapperRef = useRef(null);
const editableContainerRef = useRef(null);
useOutsideAlerter(wrapperRef, () => {
onOutsideClick?.();
});
useHotkeys(
'esc',
() => {
if (isEditMode) {
onOutsideClick?.();
}
},
{
preventDefault: true,
enableOnContentEditable: true,
enableOnFormTags: true,
},
[isEditMode, onOutsideClick],
const [isSomeInputInEditMode, setIsSomeInputInEditMode] = useRecoilState(
isSomeInputInEditModeState,
);
useHotkeys(
'enter',
() => {
if (isEditMode) {
onOutsideClick?.();
}
},
{
preventDefault: true,
enableOnContentEditable: true,
enableOnFormTags: true,
},
[isEditMode, onOutsideClick],
);
function handleOnClick() {
if (!isSomeInputInEditMode) {
onInsideClick?.();
setIsSomeInputInEditMode(true);
}
}
return (
<CellBaseContainer
ref={wrapperRef}
onClick={() => {
onInsideClick && onInsideClick();
}}
>
<CellBaseContainer onClick={handleOnClick}>
{isEditMode ? (
<CellEditModeContainer
ref={editableContainerRef}
<EditableCellEditMode
editModeContent={editModeContent}
editModeHorizontalAlign={editModeHorizontalAlign}
editModeVerticalPosition={editModeVerticalPosition}
>
{editModeContent}
</CellEditModeContainer>
isEditMode={isEditMode}
onOutsideClick={onOutsideClick}
/>
) : (
<CellNormalModeContainer>{nonEditModeContent}</CellNormalModeContainer>
)}
@@ -0,0 +1,86 @@
import { ReactElement, useMemo, useRef } from 'react';
import { CellEditModeContainer } from './CellEditModeContainer';
import { useRecoilState } from 'recoil';
import { isSomeInputInEditModeState } from '../../modules/ui/tables/states/isSomeInputInEditModeState';
import { useListenClickOutsideArrayOfRef } from '../../modules/ui/common/hooks/useListenClickOutsideArrayOfRef';
import { useHotkeys } from 'react-hotkeys-hook';
import { debounce } from '../../modules/utils/debounce';
type OwnProps = {
editModeContent: ReactElement;
editModeHorizontalAlign?: 'left' | 'right';
editModeVerticalPosition?: 'over' | 'below';
isEditMode?: boolean;
onOutsideClick?: () => void;
onInsideClick?: () => void;
};
export function EditableCellEditMode({
editModeHorizontalAlign,
editModeVerticalPosition,
editModeContent,
isEditMode,
onOutsideClick,
}: OwnProps) {
const wrapperRef = useRef(null);
const [, setIsSomeInputInEditMode] = useRecoilState(
isSomeInputInEditModeState,
);
const debouncedSetIsSomeInputInEditMode = useMemo(() => {
return debounce(setIsSomeInputInEditMode, 20);
}, [setIsSomeInputInEditMode]);
useListenClickOutsideArrayOfRef([wrapperRef], () => {
if (isEditMode) {
debouncedSetIsSomeInputInEditMode(false);
onOutsideClick?.();
}
});
useHotkeys(
'esc',
() => {
if (isEditMode) {
onOutsideClick?.();
debouncedSetIsSomeInputInEditMode(false);
}
},
{
preventDefault: true,
enableOnContentEditable: true,
enableOnFormTags: true,
},
[isEditMode, onOutsideClick, debouncedSetIsSomeInputInEditMode],
);
useHotkeys(
'enter',
() => {
if (isEditMode) {
onOutsideClick?.();
debouncedSetIsSomeInputInEditMode(false);
}
},
{
preventDefault: true,
enableOnContentEditable: true,
enableOnFormTags: true,
},
[isEditMode, onOutsideClick, debouncedSetIsSomeInputInEditMode],
);
return (
<CellEditModeContainer
data-testid="editable-cell-edit-mode-container"
ref={wrapperRef}
editModeHorizontalAlign={editModeHorizontalAlign}
editModeVerticalPosition={editModeVerticalPosition}
>
{editModeContent}
</CellEditModeContainer>
);
}
@@ -1,9 +1,9 @@
import { ReactElement, useRef } from 'react';
import { useOutsideAlerter } from '../../hooks/useOutsideAlerter';
import { useHotkeys } from 'react-hotkeys-hook';
import { ReactElement } from 'react';
import { CellBaseContainer } from './CellBaseContainer';
import styled from '@emotion/styled';
import { EditableCellMenuEditModeContainer } from './EditableCellMenuEditModeContainer';
import { useRecoilState } from 'recoil';
import { isSomeInputInEditModeState } from '../../modules/ui/tables/states/isSomeInputInEditModeState';
import { EditableCellMenuEditMode } from './EditableCellMenuEditMode';
const EditableCellMenuNormalModeContainer = styled.div`
display: flex;
@@ -34,61 +34,31 @@ export function EditableCellMenu({
onOutsideClick,
onInsideClick,
}: OwnProps) {
const wrapperRef = useRef(null);
const editableContainerRef = useRef(null);
useOutsideAlerter(wrapperRef, () => {
onOutsideClick?.();
});
useHotkeys(
'esc',
() => {
if (isEditMode) {
onOutsideClick?.();
}
},
{
preventDefault: true,
enableOnContentEditable: true,
enableOnFormTags: true,
},
[isEditMode, onOutsideClick],
const [isSomeInputInEditMode, setIsSomeInputInEditMode] = useRecoilState(
isSomeInputInEditModeState,
);
useHotkeys(
'enter',
() => {
if (isEditMode) {
onOutsideClick?.();
}
},
{
preventDefault: true,
enableOnContentEditable: true,
enableOnFormTags: true,
},
[isEditMode, onOutsideClick],
);
function handleOnClick() {
if (!isSomeInputInEditMode) {
onInsideClick?.();
setIsSomeInputInEditMode(true);
}
}
return (
<CellBaseContainer
ref={wrapperRef}
onClick={() => {
onInsideClick && onInsideClick();
}}
>
<CellBaseContainer onClick={handleOnClick}>
<EditableCellMenuNormalModeContainer>
{nonEditModeContent}
</EditableCellMenuNormalModeContainer>
{isEditMode && (
<EditableCellMenuEditModeContainer
ref={editableContainerRef}
<EditableCellMenuEditMode
editModeContent={editModeContent}
editModeHorizontalAlign={editModeHorizontalAlign}
editModeVerticalPosition={editModeVerticalPosition}
>
{editModeContent}
</EditableCellMenuEditModeContainer>
isEditMode={isEditMode}
onOutsideClick={onOutsideClick}
onInsideClick={onInsideClick}
/>
)}
</CellBaseContainer>
);
@@ -0,0 +1,85 @@
import { ReactElement, useMemo, useRef } from 'react';
import { useRecoilState } from 'recoil';
import { isSomeInputInEditModeState } from '../../modules/ui/tables/states/isSomeInputInEditModeState';
import { useListenClickOutsideArrayOfRef } from '../../modules/ui/common/hooks/useListenClickOutsideArrayOfRef';
import { useHotkeys } from 'react-hotkeys-hook';
import { debounce } from '../../modules/utils/debounce';
import { EditableCellMenuEditModeContainer } from './EditableCellMenuEditModeContainer';
type OwnProps = {
editModeContent: ReactElement;
editModeHorizontalAlign?: 'left' | 'right';
editModeVerticalPosition?: 'over' | 'below';
isEditMode?: boolean;
onOutsideClick?: () => void;
onInsideClick?: () => void;
};
export function EditableCellMenuEditMode({
editModeHorizontalAlign,
editModeVerticalPosition,
editModeContent,
isEditMode,
onOutsideClick,
}: OwnProps) {
const wrapperRef = useRef(null);
const [, setIsSomeInputInEditMode] = useRecoilState(
isSomeInputInEditModeState,
);
const debouncedSetIsSomeInputInEditMode = useMemo(() => {
return debounce(setIsSomeInputInEditMode, 20);
}, [setIsSomeInputInEditMode]);
useListenClickOutsideArrayOfRef([wrapperRef], () => {
if (isEditMode) {
debouncedSetIsSomeInputInEditMode(false);
onOutsideClick?.();
}
});
useHotkeys(
'esc',
() => {
if (isEditMode) {
onOutsideClick?.();
debouncedSetIsSomeInputInEditMode(false);
}
},
{
preventDefault: true,
enableOnContentEditable: true,
enableOnFormTags: true,
},
[isEditMode, onOutsideClick, debouncedSetIsSomeInputInEditMode],
);
useHotkeys(
'enter',
() => {
if (isEditMode) {
onOutsideClick?.();
debouncedSetIsSomeInputInEditMode(false);
}
},
{
preventDefault: true,
enableOnContentEditable: true,
enableOnFormTags: true,
},
[isEditMode, onOutsideClick, debouncedSetIsSomeInputInEditMode],
);
return (
<EditableCellMenuEditModeContainer
ref={wrapperRef}
editModeHorizontalAlign={editModeHorizontalAlign}
editModeVerticalPosition={editModeVerticalPosition}
>
{editModeContent}
</EditableCellMenuEditModeContainer>
);
}
@@ -10,6 +10,8 @@ import { FaPlus } from 'react-icons/fa';
import { HoverableMenuItem } from './HoverableMenuItem';
import { EditableCellMenu } from './EditableCellMenu';
import { CellNormalModeContainer } from './CellNormalModeContainer';
import { useRecoilState } from 'recoil';
import { isSomeInputInEditModeState } from '../../modules/ui/tables/states/isSomeInputInEditModeState';
const StyledEditModeContainer = styled.div`
width: 200px;
@@ -112,6 +114,9 @@ function EditableRelation<
onCreate,
}: EditableRelationProps<RelationType, ChipComponentPropsType>) {
const [isEditMode, setIsEditMode] = useState(false);
const [, setIsSomeInputInEditMode] = useRecoilState(
isSomeInputInEditModeState,
);
// TODO: Tie this to a react context
const [filterSearchResults, setSearchInput, setFilterSearch, searchInput] =
@@ -130,7 +135,12 @@ function EditableRelation<
function handleCreateNewRelationButtonClick() {
onCreate?.(searchInput);
closeEditMode();
}
function closeEditMode() {
setIsEditMode(false);
setIsSomeInputInEditMode(false);
}
return (
@@ -155,6 +165,7 @@ function EditableRelation<
</StyledEditModeSelectedContainer>
<StyledEditModeSearchContainer>
<StyledEditModeSearchInput
autoFocus
placeholder={searchPlaceholder}
onChange={(event: ChangeEvent<HTMLInputElement>) => {
setFilterSearch(searchConfig);
@@ -183,7 +194,7 @@ function EditableRelation<
key={index}
onClick={() => {
onChange(result.value);
setIsEditMode(false);
closeEditMode();
}}
>
<HoverableMenuItem>