118 lines
3.3 KiB
TypeScript
118 lines
3.3 KiB
TypeScript
import { Chip, ScrollShadow } from '@heroui/react';
|
|
import type { QuickReply as QuickReplyType } from '@mujian/js-sdk/types';
|
|
import { useEffect, useRef } from 'react';
|
|
import { mjChatCls } from '@/utils/cls';
|
|
|
|
export interface QuickReplyProps {
|
|
quickReplies: Array<QuickReplyType>;
|
|
onSend: (query: string) => void;
|
|
}
|
|
|
|
export const QuickReply = (props: QuickReplyProps) => {
|
|
const { quickReplies, onSend } = props;
|
|
|
|
const scrollShadowRef = useRef<HTMLDivElement>(null);
|
|
|
|
useEffect(() => {
|
|
const handleWheel = (e: WheelEvent) => {
|
|
if (Math.abs(e.deltaY) > 0 && scrollShadowRef.current) {
|
|
e.preventDefault();
|
|
scrollShadowRef.current.scrollLeft += e.deltaY;
|
|
}
|
|
};
|
|
|
|
scrollShadowRef?.current?.addEventListener('wheel', handleWheel, {
|
|
passive: false,
|
|
});
|
|
|
|
return () =>
|
|
scrollShadowRef?.current?.removeEventListener('wheel', handleWheel);
|
|
}, []);
|
|
|
|
const lastDragTime = useRef(0);
|
|
|
|
const onmousedown = (e: React.MouseEvent<HTMLDivElement>) => {
|
|
// 防止文字选择
|
|
e.preventDefault();
|
|
|
|
// 禁用页面文字选择
|
|
document.body.style.userSelect = 'none';
|
|
document.body.style.webkitUserSelect = 'none';
|
|
|
|
lastDragTime.current = 0;
|
|
let dragged = false;
|
|
// biome-ignore lint/suspicious/noExplicitAny: 抄来的代码
|
|
const mouseMove: EventListener = (e: any) => {
|
|
dragged = true;
|
|
if (scrollShadowRef.current) {
|
|
scrollShadowRef.current.scrollLeft += e.movementX * -2;
|
|
}
|
|
};
|
|
const mouseUp: EventListener = () => {
|
|
// 恢复文字选择
|
|
document.body.style.userSelect = '';
|
|
document.body.style.webkitUserSelect = '';
|
|
|
|
document.removeEventListener('mousemove', mouseMove);
|
|
document.removeEventListener('mouseup', mouseUp);
|
|
if (dragged) {
|
|
lastDragTime.current = Date.now();
|
|
}
|
|
};
|
|
|
|
document.addEventListener('mousemove', mouseMove);
|
|
document.addEventListener('mouseup', mouseUp);
|
|
};
|
|
|
|
return (
|
|
<ScrollShadow
|
|
ref={scrollShadowRef}
|
|
hideScrollBar
|
|
className={mjChatCls(
|
|
['input-quickreply-container'],
|
|
'flex w-full overflow-auto gap-2',
|
|
)}
|
|
orientation="horizontal"
|
|
style={{
|
|
userSelect: 'none',
|
|
WebkitUserSelect: 'none',
|
|
MozUserSelect: 'none',
|
|
msUserSelect: 'none',
|
|
}}
|
|
onMouseDown={onmousedown}
|
|
>
|
|
{quickReplies
|
|
.filter((qr) => !qr.isHidden)
|
|
.map((qr, index) => (
|
|
<Chip
|
|
key={index}
|
|
className={mjChatCls(
|
|
['input-quickreply-chip', `input-quickreply-chip-${index + 1}`],
|
|
'bg-white/15 text-default shrink-0 cursor-pointer',
|
|
)}
|
|
classNames={{
|
|
base: mjChatCls([
|
|
'input-quickreply-chip-base',
|
|
`input-quickreply-chip-${index + 1}-base`,
|
|
]),
|
|
content: mjChatCls([
|
|
'input-quickreply-chip-content',
|
|
`input-quickreply-chip-${index + 1}-content`,
|
|
]),
|
|
}}
|
|
radius="full"
|
|
variant="light"
|
|
onClick={() => {
|
|
const isClick = Date.now() - lastDragTime.current > 50;
|
|
if (isClick && qr.message) {
|
|
onSend(qr.message);
|
|
}
|
|
}}
|
|
>
|
|
{qr.label}
|
|
</Chip>
|
|
))}
|
|
</ScrollShadow>
|
|
);
|
|
};
|