diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/display/components/ChipFieldDisplay.tsx b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/display/components/ChipFieldDisplay.tsx index 6f3639143c..4b921623d1 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/display/components/ChipFieldDisplay.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/display/components/ChipFieldDisplay.tsx @@ -1,7 +1,5 @@ import { RecordChip } from '@/object-record/components/RecordChip'; import { useChipFieldDisplay } from '@/object-record/record-field/ui/meta-types/hooks/useChipFieldDisplay'; -import { isRecordTableScrolledLeftComponentState } from '@/object-record/record-table/states/isRecordTableScrolledLeftComponentState'; -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'; @@ -18,12 +16,9 @@ export const ChipFieldDisplay = () => { } = useChipFieldDisplay(); const isMobile = useIsMobile(); - const isRecordTableScrolledLeftComponent = useRecoilComponentValue( - isRecordTableScrolledLeftComponentState, - ); - const isLabelIdentifierCompact = - isMobile && !isRecordTableScrolledLeftComponent; + // TODO: reimplement scrolled horizontally here. + const isLabelIdentifierCompact = isMobile; if (!isDefined(recordValue)) { return null; diff --git a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableContent.tsx b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableContent.tsx index 803c142a56..55d2dfdc89 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableContent.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableContent.tsx @@ -1,6 +1,6 @@ import { RecordTableStickyBottomEffect } from '@/object-record/record-table/components/RecordTableStickyBottomEffect'; import { RecordTableStickyEffect } from '@/object-record/record-table/components/RecordTableStickyEffect'; -import { StyledTable } from '@/object-record/record-table/components/RecordTableStyles'; +import { StyledTableDiv } from '@/object-record/record-table/components/RecordTableStyles'; 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'; @@ -12,7 +12,7 @@ import styled from '@emotion/styled'; import { useRef, useState } from 'react'; import { useRecoilCallback } from 'recoil'; -const StyledTableWithPointerEvents = styled(StyledTable)<{ +const StyledTableWithPointerEvents = styled(StyledTableDiv)<{ isDragging: boolean; }>` & > * { diff --git a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableEmpty.tsx b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableEmpty.tsx index 8209cbb2ad..a92d21dac5 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableEmpty.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableEmpty.tsx @@ -1,4 +1,4 @@ -import { StyledTable } from '@/object-record/record-table/components/RecordTableStyles'; +import { StyledTableDiv } from '@/object-record/record-table/components/RecordTableStyles'; import { RecordTableEmptyState } from '@/object-record/record-table/empty-state/components/RecordTableEmptyState'; import { RecordTableHeader } from '@/object-record/record-table/record-table-header/components/RecordTableHeader'; import styled from '@emotion/styled'; @@ -14,9 +14,9 @@ export interface RecordTableEmptyProps { export const RecordTableEmpty = ({ tableBodyRef }: RecordTableEmptyProps) => ( - + - + ); diff --git a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableStickyEffect.tsx b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableStickyEffect.tsx index 4b9ba847a3..5f51863498 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableStickyEffect.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableStickyEffect.tsx @@ -1,6 +1,7 @@ import { useEffect } from 'react'; -import { isRecordTableScrolledLeftComponentState } from '@/object-record/record-table/states/isRecordTableScrolledLeftComponentState'; +import { isRecordTableScrolledHorizontallyComponentState } from '@/object-record/record-table/states/isRecordTableScrolledHorizontallyComponentState'; +import { isRecordTableScrolledVerticallyComponentState } from '@/object-record/record-table/states/isRecordTableScrolledVerticallyComponentState'; import { scrollWrapperScrollLeftComponentState } from '@/ui/utilities/scroll/states/scrollWrapperScrollLeftComponentState'; import { scrollWrapperScrollTopComponentState } from '@/ui/utilities/scroll/states/scrollWrapperScrollTopComponentState'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; @@ -11,7 +12,13 @@ export const RecordTableStickyEffect = () => { scrollWrapperScrollTopComponentState, ); + const setIsRecordTableScrolledVertically = useSetRecoilComponentState( + isRecordTableScrolledVerticallyComponentState, + ); + useEffect(() => { + setIsRecordTableScrolledVertically(scrollTop > 0); + if (scrollTop > 0) { document .getElementById('record-table-header') @@ -21,18 +28,19 @@ export const RecordTableStickyEffect = () => { .getElementById('record-table-header') ?.classList.remove('header-sticky'); } - }, [scrollTop]); + }, [scrollTop, setIsRecordTableScrolledVertically]); const scrollLeft = useRecoilComponentValue( scrollWrapperScrollLeftComponentState, ); - const setIsRecordTableScrolledLeft = useSetRecoilComponentState( - isRecordTableScrolledLeftComponentState, + const setIsRecordTableScrolledHorizontally = useSetRecoilComponentState( + isRecordTableScrolledHorizontallyComponentState, ); useEffect(() => { - setIsRecordTableScrolledLeft(scrollLeft === 0); + setIsRecordTableScrolledHorizontally(scrollLeft > 0); + if (scrollLeft > 0) { document .getElementById('record-table-body') @@ -54,7 +62,7 @@ export const RecordTableStickyEffect = () => { .getElementById('record-table-footer') ?.classList.remove('first-columns-sticky'); } - }, [scrollLeft, setIsRecordTableScrolledLeft]); + }, [scrollLeft, setIsRecordTableScrolledHorizontally]); return <>; }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableStyles.tsx b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableStyles.tsx index bcf481903b..4d67f8eb86 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableStyles.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableStyles.tsx @@ -1,9 +1,8 @@ import styled from '@emotion/styled'; -export const StyledTable = styled.table` +export const StyledTableDiv = styled.div` border-radius: ${({ theme }) => theme.border.radius.sm}; border-spacing: 0; - table-layout: fixed; width: 100%; .footer-sticky tr:nth-last-of-type(2) td { diff --git a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableWithWrappers.tsx b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableWithWrappers.tsx index 6cd4f31456..9d4967aef6 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableWithWrappers.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableWithWrappers.tsx @@ -17,7 +17,7 @@ const StyledTableContainer = styled.div` display: flex; flex-direction: column; position: relative; - width: 100%; + width: fit-content; height: 100%; `; diff --git a/packages/twenty-front/src/modules/object-record/record-table/constants/TableZIndex.ts b/packages/twenty-front/src/modules/object-record/record-table/constants/TableZIndex.ts index 662a488b95..c717959611 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/constants/TableZIndex.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/constants/TableZIndex.ts @@ -1,17 +1,37 @@ export const TABLE_Z_INDEX = { base: 1, cell: { - default: 1, - sticky: 4, - editMode: 6, + default: 3, + sticky: 10, + editMode: 20, }, footer: { - default: 4, - stickyColumn: 5, + default: 12, + stickyColumn: 20, }, - header: { - default: 6, - stickyColumn: 10, + noScrollAtAll: { + hoverPortalCell: 17, + headerRow: 13, + headerColumnsSticky: 16, + headerColumnsNormal: 15, + }, + scrolledBothVerticallyAndHorizontally: { + headerColumnsSticky: 13, + headerColumnsNormal: 12, + headerRow: 11, + hoverPortalCell: 2, + }, + scrolledHorizontallyOnly: { + headerColumnsSticky: 13, + headerColumnsNormal: 12, + headerRow: 11, + hoverPortalCell: 9, + }, + scrolledVerticallyOnly: { + headerColumnsSticky: 14, + headerColumnsNormal: 13, + headerRow: 12, + hoverPortalCell: 11, }, columnGrip: 30, }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBody.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBody.tsx index e9bc21fde5..e7d8ecc7a7 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBody.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBody.tsx @@ -1,38 +1,32 @@ import styled from '@emotion/styled'; import { TABLE_Z_INDEX } from '@/object-record/record-table/constants/TableZIndex'; +import { MOBILE_VIEWPORT } from 'twenty-ui/theme'; const StyledTbody = styled.tbody` - &.first-columns-sticky { - td:nth-of-type(1) { - position: sticky; - left: 0; - z-index: ${TABLE_Z_INDEX.cell.sticky}; - transition: transform 0.3s ease; - } - td:nth-of-type(2) { - position: sticky; - left: 11px; - z-index: ${TABLE_Z_INDEX.cell.sticky}; - transition: transform 0.3s ease; - } - tr:not(:last-child) td:nth-of-type(3) { - // Last row is aggregate footer - position: sticky; - left: 39px; - z-index: ${TABLE_Z_INDEX.cell.sticky}; - transition: transform 0.3s ease; + // TODO: re-implement horizontal scroll here after table have been refactored to divs + td:nth-of-type(1) { + position: sticky; + left: 0px; + z-index: ${TABLE_Z_INDEX.cell.sticky}; + } - &:not(.disable-shadow)::after { - content: ''; - position: absolute; - top: -1px; - height: calc(100% + 2px); - width: 4px; - right: 0px; - box-shadow: ${({ theme }) => theme.boxShadow.light}; - clip-path: inset(0px -4px 0px 0px); - } + td:nth-of-type(2) { + position: sticky; + left: 16px; + z-index: ${TABLE_Z_INDEX.cell.sticky}; + } + + tr:not(:last-child) td:nth-of-type(3) { + position: sticky; + left: 49px; + z-index: ${TABLE_Z_INDEX.cell.sticky}; + } + + td:nth-of-type(3) { + @media (max-width: ${MOBILE_VIEWPORT}px) { + width: ${38}px; + max-width: ${38}px; } } `; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyDroppable.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyDroppable.tsx index 0593f5e6f3..bbadbab21a 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyDroppable.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyDroppable.tsx @@ -2,10 +2,19 @@ import { RecordTableBody } from '@/object-record/record-table/record-table-body/ import { RecordTableBodyDroppableContextProvider } from '@/object-record/record-table/record-table-body/contexts/RecordTableBodyDroppableContext'; import { recordTableHoverPositionComponentState } from '@/object-record/record-table/states/recordTableHoverPositionComponentState'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; +import styled from '@emotion/styled'; import { Droppable } from '@hello-pangea/dnd'; import { type ReactNode, useState } from 'react'; import { v4 } from 'uuid'; +const StyledTable = styled.table` + table-layout: fixed; + + border-radius: ${({ theme }) => theme.border.radius.sm}; + border-spacing: 0; + width: 100%; +`; + type RecordTableBodyDroppableProps = { children: ReactNode; recordGroupId?: string; @@ -30,19 +39,21 @@ export const RecordTableBodyDroppable = ({ isDropDisabled={isDropDisabled} > {(provided) => ( - setRecordTableHoverPosition(null)} - > - + setRecordTableHoverPosition(null)} > - {children} - - + + {children} + + + )} ); diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellGrip.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellGrip.tsx index a7da7822b6..28e12c2884 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellGrip.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellGrip.tsx @@ -5,7 +5,7 @@ import { useRecordTableRowDraggableContextOrThrow } from '@/object-record/record import { RecordTableTd } from '@/object-record/record-table/record-table-cell/components/RecordTableTd'; import { IconListViewGrip } from 'twenty-ui/input'; -export const TABLE_CELL_GRIP_WIDTH = '16px'; +export const TABLE_CELL_GRIP_WIDTH = 16; const StyledContainer = styled.div` height: 32px; @@ -40,6 +40,7 @@ export const RecordTableCellGrip = () => { data-select-disable hasRightBorder={false} hasBottomBorder={false} + width={TABLE_CELL_GRIP_WIDTH} > diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellHoveredPortal.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellHoveredPortal.tsx index c968180845..75db4572cb 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellHoveredPortal.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellHoveredPortal.tsx @@ -6,12 +6,15 @@ import styled from '@emotion/styled'; import { FieldDisplay } from '@/object-record/record-field/ui/components/FieldDisplay'; import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext'; import { useIsFieldInputOnly } from '@/object-record/record-field/ui/hooks/useIsFieldInputOnly'; +import { TABLE_Z_INDEX } from '@/object-record/record-table/constants/TableZIndex'; 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'; import { RecordTableCellEditMode } from '@/object-record/record-table/record-table-cell/components/RecordTableCellEditMode'; import { RecordTableCellFieldInput } from '@/object-record/record-table/record-table-cell/components/RecordTableCellFieldInput'; import { isRecordTableRowActiveComponentFamilyState } from '@/object-record/record-table/states/isRecordTableRowActiveComponentFamilyState'; +import { isRecordTableScrolledHorizontallyComponentState } from '@/object-record/record-table/states/isRecordTableScrolledHorizontallyComponentState'; +import { isRecordTableScrolledVerticallyComponentState } from '@/object-record/record-table/states/isRecordTableScrolledVerticallyComponentState'; import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue'; import { useContext } from 'react'; import { BORDER_COMMON } from 'twenty-ui/theme'; @@ -20,6 +23,7 @@ import { useIsMobile } from 'twenty-ui/utilities'; const StyledRecordTableCellHoveredPortalContent = styled.div<{ isReadOnly: boolean; isRowActive: boolean; + zIndex: number; }>` align-items: center; background: ${({ theme }) => theme.background.transparent.secondary}; @@ -42,6 +46,8 @@ const StyledRecordTableCellHoveredPortalContent = styled.div<{ position: relative; user-select: none; + + z-index: ${({ zIndex }) => zIndex}; `; const RecordTableCellHoveredPortalContent = () => { @@ -67,10 +73,28 @@ const RecordTableCellHoveredPortalContent = () => { rowIndex, ); + const isRecordTableScrolledHorizontally = useRecoilComponentValue( + isRecordTableScrolledHorizontallyComponentState, + ); + + const isRecordTableScrolledVertically = useRecoilComponentValue( + isRecordTableScrolledVerticallyComponentState, + ); + + const computedZIndex = + isRecordTableScrolledHorizontally && isRecordTableScrolledVertically + ? TABLE_Z_INDEX.scrolledBothVerticallyAndHorizontally.hoverPortalCell + : isRecordTableScrolledHorizontally + ? TABLE_Z_INDEX.scrolledHorizontallyOnly.hoverPortalCell + : isRecordTableScrolledVertically + ? TABLE_Z_INDEX.scrolledVerticallyOnly.hoverPortalCell + : TABLE_Z_INDEX.noScrollAtAll.hoverPortalCell; + return ( {isFieldInputOnly ? ( diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableTd.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableTd.tsx index 376f946174..b20e29c745 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableTd.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableTd.tsx @@ -1,22 +1,23 @@ import { type DraggableProvidedDragHandleProps } from '@hello-pangea/dnd'; import { styled } from '@linaria/react'; import { type ReactNode, useContext } from 'react'; -import { MOBILE_VIEWPORT, ThemeContext } from 'twenty-ui/theme'; +import { ThemeContext } from 'twenty-ui/theme'; -export const RECORD_TABLE_TD_WIDTH = '32px'; +export const DEFAULT_RECORD_TABLE_TD_WIDTH = 32; const StyledTd = styled.td<{ backgroundColor: string; borderColor: string; isDragging?: boolean; fontColor: string; - sticky?: boolean; - freezeFirstColumns?: boolean; - left?: number; hasRightBorder?: boolean; hasBottomBorder?: boolean; - width?: number; + width: number; }>` + min-width: ${({ width }) => width}px; + width: ${({ width }) => width}px; + max-width: ${({ width }) => width}px; + border-bottom: 1px solid ${({ borderColor, hasBottomBorder, isDragging }) => hasBottomBorder && !isDragging ? borderColor : 'transparent'}; @@ -26,46 +27,31 @@ const StyledTd = styled.td<{ hasRightBorder && !isDragging ? `1px solid ${borderColor}` : 'none'}; padding: 0; - transition: transform 0.3s ease; text-align: left; background: ${({ backgroundColor, isDragging }) => isDragging ? 'transparent' : backgroundColor}; - ${({ freezeFirstColumns }) => - freezeFirstColumns - ? `@media (max-width: ${MOBILE_VIEWPORT}px) { - width: ${RECORD_TABLE_TD_WIDTH}; - max-width: ${RECORD_TABLE_TD_WIDTH}; - }` - : ''} + // TODO: reimplement horizontal scroll here once we have refactored body with divs `; export const RecordTableTd = ({ children, isSelected, isDragging, - sticky, - freezeFirstColumns, - left, hasRightBorder = true, hasBottomBorder = true, - width, - colSpan, + width = DEFAULT_RECORD_TABLE_TD_WIDTH, ...dragHandleProps }: { className?: string; children?: ReactNode; isSelected?: boolean; isDragging?: boolean; - sticky?: boolean; - freezeFirstColumns?: boolean; hasRightBorder?: boolean; hasBottomBorder?: boolean; - left?: number; width?: number; - colSpan?: number; } & (Partial | null)) => { const { theme } = useContext(ThemeContext); @@ -83,13 +69,9 @@ export const RecordTableTd = ({ backgroundColor={tdBackgroundColor} borderColor={borderColor} fontColor={fontColor} - sticky={sticky} - freezeFirstColumns={freezeFirstColumns} - left={left} hasRightBorder={hasRightBorder} hasBottomBorder={hasBottomBorder} width={width} - colSpan={colSpan} // eslint-disable-next-line react/jsx-props-no-spreading {...dragHandleProps} > diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableAggregateFooter.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableAggregateFooter.tsx index 5338d6adc3..36159d6042 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableAggregateFooter.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableAggregateFooter.tsx @@ -20,44 +20,45 @@ const StyledTableRow = styled.tr<{ position: sticky; border: none; - &.footer-sticky { - td { - border-top: ${({ theme }) => `1px solid ${theme.border.color.light}`}; - z-index: ${TABLE_Z_INDEX.footer.default}; - position: sticky; - bottom: 0; - } + // TODO: see how we reimplement horizontal scrolling after all table has been refactored + td { + border-top: ${({ theme }) => `1px solid ${theme.border.color.light}`}; + z-index: ${TABLE_Z_INDEX.footer.default}; + position: sticky; + bottom: 0; } + cursor: pointer; td:nth-of-type(1) { width: ${FIRST_TH_WIDTH}; left: 0; border-top: none; } - &.first-columns-sticky { - td:nth-of-type(1) { - z-index: ${TABLE_Z_INDEX.footer.stickyColumn}; + + td:nth-of-type(1) { + position: sticky; + z-index: ${TABLE_Z_INDEX.footer.stickyColumn}; + } + td:nth-of-type(2) { + position: sticky; + z-index: ${TABLE_Z_INDEX.footer.stickyColumn}; + transition: 0.3s ease; + &::after { + content: ''; + position: absolute; + top: -1px; + height: calc(100% + 2px); + width: 4px; + right: 0px; + box-shadow: ${({ theme }) => theme.boxShadow.light}; + clip-path: inset(0px -4px 0px 0px); } - td:nth-of-type(2) { - position: sticky; - z-index: ${TABLE_Z_INDEX.footer.stickyColumn}; - transition: 0.3s ease; - &::after { - content: ''; - position: absolute; - top: -1px; - height: calc(100% + 2px); - width: 4px; - right: 0px; - box-shadow: ${({ theme }) => theme.boxShadow.light}; - clip-path: inset(0px -4px 0px 0px); - } - @media (max-width: ${MOBILE_VIEWPORT}px) { - width: 34px; - max-width: 34px; - } + @media (max-width: ${MOBILE_VIEWPORT}px) { + width: 38px; + max-width: 38px; } } + background: ${({ theme }) => theme.background.primary}; ${({ hasHorizontalOverflow }) => `.footer-sticky { diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableColumnHead.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableColumnHead.tsx index 05d2b3b0b9..dbe7bac4bd 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableColumnHead.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableColumnHead.tsx @@ -1,7 +1,7 @@ import { css, useTheme } from '@emotion/react'; import styled from '@emotion/styled'; -import { isRecordTableScrolledLeftComponentState } from '@/object-record/record-table/states/isRecordTableScrolledLeftComponentState'; +import { isRecordTableScrolledHorizontallyComponentState } from '@/object-record/record-table/states/isRecordTableScrolledHorizontallyComponentState'; import { fieldMetadataItemByIdSelector } from '@/object-metadata/states/fieldMetadataItemByIdSelector'; import { isFieldMetadataItemLabelIdentifierSelector } from '@/object-metadata/states/isFieldMetadataItemLabelIdentifierSelector'; @@ -61,8 +61,8 @@ export const RecordTableColumnHead = ({ }), ); - const isRecordTableScrolledLeft = useRecoilComponentValue( - isRecordTableScrolledLeftComponentState, + const isRecordTableScrolledHorizontally = useRecoilComponentValue( + isRecordTableScrolledHorizontallyComponentState, ); const { getIcon } = useIcons(); @@ -77,7 +77,9 @@ export const RecordTableColumnHead = ({ ); return ( - + diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeader.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeader.tsx index 563be9bbd8..853d7c1f05 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeader.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeader.tsx @@ -2,99 +2,140 @@ import styled from '@emotion/styled'; import { TABLE_Z_INDEX } from '@/object-record/record-table/constants/TableZIndex'; import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext'; +import { RecordTableHeaderAddColumnButton } from '@/object-record/record-table/record-table-header/components/RecordTableHeaderAddColumnButton'; import { RecordTableHeaderCell } from '@/object-record/record-table/record-table-header/components/RecordTableHeaderCell'; import { RecordTableHeaderCheckboxColumn } from '@/object-record/record-table/record-table-header/components/RecordTableHeaderCheckboxColumn'; import { RecordTableHeaderDragDropColumn } from '@/object-record/record-table/record-table-header/components/RecordTableHeaderDragDropColumn'; import { RecordTableHeaderLastColumn } from '@/object-record/record-table/record-table-header/components/RecordTableHeaderLastColumn'; +import { isRecordTableScrolledHorizontallyComponentState } from '@/object-record/record-table/states/isRecordTableScrolledHorizontallyComponentState'; +import { isRecordTableScrolledVerticallyComponentState } from '@/object-record/record-table/states/isRecordTableScrolledVerticallyComponentState'; +import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { MOBILE_VIEWPORT } from 'twenty-ui/theme'; export const FIRST_TH_WIDTH = '10px'; -const StyledTableHead = styled.thead` +const StyledTableHead = styled.div<{ + stickyColumnZIndex: number; + normalColumnZIndex: number; + entireRowZIndex: number; +}>` cursor: pointer; - th:nth-of-type(1) { - width: ${FIRST_TH_WIDTH}; - left: 0; - border-right-color: ${({ theme }) => theme.background.primary}; + display: flex; + flex-direction: row; + align-items: center; + + height: 32px; + background-color: ${({ theme }) => theme.background.primary}; + + div.header-cell:nth-of-type(n + 3) { + z-index: ${({ normalColumnZIndex }) => normalColumnZIndex}; } - th:nth-of-type(2) { - border-right-color: ${({ theme }) => theme.background.primary}; + div.header-cell:nth-of-type(1) { + position: sticky; + left: 0px; + z-index: ${({ stickyColumnZIndex }) => stickyColumnZIndex}; + transition: 0.3s ease; + background-color: ${({ theme }) => theme.background.primary}; } - &.first-columns-sticky { - th:nth-of-type(1) { - position: sticky; - left: 0; - z-index: ${TABLE_Z_INDEX.header.default}; - transition: 0.3s ease; - } + div.header-cell:nth-of-type(2) { + position: sticky; + left: 17px; + top: 0; + z-index: ${({ stickyColumnZIndex }) => stickyColumnZIndex}; + transition: 0.3s ease; + background-color: ${({ theme }) => theme.background.primary}; + } - th:nth-of-type(2) { - position: sticky; - left: 11px; - z-index: ${TABLE_Z_INDEX.header.default}; - transition: 0.3s ease; - } + div.header-cell:nth-of-type(3) { + position: sticky; + left: 49px; + right: 0; + z-index: ${({ stickyColumnZIndex }) => stickyColumnZIndex}; + transition: 0.3s ease; + background-color: ${({ theme }) => theme.background.primary}; - th:nth-of-type(3) { - position: sticky; - left: 39px; - z-index: ${TABLE_Z_INDEX.header.default}; - transition: 0.3s ease; + // &::after { + // content: ''; + // position: absolute; + // top: -1px; + // height: calc(100% + 2px); + // width: 4px; + // right: 0px; + // box-shadow: ${({ theme }) => theme.boxShadow.light}; + // clip-path: inset(0px -4px 0px 0px); + // } - &::after { - content: ''; - position: absolute; - top: -1px; - height: calc(100% + 2px); - width: 4px; - right: 0px; - box-shadow: ${({ theme }) => theme.boxShadow.light}; - clip-path: inset(0px -4px 0px 0px); - } - - @media (max-width: ${MOBILE_VIEWPORT}px) { - width: 34px; - max-width: 34px; - } + @media (max-width: ${MOBILE_VIEWPORT}px) { + width: 38px; + max-width: 38px; + min-width: 38px; } } - &.header-sticky { - th { - position: sticky; - top: 0; - z-index: ${TABLE_Z_INDEX.header.default}; - } - } - - &.header-sticky.first-columns-sticky { - th:nth-of-type(1), - th:nth-of-type(2), - th:nth-of-type(3) { - z-index: ${TABLE_Z_INDEX.header.stickyColumn}; - } - } + position: sticky; + top: 0px; + z-index: ${({ entireRowZIndex }) => entireRowZIndex}; `; export const RecordTableHeader = () => { const { visibleRecordFields } = useRecordTableContextOrThrow(); + const isRecordTableScrolledHorizontally = useRecoilComponentValue( + isRecordTableScrolledHorizontallyComponentState, + ); + + const isRecordTableScrolledVertically = useRecoilComponentValue( + isRecordTableScrolledVerticallyComponentState, + ); + + const computedStickyColumnZIndex = + isRecordTableScrolledHorizontally && isRecordTableScrolledVertically + ? TABLE_Z_INDEX.scrolledBothVerticallyAndHorizontally.headerColumnsSticky + : isRecordTableScrolledHorizontally + ? TABLE_Z_INDEX.scrolledHorizontallyOnly.headerColumnsSticky + : isRecordTableScrolledVertically + ? TABLE_Z_INDEX.scrolledVerticallyOnly.headerColumnsSticky + : TABLE_Z_INDEX.noScrollAtAll.headerColumnsSticky; + + const computedNormalColumnZIndex = + isRecordTableScrolledHorizontally && isRecordTableScrolledVertically + ? TABLE_Z_INDEX.scrolledBothVerticallyAndHorizontally.headerColumnsNormal + : isRecordTableScrolledHorizontally + ? TABLE_Z_INDEX.scrolledHorizontallyOnly.headerColumnsNormal + : isRecordTableScrolledVertically + ? TABLE_Z_INDEX.scrolledVerticallyOnly.headerColumnsNormal + : TABLE_Z_INDEX.noScrollAtAll.headerColumnsNormal; + + const computedHeaderRowZIndex = + isRecordTableScrolledHorizontally && isRecordTableScrolledVertically + ? TABLE_Z_INDEX.scrolledBothVerticallyAndHorizontally.headerRow + : isRecordTableScrolledHorizontally + ? TABLE_Z_INDEX.scrolledHorizontallyOnly.headerRow + : isRecordTableScrolledVertically + ? TABLE_Z_INDEX.scrolledVerticallyOnly.headerRow + : TABLE_Z_INDEX.noScrollAtAll.headerRow; + return ( - - - - - {visibleRecordFields.map((recordField) => ( - - ))} - - + + + + {visibleRecordFields.map((recordField) => ( + + ))} + + ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeaderAddColumnButton.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeaderAddColumnButton.tsx new file mode 100644 index 0000000000..6e0dc4242b --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeaderAddColumnButton.tsx @@ -0,0 +1,90 @@ +import styled from '@emotion/styled'; + +import { HIDDEN_TABLE_COLUMN_DROPDOWN_ID } from '@/object-record/record-table/constants/HiddenTableColumnDropdownId'; +import { RecordTableHeaderPlusButtonContent } from '@/object-record/record-table/record-table-header/components/RecordTableHeaderPlusButtonContent'; +import { isRecordTableRowActiveComponentFamilyState } from '@/object-record/record-table/states/isRecordTableRowActiveComponentFamilyState'; +import { isRecordTableRowFocusedComponentFamilyState } from '@/object-record/record-table/states/isRecordTableRowFocusedComponentFamilyState'; +import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; +import { useScrollWrapperElement } from '@/ui/utilities/scroll/hooks/useScrollWrapperElement'; +import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue'; +import { useTheme } from '@emotion/react'; +import { IconPlus } from 'twenty-ui/display'; + +const StyledPlusIconHeaderCell = styled.div<{ + isTableWiderThanScreen: boolean; + isFirstRowActiveOrFocused: boolean; +}>` + border-bottom: ${({ isFirstRowActiveOrFocused, theme }) => + isFirstRowActiveOrFocused + ? 'none' + : `1px solid ${theme.border.color.light}`}; + background-color: ${({ theme }) => theme.background.primary}; + + color: ${({ theme }) => theme.font.color.tertiary}; + border-right: ${({ theme }) => theme.border.color.light} !important; + + cursor: default; + + width: 32px; + + z-index: 1; + + &:hover { + background: ${({ theme }) => theme.background.transparent.secondary}; + } +`; + +const StyledPlusIconContainer = styled.div` + align-items: center; + display: flex; + height: 32px; + width: 100%; + justify-content: center; +`; + +const StyledDropdownContainer = styled.div` + cursor: pointer; + width: 100%; +`; + +export const RecordTableHeaderAddColumnButton = () => { + const theme = useTheme(); + + const { scrollWrapperHTMLElement } = useScrollWrapperElement(); + + const isTableWiderThanScreen = + (scrollWrapperHTMLElement?.clientWidth ?? 0) < + (scrollWrapperHTMLElement?.scrollWidth ?? 0); + + const isFirstRowActive = useRecoilComponentFamilyValue( + isRecordTableRowActiveComponentFamilyState, + 0, + ); + + const isFirstRowFocused = useRecoilComponentFamilyValue( + isRecordTableRowFocusedComponentFamilyState, + 0, + ); + + const isFirstRowActiveOrFocused = isFirstRowActive || isFirstRowFocused; + + return ( + + + + + + } + dropdownComponents={} + dropdownPlacement="bottom-start" + /> + + + ); +}; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeaderCell.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeaderCell.tsx index c7e2af5511..8d4feb0fc8 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeaderCell.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeaderCell.tsx @@ -12,7 +12,6 @@ import { useCreateNewIndexRecord } from '@/object-record/record-table/hooks/useC import { RecordTableColumnHeadWithDropdown } from '@/object-record/record-table/record-table-header/components/RecordTableColumnHeadWithDropdown'; import { isRecordTableRowActiveComponentFamilyState } from '@/object-record/record-table/states/isRecordTableRowActiveComponentFamilyState'; import { isRecordTableRowFocusedComponentFamilyState } from '@/object-record/record-table/states/isRecordTableRowFocusedComponentFamilyState'; -import { isRecordTableScrolledLeftComponentState } from '@/object-record/record-table/states/isRecordTableScrolledLeftComponentState'; import { resizeFieldOffsetComponentState } from '@/object-record/record-table/states/resizeFieldOffsetComponentState'; import { useTrackPointer } from '@/ui/utilities/pointer-event/hooks/useTrackPointer'; import { type PointerEventListener } from '@/ui/utilities/pointer-event/types/PointerEventListener'; @@ -20,7 +19,6 @@ import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue'; import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; import { useSaveRecordFields } from '@/views/hooks/useSaveRecordFields'; import { throwIfNotDefined } from 'twenty-shared/utils'; @@ -29,26 +27,24 @@ import { LightIconButton } from 'twenty-ui/input'; const COLUMN_MIN_WIDTH = 104; -const StyledColumnHeaderCell = styled.th<{ +const StyledColumnHeaderCell = styled.div<{ columnWidth: number; isResizing?: boolean; isFirstRowActiveOrFocused: boolean; }>` - border-bottom: ${({ isFirstRowActiveOrFocused, theme }) => - isFirstRowActiveOrFocused - ? 'none' - : `1px solid ${theme.border.color.light}`}; color: ${({ theme }) => theme.font.color.tertiary}; padding: 0; text-align: left; background-color: ${({ theme }) => theme.background.primary}; border-right: 1px solid ${({ theme }) => theme.border.color.light}; + + border-bottom: 1px solid ${({ theme }) => theme.border.color.light}; + ${({ columnWidth }) => ` min-width: ${columnWidth}px; width: ${columnWidth}px; `} - position: relative; user-select: none; ${({ theme }) => { return ` @@ -96,8 +92,6 @@ const StyledColumnHeadContainer = styled.div` display: flex; flex-direction: row; justify-content: space-between; - position: relative; - z-index: 1; & > :first-of-type { flex: 1; @@ -202,10 +196,6 @@ export const RecordTableHeaderCell = ({ onMouseUp: handleResizeHandlerEnd, }); - const isRecordTableScrolledLeft = useRecoilComponentValue( - isRecordTableScrolledLeftComponentState, - ); - const isMobile = useIsMobile(); const { labelIdentifierFieldMetadataItem } = useRecordIndexContextOrThrow(); @@ -213,8 +203,7 @@ export const RecordTableHeaderCell = ({ const isLabelIdentifier = recordField.fieldMetadataItemId === labelIdentifierFieldMetadataItem?.id; - const disableColumnResize = - isLabelIdentifier && isMobile && !isRecordTableScrolledLeft; + const disableColumnResize = isLabelIdentifier || isMobile; const { createNewIndexRecord } = useCreateNewIndexRecord({ objectMetadataItem, @@ -241,22 +230,27 @@ export const RecordTableHeaderCell = ({ 0, ); + const widthOffsetWhileResizing = + resizedFieldMetadataItemId === recordField.fieldMetadataItemId + ? resizeFieldOffset + : 0; + + const baseWidth = recordField?.size ?? 0; + + const computedDynamicWidth = baseWidth + widthOffsetWhileResizing; + + const columnWidth = Math.max(computedDynamicWidth, COLUMN_MIN_WIDTH); + const isFirstRowActiveOrFocused = isFirstRowActive || isFirstRowFocused; return ( setIconVisibility(true)} onMouseLeave={() => setIconVisibility(false)} isFirstRowActiveOrFocused={isFirstRowActiveOrFocused} diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeaderCheckboxColumn.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeaderCheckboxColumn.tsx index 0558d140ba..c9385bd8af 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeaderCheckboxColumn.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeaderCheckboxColumn.tsx @@ -17,21 +17,20 @@ const StyledContainer = styled.div` display: flex; height: 32px; justify-content: center; - width: 24px; + min-width: 24px; padding-right: ${({ theme }) => theme.spacing(1)}; background-color: ${({ theme }) => theme.background.primary}; `; -const StyledColumnHeaderCell = styled.th<{ +const StyledColumnHeaderCell = styled.div<{ isFirstRowActiveOrFocused: boolean; }>` background-color: ${({ theme }) => theme.background.primary}; - border-bottom: ${({ isFirstRowActiveOrFocused, theme }) => - isFirstRowActiveOrFocused - ? 'none' - : `1px solid ${theme.border.color.light}`}; - width: 28px; + + min-width: 32px; box-sizing: border-box; + + border-bottom: 1px solid ${({ theme }) => theme.border.color.light}; `; export const RecordTableHeaderCheckboxColumn = () => { @@ -85,6 +84,7 @@ export const RecordTableHeaderCheckboxColumn = () => { return ( ` - background: ${({ backgroundColor }) => backgroundColor}; - border-bottom: none; - border-top: none; +const StyledTh = styled.div<{ backgroundColor: string }>` + background-color: ${({ backgroundColor }) => backgroundColor}; + min-width: 17px; + min-height: 100%; + + border-bottom: 1px solid ${({ backgroundColor }) => backgroundColor}; `; export const RecordTableHeaderDragDropColumn = () => { const { theme } = useContext(ThemeContext); - return ; + return ( + + ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeaderLastColumn.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeaderLastColumn.tsx index fecf14a307..269e8ee11d 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeaderLastColumn.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeaderLastColumn.tsx @@ -1,93 +1,16 @@ import styled from '@emotion/styled'; -import { HIDDEN_TABLE_COLUMN_DROPDOWN_ID } from '@/object-record/record-table/constants/HiddenTableColumnDropdownId'; -import { RecordTableHeaderPlusButtonContent } from '@/object-record/record-table/record-table-header/components/RecordTableHeaderPlusButtonContent'; -import { isRecordTableRowActiveComponentFamilyState } from '@/object-record/record-table/states/isRecordTableRowActiveComponentFamilyState'; -import { isRecordTableRowFocusedComponentFamilyState } from '@/object-record/record-table/states/isRecordTableRowFocusedComponentFamilyState'; -import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; -import { useScrollWrapperElement } from '@/ui/utilities/scroll/hooks/useScrollWrapperElement'; -import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue'; -import { useTheme } from '@emotion/react'; -import { IconPlus } from 'twenty-ui/display'; +const StyledLastColumnHeader = styled.div` + border-bottom: 1px solid ${({ theme }) => theme.border.color.light}; -const StyledPlusIconHeaderCell = styled.th<{ - isTableWiderThanScreen: boolean; - isFirstRowActiveOrFocused: boolean; -}>` - border-bottom: ${({ isFirstRowActiveOrFocused, theme }) => - isFirstRowActiveOrFocused - ? 'none' - : `1px solid ${theme.border.color.light}`}; background-color: ${({ theme }) => theme.background.primary}; border-left: none !important; color: ${({ theme }) => theme.font.color.tertiary}; - border-right: none !important; - cursor: default; - ${({ isTableWiderThanScreen, theme }) => - isTableWiderThanScreen - ? ` - background-color: ${theme.background.primary}; - width: 32px; - ` - : 'width: 100%'}; - z-index: 1; - - &:hover { - background: ${({ theme }) => theme.background.transparent.secondary}; - } -`; - -const StyledPlusIconContainer = styled.div` - align-items: center; - display: flex; + width: 100%; height: 32px; - justify-content: flex-start; - margin-left: ${({ theme }) => theme.spacing(2)}; -`; - -const StyledDropdownContainer = styled.div` - cursor: pointer; `; export const RecordTableHeaderLastColumn = () => { - const theme = useTheme(); - - const { scrollWrapperHTMLElement } = useScrollWrapperElement(); - - const isTableWiderThanScreen = - (scrollWrapperHTMLElement?.clientWidth ?? 0) < - (scrollWrapperHTMLElement?.scrollWidth ?? 0); - - const isFirstRowActive = useRecoilComponentFamilyValue( - isRecordTableRowActiveComponentFamilyState, - 0, - ); - - const isFirstRowFocused = useRecoilComponentFamilyValue( - isRecordTableRowFocusedComponentFamilyState, - 0, - ); - - const isFirstRowActiveOrFocused = isFirstRowActive || isFirstRowFocused; - - return ( - - - - - - } - dropdownComponents={} - dropdownPlacement="bottom-start" - /> - - - ); + return ; }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-row/components/RecordTableCellsEmpty.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-row/components/RecordTableCellsEmpty.tsx index 8cd57a8cb4..47c5151e26 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-row/components/RecordTableCellsEmpty.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-row/components/RecordTableCellsEmpty.tsx @@ -11,6 +11,7 @@ export const RecordTableCellsEmpty = () => { )); }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-row/components/RecordTableTr.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-row/components/RecordTableTr.tsx index 7838880319..7beb14df94 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-row/components/RecordTableTr.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-row/components/RecordTableTr.tsx @@ -19,8 +19,9 @@ const StyledTr = styled.tr<{ isDragging ? `1px solid ${theme.border.color.medium}` : '1px solid transparent'}; + + border-left: none; position: relative; - transition: border-left-color 0.2s ease-in-out; &[data-next-row-active-or-focused='true'] { td { diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-section/components/RecordTableRecordGroupStickyEffect.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-section/components/RecordTableRecordGroupStickyEffect.tsx index afdeb4a746..513b10f049 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-section/components/RecordTableRecordGroupStickyEffect.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-section/components/RecordTableRecordGroupStickyEffect.tsx @@ -1,7 +1,7 @@ import { useEffect } from 'react'; import { useCurrentRecordGroupId } from '@/object-record/record-group/hooks/useCurrentRecordGroupId'; -import { isRecordTableScrolledLeftComponentState } from '@/object-record/record-table/states/isRecordTableScrolledLeftComponentState'; +import { isRecordTableScrolledHorizontallyComponentState } from '@/object-record/record-table/states/isRecordTableScrolledHorizontallyComponentState'; import { scrollWrapperScrollLeftComponentState } from '@/ui/utilities/scroll/states/scrollWrapperScrollLeftComponentState'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; @@ -11,38 +11,16 @@ export const RecordTableRecordGroupStickyEffect = () => { scrollWrapperScrollLeftComponentState, ); - const setIsRecordTableScrolledLeft = useSetRecoilComponentState( - isRecordTableScrolledLeftComponentState, + const setIsRecordTableScrolledHorizontally = useSetRecoilComponentState( + isRecordTableScrolledHorizontallyComponentState, ); const currentRecordGroupId = useCurrentRecordGroupId(); useEffect(() => { - setIsRecordTableScrolledLeft(scrollLeft === 0); - if (scrollLeft > 0) { - document - .getElementById( - `record-table-footer${currentRecordGroupId ? '-' + currentRecordGroupId : ''}`, - ) - ?.classList.add('first-columns-sticky'); - document - .getElementById( - `record-table-body${currentRecordGroupId ? '-' + currentRecordGroupId : ''}`, - ) - ?.classList.add('first-columns-sticky'); - } else { - document - .getElementById( - `record-table-footer${currentRecordGroupId ? '-' + currentRecordGroupId : ''}`, - ) - ?.classList.remove('first-columns-sticky'); - document - .getElementById( - `record-table-body${currentRecordGroupId ? '-' + currentRecordGroupId : ''}`, - ) - ?.classList.remove('first-columns-sticky'); - } - }, [currentRecordGroupId, scrollLeft, setIsRecordTableScrolledLeft]); + setIsRecordTableScrolledHorizontally(scrollLeft > 0); + // TODO: see if we need to reimplement setting classes here. + }, [currentRecordGroupId, scrollLeft, setIsRecordTableScrolledHorizontally]); return <>; }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/states/isRecordTableScrolledHorizontallyComponentState.ts b/packages/twenty-front/src/modules/object-record/record-table/states/isRecordTableScrolledHorizontallyComponentState.ts new file mode 100644 index 0000000000..663b866979 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-table/states/isRecordTableScrolledHorizontallyComponentState.ts @@ -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 isRecordTableScrolledHorizontallyComponentState = + createComponentState({ + key: 'isRecordTableScrolledHorizontallyComponentState', + componentInstanceContext: RecordTableComponentInstanceContext, + defaultValue: true, + }); diff --git a/packages/twenty-front/src/modules/object-record/record-table/states/isRecordTableScrolledLeftComponentState.ts b/packages/twenty-front/src/modules/object-record/record-table/states/isRecordTableScrolledVerticallyComponentState.ts similarity index 75% rename from packages/twenty-front/src/modules/object-record/record-table/states/isRecordTableScrolledLeftComponentState.ts rename to packages/twenty-front/src/modules/object-record/record-table/states/isRecordTableScrolledVerticallyComponentState.ts index 4dd17e3612..0e4e3ef5ff 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/states/isRecordTableScrolledLeftComponentState.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/states/isRecordTableScrolledVerticallyComponentState.ts @@ -1,9 +1,9 @@ import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; -export const isRecordTableScrolledLeftComponentState = +export const isRecordTableScrolledVerticallyComponentState = createComponentState({ - key: 'isRecordTableScrolledLeftComponentState', + key: 'isRecordTableScrolledVerticallyComponentState', componentInstanceContext: RecordTableComponentInstanceContext, defaultValue: true, });