Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/next/components/landing/index-list.tsx
1450 views
1
/*
2
* This file is part of CoCalc: Copyright © 2021 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
import { Avatar, Flex, Layout, List } from "antd";
7
import { ReactNode, isValidElement, useMemo } from "react";
8
9
import { Icon, IconName } from "@cocalc/frontend/components/icon";
10
import { COLORS } from "@cocalc/util/theme";
11
import Image, { StaticImageData } from "components/landing/image";
12
import { Paragraph, Title } from "components/misc";
13
import A from "components/misc/A";
14
import { MAX_WIDTH } from "lib/config";
15
import useCustomize, { CustomizeType } from "lib/use-customize";
16
17
export interface Item {
18
link: string | ((customize: CustomizeType) => string | undefined);
19
linkText?: ReactNode;
20
title: ReactNode;
21
logo: IconName | StaticImageData;
22
logoBackground?: string; // #color
23
image?: StaticImageData;
24
imageWidth?: string;
25
description: ReactNode | ((customize: CustomizeType) => ReactNode);
26
shareServer?: boolean; // only show if the share server is enabled
27
landingPages?: boolean; // only show if landing pages are enabled.
28
hide?: (customize: CustomizeType) => boolean; // if returns true, then this item will be hidden.
29
}
30
31
export type DataSource = Item[];
32
33
// replaces the description attribute by {description: ReactNode}
34
type ItemProcessed = Omit<Item, "description" | "link"> & {
35
description: ReactNode;
36
link: string;
37
};
38
39
interface Props {
40
title: ReactNode;
41
description: ReactNode;
42
dataSource: Item[];
43
updated?: string;
44
filter?: (item) => boolean;
45
}
46
47
export default function IndexList({ title, description, dataSource }: Props) {
48
const customize = useCustomize();
49
const { shareServer, landingPages } = customize;
50
const filteredDataSource: ItemProcessed[] = useMemo(() => {
51
return dataSource
52
.filter((item) => {
53
if (item.shareServer && !shareServer) return false;
54
if (item.landingPages && !landingPages) return false;
55
if (item.hide?.(customize)) return false;
56
return true;
57
})
58
.map((item) => {
59
return {
60
...item,
61
description:
62
typeof item.description === "function"
63
? item.description(customize)
64
: item.description,
65
link:
66
typeof item.link === "function"
67
? item.link(customize) ?? ""
68
: item.link,
69
};
70
});
71
}, [shareServer, landingPages, dataSource]);
72
return (
73
<Layout.Content
74
style={{
75
backgroundColor: "white",
76
}}
77
>
78
<Paragraph
79
style={{
80
maxWidth: MAX_WIDTH,
81
margin: "15px auto",
82
padding: "15px",
83
backgroundColor: "white",
84
}}
85
>
86
<Title
87
level={1}
88
style={{
89
textAlign: "center",
90
fontSize: "32pt",
91
color: COLORS.GRAY_D,
92
}}
93
>
94
{title}
95
</Title>
96
<Paragraph style={{ fontSize: "13pt" }}>{description}</Paragraph>
97
<DataList dataSource={filteredDataSource} />
98
</Paragraph>
99
</Layout.Content>
100
);
101
}
102
103
function DataList({ dataSource }: { dataSource: ItemProcessed[] }) {
104
function renderItem(item: ItemProcessed): ReactNode {
105
const icon = (
106
<div>
107
{isValidElement(item.logo) ? (
108
item.logo
109
) : typeof item.logo === "string" ? (
110
<Icon name={item.logo} style={{ fontSize: "75px" }} />
111
) : (
112
<Image src={item.logo} width={75} height={75} alt="Logo" />
113
)}
114
</div>
115
);
116
117
const extra = item.image ? (
118
<div
119
className="cc-hidden-mobile"
120
style={{ width: item.imageWidth ?? "275px" }}
121
>
122
<A href={item.link}>
123
<Image
124
src={item.image}
125
alt={`Screenshot illustrating ${item.title}`}
126
/>
127
</A>
128
</div>
129
) : undefined;
130
131
return (
132
<List.Item key={item.link} extra={extra} style={{ marginTop: "16px" }}>
133
<List.Item.Meta
134
avatar={
135
item.logo ? (
136
<A href={item.link} alt={item.title + " logo "}>
137
<Avatar
138
style={{
139
backgroundColor: item.logoBackground,
140
}}
141
alt={item.title + " logo "}
142
size={80}
143
shape="square"
144
icon={icon}
145
/>
146
</A>
147
) : undefined
148
}
149
title={
150
item.link ? (
151
item.linkText ? (
152
<Flex vertical>
153
<div style={{ fontSize: "16pt" }}>{item.title}</div>
154
<A href={item.link}>{item.linkText}</A>
155
</Flex>
156
) : (
157
<A href={item.link} style={{ fontSize: "16pt" }}>
158
{item.title}
159
</A>
160
)
161
) : (
162
item.title
163
)
164
}
165
description={
166
<Paragraph style={{ color: COLORS.GRAY, fontSize: "12pt" }}>
167
{item.description}
168
</Paragraph>
169
}
170
/>
171
</List.Item>
172
);
173
}
174
175
return (
176
<List
177
itemLayout="vertical"
178
size="large"
179
dataSource={dataSource}
180
renderItem={renderItem}
181
/>
182
);
183
}
184
185