use-splitter
use-splitter
크기 조절 가능한 패널(splitter) 레이아웃을 관리하는 훅이에요. 키보드 접근성과 터치 지원을 포함한 리사이즈 핸들 동작을 제공해요.
출처: 문서
본문
사용법 (Usage)
useSplitter 훅에 패널 설정을 넘겨 두 개 이상의 패널을 만들 수 있어요. 각 패널의 defaultSize, min, max 등을 지정할 수 있어요:
import { useState } from 'react';
import { DotsSixVerticalIcon } from '@phosphor-icons/react';
import { Button, Group, Text } from '@mantine/core';
import { useSplitter } from '@mantine/hooks';
function Demo() {
const [sizes, setSizes] = useState([50, 50]);
const splitter = useSplitter({
panels: [
{ defaultSize: 50, min: 20 },
{ defaultSize: 50, min: 20 },
],
sizes,
onSizeChange: setSizes,
});
return (
<>
Panel A ({Math.round(splitter.sizes[0] as number)}%)
Panel B ({Math.round(splitter.sizes[1] as number)}%)
Current sizes: [{sizes.map((s) => Math.round(s)).join(', ')}]
setSizes([30, 70])}>30 / 70
setSizes([50, 50])}>50 / 50
setSizes([70, 30])}>70 / 30
);
}
키보드 지원 (Keyboard support)
핸들은 WAI-ARIA Window Splitter 패턴을 따르고 있어요:
| Key | Action |
|---|---|
| ArrowLeft/ArrowRight | Resize by step (horizontal) |
| ArrowUp/ArrowDown | Resize by step (vertical) |
| Shift + Arrow | Resize by shiftStep |
| Home | Shrink panel before handle to its min |
| End | Grow panel before handle to its max |
| Enter | Toggle collapse of the smaller adjacent collapsible panel |
터치 지원 (Touch support)
이 훅은 마우스와 터치를 모두 자동으로 처리하는 Pointer Events API를 사용해요. 터치 드래그가 스크롤로 해석되지 않도록 핸들 엘리먼트에 touch-action: none을 설정해요:
.handle {
touch-action: none;
}
정의 (Definition)
/** A bare number/`%` is a flexible size, `px`/`rem` is a fixed size */
type SplitterPaneSize = number | `${number}%` | `${number}px` | `${number}rem`;
/** A bare number/`%` is a percentage of the container, `px`/`rem` is resolved to pixels */
type SplitterStep = number | `${number}%` | `${number}px` | `${number}rem`;
interface UseSplitterPanel {
/** Initial size, a `number`/`%` is flexible, `px`/`rem` is fixed. A bare number is a percentage. */
defaultSize: SplitterPaneSize;
/** Minimum size in the same units as `defaultSize`, `0` by default */
min?: SplitterPaneSize;
/** Maximum size in the same units as `defaultSize`, no limit by default */
max?: SplitterPaneSize;
/** Whether this panel can be collapsed, `false` by default */
collapsible?: boolean;
/** Size below which the panel snaps to collapsed, defaults to `min` */
collapseThreshold?: SplitterPaneSize;
}
/** Panel config resolved to numeric units (percent or pixels) */
interface UseSplitterResolvedPanel {
defaultSize: number;
min?: number;
max?: number;
collapsible?: boolean;
collapseThreshold?: number;
}
type UseSplitterRedistributeFn = (input: {
sizes: number[];
panels: UseSplitterResolvedPanel[];
handleIndex: number;
delta: number;
}) => number[];
interface UseSplitterOptions {
/** Panel configuration array (minimum 2 panels) */
panels: UseSplitterPanel[];
/** Layout direction, `'horizontal'` by default */
orientation?: 'horizontal' | 'vertical';
/** Controlled sizes, each value keeps the unit it was declared in */
sizes?: SplitterPaneSize[];
/** Called during resize with updated sizes, each value keeps its declared unit */
onSizeChange?: (sizes: SplitterPaneSize[]) => void;
/** Called when drag starts */
onResizeStart?: (handleIndex: number) => void;
/** Called when drag ends */
onResizeEnd?: (handleIndex: number, sizes: SplitterPaneSize[]) => void;
/** Called when a panel collapses or expands */
onCollapseChange?: (panelIndex: number, collapsed: boolean) => void;
/** How to borrow space from non-adjacent panels */
redistribute?: 'nearest' | 'equal' | UseSplitterRedistributeFn;
/** Keyboard step size, a `number`/`%` is a percentage, `px`/`rem` is pixels, `1` by default */
step?: SplitterStep;
/** Shift+arrow step size, a `number`/`%` is a percentage, `px`/`rem` is pixels, `10` by default */
shiftStep?: SplitterStep;
/** Text direction for keyboard nav, `'ltr'` by default */
dir?: 'ltr' | 'rtl';
/** Enable/disable the hook, `true` by default */
enabled?: boolean;
}
interface UseSplitterReturnValue {
/** Ref callback for the container element */
ref: React.RefCallback<HTMLDivElement>;
/** Current panel sizes, each value keeps the unit it was declared in */
sizes: SplitterPaneSize[];
/** Whether sizes are tracked in pixels because any pane size, `min`, `max`, `step`, `shiftStep`
* or `collapseThreshold` uses a fixed `px`/`rem` unit */
pixelMode: boolean;
/** Which panels are currently collapsed */
collapsed: boolean[];
/** Index of handle being dragged, or -1 */
activeHandle: number;
/** Get props to spread on each resize handle */
getHandleProps: (input: { index: number }) => HandleProps;
/** Programmatically set sizes */
setSizes: (sizes: SplitterPaneSize[]) => void;
/** Collapse a panel */
collapse: (panelIndex: number) => void;
/** Expand a collapsed panel */
expand: (panelIndex: number) => void;
/** Toggle collapse of a panel */
toggleCollapse: (panelIndex: number) => void;
}
function useSplitter(
options: UseSplitterOptions
): UseSplitterReturnValue
내보내는 타입 (Exported types)
UseSplitterPanel, UseSplitterOptions, UseSplitterReturnValue, UseSplitterRedistributeInput,
UseSplitterRedistributeFn, UseSplitterResolvedPanel, SplitterPaneSize 그리고 SplitterStep 타입은
@mantine/hooks 패키지에서 내보내져요:
import type {
SplitterPaneSize,
SplitterStep,
UseSplitterPanel,
UseSplitterOptions,
UseSplitterReturnValue,
UseSplitterRedistributeInput,
UseSplitterRedistributeFn,
UseSplitterResolvedPanel,
} from '@mantine/hooks';