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:
parent
1a24f9ec25
commit
490e032ac6
52 changed files with 554 additions and 1824 deletions
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
/>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
/>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
/>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ export const KanBanProperties: FC<IKanBanProperties> = observer((props) => {
|
|||
hideDropdownArrow
|
||||
onChange={handleAssignee}
|
||||
disabled={false}
|
||||
multiple
|
||||
/>
|
||||
)}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 />
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
) : (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue