2
0

📝 Improve blog capabilities and components

This commit is contained in:
Baptiste Arnaud
2024-04-20 18:21:06 +02:00
parent e4e724d4a1
commit eb2001c06b
11 changed files with 285 additions and 11 deletions

View File

@ -36,3 +36,17 @@ summary: 'A short summary of the blog post.'
```
By default the og image is generated from the title of the blog post. If you want to use a custom og image, you can specify a `image` field in the frontmatter.
All images need to be placed under the `public/images/blog/<POST_FILENAME>` folder where `<POST_FILENAME>` is the name of the mdx file you are creating/editing.
Here are all the components you can use in your blog post:
- `Image`: To display an image. Example: `<Image src="/images/blog/my-awesome-blog-post/image.jpg" alt="My awesome image" />`
- `Callout`: To display a callout. Example: `<Callout status="info">This is an info callout</Callout>`. You can provide a `status` prop with the value `info`, `warning`, `success`, or `error`.
- `Tweet`: To embed a tweet. Example: `<Tweet id="1234567890123456789" />`
- `Typebot`: To embed a typebot as a Standard component. Example: `<Typebot typebot="<YOUR_BOT_PUBLIC_ID>" />`. You can provide the same props as the [Standard component](../../deploy/web/libraries/react#standard).
- `YouTube`: To embed a YouTube video. Example: `<YouTube id="<YOUTUBE_VIDEO_ID>" />`
- `Loom`: To embed a Loom video. Example: `<Loom id="<LOOM_VIDEO_ID>" />`
- `Cta`: To display a call-to-action that redirects to Typebot. Example: `<Cta />`
For rendering tables you should use the native html table related tags.

View File

@ -12033,6 +12033,9 @@
"content": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"maxBubbleWidth": {
"type": "number"
},

View File

@ -1,9 +1,29 @@
/* eslint-disable jsx-a11y/alt-text */
'use client'
import { Link } from '@chakra-ui/next-js'
import { Heading, Stack, Text } from '@chakra-ui/react'
import {
Alert,
AlertIcon,
Heading,
Stack,
Table,
TableCaption,
TableContainer,
Tbody,
Td,
Text,
Tfoot,
Th,
Thead,
Tr,
Image,
} from '@chakra-ui/react'
import { MDXRemote, MDXRemoteSerializeResult } from 'next-mdx-remote'
import { highlight } from 'sugar-high'
import { Tweet } from './Tweet'
import { Standard } from '@typebot.io/nextjs'
import { EndCta } from '@/components/Homepage/EndCta'
type Props = {
metadata: {
@ -14,7 +34,7 @@ type Props = {
}
export const Post = ({ metadata, mdxSource }: Props) => (
<Stack spacing={10} my="20">
<Stack spacing={10} my="20" w="full">
<Stack mx="auto" w="full" maxW="65ch">
<Heading>{metadata.title}</Heading>
<Text>{formatDate(metadata.publishedAt)}</Text>
@ -23,7 +43,7 @@ export const Post = ({ metadata, mdxSource }: Props) => (
mx="auto"
spacing={0}
as="article"
className="prose prose-quoteless prose-neutral prose-invert"
className="prose prose-quoteless prose-neutral prose-invert max-w-none w-full px-3 sm:px-0"
>
<MDXRemote
{...mdxSource}
@ -42,6 +62,87 @@ export const Post = ({ metadata, mdxSource }: Props) => (
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any
link: (props: any) => <Link {...props} />,
Image: (props) => (
<Image rounded="md" maxW={['full', '65ch']} {...props} />
),
Callout: ({ children, ...props }) => (
<Alert rounded="md" {...props}>
<AlertIcon />
{children}
</Alert>
),
Tweet,
Typebot: Standard,
Youtube: ({ id }: { id: string }) => (
<div className="w-full">
<div
style={{
position: 'relative',
paddingBottom: '64.63195691202873%',
height: 0,
width: '100%',
}}
>
<iframe
src={`https://www.youtube.com/embed/${id}`}
allowFullScreen
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
}}
></iframe>
</div>
</div>
),
Loom: ({ id }: { id: string }) => (
<div className="w-full">
<div
style={{
position: 'relative',
paddingBottom: '64.63195691202873%',
height: 0,
width: '100%',
}}
>
<iframe
src={`https://www.loom.com/embed/${id}`}
allowFullScreen
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
}}
></iframe>
</div>
</div>
),
Cta: (props) => (
<EndCta
{...props}
style={{ maxWidth: 'none' }}
w="full"
height="70vh"
className="w-full"
bgGradient={undefined}
/>
),
table: (props) => (
<TableContainer>
<Table {...props} />
</TableContainer>
),
thead: Thead,
tbody: Tbody,
th: Th,
td: Td,
tfoot: Tfoot,
tr: Tr,
caption: TableCaption,
}}
/>
</Stack>

View File

@ -0,0 +1,36 @@
import { getTweet } from 'react-tweet/api'
import { EmbeddedTweet, TweetNotFound, type TweetProps } from 'react-tweet'
import './tweet.css'
const TweetContent = async ({ id, components, onError }: TweetProps) => {
let error
const tweet = id
? await getTweet(id).catch((err) => {
if (onError) {
error = onError(err)
} else {
console.error(err)
error = err
}
})
: undefined
if (!tweet) {
const NotFound = components?.TweetNotFound || TweetNotFound
return <NotFound error={error} />
}
return <EmbeddedTweet tweet={tweet} components={components} />
}
export const ReactTweet = (props: TweetProps) => <TweetContent {...props} />
export async function Tweet({ id }: { id: string }) {
return (
<div className="tweet my-6">
<div className={`flex justify-center`}>
<ReactTweet id={id} />
</div>
</div>
)
}

View File

@ -0,0 +1,63 @@
/* Light theme (default) */
.tweet .react-tweet-theme {
/* margin is handled by our wrappers */
--tweet-container-margin: 0;
--tweet-font-family: inherit;
--tweet-font-color: inherit;
--tweet-bg-color: #222;
--tweet-bg-color-hover: var(--tweet-bg-color);
--tweet-quoted-bg-color-hover: rgba(255, 255, 255, 0.03);
--tweet-border: 1px solid #333;
--tweet-color-blue-secondary: theme('colors.white');
--tweet-color-blue-secondary-hover: #333;
--tweet-font-color-secondary: theme('colors.gray.400');
/* Common properties for both themes */
--tweet-quoted-bg-color-hover: rgba(0, 0, 0, 0.03);
--tweet-border: 1px solid rgb(64, 64, 64);
--tweet-skeleton-gradient: linear-gradient(
270deg,
#fafafa,
#eaeaea,
#eaeaea,
#fafafa
);
--tweet-color-red-primary: rgb(249, 24, 128);
--tweet-color-red-primary-hover: rgba(249, 24, 128, 0.1);
--tweet-color-green-primary: rgb(0, 186, 124);
--tweet-color-green-primary-hover: rgba(0, 186, 124, 0.1);
--tweet-twitter-icon-color: var(--tweet-font-color);
--tweet-verified-old-color: rgb(130, 154, 171);
--tweet-verified-blue-color: var(--tweet-color-blue-primary);
--tweet-actions-font-weight: 500;
--tweet-replies-font-weight: 500;
}
/* Common styles for both themes */
.tweet .react-tweet-theme p {
font-size: inherit;
line-height: 1.3rem;
}
.tweet .react-tweet-theme p a {
@apply border-b transition-[border-color] border-gray-500 text-white hover:border-white;
}
/* Remove link underline on hover for both themes */
.tweet .react-tweet-theme p a:hover {
text-decoration: none;
}
.tweet a div {
@apply font-medium tracking-tight;
}
.tweet div[class*='mediaWrapper'] {
max-height: 250px;
}
.tweet .react-tweet-theme img {
margin: 0;
}

View File

@ -4,7 +4,6 @@ import type { Metadata } from 'next'
import { Header } from 'components/common/Header/Header'
import { Footer } from 'components/common/Footer'
import { Providers } from './providers'
import { EndCta } from '@/components/Homepage/EndCta'
import 'assets/style.css'
export const metadata: Metadata = {
@ -33,7 +32,6 @@ export default function RootLayout({
<Providers>
<Header />
{children}
<EndCta />
<Footer />
</Providers>
</body>

View File

@ -13,3 +13,9 @@
--sh-keyword: #f47067;
--sh-comment: #a19595;
}
.prose > * {
max-width: 65ch;
margin-left: auto;
margin-right: auto;
}

View File

@ -1,11 +1,18 @@
'use client'
import { Heading, Button, Text, Flex, VStack } from '@chakra-ui/react'
import {
Heading,
Button,
Text,
Flex,
VStack,
StackProps,
} from '@chakra-ui/react'
import Link from 'next/link'
import React from 'react'
import { BackgroundPolygons } from './Hero/BackgroundPolygons'
export const EndCta = () => {
export const EndCta = (props: StackProps) => {
return (
<VStack
as="section"
@ -14,6 +21,7 @@ export const EndCta = () => {
bgGradient="linear(to-b, gray.900, gray.800)"
height="100vh"
justifyContent="center"
{...props}
>
<BackgroundPolygons />
<VStack
@ -29,7 +37,7 @@ export const EndCta = () => {
letterSpacing="tight"
data-aos="fade-up"
>
Take your forms to the next level
Improve conversion and user engagement with typebots
</Heading>
<Flex>
<Button
@ -46,7 +54,7 @@ export const EndCta = () => {
</Flex>
<Text color="gray.400" data-aos="fade-up" data-aos-delay="400">
No trial. Generous, unlimited <strong>free</strong> plan.
No trial. Generous <strong>free</strong> plan.
</Text>
</VStack>
</VStack>

View File

@ -3,6 +3,18 @@ title: 'Blog post example'
summary: 'A short summary of the blog post.'
---
This is a blog post example.
<Image src="/images/builder-screenshot.png" alt="awesome image" />
This can be deleted once we published the first blog post.
<Callout rounded='md'>
This is a callout. It can be used to highlight important information.
</Callout>
<Tweet id="1780513200565285038" />
Occaecat nostrud fugiat aliqua non deserunt ad adipisicing amet anim do commodo aliquip ipsum esse. Aute nisi voluptate nisi excepteur nulla velit incididunt aute laborum culpa Lorem magna reprehenderit. Aliqua cupidatat nisi ut nisi. Cillum irure enim officia aute nulla. Sunt culpa deserunt sunt. Laborum ipsum ad proident cillum officia culpa enim do id quis eiusmod.
<Cta />
Occaecat nostrud fugiat aliqua non deserunt ad adipisicing amet anim do commodo aliquip ipsum esse. Aute nisi voluptate nisi excepteur nulla velit incididunt aute laborum culpa Lorem magna reprehenderit. Aliqua cupidatat nisi ut nisi. Cillum irure enim officia aute nulla. Sunt culpa deserunt sunt. Laborum ipsum ad proident cillum officia culpa enim do id quis eiusmod.

View File

@ -26,6 +26,7 @@
"next-mdx-remote": "4.4.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-tweet": "3.2.1",
"sugar-high": "0.6.0"
},
"devDependencies": {

32
pnpm-lock.yaml generated
View File

@ -508,6 +508,9 @@ importers:
react-dom:
specifier: 18.2.0
version: 18.2.0(react@18.2.0)
react-tweet:
specifier: ^3.2.1
version: 3.2.1(react-dom@18.2.0)(react@18.2.0)
sugar-high:
specifier: 0.6.0
version: 0.6.0
@ -9302,6 +9305,12 @@ packages:
resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
dev: false
/@swc/helpers@0.5.10:
resolution: {integrity: sha512-CU+RF9FySljn7HVSkkjiB84hWkvTaI3rtLvF433+jRSBL2hMu3zX5bGhHS8C80SM++h4xy8hBSnUHFQHmRXSBw==}
dependencies:
tslib: 2.6.0
dev: false
/@swc/helpers@0.5.2:
resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==}
dependencies:
@ -20347,6 +20356,19 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/react-tweet@3.2.1(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-dktP3RMuwRB4pnSDocKpSsW5Hq1IXRW6fONkHhxT5EBIXsKZzdQuI70qtub1XN2dtZdkJWWxfBm/Q+kN+vRYFA==}
peerDependencies:
react: '>= 18.0.0'
react-dom: '>= 18.0.0'
dependencies:
'@swc/helpers': 0.5.10
clsx: 2.0.0
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
swr: 2.2.5(react@18.2.0)
dev: false
/react-universal-interface@0.6.2(react@18.2.0)(tslib@2.6.0):
resolution: {integrity: sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw==}
peerDependencies:
@ -21747,6 +21769,16 @@ packages:
use-sync-external-store: 1.2.0(react@18.2.0)
dev: false
/swr@2.2.5(react@18.2.0):
resolution: {integrity: sha512-QtxqyclFeAsxEUeZIYmsaQ0UjimSq1RZ9Un7I68/0ClKK/U3LoyQunwkQfJZr2fc22DfIXLNDc2wFyTEikCUpg==}
peerDependencies:
react: ^16.11.0 || ^17.0.0 || ^18.0.0
dependencies:
client-only: 0.0.1
react: 18.2.0
use-sync-external-store: 1.2.0(react@18.2.0)
dev: false
/swrev@4.0.0:
resolution: {integrity: sha512-LqVcOHSB4cPGgitD1riJ1Hh4vdmITOp+BkmfmXRh4hSF/t7EnS4iD+SOTmq7w5pPm/SiPeto4ADbKS6dHUDWFA==}
dev: false