Feat/pagination front (#2387)
* Finished renaming and scope * wip * WIP update * Ok * Cleaned * Finished infinite scroll * Clean * Fixed V1 tables * Fix post merge * Removed ScrollWrapper * Put back ScrollWrapper * Put back in the right place
This commit is contained in:
@ -1,78 +1,72 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { useVirtual } from '@tanstack/react-virtual';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useEffect } from 'react';
|
||||
import { useInView } from 'react-intersection-observer';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { useScrollWrapperScopedRef } from '@/ui/utilities/scroll/hooks/useScrollWrapperScopedRef';
|
||||
import { useFindOneObjectMetadataItem } from '@/metadata/hooks/useFindOneObjectMetadataItem';
|
||||
import { useTableObjects } from '@/metadata/hooks/useTableObjects';
|
||||
import { isFetchingMoreObjectsFamilyState } from '@/metadata/states/isFetchingMoreObjectsFamilyState';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
import { RowIdContext } from '../contexts/RowIdContext';
|
||||
import { RowIndexContext } from '../contexts/RowIndexContext';
|
||||
import { useRecordTable } from '../hooks/useRecordTable';
|
||||
import { isFetchingRecordTableDataState } from '../states/isFetchingRecordTableDataState';
|
||||
import { tableRowIdsState } from '../states/tableRowIdsState';
|
||||
|
||||
import { RecordTableRow } from './RecordTableRow';
|
||||
|
||||
type SpaceProps = {
|
||||
top?: number;
|
||||
bottom?: number;
|
||||
};
|
||||
|
||||
const StyledSpace = styled.td<SpaceProps>`
|
||||
${({ top }) => top && `padding-top: ${top}px;`}
|
||||
${({ bottom }) => bottom && `padding-bottom: ${bottom}px;`}
|
||||
`;
|
||||
import { RecordTableRow, StyledRow } from './RecordTableRow';
|
||||
|
||||
export const RecordTableBody = () => {
|
||||
const scrollWrapperRef = useScrollWrapperScopedRef();
|
||||
const { ref: lastTableRowRef, inView: lastTableRowIsVisible } = useInView();
|
||||
|
||||
const tableRowIds = useRecoilValue(tableRowIdsState);
|
||||
|
||||
const { scopeId: objectNamePlural } = useRecordTable();
|
||||
|
||||
const { foundObjectMetadataItem } = useFindOneObjectMetadataItem({
|
||||
objectNamePlural,
|
||||
});
|
||||
|
||||
const [isFetchingMoreObjects] = useRecoilState(
|
||||
isFetchingMoreObjectsFamilyState(foundObjectMetadataItem?.namePlural),
|
||||
);
|
||||
|
||||
const isFetchingRecordTableData = useRecoilValue(
|
||||
isFetchingRecordTableDataState,
|
||||
);
|
||||
|
||||
const rowVirtualizer = useVirtual({
|
||||
size: tableRowIds.length,
|
||||
parentRef: scrollWrapperRef,
|
||||
overscan: 50,
|
||||
});
|
||||
const { fetchMoreObjects } = useTableObjects();
|
||||
|
||||
const items = rowVirtualizer.virtualItems;
|
||||
const paddingTop = items.length > 0 ? items[0].start : 0;
|
||||
const paddingBottom =
|
||||
items.length > 0
|
||||
? rowVirtualizer.totalSize - items[items.length - 1].end
|
||||
: 0;
|
||||
useEffect(() => {
|
||||
if (lastTableRowIsVisible && isDefined(fetchMoreObjects)) {
|
||||
fetchMoreObjects();
|
||||
}
|
||||
}, [lastTableRowIsVisible, fetchMoreObjects]);
|
||||
|
||||
const lastRowId = tableRowIds[tableRowIds.length - 1];
|
||||
|
||||
if (isFetchingRecordTableData) {
|
||||
return null;
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<tbody>
|
||||
{paddingTop > 0 && (
|
||||
<tr>
|
||||
<StyledSpace top={paddingTop} />
|
||||
</tr>
|
||||
)}
|
||||
{items.map((virtualItem) => {
|
||||
const rowId = tableRowIds[virtualItem.index];
|
||||
|
||||
return (
|
||||
<RowIdContext.Provider value={rowId} key={rowId}>
|
||||
<RowIndexContext.Provider value={virtualItem.index}>
|
||||
<RecordTableRow
|
||||
key={virtualItem.index}
|
||||
ref={virtualItem.measureRef}
|
||||
rowId={rowId}
|
||||
/>
|
||||
</RowIndexContext.Provider>
|
||||
</RowIdContext.Provider>
|
||||
);
|
||||
})}
|
||||
{paddingBottom > 0 && (
|
||||
<tr>
|
||||
<StyledSpace bottom={paddingBottom} />
|
||||
</tr>
|
||||
{tableRowIds.map((rowId, rowIndex) => (
|
||||
<RowIdContext.Provider value={rowId} key={rowId}>
|
||||
<RowIndexContext.Provider value={rowIndex}>
|
||||
<RecordTableRow
|
||||
key={rowId}
|
||||
ref={rowId === lastRowId ? lastTableRowRef : undefined}
|
||||
rowId={rowId}
|
||||
/>
|
||||
</RowIndexContext.Provider>
|
||||
</RowIdContext.Provider>
|
||||
))}
|
||||
{isFetchingMoreObjects && (
|
||||
<StyledRow selected={false}>
|
||||
<td style={{ height: 50 }} colSpan={1000}>
|
||||
Fetching more...
|
||||
</td>
|
||||
</StyledRow>
|
||||
)}
|
||||
</tbody>
|
||||
);
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { RowIdContext } from '../contexts/RowIdContext';
|
||||
import { RowIndexContext } from '../contexts/RowIndexContext';
|
||||
import { isFetchingRecordTableDataState } from '../states/isFetchingRecordTableDataState';
|
||||
import { tableRowIdsState } from '../states/tableRowIdsState';
|
||||
|
||||
import { RecordTableRow } from './RecordTableRow';
|
||||
|
||||
export const RecordTableBodyV1 = () => {
|
||||
const tableRowIds = useRecoilValue(tableRowIdsState);
|
||||
|
||||
const isFetchingRecordTableData = useRecoilValue(
|
||||
isFetchingRecordTableDataState,
|
||||
);
|
||||
|
||||
if (isFetchingRecordTableData) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<tbody>
|
||||
{tableRowIds.map((rowId, rowIndex) => (
|
||||
<RowIdContext.Provider value={rowId} key={rowId}>
|
||||
<RowIndexContext.Provider value={rowIndex}>
|
||||
<RecordTableRow key={rowId} rowId={rowId} />
|
||||
</RowIndexContext.Provider>
|
||||
</RowIdContext.Provider>
|
||||
))}
|
||||
</tbody>
|
||||
);
|
||||
};
|
||||
@ -26,8 +26,7 @@ export const RecordTableEffect = ({
|
||||
}: {
|
||||
useGetRequest: typeof useGetCompaniesQuery | typeof useGetPeopleQuery;
|
||||
getRequestResultKey: string;
|
||||
getRequestOptimisticEffectDefinition: OptimisticEffectDefinition<any>;
|
||||
|
||||
getRequestOptimisticEffectDefinition: OptimisticEffectDefinition;
|
||||
filterDefinitionArray: FilterDefinition[];
|
||||
sortDefinitionArray: SortDefinition[];
|
||||
setActionBarEntries?: () => void;
|
||||
|
||||
@ -9,7 +9,7 @@ import { useCurrentRowSelected } from '../record-table-row/hooks/useCurrentRowSe
|
||||
import { CheckboxCell } from './CheckboxCell';
|
||||
import { RecordTableCell } from './RecordTableCell';
|
||||
|
||||
const StyledRow = styled.tr<{ selected: boolean }>`
|
||||
export const StyledRow = styled.tr<{ selected: boolean }>`
|
||||
background: ${(props) =>
|
||||
props.selected ? props.theme.accent.quaternary : 'none'};
|
||||
`;
|
||||
|
||||
@ -0,0 +1,139 @@
|
||||
import { useRef } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import {
|
||||
useListenClickOutside,
|
||||
useListenClickOutsideByClassName,
|
||||
} from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
|
||||
import { EntityUpdateMutationContext } from '../contexts/EntityUpdateMutationHookContext';
|
||||
import { useRecordTable } from '../hooks/useRecordTable';
|
||||
import { TableHotkeyScope } from '../types/TableHotkeyScope';
|
||||
|
||||
import { RecordTableBodyV1 } from './RecordTableBodyV1';
|
||||
import { RecordTableHeader } from './RecordTableHeader';
|
||||
|
||||
const StyledTable = styled.table`
|
||||
border-collapse: collapse;
|
||||
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
border-spacing: 0;
|
||||
margin-left: ${({ theme }) => theme.table.horizontalCellMargin};
|
||||
margin-right: ${({ theme }) => theme.table.horizontalCellMargin};
|
||||
table-layout: fixed;
|
||||
|
||||
width: calc(100% - ${({ theme }) => theme.table.horizontalCellMargin} * 2);
|
||||
|
||||
th {
|
||||
border: 1px solid ${({ theme }) => theme.border.color.light};
|
||||
border-collapse: collapse;
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
padding: 0;
|
||||
text-align: left;
|
||||
|
||||
:last-child {
|
||||
border-right-color: transparent;
|
||||
}
|
||||
:first-of-type {
|
||||
border-left-color: transparent;
|
||||
border-right-color: transparent;
|
||||
}
|
||||
:last-of-type {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
td {
|
||||
border: 1px solid ${({ theme }) => theme.border.color.light};
|
||||
border-collapse: collapse;
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
padding: 0;
|
||||
|
||||
text-align: left;
|
||||
|
||||
:last-child {
|
||||
border-right-color: transparent;
|
||||
}
|
||||
:first-of-type {
|
||||
border-left-color: transparent;
|
||||
border-right-color: transparent;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledTableWithHeader = styled.div`
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledTableContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
type RecordTableV1Props = {
|
||||
updateEntityMutation: (params: any) => void;
|
||||
};
|
||||
|
||||
export const RecordTableV1 = ({ updateEntityMutation }: RecordTableV1Props) => {
|
||||
const tableBodyRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const {
|
||||
leaveTableFocus,
|
||||
setRowSelectedState,
|
||||
resetTableRowSelection,
|
||||
useMapKeyboardToSoftFocus,
|
||||
} = useRecordTable();
|
||||
|
||||
useMapKeyboardToSoftFocus();
|
||||
|
||||
useListenClickOutside({
|
||||
refs: [tableBodyRef],
|
||||
callback: () => {
|
||||
leaveTableFocus();
|
||||
},
|
||||
});
|
||||
|
||||
useScopedHotkeys(
|
||||
'escape',
|
||||
() => {
|
||||
resetTableRowSelection();
|
||||
},
|
||||
TableHotkeyScope.Table,
|
||||
);
|
||||
|
||||
useListenClickOutsideByClassName({
|
||||
classNames: ['entity-table-cell'],
|
||||
excludeClassNames: ['action-bar', 'context-menu'],
|
||||
callback: () => {
|
||||
resetTableRowSelection();
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<EntityUpdateMutationContext.Provider value={updateEntityMutation}>
|
||||
<StyledTableWithHeader>
|
||||
<StyledTableContainer>
|
||||
<div ref={tableBodyRef}>
|
||||
<StyledTable className="entity-table-cell">
|
||||
<RecordTableHeader />
|
||||
<RecordTableBodyV1 />
|
||||
</StyledTable>
|
||||
<DragSelect
|
||||
dragSelectable={tableBodyRef}
|
||||
onDragSelectionStart={resetTableRowSelection}
|
||||
onDragSelectionChange={setRowSelectedState}
|
||||
/>
|
||||
</div>
|
||||
</StyledTableContainer>
|
||||
</StyledTableWithHeader>
|
||||
</EntityUpdateMutationContext.Provider>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user