Created
February 26, 2024 10:00
-
-
Save jamcgrath/0b2cff98d52eca8bcc0f891337e83801 to your computer and use it in GitHub Desktop.
RecipientsDisplay.svelte
This file contains 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
<script lang="ts"> | |
import { throttle } from 'lodash-es' | |
import { tick } from 'svelte' | |
export let recipients: string[] | |
let recipientsString: string = '' | |
let recipientsDisplay: HTMLElement | |
let recipientsDisplayRecipients: HTMLElement | |
let recipientsDisplayBadge: HTMLElement | |
let recipientsDisplayWidth: number = 0 | |
let recipientsBadgeCount: number = 0 | |
// throttle the setRecipientsString function on resize of span.recipients-display using lodash | |
// throttle options: https://lodash.com/docs/4.17.15#throttle | |
$: recipientsDisplayWidth, | |
throttle(setRecipientsString, 100, { leading: true })() | |
/* | |
Recursively set the `recipientsString` based on the `recipients` array and check | |
if the resulting string overflows the parent container. If it does, remove the last | |
recipient and optionally check again to see if it overflows with the badge. | |
*/ | |
async function setRecipientsString(index: number = 1, stop: boolean = false) { | |
recipientsString = recipients.slice(0, index).join(', ') | |
recipientsBadgeCount = recipients.length - index | |
if (recipientsBadgeCount >= 1) { | |
recipientsString += ', ...' | |
} | |
//wait for DOM to update | |
await tick() | |
if (checkOverflow() && index > 1) { | |
return setRecipientsString(index - 1, true) | |
} else if (checkOverflowWithBadge() && index > 1) { | |
return setRecipientsString(index - 1, true) | |
} else if (index <= recipients.length && !stop) { | |
return setRecipientsString(index + 1) | |
} | |
} | |
function checkOverflow(): boolean { | |
return recipientsDisplay?.scrollWidth > recipientsDisplayWidth | |
} | |
function checkOverflowWithBadge(): boolean { | |
return ( | |
recipientsDisplayRecipients?.clientWidth > | |
recipientsDisplayWidth - recipientsDisplayBadge?.clientWidth | |
) | |
} | |
</script> | |
<!-- | |
@component | |
Receives a `recipients` array prop and renders a comma separated list of | |
recipients with an optional badge for the number of recipients not displayed. | |
- Usage: | |
```tsx | |
<RecipientsDisplay {recipients} /> | |
``` | |
--> | |
<span | |
class="recipients-display" | |
bind:this={recipientsDisplay} | |
bind:clientWidth={recipientsDisplayWidth} | |
> | |
<span | |
class="recipients-display__recipients {recipientsBadgeCount === | |
recipients.length - 1 | |
? 'ellipsis' | |
: ''}" | |
bind:this={recipientsDisplayRecipients}>{recipientsString}</span | |
> | |
{#if recipientsBadgeCount >= 1} | |
<span class="recipients-display__badge" bind:this={recipientsDisplayBadge} | |
>+{recipientsBadgeCount}</span | |
> | |
{/if} | |
</span> | |
<style lang="scss"> | |
@use '../styles/variables.scss'; | |
.recipients-display { | |
font-size: 1rem; | |
color: #333; // this could also be inherited from the body styles | |
width: 100%; | |
display: flex; | |
align-items: center; | |
justify-content: space-between; | |
} | |
// looks nicer with the margin | |
.recipients-display__recipients { | |
margin-right: 0.3125rem; | |
} | |
.ellipsis { | |
white-space: nowrap; | |
overflow: hidden; | |
text-overflow: ellipsis; | |
} | |
.recipients-display__badge { | |
color: #f0f0f0; | |
background-color: variables.$color-primary; | |
border-radius: 0.1875rem; | |
padding: 0.125rem 0.3125rem; | |
} | |
</style> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment