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