@yamada-ui/scroll-area
Version:
Yamada UI scroll area component
181 lines (179 loc) • 5.47 kB
JavaScript
"use client"
// src/scroll-area.tsx
import {
forwardRef,
omitThemeProps,
ui,
useComponentStyle
} from "@yamada-ui/core";
import {
cx,
dataAttr,
handlerAll,
isMac,
merge,
mergeRefs,
useSafeLayoutEffect,
vendor
} from "@yamada-ui/utils";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { jsx } from "react/jsx-runtime";
var neverStyles = {
"&::-webkit-scrollbar": { display: "none" },
scrollbarWidth: "none",
_scrollbar: { display: "none" }
};
var hiddenStyles = {
"&::-webkit-scrollbar-thumb": { bg: "transparent" },
"&::-webkit-scrollbar-track": { bg: "transparent" },
"@-moz-document url-prefix()": {
scrollbarColor: "transparent transparent",
_dark: {
scrollbarColor: "transparent transparent"
},
_light: {
scrollbarColor: "transparent transparent"
}
},
_dark: {
"&::-webkit-scrollbar-thumb": { bg: "transparent" },
"&::-webkit-scrollbar-track": { bg: "transparent" },
_scrollbarThumb: { bg: "transparent" },
_scrollbarTrack: { bg: "transparent" }
},
_light: {
"&::-webkit-scrollbar-thumb": { bg: "transparent" },
"&::-webkit-scrollbar-track": { bg: "transparent" },
_scrollbarThumb: { bg: "transparent" },
_scrollbarTrack: { bg: "transparent" }
},
_scrollbarThumb: { bg: "transparent" },
_scrollbarTrack: { bg: "transparent" }
};
var ScrollArea = forwardRef((props, ref) => {
const [styles, mergedProps] = useComponentStyle("ScrollArea", props);
const {
type = "hover",
overflow = "overlay",
scrollHideDelay = 1e3,
onScrollPositionChange,
...rest
} = omitThemeProps(mergedProps);
const [isHovered, setIsHovered] = useState(false);
const [isScrolling, setIsScrolling] = useState(false);
const isAlways = type === "always";
const isNever = type === "never";
const isSafari = isMac() && vendor(/apple/i);
const hoverTimeout = useRef(void 0);
const scrollTimeout = useRef(void 0);
const scrollAreaRef = useRef(null);
const scrollPosition = useRef({ x: 0, y: 0 });
useSafeLayoutEffect(() => {
if (!scrollAreaRef.current || !isSafari) return;
scrollAreaRef.current.scrollLeft = scrollPosition.current.x;
scrollAreaRef.current.scrollTop = scrollPosition.current.y;
});
const onMouseEnter = useCallback(() => {
if (type !== "hover") return;
clearTimeout(hoverTimeout.current);
setIsHovered(true);
}, [type]);
const onMouseLeave = useCallback(() => {
if (type !== "hover") return;
hoverTimeout.current = setTimeout(
() => setIsHovered(false),
scrollHideDelay
);
}, [scrollHideDelay, type]);
const onScroll = useCallback(
(ev) => {
const el = ev.target;
const { scrollLeft: x, scrollTop: y } = el;
const { x: prevX, y: prevY } = scrollPosition.current;
const isEqual = Math.abs(x - prevX) <= 5 && Math.abs(y - prevY) <= 5;
onScrollPositionChange == null ? void 0 : onScrollPositionChange({ x, y });
scrollPosition.current = { x, y };
if (type !== "scroll" || isEqual) return;
if (!isScrolling) setIsScrolling(true);
clearTimeout(scrollTimeout.current);
scrollTimeout.current = setTimeout(
() => setIsScrolling(false),
scrollHideDelay
);
},
[isScrolling, onScrollPositionChange, scrollHideDelay, type]
);
useEffect(() => {
return () => {
if (hoverTimeout.current) clearTimeout(hoverTimeout.current);
if (scrollTimeout.current) clearTimeout(scrollTimeout.current);
};
}, []);
const css = useMemo(() => {
const baseStyle = { overflow, ...styles };
if (isNever) {
return merge(baseStyle, neverStyles);
} else {
return merge(
baseStyle,
!isAlways && !isHovered && !isScrolling ? hiddenStyles : {}
);
}
}, [isAlways, isHovered, isNever, isScrolling, overflow, styles]);
const computedProps = useMemo(
() => ({
__css: css,
...rest,
onMouseEnter: handlerAll(rest.onMouseEnter, onMouseEnter),
onMouseLeave: handlerAll(rest.onMouseLeave, onMouseLeave),
onScroll: handlerAll(rest.onScroll, onScroll)
}),
[css, onMouseEnter, onMouseLeave, onScroll, rest]
);
if (isSafari) {
const componentKey = `${isHovered}-${isScrolling}`;
return /* @__PURE__ */ jsx(
InternalScrollArea,
{
ref: mergeRefs(ref, scrollAreaRef),
"data-hovered": dataAttr(isHovered),
"data-key": componentKey,
"data-scrolling": dataAttr(isScrolling),
...computedProps
},
componentKey
);
} else {
return /* @__PURE__ */ jsx(
InternalScrollArea,
{
ref,
"data-hovered": dataAttr(isHovered),
"data-scrolling": dataAttr(isScrolling),
...computedProps
}
);
}
});
ScrollArea.displayName = "ScrollArea";
ScrollArea.__ui__ = "ScrollArea";
var InternalScrollArea = forwardRef(
({ className, children, innerProps, ...rest }, ref) => {
return /* @__PURE__ */ jsx(
ui.div,
{
ref,
className: cx("ui-scroll-area", className),
tabIndex: 0,
...rest,
children: innerProps ? /* @__PURE__ */ jsx(ui.div, { className: "ui-scroll-area__inner", ...innerProps, children }) : children
}
);
}
);
InternalScrollArea.displayName = "InternalScrollArea";
InternalScrollArea.__ui__ = "InternalScrollArea";
export {
ScrollArea
};
//# sourceMappingURL=chunk-JXGXFEFI.mjs.map