Refactor/remove react table (#642)

* Refactored tables without tan stack
* Fixed checkbox behavior with multiple handlers on click
* Fixed hotkeys scope
* Fix debounce in editable cells
* Lowered coverage

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Lucas Bordeau
2023-07-13 19:08:13 +02:00
committed by GitHub
parent e7d48d5373
commit 734e18e01a
88 changed files with 1789 additions and 671 deletions

View File

@ -1 +0,0 @@
export const TABLE_MIN_COLUMN_NUMBER_BECAUSE_OF_CHECKBOX_COLUMN = 1;

View File

@ -0,0 +1,18 @@
import { useRecoilScopedValue } from '@/recoil-scope/hooks/useRecoilScopedValue';
import { currentRowEntityIdScopedState } from '../states/currentRowEntityIdScopedState';
import { RowContext } from '../states/RowContext';
export type TableDimensions = {
numberOfColumns: number;
numberOfRows: number;
};
export function useCurrentRowEntityId() {
const currentRowEntityIdScoped = useRecoilScopedValue(
currentRowEntityIdScopedState,
RowContext,
);
return currentRowEntityIdScoped;
}

View File

@ -0,0 +1,43 @@
import { useRecoilCallback, useRecoilState } from 'recoil';
import { isRowSelectedFamilyState } from '../states/isRowSelectedFamilyState';
import { numberOfSelectedRowState } from '../states/numberOfSelectedRowState';
import { useCurrentRowEntityId } from './useCurrentEntityId';
export function useCurrentRowSelected() {
const currentRowId = useCurrentRowEntityId();
const [isRowSelected] = useRecoilState(
isRowSelectedFamilyState(currentRowId ?? ''),
);
const setCurrentRowSelected = useRecoilCallback(
({ set, snapshot }) =>
(newSelectedState: boolean) => {
if (!currentRowId) return;
const isRowSelected = snapshot
.getLoadable(isRowSelectedFamilyState(currentRowId))
.valueOrThrow();
const numberOfSelectedRow = snapshot
.getLoadable(numberOfSelectedRowState)
.valueOrThrow();
if (newSelectedState && !isRowSelected) {
set(numberOfSelectedRowState, numberOfSelectedRow + 1);
set(isRowSelectedFamilyState(currentRowId), true);
} else if (!newSelectedState && isRowSelected) {
set(numberOfSelectedRowState, numberOfSelectedRow - 1);
set(isRowSelectedFamilyState(currentRowId), false);
}
},
[currentRowId],
);
return {
currentRowSelected: isRowSelected,
setCurrentRowSelected,
};
}

View File

@ -1,21 +1,25 @@
import { useEffect } from 'react';
import { useRecoilState } from 'recoil';
import { useRecoilState, useRecoilValue } from 'recoil';
import { entityTableDimensionsState } from '../states/entityTableDimensionsState';
import { tableRowIdsState } from '../states/tableRowIdsState';
import { useResetTableRowSelection } from './useResetTableRowSelection';
export type TableDimensions = {
numberOfRows: number;
numberOfColumns: number;
numberOfRows: number;
};
export function useInitializeEntityTable({
numberOfRows,
numberOfColumns,
}: TableDimensions) {
}: {
numberOfColumns: number;
}) {
const resetTableRowSelection = useResetTableRowSelection();
const tableRowIds = useRecoilValue(tableRowIdsState);
useEffect(() => {
resetTableRowSelection();
}, [resetTableRowSelection]);
@ -25,7 +29,7 @@ export function useInitializeEntityTable({
useEffect(() => {
setTableDimensions({
numberOfColumns,
numberOfRows,
numberOfRows: tableRowIds?.length,
});
}, [numberOfRows, numberOfColumns, setTableDimensions]);
}, [tableRowIds, numberOfColumns, setTableDimensions]);
}

View File

@ -7,16 +7,16 @@ import { useRecoilScopedState } from '@/recoil-scope/hooks/useRecoilScopedState'
import { TableContext } from '../states/TableContext';
export function useInitializeEntityTableFilters({
availableTableFilters,
availableFilters,
}: {
availableTableFilters: FilterDefinition[];
availableFilters: FilterDefinition[];
}) {
const [, setAvailableTableFilters] = useRecoilScopedState(
const [, setAvailableFilters] = useRecoilScopedState(
availableFiltersScopedState,
TableContext,
);
useEffect(() => {
setAvailableTableFilters(availableTableFilters);
}, [setAvailableTableFilters, availableTableFilters]);
setAvailableFilters(availableFilters);
}, [setAvailableFilters, availableFilters]);
}

View File

@ -1,6 +1,5 @@
import { useRecoilCallback } from 'recoil';
import { TABLE_MIN_COLUMN_NUMBER_BECAUSE_OF_CHECKBOX_COLUMN } from '../constants';
import { numberOfTableColumnsSelectorState } from '../states/numberOfTableColumnsSelectorState';
import { numberOfTableRowsSelectorState } from '../states/numberOfTableRowsSelectorState';
import { softFocusPositionState } from '../states/softFocusPositionState';
@ -98,7 +97,7 @@ export function useMoveSoftFocus() {
} else if (isLastColumnButNotLastRow) {
setSoftFocusPosition({
row: currentRowNumber + 1,
column: TABLE_MIN_COLUMN_NUMBER_BECAUSE_OF_CHECKBOX_COLUMN,
column: 0,
});
}
},
@ -120,18 +119,12 @@ export function useMoveSoftFocus() {
const currentRowNumber = softFocusPosition.row;
const isFirstRowAndFirstColumn =
currentColumnNumber ===
TABLE_MIN_COLUMN_NUMBER_BECAUSE_OF_CHECKBOX_COLUMN &&
currentRowNumber === 0;
currentColumnNumber === 0 && currentRowNumber === 0;
const isFirstColumnButNotFirstRow =
currentColumnNumber ===
TABLE_MIN_COLUMN_NUMBER_BECAUSE_OF_CHECKBOX_COLUMN &&
currentRowNumber > 0;
currentColumnNumber === 0 && currentRowNumber > 0;
const isNotFirstColumn =
currentColumnNumber >
TABLE_MIN_COLUMN_NUMBER_BECAUSE_OF_CHECKBOX_COLUMN;
const isNotFirstColumn = currentColumnNumber > 0;
if (isFirstRowAndFirstColumn) {
return;
@ -149,7 +142,7 @@ export function useMoveSoftFocus() {
});
}
},
[setSoftFocusPosition, TABLE_MIN_COLUMN_NUMBER_BECAUSE_OF_CHECKBOX_COLUMN],
[setSoftFocusPosition],
);
return {

View File

@ -0,0 +1,48 @@
import { useRecoilCallback, useRecoilValue } from 'recoil';
import { allRowsSelectedStatusSelector } from '../states/allRowsSelectedStatusSelector';
import { isRowSelectedFamilyState } from '../states/isRowSelectedFamilyState';
import { numberOfSelectedRowState } from '../states/numberOfSelectedRowState';
import { numberOfTableRowsSelectorState } from '../states/numberOfTableRowsSelectorState';
import { tableRowIdsState } from '../states/tableRowIdsState';
export function useSelectAllRows() {
const allRowsSelectedStatus = useRecoilValue(allRowsSelectedStatusSelector);
const selectAllRows = useRecoilCallback(
({ set, snapshot }) =>
() => {
const allRowsSelectedStatus = snapshot
.getLoadable(allRowsSelectedStatusSelector)
.valueOrThrow();
const numberOfRows = snapshot
.getLoadable(numberOfTableRowsSelectorState)
.valueOrThrow();
const tableRowIds = snapshot
.getLoadable(tableRowIdsState)
.valueOrThrow();
if (allRowsSelectedStatus === 'none') {
set(numberOfSelectedRowState, numberOfRows);
for (const rowId of tableRowIds) {
set(isRowSelectedFamilyState(rowId), true);
}
} else {
set(numberOfSelectedRowState, 0);
for (const rowId of tableRowIds) {
set(isRowSelectedFamilyState(rowId), false);
}
}
},
[],
);
return {
allRowsSelectedStatus,
selectAllRows,
};
}

View File

@ -0,0 +1,24 @@
import { selector } from 'recoil';
import { AllRowsSelectedStatus } from '../types/AllRowSelectedStatus';
import { numberOfSelectedRowState } from './numberOfSelectedRowState';
import { numberOfTableRowsSelectorState } from './numberOfTableRowsSelectorState';
export const allRowsSelectedStatusSelector = selector<AllRowsSelectedStatus>({
key: 'allRowsSelectedStatusSelector',
get: ({ get }) => {
const numberOfRows = get(numberOfTableRowsSelectorState);
const numberOfSelectedRows = get(numberOfSelectedRowState);
const allRowsSelectedStatus =
numberOfSelectedRows === 0
? 'none'
: numberOfRows === numberOfSelectedRows
? 'all'
: 'some';
return allRowsSelectedStatus;
},
});

View File

@ -0,0 +1,6 @@
import { atomFamily } from 'recoil';
export const currentRowEntityIdScopedState = atomFamily<string | null, string>({
key: 'currentRowEntityIdScopedState',
default: null,
});

View File

@ -0,0 +1,6 @@
import { atom } from 'recoil';
export const isFetchingEntityTableDataState = atom<boolean>({
key: 'isFetchingEntityTableDataState',
default: true,
});

View File

@ -0,0 +1,6 @@
import { atomFamily } from 'recoil';
export const isRowSelectedFamilyState = atomFamily<boolean, string>({
key: 'isRowSelectedFamilyState',
default: false,
});

View File

@ -0,0 +1,6 @@
import { atom } from 'recoil';
export const numberOfSelectedRowState = atom<number>({
key: 'numberOfSelectedRowState',
default: 0,
});

View File

@ -0,0 +1,6 @@
import { atom } from 'recoil';
export const tableRowIdsState = atom<string[]>({
key: 'tableRowIdsState',
default: [],
});

View File

@ -0,0 +1 @@
export type AllRowsSelectedStatus = 'none' | 'some' | 'all';

View File

@ -1,27 +0,0 @@
import { CellContext } from '@tanstack/react-table';
import { CheckboxCell } from '@/ui/components/table/CheckboxCell';
import { SelectAllCheckbox } from '@/ui/components/table/SelectAllCheckbox';
export function getCheckBoxColumn() {
return {
id: 'select',
header: ({ table }: any) => (
<SelectAllCheckbox
checked={table.getIsAllRowsSelected()}
indeterminate={table.getIsSomeRowsSelected()}
onChange={(newValue) => table.toggleAllRowsSelected(newValue)}
/>
),
cell: (props: CellContext<any, string>) => (
<CheckboxCell
id={`checkbox-selected-${props.row.original.id}`}
name={`checkbox-selected-${props.row.original.id}`}
checked={props.row.getIsSelected()}
onChange={(newValue) => props.row.toggleSelected(newValue)}
/>
),
size: 32,
maxSize: 32,
};
}