ant-design/site/theme/template/Resources/AffixTabs.tsx
2020-02-28 17:59:06 +08:00

89 lines
2.3 KiB
TypeScript

import * as React from 'react';
import classNames from 'classnames';
import throttle from 'lodash/throttle';
import { Tabs } from 'antd';
import scrollTo from '../../../../components/_util/scrollTo';
import './AffixTabs.less';
const VIEW_BALANCE = 32;
const { TabPane } = Tabs;
export default () => {
const containerRef = React.useRef<HTMLDivElement>(null);
const idsRef = React.useRef<string[]>([]);
const [loaded, setLoaded] = React.useState(false);
const [fixedId, setFixedId] = React.useState<string | null>(null);
function scrollToId(id: string) {
const newTop =
document.getElementById(id)!.offsetTop - containerRef.current!.offsetHeight - VIEW_BALANCE;
scrollTo(newTop);
}
React.useEffect(() => {
idsRef.current = Array.from(document.querySelectorAll('h2[id]')).map(({ id }) => id);
setLoaded(true);
}, []);
React.useEffect(() => {
const hashId = decodeURIComponent((location.hash || '').slice(1));
if (hashId) {
scrollToId(hashId);
}
}, [loaded]);
const onSyncAffix = React.useMemo(() => {
function doSync() {
const scrollY = window.scrollY;
const containerHeight = containerRef.current!.offsetHeight;
for (let i = idsRef.current.length - 1; i >= 0; i -= 1) {
const id = idsRef.current[i];
const current = document.getElementById(id)!;
const offsetTop = current.offsetTop - containerHeight - VIEW_BALANCE;
if (offsetTop <= scrollY) {
setFixedId(id);
return;
}
}
setFixedId(null);
}
return throttle(doSync);
}, []);
React.useEffect(() => {
window.addEventListener('scroll', onSyncAffix);
window.addEventListener('resize', onSyncAffix);
onSyncAffix();
return () => {
window.removeEventListener('scroll', onSyncAffix);
window.removeEventListener('resize', onSyncAffix);
};
}, []);
return (
<div
className={classNames('resource-affix-tabs', {
'resource-affix-tabs-fixed': fixedId,
})}
ref={containerRef}
>
<Tabs
activeKey={fixedId || undefined}
onChange={key => {
scrollToId(key);
}}
>
{idsRef.current.map(id => (
<TabPane key={id} tab={id.replace(/-/g, ' ')} />
))}
</Tabs>
</div>
);
};