feat: Enhance scrolling behavior and styles across components; add centralized scroll utilities
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
@@ -172,6 +172,32 @@ export type ScrollInfo = {
|
||||
viewHeight: number;
|
||||
scrollable: boolean;
|
||||
};
|
||||
|
||||
export type ScrollBehavior = 'auto' | 'instant' | 'smooth';
|
||||
|
||||
export type ScrollPosition = {
|
||||
top?: number;
|
||||
left?: number;
|
||||
behavior?: ScrollBehavior;
|
||||
};
|
||||
|
||||
/**
|
||||
* Centralized wrapper for element scrolling.
|
||||
*/
|
||||
export const scrollToPosition = (scrollEl: HTMLElement, position: ScrollPosition): void => {
|
||||
scrollEl.scrollTo(position);
|
||||
};
|
||||
|
||||
/**
|
||||
* Centralized wrapper for scrolling a target into the current viewport.
|
||||
*/
|
||||
export const scrollElementIntoView = (
|
||||
element: HTMLElement,
|
||||
options?: ScrollIntoViewOptions
|
||||
): void => {
|
||||
element.scrollIntoView(options);
|
||||
};
|
||||
|
||||
export const getScrollInfo = (target: HTMLElement): ScrollInfo => ({
|
||||
offsetTop: Math.round(target.offsetTop),
|
||||
top: Math.round(target.scrollTop),
|
||||
@@ -180,17 +206,44 @@ export const getScrollInfo = (target: HTMLElement): ScrollInfo => ({
|
||||
scrollable: target.scrollHeight > target.offsetHeight,
|
||||
});
|
||||
|
||||
export const scrollToBottom = (scrollEl: HTMLElement, behavior?: 'auto' | 'instant' | 'smooth') => {
|
||||
scrollEl.scrollTo({
|
||||
export const scrollToBottom = (scrollEl: HTMLElement, behavior?: ScrollBehavior) => {
|
||||
scrollToPosition(scrollEl, {
|
||||
top: Math.round(scrollEl.scrollHeight - scrollEl.offsetHeight),
|
||||
behavior,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if scroll element is at or near the bottom (within threshold).
|
||||
* Useful for "sticky to bottom" behavior in chat-like interfaces.
|
||||
* @param scrollEl - The scrollable element
|
||||
* @param threshold - Distance from bottom in pixels (default: 50px)
|
||||
* @returns true if scrolled to bottom within threshold
|
||||
*/
|
||||
export const isScrolledToBottom = (scrollEl: HTMLElement, threshold = 50): boolean => {
|
||||
const { scrollTop, offsetHeight, scrollHeight } = scrollEl;
|
||||
const scrollPosition = scrollTop + offsetHeight;
|
||||
return scrollHeight - scrollPosition <= threshold;
|
||||
};
|
||||
|
||||
/**
|
||||
* Shared auto-scroll behavior for chat-like timelines that should stay pinned to bottom.
|
||||
* Only scrolls if already near the bottom unless a call site explicitly forces it.
|
||||
*/
|
||||
export const autoScrollToBottom = (
|
||||
scrollEl: HTMLElement,
|
||||
smooth = false,
|
||||
force = false
|
||||
): void => {
|
||||
if (force || isScrolledToBottom(scrollEl)) {
|
||||
scrollToBottom(scrollEl, smooth ? 'smooth' : 'instant');
|
||||
}
|
||||
};
|
||||
|
||||
export const copyToClipboard = (text: string) => {
|
||||
// Try Electron clipboard first (if available)
|
||||
if ('electron' in window && (window as any).electron?.clipboard?.writeText) {
|
||||
(window as any).electron.clipboard.writeText(text);
|
||||
if ('electron' in window && window.electron?.clipboard?.writeText) {
|
||||
window.electron.clipboard.writeText(text);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user