mirror of
https://github.com/arc53/DocsGPT.git
synced 2026-04-28 13:00:30 +00:00
(feat: widget): smooth transitions
This commit is contained in:
@@ -60,18 +60,53 @@ const Overlay = styled.div`
|
|||||||
z-index: 999;
|
z-index: 999;
|
||||||
transition: opacity 0.5s;
|
transition: opacity 0.5s;
|
||||||
`
|
`
|
||||||
const WidgetContainer = styled.div<{ modal?: boolean }>`
|
const WidgetContainer = styled.div<{ modal?: boolean, isOpen?: boolean }>`
|
||||||
all: initial;
|
all: initial;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
right: ${props => props.modal ? '50%' : '10px'};
|
right: ${props => props.modal ? '50%' : '10px'};
|
||||||
bottom: ${props => props.modal ? '50%' : '10px'};
|
bottom: ${props => props.modal ? '50%' : '10px'};
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
display: block;
|
display: none;
|
||||||
|
transform-origin:100% 100%;
|
||||||
|
&.open {
|
||||||
|
animation: createBox 500ms cubic-bezier(0.25, 0.1, 0.25, 1) forwards;;
|
||||||
|
}
|
||||||
|
&.close {
|
||||||
|
animation: closeBox 500ms cubic-bezier(0.6, 0.05, 0.15, 1) forwards;
|
||||||
|
}
|
||||||
${props => props.modal &&
|
${props => props.modal &&
|
||||||
"transform : translate(50%,50%);"
|
"transform : translate(50%,50%);"
|
||||||
}
|
}
|
||||||
align-items: center;
|
align-items: center;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
@keyframes createBox {
|
||||||
|
0% {
|
||||||
|
transform: scale(0.5);
|
||||||
|
}
|
||||||
|
60% {
|
||||||
|
transform: scale(1.05) translate(-2%,-2%);
|
||||||
|
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(1);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes closeBox {
|
||||||
|
0% {
|
||||||
|
transform: scale(1) translate(0, 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
40% {
|
||||||
|
transform: scale(1.05) translate(-2%, -2%);
|
||||||
|
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(0);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
const StyledContainer = styled.div`
|
const StyledContainer = styled.div`
|
||||||
all: initial;
|
all: initial;
|
||||||
@@ -97,16 +132,19 @@ const StyledContainer = styled.div`
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
const FloatingButton = styled.div<{ bgcolor: string }>`
|
const FloatingButton = styled.div<{ bgcolor: string,hidden:boolean }>`
|
||||||
position: fixed;
|
position: fixed;
|
||||||
display: flex;
|
display: ${props => props.hidden ? "none" : "flex"};
|
||||||
z-index: 500;
|
z-index: 500;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 14px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
bottom: 16px;
|
bottom: 16px;
|
||||||
|
color: white;
|
||||||
|
font-family: sans-serif;
|
||||||
right: 16px;
|
right: 16px;
|
||||||
width: 80px;
|
font-weight: 500;
|
||||||
height: 80px;
|
|
||||||
border-radius: 9999px;
|
border-radius: 9999px;
|
||||||
background: ${props => props.bgcolor};
|
background: ${props => props.bgcolor};
|
||||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||||
@@ -388,7 +426,7 @@ export const DocsGPTWidget = ({
|
|||||||
heroDescription = 'This chatbot is built with DocsGPT and utilises GenAI, please review important information using sources.',
|
heroDescription = 'This chatbot is built with DocsGPT and utilises GenAI, please review important information using sources.',
|
||||||
size = 'small',
|
size = 'small',
|
||||||
theme = 'dark',
|
theme = 'dark',
|
||||||
buttonIcon = 'https://d3dg1063dc54p9.cloudfront.net/widget/message.svg',
|
buttonIcon = 'https://d3dg1063dc54p9.cloudfront.net/widget/chat.svg',
|
||||||
buttonBg = 'linear-gradient(to bottom right, #5AF0EC, #E80D9D)',
|
buttonBg = 'linear-gradient(to bottom right, #5AF0EC, #E80D9D)',
|
||||||
collectFeedback = true,
|
collectFeedback = true,
|
||||||
deafultOpen = false
|
deafultOpen = false
|
||||||
@@ -400,6 +438,7 @@ export const DocsGPTWidget = ({
|
|||||||
const [open, setOpen] = React.useState<boolean>(deafultOpen)
|
const [open, setOpen] = React.useState<boolean>(deafultOpen)
|
||||||
const [eventInterrupt, setEventInterrupt] = React.useState<boolean>(false); //click or scroll by user while autoScrolling
|
const [eventInterrupt, setEventInterrupt] = React.useState<boolean>(false); //click or scroll by user while autoScrolling
|
||||||
const isBubbleHovered = useRef<boolean>(false)
|
const isBubbleHovered = useRef<boolean>(false)
|
||||||
|
const widgetRef = useRef<HTMLDivElement>(null)
|
||||||
const endMessageRef = React.useRef<HTMLDivElement | null>(null);
|
const endMessageRef = React.useRef<HTMLDivElement | null>(null);
|
||||||
const md = new MarkdownIt();
|
const md = new MarkdownIt();
|
||||||
|
|
||||||
@@ -511,11 +550,23 @@ export const DocsGPTWidget = ({
|
|||||||
const handleImageError = (event: React.SyntheticEvent<HTMLImageElement, Event>) => {
|
const handleImageError = (event: React.SyntheticEvent<HTMLImageElement, Event>) => {
|
||||||
event.currentTarget.src = "https://d3dg1063dc54p9.cloudfront.net/cute-docsgpt.png";
|
event.currentTarget.src = "https://d3dg1063dc54p9.cloudfront.net/cute-docsgpt.png";
|
||||||
};
|
};
|
||||||
|
const handleClose = () => {
|
||||||
|
setOpen(false);
|
||||||
|
setTimeout(() => {
|
||||||
|
if(widgetRef.current)
|
||||||
|
widgetRef.current.style.display = "none"
|
||||||
|
}, 500);
|
||||||
|
};
|
||||||
|
const handleOpen = () => {
|
||||||
|
setOpen(true)
|
||||||
|
if(widgetRef.current)
|
||||||
|
widgetRef.current.style.display = 'block'
|
||||||
|
}
|
||||||
const dimensions =
|
const dimensions =
|
||||||
typeof size === 'object' && 'custom' in size
|
typeof size === 'object' && 'custom' in size
|
||||||
? sizesConfig.getCustom(size.custom)
|
? sizesConfig.getCustom(size.custom)
|
||||||
: sizesConfig[size];
|
: sizesConfig[size];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeProvider theme={{ ...themes[theme], dimensions }}>
|
<ThemeProvider theme={{ ...themes[theme], dimensions }}>
|
||||||
{open && size === 'large' &&
|
{open && size === 'large' &&
|
||||||
@@ -523,13 +574,14 @@ export const DocsGPTWidget = ({
|
|||||||
setOpen(false)
|
setOpen(false)
|
||||||
}} />
|
}} />
|
||||||
}
|
}
|
||||||
<FloatingButton bgcolor={buttonBg} onClick={() => setOpen(!open)} hidden={open}>
|
<FloatingButton bgcolor={buttonBg} onClick={handleOpen} hidden={open}>
|
||||||
<img style={{ maxHeight: '64px', maxWidth: '64px' }} src={buttonIcon} />
|
<img width={24} src={buttonIcon} />
|
||||||
|
<span>Ask a question</span>
|
||||||
</FloatingButton>
|
</FloatingButton>
|
||||||
<WidgetContainer modal={size == 'large'}>
|
<WidgetContainer ref={widgetRef} className={open ? 'open' : 'close'} modal={size == 'large'}>
|
||||||
{open && <StyledContainer>
|
{<StyledContainer>
|
||||||
<div>
|
<div>
|
||||||
<CancelButton onClick={() => setOpen(false)}>
|
<CancelButton onClick={handleClose}>
|
||||||
<Cross2Icon width={24} height={24} color={theme === 'light' ? 'black' : 'white'} />
|
<Cross2Icon width={24} height={24} color={theme === 'light' ? 'black' : 'white'} />
|
||||||
</CancelButton>
|
</CancelButton>
|
||||||
<Header>
|
<Header>
|
||||||
|
|||||||
Reference in New Issue
Block a user