ScrollArea
ScrollArea
커스텀 스크롤바가 있는 영역 컴포넌트예요. 콘텐츠를 스크롤 가능한 컨테이너로 만들어요.
출처: 문서
본문
사용법 (Usage)
ScrollArea 컴포넌트는 다음 prop을 지원해요.
type– 스크롤바 동작 정의:hover– hover 시 스크롤바 표시scroll– 스크롤 시 스크롤바 표시auto–overflow: auto와 유사, 콘텐츠가 넘칠 때 항상 스크롤바 표시always–auto와 같지만 콘텐츠가 넘치는지와 무관하게 항상 스크롤바 표시never– 스크롤바 항상 숨김
offsetScrollbars– 스크롤바를 오프셋하는 패딩을 추가 (옵션:true는 두 스크롤바 모두,x는 가로만,y는 세로만,present는 스크롤바가 보일 때만)scrollbarSize– 스크롤바 크기, 스크롤바와 썸의 너비/높이 제어scrollHideDelay– 스크롤바를 숨기는 지연(ms),type이hover또는scroll일 때만 적용overscrollBehavior– 뷰포트의 overscroll-behavior 제어
import { ScrollArea } from '@mantine/core';
function Demo() {
return (
<ScrollArea h={250}>
{/* ... content */}
</ScrollArea>
);
}
가로 스크롤바 (Horizontal scrollbars)
scrollbars="x" prop으로 가로 스크롤바를 활성화할 수 있어요.
import { ScrollArea, Box } from '@mantine/core';
function Demo() {
return (
<ScrollArea w="100%" scrollbars="x">
<Box w="max-content">{/* ... wide content */}</Box>
</ScrollArea>
);
}
가로 스크롤바 비활성화 (Disable horizontal scrollbars)
가로 스크롤바를 비활성화하려면 scrollbars="y" prop을 설정해요.
import { ScrollArea } from '@mantine/core';
function Demo() {
return <ScrollArea scrollbars="y">{/* ... content */}</ScrollArea>;
}
세로 스크롤바 위치 (Vertical scrollbar position)
기본적으로 세로 스크롤바는 인라인 끝 가장자리를 따라가요. LTR에서는 오른쪽, RTL에서는 왼쪽에 렌더링돼요. verticalScrollbarPosition prop을 left 또는 right로 설정하면 방향과 무관하게 세로 스크롤바를 물리적 측면에 고정할 수 있어요.
이는 대부분의 데스크톱 소프트웨어(Windows, Office, Gmail 등)의 동작과 일치하게 세로 스크롤바를 오른쪽에 유지하려는 RTL 애플리케이션에 유용해요. 이 prop은 오프셋 패딩, 모서리, 가로 스크롤바 간격도 다시 정렬해서 offsetScrollbars와 scrollbars="xy"와 함께 올바르게 동작해요. prop을 생략하면 기본 방향 기반 동작이 유지돼요.
import { ScrollArea } from '@mantine/core';
function Demo() {
return <ScrollArea verticalScrollbarPosition="right">{/* ... content */}</ScrollArea>;
}
스크롤 위치 변경 구독 (Subscribe to scroll position changes)
onScrollPositionChange 함수를 설정하면 스크롤 위치 변경을 구독할 수 있어요. 사용자가 x, y 좌표로 스크롤할 때마다 호출돼요.
import { useState } from 'react';
import { Text, ScrollArea, Code, Box } from '@mantine/core';
function Demo() {
const [scrollPosition, onScrollPositionChange] = useState({ x: 0, y: 0 });
return (
<>
<ScrollArea onScrollPositionChange={onScrollPositionChange}>
{/* ... content */}
</ScrollArea>
<Code>Scroll position: {`{ x: ${scrollPosition.x}, y: ${scrollPosition.y} }`}</Code>
</>
);
}
스크롤 경계 콜백 (Scroll boundary callbacks)
ScrollArea 컴포넌트는 스크롤이 경계에 도달할 때 트리거되는 콜백을 지원해요.
import { useState } from 'react';
import { Badge, Box, Group, ScrollArea, Stack, Text } from '@mantine/core';
function Demo() {
const [topReached, setTopReached] = useState(0);
const [bottomReached, setBottomReached] = useState(0);
const [leftReached, setLeftReached] = useState(0);
const [rightReached, setRightReached] = useState(0);
return (
<>
<Badge>Top: {topReached}</Badge>
<Badge>Bottom: {bottomReached}</Badge>
<Badge>Left: {leftReached}</Badge>
<Badge>Right: {rightReached}</Badge>
<ScrollArea
onTopReached={() => setTopReached((c) => c + 1)}
onBottomReached={() => setBottomReached((c) => c + 1)}
onLeftReached={() => setLeftReached((c) => c + 1)}
onRightReached={() => setRightReached((c) => c + 1)}
scrollbars="xy"
>
<Box w="max-content">
{Array(50)
.fill(0)
.map((_, i) => (
<Text key={i}>Line {i + 1} - This is a long line that requires horizontal scrolling</Text>
))}
</Box>
</ScrollArea>
</>
);
}
위치로 스크롤 (Scroll to position)
프로그램 방식으로 어떤 위치로든 스크롤하려면 viewportRef prop으로 뷰포트 요소 ref를 얻고 scrollTo 메서드를 호출해요.
import { useRef } from 'react';
import { ScrollArea, Button, Stack, Group } from '@mantine/core';
function Demo() {
const viewport = useRef<HTMLDivElement>(null);
const scrollToBottom = () =>
viewport.current!.scrollTo({ top: viewport.current!.scrollHeight, behavior: 'smooth' });
const scrollToCenter = () =>
viewport.current!.scrollTo({ top: viewport.current!.scrollHeight / 2, behavior: 'smooth' });
const scrollToTop = () => viewport.current!.scrollTo({ top: 0, behavior: 'smooth' });
return (
<Stack align="center">
<ScrollArea viewportRef={viewport} h={250}>
{/* ... content */}
</ScrollArea>
<Group>
<Button onClick={scrollToBottom} variant="default">Scroll to bottom</Button>
<Button onClick={scrollToCenter} variant="default">Scroll to center</Button>
<Button onClick={scrollToTop} variant="default">Scroll to top</Button>
</Group>
</Stack>
);
}
시작 스크롤 위치 (Start scroll position)
startScrollPosition prop으로 컴포넌트가 마운트될 때 초기 스크롤 위치를 설정할 수 있어요. useEffect와 함께 viewportRef를 사용하는 것과 달리 이 방식은 (0, 0) 위치에서 콘텐츠가 깜빡이는 것을 피할 수 있어요.
import { ScrollArea } from '@mantine/core';
function Demo() {
return (
<ScrollArea h={250} startScrollPosition={{ x: 0, y: 120 }}>
{/* ... content */}
</ScrollArea>
);
}
Styles API
ScrollArea는 Styles API를 지원해요. classNames prop으로 컴포넌트의 내부 요소에 스타일을 추가할 수 있어요.
요소를 뷰 안으로 스크롤 (Scroll element into view)
scrollIntoView를 사용해 요소를 뷰포트 안으로 스크롤할 수 있어요 (자세한 예시는 원문의 groceries 데모 참고).
viewportRef.current
?.querySelectorAll('[data-list-item]')
?.[nextIndex]?.scrollIntoView({ block: 'nearest' });
ScrollArea.Autosize
ScrollArea.Autosize 컴포넌트는 주어진 max-height에 도달할 때 스크롤 가능한 컨테이너를 만들 수 있게 해요. 세로 넘침이 발생할 때 감지하는 콜백도 지원해요.
onOverflowChange– 콘텐츠가 max-height를 초과해서 컨테이너가 스크롤 가능해지거나 아니게 될 때 트리거
import { useCounter } from '@mantine/hooks';
import { ScrollArea, Button, Group } from '@mantine/core';
const lorem =
'Lorem ipsum, dolor sit amet consectetur adipisicing elit. Dicta perspiciatis reiciendis voluptate eaque itaque quos. Natus iure tenetur libero, reprehenderit ad, sequi, in aliquam eos necessitatibus expedita delectus veniam culpa!';
function Demo() {
const [count, handlers] = useCounter(3, { min: 0, max: 10 });
const content = Array(count)
.fill(0)
.map((_, index) => <p key={index}>{lorem}</p>);
return (
<>
<ScrollArea.Autosize mah={200} maw={300} mx="auto">
{content}
</ScrollArea.Autosize>
<Group justify="center" mt="md">
<Button variant="default" onClick={() => handlers.decrement()}>Remove paragraph</Button>
<Button variant="default" onClick={() => handlers.increment()}>Add paragraph</Button>
</Group>
</>
);
}
ScrollArea.Autosize with Popover
ScrollArea.Autosize를 Popover 안에서 사용해 검색 가능한 목록을 만들 수 있어요 (원문의 groceries 데모 참고).