mirror of
https://github.com/nimbold/Firelink.git
synced 2026-08-06 17:38:06 +00:00
feat(ui): add customizable download table columns
Add persisted column reordering, positional alignment, animated drag feedback, and resize cleanup for the download table. Keep row actions at the trailing edge, preserve RTL/LTR behavior, and include localized column controls and sidebar alignment fixes.
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
DEFAULT_COLUMN_ALIGNMENTS,
|
||||
DEFAULT_COLUMN_ORDER,
|
||||
normalizeColumnAlignments,
|
||||
normalizeColumnOrder,
|
||||
normalizeColumnWidths,
|
||||
} from './downloadTableColumns';
|
||||
|
||||
describe('download table column preferences', () => {
|
||||
it('normalizes a persisted order without losing or duplicating columns', () => {
|
||||
expect(normalizeColumnOrder(['Status', 'Status', 'not-a-column', 'File Name'])).toEqual([
|
||||
'Status',
|
||||
'File Name',
|
||||
'Size',
|
||||
'Speed',
|
||||
'ETA',
|
||||
'Date Added',
|
||||
]);
|
||||
});
|
||||
|
||||
it('clamps persisted widths to column minimums and restores malformed entries', () => {
|
||||
expect(normalizeColumnWidths([10, 70, Number.POSITIVE_INFINITY, 80, '48', 140])).toEqual([
|
||||
160,
|
||||
70,
|
||||
220,
|
||||
80,
|
||||
80,
|
||||
140,
|
||||
]);
|
||||
|
||||
expect(normalizeColumnWidths([0, 100, 220, 100, 80, 170])[0]).toBe(160);
|
||||
});
|
||||
|
||||
it('keeps only supported positional alignment values', () => {
|
||||
expect(normalizeColumnAlignments({
|
||||
'File Name': 'center',
|
||||
Size: 'right',
|
||||
Status: 'invalid',
|
||||
Speed: 'left',
|
||||
})).toEqual({
|
||||
...DEFAULT_COLUMN_ALIGNMENTS,
|
||||
'File Name': 'center',
|
||||
Size: 'right',
|
||||
Speed: 'left',
|
||||
});
|
||||
});
|
||||
|
||||
it('restores the default order for malformed persisted data', () => {
|
||||
expect(normalizeColumnOrder(null)).toEqual([...DEFAULT_COLUMN_ORDER]);
|
||||
expect(normalizeColumnAlignments(null)).toEqual(DEFAULT_COLUMN_ALIGNMENTS);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,89 @@
|
||||
import type { DownloadSortColumn } from './downloadTableSorting';
|
||||
|
||||
export type DownloadTableColumnKey = DownloadSortColumn;
|
||||
export type DownloadColumnAlignment = 'left' | 'center' | 'right';
|
||||
|
||||
export const DEFAULT_COLUMN_ORDER = [
|
||||
'File Name',
|
||||
'Size',
|
||||
'Status',
|
||||
'Speed',
|
||||
'ETA',
|
||||
'Date Added',
|
||||
] as const satisfies readonly DownloadTableColumnKey[];
|
||||
|
||||
export const DEFAULT_COLUMN_WIDTHS = [340, 100, 220, 100, 80, 170] as const;
|
||||
export const COLUMN_MINIMUMS = [160, 58, 92, 58, 48, 112] as const;
|
||||
|
||||
export const COLUMN_WIDTHS_STORAGE_KEY = 'firelink-download-column-widths';
|
||||
export const COLUMN_ORDER_STORAGE_KEY = 'firelink-download-column-order';
|
||||
export const COLUMN_ALIGNMENTS_STORAGE_KEY = 'firelink-download-column-alignments';
|
||||
|
||||
export const DEFAULT_COLUMN_ALIGNMENTS: Record<DownloadTableColumnKey, DownloadColumnAlignment> = {
|
||||
'File Name': 'left',
|
||||
Size: 'left',
|
||||
Status: 'left',
|
||||
Speed: 'left',
|
||||
ETA: 'left',
|
||||
'Date Added': 'left',
|
||||
};
|
||||
|
||||
export const COLUMN_ALIGNMENT_JUSTIFY: Record<DownloadColumnAlignment, 'flex-start' | 'center' | 'flex-end'> = {
|
||||
left: 'flex-start',
|
||||
center: 'center',
|
||||
right: 'flex-end',
|
||||
};
|
||||
|
||||
const isColumnKey = (value: unknown): value is DownloadTableColumnKey =>
|
||||
typeof value === 'string' && (DEFAULT_COLUMN_ORDER as readonly string[]).includes(value);
|
||||
|
||||
const isColumnAlignment = (value: unknown): value is DownloadColumnAlignment =>
|
||||
value === 'left' || value === 'center' || value === 'right';
|
||||
|
||||
export const normalizeColumnOrder = (value: unknown): DownloadTableColumnKey[] => {
|
||||
const seen = new Set<DownloadTableColumnKey>();
|
||||
const normalized: DownloadTableColumnKey[] = [];
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
for (const candidate of value) {
|
||||
if (isColumnKey(candidate) && !seen.has(candidate)) {
|
||||
seen.add(candidate);
|
||||
normalized.push(candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const key of DEFAULT_COLUMN_ORDER) {
|
||||
if (!seen.has(key)) normalized.push(key);
|
||||
}
|
||||
|
||||
return normalized;
|
||||
};
|
||||
|
||||
export const normalizeColumnWidths = (value: unknown): number[] => {
|
||||
if (!Array.isArray(value) || value.length !== DEFAULT_COLUMN_WIDTHS.length) {
|
||||
return [...DEFAULT_COLUMN_WIDTHS];
|
||||
}
|
||||
|
||||
return value.map((width, index) =>
|
||||
typeof width === 'number' && Number.isFinite(width)
|
||||
? Math.max(COLUMN_MINIMUMS[index], width)
|
||||
: DEFAULT_COLUMN_WIDTHS[index]
|
||||
);
|
||||
};
|
||||
|
||||
export const normalizeColumnAlignments = (value: unknown): Record<DownloadTableColumnKey, DownloadColumnAlignment> => {
|
||||
const candidate = value && typeof value === 'object' && !Array.isArray(value)
|
||||
? value as Partial<Record<DownloadTableColumnKey, unknown>>
|
||||
: {};
|
||||
|
||||
return DEFAULT_COLUMN_ORDER.reduce((alignments, key) => {
|
||||
alignments[key] = isColumnAlignment(candidate[key])
|
||||
? candidate[key]
|
||||
: DEFAULT_COLUMN_ALIGNMENTS[key];
|
||||
return alignments;
|
||||
}, {} as Record<DownloadTableColumnKey, DownloadColumnAlignment>);
|
||||
};
|
||||
|
||||
export const columnIndex = (key: DownloadTableColumnKey): number =>
|
||||
DEFAULT_COLUMN_ORDER.indexOf(key);
|
||||
Reference in New Issue
Block a user