Last active
August 25, 2024 20:23
-
-
Save danielcardeenas/499324ca693d278f069b19a17c446d74 to your computer and use it in GitHub Desktop.
Hennge Challange - Email Audit (React)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useEffect, useLayoutEffect, useRef, useState } from 'react' | |
import RecipientsBadge from './RecipientsBadge' | |
import { styled } from 'styled-components' | |
const GAP_PX = 40 | |
const TooltipComponent = styled.div` | |
position: fixed; | |
top: 8px; | |
right: 8px; | |
padding: 8px 16px; | |
background-color: #666; | |
color: #f0f0f0; | |
border-radius: 24px; | |
display: flex; | |
align-items: center; | |
` | |
const Container = styled.div` | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
` | |
const RecipientsList = styled.span` | |
overflow: hidden; | |
text-overflow: ellipsis; | |
` | |
interface RecipientDisplayProps { | |
recipients: string[] | |
} | |
export default function RecipientsDisplay({ | |
recipients, | |
}: RecipientDisplayProps) { | |
// Parent | |
const parentRef = useRef<HTMLDivElement>(null) | |
const [parentWidth, setParentWidth] = useState(0) | |
// Container | |
const ref = useRef<HTMLDivElement>(null) | |
const widthsCache = useRef<number[]>([]) | |
// Recipients | |
const [visibleRecipients, setVisibleRecipients] = | |
useState<string[]>(recipients) | |
// Tooltip | |
const [isTooltipVisible, setIsTooltipVisible] = useState(false) | |
useEffect(() => { | |
if (!ref) { | |
return | |
} | |
const resizeObserver = new ResizeObserver(event => { | |
// https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry/contentBoxSize | |
const width = event[0].contentBoxSize[0].inlineSize | |
setParentWidth(width) | |
}) | |
resizeObserver.observe(parentRef.current as Element) | |
}, [ref]) | |
useLayoutEffect(() => { | |
const { current } = ref | |
if (current && parentWidth > 0) { | |
if (widthsCache.current.length === 0) { | |
// Calculate the width of each recipient one time | |
const widths: number[] = [] | |
current.querySelectorAll(`span`).forEach(span => { | |
const spanWidth = span.getBoundingClientRect().width | |
widths.push(spanWidth) | |
}) | |
widthsCache.current = widths | |
} | |
// Find the index of the last visible recipient | |
let totalWidth = 0 | |
let lastVisibleIndex = 0 | |
for (let i = 0; i < widthsCache.current.length; i++) { | |
totalWidth += widthsCache.current[i] | |
if (totalWidth > parentWidth - GAP_PX) { | |
break | |
} | |
lastVisibleIndex = i | |
} | |
// Update the visible recipients | |
setVisibleRecipients(recipients.slice(0, lastVisibleIndex + 1)) | |
} | |
}, [ref, parentWidth]) | |
return ( | |
<Container ref={parentRef}> | |
<RecipientsList ref={ref}> | |
{visibleRecipients.map((recipient, index) => ( | |
<span key={`key-${recipient}-${index}`}> | |
{recipient} | |
{index < visibleRecipients.length - 1 ? ', ' : ''} | |
</span> | |
))} | |
{visibleRecipients.length < recipients.length ? ', ...' : ''} | |
</RecipientsList> | |
{visibleRecipients.length < recipients.length ? ( | |
<RecipientsBadge | |
onMouseEnter={() => setIsTooltipVisible(true)} | |
onMouseLeave={() => setIsTooltipVisible(false)} | |
numTruncated={recipients.length - visibleRecipients.length} | |
/> | |
) : null} | |
{isTooltipVisible && ( | |
<TooltipComponent>{recipients.join(', ')}</TooltipComponent> | |
)} | |
</Container> | |
) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment