eb82ace2de
Co-authored-by: Scott Nonnenberg <scott@signal.org>
80 lines
2.1 KiB
TypeScript
80 lines
2.1 KiB
TypeScript
// Copyright 2024 Signal Messenger, LLC
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
import { v4 as uuid } from 'uuid';
|
|
import React, { useMemo, useCallback } from 'react';
|
|
|
|
import type { LocalizerType } from '../types/Util';
|
|
import type { ConversationType } from '../state/ducks/conversations';
|
|
import { Avatar, AvatarSize } from './Avatar';
|
|
|
|
export type PropsType = Readonly<{
|
|
i18n: LocalizerType;
|
|
conversations: ReadonlyArray<ConversationType>;
|
|
}>;
|
|
|
|
const MAX_AVATARS = 2;
|
|
|
|
export function CollidingAvatars({
|
|
i18n,
|
|
conversations,
|
|
}: PropsType): JSX.Element {
|
|
const clipId = useMemo(() => uuid(), []);
|
|
const onRef = useCallback(
|
|
(elem: HTMLDivElement | null): void => {
|
|
if (elem) {
|
|
// Note that these cannot be set through html attributes
|
|
elem.style.setProperty('--clip-path', `url(#${clipId})`);
|
|
}
|
|
},
|
|
[clipId]
|
|
);
|
|
|
|
return (
|
|
<div className="CollidingAvatars" ref={onRef}>
|
|
{conversations.slice(0, MAX_AVATARS).map(({ id, type, ...convo }) => {
|
|
return (
|
|
<Avatar
|
|
key={id}
|
|
className="CollidingAvatars__avatar"
|
|
i18n={i18n}
|
|
size={AvatarSize.TWENTY_FOUR}
|
|
conversationType={type}
|
|
badge={undefined}
|
|
{...convo}
|
|
/>
|
|
);
|
|
})}
|
|
{/*
|
|
This clip path is a rectangle with the right-bottom corner cut off
|
|
by a circle:
|
|
|
|
AAAAAAA
|
|
AAAAAAA
|
|
AAAAA
|
|
AAA
|
|
AAA
|
|
AA
|
|
AA
|
|
|
|
The idea is that we cut a circle away from the top avatar so that there
|
|
is a bit of transparent area between two avatars:
|
|
|
|
AAAAAAA
|
|
AAAAAAA
|
|
AAAAA
|
|
AAA B
|
|
AAA BB
|
|
AA BBB
|
|
AA BBB
|
|
|
|
See CollidingAvatars.scss for how this clipPath is applied.
|
|
*/}
|
|
<svg width={0} height={0} className="CollidingAvatars__clip_svg">
|
|
<clipPath id={clipId} clipPathUnits="objectBoundingBox">
|
|
<path d="M0 0 h1 v0.4166 A0.54166 0.54166 0 0 0.4166 1 H0 Z" />
|
|
</clipPath>
|
|
</svg>
|
|
</div>
|
|
);
|
|
}
|