@ -3,7 +3,6 @@ import { useContext } from 'react';
|
||||
import { LinksFieldDisplay } from '@/object-record/record-field/meta-types/display/components/LinksFieldDisplay';
|
||||
import { isFieldDisplayedAsPhone } from '@/object-record/record-field/types/guards/isFieldDisplayedAsPhone';
|
||||
import { isFieldLinks } from '@/object-record/record-field/types/guards/isFieldLinks';
|
||||
import { ExpandableListProps } from '@/ui/layout/expandable-list/components/ExpandableList';
|
||||
|
||||
import { FieldContext } from '../contexts/FieldContext';
|
||||
import { AddressFieldDisplay } from '../meta-types/display/components/AddressFieldDisplay';
|
||||
@ -38,13 +37,17 @@ import { isFieldSelect } from '../types/guards/isFieldSelect';
|
||||
import { isFieldText } from '../types/guards/isFieldText';
|
||||
import { isFieldUuid } from '../types/guards/isFieldUuid';
|
||||
|
||||
type FieldDisplayProps = ExpandableListProps;
|
||||
type FieldDisplayProps = {
|
||||
isCellSoftFocused?: boolean;
|
||||
cellElement?: HTMLElement;
|
||||
fromTableCell?: boolean;
|
||||
};
|
||||
|
||||
export const FieldDisplay = ({
|
||||
isHovered,
|
||||
reference,
|
||||
isCellSoftFocused,
|
||||
cellElement,
|
||||
fromTableCell,
|
||||
}: FieldDisplayProps & { fromTableCell?: boolean }) => {
|
||||
}: FieldDisplayProps) => {
|
||||
const { fieldDefinition, isLabelIdentifier } = useContext(FieldContext);
|
||||
|
||||
const isChipDisplay =
|
||||
@ -75,7 +78,11 @@ export const FieldDisplay = ({
|
||||
) : isFieldLink(fieldDefinition) ? (
|
||||
<LinkFieldDisplay />
|
||||
) : isFieldLinks(fieldDefinition) ? (
|
||||
<LinksFieldDisplay />
|
||||
<LinksFieldDisplay
|
||||
isCellSoftFocused={isCellSoftFocused}
|
||||
cellElement={cellElement}
|
||||
fromTableCell={fromTableCell}
|
||||
/>
|
||||
) : isFieldCurrency(fieldDefinition) ? (
|
||||
<CurrencyFieldDisplay />
|
||||
) : isFieldFullName(fieldDefinition) ? (
|
||||
@ -84,9 +91,9 @@ export const FieldDisplay = ({
|
||||
<SelectFieldDisplay />
|
||||
) : isFieldMultiSelect(fieldDefinition) ? (
|
||||
<MultiSelectFieldDisplay
|
||||
isHovered={isHovered}
|
||||
reference={reference}
|
||||
withDropDownBorder={fromTableCell}
|
||||
isCellSoftFocused={isCellSoftFocused}
|
||||
cellElement={cellElement}
|
||||
fromTableCell={fromTableCell}
|
||||
/>
|
||||
) : isFieldAddress(fieldDefinition) ? (
|
||||
<AddressFieldDisplay />
|
||||
|
||||
@ -1,8 +1,25 @@
|
||||
import { useLinksField } from '@/object-record/record-field/meta-types/hooks/useLinksField';
|
||||
import { LinksDisplay } from '@/ui/field/display/components/LinksDisplay';
|
||||
|
||||
export const LinksFieldDisplay = () => {
|
||||
type LinksFieldDisplayProps = {
|
||||
isCellSoftFocused?: boolean;
|
||||
cellElement?: HTMLElement;
|
||||
fromTableCell?: boolean;
|
||||
};
|
||||
|
||||
export const LinksFieldDisplay = ({
|
||||
isCellSoftFocused,
|
||||
cellElement,
|
||||
fromTableCell,
|
||||
}: LinksFieldDisplayProps) => {
|
||||
const { fieldValue } = useLinksField();
|
||||
|
||||
return <LinksDisplay value={fieldValue} />;
|
||||
return (
|
||||
<LinksDisplay
|
||||
value={fieldValue}
|
||||
anchorElement={cellElement}
|
||||
isChipCountDisplayed={isCellSoftFocused}
|
||||
withExpandedListBorder={fromTableCell}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,15 +1,17 @@
|
||||
import { useMultiSelectField } from '@/object-record/record-field/meta-types/hooks/useMultiSelectField';
|
||||
import { Tag } from '@/ui/display/tag/components/Tag';
|
||||
import {
|
||||
ExpandableList,
|
||||
ExpandableListProps,
|
||||
} from '@/ui/layout/expandable-list/components/ExpandableList';
|
||||
import { ExpandableList } from '@/ui/layout/expandable-list/components/ExpandableList';
|
||||
|
||||
type MultiSelectFieldDisplayProps = {
|
||||
isCellSoftFocused?: boolean;
|
||||
cellElement?: HTMLElement;
|
||||
fromTableCell?: boolean;
|
||||
};
|
||||
|
||||
type MultiSelectFieldDisplayProps = ExpandableListProps;
|
||||
export const MultiSelectFieldDisplay = ({
|
||||
isHovered,
|
||||
reference,
|
||||
withDropDownBorder,
|
||||
isCellSoftFocused,
|
||||
cellElement,
|
||||
fromTableCell,
|
||||
}: MultiSelectFieldDisplayProps) => {
|
||||
const { fieldValues, fieldDefinition } = useMultiSelectField();
|
||||
|
||||
@ -19,11 +21,13 @@ export const MultiSelectFieldDisplay = ({
|
||||
)
|
||||
: [];
|
||||
|
||||
return selectedOptions ? (
|
||||
if (!selectedOptions) return null;
|
||||
|
||||
return (
|
||||
<ExpandableList
|
||||
isHovered={isHovered}
|
||||
reference={reference}
|
||||
withDropDownBorder={withDropDownBorder}
|
||||
anchorElement={cellElement}
|
||||
isChipCountDisplayed={isCellSoftFocused}
|
||||
withExpandedListBorder={fromTableCell}
|
||||
>
|
||||
{selectedOptions.map((selectedOption, index) => (
|
||||
<Tag
|
||||
@ -33,7 +37,5 @@ export const MultiSelectFieldDisplay = ({
|
||||
/>
|
||||
))}
|
||||
</ExpandableList>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
};
|
||||
|
||||
@ -99,7 +99,12 @@ export const RecordInlineCell = ({
|
||||
isReadOnly={readonly}
|
||||
/>
|
||||
}
|
||||
displayModeContent={<FieldDisplay />}
|
||||
displayModeContent={({ cellElement, isCellSoftFocused }) => (
|
||||
<FieldDisplay
|
||||
cellElement={cellElement}
|
||||
isCellSoftFocused={isCellSoftFocused}
|
||||
/>
|
||||
)}
|
||||
isDisplayModeContentEmpty={isFieldEmpty}
|
||||
isDisplayModeFixHeight
|
||||
editModeContentOnly={isFieldInputOnly}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useContext, useEffect, useRef, useState } from 'react';
|
||||
import React, { useContext, useState } from 'react';
|
||||
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
|
||||
import { Tooltip } from 'react-tooltip';
|
||||
import { css, useTheme } from '@emotion/react';
|
||||
@ -7,7 +7,6 @@ import { IconComponent } from 'twenty-ui';
|
||||
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { EllipsisDisplay } from '@/ui/field/display/components/EllipsisDisplay';
|
||||
import { ExpandableListProps } from '@/ui/layout/expandable-list/components/ExpandableList';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
|
||||
import { useInlineCell } from '../hooks/useInlineCell';
|
||||
@ -111,7 +110,13 @@ type RecordInlineCellContainerProps = {
|
||||
buttonIcon?: IconComponent;
|
||||
editModeContent?: React.ReactNode;
|
||||
editModeContentOnly?: boolean;
|
||||
displayModeContent: React.ReactNode;
|
||||
displayModeContent: ({
|
||||
isCellSoftFocused,
|
||||
cellElement,
|
||||
}: {
|
||||
isCellSoftFocused: boolean;
|
||||
cellElement?: HTMLDivElement;
|
||||
}) => React.ReactNode;
|
||||
customEditHotkeyScope?: HotkeyScope;
|
||||
isDisplayModeContentEmpty?: boolean;
|
||||
isDisplayModeFixHeight?: boolean;
|
||||
@ -136,24 +141,25 @@ export const RecordInlineCellContainer = ({
|
||||
loading = false,
|
||||
}: RecordInlineCellContainerProps) => {
|
||||
const { entityId, fieldDefinition } = useContext(FieldContext);
|
||||
const reference = useRef<HTMLDivElement>(null);
|
||||
// Used by some fields in ExpandableList as an anchor for the floating element.
|
||||
// floating-ui mentions that `useState` must be used instead of `useRef`,
|
||||
// see https://floating-ui.com/docs/useFloating#elements
|
||||
const [cellElement, setCellElement] = useState<HTMLDivElement | null>(null);
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const [isHoveredForDisplayMode, setIsHoveredForDisplayMode] = useState(false);
|
||||
const [newDisplayModeContent, setNewDisplayModeContent] =
|
||||
useState<React.ReactNode>(displayModeContent);
|
||||
const [isCellSoftFocused, setIsCellSoftFocused] = useState(false);
|
||||
|
||||
const handleContainerMouseEnter = () => {
|
||||
if (!readonly) {
|
||||
setIsHovered(true);
|
||||
}
|
||||
setIsHoveredForDisplayMode(true);
|
||||
setIsCellSoftFocused(true);
|
||||
};
|
||||
|
||||
const handleContainerMouseLeave = () => {
|
||||
if (!readonly) {
|
||||
setIsHovered(false);
|
||||
}
|
||||
setIsHoveredForDisplayMode(false);
|
||||
setIsCellSoftFocused(false);
|
||||
};
|
||||
|
||||
const { isInlineCellInEditMode, openInlineCell } = useInlineCell();
|
||||
@ -174,17 +180,6 @@ export const RecordInlineCellContainer = ({
|
||||
const theme = useTheme();
|
||||
const labelId = `label-${entityId}-${fieldDefinition?.metadata?.fieldName}`;
|
||||
|
||||
useEffect(() => {
|
||||
if (React.isValidElement<ExpandableListProps>(displayModeContent)) {
|
||||
setNewDisplayModeContent(
|
||||
React.cloneElement(displayModeContent, {
|
||||
isHovered: isHoveredForDisplayMode,
|
||||
reference: reference.current || undefined,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}, [isHoveredForDisplayMode, displayModeContent, reference]);
|
||||
|
||||
const showContent = () => {
|
||||
if (loading) {
|
||||
return <StyledInlineCellSkeletonLoader />;
|
||||
@ -215,7 +210,10 @@ export const RecordInlineCellContainer = ({
|
||||
isHovered={isHovered}
|
||||
emptyPlaceholder={showLabel ? 'Empty' : label}
|
||||
>
|
||||
{newDisplayModeContent}
|
||||
{displayModeContent({
|
||||
isCellSoftFocused,
|
||||
cellElement: cellElement ?? undefined,
|
||||
})}
|
||||
</RecordInlineCellDisplayMode>
|
||||
{showEditButton && <RecordInlineCellButton Icon={buttonIcon} />}
|
||||
</StyledClickableContainer>
|
||||
@ -252,7 +250,7 @@ export const RecordInlineCellContainer = ({
|
||||
)}
|
||||
</StyledLabelAndIconContainer>
|
||||
)}
|
||||
<StyledValueContainer ref={reference}>
|
||||
<StyledValueContainer ref={setCellElement}>
|
||||
{showContent()}
|
||||
</StyledValueContainer>
|
||||
</StyledInlineCellBaseContainer>
|
||||
|
||||
@ -102,7 +102,13 @@ export const RecordTableCell = ({
|
||||
isReadOnly={isReadOnly}
|
||||
/>
|
||||
}
|
||||
nonEditModeContent={<FieldDisplay fromTableCell />}
|
||||
nonEditModeContent={({ isCellSoftFocused, cellElement }) => (
|
||||
<FieldDisplay
|
||||
isCellSoftFocused={isCellSoftFocused}
|
||||
cellElement={cellElement}
|
||||
fromTableCell
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,10 +1,4 @@
|
||||
import React, {
|
||||
ReactElement,
|
||||
useContext,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import React, { ReactElement, useContext, useState } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { IconArrowUpRight } from 'twenty-ui';
|
||||
@ -20,7 +14,6 @@ import { useOpenRecordTableCellFromCell } from '@/object-record/record-table/rec
|
||||
import { RecordTableScopeInternalContext } from '@/object-record/record-table/scopes/scope-internal-context/RecordTableScopeInternalContext';
|
||||
import { isSoftFocusOnTableCellComponentFamilyState } from '@/object-record/record-table/states/isSoftFocusOnTableCellComponentFamilyState';
|
||||
import { isTableCellInEditModeComponentFamilyState } from '@/object-record/record-table/states/isTableCellInEditModeComponentFamilyState';
|
||||
import { ExpandableListProps } from '@/ui/layout/expandable-list/components/ExpandableList';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
|
||||
import { getScopeIdOrUndefinedFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdOrUndefinedFromComponentId';
|
||||
@ -58,7 +51,13 @@ const StyledCellBaseContainer = styled.div<{ softFocus: boolean }>`
|
||||
|
||||
export type RecordTableCellContainerProps = {
|
||||
editModeContent: ReactElement;
|
||||
nonEditModeContent: ReactElement;
|
||||
nonEditModeContent?: ({
|
||||
isCellSoftFocused,
|
||||
cellElement,
|
||||
}: {
|
||||
isCellSoftFocused: boolean;
|
||||
cellElement?: HTMLTableCellElement;
|
||||
}) => ReactElement;
|
||||
editHotkeyScope?: HotkeyScope;
|
||||
transparent?: boolean;
|
||||
maxContentWidth?: number;
|
||||
@ -76,10 +75,14 @@ export const RecordTableCellContainer = ({
|
||||
editHotkeyScope,
|
||||
}: RecordTableCellContainerProps) => {
|
||||
const { columnIndex } = useContext(RecordTableCellContext);
|
||||
const reference = useRef<HTMLTableCellElement>(null);
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const [newNonEditModeContent, setNewNonEditModeContent] =
|
||||
useState<ReactElement>(nonEditModeContent);
|
||||
// Used by some fields in ExpandableList as an anchor for the floating element.
|
||||
// floating-ui mentions that `useState` must be used instead of `useRef`,
|
||||
// see https://floating-ui.com/docs/useFloating#elements
|
||||
const [cellElement, setCellElement] = useState<HTMLTableCellElement | null>(
|
||||
null,
|
||||
);
|
||||
const [isCellBaseContainerHovered, setIsCellBaseContainerHovered] =
|
||||
useState(false);
|
||||
const { isReadOnly, isSelected, recordId } = useContext(
|
||||
RecordTableRowContext,
|
||||
);
|
||||
@ -127,13 +130,13 @@ export const RecordTableCellContainer = ({
|
||||
const handleContainerMouseEnter = () => {
|
||||
onCellMouseEnter({
|
||||
cellPosition,
|
||||
isHovered,
|
||||
setIsHovered,
|
||||
isHovered: isCellBaseContainerHovered,
|
||||
setIsHovered: setIsCellBaseContainerHovered,
|
||||
});
|
||||
};
|
||||
|
||||
const handleContainerMouseLeave = () => {
|
||||
setIsHovered(false);
|
||||
setIsCellBaseContainerHovered(false);
|
||||
};
|
||||
|
||||
const editModeContentOnly = useIsFieldInputOnly();
|
||||
@ -150,20 +153,9 @@ export const RecordTableCellContainer = ({
|
||||
(!isFirstColumn || !isEmpty) &&
|
||||
!isReadOnly;
|
||||
|
||||
useEffect(() => {
|
||||
if (React.isValidElement<ExpandableListProps>(nonEditModeContent)) {
|
||||
setNewNonEditModeContent(
|
||||
React.cloneElement(nonEditModeContent, {
|
||||
isHovered: showButton,
|
||||
reference: reference.current || undefined,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}, [nonEditModeContent, showButton, reference]);
|
||||
|
||||
return (
|
||||
<StyledTd
|
||||
ref={reference}
|
||||
ref={setCellElement}
|
||||
isSelected={isSelected}
|
||||
onContextMenu={handleContextMenu}
|
||||
isInEditMode={isCurrentTableCellInEditMode}
|
||||
@ -181,7 +173,12 @@ export const RecordTableCellContainer = ({
|
||||
) : hasSoftFocus ? (
|
||||
<>
|
||||
<RecordTableCellSoftFocusMode>
|
||||
{editModeContentOnly ? editModeContent : newNonEditModeContent}
|
||||
{editModeContentOnly
|
||||
? editModeContent
|
||||
: nonEditModeContent?.({
|
||||
isCellSoftFocused: true,
|
||||
cellElement: cellElement ?? undefined,
|
||||
})}
|
||||
</RecordTableCellSoftFocusMode>
|
||||
{showButton && (
|
||||
<RecordTableCellButton
|
||||
@ -196,7 +193,10 @@ export const RecordTableCellContainer = ({
|
||||
<RecordTableCellDisplayMode>
|
||||
{editModeContentOnly
|
||||
? editModeContent
|
||||
: newNonEditModeContent}
|
||||
: nonEditModeContent?.({
|
||||
isCellSoftFocused: false,
|
||||
cellElement: cellElement ?? undefined,
|
||||
})}
|
||||
</RecordTableCellDisplayMode>
|
||||
)}
|
||||
{showButton && (
|
||||
|
||||
Reference in New Issue
Block a user