style: new avatar and avatar group components (#2584)

* style: new avatar components

* chore: bug fixes

* chore: add pixel to size

* chore: add comments to helper functions

* fix: build errors
This commit is contained in:
Aaryan Khandelwal 2023-11-01 15:24:11 +05:30 committed by GitHub
parent 1a24f9ec25
commit 490e032ac6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
52 changed files with 554 additions and 1824 deletions

View file

@ -1,9 +1,7 @@
import { observer } from "mobx-react-lite";
// ui
import { Avatar } from "components/ui";
// icons
import { X } from "lucide-react";
// ui
import { Avatar } from "@plane/ui";
// types
import { IUserLite } from "types";
@ -25,7 +23,7 @@ export const AppliedMembersFilters: React.FC<Props> = observer((props) => {
return (
<div key={memberId} className="text-xs flex items-center gap-1 bg-custom-background-80 p-1 rounded">
<Avatar user={memberDetails} height="16px" width="16px" />
<Avatar name={memberDetails.display_name} src={memberDetails.avatar} showTooltip={false} />
<span className="normal-case">{memberDetails.display_name}</span>
<button
type="button"

View file

@ -1,10 +1,8 @@
import React, { useState } from "react";
// components
import { FilterHeader, FilterOption } from "components/issues";
// ui
import { Avatar } from "components/ui";
import { Loader } from "@plane/ui";
import { Avatar, Loader } from "@plane/ui";
// types
import { IUserLite } from "types";
@ -45,7 +43,7 @@ export const FilterAssignees: React.FC<Props> = (props) => {
key={`assignees-${member.id}`}
isChecked={appliedFilters?.includes(member.id) ? true : false}
onClick={() => handleUpdate(member.id)}
icon={<Avatar user={member} height="18px" width="18px" />}
icon={<Avatar name={member.display_name} src={member.avatar} showTooltip={false} size="sm" />}
title={member.display_name}
/>
))}

View file

@ -1,10 +1,8 @@
import React, { useState } from "react";
// components
import { FilterHeader, FilterOption } from "components/issues";
// ui
import { Avatar } from "components/ui";
import { Loader } from "@plane/ui";
import { Avatar, Loader } from "@plane/ui";
// types
import { IUserLite } from "types";
@ -45,7 +43,7 @@ export const FilterCreatedBy: React.FC<Props> = (props) => {
key={`created-by-${member.id}`}
isChecked={appliedFilters?.includes(member.id) ? true : false}
onClick={() => handleUpdate(member.id)}
icon={<Avatar user={member} height="18px" width="18px" />}
icon={<Avatar name={member.display_name} src={member.avatar} size="sm" />}
title={member.display_name}
/>
))}

View file

@ -3,7 +3,8 @@ import { observer } from "mobx-react-lite";
// components
import { HeaderGroupByCard } from "./group-by-card";
import { HeaderSubGroupByCard } from "./sub-group-by-card";
import { Avatar } from "components/ui";
// ui
import { Avatar } from "@plane/ui";
export interface IAssigneesHeader {
column_id: string;
@ -16,7 +17,7 @@ export interface IAssigneesHeader {
handleKanBanToggle: any;
}
export const Icon = ({ user }: any) => <Avatar user={user} height="22px" width="22px" fontSize="12px" />;
export const Icon = ({ user }: any) => <Avatar name={user.display_name} src={user.avatar} size="base" />;
export const AssigneesHeader: FC<IAssigneesHeader> = observer((props) => {
const {

View file

@ -114,17 +114,6 @@ export const KanBanProperties: React.FC<IKanBanProperties> = observer((props) =>
/>
)}
{/* assignee */}
{displayProperties && displayProperties?.assignee && (
<IssuePropertyAssignee
projectId={issue?.project_detail?.id || null}
value={issue?.assignees || null}
hideDropdownArrow
onChange={handleAssignee}
disabled={false}
/>
)}
{/* start date */}
{displayProperties && displayProperties?.start_date && (
<IssuePropertyDate
@ -186,6 +175,18 @@ export const KanBanProperties: React.FC<IKanBanProperties> = observer((props) =>
</div>
</Tooltip>
)}
{/* assignee */}
{displayProperties && displayProperties?.assignee && (
<IssuePropertyAssignee
projectId={issue?.project_detail?.id || null}
value={issue?.assignees || null}
hideDropdownArrow
onChange={handleAssignee}
disabled={false}
multiple
/>
)}
</div>
);
});

View file

@ -39,7 +39,7 @@ const GroupByList: React.FC<IGroupByList> = observer((props) => {
list.length > 0 &&
list.map((_list: any) => (
<div key={getValueFromObject(_list, listKey) as string} className={`flex-shrink-0 flex flex-col`}>
<div className="flex-shrink-0 w-full py-1 sticky top-0 z-[2] px-3">
<div className="flex-shrink-0 w-full py-1 sticky top-0 z-[2] px-3 bg-custom-background-90">
<ListGroupByHeaderRoot
column_id={getValueFromObject(_list, listKey) as string}
column_value={_list}
@ -193,7 +193,7 @@ export const List: React.FC<IList> = observer((props) => {
issues={issues}
group_by={group_by}
list={members}
listKey={`member.id`}
listKey={`id`}
handleIssues={handleIssues}
quickActions={quickActions}
display_properties={display_properties}
@ -206,7 +206,7 @@ export const List: React.FC<IList> = observer((props) => {
issues={issues}
group_by={group_by}
list={members}
listKey={`member.id`}
listKey={`id`}
handleIssues={handleIssues}
quickActions={quickActions}
display_properties={display_properties}

View file

@ -2,7 +2,8 @@ import { FC } from "react";
import { observer } from "mobx-react-lite";
// components
import { HeaderGroupByCard } from "./group-by-card";
import { Avatar } from "components/ui";
// ui
import { Avatar } from "@plane/ui";
export interface IAssigneesHeader {
column_id: string;
@ -10,7 +11,7 @@ export interface IAssigneesHeader {
issues_count: number;
}
export const Icon = ({ user }: any) => <Avatar user={user} height="22px" width="22px" fontSize="12px" />;
export const Icon = ({ user }: any) => <Avatar name={user.display_name} src={user.avatar} size="md" />;
export const AssigneesHeader: FC<IAssigneesHeader> = observer((props) => {
const { column_id, column_value, issues_count } = props;
@ -20,11 +21,7 @@ export const AssigneesHeader: FC<IAssigneesHeader> = observer((props) => {
return (
<>
{assignee && (
<HeaderGroupByCard
icon={<Icon user={assignee?.member} />}
title={assignee?.member?.display_name || ""}
count={issues_count}
/>
<HeaderGroupByCard icon={<Icon user={assignee} />} title={assignee?.display_name || ""} count={issues_count} />
)}
</>
);

View file

@ -19,8 +19,8 @@ export const CreatedByHeader: FC<ICreatedByHeader> = observer((props) => {
<>
{createdBy && (
<HeaderGroupByCard
icon={<Icon user={createdBy?.member} />}
title={createdBy?.member?.display_name || ""}
icon={<Icon user={createdBy} />}
title={createdBy?.display_name || ""}
count={issues_count}
/>
)}

View file

@ -94,6 +94,7 @@ export const KanBanProperties: FC<IKanBanProperties> = observer((props) => {
hideDropdownArrow
onChange={handleAssignee}
disabled={false}
multiple
/>
)}

View file

@ -1,16 +1,13 @@
import { Fragment, useState } from "react";
import { observer } from "mobx-react-lite";
import { useMobxStore } from "lib/mobx/store-provider";
import { usePopper } from "react-popper";
// ui
import { AssigneesList, Avatar } from "components/ui";
import { Combobox } from "@headlessui/react";
import { Tooltip } from "@plane/ui";
import { Check, ChevronDown, Search, User2 } from "lucide-react";
// ui
import { Avatar, AvatarGroup, Tooltip } from "@plane/ui";
// types
import { Placement } from "@popperjs/core";
import { RootStore } from "store/root";
export interface IIssuePropertyAssignee {
view?: "profile" | "workspace" | "project";
@ -41,7 +38,7 @@ export const IssuePropertyAssignee: React.FC<IIssuePropertyAssignee> = observer(
multiple = false,
} = props;
const { workspace: workspaceStore, project: projectStore }: RootStore = useMobxStore();
const { workspace: workspaceStore, project: projectStore } = useMobxStore();
const workspaceSlug = workspaceStore?.workspaceSlug;
const [query, setQuery] = useState("");
@ -49,17 +46,17 @@ export const IssuePropertyAssignee: React.FC<IIssuePropertyAssignee> = observer(
const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(null);
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
const projectMembers = projectId && projectStore?.members?.[projectId];
const projectMembers = projectId ? projectStore?.members?.[projectId] : undefined;
const fetchProjectMembers = () =>
workspaceSlug && projectId && projectStore.fetchProjectMembers(workspaceSlug, projectId);
const options = (projectMembers ? projectMembers : [])?.map((member) => ({
const options = (projectMembers ?? [])?.map((member) => ({
value: member.member.id,
query: member.member.display_name,
content: (
<div className="flex items-center gap-2">
<Avatar user={member.member} />
<Avatar name={member.member.display_name} src={member.member.avatar} showTooltip={false} />
{member.member.display_name}
</div>
),
@ -83,7 +80,15 @@ export const IssuePropertyAssignee: React.FC<IIssuePropertyAssignee> = observer(
>
<div className="flex items-center cursor-pointer h-full w-full gap-2 text-custom-text-200">
{value && value.length > 0 && Array.isArray(value) ? (
<AssigneesList userIds={value} length={3} showLength={true} />
<AvatarGroup showTooltip={false}>
{value.map((assigneeId) => {
const member = projectMembers?.find((m) => m.member.id === assigneeId)?.member;
if (!member) return null;
return <Avatar key={member.id} name={member.display_name} src={member.avatar} />;
})}
</AvatarGroup>
) : (
<span
className="flex items-center justify-between gap-1 h-full w-full text-xs px-2.5 py-1 rounded border-[0.5px] border-custom-border-300 duration-300 focus:outline-none

View file

@ -33,8 +33,6 @@ export const ProjectLayoutRoot: React.FC = observer(() => {
const issueCount = issueStore.getIssuesCount;
console.log("issueCount", issueCount);
return (
<div className="relative w-full h-full flex flex-col overflow-hidden">
<ProjectAppliedFiltersRoot />

View file

@ -1,12 +1,9 @@
import { useRouter } from "next/router";
import useSWR from "swr";
// services
import { ProjectService } from "services/project";
// ui
import { AssigneesList, Avatar } from "components/ui";
import { CustomSearchSelect } from "@plane/ui";
import { Avatar, AvatarGroup, CustomSearchSelect } from "@plane/ui";
import { User2 } from "lucide-react";
// fetch-keys
import { PROJECT_MEMBERS } from "constants/fetch-keys";
@ -35,7 +32,7 @@ export const IssueAssigneeSelect: React.FC<Props> = ({ projectId, value = [], on
query: member.member.display_name ?? "",
content: (
<div className="flex items-center gap-2">
<Avatar user={member.member} />
<Avatar name={member?.member.display_name} src={member?.member.avatar} showTooltip={false} />
{member.member.is_bot ? member.member.first_name : member.member.display_name}
</div>
),
@ -50,7 +47,15 @@ export const IssueAssigneeSelect: React.FC<Props> = ({ projectId, value = [], on
<div className="flex items-center gap-2 cursor-pointer text-xs text-custom-text-200">
{value && value.length > 0 && Array.isArray(value) ? (
<div className="-my-0.5 flex items-center justify-center gap-2">
<AssigneesList userIds={value} length={3} showLength />
<AvatarGroup showTooltip={false}>
{value.map((assigneeId) => {
const member = members?.find((m) => m.member.id === assigneeId)?.member;
if (!member) return null;
return <Avatar key={member.id} name={member.display_name} src={member.avatar} />;
})}
</AvatarGroup>
</div>
) : (
<div className="flex items-center justify-center gap-2 px-1.5 py-1 rounded shadow-sm border border-custom-border-300 hover:bg-custom-background-80">

View file

@ -1,14 +1,10 @@
import React from "react";
import { useRouter } from "next/router";
import useSWR from "swr";
// services
import { ProjectService } from "services/project";
// ui
import { CustomSearchSelect } from "@plane/ui";
import { AssigneesList, Avatar } from "components/ui/avatar";
import { Avatar, AvatarGroup, CustomSearchSelect } from "@plane/ui";
// fetch-keys
import { PROJECT_MEMBERS } from "constants/fetch-keys";
@ -37,7 +33,7 @@ export const SidebarAssigneeSelect: React.FC<Props> = ({ value, onChange, disabl
query: member.member.display_name,
content: (
<div className="flex items-center gap-2">
<Avatar user={member.member} />
<Avatar name={member?.member.display_name} src={member?.member.avatar} />
{member.member.display_name}
</div>
),
@ -50,7 +46,15 @@ export const SidebarAssigneeSelect: React.FC<Props> = ({ value, onChange, disabl
<>
{value && value.length > 0 && Array.isArray(value) ? (
<div className="-my-0.5 flex items-center gap-2">
<AssigneesList userIds={value} length={3} showLength={false} />
<AvatarGroup>
{value.map((assigneeId) => {
const member = members?.find((m) => m.member.id === assigneeId)?.member;
if (!member) return null;
return <Avatar key={member.id} name={member.display_name} src={member.avatar} />;
})}
</AvatarGroup>
<span className="text-custom-text-100 text-xs">{value.length} Assignees</span>
</div>
) : (