mirror of
https://github.com/twentyhq/twenty.git
synced 2024-12-26 13:31:45 +03:00
Fixed index column stickiness mobile (#4206)
* #4155 fixed first column stickiness on mobile
* fixed eslint error
* resolved checkbox background
* refactor: remove RecordTableFirstColumnScrollEffect
* fix: resolved comment in PR
* #4123 CurrencyFieldInput design is ready
* Revert "#4123 CurrencyFieldInput design is ready"
This reverts commit 70c4db8ee8
.
* fix: resolved label identifier issue
---------
Co-authored-by: Thaïs Guigon <guigon.thais@gmail.com>
This commit is contained in:
parent
6512a781ee
commit
8c0ec336ea
@ -16,6 +16,7 @@ const StyledContainer = styled.div`
|
||||
height: 32px;
|
||||
|
||||
justify-content: center;
|
||||
background-color: ${({ theme }) => theme.background.primary};
|
||||
`;
|
||||
|
||||
export const CheckboxCell = () => {
|
||||
|
@ -1,8 +1,11 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { css, useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { useIcons } from '@/ui/display/icon/hooks/useIcons';
|
||||
import { MOBILE_VIEWPORT } from '@/ui/theme/constants/MobileViewport';
|
||||
import { scrollLeftState } from '@/ui/utilities/scroll/states/scrollLeftState';
|
||||
|
||||
import { ColumnDefinition } from '../types/ColumnDefinition';
|
||||
|
||||
@ -10,7 +13,7 @@ type ColumnHeadProps = {
|
||||
column: ColumnDefinition<FieldMetadata>;
|
||||
};
|
||||
|
||||
const StyledTitle = styled.div`
|
||||
const StyledTitle = styled.div<{ hideTitle?: boolean }>`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@ -19,6 +22,14 @@ const StyledTitle = styled.div`
|
||||
height: ${({ theme }) => theme.spacing(8)};
|
||||
padding-left: ${({ theme }) => theme.spacing(2)};
|
||||
padding-right: ${({ theme }) => theme.spacing(2)};
|
||||
|
||||
${({ hideTitle }) =>
|
||||
hideTitle &&
|
||||
css`
|
||||
@media (max-width: ${MOBILE_VIEWPORT}px) {
|
||||
display: none;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const StyledIcon = styled.div`
|
||||
@ -42,8 +53,10 @@ export const ColumnHead = ({ column }: ColumnHeadProps) => {
|
||||
const { getIcon } = useIcons();
|
||||
const Icon = getIcon(column.iconName);
|
||||
|
||||
const scrollLeft = useRecoilValue(scrollLeftState);
|
||||
|
||||
return (
|
||||
<StyledTitle>
|
||||
<StyledTitle hideTitle={!!column.isLabelIdentifier && scrollLeft > 0}>
|
||||
<StyledIcon>
|
||||
<Icon size={theme.icon.size.md} />
|
||||
</StyledIcon>
|
||||
|
@ -1,17 +1,19 @@
|
||||
import { useRef } from 'react';
|
||||
import { css } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { useObjectMetadataItemOnly } from '@/object-metadata/hooks/useObjectMetadataItemOnly';
|
||||
import { RecordTableBody } from '@/object-record/record-table/components/RecordTableBody';
|
||||
import { RecordTableBodyEffect } from '@/object-record/record-table/components/RecordTableBodyEffect';
|
||||
import { RecordTableFirstColumnScrollEffect } from '@/object-record/record-table/components/RecordTableFirstColumnScrollObserver';
|
||||
import { RecordTableHeader } from '@/object-record/record-table/components/RecordTableHeader';
|
||||
import { RecordTableContext } from '@/object-record/record-table/contexts/RecordTableContext';
|
||||
import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
|
||||
import { RecordTableScope } from '@/object-record/record-table/scopes/RecordTableScope';
|
||||
import { MOBILE_VIEWPORT } from '@/ui/theme/constants/MobileViewport';
|
||||
import { RGBA } from '@/ui/theme/constants/Rgba';
|
||||
import { scrollLeftState } from '@/ui/utilities/scroll/states/scrollLeftState';
|
||||
|
||||
const StyledTable = styled.table`
|
||||
const StyledTable = styled.table<{ freezeFirstColumns?: boolean }>`
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
border-spacing: 0;
|
||||
margin-right: ${({ theme }) => theme.table.horizontalCellMargin};
|
||||
@ -67,29 +69,37 @@ const StyledTable = styled.table`
|
||||
tbody td:nth-of-type(1) {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
// Label identifier column
|
||||
thead th:nth-of-type(2),
|
||||
tbody td:nth-of-type(2) {
|
||||
left: calc(${({ theme }) => theme.table.checkboxColumnWidth} - 2px);
|
||||
}
|
||||
|
||||
tbody td:nth-of-type(2)::after,
|
||||
thead th:nth-of-type(2)::after {
|
||||
content: '';
|
||||
height: calc(100% + 1px);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 4px;
|
||||
right: -4px;
|
||||
}
|
||||
${({ freezeFirstColumns }) =>
|
||||
freezeFirstColumns &&
|
||||
css`
|
||||
@media (max-width: ${MOBILE_VIEWPORT}px) {
|
||||
width: 35px;
|
||||
max-width: 35px;
|
||||
}
|
||||
`}
|
||||
|
||||
&.freeze-first-columns-shadow thead th:nth-of-type(2)::after,
|
||||
&.freeze-first-columns-shadow tbody td:nth-of-type(2)::after {
|
||||
box-shadow: ${({ theme }) =>
|
||||
`4px 0px 4px -4px ${
|
||||
theme.name === 'dark'
|
||||
? RGBA(theme.grayScale.gray50, 0.8)
|
||||
: RGBA(theme.grayScale.gray100, 0.25)
|
||||
} inset`};
|
||||
&::after {
|
||||
content: '';
|
||||
height: calc(100% + 1px);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 4px;
|
||||
right: -4px;
|
||||
|
||||
${({ freezeFirstColumns, theme }) =>
|
||||
freezeFirstColumns &&
|
||||
css`
|
||||
box-shadow: 4px 0px 4px -4px ${theme.name === 'dark'
|
||||
? RGBA(theme.grayScale.gray50, 0.8)
|
||||
: RGBA(theme.grayScale.gray100, 0.25)} inset;
|
||||
`}
|
||||
}
|
||||
}
|
||||
|
||||
thead th:nth-of-type(3),
|
||||
@ -111,8 +121,8 @@ export const RecordTable = ({
|
||||
onColumnsChange,
|
||||
createRecord,
|
||||
}: RecordTableProps) => {
|
||||
const recordTableRef = useRef<HTMLTableElement>(null);
|
||||
const { scopeId } = useRecordTableStates(recordTableId);
|
||||
const scrollLeft = useRecoilValue(scrollLeftState);
|
||||
|
||||
const { objectMetadataItem } = useObjectMetadataItemOnly({
|
||||
objectNameSingular,
|
||||
@ -127,11 +137,12 @@ export const RecordTable = ({
|
||||
<RecordTableContext.Provider
|
||||
value={{
|
||||
objectMetadataItem,
|
||||
recordTableRef,
|
||||
}}
|
||||
>
|
||||
<RecordTableFirstColumnScrollEffect />
|
||||
<StyledTable ref={recordTableRef} className="entity-table-cell">
|
||||
<StyledTable
|
||||
freezeFirstColumns={scrollLeft > 0}
|
||||
className="entity-table-cell"
|
||||
>
|
||||
<RecordTableHeader createRecord={createRecord} />
|
||||
<RecordTableBodyEffect objectNameSingular={objectNameSingular} />
|
||||
<RecordTableBody objectNameSingular={objectNameSingular} />
|
||||
|
@ -1,20 +0,0 @@
|
||||
import { useContext, useEffect } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { RecordTableContext } from '@/object-record/record-table/contexts/RecordTableContext';
|
||||
import { scrollLeftState } from '@/ui/utilities/scroll/states/scrollLeftState';
|
||||
|
||||
export const RecordTableFirstColumnScrollEffect = () => {
|
||||
const { recordTableRef } = useContext(RecordTableContext);
|
||||
|
||||
const scrollLeft = useRecoilValue(scrollLeftState);
|
||||
|
||||
useEffect(() => {
|
||||
recordTableRef.current?.classList.toggle(
|
||||
'freeze-first-columns-shadow',
|
||||
scrollLeft > 0,
|
||||
);
|
||||
}, [scrollLeft, recordTableRef]);
|
||||
|
||||
return <></>;
|
||||
};
|
@ -11,6 +11,8 @@ import { IconPlus } from '@/ui/display/icon';
|
||||
import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
|
||||
import { useTrackPointer } from '@/ui/utilities/pointer-event/hooks/useTrackPointer';
|
||||
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
|
||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||
import { scrollLeftState } from '@/ui/utilities/scroll/states/scrollLeftState';
|
||||
import { mapArrayToObject } from '~/utils/array/mapArrayToObject';
|
||||
|
||||
import { ColumnHeadWithDropdown } from './ColumnHeadWithDropdown';
|
||||
@ -70,9 +72,7 @@ const StyledColumnHeadContainer = styled.div`
|
||||
`;
|
||||
|
||||
const StyledHeaderIcon = styled.div`
|
||||
margin-bottom: ${({ theme }) => theme.spacing(1)};
|
||||
margin-right: ${({ theme }) => theme.spacing(1)};
|
||||
margin-top: ${({ theme }) => theme.spacing(1)};
|
||||
margin: ${({ theme }) => theme.spacing(1, 1, 1, 1.5)};
|
||||
`;
|
||||
|
||||
export const RecordTableHeaderCell = ({
|
||||
@ -164,6 +164,12 @@ export const RecordTableHeaderCell = ({
|
||||
onMouseUp: handleResizeHandlerEnd,
|
||||
});
|
||||
|
||||
const isMobile = useIsMobile();
|
||||
const scrollLeft = useRecoilValue(scrollLeftState);
|
||||
|
||||
const disableColumnResize =
|
||||
column.isLabelIdentifier && isMobile && scrollLeft > 0;
|
||||
|
||||
return (
|
||||
<StyledColumnHeaderCell
|
||||
key={column.fieldMetadataId}
|
||||
@ -174,17 +180,16 @@ export const RecordTableHeaderCell = ({
|
||||
24,
|
||||
COLUMN_MIN_WIDTH,
|
||||
)}
|
||||
onMouseEnter={() => setIconVisibility(true)}
|
||||
onMouseLeave={() => setIconVisibility(false)}
|
||||
>
|
||||
<StyledColumnHeadContainer
|
||||
onMouseEnter={() => setIconVisibility(true)}
|
||||
onMouseLeave={() => setIconVisibility(false)}
|
||||
>
|
||||
<StyledColumnHeadContainer>
|
||||
{column.isLabelIdentifier ? (
|
||||
<ColumnHead column={column} />
|
||||
) : (
|
||||
<ColumnHeadWithDropdown column={column} />
|
||||
)}
|
||||
{iconVisibility && !!column.isLabelIdentifier && (
|
||||
{(useIsMobile() || iconVisibility) && !!column.isLabelIdentifier && (
|
||||
<StyledHeaderIcon>
|
||||
<LightIconButton
|
||||
Icon={IconPlus}
|
||||
@ -195,13 +200,15 @@ export const RecordTableHeaderCell = ({
|
||||
</StyledHeaderIcon>
|
||||
)}
|
||||
</StyledColumnHeadContainer>
|
||||
<StyledResizeHandler
|
||||
className="cursor-col-resize"
|
||||
role="separator"
|
||||
onPointerDown={() => {
|
||||
setResizedFieldKey(column.fieldMetadataId);
|
||||
}}
|
||||
/>
|
||||
{!disableColumnResize && (
|
||||
<StyledResizeHandler
|
||||
className="cursor-col-resize"
|
||||
role="separator"
|
||||
onPointerDown={() => {
|
||||
setResizedFieldKey(column.fieldMetadataId);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</StyledColumnHeaderCell>
|
||||
);
|
||||
};
|
||||
|
@ -13,6 +13,7 @@ const StyledContainer = styled.div`
|
||||
height: 32px;
|
||||
|
||||
justify-content: center;
|
||||
background-color: ${({ theme }) => theme.background.primary};
|
||||
`;
|
||||
|
||||
export const SelectAllCheckbox = () => {
|
||||
|
@ -4,7 +4,6 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
|
||||
type RecordTableContextProps = {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
recordTableRef: React.RefObject<HTMLDivElement>;
|
||||
};
|
||||
|
||||
export const RecordTableContext = createContext<RecordTableContextProps>(
|
||||
|
Loading…
Reference in New Issue
Block a user