mirror of
https://github.com/binaricat/Netcatty.git
synced 2026-09-24 15:49:09 +00:00
aaff0edc7f
* perf(vault): virtualize host pickers for large inventories Virtualize Select Host, Quick Switcher, SFTP host picker, and workspace dialogs so thousands of hosts only mount a viewport window. Shared scroll and keyboard clamp helpers keep selection and filter-while-scrolled paths stable under virtualization. * fix: address Codex review on PR #2553 * fix(vault): plugin row heights and Select Host keyboard types Give Quick Switcher plugin command/view rows a taller virtual slot so two-line chrome does not overflow into the next absolute row. Narrow Select Host navigable rows to group|host so Enter keyboard activation typechecks cleanly. * fix(vault): finish host-picker review feedback Clip and truncate fixed-height Quick Switcher / workspace rows so long labels never overlap the next virtual slot. Wire Select Host listbox aria-activedescendant to stable option ids for keyboard AT feedback. * fix: address Codex review on PR #2553 * fix: address Codex review on PR #2553 * fix: address Codex review on PR #2553 * fix: address Codex review on PR #2553 * fix(vault): clear remaining host-picker codex findings Declare multi-select listboxes, drop empty SFTP Hosts headers when only connected rows remain, size the SFTP list to content under a 360px cap, and give Quick Switcher / workspace pickers a definite max scroll height so large inventories remain reachable. --------- Co-authored-by: netcatty-bot <308658023+netcatty-bot@users.noreply.github.com>
69 lines
2.3 KiB
TypeScript
69 lines
2.3 KiB
TypeScript
/**
|
|
* Pure math for virtual lists and keyboard cursors.
|
|
* Kept free of React so tests drive the same functions the pickers use.
|
|
*/
|
|
|
|
export function clampScrollTop(
|
|
scrollTop: number,
|
|
totalHeight: number,
|
|
viewportHeight: number,
|
|
): number {
|
|
const maxScroll = Math.max(0, totalHeight - Math.max(viewportHeight, 0));
|
|
return Math.min(Math.max(0, scrollTop), maxScroll);
|
|
}
|
|
|
|
export function getFixedSizeVirtualWindow({
|
|
itemCount,
|
|
itemHeight,
|
|
scrollTop,
|
|
viewportHeight,
|
|
overscan,
|
|
}: {
|
|
itemCount: number;
|
|
itemHeight: number;
|
|
scrollTop: number;
|
|
viewportHeight: number;
|
|
overscan: number;
|
|
}): { startIndex: number; endIndex: number; effectiveScrollTop: number; totalHeight: number } {
|
|
const totalHeight = Math.max(0, itemCount * itemHeight);
|
|
const effectiveScrollTop = clampScrollTop(scrollTop, totalHeight, viewportHeight);
|
|
if (itemCount <= 0 || itemHeight <= 0) {
|
|
return { startIndex: 0, endIndex: 0, effectiveScrollTop, totalHeight };
|
|
}
|
|
const startIndex = Math.max(0, Math.floor(effectiveScrollTop / itemHeight) - overscan);
|
|
const visibleCount = Math.ceil(Math.max(viewportHeight, 1) / itemHeight) + overscan * 2;
|
|
const endIndex = Math.min(itemCount, startIndex + visibleCount);
|
|
return { startIndex, endIndex, effectiveScrollTop, totalHeight };
|
|
}
|
|
|
|
/** Clamp a keyboard cursor into [0, length-1], or 0 when the list is empty. */
|
|
export function clampListIndex(index: number, length: number): number {
|
|
if (length <= 0) return 0;
|
|
if (!Number.isFinite(index)) return 0;
|
|
return Math.max(0, Math.min(Math.trunc(index), length - 1));
|
|
}
|
|
|
|
/** Move a keyboard cursor by delta without ever landing on -1. */
|
|
export function stepListIndex(index: number, length: number, delta: number): number {
|
|
if (length <= 0) return 0;
|
|
return clampListIndex(index + delta, length);
|
|
}
|
|
|
|
/**
|
|
* Map selectable item indices to visual row indices for lists that interleave
|
|
* non-selectable chrome (headers, hints, empty rows).
|
|
*/
|
|
export function buildItemIndexToVisualIndexMap(
|
|
visualRows: ReadonlyArray<{ kind: string }>,
|
|
itemKind = 'item',
|
|
): Map<number, number> {
|
|
const map = new Map<number, number>();
|
|
let itemIndex = 0;
|
|
visualRows.forEach((row, visualIndex) => {
|
|
if (row.kind !== itemKind) return;
|
|
map.set(itemIndex, visualIndex);
|
|
itemIndex += 1;
|
|
});
|
|
return map;
|
|
}
|