diff --git a/documentation/src/components/GdkApiReference/index.tsx b/documentation/src/components/GdkApiReference/index.tsx index 7e0c02fa8..81f321e9e 100644 --- a/documentation/src/components/GdkApiReference/index.tsx +++ b/documentation/src/components/GdkApiReference/index.tsx @@ -45,22 +45,39 @@ type GdkApiDoc = { const VERSIONS = (apiData as { versions: GdkApiDoc[] }).versions; const DEFAULT_VERSION = VERSIONS[0].docVersion; +const DEFAULT_LANGUAGE = LANGUAGES[0].id; const VERSION_PARAM = "version"; +const LANGUAGE_PARAM = "language"; + +type Selection = { docVersion: string; languageId: LanguageId }; const isKnownVersion = (docVersion: string | null): docVersion is string => VERSIONS.some((entry) => entry.docVersion === docVersion); -const readVersionParam = (search: string) => - new URLSearchParams(search).get(VERSION_PARAM); +const isKnownLanguage = (languageId: string | null): languageId is LanguageId => + LANGUAGES.some((entry) => entry.id === languageId); -const versionSearch = (docVersion: string, search: string) => { +// Links carry the reader's version and language so a shared anchor lands on the +// same content. Params missing from older links fall back to the defaults. +const readSelection = (search: string): Selection => { const params = new URLSearchParams(search); - params.set(VERSION_PARAM, docVersion); + const requestedVersion = params.get(VERSION_PARAM); + const requestedLanguage = params.get(LANGUAGE_PARAM); + return { + docVersion: isKnownVersion(requestedVersion) ? requestedVersion : DEFAULT_VERSION, + languageId: isKnownLanguage(requestedLanguage) ? requestedLanguage : DEFAULT_LANGUAGE, + }; +}; + +const selectionSearch = (selection: Selection, search: string) => { + const params = new URLSearchParams(search); + params.set(VERSION_PARAM, selection.docVersion); + params.set(LANGUAGE_PARAM, selection.languageId); return `?${params.toString()}`; }; -const VersionSearchContext = React.createContext( - `?${VERSION_PARAM}=${DEFAULT_VERSION}`, +const SelectionSearchContext = React.createContext( + `?${VERSION_PARAM}=${DEFAULT_VERSION}&${LANGUAGE_PARAM}=${DEFAULT_LANGUAGE}`, ); const KIND_LABELS: Record = { @@ -152,7 +169,7 @@ function signature(func: GdkFunc, language: Language, owner?: string): string { } function HashLink({ anchor, label }: { anchor: string; label: string }) { - const search = useContext(VersionSearchContext); + const search = useContext(SelectionSearchContext); const title = `Direct link to ${label}`; return ( ("rust"); - const [docVersion, setDocVersion] = useState(DEFAULT_VERSION); + const [selection, setSelection] = useState({ + docVersion: DEFAULT_VERSION, + languageId: DEFAULT_LANGUAGE, + }); + const { docVersion, languageId } = selection; const brokenLinks = useBrokenLinks(); const history = useHistory(); const location = useLocation(); useEffect(() => { - const requested = readVersionParam(location.search); - if (isKnownVersion(requested)) { - setDocVersion(requested); - return; + const requested = readSelection(location.search); + setSelection(requested); + + const canonical = selectionSearch(requested, location.search); + if (canonical !== `?${new URLSearchParams(location.search)}`) { + history.replace({ search: canonical, hash: location.hash }); } - setDocVersion(DEFAULT_VERSION); + }, [history, location.hash, location.search]); + + const select = (next: Partial) => history.replace({ - search: versionSearch(DEFAULT_VERSION, location.search), + search: selectionSearch({ ...selection, ...next }, location.search), hash: location.hash, }); - }, [history, location.hash, location.search]); const language = LANGUAGES.find((entry) => entry.id === languageId)!; const doc = useMemo( @@ -377,7 +400,7 @@ export default function GdkApiReference() { const anchor = location.hash.slice(1); if (!anchor) return; document.getElementById(decodeURIComponent(anchor))?.scrollIntoView(); - }, [docVersion, location.hash]); + }, [docVersion, languageId, location.hash]); const grouped = useMemo(() => { const order: GdkItem["kind"][] = ["object", "callback", "record", "enum", "error"]; @@ -387,7 +410,7 @@ export default function GdkApiReference() { }, [doc]); return ( - +
@@ -398,7 +421,7 @@ export default function GdkApiReference() { role="tab" aria-selected={entry.id === languageId} className={entry.id === languageId ? styles.tabActive : styles.tab} - onClick={() => setLanguageId(entry.id)} + onClick={() => select({ languageId: entry.id })} > {entry.label} @@ -409,12 +432,7 @@ export default function GdkApiReference() { Version