mirror of
https://github.com/supabase/supabase.git
synced 2026-10-16 22:59:11 +03:00
Co-authored-by: Kang Ming <kang.ming1996@gmail.com> Co-authored-by: Joel Lee <lee.yi.jie.joel@gmail.com>
61 lines
1.7 KiB
TypeScript
61 lines
1.7 KiB
TypeScript
import { type TabsProps } from 'ui/src/components/Tabs'
|
|
import { useSticky } from './withSticky.utils'
|
|
import { PropsWithChildren, useCallback, type FC } from 'react'
|
|
|
|
interface StickyProps {
|
|
stickyTabList?: {
|
|
scrollMarginTop?: string
|
|
style?: CSSStyleDeclaration
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Wrapper around Tabs to make the tab list sticky while the tab content is in
|
|
* view.
|
|
*/
|
|
const withSticky =
|
|
<Props extends PropsWithChildren<TabsProps>>(
|
|
Component: FC<Omit<Props, 'stickyTabList' | 'onClick'>>
|
|
) =>
|
|
({ stickyTabList, onClick, ...props }: Props & StickyProps) => {
|
|
const { inView, observedRef, stickyRef } = useSticky<HTMLDivElement>({
|
|
enabled: !!stickyTabList,
|
|
style: stickyTabList?.style,
|
|
})
|
|
|
|
const onClickInternal = useCallback(
|
|
(id: string) => {
|
|
if (stickyTabList && inView && stickyRef.current) {
|
|
let elem = stickyRef.current as Element | null
|
|
while (elem && !elem.matches('[role="tabpanel"][data-state="active"]')) {
|
|
elem = elem.nextElementSibling
|
|
}
|
|
if (!elem) return
|
|
|
|
const top = elem.getBoundingClientRect().top
|
|
;(elem as HTMLElement).style.scrollMarginTop = stickyTabList?.scrollMarginTop || '0px'
|
|
if (top < 0) {
|
|
elem.scrollIntoView({
|
|
behavior: window.matchMedia('(prefers-reduced-motion: no-preference)').matches
|
|
? 'smooth'
|
|
: 'instant',
|
|
})
|
|
}
|
|
}
|
|
|
|
onClick?.(id)
|
|
},
|
|
[!!stickyTabList, inView, onClick]
|
|
)
|
|
|
|
return (
|
|
<Component
|
|
{...props}
|
|
onClick={onClickInternal}
|
|
refs={{ base: observedRef, list: stickyRef }}
|
|
/>
|
|
)
|
|
}
|
|
|
|
export { withSticky }
|