From 4e94824565825e067c7a5538b59bd4949e14432c Mon Sep 17 00:00:00 2001 From: Max Patiiuk Date: Sun, 23 Apr 2023 21:55:54 -0500 Subject: [PATCH] Make order of visible columns match ...the order in the pop up for changing column visibility --- src/src/components/Extension/Books.tsx | 35 +++++++++++++++++++++--- src/src/utils/__tests__/utils.test.ts | 23 ++++++++++++++++ src/src/utils/utils.ts | 37 ++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 4 deletions(-) diff --git a/src/src/components/Extension/Books.tsx b/src/src/components/Extension/Books.tsx index dd4da2f..46b6c87 100644 --- a/src/src/components/Extension/Books.tsx +++ b/src/src/components/Extension/Books.tsx @@ -12,7 +12,7 @@ import { commonText } from '../../localization/common'; import { f } from '../../utils/functools'; import type { GetSet, RA } from '../../utils/types'; import { writable } from '../../utils/types'; -import { debounce } from '../../utils/utils'; +import { debounce, multiSortFunction } from '../../utils/utils'; import { Button, Input, Label, Ul } from '../Atoms'; import type { Book } from '../Foreground/readPages'; import { dateColumns, numericColumns } from '../Foreground/readPages'; @@ -58,6 +58,7 @@ export function Books({ } @@ -96,12 +97,38 @@ export function Books({ ); } +const allColumns = Object.keys(columns); + +// FIXME: take a look at 2 bugs (screenshots) function VisibleColumns({ visibleColumns: [visibleColumns, setVisibleColumns], + tableState: state, }: { + readonly tableState: object | false; readonly visibleColumns: GetSet>; }): JSX.Element { const overlayRef = React.useRef(null); + const rawColumnOrder = + typeof state === 'object' && + 'columnOrder' in state && + Array.isArray(state.columnOrder) + ? state.columnOrder + : undefined; + const columnOrder = React.useMemo>( + () => + rawColumnOrder === undefined + ? allColumns + : Array.from(Object.keys(columns)).sort( + multiSortFunction( + (column) => { + const index = rawColumnOrder.indexOf(column); + return index === -1 ? rawColumnOrder.length : index; + }, + (column) => allColumns.indexOf(column) + ) + ), + [rawColumnOrder] + ); return ( <>
    - {Object.entries(columns).map(([header, config]) => - config === undefined ? undefined : ( + {columnOrder.map((header) => + columns[header] === undefined ? undefined : ( - {config.header} + {columns[header]?.header} ) )} diff --git a/src/src/utils/__tests__/utils.test.ts b/src/src/utils/__tests__/utils.test.ts index aecb6fe..edd1cea 100644 --- a/src/src/utils/__tests__/utils.test.ts +++ b/src/src/utils/__tests__/utils.test.ts @@ -3,6 +3,7 @@ import { capitalize, group, mappedFind, + multiSortFunction, replaceItem, sortFunction, toggleItem, @@ -48,6 +49,28 @@ describe('sortFunction', () => { }); }); +test('multiSortFunction', () => { + expect( + [ + { type: 'c', priority: 3 }, + { type: 'd', priority: 4 }, + { type: 'd', priority: 3 }, + { type: 'c', priority: 4 }, + ].sort( + multiSortFunction( + ({ type }) => type, + ({ priority }) => priority, + true + ) + ) + ).toEqual([ + { type: 'c', priority: 4 }, + { type: 'c', priority: 3 }, + { type: 'd', priority: 4 }, + { type: 'd', priority: 3 }, + ]); +}); + theories(toggleItem, { 'add an item that is not present': { in: [[1, 2, 3], 4], out: [1, 2, 3, 4] }, 'remove an item that is present': { in: [[1, 2, 3, 4], 4], out: [1, 2, 3] }, diff --git a/src/src/utils/utils.ts b/src/src/utils/utils.ts index 999b726..25d488b 100644 --- a/src/src/utils/utils.ts +++ b/src/src/utils/utils.ts @@ -4,6 +4,7 @@ * @module */ import type { RA, RR } from './types'; +import { filterArray } from './types'; export const capitalize = (string: T): Capitalize => (string.charAt(0).toUpperCase() + string.slice(1)) as Capitalize; @@ -31,6 +32,42 @@ export const sortFunction = return (leftValue ?? '') > (rightValue ?? '') ? 1 : -1; }; +/** Like sortFunction, but can sort based on multiple fields */ +export const multiSortFunction = + ( + ...payload: readonly ( + | true + | ((value: ORIGINAL_TYPE) => Date | boolean | number | string) + )[] + ): ((left: ORIGINAL_TYPE, right: ORIGINAL_TYPE) => -1 | 0 | 1) => + (left: ORIGINAL_TYPE, right: ORIGINAL_TYPE): -1 | 0 | 1 => { + const mappers = filterArray( + payload.map((value, index) => + typeof value === 'function' + ? ([ + value, + typeof payload[index + 1] === 'boolean' + ? (payload[index + 1] as boolean) + : false, + ] as const) + : undefined + ) + ); + + for (const [mapper, isReverse] of mappers) { + const [leftValue, rightValue] = isReverse + ? [mapper(right), mapper(left)] + : [mapper(left), mapper(right)]; + if (leftValue === rightValue) continue; + return typeof leftValue === 'string' && typeof rightValue === 'string' + ? (leftValue.localeCompare(rightValue) as -1 | 0 | 1) + : leftValue > rightValue + ? 1 + : -1; + } + return 0; + }; + /** Remove item from array if present, otherwise, add it */ export const toggleItem = (array: RA, item: T): RA => array.includes(item)