82 lines
3.4 KiB
JavaScript
82 lines
3.4 KiB
JavaScript
/*!---------------------------------------------------------------------------------------------
|
|
* Copyright (c) StackBlitz. All rights reserved.
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
import * as React from "react";
|
|
import { createContext, useContext, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, } from "react";
|
|
import { useStickToBottom, } from "./useStickToBottom.js";
|
|
const StickToBottomContext = createContext(null);
|
|
const useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
|
|
export function StickToBottom({ instance, children, resize, initial, mass, damping, stiffness, targetScrollTop: currentTargetScrollTop, contextRef, ...props }) {
|
|
const customTargetScrollTop = useRef(null);
|
|
const targetScrollTop = React.useCallback((target, elements) => {
|
|
const get = context?.targetScrollTop ?? currentTargetScrollTop;
|
|
return get?.(target, elements) ?? target;
|
|
}, [currentTargetScrollTop]);
|
|
const defaultInstance = useStickToBottom({
|
|
mass,
|
|
damping,
|
|
stiffness,
|
|
resize,
|
|
initial,
|
|
targetScrollTop,
|
|
});
|
|
const { scrollRef, contentRef, scrollToBottom, stopScroll, isAtBottom, escapedFromLock, state, } = instance ?? defaultInstance;
|
|
const context = useMemo(() => ({
|
|
scrollToBottom,
|
|
stopScroll,
|
|
scrollRef,
|
|
isAtBottom,
|
|
escapedFromLock,
|
|
contentRef,
|
|
state,
|
|
get targetScrollTop() {
|
|
return customTargetScrollTop.current;
|
|
},
|
|
set targetScrollTop(targetScrollTop) {
|
|
customTargetScrollTop.current = targetScrollTop;
|
|
},
|
|
}), [
|
|
scrollToBottom,
|
|
isAtBottom,
|
|
contentRef,
|
|
scrollRef,
|
|
stopScroll,
|
|
escapedFromLock,
|
|
state,
|
|
]);
|
|
useImperativeHandle(contextRef, () => context, [context]);
|
|
useIsomorphicLayoutEffect(() => {
|
|
if (!scrollRef.current) {
|
|
return;
|
|
}
|
|
if (getComputedStyle(scrollRef.current).overflow === "visible") {
|
|
scrollRef.current.style.overflow = "auto";
|
|
}
|
|
}, []);
|
|
return (React.createElement(StickToBottomContext.Provider, { value: context },
|
|
React.createElement("div", { ...props }, typeof children === "function" ? children(context) : children)));
|
|
}
|
|
(function (StickToBottom) {
|
|
function Content({ children, scrollClassName, ...props }) {
|
|
const context = useStickToBottomContext();
|
|
return (React.createElement("div", { ref: context.scrollRef, style: {
|
|
height: "100%",
|
|
width: "100%",
|
|
scrollbarGutter: "stable both-edges",
|
|
}, className: scrollClassName },
|
|
React.createElement("div", { ...props, ref: context.contentRef }, typeof children === "function" ? children(context) : children)));
|
|
}
|
|
StickToBottom.Content = Content;
|
|
})(StickToBottom || (StickToBottom = {}));
|
|
/**
|
|
* Use this hook inside a <StickToBottom> component to gain access to whether the component is at the bottom of the scrollable area.
|
|
*/
|
|
export function useStickToBottomContext() {
|
|
const context = useContext(StickToBottomContext);
|
|
if (!context) {
|
|
throw new Error("use-stick-to-bottom component context must be used within a StickToBottom component");
|
|
}
|
|
return context;
|
|
}
|