chore(hooks): remove unused hooks
This commit is contained in:
@@ -1,10 +0,0 @@
|
|||||||
export default function useActions<T>(actions: Global.ActionItem<T>[], ctx: T) {
|
|
||||||
return actions
|
|
||||||
.filter((action) => {
|
|
||||||
return action.visible ? action.visible(ctx) : true;
|
|
||||||
})
|
|
||||||
.map((action) => ({
|
|
||||||
...action,
|
|
||||||
disabled: action.disabled?.(ctx)
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
import { useIntl } from '@umijs/max';
|
|
||||||
import { message } from 'antd';
|
|
||||||
|
|
||||||
type MessageType = 'input' | 'select';
|
|
||||||
|
|
||||||
const useAppUtils = () => {
|
|
||||||
const intl = useIntl();
|
|
||||||
const [messageApi, contextHolder] = message.useMessage();
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param type Array<'input' | 'select'>
|
|
||||||
* @param name
|
|
||||||
* @param locale boolean
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const getRuleMessage = (
|
|
||||||
type: MessageType | MessageType[],
|
|
||||||
name: string,
|
|
||||||
locale = true
|
|
||||||
) => {
|
|
||||||
const nameStr = locale ? intl.formatMessage({ id: name }) : name;
|
|
||||||
// transform type to array
|
|
||||||
const typeList = Array.isArray(type) ? type : [type];
|
|
||||||
|
|
||||||
if (typeList.includes('select') && typeList.includes('input')) {
|
|
||||||
return intl.formatMessage(
|
|
||||||
{ id: 'common.form.rule.selectInput' },
|
|
||||||
{ name: nameStr }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeList.includes('input')) {
|
|
||||||
return intl.formatMessage(
|
|
||||||
{ id: 'common.form.rule.input' },
|
|
||||||
{ name: nameStr }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return intl.formatMessage(
|
|
||||||
{ id: 'common.form.rule.select' },
|
|
||||||
{ name: nameStr }
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const showSuccess = (msg?: string) => {
|
|
||||||
messageApi.success(
|
|
||||||
msg || intl.formatMessage({ id: 'common.message.success' })
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
getRuleMessage,
|
|
||||||
showSuccess
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default useAppUtils;
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
// broadcast channel hook
|
|
||||||
|
|
||||||
import { useEffect, useRef } from 'react';
|
|
||||||
|
|
||||||
export const useBroadcast = () => {
|
|
||||||
const broadcastChannel = useRef<BroadcastChannel | null>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
broadcastChannel.current = new BroadcastChannel('broadcast_channel');
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
broadcastChannel.current?.close();
|
|
||||||
console.log('broadcast channel closed');
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return { broadcastChannel };
|
|
||||||
};
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
import _ from 'lodash';
|
|
||||||
import { useRef } from 'react';
|
|
||||||
|
|
||||||
export default function useContainerScroll(
|
|
||||||
container: any,
|
|
||||||
options?: { toBottom?: boolean }
|
|
||||||
) {
|
|
||||||
const isWheeled = useRef(false);
|
|
||||||
const scroller = useRef(container);
|
|
||||||
const optionsRef = useRef(options);
|
|
||||||
const toBottomFlag = useRef(options?.toBottom);
|
|
||||||
const timerRef = useRef<any>(null);
|
|
||||||
|
|
||||||
const debunceResetWheeled = _.debounce(() => {
|
|
||||||
isWheeled.current = false;
|
|
||||||
}, 5000);
|
|
||||||
|
|
||||||
const handleContentWheel = (e: any) => {
|
|
||||||
isWheeled.current = true;
|
|
||||||
debunceResetWheeled.cancel?.();
|
|
||||||
debunceResetWheeled();
|
|
||||||
};
|
|
||||||
|
|
||||||
const scrollerRun = () => {
|
|
||||||
const scrollerContainer = scroller.current?.current || {};
|
|
||||||
const { scrollHeight, clientHeight, scrollTop } = scrollerContainer;
|
|
||||||
if (
|
|
||||||
optionsRef.current?.toBottom &&
|
|
||||||
toBottomFlag.current &&
|
|
||||||
scrollHeight > clientHeight + scrollTop
|
|
||||||
) {
|
|
||||||
scroller.current.current.scrollTop = scrollHeight;
|
|
||||||
// toBottomFlag.current = false;
|
|
||||||
isWheeled.current = false;
|
|
||||||
} else if (
|
|
||||||
!isWheeled.current &&
|
|
||||||
scrollHeight > clientHeight + scrollTop &&
|
|
||||||
scroller.current?.current
|
|
||||||
) {
|
|
||||||
scroller.current.current.scrollTop += 10;
|
|
||||||
window.requestAnimationFrame(scrollerRun);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateScrollerPosition = () => {
|
|
||||||
if (!isWheeled.current) {
|
|
||||||
window.requestAnimationFrame(scrollerRun);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
handleContentWheel,
|
|
||||||
updateScrollerPosition,
|
|
||||||
scroller
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import { useCallback, useState } from 'react';
|
|
||||||
|
|
||||||
const useCopyToClipboard = () => {
|
|
||||||
const [copied, setCopied] = useState(false);
|
|
||||||
|
|
||||||
const copyToClipboard = useCallback(async (text: string) => {
|
|
||||||
try {
|
|
||||||
if (navigator.clipboard) {
|
|
||||||
await navigator.clipboard.writeText(text);
|
|
||||||
setCopied(true);
|
|
||||||
setTimeout(() => {
|
|
||||||
setCopied(false);
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
setCopied(false);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return { copied, copyToClipboard };
|
|
||||||
};
|
|
||||||
|
|
||||||
export default useCopyToClipboard;
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
import { HandlerOptions } from '@/hooks/use-chunk-fetch';
|
|
||||||
import { useDownloadStream } from '@gpustack/core-ui';
|
|
||||||
import { useIntl } from '@umijs/max';
|
|
||||||
import { Progress, notification } from 'antd';
|
|
||||||
import dayjs from 'dayjs';
|
|
||||||
|
|
||||||
const renderMessage = (title: string) => {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
width: 280,
|
|
||||||
textOverflow: 'ellipsis',
|
|
||||||
whiteSpace: 'nowrap',
|
|
||||||
overflow: 'hidden'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{title}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const createFileName = (name: string) => {
|
|
||||||
const timestamp = dayjs().format('YYYY-MM-DD_HH-mm-ss');
|
|
||||||
const fileName = `${name}_${timestamp}.txt`;
|
|
||||||
return fileName;
|
|
||||||
};
|
|
||||||
|
|
||||||
const useDownloadLogs = () => {
|
|
||||||
const { downloadStream } = useDownloadStream();
|
|
||||||
const intl = useIntl();
|
|
||||||
const [api, contextHolder] = notification.useNotification({
|
|
||||||
stack: { threshold: 1 }
|
|
||||||
});
|
|
||||||
|
|
||||||
const downloadNotification = (
|
|
||||||
data: HandlerOptions & {
|
|
||||||
filename: string;
|
|
||||||
duration?: number;
|
|
||||||
chunkRequestRef: any;
|
|
||||||
}
|
|
||||||
) => {
|
|
||||||
api.open({
|
|
||||||
duration: data.duration,
|
|
||||||
message: renderMessage(data.filename),
|
|
||||||
key: data.filename,
|
|
||||||
closeIcon: (
|
|
||||||
<span>{intl.formatMessage({ id: 'common.button.cancel' })}</span>
|
|
||||||
),
|
|
||||||
description: <Progress percent={data.percent} size="small"></Progress>,
|
|
||||||
onClose() {
|
|
||||||
data.chunkRequestRef?.current?.abort();
|
|
||||||
notification.destroy?.(data.filename);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDownloadLog = async (params: { url: string; name: string }) => {
|
|
||||||
downloadStream({
|
|
||||||
url: params.url,
|
|
||||||
filename: createFileName(params.name),
|
|
||||||
downloadNotification
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
onDownloadLog: handleDownloadLog,
|
|
||||||
contextHolder
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default useDownloadLogs;
|
|
||||||
@@ -1,136 +0,0 @@
|
|||||||
import useSetChunkFetch, { HandlerOptions } from '@/hooks/use-chunk-fetch';
|
|
||||||
import { message } from 'antd';
|
|
||||||
import { useEffect, useRef } from 'react';
|
|
||||||
|
|
||||||
export default function useDownloadStream() {
|
|
||||||
const chunkRequestRef = useRef<any>(null);
|
|
||||||
const logParseWorker = useRef<any>(null);
|
|
||||||
const clearScreen = useRef(false);
|
|
||||||
const filename = useRef('log');
|
|
||||||
const downloadNotificationRef = useRef<any>(null);
|
|
||||||
|
|
||||||
const { setChunkFetch } = useSetChunkFetch();
|
|
||||||
|
|
||||||
const downloadFile = (content: string) => {
|
|
||||||
const blob = new Blob([content], { type: 'text/plain' });
|
|
||||||
const url = URL.createObjectURL(blob);
|
|
||||||
|
|
||||||
const a = document.createElement('a');
|
|
||||||
a.href = url;
|
|
||||||
a.download = filename.current;
|
|
||||||
document.body.appendChild(a);
|
|
||||||
a.click();
|
|
||||||
document.body.removeChild(a);
|
|
||||||
|
|
||||||
URL.revokeObjectURL(url);
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateContent = (data: string, options?: HandlerOptions) => {
|
|
||||||
const { isComplete, percent } = options || {};
|
|
||||||
|
|
||||||
logParseWorker.current?.postMessage({
|
|
||||||
inputStr: data,
|
|
||||||
reset: clearScreen.current,
|
|
||||||
isComplete: isComplete,
|
|
||||||
percent: percent,
|
|
||||||
chunked: false
|
|
||||||
});
|
|
||||||
clearScreen.current = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleError = (error: any) => {
|
|
||||||
const errorMsg = error?.message || error;
|
|
||||||
const msg =
|
|
||||||
typeof errorMsg === 'string' ? errorMsg : JSON.stringify(errorMsg);
|
|
||||||
message.error(msg);
|
|
||||||
downloadNotificationRef.current?.({
|
|
||||||
duration: 1,
|
|
||||||
percent: 0,
|
|
||||||
filename: filename.current
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const downloadStream = async (props: {
|
|
||||||
data?: any;
|
|
||||||
url: string;
|
|
||||||
params?: any;
|
|
||||||
signal?: AbortSignal;
|
|
||||||
method?: string;
|
|
||||||
headers?: any;
|
|
||||||
filename?: string;
|
|
||||||
downloadNotification?: (data: any) => void;
|
|
||||||
}) => {
|
|
||||||
try {
|
|
||||||
clearScreen.current = true;
|
|
||||||
filename.current = props.filename || 'log';
|
|
||||||
downloadNotificationRef.current = props.downloadNotification;
|
|
||||||
const { params, url } = props;
|
|
||||||
|
|
||||||
chunkRequestRef.current?.current?.abort?.();
|
|
||||||
|
|
||||||
chunkRequestRef.current = setChunkFetch({
|
|
||||||
url,
|
|
||||||
params,
|
|
||||||
watch: false,
|
|
||||||
contentType: 'text',
|
|
||||||
errorHandler: handleError,
|
|
||||||
handler: updateContent
|
|
||||||
});
|
|
||||||
downloadNotificationRef.current?.({
|
|
||||||
filename: filename.current,
|
|
||||||
duration: null,
|
|
||||||
chunkRequestRef: chunkRequestRef.current
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
//
|
|
||||||
downloadNotificationRef.current?.({
|
|
||||||
duration: 1,
|
|
||||||
percent: 0,
|
|
||||||
filename: filename.current
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
logParseWorker.current?.terminate?.();
|
|
||||||
|
|
||||||
logParseWorker.current = new Worker(
|
|
||||||
// @ts-ignore
|
|
||||||
new URL('@/components/logs-viewer/parse-worker.ts', import.meta.url),
|
|
||||||
{
|
|
||||||
type: 'module'
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
logParseWorker.current.onmessage = (event: any) => {
|
|
||||||
const { result, isComplete, percent } = event.data;
|
|
||||||
|
|
||||||
const isAborted = chunkRequestRef.current?.current?.signal?.aborted;
|
|
||||||
if (!isComplete && !isAborted) {
|
|
||||||
downloadNotificationRef.current?.({
|
|
||||||
percent: percent,
|
|
||||||
duration: null,
|
|
||||||
filename: filename.current,
|
|
||||||
chunkRequestRef: chunkRequestRef.current
|
|
||||||
});
|
|
||||||
} else if (isComplete && !isAborted) {
|
|
||||||
downloadNotificationRef.current?.({
|
|
||||||
duration: 1,
|
|
||||||
percent: 100,
|
|
||||||
filename: filename.current
|
|
||||||
});
|
|
||||||
downloadFile(result);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
if (logParseWorker.current) {
|
|
||||||
logParseWorker.current.terminate();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return {
|
|
||||||
downloadStream
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
import HotKeys from '@/config/hotkeys';
|
|
||||||
import { useIntl } from '@umijs/max';
|
|
||||||
import { createStyles } from 'antd-style';
|
|
||||||
import { throttle } from 'lodash';
|
|
||||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
|
||||||
|
|
||||||
const useStyles = createStyles(({ css, token }) => ({
|
|
||||||
hintOverlay: css`
|
|
||||||
position: fixed;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
background: var(--color-esc-hint-bg);
|
|
||||||
color: ${token.colorTextLightSolid};
|
|
||||||
padding: 16px 24px;
|
|
||||||
border-radius: 4px;
|
|
||||||
z-index: 2000;
|
|
||||||
font-size: 14px;
|
|
||||||
pointer-events: none;
|
|
||||||
animation: fadeInOut 2s ease-in-out;
|
|
||||||
@keyframes fadeInOut {
|
|
||||||
0% {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
10% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
90% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
}));
|
|
||||||
|
|
||||||
export function useEscHint(options?: {
|
|
||||||
enabled?: boolean;
|
|
||||||
message?: string;
|
|
||||||
throttleDelay?: number;
|
|
||||||
}) {
|
|
||||||
const { enabled = true, message, throttleDelay = 3000 } = options || {};
|
|
||||||
const intl = useIntl();
|
|
||||||
const { styles } = useStyles();
|
|
||||||
const [visible, setVisible] = useState(false);
|
|
||||||
const timeoutRef = useRef<any>(null);
|
|
||||||
const isHintActiveRef = useRef(false);
|
|
||||||
|
|
||||||
const showHintThrottled = useMemo(
|
|
||||||
() =>
|
|
||||||
throttle(
|
|
||||||
() => {
|
|
||||||
if (isHintActiveRef.current) return;
|
|
||||||
|
|
||||||
isHintActiveRef.current = true;
|
|
||||||
|
|
||||||
setVisible(true);
|
|
||||||
|
|
||||||
if (timeoutRef.current) {
|
|
||||||
clearTimeout(timeoutRef.current);
|
|
||||||
}
|
|
||||||
|
|
||||||
timeoutRef.current = setTimeout(() => {
|
|
||||||
setVisible(false);
|
|
||||||
isHintActiveRef.current = false;
|
|
||||||
}, 2000);
|
|
||||||
},
|
|
||||||
throttleDelay,
|
|
||||||
{
|
|
||||||
leading: true,
|
|
||||||
trailing: false
|
|
||||||
}
|
|
||||||
),
|
|
||||||
[throttleDelay]
|
|
||||||
);
|
|
||||||
|
|
||||||
useHotkeys(
|
|
||||||
HotKeys.ESC,
|
|
||||||
() => {
|
|
||||||
if (!enabled) return;
|
|
||||||
showHintThrottled();
|
|
||||||
},
|
|
||||||
{
|
|
||||||
enabled: enabled
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
return () => {
|
|
||||||
if (timeoutRef.current) {
|
|
||||||
clearTimeout(timeoutRef.current);
|
|
||||||
}
|
|
||||||
showHintThrottled.cancel();
|
|
||||||
};
|
|
||||||
}, [showHintThrottled]);
|
|
||||||
|
|
||||||
const EscHint = visible ? (
|
|
||||||
<div className={styles.hintOverlay}>
|
|
||||||
{message || intl.formatMessage({ id: 'common.tips.escape.disable' })}
|
|
||||||
</div>
|
|
||||||
) : null;
|
|
||||||
|
|
||||||
return { EscHint };
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
import qs from 'query-string';
|
|
||||||
import { useEffect, useRef } from 'react';
|
|
||||||
|
|
||||||
export const createEventSourceURL = (url: string) => {
|
|
||||||
const { host, protocol } = window.location;
|
|
||||||
return `${protocol}://${host}${url}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
0: connecting
|
|
||||||
1: connect successfully
|
|
||||||
2: closed
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default function useEventSource() {
|
|
||||||
const eventSourceRef = useRef<any>(null);
|
|
||||||
|
|
||||||
const createEventSourceConnection = (query: {
|
|
||||||
url: string;
|
|
||||||
params: any;
|
|
||||||
onmessage?: (data: any) => void;
|
|
||||||
}) => {
|
|
||||||
eventSourceRef.current?.close?.();
|
|
||||||
|
|
||||||
const { url, params, onmessage = () => {} } = query;
|
|
||||||
|
|
||||||
const sseurl = createEventSourceURL(url);
|
|
||||||
|
|
||||||
eventSourceRef.current = new EventSource(
|
|
||||||
`${url}?${qs.stringify({
|
|
||||||
...params
|
|
||||||
})}`,
|
|
||||||
{
|
|
||||||
withCredentials: true
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
eventSourceRef.current.onmessage = (res: any) => {
|
|
||||||
try {
|
|
||||||
console.log('event source message: ', { res, resData: res });
|
|
||||||
const data = JSON.parse(res.data);
|
|
||||||
onmessage(data);
|
|
||||||
} catch (error) {
|
|
||||||
// error
|
|
||||||
console.log('event source error: ', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
eventSourceRef.current.onclose = () => {
|
|
||||||
console.log('event source closed...');
|
|
||||||
};
|
|
||||||
|
|
||||||
eventSourceRef.current.onopen = () => {
|
|
||||||
console.log('event source connected...');
|
|
||||||
};
|
|
||||||
|
|
||||||
eventSourceRef.current.onerror = (error: any) => {
|
|
||||||
console.log('event source error: ', error);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
return () => {
|
|
||||||
eventSourceRef.current?.close?.();
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return {
|
|
||||||
eventSourceRef: eventSourceRef,
|
|
||||||
createEventSourceConnection
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,280 +0,0 @@
|
|||||||
import { useMemoizedFn } from 'ahooks';
|
|
||||||
import { throttle } from 'lodash';
|
|
||||||
import {
|
|
||||||
UseOverlayScrollbarsParams,
|
|
||||||
useOverlayScrollbars
|
|
||||||
} from 'overlayscrollbars-react';
|
|
||||||
import React, { useEffect } from 'react';
|
|
||||||
import useUserSettings from './use-user-settings';
|
|
||||||
|
|
||||||
type OverflowBehavior =
|
|
||||||
| 'hidden'
|
|
||||||
| 'scroll'
|
|
||||||
| 'visible'
|
|
||||||
| 'visible-hidden'
|
|
||||||
| 'visible-scroll';
|
|
||||||
export interface OverlayScrollerOptions {
|
|
||||||
oppositeTheme?: boolean;
|
|
||||||
overflow?: {
|
|
||||||
x?: OverflowBehavior;
|
|
||||||
y?: OverflowBehavior;
|
|
||||||
};
|
|
||||||
scrollbars?: {
|
|
||||||
theme?: 'os-theme-light' | 'os-theme-dark';
|
|
||||||
autoHide?: 'never' | 'scroll' | 'leave' | 'move';
|
|
||||||
autoHideDelay?: number;
|
|
||||||
clickScroll?: boolean | 'instant';
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const overlaySollerOptions: UseOverlayScrollbarsParams = {
|
|
||||||
options: {
|
|
||||||
update: {
|
|
||||||
debounce: 0
|
|
||||||
},
|
|
||||||
overflow: {
|
|
||||||
x: 'hidden'
|
|
||||||
},
|
|
||||||
scrollbars: {
|
|
||||||
theme: 'os-theme-light',
|
|
||||||
autoHide: 'scroll',
|
|
||||||
autoHideDelay: 600,
|
|
||||||
clickScroll: 'instant'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
defer: true
|
|
||||||
};
|
|
||||||
|
|
||||||
const RESETSCROLLDELAY = 5000;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param options.theme: if set theme, it will fix the theme
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export default function useOverlayScroller(data?: {
|
|
||||||
options?: OverlayScrollerOptions;
|
|
||||||
events?: any;
|
|
||||||
defer?: boolean;
|
|
||||||
}) {
|
|
||||||
const { userSettings } = useUserSettings();
|
|
||||||
const { options, events, defer = true } = data || {};
|
|
||||||
const { scrollbars, overflow, oppositeTheme } = options || {};
|
|
||||||
const scrollEventElement = React.useRef<any>(null);
|
|
||||||
const instanceRef = React.useRef<any>(null);
|
|
||||||
const initialized = React.useRef(false);
|
|
||||||
const scrollElementRef = React.useRef<any>(null);
|
|
||||||
const stopUpdatePosition = React.useRef(false);
|
|
||||||
const timerRef = React.useRef<any>(null);
|
|
||||||
const [initialize, instance] = useOverlayScrollbars({
|
|
||||||
options: {
|
|
||||||
update: {
|
|
||||||
debounce: 0
|
|
||||||
},
|
|
||||||
overflow: {
|
|
||||||
x: 'hidden',
|
|
||||||
...overflow
|
|
||||||
},
|
|
||||||
scrollbars: {
|
|
||||||
autoHide: 'scroll',
|
|
||||||
autoHideDelay: 600,
|
|
||||||
clickScroll: 'instant',
|
|
||||||
...scrollbars,
|
|
||||||
theme:
|
|
||||||
scrollbars?.theme ||
|
|
||||||
(userSettings.theme === 'light' || !userSettings.theme
|
|
||||||
? 'os-theme-dark'
|
|
||||||
: 'os-theme-light')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
events: {
|
|
||||||
...events
|
|
||||||
},
|
|
||||||
defer: defer
|
|
||||||
});
|
|
||||||
|
|
||||||
instanceRef.current = instance?.();
|
|
||||||
scrollEventElement.current =
|
|
||||||
instanceRef.current?.elements()?.scrollEventElement;
|
|
||||||
|
|
||||||
const handleOnScroll = () => {
|
|
||||||
const scrollTop = scrollEventElement.current?.scrollTop;
|
|
||||||
const scrollHeight = scrollEventElement.current?.scrollHeight;
|
|
||||||
const clientHeight = scrollEventElement.current?.clientHeight;
|
|
||||||
|
|
||||||
const isBottom = scrollTop + clientHeight + 20 >= scrollHeight;
|
|
||||||
|
|
||||||
if (isBottom) {
|
|
||||||
stopUpdatePosition.current = false;
|
|
||||||
} else {
|
|
||||||
stopUpdatePosition.current = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const throttledScroll = useMemoizedFn(
|
|
||||||
throttle(() => {
|
|
||||||
scrollEventElement.current?.scrollTo?.({
|
|
||||||
top: scrollEventElement.current?.scrollHeight,
|
|
||||||
behavior: 'smooth'
|
|
||||||
});
|
|
||||||
instanceRef.current?.update?.();
|
|
||||||
}, 100)
|
|
||||||
);
|
|
||||||
|
|
||||||
const scrollauto = useMemoizedFn(() => {
|
|
||||||
scrollEventElement.current?.scrollTo?.({
|
|
||||||
top: scrollEventElement.current.scrollHeight,
|
|
||||||
behavior: 'auto'
|
|
||||||
});
|
|
||||||
instanceRef.current?.update?.();
|
|
||||||
});
|
|
||||||
|
|
||||||
// scroll to bottom
|
|
||||||
const throttledUpdateScrollerPosition = useMemoizedFn((delay?: number) => {
|
|
||||||
if (stopUpdatePosition.current) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (delay === 0) {
|
|
||||||
scrollauto();
|
|
||||||
} else {
|
|
||||||
throttledScroll();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// scroll to top
|
|
||||||
const updateScrollerPositionToTop = useMemoizedFn(() => {
|
|
||||||
scrollEventElement.current?.scrollTo?.({
|
|
||||||
top: 0,
|
|
||||||
behavior: 'auto'
|
|
||||||
});
|
|
||||||
instanceRef.current?.update?.();
|
|
||||||
});
|
|
||||||
|
|
||||||
const generateInstance = () => {
|
|
||||||
instanceRef.current = instance?.();
|
|
||||||
scrollEventElement.current =
|
|
||||||
instanceRef.current?.elements()?.scrollEventElement;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleWheelCallback = useMemoizedFn((e: any) => {
|
|
||||||
handleOnScroll();
|
|
||||||
if (timerRef.current) {
|
|
||||||
clearTimeout(timerRef.current);
|
|
||||||
}
|
|
||||||
timerRef.current = setTimeout(() => {
|
|
||||||
stopUpdatePosition.current = false;
|
|
||||||
}, RESETSCROLLDELAY);
|
|
||||||
});
|
|
||||||
|
|
||||||
// add wheel event
|
|
||||||
const handleWheelEvent = () => {
|
|
||||||
scrollElementRef.current?.addEventListener?.('wheel', handleWheelCallback, {
|
|
||||||
passive: true
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// remove wheel event
|
|
||||||
const removeWheelEvent = () => {
|
|
||||||
scrollElementRef.current?.removeEventListener?.(
|
|
||||||
'wheel',
|
|
||||||
handleWheelCallback,
|
|
||||||
{ passive: true }
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const createInstance = useMemoizedFn((el: any) => {
|
|
||||||
if (instanceRef.current) {
|
|
||||||
return instanceRef.current;
|
|
||||||
}
|
|
||||||
if (el) {
|
|
||||||
initialize(el);
|
|
||||||
scrollElementRef.current = el;
|
|
||||||
initialized.current = true;
|
|
||||||
instanceRef.current = instance?.();
|
|
||||||
scrollEventElement.current =
|
|
||||||
instanceRef.current?.elements()?.scrollEventElement;
|
|
||||||
handleWheelEvent();
|
|
||||||
}
|
|
||||||
return instanceRef.current;
|
|
||||||
});
|
|
||||||
|
|
||||||
const destroyInstance = () => {
|
|
||||||
instanceRef.current?.destroy?.();
|
|
||||||
removeWheelEvent();
|
|
||||||
instanceRef.current = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const scrollToTarget = (target: any, offset = 100) => {
|
|
||||||
if (!target) return;
|
|
||||||
if (!instanceRef.current || !scrollEventElement.current) {
|
|
||||||
instanceRef.current = instance?.();
|
|
||||||
scrollEventElement.current =
|
|
||||||
instanceRef.current?.elements()?.scrollEventElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
const viewport = instanceRef.current?.elements().viewport;
|
|
||||||
const containerRect = viewport.getBoundingClientRect();
|
|
||||||
const targetRect = target.getBoundingClientRect();
|
|
||||||
|
|
||||||
const scrollerState = instanceRef.current?.state();
|
|
||||||
|
|
||||||
const currentScroll = scrollerState.current?.overflowAmount?.y;
|
|
||||||
|
|
||||||
// const currentScroll = instanceRef.current?.scroll().position.y;
|
|
||||||
const targetPos = targetRect.top - containerRect.top + currentScroll;
|
|
||||||
|
|
||||||
scrollEventElement.current.scroll({
|
|
||||||
y: targetPos - offset,
|
|
||||||
behavior: 'smooth'
|
|
||||||
});
|
|
||||||
instanceRef.current?.update?.();
|
|
||||||
};
|
|
||||||
|
|
||||||
const getScrollElementScrollableHeight = () => {
|
|
||||||
if (!instanceRef.current || !scrollEventElement.current) {
|
|
||||||
instanceRef.current = instance?.();
|
|
||||||
scrollEventElement.current =
|
|
||||||
instanceRef.current?.elements()?.scrollEventElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
const scrollOffsetElement = instanceRef.current?.elements().viewport;
|
|
||||||
|
|
||||||
return {
|
|
||||||
scrollTop: scrollOffsetElement?.scrollTop,
|
|
||||||
scrollHeight:
|
|
||||||
scrollOffsetElement?.scrollHeight - scrollOffsetElement?.clientHeight
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const getScrollElement = () => {
|
|
||||||
if (!instanceRef.current || !scrollEventElement.current) {
|
|
||||||
instanceRef.current = instance?.();
|
|
||||||
scrollEventElement.current =
|
|
||||||
instanceRef.current?.elements()?.scrollEventElement;
|
|
||||||
}
|
|
||||||
return scrollEventElement;
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
return () => {
|
|
||||||
instanceRef.current?.destroy?.();
|
|
||||||
removeWheelEvent();
|
|
||||||
};
|
|
||||||
}, [instance]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
initialize: createInstance,
|
|
||||||
instance: instanceRef,
|
|
||||||
scrollEventElement: scrollEventElement,
|
|
||||||
initialized: initialized.current,
|
|
||||||
getScrollElementScrollableHeight,
|
|
||||||
getScrollElement,
|
|
||||||
generateInstance,
|
|
||||||
destroyInstance: destroyInstance,
|
|
||||||
updateScrollerPosition: throttledUpdateScrollerPosition,
|
|
||||||
updateScrollerPositionToTop: updateScrollerPositionToTop,
|
|
||||||
scrollToBottom: scrollauto,
|
|
||||||
scrollToTop: updateScrollerPositionToTop,
|
|
||||||
scrollToTarget
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user