스플리터

스플리터 (Splitter)

영역을 가로 또는 세로로 나누고, 각 영역의 크기를 자유롭게 드래그로 조절할 수 있게 해주는 컴포넌트입니다.

출처: 문서

본문

언제 사용하나요 (When To Use)

영역을 가로 또는 세로로 나눌 때 사용할 수 있습니다. 각 영역의 크기를 드래그로 자유롭게 조절해야 할 때, 그리고 영역의 최대·최소 너비와 높이를 지정해야 할 때 사용합니다.

예제 (Examples)

기본 (Basic)

패널 크기 초기화와 크기 제한을 설정합니다.

import React from 'react';
import { Flex, Splitter, Typography } from 'antd';

export const Desc: React.FC<Readonly<{ text?: string | number }>> = (props) => (
  <Flex justify="center" align="center" style={{ height: '100%' }}>
    <Typography.Title type="secondary" level={5} style={{ whiteSpace: 'nowrap' }}>
      {props.text}
    </Typography.Title>
  </Flex>
);

const App: React.FC = () => (
  <Splitter style={{ height: 200, boxShadow: '0 0 10px rgba(0, 0, 0, 0.1)' }}>
    <Splitter.Panel defaultSize="40%" min="20%" max="70%">
      <Desc text="First" />
    </Splitter.Panel>
    <Splitter.Panel>
      <Desc text="Second" />
    </Splitter.Panel>
  </Splitter>
);

export default App;

제어 모드 (Control mode)

스플리터의 크기를 제어합니다. 패널 중 하나가 resizable을 비활성화하면 드래그가 비활성화됩니다.

import React from 'react';
import { Button, Flex, Splitter, Switch, Typography } from 'antd';

const Desc: React.FC<Readonly<{ text?: string | number }>> = (props) => (
  <Flex justify="center" align="center" style={{ height: '100%' }}>
    <Typography.Title type="secondary" level={5} style={{ whiteSpace: 'nowrap' }}>
      {props.text}
    </Typography.Title>
  </Flex>
);

const App: React.FC = () => {
  const [sizes, setSizes] = React.useState<(number | string)[]>(['50%', '50%']);
  const [enabled, setEnabled] = React.useState(true);
  return (
    <Flex vertical gap="medium">
      <Splitter
        onResize={setSizes}
        style={{ height: 200, boxShadow: '0 0 10px rgba(0, 0, 0, 0.1)' }}
      >
        <Splitter.Panel size={sizes[0]} resizable={enabled}>
          <Desc text="First" />
        </Splitter.Panel>
        <Splitter.Panel size={sizes[1]}>
          <Desc text="Second" />
        </Splitter.Panel>
      </Splitter>
      <Flex gap="medium" justify="space-between">
        <Switch
          value={enabled}
          onChange={() => setEnabled(!enabled)}
          checkedChildren="Enabled"
          unCheckedChildren="Disabled"
        />
        <Button onClick={() => setSizes(['50%', '50%'])}>Reset</Button>
      </Flex>
    </Flex>
  );
};

export default App;

세로 (Vertical)

세로 레이아웃을 사용합니다.

import React from 'react';
import { Flex, Splitter, Typography } from 'antd';

const Desc: React.FC<Readonly<{ text?: string | number }>> = (props) => (
  <Flex justify="center" align="center" style={{ height: '100%' }}>
    <Typography.Title type="secondary" level={5} style={{ whiteSpace: 'nowrap' }}>
      {props.text}
    </Typography.Title>
  </Flex>
);

const App: React.FC = () => (
  <Splitter vertical style={{ height: 300, boxShadow: '0 0 10px rgba(0, 0, 0, 0.1)' }}>
    <Splitter.Panel>
      <Desc text="First" />
    </Splitter.Panel>
    <Splitter.Panel>
      <Desc text="Second" />
    </Splitter.Panel>
  </Splitter>
);

export default App;

접기 가능 (Collapsible)

collapsible을 설정해 접기를 활성화합니다. min을 통해 접혔을 때 드래그로 펼칠 수 있는 크기를 제한할 수 있습니다.

import React, { useState } from 'react';
import { Flex, Splitter, Switch, Typography } from 'antd';
import type { SplitterProps } from 'antd';

const Desc: React.FC<Readonly<{ text?: string | number }>> = (props) => (
  <Flex justify="center" align="center" style={{ height: '100%' }}>
    <Typography.Title type="secondary" level={5} style={{ whiteSpace: 'nowrap' }}>
      {props.text}
    </Typography.Title>
  </Flex>
);

const CustomSplitter: React.FC<Readonly<SplitterProps>> = ({ style, ...restProps }) => (
  <Splitter style={{ boxShadow: '0 0 10px rgba(0, 0, 0, 0.1)', ...style }} {...restProps}>
    <Splitter.Panel collapsible min="20%">
      <Desc text="First" />
    </Splitter.Panel>
    <Splitter.Panel collapsible>
      <Desc text="Second" />
    </Splitter.Panel>
  </Splitter>
);

const App: React.FC = () => {
  const [motion, setMotion] = useState(true);

  return (
    <Flex vertical gap="middle">
      <Flex gap="middle">
        <Switch
          checked={motion}
          onChange={setMotion}
          checkedChildren="motion"
          unCheckedChildren="motion"
        />
      </Flex>
      <CustomSplitter style={{ height: 200 }} collapsible={{ motion }} />
      <CustomSplitter style={{ height: 300 }} orientation="vertical" collapsible={{ motion }} />
    </Flex>
  );
};

export default App;

접기 아이콘 제어 (Control collapsible icons)

collapsible.showCollapsibleIcon을 설정해 접기 아이콘의 표시 모드를 제어합니다.

import React, { useState } from 'react';
import { Flex, Radio, Splitter, Typography } from 'antd';
import type { RadioChangeEvent } from 'antd';
import type { CheckboxGroupProps } from 'antd/es/checkbox';

const Desc: React.FC<Readonly<{ text?: string | number }>> = (props) => (
  <Flex justify="center" align="center" style={{ height: '100%' }}>
    <Typography.Title type="secondary" level={5} style={{ whiteSpace: 'nowrap' }}>
      {props.text}
    </Typography.Title>
  </Flex>
);

const options: CheckboxGroupProps<'auto' | boolean>['options'] = [
  { label: 'Auto', value: 'auto' },
  { label: 'True', value: true },
  { label: 'False', value: false },
];

const App: React.FC = () => {
  const [showIconMode, setShowIconMode] = useState<'auto' | boolean>(true);

  const onChange = (e: RadioChangeEvent) => {
    setShowIconMode(e.target.value);
  };

  return (
    <Flex vertical gap={20}>
      <Flex gap={5}>
        <p>ShowCollapsibleIcon: </p>
        <Radio.Group options={options} value={showIconMode} onChange={onChange} />
      </Flex>
      <Splitter style={{ height: 200, boxShadow: '0 0 10px rgba(0, 0, 0, 0.1)' }}>
        <Splitter.Panel
          collapsible={{ start: true, end: true, showCollapsibleIcon: showIconMode }}
          min="20%"
        >
          <Desc text="First" />
        </Splitter.Panel>
        <Splitter.Panel collapsible={{ start: true, end: true, showCollapsibleIcon: showIconMode }}>
          <Desc text="Second" />
        </Splitter.Panel>
        <Splitter.Panel collapsible={{ start: true, end: true, showCollapsibleIcon: showIconMode }}>
          <Desc text="Third" />
        </Splitter.Panel>
      </Splitter>
    </Flex>
  );
};

export default App;

여러 패널 (Multiple panels)

여러 개의 패널입니다.

import React from 'react';
import { Flex, Splitter, Typography } from 'antd';

const Desc: React.FC<Readonly<{ text?: string | number }>> = (props) => (
  <Flex justify="center" align="center" style={{ height: '100%' }}>
    <Typography.Title type="secondary" level={5} style={{ whiteSpace: 'nowrap' }}>
      Panel {props.text}
    </Typography.Title>
  </Flex>
);

const App: React.FC = () => (
  <Splitter style={{ height: 200, boxShadow: '0 0 10px rgba(0, 0, 0, 0.1)' }}>
    <Splitter.Panel collapsible>
      <Desc text={1} />
    </Splitter.Panel>
    <Splitter.Panel collapsible={{ start: true }}>
      <Desc text={2} />
    </Splitter.Panel>
    <Splitter.Panel>
      <Desc text={3} />
    </Splitter.Panel>
  </Splitter>
);

export default App;

복합 조합 (Complex combination)

복합 조합 패널로, 빠른 접기와 크기 변경 금지를 지원합니다.

import React from 'react';
import { Flex, Splitter, Typography } from 'antd';

const Desc: React.FC<Readonly<{ text?: string | number }>> = (props) => (
  <Flex justify="center" align="center" style={{ height: '100%' }}>
    <Typography.Title type="secondary" level={5} style={{ whiteSpace: 'nowrap' }}>
      {props.text}
    </Typography.Title>
  </Flex>
);

const App: React.FC = () => (
  <Splitter style={{ height: 300, boxShadow: '0 0 10px rgba(0, 0, 0, 0.1)' }}>
    <Splitter.Panel collapsible>
      <Desc text="Left" />
    </Splitter.Panel>
    <Splitter.Panel>
      <Splitter orientation="vertical">
        <Splitter.Panel>
          <Desc text="Top" />
        </Splitter.Panel>
        <Splitter.Panel>
          <Desc text="Bottom" />
        </Splitter.Panel>
      </Splitter>
    </Splitter.Panel>
  </Splitter>
);

export default App;

지연 (Lazy)

지연(lazy) 모드로, 드래그 중에는 크기가 즉시 업데이트되지 않고 놓을 때 업데이트됩니다.

import React from 'react';
import { Flex, Space, Splitter, Typography } from 'antd';

const Desc: React.FC<Readonly<{ text?: string | number }>> = (props) => (
  <Flex justify="center" align="center" style={{ height: '100%' }}>
    <Typography.Title type="secondary" level={5} style={{ whiteSpace: 'nowrap' }}>
      {props.text}
    </Typography.Title>
  </Flex>
);

const App: React.FC = () => (
  <Space vertical style={{ width: '100%' }}>
    <Splitter lazy style={{ height: 200, boxShadow: '0 0 10px rgba(0, 0, 0, 0.1)' }}>
      <Splitter.Panel defaultSize="40%" min="20%" max="70%">
        <Desc text="First" />
      </Splitter.Panel>
      <Splitter.Panel>
        <Desc text="Second" />
      </Splitter.Panel>
    </Splitter>
    <Splitter
      lazy
      orientation="vertical"
      style={{ height: 200, boxShadow: '0 0 10px rgba(0, 0, 0, 0.1)' }}
    >
      <Splitter.Panel defaultSize="40%" min="30%" max="70%">
        <Desc text="First" />
      </Splitter.Panel>
      <Splitter.Panel>
        <Desc text="Second" />
      </Splitter.Panel>
    </Splitter>
  </Space>
);

export default App;

커스터마이즈 (Customize)

핸들 요소와 스타일을 커스터마이즈합니다.

import React from 'react';
import {
  CaretDownOutlined,
  CaretLeftOutlined,
  CaretRightOutlined,
  CaretUpOutlined,
  ColumnWidthOutlined,
} from '@ant-design/icons';
import { ConfigProvider, Divider, Flex, Splitter, Typography } from 'antd';
import { createStyles } from 'antd-style';

const useStyles = createStyles(({ token }) => ({
  dragger: {
    '&::before': {
      backgroundColor: 'transparent !important',
      border: `${token.lineWidth}px dashed ${token.controlItemBgHover}`,
    },
    '&:hover::before': {
      border: `${token.lineWidth}px dashed ${token.colorPrimary}`,
    },
  },
  draggerActive: {
    '&::before': {
      border: `${token.lineWidth}px dashed ${token.colorPrimary}`,
    },
  },
  draggerIcon: {
    '&:hover': {
      color: token.colorPrimary,
    },
  },
  collapsibleIcon: {
    fontSize: token.fontSizeLG,
    color: token.colorTextDescription,

    '&:hover': {
      color: token.colorPrimary,
    },
  },
}));

const Desc: React.FC<Readonly<{ text?: string | number }>> = (props) => (
  <Flex justify="center" align="center" style={{ height: '100%' }}>
    <Typography.Title type="secondary" level={5} style={{ whiteSpace: 'nowrap' }}>
      {props.text}
    </Typography.Title>
  </Flex>
);

const App: React.FC = () => {
  const { styles } = useStyles();

  return (
    <ConfigProvider
      theme={{
        components: {
          Splitter: { splitBarSize: 1, splitTriggerSize: 16 },
        },
      }}
    >
      <Splitter
        style={{ height: 200, boxShadow: '0 0 10px rgba(0, 0, 0, 0.1)' }}
        draggerIcon={<ColumnWidthOutlined className={styles.draggerIcon} />}
        collapsible={{
          icon: {
            start: <CaretLeftOutlined className={styles.collapsibleIcon} />,
            end: <CaretRightOutlined className={styles.collapsibleIcon} />,
          },
        }}
      >
        <Splitter.Panel defaultSize="40%" min="20%" max="70%" collapsible>
          <Desc text="Panel 1" />
        </Splitter.Panel>

        <Splitter.Panel collapsible>
          <Desc text="Panel 2" />
        </Splitter.Panel>

        <Splitter.Panel resizable={false}>
          <Desc text="Panel 3" />
        </Splitter.Panel>
      </Splitter>

      <Divider />

      <Splitter
        orientation="vertical"
        classNames={{ dragger: { default: styles.dragger, active: styles.draggerActive } }}
        style={{ height: 200, boxShadow: '0 0 10px rgba(0, 0, 0, 0.1)' }}
        draggerIcon={null}
        collapsible={{
          icon: {
            start: <CaretUpOutlined className={styles.collapsibleIcon} />,
            end: <CaretDownOutlined className={styles.collapsibleIcon} />,
          },
        }}
      >
        <Splitter.Panel defaultSize="40%" min="30%" max="70%" collapsible>
          <Desc text="First" />
        </Splitter.Panel>

        <Splitter.Panel collapsible>
          <Desc text="Second" />
        </Splitter.Panel>
      </Splitter>
    </ConfigProvider>
  );
};

export default App;

커스텀 시맨틱 DOM 스타일링 (Custom semantic dom styling)

classNames와 styles에 객체 또는 함수를 넘겨서 Splitter의 시맨틱 DOM 스타일을 커스터마이즈할 수 있습니다.

import React from 'react';
import { Flex, Splitter, Typography } from 'antd';
import type { GetProp, SplitterProps } from 'antd';
import { createStaticStyles } from 'antd-style';

const Desc: React.FC<Readonly<{ text?: string | number; style?: React.CSSProperties }>> = (
  props,
) => {
  return (
    <Flex justify="center" align="center" style={{ height: '100%' }}>
      <Typography.Title type="secondary" level={5} style={props.style}>
        {props.text}
      </Typography.Title>
    </Flex>
  );
};

const styles = createStaticStyles(({ css, cssVar }) => ({
  boxShadow: css`
    box-shadow: ${cssVar.boxShadowSecondary};
  `,
}));

const stylesObject: SplitterProps['styles'] = {
  root: { backgroundColor: '#fffbe6' },
  // dragger: { backgroundColor: 'rgba(194,223,252,0.4)' },
  dragger: { default: { backgroundColor: 'rgba(194,223,252,0.4)' } },
};

const stylesFn: SplitterProps['styles'] = ({
  props,
}): GetProp<SplitterProps, 'styles', 'Return'> => {
  if (props.orientation === 'horizontal') {
    return {
      root: {
        borderWidth: 2,
        borderStyle: 'dashed',
        marginBottom: 10,
      },
    };
  }
  return {};
};

const App: React.FC = () => {
  const splitSharedProps: SplitterProps = {
    style: { height: 200 },
    classNames: { root: styles.boxShadow },
  };

  return (
    <Flex vertical gap="large">
      <Splitter {...splitSharedProps} styles={stylesObject}>
        <Splitter.Panel>
          <Desc text="First" style={{ color: '#000' }} />
        </Splitter.Panel>
        <Splitter.Panel>
          <Desc text="Second" style={{ color: '#000' }} />
        </Splitter.Panel>
      </Splitter>
      <Splitter {...splitSharedProps} styles={stylesFn}>
        <Splitter.Panel>
          <Desc text="First" />
        </Splitter.Panel>
        <Splitter.Panel>
          <Desc text="Second" />
        </Splitter.Panel>
      </Splitter>
    </Flex>
  );
};

export default App;

더블클릭 리셋 (Double-clicked reset)

드래거를 더블클릭하면 Splitter.Panel을 기본 크기로 초기화합니다.

import React, { useState } from 'react';
import { Flex, Splitter, Typography } from 'antd';

const defaultSizes = ['30%', '40%', '30%'];

const Desc: React.FC<Readonly<{ text?: string | number }>> = (props) => (
  <Flex justify="center" align="center" style={{ height: '100%' }}>
    <Typography.Title type="secondary" level={5} style={{ whiteSpace: 'nowrap' }}>
      Panel {props.text}
    </Typography.Title>
  </Flex>
);

const App: React.FC = () => {
  const [sizes, setSizes] = useState<(number | string)[]>(defaultSizes);

  const handleDoubleClick = () => {
    setSizes(defaultSizes);
  };

  return (
    <Splitter
      style={{ height: 200, boxShadow: '0 0 10px rgba(0, 0, 0, 0.1)' }}
      onResize={setSizes}
      onDraggerDoubleClick={handleDoubleClick}
    >
      <Splitter.Panel size={sizes[0]}>
        <Desc text={1} />
      </Splitter.Panel>

      <Splitter.Panel size={sizes[1]}>
        <Desc text={2} />
      </Splitter.Panel>

      <Splitter.Panel size={sizes[2]}>
        <Desc text={3} />
      </Splitter.Panel>
    </Splitter>
  );
};

export default App;

API

Common props ref:Common props

Splitter 컴포넌트는 자식 요소를 통해 패널 크기를 계산해야 하므로, 자식 요소는 Splitter.Panel만 지원합니다.

Splitter

Property Description Type Default Version Global Config
classNames Customize class for each semantic structure inside the component. Supports object or function. Record<SemanticDOM, string> | (info: { props })=> Record<SemanticDOM, string> - 6.0.0 6.0.0
collapsible motion to enable collapse animation, icon to customize collapse icons { motion?: boolean; icon?: { start?: ReactNode; end?: ReactNode } } - 6.4.0 ×
collapsibleIcon custom collapsible icon {start?: ReactNode; end?: ReactNode} - 6.0.0 ×
destroyOnHidden Destroy panel content when collapsed (size is 0). Applies to all panels, can be overridden per panel boolean false 6.4.0 ×
draggerIcon custom dragger icon ReactNode - 6.0.0 ×
layout Layout direction horizontal | vertical horizontal - ×
lazy Lazy mode boolean false 5.23.0 ×
onCollapse Callback when expanding or collapsing (collapsed: boolean[], sizes: number[]) => void - 5.28.0 ×
orientation Orientation direction horizontal | vertical horizontal 6.0.0 ×
styles Customize inline style for each semantic structure inside the component. Supports object or function. Record<SemanticDOM, CSSProperties> | (info: { props })=> Record<SemanticDOM, CSSProperties> - 6.0.0 6.0.0
vertical Orientation. Simultaneously existing with orientation, orientation takes priority boolean false 6.0.0 ×
onDraggerDoubleClick Callback triggered when the dragger is double-clicked (index: number) => void - 6.3.0 ×
onResize Panel size change callback (sizes: number[]) => void - - ×
onResizeEnd Drag end callback (sizes: number[]) => void - - ×
onResizeStart Callback before dragging starts (sizes: number[]) => void - - ×

Panel

Property Description Type Default Version
collapsible Quick folding boolean | { start?: boolean; end?: boolean; showCollapsibleIcon?: boolean | 'auto' } false showCollapsibleIcon: 5.27.0
defaultSize Initial panel size support number for px or 'percent%' usage number | string - -
destroyOnHidden Destroy panel content when collapsed (size is 0). Overrides Splitter's destroyOnHidden boolean - 6.4.0
max Maximum threshold support number for px or 'percent%' usage number | string - -
min Minimum threshold support number for px or 'percent%' usage number | string - -
resizable Whether to enable drag and drop boolean true -
size Controlled panel size support number for px or 'percent%' usage number | string - -

시맨틱 DOM (Semantic DOM)

https://ant.design/components/splitter/semantic.md

디자인 토큰 (Design Token)

더 알아보기 (Learn more)