Skip to content

Commit

Permalink
mobile chatbot referencs
Browse files Browse the repository at this point in the history
  • Loading branch information
mruwnik committed Jun 8, 2024
1 parent b10dcdf commit 7e92c56
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 57 deletions.
21 changes: 5 additions & 16 deletions app/components/Article/Contents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {useRef, useEffect} from 'react'
import useIsMobile from '~/hooks/isMobile'
import {questionUrl} from '~/routesMapper'
import type {Glossary, PageId, GlossaryEntry} from '~/server-utils/stampy'
import {togglePopup} from '../popups'

const footnoteHTML = (el: HTMLDivElement, e: HTMLAnchorElement): string | null => {
const id = e.getAttribute('href') || ''
Expand All @@ -19,19 +20,12 @@ const footnoteHTML = (el: HTMLDivElement, e: HTMLAnchorElement): string | null =
return elem.innerHTML
}

const scrollToElement = (e: HTMLElement, offset?: number) => {
const elementPosition = e.getBoundingClientRect().top + window.pageYOffset
const offsetPosition = elementPosition - (offset || 0)

window.scrollTo({top: offsetPosition, behavior: 'smooth'})
}

const addPopup = (e: HTMLElement, id: string, contents: string, mobile?: boolean): HTMLElement => {
const preexisting = document.getElementById(id)
if (preexisting) return preexisting

const popup = document.createElement('div')
popup.className = 'link-popup bordered small'
popup.className = 'link-popup bordered small background'
popup.innerHTML = contents
popup.id = id

Expand All @@ -43,14 +37,9 @@ const addPopup = (e: HTMLElement, id: string, contents: string, mobile?: boolean
popup.addEventListener('mouseover', () => popup.classList.add('shown'))
popup.addEventListener('mouseout', () => popup.classList.remove('shown'))
} else {
const togglePopup = (event: Event) => {
event.preventDefault()
popup.classList.toggle('shown')
document.body.classList.toggle('noscroll')
scrollToElement(e, 16)
}
popup.addEventListener('click', togglePopup)
e.addEventListener('click', togglePopup)
const toggle = () => popup.classList.toggle('shown')
popup.addEventListener('click', togglePopup(toggle, e))
e.addEventListener('click', togglePopup(toggle, e))
}

return popup
Expand Down
15 changes: 0 additions & 15 deletions app/components/Article/article.css
Original file line number Diff line number Diff line change
Expand Up @@ -224,19 +224,4 @@ article .banner h3 .title {
article {
margin: 0;
}

article .link-popup {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: #ff000033;
overflow: scroll;
}
article .link-popup .footnote {
margin: auto;
background-color: white;
width: 80%;
}
}
53 changes: 38 additions & 15 deletions app/components/Chatbot/ChatEntry.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {ComponentType, ReactNode} from 'react'
import {ComponentType, ReactNode, MouseEvent, useState, useRef} from 'react'
import {Link} from '@remix-run/react'
import MarkdownIt from 'markdown-it'
import Contents from '~/components/Article/Contents'
Expand All @@ -15,6 +15,8 @@ import StampyIcon from '~/components/icons-generated/Stampy'
import PersonInCircleIcon from '~/components/icons-generated/PersonInCircle'
import IconStampySmall from '~/components/icons-generated/StampySmall'
import QuestionMarkIcon from '~/components/icons-generated/QuestionMark'
import useIsMobile from '~/hooks/isMobile'
import {togglePopup} from '../popups'

const MAX_REFERENCES = 10

Expand Down Expand Up @@ -108,33 +110,53 @@ const ReferenceSummary = ({
}

const md = new MarkdownIt({html: true})
const ReferencePopup = (citation: Citation) => {
const ReferencePopup = (
citation: Citation & {className?: string; onClose?: (event: MouseEvent) => void}
) => {
const parsed = citation.text?.match(/^###.*?###\s+"""(.*?)"""$/ms)
if (!parsed) return undefined

return (
<div className="reference-contents bordered z-index-2">
<ReferenceSummary {...citation} titleClass="large-bold" />
<div className="grey padding-bottom-16 padding-top-32 xs">Referenced excerpt</div>
<div
className="default inner-html"
dangerouslySetInnerHTML={{
__html: md.render(parsed[1]),
}}
/>
<div
className={`reference-popup background z-index-2 ${citation.className || ''}`}
onClick={citation.onClose}
>
<div className="reference-contents bordered">
<ReferenceSummary {...citation} titleClass="large-bold" />
<div className="grey padding-bottom-16 padding-top-32 xs">Referenced excerpt</div>
<div
className="default inner-html"
dangerouslySetInnerHTML={{
__html: md.render(parsed[1]),
}}
/>
</div>
</div>
)
}

const ReferenceLink = (citation: Citation) => {
const ReferenceLink = (citation: Citation & {mobile?: boolean}) => {
const ref = useRef<HTMLAnchorElement>(null)
const [shown, setShown] = useState(false)
const clickHandler = !citation.mobile
? undefined
: togglePopup(() => setShown((current) => !current), ref.current || undefined)

const {id, index} = citation
if (!index || index > MAX_REFERENCES) return ''

return (
<span className="ref-container">
<Link id={`${id}-ref`} to={`#${id}`} className={`reference-link ref-${index}`}>
<Link
ref={ref}
id={`${id}-ref`}
to={`#${id}`}
className={`reference-link ref-${index}`}
onClick={clickHandler}
>
<span>{index}</span>
</Link>
<ReferencePopup {...citation} />
<ReferencePopup {...citation} className={shown ? 'shown' : 'hidden'} onClose={clickHandler} />
</span>
)
}
Expand All @@ -149,6 +171,7 @@ const Reference = (citation: Citation) => {
}

const ChatbotReply = ({question, phase, content, citationsMap}: AssistantEntry) => {
const mobile = useIsMobile()
const citations = [] as Citation[]
citationsMap?.forEach((v) => {
citations.push(v)
Expand Down Expand Up @@ -191,7 +214,7 @@ const ChatbotReply = ({question, phase, content, citationsMap}: AssistantEntry)
if (chunk?.match(/(\[\d+\])/)) {
const refId = chunk.slice(1, chunk.length - 1)
const ref = citationsMap?.get(refId)
return ref && <ReferenceLink key={i} {...ref} />
return ref && <ReferenceLink key={i} mobile={mobile} {...ref} />
} else if (chunk === '\n') {
return <br key={i} />
} else {
Expand Down
26 changes: 16 additions & 10 deletions app/components/Chatbot/chat_entry.css
Original file line number Diff line number Diff line change
Expand Up @@ -147,26 +147,32 @@ article.stampy {
}

.reference-contents {
visibility: hidden;
transition: visibility 0.2s;
width: 512px;
word-wrap: break-word;
background-color: white;
text-decoration: unset;
width: 512px;
border: 1px solid var(--colors-cool-grey-200, #dfe3e9);
padding: var(--spacing-40);
position: absolute;
transform: translateX(50%);
text-decoration: unset;
right: 0px;
top: 40px;
background-color: var(--colors-white);
padding: var(--spacing-40);
position: relative;
}
.reference-contents .inner-html {
height: 275px;
overflow: hidden;
}

.reference-contents:hover,
.reference-link:hover + .reference-contents {
@media (min-width: 780px) {
.reference-popup {
display: inline-block;
position: absolute;
transform: translate(-50%, 35px);
min-width: 30vw;
}
}

.reference-popup:hover,
.reference-link:hover + .reference-popup {
visibility: visible;
transition-delay: 0s;
}
2 changes: 1 addition & 1 deletion app/components/Chatbot/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ const QuestionInput = ({

return (
<div
className={'widget-ask ' + (fixed ? 'fixed col-10 z-index-4' : 'col-9')}
className={'widget-ask ' + (fixed ? 'fixed col-10 z-index-1' : 'col-9')}
ref={clickDetectorRef}
>
{results.length > 0 ? (
Expand Down
14 changes: 14 additions & 0 deletions app/components/popups.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const scrollToElement = (e: HTMLElement, offset?: number) => {
const elementPosition = e.getBoundingClientRect().top + window.pageYOffset
const offsetPosition = elementPosition - (offset || 0)

window.scrollTo({top: offsetPosition, behavior: 'smooth'})
}

export const togglePopup =
(onToggle: () => void, reference?: HTMLElement) => (event: MouseEvent | React.MouseEvent) => {
event.preventDefault()
onToggle()
document.body.classList.toggle('noscroll')
reference && scrollToElement(reference, 16)
}

0 comments on commit 7e92c56

Please sign in to comment.