+ {post.title} +
++ + {post.authors + .map((author, index) => + author.link ? ( + + {author.name} + + ) : ( + author.name + ) + ) + .reduce((prev, curr) => [prev, ' & ', curr] as any)} + +
+diff --git a/components/AuthorAvatars.tsx b/components/AuthorAvatars.tsx index 3ffb1be4207..03bf39dcb44 100644 --- a/components/AuthorAvatars.tsx +++ b/components/AuthorAvatars.tsx @@ -23,7 +23,9 @@ export default function AuthorAvatars({ authors = [] }: AuthorAvatarsProps) { 0 ? `left- absolute${index * 7} top-0` : `mr- relative${(authors.length - 1) * 7}`} z-${(authors.length - 1 - index) * 10} size-10 rounded-full border-2 border-white object-cover hover:z-50`} + className={`${index > 0 ? `left- absolute${index * 7} top-0` : `mr- relative${(authors.length - 1) * 7}`} + z-${(authors.length - 1 - index) * 10} size-10 rounded-full border-2 + border-white object-cover hover:z-50`} src={author.photo} loading='lazy' data-testid='AuthorAvatars-img' diff --git a/components/Caption.tsx b/components/Caption.tsx index 043db0b7a16..cae2cafd770 100644 --- a/components/Caption.tsx +++ b/components/Caption.tsx @@ -1,14 +1,14 @@ import React from 'react'; interface CaptionProps { - children: React.ReactNode; + children: string; } /** * @description This component displays textual captions. * * @param {CaptionProps} props - The props for the Caption component. - * @param {React.ReactNode} props.children - The content to be displayed as the caption. + * @param {string} props.children - The content to be displayed as the caption. */ export default function Caption({ children }: CaptionProps) { return ( diff --git a/components/Figure.tsx b/components/Figure.tsx new file mode 100644 index 00000000000..878258ce775 --- /dev/null +++ b/components/Figure.tsx @@ -0,0 +1,45 @@ +import { Float } from '@/types/components/FigurePropsType'; + +import Caption from './Caption'; + +interface FigureProps { + src: string; + caption: string; + widthClass: string; + className: string; + float: Float; + altOnly: string; + imageClass?: string; +} + +/** + * This component displays a figure with image and a caption. + * @param {FigureProps} props - The props for the Figure component + * @param {string} props.src - The source URL for the image + * @param {string} props.caption - The caption for the image + * @param {string} props.widthClass - Additional width classes for the figure + * @param {string} props.className - Additional classes for the figure + * @param {Float} props.float - The float direction of the figure (Float.LEFT or Float.RIGHT) + * @param {string} props.altOnly - The alternative text for the image if caption is not provided + * @param {string} props.imageClass - Additional classes for the image + */ +export default function Figure({ src, caption, widthClass, className, float, altOnly, imageClass = '' }: FigureProps) { + const alt = altOnly || caption; + + let floatClassNames = ''; + + if (float === Float.LEFT) { + floatClassNames = 'mr-4 float-left'; + } else if (float === Float.RIGHT) { + floatClassNames = 'ml-4 float-right'; + } + + return ( + + ); +} diff --git a/components/Head.tsx b/components/Head.tsx new file mode 100644 index 00000000000..65b8861c5c8 --- /dev/null +++ b/components/Head.tsx @@ -0,0 +1,88 @@ +import Head from 'next/head'; +import { useContext } from 'react'; +import ReactGA from 'react-ga'; +import TagManager from 'react-gtm-module'; + +import AppContext from '../context/AppContext'; + +interface IHeadProps { + title: string; + description?: string; + image: string; + rssTitle?: string; + rssLink?: string; +} + +/** + * @param {string} props.title - The title of the page + * @param {string} props.description - The description of the page + * @param {string} props.image - The image of the page + * @param {string} props.rssTitle - The RSS title of the page + * @param {string} props.rssLink - The RSS link of the page + * @description The head of the page with the meta tags + */ +export default function HeadComponent({ + title, + description = `Open source tools to easily build and maintain your event-driven architecture. + All powered by the AsyncAPI specification, the industry standard for defining asynchronous APIs.`, + image = '/img/social/website-card.jpg', + rssTitle = 'RSS Feed for AsyncAPI Initiative Blog', + rssLink = '/rss.xml' +}: IHeadProps) { + const url = process.env.DEPLOY_PRIME_URL || process.env.DEPLOY_URL || 'http://localhost:3000'; + const appContext = useContext(AppContext); + const { path = '' } = appContext || {}; + let currImage = image; + + const permalink = `${url}${path}`; + let type = 'website'; + + if (path.startsWith('/docs') || path.startsWith('/blog')) { + type = 'article'; + } + + if (!image.startsWith('http') && !image.startsWith('https')) { + currImage = `${url}${image}`; + } + + const permTitle = 'AsyncAPI Initiative for event-driven APIs'; + + const currTitle = title ? `${title} | ${permTitle}` : permTitle; + + // enable google analytics + if (typeof window !== 'undefined' && window.location.hostname.includes('asyncapi.com')) { + TagManager.initialize({ gtmId: 'GTM-T58BTVQ' }); + ReactGA.initialize('UA-109278936-1'); + ReactGA.pageview(window.location.pathname + window.location.search); + } + + return ( +
+ + + + + + + {/* Google / Search Engine Tags */} + + + + + {/* Twitter Card data */} + + + + + + {/* Open Graph data */} + + + + + + +Tags
+External Docs
+Tags
+External Docs
+Tags
+External Docs
+Tags
+External Docs
+{description}
++ AsyncAPI v3 has landed! ⭐️ +
++ + {post.authors + .map((author, index) => + author.link ? ( + + {author.name} + + ) : ( + author.name + ) + ) + .reduce((prev, curr) => [prev, ' & ', curr] as any)} + +
+