import * as React from "react"; import { graphql, Link, PageRendererProps } from "gatsby"; import { FixedObject, FluidObject } from "gatsby-image"; import styled from "@emotion/styled"; import { Hyperbutton, Card } from "starstuff-components"; import { WebSite, BlogPosting, Blog } from "schema-dts"; import { JsonLd } from "react-schemaorg"; import { usePlugin } from "tinacms"; import { useLocalJsonForm } from "gatsby-tinacms-json"; import { CreatePostPlugin } from "gatsby-theme-early-bird/src/lib/tinacms-creator-plugin"; import BaseLayout from "../components/BaseLayout"; import { Avatar } from "../components/Avatar"; import { BannerBackground } from "../components/BannerBackground"; import { SimpleRemarkSection, KeplerSimpleRemarkSection, } from "../components/SimpleRemarkSection"; import { NavigationLinksForm } from "../lib/NavigationLinksForm"; import { ImageLD } from "../components/GatsbyImageWithLD"; const HeroBackground = styled(BannerBackground)` min-height: 66vh; `; const Hero = styled.section` min-height: 66vh; backdrop-filter: blur(20px); align-items: stretch; display: flex; flex-direction: column; justify-content: space-between; `; const Centered = styled.div` margin-left: auto; margin-right: auto; `; const Name = styled.h1` font-size: 2.5rem; font-weight: 600; line-height: 1.125; margin-bottom: 1.5rem; `; const Container = styled.div` text-align: center !important; flex-grow: 1; flex-shrink: 1; margin: 0 auto; position: relative; width: auto; @media screen and (min-width: 1024px) { max-width: 960px; } @media screen and (min-width: 1216px) { max-width: 1152px; } `; const HeroBody = styled.div` align-items: center; display: flex; flex-grow: 1; flex-shrink: 0; padding: 3rem 1.5rem; color: #fff; `; const Columns = styled.div` align-items: center; margin-left: -0.75rem; margin-right: -0.75rem; margin-top: -0.75rem; :last-child { margin-bottom: -0.75rem; } `; const CenteredColumn = styled(Centered)` display: block; padding: 0.75rem; flex: none; width: 66.6666%; `; const FeaturedContentContainer = styled.section` padding: 1em; @media screen and (min-width: 512px) { padding: 1em 2.5em; } @media screen and (min-width: 1024px) { padding: 1em 5em; } `; const ContentCarousel = styled.div` display: flex; overflow: scroll; `; const Featured = styled(Card)` min-width: 20em; max-width: 25vw; `; const FeaturedImage = styled(ImageLD)` .gatsby-image-wrapper { border-top-left-radius: 10px; border-top-right-radius: 10px; } `; const FeaturedContent = styled.div` display: flex; flex-direction: column; padding: 0.5em; `; const FlexReadMoreLink = styled(Link)` align-self: flex-end; `; const LocalButton = Hyperbutton.withComponent(Link); const StyledLocalButton = styled(LocalButton)` color: #fff; `; export interface IndexPageData { site: { siteMetadata: { title: string; description: string; siteUrl: string; social: { name: string; id: string; url: string; isProfile?: boolean; }[]; }; }; settingsJson: { navLinks: { name: string; url: string; }[]; id: string; rawJson: string; fileRelativePath: string; }; biography: KeplerSimpleRemarkSection; metadataImage: { childImageSharp: { fixed: FixedObject; }; }; blogPosts: { edges: { node: { id: string; title: string; slug: string; date: string; rawDate: string; excerpt: string; featuredImage?: { childImageSharp: { sqip: { dataURI: string }; fluid: FluidObject; }; }; }; }[]; }; } interface Props extends PageRendererProps { data: IndexPageData; } const IndexPage = ({ data, location }: Props): React.ReactElement => { const [nav] = useLocalJsonForm(data.settingsJson as any, NavigationLinksForm); usePlugin(CreatePostPlugin); const email = data.site.siteMetadata.social.find( (social): boolean => social.name === "Email" ); return ( item={{ "@context": "https://schema.org", "@type": "WebSite", name: data.site.siteMetadata.title, description: data.site.siteMetadata.description, url: data.site.siteMetadata.siteUrl, about: { "@type": "Person", name: data.site.siteMetadata.title, description: data.site.siteMetadata.description, url: data.site.siteMetadata.siteUrl, email: email?.id, sameAs: data.site.siteMetadata.social .filter((platform): boolean => platform.isProfile === true) .map((platform): string => platform.url), image: data.metadataImage.childImageSharp.fixed.src, }, }} /> {data.site.siteMetadata.title} {nav.navLinks.map( (link): React.ReactElement => ( {link.name} ) )} Read More... item={{ "@context": "https://schema.org", "@type": "Blog", "@id": `${data.site.siteMetadata.siteUrl}/blog`, url: `${data.site.siteMetadata.siteUrl}/blog`, mainEntityOfPage: `${data.site.siteMetadata.siteUrl}/blog`, blogPost: data.blogPosts.edges.map( ({ node: post }): BlogPosting => ({ "@type": "BlogPosting", "@id": `${data.site.siteMetadata.siteUrl}${post.slug}`, }) ), }} />

Blog Posts

{data.blogPosts.edges.map(({ node: post }) => ( item={{ "@context": "https://schema.org", "@type": "BlogPosting", "@id": `${data.site.siteMetadata.siteUrl}${post.slug}`, datePublished: post.rawDate, headline: post.title, name: post.title, url: `${data.site.siteMetadata.siteUrl}${post.slug}`, mainEntityOfPage: `${data.site.siteMetadata.siteUrl}${post.slug}`, image: post.featuredImage && { "@type": "ImageObject", "@id": `${data.site.siteMetadata.siteUrl}${post.featuredImage.childImageSharp.fluid.src}`, }, }} /> {post.featuredImage && ( )}

{post.title}

{post.excerpt}

Read More...
))}
); }; export default IndexPage; export const query = graphql` query KeplerIndexPageData { site { siteMetadata { title description siteUrl social { name id url isProfile } } } settingsJson { navLinks { url name } id rawJson fileRelativePath } metadataImage: file( relativePath: { eq: "avatar.jpg" } sourceInstanceName: { eq: "assets" } ) { childImageSharp { # Specify the image processing specifications right in the query. # Makes it trivial to update as your page's design changes. fixed(width: 480, height: 480) { ...GatsbyImageSharpFixed } } } biography: file( sourceInstanceName: { eq: "about" } relativePath: { eq: "biography.md" } ) { ...KeplerSimpleRemarkSection } blogPosts: allBlogPost(sort: { order: DESC, fields: date }) { edges { node { id title slug date(formatString: "MMMM DD, YYYY") rawDate: date ... on RemarkBlogPost { excerpt(pruneLength: 140) } featuredImage { childImageSharp { sqip(numberOfPrimitives: 100, blur: 0) { dataURI } # Generate Picture up to 8K 4:3 ratio, crop and cover as appropriate fluid( maxWidth: 7680 maxHeight: 5760 cropFocus: CENTER fit: COVER srcSetBreakpoints: [ 256 512 768 1024 # 720p 1280 # 1080p 1920 # 4k 3840 # 5k 5120 # 8k 7680 ] ) { ...GatsbyImageSharpFluid_withWebp_noBase64 } } } } } } } `;