mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-16 21:35:58 +08:00
feat: add the ability to drag columns (#3100)
* feat: add the ability to drag columns * feat: add the ability to drag columns in the logs explorer * feat: update drag logic * fix: resolve comments * feat: resolve comment regarding error handling --------- Co-authored-by: Vishal Sharma <makeavish786@gmail.com> Co-authored-by: Palash Gupta <palashgdev@gmail.com>
This commit is contained in:
parent
07833b9859
commit
8f1451e154
@ -69,6 +69,7 @@
|
|||||||
"papaparse": "5.4.1",
|
"papaparse": "5.4.1",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
|
"react-drag-listview": "2.0.0",
|
||||||
"react-force-graph": "^1.41.0",
|
"react-force-graph": "^1.41.0",
|
||||||
"react-grid-layout": "^1.3.4",
|
"react-grid-layout": "^1.3.4",
|
||||||
"react-i18next": "^11.16.1",
|
"react-i18next": "^11.16.1",
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
/* eslint-disable react/jsx-props-no-spreading */
|
||||||
|
|
||||||
import { Table } from 'antd';
|
import { Table } from 'antd';
|
||||||
import type { TableProps } from 'antd/es/table';
|
|
||||||
import { ColumnsType } from 'antd/lib/table';
|
import { ColumnsType } from 'antd/lib/table';
|
||||||
|
import { dragColumnParams } from 'hooks/useDragColumns/configs';
|
||||||
import {
|
import {
|
||||||
SyntheticEvent,
|
SyntheticEvent,
|
||||||
useCallback,
|
useCallback,
|
||||||
@ -8,12 +10,18 @@ import {
|
|||||||
useMemo,
|
useMemo,
|
||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
import ReactDragListView from 'react-drag-listview';
|
||||||
import { ResizeCallbackData } from 'react-resizable';
|
import { ResizeCallbackData } from 'react-resizable';
|
||||||
|
|
||||||
import ResizableHeader from './ResizableHeader';
|
import ResizableHeader from './ResizableHeader';
|
||||||
|
import { DragSpanStyle } from './styles';
|
||||||
|
import { ResizeTableProps } from './types';
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
function ResizeTable({
|
||||||
function ResizeTable({ columns, ...restprops }: TableProps<any>): JSX.Element {
|
columns,
|
||||||
|
onDragColumn,
|
||||||
|
...restProps
|
||||||
|
}: ResizeTableProps): JSX.Element {
|
||||||
const [columnsData, setColumns] = useState<ColumnsType>([]);
|
const [columnsData, setColumns] = useState<ColumnsType>([]);
|
||||||
|
|
||||||
const handleResize = useCallback(
|
const handleResize = useCallback(
|
||||||
@ -31,16 +39,32 @@ function ResizeTable({ columns, ...restprops }: TableProps<any>): JSX.Element {
|
|||||||
[columnsData],
|
[columnsData],
|
||||||
);
|
);
|
||||||
|
|
||||||
const mergeColumns = useMemo(
|
const mergedColumns = useMemo(
|
||||||
() =>
|
() =>
|
||||||
columnsData.map((col, index) => ({
|
columnsData.map((col, index) => ({
|
||||||
...col,
|
...col,
|
||||||
|
...(onDragColumn && {
|
||||||
|
title: (
|
||||||
|
<DragSpanStyle className="dragHandler">
|
||||||
|
{col?.title?.toString() || ''}
|
||||||
|
</DragSpanStyle>
|
||||||
|
),
|
||||||
|
}),
|
||||||
onHeaderCell: (column: ColumnsType<unknown>[number]): unknown => ({
|
onHeaderCell: (column: ColumnsType<unknown>[number]): unknown => ({
|
||||||
width: column.width,
|
width: column.width,
|
||||||
onResize: handleResize(index),
|
onResize: handleResize(index),
|
||||||
}),
|
}),
|
||||||
})),
|
})) as ColumnsType<any>,
|
||||||
[columnsData, handleResize],
|
[columnsData, onDragColumn, handleResize],
|
||||||
|
);
|
||||||
|
|
||||||
|
const tableParams = useMemo(
|
||||||
|
() => ({
|
||||||
|
...restProps,
|
||||||
|
components: { header: { cell: ResizableHeader } },
|
||||||
|
columns: mergedColumns,
|
||||||
|
}),
|
||||||
|
[mergedColumns, restProps],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -49,15 +73,17 @@ function ResizeTable({ columns, ...restprops }: TableProps<any>): JSX.Element {
|
|||||||
}
|
}
|
||||||
}, [columns]);
|
}, [columns]);
|
||||||
|
|
||||||
return (
|
return onDragColumn ? (
|
||||||
<Table
|
<ReactDragListView.DragColumn {...dragColumnParams} onDragEnd={onDragColumn}>
|
||||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
<Table {...tableParams} />
|
||||||
{...restprops}
|
</ReactDragListView.DragColumn>
|
||||||
components={{ header: { cell: ResizableHeader } }}
|
) : (
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
<Table {...tableParams} />
|
||||||
columns={mergeColumns as ColumnsType<any>}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ResizeTable.defaultProps = {
|
||||||
|
onDragColumn: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
export default ResizeTable;
|
export default ResizeTable;
|
||||||
|
@ -2,10 +2,16 @@ import styled from 'styled-components';
|
|||||||
|
|
||||||
export const SpanStyle = styled.span`
|
export const SpanStyle = styled.span`
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: -5px;
|
right: -0.313rem;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
width: 10px;
|
width: 0.625rem;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
cursor: col-resize;
|
cursor: col-resize;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const DragSpanStyle = styled.span`
|
||||||
|
display: flex;
|
||||||
|
margin: -1rem;
|
||||||
|
padding: 1rem;
|
||||||
|
`;
|
||||||
|
6
frontend/src/components/ResizeTable/types.ts
Normal file
6
frontend/src/components/ResizeTable/types.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { TableProps } from 'antd';
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
export interface ResizeTableProps extends TableProps<any> {
|
||||||
|
onDragColumn?: (fromIndex: number, toIndex: number) => void;
|
||||||
|
}
|
@ -8,4 +8,6 @@ export enum LOCALSTORAGE {
|
|||||||
LOGS_LINES_PER_ROW = 'LOGS_LINES_PER_ROW',
|
LOGS_LINES_PER_ROW = 'LOGS_LINES_PER_ROW',
|
||||||
LOGS_LIST_OPTIONS = 'LOGS_LIST_OPTIONS',
|
LOGS_LIST_OPTIONS = 'LOGS_LIST_OPTIONS',
|
||||||
TRACES_LIST_OPTIONS = 'TRACES_LIST_OPTIONS',
|
TRACES_LIST_OPTIONS = 'TRACES_LIST_OPTIONS',
|
||||||
|
TRACES_LIST_COLUMNS = 'TRACES_LIST_COLUMNS',
|
||||||
|
LOGS_LIST_COLUMNS = 'LOGS_LIST_COLUMNS',
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
import { dragColumnParams } from 'hooks/useDragColumns/configs';
|
||||||
|
import ReactDragListView from 'react-drag-listview';
|
||||||
|
import { TableComponents } from 'react-virtuoso';
|
||||||
|
|
||||||
|
import { TableStyled } from './styles';
|
||||||
|
|
||||||
|
interface LogsCustomTableProps {
|
||||||
|
handleDragEnd: (fromIndex: number, toIndex: number) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LogsCustomTable = ({
|
||||||
|
handleDragEnd,
|
||||||
|
}: LogsCustomTableProps): TableComponents['Table'] =>
|
||||||
|
function CustomTable({ style, children }): JSX.Element {
|
||||||
|
return (
|
||||||
|
<ReactDragListView.DragColumn
|
||||||
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
|
{...dragColumnParams}
|
||||||
|
onDragEnd={handleDragEnd}
|
||||||
|
>
|
||||||
|
<TableStyled style={style}>{children}</TableStyled>
|
||||||
|
</ReactDragListView.DragColumn>
|
||||||
|
);
|
||||||
|
};
|
@ -1,22 +1,26 @@
|
|||||||
import { ColumnTypeRender } from 'components/Logs/TableView/types';
|
import { ColumnTypeRender } from 'components/Logs/TableView/types';
|
||||||
import { useTableView } from 'components/Logs/TableView/useTableView';
|
import { useTableView } from 'components/Logs/TableView/useTableView';
|
||||||
import { cloneElement, ReactElement, ReactNode, useCallback } from 'react';
|
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||||
|
import useDragColumns from 'hooks/useDragColumns';
|
||||||
|
import { getDraggedColumns } from 'hooks/useDragColumns/utils';
|
||||||
|
import {
|
||||||
|
cloneElement,
|
||||||
|
ReactElement,
|
||||||
|
ReactNode,
|
||||||
|
useCallback,
|
||||||
|
useMemo,
|
||||||
|
} from 'react';
|
||||||
import { TableComponents, TableVirtuoso } from 'react-virtuoso';
|
import { TableComponents, TableVirtuoso } from 'react-virtuoso';
|
||||||
|
|
||||||
import { infinityDefaultStyles } from './config';
|
import { infinityDefaultStyles } from './config';
|
||||||
|
import { LogsCustomTable } from './LogsCustomTable';
|
||||||
import {
|
import {
|
||||||
TableCellStyled,
|
TableCellStyled,
|
||||||
TableHeaderCellStyled,
|
TableHeaderCellStyled,
|
||||||
TableRowStyled,
|
TableRowStyled,
|
||||||
TableStyled,
|
|
||||||
} from './styles';
|
} from './styles';
|
||||||
import { InfinityTableProps } from './types';
|
import { InfinityTableProps } from './types';
|
||||||
|
|
||||||
// eslint-disable-next-line react/function-component-definition
|
|
||||||
const CustomTable: TableComponents['Table'] = ({ style, children }) => (
|
|
||||||
<TableStyled style={style}>{children}</TableStyled>
|
|
||||||
);
|
|
||||||
|
|
||||||
// eslint-disable-next-line react/function-component-definition
|
// eslint-disable-next-line react/function-component-definition
|
||||||
const CustomTableRow: TableComponents['TableRow'] = ({
|
const CustomTableRow: TableComponents['TableRow'] = ({
|
||||||
children,
|
children,
|
||||||
@ -31,11 +35,25 @@ function InfinityTable({
|
|||||||
}: InfinityTableProps): JSX.Element | null {
|
}: InfinityTableProps): JSX.Element | null {
|
||||||
const { onEndReached } = infitiyTableProps;
|
const { onEndReached } = infitiyTableProps;
|
||||||
const { dataSource, columns } = useTableView(tableViewProps);
|
const { dataSource, columns } = useTableView(tableViewProps);
|
||||||
|
const { draggedColumns, onDragColumns } = useDragColumns<
|
||||||
|
Record<string, unknown>
|
||||||
|
>(LOCALSTORAGE.LOGS_LIST_COLUMNS);
|
||||||
|
|
||||||
|
const tableColumns = useMemo(
|
||||||
|
() => getDraggedColumns<Record<string, unknown>>(columns, draggedColumns),
|
||||||
|
[columns, draggedColumns],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleDragEnd = useCallback(
|
||||||
|
(fromIndex: number, toIndex: number) =>
|
||||||
|
onDragColumns(tableColumns, fromIndex, toIndex),
|
||||||
|
[tableColumns, onDragColumns],
|
||||||
|
);
|
||||||
|
|
||||||
const itemContent = useCallback(
|
const itemContent = useCallback(
|
||||||
(index: number, log: Record<string, unknown>): JSX.Element => (
|
(index: number, log: Record<string, unknown>): JSX.Element => (
|
||||||
<>
|
<>
|
||||||
{columns.map((column) => {
|
{tableColumns.map((column) => {
|
||||||
if (!column.render) return <td>Empty</td>;
|
if (!column.render) return <td>Empty</td>;
|
||||||
|
|
||||||
const element: ColumnTypeRender<Record<string, unknown>> = column.render(
|
const element: ColumnTypeRender<Record<string, unknown>> = column.render(
|
||||||
@ -60,20 +78,29 @@ function InfinityTable({
|
|||||||
})}
|
})}
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
[columns],
|
[tableColumns],
|
||||||
);
|
);
|
||||||
|
|
||||||
const tableHeader = useCallback(
|
const tableHeader = useCallback(
|
||||||
() => (
|
() => (
|
||||||
<tr>
|
<tr>
|
||||||
{columns.map((column) => (
|
{tableColumns.map((column) => {
|
||||||
<TableHeaderCellStyled key={column.key}>
|
const isDragColumn = column.key !== 'expand';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableHeaderCellStyled
|
||||||
|
isDragColumn={isDragColumn}
|
||||||
|
key={column.key}
|
||||||
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
|
{...(isDragColumn && { className: 'dragHandler' })}
|
||||||
|
>
|
||||||
{column.title as string}
|
{column.title as string}
|
||||||
</TableHeaderCellStyled>
|
</TableHeaderCellStyled>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</tr>
|
</tr>
|
||||||
),
|
),
|
||||||
[columns],
|
[tableColumns],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -81,7 +108,8 @@ function InfinityTable({
|
|||||||
style={infinityDefaultStyles}
|
style={infinityDefaultStyles}
|
||||||
data={dataSource}
|
data={dataSource}
|
||||||
components={{
|
components={{
|
||||||
Table: CustomTable,
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
|
Table: LogsCustomTable({ handleDragEnd }),
|
||||||
// TODO: fix it in the future
|
// TODO: fix it in the future
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
import { themeColors } from 'constants/theme';
|
import { themeColors } from 'constants/theme';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
interface TableHeaderCellStyledProps {
|
||||||
|
isDragColumn: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export const TableStyled = styled.table`
|
export const TableStyled = styled.table`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-top: 1px solid rgba(253, 253, 253, 0.12);
|
border-top: 1px solid rgba(253, 253, 253, 0.12);
|
||||||
@ -26,10 +30,12 @@ export const TableRowStyled = styled.tr`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const TableHeaderCellStyled = styled.th`
|
export const TableHeaderCellStyled = styled.th<TableHeaderCellStyledProps>`
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
border-inline-end: 1px solid rgba(253, 253, 253, 0.12);
|
border-inline-end: 1px solid rgba(253, 253, 253, 0.12);
|
||||||
background-color: #1d1d1d;
|
background-color: #1d1d1d;
|
||||||
|
${({ isDragColumn }): string => (isDragColumn ? 'cursor: col-resize;' : '')}
|
||||||
|
|
||||||
&:first-child {
|
&:first-child {
|
||||||
border-start-start-radius: 2px;
|
border-start-start-radius: 2px;
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ interface UseOptionsMenuProps {
|
|||||||
interface UseOptionsMenu {
|
interface UseOptionsMenu {
|
||||||
options: OptionsQuery;
|
options: OptionsQuery;
|
||||||
config: OptionsMenuConfig;
|
config: OptionsMenuConfig;
|
||||||
|
handleOptionsChange: (newQueryData: OptionsQuery) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const useOptionsMenu = ({
|
const useOptionsMenu = ({
|
||||||
@ -306,6 +307,7 @@ const useOptionsMenu = ({
|
|||||||
return {
|
return {
|
||||||
options: optionsQueryData,
|
options: optionsQueryData,
|
||||||
config: optionsMenuConfig,
|
config: optionsMenuConfig,
|
||||||
|
handleOptionsChange: handleRedirectWithOptionsData,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -6,6 +6,8 @@ import { useOptionsMenu } from 'container/OptionsMenu';
|
|||||||
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
|
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
|
||||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
import { Pagination, URL_PAGINATION } from 'hooks/queryPagination';
|
import { Pagination, URL_PAGINATION } from 'hooks/queryPagination';
|
||||||
|
import useDragColumns from 'hooks/useDragColumns';
|
||||||
|
import { getDraggedColumns } from 'hooks/useDragColumns/utils';
|
||||||
import useUrlQueryData from 'hooks/useUrlQueryData';
|
import useUrlQueryData from 'hooks/useUrlQueryData';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import { RowData } from 'lib/query/createTableColumnsFromQuery';
|
import { RowData } from 'lib/query/createTableColumnsFromQuery';
|
||||||
@ -37,6 +39,10 @@ function ListView(): JSX.Element {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { draggedColumns, onDragColumns } = useDragColumns<RowData>(
|
||||||
|
LOCALSTORAGE.TRACES_LIST_COLUMNS,
|
||||||
|
);
|
||||||
|
|
||||||
const { queryData: paginationQueryData } = useUrlQueryData<Pagination>(
|
const { queryData: paginationQueryData } = useUrlQueryData<Pagination>(
|
||||||
URL_PAGINATION,
|
URL_PAGINATION,
|
||||||
);
|
);
|
||||||
@ -82,9 +88,10 @@ function ListView(): JSX.Element {
|
|||||||
queryTableDataResult,
|
queryTableDataResult,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const columns = useMemo(() => getListColumns(options?.selectColumns || []), [
|
const columns = useMemo(() => {
|
||||||
options?.selectColumns,
|
const updatedColumns = getListColumns(options?.selectColumns || []);
|
||||||
]);
|
return getDraggedColumns(updatedColumns, draggedColumns);
|
||||||
|
}, [options?.selectColumns, draggedColumns]);
|
||||||
|
|
||||||
const transformedQueryTableData = useMemo(
|
const transformedQueryTableData = useMemo(
|
||||||
() => transformDataWithDate(queryTableData) || [],
|
() => transformDataWithDate(queryTableData) || [],
|
||||||
@ -106,6 +113,12 @@ function ListView(): JSX.Element {
|
|||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleDragColumn = useCallback(
|
||||||
|
(fromIndex: number, toIndex: number) =>
|
||||||
|
onDragColumns(columns, fromIndex, toIndex),
|
||||||
|
[columns, onDragColumns],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<TraceExplorerControls
|
<TraceExplorerControls
|
||||||
@ -127,6 +140,7 @@ function ListView(): JSX.Element {
|
|||||||
dataSource={transformedQueryTableData}
|
dataSource={transformedQueryTableData}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
onRow={handleRow}
|
onRow={handleRow}
|
||||||
|
onDragColumn={handleDragColumn}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Container>
|
</Container>
|
||||||
|
7
frontend/src/hooks/useDragColumns/configs.ts
Normal file
7
frontend/src/hooks/useDragColumns/configs.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export const COLUMNS = 'columns';
|
||||||
|
|
||||||
|
export const dragColumnParams = {
|
||||||
|
ignoreSelector: '.react-resizable-handle',
|
||||||
|
nodeSelector: 'th',
|
||||||
|
handleSelector: '.dragHandler',
|
||||||
|
};
|
75
frontend/src/hooks/useDragColumns/index.ts
Normal file
75
frontend/src/hooks/useDragColumns/index.ts
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import { ColumnsType } from 'antd/es/table';
|
||||||
|
import getFromLocalstorage from 'api/browser/localstorage/get';
|
||||||
|
import setToLocalstorage from 'api/browser/localstorage/set';
|
||||||
|
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||||
|
import useUrlQueryData from 'hooks/useUrlQueryData';
|
||||||
|
import { useCallback, useEffect, useMemo } from 'react';
|
||||||
|
|
||||||
|
import { COLUMNS } from './configs';
|
||||||
|
import { UseDragColumns } from './types';
|
||||||
|
|
||||||
|
const useDragColumns = <T>(storageKey: LOCALSTORAGE): UseDragColumns<T> => {
|
||||||
|
const {
|
||||||
|
query: draggedColumnsQuery,
|
||||||
|
queryData: draggedColumns,
|
||||||
|
redirectWithQuery: redirectWithDraggedColumns,
|
||||||
|
} = useUrlQueryData<ColumnsType<T>>(COLUMNS, []);
|
||||||
|
|
||||||
|
const localStorageDraggedColumns = useMemo(
|
||||||
|
() => getFromLocalstorage(storageKey),
|
||||||
|
[storageKey],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleRedirectWithDraggedColumns = useCallback(
|
||||||
|
(columns: ColumnsType<T>) => {
|
||||||
|
redirectWithDraggedColumns(columns);
|
||||||
|
|
||||||
|
setToLocalstorage(storageKey, JSON.stringify(columns));
|
||||||
|
},
|
||||||
|
[storageKey, redirectWithDraggedColumns],
|
||||||
|
);
|
||||||
|
|
||||||
|
const onDragColumns = useCallback(
|
||||||
|
(columns: ColumnsType<T>, fromIndex: number, toIndex: number): void => {
|
||||||
|
const columnsData = [...columns];
|
||||||
|
const item = columnsData.splice(fromIndex, 1)[0];
|
||||||
|
columnsData.splice(toIndex, 0, item);
|
||||||
|
|
||||||
|
handleRedirectWithDraggedColumns(columnsData);
|
||||||
|
},
|
||||||
|
[handleRedirectWithDraggedColumns],
|
||||||
|
);
|
||||||
|
|
||||||
|
const redirectWithNewDraggedColumns = useCallback(
|
||||||
|
async (localStorageColumns: string) => {
|
||||||
|
let nextDraggedColumns: ColumnsType<T> = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
const parsedDraggedColumns = await JSON.parse(localStorageColumns);
|
||||||
|
nextDraggedColumns = parsedDraggedColumns;
|
||||||
|
} catch (e) {
|
||||||
|
console.log('error while parsing json');
|
||||||
|
} finally {
|
||||||
|
redirectWithDraggedColumns(nextDraggedColumns);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[redirectWithDraggedColumns],
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (draggedColumnsQuery || !localStorageDraggedColumns) return;
|
||||||
|
|
||||||
|
redirectWithNewDraggedColumns(localStorageDraggedColumns);
|
||||||
|
}, [
|
||||||
|
draggedColumnsQuery,
|
||||||
|
localStorageDraggedColumns,
|
||||||
|
redirectWithNewDraggedColumns,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
draggedColumns,
|
||||||
|
onDragColumns,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useDragColumns;
|
10
frontend/src/hooks/useDragColumns/types.ts
Normal file
10
frontend/src/hooks/useDragColumns/types.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { ColumnsType } from 'antd/es/table';
|
||||||
|
|
||||||
|
export type UseDragColumns<T> = {
|
||||||
|
draggedColumns: ColumnsType<T>;
|
||||||
|
onDragColumns: (
|
||||||
|
columns: ColumnsType<T>,
|
||||||
|
fromIndex: number,
|
||||||
|
toIndex: number,
|
||||||
|
) => void;
|
||||||
|
};
|
37
frontend/src/hooks/useDragColumns/utils.ts
Normal file
37
frontend/src/hooks/useDragColumns/utils.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { ColumnsType } from 'antd/es/table';
|
||||||
|
|
||||||
|
const filterColumns = <T>(
|
||||||
|
initialColumns: ColumnsType<T>,
|
||||||
|
findColumns: ColumnsType<T>,
|
||||||
|
isColumnExist = true,
|
||||||
|
): ColumnsType<T> =>
|
||||||
|
initialColumns.filter(({ title: columnTitle }) => {
|
||||||
|
const column = findColumns.find(({ title }) => title === columnTitle);
|
||||||
|
|
||||||
|
return isColumnExist ? !!column : !column;
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getDraggedColumns = <T>(
|
||||||
|
currentColumns: ColumnsType<T>,
|
||||||
|
draggedColumns: ColumnsType<T>,
|
||||||
|
): ColumnsType<T> => {
|
||||||
|
if (draggedColumns.length) {
|
||||||
|
const actualDruggedColumns = filterColumns<T>(draggedColumns, currentColumns);
|
||||||
|
const newColumns = filterColumns<T>(
|
||||||
|
currentColumns,
|
||||||
|
actualDruggedColumns,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
return [...actualDruggedColumns, ...newColumns].reduce((acc, { title }) => {
|
||||||
|
const column = currentColumns.find(
|
||||||
|
({ title: columnTitle }) => title === columnTitle,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (column) return [...acc, column];
|
||||||
|
return acc;
|
||||||
|
}, [] as ColumnsType<T>);
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentColumns;
|
||||||
|
};
|
@ -3719,6 +3719,14 @@ babel-preset-react-app@^10.0.0:
|
|||||||
babel-plugin-macros "^3.1.0"
|
babel-plugin-macros "^3.1.0"
|
||||||
babel-plugin-transform-react-remove-prop-types "^0.4.24"
|
babel-plugin-transform-react-remove-prop-types "^0.4.24"
|
||||||
|
|
||||||
|
babel-runtime@^6.26.0:
|
||||||
|
version "6.26.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
|
||||||
|
integrity sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==
|
||||||
|
dependencies:
|
||||||
|
core-js "^2.4.0"
|
||||||
|
regenerator-runtime "^0.11.0"
|
||||||
|
|
||||||
balanced-match@^1.0.0:
|
balanced-match@^1.0.0:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz"
|
resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz"
|
||||||
@ -4474,6 +4482,11 @@ core-js-compat@^3.25.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
browserslist "^4.21.5"
|
browserslist "^4.21.5"
|
||||||
|
|
||||||
|
core-js@^2.4.0:
|
||||||
|
version "2.6.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec"
|
||||||
|
integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==
|
||||||
|
|
||||||
core-util-is@~1.0.0:
|
core-util-is@~1.0.0:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz"
|
resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz"
|
||||||
@ -9999,7 +10012,7 @@ prompts@^2.0.1, prompts@^2.4.1:
|
|||||||
kleur "^3.0.3"
|
kleur "^3.0.3"
|
||||||
sisteransi "^1.0.5"
|
sisteransi "^1.0.5"
|
||||||
|
|
||||||
prop-types@15, prop-types@15.x, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1:
|
prop-types@15, prop-types@15.x, prop-types@^15.5.8, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1:
|
||||||
version "15.8.1"
|
version "15.8.1"
|
||||||
resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz"
|
resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz"
|
||||||
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
|
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
|
||||||
@ -10513,6 +10526,14 @@ react-dom@18.2.0:
|
|||||||
loose-envify "^1.1.0"
|
loose-envify "^1.1.0"
|
||||||
scheduler "^0.23.0"
|
scheduler "^0.23.0"
|
||||||
|
|
||||||
|
react-drag-listview@2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-drag-listview/-/react-drag-listview-2.0.0.tgz#b8e7ec5f980ecbbf3abb85f50db0b03cd764edbf"
|
||||||
|
integrity sha512-7Apx/1Xt4qu+JHHP0rH6aLgZgS7c2MX8ocHVGCi03KfeIWEu0t14MhT3boQKM33l5eJrE/IWfExFTvoYq22fsg==
|
||||||
|
dependencies:
|
||||||
|
babel-runtime "^6.26.0"
|
||||||
|
prop-types "^15.5.8"
|
||||||
|
|
||||||
react-draggable@^4.0.0, react-draggable@^4.0.3:
|
react-draggable@^4.0.0, react-draggable@^4.0.3:
|
||||||
version "4.4.5"
|
version "4.4.5"
|
||||||
resolved "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.5.tgz"
|
resolved "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.5.tgz"
|
||||||
@ -10808,6 +10829,11 @@ regenerator-runtime@0.13.9:
|
|||||||
resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz"
|
resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz"
|
||||||
integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
|
integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
|
||||||
|
|
||||||
|
regenerator-runtime@^0.11.0:
|
||||||
|
version "0.11.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
|
||||||
|
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
|
||||||
|
|
||||||
regenerator-runtime@^0.13.11:
|
regenerator-runtime@^0.13.11:
|
||||||
version "0.13.11"
|
version "0.13.11"
|
||||||
resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz"
|
resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user