FieldDisplay & FieldInput (#1708)
* Removed view field duplicate types * wip * wip 2 * wip 3 * Unified state for fields * Renaming * Wip * Post merge * Post post merge * wip * Delete unused file * Boolean and Probability * Finished InlineCell * Renamed EditableCell to TableCell * Finished double texts * Finished MoneyField * Fixed bug inline cell click outside * Fixed hotkey scope * Final fixes * Phone * Fix url and number input validation * Fix * Fix position * wip refactor activity editor * Fixed activity editor --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -1,24 +0,0 @@
|
||||
import { useEditableCell } from '../hooks/useEditableCell';
|
||||
import { useSetSoftFocusOnCurrentCell } from '../hooks/useSetSoftFocusOnCurrentCell';
|
||||
|
||||
import { EditableCellDisplayContainer } from './EditableCellDisplayContainer';
|
||||
|
||||
export const EditableCellDisplayMode = ({
|
||||
children,
|
||||
isHovered,
|
||||
}: React.PropsWithChildren<unknown> & { isHovered?: boolean }) => {
|
||||
const setSoftFocusOnCurrentCell = useSetSoftFocusOnCurrentCell();
|
||||
|
||||
const { openEditableCell } = useEditableCell();
|
||||
|
||||
const handleClick = () => {
|
||||
setSoftFocusOnCurrentCell();
|
||||
openEditableCell();
|
||||
};
|
||||
|
||||
return (
|
||||
<EditableCellDisplayContainer isHovered={isHovered} onClick={handleClick}>
|
||||
{children}
|
||||
</EditableCellDisplayContainer>
|
||||
);
|
||||
};
|
||||
@ -1,68 +0,0 @@
|
||||
import { isViewFieldBoolean } from '@/ui/editable-field/types/guards/isViewFieldBoolean';
|
||||
import { isViewFieldChip } from '@/ui/editable-field/types/guards/isViewFieldChip';
|
||||
import { isViewFieldDate } from '@/ui/editable-field/types/guards/isViewFieldDate';
|
||||
import { isViewFieldDoubleText } from '@/ui/editable-field/types/guards/isViewFieldDoubleText';
|
||||
import { isViewFieldDoubleTextChip } from '@/ui/editable-field/types/guards/isViewFieldDoubleTextChip';
|
||||
import { isViewFieldEmail } from '@/ui/editable-field/types/guards/isViewFieldEmail';
|
||||
import { isViewFieldMoney } from '@/ui/editable-field/types/guards/isViewFieldMoney';
|
||||
import { isViewFieldNumber } from '@/ui/editable-field/types/guards/isViewFieldNumber';
|
||||
import { isViewFieldPhone } from '@/ui/editable-field/types/guards/isViewFieldPhone';
|
||||
import { isViewFieldRelation } from '@/ui/editable-field/types/guards/isViewFieldRelation';
|
||||
import { isViewFieldText } from '@/ui/editable-field/types/guards/isViewFieldText';
|
||||
import { isViewFieldURL } from '@/ui/editable-field/types/guards/isViewFieldURL';
|
||||
import { ViewFieldMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
|
||||
import { ColumnDefinition } from '../../types/ColumnDefinition';
|
||||
import { GenericEditableBooleanCell } from '../type/components/GenericEditableBooleanCell';
|
||||
import { GenericEditableChipCell } from '../type/components/GenericEditableChipCell';
|
||||
import { GenericEditableDateCell } from '../type/components/GenericEditableDateCell';
|
||||
import { GenericEditableDoubleTextCell } from '../type/components/GenericEditableDoubleTextCell';
|
||||
import { GenericEditableDoubleTextChipCell } from '../type/components/GenericEditableDoubleTextChipCell';
|
||||
import { GenericEditableEmailCell } from '../type/components/GenericEditableEmailCell';
|
||||
import { GenericEditableMoneyCell } from '../type/components/GenericEditableMoneyCell';
|
||||
import { GenericEditableNumberCell } from '../type/components/GenericEditableNumberCell';
|
||||
import { GenericEditablePhoneCell } from '../type/components/GenericEditablePhoneCell';
|
||||
import { GenericEditableRelationCell } from '../type/components/GenericEditableRelationCell';
|
||||
import { GenericEditableTextCell } from '../type/components/GenericEditableTextCell';
|
||||
import { GenericEditableURLCell } from '../type/components/GenericEditableURLCell';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldMetadata>;
|
||||
};
|
||||
|
||||
export const GenericEditableCell = ({ columnDefinition }: OwnProps) => {
|
||||
if (isViewFieldEmail(columnDefinition)) {
|
||||
return <GenericEditableEmailCell columnDefinition={columnDefinition} />;
|
||||
} else if (isViewFieldText(columnDefinition)) {
|
||||
return <GenericEditableTextCell columnDefinition={columnDefinition} />;
|
||||
} else if (isViewFieldRelation(columnDefinition)) {
|
||||
return <GenericEditableRelationCell columnDefinition={columnDefinition} />;
|
||||
} else if (isViewFieldDoubleTextChip(columnDefinition)) {
|
||||
return (
|
||||
<GenericEditableDoubleTextChipCell columnDefinition={columnDefinition} />
|
||||
);
|
||||
} else if (isViewFieldDoubleText(columnDefinition)) {
|
||||
return (
|
||||
<GenericEditableDoubleTextCell columnDefinition={columnDefinition} />
|
||||
);
|
||||
} else if (isViewFieldPhone(columnDefinition)) {
|
||||
return <GenericEditablePhoneCell columnDefinition={columnDefinition} />;
|
||||
} else if (isViewFieldURL(columnDefinition)) {
|
||||
return <GenericEditableURLCell columnDefinition={columnDefinition} />;
|
||||
} else if (isViewFieldDate(columnDefinition)) {
|
||||
return <GenericEditableDateCell columnDefinition={columnDefinition} />;
|
||||
} else if (isViewFieldNumber(columnDefinition)) {
|
||||
return <GenericEditableNumberCell columnDefinition={columnDefinition} />;
|
||||
} else if (isViewFieldBoolean(columnDefinition)) {
|
||||
return <GenericEditableBooleanCell columnDefinition={columnDefinition} />;
|
||||
} else if (isViewFieldChip(columnDefinition)) {
|
||||
return <GenericEditableChipCell columnDefinition={columnDefinition} />;
|
||||
} else if (isViewFieldMoney(columnDefinition)) {
|
||||
return <GenericEditableMoneyCell columnDefinition={columnDefinition} />;
|
||||
} else {
|
||||
console.warn(
|
||||
`Unknown field metadata type: ${columnDefinition.metadata.type} in GenericEditableCell`,
|
||||
);
|
||||
return <></>;
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,74 @@
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { FieldDisplay } from '@/ui/field/components/FieldDisplay';
|
||||
import { FieldInput } from '@/ui/field/components/FieldInput';
|
||||
import { FieldContext } from '@/ui/field/contexts/FieldContext';
|
||||
import { FieldInputEvent } from '@/ui/field/types/FieldInputEvent';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
|
||||
import { useMoveSoftFocus } from '../../hooks/useMoveSoftFocus';
|
||||
import { useTableCell } from '../hooks/useTableCell';
|
||||
|
||||
import { TableCellContainer } from './TableCellContainer';
|
||||
|
||||
export const TableCell = ({
|
||||
customHotkeyScope,
|
||||
}: {
|
||||
customHotkeyScope: HotkeyScope;
|
||||
}) => {
|
||||
const { fieldDefinition } = useContext(FieldContext);
|
||||
|
||||
const { closeTableCell } = useTableCell();
|
||||
|
||||
const { moveLeft, moveRight, moveDown } = useMoveSoftFocus();
|
||||
|
||||
const handleEnter: FieldInputEvent = (persistField) => {
|
||||
persistField();
|
||||
closeTableCell();
|
||||
moveDown();
|
||||
};
|
||||
|
||||
const handleSubmit: FieldInputEvent = (persistField) => {
|
||||
persistField();
|
||||
closeTableCell();
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
closeTableCell();
|
||||
};
|
||||
|
||||
const handleEscape = () => {
|
||||
closeTableCell();
|
||||
};
|
||||
|
||||
const handleTab: FieldInputEvent = (persistField) => {
|
||||
persistField();
|
||||
closeTableCell();
|
||||
moveRight();
|
||||
};
|
||||
|
||||
const handleShiftTab: FieldInputEvent = (persistField) => {
|
||||
persistField();
|
||||
closeTableCell();
|
||||
moveLeft();
|
||||
};
|
||||
|
||||
return (
|
||||
<TableCellContainer
|
||||
editHotkeyScope={customHotkeyScope}
|
||||
editModeContent={
|
||||
<FieldInput
|
||||
onCancel={handleCancel}
|
||||
onClickOutside={handleCancel}
|
||||
onEnter={handleEnter}
|
||||
onEscape={handleEscape}
|
||||
onShiftTab={handleShiftTab}
|
||||
onSubmit={handleSubmit}
|
||||
onTab={handleTab}
|
||||
/>
|
||||
}
|
||||
nonEditModeContent={<FieldDisplay />}
|
||||
useEditButton={fieldDefinition.useEditButton}
|
||||
></TableCellContainer>
|
||||
);
|
||||
};
|
||||
@ -1,19 +1,20 @@
|
||||
import { ReactElement, useState } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { useIsFieldInputOnly } from '@/ui/field/hooks/useIsFieldInputOnly';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
|
||||
import { CellHotkeyScopeContext } from '../../contexts/CellHotkeyScopeContext';
|
||||
import { TableHotkeyScope } from '../../types/TableHotkeyScope';
|
||||
import { useCurrentCellEditMode } from '../hooks/useCurrentCellEditMode';
|
||||
import { useEditableCell } from '../hooks/useEditableCell';
|
||||
import { useIsSoftFocusOnCurrentCell } from '../hooks/useIsSoftFocusOnCurrentCell';
|
||||
import { useSetSoftFocusOnCurrentCell } from '../hooks/useSetSoftFocusOnCurrentCell';
|
||||
import { useCurrentTableCellEditMode } from '../hooks/useCurrentTableCellEditMode';
|
||||
import { useIsSoftFocusOnCurrentTableCell } from '../hooks/useIsSoftFocusOnCurrentTableCell';
|
||||
import { useSetSoftFocusOnCurrentTableCell } from '../hooks/useSetSoftFocusOnCurrentTableCell';
|
||||
import { useTableCell } from '../hooks/useTableCell';
|
||||
|
||||
import { EditableCellDisplayMode } from './EditableCellDisplayMode';
|
||||
import { EditableCellEditButton } from './EditableCellEditButton';
|
||||
import { EditableCellEditMode } from './EditableCellEditMode';
|
||||
import { EditableCellSoftFocusMode } from './EditableCellSoftFocusMode';
|
||||
import { TableCellDisplayMode } from './TableCellDisplayMode';
|
||||
import { TableCellEditButton } from './TableCellEditButton';
|
||||
import { TableCellEditMode } from './TableCellEditMode';
|
||||
import { TableCellSoftFocusMode } from './TableCellSoftFocusMode';
|
||||
|
||||
const StyledCellBaseContainer = styled.div`
|
||||
align-items: center;
|
||||
@ -43,7 +44,7 @@ const DEFAULT_CELL_SCOPE: HotkeyScope = {
|
||||
scope: TableHotkeyScope.CellEditMode,
|
||||
};
|
||||
|
||||
export const EditableCell = ({
|
||||
export const TableCellContainer = ({
|
||||
editModeHorizontalAlign = 'left',
|
||||
editModeVerticalPosition = 'over',
|
||||
editModeContent,
|
||||
@ -53,16 +54,16 @@ export const EditableCell = ({
|
||||
maxContentWidth,
|
||||
useEditButton,
|
||||
}: EditableCellProps) => {
|
||||
const { isCurrentCellInEditMode } = useCurrentCellEditMode();
|
||||
const { isCurrentTableCellInEditMode } = useCurrentTableCellEditMode();
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
||||
const setSoftFocusOnCurrentCell = useSetSoftFocusOnCurrentCell();
|
||||
const setSoftFocusOnCurrentTableCell = useSetSoftFocusOnCurrentTableCell();
|
||||
|
||||
const { openEditableCell } = useEditableCell();
|
||||
const { openTableCell } = useTableCell();
|
||||
|
||||
const handlePenClick = () => {
|
||||
setSoftFocusOnCurrentCell();
|
||||
openEditableCell();
|
||||
setSoftFocusOnCurrentTableCell();
|
||||
openTableCell();
|
||||
};
|
||||
|
||||
const handleContainerMouseEnter = () => {
|
||||
@ -73,9 +74,15 @@ export const EditableCell = ({
|
||||
setIsHovered(false);
|
||||
};
|
||||
|
||||
const showEditButton = useEditButton && isHovered && !isCurrentCellInEditMode;
|
||||
const editModeContentOnly = useIsFieldInputOnly();
|
||||
|
||||
const hasSoftFocus = useIsSoftFocusOnCurrentCell();
|
||||
const showEditButton =
|
||||
useEditButton &&
|
||||
isHovered &&
|
||||
!isCurrentTableCellInEditMode &&
|
||||
!editModeContentOnly;
|
||||
|
||||
const hasSoftFocus = useIsSoftFocusOnCurrentTableCell();
|
||||
|
||||
return (
|
||||
<CellHotkeyScopeContext.Provider
|
||||
@ -85,32 +92,28 @@ export const EditableCell = ({
|
||||
onMouseEnter={handleContainerMouseEnter}
|
||||
onMouseLeave={handleContainerMouseLeave}
|
||||
>
|
||||
{isCurrentCellInEditMode ? (
|
||||
<EditableCellEditMode
|
||||
{isCurrentTableCellInEditMode ? (
|
||||
<TableCellEditMode
|
||||
maxContentWidth={maxContentWidth}
|
||||
transparent={transparent}
|
||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||
editModeVerticalPosition={editModeVerticalPosition}
|
||||
>
|
||||
{editModeContent}
|
||||
</EditableCellEditMode>
|
||||
</TableCellEditMode>
|
||||
) : hasSoftFocus ? (
|
||||
<>
|
||||
{showEditButton && (
|
||||
<EditableCellEditButton onClick={handlePenClick} />
|
||||
)}
|
||||
<EditableCellSoftFocusMode>
|
||||
{nonEditModeContent}
|
||||
</EditableCellSoftFocusMode>
|
||||
{showEditButton && <TableCellEditButton onClick={handlePenClick} />}
|
||||
<TableCellSoftFocusMode>
|
||||
{editModeContentOnly ? editModeContent : nonEditModeContent}
|
||||
</TableCellSoftFocusMode>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{showEditButton && (
|
||||
<EditableCellEditButton onClick={handlePenClick} />
|
||||
)}
|
||||
<EditableCellDisplayMode isHovered={isHovered}>
|
||||
{nonEditModeContent}
|
||||
</EditableCellDisplayMode>
|
||||
{showEditButton && <TableCellEditButton onClick={handlePenClick} />}
|
||||
<TableCellDisplayMode isHovered={isHovered}>
|
||||
{editModeContentOnly ? editModeContent : nonEditModeContent}
|
||||
</TableCellDisplayMode>
|
||||
</>
|
||||
)}
|
||||
</StyledCellBaseContainer>
|
||||
@ -36,7 +36,7 @@ const StyledEditableCellDisplayModeInnerContainer = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export const EditableCellDisplayContainer = ({
|
||||
export const TableCellDisplayContainer = ({
|
||||
children,
|
||||
softFocus,
|
||||
onClick,
|
||||
@ -0,0 +1,31 @@
|
||||
import { useIsFieldInputOnly } from '@/ui/field/hooks/useIsFieldInputOnly';
|
||||
|
||||
import { useSetSoftFocusOnCurrentTableCell } from '../hooks/useSetSoftFocusOnCurrentTableCell';
|
||||
import { useTableCell } from '../hooks/useTableCell';
|
||||
|
||||
import { TableCellDisplayContainer } from './TableCellDisplayContainer';
|
||||
|
||||
export const TableCellDisplayMode = ({
|
||||
children,
|
||||
isHovered,
|
||||
}: React.PropsWithChildren<unknown> & { isHovered?: boolean }) => {
|
||||
const setSoftFocusOnCurrentCell = useSetSoftFocusOnCurrentTableCell();
|
||||
|
||||
const isFieldInputOnly = useIsFieldInputOnly();
|
||||
|
||||
const { openTableCell } = useTableCell();
|
||||
|
||||
const handleClick = () => {
|
||||
setSoftFocusOnCurrentCell();
|
||||
|
||||
if (!isFieldInputOnly) {
|
||||
openTableCell();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<TableCellDisplayContainer isHovered={isHovered} onClick={handleClick}>
|
||||
{children}
|
||||
</TableCellDisplayContainer>
|
||||
);
|
||||
};
|
||||
@ -13,7 +13,7 @@ type EditableCellEditButtonProps = {
|
||||
onClick?: () => void;
|
||||
};
|
||||
|
||||
export const EditableCellEditButton = ({
|
||||
export const TableCellEditButton = ({
|
||||
onClick,
|
||||
}: EditableCellEditButtonProps) => (
|
||||
<StyledEditButtonContainer
|
||||
@ -37,7 +37,7 @@ export type EditableCellEditModeProps = {
|
||||
initialValue?: string;
|
||||
};
|
||||
|
||||
export const EditableCellEditMode = ({
|
||||
export const TableCellEditMode = ({
|
||||
editModeHorizontalAlign,
|
||||
editModeVerticalPosition,
|
||||
children,
|
||||
@ -1,17 +1,20 @@
|
||||
import { PropsWithChildren, useEffect, useRef } from 'react';
|
||||
|
||||
import { useIsFieldInputOnly } from '@/ui/field/hooks/useIsFieldInputOnly';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { isNonTextWritingKey } from '@/ui/utilities/hotkey/utils/isNonTextWritingKey';
|
||||
|
||||
import { TableHotkeyScope } from '../../types/TableHotkeyScope';
|
||||
import { useEditableCell } from '../hooks/useEditableCell';
|
||||
import { useTableCell } from '../hooks/useTableCell';
|
||||
|
||||
import { EditableCellDisplayContainer } from './EditableCellDisplayContainer';
|
||||
import { TableCellDisplayContainer } from './TableCellDisplayContainer';
|
||||
|
||||
type OwnProps = PropsWithChildren<unknown>;
|
||||
|
||||
export const EditableCellSoftFocusMode = ({ children }: OwnProps) => {
|
||||
const { openEditableCell } = useEditableCell();
|
||||
export const TableCellSoftFocusMode = ({ children }: OwnProps) => {
|
||||
const { openTableCell } = useTableCell();
|
||||
|
||||
const isFieldInputOnly = useIsFieldInputOnly();
|
||||
|
||||
const scrollRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
@ -19,17 +22,16 @@ export const EditableCellSoftFocusMode = ({ children }: OwnProps) => {
|
||||
scrollRef.current?.scrollIntoView({ block: 'nearest' });
|
||||
}, []);
|
||||
|
||||
const openEditMode = () => {
|
||||
openEditableCell();
|
||||
};
|
||||
|
||||
useScopedHotkeys(
|
||||
'enter',
|
||||
() => {
|
||||
openEditMode();
|
||||
openTableCell();
|
||||
},
|
||||
TableHotkeyScope.TableSoftFocus,
|
||||
[openEditMode],
|
||||
[openTableCell],
|
||||
{
|
||||
enabled: !isFieldInputOnly,
|
||||
},
|
||||
);
|
||||
|
||||
useScopedHotkeys(
|
||||
@ -44,26 +46,29 @@ export const EditableCellSoftFocusMode = ({ children }: OwnProps) => {
|
||||
return;
|
||||
}
|
||||
|
||||
openEditMode();
|
||||
openTableCell();
|
||||
},
|
||||
TableHotkeyScope.TableSoftFocus,
|
||||
[openEditMode],
|
||||
[openTableCell],
|
||||
{
|
||||
preventDefault: false,
|
||||
enabled: !isFieldInputOnly,
|
||||
},
|
||||
);
|
||||
|
||||
const handleClick = () => {
|
||||
openEditMode();
|
||||
if (!isFieldInputOnly) {
|
||||
openTableCell();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<EditableCellDisplayContainer
|
||||
<TableCellDisplayContainer
|
||||
onClick={handleClick}
|
||||
softFocus
|
||||
scrollRef={scrollRef}
|
||||
>
|
||||
{children}
|
||||
</EditableCellDisplayContainer>
|
||||
</TableCellDisplayContainer>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user