zent
Version:
一套前端设计语言和基于React的实现
101 lines (100 loc) • 4.6 kB
JavaScript
import { __assign } from "tslib";
import { jsx as _jsx } from "react/jsx-runtime";
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import isNil from '../utils/isNil';
import { smoothScroll } from '../utils/scroll';
import { ElevatorContext } from './context';
import { ElevatorAnchor } from './ElevatorAnchor';
import { ElevatorLinks } from './ElevatorLinks';
var SCROLL_DURATION = 200;
var getDefaultContainer = function () { return window; };
export var Elevator = function (_a) {
var children = _a.children, onChange = _a.onChange, targetOffset = _a.targetOffset, getContainer = _a.getContainer, defaultActiveLink = _a.defaultActiveLink, propOffsetTop = _a.offsetTop, propActiveLink = _a.activeLink;
var getContainerResult = getContainer === null || getContainer === void 0 ? void 0 : getContainer();
var _b = useState(new Map()), anchorElementsMap = _b[0], setAnchorElementsMap = _b[1];
var _c = useState(''), internalActiveLink = _c[0], setInternalActiveLink = _c[1];
var activeLink = useMemo(function () { return (!isNil(propActiveLink) ? propActiveLink : internalActiveLink); }, [propActiveLink, internalActiveLink]);
var isScrolling = useRef(false);
var handleAnchorEnter = function (link) {
setInternalActiveLink(link);
if (isScrolling.current || link === activeLink)
return;
onChange === null || onChange === void 0 ? void 0 : onChange(link, activeLink);
};
var offsetTop = useMemo(function () {
var containerHeight = getDefaultContainer().innerHeight;
if (getContainerResult) {
containerHeight = getContainerResult.getBoundingClientRect().height;
}
var defaultOffsetTop = containerHeight / 2;
return !isNil(propOffsetTop)
? containerHeight - (propOffsetTop || 1)
: defaultOffsetTop;
}, [propOffsetTop, getContainerResult]);
var handleRegisterAnchor = useCallback(function (link, el) {
setAnchorElementsMap(function (prev) {
var map = new Map(prev);
map.set(link, el);
return map;
});
}, []);
var handleUnRegister = useCallback(function (link) {
setAnchorElementsMap(function (prev) {
var map = new Map(prev);
map.delete(link);
return map;
});
}, []);
var handleScrollToLink = function (link, controlled) {
if (controlled === void 0) { controlled = false; }
var el = anchorElementsMap.get(link);
if (!el)
return;
var bounds = el.getBoundingClientRect();
var container = getDefaultContainer();
var containerTop = 0;
var scrollTop = container.scrollY;
var scrollLeft = container.scrollX;
if (getContainerResult) {
container = getContainerResult;
var containerBounds = container.getBoundingClientRect();
containerTop = containerBounds.top;
scrollTop = container.scrollTop;
scrollLeft = container.scrollLeft;
}
var scrollTopTarget = bounds.top - containerTop + scrollTop - (targetOffset || 0);
isScrolling.current = true;
smoothScroll(container, scrollLeft, scrollTopTarget, SCROLL_DURATION).then(function () {
isScrolling.current = false;
!controlled && (onChange === null || onChange === void 0 ? void 0 : onChange(link, activeLink));
});
};
var handleLinkClick = function (link) {
if (isNil(propActiveLink)) {
handleScrollToLink(link);
}
else if (link !== activeLink) {
onChange === null || onChange === void 0 ? void 0 : onChange(link, activeLink);
}
};
useEffect(function () {
if (!isNil(propActiveLink) && propActiveLink !== internalActiveLink) {
handleScrollToLink(propActiveLink, true);
}
if (isNil(propActiveLink) && defaultActiveLink) {
handleScrollToLink(defaultActiveLink);
}
}, [propActiveLink, anchorElementsMap, defaultActiveLink]);
return (_jsx(ElevatorContext.Provider, __assign({ value: {
activeLink: activeLink,
offsetTop: offsetTop,
getContainer: getContainer,
onLinkClick: handleLinkClick,
onAnchorEnter: handleAnchorEnter,
registerAnchor: handleRegisterAnchor,
unRegisterAnchor: handleUnRegister,
} }, { children: children }), void 0));
};
Elevator.Links = ElevatorLinks;
Elevator.Anchor = ElevatorAnchor;
export default Elevator;