feat(app): add shared desktop web overlay scroll handles
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
const DEFAULT_MIN_HANDLE_SIZE = 36;
|
||||
|
||||
function clamp(value: number, min: number, max: number): number {
|
||||
return Math.min(max, Math.max(min, value));
|
||||
}
|
||||
|
||||
export type VerticalScrollbarGeometryInput = {
|
||||
viewportSize: number;
|
||||
contentSize: number;
|
||||
offset: number;
|
||||
minHandleSize?: number;
|
||||
};
|
||||
|
||||
export type VerticalScrollbarGeometry = {
|
||||
isVisible: boolean;
|
||||
maxScrollOffset: number;
|
||||
handleSize: number;
|
||||
handleOffset: number;
|
||||
maxHandleOffset: number;
|
||||
};
|
||||
|
||||
export function computeVerticalScrollbarGeometry(
|
||||
input: VerticalScrollbarGeometryInput
|
||||
): VerticalScrollbarGeometry {
|
||||
const viewportSize = Number.isFinite(input.viewportSize)
|
||||
? Math.max(0, input.viewportSize)
|
||||
: 0;
|
||||
const contentSize = Number.isFinite(input.contentSize)
|
||||
? Math.max(0, input.contentSize)
|
||||
: 0;
|
||||
const minHandleSize = Number.isFinite(input.minHandleSize)
|
||||
? Math.max(0, input.minHandleSize ?? DEFAULT_MIN_HANDLE_SIZE)
|
||||
: DEFAULT_MIN_HANDLE_SIZE;
|
||||
|
||||
const maxScrollOffset = Math.max(0, contentSize - viewportSize);
|
||||
if (maxScrollOffset <= 0 || viewportSize <= 0 || contentSize <= 0) {
|
||||
return {
|
||||
isVisible: false,
|
||||
maxScrollOffset: 0,
|
||||
handleSize: 0,
|
||||
handleOffset: 0,
|
||||
maxHandleOffset: 0,
|
||||
};
|
||||
}
|
||||
|
||||
const rawHandleSize = (viewportSize * viewportSize) / contentSize;
|
||||
const handleSize = clamp(rawHandleSize, minHandleSize, viewportSize);
|
||||
const maxHandleOffset = Math.max(0, viewportSize - handleSize);
|
||||
const clampedOffset = clamp(input.offset, 0, maxScrollOffset);
|
||||
const handleOffset =
|
||||
maxScrollOffset > 0
|
||||
? (clampedOffset / maxScrollOffset) * maxHandleOffset
|
||||
: 0;
|
||||
|
||||
return {
|
||||
isVisible: true,
|
||||
maxScrollOffset,
|
||||
handleSize,
|
||||
handleOffset,
|
||||
maxHandleOffset,
|
||||
};
|
||||
}
|
||||
|
||||
export type ScrollOffsetFromDragDeltaInput = {
|
||||
startOffset: number;
|
||||
dragDelta: number;
|
||||
maxScrollOffset: number;
|
||||
maxHandleOffset: number;
|
||||
};
|
||||
|
||||
export function computeScrollOffsetFromDragDelta(
|
||||
input: ScrollOffsetFromDragDeltaInput
|
||||
): number {
|
||||
if (input.maxScrollOffset <= 0 || input.maxHandleOffset <= 0) {
|
||||
return clamp(input.startOffset, 0, Math.max(0, input.maxScrollOffset));
|
||||
}
|
||||
|
||||
const scrollPerPixel = input.maxScrollOffset / input.maxHandleOffset;
|
||||
const nextOffset = input.startOffset + input.dragDelta * scrollPerPixel;
|
||||
return clamp(nextOffset, 0, input.maxScrollOffset);
|
||||
}
|
||||
Reference in New Issue
Block a user