Add back first column shrink on mobile (#16244)

This PR fixes https://github.com/twentyhq/twenty/issues/14829

I created two states because one was needed for the table and the other
for the ChipFieldDisplay, which can appear anywhere.

The states tell if the first column and the ChipFieldDisplay should be
shrinked.

I also removed the usage of `useRecordTableLastColumnWidthToFill` to
avoid unnecessary re-renders of the whole table on scroll.

## Before


https://github.com/user-attachments/assets/8b6886b3-8976-41c2-9937-5d7ea396ec56

## After


https://github.com/user-attachments/assets/dc3b5ff9-59c4-4954-a973-57f6edc2508e
This commit is contained in:
Lucas Bordeau
2025-12-02 18:51:06 +01:00
committed by GitHub
parent 3274c18a90
commit 05b30554c3
15 changed files with 179 additions and 97 deletions
@@ -1,8 +1,9 @@
import { RecordChip } from '@/object-record/components/RecordChip';
import { useChipFieldDisplay } from '@/object-record/record-field/ui/meta-types/hooks/useChipFieldDisplay';
import { shouldCompactRecordIndexLabelIdentifierComponentState } from '@/object-record/record-index/states/shouldCompactRecordIndexLabelIdentifierComponentState';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { isDefined } from 'twenty-shared/utils';
import { ChipSize } from 'twenty-ui/components';
import { useIsMobile } from 'twenty-ui/utilities';
export const ChipFieldDisplay = () => {
const {
@@ -15,10 +16,11 @@ export const ChipFieldDisplay = () => {
onRecordChipClick,
} = useChipFieldDisplay();
const isMobile = useIsMobile();
const shouldCompactRecordIndexLabelIdentifier = useRecoilComponentValue(
shouldCompactRecordIndexLabelIdentifierComponentState,
);
// TODO: reimplement scrolled horizontally here.
const isLabelIdentifierCompact = isMobile;
const isLabelIdentifierCompact = shouldCompactRecordIndexLabelIdentifier;
if (!isDefined(recordValue)) {
return null;
@@ -8,6 +8,7 @@ import { useRecordIndexFieldMetadataDerivedStates } from '@/object-record/record
import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext';
import { ComponentDecorator } from 'twenty-ui/testing';
import { ChipGeneratorsDecorator } from '~/testing/decorators/ChipGeneratorsDecorator';
import { ContextStoreDecorator } from '~/testing/decorators/ContextStoreDecorator';
import { MemoryRouterDecorator } from '~/testing/decorators/MemoryRouterDecorator';
import { getFieldDecorator } from '~/testing/decorators/getFieldDecorator';
import { getProfilingStory } from '~/testing/profiling/utils/getProfilingStory';
@@ -16,6 +17,7 @@ import { generatedMockObjectMetadataItems } from '~/testing/utils/generatedMockO
const meta: Meta = {
title: 'UI/Data/Field/Display/ChipFieldDisplay',
decorators: [
ContextStoreDecorator,
(Story) => {
const instanceId = 'child-field-display-scope';
@@ -0,0 +1,9 @@
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
export const shouldCompactRecordIndexLabelIdentifierComponentState =
createComponentState<boolean>({
key: 'shouldCompactRecordIndexLabelIdentifierComponentState',
componentInstanceContext: ContextStoreComponentInstanceContext,
defaultValue: false,
});
@@ -1,11 +1,21 @@
import { RECORD_TABLE_COLUMN_ADD_COLUMN_BUTTON_WIDTH } from '@/object-record/record-table/constants/RecordTableColumnAddColumnButtonWidth';
import { RECORD_TABLE_COLUMN_CHECKBOX_WIDTH } from '@/object-record/record-table/constants/RecordTableColumnCheckboxWidth';
import { RECORD_TABLE_COLUMN_DRAG_AND_DROP_WIDTH } from '@/object-record/record-table/constants/RecordTableColumnDragAndDropWidth';
import { RECORD_TABLE_COLUMN_LAST_EMPTY_COLUMN_WIDTH_VARIABLE_NAME } from '@/object-record/record-table/constants/RecordTableColumnLastEmptyColumnWidthVariableName';
import { RECORD_TABLE_COLUMN_MIN_WIDTH } from '@/object-record/record-table/constants/RecordTableColumnMinWidth';
import { RECORD_TABLE_COLUMN_WITH_GROUP_LAST_EMPTY_COLUMN_WIDTH_VARIABLE_NAME } from '@/object-record/record-table/constants/RecordTableColumnWithGroupLastEmptyColumnWidthVariableName';
import { RECORD_TABLE_LABEL_IDENTIFIER_COLUMN_WIDTH_ON_MOBILE } from '@/object-record/record-table/constants/RecordTableLabelIdentifierColumnWidthOnMobile';
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
import { useRecordTableLastColumnWidthToFill } from '@/object-record/record-table/hooks/useRecordTableLastColumnWidthToFill';
import { recordTableWidthComponentState } from '@/object-record/record-table/states/recordTableWidthComponentState';
import { resizedFieldMetadataIdComponentState } from '@/object-record/record-table/states/resizedFieldMetadataIdComponentState';
import { shouldCompactRecordTableFirstColumnComponentState } from '@/object-record/record-table/states/shouldCompactRecordTableFirstColumnComponentState';
import { computeLastRecordTableColumnWidth } from '@/object-record/record-table/utils/computeLastRecordTableColumnWidth';
import { computeVisibleRecordFieldsWidthOnTable } from '@/object-record/record-table/utils/computeVisibleRecordFieldsWidthOnTable';
import { getRecordTableColumnFieldWidthCSSVariableName } from '@/object-record/record-table/utils/getRecordTableColumnFieldWidthCSSVariableName';
import { updateRecordTableCSSVariable } from '@/object-record/record-table/utils/updateRecordTableCSSVariable';
import { RECORD_TABLE_VIRTUALIZATION_BODY_PLACEHOLDER_WIDTH_CSS_VARIABLE_NAME } from '@/object-record/record-table/virtualization/components/RecordTableVirtualizedBodyPlaceholder';
import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useEffect } from 'react';
import { isDefined } from 'twenty-shared/utils';
@@ -15,15 +25,47 @@ export const RecordTableColumnWidthEffect = () => {
resizedFieldMetadataIdComponentState,
);
const { lastColumnWidth } = useRecordTableLastColumnWidthToFill();
const { visibleRecordFields } = useRecordTableContextOrThrow();
const shouldCompactRecordTableFirstColumn = useRecoilComponentValue(
shouldCompactRecordTableFirstColumnComponentState,
);
const recordTableWidth = useRecoilComponentValue(
recordTableWidthComponentState,
);
useEffect(() => {
if (isDefined(resizedFieldMetadataItemId)) {
return;
}
const { lastColumnWidth } = computeLastRecordTableColumnWidth({
recordFields: visibleRecordFields,
shouldCompactFirstColumn: shouldCompactRecordTableFirstColumn,
tableWidth: recordTableWidth,
});
const { visibleRecordFieldsWidth } = computeVisibleRecordFieldsWidthOnTable(
{
shouldCompactFirstColumn: shouldCompactRecordTableFirstColumn,
visibleRecordFields,
},
);
const totalTableBodyWidth =
visibleRecordFieldsWidth +
RECORD_TABLE_COLUMN_DRAG_AND_DROP_WIDTH +
RECORD_TABLE_COLUMN_CHECKBOX_WIDTH +
RECORD_TABLE_COLUMN_ADD_COLUMN_BUTTON_WIDTH +
lastColumnWidth +
visibleRecordFields.length;
updateRecordTableCSSVariable(
RECORD_TABLE_VIRTUALIZATION_BODY_PLACEHOLDER_WIDTH_CSS_VARIABLE_NAME,
`${totalTableBodyWidth}px`,
);
updateRecordTableCSSVariable(
RECORD_TABLE_COLUMN_LAST_EMPTY_COLUMN_WIDTH_VARIABLE_NAME,
`${lastColumnWidth}px`,
@@ -40,7 +82,27 @@ export const RecordTableColumnWidthEffect = () => {
`${recordField.size}px`,
);
}
}, [lastColumnWidth, resizedFieldMetadataItemId, visibleRecordFields]);
if (shouldCompactRecordTableFirstColumn) {
updateRecordTableCSSVariable(
getRecordTableColumnFieldWidthCSSVariableName(0),
`${RECORD_TABLE_LABEL_IDENTIFIER_COLUMN_WIDTH_ON_MOBILE}px`,
);
} else {
const firstColumnWidth =
visibleRecordFields[0]?.size ?? RECORD_TABLE_COLUMN_MIN_WIDTH;
updateRecordTableCSSVariable(
getRecordTableColumnFieldWidthCSSVariableName(0),
`${firstColumnWidth}px`,
);
}
}, [
resizedFieldMetadataItemId,
visibleRecordFields,
recordTableWidth,
shouldCompactRecordTableFirstColumn,
]);
return null;
};
@@ -4,7 +4,6 @@ import { RecordTableStyleWrapper } from '@/object-record/record-table/components
import { RecordTableWidthEffect } from '@/object-record/record-table/components/RecordTableWidthEffect';
import { RECORD_TABLE_HTML_ID } from '@/object-record/record-table/constants/RecordTableHtmlId';
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
import { useRecordTableLastColumnWidthToFill } from '@/object-record/record-table/hooks/useRecordTableLastColumnWidthToFill';
import { RecordTableNoRecordGroupBody } from '@/object-record/record-table/record-table-body/components/RecordTableNoRecordGroupBody';
import { RecordTableRecordGroupsBody } from '@/object-record/record-table/record-table-body/components/RecordTableRecordGroupsBody';
import { RecordTableHeader } from '@/object-record/record-table/record-table-header/components/RecordTableHeader';
@@ -72,8 +71,6 @@ export const RecordTableContent = ({
const { visibleRecordFields } = useRecordTableContextOrThrow();
const { lastColumnWidth } = useRecordTableLastColumnWidthToFill();
const setRecordTableHoverPosition = useSetRecoilComponentState(
recordTableHoverPositionComponentState,
);
@@ -103,7 +100,6 @@ export const RecordTableContent = ({
ref={tableBodyRef}
isDragging={isDragging}
visibleRecordFields={visibleRecordFields}
lastColumnWidth={lastColumnWidth}
id={RECORD_TABLE_HTML_ID}
onMouseLeave={handleMouseLeave}
hasRecordGroups={hasRecordGroups}
@@ -8,17 +8,16 @@ import { RECORD_TABLE_COLUMN_DRAG_AND_DROP_WIDTH } from '@/object-record/record-
import { RECORD_TABLE_HTML_ID } from '@/object-record/record-table/constants/RecordTableHtmlId';
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
import { RecordTableEmptyState } from '@/object-record/record-table/empty-state/components/RecordTableEmptyState';
import { useRecordTableLastColumnWidthToFill } from '@/object-record/record-table/hooks/useRecordTableLastColumnWidthToFill';
import { RecordTableHeader } from '@/object-record/record-table/record-table-header/components/RecordTableHeader';
import { recordTableWidthComponentState } from '@/object-record/record-table/states/recordTableWidthComponentState';
import { resizedFieldMetadataIdComponentState } from '@/object-record/record-table/states/resizedFieldMetadataIdComponentState';
import { resizeFieldOffsetComponentState } from '@/object-record/record-table/states/resizeFieldOffsetComponentState';
import { shouldCompactRecordTableFirstColumnComponentState } from '@/object-record/record-table/states/shouldCompactRecordTableFirstColumnComponentState';
import { computeVisibleRecordFieldsWidthOnTable } from '@/object-record/record-table/utils/computeVisibleRecordFieldsWidthOnTable';
import { RecordTableVirtualizedDataChangedEffect } from '@/object-record/record-table/virtualization/components/RecordTableVirtualizedDataChangedEffect';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import styled from '@emotion/styled';
import { isDefined } from 'twenty-shared/utils';
import { useIsMobile } from 'twenty-ui/utilities';
const StyledEmptyStateContainer = styled.div<{ width: number }>`
height: 100%;
@@ -47,7 +46,9 @@ export const RecordTableEmpty = ({ tableBodyRef }: RecordTableEmptyProps) => {
const isResizing = isDefined(resizedFieldMetadataId);
const isMobile = useIsMobile();
const shouldCompactRecordTableFirstColumn = useRecoilComponentValue(
shouldCompactRecordTableFirstColumnComponentState,
);
const resizeOffsetToAddOnlyIfItMakesTableContainerGrow = isResizing
? resizeFieldOffset > 0
@@ -58,12 +59,10 @@ export const RecordTableEmpty = ({ tableBodyRef }: RecordTableEmptyProps) => {
const totalColumnsBorderWidth = visibleRecordFields.length;
const { visibleRecordFieldsWidth } = computeVisibleRecordFieldsWidthOnTable({
isMobile,
shouldCompactFirstColumn: shouldCompactRecordTableFirstColumn,
visibleRecordFields,
});
const { lastColumnWidth } = useRecordTableLastColumnWidthToFill();
const emptyTableContainerComputedWidth =
visibleRecordFieldsWidth +
RECORD_TABLE_COLUMN_CHECKBOX_WIDTH +
@@ -86,7 +85,6 @@ export const RecordTableEmpty = ({ tableBodyRef }: RecordTableEmptyProps) => {
<RecordTableStyleWrapper
ref={tableBodyRef}
visibleRecordFields={visibleRecordFields}
lastColumnWidth={lastColumnWidth}
id={RECORD_TABLE_HTML_ID}
hasRecordGroups={hasRecordGroups}
>
@@ -1,23 +1,35 @@
import { shouldCompactRecordIndexLabelIdentifierComponentState } from '@/object-record/record-index/states/shouldCompactRecordIndexLabelIdentifierComponentState';
import { RECORD_TABLE_HORIZONTAL_SCROLL_SHADOW_VISIBILITY_CSS_VARIABLE_NAME } from '@/object-record/record-table/constants/RecordTableHorizontalScrollShadowVisibilityCssVariableName';
import { RECORD_TABLE_VERTICAL_SCROLL_SHADOW_VISIBILITY_CSS_VARIABLE_NAME } from '@/object-record/record-table/constants/RecordTableVerticalScrollShadowVisibilityCssVariableName';
import { isRecordTableScrolledHorizontallyComponentState } from '@/object-record/record-table/states/isRecordTableScrolledHorizontallyComponentState';
import { isRecordTableScrolledVerticallyComponentState } from '@/object-record/record-table/states/isRecordTableScrolledVerticallyComponentState';
import { shouldCompactRecordTableFirstColumnComponentState } from '@/object-record/record-table/states/shouldCompactRecordTableFirstColumnComponentState';
import { updateRecordTableCSSVariable } from '@/object-record/record-table/utils/updateRecordTableCSSVariable';
import { useScrollWrapperHTMLElement } from '@/ui/utilities/scroll/hooks/useScrollWrapperHTMLElement';
import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState';
import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
import { useEffect } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { useIsMobile } from 'twenty-ui/utilities';
export const RecordTableScrollAndZIndexEffect = () => {
const { scrollWrapperHTMLElement } = useScrollWrapperHTMLElement();
const isMobile = useIsMobile();
const [
isRecordTableScrolledHorizontally,
setIsRecordTableScrolledHorizontally,
] = useRecoilComponentState(isRecordTableScrolledHorizontallyComponentState);
const setShouldCompactRecordTableFirstColumn = useSetRecoilComponentState(
shouldCompactRecordTableFirstColumnComponentState,
);
const setShouldCompactRecordIndexLabelIdentifier = useSetRecoilComponentState(
shouldCompactRecordIndexLabelIdentifierComponentState,
);
const [isRecordTableScrolledVertically, setIsRecordTableScrolledVertically] =
useRecoilComponentState(isRecordTableScrolledVerticallyComponentState);
@@ -57,6 +69,16 @@ export const RecordTableScrollAndZIndexEffect = () => {
RECORD_TABLE_HORIZONTAL_SCROLL_SHADOW_VISIBILITY_CSS_VARIABLE_NAME,
newVisibilityOfShadows,
);
if (isMobile) {
if (newIsScrolledHorizontally) {
setShouldCompactRecordTableFirstColumn(true);
setShouldCompactRecordIndexLabelIdentifier(true);
} else {
setShouldCompactRecordTableFirstColumn(false);
setShouldCompactRecordIndexLabelIdentifier(false);
}
}
}
};
@@ -71,6 +93,9 @@ export const RecordTableScrollAndZIndexEffect = () => {
isRecordTableScrolledHorizontally,
setIsRecordTableScrolledVertically,
setIsRecordTableScrolledHorizontally,
isMobile,
setShouldCompactRecordTableFirstColumn,
setShouldCompactRecordIndexLabelIdentifier,
]);
return <></>;
@@ -10,7 +10,6 @@ import { RECORD_TABLE_COLUMN_LAST_EMPTY_COLUMN_WIDTH_VARIABLE_NAME } from '@/obj
import { RECORD_TABLE_COLUMN_WITH_GROUP_LAST_EMPTY_COLUMN_WIDTH_CLASS_NAME } from '@/object-record/record-table/constants/RecordTableColumnWithGroupLastEmptyColumnWidthClassName';
import { RECORD_TABLE_COLUMN_WITH_GROUP_LAST_EMPTY_COLUMN_WIDTH_VARIABLE_NAME } from '@/object-record/record-table/constants/RecordTableColumnWithGroupLastEmptyColumnWidthVariableName';
import { RECORD_TABLE_HORIZONTAL_SCROLL_SHADOW_VISIBILITY_CSS_VARIABLE_NAME } from '@/object-record/record-table/constants/RecordTableHorizontalScrollShadowVisibilityCssVariableName';
import { RECORD_TABLE_LABEL_IDENTIFIER_COLUMN_WIDTH_ON_MOBILE } from '@/object-record/record-table/constants/RecordTableLabelIdentifierColumnWidthOnMobile';
import { RECORD_TABLE_VERTICAL_SCROLL_SHADOW_VISIBILITY_CSS_VARIABLE_NAME } from '@/object-record/record-table/constants/RecordTableVerticalScrollShadowVisibilityCssVariableName';
import { TABLE_Z_INDEX } from '@/object-record/record-table/constants/TableZIndex';
@@ -18,7 +17,6 @@ import { getRecordTableColumnFieldWidthClassName } from '@/object-record/record-
import { getRecordTableColumnFieldWidthCSSVariableName } from '@/object-record/record-table/utils/getRecordTableColumnFieldWidthCSSVariableName';
import { css, type Theme } from '@emotion/react';
import styled from '@emotion/styled';
import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
export const VerticalScrollBoxShadowCSS = ({ theme }: { theme: Theme }) => css`
&::before {
@@ -64,7 +62,6 @@ export const HorizontalScrollBoxShadowCSS = ({
const StyledTable = styled.div<{
isDragging?: boolean;
visibleRecordFields: RecordField[];
lastColumnWidth: number;
hasRecordGroups: boolean;
}>`
& > * {
@@ -128,12 +125,6 @@ const StyledTable = styled.div<{
: TABLE_Z_INDEX.headerColumns.withoutGroups.headerColumnsSticky};
${HorizontalScrollBoxShadowCSS}
@media (max-width: ${MOBILE_VIEWPORT}px) {
width: ${RECORD_TABLE_LABEL_IDENTIFIER_COLUMN_WIDTH_ON_MOBILE}px;
max-width: ${RECORD_TABLE_LABEL_IDENTIFIER_COLUMN_WIDTH_ON_MOBILE}px;
min-width: ${RECORD_TABLE_LABEL_IDENTIFIER_COLUMN_WIDTH_ON_MOBILE}px;
}
}
div.table-cell:nth-of-type(1) {
@@ -192,7 +183,7 @@ const StyledTable = styled.div<{
max-width: ${RECORD_TABLE_COLUMN_ADD_COLUMN_BUTTON_WIDTH}px;
}
${({ visibleRecordFields, lastColumnWidth }) => {
${({ visibleRecordFields }) => {
let returnedCSS = '';
for (let i = 0; i < visibleRecordFields.length; i++) {
@@ -200,28 +191,13 @@ const StyledTable = styled.div<{
}
for (let i = 0; i < visibleRecordFields.length; i++) {
returnedCSS += `div.${getRecordTableColumnFieldWidthClassName(i)} {
width: var(${getRecordTableColumnFieldWidthCSSVariableName(i)});
min-width: var(${getRecordTableColumnFieldWidthCSSVariableName(i)});
max-width: var(${getRecordTableColumnFieldWidthCSSVariableName(i)});
returnedCSS += `div.${getRecordTableColumnFieldWidthClassName(i)} {
width: var(${getRecordTableColumnFieldWidthCSSVariableName(i)});
min-width: var(${getRecordTableColumnFieldWidthCSSVariableName(i)});
max-width: var(${getRecordTableColumnFieldWidthCSSVariableName(i)});
} \n`;
const isLabelIdentifierColumn = i === 0;
if (isLabelIdentifierColumn) {
returnedCSS += `div.${getRecordTableColumnFieldWidthClassName(i)} {
@media (max-width: ${MOBILE_VIEWPORT}px) {
width: ${RECORD_TABLE_LABEL_IDENTIFIER_COLUMN_WIDTH_ON_MOBILE}px;
max-width: ${RECORD_TABLE_LABEL_IDENTIFIER_COLUMN_WIDTH_ON_MOBILE}px;
min-width: ${RECORD_TABLE_LABEL_IDENTIFIER_COLUMN_WIDTH_ON_MOBILE}px;
}
} \n`;
}
}
returnedCSS += `${RECORD_TABLE_COLUMN_LAST_EMPTY_COLUMN_WIDTH_VARIABLE_NAME}: ${lastColumnWidth}px;`;
returnedCSS += `${RECORD_TABLE_COLUMN_WITH_GROUP_LAST_EMPTY_COLUMN_WIDTH_VARIABLE_NAME}: ${lastColumnWidth}px;`;
return returnedCSS;
}};
@@ -1,8 +1,8 @@
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
import { recordTableWidthComponentState } from '@/object-record/record-table/states/recordTableWidthComponentState';
import { shouldCompactRecordTableFirstColumnComponentState } from '@/object-record/record-table/states/shouldCompactRecordTableFirstColumnComponentState';
import { computeLastRecordTableColumnWidth } from '@/object-record/record-table/utils/computeLastRecordTableColumnWidth';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useIsMobile } from 'twenty-ui/utilities';
export const useRecordTableLastColumnWidthToFill = () => {
const { visibleRecordFields } = useRecordTableContextOrThrow();
@@ -11,12 +11,14 @@ export const useRecordTableLastColumnWidthToFill = () => {
recordTableWidthComponentState,
);
const isMobile = useIsMobile();
const shouldCompactRecordTableFirstColumn = useRecoilComponentValue(
shouldCompactRecordTableFirstColumnComponentState,
);
const { lastColumnWidth } = computeLastRecordTableColumnWidth({
recordFields: visibleRecordFields,
tableWidth: recordTableWidth,
isMobile,
shouldCompactFirstColumn: shouldCompactRecordTableFirstColumn,
});
return {
@@ -4,6 +4,8 @@ import styled from '@emotion/styled';
import { fieldMetadataItemByIdSelector } from '@/object-metadata/states/fieldMetadataItemByIdSelector';
import { isFieldMetadataItemLabelIdentifierSelector } from '@/object-metadata/states/isFieldMetadataItemLabelIdentifierSelector';
import { type RecordField } from '@/object-record/record-field/types/RecordField';
import { shouldCompactRecordTableFirstColumnComponentState } from '@/object-record/record-table/states/shouldCompactRecordTableFirstColumnComponentState';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useRecoilValue } from 'recoil';
import { useIcons } from 'twenty-ui/display';
import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
@@ -69,10 +71,15 @@ export const RecordTableColumnHead = ({
}),
);
// TODO: fix previous behavior with isRecordTableScrolledHorizontally
const shouldCompactRecordTableFirstColumn = useRecoilComponentValue(
shouldCompactRecordTableFirstColumnComponentState,
);
const shouldHideTitle =
shouldCompactRecordTableFirstColumn && isLabelIdentifier;
return (
<StyledTitle hideTitle={isLabelIdentifier}>
<StyledTitle hideTitle={shouldHideTitle}>
<StyledIcon>
<Icon size={theme.icon.size.md} />
</StyledIcon>
@@ -5,10 +5,12 @@ import { RECORD_TABLE_COLUMN_MIN_WIDTH } from '@/object-record/record-table/cons
import { RECORD_TABLE_COLUMN_WITH_GROUP_LAST_EMPTY_COLUMN_WIDTH_VARIABLE_NAME } from '@/object-record/record-table/constants/RecordTableColumnWithGroupLastEmptyColumnWidthVariableName';
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
import { useResetTableRowSelection } from '@/object-record/record-table/hooks/internal/useResetTableRowSelection';
import { useRecordTableLastColumnWidthToFill } from '@/object-record/record-table/hooks/useRecordTableLastColumnWidthToFill';
import { recordTableWidthComponentState } from '@/object-record/record-table/states/recordTableWidthComponentState';
import { resizedFieldMetadataIdComponentState } from '@/object-record/record-table/states/resizedFieldMetadataIdComponentState';
import { resizeFieldOffsetComponentState } from '@/object-record/record-table/states/resizeFieldOffsetComponentState';
import { shouldCompactRecordTableFirstColumnComponentState } from '@/object-record/record-table/states/shouldCompactRecordTableFirstColumnComponentState';
import { computeLastRecordTableColumnWidth } from '@/object-record/record-table/utils/computeLastRecordTableColumnWidth';
import { getRecordTableColumnFieldWidthCSSVariableName } from '@/object-record/record-table/utils/getRecordTableColumnFieldWidthCSSVariableName';
import { updateRecordTableCSSVariable } from '@/object-record/record-table/utils/updateRecordTableCSSVariable';
import { useDragSelect } from '@/ui/utilities/drag-select/hooks/useDragSelect';
@@ -16,6 +18,7 @@ import { useTrackPointer } from '@/ui/utilities/pointer-event/hooks/useTrackPoin
import { type PointerEventListener } from '@/ui/utilities/pointer-event/types/PointerEventListener';
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue';
import { useSaveRecordFields } from '@/views/hooks/useSaveRecordFields';
@@ -55,7 +58,13 @@ export const useResizeTableHeader = () => {
const { updateRecordField } = useUpdateRecordField();
const { lastColumnWidth } = useRecordTableLastColumnWidthToFill();
const recordTableWidth = useRecoilComponentValue(
recordTableWidthComponentState,
);
const shouldCompactRecordTableFirstColumn = useRecoilComponentValue(
shouldCompactRecordTableFirstColumnComponentState,
);
const handleResizeHandlerStart = useCallback<PointerEventListener>(
({ x }) => {
@@ -90,6 +99,12 @@ export const useResizeTableHeader = () => {
`${newWidth}px`,
);
const { lastColumnWidth } = computeLastRecordTableColumnWidth({
recordFields: visibleRecordFields,
shouldCompactFirstColumn: shouldCompactRecordTableFirstColumn,
tableWidth: recordTableWidth,
});
const newLastColumnWidth = lastColumnWidth - newResizeOffset;
updateRecordTableCSSVariable(
@@ -110,11 +125,12 @@ export const useResizeTableHeader = () => {
setResizeFieldOffset(x - initialPointerPositionX);
},
[
setResizeFieldOffset,
initialPointerPositionX,
recordField,
lastColumnWidth,
visibleRecordFields,
shouldCompactRecordTableFirstColumn,
recordTableWidth,
setResizeFieldOffset,
],
);
@@ -0,0 +1,9 @@
import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext';
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
export const shouldCompactRecordTableFirstColumnComponentState =
createComponentState<boolean>({
key: 'shouldCompactRecordTableFirstColumnComponentState',
componentInstanceContext: RecordTableComponentInstanceContext,
defaultValue: false,
});
@@ -7,14 +7,14 @@ import { computeVisibleRecordFieldsWidthOnTable } from '@/object-record/record-t
export const computeLastRecordTableColumnWidth = ({
recordFields,
tableWidth,
isMobile,
shouldCompactFirstColumn,
}: {
recordFields: Pick<RecordField, 'size'>[];
tableWidth: number;
isMobile: boolean;
shouldCompactFirstColumn: boolean;
}) => {
const { visibleRecordFieldsWidth } = computeVisibleRecordFieldsWidthOnTable({
isMobile,
shouldCompactFirstColumn,
visibleRecordFields: recordFields,
});
@@ -3,10 +3,10 @@ import { RECORD_TABLE_LABEL_IDENTIFIER_COLUMN_WIDTH_ON_MOBILE } from '@/object-r
import { sumByProperty } from 'twenty-shared/utils';
export const computeVisibleRecordFieldsWidthOnTable = ({
isMobile,
shouldCompactFirstColumn,
visibleRecordFields,
}: {
isMobile: boolean;
shouldCompactFirstColumn: boolean;
visibleRecordFields: Pick<RecordField, 'size'>[];
}) => {
const visibleRecordFieldsWithoutFirst = visibleRecordFields.slice(1);
@@ -18,12 +18,14 @@ export const computeVisibleRecordFieldsWidthOnTable = ({
const sumWithAllFields = visibleRecordFields.reduce(sumByProperty('size'), 0);
const sumForMobile =
const sumForCompactedFirstColumn =
RECORD_TABLE_LABEL_IDENTIFIER_COLUMN_WIDTH_ON_MOBILE + sumWithoutFirstField;
const sumForNonMobile = sumWithAllFields;
const sumForNoCompactColumn = sumWithAllFields;
const visibleRecordFieldsWidth = isMobile ? sumForMobile : sumForNonMobile;
const visibleRecordFieldsWidth = shouldCompactFirstColumn
? sumForCompactedFirstColumn
: sumForNoCompactColumn;
return {
visibleRecordFieldsWidth,
@@ -1,54 +1,30 @@
import { RECORD_TABLE_COLUMN_ADD_COLUMN_BUTTON_WIDTH } from '@/object-record/record-table/constants/RecordTableColumnAddColumnButtonWidth';
import { RECORD_TABLE_COLUMN_CHECKBOX_WIDTH } from '@/object-record/record-table/constants/RecordTableColumnCheckboxWidth';
import { RECORD_TABLE_COLUMN_DRAG_AND_DROP_WIDTH } from '@/object-record/record-table/constants/RecordTableColumnDragAndDropWidth';
import { RECORD_TABLE_ROW_HEIGHT } from '@/object-record/record-table/constants/RecordTableRowHeight';
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
import { useRecordTableLastColumnWidthToFill } from '@/object-record/record-table/hooks/useRecordTableLastColumnWidthToFill';
import { computeVisibleRecordFieldsWidthOnTable } from '@/object-record/record-table/utils/computeVisibleRecordFieldsWidthOnTable';
import { totalNumberOfRecordsToVirtualizeComponentState } from '@/object-record/record-table/virtualization/states/totalNumberOfRecordsToVirtualizeComponentState';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { styled } from '@linaria/react';
import { isDefined } from 'twenty-shared/utils';
import { useIsMobile } from 'twenty-ui/utilities';
export const RECORD_TABLE_VIRTUALIZATION_BODY_PLACEHOLDER_WIDTH_CSS_VARIABLE_NAME =
'--record-table-virtualization-body-placeholder-width';
const StyledVirtualizationContainer = styled.div<{
width: number;
height: number;
}>`
height: ${({ height }) => height}px;
width: ${({ width }) => width}px;
width: var(
${RECORD_TABLE_VIRTUALIZATION_BODY_PLACEHOLDER_WIDTH_CSS_VARIABLE_NAME}
);
`;
export const RecordTableVirtualizedBodyPlaceholder = () => {
const { visibleRecordFields } = useRecordTableContextOrThrow();
const isMobile = useIsMobile();
const { visibleRecordFieldsWidth } = computeVisibleRecordFieldsWidthOnTable({
isMobile,
visibleRecordFields,
});
const totalNumberOfRecordsToVirtualize = useRecoilComponentValue(
totalNumberOfRecordsToVirtualizeComponentState,
);
const { lastColumnWidth } = useRecordTableLastColumnWidthToFill();
const totalWidth =
visibleRecordFieldsWidth +
RECORD_TABLE_COLUMN_DRAG_AND_DROP_WIDTH +
RECORD_TABLE_COLUMN_CHECKBOX_WIDTH +
RECORD_TABLE_COLUMN_ADD_COLUMN_BUTTON_WIDTH +
lastColumnWidth +
visibleRecordFields.length;
const totalHeight = isDefined(totalNumberOfRecordsToVirtualize)
? totalNumberOfRecordsToVirtualize * (RECORD_TABLE_ROW_HEIGHT + 1)
: 0;
return (
<StyledVirtualizationContainer width={totalWidth} height={totalHeight} />
);
return <StyledVirtualizationContainer height={totalHeight} />;
};