mirror of
https://github.com/lukevella/rallly.git
synced 2025-08-06 01:48:32 +02:00
💄 Update avatar component (#1812)
This commit is contained in:
parent
965e969fd5
commit
f5eb092d5d
11 changed files with 37 additions and 72 deletions
|
@ -40,7 +40,7 @@ export default async function Layout({
|
||||||
<OptimizedAvatarImage
|
<OptimizedAvatarImage
|
||||||
src={user.image}
|
src={user.image}
|
||||||
name={user.name}
|
name={user.name}
|
||||||
size="xs"
|
size="sm"
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -204,7 +204,7 @@ function DiscussionInner() {
|
||||||
<Participant>
|
<Participant>
|
||||||
<OptimizedAvatarImage
|
<OptimizedAvatarImage
|
||||||
name={comment.authorName}
|
name={comment.authorName}
|
||||||
size="xs"
|
size="sm"
|
||||||
/>
|
/>
|
||||||
<ParticipantName>{comment.authorName}</ParticipantName>
|
<ParticipantName>{comment.authorName}</ParticipantName>
|
||||||
{session.ownsObject(comment) ? (
|
{session.ownsObject(comment) ? (
|
||||||
|
|
|
@ -4,31 +4,20 @@ import { Avatar, AvatarFallback, AvatarImage } from "@rallly/ui/avatar";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
const sizeToWidth = {
|
|
||||||
xs: 20,
|
|
||||||
sm: 24,
|
|
||||||
md: 32,
|
|
||||||
lg: 48,
|
|
||||||
xl: 64,
|
|
||||||
};
|
|
||||||
|
|
||||||
export function OptimizedAvatarImage({
|
export function OptimizedAvatarImage({
|
||||||
size,
|
size,
|
||||||
className,
|
className,
|
||||||
src,
|
src,
|
||||||
name,
|
name,
|
||||||
}: {
|
}: {
|
||||||
size: "xs" | "sm" | "md" | "lg" | "xl";
|
size: "sm" | "md" | "lg" | "xl";
|
||||||
src?: string;
|
src?: string;
|
||||||
name: string;
|
name: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
}) {
|
}) {
|
||||||
const [isLoaded, setLoaded] = React.useState(false);
|
const [isLoaded, setLoaded] = React.useState(false);
|
||||||
return (
|
return (
|
||||||
<Avatar
|
<Avatar className={cn("rounded-full", className)} size={size}>
|
||||||
className={cn("rounded-full", className)}
|
|
||||||
style={{ width: sizeToWidth[size], height: sizeToWidth[size] }}
|
|
||||||
>
|
|
||||||
{src ? (
|
{src ? (
|
||||||
src.startsWith("https") || src.startsWith("data:") ? (
|
src.startsWith("https") || src.startsWith("data:") ? (
|
||||||
<AvatarImage src={src} alt={name} />
|
<AvatarImage src={src} alt={name} />
|
||||||
|
@ -49,11 +38,9 @@ export function OptimizedAvatarImage({
|
||||||
<AvatarFallback
|
<AvatarFallback
|
||||||
seed={name}
|
seed={name}
|
||||||
className={cn("shrink-0", {
|
className={cn("shrink-0", {
|
||||||
"text-[10px]": size === "xs",
|
"text-[10px]": size === "sm",
|
||||||
"text-[12px]": size === "sm",
|
"text-[12px]": size === "md",
|
||||||
"text-md": size === "md",
|
"text-md": size === "lg",
|
||||||
"text-lg": size === "lg",
|
|
||||||
"text-3xl": size === "xl",
|
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{name?.[0]?.toUpperCase()}
|
{name?.[0]?.toUpperCase()}
|
||||||
|
|
|
@ -44,7 +44,7 @@ export const ParticipantAvatarBar = ({
|
||||||
<OptimizedAvatarImage
|
<OptimizedAvatarImage
|
||||||
name={participant.name}
|
name={participant.name}
|
||||||
src={participant.image}
|
src={participant.image}
|
||||||
size="xs"
|
size="sm"
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
|
|
|
@ -1,42 +1,10 @@
|
||||||
import { cn } from "@rallly/ui";
|
import { cn } from "@rallly/ui";
|
||||||
import { Avatar, AvatarFallback } from "@rallly/ui/avatar";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
export function Participant({ children }: { children: React.ReactNode }) {
|
export function Participant({ children }: { children: React.ReactNode }) {
|
||||||
return <div className="flex min-w-0 items-center gap-x-2">{children}</div>;
|
return <div className="flex min-w-0 items-center gap-x-2">{children}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sizeToWidth = {
|
|
||||||
xs: 20,
|
|
||||||
sm: 24,
|
|
||||||
md: 32,
|
|
||||||
lg: 48,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ParticipantAvatar = ({
|
|
||||||
size = "md",
|
|
||||||
name,
|
|
||||||
}: {
|
|
||||||
size?: "xs" | "sm" | "md" | "lg";
|
|
||||||
name: string;
|
|
||||||
}) => {
|
|
||||||
return (
|
|
||||||
<Avatar size={sizeToWidth[size]}>
|
|
||||||
<AvatarFallback
|
|
||||||
className={cn({
|
|
||||||
"text-xs": size === "xs",
|
|
||||||
"text-sm": size === "sm",
|
|
||||||
"text-md": size === "md",
|
|
||||||
"text-lg": size === "lg",
|
|
||||||
})}
|
|
||||||
seed={name}
|
|
||||||
>
|
|
||||||
{name[0]?.toUpperCase()}
|
|
||||||
</AvatarFallback>
|
|
||||||
</Avatar>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ParticipantName = ({
|
export const ParticipantName = ({
|
||||||
children,
|
children,
|
||||||
}: {
|
}: {
|
||||||
|
|
|
@ -62,7 +62,7 @@ const ParticipantRowForm = ({
|
||||||
<div className="flex items-center justify-between gap-x-2.5">
|
<div className="flex items-center justify-between gap-x-2.5">
|
||||||
<Participant>
|
<Participant>
|
||||||
{name ? (
|
{name ? (
|
||||||
<OptimizedAvatarImage name={participantName} size="xs" />
|
<OptimizedAvatarImage name={participantName} size="sm" />
|
||||||
) : (
|
) : (
|
||||||
<YouAvatar />
|
<YouAvatar />
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -52,7 +52,7 @@ export const ParticipantRowView: React.FunctionComponent<{
|
||||||
>
|
>
|
||||||
<div className="flex max-w-full items-center justify-between gap-x-2">
|
<div className="flex max-w-full items-center justify-between gap-x-2">
|
||||||
<Participant>
|
<Participant>
|
||||||
<OptimizedAvatarImage size="xs" name={name} />
|
<OptimizedAvatarImage size="sm" name={name} />
|
||||||
<ParticipantName>{name}</ParticipantName>
|
<ParticipantName>{name}</ParticipantName>
|
||||||
</Participant>
|
</Participant>
|
||||||
<div className="flex items-center gap-x-2">
|
<div className="flex items-center gap-x-2">
|
||||||
|
|
|
@ -104,7 +104,7 @@ const MobilePoll: React.FunctionComponent = () => {
|
||||||
{visibleParticipants.map((participant) => (
|
{visibleParticipants.map((participant) => (
|
||||||
<SelectItem key={participant.id} value={participant.id}>
|
<SelectItem key={participant.id} value={participant.id}>
|
||||||
<Participant>
|
<Participant>
|
||||||
<OptimizedAvatarImage name={participant.name} size="xs" />
|
<OptimizedAvatarImage name={participant.name} size="sm" />
|
||||||
<ParticipantName>{participant.name}</ParticipantName>
|
<ParticipantName>{participant.name}</ParticipantName>
|
||||||
{session.ownsObject(participant) && (
|
{session.ownsObject(participant) && (
|
||||||
<Badge>
|
<Badge>
|
||||||
|
|
|
@ -52,7 +52,7 @@ const PollOptionVoteSummary: React.FunctionComponent<{ optionId: string }> = ({
|
||||||
// biome-ignore lint/suspicious/noArrayIndexKey: Fix this later
|
// biome-ignore lint/suspicious/noArrayIndexKey: Fix this later
|
||||||
<div key={i} className="flex">
|
<div key={i} className="flex">
|
||||||
<div className="relative mr-2.5 flex size-5 items-center justify-center">
|
<div className="relative mr-2.5 flex size-5 items-center justify-center">
|
||||||
<OptimizedAvatarImage size="xs" name={name} />
|
<OptimizedAvatarImage size="sm" name={name} />
|
||||||
<VoteIcon
|
<VoteIcon
|
||||||
type="yes"
|
type="yes"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
@ -66,7 +66,7 @@ const PollOptionVoteSummary: React.FunctionComponent<{ optionId: string }> = ({
|
||||||
// biome-ignore lint/suspicious/noArrayIndexKey: Fix this later
|
// biome-ignore lint/suspicious/noArrayIndexKey: Fix this later
|
||||||
<div key={i} className="flex">
|
<div key={i} className="flex">
|
||||||
<div className="relative mr-2.5 flex size-5 items-center justify-center">
|
<div className="relative mr-2.5 flex size-5 items-center justify-center">
|
||||||
<OptimizedAvatarImage size="xs" name={name} />
|
<OptimizedAvatarImage size="sm" name={name} />
|
||||||
<VoteIcon
|
<VoteIcon
|
||||||
type="ifNeedBe"
|
type="ifNeedBe"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
@ -82,7 +82,7 @@ const PollOptionVoteSummary: React.FunctionComponent<{ optionId: string }> = ({
|
||||||
// biome-ignore lint/suspicious/noArrayIndexKey: Fix this later
|
// biome-ignore lint/suspicious/noArrayIndexKey: Fix this later
|
||||||
<div key={i} className="flex">
|
<div key={i} className="flex">
|
||||||
<div className="relative mr-2.5 flex size-5 items-center justify-center">
|
<div className="relative mr-2.5 flex size-5 items-center justify-center">
|
||||||
<OptimizedAvatarImage size="xs" name={name} />
|
<OptimizedAvatarImage size="sm" name={name} />
|
||||||
<VoteIcon
|
<VoteIcon
|
||||||
type="no"
|
type="no"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|
|
@ -48,7 +48,7 @@ export const UserDropdown = ({ className }: { className?: string }) => {
|
||||||
<OptimizedAvatarImage
|
<OptimizedAvatarImage
|
||||||
src={user.image ?? undefined}
|
src={user.image ?? undefined}
|
||||||
name={user.name}
|
name={user.name}
|
||||||
size="xs"
|
size="sm"
|
||||||
/>
|
/>
|
||||||
<span className="truncate">{user.name}</span>
|
<span className="truncate">{user.name}</span>
|
||||||
<Icon>
|
<Icon>
|
||||||
|
|
|
@ -2,28 +2,38 @@
|
||||||
|
|
||||||
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
||||||
import { cn } from "@rallly/ui";
|
import { cn } from "@rallly/ui";
|
||||||
|
import { type VariantProps, cva } from "class-variance-authority";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
|
const avatarVariants = cva("relative flex shrink-0 overflow-hidden", {
|
||||||
|
variants: {
|
||||||
|
size: {
|
||||||
|
xl: "size-12 text-xl",
|
||||||
|
lg: "size-9 text-lg",
|
||||||
|
md: "size-7 text-base",
|
||||||
|
sm: "size-5 text-xs",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
size: "md",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const Avatar = React.forwardRef<
|
const Avatar = React.forwardRef<
|
||||||
React.ElementRef<typeof AvatarPrimitive.Root>,
|
React.ComponentRef<typeof AvatarPrimitive.Root>,
|
||||||
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root> & {
|
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root> &
|
||||||
size?: number;
|
VariantProps<typeof avatarVariants>
|
||||||
}
|
>(({ className, size = "md", ...props }, ref) => (
|
||||||
>(({ className, size = 48, ...props }, ref) => (
|
|
||||||
<AvatarPrimitive.Root
|
<AvatarPrimitive.Root
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(avatarVariants({ size }), className)}
|
||||||
"relative flex shrink-0 overflow-hidden rounded-full",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
style={{ width: size, height: size }}
|
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
Avatar.displayName = AvatarPrimitive.Root.displayName;
|
Avatar.displayName = AvatarPrimitive.Root.displayName;
|
||||||
|
|
||||||
const AvatarImage = React.forwardRef<
|
const AvatarImage = React.forwardRef<
|
||||||
React.ElementRef<typeof AvatarPrimitive.Image>,
|
React.ComponentRef<typeof AvatarPrimitive.Image>,
|
||||||
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
|
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
|
||||||
>(({ className, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<AvatarPrimitive.Image
|
<AvatarPrimitive.Image
|
||||||
|
@ -62,7 +72,7 @@ export function getGradient(seed: string): {
|
||||||
}
|
}
|
||||||
|
|
||||||
const AvatarFallback = React.forwardRef<
|
const AvatarFallback = React.forwardRef<
|
||||||
React.ElementRef<typeof AvatarPrimitive.Fallback>,
|
React.ComponentRef<typeof AvatarPrimitive.Fallback>,
|
||||||
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback> & {
|
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback> & {
|
||||||
seed: string;
|
seed: string;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue