-
Notifications
You must be signed in to change notification settings - Fork 92
add component DateGate conditionally render content based on build-time date #1845
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
base: ep2026
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ | |
| import HeaderLogo from "@components/header/header-logo.astro"; | ||
| import Search from "@components/Search.astro"; | ||
| import ThemeToggle from "@components/ThemeToggle.astro"; | ||
| import DateGate from "@ui/DateGate.astro"; | ||
|
|
||
| import { NAV_MENUS } from "@data/nav"; | ||
| import { buildLinkChecker } from "@utils/nav"; | ||
|
|
@@ -111,12 +112,24 @@ const activeMenus = NAV_MENUS.map((menu) => ({ | |
| ), | ||
| ) | ||
| } | ||
| <li class="nav-item nav-mobile-cta"> | ||
| <a class="nav-link-btn" href="/jobs">Jobs</a> | ||
| </li> | ||
| <li class="nav-item nav-mobile-cta"> | ||
| <a class="nav-link-btn" href="/tickets">Register Now</a> | ||
| </li> | ||
| <DateGate before="2026-07-20"> | ||
| <Fragment slot="before"> | ||
| <li class="nav-item nav-mobile-cta"> | ||
| <a class="nav-link-btn" href="/jobs">Jobs</a> | ||
| </li> | ||
| <li class="nav-item nav-mobile-cta"> | ||
| <a class="nav-link-btn" href="/tickets">Register Now</a> | ||
| </li> | ||
| </Fragment> | ||
| <Fragment slot="during"> | ||
| <li class="nav-item nav-mobile-cta"> | ||
| <a class="nav-link-btn" href="/jobs">Jobs</a> | ||
| </li> | ||
| <li class="nav-item nav-mobile-cta"> | ||
| <a class="nav-link-btn" href="/tickets">Register Now</a> | ||
| </li> | ||
| </Fragment> | ||
| </DateGate> | ||
| </ul> | ||
|
|
||
| <button | ||
|
|
@@ -160,8 +173,21 @@ const activeMenus = NAV_MENUS.map((menu) => ({ | |
| </svg> | ||
| </a> | ||
| <ThemeToggle /> | ||
| <a href="/jobs" class="nav-cta-desktop">Jobs</a> | ||
| <a href="/tickets" class="nav-cta">Register Now</a> | ||
| <DateGate before="2026-07-20"> | ||
| <Fragment slot="before"> | ||
| <a href="/jobs" class="nav-cta-desktop">Jobs</a> | ||
| <a href="/tickets" class="nav-cta">Register Now</a> | ||
| </Fragment> | ||
| <Fragment slot="during"> | ||
| <a href="/jobs" class="nav-cta-desktop">Jobs</a> | ||
| <a href="/tickets" class="nav-cta">Register Now</a> | ||
| </Fragment> | ||
| <Fragment slot="after"> | ||
| <span class="nav-cta" style="opacity:0.5;cursor:default" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. opacity: 0.5 here fades both the txt and bg into the navbar, so in lightmode, the "see you next year" is so faint that I barely think you mean it. 😢 Maybe remove the opacity and give this post-event label its own txt and bg colour, or something else cleverer, to increase the contrast and sincerity of wanting to see us next year? |
||
| >See you next year!</span | ||
| > | ||
| </Fragment> | ||
| </DateGate> | ||
| <button | ||
| class="nav-toggle" | ||
| aria-label="Toggle menu" | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| --- | ||
| /** | ||
| * DateGate — conditionally render content based on build-time date. | ||
| * | ||
| * Defines a date window via `after` and/or `before` props. At build time, | ||
| * the component determines which time period "now" falls into and renders | ||
| * exactly one of three named slots: "before", "during", or "after". | ||
| * | ||
| * Any omitted slot simply renders nothing — use 2 slots for a simple | ||
| * on/off gate or all 3 for a full before/during/after split. | ||
| * | ||
| * Date strings are parsed as UTC midnight when no time portion is given | ||
| * (e.g. "2026-07-14" = 2026-07-14T00:00:00.000Z). For precise time-of-day | ||
| * gating in a specific timezone, include the offset: | ||
| * after="2026-07-14T00:00:00+02:00" (midnight CEST) | ||
| * | ||
| * @example | ||
| * // 3-state: before CfP / open / closed | ||
| * <DateGate after="2026-06-01" before="2026-07-01"> | ||
| * <p slot="before">CfP opens June 1st!</p> | ||
| * <a slot="during" href="/cfp">Submit your talk</a> | ||
| * <p slot="after">CfP closed. See you in Kraków!</p> | ||
| * </DateGate> | ||
| * | ||
| * @example | ||
| * // 2-state: teaser → live (omit "after" slot) | ||
| * <DateGate after="2026-07-14"> | ||
| * <p slot="before">Tickets coming soon!</p> | ||
| * <a slot="during" href="/tickets">Buy Tickets</a> | ||
| * </DateGate> | ||
| */ | ||
|
|
||
| interface Props { | ||
| /** | ||
| * Window start — ISO 8601 date string, inclusive. | ||
| * Date-only strings (e.g. "2026-07-14") are parsed as UTC midnight. | ||
| * Include offset for timezone-aware gating: "2026-07-14T00:00:00+02:00" | ||
| */ | ||
| after?: string; | ||
| /** | ||
| * Window end — ISO 8601 date string, inclusive. | ||
| * Same parsing rules as `after`. | ||
| */ | ||
| before?: string; | ||
| } | ||
|
|
||
| const { after, before } = Astro.props; | ||
|
|
||
| // --- Parse dates --- | ||
| // Date-only strings (no "T") get interpreted as UTC midnight by new Date(). | ||
| // DateTime strings with offset (e.g. "...T00:00:00+02:00") parse correctly. | ||
| const parseDate = (s: string): Date => { | ||
| const iso = s.includes("T") ? s : `${s}T00:00:00.000Z`; | ||
| return new Date(iso); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: is it a good idea for parseDate to check if it produced a valid date and throw a build error if not? Like if i have a typo, e.g. before="2026-13-20", that's an invalid date, I think in the before-only case, comparing now with that invalid date returns false, so we select |
||
| }; | ||
|
|
||
| const now = new Date(); | ||
| const afterDate = after ? parseDate(after) : null; | ||
| const beforeDate = before ? parseDate(before) : null; | ||
|
|
||
| // --- Determine which period "now" falls into --- | ||
| type Period = "before" | "during" | "after"; | ||
|
|
||
| let period: Period = "during"; | ||
|
|
||
| if (afterDate && beforeDate) { | ||
| if (now < afterDate) period = "before"; | ||
| else if (now > beforeDate) period = "after"; | ||
| else period = "during"; | ||
| } else if (afterDate) { | ||
| period = now < afterDate ? "before" : "during"; | ||
| } else if (beforeDate) { | ||
| period = now > beforeDate ? "after" : "during"; | ||
| } | ||
| --- | ||
|
|
||
| {period === "before" ? <slot name="before" /> : null} | ||
| {period === "during" ? <slot name="during" /> : null} | ||
| {period === "after" ? <slot name="after" /> : null} | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’m confused here. so when only
before="2026-07-20"is provided, thebeforeDateselectsduringup to 20 Jul andafterafterward, right? If so, it never selectsbefore, so thisslot="before"fragment doesn't reach / render? Then isn't it just a dup ofduringfragment?Also, could we give an example to the the before-only case in
DateGateto help with the confusedpoke?