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>
|
||||
);
|
||||
};
|
||||
@ -1,23 +0,0 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { useMoveEditModeToCellPosition } from '../../hooks/useMoveEditModeToCellPosition';
|
||||
import { isCellInEditModeFamilyState } from '../../states/isCellInEditModeFamilyState';
|
||||
|
||||
import { useCurrentCellPosition } from './useCurrentCellPosition';
|
||||
|
||||
export const useCurrentCellEditMode = () => {
|
||||
const moveEditModeToCellPosition = useMoveEditModeToCellPosition();
|
||||
|
||||
const currentCellPosition = useCurrentCellPosition();
|
||||
|
||||
const [isCurrentCellInEditMode] = useRecoilState(
|
||||
isCellInEditModeFamilyState(currentCellPosition),
|
||||
);
|
||||
|
||||
const setCurrentCellInEditMode = useCallback(() => {
|
||||
moveEditModeToCellPosition(currentCellPosition);
|
||||
}, [currentCellPosition, moveEditModeToCellPosition]);
|
||||
|
||||
return { isCurrentCellInEditMode, setCurrentCellInEditMode };
|
||||
};
|
||||
@ -2,13 +2,13 @@ import { useContext, useMemo } from 'react';
|
||||
|
||||
import { ColumnIndexContext } from '../../contexts/ColumnIndexContext';
|
||||
import { RowIndexContext } from '../../contexts/RowIndexContext';
|
||||
import { CellPosition } from '../../types/CellPosition';
|
||||
import { TableCellPosition } from '../../types/TableCellPosition';
|
||||
|
||||
export const useCurrentCellPosition = () => {
|
||||
export const useCurrentTableCellPosition = () => {
|
||||
const currentRowNumber = useContext(RowIndexContext);
|
||||
const currentColumnNumber = useContext(ColumnIndexContext);
|
||||
|
||||
const currentCellPosition: CellPosition = useMemo(
|
||||
const currentTableCellPosition: TableCellPosition = useMemo(
|
||||
() => ({
|
||||
column: currentColumnNumber,
|
||||
row: currentRowNumber,
|
||||
@ -16,5 +16,5 @@ export const useCurrentCellPosition = () => {
|
||||
[currentColumnNumber, currentRowNumber],
|
||||
);
|
||||
|
||||
return currentCellPosition;
|
||||
return currentTableCellPosition;
|
||||
};
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { useMoveEditModeToTableCellPosition } from '../../hooks/useMoveEditModeToCellPosition';
|
||||
import { isTableCellInEditModeFamilyState } from '../../states/isTableCellInEditModeFamilyState';
|
||||
|
||||
import { useCurrentTableCellPosition } from './useCurrentCellPosition';
|
||||
|
||||
export const useCurrentTableCellEditMode = () => {
|
||||
const moveEditModeToTableCellPosition = useMoveEditModeToTableCellPosition();
|
||||
|
||||
const currentTableCellPosition = useCurrentTableCellPosition();
|
||||
|
||||
const [isCurrentTableCellInEditMode] = useRecoilState(
|
||||
isTableCellInEditModeFamilyState(currentTableCellPosition),
|
||||
);
|
||||
|
||||
const setCurrentTableCellInEditMode = useCallback(() => {
|
||||
moveEditModeToTableCellPosition(currentTableCellPosition);
|
||||
}, [currentTableCellPosition, moveEditModeToTableCellPosition]);
|
||||
|
||||
return {
|
||||
isCurrentTableCellInEditMode,
|
||||
setCurrentTableCellInEditMode,
|
||||
};
|
||||
};
|
||||
@ -1,15 +0,0 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { isSoftFocusOnCellFamilyState } from '../../states/isSoftFocusOnCellFamilyState';
|
||||
|
||||
import { useCurrentCellPosition } from './useCurrentCellPosition';
|
||||
|
||||
export const useIsSoftFocusOnCurrentCell = () => {
|
||||
const currentCellPosition = useCurrentCellPosition();
|
||||
|
||||
const isSoftFocusOnCell = useRecoilValue(
|
||||
isSoftFocusOnCellFamilyState(currentCellPosition),
|
||||
);
|
||||
|
||||
return isSoftFocusOnCell;
|
||||
};
|
||||
@ -0,0 +1,15 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { isSoftFocusOnTableCellFamilyState } from '../../states/isSoftFocusOnTableCellFamilyState';
|
||||
|
||||
import { useCurrentTableCellPosition } from './useCurrentCellPosition';
|
||||
|
||||
export const useIsSoftFocusOnCurrentTableCell = () => {
|
||||
const currentTableCellPosition = useCurrentTableCellPosition();
|
||||
|
||||
const isSoftFocusOnTableCell = useRecoilValue(
|
||||
isSoftFocusOnTableCellFamilyState(currentTableCellPosition),
|
||||
);
|
||||
|
||||
return isSoftFocusOnTableCell;
|
||||
};
|
||||
@ -1,75 +0,0 @@
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
|
||||
import { useMoveSoftFocus } from '../../hooks/useMoveSoftFocus';
|
||||
import { TableHotkeyScope } from '../../types/TableHotkeyScope';
|
||||
|
||||
import { useCurrentCellEditMode } from './useCurrentCellEditMode';
|
||||
import { useEditableCell } from './useEditableCell';
|
||||
|
||||
export const useRegisterCloseCellHandlers = (
|
||||
wrapperRef: React.RefObject<HTMLDivElement>,
|
||||
onSubmit?: () => void,
|
||||
onCancel?: () => void,
|
||||
) => {
|
||||
const { closeEditableCell } = useEditableCell();
|
||||
const { isCurrentCellInEditMode } = useCurrentCellEditMode();
|
||||
|
||||
useListenClickOutside({
|
||||
refs: [wrapperRef],
|
||||
callback: (event) => {
|
||||
if (isCurrentCellInEditMode) {
|
||||
event.stopImmediatePropagation();
|
||||
|
||||
onSubmit?.();
|
||||
|
||||
closeEditableCell();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const { moveRight, moveLeft, moveDown } = useMoveSoftFocus();
|
||||
|
||||
useScopedHotkeys(
|
||||
'enter',
|
||||
() => {
|
||||
onSubmit?.();
|
||||
closeEditableCell();
|
||||
moveDown();
|
||||
},
|
||||
TableHotkeyScope.CellEditMode,
|
||||
[closeEditableCell, onSubmit, moveDown],
|
||||
);
|
||||
|
||||
useScopedHotkeys(
|
||||
'esc',
|
||||
() => {
|
||||
closeEditableCell();
|
||||
onCancel?.();
|
||||
},
|
||||
TableHotkeyScope.CellEditMode,
|
||||
[closeEditableCell, onCancel],
|
||||
);
|
||||
|
||||
useScopedHotkeys(
|
||||
'tab',
|
||||
() => {
|
||||
onSubmit?.();
|
||||
closeEditableCell();
|
||||
moveRight();
|
||||
},
|
||||
TableHotkeyScope.CellEditMode,
|
||||
[closeEditableCell, onSubmit, moveRight],
|
||||
);
|
||||
|
||||
useScopedHotkeys(
|
||||
'shift+tab',
|
||||
() => {
|
||||
onSubmit?.();
|
||||
closeEditableCell();
|
||||
moveLeft();
|
||||
},
|
||||
TableHotkeyScope.CellEditMode,
|
||||
[closeEditableCell, onSubmit, moveRight],
|
||||
);
|
||||
};
|
||||
@ -6,24 +6,24 @@ import { useSetSoftFocusPosition } from '../../hooks/useSetSoftFocusPosition';
|
||||
import { isSoftFocusActiveState } from '../../states/isSoftFocusActiveState';
|
||||
import { TableHotkeyScope } from '../../types/TableHotkeyScope';
|
||||
|
||||
import { useCurrentCellPosition } from './useCurrentCellPosition';
|
||||
import { useCurrentTableCellPosition } from './useCurrentCellPosition';
|
||||
|
||||
export const useSetSoftFocusOnCurrentCell = () => {
|
||||
export const useSetSoftFocusOnCurrentTableCell = () => {
|
||||
const setSoftFocusPosition = useSetSoftFocusPosition();
|
||||
|
||||
const currentCellPosition = useCurrentCellPosition();
|
||||
const currentTableCellPosition = useCurrentTableCellPosition();
|
||||
|
||||
const setHotkeyScope = useSetHotkeyScope();
|
||||
|
||||
return useRecoilCallback(
|
||||
({ set }) =>
|
||||
() => {
|
||||
setSoftFocusPosition(currentCellPosition);
|
||||
setSoftFocusPosition(currentTableCellPosition);
|
||||
|
||||
set(isSoftFocusActiveState, true);
|
||||
|
||||
setHotkeyScope(TableHotkeyScope.TableSoftFocus);
|
||||
},
|
||||
[setHotkeyScope, currentCellPosition, setSoftFocusPosition],
|
||||
[setHotkeyScope, currentTableCellPosition, setSoftFocusPosition],
|
||||
);
|
||||
};
|
||||
@ -5,34 +5,34 @@ import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
|
||||
import { CellHotkeyScopeContext } from '../../contexts/CellHotkeyScopeContext';
|
||||
import { useCloseCurrentCellInEditMode } from '../../hooks/useClearCellInEditMode';
|
||||
import { useCloseCurrentTableCellInEditMode } from '../../hooks/useCloseCurrentTableCellInEditMode';
|
||||
import { TableHotkeyScope } from '../../types/TableHotkeyScope';
|
||||
|
||||
import { useCurrentCellEditMode } from './useCurrentCellEditMode';
|
||||
import { useCurrentTableCellEditMode } from './useCurrentTableCellEditMode';
|
||||
|
||||
const DEFAULT_CELL_SCOPE: HotkeyScope = {
|
||||
scope: TableHotkeyScope.CellEditMode,
|
||||
};
|
||||
|
||||
export const useEditableCell = () => {
|
||||
const { setCurrentCellInEditMode } = useCurrentCellEditMode();
|
||||
export const useTableCell = () => {
|
||||
const { setCurrentTableCellInEditMode } = useCurrentTableCellEditMode();
|
||||
|
||||
const setHotkeyScope = useSetHotkeyScope();
|
||||
const { setDragSelectionStartEnabled } = useDragSelect();
|
||||
|
||||
const closeCurrentCellInEditMode = useCloseCurrentCellInEditMode();
|
||||
const closeCurrentTableCellInEditMode = useCloseCurrentTableCellInEditMode();
|
||||
|
||||
const customCellHotkeyScope = useContext(CellHotkeyScopeContext);
|
||||
|
||||
const closeEditableCell = () => {
|
||||
const closeTableCell = () => {
|
||||
setDragSelectionStartEnabled(true);
|
||||
closeCurrentCellInEditMode();
|
||||
closeCurrentTableCellInEditMode();
|
||||
setHotkeyScope(TableHotkeyScope.TableSoftFocus);
|
||||
};
|
||||
|
||||
const openEditableCell = () => {
|
||||
const openTableCell = () => {
|
||||
setDragSelectionStartEnabled(false);
|
||||
setCurrentCellInEditMode();
|
||||
setCurrentTableCellInEditMode();
|
||||
|
||||
if (customCellHotkeyScope) {
|
||||
setHotkeyScope(
|
||||
@ -45,7 +45,7 @@ export const useEditableCell = () => {
|
||||
};
|
||||
|
||||
return {
|
||||
closeEditableCell,
|
||||
openEditableCell,
|
||||
closeTableCell,
|
||||
openTableCell,
|
||||
};
|
||||
};
|
||||
@ -1,141 +0,0 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { Key } from 'ts-key-enum';
|
||||
|
||||
import { DoubleTextInput } from '@/ui/input/components/DoubleTextInput';
|
||||
import { useEditableCell } from '@/ui/table/editable-cell/hooks/useEditableCell';
|
||||
import { useRegisterCloseCellHandlers } from '@/ui/table/editable-cell/hooks/useRegisterCloseCellHandlers';
|
||||
import { useMoveSoftFocus } from '@/ui/table/hooks/useMoveSoftFocus';
|
||||
import { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
|
||||
type OwnProps = {
|
||||
firstValue: string;
|
||||
secondValue: string;
|
||||
firstValuePlaceholder: string;
|
||||
secondValuePlaceholder: string;
|
||||
onChange?: (firstValue: string, secondValue: string) => void;
|
||||
onSubmit?: (firstValue: string, secondValue: string) => void;
|
||||
onCancel?: () => void;
|
||||
};
|
||||
|
||||
export const DoubleTextCellEdit = ({
|
||||
firstValue,
|
||||
secondValue,
|
||||
firstValuePlaceholder,
|
||||
secondValuePlaceholder,
|
||||
onSubmit,
|
||||
onCancel,
|
||||
}: OwnProps) => {
|
||||
const [firstInternalValue, setFirstInternalValue] = useState(firstValue);
|
||||
const [secondInternalValue, setSecondInternalValue] = useState(secondValue);
|
||||
|
||||
useEffect(() => {
|
||||
setFirstInternalValue(firstValue);
|
||||
setSecondInternalValue(secondValue);
|
||||
}, [firstValue, secondValue]);
|
||||
|
||||
const handleOnChange = (
|
||||
newFirstValue: string,
|
||||
newSecondValue: string,
|
||||
): void => {
|
||||
setFirstInternalValue(newFirstValue);
|
||||
setSecondInternalValue(newSecondValue);
|
||||
};
|
||||
|
||||
const [focusPosition, setFocusPosition] = useState<'left' | 'right'>('left');
|
||||
|
||||
const firstValueInputRef = useRef<HTMLInputElement>(null);
|
||||
const secondValueInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const { closeEditableCell } = useEditableCell();
|
||||
const { moveRight, moveLeft, moveDown } = useMoveSoftFocus();
|
||||
|
||||
const closeCell = () => {
|
||||
setFocusPosition('left');
|
||||
closeEditableCell();
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
setFirstInternalValue(firstValue);
|
||||
setSecondInternalValue(secondValue);
|
||||
|
||||
onCancel?.();
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
onSubmit?.(firstInternalValue, secondInternalValue);
|
||||
};
|
||||
|
||||
useScopedHotkeys(
|
||||
Key.Enter,
|
||||
() => {
|
||||
closeCell();
|
||||
moveDown();
|
||||
handleSubmit();
|
||||
},
|
||||
TableHotkeyScope.CellDoubleTextInput,
|
||||
[closeCell],
|
||||
);
|
||||
|
||||
useScopedHotkeys(
|
||||
Key.Escape,
|
||||
() => {
|
||||
handleCancel();
|
||||
closeCell();
|
||||
},
|
||||
TableHotkeyScope.CellDoubleTextInput,
|
||||
[closeCell],
|
||||
);
|
||||
|
||||
useScopedHotkeys(
|
||||
'tab',
|
||||
() => {
|
||||
if (focusPosition === 'left') {
|
||||
setFocusPosition('right');
|
||||
secondValueInputRef.current?.focus();
|
||||
} else {
|
||||
handleSubmit();
|
||||
|
||||
closeCell();
|
||||
moveRight();
|
||||
}
|
||||
},
|
||||
TableHotkeyScope.CellDoubleTextInput,
|
||||
[closeCell, moveRight, focusPosition],
|
||||
);
|
||||
|
||||
useScopedHotkeys(
|
||||
'shift+tab',
|
||||
() => {
|
||||
if (focusPosition === 'right') {
|
||||
setFocusPosition('left');
|
||||
firstValueInputRef.current?.focus();
|
||||
} else {
|
||||
handleSubmit();
|
||||
closeCell();
|
||||
moveLeft();
|
||||
}
|
||||
},
|
||||
TableHotkeyScope.CellDoubleTextInput,
|
||||
[closeCell, moveRight, focusPosition],
|
||||
);
|
||||
|
||||
const wrapperRef = useRef(null);
|
||||
|
||||
useRegisterCloseCellHandlers(wrapperRef, handleSubmit, handleCancel);
|
||||
|
||||
return (
|
||||
<DoubleTextInput
|
||||
{...{
|
||||
firstValue,
|
||||
secondValue,
|
||||
firstValuePlaceholder,
|
||||
secondValuePlaceholder,
|
||||
firstValueInputRef,
|
||||
secondValueInputRef,
|
||||
}}
|
||||
onChange={handleOnChange}
|
||||
containerRef={wrapperRef}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,64 +0,0 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { ViewFieldBooleanMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { BooleanInput } from '@/ui/input/components/BooleanInput';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
|
||||
import { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
import { EditableCellDisplayContainer } from '../../components/EditableCellDisplayContainer';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldBooleanMetadata>;
|
||||
editModeHorizontalAlign?: 'left' | 'right';
|
||||
};
|
||||
|
||||
const StyledCellBaseContainer = styled.div`
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
height: ${({ theme }) => theme.spacing(8)};
|
||||
position: relative;
|
||||
user-select: none;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export const GenericEditableBooleanCell = ({ columnDefinition }: OwnProps) => {
|
||||
const currentRowEntityId = useCurrentRowEntityId();
|
||||
|
||||
const [fieldValue, setFieldValue] = useRecoilState<boolean>(
|
||||
tableEntityFieldFamilySelector({
|
||||
entityId: currentRowEntityId ?? '',
|
||||
fieldName: columnDefinition.metadata.fieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
const updateField = useUpdateEntityField();
|
||||
|
||||
const handleClick = () => {
|
||||
const newValue = !fieldValue;
|
||||
|
||||
try {
|
||||
setFieldValue(newValue);
|
||||
|
||||
if (currentRowEntityId && updateField) {
|
||||
updateField(currentRowEntityId, columnDefinition, newValue);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
`In GenericEditableBooleanCellEditMode, Invalid value: ${newValue}, ${error}`,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledCellBaseContainer>
|
||||
<EditableCellDisplayContainer onClick={handleClick}>
|
||||
<BooleanInput value={fieldValue} />
|
||||
</EditableCellDisplayContainer>
|
||||
</StyledCellBaseContainer>
|
||||
);
|
||||
};
|
||||
@ -1,28 +0,0 @@
|
||||
import { ViewFieldChipMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
|
||||
|
||||
import { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
|
||||
import { GenericEditableChipCellDisplayMode } from './GenericEditableChipCellDisplayMode';
|
||||
import { GenericEditableChipCellEditMode } from './GenericEditableChipCellEditMode';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldChipMetadata>;
|
||||
editModeHorizontalAlign?: 'left' | 'right';
|
||||
placeholder?: string;
|
||||
};
|
||||
|
||||
export const GenericEditableChipCell = ({
|
||||
columnDefinition,
|
||||
editModeHorizontalAlign,
|
||||
}: OwnProps) => (
|
||||
<EditableCell
|
||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||
editModeContent={
|
||||
<GenericEditableChipCellEditMode columnDefinition={columnDefinition} />
|
||||
}
|
||||
nonEditModeContent={
|
||||
<GenericEditableChipCellDisplayMode columnDefinition={columnDefinition} />
|
||||
}
|
||||
></EditableCell>
|
||||
);
|
||||
@ -1,51 +0,0 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { CompanyChip } from '@/companies/components/CompanyChip';
|
||||
import { ViewFieldChipMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
import { getLogoUrlFromDomainName } from '~/utils';
|
||||
|
||||
import { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldChipMetadata>;
|
||||
};
|
||||
|
||||
export const GenericEditableChipCellDisplayMode = ({
|
||||
columnDefinition,
|
||||
}: OwnProps) => {
|
||||
const currentRowEntityId = useCurrentRowEntityId();
|
||||
|
||||
const content = useRecoilValue<any | null>(
|
||||
tableEntityFieldFamilySelector({
|
||||
entityId: currentRowEntityId ?? '',
|
||||
fieldName: columnDefinition.metadata.contentFieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
const chipUrl = useRecoilValue<any | null>(
|
||||
tableEntityFieldFamilySelector({
|
||||
entityId: currentRowEntityId ?? '',
|
||||
fieldName: columnDefinition.metadata.urlFieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
switch (columnDefinition.metadata.relationType) {
|
||||
case Entity.Company: {
|
||||
return (
|
||||
<CompanyChip
|
||||
id={currentRowEntityId ?? ''}
|
||||
name={content ?? ''}
|
||||
pictureUrl={getLogoUrlFromDomainName(chipUrl)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
default:
|
||||
console.warn(
|
||||
`Unknown relation type: "${columnDefinition.metadata.relationType}" in GenericEditableChipCellEditMode`,
|
||||
);
|
||||
return <> </>;
|
||||
}
|
||||
};
|
||||
@ -1,65 +0,0 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { ViewFieldChipMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { useCellInputEventHandlers } from '@/ui/table/hooks/useCellInputEventHandlers';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
import { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope';
|
||||
|
||||
import { TextInput } from '../../../../input/components/TextInput';
|
||||
import { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldChipMetadata>;
|
||||
};
|
||||
|
||||
export const GenericEditableChipCellEditMode = ({
|
||||
columnDefinition,
|
||||
}: OwnProps) => {
|
||||
const currentRowEntityId = useCurrentRowEntityId();
|
||||
|
||||
// TODO: we could use a hook that would return the field value with the right type
|
||||
const [fieldValue, setFieldValue] = useRecoilState<string>(
|
||||
tableEntityFieldFamilySelector({
|
||||
entityId: currentRowEntityId ?? '',
|
||||
fieldName: columnDefinition.metadata.contentFieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
const updateField = useUpdateEntityField();
|
||||
|
||||
const handleSubmit = (newText: string) => {
|
||||
if (newText === fieldValue) return;
|
||||
|
||||
setFieldValue(newText);
|
||||
|
||||
if (currentRowEntityId && updateField) {
|
||||
updateField(currentRowEntityId, columnDefinition, newText);
|
||||
}
|
||||
};
|
||||
|
||||
const {
|
||||
handleEnter,
|
||||
handleEscape,
|
||||
handleTab,
|
||||
handleShiftTab,
|
||||
handleClickOutside,
|
||||
} = useCellInputEventHandlers({
|
||||
onSubmit: handleSubmit,
|
||||
});
|
||||
|
||||
return (
|
||||
<TextInput
|
||||
placeholder={columnDefinition.metadata.placeHolder ?? ''}
|
||||
autoFocus
|
||||
value={fieldValue ?? ''}
|
||||
onClickOutside={handleClickOutside}
|
||||
onEnter={handleEnter}
|
||||
onEscape={handleEscape}
|
||||
onTab={handleTab}
|
||||
onShiftTab={handleShiftTab}
|
||||
hotkeyScope={TableHotkeyScope.CellEditMode}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,40 +0,0 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { DateDisplay } from '@/ui/content-display/components/DateDisplay';
|
||||
import { ViewFieldDateMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
|
||||
import { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
|
||||
import { GenericEditableDateCellEditMode } from './GenericEditableDateCellEditMode';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldDateMetadata>;
|
||||
editModeHorizontalAlign?: 'left' | 'right';
|
||||
};
|
||||
|
||||
export const GenericEditableDateCell = ({
|
||||
columnDefinition,
|
||||
editModeHorizontalAlign,
|
||||
}: OwnProps) => {
|
||||
const currentRowEntityId = useCurrentRowEntityId();
|
||||
|
||||
const fieldValue = useRecoilValue<string>(
|
||||
tableEntityFieldFamilySelector({
|
||||
entityId: currentRowEntityId ?? '',
|
||||
fieldName: columnDefinition.metadata.fieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
return (
|
||||
<EditableCell
|
||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||
editModeContent={
|
||||
<GenericEditableDateCellEditMode columnDefinition={columnDefinition} />
|
||||
}
|
||||
nonEditModeContent={<DateDisplay value={fieldValue} />}
|
||||
></EditableCell>
|
||||
);
|
||||
};
|
||||
@ -1,65 +0,0 @@
|
||||
import { DateTime } from 'luxon';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { ViewFieldDateMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { DateInput } from '@/ui/input/components/DateInput';
|
||||
import { useCellInputEventHandlers } from '@/ui/table/hooks/useCellInputEventHandlers';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
import { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope';
|
||||
import { Nullable } from '~/types/Nullable';
|
||||
|
||||
import { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldDateMetadata>;
|
||||
};
|
||||
|
||||
export const GenericEditableDateCellEditMode = ({
|
||||
columnDefinition,
|
||||
}: OwnProps) => {
|
||||
const currentRowEntityId = useCurrentRowEntityId();
|
||||
|
||||
// TODO: we could use a hook that would return the field value with the right type
|
||||
const [fieldValue, setFieldValue] = useRecoilState<string>(
|
||||
tableEntityFieldFamilySelector({
|
||||
entityId: currentRowEntityId ?? '',
|
||||
fieldName: columnDefinition.metadata.fieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
const updateField = useUpdateEntityField();
|
||||
|
||||
// Wrap this into a hook
|
||||
const handleSubmit = (newDate: Nullable<Date>) => {
|
||||
const fieldValueDate = fieldValue
|
||||
? DateTime.fromISO(fieldValue).toJSDate()
|
||||
: null;
|
||||
|
||||
const newDateISO = newDate ? DateTime.fromJSDate(newDate).toISO() : null;
|
||||
|
||||
if (newDate === fieldValueDate || !newDateISO) return;
|
||||
|
||||
setFieldValue(newDateISO);
|
||||
|
||||
if (currentRowEntityId && updateField && newDateISO) {
|
||||
updateField(currentRowEntityId, columnDefinition, newDateISO);
|
||||
}
|
||||
};
|
||||
|
||||
const { handleEnter, handleEscape, handleClickOutside } =
|
||||
useCellInputEventHandlers({
|
||||
onSubmit: handleSubmit,
|
||||
});
|
||||
|
||||
return (
|
||||
<DateInput
|
||||
value={DateTime.fromISO(fieldValue).toJSDate()}
|
||||
onClickOutside={handleClickOutside}
|
||||
onEnter={handleEnter}
|
||||
onEscape={handleEscape}
|
||||
hotkeyScope={TableHotkeyScope.CellEditMode}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,48 +0,0 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { DoubleTextDisplay } from '@/ui/content-display/components/DoubleTextDisplay';
|
||||
import { ViewFieldDoubleTextMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
|
||||
import { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
|
||||
import { GenericEditableDoubleTextCellEditMode } from './GenericEditableDoubleTextCellEditMode';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldDoubleTextMetadata>;
|
||||
};
|
||||
|
||||
export const GenericEditableDoubleTextCell = ({
|
||||
columnDefinition,
|
||||
}: OwnProps) => {
|
||||
const currentRowEntityId = useCurrentRowEntityId();
|
||||
|
||||
const firstValue = useRecoilValue<string>(
|
||||
tableEntityFieldFamilySelector({
|
||||
entityId: currentRowEntityId ?? '',
|
||||
fieldName: columnDefinition.metadata.firstValueFieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
const secondValue = useRecoilValue<string>(
|
||||
tableEntityFieldFamilySelector({
|
||||
entityId: currentRowEntityId ?? '',
|
||||
fieldName: columnDefinition.metadata.secondValueFieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
const displayName = `${firstValue ?? ''} ${secondValue ?? ''}`;
|
||||
|
||||
return (
|
||||
<EditableCell
|
||||
editModeContent={
|
||||
<GenericEditableDoubleTextCellEditMode
|
||||
columnDefinition={columnDefinition}
|
||||
/>
|
||||
}
|
||||
nonEditModeContent={<DoubleTextDisplay text={displayName} />}
|
||||
></EditableCell>
|
||||
);
|
||||
};
|
||||
@ -1,61 +0,0 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { ViewFieldDoubleTextMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
|
||||
import { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
|
||||
import { DoubleTextCellEdit } from './DoubleTextCellEdit';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldDoubleTextMetadata>;
|
||||
};
|
||||
|
||||
export const GenericEditableDoubleTextCellEditMode = ({
|
||||
columnDefinition,
|
||||
}: OwnProps) => {
|
||||
const currentRowEntityId = useCurrentRowEntityId();
|
||||
|
||||
// TODO: we could use a hook that would return the field value with the right type
|
||||
const [firstValue, setFirstValue] = useRecoilState<string>(
|
||||
tableEntityFieldFamilySelector({
|
||||
entityId: currentRowEntityId ?? '',
|
||||
fieldName: columnDefinition.metadata.firstValueFieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
const [secondValue, setSecondValue] = useRecoilState<string>(
|
||||
tableEntityFieldFamilySelector({
|
||||
entityId: currentRowEntityId ?? '',
|
||||
fieldName: columnDefinition.metadata.secondValueFieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
const updateField = useUpdateEntityField();
|
||||
|
||||
const handleSubmit = (newFirstValue: string, newSecondValue: string) => {
|
||||
if (newFirstValue === firstValue && newSecondValue === secondValue) return;
|
||||
|
||||
setFirstValue(newFirstValue);
|
||||
setSecondValue(newSecondValue);
|
||||
|
||||
if (currentRowEntityId && updateField) {
|
||||
updateField(currentRowEntityId, columnDefinition, {
|
||||
firstValue: newFirstValue,
|
||||
secondValue: newSecondValue,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<DoubleTextCellEdit
|
||||
firstValuePlaceholder={columnDefinition.metadata.firstValuePlaceholder}
|
||||
secondValuePlaceholder={columnDefinition.metadata.secondValuePlaceholder}
|
||||
firstValue={firstValue ?? ''}
|
||||
secondValue={secondValue ?? ''}
|
||||
onSubmit={handleSubmit}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,30 +0,0 @@
|
||||
import { ViewFieldDoubleTextChipMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
|
||||
import { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope';
|
||||
|
||||
import { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
|
||||
import { GenericEditableDoubleTextChipCellDisplayMode } from './GenericEditableDoubleTextChipCellDisplayMode';
|
||||
import { GenericEditableDoubleTextChipCellEditMode } from './GenericEditableDoubleTextChipCellEditMode';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldDoubleTextChipMetadata>;
|
||||
};
|
||||
|
||||
export const GenericEditableDoubleTextChipCell = ({
|
||||
columnDefinition,
|
||||
}: OwnProps) => (
|
||||
<EditableCell
|
||||
editHotkeyScope={{ scope: TableHotkeyScope.CellDoubleTextInput }}
|
||||
editModeContent={
|
||||
<GenericEditableDoubleTextChipCellEditMode
|
||||
columnDefinition={columnDefinition}
|
||||
/>
|
||||
}
|
||||
nonEditModeContent={
|
||||
<GenericEditableDoubleTextChipCellDisplayMode
|
||||
columnDefinition={columnDefinition}
|
||||
/>
|
||||
}
|
||||
></EditableCell>
|
||||
);
|
||||
@ -1,50 +0,0 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { DoubleTextChipDisplay } from '@/ui/content-display/components/DoubleTextChipDisplay';
|
||||
import { ViewFieldDoubleTextChipMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
|
||||
import { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldDoubleTextChipMetadata>;
|
||||
};
|
||||
|
||||
export const GenericEditableDoubleTextChipCellDisplayMode = ({
|
||||
columnDefinition,
|
||||
}: OwnProps) => {
|
||||
const currentRowEntityId = useCurrentRowEntityId();
|
||||
|
||||
const [firstValue] = useRecoilState<string>(
|
||||
tableEntityFieldFamilySelector({
|
||||
entityId: currentRowEntityId ?? '',
|
||||
fieldName: columnDefinition.metadata.firstValueFieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
const [secondValue] = useRecoilState<string>(
|
||||
tableEntityFieldFamilySelector({
|
||||
entityId: currentRowEntityId ?? '',
|
||||
fieldName: columnDefinition.metadata.secondValueFieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
const [avatarUrlValue] = useRecoilState<string>(
|
||||
tableEntityFieldFamilySelector({
|
||||
entityId: currentRowEntityId ?? '',
|
||||
fieldName: columnDefinition.metadata.avatarUrlFieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
const displayName = [firstValue, secondValue].filter(Boolean).join(' ');
|
||||
|
||||
return (
|
||||
<DoubleTextChipDisplay
|
||||
entityType={columnDefinition.metadata.entityType}
|
||||
displayName={displayName}
|
||||
entityId={currentRowEntityId}
|
||||
avatarUrlValue={avatarUrlValue}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,71 +0,0 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { ViewFieldDoubleTextChipMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
|
||||
import { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
|
||||
import { DoubleTextCellEdit } from './DoubleTextCellEdit';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldDoubleTextChipMetadata>;
|
||||
};
|
||||
|
||||
export const GenericEditableDoubleTextChipCellEditMode = ({
|
||||
columnDefinition,
|
||||
}: OwnProps) => {
|
||||
const currentRowEntityId = useCurrentRowEntityId();
|
||||
|
||||
// TODO: we could use a hook that would return the field value with the right type
|
||||
const [firstValue, setFirstValue] = useRecoilState<string>(
|
||||
tableEntityFieldFamilySelector({
|
||||
entityId: currentRowEntityId ?? '',
|
||||
fieldName: columnDefinition.metadata.firstValueFieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
const [secondValue, setSecondValue] = useRecoilState<string>(
|
||||
tableEntityFieldFamilySelector({
|
||||
entityId: currentRowEntityId ?? '',
|
||||
fieldName: columnDefinition.metadata.secondValueFieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
const updateField = useUpdateEntityField();
|
||||
|
||||
const handleSubmit = (newFirstValue: string, newSecondValue: string) => {
|
||||
const firstValueChanged = newFirstValue !== firstValue;
|
||||
const secondValueChanged = newSecondValue !== secondValue;
|
||||
|
||||
if (firstValueChanged) {
|
||||
setFirstValue(newFirstValue);
|
||||
}
|
||||
|
||||
if (secondValueChanged) {
|
||||
setSecondValue(newSecondValue);
|
||||
}
|
||||
|
||||
if (
|
||||
currentRowEntityId &&
|
||||
updateField &&
|
||||
(firstValueChanged || secondValueChanged)
|
||||
) {
|
||||
updateField(currentRowEntityId, columnDefinition, {
|
||||
firstValue: firstValueChanged ? newFirstValue : firstValue,
|
||||
secondValue: secondValueChanged ? newSecondValue : secondValue,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<DoubleTextCellEdit
|
||||
firstValuePlaceholder={columnDefinition.metadata.firstValuePlaceholder}
|
||||
secondValuePlaceholder={columnDefinition.metadata.secondValuePlaceholder}
|
||||
firstValue={firstValue ?? ''}
|
||||
secondValue={secondValue ?? ''}
|
||||
onSubmit={handleSubmit}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,40 +0,0 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { EmailDisplay } from '@/ui/content-display/components/EmailDisplay';
|
||||
import { ViewFieldEmailMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
|
||||
import { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
|
||||
import { GenericEditableEmailCellEditMode } from './GenericEditableEmailCellEditMode';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldEmailMetadata>;
|
||||
editModeHorizontalAlign?: 'left' | 'right';
|
||||
};
|
||||
|
||||
export const GenericEditableEmailCell = ({
|
||||
columnDefinition,
|
||||
editModeHorizontalAlign,
|
||||
}: OwnProps) => {
|
||||
const currentRowEntityId = useCurrentRowEntityId();
|
||||
|
||||
const fieldValue = useRecoilValue<string>(
|
||||
tableEntityFieldFamilySelector({
|
||||
entityId: currentRowEntityId ?? '',
|
||||
fieldName: columnDefinition.metadata.fieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
return (
|
||||
<EditableCell
|
||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||
editModeContent={
|
||||
<GenericEditableEmailCellEditMode columnDefinition={columnDefinition} />
|
||||
}
|
||||
nonEditModeContent={<EmailDisplay value={fieldValue} />}
|
||||
></EditableCell>
|
||||
);
|
||||
};
|
||||
@ -1,65 +0,0 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { ViewFieldEmailMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { useCellInputEventHandlers } from '@/ui/table/hooks/useCellInputEventHandlers';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
import { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope';
|
||||
|
||||
import { TextInput } from '../../../../input/components/TextInput';
|
||||
import { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldEmailMetadata>;
|
||||
};
|
||||
|
||||
export const GenericEditableEmailCellEditMode = ({
|
||||
columnDefinition,
|
||||
}: OwnProps) => {
|
||||
const currentRowEntityId = useCurrentRowEntityId();
|
||||
|
||||
// TODO: we could use a hook that would return the field value with the right type
|
||||
const [fieldValue, setFieldValue] = useRecoilState<string>(
|
||||
tableEntityFieldFamilySelector({
|
||||
entityId: currentRowEntityId ?? '',
|
||||
fieldName: columnDefinition.metadata.fieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
const updateField = useUpdateEntityField();
|
||||
|
||||
const handleSubmit = (newEmail: string) => {
|
||||
if (newEmail === fieldValue) return;
|
||||
|
||||
setFieldValue(newEmail);
|
||||
|
||||
if (currentRowEntityId && updateField) {
|
||||
updateField(currentRowEntityId, columnDefinition, newEmail);
|
||||
}
|
||||
};
|
||||
|
||||
const {
|
||||
handleEnter,
|
||||
handleEscape,
|
||||
handleTab,
|
||||
handleShiftTab,
|
||||
handleClickOutside,
|
||||
} = useCellInputEventHandlers({
|
||||
onSubmit: handleSubmit,
|
||||
});
|
||||
|
||||
return (
|
||||
<TextInput
|
||||
placeholder={columnDefinition.metadata.placeHolder ?? ''}
|
||||
autoFocus
|
||||
value={fieldValue ?? ''}
|
||||
onClickOutside={handleClickOutside}
|
||||
onEnter={handleEnter}
|
||||
onEscape={handleEscape}
|
||||
onTab={handleTab}
|
||||
onShiftTab={handleShiftTab}
|
||||
hotkeyScope={TableHotkeyScope.CellEditMode}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,40 +0,0 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { MoneyDisplay } from '@/ui/content-display/components/MoneyDisplay';
|
||||
import { ViewFieldMoneyMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
|
||||
import { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
|
||||
import { GenericEditableMoneyCellEditMode } from './GenericEditableMoneyCellEditMode';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldMoneyMetadata>;
|
||||
editModeHorizontalAlign?: 'left' | 'right';
|
||||
};
|
||||
|
||||
export const GenericEditableMoneyCell = ({
|
||||
columnDefinition,
|
||||
editModeHorizontalAlign,
|
||||
}: OwnProps) => {
|
||||
const currentRowEntityId = useCurrentRowEntityId();
|
||||
|
||||
const fieldValue = useRecoilValue<number>(
|
||||
tableEntityFieldFamilySelector({
|
||||
entityId: currentRowEntityId ?? '',
|
||||
fieldName: columnDefinition.metadata.fieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
return (
|
||||
<EditableCell
|
||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||
editModeContent={
|
||||
<GenericEditableMoneyCellEditMode columnDefinition={columnDefinition} />
|
||||
}
|
||||
nonEditModeContent={<MoneyDisplay value={fieldValue} />}
|
||||
></EditableCell>
|
||||
);
|
||||
};
|
||||
@ -1,81 +0,0 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { ViewFieldMoneyMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { useCellInputEventHandlers } from '@/ui/table/hooks/useCellInputEventHandlers';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
import { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope';
|
||||
|
||||
import { TextInput } from '../../../../input/components/TextInput';
|
||||
import { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldMoneyMetadata>;
|
||||
};
|
||||
|
||||
export const GenericEditableMoneyCellEditMode = ({
|
||||
columnDefinition,
|
||||
}: OwnProps) => {
|
||||
const currentRowEntityId = useCurrentRowEntityId();
|
||||
|
||||
const [fieldValue, setFieldValue] = useRecoilState<string>(
|
||||
tableEntityFieldFamilySelector({
|
||||
entityId: currentRowEntityId ?? '',
|
||||
fieldName: columnDefinition.metadata.fieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
const updateField = useUpdateEntityField();
|
||||
|
||||
// TODO: handle this logic in a number input
|
||||
const handleSubmit = (newText: string) => {
|
||||
if (newText === fieldValue) return;
|
||||
|
||||
try {
|
||||
const numberValue = newText !== '' ? parseInt(newText) : null;
|
||||
|
||||
if (numberValue && isNaN(numberValue)) {
|
||||
throw new Error('Not a number');
|
||||
}
|
||||
|
||||
if (numberValue && numberValue > 2000000000) {
|
||||
throw new Error('Number too big');
|
||||
}
|
||||
|
||||
setFieldValue(numberValue ? numberValue.toString() : '');
|
||||
|
||||
if (currentRowEntityId && updateField) {
|
||||
updateField(currentRowEntityId, columnDefinition, numberValue);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
`In GenericEditableMoneyCellEditMode, Invalid number: ${newText}, ${error}`,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const {
|
||||
handleEnter,
|
||||
handleEscape,
|
||||
handleTab,
|
||||
handleShiftTab,
|
||||
handleClickOutside,
|
||||
} = useCellInputEventHandlers({
|
||||
onSubmit: handleSubmit,
|
||||
});
|
||||
|
||||
// TODO: use a number input
|
||||
return (
|
||||
<TextInput
|
||||
autoFocus
|
||||
value={fieldValue ?? ''}
|
||||
onClickOutside={handleClickOutside}
|
||||
onEnter={handleEnter}
|
||||
onEscape={handleEscape}
|
||||
onTab={handleTab}
|
||||
onShiftTab={handleShiftTab}
|
||||
hotkeyScope={TableHotkeyScope.CellEditMode}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,42 +0,0 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { NumberDisplay } from '@/ui/content-display/components/NumberDisplay';
|
||||
import { ViewFieldNumberMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
|
||||
import { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
|
||||
import { GenericEditableNumberCellEditMode } from './GenericEditableNumberCellEditMode';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldNumberMetadata>;
|
||||
editModeHorizontalAlign?: 'left' | 'right';
|
||||
};
|
||||
|
||||
export const GenericEditableNumberCell = ({
|
||||
columnDefinition,
|
||||
editModeHorizontalAlign,
|
||||
}: OwnProps) => {
|
||||
const currentRowEntityId = useCurrentRowEntityId();
|
||||
|
||||
const fieldValue = useRecoilValue<string>(
|
||||
tableEntityFieldFamilySelector({
|
||||
entityId: currentRowEntityId ?? '',
|
||||
fieldName: columnDefinition.metadata.fieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
return (
|
||||
<EditableCell
|
||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||
editModeContent={
|
||||
<GenericEditableNumberCellEditMode
|
||||
columnDefinition={columnDefinition}
|
||||
/>
|
||||
}
|
||||
nonEditModeContent={<NumberDisplay value={fieldValue} />}
|
||||
></EditableCell>
|
||||
);
|
||||
};
|
||||
@ -1,100 +0,0 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { ViewFieldNumberMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { useCellInputEventHandlers } from '@/ui/table/hooks/useCellInputEventHandlers';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
import { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope';
|
||||
import {
|
||||
canBeCastAsPositiveIntegerOrNull,
|
||||
castAsPositiveIntegerOrNull,
|
||||
} from '~/utils/cast-as-positive-integer-or-null';
|
||||
|
||||
import { TextInput } from '../../../../input/components/TextInput';
|
||||
import { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldNumberMetadata>;
|
||||
};
|
||||
|
||||
export const GenericEditableNumberCellEditMode = ({
|
||||
columnDefinition,
|
||||
}: OwnProps) => {
|
||||
const currentRowEntityId = useCurrentRowEntityId();
|
||||
|
||||
// TODO: we could use a hook that would return the field value with the right type
|
||||
const [fieldValue, setFieldValue] = useRecoilState<string>(
|
||||
tableEntityFieldFamilySelector({
|
||||
entityId: currentRowEntityId ?? '',
|
||||
fieldName: columnDefinition.metadata.fieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
const updateField = useUpdateEntityField();
|
||||
|
||||
const handleSubmit = (newText: string) => {
|
||||
if (newText === fieldValue) return;
|
||||
|
||||
try {
|
||||
let numberValue = parseInt(newText);
|
||||
|
||||
if (isNaN(numberValue)) {
|
||||
throw new Error('Not a number');
|
||||
}
|
||||
|
||||
if (columnDefinition.metadata.isPositive) {
|
||||
if (!canBeCastAsPositiveIntegerOrNull(newText)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const valueCastedAsPositiveNumberOrNull =
|
||||
castAsPositiveIntegerOrNull(newText);
|
||||
|
||||
if (valueCastedAsPositiveNumberOrNull === null) {
|
||||
throw Error('Not a number');
|
||||
}
|
||||
|
||||
numberValue = valueCastedAsPositiveNumberOrNull;
|
||||
}
|
||||
|
||||
// TODO: find a way to store this better in DB
|
||||
if (numberValue > 2000000000) {
|
||||
throw new Error('Number too big');
|
||||
}
|
||||
|
||||
setFieldValue(numberValue.toString());
|
||||
|
||||
if (currentRowEntityId && updateField) {
|
||||
updateField(currentRowEntityId, columnDefinition, numberValue);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
`In GenericEditableNumberCellEditMode, Invalid number: ${newText}, ${error}`,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const {
|
||||
handleEnter,
|
||||
handleEscape,
|
||||
handleTab,
|
||||
handleShiftTab,
|
||||
handleClickOutside,
|
||||
} = useCellInputEventHandlers({
|
||||
onSubmit: handleSubmit,
|
||||
});
|
||||
|
||||
return (
|
||||
<TextInput
|
||||
autoFocus
|
||||
value={fieldValue ?? ''}
|
||||
onClickOutside={handleClickOutside}
|
||||
onEnter={handleEnter}
|
||||
onEscape={handleEscape}
|
||||
onTab={handleTab}
|
||||
onShiftTab={handleShiftTab}
|
||||
hotkeyScope={TableHotkeyScope.CellEditMode}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,41 +0,0 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { PhoneDisplay } from '@/ui/content-display/components/PhoneDisplay';
|
||||
import { ViewFieldPhoneMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
|
||||
import { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
|
||||
import { GenericEditablePhoneCellEditMode } from './GenericEditablePhoneCellEditMode';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldPhoneMetadata>;
|
||||
editModeHorizontalAlign?: 'left' | 'right';
|
||||
};
|
||||
|
||||
export const GenericEditablePhoneCell = ({
|
||||
columnDefinition,
|
||||
editModeHorizontalAlign,
|
||||
}: OwnProps) => {
|
||||
const currentRowEntityId = useCurrentRowEntityId();
|
||||
|
||||
const fieldValue = useRecoilValue<string>(
|
||||
tableEntityFieldFamilySelector({
|
||||
entityId: currentRowEntityId ?? '',
|
||||
fieldName: columnDefinition.metadata.fieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
return (
|
||||
<EditableCell
|
||||
useEditButton
|
||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||
editModeContent={
|
||||
<GenericEditablePhoneCellEditMode columnDefinition={columnDefinition} />
|
||||
}
|
||||
nonEditModeContent={<PhoneDisplay value={fieldValue} />}
|
||||
></EditableCell>
|
||||
);
|
||||
};
|
||||
@ -1,68 +0,0 @@
|
||||
import { isPossiblePhoneNumber } from 'libphonenumber-js';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { ViewFieldPhoneMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { useCellInputEventHandlers } from '@/ui/table/hooks/useCellInputEventHandlers';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
import { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope';
|
||||
|
||||
import { PhoneInput } from '../../../../input/components/PhoneInput';
|
||||
import { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldPhoneMetadata>;
|
||||
};
|
||||
|
||||
export const GenericEditablePhoneCellEditMode = ({
|
||||
columnDefinition,
|
||||
}: OwnProps) => {
|
||||
const currentRowEntityId = useCurrentRowEntityId();
|
||||
|
||||
// TODO: we could use a hook that would return the field value with the right type
|
||||
const [fieldValue, setFieldValue] = useRecoilState<string>(
|
||||
tableEntityFieldFamilySelector({
|
||||
entityId: currentRowEntityId ?? '',
|
||||
fieldName: columnDefinition.metadata.fieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
const updateField = useUpdateEntityField();
|
||||
|
||||
const handleSubmit = (newValue: string) => {
|
||||
if (!isPossiblePhoneNumber(newValue)) return;
|
||||
|
||||
if (newValue === fieldValue) return;
|
||||
|
||||
setFieldValue(newValue);
|
||||
|
||||
if (currentRowEntityId && updateField) {
|
||||
updateField(currentRowEntityId, columnDefinition, newValue);
|
||||
}
|
||||
};
|
||||
|
||||
const {
|
||||
handleEnter,
|
||||
handleEscape,
|
||||
handleTab,
|
||||
handleShiftTab,
|
||||
handleClickOutside,
|
||||
} = useCellInputEventHandlers({
|
||||
onSubmit: handleSubmit,
|
||||
});
|
||||
|
||||
return (
|
||||
<PhoneInput
|
||||
placeholder={columnDefinition.metadata.placeHolder ?? ''}
|
||||
autoFocus
|
||||
value={fieldValue ?? ''}
|
||||
onClickOutside={handleClickOutside}
|
||||
onEnter={handleEnter}
|
||||
onEscape={handleEscape}
|
||||
onShiftTab={handleShiftTab}
|
||||
onTab={handleTab}
|
||||
hotkeyScope={TableHotkeyScope.CellEditMode}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,38 +0,0 @@
|
||||
import { ViewFieldRelationMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope';
|
||||
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
|
||||
|
||||
import { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
|
||||
import { GenericEditableRelationCellDisplayMode } from './GenericEditableRelationCellDisplayMode';
|
||||
import { GenericEditableRelationCellEditMode } from './GenericEditableRelationCellEditMode';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldRelationMetadata>;
|
||||
editModeHorizontalAlign?: 'left' | 'right';
|
||||
placeholder?: string;
|
||||
};
|
||||
|
||||
export const GenericEditableRelationCell = ({
|
||||
columnDefinition,
|
||||
editModeHorizontalAlign,
|
||||
placeholder,
|
||||
}: OwnProps) => (
|
||||
<EditableCell
|
||||
maxContentWidth={160}
|
||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||
editHotkeyScope={{ scope: RelationPickerHotkeyScope.RelationPicker }}
|
||||
editModeContent={
|
||||
<GenericEditableRelationCellEditMode
|
||||
columnDefinition={columnDefinition}
|
||||
/>
|
||||
}
|
||||
nonEditModeContent={
|
||||
<GenericEditableRelationCellDisplayMode
|
||||
columnDefinition={columnDefinition}
|
||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
}
|
||||
></EditableCell>
|
||||
);
|
||||
@ -1,57 +0,0 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { CompanyChip } from '@/companies/components/CompanyChip';
|
||||
import { ViewFieldRelationMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
import { UserChip } from '@/users/components/UserChip';
|
||||
import { getLogoUrlFromDomainName } from '~/utils';
|
||||
|
||||
import { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldRelationMetadata>;
|
||||
editModeHorizontalAlign?: 'left' | 'right';
|
||||
placeholder?: string;
|
||||
};
|
||||
|
||||
export const GenericEditableRelationCellDisplayMode = ({
|
||||
columnDefinition,
|
||||
}: OwnProps) => {
|
||||
const currentRowEntityId = useCurrentRowEntityId();
|
||||
|
||||
// TODO: type value with generic getter
|
||||
const fieldValue = useRecoilValue<any | null>(
|
||||
tableEntityFieldFamilySelector({
|
||||
entityId: currentRowEntityId ?? '',
|
||||
fieldName: columnDefinition.metadata.fieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
switch (columnDefinition.metadata.relationType) {
|
||||
case Entity.Company: {
|
||||
return (
|
||||
<CompanyChip
|
||||
id={fieldValue?.id ?? ''}
|
||||
name={fieldValue?.name ?? ''}
|
||||
pictureUrl={getLogoUrlFromDomainName(fieldValue?.domainName)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case Entity.User: {
|
||||
return (
|
||||
<UserChip
|
||||
id={fieldValue?.id ?? ''}
|
||||
name={fieldValue?.displayName ?? ''}
|
||||
pictureUrl={fieldValue?.avatarUrl ?? ''}
|
||||
/>
|
||||
);
|
||||
}
|
||||
default:
|
||||
console.warn(
|
||||
`Unknown relation type: "${columnDefinition.metadata.relationType}" in GenericEditableRelationCellEditMode`,
|
||||
);
|
||||
return <> </>;
|
||||
}
|
||||
};
|
||||
@ -1,120 +0,0 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import {
|
||||
CompanyPickerCell,
|
||||
CompanyPickerSelectedCompany,
|
||||
} from '@/companies/components/CompanyPickerCell';
|
||||
import { ViewFieldRelationMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
|
||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||
import { useEditableCell } from '@/ui/table/editable-cell/hooks/useEditableCell';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
import { UserPicker } from '@/users/components/UserPicker';
|
||||
|
||||
import { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldRelationMetadata>;
|
||||
};
|
||||
|
||||
export const GenericEditableRelationCellEditMode = ({
|
||||
columnDefinition,
|
||||
}: OwnProps) => {
|
||||
const currentRowEntityId = useCurrentRowEntityId();
|
||||
|
||||
const { closeEditableCell } = useEditableCell();
|
||||
|
||||
const [fieldValueEntity, setFieldValueEntity] = useRecoilState<any | null>(
|
||||
tableEntityFieldFamilySelector({
|
||||
entityId: currentRowEntityId ?? '',
|
||||
fieldName: columnDefinition.metadata.fieldName,
|
||||
}),
|
||||
);
|
||||
const updateEntityField = useUpdateEntityField();
|
||||
|
||||
const updateCachedPersonField = (newFieldEntity: EntityForSelect | null) => {
|
||||
setFieldValueEntity({
|
||||
avatarUrl: newFieldEntity?.avatarUrl ?? '',
|
||||
entityType: Entity.Company,
|
||||
id: newFieldEntity?.id ?? '',
|
||||
displayName: newFieldEntity?.name ?? '',
|
||||
});
|
||||
};
|
||||
|
||||
const updateCachedCompanyField = (
|
||||
newFieldEntity: CompanyPickerSelectedCompany | null,
|
||||
) => {
|
||||
setFieldValueEntity({
|
||||
id: newFieldEntity?.id ?? '',
|
||||
name: newFieldEntity?.name ?? '',
|
||||
domainName: newFieldEntity?.domainName ?? '',
|
||||
});
|
||||
};
|
||||
|
||||
const handleCompanySubmit = (
|
||||
newFieldEntity: CompanyPickerSelectedCompany | null,
|
||||
) => {
|
||||
if (
|
||||
newFieldEntity?.id !== fieldValueEntity?.id &&
|
||||
currentRowEntityId &&
|
||||
updateEntityField
|
||||
) {
|
||||
updateCachedCompanyField(newFieldEntity);
|
||||
updateEntityField<ViewFieldRelationMetadata, EntityForSelect>(
|
||||
currentRowEntityId,
|
||||
columnDefinition,
|
||||
newFieldEntity,
|
||||
);
|
||||
}
|
||||
|
||||
closeEditableCell();
|
||||
};
|
||||
|
||||
const handlePersonSubmit = (newFieldEntity: EntityForSelect | null) => {
|
||||
if (
|
||||
newFieldEntity?.id !== fieldValueEntity?.id &&
|
||||
currentRowEntityId &&
|
||||
updateEntityField
|
||||
) {
|
||||
updateCachedPersonField(newFieldEntity);
|
||||
updateEntityField(currentRowEntityId, columnDefinition, newFieldEntity);
|
||||
}
|
||||
|
||||
closeEditableCell();
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
closeEditableCell();
|
||||
};
|
||||
|
||||
switch (columnDefinition.metadata.relationType) {
|
||||
case Entity.Company: {
|
||||
return (
|
||||
<CompanyPickerCell
|
||||
companyId={fieldValueEntity?.id ?? null}
|
||||
onSubmit={handleCompanySubmit}
|
||||
onCancel={handleCancel}
|
||||
width={columnDefinition.size}
|
||||
createModeEnabled
|
||||
/>
|
||||
);
|
||||
}
|
||||
case Entity.User: {
|
||||
return (
|
||||
<UserPicker
|
||||
userId={fieldValueEntity?.id ?? null}
|
||||
onSubmit={handlePersonSubmit}
|
||||
onCancel={handleCancel}
|
||||
width={columnDefinition.size}
|
||||
/>
|
||||
);
|
||||
}
|
||||
default:
|
||||
console.warn(
|
||||
`Unknown relation type: "${columnDefinition.metadata.relationType}" in GenericEditableRelationCellEditMode`,
|
||||
);
|
||||
return <></>;
|
||||
}
|
||||
};
|
||||
@ -1,40 +0,0 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { TextDisplay } from '@/ui/content-display/components/TextDisplay';
|
||||
import { ViewFieldTextMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
|
||||
import { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
|
||||
import { GenericEditableTextCellEditMode } from './GenericEditableTextCellEditMode';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldTextMetadata>;
|
||||
editModeHorizontalAlign?: 'left' | 'right';
|
||||
};
|
||||
|
||||
export const GenericEditableTextCell = ({
|
||||
columnDefinition,
|
||||
editModeHorizontalAlign,
|
||||
}: OwnProps) => {
|
||||
const currentRowEntityId = useCurrentRowEntityId();
|
||||
|
||||
const fieldValue = useRecoilValue<string>(
|
||||
tableEntityFieldFamilySelector({
|
||||
entityId: currentRowEntityId ?? '',
|
||||
fieldName: columnDefinition.metadata.fieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
return (
|
||||
<EditableCell
|
||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||
editModeContent={
|
||||
<GenericEditableTextCellEditMode columnDefinition={columnDefinition} />
|
||||
}
|
||||
nonEditModeContent={<TextDisplay text={fieldValue} />}
|
||||
></EditableCell>
|
||||
);
|
||||
};
|
||||
@ -1,65 +0,0 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { ViewFieldTextMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { useCellInputEventHandlers } from '@/ui/table/hooks/useCellInputEventHandlers';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
import { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope';
|
||||
|
||||
import { TextInput } from '../../../../input/components/TextInput';
|
||||
import { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldTextMetadata>;
|
||||
};
|
||||
|
||||
export const GenericEditableTextCellEditMode = ({
|
||||
columnDefinition,
|
||||
}: OwnProps) => {
|
||||
const currentRowEntityId = useCurrentRowEntityId();
|
||||
|
||||
// TODO: we could use a hook that would return the field value with the right type
|
||||
const [fieldValue, setFieldValue] = useRecoilState<string>(
|
||||
tableEntityFieldFamilySelector({
|
||||
entityId: currentRowEntityId ?? '',
|
||||
fieldName: columnDefinition.metadata.fieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
const updateField = useUpdateEntityField();
|
||||
|
||||
const handleSubmit = (newText: string) => {
|
||||
if (newText === fieldValue) return;
|
||||
|
||||
setFieldValue(newText);
|
||||
|
||||
if (currentRowEntityId && updateField) {
|
||||
updateField(currentRowEntityId, columnDefinition, newText);
|
||||
}
|
||||
};
|
||||
|
||||
const {
|
||||
handleEnter,
|
||||
handleEscape,
|
||||
handleTab,
|
||||
handleShiftTab,
|
||||
handleClickOutside,
|
||||
} = useCellInputEventHandlers({
|
||||
onSubmit: handleSubmit,
|
||||
});
|
||||
|
||||
return (
|
||||
<TextInput
|
||||
placeholder={columnDefinition.metadata.placeHolder ?? ''}
|
||||
autoFocus
|
||||
value={fieldValue ?? ''}
|
||||
onClickOutside={handleClickOutside}
|
||||
onEnter={handleEnter}
|
||||
onEscape={handleEscape}
|
||||
onTab={handleTab}
|
||||
onShiftTab={handleShiftTab}
|
||||
hotkeyScope={TableHotkeyScope.CellEditMode}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,42 +0,0 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { URLDisplay } from '@/ui/content-display/components/URLDisplay';
|
||||
import { ViewFieldURLMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
import { sanitizeURL } from '~/utils';
|
||||
|
||||
import { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
|
||||
import { GenericEditableURLCellEditMode } from './GenericEditableURLCellEditMode';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldURLMetadata>;
|
||||
editModeHorizontalAlign?: 'left' | 'right';
|
||||
};
|
||||
|
||||
export const GenericEditableURLCell = ({
|
||||
columnDefinition,
|
||||
editModeHorizontalAlign,
|
||||
}: OwnProps) => {
|
||||
const currentRowEntityId = useCurrentRowEntityId();
|
||||
|
||||
const fieldValue = useRecoilValue<string>(
|
||||
tableEntityFieldFamilySelector({
|
||||
entityId: currentRowEntityId ?? '',
|
||||
fieldName: columnDefinition.metadata.fieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
return (
|
||||
<EditableCell
|
||||
useEditButton
|
||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||
editModeContent={
|
||||
<GenericEditableURLCellEditMode columnDefinition={columnDefinition} />
|
||||
}
|
||||
nonEditModeContent={<URLDisplay value={sanitizeURL(fieldValue)} />}
|
||||
></EditableCell>
|
||||
);
|
||||
};
|
||||
@ -1,68 +0,0 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { ViewFieldURLMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { useCellInputEventHandlers } from '@/ui/table/hooks/useCellInputEventHandlers';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
import { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope';
|
||||
import { isURL } from '~/utils/is-url';
|
||||
|
||||
import { TextInput } from '../../../../input/components/TextInput';
|
||||
import { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldURLMetadata>;
|
||||
};
|
||||
|
||||
export const GenericEditableURLCellEditMode = ({
|
||||
columnDefinition,
|
||||
}: OwnProps) => {
|
||||
const currentRowEntityId = useCurrentRowEntityId();
|
||||
|
||||
// TODO: we could use a hook that would return the field value with the right type
|
||||
const [fieldValue, setFieldValue] = useRecoilState<string>(
|
||||
tableEntityFieldFamilySelector({
|
||||
entityId: currentRowEntityId ?? '',
|
||||
fieldName: columnDefinition.metadata.fieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
const updateField = useUpdateEntityField();
|
||||
|
||||
const handleSubmit = (newText: string) => {
|
||||
if (newText === fieldValue) return;
|
||||
|
||||
if (newText !== '' && !isURL(newText)) return;
|
||||
|
||||
setFieldValue(newText);
|
||||
|
||||
if (currentRowEntityId && updateField) {
|
||||
updateField(currentRowEntityId, columnDefinition, newText);
|
||||
}
|
||||
};
|
||||
|
||||
const {
|
||||
handleEnter,
|
||||
handleEscape,
|
||||
handleTab,
|
||||
handleShiftTab,
|
||||
handleClickOutside,
|
||||
} = useCellInputEventHandlers({
|
||||
onSubmit: handleSubmit,
|
||||
});
|
||||
|
||||
return (
|
||||
<TextInput
|
||||
placeholder={columnDefinition.metadata.placeHolder ?? ''}
|
||||
autoFocus
|
||||
value={fieldValue ?? ''}
|
||||
onClickOutside={handleClickOutside}
|
||||
onEnter={handleEnter}
|
||||
onEscape={handleEscape}
|
||||
onTab={handleTab}
|
||||
onShiftTab={handleShiftTab}
|
||||
hotkeyScope={TableHotkeyScope.CellEditMode}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,23 +0,0 @@
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
|
||||
import { PhoneInput } from '@/ui/input/components/PhoneInput';
|
||||
import { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
import { ComponentWithRecoilScopeDecorator } from '~/testing/decorators/ComponentWithRecoilScopeDecorator';
|
||||
|
||||
const meta: Meta<typeof PhoneInput> = {
|
||||
title: 'UI/Table/EditableCell/PhoneCellEdit',
|
||||
component: PhoneInput,
|
||||
decorators: [ComponentWithRecoilScopeDecorator],
|
||||
args: {
|
||||
value: '+33714446494',
|
||||
autoFocus: true,
|
||||
},
|
||||
parameters: {
|
||||
customRecoilScopeContext: TableRecoilScopeContext,
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof PhoneInput>;
|
||||
|
||||
export const Default: Story = {};
|
||||
Reference in New Issue
Block a user