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 70c4db8ee8ba57dbf163bae1aef1ccb63ec83797.

* fix: resolved label identifier issue

---------

Co-authored-by: Thaïs Guigon <guigon.thais@gmail.com>
This commit is contained in:
Jeet Desai
2024-03-04 21:11:42 +05:30
committed by GitHub
parent 6512a781ee
commit 8c0ec336ea
7 changed files with 76 additions and 64 deletions

View File

@ -16,6 +16,7 @@ const StyledContainer = styled.div`
height: 32px; height: 32px;
justify-content: center; justify-content: center;
background-color: ${({ theme }) => theme.background.primary};
`; `;
export const CheckboxCell = () => { export const CheckboxCell = () => {

View File

@ -1,8 +1,11 @@
import { useTheme } from '@emotion/react'; import { css, useTheme } from '@emotion/react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { useRecoilValue } from 'recoil';
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'; import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
import { useIcons } from '@/ui/display/icon/hooks/useIcons'; 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'; import { ColumnDefinition } from '../types/ColumnDefinition';
@ -10,7 +13,7 @@ type ColumnHeadProps = {
column: ColumnDefinition<FieldMetadata>; column: ColumnDefinition<FieldMetadata>;
}; };
const StyledTitle = styled.div` const StyledTitle = styled.div<{ hideTitle?: boolean }>`
align-items: center; align-items: center;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@ -19,6 +22,14 @@ const StyledTitle = styled.div`
height: ${({ theme }) => theme.spacing(8)}; height: ${({ theme }) => theme.spacing(8)};
padding-left: ${({ theme }) => theme.spacing(2)}; padding-left: ${({ theme }) => theme.spacing(2)};
padding-right: ${({ 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` const StyledIcon = styled.div`
@ -42,8 +53,10 @@ export const ColumnHead = ({ column }: ColumnHeadProps) => {
const { getIcon } = useIcons(); const { getIcon } = useIcons();
const Icon = getIcon(column.iconName); const Icon = getIcon(column.iconName);
const scrollLeft = useRecoilValue(scrollLeftState);
return ( return (
<StyledTitle> <StyledTitle hideTitle={!!column.isLabelIdentifier && scrollLeft > 0}>
<StyledIcon> <StyledIcon>
<Icon size={theme.icon.size.md} /> <Icon size={theme.icon.size.md} />
</StyledIcon> </StyledIcon>

View File

@ -1,17 +1,19 @@
import { useRef } from 'react'; import { css } from '@emotion/react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { useRecoilValue } from 'recoil';
import { useObjectMetadataItemOnly } from '@/object-metadata/hooks/useObjectMetadataItemOnly'; import { useObjectMetadataItemOnly } from '@/object-metadata/hooks/useObjectMetadataItemOnly';
import { RecordTableBody } from '@/object-record/record-table/components/RecordTableBody'; import { RecordTableBody } from '@/object-record/record-table/components/RecordTableBody';
import { RecordTableBodyEffect } from '@/object-record/record-table/components/RecordTableBodyEffect'; 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 { RecordTableHeader } from '@/object-record/record-table/components/RecordTableHeader';
import { RecordTableContext } from '@/object-record/record-table/contexts/RecordTableContext'; import { RecordTableContext } from '@/object-record/record-table/contexts/RecordTableContext';
import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates'; import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
import { RecordTableScope } from '@/object-record/record-table/scopes/RecordTableScope'; 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 { 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-radius: ${({ theme }) => theme.border.radius.sm};
border-spacing: 0; border-spacing: 0;
margin-right: ${({ theme }) => theme.table.horizontalCellMargin}; margin-right: ${({ theme }) => theme.table.horizontalCellMargin};
@ -67,29 +69,37 @@ const StyledTable = styled.table`
tbody td:nth-of-type(1) { tbody td:nth-of-type(1) {
left: 0; left: 0;
} }
// Label identifier column
thead th:nth-of-type(2), thead th:nth-of-type(2),
tbody td:nth-of-type(2) { tbody td:nth-of-type(2) {
left: calc(${({ theme }) => theme.table.checkboxColumnWidth} - 2px); left: calc(${({ theme }) => theme.table.checkboxColumnWidth} - 2px);
}
tbody td:nth-of-type(2)::after, ${({ freezeFirstColumns }) =>
thead th:nth-of-type(2)::after { freezeFirstColumns &&
content: ''; css`
height: calc(100% + 1px); @media (max-width: ${MOBILE_VIEWPORT}px) {
position: absolute; width: 35px;
top: 0; max-width: 35px;
width: 4px; }
right: -4px; `}
}
&.freeze-first-columns-shadow thead th:nth-of-type(2)::after, &::after {
&.freeze-first-columns-shadow tbody td:nth-of-type(2)::after { content: '';
box-shadow: ${({ theme }) => height: calc(100% + 1px);
`4px 0px 4px -4px ${ position: absolute;
theme.name === 'dark' top: 0;
? RGBA(theme.grayScale.gray50, 0.8) width: 4px;
: RGBA(theme.grayScale.gray100, 0.25) right: -4px;
} inset`};
${({ 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), thead th:nth-of-type(3),
@ -111,8 +121,8 @@ export const RecordTable = ({
onColumnsChange, onColumnsChange,
createRecord, createRecord,
}: RecordTableProps) => { }: RecordTableProps) => {
const recordTableRef = useRef<HTMLTableElement>(null);
const { scopeId } = useRecordTableStates(recordTableId); const { scopeId } = useRecordTableStates(recordTableId);
const scrollLeft = useRecoilValue(scrollLeftState);
const { objectMetadataItem } = useObjectMetadataItemOnly({ const { objectMetadataItem } = useObjectMetadataItemOnly({
objectNameSingular, objectNameSingular,
@ -127,11 +137,12 @@ export const RecordTable = ({
<RecordTableContext.Provider <RecordTableContext.Provider
value={{ value={{
objectMetadataItem, objectMetadataItem,
recordTableRef,
}} }}
> >
<RecordTableFirstColumnScrollEffect /> <StyledTable
<StyledTable ref={recordTableRef} className="entity-table-cell"> freezeFirstColumns={scrollLeft > 0}
className="entity-table-cell"
>
<RecordTableHeader createRecord={createRecord} /> <RecordTableHeader createRecord={createRecord} />
<RecordTableBodyEffect objectNameSingular={objectNameSingular} /> <RecordTableBodyEffect objectNameSingular={objectNameSingular} />
<RecordTableBody objectNameSingular={objectNameSingular} /> <RecordTableBody objectNameSingular={objectNameSingular} />

View File

@ -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 <></>;
};

View File

@ -11,6 +11,8 @@ import { IconPlus } from '@/ui/display/icon';
import { LightIconButton } from '@/ui/input/button/components/LightIconButton'; import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
import { useTrackPointer } from '@/ui/utilities/pointer-event/hooks/useTrackPointer'; import { useTrackPointer } from '@/ui/utilities/pointer-event/hooks/useTrackPointer';
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; 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 { mapArrayToObject } from '~/utils/array/mapArrayToObject';
import { ColumnHeadWithDropdown } from './ColumnHeadWithDropdown'; import { ColumnHeadWithDropdown } from './ColumnHeadWithDropdown';
@ -70,9 +72,7 @@ const StyledColumnHeadContainer = styled.div`
`; `;
const StyledHeaderIcon = styled.div` const StyledHeaderIcon = styled.div`
margin-bottom: ${({ theme }) => theme.spacing(1)}; margin: ${({ theme }) => theme.spacing(1, 1, 1, 1.5)};
margin-right: ${({ theme }) => theme.spacing(1)};
margin-top: ${({ theme }) => theme.spacing(1)};
`; `;
export const RecordTableHeaderCell = ({ export const RecordTableHeaderCell = ({
@ -164,6 +164,12 @@ export const RecordTableHeaderCell = ({
onMouseUp: handleResizeHandlerEnd, onMouseUp: handleResizeHandlerEnd,
}); });
const isMobile = useIsMobile();
const scrollLeft = useRecoilValue(scrollLeftState);
const disableColumnResize =
column.isLabelIdentifier && isMobile && scrollLeft > 0;
return ( return (
<StyledColumnHeaderCell <StyledColumnHeaderCell
key={column.fieldMetadataId} key={column.fieldMetadataId}
@ -174,17 +180,16 @@ export const RecordTableHeaderCell = ({
24, 24,
COLUMN_MIN_WIDTH, COLUMN_MIN_WIDTH,
)} )}
onMouseEnter={() => setIconVisibility(true)}
onMouseLeave={() => setIconVisibility(false)}
> >
<StyledColumnHeadContainer <StyledColumnHeadContainer>
onMouseEnter={() => setIconVisibility(true)}
onMouseLeave={() => setIconVisibility(false)}
>
{column.isLabelIdentifier ? ( {column.isLabelIdentifier ? (
<ColumnHead column={column} /> <ColumnHead column={column} />
) : ( ) : (
<ColumnHeadWithDropdown column={column} /> <ColumnHeadWithDropdown column={column} />
)} )}
{iconVisibility && !!column.isLabelIdentifier && ( {(useIsMobile() || iconVisibility) && !!column.isLabelIdentifier && (
<StyledHeaderIcon> <StyledHeaderIcon>
<LightIconButton <LightIconButton
Icon={IconPlus} Icon={IconPlus}
@ -195,13 +200,15 @@ export const RecordTableHeaderCell = ({
</StyledHeaderIcon> </StyledHeaderIcon>
)} )}
</StyledColumnHeadContainer> </StyledColumnHeadContainer>
<StyledResizeHandler {!disableColumnResize && (
className="cursor-col-resize" <StyledResizeHandler
role="separator" className="cursor-col-resize"
onPointerDown={() => { role="separator"
setResizedFieldKey(column.fieldMetadataId); onPointerDown={() => {
}} setResizedFieldKey(column.fieldMetadataId);
/> }}
/>
)}
</StyledColumnHeaderCell> </StyledColumnHeaderCell>
); );
}; };

View File

@ -13,6 +13,7 @@ const StyledContainer = styled.div`
height: 32px; height: 32px;
justify-content: center; justify-content: center;
background-color: ${({ theme }) => theme.background.primary};
`; `;
export const SelectAllCheckbox = () => { export const SelectAllCheckbox = () => {

View File

@ -4,7 +4,6 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
type RecordTableContextProps = { type RecordTableContextProps = {
objectMetadataItem: ObjectMetadataItem; objectMetadataItem: ObjectMetadataItem;
recordTableRef: React.RefObject<HTMLDivElement>;
}; };
export const RecordTableContext = createContext<RecordTableContextProps>( export const RecordTableContext = createContext<RecordTableContextProps>(