chore: wrapped the base component with ThemeProvider at the root level to make theme props available globally

This commit is contained in:
utin-francis-peter
2024-10-28 17:33:40 +01:00
parent bd66d0a987
commit f8d65b84db
2 changed files with 59 additions and 92 deletions

View File

@@ -1,42 +1,15 @@
"use client"; "use client";
import React, { useRef, useState } from 'react' import React, { useRef } from 'react'
import DOMPurify from 'dompurify'; import DOMPurify from 'dompurify';
import styled, { keyframes, createGlobalStyle, ThemeProvider } from 'styled-components'; import styled, { keyframes, createGlobalStyle, } from 'styled-components';
import { PaperPlaneIcon, RocketIcon, ExclamationTriangleIcon, Cross2Icon, ExternalLinkIcon } from '@radix-ui/react-icons'; import { PaperPlaneIcon, RocketIcon, ExclamationTriangleIcon, Cross2Icon, } from '@radix-ui/react-icons';
import { FEEDBACK, MESSAGE_TYPE, Query, Status, WidgetProps } from '../types/index'; import { FEEDBACK, MESSAGE_TYPE, Query, Status, WidgetProps } from '../types/index';
import { fetchAnswerStreaming, sendFeedback } from '../requests/streamingApi'; import { fetchAnswerStreaming, sendFeedback } from '../requests/streamingApi';
import QuerySources from "./QuerySources";
import Like from "../assets/like.svg" import Like from "../assets/like.svg"
import Dislike from "../assets/dislike.svg" import Dislike from "../assets/dislike.svg"
import MarkdownIt from 'markdown-it'; import MarkdownIt from 'markdown-it';
const themes = {
dark: {
bg: '#222327',
text: '#fff',
primary: {
text: "#FAFAFA",
bg: '#222327'
},
secondary: {
text: "#A1A1AA",
bg: "#38383b"
}
},
light: {
bg: '#fff',
text: '#000',
primary: {
text: "#222327",
bg: "#fff"
},
secondary: {
text: "#A1A1AA",
bg: "#F6F6F6"
}
}
}
const GlobalStyles = createGlobalStyle` const GlobalStyles = createGlobalStyle`
.response pre { .response pre {
padding: 8px; padding: 8px;
@@ -354,44 +327,6 @@ const HeroDescription = styled.p`
line-height: 1.5; line-height: 1.5;
`; `;
const SourcesContainer = styled.div`
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 8px;
`;
const SourceBox = styled.div`
background-color: ${props => props.theme.secondary.bg};
border-radius: 6px;
padding: 8px;
font-size: 12px;
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
max-width: 200px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
`;
const LoadingIndicator = styled.div`
display: inline-block;
width: 20px;
height: 20px;
border: 2px solid ${props => props.theme.secondary.text};
border-radius: 50%;
border-top-color: transparent;
animation: spin 1s linear infinite;
@keyframes spin {
to {
transform: rotate(360deg);
}
}
`;
const Hero = ({ title, description, theme }: { title: string, description: string, theme: string }) => { const Hero = ({ title, description, theme }: { title: string, description: string, theme: string }) => {
return ( return (
<> <>
@@ -412,6 +347,8 @@ const Hero = ({ title, description, theme }: { title: string, description: strin
); );
}; };
export const DocsGPTWidget = ({ export const DocsGPTWidget = ({
apiHost = 'https://gptcloud.arc53.com', apiHost = 'https://gptcloud.arc53.com',
apiKey = '82962c9a-aa77-4152-94e5-a4f84fd44c6a', apiKey = '82962c9a-aa77-4152-94e5-a4f84fd44c6a',
@@ -494,13 +431,14 @@ export const DocsGPTWidget = ({
try { try {
await fetchAnswerStreaming( await fetchAnswerStreaming(
{ {
question: question, question,
apiKey: apiKey, apiKey,
apiHost: apiHost, apiHost,
history: queries, history: queries,
conversationId: conversationId, conversationId,
onEvent: (event: MessageEvent) => { onEvent: (event: MessageEvent) => {
const data = JSON.parse(event.data); const data = JSON.parse(event.data);
if (data.type === 'end') { if (data.type === 'end') {
setStatus('idle'); setStatus('idle');
} }
@@ -513,10 +451,11 @@ export const DocsGPTWidget = ({
setQueries(updatedQueries); setQueries(updatedQueries);
setStatus('idle') setStatus('idle')
} }
else if (data.type === 'sources') { else if (data.type === 'source') {
const updatedQueries = [...queries]; const updatedQueries = [...queries];
updatedQueries[updatedQueries.length - 1].sources = data.sources; updatedQueries[updatedQueries.length - 1].sources = data.source;
setQueries(updatedQueries); setQueries(updatedQueries);
} }
else { else {
const result = data.answer; const result = data.answer;
@@ -549,15 +488,17 @@ export const DocsGPTWidget = ({
}; };
return ( return (
<ThemeProvider theme={themes[theme]}> <>
{open && size === 'large' && {open && size === 'large' &&
<Overlay onClick={() => { <Overlay onClick={() => {
setOpen(false) setOpen(false)
}} /> }} />
} }
<FloatingButton bgcolor={buttonBg} onClick={() => setOpen(!open)} hidden={open}> <FloatingButton bgcolor={buttonBg} onClick={() => setOpen(!open)} hidden={open}>
<img style={{ maxHeight: '4rem', maxWidth: '4rem' }} src={buttonIcon} /> <img style={{ maxHeight: '4rem', maxWidth: '4rem' }} src={buttonIcon} />
</FloatingButton> </FloatingButton>
<WidgetContainer modal={size == 'large'}> <WidgetContainer modal={size == 'large'}>
<GlobalStyles /> <GlobalStyles />
{open && <StyledContainer> {open && <StyledContainer>
@@ -593,18 +534,7 @@ export const DocsGPTWidget = ({
query.response ? ( query.response ? (
<MessageBubble onMouseOver={() => { isBubbleHovered.current = true }} type='ANSWER'> <MessageBubble onMouseOver={() => { isBubbleHovered.current = true }} type='ANSWER'>
{showSources && query.sources && ( {showSources && query.sources && (
<SourcesContainer> <QuerySources sources={query.sources} theme={theme}/>
{query.sources.map((source, sourceIndex) => (
<SourceBox
key={sourceIndex}
onClick={() => window.open(source.source, '_blank', 'noopener,noreferrer')}
title={source.title}
>
{source.title}
<ExternalLinkIcon />
</SourceBox>
))}
</SourcesContainer>
)} )}
<Message <Message
type='ANSWER' type='ANSWER'
@@ -662,6 +592,7 @@ export const DocsGPTWidget = ({
: <Hero title={heroTitle} description={heroDescription} theme={theme} /> : <Hero title={heroTitle} description={heroDescription} theme={theme} />
} }
</Conversation> </Conversation>
<PromptContainer <PromptContainer
size={size} size={size}
onSubmit={handleSubmit}> onSubmit={handleSubmit}>
@@ -676,6 +607,6 @@ export const DocsGPTWidget = ({
</PromptContainer> </PromptContainer>
</StyledContainer>} </StyledContainer>}
</WidgetContainer> </WidgetContainer>
</ThemeProvider> </>
) )
} }

View File

@@ -1,11 +1,47 @@
import React from 'react'; import React from 'react';
import { createRoot } from 'react-dom/client'; import { createRoot } from 'react-dom/client';
import { DocsGPTWidget } from './components/DocsGPTWidget'; import { DocsGPTWidget } from './components/DocsGPTWidget';
import { ThemeProvider } from 'styled-components';
import { THEME } from './types';
const themes = {
dark: {
bg: '#222327',
text: '#fff',
primary: {
text: "#FAFAFA",
bg: '#222327'
},
secondary: {
text: "#A1A1AA",
bg: "#38383b"
}
},
light: {
bg: '#fff',
text: '#000',
primary: {
text: "#222327",
bg: "#fff"
},
secondary: {
text: "#A1A1AA",
bg: "#F6F6F6"
}
}
}
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
const renderWidget = (elementId: string, props = {}) => { const renderWidget = (elementId: string, props={
theme: "dark" as THEME
}) => {
const root = createRoot(document.getElementById(elementId) as HTMLElement); const root = createRoot(document.getElementById(elementId) as HTMLElement);
root.render(<DocsGPTWidget {...props} />); root.render(
<ThemeProvider theme={themes[props.theme]}>
<DocsGPTWidget {...props} />
</ThemeProvider>
);
}; };
(window as any).renderDocsGPTWidget = renderWidget; (window as any).renderDocsGPTWidget = renderWidget;
} }