Files
gpustack-ui/src/components/logs-viewer/logs-list.tsx
T
2025-04-28 10:05:06 +08:00

159 lines
3.9 KiB
TypeScript

import useOverlayScroller from '@/hooks/use-overlay-scroller';
import classNames from 'classnames';
import _, { throttle } from 'lodash';
import React, {
forwardRef,
useCallback,
useEffect,
useImperativeHandle,
useRef,
useState
} from 'react';
import './styles/logs-list.less';
interface LogsListProps {
dataList: any[];
height?: number;
onScroll?: (data: { isTop: boolean; isBottom: boolean }) => void;
diffHeight?: number;
showNum?: boolean;
ref?: any;
}
const LogsList: React.FC<LogsListProps> = forwardRef((props, ref) => {
const { dataList, height, showNum, onScroll, diffHeight = 96 } = props;
const {
initialize,
updateScrollerPosition,
updateScrollerPositionToTop,
generateInstance,
scrollEventElement,
instance,
initialized
} = useOverlayScroller({
options: {
scrollbars: {
theme: 'os-theme-light'
}
}
});
const [innerHieght, setInnerHeight] = useState(
window.innerHeight - diffHeight
);
const scroller = useRef<any>({});
const stopScroll = useRef(false);
const scrollToBottom = useCallback(() => {
updateScrollerPosition(0);
}, [updateScrollerPosition]);
const debounceResetStopScroll = _.debounce(() => {
stopScroll.current = false;
}, 30000);
const scrollToTop = useCallback(() => {
stopScroll.current = true;
updateScrollerPositionToTop();
debounceResetStopScroll();
}, [updateScrollerPositionToTop]);
useImperativeHandle(ref, () => ({
scrollToBottom,
scrollToTop,
scroller: scroller.current
}));
const handleOnWheel = useCallback(
(e: any) => {
const scrollTop = scrollEventElement?.scrollTop;
const scrollHeight = scrollEventElement?.scrollHeight;
const clientHeight = scrollEventElement?.clientHeight;
stopScroll.current = scrollTop + clientHeight <= scrollHeight;
const isBottom = scrollTop + clientHeight + 150 >= scrollHeight;
// is scroll to top
if (scrollTop === 0) {
onScroll?.({
isTop: true,
isBottom: false
});
} else if (isBottom) {
onScroll?.({
isTop: false,
isBottom: true
});
stopScroll.current = false;
} else {
onScroll?.({
isTop: false,
isBottom: false
});
}
},
[debounceResetStopScroll, scrollEventElement]
);
const debounceUpdateScrollerPosition = _.debounce(() => {
generateInstance();
updateScrollerPosition(0);
}, 200);
useEffect(() => {
const handleResize = throttle(() => {
const viewportHeight = window.innerHeight;
const viewHeight = viewportHeight - diffHeight;
setInnerHeight(viewHeight);
}, 100);
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, [diffHeight]);
useEffect(() => {
if (scroller.current) {
initialize(scroller.current);
}
}, [initialize]);
useEffect(() => {
if (dataList.length && !stopScroll.current && instance) {
updateScrollerPosition(0);
} else if (dataList.length && !stopScroll.current && scroller.current) {
if (!initialized) {
initialize(scroller.current);
}
if (!instance) {
debounceUpdateScrollerPosition();
} else {
updateScrollerPosition(0);
}
}
}, [dataList, stopScroll.current, instance, scroller.current]);
return (
<div
className="logs-wrap"
style={{ height: innerHieght }}
ref={scroller}
onWheel={handleOnWheel}
>
<div className={classNames('content')}>
{_.map(dataList, (item: any, index: number) => {
return (
<div
key={item.uid}
className={classNames('text')}
data-uid={item.uid}
>
{item.content}
</div>
);
})}
</div>
</div>
);
});
export default React.memo(LogsList);