Skip to content

Commit

Permalink
Add ecosystem page (Components v7) (#6124)
Browse files Browse the repository at this point in the history
Co-authored-by: Dimitri POSTOLOV <dmytropostolov@gmail.com>
  • Loading branch information
hasparus and dimaMachina authored Dec 13, 2024
1 parent 6d78547 commit 8c2e2e3
Show file tree
Hide file tree
Showing 15 changed files with 399 additions and 59 deletions.
1 change: 1 addition & 0 deletions packages/web/docs/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export default withGuildDocs({
eslint: {
ignoreDuringBuilds: true,
},

redirects: async () => [
{
source: '/docs/get-started/organizations',
Expand Down
2 changes: 1 addition & 1 deletion packages/web/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"@radix-ui/react-tabs": "1.1.1",
"@radix-ui/react-tooltip": "1.1.3",
"@tailwindcss/typography": "0.5.15",
"@theguild/components": "7.3.0",
"@theguild/components": "7.4.0",
"clsx": "2.1.1",
"date-fns": "4.1.0",
"next": "14.2.18",
Expand Down
96 changes: 96 additions & 0 deletions packages/web/docs/src/components/ecosystem-page/components.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { DecorationIsolation, Heading, ProductCard, PRODUCTS } from '@theguild/components';
import EcosystemPageContent from './content.mdx';
import { EcosystemPageNavH2 } from './ecosystem-page-nav-h2';

export const components = {
EcosystemHeader: (props: React.HTMLAttributes<HTMLDivElement>) => (
<header
className="relative isolate flex max-w-[90rem] flex-col items-center gap-6 overflow-visible rounded-3xl bg-blue-400 px-4 py-6 text-center sm:py-12 md:gap-8 lg:py-24 [&>h1]:max-w-[800px] [&>p]:max-w-[520px]"
{...props}
>
<span className="font-medium">The Ecosystem</span>
{props.children}
<CrossDecoration />
<nav className="absolute top-full grid -translate-y-1/2 grid-flow-col rounded-2xl bg-blue-400 [grid-auto-columns:1fr]">
<EcosystemPageContent components={ecosystemPageNav} />
</nav>
</header>
),
h1: (props: React.HTMLAttributes<HTMLHeadingElement>) => <Heading as="h1" size="xl" {...props} />,
h2: (props: React.HTMLAttributes<HTMLHeadingElement>) => (
<Heading as="h2" size="xs" className="mb-4 mt-24" {...props} />
),
p: (props: React.HTMLAttributes<HTMLParagraphElement>) => (
<p className="text-green-800" {...props} />
),
ul: (props: React.HTMLAttributes<HTMLUListElement>) => {
return (
<ul
className="mt-5 grid grid-cols-4 gap-5 overflow-x-auto p-4 last-of-type:mb-24"
{...props}
/>
);
},
li: (props: React.LiHTMLAttributes<HTMLLIElement>) => {
const productName = String(props.children)
.toUpperCase()
.replace(' ', '_') as keyof typeof PRODUCTS;

const product = PRODUCTS[productName];
if (!product) {
throw new Error(`Product ${productName} is missing`);
}

return <ProductCard as="li" product={product} className="h-[222px]" {...props} />;
},
};

/**
* Take all sections headings and render them as navigation links
*/
const ecosystemPageNav = {
...Object.fromEntries(Object.keys(components).map(key => [key, () => null])),
h2: EcosystemPageNavH2,
};

function CrossDecoration() {
return (
<DecorationIsolation className="-z-10 *:absolute">
<ArchDecoration className="-left-10" />
<ArchDecoration className="-right-10 scale-x-[-1]" />
<ArchDecoration className="-right-10 bottom-0 rotate-180" />
<ArchDecoration className="-left-10 bottom-0 scale-y-[-1]" />
</DecorationIsolation>
);
}

function ArchDecoration({ className }: { className?: string }) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="569"
height="228"
viewBox="0 0 569 228"
fill="none"
className={className}
>
<path
d="M569 37.1357C569 49.5249 564.068 61.4232 555.302 70.1892L487.231 138.26L479.26 146.231L411.189 214.302C402.423 223.068 390.525 228 378.136 228L-16 228L-16 138.26L409.132 138.26C447.866 138.26 479.26 106.866 479.26 68.1321L479.26 3.92267e-06L569 0L569 37.1357Z"
fill="url(#paint0_linear_2003_9841)"
/>
<defs>
<linearGradient
id="paint0_linear_2003_9841"
x1="-16"
y1="178.5"
x2="293.759"
y2="370.89"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="white" stopOpacity="0.3" />
<stop offset="1" stopColor="white" stopOpacity="0.1" />
</linearGradient>
</defs>
</svg>
);
}
52 changes: 52 additions & 0 deletions packages/web/docs/src/components/ecosystem-page/content.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<EcosystemHeader>

# Everything you need to scale your API infrastructure

Enhance your GraphQL experience with advanced, modular solutions that can be adopted gradually as
individual open source libraries or as a complete unified API platform.

</EcosystemHeader>

## Schema Evolution

Our tools help you track changes, prevent breaking changes, and ensure backward compatibility as
your schema evolves. Through continuous monitoring and validation, you can confidently make schema
changes while maintaining API stability for your consumers.

- Hive
- Inspector
- Tools

## Gateway

Hive Gateway is a fully open-source, MIT-licensed GraphQL router that can act as a GraphQL
Federation gateway, a subgraph or a proxy gateway for any GraphQL API service.

- Hive Gateway

## Subgraph / Schema

Our schema tools empower you to build and manage GraphQL schemas with confidence and flexibility.
Whether you're building a new schema from scratch or integrating existing ones, our suite of
libraries provides the foundation for robust and maintainable GraphQL APIs.

- Yoga
- Mesh
- Sofa
- Envelop

## Developer Experience

Enhance your development workflow with tools that streamline GraphQL development, from code
generation to documentation and linting.

- Codegen
- ESLint
- Nextra

## Security

Reduce Cloud costs, handle traffic spikes, boost performance, get detailed observability, and secure
your API.

- Stellate
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use client';

export const EcosystemPageNavH2 = (props: React.HTMLAttributes<HTMLHeadingElement>) => (
<a
href={`#${props.id}`}
className="hive-focus focus-visible:text-green-1000 hover:text-green-1000 overflow-visible text-nowrap rounded-2xl px-4 py-5 font-medium text-green-800 transition hover:bg-white/10 focus:z-10 focus-visible:bg-white/10 focus-visible:ring-inset"
onKeyDown={event => {
if (event.key === 'ArrowLeft') {
const previousElement = event.currentTarget.previousElementSibling;
if (previousElement) {
(previousElement as HTMLElement).focus();
}
} else if (event.key === 'ArrowRight') {
const nextElement = event.currentTarget.nextElementSibling;
if (nextElement) {
(nextElement as HTMLElement).focus();
}
}
}}
>
{props.children}
</a>
);
6 changes: 6 additions & 0 deletions packages/web/docs/src/components/ecosystem-page/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
title: The Ecosystem
description: Everything you need to scale your API infrastructure
---

export { default } from './page'
13 changes: 13 additions & 0 deletions packages/web/docs/src/components/ecosystem-page/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { GotAnIdeaSection } from '../got-an-idea-section';
import { Page as LandingPageContainer } from '../page';
import { components } from './components';
import EcosystemPageContent from './content.mdx';

export default function EcosystemPage() {
return (
<LandingPageContainer className="text-green-1000 light mx-auto max-w-[90rem] overflow-hidden [&>:not(header)]:px-4 lg:[&>:not(header)]:px-8 xl:[&>:not(header)]:px-[120px]">
<EcosystemPageContent components={components} />
<GotAnIdeaSection />
</LandingPageContainer>
);
}
128 changes: 128 additions & 0 deletions packages/web/docs/src/components/got-an-idea-section.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { CallToAction, DecorationIsolation, Heading } from '@theguild/components';

export function GotAnIdeaSection() {
return (
<div className="relative flex flex-col items-center rounded-3xl bg-[#003834] px-4 py-6 lg:px-8 lg:py-16 xl:p-24">
<DecorationIsolation>
<svg
className="absolute right-[481px] top-[-29px] overflow-visible"
width="930"
height="373"
viewBox="0 0 930 373"
fill="none"
>
<g opacity="0.5" filter="url(#filter0_f_2003_9852)">
<path
d="M489.136 401C501.525 401 513.423 396.068 522.189 387.302L590.26 319.231L598.231 311.26L666.302 243.189C675.068 234.423 680 222.525 680 210.136L680 -29L590.26 -29L590.26 241.132C590.26 279.866 558.866 311.26 520.132 311.26L250 311.26L250 401L489.136 401Z"
fill="#A2C1C4"
/>
</g>
<defs>
<filter
id="filter0_f_2003_9852"
x="0"
y="-279"
width="930"
height="930"
filterUnits="userSpaceOnUse"
colorInterpolationFilters="sRGB"
>
<feFlood floodOpacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="125" result="effect1_foregroundBlur_2003_9852" />
</filter>
</defs>
</svg>
<svg
width="1392"
height="373"
viewBox="0 0 1392 373"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M530.948 3.83562C539.62 -4.8367 544.5 -16.6079 544.5 -28.8644L544.5 -65.5L455.76 -65.5L455.76 2.1321C455.76 41.1421 424.142 72.7599 385.132 72.7599L-39.5 72.7599L-39.5 161.5L354.136 161.5C366.392 161.5 378.163 156.62 386.836 147.948L454.906 79.8775L462.877 71.9063L530.948 3.83562Z"
stroke="url(#paint0_linear_2003_9879)"
/>
<path
d="M861.052 3.83562C852.38 -4.8367 847.5 -16.6079 847.5 -28.8644L847.5 -65.5L936.24 -65.5L936.24 2.1321C936.24 41.1421 967.858 72.7599 1006.87 72.7599L1431.5 72.7599L1431.5 161.5L1037.86 161.5C1025.61 161.5 1013.84 156.62 1005.16 147.948L937.094 79.8775L929.123 71.9063L861.052 3.83562Z"
stroke="url(#paint1_linear_2003_9879)"
/>
<path
d="M530.948 368.164C539.62 376.837 544.5 388.608 544.5 400.864L544.5 437.5L455.76 437.5L455.76 369.868C455.76 330.858 424.142 299.24 385.132 299.24L-39.5 299.24L-39.5 210.5L354.136 210.5C366.392 210.5 378.163 215.38 386.836 224.052L454.906 292.123L462.877 300.094L530.948 368.164Z"
stroke="url(#paint2_linear_2003_9879)"
/>
<path
d="M861.052 368.164C852.38 376.837 847.5 388.608 847.5 400.864L847.5 437.5L936.24 437.5L936.24 369.868C936.24 330.858 967.858 299.24 1006.87 299.24L1431.5 299.24L1431.5 210.5L1037.86 210.5C1025.61 210.5 1013.84 215.38 1005.16 224.052L937.094 292.123L929.123 300.094L861.052 368.164Z"
stroke="url(#paint3_linear_2003_9879)"
/>
<defs>
<linearGradient
id="paint0_linear_2003_9879"
x1="252.5"
y1="48"
x2="329.643"
y2="245.934"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#A2C1C4" stopOpacity="0" />
<stop offset="1" stopColor="#A2C1C4" stopOpacity="0.8" />
</linearGradient>
<linearGradient
id="paint1_linear_2003_9879"
x1="1139.5"
y1="48"
x2="1062.36"
y2="245.934"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#A2C1C4" stopOpacity="0" />
<stop offset="1" stopColor="#A2C1C4" stopOpacity="0.8" />
</linearGradient>
<linearGradient
id="paint2_linear_2003_9879"
x1="252.5"
y1="324"
x2="329.643"
y2="126.066"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#A2C1C4" stopOpacity="0" />
<stop offset="1" stopColor="#A2C1C4" stopOpacity="0.8" />
</linearGradient>
<linearGradient
id="paint3_linear_2003_9879"
x1="1139.5"
y1="324"
x2="1062.36"
y2="126.066"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#A2C1C4" stopOpacity="0" />
<stop offset="1" stopColor="#A2C1C4" stopOpacity="0.8" />
</linearGradient>
</defs>
</svg>
</DecorationIsolation>
<Heading as="h2" size="md" className="text-white">
Got an idea for a new library?
</Heading>
<p className="mt-4 text-white/80">
Join our community to chat with us and let's build something together!
</p>
<CallToAction
href="https://the-guild.dev/contact"
variant="primary-inverted"
className="mt-8"
onClick={event => {
if (window.$crisp) {
event.preventDefault();
window.$crisp?.push(['do', 'chat:open']);
}
}}
>
Get in touch
</CallToAction>
</div>
);
}
2 changes: 1 addition & 1 deletion packages/web/docs/src/components/navigation-menu/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ export function NavigationMenu(props: ComponentPropsWithoutRef<typeof Navbar>) {
);
}

const landingLikePages = ['/', '/pricing', '/federation', '/oss-friends'];
const landingLikePages = ['/', '/pricing', '/federation', '/oss-friends', '/ecosystem'];
export const isLandingPage = (route: string) => landingLikePages.includes(route);
11 changes: 6 additions & 5 deletions packages/web/docs/src/components/product-updates.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,22 @@ export async function getChangelogs(): Promise<Changelog[]> {
return productUpdatesFolder
.slice(1) // cut `_meta.ts` which always comes first
.map(item => {
if (!item.children) {
if (!('children' in item)) {
if (!('title' in item.frontMatter!)) {
throw new Error(`Incorrect Front matter on page ${item.route}`);
}

// Regular mdx page
return {
title: item.frontMatter.title,
date: item.frontMatter.date.toISOString(),
description: item.frontMatter.description,
title: item.frontMatter.title || '',
date: 'date' in item.frontMatter ? item.frontMatter.date.toISOString() : '',
description: item.frontMatter.description || '',
route: item.route!,
};
}
// Folder
const indexPage = item.children.find(item => item.name === 'index');
const indexPage = 'children' in item && item.children?.find(item => item.name === 'index');

if (!indexPage) {
throw new Error('Changelog folder must have an "index.mdx" page');
}
Expand Down
5 changes: 5 additions & 0 deletions packages/web/docs/src/env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
interface Window {
$crisp?: {
push: (args: any[]) => void;
};
}
8 changes: 8 additions & 0 deletions packages/web/docs/src/pages/_meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ const meta: Record<string, DeepPartial<Item | MenuItem | PageItem>> = {
layout: 'raw',
},
},
ecosystem: {
title: 'Ecosystem',
type: 'page',
display: 'hidden',
theme: {
layout: 'raw',
},
},
products: {
title: 'Products',
type: 'menu',
Expand Down
Loading

0 comments on commit 8c2e2e3

Please sign in to comment.