Skip to content

Commit

Permalink
feat: order multiple spectra analysis table (#1887)
Browse files Browse the repository at this point in the history
* refactor: react-table header icon and fix style

* feat: order multiple spectra analysis table

close #1876

* fix: serial number column

* refactor: cast formatted string to number

* refactor: react table

* fix: multiple spectra analysis sorting

* test: fix peaks test to look for a number and not a string

* fix: format hook

* refactor: stop event propagation for delete button

* fix: peak pointer shift

* feat: add onSortEnd event to react-table

* feat: reorder spectra and columns in multi-spectra analysis

reorder spectra based on the columns ordered in multi-spectra analysis
enable/disable spectra reorder from multi-spectra analysis preferences, by default it's enabled

close  #1876
  • Loading branch information
hamed-musallam committed Nov 10, 2022
1 parent c04d6df commit 9f90f13
Show file tree
Hide file tree
Showing 32 changed files with 501 additions and 342 deletions.
10 changes: 10 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Expand Up @@ -114,6 +114,7 @@
"@types/node": "^18.11.8",
"@types/react": "^18.0.24",
"@types/react-dom": "^18.0.8",
"@types/react-table": "^7.7.12",
"@vitejs/plugin-react": "^2.2.0",
"@vitest/coverage-c8": "^0.24.4",
"@zakodium/eslint-config": "^6.0.0",
Expand Down
3 changes: 1 addition & 2 deletions src/component/1d/tool/PeakPointer.tsx
Expand Up @@ -43,10 +43,9 @@ function PeakPointer() {
const [closePeakPosition, setPosition] = useState<PeakPosition | null>();

useEffect(() => {
const vShift = activeSpectrum
const vShift = activeSpectrum?.id
? getVerticalShift(verticalAlign, {
index: activeSpectrum?.index || 1,
align: 'center',
})
: 0;

Expand Down
1 change: 0 additions & 1 deletion src/component/1d/utilities/getVerticalShift.ts
Expand Up @@ -8,7 +8,6 @@ export default function getVerticalShift(
index: 1,
align: 'stack',
};

return verticalAlign.align === align
? index * verticalAlign.verticalShift
: 0;
Expand Down
90 changes: 67 additions & 23 deletions src/component/elements/ReactTable/Elements/ReactTableHeader.tsx
@@ -1,33 +1,77 @@
import { memo } from 'react';
import { CSSProperties, MouseEvent } from 'react';
import { FaSortAmountDown, FaSortAmountUp } from 'react-icons/fa';

interface ReactTableHeaderProps {
interface TableCellEvent {
onClick: (e: MouseEvent<HTMLTableCellElement>) => void;
}
interface ReactTableHeaderProps extends TableCellEvent {
headerGroups: any;
}

function ReactTableHeader({ headerGroups }: ReactTableHeaderProps) {
const sortIconStyle: CSSProperties = {
position: 'absolute',
top: '50%',
transform: 'translateY(-50%)',
left: '2px',
};

function ReactTableHeader({ headerGroups, onClick }: ReactTableHeaderProps) {
return (
<thead>
{headerGroups.map((headerGroup) => (
<tr
key={headerGroup.getHeaderGroupProps().key}
{...headerGroup.getHeaderGroupProps()}
>
{headerGroup.headers.map((column) => (
<th
key={column.getHeaderProps().key}
{...column.getHeaderProps(column.getSortByToggleProps())}
style={column.style}
>
{column.render('Header')}
<span>
{column.isSorted ? (column.isSortedDesc ? ' ▼' : ' ▲') : ''}
</span>
</th>
))}
</tr>
))}
{headerGroups.map((headerGroup) => {
const { key: headerGroupKey, ...restHeaderGroupProps } =
headerGroup.getHeaderGroupProps();
return (
<tr key={headerGroupKey} {...restHeaderGroupProps}>
{headerGroup.headers.map((column) => (
<HeaderCell key={column.key} column={column} onClick={onClick} />
))}
</tr>
);
})}
</thead>
);
}

export default memo(ReactTableHeader);
interface HeaderCellProps extends TableCellEvent {
column: any;
}

const HeaderCell = (props: HeaderCellProps) => {
const { column } = props;
const {
key: headerKey,
style: headerStyle,
onClick,
...restHeaderProps
} = column.getHeaderProps(column.getSortByToggleProps());
function clickHandler(e: MouseEvent<HTMLTableCellElement>) {
if (onClick) {
onClick(e);
props.onClick(e);
}
}
return (
<th
key={headerKey}
{...restHeaderProps}
style={{ ...headerStyle, ...column.style, height: '1px' }}
onClick={clickHandler}
>
<span style={sortIconStyle}>
{column.isSorted ? (
column.isSortedDesc ? (
<FaSortAmountDown />
) : (
<FaSortAmountUp />
)
) : (
''
)}
</span>
{column.render('Header') && column.render('Header')}
</th>
);
};

export default ReactTableHeader;
5 changes: 2 additions & 3 deletions src/component/elements/ReactTable/Elements/ReactTableRow.tsx
@@ -1,7 +1,7 @@
/** @jsxImportSource @emotion/react */

import { css, CSSObject } from '@emotion/react';
import { useMemo, forwardRef, useEffect, useCallback } from 'react';
import { useMemo, forwardRef, useEffect, useCallback, MouseEvent } from 'react';

import { HighlightEventSource, useHighlight } from '../../../highlight/index';

Expand Down Expand Up @@ -30,8 +30,7 @@ export interface ClickEvent {
interface ReactTableRowProps extends ClickEvent {
row: any;
highlightedSource?: HighlightEventSource;
onContextMenu: () => void;
isVisible: boolean;
onContextMenu: (e: MouseEvent<HTMLTableRowElement>) => void;
isRowActive: boolean;
}

Expand Down
68 changes: 58 additions & 10 deletions src/component/elements/ReactTable/ReactTable.tsx
Expand Up @@ -9,8 +9,17 @@ import {
CSSProperties,
WheelEvent,
useLayoutEffect,
useEffect,
} from 'react';
import { useTable, useSortBy } from 'react-table';
import {
useTable,
useSortBy,
TableInstance,
CellProps,
Column as ReactColumn,
UseSortByColumnOptions,
UseSortByInstanceProps,
} from 'react-table';
import { useMeasure } from 'react-use';

import checkModifierKeyActivated from '../../../data/utilities/checkModifierKeyActivated';
Expand All @@ -24,9 +33,29 @@ import {
ReactTableProvider,
useReactTableContext,
} from './utility/ReactTableContext';
import useRowSpan, { prepareRowSpan } from './utility/useRowSpan';
import useRowSpan, {
prepareRowSpan,
RowSpanHeaders,
} from './utility/useRowSpan';

interface ExtraColumn<T extends object> {
enableRowSpan?: boolean;
style?: CSSProperties;
Cell?: (cell: CellProps<T, any>) => JSX.Element | string;
}

export type Column<T extends object> = ReactColumn<T> &
ExtraColumn<T> &
UseSortByColumnOptions<T>;

interface ReactTableProps extends ClickEvent {
type TableInstanceWithHooks = TableInstance & {
rowSpanHeaders: RowSpanHeaders;
} & UseSortByInstanceProps<any>;

interface SortEvent {
onSortEnd?: (data: any) => void;
}
interface ReactTableProps extends ClickEvent, SortEvent {
data: any;
columns: any;
highlightedSource?: HighlightEventSource;
Expand All @@ -44,10 +73,11 @@ interface ReactTableInnerProps extends ReactTableProps {
}

const styles = {
table: (enableVirtualScroll: boolean) => {
table: (enableVirtualScroll: boolean): CSSProperties => {
if (enableVirtualScroll) {
return { position: 'sticky', top: 0 };
}
return {};
},
};

Expand Down Expand Up @@ -80,9 +110,11 @@ const ReactTableInner = forwardRef(function ReactTableInner(
highlightActiveRow = false,
totalCount,
indexKey = 'index',
onSortEnd,
} = props;

const contextRef = useRef<any>(null);
const isSortedEventTriggered = useRef<boolean>(false);
const virtualBoundary = useReactTableContext();
const [rowIndex, setRowIndex] = useState<number>();
const timeoutIdRef = useRef<NodeJS.Timeout>();
Expand All @@ -102,7 +134,7 @@ const ReactTableInner = forwardRef(function ReactTableInner(
},
useSortBy,
useRowSpan,
);
) as TableInstanceWithHooks;
function contextMenuHandler(e, row) {
if (!checkModifierKeyActivated(e)) {
e.preventDefault();
Expand Down Expand Up @@ -130,6 +162,18 @@ const ReactTableInner = forwardRef(function ReactTableInner(
}, 1000);
}

useEffect(() => {
if (isSortedEventTriggered.current) {
const data = rows.map((row) => row.original);
onSortEnd?.(data);
isSortedEventTriggered.current = false;
}
}, [onSortEnd, rows]);

function headerClickHandler() {
isSortedEventTriggered.current = true;
}

const end =
virtualBoundary.end === rows.length - 1
? virtualBoundary.end + 1
Expand Down Expand Up @@ -170,7 +214,10 @@ const ReactTableInner = forwardRef(function ReactTableInner(
css={ReactTableStyle}
style={styles.table(enableVirtualScroll)}
>
<ReactTableHeader headerGroups={headerGroups} />
<ReactTableHeader
headerGroups={headerGroups}
onClick={headerClickHandler}
/>
<tbody {...getTableBodyProps()}>
{rowsData.map((row, index) => {
prepareRow(row);
Expand All @@ -181,12 +228,12 @@ const ReactTableInner = forwardRef(function ReactTableInner(
rowSpanHeaders,
groupKey,
);

const { key, ...restRowProps } = row.getRowProps();
return (
<ReactTableRow
key={row.key}
key={key}
{...restRowProps}
row={row}
{...row.getRowProps()}
onContextMenu={(e) => contextMenuHandler(e, row)}
onClick={highlightActiveRow ? clickHandler : onClick}
highlightedSource={highlightedSource}
Expand Down Expand Up @@ -220,7 +267,7 @@ export interface TableVirtualBoundary {
}

function ReactTable(props: ReactTableProps) {
const { data, approxItemHeight = 40, groupKey } = props;
const { data, approxItemHeight = 40, groupKey, onSortEnd } = props;
const containerRef = useRef<HTMLDivElement | null>(null);
const visibleRowsCountRef = useRef<number>(0);
const [mRef, { height }] = useMeasure<HTMLDivElement>();
Expand Down Expand Up @@ -310,6 +357,7 @@ function ReactTable(props: ReactTableProps) {
>
<ReactTableInner
onScroll={scrollHandler}
onSortEnd={onSortEnd}
ref={containerRef}
{...{ ...props }}
/>
Expand Down

0 comments on commit 9f90f13

Please sign in to comment.