Skip to content

Instantly share code, notes, and snippets.

@jamcgrath
Created February 26, 2024 10:00
Show Gist options
  • Save jamcgrath/0b2cff98d52eca8bcc0f891337e83801 to your computer and use it in GitHub Desktop.
Save jamcgrath/0b2cff98d52eca8bcc0f891337e83801 to your computer and use it in GitHub Desktop.
RecipientsDisplay.svelte
<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