-
Notifications
You must be signed in to change notification settings - Fork 6
/
GeneralCard.tsx
195 lines (170 loc) · 6.05 KB
/
GeneralCard.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
import { Text } from "@bbtgnn/polaris-interfacer";
import classNames from "classnames";
import { useAuth } from "hooks/useAuth";
import findProjectImages from "lib/findProjectImages";
import { isProjectType } from "lib/isProjectType";
import { EconomicResource } from "lib/types";
import { useTranslation } from "next-i18next";
import Link from "next/link";
import React, { createContext, useContext, useState } from "react";
import AddStar from "./AddStar";
import LocationText from "./LocationText";
import ProjectCardImage from "./ProjectCardImage";
import ProjectTypeChip from "./ProjectTypeChip";
import BrTags from "./brickroom/BrTags";
import BrUserAvatar from "./brickroom/BrUserAvatar";
import { ProjectType } from "./types";
export interface GeneralCardProps {
project: Partial<EconomicResource>;
children: JSX.Element[];
baseUrl?: string;
}
interface ProjectContextValue {
project: Partial<EconomicResource>;
setHoverTrue: () => void;
setHoverFalse: () => void;
baseUrl?: string;
}
const CardProjectContext = createContext<ProjectContextValue>({} as ProjectContextValue);
export const useCardProject = () => useContext(CardProjectContext);
const GeneralCard = (props: GeneralCardProps) => {
const { project, children, baseUrl } = props;
const getChildrenOnDisplayName = (children: JSX.Element[], displayName: string) =>
React.Children.map(children, child => (child.type.DisplayName === displayName ? child : null));
const header = getChildrenOnDisplayName(children, "CardHeader");
const body = getChildrenOnDisplayName(children, "CardBody");
const footer = getChildrenOnDisplayName(children, "CardFooter");
const [hover, setHover] = useState(false);
const setHoverTrue = () => setHover(true);
const setHoverFalse = () => setHover(false);
const classes = classNames("rounded-lg bg-white shadow flex flex-col justify-between", {
"ring-2 ring-primary": hover,
});
return (
<CardProjectContext.Provider value={{ project, setHoverTrue, setHoverFalse, baseUrl }}>
<div className={classes}>
<div className="flex flex-grow flex-col space-y-3 p-3">
{header}
{body}
</div>
{footer}
</div>
</CardProjectContext.Provider>
);
};
/* Partials */
const Tags = () => {
const { project } = useCardProject();
if (!project.classifiedAs?.length) return null;
return (
<div className="p-3 border-t-1 border-t-gray-200">
<BrTags wrap={false} tags={project.classifiedAs || []} />
</div>
);
};
const CardBody = (props: { children?: React.ReactNode; baseUrl?: string }) => {
const { children } = props;
const { project, setHoverTrue, setHoverFalse, baseUrl = "/project/" } = useCardProject();
const location = project.currentLocation?.mappableAddress;
const isDesign = isProjectType(project.conformsTo?.name!).Design;
return (
<div onMouseOver={setHoverTrue} onMouseLeave={setHoverFalse} className="flex flex-col flex-grow">
<Link href={`${baseUrl}${project.id}`}>
<a className="space-y-3 flex-grow">
<div className="flex flex-col h-full justify-between">
<div>{children}</div>
<div className="space-y-1">
{location && !isDesign && <LocationText color="primary" name={location} />}
<ProjectTypeChip project={project} link={false} />
</div>
</div>
</a>
</Link>
</div>
);
};
const RemoteImage = () => {
const { project } = useCardProject();
const images = findProjectImages(project);
return <ProjectCardImage projectType={project.conformsTo!.name as ProjectType} image={images?.[0]} />;
};
const ProjectTitleAndStats = () => {
const { project } = useCardProject();
return (
<div className="line-clamp-2">
<Text variant="headingLg" as="h4" breakWord={true}>
{project.name}
</Text>
<StatsDisplay />
</div>
);
};
function UserDisplay() {
const { project } = useCardProject();
const user = project.primaryAccountable;
if (!user) return null;
return (
<Link href={`/profile/${user.id}`}>
<a>
<div className="flex items-center space-x-2 hover:underline">
<BrUserAvatar user={user} size="36px" />
<Text as="p" variant="bodyMd" fontWeight="medium">
<span className="text-primary">{user.name}</span>
</Text>
</div>
</a>
</Link>
);
}
const UserAndStarHeader = () => {
const { project } = useCardProject();
const { user } = useAuth();
return (
<div className="flex justify-between items-center">
<UserDisplay />
{user && <AddStar id={project?.id!} owner={project?.primaryAccountable!.id} tiny />}
</div>
);
};
function StatDisplay(props: { stat: number; label: string }) {
const { stat, label } = props;
return (
<>
{Boolean(stat) && (
<Text as="p" variant="bodyMd" color="subdued">
<strong>{stat}</strong> {label}
</Text>
)}
</>
);
}
function StatsDisplay() {
const { t } = useTranslation("common");
const { project } = useCardProject();
const contributorsNum = project.metadata?.contributors?.length;
const relationsNum = project.metadata?.relations?.length;
const stats = [
{ stat: contributorsNum, label: t("contributor", { count: contributorsNum }) },
{ stat: relationsNum, label: t("included", { count: relationsNum }) },
];
return (
<div className="flex [&>*+*]:before:content-[',_']">
{stats.map((s, i) => (
<StatDisplay stat={s.stat} label={s.label} key={s.stat} />
))}
</div>
);
}
const CardFooter = ({ children }: { children: JSX.Element }) => <>{children}</>;
const CardHeader = ({ children }: { children: JSX.Element }) => <>{children}</>;
CardBody.DisplayName = "CardBody";
CardFooter.DisplayName = "CardFooter";
CardHeader.DisplayName = "CardHeader";
GeneralCard.RemoteImage = RemoteImage;
GeneralCard.ProjectTitleAndStats = ProjectTitleAndStats;
GeneralCard.UserAndStarHeader = UserAndStarHeader;
GeneralCard.CardBody = CardBody;
GeneralCard.CardFooter = CardFooter;
GeneralCard.CardHeader = CardHeader;
GeneralCard.Tags = Tags;
export default GeneralCard;