UNPKG

@nutui/nutui-react

Version:

京东风格的轻量级移动端 React 组件库,支持一套代码生成 H5 和小程序

135 lines (134 loc) 5.45 kB
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 };