Skip to content

Add the wires / API gateway section #39

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 23 commits into
base: new-landing--data-colocation
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/_design-system/syntax/light.json
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,16 @@
"settings": {
"foreground": "#6E7557"
}
},
{
"scope": [
"keyword.type.graphql",
"keyword.operation.graphql",
"keyword.fragment.graphql"
],
"settings": {
"foreground": "#990069"
}
}
]
}
7 changes: 4 additions & 3 deletions src/app/conf/_design-system/utils/useOnClickOutside.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { useEffect } from "react"

export function useOnClickOutside(
ref: React.RefObject<HTMLElement | null>,
refs: React.RefObject<HTMLElement | null>[],
handler: (event: MouseEvent) => void,
) {
useEffect(() => {
const listener = (event: MouseEvent) => {
if (ref.current && event.composedPath().includes(ref.current)) {
const path = event.composedPath()
if (refs.every(ref => !ref.current || !path.includes(ref.current))) {
handler(event)
return
}
handler(event)
}

document.addEventListener("click", listener)
Expand Down
144 changes: 96 additions & 48 deletions src/components/footer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,94 @@ interface FooterSection {
links: FooterLink[]
}

const FOOTER_SECTIONS_COUNT = 4
const MAX_LINKS_PER_SECTION = 5
const FOOTER_SECTIONS: FooterSection[] = [
{
title: "Learn",
route: "/learn",
links: [
{ title: "Introduction", route: "/learn" },
{ title: "Best Practices", route: "/learn/best-practices" },
{
title: (
<span>
<span className="max-md:hidden">Frequently Asked Questions</span>
<span className="md:hidden">FAQ</span>
</span>
),
route: "/faq",
},
{
title: "Training Courses",
route: "/community/resources/training-courses",
},
],
},
{
title: "Code",
links: [
{ title: "GitHub", route: "https://github.com/graphql" },
{
title: "Specification",
route: "/spec",
},
{ title: "Libraries & Tools", route: "/code" },
{
title: "Services & Vendors",
route: "/code/?tags=services",
},
],
},
{
title: "Community",
links: [
{
title: "Resources",
route: "/community/resources/official-channels",
},
{
title: "Events & Meetups",
route: "/community/events",
},
{
title: (
<span>
Contribute<span className="max-md:hidden"> to GraphQL</span>
</span>
),
route: "/community/contribute/essential-links",
},
{ title: "Landscape", route: "/landscape" },
{ title: "Shop", route: "https://store.graphql.org/" },
],
},
{
title: "& More",
links: [
{ title: "Blog", route: "/blog" },
{
title: "GraphQL Foundation",
route: "/foundation",
},
{
title: "Community Grant",
route: "/foundation/community-grant",
},
{
title: "Brand Guidelines",
route: "/brand",
},
{
title: "Code of Conduct",
route: "/codeofconduct",
},
],
},
]

const CONFERENCE_YEAR = 2025
const HAS_CONFERENCE_BOX = true

export function Footer({ extraLinks }: { extraLinks: FooterLink[] }) {
const { sections, hasConferenceBox } = useFooterSections(extraLinks)
export function Footer() {
const themeConfig = useThemeConfig()

return (
Expand All @@ -39,10 +121,13 @@ export function Footer({ extraLinks }: { extraLinks: FooterLink[] }) {
<NextLink href="/" className="nextra-logo flex items-center">
<GraphQLWordmarkLogo className="h-6" title="GraphQL" />
</NextLink>
<div className="typography-menu flex items-center *:rounded-none dark:*:text-neu-900 md:hidden">
{renderComponent(themeConfig.themeSwitch.component)}
</div>
</div>

<div className="grid grid-cols-2 gap-px bg-neu-400 py-px dark:bg-neu-100 lg:grid-cols-5">
{sections.map((section, i) => (
{FOOTER_SECTIONS.map((section, i) => (
<div
className="typography-menu relative bg-neu-100 py-4 dark:bg-neu-0 lg:py-6 3xl:py-10"
key={i}
Expand All @@ -57,7 +142,9 @@ export function Footer({ extraLinks }: { extraLinks: FooterLink[] }) {
{section.title}
</Anchor>
) : (
<span className="block p-4 3xl:px-10">{section.title}</span>
<span className="block p-4 md:px-6 2xl:px-10">
{section.title}
</span>
)}
</h3>
)}
Expand All @@ -73,7 +160,7 @@ export function Footer({ extraLinks }: { extraLinks: FooterLink[] }) {
</div>
))}
<div className="flex flex-col max-lg:contents">
{hasConferenceBox && (
{HAS_CONFERENCE_BOX && (
<ConferenceFooterBox
href={`/conf/${CONFERENCE_YEAR}`}
className="z-[2] col-span-full flex-1 max-lg:row-start-1"
Expand All @@ -86,10 +173,10 @@ export function Footer({ extraLinks }: { extraLinks: FooterLink[] }) {
</div>
</div>

<div className="relative flex items-center justify-between gap-4 p-4 md:px-6 2xl:px-10">
<div className="relative flex items-center justify-between gap-4 p-4 max-md:justify-center md:px-6 2xl:px-10">
{themeConfig.darkMode && (
// todo: new theme switch component
<div className="typography-menu flex items-center *:rounded-none dark:*:text-neu-900">
<div className="typography-menu flex items-center *:rounded-none dark:*:text-neu-900 max-md:hidden">
{renderComponent(themeConfig.themeSwitch.component)}
</div>
)}
Expand Down Expand Up @@ -151,42 +238,3 @@ function Stripes() {
</div>
)
}

function useFooterSections(extraLinks: FooterLink[]): {
sections: FooterSection[]
hasConferenceBox: boolean
} {
const { normalizePagesResult } = useConfig()

const sections: FooterSection[] = []
const singleLinks: FooterLink[] = []
let hasConferenceBox = false

for (const item of normalizePagesResult.topLevelNavbarItems) {
if (
(item.type === "page" || item.type === "menu") &&
item.children?.length &&
sections.length < FOOTER_SECTIONS_COUNT - 1
) {
sections.push({
title: item.title,
route: item.route,
links: (item.children || [])
.filter(child => child.route)
.slice(0, MAX_LINKS_PER_SECTION)
.map(child => ({ title: child.title, route: child.route })),
})
} else if (singleLinks.length < MAX_LINKS_PER_SECTION) {
if (item.route && item.route.startsWith("/conf/")) {
hasConferenceBox = true
} else {
singleLinks.push({ title: item.title, route: item.route })
}
}
}

singleLinks.push(...extraLinks)
sections.push({ links: singleLinks })

return { sections, hasConferenceBox }
}
19 changes: 10 additions & 9 deletions src/components/index-page/data-colocation/component-tree.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import ModemIcon from "@/app/conf/_design-system/pixelarticons/modem.svg?svgr"
import clsx from "clsx"
import { forwardRef } from "react"

const INNER_BOX_SIZE = 16

interface ComponentTreeProps extends React.HTMLAttributes<HTMLDivElement> {
names: [string, string, string, string]
}

export function ComponentTree({
names,
className,
...rest
}: ComponentTreeProps) {
export const ComponentTree = forwardRef(function ComponentTree(
{ names, className, ...rest }: ComponentTreeProps,
ref: React.Ref<HTMLDivElement>,
) {
return (
<div
ref={ref}
className={clsx(
"sector-opacity mx-auto flex max-w-[500px] justify-between [--gap-x:20px] md:gap-x-10 md:[--gap-x:32px] 3xl:gap-x-20",
"sector-opacity flex max-w-[500px] justify-between [--gap-x:20px] md:gap-x-10 md:[--gap-x:32px] 3xl:gap-x-20",
className,
)}
{...rest}
Expand Down Expand Up @@ -57,7 +58,7 @@ export function ComponentTree({
className="flex size-12 items-center justify-center bg-neu-100 dark:bg-neu-50"
data-sector="1"
>
<ModemIcon className="size-6 text-neu-600" />
<ModemIcon className="pointer-events-none size-6 text-neu-600" />
</div>

<div className="h-4 w-px bg-neu-300 dark:bg-neu-100" />
Expand Down Expand Up @@ -107,7 +108,7 @@ export function ComponentTree({
</div>
</div>
)
}
})

interface NestedBoxProps extends React.HTMLAttributes<HTMLDivElement> {
bgColor: string
Expand Down Expand Up @@ -175,7 +176,7 @@ function Fork(props: React.SVGProps<SVGSVGElement>) {
<path d="M40.5 8V0M1 16V8m79 8V8" vectorEffect="non-scaling-stroke" />
<path
d="m1 8 79-.00001"
stroke-linecap="square"
strokeLinecap="square"
vectorEffect="non-scaling-stroke"
/>
</svg>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@
[data-active-sector="2"] & [data-sector="2"],
[data-active-sector="3"] & [data-sector="3"],
[data-active-sector="4"] & [data-sector="4"] {
@apply ring-2 ring-neu-500 duration-200 ease-out dark:ring-neu-400 dark:ring-offset-neu-0;
@apply ring-2 ring-neu-500 ring-offset-neu-0 duration-200 ease-out dark:ring-neu-400;
}
}
2 changes: 1 addition & 1 deletion src/components/index-page/data-colocation/friend-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export function FriendList({ friends }: FriendList) {
<h4 className="typography-body-lg border-b border-neu-200 bg-neu-50 px-3 py-2 dark:border-neu-100 dark:bg-neu-50/50">
Friends
</h4>
<ul className="flex flex-col gap-6 p-3 lg:p-6 max-sm:[&>*:not(:first-child)]:hidden">
<ul className="flex flex-col gap-6 p-3 lg:p-6 max-md:[&>*:not(:first-child)]:hidden">
{friends.map(friend => (
<FriendListItem key={friend.name} {...friend} />
))}
Expand Down
33 changes: 19 additions & 14 deletions src/components/index-page/data-colocation/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export function DataColocation() {
const sectorElement = target?.closest("[data-sector]") as HTMLElement | null
const sector = sectorElement?.dataset.sector

if (!sector) return
if (sector == null) return

const currentTarget = event.currentTarget

Expand All @@ -119,7 +119,6 @@ export function DataColocation() {
}

const unmarkSector = (event: React.MouseEvent<HTMLElement>) => {
console.log("unmarkSector", window.matchMedia("(hover: none)").matches)
if (window.matchMedia("(hover: none)").matches) return

const target =
Expand All @@ -143,33 +142,36 @@ export function DataColocation() {
currentTarget.dataset.activeSector = targetSector
}

const ref = useRef<HTMLDivElement>(null)
useOnClickOutside(ref, event => {
console.log("clicked outside")
if (event.currentTarget && event.currentTarget instanceof HTMLElement) {
delete event.currentTarget.dataset.activeSector
const sectionRef = useRef<HTMLElement>(null)
const figureRef = useRef<HTMLElement>(null)
const componentTreeRef = useRef<HTMLDivElement>(null)
useOnClickOutside([figureRef, componentTreeRef], () => {
const section = sectionRef.current
if (section && section.dataset.activeSector) {
delete section.dataset.activeSector
}
})

return (
<section
ref={ref}
className="gql-container gql-section flex flex-wrap justify-between gap-4 sm:max-xl:gap-y-8 xl:p-24"
ref={sectionRef}
className="gql-container gql-section flex justify-between gap-4 max-xl:flex-wrap sm:max-xl:gap-y-8 xl:p-24"
onMouseOver={markSector}
onMouseOut={unmarkSector}
onPointerDown={markSector}
>
<div className="shrink">
<div className="basis-full justify-between gap-x-8 sm:max-xl:flex lg:shrink">
<header>
<SectionLabel>Data Colocation</SectionLabel>
<h2 className="typography-h2 mt-6">Data Colocation</h2>
<p className="typography-body-md mt-6 text-pretty xl:max-w-[438px]">
<p className="typography-body-md mt-6 text-pretty lg:max-w-[500px] xl:max-w-[438px]">
GraphQL fragments let you reuse common field selections across
queries, making your code more maintainable and consistent.
</p>
</header>
<ComponentTree
className="mt-6 md:mt-8 lg:mt-12 xl:mt-16"
ref={componentTreeRef}
className="mt-6 max-sm:mx-auto md:mt-8 lg:mt-12 xl:mt-16"
names={[
"Server",
"<FriendList>",
Expand All @@ -178,8 +180,11 @@ export function DataColocation() {
]}
/>
</div>
<article className="flex flex-wrap divide-neu-100 dark:divide-neu-50 dark:shadow-[0_.5px_20px_0_hsl(0_0_0/.25)] max-xl:w-full max-lg:gap-4 lg:shadow-[0_.5px_20px_0_hsl(var(--color-neu-400))] lg:dark:bg-neu-50/10 xl:divide-x xl:rounded-lg">
<div className="flex grow flex-col gap-y-4 lg:flex-col-reverse lg:p-4">
<article
className="flex divide-neu-100 dark:divide-neu-50 dark:shadow-[0_.5px_20px_0_hsl(0_0_0/.25)] max-xl:w-full max-lg:gap-4 max-md:flex-col lg:rounded-lg lg:shadow-[0_.5px_20px_0_hsl(var(--color-neu-400))] lg:dark:bg-neu-50/10 xl:divide-x"
ref={figureRef}
>
<div className="flex grow flex-col gap-y-4 sm:flex-col-reverse lg:p-4">
<FigureInfo />
<div className="sector-ring">
<FriendList friends={json.friends} />
Expand Down
2 changes: 2 additions & 0 deletions src/components/index-page/hero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ function HeroStripes() {
className="relative h-full bg-gradient-to-b from-pri-base to-pri-lighter opacity-0 transition-opacity duration-[1.5s] [mask-position:center_12%] [mask-size:110%] data-[loaded=true]:opacity-100 dark:to-pri-base sm:[mask-size:auto_300%] lg:[mask-position:7%_7%] lg:[mask-size:200%]"
style={{
maskImage: `url(${logoBlurred.src})`,
WebkitMaskImage: `url(${logoBlurred.src})`,
maskRepeat: "no-repeat",
WebkitMaskRepeat: "no-repeat",
}}
/>
<StripesDecoration
Expand Down
Loading