Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
rework tabs query state to work better across both routers
- Loading branch information
Showing
8 changed files
with
172 additions
and
64 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
'use client' | ||
|
||
import { useCallback, type ComponentProps } from 'react' | ||
import { Tabs as TabsPrimitive } from 'ui' | ||
import { useTocRerenderTrigger } from '~/components/GuidesTableOfContents' | ||
|
||
const TabPanel = TabsPrimitive.Panel | ||
const Tabs = ({ onChange, ...props }: ComponentProps<typeof TabsPrimitive>) => { | ||
const rerenderToc = useTocRerenderTrigger() | ||
const onChangeInternal = useCallback( | ||
(...args: Parameters<typeof onChange>) => { | ||
rerenderToc() | ||
onChange?.(...args) | ||
}, | ||
[rerenderToc, onChange] | ||
) | ||
|
||
return <TabsPrimitive wrappable onChange={onChangeInternal} {...props} /> | ||
} | ||
|
||
export { Tabs, TabPanel } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
import { useCallback, useEffect, useId, useMemo, useReducer, useRef } from 'react' | ||
|
||
/** | ||
* Stores state in search params while bypassing Next Router. | ||
* | ||
* The purpose of this is to use search params while maintaining SSG ability, | ||
* because Next.js's `useSearchParams` forces at least the children to be | ||
* client-side rendered on static routes. | ||
* | ||
* See https://nextjs.org/docs/app/api-reference/functions/use-search-params | ||
*/ | ||
const useSearchParamsShallow = () => { | ||
const EVENT_NAME = 'supabase.events.packages.common.useSearchParamsShallow' | ||
const id = useId() | ||
const timeoutHandle = useRef<ReturnType<typeof setTimeout>>() | ||
|
||
const reducer = useCallback( | ||
(_: URLSearchParams, action: { target: 'int' | 'ext'; newParams: URLSearchParams }) => { | ||
clearTimeout(timeoutHandle.current) | ||
if (action.target === 'ext') { | ||
/** | ||
* Doing this in the next tick makes sure that the originating | ||
* component finishes rendering before it triggers updates to other | ||
* components. | ||
*/ | ||
timeoutHandle.current = setTimeout(() => { | ||
document.dispatchEvent(new CustomEvent(EVENT_NAME, { detail: { id } })) | ||
}) | ||
} | ||
return action.newParams | ||
}, | ||
[id] | ||
) | ||
|
||
const [localParams, setLocalParams] = useReducer(reducer, undefined, () => new URLSearchParams()) | ||
|
||
useEffect(() => { | ||
const handler = (event: CustomEvent<{ id: string }>) => { | ||
if (event.detail.id !== id) { | ||
const globalParams = new URLSearchParams(window.location.search) | ||
setLocalParams({ target: 'int', newParams: globalParams }) | ||
} | ||
} | ||
|
||
document.addEventListener(EVENT_NAME, handler as EventListener) | ||
return () => document.removeEventListener(EVENT_NAME, handler as EventListener) | ||
}, [id]) | ||
|
||
useEffect(() => { | ||
const globalParams = new URLSearchParams(window.location.search) | ||
setLocalParams({ target: 'int', newParams: globalParams }) | ||
}, []) | ||
|
||
const has = useCallback((key: string) => localParams.has(key), [localParams]) | ||
|
||
const get = useCallback((key: string) => localParams.get(key), [localParams]) | ||
|
||
const getAll = useCallback((key: string) => localParams.getAll(key), [localParams]) | ||
|
||
const set = useCallback((key: string, value: any) => { | ||
if (typeof window === 'undefined') return | ||
|
||
const url = new URL(window.location.href) | ||
url.searchParams.set(key, value) | ||
window.history.replaceState(null, '', url) | ||
setLocalParams({ target: 'ext', newParams: url.searchParams }) | ||
}, []) | ||
|
||
const append = useCallback((key: string, value: any) => { | ||
if (typeof window === 'undefined') return | ||
|
||
const url = new URL(window.location.href) | ||
url.searchParams.append(key, value) | ||
window.history.replaceState(null, '', url) | ||
setLocalParams({ target: 'ext', newParams: url.searchParams }) | ||
}, []) | ||
|
||
const _delete = useCallback((key: string) => { | ||
if (typeof window === 'undefined') return | ||
|
||
const url = new URL(window.location.href) | ||
url.searchParams.delete(key) | ||
window.history.replaceState(null, '', url) | ||
setLocalParams({ target: 'ext', newParams: url.searchParams }) | ||
}, []) | ||
|
||
const api = useMemo( | ||
() => ({ | ||
has, | ||
get, | ||
getAll, | ||
append, | ||
set, | ||
delete: _delete, | ||
}), | ||
[has, get, getAll, append, set, _delete] | ||
) | ||
|
||
return api | ||
} | ||
|
||
export { useSearchParamsShallow } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters