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:
@ -1 +0,0 @@
|
||||
export const TABLE_MIN_COLUMN_NUMBER_BECAUSE_OF_CHECKBOX_COLUMN = 1;
|
||||
18
front/src/modules/ui/tables/hooks/useCurrentEntityId.ts
Normal file
18
front/src/modules/ui/tables/hooks/useCurrentEntityId.ts
Normal 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;
|
||||
}
|
||||
43
front/src/modules/ui/tables/hooks/useCurrentRowSelected.ts
Normal file
43
front/src/modules/ui/tables/hooks/useCurrentRowSelected.ts
Normal 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,
|
||||
};
|
||||
}
|
||||
@ -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]);
|
||||
}
|
||||
|
||||
@ -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]);
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
48
front/src/modules/ui/tables/hooks/useSelectAllRows.ts
Normal file
48
front/src/modules/ui/tables/hooks/useSelectAllRows.ts
Normal 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,
|
||||
};
|
||||
}
|
||||
@ -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;
|
||||
},
|
||||
});
|
||||
@ -0,0 +1,6 @@
|
||||
import { atomFamily } from 'recoil';
|
||||
|
||||
export const currentRowEntityIdScopedState = atomFamily<string | null, string>({
|
||||
key: 'currentRowEntityIdScopedState',
|
||||
default: null,
|
||||
});
|
||||
@ -0,0 +1,6 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
export const isFetchingEntityTableDataState = atom<boolean>({
|
||||
key: 'isFetchingEntityTableDataState',
|
||||
default: true,
|
||||
});
|
||||
@ -0,0 +1,6 @@
|
||||
import { atomFamily } from 'recoil';
|
||||
|
||||
export const isRowSelectedFamilyState = atomFamily<boolean, string>({
|
||||
key: 'isRowSelectedFamilyState',
|
||||
default: false,
|
||||
});
|
||||
@ -0,0 +1,6 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
export const numberOfSelectedRowState = atom<number>({
|
||||
key: 'numberOfSelectedRowState',
|
||||
default: 0,
|
||||
});
|
||||
6
front/src/modules/ui/tables/states/tableRowIdsState.ts
Normal file
6
front/src/modules/ui/tables/states/tableRowIdsState.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
export const tableRowIdsState = atom<string[]>({
|
||||
key: 'tableRowIdsState',
|
||||
default: [],
|
||||
});
|
||||
@ -0,0 +1 @@
|
||||
export type AllRowsSelectedStatus = 'none' | 'some' | 'all';
|
||||
@ -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,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user