@nutui/nutui-react
Version:
京东风格的轻量级移动端 React 组件库,支持一套代码生成 H5 和小程序
135 lines (134 loc) • 5.45 kB
JavaScript
import { _ as __rest } from "./tslib.es6.js";
import React__default, { useRef, useEffect, useState, useCallback } from "react";
import classNames from "classnames";
import { g as getScrollParent } from "./get-scroll-parent.js";
import { g as getRect } from "./use-client-rect.js";
import { C as ComponentDefaults } from "./typings.js";
function useWatch(dep, callback, config = { immediate: false }) {
const { immediate } = config;
const prev = useRef();
const inited = useRef(false);
const stop = useRef(false);
useEffect(() => {
const execute = () => callback(prev.current);
if (!stop.current) {
if (!inited.current) {
inited.current = true;
if (immediate) {
execute();
}
} else {
execute();
}
prev.current = dep;
}
}, [dep]);
return () => {
stop.current = true;
};
}
const defaultProps = Object.assign(Object.assign({}, ComponentDefaults), { position: "top", threshold: 0, zIndex: 900 });
const classPrefix = "nut-sticky";
const Sticky = (props) => {
const _a = Object.assign(Object.assign({}, defaultProps), props), { position, zIndex, children, container, style, className, threshold, onChange } = _a, rest = __rest(_a, ["position", "zIndex", "children", "container", "style", "className", "threshold", "onChange"]);
const stickyRef = useRef(null);
const rootRef = useRef(null);
const [isFixed, setIsFixed] = useState(false);
const [stickyStyle, setStickyStyle] = useState({
[position]: `${threshold}px`,
zIndex
});
useEffect(() => {
setStickyStyle(Object.assign(Object.assign({}, stickyStyle), { [position]: `${threshold}px`, zIndex }));
}, [threshold, position, zIndex]);
const [rootStyle, setRootStyle] = useState({});
const getElement = useCallback(() => {
return getScrollParent(rootRef.current);
}, []);
useEffect(() => {
if (position === "top")
return;
const containerEle = container && container.current;
const rootEle = rootRef.current;
const stickyEle = stickyRef.current;
if (!rootEle && !containerEle)
return;
const rootRect = getRect(rootEle);
const containerRect = getRect(containerEle);
const clientHeight = document.documentElement.clientHeight;
const stickyRect = getRect(stickyEle);
let fixed = clientHeight - threshold < rootRect.bottom;
if (containerEle) {
fixed = containerRect.bottom > clientHeight - threshold - stickyRect.height && clientHeight - threshold - stickyRect.height > containerRect.top;
}
const defaultPostVal = fixed ? "fixed" : "inherit";
setStickyStyle((prev) => {
return Object.assign(Object.assign({}, prev), { position: defaultPostVal });
});
setIsFixed(fixed);
}, [position, container, threshold]);
const handleScroll = useCallback(() => {
const containerEle = container && container.current;
const rootEle = rootRef.current;
const stickyEle = stickyRef.current;
if (!rootEle && !containerEle)
return;
const rootRect = getRect(rootEle);
const stickyRect = getRect(stickyEle);
const containerRect = getRect(containerEle);
if (rootRect.height) {
setRootStyle((prev) => Object.assign(Object.assign({}, prev), { height: rootRect.height }));
}
const getFixed = () => {
let fixed2 = false;
if (position === "top") {
fixed2 = containerEle ? threshold > rootRect.top && containerRect.bottom > 0 : threshold > rootRect.top;
} else {
const clientHeight = document.documentElement.clientHeight;
fixed2 = containerEle ? containerRect.bottom > 0 && clientHeight - threshold - stickyRect.height > containerRect.top : clientHeight - threshold < rootRect.bottom;
}
return {
position: fixed2 ? "fixed" : "inherit",
fixed: fixed2
};
};
const getTransform = () => {
if (position === "top" && containerEle) {
const diff = containerRect.bottom - threshold - stickyRect.height;
const transform = diff < 0 ? diff : 0;
return { transform: `translate3d(0, ${transform}px, 0)` };
}
if (position === "bottom" && containerEle) {
const clientHeight = document.documentElement.clientHeight;
const diff = containerRect.bottom - (clientHeight - threshold);
const transform = diff < 0 ? diff : 0;
return { transform: `translate3d(0, ${transform}px, 0)` };
}
return {};
};
const fixed = getFixed();
setStickyStyle((prev) => {
return Object.assign(Object.assign(Object.assign({}, prev), getTransform()), { position: fixed.position });
});
setIsFixed(fixed.fixed);
}, [position, threshold, container]);
useWatch(isFixed, () => {
onChange && onChange(isFixed);
});
useEffect(() => {
const el = getElement();
el === null || el === void 0 ? void 0 : el.addEventListener("scroll", handleScroll, false);
return () => {
el === null || el === void 0 ? void 0 : el.removeEventListener("scroll", handleScroll);
};
}, [getElement, handleScroll]);
return React__default.createElement(
"div",
Object.assign({ ref: rootRef, style: Object.assign(Object.assign({}, style), rootStyle), className: classNames(classPrefix, className) }, rest),
React__default.createElement("div", { className: "nut-sticky-box", ref: stickyRef, style: stickyStyle }, children)
);
};
Sticky.displayName = "NutSticky";
export {
Sticky as default
};