Compare commits

...
Author SHA1 Message Date
Lucas Bordeau cca15c315a POC 2026-03-27 18:18:02 +01:00
6 changed files with 150 additions and 31 deletions
@@ -31,6 +31,10 @@ export const RecordTableCellEditModePortal = () => {
{recordTableCellEditModePosition && (
<RecordTableCellPortalRootContainer
zIndex={TABLE_Z_INDEX.cell.editMode}
topOffset={0}
leftOffset={0}
widthExpansion={0}
heightExpansion={0}
>
<RecordTableCellEditMode>
<RecordTableCellFieldInput />
@@ -26,9 +26,23 @@ export const RecordTableCellFocusedPortal = () => {
return null;
}
const isTouchingHeader = recordTableFocusPosition.row === 0;
const isTouchingFirstColumn = recordTableFocusPosition.column === 1;
const topOffset = isTouchingHeader ? 0 : -1;
const leftOffset = isTouchingFirstColumn ? 0 : -1;
const widthExpansion = isTouchingFirstColumn ? 1 : 2;
const heightExpansion = isTouchingHeader ? 1 : 2;
return (
<RecordTableCellPortalWrapper position={recordTableFocusPosition}>
<RecordTableCellPortalRootContainer zIndex={TABLE_Z_INDEX.hoverPortal}>
<RecordTableCellPortalRootContainer
zIndex={TABLE_Z_INDEX.hoverPortal}
topOffset={topOffset}
leftOffset={leftOffset}
widthExpansion={widthExpansion}
heightExpansion={heightExpansion}
>
<RecordTableCellFocusedPortalContent />
</RecordTableCellPortalRootContainer>
</RecordTableCellPortalWrapper>
@@ -1,5 +1,4 @@
import { FieldDisplay } from '@/object-record/record-field/ui/components/FieldDisplay';
import { RECORD_TABLE_ROW_HEIGHT } from '@/object-record/record-table/constants/RecordTableRowHeight';
import { useRecordTableBodyContextOrThrow } from '@/object-record/record-table/contexts/RecordTableBodyContext';
import { useRecordTableRowContextOrThrow } from '@/object-record/record-table/contexts/RecordTableRowContext';
import { RecordTableCellDisplayMode } from '@/object-record/record-table/record-table-cell/components/RecordTableCellDisplayMode';
@@ -16,7 +15,6 @@ import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledRecordTableCellFocusPortalContent = styled.div<{
isRecordTableRowActive: boolean;
}>`
align-items: center;
background: ${themeCssVariables.background.transparent.secondary};
background-color: ${({ isRecordTableRowActive }) =>
isRecordTableRowActive
@@ -24,16 +22,32 @@ const StyledRecordTableCellFocusPortalContent = styled.div<{
: themeCssVariables.background.primary};
border-radius: ${themeCssVariables.border.radius.sm};
box-sizing: border-box;
display: flex;
height: ${RECORD_TABLE_ROW_HEIGHT}px;
height: 100%;
outline: 1px solid ${themeCssVariables.color.blue8};
outline-offset: -1px;
position: relative;
user-select: none;
`;
const StyledFocusedContentWrapper = styled.div<{
contentTop: number;
contentLeft: number;
contentWidth: string;
contentHeight: string;
}>`
align-items: center;
box-sizing: border-box;
display: flex;
height: ${({ contentHeight }) => contentHeight};
left: ${({ contentLeft }) => contentLeft}px;
position: absolute;
top: ${({ contentTop }) => contentTop}px;
width: ${({ contentWidth }) => contentWidth};
`;
export const RecordTableCellFocusedPortalContent = () => {
const { rowIndex } = useRecordTableRowContextOrThrow();
const { onMoveHoverToCurrentCell } = useRecordTableBodyContextOrThrow();
@@ -61,14 +75,33 @@ export const RecordTableCellFocusedPortalContent = () => {
}
};
const isTouchingHeader = recordTableFocusPosition?.row === 0;
const isTouchingFirstColumn = recordTableFocusPosition?.column === 1;
const contentTop = isTouchingHeader ? 0 : 1;
const contentLeft = isTouchingFirstColumn ? 0 : 1;
const contentWidth = isTouchingFirstColumn
? 'calc(100% - 1px)'
: 'calc(100% - 2px)';
const contentHeight = isTouchingHeader
? 'calc(100% - 1px)'
: 'calc(100% - 2px)';
return (
<StyledRecordTableCellFocusPortalContent
isRecordTableRowActive={isRecordTableRowActive}
onMouseMove={handleContainerMouseMove}
>
<RecordTableCellDisplayMode>
<FieldDisplay />
</RecordTableCellDisplayMode>
<StyledFocusedContentWrapper
contentTop={contentTop}
contentLeft={contentLeft}
contentWidth={contentWidth}
contentHeight={contentHeight}
>
<RecordTableCellDisplayMode>
<FieldDisplay />
</RecordTableCellDisplayMode>
</StyledFocusedContentWrapper>
</StyledRecordTableCellFocusPortalContent>
);
};
@@ -17,7 +17,13 @@ export const RecordTableCellHoveredPortal = () => {
return (
<RecordTableCellPortalWrapper position={recordTableHoverPosition}>
<RecordTableCellPortalRootContainer zIndex={TABLE_Z_INDEX.hoverPortal}>
<RecordTableCellPortalRootContainer
zIndex={TABLE_Z_INDEX.hoverPortal}
topOffset={0}
leftOffset={0}
widthExpansion={0}
heightExpansion={0}
>
<RecordTableCellHoveredPortalContent />
</RecordTableCellPortalRootContainer>
</RecordTableCellPortalWrapper>
@@ -2,7 +2,6 @@ import { FieldDisplay } from '@/object-record/record-field/ui/components/FieldDi
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
import { FieldFocusStaticFocusedProvider } from '@/object-record/record-field/ui/contexts/FieldFocusContextProvider';
import { useIsFieldInputOnly } from '@/object-record/record-field/ui/hooks/useIsFieldInputOnly';
import { RECORD_TABLE_ROW_HEIGHT } from '@/object-record/record-table/constants/RecordTableRowHeight';
import { useRecordTableRowContextOrThrow } from '@/object-record/record-table/contexts/RecordTableRowContext';
import { RecordTableCellDisplayMode } from '@/object-record/record-table/record-table-cell/components/RecordTableCellDisplayMode';
import { RecordTableCellEditButton } from '@/object-record/record-table/record-table-cell/components/RecordTableCellEditButton';
@@ -21,21 +20,31 @@ import { useIsMobile } from 'twenty-ui/utilities';
const StyledRecordTableCellHoveredPortalContent = styled.div<{
showInteractiveStyle: boolean;
isRecordTableRowActive: boolean;
topExpansion: number;
leftExpansion: number;
rightExpansion: number;
bottomExpansion: number;
}>`
align-items: center;
background: ${themeCssVariables.background.transparent.secondary};
background-color: ${({ isRecordTableRowActive }) =>
background-color: ${({ isRecordTableRowActive, showInteractiveStyle }) =>
isRecordTableRowActive
? themeCssVariables.accent.quaternary
: themeCssVariables.background.primary};
: showInteractiveStyle
? themeCssVariables.background.primary
: themeCssVariables.background.transparent.lighter};
border-radius: ${({ showInteractiveStyle }) =>
showInteractiveStyle ? themeCssVariables.border.radius.sm : 'none'};
box-sizing: border-box;
cursor: ${({ showInteractiveStyle }) =>
showInteractiveStyle ? 'pointer' : 'default'};
display: flex;
height: ${RECORD_TABLE_ROW_HEIGHT}px;
height: calc(
100% +
${({ topExpansion, bottomExpansion }) => topExpansion + bottomExpansion}px
);
margin-left: ${({ leftExpansion }) => -leftExpansion}px;
margin-top: ${({ topExpansion }) => -topExpansion}px;
outline: ${({ showInteractiveStyle, isRecordTableRowActive }) =>
isRecordTableRowActive
@@ -45,7 +54,28 @@ const StyledRecordTableCellHoveredPortalContent = styled.div<{
: `1px solid ${themeCssVariables.border.color.medium}`};
outline-offset: -1px;
position: relative;
user-select: none;
width: calc(
100% +
${({ leftExpansion, rightExpansion }) => leftExpansion + rightExpansion}px
);
`;
const StyledContentWrapper = styled.div<{
contentTop: number;
contentLeft: number;
contentWidth: string;
contentHeight: string;
}>`
align-items: center;
box-sizing: border-box;
display: flex;
height: ${({ contentHeight }) => contentHeight};
left: ${({ contentLeft }) => contentLeft}px;
position: absolute;
top: ${({ contentTop }) => contentTop}px;
width: ${({ contentWidth }) => contentWidth};
`;
export const RecordTableCellHoveredPortalContent = () => {
@@ -75,23 +105,51 @@ export const RecordTableCellHoveredPortalContent = () => {
rowIndex,
);
const isTouchingHeader = recordTableHoverPosition?.row === 0;
const isTouchingFirstColumn = recordTableHoverPosition?.column === 1;
const topExpansion = showInteractiveStyle ? (isTouchingHeader ? 0 : 1) : 1;
const leftExpansion = showInteractiveStyle
? isTouchingFirstColumn
? 0
: 1
: 1;
const rightExpansion = 1;
const bottomExpansion = 1;
const contentTop = topExpansion;
const contentLeft = leftExpansion;
const contentWidth = `calc(100% - ${leftExpansion + rightExpansion}px)`;
const contentHeight = `calc(100% - ${topExpansion + bottomExpansion}px)`;
return (
<StyledRecordTableCellHoveredPortalContent
showInteractiveStyle={showInteractiveStyle}
isRecordTableRowActive={isRecordTableRowActive}
topExpansion={topExpansion}
leftExpansion={leftExpansion}
rightExpansion={rightExpansion}
bottomExpansion={bottomExpansion}
>
<FieldFocusStaticFocusedProvider>
{isFieldInputOnly ? (
<RecordTableCellEditMode>
<RecordTableCellFieldInput />
</RecordTableCellEditMode>
) : (
<RecordTableCellDisplayMode>
<FieldDisplay />
</RecordTableCellDisplayMode>
)}
</FieldFocusStaticFocusedProvider>
{showButton && <RecordTableCellEditButton />}
<StyledContentWrapper
contentTop={contentTop}
contentLeft={contentLeft}
contentWidth={contentWidth}
contentHeight={contentHeight}
>
<FieldFocusStaticFocusedProvider>
{isFieldInputOnly ? (
<RecordTableCellEditMode>
<RecordTableCellFieldInput />
</RecordTableCellEditMode>
) : (
<RecordTableCellDisplayMode>
<FieldDisplay />
</RecordTableCellDisplayMode>
)}
</FieldFocusStaticFocusedProvider>
{showButton && <RecordTableCellEditButton />}
</StyledContentWrapper>
</StyledRecordTableCellHoveredPortalContent>
);
};
@@ -2,12 +2,16 @@ import { styled } from '@linaria/react';
const StyledRecordTableCellPortalRootContainer = styled.div<{
zIndex?: number;
topOffset: number;
leftOffset: number;
widthExpansion: number;
heightExpansion: number;
}>`
height: 100%;
left: 0;
height: calc(100% + ${({ heightExpansion }) => heightExpansion}px);
left: ${({ leftOffset }) => leftOffset}px;
position: absolute;
top: 0;
width: 100%;
top: ${({ topOffset }) => topOffset}px;
width: calc(100% + ${({ widthExpansion }) => widthExpansion}px);
z-index: ${({ zIndex }) => zIndex ?? 'auto'};
`;