post.tsx (7777B)
1 /** 2 Copyright 2021 Forestry.io Holdings, Inc. 3 Licensed under the Apache License, Version 2.0 (the "License"); 4 you may not use this file except in compliance with the License. 5 You may obtain a copy of the License at 6 http://www.apache.org/licenses/LICENSE-2.0 7 Unless required by applicable law or agreed to in writing, software 8 distributed under the License is distributed on an "AS IS" BASIS, 9 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 See the License for the specific language governing permissions and 11 limitations under the License. 12 */ 13 14 import React from "react"; 15 import { Container } from "../util/container"; 16 import { Section } from "../util/section"; 17 import { useTheme } from "../layout"; 18 import {format} from "date-fns"; 19 import { TinaMarkdown } from "tinacms/dist/rich-text"; 20 import { Prism } from "tinacms/dist/rich-text/prism"; 21 import type { TinaMarkdownContent, Components } from "tinacms/dist/rich-text"; 22 import { tinaField } from "tinacms/dist/react"; 23 import Image from "next/image"; 24 import { PostType } from "../../pages/posts/[...filename]"; 25 26 const components: Components<{ 27 BlockQuote: { 28 children: TinaMarkdownContent; 29 authorName: string; 30 }; 31 DateTime: { 32 format?: string; 33 }; 34 NewsletterSignup: { 35 placeholder: string; 36 buttonText: string; 37 children: TinaMarkdownContent; 38 disclaimer?: TinaMarkdownContent; 39 }; 40 }> = { 41 code_block: (props) => <Prism {...props} />, 42 BlockQuote: (props: { 43 children: TinaMarkdownContent; 44 authorName: string; 45 }) => { 46 return ( 47 <div> 48 <blockquote> 49 <TinaMarkdown content={props.children} /> 50 {props.authorName} 51 </blockquote> 52 </div> 53 ); 54 }, 55 DateTime: (props) => { 56 const dt = React.useMemo(() => { 57 return new Date(); 58 }, []); 59 60 switch (props.format) { 61 case "iso": 62 return <span>{format(dt, "yyyy-MM-dd")}</span>; 63 case "utc": 64 return <span>{format(dt, "eee, dd MMM yyyy HH:mm:ss OOOO")}</span>; 65 case "local": 66 return <span>{format(dt, "P")}</span>; 67 default: 68 return <span>{format(dt, "P")}</span>; 69 } 70 }, 71 NewsletterSignup: (props) => { 72 return ( 73 <div className="bg-white"> 74 <div className="max-w-7xl mx-auto py-8 px-4 sm:px-6 lg:px-8"> 75 <div className=""> 76 <TinaMarkdown content={props.children} /> 77 </div> 78 <div className="mt-8 "> 79 <form className="sm:flex"> 80 <label htmlFor="email-address" className="sr-only"> 81 Email address 82 </label> 83 <input 84 id="email-address" 85 name="email-address" 86 type="email" 87 autoComplete="email" 88 required 89 className="w-full px-5 py-3 border border-gray-300 shadow-sm placeholder-gray-400 focus:ring-1 focus:ring-teal-500 focus:border-teal-500 sm:max-w-xs rounded-md" 90 placeholder={props.placeholder} 91 /> 92 <div className="mt-3 rounded-md shadow sm:mt-0 sm:ml-3 sm:flex-shrink-0"> 93 <button 94 type="submit" 95 className="w-full flex items-center justify-center py-3 px-5 border border-transparent text-base font-medium rounded-md text-white bg-teal-600 hover:bg-teal-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-teal-500" 96 > 97 {props.buttonText} 98 </button> 99 </div> 100 </form> 101 <div className="mt-3 text-sm text-gray-500"> 102 {props.disclaimer && <TinaMarkdown content={props.disclaimer} />} 103 </div> 104 </div> 105 </div> 106 </div> 107 ); 108 }, 109 img: (props) => ( 110 <span className="flex items-center justify-center"> 111 <Image src={props.url} alt={props.alt} width={500} height={500} /> 112 </span> 113 ), 114 }; 115 116 export const Post = (props: PostType) => { 117 const theme = useTheme(); 118 const titleColorClasses = { 119 blue: "from-blue-400 to-blue-600 dark:from-blue-300 dark:to-blue-500", 120 teal: "from-teal-400 to-teal-600 dark:from-teal-300 dark:to-teal-500", 121 green: "from-green-400 to-green-600", 122 red: "from-red-400 to-red-600", 123 pink: "from-pink-300 to-pink-500", 124 purple: 125 "from-purple-400 to-purple-600 dark:from-purple-300 dark:to-purple-500", 126 orange: 127 "from-orange-300 to-orange-600 dark:from-orange-200 dark:to-orange-500", 128 yellow: 129 "from-yellow-400 to-yellow-500 dark:from-yellow-300 dark:to-yellow-500", 130 }; 131 132 const date = new Date(props.date); 133 let formattedDate = ""; 134 if (!isNaN(date.getTime())) { 135 formattedDate = format(date, "MMM dd, yyyy"); 136 } 137 138 return ( 139 <Section className="flex-1"> 140 <Container width="small" className={`flex-1 pb-2`} size="large"> 141 <h2 142 data-tina-field={tinaField(props, "title")} 143 className={`w-full relative mb-8 text-6xl font-extrabold tracking-normal text-center title-font`} 144 > 145 <span 146 className={`bg-clip-text text-transparent bg-gradient-to-r ${ 147 titleColorClasses[theme.color] 148 }`} 149 > 150 {props.title} 151 </span> 152 </h2> 153 <div 154 data-tina-field={tinaField(props, "author")} 155 className="flex items-center justify-center mb-16" 156 > 157 {props.author && ( 158 <> 159 <div className="flex-shrink-0 mr-4"> 160 <Image 161 data-tina-field={tinaField(props.author, "avatar")} 162 className="h-14 w-14 object-cover rounded-full shadow-sm" 163 src={props.author.avatar} 164 alt={props.author.name} 165 width={500} 166 height={500} 167 /> 168 </div> 169 <p 170 data-tina-field={tinaField(props.author, "name")} 171 className="text-base font-medium text-gray-600 group-hover:text-gray-800 dark:text-gray-200 dark:group-hover:text-white" 172 > 173 {props.author.name} 174 </p> 175 <span className="font-bold text-gray-200 dark:text-gray-500 mx-2"> 176 — 177 </span> 178 </> 179 )} 180 <p 181 data-tina-field={tinaField(props, "date")} 182 className="text-base text-gray-400 group-hover:text-gray-500 dark:text-gray-300 dark:group-hover:text-gray-150" 183 > 184 {formattedDate} 185 </p> 186 </div> 187 </Container> 188 {props.heroImg && ( 189 <div className="px-4 w-full"> 190 <div 191 data-tina-field={tinaField(props, "heroImg")} 192 className="relative max-w-4xl lg:max-w-5xl mx-auto" 193 > 194 <Image 195 src={props.heroImg} 196 alt={props.title} 197 className="absolute block rounded-lg w-full h-auto blur-2xl brightness-150 contrast-[0.9] dark:brightness-150 saturate-200 opacity-50 dark:opacity-30 mix-blend-multiply dark:mix-blend-hard-light" 198 aria-hidden="true" 199 width={500} 200 height={500} 201 /> 202 <Image 203 src={props.heroImg} 204 alt={props.title} 205 width={500} 206 height={500} 207 className="relative z-10 mb-14 block rounded-lg w-full h-auto opacity-100" 208 /> 209 </div> 210 </div> 211 )} 212 <Container className={`flex-1 pt-4`} width="small" size="large"> 213 <div 214 data-tina-field={tinaField(props, "_body")} 215 className="prose dark:prose-dark w-full max-w-none" 216 > 217 <TinaMarkdown components={components} content={props._body} /> 218 </div> 219 </Container> 220 </Section> 221 ); 222 };