From e5709742e1a0508ca0d26b37ea01dae353891c40 Mon Sep 17 00:00:00 2001 From: Piotr Monwid-Olechnowicz Date: Tue, 14 Oct 2025 18:28:07 +0200 Subject: [PATCH 1/6] Move EventCard to a separate file and add types --- src/components/events/event-card.tsx | 77 +++++++++++++++ src/components/{events.ts => events/index.ts} | 2 + src/pages/community/events.mdx | 96 +++++-------------- 3 files changed, 102 insertions(+), 73 deletions(-) create mode 100644 src/components/events/event-card.tsx rename src/components/{events.ts => events/index.ts} (99%) diff --git a/src/components/events/event-card.tsx b/src/components/events/event-card.tsx new file mode 100644 index 0000000000..41b2e408d6 --- /dev/null +++ b/src/components/events/event-card.tsx @@ -0,0 +1,77 @@ +import type { ReactNode } from "react" +import { clsx } from "clsx" + +import { PinIcon } from "@/app/conf/_design-system/pixelarticons/pin-icon" +import ClockIcon from "@/app/conf/_design-system/pixelarticons/clock.svg?svgr" + +export interface EventCardProps { + href: string + date?: Date + city: ReactNode + name: ReactNode + meta: ReactNode + official?: boolean +} + +export function EventCard({ + href, + date, + city, + name, + meta, + official, +}: EventCardProps) { + return ( + + {date && date instanceof Date && ( +
+
{date.getDate()}
+
+ {date.toLocaleString("en", { + month: "short", + year: "numeric", + })} +
+
+ )} +
+ {meta} +
+ {name} + {official ? ( + <> + {" "} + ⭐️ + + ) : ( + "" + )} +
+
+
+ + {city} +
+ {date && ( +
+ + {date.toLocaleString("en", { + hour: "numeric", + minute: "numeric", + })} +
+ )} +
+
+
+ ) +} diff --git a/src/components/events.ts b/src/components/events/index.ts similarity index 99% rename from src/components/events.ts rename to src/components/events/index.ts index 7d0bf687b4..89521c16ff 100644 --- a/src/components/events.ts +++ b/src/components/events/index.ts @@ -1,3 +1,5 @@ +export * from './event-card' + export const events = [ { name: "GraphQLConf 2024", diff --git a/src/pages/community/events.mdx b/src/pages/community/events.mdx index 347844939a..f76dccfff7 100644 --- a/src/pages/community/events.mdx +++ b/src/pages/community/events.mdx @@ -2,65 +2,19 @@ title: Events & Meetups --- -{/* title can be removed in Nextra 4, since sidebar title will take from first h1 */} - # Events & Meetups import { LocationIcon, ClockIcon } from "../../icons" import { clsx } from "clsx" import { useEffect } from "react" import { useData } from "nextra/hooks" -import { meetups } from "../../components/meetups" -import { events } from "../../components/events" import "leaflet/dist/leaflet.css" + +import { meetups } from "../../components/meetups" +import { events, EventCard } from "../../components/events" import pinkCircle from "./pink-circle.svg" import { Button } from '../../app/conf/_components/button' -export function EventCard({ href, date, city, name, meta, official }) { - return ( - - {date && date instanceof Date && ( -
-
{date.getDate()}
-
- {date.toLocaleString("en", { - month: "short", - year: "numeric", - })} -
-
- )} -
- {meta} -
{name}{official ? <>{" "}⭐️ : ""}
-
-
- - {city} -
- {date && ( -
- - {date.toLocaleString("en", { - hour: "numeric", - minute: "numeric", - })} -
- )} -
-
-
- ) -} export const { pastEvents, upcomingEvents } = events.reduce( (acc, event) => { @@ -76,35 +30,31 @@ export const { pastEvents, upcomingEvents } = events.reduce( { pastEvents: [], upcomingEvents: [] }, ) -export function Events({ events }) { +export function Events({ events, heading }) { + if (events.length === 0) return null; + return ( -
- {events.map(event => ( - - ))} -
+ <> +

{heading}

+
+ {events.map(event => ( + + ))} +
+ ) } -{/* -## Events - - + - - -
- Past Events - -
-*/} + ## Meetups From d39bd4147a99c76ae882b6b7a3154eb13d975974 Mon Sep 17 00:00:00 2001 From: Piotr Monwid-Olechnowicz Date: Tue, 14 Oct 2025 20:16:31 +0200 Subject: [PATCH 2/6] Revamp EventCard to match designs --- src/components/events/event-card.tsx | 140 +++++++++++++++++++-------- 1 file changed, 101 insertions(+), 39 deletions(-) diff --git a/src/components/events/event-card.tsx b/src/components/events/event-card.tsx index 41b2e408d6..97a93db6c8 100644 --- a/src/components/events/event-card.tsx +++ b/src/components/events/event-card.tsx @@ -1,15 +1,59 @@ import type { ReactNode } from "react" import { clsx } from "clsx" +import { CalendarIcon } from "@/app/conf/_design-system/pixelarticons/calendar-icon" import { PinIcon } from "@/app/conf/_design-system/pixelarticons/pin-icon" -import ClockIcon from "@/app/conf/_design-system/pixelarticons/clock.svg?svgr" +import { Tag } from "../../app/conf/_design-system/tag" + +const dateFormatter = new Intl.DateTimeFormat("en", { + day: "numeric", + month: "short", + year: "numeric", +}) + +const isoLikeDatePattern = + /^(\d{4}-\d{2}-\d{2}|\d{4}\/?\d{2}\/?\d{2}|\d{2}\/\d{2}\/\d{4})/ + +function normaliseDate(value: EventCardProps["date"]) { + if (value instanceof Date && !Number.isNaN(value.getTime())) { + return value + } + + if (typeof value === "string") { + const parsed = new Date(value) + if (!Number.isNaN(parsed.getTime())) { + return parsed + } + } + + return undefined +} + +function formatDateLabel(value: EventCardProps["date"]) { + const parsed = normaliseDate(value) + + if (parsed) { + if (typeof value === "string" && !isoLikeDatePattern.test(value.trim())) { + return value.trim() || undefined + } + + return dateFormatter.format(parsed) + } + + if (typeof value === "string") { + const trimmed = value.trim() + return trimmed.length > 0 ? trimmed : undefined + } + + return undefined +} export interface EventCardProps { href: string - date?: Date + date?: Date | string | null city: ReactNode name: ReactNode - meta: ReactNode + meta?: ReactNode official?: boolean } @@ -21,53 +65,71 @@ export function EventCard({ meta, official, }: EventCardProps) { + const dateLabel = formatDateLabel(date) + const parsedDate = normaliseDate(date) + return ( - {date && date instanceof Date && ( -
-
{date.getDate()}
-
- {date.toLocaleString("en", { - month: "short", - year: "numeric", - })} -
-
- )} -
- {meta} -
- {name} - {official ? ( - <> - {" "} - ⭐️ - +
+
+ {meta ? ( + {meta} ) : ( - "" + Official GraphQL Local + )} + {official && ( + + + ★ + + Official + )}
-
-
- - {city} -
- {date && ( -
- - {date.toLocaleString("en", { - hour: "numeric", - minute: "numeric", - })} + +
+ {name} +
+ +
+ {dateLabel && ( +
+ + {parsedDate ? ( + + ) : ( + {dateLabel} + )} +
+ )} + {city && ( +
+ + {city}
)}
From e88ea20289f349b3bb5639339d70f35238eab4de Mon Sep 17 00:00:00 2001 From: Piotr Monwid-Olechnowicz Date: Tue, 14 Oct 2025 20:16:42 +0200 Subject: [PATCH 3/6] Add scrollviewFadePlugin --- tailwind.config.ts | 52 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/tailwind.config.ts b/tailwind.config.ts index eeb7669d61..82107207ca 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -202,6 +202,7 @@ const config: Config = { }), tailwindMediaHover(), scrollStartPlugin(), + scrollviewFadePlugin(), browserPlugin, ], darkMode: ["class", 'html[class~="dark"]'], @@ -294,3 +295,54 @@ function scrollStartPlugin() { ) }) } + +function scrollviewFadePlugin() { + return plugin(({ addComponents, addBase }) => { + addComponents({ + ".scrollview-x-fade": { + position: "relative", + scrollTimeline: "--scroll-timeline-x inline", + "--fade-start-opacity": "1", + "--fade-end-opacity": "1", + maskImage: ` + linear-gradient(to right, + hsl(0 0% 0% / var(--fade-start-opacity)), + black var(--fade-size), + black calc(100% - var(--fade-size)), + hsl(0 0% 0% / var(--fade-end-opacity)) + ) + `, + WebkitMaskImage: ` + linear-gradient(to right, + hsl(0 0% 0% / var(--fade-start-opacity)), + black var(--fade-size), + black calc(100% - var(--fade-size)), + hsl(0 0% 0% / var(--fade-end-opacity)) + ) + `, + animation: + "scrollview-fade-start 10s ease-out both, scrollview-fade-end 10s ease-out both", + animationTimeline: "--scroll-timeline-x, --scroll-timeline-x", + animationRange: "0 2em, calc(100% - 2em) 100%", + }, + "@keyframes scrollview-fade-start": { + from: { "--fade-start-opacity": "1" }, + to: { "--fade-start-opacity": "0" }, + }, + "@keyframes scrollview-fade-end": { + from: { "--fade-end-opacity": "0" }, + to: { "--fade-end-opacity": "1" }, + }, + "@property --fade-start-opacity": { + syntax: '""', + initialValue: "1", + inherits: "false", + }, + "@property --fade-end-opacity": { + syntax: '""', + initialValue: "1", + inherits: "false", + }, + }) + }) +} From 84eaa8b23101b2d0ba230c90a5a9d7d6634d82db Mon Sep 17 00:00:00 2001 From: Piotr Monwid-Olechnowicz Date: Tue, 14 Oct 2025 20:16:58 +0200 Subject: [PATCH 4/6] Add GraphQLConf 2025 and GraphQL Day at APIDays to events --- src/components/events/index.ts | 48 +++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/src/components/events/index.ts b/src/components/events/index.ts index 89521c16ff..8befb6af03 100644 --- a/src/components/events/index.ts +++ b/src/components/events/index.ts @@ -1,13 +1,41 @@ -export * from './event-card' +export * from "./event-card" -export const events = [ +interface Event { + name: string + slug: string + location: string + date: string + coverImage?: string + eventLink: string + host: string + hostLink?: string +} + +export const events: Event[] = [ + { + name: "GraphQL Day at APIDays", + slug: "graphql-day-at-apidays", + location: "CNIT La Defense, Paris", + date: "2025-12-11T08:00:00+00:00", + eventLink: "https://graphql.day", + host: "APIDays & GraphQL Community", + hostLink: "https://apidays.co", + }, + { + name: "GraphQLConf 2025", + slug: "graphql-conf-2025", + location: "Amsterdam", + date: "2025-09-08T07:00:00+00:00", + eventLink: "/conf/2025", + host: "GraphQL Foundation", + }, { name: "GraphQLConf 2024", slug: "graphql-conf-2024", location: "San Francisco", date: "2024-09-09T07:00:00+00:00", eventLink: "/conf/2024", - host: "JW Marriott San Francisco Union Square", + host: "GraphQL Foundation", }, { name: "GraphQL Bali 001", @@ -32,7 +60,7 @@ export const events = [ hostLink: "https://www.oursky.com/", }, { - name: "Paris December 7", + name: "GraphQL Paris before APIDays", slug: "graphql-paris-pre-apidays", location: "Paris", date: "2023-12-07T18:00:00+02:00", @@ -53,7 +81,7 @@ export const events = [ hostLink: "https://developer.microsoft.com/en-us/reactor/", }, { - name: "Singapore November 6", + name: "GraphQL Singapore Meetup #6", slug: "graphql-sigapore-6", location: "Singapore", date: "2023-11-15T18:00:00+08:00", @@ -75,7 +103,7 @@ export const events = [ hostLink: "https://www.pinterest.com/", }, { - name: "Bangkok November 12", + name: "GraphQL BKK 12.0", slug: "graphql-bangkok-12", location: "Bangkok", date: "2023-11-02T18:00:00+07:00", @@ -86,7 +114,7 @@ export const events = [ hostLink: "https://sevenpeakssoftware.com/", }, { - name: "Seattle October", + name: "GraphQL Seattle #1", slug: "graphql-seattle-01", location: "Seattle", date: "2023-10-26T18:00:00-07:00", @@ -97,7 +125,7 @@ export const events = [ hostLink: "https://www.microsoft.com/", }, { - name: "San Francisco September", + name: "GraphQL SF #12", slug: "graphql-sf-12", location: "San Francisco", date: "2023-09-19T18:00:00-07:00", @@ -108,7 +136,7 @@ export const events = [ hostLink: "https://github.com/", }, { - name: "London September", + name: "GraphQL London #1", slug: "graphql-london-01", location: "London", date: "2023-09-07T17:30:00+01:00", @@ -119,7 +147,7 @@ export const events = [ hostLink: "https://neo4j.com/", }, { - name: "Bangkok July 11", + name: "GraphQL BKK 11.0", slug: "graphql-bangkok-11", location: "Bangkok", date: "2023-07-31T18:00:00+07:00", From 8685f33cdab32a22c825c2a25f088773f311f4c5 Mon Sep 17 00:00:00 2001 From: Piotr Monwid-Olechnowicz Date: Tue, 14 Oct 2025 20:17:15 +0200 Subject: [PATCH 5/6] Show upcoming and past events on /community/events page --- src/pages/community/events.mdx | 77 ++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 32 deletions(-) diff --git a/src/pages/community/events.mdx b/src/pages/community/events.mdx index f76dccfff7..b067f311db 100644 --- a/src/pages/community/events.mdx +++ b/src/pages/community/events.mdx @@ -13,7 +13,7 @@ import "leaflet/dist/leaflet.css" import { meetups } from "../../components/meetups" import { events, EventCard } from "../../components/events" import pinkCircle from "./pink-circle.svg" -import { Button } from '../../app/conf/_components/button' +import { Button } from '../../app/conf/_design-system/button' export const { pastEvents, upcomingEvents } = events.reduce( @@ -30,31 +30,40 @@ export const { pastEvents, upcomingEvents } = events.reduce( { pastEvents: [], upcomingEvents: [] }, ) -export function Events({ events, heading }) { +export function EventsScrollview({ children }) { + return ( +
+ {children} +
+ ) +} + +export function Events({ events }) { if (events.length === 0) return null; return ( - <> -

{heading}

-
- {events.map(event => ( - - ))} -
- +
+ {events.map(event => ( + + ))} +
) } - +## Upcoming Events - + + +## Past Events + + ## Meetups @@ -65,9 +74,11 @@ happy to promote your GraphQL event through the Please contact us in the `#meetups-admin` channel on [the community Discord channel](/community/#official-channels). - +
+ +
export function Meetups() { useEffect(() => { @@ -93,16 +104,18 @@ export function Meetups() { return ( <>
- {meetups.map(({ node }) => ( - - ))} + + {meetups.map(({ node }) => ( + + ))} + ) } From 3afd0df2b9eaa58c1eeaafa42d357379fa03f844 Mon Sep 17 00:00:00 2001 From: Piotr Monwid-Olechnowicz Date: Tue, 14 Oct 2025 20:21:30 +0200 Subject: [PATCH 6/6] Tweak spacing --- src/components/events/event-card.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/events/event-card.tsx b/src/components/events/event-card.tsx index 97a93db6c8..f39d85e807 100644 --- a/src/components/events/event-card.tsx +++ b/src/components/events/event-card.tsx @@ -80,8 +80,10 @@ export function EventCard({
{meta ? (