Skip to content

Commit 6420fbc

Browse files
Control tab with query parameter, fix navbar navigation
1 parent f6ef54f commit 6420fbc

File tree

3 files changed

+59
-43
lines changed

3 files changed

+59
-43
lines changed

src/lib/types.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
export type Prettify<T> = {
2-
[K in keyof T]: T[K];
3-
} & {};
4-
51
export type Repo = {
62
/**
73
* Repository name on GitHub
@@ -23,6 +19,7 @@ export type Repo = {
2319
versionFromTag: (tag: string) => string;
2420
};
2521

26-
export type Tab = "svelte" | "kit" | "others";
22+
export const availableTabs = ["svelte", "kit", "others"] as const;
23+
export type Tab = (typeof availableTabs)[number];
2724

2825
export const tokenKey = "token";

src/routes/+layout.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@
143143
<div class="border-b">
144144
<div class="mx-auto flex h-14 w-full items-center px-8">
145145
<!-- Left part -->
146-
<a href="/" class="flex items-center gap-2">
146+
<a href="/" class="flex items-center gap-2" on:click={() => tabState.set("svelte")}>
147147
<img src={FAVICON_URL} alt="Svelte" class="size-8" />
148148
<h2 class="hidden text-xl font-semibold xs:inline-block">
149149
Svelte

src/routes/+page.svelte

Lines changed: 56 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
<script lang="ts">
22
import { onMount } from "svelte";
33
import { get } from "svelte/store";
4+
import { pushState } from "$app/navigation";
5+
import { page } from "$app/stores";
6+
import type { TabsProps } from "bits-ui";
47
import type { Octokit } from "octokit";
58
import { ArrowUpRight, LoaderCircle } from "lucide-svelte";
69
import { MetaTags } from "svelte-meta-tags";
710
import { persisted } from "svelte-persisted-store";
811
import semver from "semver";
912
import type { Snapshot } from "./$types";
10-
import type { Tab } from "$lib/types";
13+
import { availableTabs, type Tab } from "$lib/types";
1114
import { FAVICON_PNG_URL, PROD_URL } from "$lib/config";
1215
import { getOctokit } from "$lib/octokit";
1316
import { getTabState } from "$lib/stores";
@@ -29,24 +32,54 @@
2932
3033
const octokit = getOctokit();
3134
32-
// Repositories to fetch releases from
33-
let currentRepo: Tab = "svelte";
35+
let currentTab: Tab = "svelte";
3436
35-
// Tab change
37+
function onTabChange(newTab: TabsProps["value"]) {
38+
const toSet = new Set(visitedTabs);
39+
toSet.add(previousTab);
40+
visitedTabs = [...toSet];
41+
42+
// I have no clue how this can be undefined
43+
if (newTab) {
44+
// @ts-expect-error Svelte 5, please
45+
previousTab = newTab;
46+
pushState(`?${queryParam}=${newTab}`, {});
47+
}
48+
}
49+
50+
// Tab change from the store (layout)
3651
let tabChangeAsked = false;
3752
const tabState = getTabState();
38-
tabState.subscribe(value => {
39-
if (value === currentRepo) return;
53+
tabState.subscribe(newTab => {
54+
if (newTab === currentTab) return;
4055
tabChangeAsked = true;
4156
window.scrollTo({ top: 0, behavior: "smooth" });
4257
});
4358
4459
let scrollY = 0;
4560
$: if (tabChangeAsked && scrollY === 0) {
46-
currentRepo = get(tabState);
61+
currentTab = get(tabState);
62+
onTabChange(currentTab);
4763
tabChangeAsked = false;
4864
}
4965
66+
// Tab change from the URL
67+
const queryParam = "tab";
68+
let shouldUnsubscribe = false;
69+
const unsubscribe = page.subscribe(({ url }) => {
70+
const tab = url.searchParams.get(queryParam) as Tab | null;
71+
if (!tab) {
72+
shouldUnsubscribe = true;
73+
return;
74+
}
75+
if (availableTabs.includes(tab)) {
76+
tabState.set(tab);
77+
currentTab = tab;
78+
shouldUnsubscribe = true;
79+
}
80+
});
81+
$: if (shouldUnsubscribe) unsubscribe();
82+
5083
/**
5184
* Fetches releases from GitHub for the given category, for
5285
* all the repositories in that category.
@@ -100,7 +133,7 @@
100133
};
101134
102135
// Badges
103-
let previousTab: Tab = currentRepo;
136+
let previousTab: Tab = currentTab;
104137
let visitedTabs: Tab[] = [];
105138
let loadedTabs: Tab[] = [];
106139
let isLoadingDone = false;
@@ -172,7 +205,7 @@
172205
<svelte:window bind:scrollY />
173206

174207
<MetaTags
175-
title={repos[currentRepo].name}
208+
title={repos[currentTab].name}
176209
titleTemplate="%s | Svelte Changelog"
177210
description="A nice UI to stay up-to-date with Svelte releases"
178211
canonical={PROD_URL}
@@ -202,32 +235,18 @@
202235

203236
<div class="container py-8">
204237
<h2 class="text-3xl font-bold">
205-
<span class="text-primary">{repos[currentRepo].name}</span>
238+
<span class="text-primary">{repos[currentTab].name}</span>
206239
Releases
207240
</h2>
208-
<Tabs.Root
209-
bind:value={currentRepo}
210-
class="mt-8"
211-
onValueChange={newValue => {
212-
const toSet = new Set(visitedTabs);
213-
toSet.add(previousTab);
214-
visitedTabs = [...toSet];
215-
216-
// I have no clue how this can be undefined
217-
if (newValue) {
218-
// @ts-expect-error Svelte 5, please
219-
previousTab = newValue;
220-
}
221-
}}
222-
>
241+
<Tabs.Root bind:value={currentTab} class="mt-8" onValueChange={onTabChange}>
223242
<div
224243
class="flex flex-col items-start gap-4 xs:flex-row xs:items-center xs:justify-between xs:gap-0"
225244
>
226245
<Tabs.List class="bg-input dark:bg-muted">
227246
{#each typedEntries(repos) as [id, { name }]}
228247
<BlinkingBadge
229248
storedDateItem="{id}MostRecentUpdate"
230-
show={!visitedTabs.includes(id) && id !== currentRepo}
249+
show={!visitedTabs.includes(id) && id !== currentTab}
231250
>
232251
<Tabs.Trigger
233252
class="data-[state=inactive]:text-foreground/60 data-[state=inactive]:hover:bg-background/50 data-[state=active]:hover:text-foreground/75 data-[state=inactive]:hover:text-foreground dark:data-[state=inactive]:hover:bg-background/25"
@@ -240,31 +259,31 @@
240259
</Tabs.List>
241260
<div class="ml-auto flex items-center space-x-2 xs:ml-0">
242261
<!-- Tab-specific settings -->
243-
{#if currentRepo === "svelte"}
262+
{#if currentTab === "svelte"}
244263
<Checkbox
245-
id="beta-releases-{currentRepo}"
264+
id="beta-releases-{currentTab}"
246265
bind:checked={$displaySvelteBetaReleases}
247-
aria-labelledby="beta-releases-label-{currentRepo}"
266+
aria-labelledby="beta-releases-label-{currentTab}"
248267
/>
249-
{:else if currentRepo === "kit"}
268+
{:else if currentTab === "kit"}
250269
<Checkbox
251-
id="beta-releases-{currentRepo}"
270+
id="beta-releases-{currentTab}"
252271
bind:checked={$displayKitBetaReleases}
253-
aria-labelledby="beta-releases-label-{currentRepo}"
272+
aria-labelledby="beta-releases-label-{currentTab}"
254273
/>
255274
{:else}
256275
<Checkbox
257-
id="beta-releases-{currentRepo}"
276+
id="beta-releases-{currentTab}"
258277
bind:checked={$displayOtherBetaReleases}
259-
aria-labelledby="beta-releases-label-{currentRepo}"
278+
aria-labelledby="beta-releases-label-{currentTab}"
260279
/>
261280
{/if}
262281
<Label
263-
id="beta-releases-label-{currentRepo}"
264-
for="beta-releases-{currentRepo}"
282+
id="beta-releases-label-{currentTab}"
283+
for="beta-releases-{currentTab}"
265284
class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
266285
>
267-
Show {repos[currentRepo].name} prereleases
286+
Show {repos[currentTab].name} prereleases
268287
</Label>
269288
</div>
270289
</div>

0 commit comments

Comments
 (0)