mirror of
https://github.com/nimbold/Firelink.git
synced 2026-08-05 08:59:05 +00:00
159 lines
5.5 KiB
TypeScript
159 lines
5.5 KiB
TypeScript
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, 144] as const;
|
|
// Keep the viewport-anchored rail clear of the window edge and scrollbar.
|
|
export const DOWNLOAD_ACTIONS_VIEWPORT_INSET = 8;
|
|
// Match the 28px master-control group height.
|
|
export const DOWNLOAD_ACTIONS_MAX_HEIGHT = 28;
|
|
export const DOWNLOAD_ACTIONS_VIEWPORT_TOLERANCE = 1;
|
|
|
|
export interface DownloadActionViewportRect {
|
|
top: number;
|
|
right: number;
|
|
bottom: number;
|
|
left: number;
|
|
}
|
|
|
|
export interface DownloadActionPosition {
|
|
top: number;
|
|
right: number;
|
|
height: number;
|
|
overflow: 'hidden' | 'visible';
|
|
visibility: 'hidden' | 'visible';
|
|
}
|
|
|
|
export const getDownloadActionPosition = (
|
|
rowRect: DownloadActionViewportRect,
|
|
horizontalViewportRect: DownloadActionViewportRect,
|
|
verticalViewportRect: DownloadActionViewportRect,
|
|
windowWidth: number,
|
|
actionInset = DOWNLOAD_ACTIONS_VIEWPORT_INSET
|
|
): DownloadActionPosition => {
|
|
const visibleTop = Math.max(rowRect.top, verticalViewportRect.top);
|
|
const visibleBottom = Math.min(rowRect.bottom, verticalViewportRect.bottom);
|
|
const visibleHeight = Math.max(0, visibleBottom - visibleTop);
|
|
const visibleLeft = Math.max(rowRect.left, horizontalViewportRect.left);
|
|
const visibleRight = Math.min(rowRect.right, horizontalViewportRect.right);
|
|
const visibleWidth = Math.max(0, visibleRight - visibleLeft);
|
|
const isVisible = visibleHeight > DOWNLOAD_ACTIONS_VIEWPORT_TOLERANCE &&
|
|
visibleWidth > DOWNLOAD_ACTIONS_VIEWPORT_TOLERANCE;
|
|
const actionHeight = Math.min(DOWNLOAD_ACTIONS_MAX_HEIGHT, rowRect.bottom - rowRect.top, visibleHeight);
|
|
const actionRightEdge = Math.min(rowRect.right, horizontalViewportRect.right);
|
|
const safeActionInset = Number.isFinite(actionInset)
|
|
? Math.max(0, actionInset)
|
|
: DOWNLOAD_ACTIONS_VIEWPORT_INSET;
|
|
|
|
return {
|
|
top: visibleTop + Math.max(0, (visibleHeight - actionHeight) / 2),
|
|
right: Math.max(0, windowWidth - actionRightEdge) + safeActionInset,
|
|
height: actionHeight,
|
|
overflow: actionHeight < rowRect.bottom - rowRect.top ? 'hidden' : 'visible',
|
|
visibility: isVisible ? 'visible' : 'hidden',
|
|
};
|
|
};
|
|
|
|
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);
|
|
|
|
export const buildColumnGridTemplate = (
|
|
order: DownloadTableColumnKey[],
|
|
widths: number[]
|
|
): string => {
|
|
const normalizedWidths = normalizeColumnWidths(widths);
|
|
const orderedWidths = order.map(key => normalizedWidths[columnIndex(key)]);
|
|
return [
|
|
...orderedWidths.map(width => `${width}px`),
|
|
'minmax(0, 1fr)',
|
|
].join(' ');
|
|
};
|
|
|
|
export const getColumnGridColumn = (
|
|
key: DownloadTableColumnKey,
|
|
order: DownloadTableColumnKey[]
|
|
): string | undefined => key === order[order.length - 1]
|
|
? `${order.length}`
|
|
: undefined;
|