Skip to content

Commit

Permalink
feat(app-crm): add custom avatar group
Browse files Browse the repository at this point in the history
  • Loading branch information
alicanerdurmaz committed Aug 31, 2023
1 parent 5f1dc62 commit 3a68d75
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 50 deletions.
45 changes: 17 additions & 28 deletions examples/app-crm/src/components/company/card-view.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { FC } from "react";
import {
Avatar,
Button,
Card,
Col,
Expand All @@ -16,6 +15,7 @@ import { Text, CustomAvatar } from "..";
import { currencyNumber } from "../../utilities";
import { Company } from "../../interfaces/graphql";
import { useDelete, useNavigation } from "@refinedev/core";
import { CustomAvatarGroup } from "../custom-avatar-group";

type Props = {
loading?: boolean;
Expand All @@ -36,6 +36,15 @@ export const CompaniesCardView: FC<Props> = ({ companies, pagination }) => {
<>
<Row wrap gutter={[32, 32]}>
{companies.map((company) => {
const relatedContactAvatars = company.contacts?.nodes?.map(
(contact) => {
return {
name: contact.name,
src: contact.avatarUrl as string | undefined,
};
},
);

return (
<Col
key={company.id}
Expand All @@ -56,7 +65,7 @@ export const CompaniesCardView: FC<Props> = ({ companies, pagination }) => {
height: "60px",
display: "flex",
justifyContent: "space-between",
alignItems: "center",
alignItems: "flex-start",
padding: "0 16px",
}}
>
Expand All @@ -67,32 +76,12 @@ export const CompaniesCardView: FC<Props> = ({ companies, pagination }) => {
<Text size="xs">
Related contacts
</Text>
<Avatar.Group
maxCount={3}
size="small"
>
{company.contacts?.nodes?.map(
(contact) => {
return (
<Tooltip
title={
contact.name
}
key={contact.id}
>
<CustomAvatar
name={
contact.name
}
src={
contact.avatarUrl
}
/>
</Tooltip>
);
},
)}
</Avatar.Group>
<CustomAvatarGroup
size={"small"}
overlap
gap="4px"
avatars={relatedContactAvatars}
/>
</Space>
<Space direction="vertical" align="end">
<Text size="xs">Sales owner</Text>
Expand Down
32 changes: 10 additions & 22 deletions examples/app-crm/src/components/company/table-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,24 @@ import {
DeleteButton,
EditButton,
FilterDropdown,
getDefaultSortOrder,
useSelect,
} from "@refinedev/antd";
import { CrudFilters, CrudSorting, getDefaultFilter } from "@refinedev/core";
import { Avatar, Input, Select, Space, Table, TableProps, Tooltip } from "antd";
import { Input, Select, Space, Table, TableProps } from "antd";

import { Text, CustomAvatar } from "..";
import { currencyNumber } from "../../utilities";
import { Company } from "../../interfaces/graphql";
import { EyeOutlined, SearchOutlined } from "@ant-design/icons";
import { CustomAvatarGroup } from "../custom-avatar-group";

type Props = {
tableProps: TableProps<Company>;
filters: CrudFilters;
sorters: CrudSorting;
};

export const CompaniesTableView: FC<Props> = ({
tableProps,
filters,
sorters,
}) => {
export const CompaniesTableView: FC<Props> = ({ tableProps, filters }) => {
const { selectProps: selectPropsUsers } = useSelect({
resource: "users",
optionLabel: "name",
Expand Down Expand Up @@ -165,23 +161,15 @@ export const CompaniesTableView: FC<Props> = ({
)}
render={(_, record: Company) => {
const value = record.contacts;
const avatars = value?.nodes?.map((contact) => {
return {
name: contact.name,
src: contact.avatarUrl as string | undefined,
};
});

return (
<Avatar.Group maxCount={3} size="small">
{value?.nodes?.map((contact) => {
return (
<Tooltip
title={contact.name}
key={contact.id}
>
<CustomAvatar
name={contact.name}
src={contact.avatarUrl}
/>
</Tooltip>
);
})}
</Avatar.Group>
<CustomAvatarGroup avatars={avatars} size={"small"} />
);
}}
/>
Expand Down
140 changes: 140 additions & 0 deletions examples/app-crm/src/components/custom-avatar-group.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { FC } from "react";
import { CustomAvatar } from "./custom-avatar";
import { AvatarProps, Space, Tooltip } from "antd";
import { Text } from "./text";

type Props = {
avatars: {
name?: string;
src?: string;
}[];
size?: AvatarProps["size"];
maxCount?: number;
containerStyle?: React.CSSProperties;
avatarStyle?: AvatarProps["style"];
gap?: string;
overlap?: boolean;
};

export const CustomAvatarGroup: FC<Props> = ({
avatars,
size,
overlap,
maxCount = 3,
gap = "8px",
containerStyle,
avatarStyle,
}) => {
const visibleAvatars = avatars.slice(0, maxCount);
const remainingAvatars = avatars.slice(maxCount);
const hasRemainingAvatars = remainingAvatars.length > 0;

const getImageSize = (size: AvatarProps["size"] | number) => {
if (typeof size === "number") {
return overlap ? `${size + 4}px` : `${size}px`;
}

switch (size) {
case "large":
return overlap ? "44px" : "40px";
case "small":
return overlap ? "28px" : "24px";
default:
return overlap ? "36px" : "32px";
}
};

const shouldOverlap = overlap && avatars.length > 3;

return (
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "flex-start",
gap: shouldOverlap ? "0" : gap,
...containerStyle,
}}
>
{visibleAvatars.map((avatar, index) => {
const transform = shouldOverlap
? `translateX(-${index * 8}px)`
: undefined;

return (
<Tooltip title={avatar.name} key={index}>
<CustomAvatar
style={{
cursor: "pointer",
transform,
zIndex: index,
border: overlap ? "2px solid #fff" : "none",
width: getImageSize(size),
height: getImageSize(size),
...avatarStyle,
}}
name={avatar?.name}
src={avatar?.src}
size={size}
/>
</Tooltip>
);
})}

{hasRemainingAvatars && (
<Tooltip
destroyTooltipOnHide
title={
<Space direction="vertical">
{remainingAvatars.map((avatar, index) => {
return (
<Space key={index}>
<CustomAvatar
name={avatar.name}
src={avatar.src}
size="small"
/>
<Text
style={{
color: "#fff",
}}
key={avatar.name}
>
{avatar.name}
</Text>
</Space>
);
})}
</Space>
}
>
<Text
className="tertiary"
style={{
userSelect: "none",
cursor: "pointer",
fontSize: "10px",
lineHeight: "22px",
letterSpacing: "0.5px",
fontWeight: 600,
display: "flex",
alignItems: "center",
justifyContent: "center",
borderRadius: "50%",
width: getImageSize(size),
height: getImageSize(size),
transform: shouldOverlap
? `translateX(-${visibleAvatars.length * 8}px)`
: undefined,
zIndex: shouldOverlap ? visibleAvatars.length : 1,
backgroundColor: "#D9D9D9",
border: overlap ? "2px solid #fff" : "none",
}}
>
+{remainingAvatars.length}
</Text>
</Tooltip>
)}
</div>
);
};

0 comments on commit 3a68d75

Please sign in to comment.