Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/next/components/landing/content.tsx
Views: 687
/*1* This file is part of CoCalc: Copyright © 2021 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import { Col, Row, Space } from "antd";6import { ReactNode } from "react";7import StaticMarkdown from "@cocalc/frontend/editors/slate/static-markdown";8import { COLORS } from "@cocalc/util/theme";9import Path from "components/app/path";10import { CSS, Paragraph, Title } from "components/misc";11import SanitizedMarkdown from "components/misc/sanitized-markdown";12import { MAX_WIDTH_LANDING } from "lib/config";13import useCustomize from "lib/use-customize";14import Image from "./image";15import SignIn from "./sign-in";16import LiveDemo from "components/landing/live-demo";1718// See https://github.com/vercel/next.js/issues/29788 for why we have to define this for now (it's to work around a bug).19interface StaticImageData {20src: string;21height: number;22width: number;23blurDataURL?: string;24}2526interface Props {27aboveImage?: ReactNode;28alignItems?: "center" | "flex-start";29alt?: string;30caption?;31description?: ReactNode;32image?: string | StaticImageData;33imageAlternative?: JSX.Element | string; // string as markdown, replaces the image34landing?: boolean;35body?: ReactNode | string | StaticImageData;36startup?: ReactNode;37style?: React.CSSProperties;38subtitle?: ReactNode;39subtitleBelow?: boolean;40title: ReactNode;41}4243const SUBTITLE_STYLE: CSS = {44color: COLORS.GRAY_D,45textAlign: "center",46};4748function Logo({ logo, title }) {49if (!logo) return null;50if (typeof logo === "string" || logo.src != null) {51return <Image src={logo} style={{ width: "40%" }} alt={`${title} logo`} />;52} else {53return logo;54}55}5657export default function Content(props: Props) {58const {59aboveImage,60alignItems = "center",61alt,62caption,63description,64image,65imageAlternative,66landing = false, // for all pages on /landing/* – makes the splash content background at the top blue-ish67body,68startup,69style,70subtitle,71subtitleBelow = false,72title,73} = props;7475const { sandboxProjectId } = useCustomize();7677function renderIndexInfo() {78if (!imageAlternative) return;79if (typeof imageAlternative === "string") {80return (81<Col xs={24}>82<SanitizedMarkdown83value={imageAlternative}84style={{ padding: "20xp" }}85/>86</Col>87);88} else {89return <Col xs={24}>{imageAlternative}</Col>;90}91}9293function renderTitle() {94if (title)95return (96<Title level={2} style={{ color: COLORS.GRAY_DD }}>97{title}98</Title>99);100}101102function renderSubtitleTop() {103if (subtitleBelow) return;104return (105<Title level={4} style={SUBTITLE_STYLE}>106{typeof subtitle === "string" ? (107<StaticMarkdown value={subtitle} />108) : (109subtitle110)}111</Title>112);113}114115function renderSubtitleBelow() {116if (!subtitleBelow) return;117return (118<>119<Col xs={0} sm={4}></Col>120<Col xs={24} sm={16}>121<Title level={4} style={SUBTITLE_STYLE}>122{subtitle}123</Title>124</Col>125</>126);127}128129function renderImage() {130// if the index info is anything more than an empty string, we render this here instead131if (!!imageAlternative) return renderIndexInfo();132if (!image) return;133return (134<>135<Image136src={image}137priority={true}138style={{ paddingRight: "15px", paddingLeft: "15px" }}139alt={alt ?? `Image illustrating ${title}`}140/>141<Paragraph142style={{143textAlign: "center",144color: COLORS.GRAY_DD,145fontSize: "12pt",146}}147>148{caption}149</Paragraph>150</>151);152}153154function renderAboveImage() {155if (aboveImage != null) return aboveImage;156}157158function renderBelowImage() {159if (aboveImage == null && sandboxProjectId) {160return (161<div style={{ margin: "15px" }}>162<Path163style={{ marginBottom: "15px" }}164project_id={sandboxProjectId}165description="Public Sandbox"166/>167</div>168);169}170}171172function renderLogo() {173if (typeof body === "string" || (body as StaticImageData)?.src != null) {174return <Logo logo={body} title={title} />;175} else {176return <>{body}</>;177}178}179180return (181<div182style={{183...(landing && { backgroundColor: COLORS.LANDING.TOP_BG }),184...style,185}}186>187<Row188gutter={[20, 30]}189style={{190paddingTop: "12px",191maxWidth: MAX_WIDTH_LANDING,192marginTop: "0",193marginBottom: "0",194marginLeft: "auto",195marginRight: "auto",196}}197>198<Col199sm={10}200xs={24}201style={{202display: "flex",203alignItems: alignItems,204}}205>206<Space207size="large"208direction="vertical"209style={{ textAlign: "center", width: "100%" }}210>211{renderLogo()}212{renderTitle()}213{subtitle && renderSubtitleTop()}214{description && (215<Title level={4} style={{ color: COLORS.GRAY }}>216{description}217</Title>218)}219<div style={{ marginTop: "15px" }}>220<LiveDemo221context={222typeof title == "string" ? title : alt ?? "Feature Page"223}224/>225</div>226<SignIn startup={startup ?? title} hideFree={true} />227</Space>228</Col>229<Col sm={14} xs={24}>230{renderAboveImage()}231{renderImage()}232{renderBelowImage()}233</Col>234{subtitle && renderSubtitleBelow()}235</Row>236</div>237);238}239240241